grammar WSL;/** Lexer*/Whitespace: [ \t\r\n]+ -> skip ;LineComment: '//'[^\r\n] -> skip ;LongComment: '/*'.*?'*/' -> skip ;// Note: we forbid leading 0s in decimal integers. to bikeshed.fragment CoreDecimalIntLiteral: [1-9] [0-9]* ;// Note: we allow a leading '-' but not a leading '+' in all kind of numeric literals. to bikeshed.fragment DecimalIntLiteral: '-'? CoreDecimalIntLiteral ;fragment DecimalUIntLiteral: CoreDecimalIntLiteral 'u' ;fragment CoreHexadecimalIntLiteral: '0x' [0-9a-fA-F]+ ;fragment HexadecimalIntLiteral: '-'? CoreHexadecimalIntLiteral;fragment HexadecimalUIntLiteral: CoreHexadecimalIntLiteral 'u';IntLiteral: DecimalIntLiteral | DecimalUIntLiteral | HexadecimalIntLiteral | HexadecimalUIntLiteral ;// Do we want to allow underscores in the middle of numbers for readability?fragment CoreFloatLiteral: [0-9]+'.'[0-9]* | [0-9]*'.'[0-9]+ ;FloatLiteral: '-'? CoreFloatLiteral [fd]? ;// TODO: what to do about floats that are too big or too small to represent?// TODO: what is the default precision? double?// IDEA: add Nan, +infinity, -infinity// IDEA: add half-precision literals// One rule per keyword, to prevent them from being recognized as identifiersSTRUCT: 'struct';PROTOCOL: 'protocol';TYPEDEF: 'typedef';ENUM: 'enum';OPERATOR: 'operator';IF: 'if';ELSE: 'else';CONTINUE: 'continue';BREAK: 'break';SWITCH: 'switch';CASE: 'case';DEFAULT: 'default';FALLTHROUGH: 'fallthrough';FOR: 'for';WHILE: 'while';DO: 'do';RETURN: 'return';TRAP: 'trap';NULL: 'null';TRUE: 'true';FALSE: 'false';// Note: We could make these three fully case sensitive or insensitive. to bikeshed.CONSTANT: 'constant';DEVICE: 'device';THREADGROUP: 'threadgroup';THREAD: 'thread';VERTEX: 'vertex';FRAGMENT: 'fragment';NATIVE: 'native';RESTRICTED: 'restricted';// Note: these could be only keyword in the native mode, but I decided to make them always reserved. to bikeshed.UNDERSCORE: '_';AUTO: 'auto';// Note: these are currently not used by the grammar, but I would like to make them reserved keywords for future expansion of the language. to bikeshedfragment ValidIdentifier: [a-zA-Z_] [a-zA-Z0-9_]* ;Identifier: ValidIdentifier ;// Note: this currently excludes unicode, but allows digits in the middle of identifiers. We could easily restrict or extend this definition. to bikeshedOperatorName: 'operator' ('>>' | '<<' | '+' | '-' | '*' | '/' | '%' | '&&' | '||' | '&' | '^' | '|' | '>=' | '<=' | '==' | '<' | '>' | '++' | '--' | '!' | '~' | '[]' | '[]=' | '&[]')| 'operator&.' ValidIdentifier| 'operator.' ValidIdentifier '='| 'operator.' ValidIdentifier ;// Note: operator!= is not user-definable, it is automatically derived from operator==/** Parser: Top-level*/file: topLevelDecl* EOF ;topLevelDecl: ';'| variableDecls ';'| typeDef| structDef| enumDef| funcDef| nativeFuncDecl| nativeTypeDecl| protocolDecl ;typeDef: TYPEDEF Identifier typeParameters '=' type ';' ;structDef: STRUCT Identifier typeParameters '{' structElement* '}' ;structElement: type Identifier ';' ;enumDef: ENUM Identifier (':' type)? '{' enumMember (',' enumMember)* '}' ;// Note: we could allow an extra ',' at the end of the list of enumMembers, ala Rust, to make it easier to reorder the members. to bikeshedenumMember: Identifier ('=' constexpr)? ;funcDef: RESTRICTED? funcDecl block;funcDecl: (VERTEX | FRAGMENT) type Identifier parameters| type (Identifier | OperatorName) typeParameters parameters| OPERATOR typeParameters type parameters ;// Note: the return type is moved in a different place for operator casts, as a hint that it plays a role in overload resolution. to bikeshedparameters: '(' ')'| '(' parameter (',' parameter)* ')' ;parameter: type Identifier? ;nativeFuncDecl: RESTRICTED? NATIVE funcDecl ';' ;nativeTypeDecl: NATIVE TYPEDEF Identifier typeParameters ';' ;protocolDecl: PROTOCOL Identifier (':' protocolRef (',' protocolRef)*)? '{' (funcDecl ';')* '}' ;// Note: I forbid empty extensions lists in protocol declarations, while the original js parser allowed them. to bikeshedprotocolRef: Identifier ;/** Parser: Types*/typeParameters: '<' typeParameter (',' typeParameter)* '>'| ('<' '>')?;// Note: contrary to C++ for example, we allow '<>' and consider it equivalent to having no type parameters at all. to bikeshedtypeParameter: type Identifier| Identifier (':' protocolRef ('+' protocolRef)*)? ;type: addressSpace Identifier typeArguments typeSuffixAbbreviated+| Identifier typeArguments typeSuffixNonAbbreviated* ;addressSpace: CONSTANT | DEVICE | THREADGROUP | THREAD ;typeSuffixAbbreviated: '*' | '[]' | '[' IntLiteral ']';typeSuffixNonAbbreviated: '*' addressSpace | '[]' addressSpace | '[' IntLiteral ']' ;// Note: in this formulation of typeSuffix*, we don't allow whitespace between the '[' and the ']' in '[]'. We easily could at the cost of a tiny more bit of lookahead. to bikeshedtypeArguments: '<' (typeArgument ',')* addressSpace? Identifier '<' (typeArgument (',' typeArgument)*)? '>>'//Note: this first alternative is a horrible hack to deal with nested generics that end with '>>'. As far as I can tell it works fine, but requires arbitrary lookahead.| '<' typeArgument (',' typeArgument)* '>'| ('<' '>')? ;typeArgument: constexpr | type ;/** Parser: Statements*/block: '{' blockBody '}' ;blockBody: stmt* ;stmt: block| ifStmt| switchStmt| forStmt| whileStmt| doStmt ';'| BREAK ';'| CONTINUE ';'| FALLTHROUGH ';'| TRAP ';'| RETURN expr? ';'| variableDecls ';'| effectfulExpr ';' ;ifStmt: IF '(' expr ')' stmt (ELSE stmt)? ;switchStmt: SWITCH '(' expr ')' '{' switchCase* '}' ;switchCase: (CASE constexpr | DEFAULT) ':' blockBody ;forStmt: FOR '(' (variableDecls | effectfulExpr) ';' expr? ';' expr? ')' stmt ;whileStmt: WHILE '(' expr ')' stmt ;doStmt: DO stmt WHILE '(' expr ')' ;variableDecls: type variableDecl (',' variableDecl)* ;variableDecl: Identifier ('=' expr)? ;/** Parser: Expressions*/constexpr: literal| Identifier // to get the (constexpr) value of a type variable| Identifier '.' Identifier; // to get a value out of an enum// Note: we separate effectful expressions from normal expressions, and only allow the former in statement positions, to disambiguate the following:// "x * y;". Without this trick, it would look like both an expression and a variable declaration, and could not be disambiguated until name resolution.effectfulExpr: ((effAssignment ',')* effAssignment)? ;effAssignment: possiblePrefix assignOperator possibleTernaryConditional| effPrefix ;assignOperator: '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '^=' | '&=' | '|=' | '>>=' | '<<=' ;effPrefix: ('++' | '--') possiblePrefix| effSuffix ;effSuffix: possibleSuffix ('++' | '--')| callExpression| '(' expr ')' ;// Note: this last case is to allow craziness like "(x < y ? z += 42 : w += 13);"// TODO: Not sure at all how useful it is, I also still have to double check that it introduces no ambiguity.limitedSuffixOperator: '.' Identifier| '->' Identifier| '[' expr ']' ;expr: (possibleTernaryConditional ',')* possibleTernaryConditional;// TODO: I tried to mimic https://en.cppreference.com/w/cpp/language/operator_precedence with regards to assignment and ternary conditionals, but it still needs some testingpossibleTernaryConditional: possibleLogicalBinop '?' expr ':' possibleTernaryConditional| possiblePrefix assignOperator possibleTernaryConditional| possibleLogicalBinop ;possibleLogicalBinop: possibleRelationalBinop (logicalBinop possibleRelationallBinop)*;logicalBinop: '||' | '&&' | '|' | '^' | '&' ;// Note: the list above may need some manipulation to get the proper left-to-right associativitypossibleRelationalBinop: possibleShift (relationalBinop possibleShift)?;relationalBinop: '<' | '>' | '<=' | '>=' | '==' | '!=' ;// Note: we made relational binops non-associative to better disambiguate "x<y>(z)" into a call expression and not a comparison of comparison// Idea: https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparisonpossibleShift: possibleAdd (('>>' | '<<') possibleAdd)* ;possibleAdd: possibleMult (('+' | '-') possibleMult)* ;possibleMult: possiblePrefix (('*' | '/' | '%') possiblePrefix)* ;possiblePrefix: prefixOp* possibleSuffix ;prefixOp: '++' | '--' | '+' | '-' | '~' | '!' | '&' | '@' | '*' ;possibleSuffix: callExpression limitedSuffixOperator*| term (limitedSuffixOperator | '++' | '--')* ;callExpression: Identifier typeArguments '(' (possibleTernaryConditional (',' possibleTernaryConditional)*)? ')';term: literal| Identifier| '(' expr ')' ;literal: IntLiteral | FloatLiteral | NULL | TRUE | FALSE;