-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwizard.g4
302 lines (264 loc) · 10.1 KB
/
wizard.g4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
// TODO See if the use of fragments for keywords is OK or annoying in semantic analysis
// TODO Check if the old parser allowed incrementing/decrementing anything and make that a breaking change
// Style guide:
// - Parser rules are camelCase
// - Lexer rules are PascalCase
// - Exception: fragments and skips (i.e. ones that users will
// _never_ see) are entirely uppercase.
// - Wrap to 70 characters
// - If a token has a single usage, move it up to its usage in the
// parser. Saves a line. Exception is if the definition is long or
// complex (e.g. Keyword).
grammar wizard;
/* ==== PARSER ==== */
/* === BASICS === */
// The main entry point.
// A body, followed by EOF.
parseWizard: body EOF;
// A series of commands.
body: (stmt | expr)*;
/* === STATEMENTS === */
// A command without a return value. All statements can stand on
// their own.
stmt: assignment
| compoundAssignment
| controlFlowStmt
| keywordStmt;
/* = ASSIGNMENT = */
// Just assigns a value to a variable.
assignment: Identifier Assign expr;
// Compound assignments are statements of the form a x= b, where:
// a is a variable
// x is a mathematical operation
// b is an expression
compoundAssignment: Identifier (CompoundExp
| CompoundMul
| CompoundDiv
| CompoundMod
| CompoundAdd
| CompoundSub) expr;
/* = CONTROL FLOW = */
// Statements that alter control flow.
controlFlowStmt: Break # Break
| cancelStmt # Cancel
| Continue # Continue
| forStmt # For
| ifStmt # If
| Return # Return
| selectStmt # Select
| whileStmt # While
;
// Cancels the entire wizard and optionally shows a reason to the
// user. The expr (which is the optional reason) must be a string if
// present, type-check is during semantic analysis.
cancelStmt: 'Cancel' expr?;
// Describes what do in a select statement if a certain case is hit.
// expr must be a string, type-check is during semantic analysis.
caseStmt: 'Case' expr body;
// Describes what to do in a select statement if none of the cases
// are hit.
defaultStmt: 'Default' body;
// An elif statement, parsed like a regular if statement.
elifStmt: Elif expr body;
// An else statement, parsed like an if statement without a guard
// expression.
elseStmt: Else body;
// A for loop. There are two possible types of for loop that differ
// only in their headers. They each begin with the keyword For,
// followed by an iteration variable a and then the header. Finally,
// a body and an EndFor keyword terminate the for loop.
forStmt: 'For' Identifier (forRangeHeader | forInHeader) body EndFor;
// The header of a for loop of the form 'For a from b to c [by d]',
// where:
// a is the iteration variable, as specified in forStmt
// b is the start value
// c is the end value
// d (optional) is the step size
forRangeHeader: 'from' expr 'to' expr ('by' expr)?;
// The header of a for loop of the form 'For a in b', where:
// a is the iteration variable, as specified in forStmt
// b is a value to iterate over
forInHeader: In expr;
// An if statement may have any number of elif statements, but at
// most one else statement.
ifStmt: 'If' expr body elifStmt* elseStmt? EndIf;
// There are two types of Select statement.
selectStmt: (selectOne | selectMany) EndSelect;
// The two types differ only in their initial keyword.
// We copy their signature here to simplify the semantic analysis.
// Note that we check whether or not the selectCaseList is valid
// and all these expr's resolve to strings during semantic analysis.
// A trailing comma is allowed here - it's simply ignored.
selectCaseList: caseStmt* defaultStmt?;
optionTuple: expr Comma expr Comma expr;
selectOne: 'SelectOne' expr (Comma optionTuple)* Comma? selectCaseList;
selectMany: 'SelectMany' expr (Comma optionTuple)* Comma? selectCaseList;
// A simple while loop. Runs until the guard is false.
whileStmt: 'While' expr body EndWhile;
/* = Keyword STATEMENTS = */
// A keyword statement is just a keyword followed by a
// comma-separated list of arguments.
// Note that argList is reused for functions later down.
argList: (expr (Comma expr)*)?;
keywordStmt: Keyword argList;
/* === EXPRESSIONS === */
// A command with a return value.
// The order matters here - it specifies the operator precedence.
expr: LeftParenthesis expr RightParenthesis # ParenExpr
// Function calls
// May not actually return anything - we still parse them as
// expressions for simplicity and check the return type when
// doing semantic analysis.
| expr Dot Identifier LeftParenthesis argList RightParenthesis # DotFunctionCall
| Identifier LeftParenthesis argList RightParenthesis # FunctionCall
// Increment / Decrement
// Note that, for backwards compatibility, postfix and prefix
// should both return the new value.
| Increment Identifier # PreIncrement
| Identifier Increment # PostIncrement
| Decrement Identifier # PreDecrement
| Identifier Decrement # PostDecrement
// Mathematical operators, part 1
| Minus expr # Negative
// Logic operators, part 1
| ('!' | 'not') expr # Not
// Indexing
| expr LeftBracket expr RightBracket # Index
// Slicing
| expr LeftBracket expr? Colon expr? (Colon expr?)? RightBracket # Slice
// Mathematical operators, part 2
| expr Raise expr # Exponentiation
| expr (Times | Divide | Modulo) expr # TimesDivideModulo
| expr (Plus | Minus) expr # PlusMinus
// Comparison operators
// Colon present => case-insensitive
| expr (Greater | GreaterOrEqual) Colon? expr # Greater
| expr (Lesser | LesserOrEqual) Colon? expr # Lesser
| expr (Equal | NotEqual) Colon? expr # Equal
// Logic operators, part 2
| expr ('|' | 'or') expr # Or
| expr ('&' | 'and') expr # And
// 'in' operator
// Colon present => case-insensitive
| expr In expr Colon? # In
// Direct values
// Constants, literals (three types) and variables
| (constant | decimal | integer | string | Identifier) # Value
;
/* == NONEXECUTABLE EXPRESSIONS == */
// These are expressions that immediately resolve to a value,
// without any operation being involved.
// They cannot stand on their own, but we check this during semantic
// analysis.
/* = CONSTANTS = */
// One of the predefined constants for wizards.
constant: 'False' | 'True' | 'SubPackages';
/* = LITERALS = */
// Only three types in this language - plus some 'pseudotypes' for
// list-like objects such as SubPackages.
// Numbers - may be positive, negative or zero.
// Note that we keep these unnecessarily complex to simplify the
// semantic analysis.
integer: Number;
decimal: Number Dot Number;
// Strings - can use either "my string" or 'my string'. May also
// contain escape sequences.
string: DoubleQuotedString | SingleQuotedString;
/* ==== LEXER ==== */
// Skip all comments.
COMMENT: ';' ~[\r\n]* -> skip;
// A line continuation - a backslash and a newline.
// We simply ignore them, eating the newline in the process. This
// simulates us appending the next line to the end of this one.
// Note the [ \\\t]* section - eats up any backslashes and whitespace
// between the initial backslash and the newline. The old parser
// accepted those too, so we accept them for the sake of (relatively)
// painless backwards compatibility.
CONTINUATION: '\\' [ \\\t]* '\r'? '\n' -> skip;
// Common Operators
Comma: ',';
Dot: '.';
LeftParenthesis: '(';
RightParenthesis: ')';
LeftBracket: '[';
RightBracket: ']';
Colon: ':';
// Assignment Operators
// Note that the compound assignment operators use the math
// operators defined below.
fragment EQ_SIGN: '=';
CompoundAdd: Plus EQ_SIGN;
CompoundSub: Minus EQ_SIGN;
CompoundMul: Times EQ_SIGN;
CompoundDiv: Divide EQ_SIGN;
CompoundExp: Raise EQ_SIGN;
CompoundMod: Modulo EQ_SIGN;
// Comparison Operators
// Note that the order matters here - we want the LesserOrEqual
// token filled first, if possible. Otherwise, the Lesser token
// would be matched even for legitimate cases where the
// LesserOrEqual one needs to match.
fragment GT_SIGN: '>';
fragment LT_SIGN: '<';
fragment EXMARK: '!';
Equal: EQ_SIGN EQ_SIGN;
GreaterOrEqual: GT_SIGN EQ_SIGN;
Greater: GT_SIGN;
LesserOrEqual: LT_SIGN EQ_SIGN;
Lesser: LT_SIGN;
NotEqual: EXMARK EQ_SIGN;
// Special Case: need to define this last so it doesn't swallow all
// equals signs -> we want compound assignments and comparisons to
// process first.
Assign: EQ_SIGN;
// Control Flow Keywords
// These are separate tokens for the benefit of interpreters
// (specifically, to allow them to recover from errors due to
// incorrect wizard syntax more easily).
Break: 'Break';
Continue: 'Continue';
Elif: 'Elif';
Else: 'Else';
EndFor: 'EndFor';
EndIf: 'EndIf';
EndSelect: 'EndSelect';
EndWhile: 'EndWhile';
Return: 'Return';
// Keywords
// Note the alternatives that are kept for backwards compatibility.
Keyword: 'DeSelectAll'
| 'DeSelectAllPlugins' | 'DeSelectAllEspms'
| 'DeSelectPlugin' | 'DeSelectEspm'
| 'DeSelectSubPackage'
| 'Note'
| 'RenamePlugin' | 'RenameEspm'
| 'RequireVersions'
| 'ResetPluginName' | 'ResetEspmName'
| 'ResetAllPluginNames' | 'ResetAllEspmNames'
| 'SelectAll'
| 'SelectAllPlugins' | 'SelectAllEspms'
| 'SelectPlugin' | 'SelectEspm'
| 'SelectSubPackage';
// Literals
fragment ESC: '\\' ~[\n\r];
Number: [0-9]+;
DoubleQuotedString: '"' (ESC | ~[\\"])* '"';
SingleQuotedString: '\'' (ESC | ~[\\'])* '\'';
// Mathematical Operators
Divide: '/';
Minus: '-';
Plus: '+';
Raise: '^';
Times: '*';
Modulo: '%';
// Misc Operators
Decrement: Minus Minus;
In: 'in';
Increment: Plus Plus;
// These rules need to pretty much come last, otherwise they would
// swallow up previous definitions.
// Identifiers - basically captures most leftovers.
Identifier: [A-Za-z_][A-Za-z0-9_]*;
// Ignore whitespace - at least for now, we might actually need this
// token though.
WHITESPACE: [ \t\n\r]+ -> skip;