rlang 0.3.1 → 0.4.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 +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
|