myco 0.1.0.dev
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|