rlang 0.3.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/.gitignore +10 -0
- data/.rake_tasks~ +0 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE +373 -0
- data/README.md +61 -0
- data/Rakefile +10 -0
- data/bin/rlang +164 -0
- data/docs/RlangCompiler.md +37 -0
- data/docs/RlangManual.md +391 -0
- data/lib/builder/ext/tempfile.rb +7 -0
- data/lib/builder/ext.rb +5 -0
- data/lib/builder/rlang/builder.rb +31 -0
- data/lib/builder/rlang.rb +2 -0
- data/lib/builder/wat/builder.rb +52 -0
- data/lib/builder/wat/renderer.rb +28 -0
- data/lib/builder/wat.rb +3 -0
- data/lib/builder.rb +7 -0
- data/lib/rlang/lib/malloc.c +97 -0
- data/lib/rlang/lib/malloc.rb +169 -0
- data/lib/rlang/lib/memory.rb +11 -0
- data/lib/rlang/lib/type/i32.rb +7 -0
- data/lib/rlang/lib/type/i64.rb +7 -0
- data/lib/rlang/lib/type.rb +6 -0
- data/lib/rlang/lib/unistd.rb +47 -0
- data/lib/rlang/lib.rb +10 -0
- data/lib/rlang/parser/const.rb +15 -0
- data/lib/rlang/parser/cvar.rb +44 -0
- data/lib/rlang/parser/data.rb +105 -0
- data/lib/rlang/parser/export.rb +22 -0
- data/lib/rlang/parser/ext/integer.rb +5 -0
- data/lib/rlang/parser/ext/string.rb +5 -0
- data/lib/rlang/parser/ext/type.rb +64 -0
- data/lib/rlang/parser/global.rb +65 -0
- data/lib/rlang/parser/lvar.rb +29 -0
- data/lib/rlang/parser/marg.rb +30 -0
- data/lib/rlang/parser/method.rb +76 -0
- data/lib/rlang/parser/wattr.rb +65 -0
- data/lib/rlang/parser/wgenerator.rb +509 -0
- data/lib/rlang/parser/winstruction.rb +148 -0
- data/lib/rlang/parser/wnode.rb +455 -0
- data/lib/rlang/parser/wtree.rb +19 -0
- data/lib/rlang/parser/wtype.rb +116 -0
- data/lib/rlang/parser.rb +1842 -0
- data/lib/rlang/version.rb +3 -0
- data/lib/rlang.rb +4 -0
- data/lib/simul/classes/data.rb +80 -0
- data/lib/simul/classes/global.rb +38 -0
- data/lib/simul/classes/memory.rb +131 -0
- data/lib/utils/log.rb +32 -0
- data/rlang.gemspec +38 -0
- metadata +158 -0
data/lib/rlang/parser.rb
ADDED
@@ -0,0 +1,1842 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
|
5
|
+
# Rlang parser
|
6
|
+
# Rlang is a subset of the Ruby language that can be transpiled
|
7
|
+
# to WAT and then compiled to WASM. The Rubinius WASM virtual
|
8
|
+
# machine is written in Rlang.
|
9
|
+
|
10
|
+
# TODO: write a short documentation about what subset of Ruby is
|
11
|
+
# supported in Rlang
|
12
|
+
|
13
|
+
require 'parser/current'
|
14
|
+
require 'pathname'
|
15
|
+
require_relative '../utils/log'
|
16
|
+
require_relative './parser/wtype'
|
17
|
+
require_relative './parser/wtree'
|
18
|
+
require_relative './parser/wnode'
|
19
|
+
require_relative './parser/cvar'
|
20
|
+
require_relative './parser/lvar'
|
21
|
+
require_relative './parser/marg'
|
22
|
+
require_relative './parser/global'
|
23
|
+
require_relative './parser/data'
|
24
|
+
require_relative './parser/wgenerator'
|
25
|
+
|
26
|
+
module Rlang::Parser
|
27
|
+
class Parser
|
28
|
+
|
29
|
+
include Log
|
30
|
+
|
31
|
+
ARITHMETIC_OPS = [:+, :-, :*, :/, :%, :&, :|, :^, :>>, :<<]
|
32
|
+
RELATIONAL_OPS = [:==, :!=, :>, :<, :>=, :<=, :'>s', :'<s', :'>=s', :'>=s']
|
33
|
+
UNARY_OPS = [:'!']
|
34
|
+
|
35
|
+
# Type cast order in decreading order of precedence
|
36
|
+
TYPE_CAST_PRECEDENCE = [Type::F64, Type::F32, Type::I64, Type::I32]
|
37
|
+
|
38
|
+
# WARNING!! THIS IS A **VERY** NASTY HACK PRETENDING
|
39
|
+
# THAT THIS int VALUE means NIL. It's totally unsafe
|
40
|
+
# of course as an expression could end up evaluating
|
41
|
+
# to this value and not be nil at all. But I'm using
|
42
|
+
# it for now in the xxxx_with_result_type variants of
|
43
|
+
# some parsing methods (if, while,...)
|
44
|
+
NIL = 999999999
|
45
|
+
|
46
|
+
# export toggle for method declaration
|
47
|
+
@@export = false
|
48
|
+
|
49
|
+
|
50
|
+
attr_accessor :wgenerator, :source, :config
|
51
|
+
|
52
|
+
def initialize(wgenerator)
|
53
|
+
@wgenerator = wgenerator
|
54
|
+
# LIFO of parsed files (stacked by require)
|
55
|
+
@requires = []
|
56
|
+
config_init
|
57
|
+
end
|
58
|
+
|
59
|
+
def config_init
|
60
|
+
@config = {}
|
61
|
+
@config[:LOADED_FEATURES] = []
|
62
|
+
@config[:LOAD_PATH] = ''
|
63
|
+
@config[:__FILE__] = ''
|
64
|
+
end
|
65
|
+
|
66
|
+
# Note : this method can be called recursively
|
67
|
+
# through require statements
|
68
|
+
def parse_file(file)
|
69
|
+
raise "parse_file only acccepts absolute path (got #{file})" \
|
70
|
+
unless Pathname.new(file).absolute? || file.nil?
|
71
|
+
# Already parsed. Ignore.
|
72
|
+
if self.config[:LOADED_FEATURES].include? file
|
73
|
+
logger.debug "File already loaded."
|
74
|
+
return
|
75
|
+
end
|
76
|
+
|
77
|
+
# Set file currently parsed
|
78
|
+
# Maintain a list of embedded require's
|
79
|
+
@requires << (@config[:__FILE__] = file) if file
|
80
|
+
|
81
|
+
# Parse file
|
82
|
+
source = file ? File.open(file) : STDIN
|
83
|
+
self.parse(File.read(source) )
|
84
|
+
# Ff parsing went to completion then add this
|
85
|
+
# file to the list of successfully loaded files
|
86
|
+
(@config[:LOADED_FEATURES] ||= []) << file if file
|
87
|
+
# and go back to previously parsed file
|
88
|
+
if file
|
89
|
+
@requires.pop
|
90
|
+
@config[:__FILE__] = @requires.last
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def parse(source)
|
95
|
+
ast = ::Parser::CurrentRuby.parse(source)
|
96
|
+
parse_node(ast, @wgenerator.root) if ast
|
97
|
+
end
|
98
|
+
|
99
|
+
# Parse Ruby AST node and generate WAT
|
100
|
+
# code as a child of wnode
|
101
|
+
# - node: the Ruby AST node to parse
|
102
|
+
# - wnode: the parent wnode for the generated WAT code
|
103
|
+
# - keep_eval: whether to keep the value of the evaluated
|
104
|
+
# WAT expression on stock or not
|
105
|
+
def parse_node(node, wnode, keep_eval=true)
|
106
|
+
logger.debug "\n---------------------->>\n" +
|
107
|
+
"Parsing node: #{node}, wnode: #{wnode}, keep_eval: #{keep_eval}"
|
108
|
+
|
109
|
+
case node.type
|
110
|
+
when :self
|
111
|
+
wn = parse_self(node, wnode)
|
112
|
+
|
113
|
+
when :class
|
114
|
+
wn = parse_class(node, wnode)
|
115
|
+
|
116
|
+
when :defs
|
117
|
+
wn = parse_defs(node, wnode, keep_eval)
|
118
|
+
|
119
|
+
when :def
|
120
|
+
wn = parse_def(node, wnode, keep_eval)
|
121
|
+
|
122
|
+
when :begin
|
123
|
+
wn = parse_begin(node, wnode, keep_eval)
|
124
|
+
|
125
|
+
when :casgn
|
126
|
+
wn = parse_casgn(node, wnode, keep_eval)
|
127
|
+
|
128
|
+
when :cvasgn
|
129
|
+
wn = parse_cvasgn(node, wnode, keep_eval)
|
130
|
+
|
131
|
+
when :gvasgn
|
132
|
+
wn = parse_gvasgn(node, wnode, keep_eval)
|
133
|
+
|
134
|
+
when :lvasgn
|
135
|
+
wn = parse_lvasgn(node, wnode, keep_eval)
|
136
|
+
|
137
|
+
when :op_asgn
|
138
|
+
wn = parse_op_asgn(node, wnode, keep_eval)
|
139
|
+
|
140
|
+
when :lvar
|
141
|
+
wn = parse_lvar(node, wnode, keep_eval)
|
142
|
+
|
143
|
+
when :ivar, :ivasgn
|
144
|
+
raise "Instance variable not supported"
|
145
|
+
|
146
|
+
when :cvar
|
147
|
+
wn = parse_cvar(node, wnode, keep_eval)
|
148
|
+
|
149
|
+
when :gvar
|
150
|
+
wn = parse_gvar(node, wnode, keep_eval)
|
151
|
+
|
152
|
+
when :int
|
153
|
+
wn = parse_int(node, wnode, keep_eval)
|
154
|
+
|
155
|
+
when :float
|
156
|
+
raise "float instructions not supported"
|
157
|
+
#parse_float(node, wnode, keep_eval)
|
158
|
+
|
159
|
+
when :nil
|
160
|
+
raise "nil not supported"
|
161
|
+
|
162
|
+
when :const
|
163
|
+
wn = parse_const(node, wnode, keep_eval)
|
164
|
+
|
165
|
+
when :send
|
166
|
+
wn = parse_send(node, wnode, keep_eval)
|
167
|
+
|
168
|
+
when :return
|
169
|
+
wn = parse_return(node, wnode, keep_eval)
|
170
|
+
|
171
|
+
when :if
|
172
|
+
#parse_if_with_result_type(node, wnode, keep_eval)
|
173
|
+
wn = parse_if_without_result_type(node, wnode, keep_eval)
|
174
|
+
|
175
|
+
when :while
|
176
|
+
#parse_while_with_result_type(node, wnode, keep_eval)
|
177
|
+
wn = parse_while_without_result_type(node, wnode, keep_eval)
|
178
|
+
|
179
|
+
when :until
|
180
|
+
#parse_while_with_result_type(node, wnode, keep_eval)
|
181
|
+
wn = parse_while_without_result_type(node, wnode, keep_eval)
|
182
|
+
|
183
|
+
when :break
|
184
|
+
wn = parse_break(node, wnode, keep_eval)
|
185
|
+
|
186
|
+
when :next
|
187
|
+
wn = parse_next(node, wnode, keep_eval)
|
188
|
+
|
189
|
+
when :or, :and
|
190
|
+
wn = parse_logical_op(node, wnode, keep_eval)
|
191
|
+
|
192
|
+
when :true
|
193
|
+
wn = parse_true(node, wnode, keep_eval)
|
194
|
+
|
195
|
+
when :false
|
196
|
+
wn = parse_false(node, wnode, keep_eval)
|
197
|
+
|
198
|
+
else
|
199
|
+
raise "Unknown node type: #{node.type} => #{node}"
|
200
|
+
end
|
201
|
+
logger.debug "\n----------------------<<\n" +
|
202
|
+
"End parsing node: #{node}, parent wnode: #{wnode}, keep_eval: #{keep_eval}\n generated wnode #{wn}" +
|
203
|
+
"\n----------------------<<\n"
|
204
|
+
wn
|
205
|
+
end
|
206
|
+
|
207
|
+
def parse_begin(node, wnode, keep_eval)
|
208
|
+
child_count = node.children.count
|
209
|
+
logger.debug "child count: #{child_count}"
|
210
|
+
wn = nil
|
211
|
+
node.children.each_with_index do |n, idx|
|
212
|
+
logger.debug "processing begin node ##{idx}..."
|
213
|
+
# A begin block always evaluates to the value of
|
214
|
+
# its **last** child except
|
215
|
+
if (idx == child_count-1)
|
216
|
+
local_keep_eval = keep_eval
|
217
|
+
else
|
218
|
+
local_keep_eval = false
|
219
|
+
end
|
220
|
+
logger.debug "node idx: #{idx}/#{child_count-1}, wnode type: #{wnode.type}, keep_eval: #{keep_eval}, local_keep_eval: #{local_keep_eval}"
|
221
|
+
wn = parse_node(n, wnode, local_keep_eval)
|
222
|
+
logger.debug "in begin: parsing node #{n} gives wnode #{wn}"
|
223
|
+
end
|
224
|
+
return wn # return last wnode
|
225
|
+
end
|
226
|
+
|
227
|
+
# Example:
|
228
|
+
# class Stack
|
229
|
+
# ... body ...
|
230
|
+
# end
|
231
|
+
# -----
|
232
|
+
# [s(:const, nil, :Stack), nil, s(:begin ... body.... )]
|
233
|
+
#
|
234
|
+
def parse_class(node, wnode)
|
235
|
+
const_node = node.children.first
|
236
|
+
body_node = node.children.last
|
237
|
+
raise "expecting a constant for class name (got #{const_node})" \
|
238
|
+
unless const_node.type == :const
|
239
|
+
|
240
|
+
wn_class = @wgenerator.klass(wnode, const_node.children.last)
|
241
|
+
parse_node(body_node, wn_class) if body_node
|
242
|
+
|
243
|
+
# now that we finished parsing the class node
|
244
|
+
# process the class attributes if any
|
245
|
+
@wgenerator.attributes(wn_class)
|
246
|
+
return wn_class
|
247
|
+
end
|
248
|
+
|
249
|
+
# TODO: the code for op_asgn is quite murky but I thought
|
250
|
+
# we would use quite often so I implemented it. We could do
|
251
|
+
# without it though..
|
252
|
+
#
|
253
|
+
# Example (local var)
|
254
|
+
# arg1 *= 20
|
255
|
+
# ---
|
256
|
+
# (op-asgn
|
257
|
+
# (lvasgn :arg1)
|
258
|
+
# :*
|
259
|
+
# (int 20)) )
|
260
|
+
#
|
261
|
+
# Example (class var)
|
262
|
+
# @@stack_ptr -= nbytes
|
263
|
+
# ---
|
264
|
+
# s(:op_asgn,
|
265
|
+
# s(:cvasgn, :@@stack_ptr), :-, s(:lvar, :nbytes))
|
266
|
+
#
|
267
|
+
# Example (global var)
|
268
|
+
# $MYGLOBAL -= nbytes
|
269
|
+
# ---
|
270
|
+
# s(:op_asgn,
|
271
|
+
# s(:gvasgn, :$MYGLOBAL), :-, s(:lvar, :nbytes))
|
272
|
+
#
|
273
|
+
#
|
274
|
+
# Example (setter/getter)
|
275
|
+
# p.size -= nunits
|
276
|
+
# ---
|
277
|
+
# (op-asgn
|
278
|
+
# (send
|
279
|
+
# (lvar :p) :size) :-
|
280
|
+
# (lvar :nunits))
|
281
|
+
#
|
282
|
+
# **** DEPRECATED FORM OF GLOBALS *****
|
283
|
+
# Example (Global)
|
284
|
+
# Global[:$DEBUG] += 1
|
285
|
+
# ---
|
286
|
+
# (op-asgn
|
287
|
+
# (send
|
288
|
+
# (const nil :Global) :[] (sym :$DEBUG))
|
289
|
+
# :+
|
290
|
+
# (int 1)))
|
291
|
+
#
|
292
|
+
def parse_op_asgn(node, wnode, keep_eval)
|
293
|
+
logger.debug "wnode: #{wnode}, keep_eval: #{keep_eval}"
|
294
|
+
|
295
|
+
case node.children.first.type
|
296
|
+
# Global variable case
|
297
|
+
when :gvasgn
|
298
|
+
var_asgn_node, op, exp_node = *node.children
|
299
|
+
var_name = var_asgn_node.children.last
|
300
|
+
|
301
|
+
# parse the variable setting part
|
302
|
+
# Note: this will also create the variable setting
|
303
|
+
# wnode as a child of wnode
|
304
|
+
wn_var_set = parse_node(var_asgn_node, wnode, keep_eval)
|
305
|
+
gvar = Global.find(var_name)
|
306
|
+
raise "Unknown global variable #{var_name}" unless gvar
|
307
|
+
|
308
|
+
# Create the operator node (infer operator type from variable)
|
309
|
+
wn_op = @wgenerator.operator(wn_var_set, op, gvar.wtype)
|
310
|
+
# Create the var getter node as a child of operator node
|
311
|
+
wn_var_get = @wgenerator.gvar(wn_op, gvar)
|
312
|
+
|
313
|
+
# Class variable case
|
314
|
+
when :cvasgn
|
315
|
+
var_asgn_node, op, exp_node = *node.children
|
316
|
+
var_name = var_asgn_node.children.last
|
317
|
+
|
318
|
+
# parse the variable setting part
|
319
|
+
# Note: this will also create the variable setting
|
320
|
+
# wnode as a child of wnode
|
321
|
+
wn_var_set = parse_node(var_asgn_node, wnode, keep_eval)
|
322
|
+
cvar = wnode.find_cvar(var_name)
|
323
|
+
raise "Unknown class variable #{var_name}" unless cvar
|
324
|
+
|
325
|
+
# Create the operator node (infer operator type from variable)
|
326
|
+
wn_op = @wgenerator.operator(wn_var_set, op, cvar.wtype)
|
327
|
+
# Create the var getter node as a child of operator node
|
328
|
+
wn_var_get = @wgenerator.cvar(wn_op, cvar)
|
329
|
+
|
330
|
+
# Local variable case
|
331
|
+
when :lvasgn
|
332
|
+
var_asgn_node, op, exp_node = *node.children
|
333
|
+
var_name = var_asgn_node.children.last
|
334
|
+
|
335
|
+
# parse the variable setter node
|
336
|
+
# Note: this will also create the variable setting
|
337
|
+
# wnode as a child of wnode
|
338
|
+
wn_var_set = parse_node(var_asgn_node, wnode, keep_eval)
|
339
|
+
lvar = wnode.find_lvar(var_name) || wnode.find_marg(var_name)
|
340
|
+
raise "Unknown local variable #{var_name}" unless lvar
|
341
|
+
|
342
|
+
# Create the operator node (infer operator type from variable)
|
343
|
+
wn_op = @wgenerator.operator(wn_var_set, op, lvar.wtype)
|
344
|
+
# Create the var getter node as a child of operator node
|
345
|
+
wn_var_get = @wgenerator.lvar(wn_op, lvar)
|
346
|
+
|
347
|
+
# setter/getter case
|
348
|
+
# Example (setter/getter)
|
349
|
+
# p.size -= nunits
|
350
|
+
# ---
|
351
|
+
# (op-asgn
|
352
|
+
# (send
|
353
|
+
# (lvar :p) :size) :-
|
354
|
+
# (lvar :nunits))
|
355
|
+
when :send
|
356
|
+
send_node, op, exp_node = *node.children
|
357
|
+
recv_node, method_name = *send_node.children
|
358
|
+
|
359
|
+
# Parse the receiver node ((lvar :p) in the example)
|
360
|
+
# above to get its wtype
|
361
|
+
# Force keep_eval to true whatever upper level
|
362
|
+
# keep_eval says
|
363
|
+
wn_recv_node = parse_node(recv_node, wnode, true)
|
364
|
+
|
365
|
+
# Create the top level setter call
|
366
|
+
wn_var_set = @wgenerator.call(wnode, wn_recv_node.wtype.name, "#{method_name}=", :instance)
|
367
|
+
|
368
|
+
# First argument of the setter must be the recv_node
|
369
|
+
wn_recv_node.reparent_to(wn_var_set)
|
370
|
+
|
371
|
+
# Second argument of the setter is the operator wnode
|
372
|
+
# Create it with wtype :none for now. We'll fix that
|
373
|
+
# with the operands call later on
|
374
|
+
wn_op = @wgenerator.operator(wn_var_set, op)
|
375
|
+
|
376
|
+
# Parsing the send node will create the getter wnode
|
377
|
+
# this is the first argument of the operator wnode,
|
378
|
+
# the second is wn_exp below
|
379
|
+
# Force keep_eval to true whatever upper level
|
380
|
+
# keep_eval says
|
381
|
+
wn_var_get = parse_node(send_node, wn_op, true)
|
382
|
+
|
383
|
+
# If the setter returns something and last evaluated value
|
384
|
+
# must be ignored then drop it
|
385
|
+
unless keep_eval && !wn_var_set.wtype.blank?
|
386
|
+
@wgenerator.drop(wnode)
|
387
|
+
#@wgenerator.call(wnode, wn_recv_node.wtype.name, "#{method_name}", :instance)
|
388
|
+
end
|
389
|
+
else
|
390
|
+
raise "op_asgn not supported for #{node.children.first}"
|
391
|
+
end
|
392
|
+
|
393
|
+
# Finally, parse the expression node and make it
|
394
|
+
# the second child of the operator node
|
395
|
+
# Last evaluated value must be kept of course
|
396
|
+
wn_exp = parse_node(exp_node, wn_op, true)
|
397
|
+
|
398
|
+
# And process operands (cast and such...)
|
399
|
+
@wgenerator.operands(wn_op, wn_var_get, [wn_exp])
|
400
|
+
|
401
|
+
return wn_var_set
|
402
|
+
end
|
403
|
+
|
404
|
+
# Example
|
405
|
+
# MYCONST = 2000
|
406
|
+
# ---
|
407
|
+
# (casgn nil :MYCONST
|
408
|
+
# (int 2000))
|
409
|
+
def parse_casgn(node, wnode, keep_eval)
|
410
|
+
class_name_node, constant_name, exp_node = *node.children
|
411
|
+
raise "dynamic constant assignment" unless wnode.in_class_scope?
|
412
|
+
raise "constant initialization can only take a number" unless exp_node.type == :int
|
413
|
+
|
414
|
+
unless class_name_node.nil?
|
415
|
+
raise "constant assignment with class path not supported (got #{class_name_node})"
|
416
|
+
end
|
417
|
+
if wnode.find_const(constant_name)
|
418
|
+
raise "constant #{constant_name} already initialized"
|
419
|
+
end
|
420
|
+
|
421
|
+
# TODO: const are I32 hardcoded for now. Must find a way to
|
422
|
+
# initialize I64 constant too
|
423
|
+
value = exp_node.children.last
|
424
|
+
const = wnode.create_const(constant_name, nil, value, WType::DEFAULT)
|
425
|
+
|
426
|
+
wn_casgn = @wgenerator.casgn(wnode, const)
|
427
|
+
return wn_casgn
|
428
|
+
end
|
429
|
+
|
430
|
+
# Example
|
431
|
+
# $MYGLOBAL = 2000
|
432
|
+
# ---
|
433
|
+
# (gvasgn :MYGLOBAL
|
434
|
+
# (int 2000))
|
435
|
+
#
|
436
|
+
# Example with type cast
|
437
|
+
# $MYGLOBAL = 2000.to_I64
|
438
|
+
# ---
|
439
|
+
# (gvasgn :MYGLOBAL
|
440
|
+
# (send (int 2000) :to_I64))
|
441
|
+
def parse_gvasgn(node, wnode, keep_eval)
|
442
|
+
gv_name, exp_node = *node.children
|
443
|
+
gvar = Global.find(gv_name)
|
444
|
+
|
445
|
+
if wnode.in_method_scope?
|
446
|
+
# if exp_node is nil then this is the form of
|
447
|
+
# :gvasgn that comes from op_asgn
|
448
|
+
if exp_node
|
449
|
+
if gvar.nil?
|
450
|
+
# first gvar occurence
|
451
|
+
# type cast the gvar to the wtype of the expression
|
452
|
+
gvar = Global.new(gv_name)
|
453
|
+
# Do not export global for now
|
454
|
+
#gvar.export! if self.config[:export_all]
|
455
|
+
wn_gvasgn = @wgenerator.gvasgn(wnode, gvar)
|
456
|
+
wn_exp = parse_node(exp_node, wn_gvasgn)
|
457
|
+
gvar.wtype = wn_exp.wtype
|
458
|
+
else
|
459
|
+
# if gvar already exists then type cast the
|
460
|
+
# expression to the wtype of the existing gvar
|
461
|
+
wn_gvasgn = @wgenerator.gvasgn(wnode, gvar)
|
462
|
+
wn_exp = parse_node(exp_node, wn_gvasgn)
|
463
|
+
@wgenerator.cast(wn_exp, gvar.wtype, false)
|
464
|
+
end
|
465
|
+
else
|
466
|
+
raise "Global variable #{cv_name} not declared before" unless gvar
|
467
|
+
wn_gvasgn = @wgenerator.gvasgn(wnode, gvar)
|
468
|
+
end
|
469
|
+
# to mimic Ruby push the variable value on stack if needed
|
470
|
+
@wgenerator.gvar(wnode, gvar) if keep_eval
|
471
|
+
return wn_gvasgn
|
472
|
+
else
|
473
|
+
# If we are at root or in class scope
|
474
|
+
# then it is a global variable initialization
|
475
|
+
raise "Global #{gv_name} already declared" if gvar
|
476
|
+
raise "Global op_asgn can only happen in method scope" unless exp_node
|
477
|
+
# In the class or root scope
|
478
|
+
# it can only be a Global var **declaration**
|
479
|
+
# In this case the expression has to reduce
|
480
|
+
# to a const wnode that can be used as value
|
481
|
+
# in the declaration (so it could for instance
|
482
|
+
# Global[:label] = 10 or Global[:label] = 10.to_I64)
|
483
|
+
# Then remove the generated wnode because it is not for
|
484
|
+
# execution. It is just to get the init value
|
485
|
+
wn_exp = parse_node(exp_node, wnode)
|
486
|
+
raise "Global initializer can only be a straight number" \
|
487
|
+
unless wn_exp.const?
|
488
|
+
wnode.remove_child(wn_exp)
|
489
|
+
gvar = Global.new(gv_name, wn_exp.wtype, wn_exp.wargs[:value])
|
490
|
+
# Do not export global for now
|
491
|
+
#gvar.export! if self.config[:export_all]
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
# Example
|
496
|
+
# @@stack_ptr = 10 + nbytes
|
497
|
+
# ---
|
498
|
+
# s(:cvasgn, :@@stack_ptr, s(:send, s(:int, 10), :+, s(:lvar, :nbytes)))
|
499
|
+
def parse_cvasgn(node, wnode, keep_eval)
|
500
|
+
cv_name, exp_node = *node.children
|
501
|
+
cvar = wnode.find_cvar(cv_name)
|
502
|
+
|
503
|
+
if wnode.in_method_scope?
|
504
|
+
# if exp_node is nil then this is the form of
|
505
|
+
# :cvasgn that comes from op_asgn
|
506
|
+
if exp_node
|
507
|
+
if cvar.nil?
|
508
|
+
# first cvar occurence
|
509
|
+
# type cast the cvar to the wtype of the expression
|
510
|
+
cvar = wnode.create_cvar(cv_name)
|
511
|
+
wn_cvasgn = @wgenerator.cvasgn(wnode, cvar)
|
512
|
+
wn_exp = parse_node(exp_node, wn_cvasgn)
|
513
|
+
cvar.wtype = wn_exp.wtype
|
514
|
+
else
|
515
|
+
# if cvar already exists then type cast the
|
516
|
+
# expression to the wtype of the existing cvar
|
517
|
+
wn_cvasgn = @wgenerator.cvasgn(wnode, cvar)
|
518
|
+
wn_exp = parse_node(exp_node, wn_cvasgn)
|
519
|
+
@wgenerator.cast(wn_exp, cvar.wtype, false)
|
520
|
+
end
|
521
|
+
else
|
522
|
+
raise "Class variable #{cv_name} not declared before" unless cvar
|
523
|
+
wn_cvasgn = @wgenerator.cvasgn(wnode, cvar)
|
524
|
+
end
|
525
|
+
# to mimic Ruby push the variable value on stack if needed
|
526
|
+
@wgenerator.cvar(wnode, cvar) if keep_eval
|
527
|
+
return wn_cvasgn
|
528
|
+
|
529
|
+
elsif wnode.in_class_scope?
|
530
|
+
# If we are in class scope
|
531
|
+
# then it is a class variable initialization
|
532
|
+
raise "Class variable #{cv_name} already declared" if cvar
|
533
|
+
raise "Class variable op_asgn can only happen in method scope" unless exp_node
|
534
|
+
# Parse the expression node to see if it's a ixx.const
|
535
|
+
# in the end but get rid of it then because we are not
|
536
|
+
# executing this code. Just statically initiliazing the
|
537
|
+
#cvar with the value
|
538
|
+
wn_exp = parse_node(exp_node, wnode)
|
539
|
+
raise "Class variable initializer can only be a straight number (got #{wn_exp}" \
|
540
|
+
unless wn_exp.const?
|
541
|
+
cvar = wnode.create_cvar(cv_name, wn_exp.wargs[:value], wn_exp.wtype)
|
542
|
+
wnode.remove_child(wn_exp)
|
543
|
+
logger.debug "Class variable #{cv_name} initialzed with value #{cvar.value} and wtype #{cvar.wtype}"
|
544
|
+
else
|
545
|
+
raise "Class variable can only be defined in method or class scope"
|
546
|
+
end
|
547
|
+
end
|
548
|
+
|
549
|
+
# Regular Example
|
550
|
+
# var1 = @@stack_ptr + nbytes
|
551
|
+
# ---
|
552
|
+
# s(:lvasgn, :var1, s(:send, s(:cvar, :@@stack_ptr), :+, s(:lvar, :nbytes)))
|
553
|
+
#
|
554
|
+
# Example coming from an op_asgn node
|
555
|
+
# arg1 += 2
|
556
|
+
# ---
|
557
|
+
# s(s(lvasgn, :arg1), :+, s(int 2)))
|
558
|
+
def parse_lvasgn(node, wnode, keep_eval)
|
559
|
+
lv_name, exp_node = *node.children
|
560
|
+
lvar = wnode.find_lvar(lv_name) || wnode.find_marg(lv_name)
|
561
|
+
|
562
|
+
logger.debug "Assign to #{lv_name}, exp_node: #{exp_node}, keep_eval: #{keep_eval}"
|
563
|
+
logger.debug "lvar found: #{lvar}"
|
564
|
+
|
565
|
+
# if exp_node is nil then this is the form of
|
566
|
+
# :lvasgn that comes from op_asgn
|
567
|
+
if exp_node
|
568
|
+
if lvar.nil?
|
569
|
+
# first lvar occurence
|
570
|
+
# type cast the lvar to the wtype of the expression
|
571
|
+
lvar = wnode.create_lvar(lv_name)
|
572
|
+
wn_lvasgn = @wgenerator.lvasgn(wnode, lvar)
|
573
|
+
wn_exp = parse_node(exp_node, wn_lvasgn)
|
574
|
+
lvar.wtype = wn_exp.wtype
|
575
|
+
else
|
576
|
+
# if cvar already exists then type cast the
|
577
|
+
# expression to the wtype of the existing cvar
|
578
|
+
wn_lvasgn = @wgenerator.lvasgn(wnode, lvar)
|
579
|
+
wn_exp = parse_node(exp_node, wn_lvasgn)
|
580
|
+
@wgenerator.cast(wn_exp, lvar.wtype, false)
|
581
|
+
end
|
582
|
+
else
|
583
|
+
raise "Local variable #{cv_name} not declared before" unless lvar
|
584
|
+
wn_lvasgn = @wgenerator.lvasgn(wnode, lvar)
|
585
|
+
end
|
586
|
+
# to mimic Ruby push the variable value on stack if needed
|
587
|
+
@wgenerator.lvar(wnode, lvar) if keep_eval
|
588
|
+
return wn_lvasgn
|
589
|
+
end
|
590
|
+
|
591
|
+
# Example
|
592
|
+
# ... $MYGLOBAL
|
593
|
+
# ---
|
594
|
+
# ... s(:gvar, :$MYGLOBAL)
|
595
|
+
def parse_gvar(node, wnode, keep_eval)
|
596
|
+
gv_name, = *node.children
|
597
|
+
gvar = Global.find(gv_name)
|
598
|
+
raise "Unknown Global variable #{gv_name}" unless gvar
|
599
|
+
wn_gvar = @wgenerator.gvar(wnode, gvar)
|
600
|
+
# Drop last evaluated result if asked to
|
601
|
+
@wgenerator.drop(wnode) unless keep_eval
|
602
|
+
return wn_gvar
|
603
|
+
end
|
604
|
+
|
605
|
+
# Example
|
606
|
+
# ... @@stack_ptr
|
607
|
+
# ---
|
608
|
+
# ... s(:cvar, :@@stack_ptr)
|
609
|
+
def parse_cvar(node, wnode, keep_eval)
|
610
|
+
raise "Class variable can only be accessed in method scope" \
|
611
|
+
unless wnode.in_method_scope?
|
612
|
+
cv_name, = *node.children
|
613
|
+
if (cvar = wnode.find_cvar(cv_name))
|
614
|
+
wn_cvar = @wgenerator.cvar(wnode, cvar)
|
615
|
+
else
|
616
|
+
raise "unknown class variable #{cv_name}"
|
617
|
+
end
|
618
|
+
# Drop last evaluated result if asked to
|
619
|
+
@wgenerator.drop(wnode) unless keep_eval
|
620
|
+
return wn_cvar
|
621
|
+
end
|
622
|
+
|
623
|
+
# Example
|
624
|
+
# ... nbytes
|
625
|
+
# ---
|
626
|
+
# ... s(:lvar, :nbytes)
|
627
|
+
def parse_lvar(node, wnode, keep_eval)
|
628
|
+
logger.debug("node: #{node}, wnode: #{wnode}, keep_eval: #{keep_eval}")
|
629
|
+
|
630
|
+
lv_name, = *node.children
|
631
|
+
if (lvar = wnode.find_lvar(lv_name) || wnode.find_marg(lv_name))
|
632
|
+
wn_lvar = @wgenerator.lvar(wnode, lvar)
|
633
|
+
else
|
634
|
+
raise "unknown local variable #{lv_name}"
|
635
|
+
end
|
636
|
+
logger.debug "wnode: #{wnode}"
|
637
|
+
# Drop last evaluated result if asked to
|
638
|
+
@wgenerator.drop(wnode) unless keep_eval
|
639
|
+
return wn_lvar
|
640
|
+
end
|
641
|
+
|
642
|
+
def parse_int(node, wnode, keep_eval)
|
643
|
+
value, = *node.children
|
644
|
+
logger.debug "int: #{value} for parent wnode #{wnode} keep_eval:#{keep_eval}"
|
645
|
+
wn_int = @wgenerator.int(wnode, WType::DEFAULT, value)
|
646
|
+
# Drop last evaluated result if asked to
|
647
|
+
@wgenerator.drop(wnode) unless keep_eval
|
648
|
+
|
649
|
+
logger.debug "wn_int:#{wn_int} wtype:#{wn_int.wtype} keep_eval:#{keep_eval}"
|
650
|
+
return wn_int
|
651
|
+
end
|
652
|
+
|
653
|
+
def parse_true(node, wnode, keep_eval)
|
654
|
+
wn_true = @wgenerator.int(wnode, WType::DEFAULT, 1)
|
655
|
+
# Drop last evaluated result if asked to
|
656
|
+
@wgenerator.drop(wnode) unless keep_eval
|
657
|
+
|
658
|
+
logger.debug "wn_true:#{wn_true} wtype:#{wn_true.wtype} keep_eval:#{keep_eval}"
|
659
|
+
return wn_true
|
660
|
+
end
|
661
|
+
|
662
|
+
def parse_false(node, wnode, keep_eval)
|
663
|
+
wn_false = @wgenerator.int(wnode, WType::DEFAULT, 0)
|
664
|
+
# Drop last evaluated result if asked to
|
665
|
+
@wgenerator.drop(wnode) unless keep_eval
|
666
|
+
|
667
|
+
logger.debug "wn_false:#{wn_false} wtype:#{wn_false.wtype} keep_eval:#{keep_eval}"
|
668
|
+
return wn_false
|
669
|
+
end
|
670
|
+
|
671
|
+
# Example
|
672
|
+
# TestA::C::MYCONST
|
673
|
+
# -------
|
674
|
+
# (const (const (const nil :TESTA) :C) :MYCONST))
|
675
|
+
def parse_const(node, wnode, keep_eval)
|
676
|
+
# Build constant path from embeddec const sexp
|
677
|
+
const_path = []
|
678
|
+
n = node
|
679
|
+
while n
|
680
|
+
logger.debug "adding #{n.children.last} to constant path"
|
681
|
+
const_path.unshift(n.children.last)
|
682
|
+
n = n.children.first
|
683
|
+
end
|
684
|
+
full_const_name = const_path.join('::')
|
685
|
+
if const_path.size == 1
|
686
|
+
class_name = wnode.class_name
|
687
|
+
const_name = const_path.first
|
688
|
+
elsif const_path.size == 2
|
689
|
+
class_name, const_name = *const_path
|
690
|
+
else
|
691
|
+
raise "only constant of the form X or X::Y is supported (got #{full_const_name}"
|
692
|
+
end
|
693
|
+
|
694
|
+
# See if constant exists. It should at this point
|
695
|
+
unless (const = wnode.find_const(const_name, class_name))
|
696
|
+
raise "unknown constant #{full_const_name}"
|
697
|
+
end
|
698
|
+
wn_const = @wgenerator.const(wnode, const)
|
699
|
+
|
700
|
+
# Drop last evaluated result if asked to
|
701
|
+
@wgenerator.drop(wnode) unless keep_eval
|
702
|
+
return wn_const
|
703
|
+
end
|
704
|
+
|
705
|
+
# method arguments
|
706
|
+
def parse_args(node, wnode)
|
707
|
+
# collect method arguments
|
708
|
+
node.children.each do |arg_node|
|
709
|
+
raise "only regular method argument is supported (got #{arg_node.type})" if arg_node.type != :arg
|
710
|
+
# keep track of method arguments. Do not generate wasm code yet
|
711
|
+
# as 'arg' directives may later affect argument types (see parse_send)
|
712
|
+
wnode.create_marg(arg_node.children.last)
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
# class method definition
|
717
|
+
# Example
|
718
|
+
# s(:defs,
|
719
|
+
# s(:self), :push,
|
720
|
+
# s(:args,
|
721
|
+
# s(:arg, :value)),... )
|
722
|
+
#-----
|
723
|
+
# def self.push(value)
|
724
|
+
# ...
|
725
|
+
# end
|
726
|
+
def parse_defs(node, wnode, keep_eval)
|
727
|
+
logger.debug "node: #{node}\nwnode: #{wnode}"
|
728
|
+
recv_node, method_name, arg_nodes, body_node = *node.children
|
729
|
+
raise "only class method is supported. Wrong receiver at #{recv_node.loc.expression}" if recv_node.type != :self
|
730
|
+
logger.debug "recv_node: #{recv_node}\nmethod_name: #{method_name}"
|
731
|
+
|
732
|
+
# create corresponding func node
|
733
|
+
method = wnode.find_or_create_method(method_name, nil, :class)
|
734
|
+
method.export! if (@@export || self.config[:export_all])
|
735
|
+
logger.debug "Method object : #{method.inspect}"
|
736
|
+
wn_method = @wgenerator.class_method(wnode, method)
|
737
|
+
# collect method arguments
|
738
|
+
parse_args(arg_nodes, wn_method)
|
739
|
+
# Look for any result directive and parse it so
|
740
|
+
# that we know what the return type is in advance
|
741
|
+
# If :nil for instance then it may change the way
|
742
|
+
# we generate code in the body of the method
|
743
|
+
if (result_node = body_node.children.find {|n| n.respond_to?(:type) && n.type == :send && n.children[1] == :result})
|
744
|
+
logger.debug "result directive found: #{result_node}"
|
745
|
+
parse_node(result_node, wn_method, keep_eval)
|
746
|
+
end
|
747
|
+
|
748
|
+
# method body -- A method evaluates to its last
|
749
|
+
# computed value unless a result :nil directive
|
750
|
+
# is specified
|
751
|
+
logger.debug "method_name: #{method_name}, wtype: #{wn_method.wtype}"
|
752
|
+
parse_node(body_node, wn_method, !wn_method.wtype.blank?)
|
753
|
+
|
754
|
+
# Now that we have parsed the whole method we can
|
755
|
+
# prepend locals, result and method args to the
|
756
|
+
# method wnode (in that order)
|
757
|
+
@wgenerator.locals(wn_method)
|
758
|
+
@wgenerator.result(wn_method)
|
759
|
+
@wgenerator.params(wn_method)
|
760
|
+
logger.debug "Full method wnode: #{wn_method}"
|
761
|
+
# reset export toggle
|
762
|
+
@@export = false
|
763
|
+
return wn_method
|
764
|
+
end
|
765
|
+
|
766
|
+
# Instance method definition
|
767
|
+
# Example
|
768
|
+
# (def :push,
|
769
|
+
# (args,
|
770
|
+
# s(:arg, :value)),... )
|
771
|
+
#-----
|
772
|
+
# def push(value)
|
773
|
+
# ...
|
774
|
+
# end
|
775
|
+
def parse_def(node, wnode, keep_eval)
|
776
|
+
logger.debug "node: #{node}\nwnode: #{wnode}"
|
777
|
+
method_name, arg_nodes, body_node = *node.children
|
778
|
+
logger.debug "method_name: #{method_name}"
|
779
|
+
|
780
|
+
# create corresponding func node
|
781
|
+
method = wnode.find_or_create_method(method_name, nil, :instance)
|
782
|
+
method.export! if (@@export || self.config[:export_all])
|
783
|
+
logger.debug "Method object : #{method.inspect}"
|
784
|
+
wn_method = @wgenerator.instance_method(wnode, method)
|
785
|
+
# add a receiver argument
|
786
|
+
|
787
|
+
# collect method arguments
|
788
|
+
parse_args(arg_nodes, wn_method)
|
789
|
+
# Look for any result directive and parse it so
|
790
|
+
# that we know what the return type is in advance
|
791
|
+
# If :nil for instance then it may change the way
|
792
|
+
# we generate code in the body of the method
|
793
|
+
if (result_node = body_node.children.find {|n| n.respond_to?(:type) && n.type == :send && n.children[1] == :result})
|
794
|
+
logger.debug "result directive found: #{result_node}"
|
795
|
+
parse_node(result_node, wn_method, keep_eval)
|
796
|
+
end
|
797
|
+
|
798
|
+
# method body -- A method evaluates to its last
|
799
|
+
# computed value unless a result :nil directive
|
800
|
+
# is specified
|
801
|
+
logger.debug "method_name: #{method_name}, wtype: #{wn_method.wtype}"
|
802
|
+
parse_node(body_node, wn_method, !wn_method.wtype.blank?)
|
803
|
+
|
804
|
+
# Now that we have parsed the whole method we can
|
805
|
+
# prepend locals, result and method args to the
|
806
|
+
# method wnode (in that order)
|
807
|
+
@wgenerator.locals(wn_method)
|
808
|
+
@wgenerator.result(wn_method)
|
809
|
+
@wgenerator.params(wn_method)
|
810
|
+
logger.debug "Full method wnode: #{wn_method}"
|
811
|
+
# reset export toggle
|
812
|
+
@@export = false
|
813
|
+
return wn_method
|
814
|
+
end
|
815
|
+
|
816
|
+
def parse_require(wnode, file)
|
817
|
+
logger.debug "File required: #{file}"
|
818
|
+
extensions = ['', '.wat', '.rb']
|
819
|
+
full_path_file = nil
|
820
|
+
if Pathname.new(file).absolute?
|
821
|
+
logger.debug "Absolute path detected"
|
822
|
+
extensions.each do |ext|
|
823
|
+
full_path_file = file+ext
|
824
|
+
break if File.file?(full_path_file)
|
825
|
+
end
|
826
|
+
else
|
827
|
+
case file
|
828
|
+
when /^\./
|
829
|
+
# If file starts with . then look for file in pwd
|
830
|
+
load_path = [Dir.pwd]
|
831
|
+
when /^rlang/
|
832
|
+
# If it starts with rlang then look for it in the
|
833
|
+
# installed rlang gem in addition to load path
|
834
|
+
load_path = self.config[:LOAD_PATH] + $LOAD_PATH
|
835
|
+
else
|
836
|
+
load_path = self.config[:LOAD_PATH]
|
837
|
+
load_path = [Dir.pwd] if self.config[:LOAD_PATH].empty?
|
838
|
+
end
|
839
|
+
logger.debug "load_path: #{load_path} for file #{file}"
|
840
|
+
|
841
|
+
# Now try each possible extension foreach possible
|
842
|
+
# directory in the load path
|
843
|
+
load_path.each do |dir|
|
844
|
+
logger.debug "Searching in dir: #{dir}"
|
845
|
+
break unless extensions.each do |ext|
|
846
|
+
fpf = File.expand_path(File.join(dir, file+ext))
|
847
|
+
if File.file?(fpf)
|
848
|
+
logger.debug "Found required file: #{fpf}"
|
849
|
+
full_path_file = fpf; break
|
850
|
+
end
|
851
|
+
end
|
852
|
+
end
|
853
|
+
end
|
854
|
+
raise LoadError, "no such file to load: #{full_path_file}" unless full_path_file
|
855
|
+
|
856
|
+
# Now load the file
|
857
|
+
if File.extname(full_path_file) == '.wat'
|
858
|
+
wat_code = File.read(full_path_file)
|
859
|
+
@wgenerator.inline(wnode, wat_code)
|
860
|
+
else
|
861
|
+
parse_file(full_path_file)
|
862
|
+
end
|
863
|
+
end
|
864
|
+
|
865
|
+
def parse_require_relative(wnode, file)
|
866
|
+
logger.debug "Require file: #{file}...\n ...relative to #{self.config[:__FILE__]}"
|
867
|
+
full_path_file = File.expand_path(file, File.dirname(self.config[:__FILE__]))
|
868
|
+
parse_require(wnode, full_path_file)
|
869
|
+
end
|
870
|
+
|
871
|
+
# Parse the many differents forms of send
|
872
|
+
# (both compile time directives and application calls)
|
873
|
+
def parse_send(node, wnode, keep_eval)
|
874
|
+
recv_node = node.children[0]
|
875
|
+
method_name = node.children[1]
|
876
|
+
logger.debug "recv_node #{recv_node}, method_name : #{method_name}"
|
877
|
+
logger.debug "scope: #{wnode.scope}"
|
878
|
+
|
879
|
+
if recv_node.nil?
|
880
|
+
return parse_send_nil_receiver(node, wnode, keep_eval)
|
881
|
+
end
|
882
|
+
|
883
|
+
# Special case : DAta initializers
|
884
|
+
#
|
885
|
+
# Example (setting DAta address)
|
886
|
+
# DAta.current_address = 0
|
887
|
+
# ---------
|
888
|
+
# (send
|
889
|
+
# (const nil :DAta) :current_address=
|
890
|
+
# (int 0))
|
891
|
+
#
|
892
|
+
# Example (setting DAta alignment)
|
893
|
+
# DAta.align(8)
|
894
|
+
# ---------
|
895
|
+
# (send
|
896
|
+
# (const nil :DAta) :align
|
897
|
+
# (int 8))
|
898
|
+
#
|
899
|
+
# Example (value is an i32)
|
900
|
+
# DAta[:an_I32] = 3200
|
901
|
+
# ---------
|
902
|
+
# (send
|
903
|
+
# (const nil :DAta) :[]=
|
904
|
+
# (sym :an_I32)
|
905
|
+
# (int 32000))
|
906
|
+
# )
|
907
|
+
#
|
908
|
+
# Example (value is an i64)
|
909
|
+
# DAta[:an_I64] = 3200.to_I64
|
910
|
+
# ---------
|
911
|
+
# (send
|
912
|
+
# (const nil :DAta) :[]=
|
913
|
+
# (sym :an_I64)
|
914
|
+
# (send
|
915
|
+
# (int 32000) :to_Ixx))
|
916
|
+
# )
|
917
|
+
#
|
918
|
+
# Example (value is a String)
|
919
|
+
# DAta[:a_string] = "My\tLitte\tRlang\x00"
|
920
|
+
# ---------
|
921
|
+
# (send
|
922
|
+
# (const nil :Data) :[]=
|
923
|
+
# (sym :a_string)
|
924
|
+
# (str "My\tLitte\tRlang\u0000"))
|
925
|
+
#
|
926
|
+
# Example (value is a data address)
|
927
|
+
# DAta[:an_address] = DAta[:a_string]
|
928
|
+
# ---------
|
929
|
+
# (send
|
930
|
+
# (const nil :DAta) :[]=
|
931
|
+
# (sym :an_address)
|
932
|
+
# (send
|
933
|
+
# (const nil :DAta) :[]
|
934
|
+
# (sym :a_string)))
|
935
|
+
#
|
936
|
+
# Example (value is an array)
|
937
|
+
# Data[:an_array] = [ Data[:an_I64], 5, 257, "A string\n"]
|
938
|
+
# ---------
|
939
|
+
# (send
|
940
|
+
# (const nil :Data) :[]=
|
941
|
+
# (sym :an_array)
|
942
|
+
# (array
|
943
|
+
# (send
|
944
|
+
# (const nil :Data) :[]
|
945
|
+
# (sym :an_I64))
|
946
|
+
# (int 5)
|
947
|
+
# (int 257)
|
948
|
+
# (str "A string\n")))
|
949
|
+
#
|
950
|
+
|
951
|
+
if recv_node.type == :const && recv_node.children.last == :DAta
|
952
|
+
case method_name
|
953
|
+
when :current_address=
|
954
|
+
value_node = node.children[2]
|
955
|
+
raise "DAta address must be an integer" unless value_node.type == :int
|
956
|
+
DAta.current_address = value_node.children.last
|
957
|
+
when :align
|
958
|
+
value_node = node.children[2]
|
959
|
+
raise "DAta alignment argument must be an integer" unless value_node.type == :int
|
960
|
+
DAta.align(value_node.children.last)
|
961
|
+
when :[]=
|
962
|
+
if (data_label_node = node.children[2]).type == :sym
|
963
|
+
label = data_label_node.children.last
|
964
|
+
else
|
965
|
+
raise "Data label must be a symbol (got #{data_label_node}"
|
966
|
+
end
|
967
|
+
arg_node = node.children[3]
|
968
|
+
parse_data_value(label, arg_node)
|
969
|
+
else
|
970
|
+
raise "Unsupported DAta method #{method_name}"
|
971
|
+
end
|
972
|
+
return
|
973
|
+
end
|
974
|
+
|
975
|
+
# General type cast directive
|
976
|
+
# this must be processed at compile time. It's not dynamic
|
977
|
+
# An expression can be cast to any Rlang class including
|
978
|
+
# native types like :I64,:I32,...
|
979
|
+
#
|
980
|
+
# Example
|
981
|
+
# (expression).cast_to(class_name, argument)
|
982
|
+
# -----
|
983
|
+
# s(:begin,
|
984
|
+
# s(expression),
|
985
|
+
# :cast_to, s(sym, :Class_name))
|
986
|
+
# the signed argument true|false is optional and
|
987
|
+
# it defaults to false
|
988
|
+
if method_name == :cast_to
|
989
|
+
class_name_node = node.children.last
|
990
|
+
raise "cast_to expects a symbol argument (got #{class_name_node}" unless class_name_node.type == :sym
|
991
|
+
tgt_wtype = WType.new(class_name_node.children.first)
|
992
|
+
logger.debug "in cast_to: target type #{tgt_wtype}"
|
993
|
+
|
994
|
+
# Parse the expression and cast it
|
995
|
+
wn_to_cast = parse_node(recv_node, wnode)
|
996
|
+
logger.debug("wn_to_cast: #{wn_to_cast}")
|
997
|
+
wn_cast = @wgenerator.cast(wn_to_cast, tgt_wtype)
|
998
|
+
logger.debug("wn_cast: #{wn_cast}")
|
999
|
+
# Drop last evaluated result if asked to
|
1000
|
+
@wgenerator.drop(wnode) unless keep_eval
|
1001
|
+
return wn_cast
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
|
1005
|
+
# Type cast directives specific for native types
|
1006
|
+
# can pass the signed argument wheres cast_to cannot
|
1007
|
+
# this must be processed at compile time. It's not dynamic
|
1008
|
+
# Example
|
1009
|
+
# (recv).to_Ixx(true|fasle) where xx is 64 or 32
|
1010
|
+
# -----
|
1011
|
+
# s(:begin,
|
1012
|
+
# s(expression),
|
1013
|
+
# :to_I64, [true|false])
|
1014
|
+
# the signed argument true|false is optional and
|
1015
|
+
# it defaults to false
|
1016
|
+
if method_name == :to_I64 || method_name == :to_I32
|
1017
|
+
tgt_wtype = (method_name == :to_I64) ? WType.new(:I64) : WType.new(:I32)
|
1018
|
+
if (cnt = node.children.count) == 3
|
1019
|
+
signed = true if node.children.last.type == :true
|
1020
|
+
elsif cnt == 2
|
1021
|
+
signed = false
|
1022
|
+
else
|
1023
|
+
raise "cast directive should have 0 or 1 argument (got #{cnt - 2})"
|
1024
|
+
end
|
1025
|
+
logger.debug "in cast section: child count #{cnt}, tgt_wtype #{tgt_wtype}, signed: #{signed}"
|
1026
|
+
|
1027
|
+
# Parse the expression and cast it
|
1028
|
+
wn_to_cast = parse_node(recv_node, wnode)
|
1029
|
+
logger.debug("wn_to_cast: #{wn_to_cast}")
|
1030
|
+
wn_cast = @wgenerator.cast(wn_to_cast, tgt_wtype, signed)
|
1031
|
+
logger.debug("wn_cast: #{wn_cast}")
|
1032
|
+
# Drop last evaluated result if asked to
|
1033
|
+
@wgenerator.drop(wnode) unless keep_eval
|
1034
|
+
return wn_cast
|
1035
|
+
end
|
1036
|
+
|
1037
|
+
# A that stage it's a method call of some sort
|
1038
|
+
# (call on class or instance)
|
1039
|
+
if ARITHMETIC_OPS.include?(method_name) ||
|
1040
|
+
RELATIONAL_OPS.include?(method_name) ||
|
1041
|
+
UNARY_OPS.include?(method_name)
|
1042
|
+
return parse_operator(node, wnode, keep_eval)
|
1043
|
+
else
|
1044
|
+
return parse_send_method_lookup(node, wnode, keep_eval)
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
raise "FATAL ERROR!! Unreachable point at end of parse_send (node: #{node})"
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
def parse_send_nil_receiver(node, wnode, keep_eval)
|
1051
|
+
recv_node = node.children[0]
|
1052
|
+
method_name = node.children[1]
|
1053
|
+
raise "receiver should be nil here (got #{recv_node})" \
|
1054
|
+
unless recv_node.nil?
|
1055
|
+
|
1056
|
+
if recv_node.nil? && method_name == :require
|
1057
|
+
return parse_send_require(node, wnode, keep_eval)
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
if recv_node.nil? && method_name == :require_relative
|
1061
|
+
return parse_send_require_relative(node, wnode, keep_eval)
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
if recv_node.nil? && method_name == :export
|
1065
|
+
return parse_send_export(node, wnode, keep_eval)
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
if recv_node.nil? && method_name == :local
|
1069
|
+
return parse_send_local(node, wnode, keep_eval)
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
if recv_node.nil? && method_name == :arg
|
1073
|
+
return parse_send_arg(node, wnode, keep_eval)
|
1074
|
+
end
|
1075
|
+
|
1076
|
+
if recv_node.nil? && method_name == :result
|
1077
|
+
return parse_send_result(node, wnode, keep_eval)
|
1078
|
+
end
|
1079
|
+
|
1080
|
+
if recv_node.nil? && method_name == :wattr
|
1081
|
+
return parse_send_wattr(node, wnode, keep_eval)
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
if recv_node.nil? && method_name == :wattr_type
|
1085
|
+
return parse_send_wattr_type(node, wnode, keep_eval)
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
if recv_node.nil? && method_name == :inline
|
1089
|
+
return parse_send_inline(node, wnode, keep_eval)
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
# All other cases : it is a regular method call
|
1093
|
+
return parse_send_method_lookup(node, wnode, keep_eval)
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
# Directive to require a file
|
1097
|
+
# Example
|
1098
|
+
# (send nil :require
|
1099
|
+
# (str "test5"))
|
1100
|
+
def parse_send_require(node, wnode, keep_eval)
|
1101
|
+
raise "require must be used at root level" \
|
1102
|
+
unless wnode.in_root_scope?
|
1103
|
+
file_node = node.children.last
|
1104
|
+
raise "require only accepts a string argument (got #{file_node})" \
|
1105
|
+
unless file_node.type == :str
|
1106
|
+
parse_require(wnode, file_node.children.last)
|
1107
|
+
return
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
# Directive to require_a file relative to
|
1111
|
+
# current file
|
1112
|
+
# Example
|
1113
|
+
# (send nil :require_relative
|
1114
|
+
# (str "test5"))
|
1115
|
+
def parse_send_require_relative(node, wnode, keep_eval)
|
1116
|
+
raise "require_relative must be used at root level" \
|
1117
|
+
unless wnode.in_root_scope?
|
1118
|
+
file_node = node.children.last
|
1119
|
+
raise "require only accepts a string argument (got #{file_node})" \
|
1120
|
+
unless file_node.type == :str
|
1121
|
+
parse_require_relative(wnode, file_node.children.last)
|
1122
|
+
return
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
# Directive to declare the current method
|
1126
|
+
# in the WASM exports
|
1127
|
+
def parse_send_export(node, wnode, keep_eval)
|
1128
|
+
raise "export must be used in class scope" unless wnode.in_class_scope?
|
1129
|
+
@@export = true
|
1130
|
+
return
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
# Directive to define local variable type
|
1134
|
+
# this must be processed at compile time
|
1135
|
+
# if method name is :local then it is
|
1136
|
+
# a type definition for a local variable
|
1137
|
+
# local :value, :I64
|
1138
|
+
# ---------
|
1139
|
+
# s(:send, nil, :local,
|
1140
|
+
# (hash
|
1141
|
+
# (pair
|
1142
|
+
# s(:sym, :value)
|
1143
|
+
# s(:sym, :I64))
|
1144
|
+
# ))
|
1145
|
+
def parse_send_local(node, wnode, keep_eval)
|
1146
|
+
raise "local declaration can only be used in methods" \
|
1147
|
+
unless wnode.in_method_scope?
|
1148
|
+
hash_node = node.children.last
|
1149
|
+
local_types = parse_type_args(hash_node, :local)
|
1150
|
+
local_types.each do |name, wtype|
|
1151
|
+
lvar = wnode.find_or_create_lvar(name)
|
1152
|
+
raise "couldn't find or create local variable #{name}" unless lvar
|
1153
|
+
lvar.wtype = WType.new(wtype)
|
1154
|
+
end
|
1155
|
+
return
|
1156
|
+
end
|
1157
|
+
|
1158
|
+
# Directive to define method argument type
|
1159
|
+
# this must be processed at compile time
|
1160
|
+
# if method name is :arg then it is
|
1161
|
+
# a type definition for a method argument
|
1162
|
+
# arg value: :I64
|
1163
|
+
# ---------
|
1164
|
+
# s(:send, nil, :arg,
|
1165
|
+
# (hash
|
1166
|
+
# (pair
|
1167
|
+
# s(:sym, :value)
|
1168
|
+
# s(:sym, :I64))
|
1169
|
+
# ))
|
1170
|
+
def parse_send_arg(node, wnode, keep_eval)
|
1171
|
+
raise "arg declaration can only be used in methods" \
|
1172
|
+
unless wnode.in_method_scope?
|
1173
|
+
hash_node = node.children.last
|
1174
|
+
marg_types = parse_type_args(hash_node, :argument)
|
1175
|
+
marg_types.each do |name, wtype|
|
1176
|
+
marg = wnode.find_marg(name)
|
1177
|
+
raise "couldn't find method argument #{name}" unless marg
|
1178
|
+
marg.wtype = WType.new(wtype)
|
1179
|
+
end
|
1180
|
+
return
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
# result directive in method scope
|
1184
|
+
# ======
|
1185
|
+
# Directive to define method return type
|
1186
|
+
# in the method itself
|
1187
|
+
# this must be processed at compile time
|
1188
|
+
# Supported types : :I32, :I64, :none
|
1189
|
+
# (:nil means no value is returned)
|
1190
|
+
#
|
1191
|
+
# Example
|
1192
|
+
# result :I64
|
1193
|
+
# ---------
|
1194
|
+
# s(:send, nil, :result,
|
1195
|
+
# s(:sym, :I64))
|
1196
|
+
#
|
1197
|
+
# result directive in class scope
|
1198
|
+
# ======
|
1199
|
+
# Directive to define method return type
|
1200
|
+
# at the class level. This allows to declare
|
1201
|
+
# a method type before the method is parsed
|
1202
|
+
# this must be processed at compile time
|
1203
|
+
# Supported types : :I32, :I64, :none
|
1204
|
+
# (:none means no value is returned)
|
1205
|
+
#
|
1206
|
+
# Example
|
1207
|
+
# result :class_name, :method_name, :I64
|
1208
|
+
# ---------
|
1209
|
+
# s(:send, nil, :result,
|
1210
|
+
# s(:sym, :class_name),
|
1211
|
+
# s(:sym, :method_name),
|
1212
|
+
# s(:sym, :I64))
|
1213
|
+
#
|
1214
|
+
# if name starts with # it's a n instance method,
|
1215
|
+
# otherwise a class method
|
1216
|
+
def parse_send_result(node, wnode, keep_eval)
|
1217
|
+
if wnode.in_method_scope?
|
1218
|
+
result_type, = *node.children[2]
|
1219
|
+
raise "result directive expects a symbol argument (got #{result_type})" \
|
1220
|
+
unless result_type.is_a? Symbol
|
1221
|
+
wnode.method_wnode.wtype = WType.new(result_type)
|
1222
|
+
logger.debug "result_type #{result_type} updated for method #{wnode.method_wnode.method.inspect}"
|
1223
|
+
elsif wnode.in_class_scope?
|
1224
|
+
cn_name, = *node.children[2]
|
1225
|
+
mn_name, = *node.children[3]
|
1226
|
+
result_type, = *node.children[4]
|
1227
|
+
method_type = (mn_name[0] == '#' ? :instance : :class)
|
1228
|
+
(mwn = wnode.find_or_create_method(mn_name, cn_name, method_type)).wtype = WType.new(result_type)
|
1229
|
+
logger.debug "result_type #{mwn.wtype} for method #{mwn.name}"
|
1230
|
+
else
|
1231
|
+
raise "result declaration not supported #{wn.scope} scope"
|
1232
|
+
end
|
1233
|
+
return
|
1234
|
+
end
|
1235
|
+
|
1236
|
+
# Directive to define class attributes. This defines
|
1237
|
+
# a list of getters and setters and access them in
|
1238
|
+
# memory with an offset from the base address given as
|
1239
|
+
# an argument.
|
1240
|
+
#
|
1241
|
+
# Example
|
1242
|
+
# wattr :ptr, :size
|
1243
|
+
# ---------
|
1244
|
+
# s(:send, nil, :wattr,
|
1245
|
+
# s(:sym, :ptr),
|
1246
|
+
# s(:sym, :size))
|
1247
|
+
def parse_send_wattr(node, wnode, keep_eval)
|
1248
|
+
raise "wattr directives can only happen in class scope" \
|
1249
|
+
unless wnode.in_class_scope?
|
1250
|
+
wattr_nodes = node.children[2..-1]
|
1251
|
+
wattr_nodes.each do |wan|
|
1252
|
+
logger.debug "processing wattr node #{wan}"
|
1253
|
+
raise "attribute name must be a symbol (got #{wan})" unless wan.type == :sym
|
1254
|
+
wattr_name = wan.children.last
|
1255
|
+
if (wattr = wnode.find_wattr(wattr_name))
|
1256
|
+
raise "attribute #{wattr_name} already declared" if wattr
|
1257
|
+
else
|
1258
|
+
wattr = wnode.create_wattr(wattr_name)
|
1259
|
+
wattr.getter.export! if (@@export || self.config[:export_all])
|
1260
|
+
wattr.setter.export! if (@@export || self.config[:export_all])
|
1261
|
+
end
|
1262
|
+
end
|
1263
|
+
return
|
1264
|
+
end
|
1265
|
+
|
1266
|
+
# Directive to specify wasm type of class attributes
|
1267
|
+
# in case it's not the default type
|
1268
|
+
#
|
1269
|
+
# Example
|
1270
|
+
# wattr_type ptr: :I64, size: :I32
|
1271
|
+
# ---------
|
1272
|
+
# s(:send, nil, :wattr_type,
|
1273
|
+
# (hash
|
1274
|
+
# (pair
|
1275
|
+
# s(:sym, :ptr)
|
1276
|
+
# s(:sym, :I64))
|
1277
|
+
# (pair
|
1278
|
+
# s(:sym, :size)
|
1279
|
+
# s(:sym, :I32)) ))
|
1280
|
+
#
|
1281
|
+
def parse_send_wattr_type(node, wnode, keep_eval)
|
1282
|
+
raise "wattr directives can only happen in class scope" \
|
1283
|
+
unless wnode.in_class_scope?
|
1284
|
+
hash_node = node.children.last
|
1285
|
+
wattr_types = parse_type_args(hash_node, :attribute)
|
1286
|
+
wattr_types.each do |name, wtype|
|
1287
|
+
if (wattr = wnode.find_wattr(name))
|
1288
|
+
logger.debug "Setting wattr #{name} type to #{wtype}"
|
1289
|
+
wattr.wtype = WType.new(wtype)
|
1290
|
+
else
|
1291
|
+
raise "Unknown class attribute #{name} in #{wnode}"
|
1292
|
+
end
|
1293
|
+
end
|
1294
|
+
return
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
# Directive to inline WAT / Ruby code
|
1298
|
+
# the wat entry is used when the Rlang code is
|
1299
|
+
# comiled to WAT code. The Ruby entry is used
|
1300
|
+
# when the rlang code is simulated in plain Ruby
|
1301
|
+
# **CAUTION** the inline code is supposed to always
|
1302
|
+
# leave a value on the stack
|
1303
|
+
# Example
|
1304
|
+
# inline wat: '(call_indirect (type $insn_t)
|
1305
|
+
# (local.get $state)
|
1306
|
+
# (local.get $cf)
|
1307
|
+
# (local.get $opcodes)
|
1308
|
+
# (local.get $opcode) ;; instruction function pointer
|
1309
|
+
# )',
|
1310
|
+
# ruby: 'call_indirect(state, cf, opcodes, opcode)'
|
1311
|
+
#
|
1312
|
+
# ---------
|
1313
|
+
# (send, nil, :inline,
|
1314
|
+
# (hash
|
1315
|
+
# (pair
|
1316
|
+
# (sym :wat)
|
1317
|
+
# (dstr
|
1318
|
+
# (str "(call_indirect (type $insn_t) \n")
|
1319
|
+
# (str "...")
|
1320
|
+
# ...))
|
1321
|
+
# (pair)
|
1322
|
+
# (sym :wtype)
|
1323
|
+
# (sym :I64)
|
1324
|
+
# (pair
|
1325
|
+
# (sym :ruby)
|
1326
|
+
# (str "call_indirect(state, cf, opcodes, opcode)"))
|
1327
|
+
#
|
1328
|
+
def parse_send_inline(node, wnode, keep_eval)
|
1329
|
+
raise "inline can only happen in a method body or at root" \
|
1330
|
+
unless wnode.in_method_scope? || wnode.in_root_scope?
|
1331
|
+
hash_node = node.children.last
|
1332
|
+
raise "inline expects a hash argument (got #{hash_node.type}" \
|
1333
|
+
unless hash_node.type == :hash
|
1334
|
+
|
1335
|
+
# Find the :wat entry in hash
|
1336
|
+
logger.debug "Hash node: #{hash_node} "
|
1337
|
+
wat_node = hash_node.children. \
|
1338
|
+
find {|pair| sym_node, = *pair.children; sym_node.children.last == :wat}
|
1339
|
+
raise "inline has no wat: hash entry" unless wat_node
|
1340
|
+
|
1341
|
+
# Find the :wtype entry in hash if any
|
1342
|
+
wtype_node = hash_node.children. \
|
1343
|
+
find {|pair| sym_node, = *pair.children; sym_node.children.last == :wtype}
|
1344
|
+
if wtype_node
|
1345
|
+
wtype = WType.new(wtype_node.children.last.children.last)
|
1346
|
+
else
|
1347
|
+
wtype = WType::DEFAULT
|
1348
|
+
end
|
1349
|
+
|
1350
|
+
# Now extract the WAT code itself
|
1351
|
+
raise "inline has no wat: hash entry" unless wat_node
|
1352
|
+
wcode_node = wat_node.children.last
|
1353
|
+
if wcode_node.type == :dstr
|
1354
|
+
# iterate over str children
|
1355
|
+
wat_code = wcode_node.children.collect {|n| n.children.last}.join('')
|
1356
|
+
elsif wcode_node.type == :str
|
1357
|
+
wat_code = wcode_node.children.last
|
1358
|
+
else
|
1359
|
+
raise "inline WAT code must be a string (got #{wcode_node})"
|
1360
|
+
end
|
1361
|
+
wn_inline = @wgenerator.inline(wnode, wat_code, wtype)
|
1362
|
+
# Drop last evaluated result if asked to
|
1363
|
+
@wgenerator.drop(wnode) unless keep_eval
|
1364
|
+
return wn_inline
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
# If receiver not self or const then it could
|
1368
|
+
# be an arithmetic or relational expression
|
1369
|
+
# a method call on class "instance"
|
1370
|
+
#
|
1371
|
+
# Example for binary op
|
1372
|
+
# 1 + 2
|
1373
|
+
# ----------
|
1374
|
+
# (send
|
1375
|
+
# (int 1) :+
|
1376
|
+
# (int 2)
|
1377
|
+
# )
|
1378
|
+
#
|
1379
|
+
# Example unary op
|
1380
|
+
# !(n==1)
|
1381
|
+
# ----------
|
1382
|
+
# (send
|
1383
|
+
# (begin
|
1384
|
+
# (send (lvar :n) :== (int 1))
|
1385
|
+
# ) :!)
|
1386
|
+
#
|
1387
|
+
def parse_operator(node, wnode, keep_eval)
|
1388
|
+
recv_node = node.children[0]
|
1389
|
+
operator = node.children[1]
|
1390
|
+
wn_op = @wgenerator.operator(wnode, operator)
|
1391
|
+
wn_recv = parse_node(recv_node, wn_op, true)
|
1392
|
+
# now process the 2nd op arguments (there should
|
1393
|
+
# be only one but do as if we had several
|
1394
|
+
arg_nodes = node.children[2..-1]
|
1395
|
+
raise "method #{operator} got #{arg_nodes.count} arguments (expected 0 or 1)" \
|
1396
|
+
unless arg_nodes.count <= 1
|
1397
|
+
wn_args = arg_nodes.collect {|n| parse_node(n, wn_op, true)}
|
1398
|
+
@wgenerator.operands(wn_op, wn_recv, wn_args)
|
1399
|
+
logger.debug " After type cast: #{wn_op} wtype: #{wn_op.wtype}, op children types: #{wn_op.children.map(&:wtype)}"
|
1400
|
+
# Drop last evaluated result if asked to
|
1401
|
+
@wgenerator.drop(wnode) unless keep_eval
|
1402
|
+
return wn_op
|
1403
|
+
end
|
1404
|
+
|
1405
|
+
# Method lookup
|
1406
|
+
# In the example below mem_size wasn' t
|
1407
|
+
# recognized as a local var because it was not
|
1408
|
+
# assigned a value before. It's recognized as
|
1409
|
+
# a tentative method call
|
1410
|
+
# Example
|
1411
|
+
# some_var = mem_size + 10
|
1412
|
+
# ------
|
1413
|
+
# (send
|
1414
|
+
# (send nil :mem_size) :+
|
1415
|
+
# (int 10))
|
1416
|
+
#
|
1417
|
+
#
|
1418
|
+
# Example for method call on class instance
|
1419
|
+
# @@cvar.x = 100
|
1420
|
+
# ----------
|
1421
|
+
# (send
|
1422
|
+
# (cvar :@@cvar) :x= (int 100)
|
1423
|
+
# )
|
1424
|
+
# TODO must guess type arg from operator type
|
1425
|
+
def parse_send_method_lookup(node, wnode, keep_eval)
|
1426
|
+
recv_node = node.children[0]
|
1427
|
+
#method_name = node.children[1]
|
1428
|
+
if wnode.in_class_scope? || wnode.in_class_method_scope?
|
1429
|
+
if recv_node.nil? || recv_node.type == :self
|
1430
|
+
return parse_send_class_method_call(node, wnode, keep_eval)
|
1431
|
+
elsif recv_node.type == :const
|
1432
|
+
const_name = recv_node.children.last
|
1433
|
+
# if this is a Constant, not a class
|
1434
|
+
# then it's actually an instance method call
|
1435
|
+
if wnode.find_const(const_name)
|
1436
|
+
return parse_send_instance_method_call(node, wnode, keep_eval)
|
1437
|
+
else
|
1438
|
+
return parse_send_class_method_call(node, wnode, keep_eval)
|
1439
|
+
end
|
1440
|
+
else
|
1441
|
+
return parse_send_instance_method_call(node, wnode, keep_eval)
|
1442
|
+
end
|
1443
|
+
elsif wnode.in_instance_method_scope?
|
1444
|
+
if recv_node&.type == :const
|
1445
|
+
const_name = recv_node.children.last
|
1446
|
+
# if this is a Constant, not a class
|
1447
|
+
# then it's actually an instance method call
|
1448
|
+
if wnode.find_const(const_name)
|
1449
|
+
return parse_send_instance_method_call(node, wnode, keep_eval)
|
1450
|
+
else
|
1451
|
+
return parse_send_class_method_call(node, wnode, keep_eval)
|
1452
|
+
end
|
1453
|
+
else
|
1454
|
+
return parse_send_instance_method_call(node, wnode, keep_eval)
|
1455
|
+
end
|
1456
|
+
else
|
1457
|
+
raise "Don't know how to call method in scope #{wnode.scope} from node #{recv_node}"
|
1458
|
+
end
|
1459
|
+
end
|
1460
|
+
|
1461
|
+
# Regular class Method call to self class
|
1462
|
+
# or another class
|
1463
|
+
# Example
|
1464
|
+
# self.m_one_arg(arg1, 200)
|
1465
|
+
# ---------
|
1466
|
+
# (send
|
1467
|
+
# (self) :m_one_arg
|
1468
|
+
# (lvar :arg1)
|
1469
|
+
# (int 200)
|
1470
|
+
# )
|
1471
|
+
# OR
|
1472
|
+
# Test.m_one_arg(arg1, 200)
|
1473
|
+
# (send
|
1474
|
+
# (const nil :Test) :m_one_arg
|
1475
|
+
# (lvar :arg1)
|
1476
|
+
# (int 200)
|
1477
|
+
# )
|
1478
|
+
#
|
1479
|
+
# OR New object instantiation
|
1480
|
+
# This is class object instantiation. Statically
|
1481
|
+
# allocated though. So it can only happen in the
|
1482
|
+
# class scope for a class variable or a constant
|
1483
|
+
# Example
|
1484
|
+
# self.new
|
1485
|
+
# ---------
|
1486
|
+
# (send
|
1487
|
+
# (self) :new) )
|
1488
|
+
#
|
1489
|
+
# OR
|
1490
|
+
# Header.new
|
1491
|
+
# ---------
|
1492
|
+
# (send
|
1493
|
+
# (const nil :Header) :new) )
|
1494
|
+
#
|
1495
|
+
def parse_send_class_method_call(node, wnode, keep_eval)
|
1496
|
+
logger.debug "Parsing class method call:..."
|
1497
|
+
recv_node = node.children[0]
|
1498
|
+
method_name = node.children[1]
|
1499
|
+
if recv_node.nil? || recv_node.type == :self
|
1500
|
+
class_name = wnode.class_name
|
1501
|
+
elsif recv_node.type == :const
|
1502
|
+
class_name = recv_node.children.last
|
1503
|
+
else
|
1504
|
+
raise "Can only call method class on self or class objects (got #{recv_node} in node #{node})"
|
1505
|
+
end
|
1506
|
+
logger.debug "...#{class_name}::#{method_name}"
|
1507
|
+
if method_name == :new
|
1508
|
+
raise "Cannot instantiate object in scope #{wnode.scope} on #{node}" \
|
1509
|
+
unless wnode.in_class_scope?
|
1510
|
+
# This is class object instantiation. Statically
|
1511
|
+
# allocated though. So it can only happen in the
|
1512
|
+
# class scope for a class variable or a constant
|
1513
|
+
# Returns a wnode with a i32.const containing the address
|
1514
|
+
wn_addr = @wgenerator.new(wnode, class_name)
|
1515
|
+
return wn_addr
|
1516
|
+
else
|
1517
|
+
wn_call = @wgenerator.call(wnode, class_name, method_name, :class)
|
1518
|
+
arg_nodes = node.children[2..-1]
|
1519
|
+
arg_nodes.each { |node| parse_node(node, wn_call) }
|
1520
|
+
# Drop last evaluated result if asked to or if
|
1521
|
+
# the method called doesn't return any value
|
1522
|
+
@wgenerator.drop(wnode) unless (keep_eval || wn_call.wtype.blank?)
|
1523
|
+
return wn_call
|
1524
|
+
end
|
1525
|
+
raise "FATAL ERROR!! Unreachable point at end of parse_send_class_method_call (node: #{node})"
|
1526
|
+
end
|
1527
|
+
|
1528
|
+
def parse_send_instance_method_call(node, wnode, keep_eval)
|
1529
|
+
recv_node = node.children[0]
|
1530
|
+
method_name = node.children[1]
|
1531
|
+
# Parse receiver node and temporarily attach it
|
1532
|
+
# to parent wnode. It will later become the first
|
1533
|
+
# argument of the method call by reparenting it
|
1534
|
+
logger.debug "Parsing instance method call #{method_name}..."
|
1535
|
+
# parse the receiver node
|
1536
|
+
# if nil it means self
|
1537
|
+
wn_recv = recv_node.nil? ? parse_self(recv_node, wnode) : parse_node(recv_node, wnode)
|
1538
|
+
logger.debug "... on #{wn_recv}"
|
1539
|
+
class_name = wn_recv.wtype.name
|
1540
|
+
|
1541
|
+
wn_call = @wgenerator.call(wnode, class_name, method_name, :instance)
|
1542
|
+
wn_recv.reparent_to(wn_call) if wn_recv
|
1543
|
+
|
1544
|
+
# Grab all arguments and add them as child of the call node
|
1545
|
+
arg_nodes = node.children[2..-1]
|
1546
|
+
arg_nodes.each do |node|
|
1547
|
+
wn = parse_node(node, wn_call)
|
1548
|
+
logger.debug "...with arg #{wn}"
|
1549
|
+
end
|
1550
|
+
logger.debug "Resulting in call node #{wn_call}"
|
1551
|
+
# Drop last evaluated result if asked to or if
|
1552
|
+
# the method called doesn't return any value
|
1553
|
+
@wgenerator.drop(wnode) unless (keep_eval || wn_call.wtype.blank?)
|
1554
|
+
return wn_call
|
1555
|
+
end
|
1556
|
+
|
1557
|
+
def parse_type_args(hash_node, entity)
|
1558
|
+
types = {}
|
1559
|
+
# Is this a hash Node ?
|
1560
|
+
unless hash_node.respond_to?(:type) && hash_node.type == :hash
|
1561
|
+
raise "#{entity} expects a hash argument (got #{hash_node}" \
|
1562
|
+
end
|
1563
|
+
logger.debug "#{entity} hash node: #{hash_node}"
|
1564
|
+
hash_node.children.each do |pair_node|
|
1565
|
+
name_node, type_node = pair_node.children
|
1566
|
+
raise "The name of an #{entity} must be a symbol (got #{name_node})" \
|
1567
|
+
unless name_node.type == :sym
|
1568
|
+
raise "The type of an #{entity} must be a symbol (got #{type_node})" \
|
1569
|
+
unless type_node.type == :sym
|
1570
|
+
name = name_node.children.last
|
1571
|
+
type = type_node.children.last
|
1572
|
+
types[name] = type
|
1573
|
+
end
|
1574
|
+
types
|
1575
|
+
end
|
1576
|
+
|
1577
|
+
# Parse self. We should ge there only
|
1578
|
+
# when sis an object instance (not a class instance)
|
1579
|
+
def parse_self(node, wnode)
|
1580
|
+
if wnode.in_instance_method_scope?
|
1581
|
+
wn = @wgenerator._self(wnode)
|
1582
|
+
logger.debug "self in instance method scope"
|
1583
|
+
elsif wnode.in_class_method_scope?
|
1584
|
+
# Nothing to do just return nil
|
1585
|
+
logger.debug "self in class method scope"
|
1586
|
+
elsif wnode.in_class_scope?
|
1587
|
+
# Nothing to do just return nil
|
1588
|
+
logger.debug "self in class definition scope"
|
1589
|
+
else
|
1590
|
+
raise "Don't know what self means in this context: #{wnode}"
|
1591
|
+
end
|
1592
|
+
wn
|
1593
|
+
end
|
1594
|
+
|
1595
|
+
# Data value node can be either of type
|
1596
|
+
# int, str, send, array
|
1597
|
+
def parse_data_value(label, node)
|
1598
|
+
case node.type
|
1599
|
+
when :int, :str
|
1600
|
+
logger.debug "in :int/:str label #{label}, value #{node.children.last}"
|
1601
|
+
DAta.append(label, node.children.last)
|
1602
|
+
when :array
|
1603
|
+
node.children.each {|n| parse_data_value(label,n)}
|
1604
|
+
when :send
|
1605
|
+
recv_node,method_name,arg_node = *node.children
|
1606
|
+
logger.debug "in send: recv_node #{recv_node}, method_name #{method_name}"
|
1607
|
+
case method_name
|
1608
|
+
when :to_I64
|
1609
|
+
raise "Data type casting can only apply to int (got #{recv_node}" \
|
1610
|
+
unless recv_node.type == :int
|
1611
|
+
value = recv_node.children.last
|
1612
|
+
DAta.append(label, value, Type::I64)
|
1613
|
+
when :[]
|
1614
|
+
raise "Initializer can only be a Data address (got #{node})" \
|
1615
|
+
unless recv_node.children.last == :DAta
|
1616
|
+
raise "Initializer expects a symbol (got #{arg_node})" \
|
1617
|
+
unless arg_node.type == :sym
|
1618
|
+
DAta.append(label, DAta[arg_node.children.last])
|
1619
|
+
else
|
1620
|
+
raise "Unknow data initializer #{node}"
|
1621
|
+
end
|
1622
|
+
else
|
1623
|
+
raise "Unknow data initializer #{node}"
|
1624
|
+
end
|
1625
|
+
end
|
1626
|
+
|
1627
|
+
def parse_return(node, wnode, keep_eval)
|
1628
|
+
ret_count = node.children.count
|
1629
|
+
raise "only one or no value can be returned (got #{ret_count})" if ret_count > 1
|
1630
|
+
exp_node = node.children.first
|
1631
|
+
wn_ret = @wgenerator.return(wnode)
|
1632
|
+
if exp_node
|
1633
|
+
wn_exp = parse_node(exp_node, wn_ret)
|
1634
|
+
wn_ret.wtype = wn_exp.wtype
|
1635
|
+
else
|
1636
|
+
wn_ret.wtype = WType.new(:none)
|
1637
|
+
end
|
1638
|
+
wn_ret
|
1639
|
+
end
|
1640
|
+
|
1641
|
+
# Process the if then else conditional statement
|
1642
|
+
# (the else clause can be absent) with a result
|
1643
|
+
# type
|
1644
|
+
# Example
|
1645
|
+
# if|unless (result type) expression
|
1646
|
+
# ...
|
1647
|
+
# else
|
1648
|
+
# ...
|
1649
|
+
# end
|
1650
|
+
# ----------
|
1651
|
+
# (if
|
1652
|
+
# (sexp)
|
1653
|
+
# (then
|
1654
|
+
# ...
|
1655
|
+
# )
|
1656
|
+
# (else
|
1657
|
+
# ...
|
1658
|
+
# )
|
1659
|
+
def parse_if_without_result_type(node, wnode, keep_eval)
|
1660
|
+
cond_node, then_node, else_node = *node.children
|
1661
|
+
# process the if clause
|
1662
|
+
# always keep eval on stack for the if statement
|
1663
|
+
wn_if = @wgenerator.if(wnode)
|
1664
|
+
parse_node(cond_node, wn_if, true)
|
1665
|
+
|
1666
|
+
# process the then clause
|
1667
|
+
# DO NOT keep the last evaluated value
|
1668
|
+
# if then clause is nil it's probably
|
1669
|
+
# because it's actually an unless statement
|
1670
|
+
wn_then = @wgenerator.then(wn_if)
|
1671
|
+
if then_node
|
1672
|
+
parse_node(then_node, wn_then, false)
|
1673
|
+
else
|
1674
|
+
@wgenerator.nop(wn_then)
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
# The result type is always nil in this variant
|
1678
|
+
# of the parse_if_... method
|
1679
|
+
wn_if.wtype = WType.new(:nil); wn_then.wtype = WType.new(:nil)
|
1680
|
+
|
1681
|
+
# process the else clause if it exists
|
1682
|
+
# DO NOT keep the last evaluated value
|
1683
|
+
if else_node
|
1684
|
+
wn_else = @wgenerator.else(wn_if)
|
1685
|
+
parse_node(else_node, wn_else, false)
|
1686
|
+
wn_else.wtype = WType.new(:nil)
|
1687
|
+
end
|
1688
|
+
|
1689
|
+
# Drop last evaluated result if asked to
|
1690
|
+
# No need to drop the last evaluated value
|
1691
|
+
# here as the then and else branches do not
|
1692
|
+
# return any
|
1693
|
+
# @wgenerator.drop(wnode) unless keep_eval
|
1694
|
+
return wn_if
|
1695
|
+
end
|
1696
|
+
|
1697
|
+
# Process the if then else conditional statement
|
1698
|
+
# (the Ruby else clause is optional) with a result
|
1699
|
+
# type
|
1700
|
+
# Example
|
1701
|
+
# if (result type) expression
|
1702
|
+
# ...
|
1703
|
+
# else
|
1704
|
+
# ...
|
1705
|
+
# end
|
1706
|
+
# ----------
|
1707
|
+
# (if
|
1708
|
+
# (sexp)
|
1709
|
+
# (then
|
1710
|
+
# ...
|
1711
|
+
# )
|
1712
|
+
# (else
|
1713
|
+
# ...
|
1714
|
+
# )
|
1715
|
+
def parse_if_with_result_type(node, wnode, keep_eval)
|
1716
|
+
cond_node, then_node, else_node = *node.children
|
1717
|
+
# process the if clause
|
1718
|
+
wn_if = @wgenerator.if(wnode)
|
1719
|
+
parse_node(cond_node, wn_if, true) # always keep eval on stack
|
1720
|
+
# process the then clause
|
1721
|
+
wn_then = @wgenerator.then(wn_if)
|
1722
|
+
parse_node(then_node, wn_then, keep_eval)
|
1723
|
+
|
1724
|
+
# infer the result type of the if from the
|
1725
|
+
# the type of the then clause
|
1726
|
+
wn_then.wtype = wn_then.children.last.wtype
|
1727
|
+
wn_if.wtype = wn_then.wtype
|
1728
|
+
# Now that we know the result type
|
1729
|
+
# prepend it to the if children
|
1730
|
+
logger.debug("prepending result to wnode #{wn_if}")
|
1731
|
+
@wgenerator.result(wn_if) unless wn_if.wtype.blank?
|
1732
|
+
|
1733
|
+
# process the else clause if it exists
|
1734
|
+
wn_else = @wgenerator.else(wn_if)
|
1735
|
+
if else_node
|
1736
|
+
parse_node(else_node, wn_else, keep_eval)
|
1737
|
+
wn_else.wtype = wn_else.children.last.wtype
|
1738
|
+
if wn_then.wtype != wn_else.wtype
|
1739
|
+
raise "then and else clauses must return same wtype (got #{wn_then.wtype} and #{wn_else.wtype}"
|
1740
|
+
end
|
1741
|
+
else
|
1742
|
+
# if the else clause doesn't exist in Ruby we still
|
1743
|
+
# have to generate it in WAT because the else branch
|
1744
|
+
# **MUST** be there and return the same result type
|
1745
|
+
# as the then branch
|
1746
|
+
# In this case in Ruby the missing else clause would
|
1747
|
+
# cause the if statement to evaluate to nil if the
|
1748
|
+
# condition is false. Problem is "nil" doesn't exist in
|
1749
|
+
# Webassembly. For now let's return a "remarkable" value
|
1750
|
+
# (see NIL constant)
|
1751
|
+
# WARNING!! This is not satisfactory of course because
|
1752
|
+
# the then branch could one day return this exact same
|
1753
|
+
# value too
|
1754
|
+
#
|
1755
|
+
# A safer alternative (but not compliant with plain Ruby)
|
1756
|
+
# would be to assume that if-then-else in Rlang never
|
1757
|
+
# evaluates to a value (see method parse_if_without_result_type)
|
1758
|
+
@wgenerator.int(wn_else, wn_then.wtype, NIL)
|
1759
|
+
wn_else.wtype = wn_then.wtype
|
1760
|
+
end
|
1761
|
+
|
1762
|
+
# Drop last evaluated result if asked to
|
1763
|
+
@wgenerator.drop(wnode) unless keep_eval
|
1764
|
+
return wn_if
|
1765
|
+
end
|
1766
|
+
|
1767
|
+
# Example
|
1768
|
+
# (while (cond)
|
1769
|
+
# (body)
|
1770
|
+
# end
|
1771
|
+
# -----------
|
1772
|
+
# (block $lbl_xx
|
1773
|
+
# (loop $lbl_yy
|
1774
|
+
# (br_if $lbl_xx (ixx.eqz (cond)))
|
1775
|
+
# (body)
|
1776
|
+
# (br $lbl_yy)
|
1777
|
+
# )
|
1778
|
+
# )
|
1779
|
+
def parse_while_without_result_type(node, wnode, keep_eval)
|
1780
|
+
cond_node, body_node = *node.children
|
1781
|
+
wn_while,wn_while_cond,wn_body = @wgenerator.while(wnode)
|
1782
|
+
|
1783
|
+
# Parse the while condition...
|
1784
|
+
# Plus negate the condition if it's a while statement
|
1785
|
+
# Keep it as is for a until statement
|
1786
|
+
wn_cond_exp = parse_node(cond_node, wn_while_cond)
|
1787
|
+
if node.type == :while
|
1788
|
+
@wgenerator.while_cond(wn_while_cond, wn_cond_exp)
|
1789
|
+
end
|
1790
|
+
|
1791
|
+
# Parse the body of the while block and
|
1792
|
+
# do not keep the last evaluated expression
|
1793
|
+
parse_node(body_node, wn_body, false)
|
1794
|
+
@wgenerator.while_end(wn_body)
|
1795
|
+
return wn_while
|
1796
|
+
end
|
1797
|
+
|
1798
|
+
def parse_break(node, wnode, keep_eval)
|
1799
|
+
@wgenerator.break(wnode)
|
1800
|
+
end
|
1801
|
+
|
1802
|
+
def parse_next(node, wnode, keep_eval)
|
1803
|
+
@wgenerator.next(wnode)
|
1804
|
+
end
|
1805
|
+
|
1806
|
+
# Ruby || operator
|
1807
|
+
# Example
|
1808
|
+
# n==1 || n==2
|
1809
|
+
# ------
|
1810
|
+
# (or
|
1811
|
+
# (send (lvar :n) :== (int 1))
|
1812
|
+
# (send (lvar :n) :== (int 2))
|
1813
|
+
# )
|
1814
|
+
# Ruby && operator
|
1815
|
+
# Example
|
1816
|
+
# n==1 && m==3
|
1817
|
+
# ------
|
1818
|
+
# (and
|
1819
|
+
# (send (lvar :n) :== (int 1))
|
1820
|
+
# (send (lvar :m) :== (int 3))
|
1821
|
+
# )
|
1822
|
+
def parse_logical_op(node, wnode, keep_eval)
|
1823
|
+
logger.debug "logical operator section : #{node.type}"
|
1824
|
+
cond1_node, cond2_node = *node.children
|
1825
|
+
# Prepare the operator wnode
|
1826
|
+
wn_op = @wgenerator.operator(wnode, node.type)
|
1827
|
+
# Parse operand nodes and attach them to the
|
1828
|
+
# operator wnode
|
1829
|
+
wn_cond1 = parse_node(cond1_node, wn_op)
|
1830
|
+
wn_cond2 = parse_node(cond2_node, wn_op)
|
1831
|
+
@wgenerator.operands(wn_op, wn_cond1, [wn_cond2])
|
1832
|
+
# Drop last evaluated result if asked to
|
1833
|
+
@wgenerator.drop(wnode) unless keep_eval
|
1834
|
+
return wn_op
|
1835
|
+
end
|
1836
|
+
|
1837
|
+
def dump
|
1838
|
+
@ast
|
1839
|
+
end
|
1840
|
+
end
|
1841
|
+
|
1842
|
+
end
|