Make JetStream 2
[WebKit-https.git] / PerformanceTests / JetStream2 / WSL / SpecWork / WSL.g4
1 grammar WSL;
2
3 /*
4  * Lexer
5  */
6 Whitespace: [ \t\r\n]+ -> skip ;
7 LineComment: '//'[^\r\n] -> skip ;
8 LongComment: '/*'.*?'*/' -> skip ;
9
10 // Note: we forbid leading 0s in decimal integers. to bikeshed.
11 fragment CoreDecimalIntLiteral: [1-9] [0-9]* ;
12 // Note: we allow a leading '-' but not a leading '+' in all kind of numeric literals. to bikeshed.
13 fragment DecimalIntLiteral: '-'? CoreDecimalIntLiteral ;
14 fragment DecimalUIntLiteral: CoreDecimalIntLiteral 'u' ;
15 fragment CoreHexadecimalIntLiteral: '0x' [0-9a-fA-F]+ ;
16 fragment HexadecimalIntLiteral: '-'? CoreHexadecimalIntLiteral;
17 fragment HexadecimalUIntLiteral: CoreHexadecimalIntLiteral 'u';
18 IntLiteral: DecimalIntLiteral | DecimalUIntLiteral | HexadecimalIntLiteral | HexadecimalUIntLiteral ;
19 // Do we want to allow underscores in the middle of numbers for readability?
20
21 fragment CoreFloatLiteral: [0-9]+'.'[0-9]* | [0-9]*'.'[0-9]+ ;
22 FloatLiteral: '-'? CoreFloatLiteral [fd]? ;
23 // TODO: what to do about floats that are too big or too small to represent?
24 // TODO: what is the default precision? double?
25 // IDEA: add Nan, +infinity, -infinity
26 // IDEA: add half-precision literals
27
28 // One rule per keyword, to prevent them from being recognized as identifiers
29 STRUCT: 'struct';
30 PROTOCOL: 'protocol';
31 TYPEDEF: 'typedef';
32 ENUM: 'enum';
33 OPERATOR: 'operator';
34
35 IF: 'if';
36 ELSE: 'else';
37 CONTINUE: 'continue';
38 BREAK: 'break';
39 SWITCH: 'switch';
40 CASE: 'case';
41 DEFAULT: 'default';
42 FALLTHROUGH: 'fallthrough';
43 FOR: 'for';
44 WHILE: 'while';
45 DO: 'do';
46 RETURN: 'return';
47 TRAP: 'trap';
48
49 NULL: 'null';
50 TRUE: 'true';
51 FALSE: 'false';
52 // Note: We could make these three fully case sensitive or insensitive. to bikeshed.
53
54 CONSTANT: 'constant';
55 DEVICE: 'device';
56 THREADGROUP: 'threadgroup';
57 THREAD: 'thread';
58
59 VERTEX: 'vertex';
60 FRAGMENT: 'fragment';
61
62 NATIVE: 'native';
63 RESTRICTED: 'restricted';
64 // Note: these could be only keyword in the native mode, but I decided to make them always reserved. to bikeshed.
65
66 UNDERSCORE: '_';
67 AUTO: 'auto';
68 // 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 bikeshed
69
70 fragment ValidIdentifier: [a-zA-Z_] [a-zA-Z0-9_]* ;
71 Identifier: ValidIdentifier ;
72 // Note: this currently excludes unicode, but allows digits in the middle of identifiers. We could easily restrict or extend this definition. to bikeshed
73
74 OperatorName
75     : 'operator' ('>>' | '<<' | '+' | '-' | '*' | '/' | '%' | '&&' | '||' | '&' | '^' | '|' | '>=' | '<=' | '==' | '<' | '>' | '++' | '--' | '!' | '~' | '[]' | '[]=' | '&[]')
76     | 'operator&.' ValidIdentifier
77     | 'operator.' ValidIdentifier '='
78     | 'operator.' ValidIdentifier ;
79 // Note: operator!= is not user-definable, it is automatically derived from operator==
80
81 /*
82  * Parser: Top-level
83  */
84 file: topLevelDecl* EOF ;
85 topLevelDecl
86     : ';'
87     | variableDecls ';'
88     | typeDef
89     | structDef
90     | enumDef
91     | funcDef
92     | nativeFuncDecl
93     | nativeTypeDecl
94     | protocolDecl ;
95
96 typeDef: TYPEDEF Identifier typeParameters '=' type ';' ;
97
98 structDef: STRUCT Identifier typeParameters '{' structElement* '}' ;
99 structElement: type Identifier ';' ;
100
101 enumDef: ENUM Identifier (':' type)? '{' enumMember (',' enumMember)* '}' ;
102 // 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 bikeshed
103 enumMember: Identifier ('=' constexpr)? ;
104
105 funcDef: RESTRICTED? funcDecl block;
106 funcDecl
107     : (VERTEX | FRAGMENT) type Identifier parameters
108     | type (Identifier | OperatorName) typeParameters parameters
109     | OPERATOR typeParameters type parameters ;
110 // 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 bikeshed
111 parameters
112     : '(' ')'
113     | '(' parameter (',' parameter)* ')' ;
114 parameter: type Identifier? ;
115
116 nativeFuncDecl: RESTRICTED? NATIVE funcDecl ';' ;
117 nativeTypeDecl: NATIVE TYPEDEF Identifier typeParameters ';' ;
118
119 protocolDecl: PROTOCOL Identifier (':' protocolRef (',' protocolRef)*)? '{' (funcDecl ';')* '}' ;
120 // Note: I forbid empty extensions lists in protocol declarations, while the original js parser allowed them. to bikeshed
121 protocolRef: Identifier ;
122
123 /*
124  * Parser: Types 
125  */
126 typeParameters
127     : '<' typeParameter (',' typeParameter)* '>'
128     | ('<' '>')?;
129 // Note: contrary to C++ for example, we allow '<>' and consider it equivalent to having no type parameters at all. to bikeshed
130 typeParameter
131     : type Identifier
132     | Identifier (':' protocolRef ('+' protocolRef)*)? ;
133
134 type
135     : addressSpace Identifier typeArguments typeSuffixAbbreviated+
136     | Identifier typeArguments typeSuffixNonAbbreviated* ;
137 addressSpace: CONSTANT | DEVICE | THREADGROUP | THREAD ;
138 typeSuffixAbbreviated: '*' | '[]' | '[' IntLiteral ']';
139 typeSuffixNonAbbreviated: '*' addressSpace | '[]' addressSpace | '[' IntLiteral ']' ;
140 // 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 bikeshed
141
142 typeArguments
143     : '<' (typeArgument ',')* addressSpace? Identifier '<' (typeArgument (',' typeArgument)*)? '>>'
144     //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.
145     | '<' typeArgument (',' typeArgument)* '>'
146     | ('<' '>')? ;
147 typeArgument: constexpr | type ;
148
149 /* 
150  * Parser: Statements 
151  */
152 block: '{' blockBody '}' ;
153 blockBody: stmt* ;
154
155 stmt
156     : block
157     | ifStmt
158     | switchStmt
159     | forStmt
160     | whileStmt
161     | doStmt ';'
162     | BREAK ';'
163     | CONTINUE ';'
164     | FALLTHROUGH ';'
165     | TRAP ';'
166     | RETURN expr? ';'
167     | variableDecls ';'
168     | effectfulExpr ';' ;
169
170 ifStmt: IF '(' expr ')' stmt (ELSE stmt)? ;
171
172 switchStmt: SWITCH '(' expr ')' '{' switchCase* '}' ;
173 switchCase: (CASE constexpr | DEFAULT) ':' blockBody ;
174
175 forStmt: FOR '(' (variableDecls | effectfulExpr) ';' expr? ';' expr? ')' stmt ;
176 whileStmt: WHILE '(' expr ')' stmt ;
177 doStmt: DO stmt WHILE '(' expr ')' ;
178
179 variableDecls: type variableDecl (',' variableDecl)* ;
180 variableDecl: Identifier ('=' expr)? ;
181
182 /* 
183  * Parser: Expressions
184  */
185 constexpr
186     : literal 
187     | Identifier // to get the (constexpr) value of a type variable
188     | Identifier '.' Identifier; // to get a value out of an enum
189
190 // Note: we separate effectful expressions from normal expressions, and only allow the former in statement positions, to disambiguate the following:
191 // "x * y;". Without this trick, it would look like both an expression and a variable declaration, and could not be disambiguated until name resolution.
192 effectfulExpr: ((effAssignment ',')* effAssignment)? ; 
193 effAssignment
194     : possiblePrefix assignOperator possibleTernaryConditional
195     | effPrefix ;
196 assignOperator: '=' | '+=' | '-=' | '*=' | '/=' | '%=' | '^=' | '&=' | '|=' | '>>=' | '<<=' ;
197 effPrefix
198     : ('++' | '--') possiblePrefix
199     | effSuffix ;
200 effSuffix
201     : possibleSuffix ('++' | '--')
202     | callExpression
203     | '(' expr ')' ;
204 // Note: this last case is to allow craziness like "(x < y ? z += 42 : w += 13);" 
205 // TODO: Not sure at all how useful it is, I also still have to double check that it introduces no ambiguity.
206 limitedSuffixOperator
207     : '.' Identifier 
208     | '->' Identifier 
209     | '[' expr ']' ;
210
211 expr: (possibleTernaryConditional ',')* possibleTernaryConditional;
212 // 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 testing
213 possibleTernaryConditional
214     : possibleLogicalBinop '?' expr ':' possibleTernaryConditional
215     | possiblePrefix assignOperator possibleTernaryConditional
216     | possibleLogicalBinop ;
217 possibleLogicalBinop: possibleRelationalBinop (logicalBinop possibleRelationallBinop)*;
218 logicalBinop: '||' | '&&' | '|' | '^' | '&' ;
219 // Note: the list above may need some manipulation to get the proper left-to-right associativity
220 possibleRelationalBinop: possibleShift (relationalBinop possibleShift)?;
221 relationalBinop: '<' | '>' | '<=' | '>=' | '==' | '!=' ; 
222 // Note: we made relational binops non-associative to better disambiguate "x<y>(z)" into a call expression and not a comparison of comparison
223 // Idea: https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison
224 possibleShift: possibleAdd (('>>' | '<<') possibleAdd)* ;
225 possibleAdd: possibleMult (('+' | '-') possibleMult)* ;
226 possibleMult: possiblePrefix (('*' | '/' | '%') possiblePrefix)* ;
227 possiblePrefix: prefixOp* possibleSuffix ;
228 prefixOp: '++' | '--' | '+' | '-' | '~' | '!' | '&' | '@' | '*' ;
229 possibleSuffix
230     : callExpression limitedSuffixOperator*
231     | term (limitedSuffixOperator | '++' | '--')* ;
232 callExpression: Identifier typeArguments '(' (possibleTernaryConditional (',' possibleTernaryConditional)*)? ')';
233 term
234     : literal
235     | Identifier
236     | '(' expr ')' ;
237
238 literal: IntLiteral | FloatLiteral | NULL | TRUE | FALSE;
239