myco 0.1.0.dev

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +2 -0
  3. data/bin/myco +7 -0
  4. data/lib/myco/backtrace.rb +56 -0
  5. data/lib/myco/bootstrap/component.rb +142 -0
  6. data/lib/myco/bootstrap/empty_object.rb +4 -0
  7. data/lib/myco/bootstrap/file_toplevel.rb +5 -0
  8. data/lib/myco/bootstrap/find_constant.rb +86 -0
  9. data/lib/myco/bootstrap/instance.rb +52 -0
  10. data/lib/myco/bootstrap/meme.rb +160 -0
  11. data/lib/myco/bootstrap/void.rb +40 -0
  12. data/lib/myco/bootstrap.my +15 -0
  13. data/lib/myco/bootstrap.rb +10 -0
  14. data/lib/myco/command.my +33 -0
  15. data/lib/myco/core/BasicObject.my +46 -0
  16. data/lib/myco/core/Category.my +5 -0
  17. data/lib/myco/core/Decorator.my +18 -0
  18. data/lib/myco/core/FileToplevel.my +23 -0
  19. data/lib/myco/core/Object.my +24 -0
  20. data/lib/myco/core/Switch.my +31 -0
  21. data/lib/myco/eval.rb +63 -0
  22. data/lib/myco/parser/ast/constant_access.rb +29 -0
  23. data/lib/myco/parser/ast/constant_define.rb +40 -0
  24. data/lib/myco/parser/ast/constant_reopen.rb +47 -0
  25. data/lib/myco/parser/ast/declare_category.rb +51 -0
  26. data/lib/myco/parser/ast/declare_decorator.rb +35 -0
  27. data/lib/myco/parser/ast/declare_file.rb +54 -0
  28. data/lib/myco/parser/ast/declare_meme.rb +44 -0
  29. data/lib/myco/parser/ast/declare_object.rb +75 -0
  30. data/lib/myco/parser/ast/declare_string.rb +37 -0
  31. data/lib/myco/parser/ast/invoke.rb +66 -0
  32. data/lib/myco/parser/ast/local_variable_access_ambiguous.rb +38 -0
  33. data/lib/myco/parser/ast/misc.rb +61 -0
  34. data/lib/myco/parser/ast/myco_module_scope.rb +58 -0
  35. data/lib/myco/parser/ast/quest.rb +82 -0
  36. data/lib/myco/parser/ast.rb +15 -0
  37. data/lib/myco/parser/builder.output +3995 -0
  38. data/lib/myco/parser/builder.racc +585 -0
  39. data/lib/myco/parser/builder.rb +1592 -0
  40. data/lib/myco/parser/lexer.rb +2306 -0
  41. data/lib/myco/parser/lexer.rl +393 -0
  42. data/lib/myco/parser/lexer_char_classes.rl +56 -0
  43. data/lib/myco/parser/lexer_common.rb +95 -0
  44. data/lib/myco/parser/lexer_skeleton.rl +154 -0
  45. data/lib/myco/parser/peg_parser.kpeg +759 -0
  46. data/lib/myco/parser/peg_parser.rb +7094 -0
  47. data/lib/myco/parser.rb +40 -0
  48. data/lib/myco/tools/OptionParser.my +38 -0
  49. data/lib/myco/tools/mycompile.my +51 -0
  50. data/lib/myco/toolset.rb +16 -0
  51. data/lib/myco/version.rb +22 -0
  52. data/lib/myco.rb +15 -0
  53. metadata +247 -0
@@ -0,0 +1,759 @@
1
+ %% name = CodeTools::PegParser
2
+ #%
3
+
4
+ %% { #%
5
+ attr_accessor :processor
6
+ attr_accessor :root_node
7
+
8
+ # Generate an AST::Node of the given type (generated by the @processor)
9
+ def node type, locator, *args
10
+ @processor.send :"process_#{type}", locator.line, *args
11
+ end
12
+
13
+ # Generate a Token with the given type, text, and row_col
14
+ def token type, text, row_col=nil
15
+ row_col ||= [current_line, current_column-text.length]
16
+ Token.new type, text, row_col
17
+ end
18
+
19
+ # A token is a lightweight unit of text with type and location info
20
+ # as well as some convenience function for common conversion operations.
21
+ class Token
22
+ attr_accessor :type
23
+ attr_accessor :text
24
+ attr_accessor :line
25
+
26
+ def inspect
27
+ [@type, @text, @line].inspect
28
+ end
29
+ alias_method :to_s, :inspect
30
+
31
+ def initialize type, text, row_col
32
+ @type = type
33
+ @text = text
34
+ # TODO: integrate columns from location instead of just rows
35
+ @line = row_col.first
36
+ end
37
+
38
+ def sym; @text.to_sym end
39
+ def float; Float(@text) end
40
+ def integer; Integer(@text) end
41
+ end
42
+ }
43
+
44
+ ##
45
+ # Things to remember about PEG:
46
+ #
47
+ # Given the choice (a | b | c),
48
+ # PEG will consume the first matching choice, not the longest.
49
+ # There is no ambiguity in PEG because all rule choices are order-dependent.
50
+ # This means that you should avoid changing the order of choices
51
+ # in a rule without being mindful of the consequences.
52
+ #
53
+ # Given a left-recursive set of rules such as
54
+ # x = a | b | c
55
+ # a = x "+" x
56
+ # PEG will silently ignore the 'a' rule when trying to contruct 'x' and
57
+ # not even try to pursue it because it would result in infinite recursion.
58
+ # To avoid left-recursion while retaining left-associativity,
59
+ # use an iteration rule instead of a recursion rule.
60
+ # (see the chained_left_atoms rule)
61
+
62
+
63
+ ##
64
+ # Toplevel Terminal Categorizations
65
+
66
+ root = declobj_expr_body:n0 { @root_node = node(:declfile, n0, n0) }
67
+
68
+ # Declarations
69
+ decl =
70
+ declobj
71
+ | declstr
72
+ | copen
73
+
74
+ # Expressions allowable inside object declarations
75
+ declobj_expr =
76
+ category
77
+ | declobj_expr_not_category
78
+
79
+ # Expressions allowable inside object declarations that is not a category
80
+ declobj_expr_not_category =
81
+ decl
82
+ | cdefn
83
+ | cmeme
84
+ | constant
85
+ | meme
86
+
87
+ # Expressions allowable inside memes
88
+ meme_expr =
89
+ return_arg_expr
90
+ | arg_expr
91
+
92
+ # Expressions allowable as function arguments
93
+ arg_expr =
94
+ assignment
95
+ | left_chained_atoms
96
+ | dyn_string
97
+ | dyn_symstr
98
+ | expr_atom
99
+
100
+ # TODO: make expr_atom not redundant with below rules
101
+ # Expression atoms
102
+ expr_atom =
103
+ decl
104
+ | left_chained_invocations
105
+ | lit_string
106
+ | lit_symstr
107
+ | unary_operation
108
+ | paren_expr
109
+ | constant
110
+ | lit_simple
111
+ | lit_array
112
+ | invoke
113
+
114
+ # Expression atoms that are not invocation chains
115
+ expr_atom_not_chained =
116
+ decl
117
+ | lit_string
118
+ | lit_symstr
119
+ | unary_operation
120
+ | paren_expr
121
+ | constant
122
+ | lit_simple
123
+ | lit_array
124
+ | invoke
125
+
126
+ # Expression atoms that are not strings
127
+ expr_atom_not_string =
128
+ decl
129
+ | left_chained_invocations
130
+ | unary_operation
131
+ | paren_expr
132
+ | constant
133
+ | lit_simple
134
+ | lit_array
135
+ | invoke
136
+
137
+
138
+ ##
139
+ # Character Classes
140
+
141
+ eol_comment = '#' (!c_eol .)*
142
+
143
+ c_nl = "\n"
144
+ c_spc = /[ \t\r\f\v]/ | "\\\n" | eol_comment
145
+ c_spc_nl = c_spc | c_nl
146
+
147
+ c_eof = !.
148
+ c_eol = c_nl | c_eof
149
+ c_any = .
150
+
151
+ c_upper = /[[:upper:]]/
152
+ c_lower = /[[:lower:]]/ | "_"
153
+ c_num = /[0-9]/
154
+ c_alpha = c_lower | c_upper
155
+ c_alnum = c_alpha | c_num
156
+ c_suffix = '!' | '?'
157
+
158
+
159
+ ##
160
+ # Tokens
161
+
162
+ t_CONST_SEP = < ',' > ~token(:t_CONST_SEP, text)
163
+ t_EXPR_SEP = < ';' | c_nl > ~token(:t_EXPR_SEP, text)
164
+ t_ARG_SEP = < ',' | c_nl > ~token(:t_ARG_SEP, text)
165
+ t_DECLARE_BEGIN = < '{' > ~token(:t_DECLARE_BEGIN, text)
166
+ t_DECLARE_END = < '}' | c_eof > ~token(:t_DECLARE_END, text)
167
+ t_MEME_MARK = < ':' > ~token(:t_MEME_MARK, text)
168
+ t_MEME_BEGIN = < '{' > ~token(:t_MEME_BEGIN, text)
169
+ t_MEME_END = < '}' > ~token(:t_MEME_END, text)
170
+ t_PAREN_BEGIN = < '(' > ~token(:t_PAREN_BEGIN, text)
171
+ t_PAREN_END = < ')' > ~token(:t_PAREN_END, text)
172
+ t_DEFINE = < '<' > ~token(:t_DEFINE, text)
173
+ t_REOPEN = < '<<' > ~token(:t_REOPEN, text)
174
+ t_PARAMS_BEGIN = < '|' > ~token(:t_PARAMS_BEGIN, text)
175
+ t_PARAMS_END = < '|' > ~token(:t_PARAMS_END, text)
176
+ t_ARGS_BEGIN = < '(' > ~token(:t_ARGS_BEGIN, text)
177
+ t_ARGS_END = < ')' > ~token(:t_ARGS_END, text)
178
+ t_ARRAY_BEGIN = < '[' > ~token(:t_ARRAY_BEGIN, text)
179
+ t_ARRAY_END = < ']' > ~token(:t_ARRAY_END, text)
180
+
181
+ t_CONSTANT = < (c_upper c_alnum*) > ~token(:t_CONSTANT, text)
182
+ t_IDENTIFIER = < (c_lower c_alnum*) c_suffix? >
183
+ ~token(:t_IDENTIFIER, text)
184
+ t_SYMBOL = ':' < (c_lower c_alnum*) > ~token(:t_SYMBOL, text)
185
+
186
+ t_NULL = < 'null' > ~token(:t_NULL, text)
187
+ t_VOID = < 'void' > ~token(:t_VOID, text)
188
+ t_TRUE = < 'true' > ~token(:t_TRUE, text)
189
+ t_FALSE = < 'false' > ~token(:t_FALSE, text)
190
+ t_SELF = < 'self' > ~token(:t_SELF, text)
191
+ t_FLOAT = < c_num+ '.' c_num+ > ~token(:t_FLOAT, text)
192
+ t_INTEGER = < c_num+ > ~token(:t_INTEGER, text)
193
+
194
+ t_JUMP = < '->' > ~token(:t_JUMP, text)
195
+ t_DOT = < '.' > ~token(:t_DOT, text)
196
+ t_QUEST = < '.' c_spc_nl* '?' > ~token(:t_QUEST, text)
197
+ t_SCOPE = < '::' > ~token(:t_SCOPE, text)
198
+ t_ASSIGN = < '=' > ~token(:t_ASSIGN, text)
199
+ t_OP_TOPROC = < '&' > ~token(:t_OP_TOPROC, text)
200
+ t_OP_NOT = < '!' > ~token(:t_OP_NOT, text)
201
+ t_OP_PLUS = < '+' > ~token(:t_OP_PLUS, text)
202
+ t_OP_MINUS = < '-' > ~token(:t_OP_MINUS, text)
203
+ t_OP_MULT = < '*' > ~token(:t_OP_MULT, text)
204
+ t_OP_DIV = < '/' > ~token(:t_OP_DIV, text)
205
+ t_OP_MOD = < '%' > ~token(:t_OP_MOD, text)
206
+ t_OP_EXP = < '**' > ~token(:t_OP_EXP, text)
207
+ t_OP_AND = < '&&' > ~token(:t_OP_AND, text)
208
+ t_OP_OR = < '||' > ~token(:t_OP_OR, text)
209
+ t_OP_COMPARE = < ('<=>'|'=~'|'=='|'<='|'>='|'<'|'>') >
210
+ ~token(:t_OP_COMPARE, text)
211
+
212
+ string_norm = /[^\\\"]/
213
+ t_STRING_BODY = < string_norm* ("\\" c_any string_norm*)* >
214
+ ~token(:t_STRING_BODY, text)
215
+ t_STRING_BEGIN = < '"' > ~token(:t_STRING_BEGIN, text)
216
+ t_STRING_END = < '"' > ~token(:t_STRING_END, text)
217
+ t_SYMSTR_BEGIN = < ':"' > ~token(:t_SYMSTR_BEGIN, text)
218
+
219
+ sstring_norm = /[^\\\']/
220
+ t_SSTRING_BODY = < sstring_norm* ("\\" c_any sstring_norm*)* >
221
+ ~token(:t_SSTRING_BODY, text)
222
+ t_SSTRING_BEGIN = < "'" > ~token(:t_SSTRING_BEGIN, text)
223
+ t_SSTRING_END = < "'" > ~token(:t_SSTRING_END, text)
224
+
225
+ catgry_norm = /[^\\\[\]]/
226
+ t_CATGRY_BODY = < catgry_norm* ("\\" c_any catgry_norm*)* >
227
+ ~token(:t_CATGRY_BODY, text)
228
+ t_CATGRY_BEGIN = < '[' > ~token(:t_CATGRY_BEGIN, text)
229
+ t_CATGRY_END = < ']' > ~token(:t_CATGRY_END, text)
230
+
231
+
232
+ ##
233
+ # Simple literals
234
+
235
+ lit_simple =
236
+ t_NULL:t0 ~node(:null, t0)
237
+ | t_VOID:t0 ~node(:void, t0)
238
+ | t_TRUE:t0 ~node(:true, t0)
239
+ | t_FALSE:t0 ~node(:false, t0)
240
+ | t_SELF:t0 ~node(:self, t0)
241
+ | t_FLOAT:t0 ~node(:lit, t0, t0.float)
242
+ | t_INTEGER:t0 ~node(:lit, t0, t0.integer)
243
+ | t_SYMBOL:t0 ~node(:lit, t0, t0.sym)
244
+
245
+ ##
246
+ # Enclosed literals
247
+
248
+ lit_string =
249
+ t_STRING_BEGIN t_STRING_BODY:tb t_STRING_END
250
+ ~node(:lit, tb, encode_escapes(tb.text))
251
+ | t_SSTRING_BEGIN t_SSTRING_BODY:tb t_SSTRING_END
252
+ ~node(:lit, tb, encode_escapes(tb.text))
253
+
254
+ lit_string_as_symbol = # Used in contexts where a bare string is a symbol
255
+ t_STRING_BEGIN t_STRING_BODY:tb t_STRING_END
256
+ ~node(:lit, tb, encode_escapes(tb.text).to_sym)
257
+ | t_SSTRING_BEGIN t_SSTRING_BODY:tb t_SSTRING_END
258
+ ~node(:lit, tb, encode_escapes(tb.text).to_sym)
259
+
260
+ lit_symstr =
261
+ t_SYMSTR_BEGIN t_STRING_BODY:tb t_STRING_END
262
+ ~node(:lit, tb, encode_escapes(tb.text).to_sym)
263
+
264
+ category_name = # Used only in declobj body
265
+ t_CATGRY_BEGIN t_CATGRY_BODY:tb t_CATGRY_END
266
+ ~node(:lit, tb, encode_escapes(tb.text).to_sym)
267
+
268
+ ##
269
+ # String interpolations / juxtapositions
270
+
271
+ expr_atom_evstr =
272
+ expr_atom_not_string:n0
273
+ ~node(:evstr, n0, n0)
274
+
275
+ dyn_string_parts =
276
+ (c_spc* expr_atom_evstr:n0 c_spc* lit_string:n1 {[n0,n1]})+:nlist
277
+ { nlist.flatten }
278
+
279
+ dyn_string =
280
+ lit_string:n0 dyn_string_parts:nrest
281
+ ~node(:dstr, n0, n0.value, nrest)
282
+
283
+ dyn_symstr =
284
+ lit_symstr:n0 dyn_string_parts:nrest
285
+ ~node(:dsym, n0, n0.value.to_s, nrest)
286
+
287
+ ##
288
+ # Constants
289
+
290
+ constant =
291
+ constant:n0 t_SCOPE:ts t_CONSTANT:tc ~node(:colon2, ts, n0, tc.sym)
292
+ | t_SCOPE:ts t_CONSTANT:tc ~node(:colon3, ts, tc.sym)
293
+ | t_CONSTANT:tc ~node(:const, tc, tc.sym)
294
+
295
+ const_sep = (c_spc_nl* t_CONST_SEP c_spc_nl*)+
296
+
297
+ constant_list =
298
+ constant:n0 (const_sep constant:n)*:nrest
299
+ ~node(:array, n0, [n0, *nrest])
300
+
301
+ ##
302
+ # Bare identifiers
303
+
304
+ # Used in contexts where a bare identifier is a symbol
305
+ id_as_symbol = t_IDENTIFIER:t0 ~node(:lit, t0, t0.sym)
306
+
307
+ ##
308
+ # Object declarations
309
+
310
+ declobj_sepd_exprs =
311
+ declobj_expr:n0 (arg_sep declobj_expr:n)*:nrest arg_sep_opt { [n0, *nrest] }
312
+
313
+ declobj_expr_body =
314
+ arg_sep_opt declobj_sepd_exprs:nlist t_DECLARE_END:te
315
+ ~node(:block, nlist.first, nlist)
316
+ | arg_sep_opt t_DECLARE_END:te
317
+ ~node(:null, te)
318
+
319
+ declobj =
320
+ constant_list:n0 c_spc_nl* t_DECLARE_BEGIN:t declobj_expr_body:n1
321
+ ~node(:declobj, t, n0, n1)
322
+
323
+ category_expr =
324
+ declobj_expr_not_category
325
+
326
+ category_sepd_exprs =
327
+ category_expr:n0 (arg_sep category_expr:n)*:nrest { [n0, *nrest] }
328
+
329
+ category =
330
+ category_name:n0 arg_sep category_sepd_exprs?:nlist &(arg_sep_opt (t_CATGRY_BEGIN | t_DECLARE_END))
331
+ ~node(:category, n0, n0,
332
+ (nlist ? node(:block, nlist.first, nlist) : node(:null, n0)))
333
+
334
+ copen =
335
+ constant:n0 c_spc_nl* t_REOPEN:tb c_spc_nl* t_DECLARE_BEGIN declobj_expr_body:n1
336
+ ~node(:copen, tb, n0, n1)
337
+
338
+ cdefn =
339
+ constant:n0 c_spc_nl* t_DEFINE:t c_spc_nl* declobj:n1
340
+ ~node(:cdefn, t, n0, n1)
341
+
342
+ ##
343
+ # String object declarations
344
+
345
+ t_DECLSTR_BEGIN =
346
+ < /[^\s{:,<][^\s]+/ >
347
+ {
348
+
349
+ # Table of replacement characters to use when calculating
350
+ # the ending delimiter from the starting delimiter.
351
+ # Directional characters are replaced with their opposite.
352
+ @declstr_replace_tbl ||= %w{
353
+ < > ( ) { } [ ]
354
+ }
355
+
356
+ # Calculate the ending delimiter to look for and store it
357
+ @declstr_destrlim = text \
358
+ .split(/(?<=[^a-zA-Z])|(?=[^a-zA-Z])/)
359
+ .map { |str|
360
+ idx = @declstr_replace_tbl.find_index(str)
361
+ idx.nil? ? str :
362
+ (idx.odd? ? @declstr_replace_tbl[idx-1] : @declstr_replace_tbl[idx+1])
363
+ }
364
+ .reverse
365
+ .join ''
366
+
367
+ token(:t_DECLSTR_BEGIN, text)
368
+ }
369
+
370
+ t_DECLSTR_END =
371
+ c_spc_nl* < (< /\S+/ > &{text == @declstr_destrlim}) >
372
+ ~token(:t_DECLSTR_END, text)
373
+
374
+ s_DECLSTR_BODYLINE =
375
+ < /[^\n]*\n/ >
376
+ &{ text =~ /^(\s*)(\S+)/; $2!=@declstr_destrlim }
377
+ { text }
378
+
379
+ s_DECLSTR_BODY =
380
+ s_DECLSTR_BODYLINE*:slist
381
+ { slist[1..-1].join('') }
382
+
383
+ declstr_body =
384
+ t_DECLSTR_BEGIN:tb s_DECLSTR_BODY:st c_spc_nl* t_DECLSTR_END
385
+ ~node(:str, tb, st)
386
+
387
+ declstr =
388
+ constant_list:nc c_spc+ declstr_body:nb
389
+ ~node(:declstr, nc, nc, nb)
390
+
391
+ ##
392
+ # Jumps
393
+
394
+ return_arg_expr =
395
+ arg_expr:n0 c_spc* t_JUMP:to
396
+ ~node(:return, to, n0)
397
+
398
+ ##
399
+ # Assignment
400
+
401
+ assignment =
402
+ local_assignment
403
+ | invoke_assignment
404
+
405
+ assign_rhs =
406
+ arg_expr
407
+
408
+ local_assignment =
409
+ t_IDENTIFIER:ti c_spc_nl* t_ASSIGN:to c_spc_nl* assign_rhs:rhs
410
+ ~node(:lasgn, to, ti.sym, rhs)
411
+
412
+ invoke_assignment_lhs =
413
+ left_chained_invocations
414
+ | invoke
415
+
416
+ invoke_assignment =
417
+ invoke_assignment_lhs:lhs c_spc_nl* t_ASSIGN:to c_spc_nl* assign_rhs:rhs
418
+ {
419
+ lhs.name = :"#{lhs.name}="
420
+ orig_arguments = lhs.arguments && lhs.arguments.body || []
421
+ lhs.arguments = node(:array, rhs, [rhs, *orig_arguments])
422
+ lhs
423
+ }
424
+
425
+
426
+ ##
427
+ # Invoke - Results in a :lambig, :call, or :iter with a :call within
428
+
429
+ invoke_body =
430
+ (c_spc_nl* param_list:n)?:np c_spc_nl* meme_enclosed_expr_body:nb
431
+ { [np, nb] }
432
+
433
+ invoke =
434
+ t_IDENTIFIER:tn (c_spc_nl* arg_list:na)?:na (c_spc_nl* invoke_body:n)?:nlist
435
+ ~node(:invoke, tn, nil, tn.sym, na, *nlist)
436
+
437
+ op_invoke = # Allow some binary operators to be invoked with a dot
438
+ op_invoke_id:tn (c_spc_nl* arg_list:na)?:na (c_spc_nl* invoke_body:n)?:nlist
439
+ ~node(:invoke, tn, nil, tn.sym, na, *nlist)
440
+
441
+ op_invoke_id =
442
+ left_op
443
+
444
+ ##
445
+ # Argument lists
446
+
447
+ arg_sep = (c_spc* t_ARG_SEP c_spc*)+
448
+ arg_sep_opt = (c_spc | t_ARG_SEP)*
449
+
450
+ in_arg_normal =
451
+ in_arg_splat
452
+ | arg_expr:n0 !in_arg_kwarg_mark { n0 }
453
+
454
+ in_arg_normals =
455
+ in_arg_normal:n0 (arg_sep in_arg_normal:n)*:nrest
456
+ { [n0,*nrest] }
457
+
458
+ in_arg_kwargs =
459
+ in_arg_kwarg:n0 (arg_sep in_arg_kwarg:n)*:nrest
460
+ ~node(:hash, n0.first, [n0,*nrest].flatten)
461
+
462
+ in_arg_kwarg_mark = c_spc_nl* t_MEME_MARK:to
463
+ in_arg_kwarg =
464
+ id_as_symbol:n0 in_arg_kwarg_mark c_spc_nl* arg_expr:n1
465
+ { [n0, n1] }
466
+
467
+ in_arg_splat =
468
+ t_OP_MULT:to expr_atom:n0
469
+ ~node(:splat, to, n0)
470
+
471
+ in_arg_block =
472
+ t_OP_TOPROC:to expr_atom:n0
473
+ ~node(:block_pass, to, nil, n0)
474
+
475
+ in_arg_list =
476
+ in_arg_normals:n0 arg_sep in_arg_kwargs:n1 arg_sep in_arg_block:n2 { [*n0,n1,n2] }
477
+ | in_arg_normals:n0 arg_sep in_arg_kwargs:n1 { [*n0,n1] }
478
+ | in_arg_normals:n0 arg_sep in_arg_block:n1 { [*n0,n1] }
479
+ | in_arg_kwargs:n0 arg_sep in_arg_block:n1 { [n0, n1] }
480
+ | in_arg_normals:n0 { [*n0] }
481
+ | in_arg_kwargs:n0 { [n0] }
482
+ | in_arg_block:n0 { [n0] }
483
+
484
+ arg_list =
485
+ t_ARGS_BEGIN:tb arg_sep_opt t_ARGS_END
486
+ ~args_assemble(tb, node(:array, tb, []))
487
+ | t_ARGS_BEGIN:tb arg_sep_opt in_arg_list:nlist arg_sep_opt t_ARGS_END
488
+ ~args_assemble(tb, node(:array, tb, nlist))
489
+
490
+ lit_array =
491
+ t_ARRAY_BEGIN:tb arg_sep_opt t_ARRAY_END
492
+ ~args_assemble(tb, node(:array, tb, []))
493
+ | t_ARRAY_BEGIN:tb arg_sep_opt in_arg_list:nlist arg_sep_opt t_ARRAY_END
494
+ ~args_assemble(tb, node(:array, tb, nlist))
495
+
496
+ ##
497
+ # Parameter lists
498
+
499
+ param =
500
+ t_IDENTIFIER:ti c_spc_nl* t_ASSIGN:to c_spc_nl* arg_expr:nv
501
+ { [:optional, node(:lasgn, ti, ti.sym, nv)] }
502
+ | t_IDENTIFIER:ti c_spc_nl* t_MEME_MARK:to c_spc_nl* arg_expr?:nv
503
+ { [:kwargs, node(:lasgn, ti, ti.sym, (nv || node(:lit, to, :*)))] }
504
+ | t_OP_EXP c_spc_nl* t_IDENTIFIER:ti { [:kwrest, ti.sym] }
505
+ | t_OP_MULT c_spc_nl* t_IDENTIFIER:ti { [:rest, ti.sym] }
506
+ | t_OP_TOPROC c_spc_nl* t_IDENTIFIER:ti { [:block, ti.sym] }
507
+ | t_IDENTIFIER:ti { [:required, ti.sym] }
508
+
509
+ param_sepd =
510
+ arg_sep param:n0 { n0 }
511
+
512
+ param_sepds =
513
+ param:n0 (arg_sep param:n)*:nrest arg_sep_opt { [n0, *nrest] }
514
+
515
+ param_list =
516
+ t_PARAMS_BEGIN:tb t_PARAMS_END
517
+ { node(:args, tb, nil, nil, nil, nil, nil, nil, nil) }
518
+ | t_PARAMS_BEGIN:tb param_sepds:plist t_PARAMS_END
519
+ {
520
+ required, optional, rest, kwargs, kwrest, block = 6.times.map { [] }
521
+
522
+ required << plist.shift[1] while plist[0] && plist[0][0] == :required
523
+ optional << plist.shift[1] while plist[0] && plist[0][0] == :optional
524
+ rest << plist.shift[1] while plist[0] && plist[0][0] == :rest
525
+ kwargs << plist.shift[1] while plist[0] && plist[0][0] == :kwargs
526
+ kwrest << plist.shift[1] while plist[0] && plist[0][0] == :kwrest
527
+ block << plist.shift[1] while plist[0] && plist[0][0] == :block
528
+
529
+ required = required
530
+ optional = optional.empty? ? nil : node(:block, tb, optional)
531
+ rest = rest.first
532
+ post = nil
533
+ kwargs = kwargs.empty? ? nil : node(:block, tb, kwargs)
534
+ kwrest = kwrest.first
535
+ block = block.first
536
+
537
+ node(:args, tb, required, optional, rest, post, kwargs, kwrest, block)
538
+ }
539
+
540
+ ##
541
+ # Two-term operators
542
+
543
+ left_op =
544
+ t_OP_EXP
545
+ | t_OP_MULT | t_OP_DIV | t_OP_MOD
546
+ | t_OP_PLUS | t_OP_MINUS
547
+ | t_OP_COMPARE
548
+ | t_OP_AND | t_OP_OR
549
+
550
+ # Achieve left-associativity through iteration.
551
+ #
552
+ # PEG parsers get tripped up by left recursion
553
+ # (in contrast to LALR parsers, which prefer left recursion).
554
+ # This is a well-understood limitation, but refer to:
555
+ # http://www.dalnefre.com/wp/2011/05/parsing-expression-grammars-part-4/
556
+ # for an easy-to-understand explanation of this problem and this solution.
557
+ #
558
+ left_chained_atoms =
559
+ expr_atom:n0 (c_spc_nl* left_op:to c_spc_nl* expr_atom:n1 { [to, n1] })+:list
560
+ {
561
+ list.unshift n0
562
+ list.flatten!
563
+
564
+ collapse(list, :t_OP_EXP)
565
+ collapse(list, :t_OP_MULT, :t_OP_DIV, :t_OP_MOD)
566
+ collapse(list, :t_OP_PLUS, :t_OP_MINUS)
567
+ collapse(list, :t_OP_COMPARE)
568
+ collapse(list, :t_OP_AND, :t_OP_OR) do |n0,op,n1|
569
+ type = { :t_OP_AND=>:and, :t_OP_OR=>:or }[op.type]
570
+ node(type, op, n0, n1)
571
+ end
572
+
573
+ # There should only be one resulting node left
574
+ raise "Failed to fully collapse left_chained_atoms: #{list}" \
575
+ unless list.count == 1
576
+
577
+ list.first
578
+ }
579
+
580
+ ##
581
+ # Invocations and Quests (soft-failing invocations)
582
+
583
+ left_invoke_op =
584
+ t_QUEST
585
+ | t_DOT
586
+
587
+ # Achieve left-associativity through iteration.
588
+ # (see left_chained_atoms).
589
+ #
590
+ left_chained_invocations =
591
+ expr_atom_not_chained:n0 (c_spc_nl* left_invoke_op:to c_spc_nl* (invoke | op_invoke):n1 { [to, n1] })+:list
592
+ {
593
+ list.unshift n0
594
+ list.flatten!
595
+
596
+ collapse(list, :t_DOT, :t_QUEST) do |n0,op,n1|
597
+ op.type==:t_DOT ? (n1.receiver=n0; n1) : node(:quest, op, n0, n1)
598
+ end
599
+
600
+ # There should only be one resulting node left
601
+ raise "Failed to fully collapse left_chained_invocations: #{list}" \
602
+ unless list.count == 1
603
+
604
+ list.first
605
+ }
606
+
607
+ ##
608
+ # Unary operators
609
+
610
+ unary_operation =
611
+ t_OP_NOT:to expr_atom:n0
612
+ ~node(:call, to, n0, :"!", nil)
613
+
614
+ ##
615
+ # Memes and etc..
616
+
617
+ t_inln_sep = !t_ARG_SEP t_EXPR_SEP
618
+
619
+ inln_sep = (c_spc* t_inln_sep c_spc*)+
620
+ inln_sep_opt = (c_spc | t_inln_sep)*
621
+
622
+ expr_sep = (c_spc* t_EXPR_SEP c_spc*)+
623
+ expr_sep_opt = (c_spc | t_EXPR_SEP)*
624
+
625
+ meme_inline_sepd_exprs =
626
+ meme_expr:n0 (inln_sep meme_expr:n)*:nrest inln_sep_opt { [n0, *nrest] }
627
+
628
+ meme_sepd_exprs =
629
+ meme_expr:n0 (expr_sep meme_expr:n)*:nrest expr_sep_opt { [n0, *nrest] }
630
+
631
+ meme_inline_expr_body =
632
+ inln_sep_opt meme_inline_sepd_exprs:nlist
633
+ ~node(:block, nlist.first, nlist)
634
+
635
+ meme_expr_body =
636
+ expr_sep_opt meme_sepd_exprs:nlist t_MEME_END:te
637
+ ~node(:block, nlist.first, nlist)
638
+ | expr_sep_opt t_MEME_END:te
639
+ ~node(:null, te)
640
+
641
+ paren_expr_body =
642
+ expr_sep_opt meme_sepd_exprs:nlist t_PAREN_END:te
643
+ { nlist.count==1 ? nlist.first : node(:block, nlist.first, nlist) }
644
+ | expr_sep_opt t_PAREN_END:te
645
+ ~node(:null, te)
646
+
647
+ paren_expr =
648
+ t_PAREN_BEGIN paren_expr_body:n0 { n0 }
649
+
650
+ meme_enclosed_expr_body =
651
+ t_MEME_BEGIN meme_expr_body:n0 { n0 }
652
+
653
+ meme_either_body =
654
+ meme_enclosed_expr_body
655
+ | meme_inline_expr_body
656
+
657
+ cmeme =
658
+ constant:n0 c_spc* t_MEME_MARK:tm c_spc_nl* meme_inline_expr_body:n1
659
+ ~node(:cdecl, tm, n0, n1)
660
+
661
+ meme_name =
662
+ id_as_symbol
663
+ | lit_string_as_symbol
664
+
665
+ decorator =
666
+ meme_name:ni arg_list?:na
667
+ ~node(:deco, ni, ni, na)
668
+
669
+ decorators_and_meme_name =
670
+ decorator:n0 (c_spc* decorator:n)*:nrest
671
+ ~node(:array, n0, [n0, *nrest].reverse)
672
+
673
+ meme =
674
+ decorators_and_meme_name:nd c_spc* t_MEME_MARK:tm (c_spc_nl* param_list:n)?:np c_spc_nl* meme_either_body:nb
675
+ ~node(:meme, tm, nd.body.shift.name, nd, np, nb)
676
+ | decorators_and_meme_name:nd
677
+ ~node(:meme, tm, nd.body.shift.name, nd, nil, nil)
678
+
679
+
680
+ %% {
681
+ #%
682
+ # Encode escape characters in string literals
683
+ # TODO: rigorously test and refine
684
+ #
685
+ def encode_escapes str
686
+ str.gsub /\\(.)/ do "#{$1}" end
687
+ end
688
+
689
+ # Given a node,op list ([node, op, node, op, ... node]) and operator types,
690
+ # collapse the (node, op, node) groups where the operator is one of the types
691
+ #
692
+ # This function is meant to be called several times on the same list,
693
+ # with a different set of operator types each time, in order of precedence.
694
+ #
695
+ def collapse input, *types
696
+ output = []
697
+
698
+ # Scan through, reducing or shifting based on the operator
699
+ while input.count > 2
700
+ n0 = input.shift
701
+ op = input.shift
702
+
703
+ if types.include? op.type
704
+ n1 = input.shift
705
+
706
+ result = block_given? ?
707
+ yield(n0,op,n1) :
708
+ node(:call, op, n0, op.sym, node(:array, n1, [n1]))
709
+ input.unshift result
710
+ else
711
+ output.push n0
712
+ output.push op
713
+ end
714
+ end
715
+
716
+ # Push the last item remaining
717
+ output.push input.shift
718
+
719
+ input.replace output
720
+ end
721
+
722
+ # Given a locator and an node(:array), refactor the splat-related nodes
723
+ # in a copy of the original node body and return a replacement for the node.
724
+ #
725
+ def args_assemble loc, orig_node
726
+ list = orig_node.body.dup
727
+ tmp = []
728
+
729
+ special_type_check = Proc.new { |x|
730
+ case x
731
+ when Myco::ToolSet::AST::SplatValue; :splat
732
+ when Myco::ToolSet::AST::ConcatArgs; :argscat
733
+ when Myco::ToolSet::AST::PushArgs; :argspush
734
+ when Myco::ToolSet::AST::BlockPass; :block_pass
735
+ else; nil
736
+ end
737
+ }
738
+
739
+ # Get the original value in the new_node
740
+ tmp << list.shift until list.empty? or special_type_check.call(list.first)
741
+ new_node = tmp.empty? ? list.shift : node(:array, loc, tmp)
742
+
743
+ # Continue to reduce until all elements have been used
744
+ until list.empty?
745
+ arg = list.shift
746
+ type = special_type_check.call(arg)
747
+ if type == :block_pass
748
+ new_node = arg.tap { |n| n.arguments = new_node }
749
+ elsif type != nil
750
+ new_node = node(:argscat, loc, new_node, arg)
751
+ else
752
+ new_node = node(:argspush, loc, new_node, arg)
753
+ end
754
+ end
755
+
756
+ new_node || orig_node
757
+ end
758
+
759
+ }