nscript 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,483 @@
1
+ class Parser
2
+
3
+ # Declare terminal tokens produced by the lexer.
4
+ token IF ELSE UNLESS
5
+ token NUMBER STRING REGEX
6
+ token TRUE FALSE YES NO ON OFF
7
+ token IDENTIFIER PROPERTY_ACCESS PROTOTYPE_ACCESS SOAK_ACCESS
8
+ token CODE PARAM_START PARAM PARAM_END NEW RETURN
9
+ token CALL_START CALL_END INDEX_START INDEX_END
10
+ token TRY CATCH FINALLY THROW
11
+ token BREAK CONTINUE
12
+ token FOR IN OF BY WHEN WHILE
13
+ token SWITCH LEADING_WHEN
14
+ token DELETE INSTANCEOF TYPEOF
15
+ token SUPER EXTENDS
16
+ token ASSIGN RETURN
17
+ token NEWLINE
18
+ token COMMENT
19
+ token JS
20
+ token INDENT OUTDENT
21
+
22
+ # Declare order of operations.
23
+ prechigh
24
+ left '?'
25
+ nonassoc UMINUS UPLUS NOT '!' '!!' '~' '++' '--'
26
+ left '*' '/' '%' '.'
27
+ left '+' '-'
28
+ left '<<' '>>' '>>>' '&' '|' '^'
29
+ left '<=' '<' '>' '>='
30
+ right '==' '!=' IS ISNT
31
+ left '&&' '||' AND OR
32
+ right '-=' '+=' '/=' '*=' '%=' '||=' '&&=' '?='
33
+ right DELETE INSTANCEOF TYPEOF
34
+ right INDENT
35
+ left OUTDENT
36
+ right WHEN LEADING_WHEN IN OF BY
37
+ right THROW FOR NEW SUPER
38
+ left EXTENDS
39
+ right ASSIGN RETURN
40
+ right '->' '=>' UNLESS IF ELSE WHILE
41
+ preclow
42
+
43
+ rule
44
+
45
+ # All parsing will end in this rule, being the trunk of the AST.
46
+ Root:
47
+ /* nothing */ { result = Expressions.new }
48
+ | Terminator { result = Expressions.new }
49
+ | Expressions { result = val[0] }
50
+ | Block Terminator { result = val[0] }
51
+ ;
52
+
53
+ # Any list of expressions or method body, seperated by line breaks or semis.
54
+ Expressions:
55
+ Expression { result = Expressions.wrap(val) }
56
+ | Expressions Terminator Expression { result = val[0] << val[2] }
57
+ | Expressions Terminator { result = val[0] }
58
+ ;
59
+
60
+ # All types of expressions in our language. The basic unit of NScript
61
+ # is the expression.
62
+ Expression:
63
+ Value
64
+ | Call
65
+ | Code
66
+ | Operation
67
+ | Assign
68
+ | If
69
+ | Try
70
+ | Throw
71
+ | Return
72
+ | While
73
+ | For
74
+ | Switch
75
+ | Extends
76
+ | Splat
77
+ | Existence
78
+ | Comment
79
+ ;
80
+
81
+ # A block of expressions. Note that the Rewriter will convert some postfix
82
+ # forms into blocks for us, by altering the token stream.
83
+ Block:
84
+ INDENT Expressions OUTDENT { result = val[1] }
85
+ | INDENT OUTDENT { result = Expressions.new }
86
+ ;
87
+
88
+ # Tokens that can terminate an expression.
89
+ Terminator:
90
+ "\n"
91
+ | ";"
92
+ ;
93
+
94
+ # All hard-coded values. These can be printed straight to JavaScript.
95
+ Literal:
96
+ NUMBER { result = LiteralNode.new(val[0]) }
97
+ | STRING { result = LiteralNode.new(val[0]) }
98
+ | JS { result = LiteralNode.new(val[0]) }
99
+ | REGEX { result = LiteralNode.new(val[0]) }
100
+ | BREAK { result = LiteralNode.new(val[0]) }
101
+ | CONTINUE { result = LiteralNode.new(val[0]) }
102
+ | TRUE { result = LiteralNode.new(Value.new(true)) }
103
+ | FALSE { result = LiteralNode.new(Value.new(false)) }
104
+ | YES { result = LiteralNode.new(Value.new(true)) }
105
+ | NO { result = LiteralNode.new(Value.new(false)) }
106
+ | ON { result = LiteralNode.new(Value.new(true)) }
107
+ | OFF { result = LiteralNode.new(Value.new(false)) }
108
+ ;
109
+
110
+ # Assignment to a variable (or index).
111
+ Assign:
112
+ Value ASSIGN Expression { result = AssignNode.new(val[0], val[2]) }
113
+ ;
114
+
115
+ # Assignment within an object literal (can be quoted).
116
+ AssignObj:
117
+ IDENTIFIER ASSIGN Expression { result = AssignNode.new(ValueNode.new(val[0]), val[2], :object) }
118
+ | STRING ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
119
+ | NUMBER ASSIGN Expression { result = AssignNode.new(ValueNode.new(LiteralNode.new(val[0])), val[2], :object) }
120
+ | Comment { result = val[0] }
121
+ ;
122
+
123
+ # A return statement.
124
+ Return:
125
+ RETURN Expression { result = ReturnNode.new(val[1]) }
126
+ | RETURN { result = ReturnNode.new(ValueNode.new(Value.new('null'))) }
127
+ ;
128
+
129
+ # A comment.
130
+ Comment:
131
+ COMMENT { result = CommentNode.new(val[0]) }
132
+ ;
133
+
134
+ # Arithmetic and logical operators
135
+ # For Ruby's Operator precedence, see:
136
+ # https://www.cs.auckland.ac.nz/references/ruby/ProgrammingRuby/language.html
137
+ Operation:
138
+ '!' Expression { result = OpNode.new(val[0], val[1]) }
139
+ | '!!' Expression { result = OpNode.new(val[0], val[1]) }
140
+ | '-' Expression = UMINUS { result = OpNode.new(val[0], val[1]) }
141
+ | '+' Expression = UPLUS { result = OpNode.new(val[0], val[1]) }
142
+ | NOT Expression { result = OpNode.new(val[0], val[1]) }
143
+ | '~' Expression { result = OpNode.new(val[0], val[1]) }
144
+ | '--' Expression { result = OpNode.new(val[0], val[1]) }
145
+ | '++' Expression { result = OpNode.new(val[0], val[1]) }
146
+ | DELETE Expression { result = OpNode.new(val[0], val[1]) }
147
+ | TYPEOF Expression { result = OpNode.new(val[0], val[1]) }
148
+ | Expression '--' { result = OpNode.new(val[1], val[0], nil, true) }
149
+ | Expression '++' { result = OpNode.new(val[1], val[0], nil, true) }
150
+
151
+ | Expression '*' Expression { result = OpNode.new(val[1], val[0], val[2]) }
152
+ | Expression '/' Expression { result = OpNode.new(val[1], val[0], val[2]) }
153
+ | Expression '%' Expression { result = OpNode.new(val[1], val[0], val[2]) }
154
+
155
+ | Expression '+' Expression { result = OpNode.new(val[1], val[0], val[2]) }
156
+ | Expression '-' Expression { result = OpNode.new(val[1], val[0], val[2]) }
157
+
158
+ | Expression '<<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
159
+ | Expression '>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
160
+ | Expression '>>>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
161
+
162
+ | Expression '&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
163
+ | Expression '|' Expression { result = OpNode.new(val[1], val[0], val[2]) }
164
+ | Expression '^' Expression { result = OpNode.new(val[1], val[0], val[2]) }
165
+
166
+ | Expression '<=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
167
+ | Expression '<' Expression { result = OpNode.new(val[1], val[0], val[2]) }
168
+ | Expression '>' Expression { result = OpNode.new(val[1], val[0], val[2]) }
169
+ | Expression '>=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
170
+
171
+ | Expression '==' Expression { result = OpNode.new(val[1], val[0], val[2]) }
172
+ | Expression '!=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
173
+ | Expression IS Expression { result = OpNode.new(val[1], val[0], val[2]) }
174
+ | Expression ISNT Expression { result = OpNode.new(val[1], val[0], val[2]) }
175
+
176
+ | Expression '&&' Expression { result = OpNode.new(val[1], val[0], val[2]) }
177
+ | Expression '||' Expression { result = OpNode.new(val[1], val[0], val[2]) }
178
+ | Expression AND Expression { result = OpNode.new(val[1], val[0], val[2]) }
179
+ | Expression OR Expression { result = OpNode.new(val[1], val[0], val[2]) }
180
+ | Expression '?' Expression { result = OpNode.new(val[1], val[0], val[2]) }
181
+
182
+ | Expression '-=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
183
+ | Expression '+=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
184
+ | Expression '/=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
185
+ | Expression '*=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
186
+ | Expression '%=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
187
+ | Expression '||=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
188
+ | Expression '&&=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
189
+ | Expression '?=' Expression { result = OpNode.new(val[1], val[0], val[2]) }
190
+
191
+ | Expression INSTANCEOF Expression { result = OpNode.new(val[1], val[0], val[2]) }
192
+ | Expression IN Expression { result = OpNode.new(val[1], val[0], val[2]) }
193
+ ;
194
+
195
+ # The existence operator.
196
+ Existence:
197
+ Expression '?' { result = ExistenceNode.new(val[0]) }
198
+ ;
199
+
200
+ # Function definition.
201
+ Code:
202
+ PARAM_START ParamList PARAM_END
203
+ FuncGlyph Block { result = CodeNode.new(val[1], val[4], val[3]) }
204
+ | FuncGlyph Block { result = CodeNode.new([], val[1], val[0]) }
205
+ ;
206
+
207
+ # The symbols to signify functions, and bound functions.
208
+ FuncGlyph:
209
+ '->' { result = :func }
210
+ | '=>' { result = :boundfunc }
211
+ ;
212
+
213
+ # The parameters to a function definition.
214
+ ParamList:
215
+ Param { result = val }
216
+ | ParamList "," Param { result = val[0] << val[2] }
217
+ ;
218
+
219
+ # A Parameter (or ParamSplat) in a function definition.
220
+ Param:
221
+ PARAM
222
+ | PARAM "." "." "." { result = SplatNode.new(val[0]) }
223
+ ;
224
+
225
+ # A regular splat.
226
+ Splat:
227
+ Expression "." "." "." { result = SplatNode.new(val[0]) }
228
+ ;
229
+
230
+ # Expressions that can be treated as values.
231
+ Value:
232
+ IDENTIFIER { result = ValueNode.new(val[0]) }
233
+ | Literal { result = ValueNode.new(val[0]) }
234
+ | Array { result = ValueNode.new(val[0]) }
235
+ | Object { result = ValueNode.new(val[0]) }
236
+ | Parenthetical { result = ValueNode.new(val[0]) }
237
+ | Range { result = ValueNode.new(val[0]) }
238
+ | This { result = ValueNode.new(val[0]) }
239
+ | Value Accessor { result = val[0] << val[1] }
240
+ | Invocation Accessor { result = ValueNode.new(val[0], [val[1]]) }
241
+ ;
242
+
243
+ # Accessing into an object or array, through dot or index notation.
244
+ Accessor:
245
+ PROPERTY_ACCESS IDENTIFIER { result = AccessorNode.new(val[1]) }
246
+ | PROTOTYPE_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :prototype) }
247
+ | SOAK_ACCESS IDENTIFIER { result = AccessorNode.new(val[1], :soak) }
248
+ | Index { result = val[0] }
249
+ | Slice { result = SliceNode.new(val[0]) }
250
+ ;
251
+
252
+ # Indexing into an object or array.
253
+ Index:
254
+ INDEX_START Expression INDEX_END { result = IndexNode.new(val[1]) }
255
+ ;
256
+
257
+ # An object literal.
258
+ Object:
259
+ "{" AssignList "}" { result = ObjectNode.new(val[1]) }
260
+ ;
261
+
262
+ # Assignment within an object literal (comma or newline separated).
263
+ AssignList:
264
+ /* nothing */ { result = [] }
265
+ | AssignObj { result = val }
266
+ | AssignList "," AssignObj { result = val[0] << val[2] }
267
+ | AssignList Terminator AssignObj { result = val[0] << val[2] }
268
+ | AssignList ","
269
+ Terminator AssignObj { result = val[0] << val[3] }
270
+ | INDENT AssignList OUTDENT { result = val[1] }
271
+ ;
272
+
273
+ # All flavors of function call (instantiation, super, and regular).
274
+ Call:
275
+ Invocation { result = val[0] }
276
+ | NEW Invocation { result = val[1].new_instance }
277
+ | Super { result = val[0] }
278
+ ;
279
+
280
+ # Extending an object's prototype.
281
+ Extends:
282
+ Value EXTENDS Value { result = ExtendsNode.new(val[0], val[2]) }
283
+ ;
284
+
285
+ # A generic function invocation.
286
+ Invocation:
287
+ Value Arguments { result = CallNode.new(val[0], val[1]) }
288
+ | Invocation Arguments { result = CallNode.new(val[0], val[1]) }
289
+ ;
290
+
291
+ # The list of arguments to a function invocation.
292
+ Arguments:
293
+ CALL_START ArgList CALL_END { result = val[1] }
294
+ ;
295
+
296
+ # Calling super.
297
+ Super:
298
+ SUPER CALL_START ArgList CALL_END { result = CallNode.new(Value.new('super'), val[2]) }
299
+ ;
300
+
301
+ # This references, either naked or to a property.
302
+ This:
303
+ '@' { result = ThisNode.new }
304
+ | '@' IDENTIFIER { result = ThisNode.new(val[1]) }
305
+ ;
306
+
307
+ # The range literal.
308
+ Range:
309
+ "[" Expression
310
+ "." "." Expression "]" { result = RangeNode.new(val[1], val[4]) }
311
+ | "[" Expression
312
+ "." "." "." Expression "]" { result = RangeNode.new(val[1], val[5], true) }
313
+ ;
314
+
315
+ # The slice literal.
316
+ Slice:
317
+ INDEX_START Expression "." "."
318
+ Expression INDEX_END { result = RangeNode.new(val[1], val[4]) }
319
+ | INDEX_START Expression "." "." "."
320
+ Expression INDEX_END { result = RangeNode.new(val[1], val[5], true) }
321
+ ;
322
+
323
+ # The array literal.
324
+ Array:
325
+ "[" ArgList "]" { result = ArrayNode.new(val[1]) }
326
+ ;
327
+
328
+ # A list of arguments to a method call, or as the contents of an array.
329
+ ArgList:
330
+ /* nothing */ { result = [] }
331
+ | Expression { result = val }
332
+ | INDENT Expression { result = [val[1]] }
333
+ | ArgList "," Expression { result = val[0] << val[2] }
334
+ | ArgList Terminator Expression { result = val[0] << val[2] }
335
+ | ArgList "," Terminator Expression { result = val[0] << val[3] }
336
+ | ArgList "," INDENT Expression { result = val[0] << val[3] }
337
+ | ArgList OUTDENT { result = val[0] }
338
+ ;
339
+
340
+ # Just simple, comma-separated, required arguments (no fancy syntax).
341
+ SimpleArgs:
342
+ Expression { result = val[0] }
343
+ | SimpleArgs "," Expression { result = ([val[0]] << val[2]).flatten }
344
+ ;
345
+
346
+ # Try/catch/finally exception handling blocks.
347
+ Try:
348
+ TRY Block Catch { result = TryNode.new(val[1], val[2][0], val[2][1]) }
349
+ | TRY Block FINALLY Block { result = TryNode.new(val[1], nil, nil, val[3]) }
350
+ | TRY Block Catch
351
+ FINALLY Block { result = TryNode.new(val[1], val[2][0], val[2][1], val[4]) }
352
+ ;
353
+
354
+ # A catch clause.
355
+ Catch:
356
+ CATCH IDENTIFIER Block { result = [val[1], val[2]] }
357
+ ;
358
+
359
+ # Throw an exception.
360
+ Throw:
361
+ THROW Expression { result = ThrowNode.new(val[1]) }
362
+ ;
363
+
364
+ # Parenthetical expressions.
365
+ Parenthetical:
366
+ "(" Expression ")" { result = ParentheticalNode.new(val[1], val[0].line) }
367
+ ;
368
+
369
+ # The while loop. (there is no do..while).
370
+ While:
371
+ WHILE Expression Block { result = WhileNode.new(val[1], val[2]) }
372
+ | WHILE Expression { result = WhileNode.new(val[1], nil) }
373
+ | Expression WHILE Expression { result = WhileNode.new(val[2], Expressions.wrap(val[0])) }
374
+ ;
375
+
376
+ # Array comprehensions, including guard and current index.
377
+ # Looks a little confusing, check nodes.rb for the arguments to ForNode.
378
+ For:
379
+ Expression FOR
380
+ ForVariables ForSource { result = ForNode.new(val[0], val[3], val[2][0], val[2][1]) }
381
+ | FOR ForVariables ForSource Block { result = ForNode.new(val[3], val[2], val[1][0], val[1][1]) }
382
+ ;
383
+
384
+ # An array comprehension has variables for the current element and index.
385
+ ForVariables:
386
+ IDENTIFIER { result = val }
387
+ | IDENTIFIER "," IDENTIFIER { result = [val[0], val[2]] }
388
+ ;
389
+
390
+ # The source of the array comprehension can optionally be filtered.
391
+ ForSource:
392
+ IN Expression { result = {:source => val[1]} }
393
+ | OF Expression { result = {:source => val[1], :object => true} }
394
+ | ForSource
395
+ WHEN Expression { result = val[0].merge(:filter => val[2]) }
396
+ | ForSource
397
+ BY Expression { result = val[0].merge(:step => val[2]) }
398
+ ;
399
+
400
+ # Switch/When blocks.
401
+ Switch:
402
+ SWITCH Expression INDENT
403
+ Whens OUTDENT { result = val[3].rewrite_condition(val[1]) }
404
+ | SWITCH Expression INDENT
405
+ Whens ELSE Block OUTDENT { result = val[3].rewrite_condition(val[1]).add_else(val[5]) }
406
+ ;
407
+
408
+ # The inner list of whens.
409
+ Whens:
410
+ When { result = val[0] }
411
+ | Whens When { result = val[0] << val[1] }
412
+ ;
413
+
414
+ # An individual when.
415
+ When:
416
+ LEADING_WHEN SimpleArgs Block { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
417
+ | LEADING_WHEN SimpleArgs Block
418
+ Terminator { result = IfNode.new(val[1], val[2], nil, {:statement => true}) }
419
+ | Comment Terminator When { result = val[2].add_comment(val[0]) }
420
+ ;
421
+
422
+ # The most basic form of "if".
423
+ IfBlock:
424
+ IF Expression Block { result = IfNode.new(val[1], val[2]) }
425
+ ;
426
+
427
+ # An elsif portion of an if-else block.
428
+ ElsIf:
429
+ ELSE IfBlock { result = val[1].force_statement }
430
+ ;
431
+
432
+ # Multiple elsifs can be chained together.
433
+ ElsIfs:
434
+ ElsIf { result = val[0] }
435
+ | ElsIfs ElsIf { result = val[0].add_else(val[1]) }
436
+ ;
437
+
438
+ # Terminating else bodies are strictly optional.
439
+ ElseBody
440
+ /* nothing */ { result = nil }
441
+ | ELSE Block { result = val[1] }
442
+ ;
443
+
444
+ # All the alternatives for ending an if-else block.
445
+ IfEnd:
446
+ ElseBody { result = val[0] }
447
+ | ElsIfs ElseBody { result = val[0].add_else(val[1]) }
448
+ ;
449
+
450
+ # The full complement of if blocks, including postfix one-liner ifs and unlesses.
451
+ If:
452
+ IfBlock IfEnd { result = val[0].add_else(val[1]) }
453
+ | Expression IF Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true}) }
454
+ | Expression UNLESS Expression { result = IfNode.new(val[2], Expressions.wrap(val[0]), nil, {:statement => true, :invert => true}) }
455
+ ;
456
+
457
+ end
458
+
459
+ ---- header
460
+ module NScript
461
+
462
+ ---- inner
463
+ # Lex and parse a NScript.
464
+ def parse(code)
465
+ # Uncomment the following line to enable grammar debugging, in combination
466
+ # with the -g flag in the Rake build task.
467
+ # @yydebug = true
468
+ @tokens = Lexer.new.tokenize(code)
469
+ do_parse
470
+ end
471
+
472
+ # Retrieve the next token from the list.
473
+ def next_token
474
+ @tokens.shift
475
+ end
476
+
477
+ # Raise a custom error class that knows about line numbers.
478
+ def on_error(error_token_id, error_value, value_stack)
479
+ raise ParseError.new(token_to_str(error_token_id), error_value, value_stack)
480
+ end
481
+
482
+ ---- footer
483
+ end