rogems 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.
- checksums.yaml +7 -0
- data/.rspec +1 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +112 -0
- data/LICENSE +21 -0
- data/README.md +41 -0
- data/examples/classes/.gitignore +6 -0
- data/examples/classes/README.md +17 -0
- data/examples/classes/default.project.json +72 -0
- data/examples/classes/out/client/main.client.lua +91 -0
- data/examples/classes/rogems.json +6 -0
- data/examples/classes/src/client/main.client.rb +25 -0
- data/examples/lava_brick/aftman.toml +7 -0
- data/examples/lava_brick/default.project.json +35 -0
- data/examples/lava_brick/out/client/test.lua +13 -0
- data/examples/lava_brick/rogems.json +6 -0
- data/examples/lava_brick/src/client/test.rb +12 -0
- data/examples/test/.gitignore +6 -0
- data/examples/test/README.md +17 -0
- data/examples/test/default.project.json +72 -0
- data/examples/test/out/client/main.client.lua +6 -0
- data/examples/test/rogems.json +6 -0
- data/examples/test/src/client/main.client.rb +4 -0
- data/lib/RoGems.rb +4 -0
- data/lib/rogems/CLI.rb +121 -0
- data/lib/rogems/CodeGenerator.rb +840 -0
- data/lib/rogems/Exceptions.rb +46 -0
- data/lib/rogems/Transpiler.rb +39 -0
- data/lib/rogems/Version.rb +3 -0
- data/lib/rogems/default_rogems.json +6 -0
- data/lib/rogems/lua/Runtime.lua +13 -0
- metadata +77 -0
@@ -0,0 +1,840 @@
|
|
1
|
+
require "Exceptions"
|
2
|
+
require "parser/ruby30"
|
3
|
+
# opt-in to most recent AST format:
|
4
|
+
Parser::Builders::Default.emit_lambda = true
|
5
|
+
Parser::Builders::Default.emit_procarg0 = true
|
6
|
+
Parser::Builders::Default.emit_encoding = true
|
7
|
+
Parser::Builders::Default.emit_index = true
|
8
|
+
Parser::Builders::Default.emit_arg_inside_procarg0 = true
|
9
|
+
Parser::Builders::Default.emit_forward_arg = true
|
10
|
+
Parser::Builders::Default.emit_kwargs = true
|
11
|
+
Parser::Builders::Default.emit_match_pattern = true
|
12
|
+
|
13
|
+
module RoGems
|
14
|
+
class CodeGenerator
|
15
|
+
attr_reader :output
|
16
|
+
|
17
|
+
def initialize(config, source)
|
18
|
+
@source = source
|
19
|
+
@debug_mode = !config["debugging"].nil? && config["debugging"] == true
|
20
|
+
@output = ""
|
21
|
+
@block = 0
|
22
|
+
@line = 0
|
23
|
+
|
24
|
+
@method_aliases = [:each, :each_with_index, :nil?, :to_s]
|
25
|
+
@dont_return_nodes = [:cvasgn, :ivasgn, :puts, :if, :while, :until, :for, :break]
|
26
|
+
@return_later_nodes = [:lvasgn, :class, :module, :def]
|
27
|
+
@literals = [:true, :false, :nil, :float, :int, :str, :sym, :array, :hash]
|
28
|
+
end
|
29
|
+
|
30
|
+
def destroy
|
31
|
+
@source = nil
|
32
|
+
@debug_mode = nil
|
33
|
+
@output = nil
|
34
|
+
@block = nil
|
35
|
+
@line = nil
|
36
|
+
@method_aliases = nil
|
37
|
+
@dont_return_nodes = nil
|
38
|
+
@return_later_nodes = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def generate
|
42
|
+
@output = ""
|
43
|
+
write(@debug_mode ? "-- " : "")
|
44
|
+
write("local ruby = require(game.ReplicatedStorage.Ruby.Runtime)")
|
45
|
+
self.newline
|
46
|
+
self.newline
|
47
|
+
|
48
|
+
root_node = Parser::Ruby30.parse(@source)
|
49
|
+
walk_ast(root_node)
|
50
|
+
if @debug_mode then
|
51
|
+
puts root_node
|
52
|
+
end
|
53
|
+
@output
|
54
|
+
end
|
55
|
+
|
56
|
+
def write(text)
|
57
|
+
@output << text
|
58
|
+
end
|
59
|
+
|
60
|
+
def writeln(text)
|
61
|
+
write(text)
|
62
|
+
self.newline
|
63
|
+
end
|
64
|
+
|
65
|
+
def newline()
|
66
|
+
write("\n")
|
67
|
+
write(" " * @block)
|
68
|
+
@line += 1
|
69
|
+
end
|
70
|
+
|
71
|
+
def block()
|
72
|
+
write(" " * @block)
|
73
|
+
@block += 1
|
74
|
+
end
|
75
|
+
|
76
|
+
def end()
|
77
|
+
@block -= 1
|
78
|
+
self.newline
|
79
|
+
write("end")
|
80
|
+
end
|
81
|
+
|
82
|
+
def walk_ast(node, *extra_data)
|
83
|
+
if node.is_a?(Parser::AST::Node)
|
84
|
+
case node.type
|
85
|
+
when :true, :false, :nil # literals
|
86
|
+
write(node.type.to_s)
|
87
|
+
when :float, :int
|
88
|
+
write(node.children[0].to_s)
|
89
|
+
when :str, :sym
|
90
|
+
content = node.children[0].to_s
|
91
|
+
write(self.quote_surround(content))
|
92
|
+
when :array
|
93
|
+
write("{")
|
94
|
+
node.children.each do |child|
|
95
|
+
walk_ast(child)
|
96
|
+
write(child != node.children.last ? ", " : "")
|
97
|
+
end
|
98
|
+
write("}")
|
99
|
+
when :hash
|
100
|
+
write("{")
|
101
|
+
self.block
|
102
|
+
self.newline
|
103
|
+
node.children.each do |pair|
|
104
|
+
walk_ast(pair)
|
105
|
+
if pair != node.children.last then
|
106
|
+
write(",")
|
107
|
+
self.newline
|
108
|
+
end
|
109
|
+
end
|
110
|
+
@block -= 1
|
111
|
+
self.newline
|
112
|
+
write("}")
|
113
|
+
when :pair
|
114
|
+
key, value = *node.children
|
115
|
+
is_lit = @literals.include?(key.type)
|
116
|
+
write(is_lit ? "[" : key.children[1].to_s)
|
117
|
+
if is_lit then
|
118
|
+
walk_ast(key)
|
119
|
+
write("]")
|
120
|
+
end
|
121
|
+
write(" = ")
|
122
|
+
walk_ast(value)
|
123
|
+
when :if # control flow
|
124
|
+
add_end = extra_data[0]
|
125
|
+
condition, block, elseif = *node.children
|
126
|
+
is_nextif = !block.nil? && block.type == :next
|
127
|
+
if is_nextif then
|
128
|
+
self.newline
|
129
|
+
end
|
130
|
+
write("if ")
|
131
|
+
write(is_nextif ? "not (" : "")
|
132
|
+
walk_ast(condition)
|
133
|
+
write(is_nextif ? ")" : "")
|
134
|
+
write(" then")
|
135
|
+
self.block
|
136
|
+
self.newline
|
137
|
+
walk_ast(block)
|
138
|
+
|
139
|
+
unless elseif.nil? then
|
140
|
+
@block -= 1
|
141
|
+
self.newline
|
142
|
+
write("else")
|
143
|
+
if elseif.type == :if then
|
144
|
+
walk_ast(elseif, false)
|
145
|
+
else
|
146
|
+
# @block -= 1
|
147
|
+
self.block
|
148
|
+
self.newline
|
149
|
+
walk_ast(elseif)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
if (add_end.nil? && add_end != false) && !is_nextif then
|
153
|
+
self.end
|
154
|
+
end
|
155
|
+
when :while
|
156
|
+
condition, block = *node.children
|
157
|
+
write("while ")
|
158
|
+
walk_ast(condition)
|
159
|
+
write(" do")
|
160
|
+
self.block
|
161
|
+
|
162
|
+
walk_next_conditions(block)
|
163
|
+
self.end
|
164
|
+
when :until
|
165
|
+
condition, block = *node.children
|
166
|
+
write("repeat ")
|
167
|
+
self.block
|
168
|
+
|
169
|
+
walk_next_conditions(block)
|
170
|
+
|
171
|
+
@block -= 1
|
172
|
+
self.newline
|
173
|
+
write("until ")
|
174
|
+
walk_ast(condition)
|
175
|
+
when :irange
|
176
|
+
min, max = *node.children
|
177
|
+
walk_ast(min)
|
178
|
+
write(", ")
|
179
|
+
walk_ast(max)
|
180
|
+
when :for
|
181
|
+
symbol, iterable, block = *node.children
|
182
|
+
var_name = symbol.type == :mlhs ? symbol.children.map { |s| s.children[0].to_s }.join(", ") : symbol.children[0].to_s
|
183
|
+
|
184
|
+
write("for #{iterable.type == :irange ? var_name : "_, " + var_name}")
|
185
|
+
if iterable.type == :irange then
|
186
|
+
write(" = ")
|
187
|
+
walk_ast(iterable)
|
188
|
+
else
|
189
|
+
write(" in pairs(")
|
190
|
+
walk_ast(iterable)
|
191
|
+
write(")")
|
192
|
+
end
|
193
|
+
write(" do")
|
194
|
+
self.block
|
195
|
+
|
196
|
+
walk_next_conditions(block)
|
197
|
+
self.end
|
198
|
+
when :break
|
199
|
+
write("break")
|
200
|
+
when :and # conditionals
|
201
|
+
left_op, right_op = *node.children
|
202
|
+
write("(")
|
203
|
+
walk_ast(left_op)
|
204
|
+
write(") and (")
|
205
|
+
walk_ast(right_op)
|
206
|
+
write(")")
|
207
|
+
when :or
|
208
|
+
left_op, right_op = *node.children
|
209
|
+
write("(")
|
210
|
+
walk_ast(left_op)
|
211
|
+
write(") or (")
|
212
|
+
walk_ast(right_op)
|
213
|
+
write(")")
|
214
|
+
when :send # operations
|
215
|
+
send(node, *extra_data)
|
216
|
+
when :index
|
217
|
+
table, idx = *node.children
|
218
|
+
write("ruby.Array.at(")
|
219
|
+
walk_ast(table)
|
220
|
+
write(", ")
|
221
|
+
walk_ast(idx)
|
222
|
+
write(")")
|
223
|
+
when :module # module defs
|
224
|
+
module_def(node)
|
225
|
+
when :class # class defs
|
226
|
+
class_def(node)
|
227
|
+
when :begin # blocks
|
228
|
+
explicit_no_return = extra_data[0]
|
229
|
+
node.children.each do |child|
|
230
|
+
has_aliased = has_aliased_method?(child)
|
231
|
+
if !explicit_no_return && child == node.children.last && ((child.type == :send && !is_assignment?(child)) || child.children[1] != :puts) && !@dont_return_nodes.include?(child.type) && !@return_later_nodes.include?(child.type) && !has_aliased then
|
232
|
+
write("return ")
|
233
|
+
end
|
234
|
+
if child.is_a?(Parser::AST::Node)
|
235
|
+
walk_ast(child, *extra_data)
|
236
|
+
if child == node.children.last then
|
237
|
+
unless @return_later_nodes.include?(child.type) then return end
|
238
|
+
puts child
|
239
|
+
write("return #{}")
|
240
|
+
end
|
241
|
+
end
|
242
|
+
unless child == node.children.last then
|
243
|
+
self.newline
|
244
|
+
end
|
245
|
+
end
|
246
|
+
when :def # defs
|
247
|
+
set_args = extra_data[0]
|
248
|
+
def_name = node.children[0].to_s.gsub("?", "").gsub("!", "")
|
249
|
+
args = (set_args || node.children[1]).children
|
250
|
+
arg_list = args.map { |arg| arg.children[0] }.join(", ")
|
251
|
+
block = node.children[2]
|
252
|
+
class_name = extra_data[4]
|
253
|
+
if def_name == :initialize then return end
|
254
|
+
|
255
|
+
write("function #{class_name.nil? ? "" : class_name + ":"}#{def_name}(#{arg_list})")
|
256
|
+
self.block
|
257
|
+
self.newline
|
258
|
+
unless block.nil? then
|
259
|
+
if block.type != :begin && !is_assignment?(block) then
|
260
|
+
write("return ")
|
261
|
+
end
|
262
|
+
walk_ast(block, *extra_data)
|
263
|
+
end
|
264
|
+
self.end
|
265
|
+
when :block # lambdas
|
266
|
+
preceding, args_node, block = *node.children
|
267
|
+
has_aliased_method, aliased_methods = *has_aliased_method?(preceding)
|
268
|
+
args = args_node.children.map { |a| a.children[0].is_a?(Parser::AST::Node) ? a.children[0].children[0].to_s : a.children[0].to_s }
|
269
|
+
walk_ast(preceding, nil, nil, true, preceding.children.last, args)
|
270
|
+
if has_aliased_method then
|
271
|
+
aliased_methods.each { |a| handle_aliased_suffix(a, block) }
|
272
|
+
else
|
273
|
+
write("(function(#{args.join(", ")})")
|
274
|
+
self.block
|
275
|
+
self.newline
|
276
|
+
walk_ast(block)
|
277
|
+
self.end
|
278
|
+
write(")")
|
279
|
+
end
|
280
|
+
when :block_pass # passing fns
|
281
|
+
block = node.children[0]
|
282
|
+
walk_ast(block)
|
283
|
+
when :masgn # multiple assignment
|
284
|
+
self.multiple_assign(node)
|
285
|
+
when :op_asgn # op assignment
|
286
|
+
name_node, op, val = *node.children
|
287
|
+
if @debug_mode then
|
288
|
+
walk_ast(name_node, true, true)
|
289
|
+
write(" = ")
|
290
|
+
walk_ast(name_node, true, true)
|
291
|
+
write(" #{op.to_s} ")
|
292
|
+
else
|
293
|
+
walk_ast(name_node, true, true)
|
294
|
+
write(" #{op.to_s}= ")
|
295
|
+
end
|
296
|
+
if val.is_a?(Parser::AST::Node)
|
297
|
+
walk_ast(val, *extra_data)
|
298
|
+
else
|
299
|
+
write(val.children[0].to_s)
|
300
|
+
end
|
301
|
+
when :lvasgn, :gvasgn # local var assignment
|
302
|
+
name, val = *node.children
|
303
|
+
write((node.type == :lvasgn ? "local " : "") + (node.type == :gvasgn ? name.gsub!("$", "") : name.to_s) + " = ")
|
304
|
+
if val.is_a?(Parser::AST::Node) then
|
305
|
+
walk_ast(val, *extra_data)
|
306
|
+
else
|
307
|
+
write(val.children[0].to_s)
|
308
|
+
end
|
309
|
+
when :cvasgn
|
310
|
+
class_name = extra_data[0]
|
311
|
+
self.instancevar_assign(node, "#{class_name}.")
|
312
|
+
when :ivasgn
|
313
|
+
var_name = node.children[0].to_s.gsub("@", "")
|
314
|
+
readers, writers, accessors = *extra_data
|
315
|
+
readers.map! { |n| (n.is_a?(String) ? n : n.children[2].children[0]).to_s }
|
316
|
+
writers.map! { |n| (n.is_a?(String) ? n : n.children[2].children[0]).to_s }
|
317
|
+
accessors.map! { |n| (n.is_a?(String) ? n : n.children[2].children[0]).to_s }
|
318
|
+
location = self.get_v_location_name(var_name, readers, writers, accessors)
|
319
|
+
self.instancevar_assign(node, location)
|
320
|
+
when :lvar # variable indexing
|
321
|
+
self.index_var(node)
|
322
|
+
when :const
|
323
|
+
write(node.children[1].to_s)
|
324
|
+
else
|
325
|
+
warn("unhandled ast node: #{node.type}")
|
326
|
+
end
|
327
|
+
elsif node.is_a?(Symbol) then
|
328
|
+
sym = check_operator(node.to_s)
|
329
|
+
write(sym)
|
330
|
+
end
|
331
|
+
@last_line = @line
|
332
|
+
end
|
333
|
+
|
334
|
+
def handle_aliased_prefix(a, preceding, block_args)
|
335
|
+
case a
|
336
|
+
when :each, :each_with_index
|
337
|
+
use_pairs = a == :each_with_index || @debug_mode
|
338
|
+
v, i = *block_args
|
339
|
+
write("for ")
|
340
|
+
write(use_pairs ? "#{a == :each_with_index ? i.to_s : "_"}, " : "")
|
341
|
+
write("#{v.to_s} in ")
|
342
|
+
write(use_pairs ? "pairs" : "ruby.list")
|
343
|
+
write("(")
|
344
|
+
walk_ast(preceding)
|
345
|
+
write(") do")
|
346
|
+
self.block
|
347
|
+
self.newline
|
348
|
+
when :to_s
|
349
|
+
walk_ast(preceding)
|
350
|
+
write("tostring(")
|
351
|
+
when :nil?
|
352
|
+
walk_ast(preceding, true)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def handle_aliased_suffix(a, block)
|
357
|
+
case a
|
358
|
+
when :each, :each_with_index
|
359
|
+
walk_ast(block)
|
360
|
+
self.end
|
361
|
+
when :to_s
|
362
|
+
write(")")
|
363
|
+
when :nil?
|
364
|
+
write(" == nil")
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|
368
|
+
def walk_next_conditions(node)
|
369
|
+
nextif_nodes = node.children.filter { |n| n.is_a?(Parser::AST::Node) && n.type == :if && n.children[1].is_a?(Parser::AST::Node) && n.children[1].type == :next }
|
370
|
+
node.children.each do |child|
|
371
|
+
self.walk_ast(child)
|
372
|
+
end
|
373
|
+
node.children.each do |child|
|
374
|
+
next if nextif_nodes.include?(child)
|
375
|
+
self.end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
def is_guaranteed_function_call?(node, child, next_child)
|
380
|
+
op = is_op?(node.children[1].to_s)
|
381
|
+
is_assignment = is_assignment?(node)
|
382
|
+
!next_child.nil? && !op && child.is_a?(Symbol) && !is_assignment
|
383
|
+
end
|
384
|
+
|
385
|
+
def send(node, *extra_data)
|
386
|
+
if node.children[1] == :attr_accessor || node.children[1] == :attr_reader || node.children[1] == :attr_writer || node.children[1] == :include then return end
|
387
|
+
|
388
|
+
dont_emit_function_check, not_function, is_block, block_method, block_args = *extra_data
|
389
|
+
is_assignment = is_assignment?(node)
|
390
|
+
op = is_op?(node.children[1].to_s)
|
391
|
+
first_child = node.children[0]
|
392
|
+
is_send = !first_child.nil? && first_child.is_a?(Parser::AST::Node) && first_child.children.length > 0 && !first_child.children[0].nil? && first_child.children[0].is_a?(Parser::AST::Node) && (first_child.children[0].type == :send || first_child.children[0].type == :lvar)
|
393
|
+
child = node.children[node.children.length - 2]
|
394
|
+
next_child = node.children.last
|
395
|
+
guaranteed_function_call = is_guaranteed_function_call?(node, child, next_child)
|
396
|
+
do_function_check = !guaranteed_function_call && (dont_emit_function_check || false) == false && !is_assignment && !op && !first_child.nil? && (first_child.type == :lvar || is_send) && !(is_block && block_method == node.children[1])
|
397
|
+
|
398
|
+
if is_op?(next_child.to_s) && next_child == node.children.last then
|
399
|
+
walk_ast(next_child)
|
400
|
+
end
|
401
|
+
if do_function_check then
|
402
|
+
current_line = @output.split("\n")[@line]
|
403
|
+
if current_line.nil? && is_block.nil? then
|
404
|
+
write("local _ = ")
|
405
|
+
end
|
406
|
+
write("(type(")
|
407
|
+
end
|
408
|
+
|
409
|
+
is_aliased_method, aliased_methods = *has_aliased_method?(node, guaranteed_function_call, do_function_check)
|
410
|
+
if is_aliased_method then
|
411
|
+
aliased_methods.each { |a| handle_aliased_prefix(a, first_child, block_args) }
|
412
|
+
end
|
413
|
+
|
414
|
+
if !is_aliased_method || child == :puts then
|
415
|
+
idx = 1
|
416
|
+
node.children.each do |child|
|
417
|
+
next_child = node.children[idx] # 1 based
|
418
|
+
last_child = node.children[idx - 2]
|
419
|
+
guaranteed_function_call = (is_guaranteed_function_call?(node, child, next_child) || ((!is_block.nil? || is_block) && block_method == child)) || false
|
420
|
+
|
421
|
+
if child.is_a?(Parser::AST::Node) then
|
422
|
+
handle_send_child(node, child, idx, guaranteed_function_call, *extra_data)
|
423
|
+
elsif child == :puts then
|
424
|
+
args = [*node.children]
|
425
|
+
args.shift(2)
|
426
|
+
write("print(")
|
427
|
+
args.each do |a|
|
428
|
+
walk_ast(a, true)
|
429
|
+
if a != args.last then
|
430
|
+
write(", ")
|
431
|
+
end
|
432
|
+
end
|
433
|
+
write(")")
|
434
|
+
break
|
435
|
+
elsif child == :new then
|
436
|
+
write(".new(")
|
437
|
+
else
|
438
|
+
next if node.children[1] == :puts
|
439
|
+
is_var = !node.nil? && node.children[1].is_a?(Symbol) && !self.is_op?(node.children[1].to_s)
|
440
|
+
obj_asgn = !first_child.nil? && (first_child.type == :lvar || first_child.type == :send)
|
441
|
+
write_dot = nil
|
442
|
+
|
443
|
+
sym = child.to_s
|
444
|
+
str = check_operator(sym)
|
445
|
+
if guaranteed_function_call then
|
446
|
+
str.gsub!("?", "")
|
447
|
+
str.gsub!("!", "")
|
448
|
+
end
|
449
|
+
|
450
|
+
is_aliased_method, aliased_methods = *has_aliased_method?(child, guaranteed_function_call, do_function_check)
|
451
|
+
if is_var && obj_asgn && !is_aliased_method then
|
452
|
+
write_dot = (do_function_check || (not_function || false) == true || is_assignment) || (!guaranteed_function_call && !(next_child.nil? && not_function == false))
|
453
|
+
write(write_dot ? "." : ":")
|
454
|
+
end
|
455
|
+
unless is_aliased_method then
|
456
|
+
write(str)
|
457
|
+
end
|
458
|
+
if (is_var && obj_asgn && !do_function_check && !not_function && !guaranteed_function_call && !is_assignment && !op && !write_dot) then
|
459
|
+
args = [*node.children]
|
460
|
+
args.shift(2)
|
461
|
+
write("(#{args.join(", ")})")
|
462
|
+
end
|
463
|
+
if guaranteed_function_call && !write_dot && (is_block.nil? || !is_block) then
|
464
|
+
write("(")
|
465
|
+
self.walk_ast(next_child)
|
466
|
+
write(")")
|
467
|
+
break
|
468
|
+
end
|
469
|
+
end
|
470
|
+
idx += 1
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
if do_function_check then
|
475
|
+
write(") == \"function\" and ")
|
476
|
+
walk_ast(node, true, false)
|
477
|
+
write(" or ")
|
478
|
+
walk_ast(node, true, true)
|
479
|
+
write(")")
|
480
|
+
end
|
481
|
+
if node.children[1] == :new then
|
482
|
+
write(")")
|
483
|
+
end
|
484
|
+
if (is_block == false || is_block.nil?) && is_aliased_method && !is_send then
|
485
|
+
aliased_methods.each { |a| handle_aliased_suffix(a, child) }
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
def get_symbols_in(node)
|
490
|
+
if node.nil? then return [] end
|
491
|
+
descendants = []
|
492
|
+
if node.is_a?(Parser::AST::Node) then
|
493
|
+
node.children.each do |child|
|
494
|
+
if child.is_a?(Parser::AST::Node) && child.children.length > 0 then
|
495
|
+
descendants.push(*get_symbols_in(child))
|
496
|
+
else
|
497
|
+
descendants.push(child)
|
498
|
+
end
|
499
|
+
end
|
500
|
+
else
|
501
|
+
descendants.push(node)
|
502
|
+
end
|
503
|
+
descendants
|
504
|
+
end
|
505
|
+
|
506
|
+
def has_aliased_method?(node, guaranteed_function_call = false, do_function_check = false)
|
507
|
+
symbols = get_symbols_in(node)
|
508
|
+
|
509
|
+
aliased_methods = []
|
510
|
+
symbols.each do |sym|
|
511
|
+
is_aliased_method = @method_aliases.include?(sym)
|
512
|
+
if is_aliased_method then
|
513
|
+
aliased_methods.push(sym)
|
514
|
+
end
|
515
|
+
end
|
516
|
+
|
517
|
+
if aliased_methods.length > 0 && (guaranteed_function_call || !do_function_check) then
|
518
|
+
return true, aliased_methods.uniq
|
519
|
+
else
|
520
|
+
return false
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
def handle_send_child(node, child, idx, guaranteed_function_call, *extra_data)
|
525
|
+
next_child = node.children[idx] # idx is 1 based, not 0 based
|
526
|
+
last_child = node.children[idx - 2]
|
527
|
+
op = is_op?(node.children[1].to_s)
|
528
|
+
|
529
|
+
case child.type
|
530
|
+
when :str
|
531
|
+
walk_ast(child)
|
532
|
+
write(node.children.last != child ? ", " : "")
|
533
|
+
when :int, :float, :true, :false, :send, :lvar
|
534
|
+
walk_ast(child, *extra_data)
|
535
|
+
when :ivar
|
536
|
+
var_name = child.children[0].to_s.gsub("@", "")
|
537
|
+
readers, writers, accessors = *extra_data
|
538
|
+
readers.map! { |n| (n.is_a?(String) ? n : n.children[2].children[0]).to_s }
|
539
|
+
writers.map! { |n| (n.is_a?(String) ? n : n.children[2].children[0]).to_s }
|
540
|
+
accessors.map! { |n| (n.is_a?(String) ? n : n.children[2].children[0]).to_s }
|
541
|
+
location = self.get_v_location_name(var_name, readers, writers, accessors) || "private."
|
542
|
+
write(location + var_name)
|
543
|
+
when :const
|
544
|
+
walk_ast(child)
|
545
|
+
if next_child.is_a?(Symbol) && next_child != :new then
|
546
|
+
write(".")
|
547
|
+
end
|
548
|
+
when :begin
|
549
|
+
handle_send_child(child, child.children[0], idx, last_child == :puts)
|
550
|
+
when :block_pass
|
551
|
+
|
552
|
+
else
|
553
|
+
walk_ast(child, *extra_data)
|
554
|
+
# var_name = child.children[0].to_s.strip
|
555
|
+
# write(var_name)
|
556
|
+
end
|
557
|
+
end
|
558
|
+
|
559
|
+
def is_assignment?(node)
|
560
|
+
node.children[1].to_s.include?("=") || node.type == :lvasgn || node.type == :gvasgn
|
561
|
+
end
|
562
|
+
|
563
|
+
def primary_privates(inited_privates)
|
564
|
+
inited_privates.each do |var|
|
565
|
+
instancevar_assign(var)
|
566
|
+
unless var == inited_privates.last then
|
567
|
+
self.newline
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def class_initializer(class_name, class_block, initializer = nil, parent = nil, readers = nil, writers = nil, accessors = nil, inited_privates = nil)
|
573
|
+
if initializer.nil? then
|
574
|
+
writeln(")")
|
575
|
+
end
|
576
|
+
|
577
|
+
args = []
|
578
|
+
unless initializer.nil? then
|
579
|
+
args = initializer.children.map do |child|
|
580
|
+
res = []
|
581
|
+
if child.is_a?(Parser::AST::Node) && child.type == :super
|
582
|
+
vals = child.children.map do |a|
|
583
|
+
sym = a.type == :str ? self.quote_surround(a.children[0]) : a.children[0]
|
584
|
+
end
|
585
|
+
vals.each { |c| res.push(c) }
|
586
|
+
res
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
if args.length > 0 then
|
592
|
+
self.newline
|
593
|
+
end
|
594
|
+
|
595
|
+
mixins = class_block.children.filter { |child| child.is_a?(Parser::AST::Node) && child.children[1] == :include }.map { |mixin| mixin.children[2].children[1].to_s }
|
596
|
+
writeln("local include = {#{mixins.join(", ")}}")
|
597
|
+
base_table = parent.nil? ? "{}" : "#{parent.children[1]}.new(#{args.compact.join(", ")})"
|
598
|
+
writeln("local idxMeta = setmetatable(#{class_name}, { __index = #{base_table} })")
|
599
|
+
writeln("idxMeta.__type = \"#{class_name}\"")
|
600
|
+
|
601
|
+
write("for ")
|
602
|
+
write(@debug_mode ? "_, " : "")
|
603
|
+
write("mixin in ")
|
604
|
+
write(@debug_mode ? "pairs" : "ruby.list")
|
605
|
+
write("(include) do")
|
606
|
+
@block -= 1
|
607
|
+
self.newline
|
608
|
+
@block += 1
|
609
|
+
self.block
|
610
|
+
write("for k, v in pairs(mixin) do")
|
611
|
+
@block -= 2
|
612
|
+
self.newline
|
613
|
+
@block += 2
|
614
|
+
self.block
|
615
|
+
write("idxMeta[k] = v")
|
616
|
+
self.end
|
617
|
+
self.end
|
618
|
+
self.newline
|
619
|
+
|
620
|
+
|
621
|
+
writeln("local self = setmetatable({}, { __index = idxMeta })")
|
622
|
+
writeln("self.attr_accessor = setmetatable({}, { __index = idxMeta.attr_accessor or {} })")
|
623
|
+
writeln("self.attr_reader = setmetatable({}, { __index = idxMeta.attr_reader or {} })")
|
624
|
+
writeln("self.attr_writer = setmetatable({}, { __index = idxMeta.attr_writer or {} })")
|
625
|
+
writeln("self.writable = {}")
|
626
|
+
writeln("self.private = {}")
|
627
|
+
self.newline
|
628
|
+
|
629
|
+
primary_privates(inited_privates)
|
630
|
+
unless initializer.nil? then
|
631
|
+
initializer_block = initializer.children[2]
|
632
|
+
walk_ast(initializer_block, readers, writers, accessors)
|
633
|
+
end
|
634
|
+
|
635
|
+
self.newline
|
636
|
+
self.newline
|
637
|
+
writeln("return setmetatable(self, {")
|
638
|
+
@block -= 1
|
639
|
+
self.block
|
640
|
+
writeln("__index = function(t, k)")
|
641
|
+
self.block
|
642
|
+
writeln("if not self.attr_reader[k] and not self.attr_accessor[k] and self.private[k] then")
|
643
|
+
@block -= 1
|
644
|
+
self.block
|
645
|
+
@block += 2
|
646
|
+
write("return nil")
|
647
|
+
self.end
|
648
|
+
self.newline
|
649
|
+
write("return self.attr_reader[k] or self.attr_accessor[k] or #{class_name}[k]")
|
650
|
+
self.end
|
651
|
+
writeln(",")
|
652
|
+
|
653
|
+
writeln("__newindex = function(t, k, v)")
|
654
|
+
@block -= 2
|
655
|
+
self.block
|
656
|
+
writeln("if t.writable[k] or self.writable[k] or idxMeta.writable[k] then")
|
657
|
+
@block += 1
|
658
|
+
self.block
|
659
|
+
@block -= 1
|
660
|
+
writeln("if self.attr_writer[k] then")
|
661
|
+
self.block
|
662
|
+
@block += 1
|
663
|
+
writeln("self.attr_writer[k] = v")
|
664
|
+
write("elseif self.attr_accessor[k] then")
|
665
|
+
@block += 1
|
666
|
+
self.newline
|
667
|
+
write("self.attr_accessor[k] = v")
|
668
|
+
self.end
|
669
|
+
@block -= 1
|
670
|
+
self.newline
|
671
|
+
|
672
|
+
write("else")
|
673
|
+
@block += 1
|
674
|
+
self.newline
|
675
|
+
write("error(\"Attempt to write to un-writable attribute '\"..k..\"'\")")
|
676
|
+
self.end
|
677
|
+
self.end
|
678
|
+
@block -= 1
|
679
|
+
|
680
|
+
self.newline
|
681
|
+
write("})")
|
682
|
+
|
683
|
+
self.end
|
684
|
+
@block -= 1
|
685
|
+
end
|
686
|
+
|
687
|
+
def get_class_initer_def(block)
|
688
|
+
initializer = block.children.filter { |def_node| def_node.type == :def && def_node.children[0] == :initialize }[0]
|
689
|
+
if initializer && initializer.children[0] == :initialize then
|
690
|
+
initializer_args = initializer.children[1]
|
691
|
+
arg_list = initializer_args.children.map { |arg| arg.children[0] }.join(", ")
|
692
|
+
write("#{arg_list})")
|
693
|
+
end
|
694
|
+
initializer
|
695
|
+
end
|
696
|
+
|
697
|
+
def class_def(node)
|
698
|
+
class_name = node.children[0].children[1].to_s
|
699
|
+
parent = node.children[1]
|
700
|
+
block = node.children[2]
|
701
|
+
|
702
|
+
write("--classdef")
|
703
|
+
self.newline
|
704
|
+
write("local #{class_name} = {} do")
|
705
|
+
self.newline
|
706
|
+
@block += 1
|
707
|
+
self.block
|
708
|
+
@block -= 2
|
709
|
+
self.block
|
710
|
+
|
711
|
+
stmts = block.children.filter { |stmt| stmt.type == :cvasgn }
|
712
|
+
stmts.each do |stmt|
|
713
|
+
walk_ast(stmt, class_name)
|
714
|
+
self.newline
|
715
|
+
end
|
716
|
+
|
717
|
+
added_initializer = false
|
718
|
+
write("function #{class_name}.new(")
|
719
|
+
@block += 1
|
720
|
+
|
721
|
+
unless block.nil? then
|
722
|
+
unless block.type == :begin || added_initializer then
|
723
|
+
class_initializer(class_name, block, nil, parent)
|
724
|
+
added_initializer = true
|
725
|
+
end
|
726
|
+
case block.type
|
727
|
+
when :begin
|
728
|
+
unless added_initializer then
|
729
|
+
readers = block.children.filter { |stmt| stmt.is_a?(Parser::AST::Node) && stmt.children[1] == :attr_reader }.map { |stmt| stmt.children[2].children[0].to_s }
|
730
|
+
writers = block.children.filter { |stmt| stmt.is_a?(Parser::AST::Node) && stmt.children[1] == :attr_writer }.map { |stmt| stmt.children[2].children[0].to_s }
|
731
|
+
accessors = block.children.filter { |stmt| stmt.is_a?(Parser::AST::Node) && stmt.children[1] == :attr_accessor }
|
732
|
+
inited_privates = block.children.filter { |stmt| stmt.is_a?(Parser::AST::Node) && stmt.type == :ivasgn }
|
733
|
+
initializer = get_class_initer_def(block)
|
734
|
+
|
735
|
+
class_initializer(class_name, block, initializer, parent, readers, writers, accessors, inited_privates)
|
736
|
+
added_initializer = true
|
737
|
+
end
|
738
|
+
when :send
|
739
|
+
|
740
|
+
when :cvasgn
|
741
|
+
walk_ast(node, class_name)
|
742
|
+
end
|
743
|
+
else
|
744
|
+
class_initializer(class_name, block, nil, parent)
|
745
|
+
end
|
746
|
+
|
747
|
+
@block += 1
|
748
|
+
self.end
|
749
|
+
end
|
750
|
+
|
751
|
+
def module_def(node)
|
752
|
+
module_name = node.children[0].children[1].to_s
|
753
|
+
block = node.children[1]
|
754
|
+
|
755
|
+
writeln("--moduledef")
|
756
|
+
writeln("local #{module_name} = {} do")
|
757
|
+
@block += 1
|
758
|
+
self.block
|
759
|
+
@block -= 1
|
760
|
+
|
761
|
+
unless block.nil? then
|
762
|
+
_, args, def_block = *block.children
|
763
|
+
new_block = block.updated(nil, ["#{module_name}:#{block.children[0].to_s}".to_sym, args, def_block])
|
764
|
+
walk_ast(new_block)
|
765
|
+
end
|
766
|
+
|
767
|
+
self.end
|
768
|
+
end
|
769
|
+
|
770
|
+
def quote_surround(s)
|
771
|
+
"\"#{s}\""
|
772
|
+
end
|
773
|
+
|
774
|
+
def instancevar_assign(node, location = nil)
|
775
|
+
name_node, val_node = *node.children
|
776
|
+
key = name_node.to_s.gsub("@", "")
|
777
|
+
|
778
|
+
write((location || "self.private.") + "#{key} = ")
|
779
|
+
walk_ast(val_node)
|
780
|
+
if location == "self.attr_accessor." || location == "self.attr_writer." then
|
781
|
+
self.newline
|
782
|
+
write("self.writable.#{key} = true")
|
783
|
+
end
|
784
|
+
end
|
785
|
+
|
786
|
+
def multiple_assign(node)
|
787
|
+
names_node, vals_node = *node.children
|
788
|
+
|
789
|
+
added_keyword = false
|
790
|
+
names_node.children.each do |name|
|
791
|
+
if name.type == :lvasgn && !added_keyword then
|
792
|
+
added_keyword = true
|
793
|
+
write("local ")
|
794
|
+
end
|
795
|
+
var_name = name.children[0].to_s
|
796
|
+
write((name.type == :gvasgn ? var_name.gsub!("$", "") : var_name) + (name == names_node.children.last ? "" : ", "))
|
797
|
+
end
|
798
|
+
write(" = ")
|
799
|
+
|
800
|
+
vals_node.children.each do |val|
|
801
|
+
write(val.children[0].to_s + (val == vals_node.children.last ? "" : ", "))
|
802
|
+
end
|
803
|
+
end
|
804
|
+
|
805
|
+
def get_v_location_name(var_name, readers, writers, accessors)
|
806
|
+
if !readers.nil? && readers.include?(var_name) then
|
807
|
+
"self.attr_reader."
|
808
|
+
elsif !writers.nil? && writers.include?(var_name) then
|
809
|
+
"self.attr_writer."
|
810
|
+
elsif !accessors.nil? && accessors.include?(var_name) then
|
811
|
+
"self.attr_accessor."
|
812
|
+
end
|
813
|
+
end
|
814
|
+
|
815
|
+
def is_op?(str)
|
816
|
+
operators = %w(+ - * / += -= *= /= %= **= % ** & | ^ > >= < <= == === != =~ !~ && || =)
|
817
|
+
operators.include?(str.strip) || is_unary_op?(str)
|
818
|
+
end
|
819
|
+
|
820
|
+
def is_unary_op?(str)
|
821
|
+
operators = %w(- !)
|
822
|
+
operators.include?(str.strip)
|
823
|
+
end
|
824
|
+
|
825
|
+
def check_operator(str)
|
826
|
+
if is_op?(str)
|
827
|
+
if str == "=~" || str == "!~" || str == "^" || str == "&" || str == "|" then
|
828
|
+
raise Exceptions::UnsupportedBitOpError.new
|
829
|
+
end
|
830
|
+
"#{is_unary_op?(str) ? "" : " "}#{str.gsub("!=", "~=").gsub("**", "^").gsub("===", "==").gsub("&&", "and").gsub("||", "or").gsub("!", "not")} "
|
831
|
+
else
|
832
|
+
str.gsub("=", " = ")
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
def index_var(node)
|
837
|
+
write(node.children[0].to_s.strip)
|
838
|
+
end
|
839
|
+
end
|
840
|
+
end
|