melbourne 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY +3 -0
- data/LICENSE +27 -0
- data/README.rdoc +38 -0
- data/Rakefile +38 -0
- data/VERSION.yml +4 -0
- data/ext/melbourne/bstring-license.txt +29 -0
- data/ext/melbourne/bstrlib.c +2918 -0
- data/ext/melbourne/bstrlib.h +302 -0
- data/ext/melbourne/extconf.rb +76 -0
- data/ext/melbourne/grammar.cpp +11885 -0
- data/ext/melbourne/grammar.hpp +14 -0
- data/ext/melbourne/grammar.y +6013 -0
- data/ext/melbourne/internal.hpp +137 -0
- data/ext/melbourne/lex.c.tab +136 -0
- data/ext/melbourne/local_state.hpp +41 -0
- data/ext/melbourne/melbourne.cpp +37 -0
- data/ext/melbourne/node.hpp +262 -0
- data/ext/melbourne/node_types.cpp +245 -0
- data/ext/melbourne/node_types.hpp +135 -0
- data/ext/melbourne/node_types.rb +190 -0
- data/ext/melbourne/quark.cpp +52 -0
- data/ext/melbourne/quark.hpp +14 -0
- data/ext/melbourne/symbols.cpp +219 -0
- data/ext/melbourne/symbols.hpp +116 -0
- data/ext/melbourne/var_table.cpp +113 -0
- data/ext/melbourne/var_table.hpp +33 -0
- data/ext/melbourne/visitor.cpp +1052 -0
- data/ext/melbourne/visitor.hpp +20 -0
- data/lib/melbourne/ast/constants.rb +128 -0
- data/lib/melbourne/ast/control_flow.rb +382 -0
- data/lib/melbourne/ast/data.rb +19 -0
- data/lib/melbourne/ast/definitions.rb +561 -0
- data/lib/melbourne/ast/exceptions.rb +182 -0
- data/lib/melbourne/ast/file.rb +15 -0
- data/lib/melbourne/ast/grapher.rb +75 -0
- data/lib/melbourne/ast/literals.rb +268 -0
- data/lib/melbourne/ast/node.rb +21 -0
- data/lib/melbourne/ast/operators.rb +117 -0
- data/lib/melbourne/ast/self.rb +17 -0
- data/lib/melbourne/ast/sends.rb +451 -0
- data/lib/melbourne/ast/values.rb +74 -0
- data/lib/melbourne/ast/variables.rb +251 -0
- data/lib/melbourne/ast.rb +22 -0
- data/lib/melbourne/parser.rb +38 -0
- data/lib/melbourne/processor.rb +460 -0
- data/lib/melbourne.rb +46 -0
- data/spec/helpers/ast/node.rb +15 -0
- data/spec/helpers/ast/reduced_graph.rb +64 -0
- data/spec/lib/parser/alias_spec.rb +97 -0
- data/spec/lib/parser/and_spec.rb +63 -0
- data/spec/lib/parser/array_spec.rb +157 -0
- data/spec/lib/parser/attrasgn_spec.rb +401 -0
- data/spec/lib/parser/back_ref_spec.rb +20 -0
- data/spec/lib/parser/call_spec.rb +958 -0
- data/spec/lib/parser/case_spec.rb +577 -0
- data/spec/lib/parser/cdecl_spec.rb +108 -0
- data/spec/lib/parser/class_spec.rb +221 -0
- data/spec/lib/parser/colon2_spec.rb +13 -0
- data/spec/lib/parser/colon3_spec.rb +12 -0
- data/spec/lib/parser/const_spec.rb +12 -0
- data/spec/lib/parser/cvar_spec.rb +55 -0
- data/spec/lib/parser/cvasgn_spec.rb +71 -0
- data/spec/lib/parser/cvdecl_spec.rb +31 -0
- data/spec/lib/parser/defined_spec.rb +353 -0
- data/spec/lib/parser/defn_spec.rb +1409 -0
- data/spec/lib/parser/defs_spec.rb +247 -0
- data/spec/lib/parser/dot2_spec.rb +29 -0
- data/spec/lib/parser/dot3_spec.rb +29 -0
- data/spec/lib/parser/dregx_spec.rb +127 -0
- data/spec/lib/parser/dstr_spec.rb +453 -0
- data/spec/lib/parser/dsym_spec.rb +31 -0
- data/spec/lib/parser/dxstr_spec.rb +31 -0
- data/spec/lib/parser/ensure_spec.rb +279 -0
- data/spec/lib/parser/false_spec.rb +12 -0
- data/spec/lib/parser/flip2_spec.rb +138 -0
- data/spec/lib/parser/flip3_spec.rb +100 -0
- data/spec/lib/parser/for_spec.rb +279 -0
- data/spec/lib/parser/gasgn_spec.rb +34 -0
- data/spec/lib/parser/gvar_spec.rb +33 -0
- data/spec/lib/parser/hash_spec.rb +77 -0
- data/spec/lib/parser/iasgn_spec.rb +54 -0
- data/spec/lib/parser/if_spec.rb +439 -0
- data/spec/lib/parser/iter_spec.rb +2582 -0
- data/spec/lib/parser/lasgn_spec.rb +1066 -0
- data/spec/lib/parser/lit_spec.rb +75 -0
- data/spec/lib/parser/masgn_spec.rb +1970 -0
- data/spec/lib/parser/match2_spec.rb +47 -0
- data/spec/lib/parser/match3_spec.rb +54 -0
- data/spec/lib/parser/match_spec.rb +19 -0
- data/spec/lib/parser/module_spec.rb +102 -0
- data/spec/lib/parser/nil_spec.rb +13 -0
- data/spec/lib/parser/not_spec.rb +39 -0
- data/spec/lib/parser/nth_ref_spec.rb +12 -0
- data/spec/lib/parser/op_asgn_spec.rb +619 -0
- data/spec/lib/parser/or_spec.rb +155 -0
- data/spec/lib/parser/postexe_spec.rb +31 -0
- data/spec/lib/parser/regex_spec.rb +52 -0
- data/spec/lib/parser/rescue_spec.rb +1028 -0
- data/spec/lib/parser/return_spec.rb +151 -0
- data/spec/lib/parser/sclass_spec.rb +172 -0
- data/spec/lib/parser/str_spec.rb +162 -0
- data/spec/lib/parser/super_spec.rb +276 -0
- data/spec/lib/parser/true_spec.rb +12 -0
- data/spec/lib/parser/undef_spec.rb +222 -0
- data/spec/lib/parser/until_spec.rb +286 -0
- data/spec/lib/parser/valias_spec.rb +12 -0
- data/spec/lib/parser/while_spec.rb +458 -0
- data/spec/lib/parser/xstr_spec.rb +12 -0
- data/spec/lib/parser/yield_spec.rb +202 -0
- data/spec/lib/parser/zsuper_spec.rb +101 -0
- data/spec/matchers/parse_as.rb +27 -0
- data/spec/spec_helper.rb +10 -0
- metadata +168 -0
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'ast')
|
|
2
|
+
|
|
3
|
+
module Melbourne
|
|
4
|
+
|
|
5
|
+
# Exception that is raised whenever Melbourne encounters anything it cannot parse.
|
|
6
|
+
# When this exception is raised, the parsed code is most likely not syntactically valid.
|
|
7
|
+
#
|
|
8
|
+
class ParserError < Exception; end
|
|
9
|
+
|
|
10
|
+
class Parser
|
|
11
|
+
def process_parse_error(message, column, line, source)
|
|
12
|
+
@exc = SyntaxError.from message, column, line, source, @name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def process_dangling_node
|
|
16
|
+
raise ParserError.new('Processing called but node was NULL')
|
|
17
|
+
# TODO: output info about the current AST node
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# This method is analogous to #method_missing. It is called
|
|
21
|
+
# if there is no processing method defined for a node.
|
|
22
|
+
def process_missing_node(line, node_name, node_type)
|
|
23
|
+
raise ParserError.new("Unhandled node #{node_name} (#{node_type})")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Processing methods
|
|
27
|
+
|
|
28
|
+
def process_alias(line, to, from)
|
|
29
|
+
AST::Alias.new line, to, from
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def process_and(line, left, right)
|
|
33
|
+
AST::And.new line, left, right
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def process_args(line, args, defaults, splat)
|
|
37
|
+
AST::FormalArguments.new line, args, defaults, splat
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def process_argscat(line, array, rest)
|
|
41
|
+
AST::ConcatArgs.new line, array, rest
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def process_argspush(line, arguments, value)
|
|
45
|
+
AST::PushArgs.new line, arguments, value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def process_array(line, array)
|
|
49
|
+
AST::ArrayLiteral.new line, array
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def process_attrasgn(line, receiver, name, arguments)
|
|
53
|
+
if name == :[]=
|
|
54
|
+
AST::ElementAssignment.new line, receiver, arguments
|
|
55
|
+
else
|
|
56
|
+
AST::AttributeAssignment.new line, receiver, name, arguments
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def process_back_ref(line, ref)
|
|
61
|
+
AST::BackRef.new line, ref
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def process_begin(line, body)
|
|
65
|
+
AST::Begin.new line, body
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def process_block(line, array)
|
|
69
|
+
AST::Block.new line, array
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def process_block_arg(line, name)
|
|
73
|
+
AST::BlockArgument.new line, name
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def process_block_pass(line, method_send, body)
|
|
77
|
+
node = AST::BlockPass.new line, body
|
|
78
|
+
if method_send
|
|
79
|
+
method_send.block = node
|
|
80
|
+
method_send
|
|
81
|
+
else
|
|
82
|
+
node
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def process_break(line, value)
|
|
87
|
+
AST::Break.new line, value
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def process_call(line, receiver, name, arguments)
|
|
91
|
+
if arguments
|
|
92
|
+
AST::SendWithArguments.new line, receiver, name, arguments
|
|
93
|
+
else
|
|
94
|
+
AST::Send.new line, receiver, name
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def process_case(line, receiver, whens, else_body)
|
|
99
|
+
if receiver
|
|
100
|
+
AST::ReceiverCase.new line, receiver, whens, else_body
|
|
101
|
+
else
|
|
102
|
+
AST::Case.new line, whens, else_body
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def process_cdecl(line, expr, value)
|
|
107
|
+
AST::ConstSet.new line, expr, value
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def process_class(line, name, superclass, body)
|
|
111
|
+
AST::Class.new line, name, superclass, body
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def process_colon2(line, outer, name)
|
|
115
|
+
if outer
|
|
116
|
+
AST::ConstAccess.new line, outer, name
|
|
117
|
+
else
|
|
118
|
+
AST::ConstFind.new line, name
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def process_colon3(line, name)
|
|
123
|
+
AST::ConstAtTop.new line, name
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def process_const(line, name)
|
|
127
|
+
AST::ConstFind.new line, name
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def process_cvar(line, name)
|
|
131
|
+
AST::ClassVariableAccess.new line, name
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def process_cvasgn(line, name, value)
|
|
135
|
+
AST::ClassVariableAssignment.new line, name, value
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def process_cvdecl(line, name, value)
|
|
139
|
+
AST::ClassVariableAssignment.new line, name, value
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def process_data(line, data)
|
|
143
|
+
AST::EndData.new line, data
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def process_defined(line, expr)
|
|
147
|
+
AST::Defined.new line, expr
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def process_defn(line, name, body)
|
|
151
|
+
AST::Define.new line, name, body
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def process_defs(line, receiver, name, body)
|
|
155
|
+
AST::DefineSingleton.new line, receiver, name, body
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def process_dot2(line, start, finish)
|
|
159
|
+
AST::Range.new line, start, finish
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def process_dot3(line, start, finish)
|
|
163
|
+
AST::RangeExclude.new line, start, finish
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def process_dregx(line, str, array, flags)
|
|
167
|
+
AST::DynamicRegex.new line, str, array, flags
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def process_dregx_once(line, str, array, flags)
|
|
171
|
+
AST::DynamicOnceRegex.new line, str, array, flags
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def process_dstr(line, str, array)
|
|
175
|
+
AST::DynamicString.new line, str, array
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def process_dsym(line, str, array)
|
|
179
|
+
AST::DynamicSymbol.new line, str, array
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def process_dxstr(line, str, array)
|
|
183
|
+
AST::DynamicExecuteString.new line, str, array
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def process_ensure(line, body, ensr)
|
|
187
|
+
AST::Ensure.new line, body, ensr
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def process_evstr(line, value)
|
|
191
|
+
if value
|
|
192
|
+
AST::ToString.new line, value
|
|
193
|
+
else
|
|
194
|
+
AST::StringLiteral.new line, ""
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def process_false(line)
|
|
199
|
+
AST::False.new line
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def process_fcall(line, name, arguments)
|
|
203
|
+
receiver = AST::Self.new line
|
|
204
|
+
|
|
205
|
+
if arguments
|
|
206
|
+
AST::SendWithArguments.new line, receiver, name, arguments, true
|
|
207
|
+
else
|
|
208
|
+
AST::Send.new line, receiver, name, true
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def process_file(line)
|
|
213
|
+
AST::File.new line
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def process_fixnum(line, value)
|
|
217
|
+
AST::FixnumLiteral.new line, value
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def process_flip2(line, start, finish)
|
|
221
|
+
AST::Flip2.new line, start, finish
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def process_flip3(line, start, finish)
|
|
225
|
+
AST::Flip3.new line, start, finish
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def process_float(line, str)
|
|
229
|
+
AST::Float.new line, str
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def process_for(line, iter, arguments, body)
|
|
233
|
+
method_send = AST::Send.new line, iter, :each
|
|
234
|
+
method_send.block = AST::For.new line, arguments, body
|
|
235
|
+
method_send
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def process_gasgn(line, name, expr)
|
|
239
|
+
AST::GlobalVariableAssignment.new line, name, expr
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def process_gvar(line, name)
|
|
243
|
+
AST::GlobalVariableAccess.new line, name
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def process_hash(line, array)
|
|
247
|
+
AST::HashLiteral.new line, array
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
def process_iasgn(line, name, value)
|
|
251
|
+
AST::InstanceVariableAssignment.new line, name, value
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def process_if(line, cond, body, else_body)
|
|
255
|
+
AST::If.new line, cond, body, else_body
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def process_iter(line, method_send, arguments, body)
|
|
259
|
+
method_send.block = AST::Iter.new line, arguments, body
|
|
260
|
+
method_send
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def process_ivar(line, name)
|
|
264
|
+
AST::InstanceVariableAccess.new line, name
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def process_lasgn(line, name, value)
|
|
268
|
+
AST::LocalVariableAssignment.new line, name, value
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def process_lit(line, sym)
|
|
272
|
+
AST::SymbolLiteral.new line, sym
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def process_lvar(line, name)
|
|
276
|
+
AST::LocalVariableAccess.new line, name
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
def process_masgn(line, left, right, splat)
|
|
280
|
+
AST::MAsgn.new line, left, right, splat
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def process_match(line, pattern, flags)
|
|
284
|
+
AST::Match.new line, pattern, flags
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
def process_match2(line, pattern, value)
|
|
288
|
+
AST::Match2.new line, pattern, value
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def process_match3(line, pattern, value)
|
|
292
|
+
AST::Match3.new line, pattern, value
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def process_module(line, name, body)
|
|
296
|
+
AST::Module.new line, name, body
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def process_negate(line, value)
|
|
300
|
+
AST::Negate.new line, value
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def process_next(line, value)
|
|
304
|
+
AST::Next.new line, value
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
def process_nil(line)
|
|
308
|
+
AST::Nil.new line
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
def process_not(line, value)
|
|
312
|
+
AST::Not.new line, value
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def process_nth_ref(line, ref)
|
|
316
|
+
AST::NthRef.new line, ref
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
def process_number(line, base, str)
|
|
320
|
+
value = str.to_i base
|
|
321
|
+
case value
|
|
322
|
+
when Fixnum
|
|
323
|
+
AST::FixnumLiteral.new line, value
|
|
324
|
+
when Bignum
|
|
325
|
+
AST::NumberLiteral.new line, value
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def process_op_asgn1(line, receiver, index, op, value)
|
|
330
|
+
AST::OpAssign1.new line, receiver, index, op, value
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def process_op_asgn2(line, receiver, name, op, value)
|
|
334
|
+
AST::OpAssign2.new line, receiver, name, op, value
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
def process_op_asgn_and(line, var, value)
|
|
338
|
+
AST::OpAssignAnd.new line, var, value
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
def process_op_asgn_or(line, var, value)
|
|
342
|
+
AST::OpAssignOr.new line, var, value
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
def process_or(line, left, right)
|
|
346
|
+
AST::Or.new line, left, right
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
def process_postexe(line)
|
|
350
|
+
AST::Send.new line, AST::Self.new(line), :at_exit, true
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def process_redo(line)
|
|
354
|
+
AST::Redo.new line
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def process_regex(line, str, flags)
|
|
358
|
+
AST::RegexLiteral.new line, str, flags
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def process_resbody(line, conditions, body, nxt)
|
|
362
|
+
AST::RescueCondition.new line, conditions, body, nxt
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def process_rescue(line, body, rescue_body, else_body)
|
|
366
|
+
AST::Rescue.new line, body, rescue_body, else_body
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
def process_retry(line)
|
|
370
|
+
AST::Retry.new line
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def process_return(line, value)
|
|
374
|
+
AST::Return.new line, value
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def process_sclass(line, receiver, body)
|
|
378
|
+
AST::SClass.new line, receiver, body
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def process_scope(line, body)
|
|
382
|
+
if body.kind_of? AST::Block
|
|
383
|
+
body
|
|
384
|
+
elsif body
|
|
385
|
+
AST::Block.new line, [body]
|
|
386
|
+
end
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
def process_self(line)
|
|
390
|
+
AST::Self.new line
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
def process_splat(line, expr)
|
|
394
|
+
AST::SplatValue.new line, expr
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
def process_str(line, str)
|
|
398
|
+
AST::StringLiteral.new line, str
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
def process_super(line, args)
|
|
402
|
+
AST::Super.new line, args
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def process_svalue(line, expr)
|
|
406
|
+
AST::SValue.new line, expr
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def process_to_ary(line, expr)
|
|
410
|
+
AST::ToArray.new line, expr
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def process_true(line)
|
|
414
|
+
AST::True.new line
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
def process_undef(line, sym)
|
|
418
|
+
AST::Undef.new line, sym
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
def process_until(line, cond, body, check_first)
|
|
422
|
+
AST::Until.new line, cond, body, check_first
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
def process_vcall(line, name)
|
|
426
|
+
receiver = AST::Self.new line
|
|
427
|
+
|
|
428
|
+
AST::Send.new line, receiver, name, true
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
def process_valias(line, to, from)
|
|
432
|
+
AST::VAlias.new line, to, from
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def process_when(line, conditions, body)
|
|
436
|
+
AST::When.new line, conditions, body
|
|
437
|
+
end
|
|
438
|
+
|
|
439
|
+
def process_while(line, cond, body, check_first)
|
|
440
|
+
AST::While.new line, cond, body, check_first
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def process_xstr(line, str)
|
|
444
|
+
AST::ExecuteString.new line, str
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def process_yield(line, arguments, unwrap)
|
|
448
|
+
AST::Yield.new line, arguments, unwrap
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def process_zarray(line)
|
|
452
|
+
AST::EmptyArray.new line
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
def process_zsuper(line)
|
|
456
|
+
AST::ZSuper.new line
|
|
457
|
+
end
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
end
|
data/lib/melbourne.rb
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
base = File.dirname(__FILE__)
|
|
2
|
+
|
|
3
|
+
require File.join(base, 'ext/melbourne')
|
|
4
|
+
require File.join(base, 'melbourne/parser')
|
|
5
|
+
require File.join(base, 'melbourne/processor')
|
|
6
|
+
|
|
7
|
+
module Melbourne #:nodoc:
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
class String
|
|
11
|
+
|
|
12
|
+
# Creates an AST for a +String+ containing Ruby source code.
|
|
13
|
+
#
|
|
14
|
+
# === Example
|
|
15
|
+
#
|
|
16
|
+
# 'class Test; end'.to_ast # => <AST::Class:0x1017800f8...
|
|
17
|
+
#
|
|
18
|
+
# === Arguments
|
|
19
|
+
#
|
|
20
|
+
# * +name+: the name of the source (this is usuall the name of the file the code was read from); defaults to <tt>(eval)</tt>
|
|
21
|
+
# * +line+: the starting line (if it's not 1 for some reason); defaults to <tt>1</tt>
|
|
22
|
+
#
|
|
23
|
+
def to_ast(name = '(eval)', line = 1)
|
|
24
|
+
Melbourne::Parser.parse_string(self, name, line)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class File
|
|
30
|
+
|
|
31
|
+
# Creates an AST for Ruby source code read from a file.
|
|
32
|
+
#
|
|
33
|
+
# === Example
|
|
34
|
+
#
|
|
35
|
+
# File.to_ast('user.rb') # => <AST::Class:0x1017800f8...
|
|
36
|
+
#
|
|
37
|
+
# === Arguments
|
|
38
|
+
#
|
|
39
|
+
# * +name+: the name of the file to read the source code from.
|
|
40
|
+
# * +line+: the starting line (if it's not 1 for some reason); defaults to <tt>1</tt>
|
|
41
|
+
#
|
|
42
|
+
def self.to_ast(name, line = 1)
|
|
43
|
+
Melbourne::Parser.parse_file(name, line)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
module Melbourne
|
|
2
|
+
|
|
3
|
+
module AST
|
|
4
|
+
|
|
5
|
+
class ReducedGraph
|
|
6
|
+
|
|
7
|
+
def initialize(ast)
|
|
8
|
+
@graph = {}
|
|
9
|
+
graph_node(ast)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_hash
|
|
13
|
+
@graph
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def graph_node(node, parent = @graph)
|
|
19
|
+
return if node.kind_of?(Nil)
|
|
20
|
+
name = format_class_name(node.class)
|
|
21
|
+
parent[name] = {}
|
|
22
|
+
|
|
23
|
+
node.instance_variables.each do |instance_variable|
|
|
24
|
+
next if instance_variable == '@compiler'
|
|
25
|
+
value = node.instance_variable_get(instance_variable)
|
|
26
|
+
graph_instance_variable(instance_variable, value, parent[name])
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def graph_instance_variable(variable, value, parent)
|
|
31
|
+
if value.kind_of?(Node)
|
|
32
|
+
graph_node(value, parent[variable.to_sym] = {})
|
|
33
|
+
elsif value.kind_of?(Array)
|
|
34
|
+
parent = parent[variable.to_sym] = []
|
|
35
|
+
value.each do |element|
|
|
36
|
+
if element.kind_of?(Node)
|
|
37
|
+
graph_node(element, (parent << {}).last)
|
|
38
|
+
else
|
|
39
|
+
parent << format_value(element)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
else
|
|
43
|
+
parent[variable.to_sym] = format_value(value)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def format_value(value)
|
|
48
|
+
case value
|
|
49
|
+
when NilClass, TrueClass, FalseClass, Symbol, Fixnum
|
|
50
|
+
value
|
|
51
|
+
else
|
|
52
|
+
value.to_s.empty? ? :'<blank>' : value.to_s.to_sym
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def format_class_name(klass)
|
|
57
|
+
klass.to_s.split('::').last.downcase.to_sym
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
|
2
|
+
|
|
3
|
+
describe Melbourne::Parser do
|
|
4
|
+
|
|
5
|
+
it 'should correctly parse "class X; alias :y :x; end"' do
|
|
6
|
+
ruby = <<-ruby
|
|
7
|
+
class X
|
|
8
|
+
alias :y :x
|
|
9
|
+
end
|
|
10
|
+
ruby
|
|
11
|
+
ast = {:class=>
|
|
12
|
+
{:@superclass=>{},
|
|
13
|
+
:@body=>
|
|
14
|
+
{:classscope=>
|
|
15
|
+
{:@body=>
|
|
16
|
+
{:block=>
|
|
17
|
+
{:@array=>
|
|
18
|
+
[{:alias=>
|
|
19
|
+
{:@line=>2,
|
|
20
|
+
:@to=>{:symbolliteral=>{:@line=>2, :@value=>:y}},
|
|
21
|
+
:@from=>{:symbolliteral=>{:@line=>2, :@value=>:x}}}}],
|
|
22
|
+
:@line=>3}},
|
|
23
|
+
:@name=>:X,
|
|
24
|
+
:@line=>1}},
|
|
25
|
+
:@name=>{:classname=>{:@superclass=>{}, :@name=>:X, :@line=>1}},
|
|
26
|
+
:@line=>1}}
|
|
27
|
+
|
|
28
|
+
ruby.should parse_as(ast)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'should correctly parse "class X; alias y x; end"' do
|
|
32
|
+
ruby = <<-ruby
|
|
33
|
+
class X
|
|
34
|
+
alias y x
|
|
35
|
+
end
|
|
36
|
+
ruby
|
|
37
|
+
ast = {:class=>
|
|
38
|
+
{:@superclass=>{},
|
|
39
|
+
:@body=>
|
|
40
|
+
{:classscope=>
|
|
41
|
+
{:@body=>
|
|
42
|
+
{:block=>
|
|
43
|
+
{:@array=>
|
|
44
|
+
[{:alias=>
|
|
45
|
+
{:@line=>2,
|
|
46
|
+
:@to=>{:symbolliteral=>{:@line=>2, :@value=>:y}},
|
|
47
|
+
:@from=>{:symbolliteral=>{:@line=>2, :@value=>:x}}}}],
|
|
48
|
+
:@line=>3}},
|
|
49
|
+
:@name=>:X,
|
|
50
|
+
:@line=>1}},
|
|
51
|
+
:@name=>{:classname=>{:@superclass=>{}, :@name=>:X, :@line=>1}},
|
|
52
|
+
:@line=>1}}
|
|
53
|
+
|
|
54
|
+
ruby.should parse_as(ast)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
it 'should correctly parse "class X; alias :"y#{1}" :"x#{2}"; end"' do
|
|
58
|
+
ruby = <<-ruby
|
|
59
|
+
class X
|
|
60
|
+
alias :"y\#{1}" :"x\#{2}"
|
|
61
|
+
end
|
|
62
|
+
ruby
|
|
63
|
+
ast = {:class=>
|
|
64
|
+
{:@superclass=>{},
|
|
65
|
+
:@body=>
|
|
66
|
+
{:classscope=>
|
|
67
|
+
{:@body=>
|
|
68
|
+
{:block=>
|
|
69
|
+
{:@array=>
|
|
70
|
+
[{:alias=>
|
|
71
|
+
{:@line=>2,
|
|
72
|
+
:@to=>
|
|
73
|
+
{:dynamicsymbol=>
|
|
74
|
+
{:@string=>:y,
|
|
75
|
+
:@array=>
|
|
76
|
+
[{:tostring=>
|
|
77
|
+
{:@line=>2,
|
|
78
|
+
:@value=>{:fixnumliteral=>{:@line=>2, :@value=>1}}}}],
|
|
79
|
+
:@line=>2}},
|
|
80
|
+
:@from=>
|
|
81
|
+
{:dynamicsymbol=>
|
|
82
|
+
{:@string=>:x,
|
|
83
|
+
:@array=>
|
|
84
|
+
[{:tostring=>
|
|
85
|
+
{:@line=>2,
|
|
86
|
+
:@value=>{:fixnumliteral=>{:@line=>2, :@value=>2}}}}],
|
|
87
|
+
:@line=>2}}}}],
|
|
88
|
+
:@line=>3}},
|
|
89
|
+
:@name=>:X,
|
|
90
|
+
:@line=>1}},
|
|
91
|
+
:@name=>{:classname=>{:@superclass=>{}, :@name=>:X, :@line=>1}},
|
|
92
|
+
:@line=>1}}
|
|
93
|
+
|
|
94
|
+
ruby.should parse_as(ast)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|