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.
- checksums.yaml +7 -0
- data/LICENSE +2 -0
- data/bin/myco +7 -0
- data/lib/myco/backtrace.rb +56 -0
- data/lib/myco/bootstrap/component.rb +142 -0
- data/lib/myco/bootstrap/empty_object.rb +4 -0
- data/lib/myco/bootstrap/file_toplevel.rb +5 -0
- data/lib/myco/bootstrap/find_constant.rb +86 -0
- data/lib/myco/bootstrap/instance.rb +52 -0
- data/lib/myco/bootstrap/meme.rb +160 -0
- data/lib/myco/bootstrap/void.rb +40 -0
- data/lib/myco/bootstrap.my +15 -0
- data/lib/myco/bootstrap.rb +10 -0
- data/lib/myco/command.my +33 -0
- data/lib/myco/core/BasicObject.my +46 -0
- data/lib/myco/core/Category.my +5 -0
- data/lib/myco/core/Decorator.my +18 -0
- data/lib/myco/core/FileToplevel.my +23 -0
- data/lib/myco/core/Object.my +24 -0
- data/lib/myco/core/Switch.my +31 -0
- data/lib/myco/eval.rb +63 -0
- data/lib/myco/parser/ast/constant_access.rb +29 -0
- data/lib/myco/parser/ast/constant_define.rb +40 -0
- data/lib/myco/parser/ast/constant_reopen.rb +47 -0
- data/lib/myco/parser/ast/declare_category.rb +51 -0
- data/lib/myco/parser/ast/declare_decorator.rb +35 -0
- data/lib/myco/parser/ast/declare_file.rb +54 -0
- data/lib/myco/parser/ast/declare_meme.rb +44 -0
- data/lib/myco/parser/ast/declare_object.rb +75 -0
- data/lib/myco/parser/ast/declare_string.rb +37 -0
- data/lib/myco/parser/ast/invoke.rb +66 -0
- data/lib/myco/parser/ast/local_variable_access_ambiguous.rb +38 -0
- data/lib/myco/parser/ast/misc.rb +61 -0
- data/lib/myco/parser/ast/myco_module_scope.rb +58 -0
- data/lib/myco/parser/ast/quest.rb +82 -0
- data/lib/myco/parser/ast.rb +15 -0
- data/lib/myco/parser/builder.output +3995 -0
- data/lib/myco/parser/builder.racc +585 -0
- data/lib/myco/parser/builder.rb +1592 -0
- data/lib/myco/parser/lexer.rb +2306 -0
- data/lib/myco/parser/lexer.rl +393 -0
- data/lib/myco/parser/lexer_char_classes.rl +56 -0
- data/lib/myco/parser/lexer_common.rb +95 -0
- data/lib/myco/parser/lexer_skeleton.rl +154 -0
- data/lib/myco/parser/peg_parser.kpeg +759 -0
- data/lib/myco/parser/peg_parser.rb +7094 -0
- data/lib/myco/parser.rb +40 -0
- data/lib/myco/tools/OptionParser.my +38 -0
- data/lib/myco/tools/mycompile.my +51 -0
- data/lib/myco/toolset.rb +16 -0
- data/lib/myco/version.rb +22 -0
- data/lib/myco.rb +15 -0
- 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
|
+
}
|