nscript 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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