rlang 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +7 -1
- data/README.md +21 -1
- data/bin/rlang +18 -53
- data/docs/RlangManual.md +65 -27
- data/examples/fib/fib.rb +11 -0
- data/examples/fib/index.html +38 -0
- data/examples/fib/server.rb +16 -0
- data/lib/builder/rlang.rb +1 -1
- data/lib/builder/rlang/builder.rb +41 -9
- data/lib/builder/rlang/compiler.rb +83 -0
- data/lib/builder/wat/builder.rb +12 -21
- data/lib/rlang/lib.rb +1 -1
- data/lib/rlang/lib/malloc.rb +9 -19
- data/lib/rlang/lib/object.rb +16 -0
- data/lib/rlang/lib/unistd.rb +22 -0
- data/lib/rlang/parser.rb +176 -33
- data/lib/rlang/parser/data.rb +5 -0
- data/lib/rlang/parser/export.rb +4 -0
- data/lib/rlang/parser/global.rb +4 -0
- data/lib/rlang/parser/ivar.rb +36 -0
- data/lib/rlang/parser/klass.rb +49 -0
- data/lib/rlang/parser/marg.rb +4 -0
- data/lib/rlang/parser/method.rb +8 -4
- data/lib/rlang/parser/wattr.rb +19 -6
- data/lib/rlang/parser/wgenerator.rb +146 -47
- data/lib/rlang/parser/wnode.rb +108 -60
- data/lib/rlang/parser/wtype.rb +1 -0
- data/lib/rlang/version.rb +1 -1
- data/rlang.gemspec +1 -0
- metadata +23 -2
@@ -0,0 +1,83 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Turns an RLang source file into a Wasm text
|
6
|
+
# code file (WAT)
|
7
|
+
require_relative '../../utils/log'
|
8
|
+
require_relative '../ext/tempfile'
|
9
|
+
require_relative '../../rlang/parser'
|
10
|
+
|
11
|
+
module Builder::Rlang
|
12
|
+
class Compiler
|
13
|
+
include Log
|
14
|
+
|
15
|
+
attr_reader :target
|
16
|
+
|
17
|
+
# WAT source frame
|
18
|
+
WAT_FRAME = %q{
|
19
|
+
;; Generated by Rlang compiler version %{version} on %{time}\n"
|
20
|
+
(module %{module}
|
21
|
+
(memory $0 %{memory_min} %{memory_max})
|
22
|
+
|
23
|
+
;; ======= EXPORTS =======
|
24
|
+
(export "memory" (memory $0))
|
25
|
+
%{exports}
|
26
|
+
|
27
|
+
;; ======= GLOBAL VARIABLES =======
|
28
|
+
%{globals}
|
29
|
+
|
30
|
+
;; ======= STATIC DATA =======
|
31
|
+
%{data}
|
32
|
+
|
33
|
+
;; ======= CODE =======
|
34
|
+
%{code}
|
35
|
+
)
|
36
|
+
}
|
37
|
+
|
38
|
+
# source: Path to Rlang file (.rb)
|
39
|
+
# target: Path to Wat file (.wat)
|
40
|
+
# options: Rlang parser options (parser.config)
|
41
|
+
def initialize(source, target, options={})
|
42
|
+
@source = source # Path to Rlang file (.rb)
|
43
|
+
@target = target # Path to Wat file (.wat)
|
44
|
+
@options = options # Rlang parser options (parser.config)
|
45
|
+
@temp_target = target.nil?
|
46
|
+
# Initialize parser and WAT code generator
|
47
|
+
@parser = Rlang::Parser::Parser.new(nil, @options)
|
48
|
+
@wgenerator = Rlang::Parser::WGenerator.new(@parser)
|
49
|
+
@parser.wgenerator = @wgenerator
|
50
|
+
end
|
51
|
+
|
52
|
+
# return true if everything went well, false otherwise
|
53
|
+
def compile
|
54
|
+
@parser.parse_file(@source)
|
55
|
+
# Write generated WAT code in a temp file if
|
56
|
+
# target file not given
|
57
|
+
# Do not delete temp file when closing
|
58
|
+
if @target
|
59
|
+
@tf = File.open(@target, 'r')
|
60
|
+
else
|
61
|
+
@tf = Tempfile.new([File.basename(@source), '.wat'])
|
62
|
+
@tf.persist!
|
63
|
+
@target = @tf.path
|
64
|
+
end
|
65
|
+
@tf.write(WAT_FRAME % {version: Rlang::VERSION,
|
66
|
+
time: Time.now,
|
67
|
+
module: @options[:module],
|
68
|
+
memory_min: @options[:memory_min],
|
69
|
+
memory_max: @options[:memory_max],
|
70
|
+
exports: Rlang::Parser::Export.transpile,
|
71
|
+
globals: Rlang::Parser::Global.transpile,
|
72
|
+
data: Rlang::Parser::DAta.transpile,
|
73
|
+
code: @wgenerator.root.transpile
|
74
|
+
})
|
75
|
+
@tf.close
|
76
|
+
true
|
77
|
+
end
|
78
|
+
|
79
|
+
def cleanup
|
80
|
+
File.unlink(@tf.path) if @temp_target
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/builder/wat/builder.rb
CHANGED
@@ -2,25 +2,29 @@
|
|
2
2
|
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
3
|
# All rights reserved.
|
4
4
|
|
5
|
+
require_relative '../../utils/log'
|
5
6
|
require_relative '../ext/tempfile'
|
7
|
+
require_relative './renderer'
|
6
8
|
|
7
9
|
module Builder::Wat
|
8
10
|
class Builder
|
11
|
+
include Log
|
9
12
|
|
10
13
|
@@wat_compiler = 'wat2wasm'
|
11
14
|
|
12
15
|
attr_reader :target, :source
|
13
16
|
|
14
|
-
def initialize(source, target
|
17
|
+
def initialize(source, target)
|
15
18
|
check_compiler
|
16
19
|
@source = source
|
17
|
-
|
18
|
-
|
19
|
-
if File.extname(source) == '.erb'
|
20
|
-
@wat_path = self.assemble
|
20
|
+
if target
|
21
|
+
@target = target
|
21
22
|
else
|
22
|
-
@
|
23
|
+
@target = @source.gsub(/\.wat$/,'.wasm')
|
24
|
+
@temp_target = true
|
23
25
|
end
|
26
|
+
logger.debug "Wat Builder Source: #{@source}"
|
27
|
+
logger.debug "Wat Builder Target: #{@target}"
|
24
28
|
end
|
25
29
|
|
26
30
|
def check_compiler
|
@@ -29,24 +33,11 @@ module Builder::Wat
|
|
29
33
|
end
|
30
34
|
|
31
35
|
def compile
|
32
|
-
@
|
33
|
-
%x{ #{@@wat_compiler} #{@wat_path} -o #{@target} }
|
34
|
-
@target
|
36
|
+
system("#{@@wat_compiler} #{@source} -o #{@target}")
|
35
37
|
end
|
36
38
|
|
37
39
|
def cleanup
|
38
|
-
File.unlink(@
|
39
|
-
end
|
40
|
-
|
41
|
-
# Create a tempfile with .wat extension from
|
42
|
-
# an erb template
|
43
|
-
def assemble
|
44
|
-
renderer = Renderer.new(@include_paths)
|
45
|
-
tf = Tempfile.new([File.basename(@source), '.wat'])
|
46
|
-
tf.persist! # do not delete tempfile if inspection needed
|
47
|
-
tf.write(renderer.render(@source))
|
48
|
-
tf.close
|
49
|
-
tf.path
|
40
|
+
File.unlink(@target) if @temp_target
|
50
41
|
end
|
51
42
|
end
|
52
43
|
end
|
data/lib/rlang/lib.rb
CHANGED
data/lib/rlang/lib/malloc.rb
CHANGED
@@ -11,23 +11,6 @@
|
|
11
11
|
require 'rlang/lib/memory'
|
12
12
|
require 'rlang/lib/unistd'
|
13
13
|
|
14
|
-
# These 3 global variables below are used by the
|
15
|
-
# dynamic memory allocator. They must however be
|
16
|
-
# defined in the end applications.
|
17
|
-
|
18
|
-
# Heap base address (make sure it's aligned on
|
19
|
-
# an address compatible with the most restrictive data type
|
20
|
-
# used in WASM (I64). So make this address a multiple of 8
|
21
|
-
# $HEAP = 10024
|
22
|
-
|
23
|
-
# Maximum amount of memory the heap can grow
|
24
|
-
# NOTE: must be less than the upper WASM memory limit (4GB)
|
25
|
-
# $HEAP_MAX_SIZE = 1073741824 # 1GB
|
26
|
-
|
27
|
-
# Current heap size (starts at 0, the first malloc will
|
28
|
-
# set it up)
|
29
|
-
# $HEAP_SIZE = 0
|
30
|
-
|
31
14
|
# minimum number of units to request
|
32
15
|
$NALLOC = 1024
|
33
16
|
|
@@ -75,7 +58,7 @@ class Malloc
|
|
75
58
|
# allocate memory by chunk of units (the unit is
|
76
59
|
# the size of a Header object here)
|
77
60
|
# units = (nbytes+sizeof(Header)-1)/sizeof(Header) + 1;
|
78
|
-
nunits = (nbytes + Header.
|
61
|
+
nunits = (nbytes + Header._size_ - 1) / Header._size_ + 1
|
79
62
|
|
80
63
|
# No free list yet. Initialize it.
|
81
64
|
if (prevp = @@freep) == 0
|
@@ -126,7 +109,7 @@ class Malloc
|
|
126
109
|
local up: :Header
|
127
110
|
|
128
111
|
nu = $NALLOC if nu < $NALLOC
|
129
|
-
cp = Unistd::sbrk(nu * Header.
|
112
|
+
cp = Unistd::sbrk(nu * Header._size_)
|
130
113
|
return 0 if cp == -1 # no space at all
|
131
114
|
|
132
115
|
up = cp.cast_to(:Header)
|
@@ -137,9 +120,16 @@ class Malloc
|
|
137
120
|
|
138
121
|
# Free memory block
|
139
122
|
def self.free(ap)
|
123
|
+
arg ap: :I32
|
140
124
|
result :none
|
141
125
|
local bp: :Header, p: :Header
|
142
126
|
|
127
|
+
# NULL is a special value used for
|
128
|
+
# all Rlag object instances that doesn't
|
129
|
+
# have any instance variables and therefore
|
130
|
+
# doesn't use any memory
|
131
|
+
return if ap == 0
|
132
|
+
|
143
133
|
bp = ap.cast_to(:Header) - 1 # point to block header
|
144
134
|
p = @@freep
|
145
135
|
while !(bp > p && bp < p.ptr)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require_relative './malloc'
|
2
|
+
|
3
|
+
class Object
|
4
|
+
# don't use allocate as a name to avoid
|
5
|
+
# colliding with Ruby native method in
|
6
|
+
# Rlang simulator
|
7
|
+
def self.alloc(nbytes)
|
8
|
+
result :I32
|
9
|
+
Malloc.malloc(nbytes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.free(object_ptr)
|
13
|
+
result :none
|
14
|
+
Malloc.free(object_ptr)
|
15
|
+
end
|
16
|
+
end
|
data/lib/rlang/lib/unistd.rb
CHANGED
@@ -7,7 +7,29 @@
|
|
7
7
|
# sbrk(n) returns the address of the allocated block or -1 if it failed
|
8
8
|
# srbk(0) returns the current value of the break
|
9
9
|
# Note: in WASM we can only grow linear memory by pages (64 KB block)
|
10
|
+
|
11
|
+
|
12
|
+
# These 3 global variables below are used by the
|
13
|
+
# dynamic memory allocator. You can redefine them
|
14
|
+
# in your own module
|
15
|
+
|
16
|
+
# Heap base address (make sure it's aligned on
|
17
|
+
# an address compatible with the most restrictive data type
|
18
|
+
# used in WASM (I64). So make this address a multiple of 8
|
19
|
+
$HEAP = 1024
|
20
|
+
|
21
|
+
# Maximum amount of memory the heap can grow
|
22
|
+
# NOTE: must be less than the upper WASM memory limit (4GB)
|
23
|
+
$HEAP_MAX_SIZE = 1073741824 # 1GB
|
24
|
+
|
25
|
+
# Current heap size (starts at 0, the first malloc will
|
26
|
+
# set it up)
|
27
|
+
$HEAP_SIZE = 0
|
28
|
+
|
10
29
|
class Unistd
|
30
|
+
result :Memory, :grow, :I32
|
31
|
+
result :Memory, :size, :I32
|
32
|
+
|
11
33
|
def self.sbrk(n)
|
12
34
|
# local variables used. All default type
|
13
35
|
# wasm_mem_size: current wasm memory size (in bytes)
|
data/lib/rlang/parser.rb
CHANGED
@@ -16,6 +16,7 @@ require_relative '../utils/log'
|
|
16
16
|
require_relative './parser/wtype'
|
17
17
|
require_relative './parser/wtree'
|
18
18
|
require_relative './parser/wnode'
|
19
|
+
require_relative './parser/ivar'
|
19
20
|
require_relative './parser/cvar'
|
20
21
|
require_relative './parser/lvar'
|
21
22
|
require_relative './parser/marg'
|
@@ -49,18 +50,32 @@ module Rlang::Parser
|
|
49
50
|
|
50
51
|
attr_accessor :wgenerator, :source, :config
|
51
52
|
|
52
|
-
def initialize(wgenerator)
|
53
|
+
def initialize(wgenerator, options={})
|
53
54
|
@wgenerator = wgenerator
|
54
55
|
# LIFO of parsed files (stacked by require)
|
55
56
|
@requires = []
|
56
|
-
config_init
|
57
|
+
config_init(options)
|
58
|
+
logger.level = Kernel.const_get("Logger::#{@config[:log_level].upcase}")
|
59
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
60
|
+
loc = caller_locations[3] # skip over the logger call itself
|
61
|
+
"#{severity[0]}: #{File.basename(loc.path)}:#{loc.lineno}##{loc.label} > #{msg}\n"
|
62
|
+
end
|
63
|
+
# reset all persistent objects
|
64
|
+
# TODO: change the design so those objects are
|
65
|
+
# stored with the parser instance and not in a
|
66
|
+
# class variable
|
67
|
+
Global.reset!
|
68
|
+
Export.reset!
|
69
|
+
DAta.reset!
|
57
70
|
end
|
58
71
|
|
59
|
-
def config_init
|
72
|
+
def config_init(options)
|
60
73
|
@config = {}
|
61
74
|
@config[:LOADED_FEATURES] = []
|
62
|
-
@config[:LOAD_PATH] =
|
75
|
+
@config[:LOAD_PATH] = []
|
63
76
|
@config[:__FILE__] = ''
|
77
|
+
@config[:log_level] = 'FATAL'
|
78
|
+
@config.merge!(options)
|
64
79
|
end
|
65
80
|
|
66
81
|
# Note : this method can be called recursively
|
@@ -91,9 +106,9 @@ module Rlang::Parser
|
|
91
106
|
end
|
92
107
|
end
|
93
108
|
|
94
|
-
def parse(source)
|
109
|
+
def parse(source, wnode=nil)
|
95
110
|
ast = ::Parser::CurrentRuby.parse(source)
|
96
|
-
parse_node(ast, @wgenerator.root) if ast
|
111
|
+
parse_node(ast, wnode || @wgenerator.root) if ast
|
97
112
|
end
|
98
113
|
|
99
114
|
# Parse Ruby AST node and generate WAT
|
@@ -125,6 +140,9 @@ module Rlang::Parser
|
|
125
140
|
when :casgn
|
126
141
|
wn = parse_casgn(node, wnode, keep_eval)
|
127
142
|
|
143
|
+
when :ivasgn
|
144
|
+
wn = parse_ivasgn(node, wnode, keep_eval)
|
145
|
+
|
128
146
|
when :cvasgn
|
129
147
|
wn = parse_cvasgn(node, wnode, keep_eval)
|
130
148
|
|
@@ -140,8 +158,8 @@ module Rlang::Parser
|
|
140
158
|
when :lvar
|
141
159
|
wn = parse_lvar(node, wnode, keep_eval)
|
142
160
|
|
143
|
-
when :ivar
|
144
|
-
|
161
|
+
when :ivar
|
162
|
+
wn = parse_ivar(node, wnode, keep_eval)
|
145
163
|
|
146
164
|
when :cvar
|
147
165
|
wn = parse_cvar(node, wnode, keep_eval)
|
@@ -237,12 +255,19 @@ module Rlang::Parser
|
|
237
255
|
raise "expecting a constant for class name (got #{const_node})" \
|
238
256
|
unless const_node.type == :const
|
239
257
|
|
258
|
+
# create the class wnode
|
240
259
|
wn_class = @wgenerator.klass(wnode, const_node.children.last)
|
260
|
+
|
261
|
+
# Parse the body of the class
|
241
262
|
parse_node(body_node, wn_class) if body_node
|
242
263
|
|
243
|
-
#
|
244
|
-
#
|
245
|
-
|
264
|
+
# We finished parsing the class body so
|
265
|
+
# 1) generate the wnodes for the new/initialize function
|
266
|
+
# 2) generate the wnodes for accessing attributes
|
267
|
+
@wgenerator.def_initialize(wn_class) # generate **BEFORE** new
|
268
|
+
@wgenerator.def_new(wn_class)
|
269
|
+
@wgenerator.def_wattr(wn_class)
|
270
|
+
|
246
271
|
return wn_class
|
247
272
|
end
|
248
273
|
|
@@ -270,6 +295,11 @@ module Rlang::Parser
|
|
270
295
|
# s(:op_asgn,
|
271
296
|
# s(:gvasgn, :$MYGLOBAL), :-, s(:lvar, :nbytes))
|
272
297
|
#
|
298
|
+
# Example (instance var)
|
299
|
+
# @stack_ptr -= nbytes
|
300
|
+
# ---
|
301
|
+
# s(:op_asgn,
|
302
|
+
# s(:ivasgn, :@stack_ptr), :-, s(:lvar, :nbytes))
|
273
303
|
#
|
274
304
|
# Example (setter/getter)
|
275
305
|
# p.size -= nunits
|
@@ -344,6 +374,44 @@ module Rlang::Parser
|
|
344
374
|
# Create the var getter node as a child of operator node
|
345
375
|
wn_var_get = @wgenerator.lvar(wn_op, lvar)
|
346
376
|
|
377
|
+
# Instance variable case
|
378
|
+
# Example (instance var)
|
379
|
+
# @stack_ptr -= nbytes
|
380
|
+
# ---
|
381
|
+
# s(:op_asgn,
|
382
|
+
# s(:ivasgn, :@stack_ptr), :-, s(:lvar, :nbytes))
|
383
|
+
when :ivasgn
|
384
|
+
var_asgn_node, op, exp_node = *node.children
|
385
|
+
var_name = var_asgn_node.children.last
|
386
|
+
|
387
|
+
# To op_asgn to work, ivar must already be declared
|
388
|
+
wattr = wnode.find_ivar(var_name)
|
389
|
+
raise "Unknown instance variable #{var_name}" unless wattr
|
390
|
+
|
391
|
+
# Instance variable op_asgn case is actually like
|
392
|
+
# the getter/setter case below where the receiver
|
393
|
+
# wnode is self
|
394
|
+
wn_recv = parse_self(node, wnode)
|
395
|
+
|
396
|
+
# Create the top level variable setter node
|
397
|
+
wn_var_set = @wgenerator.ivasgn(wnode, wn_recv, wattr)
|
398
|
+
|
399
|
+
# Second argument of the setter is the operator wnode
|
400
|
+
# Create it with wtype :none for now. We'll fix that
|
401
|
+
# with the operands call later on
|
402
|
+
wn_op = @wgenerator.operator(wn_var_set, op)
|
403
|
+
|
404
|
+
# now create the getter node as the first child of the
|
405
|
+
# operator
|
406
|
+
wn_var_get = @wgenerator.ivar(wnode, wn_recv, wattr)
|
407
|
+
|
408
|
+
# If the setter returns something and last evaluated value
|
409
|
+
# must be ignored then drop it
|
410
|
+
unless keep_eval && !wn_var_set.wtype.blank?
|
411
|
+
@wgenerator.drop(wnode)
|
412
|
+
#@wgenerator.call(wnode, wn_recv.wtype.name, "#{method_name}", :instance)
|
413
|
+
end
|
414
|
+
|
347
415
|
# setter/getter case
|
348
416
|
# Example (setter/getter)
|
349
417
|
# p.size -= nunits
|
@@ -360,13 +428,13 @@ module Rlang::Parser
|
|
360
428
|
# above to get its wtype
|
361
429
|
# Force keep_eval to true whatever upper level
|
362
430
|
# keep_eval says
|
363
|
-
|
431
|
+
wn_recv = parse_node(recv_node, wnode, true)
|
364
432
|
|
365
433
|
# Create the top level setter call
|
366
|
-
wn_var_set = @wgenerator.call(wnode,
|
434
|
+
wn_var_set = @wgenerator.call(wnode, wn_recv.wtype.name, "#{method_name}=", :instance)
|
367
435
|
|
368
436
|
# First argument of the setter must be the recv_node
|
369
|
-
|
437
|
+
wn_recv.reparent_to(wn_var_set)
|
370
438
|
|
371
439
|
# Second argument of the setter is the operator wnode
|
372
440
|
# Create it with wtype :none for now. We'll fix that
|
@@ -384,7 +452,7 @@ module Rlang::Parser
|
|
384
452
|
# must be ignored then drop it
|
385
453
|
unless keep_eval && !wn_var_set.wtype.blank?
|
386
454
|
@wgenerator.drop(wnode)
|
387
|
-
#@wgenerator.call(wnode,
|
455
|
+
#@wgenerator.call(wnode, wn_recv.wtype.name, "#{method_name}", :instance)
|
388
456
|
end
|
389
457
|
else
|
390
458
|
raise "op_asgn not supported for #{node.children.first}"
|
@@ -472,7 +540,6 @@ module Rlang::Parser
|
|
472
540
|
else
|
473
541
|
# If we are at root or in class scope
|
474
542
|
# then it is a global variable initialization
|
475
|
-
raise "Global #{gv_name} already declared" if gvar
|
476
543
|
raise "Global op_asgn can only happen in method scope" unless exp_node
|
477
544
|
# In the class or root scope
|
478
545
|
# it can only be a Global var **declaration**
|
@@ -486,12 +553,61 @@ module Rlang::Parser
|
|
486
553
|
raise "Global initializer can only be a straight number" \
|
487
554
|
unless wn_exp.const?
|
488
555
|
wnode.remove_child(wn_exp)
|
489
|
-
gvar
|
556
|
+
if gvar
|
557
|
+
gvar.value = wn_exp.wargs[:value]
|
558
|
+
else
|
559
|
+
gvar = Global.new(gv_name, wn_exp.wtype, wn_exp.wargs[:value])
|
560
|
+
end
|
490
561
|
# Do not export global for now
|
491
562
|
#gvar.export! if self.config[:export_all]
|
492
563
|
end
|
493
564
|
end
|
494
565
|
|
566
|
+
|
567
|
+
# Example
|
568
|
+
# @stack_ptr = 10 + nbytes
|
569
|
+
# ---
|
570
|
+
# s(:ivasgn, :@stack_ptr, s(:send, s(:int, 10), :+, s(:lvar, :nbytes)))
|
571
|
+
def parse_ivasgn(node, wnode, keep_eval)
|
572
|
+
iv_name, exp_node = *node.children
|
573
|
+
|
574
|
+
raise "Instance variable #{iv_name} can only be used in method scope" \
|
575
|
+
unless wnode.in_method_scope?
|
576
|
+
|
577
|
+
unless (wattr = wnode.find_ivar(iv_name))
|
578
|
+
# first ivar occurence, create it
|
579
|
+
wattr = wnode.create_ivar(iv_name)
|
580
|
+
new_ivar = true
|
581
|
+
end
|
582
|
+
|
583
|
+
# ivar assignment is like calling the corresponding
|
584
|
+
# setter on self. So create self wnode first
|
585
|
+
wn_recv = parse_self(node, wnode)
|
586
|
+
wn_ivasgn = @wgenerator.ivasgn(wnode, wn_recv, wattr)
|
587
|
+
|
588
|
+
# Second argument is the expression
|
589
|
+
wn_exp = parse_node(exp_node, wn_ivasgn)
|
590
|
+
|
591
|
+
if new_ivar
|
592
|
+
# type cast the wattr and its ivar to the wtype
|
593
|
+
# of the expression as this its first occurence
|
594
|
+
logger.debug "Setting new ivar #{wattr.name} wtype to #{wn_exp.wtype.name}"
|
595
|
+
wattr.wtype = wn_exp.wtype
|
596
|
+
else
|
597
|
+
# if ivar already exists then type cast the
|
598
|
+
# expression to the wtype of the existing ivar
|
599
|
+
logger.debug "Casting exp. wtype #{wn_exp.wtype} to existing ivar #{wattr.name} wtype #{wattr.wtype}"
|
600
|
+
@wgenerator.cast(wn_exp, wattr.wtype, false)
|
601
|
+
end
|
602
|
+
|
603
|
+
# If the setter returns something and last evaluated value
|
604
|
+
# must be ignored then drop it
|
605
|
+
unless keep_eval && !wn_ivasgn.wtype.blank?
|
606
|
+
@wgenerator.drop(wnode)
|
607
|
+
end
|
608
|
+
return wn_ivasgn
|
609
|
+
end
|
610
|
+
|
495
611
|
# Example
|
496
612
|
# @@stack_ptr = 10 + nbytes
|
497
613
|
# ---
|
@@ -602,6 +718,25 @@ module Rlang::Parser
|
|
602
718
|
return wn_gvar
|
603
719
|
end
|
604
720
|
|
721
|
+
# Example
|
722
|
+
# ... @stack_ptr
|
723
|
+
# ---
|
724
|
+
# ... s(:ivar, :@stack_ptr)
|
725
|
+
def parse_ivar(node, wnode, keep_eval)
|
726
|
+
raise "Instance variable can only be accessed in method scope" \
|
727
|
+
unless wnode.in_method_scope?
|
728
|
+
iv_name, = *node.children
|
729
|
+
if (wattr = wnode.find_ivar(iv_name))
|
730
|
+
wn_recv = parse_self(node, wnode)
|
731
|
+
wn_ivar = @wgenerator.ivar(wnode, wn_recv, wattr)
|
732
|
+
else
|
733
|
+
raise "unknown instance variable #{cv_name}"
|
734
|
+
end
|
735
|
+
# Drop last evaluated result if asked to
|
736
|
+
@wgenerator.drop(wnode) unless keep_eval
|
737
|
+
return wn_ivar
|
738
|
+
end
|
739
|
+
|
605
740
|
# Example
|
606
741
|
# ... @@stack_ptr
|
607
742
|
# ---
|
@@ -730,9 +865,9 @@ module Rlang::Parser
|
|
730
865
|
logger.debug "recv_node: #{recv_node}\nmethod_name: #{method_name}"
|
731
866
|
|
732
867
|
# create corresponding func node
|
733
|
-
method = wnode.find_or_create_method(method_name, nil, :class)
|
868
|
+
method = wnode.find_or_create_method(method_name, nil, nil, :class)
|
734
869
|
method.export! if (@@export || self.config[:export_all])
|
735
|
-
logger.debug "Method object : #{method
|
870
|
+
logger.debug "Method object : #{method}"
|
736
871
|
wn_method = @wgenerator.class_method(wnode, method)
|
737
872
|
# collect method arguments
|
738
873
|
parse_args(arg_nodes, wn_method)
|
@@ -778,11 +913,10 @@ module Rlang::Parser
|
|
778
913
|
logger.debug "method_name: #{method_name}"
|
779
914
|
|
780
915
|
# create corresponding func node
|
781
|
-
method = wnode.find_or_create_method(method_name, nil, :instance)
|
916
|
+
method = wnode.find_or_create_method(method_name, nil, nil, :instance)
|
782
917
|
method.export! if (@@export || self.config[:export_all])
|
783
|
-
logger.debug "Method object : #{method
|
918
|
+
logger.debug "Method object : #{method}"
|
784
919
|
wn_method = @wgenerator.instance_method(wnode, method)
|
785
|
-
# add a receiver argument
|
786
920
|
|
787
921
|
# collect method arguments
|
788
922
|
parse_args(arg_nodes, wn_method)
|
@@ -821,7 +955,10 @@ module Rlang::Parser
|
|
821
955
|
logger.debug "Absolute path detected"
|
822
956
|
extensions.each do |ext|
|
823
957
|
full_path_file = file+ext
|
824
|
-
|
958
|
+
if File.file?(full_path_file)
|
959
|
+
logger.debug "Found required file: #{full_path_file}"
|
960
|
+
break
|
961
|
+
end
|
825
962
|
end
|
826
963
|
else
|
827
964
|
case file
|
@@ -1219,13 +1356,18 @@ module Rlang::Parser
|
|
1219
1356
|
raise "result directive expects a symbol argument (got #{result_type})" \
|
1220
1357
|
unless result_type.is_a? Symbol
|
1221
1358
|
wnode.method_wnode.wtype = WType.new(result_type)
|
1222
|
-
logger.debug "result_type #{result_type} updated for method #{wnode.method_wnode.method
|
1359
|
+
logger.debug "result_type #{result_type} updated for method #{wnode.method_wnode.method}"
|
1223
1360
|
elsif wnode.in_class_scope?
|
1224
1361
|
cn_name, = *node.children[2]
|
1225
1362
|
mn_name, = *node.children[3]
|
1226
1363
|
result_type, = *node.children[4]
|
1364
|
+
raise "result directive expects a symbol argument (got #{result_type}) in node #{node}" \
|
1365
|
+
unless result_type.is_a? Symbol
|
1366
|
+
# Create class and method objects as we known we'll
|
1367
|
+
# be calling them later on
|
1368
|
+
WNode.root.find_or_create_class(cn_name)
|
1227
1369
|
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)
|
1370
|
+
(mwn = wnode.find_or_create_method(mn_name, cn_name, nil, method_type)).wtype = WType.new(result_type)
|
1229
1371
|
logger.debug "result_type #{mwn.wtype} for method #{mwn.name}"
|
1230
1372
|
else
|
1231
1373
|
raise "result declaration not supported #{wn.scope} scope"
|
@@ -1286,7 +1428,8 @@ module Rlang::Parser
|
|
1286
1428
|
wattr_types.each do |name, wtype|
|
1287
1429
|
if (wattr = wnode.find_wattr(name))
|
1288
1430
|
logger.debug "Setting wattr #{name} type to #{wtype}"
|
1289
|
-
|
1431
|
+
# TODO find a way to update both wtype at once
|
1432
|
+
wattr.wtype = wattr.ivar.wtype = WType.new(wtype)
|
1290
1433
|
else
|
1291
1434
|
raise "Unknown class attribute #{name} in #{wnode}"
|
1292
1435
|
end
|
@@ -1504,14 +1647,12 @@ module Rlang::Parser
|
|
1504
1647
|
raise "Can only call method class on self or class objects (got #{recv_node} in node #{node})"
|
1505
1648
|
end
|
1506
1649
|
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?
|
1650
|
+
if method_name == :new && wnode.in_class_scope?
|
1510
1651
|
# This is class object instantiation. Statically
|
1511
1652
|
# allocated though. So it can only happen in the
|
1512
1653
|
# class scope for a class variable or a constant
|
1513
1654
|
# Returns a wnode with a i32.const containing the address
|
1514
|
-
wn_addr = @wgenerator.
|
1655
|
+
wn_addr = @wgenerator.static_new(wnode, class_name)
|
1515
1656
|
return wn_addr
|
1516
1657
|
else
|
1517
1658
|
wn_call = @wgenerator.call(wnode, class_name, method_name, :class)
|
@@ -1578,14 +1719,16 @@ module Rlang::Parser
|
|
1578
1719
|
# when sis an object instance (not a class instance)
|
1579
1720
|
def parse_self(node, wnode)
|
1580
1721
|
if wnode.in_instance_method_scope?
|
1581
|
-
wn = @wgenerator.
|
1722
|
+
wn = @wgenerator._self_(wnode)
|
1582
1723
|
logger.debug "self in instance method scope"
|
1583
1724
|
elsif wnode.in_class_method_scope?
|
1584
1725
|
# Nothing to do just return nil
|
1585
|
-
|
1726
|
+
# TODO: not sure this is the right thing to do. Double check
|
1727
|
+
logger.debug "self in class method scope. Nothing to do."
|
1586
1728
|
elsif wnode.in_class_scope?
|
1587
1729
|
# Nothing to do just return nil
|
1588
|
-
|
1730
|
+
# TODO: not sure this is the right thing to do. Double check
|
1731
|
+
logger.debug "self in class definition scope. Nothing to do."
|
1589
1732
|
else
|
1590
1733
|
raise "Don't know what self means in this context: #{wnode}"
|
1591
1734
|
end
|