myco 0.1.0.dev

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.
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
+ }