akaza 0.2.0 → 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 +4 -4
- data/.gitignore +0 -1
- data/README.md +6 -0
- data/akaza.gemspec +1 -0
- data/doc/ruby2ws.md +237 -0
- data/exe/akaza +22 -0
- data/lib/akaza/ast_ext.rb +11 -6
- data/lib/akaza/parser.rb +22 -22
- data/lib/akaza/ruby2ws.rb +1558 -166
- data/lib/akaza/ruby2ws/prelude.rb +80 -0
- data/lib/akaza/version.rb +1 -1
- data/lib/akaza/vm.rb +40 -40
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2acfb98593d59509b9e04d79bc2caad1a69ad0a73608b0046b401b3417e8a8a5
|
4
|
+
data.tar.gz: a7cc5518798a5ee863cf1e34e09ef95f5b8143ed47a0f9bc36b66a9d010b358e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80bf448885f9b9b49bf730945a9d0eceff3ae51c01c922fd6551f2a5a4bb8cdd159936bc09f2747eb23ac13772095915cdb5ef2aaedb884133d88a9fe80658c3
|
7
|
+
data.tar.gz: a147e1a53eee6f41d65238fdf1edefc656fb470e322b723f9bb07e25ec097f9c37b82f279ded9df3bf020d8d2e236c178f2c905e48e59af94fafef460fc6aa6a
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -124,6 +124,12 @@ a.sum(input, output)
|
|
124
124
|
p output.string # => "42"
|
125
125
|
```
|
126
126
|
|
127
|
+
## Convert Ruby to Whitespace
|
128
|
+
|
129
|
+
You can convert Ruby to Whitespace. It is an easy way to write Whitespace.
|
130
|
+
|
131
|
+
See https://github.com/pocke/akaza/blob/master/doc/ruby2ws.md
|
132
|
+
|
127
133
|
## Development
|
128
134
|
|
129
135
|
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/akaza.gemspec
CHANGED
data/doc/ruby2ws.md
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
Wsrb
|
2
|
+
===
|
3
|
+
|
4
|
+
Wsrb is a language to write whitespace easily.
|
5
|
+
It is a subset of Ruby syntactically. It is almost a subset of Ruby semantically.
|
6
|
+
|
7
|
+
Usage
|
8
|
+
---
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
# test.ws.rb
|
12
|
+
def calc(x, y)
|
13
|
+
x + y
|
14
|
+
end
|
15
|
+
|
16
|
+
put_as_number(calc(1, 2))
|
17
|
+
```
|
18
|
+
|
19
|
+
```bash
|
20
|
+
$ akaza wsrb test.ws.rb > test.ws
|
21
|
+
$ akaza exec test.ws # => 3
|
22
|
+
```
|
23
|
+
|
24
|
+
|
25
|
+
Supported feature
|
26
|
+
---
|
27
|
+
|
28
|
+
|
29
|
+
|
30
|
+
### Differences from Ruby
|
31
|
+
|
32
|
+
|
33
|
+
#### built-in functions
|
34
|
+
|
35
|
+
Wsrb has methods that are not included in Ruby.
|
36
|
+
|
37
|
+
* `get_as_char`
|
38
|
+
* `get_as_number`
|
39
|
+
* `put_as_char`
|
40
|
+
* `put_as_number`
|
41
|
+
|
42
|
+
|
43
|
+
#### Character equals Integer
|
44
|
+
|
45
|
+
Wsrb does not distinguish between character and integer, like C language.
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
### Method definition and call
|
50
|
+
|
51
|
+
You can define method at the top level.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
def put_3_times(ch)
|
55
|
+
put_as_char ch
|
56
|
+
put_as_char ch
|
57
|
+
put_as_char ch
|
58
|
+
end
|
59
|
+
|
60
|
+
put_3_times('a') # => aaa
|
61
|
+
```
|
62
|
+
|
63
|
+
It only allows argument without default value. It also does not allow keyword argument, rest argument, and so on.
|
64
|
+
|
65
|
+
Arguments number is not checked.
|
66
|
+
|
67
|
+
|
68
|
+
### Extend existing classes
|
69
|
+
|
70
|
+
You can define instance methods for existing classes, that are `Array`, `Hash` and `Integer`.
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
class Hash
|
74
|
+
def fetch(key)
|
75
|
+
self[key]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
More examples: https://github.com/pocke/akaza/blob/master/lib/akaza/ruby2ws/prelude.rb
|
81
|
+
That is built-in methods definitions with the extending class feature.
|
82
|
+
|
83
|
+
Note: You cannot extend other classes, such as `TrueClass`.
|
84
|
+
|
85
|
+
### Literals
|
86
|
+
|
87
|
+
You can use Integer and Character literal.
|
88
|
+
Character literal is a String literal, but it has only one character.
|
89
|
+
And character will be converted to Integer that is based on code point implicitly.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
put_as_char 'A' # => A
|
93
|
+
put_as_number 'A' # => 65
|
94
|
+
put_as_number 42 # => 42
|
95
|
+
```
|
96
|
+
|
97
|
+
### Array
|
98
|
+
|
99
|
+
You can use Array
|
100
|
+
|
101
|
+
Must not access to out of range of array.
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
x = [1, 2, 3]
|
105
|
+
put_as_number x[0]
|
106
|
+
```
|
107
|
+
|
108
|
+
It support only a few methods.
|
109
|
+
|
110
|
+
* `push`
|
111
|
+
* `unshift`
|
112
|
+
* `[]`
|
113
|
+
* It does not support negative index. Use `array[array.size - 1]` instead of `array[-1]`. (Implementing pull request is welcome!)
|
114
|
+
* `[]=`
|
115
|
+
* It works only with existing index. Use `Array#push` to add a value to not initialized place.
|
116
|
+
* `first`
|
117
|
+
* `size`
|
118
|
+
|
119
|
+
### Hash
|
120
|
+
|
121
|
+
You can use Hash.
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
x = {
|
125
|
+
1 => 2,
|
126
|
+
3 => 4,
|
127
|
+
}
|
128
|
+
|
129
|
+
x[5] = 6
|
130
|
+
put_as_number x[1] # => 2
|
131
|
+
x[100] # => nil
|
132
|
+
```
|
133
|
+
|
134
|
+
It supports only a few methods.
|
135
|
+
|
136
|
+
* `[]`
|
137
|
+
* `[]=`
|
138
|
+
|
139
|
+
### Local variables
|
140
|
+
|
141
|
+
You can use local variables.
|
142
|
+
|
143
|
+
```
|
144
|
+
x = 1
|
145
|
+
|
146
|
+
def foo
|
147
|
+
x = 2
|
148
|
+
put_as_number x
|
149
|
+
end
|
150
|
+
|
151
|
+
foo # => 2
|
152
|
+
put_as_number x # => 1
|
153
|
+
```
|
154
|
+
|
155
|
+
|
156
|
+
### if / unless
|
157
|
+
|
158
|
+
You can use `if` and `unless`.
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
x = 0
|
162
|
+
if x == 0
|
163
|
+
put_as_number x
|
164
|
+
else
|
165
|
+
put_as_number 2
|
166
|
+
end
|
167
|
+
|
168
|
+
put_as_number 3 unless x < 0
|
169
|
+
```
|
170
|
+
|
171
|
+
|
172
|
+
### while
|
173
|
+
|
174
|
+
You can use `while`.
|
175
|
+
|
176
|
+
```ruby
|
177
|
+
x = -10
|
178
|
+
while x < 0
|
179
|
+
put_as_number x
|
180
|
+
x = x + 1
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
### add, sub, multi, div, mod
|
185
|
+
|
186
|
+
You can use operators, that are `+`, `-`, `*`, `/` and `%`.
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
put_as_number 1 + 2
|
190
|
+
put_as_number 1 - 2
|
191
|
+
put_as_number 1 * 2
|
192
|
+
put_as_number 4 / 2
|
193
|
+
put_as_number 10 % 3
|
194
|
+
```
|
195
|
+
|
196
|
+
### exception
|
197
|
+
|
198
|
+
You can use `raise` method to raise an exception.
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
raise "This is error message"
|
202
|
+
```
|
203
|
+
|
204
|
+
Program will exit by `raise` method with error message.
|
205
|
+
But the exit status is `0`.
|
206
|
+
|
207
|
+
|
208
|
+
Implementation
|
209
|
+
---
|
210
|
+
|
211
|
+
### Array
|
212
|
+
|
213
|
+
An Array object uses three heap.
|
214
|
+
|
215
|
+
* address to the first item
|
216
|
+
* Array size
|
217
|
+
* capacity
|
218
|
+
|
219
|
+
|
220
|
+
|
221
|
+
### Hash
|
222
|
+
|
223
|
+
It is implemented with Hash table and use chaining to resolve collision.
|
224
|
+
|
225
|
+
|
226
|
+
A Hash object uses one heap.
|
227
|
+
|
228
|
+
* address to the first item
|
229
|
+
|
230
|
+
An item uses three heaps.
|
231
|
+
|
232
|
+
* key
|
233
|
+
* value
|
234
|
+
* address to the next chain
|
235
|
+
|
236
|
+
On initialize, it reserve `HASH_SIZE * 3` heaps. And it sets `NONE` to all keys.
|
237
|
+
Value and address is not initialized. Do not access these heaps before initializing.
|
data/exe/akaza
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!ruby
|
2
|
+
|
3
|
+
require 'akaza'
|
4
|
+
|
5
|
+
# TODO: Move it to Akaza::CLI
|
6
|
+
|
7
|
+
def usage
|
8
|
+
puts "Usage: akaza exec|wsrb|exec_wsrb FILE_NAME"
|
9
|
+
end
|
10
|
+
|
11
|
+
case ARGV
|
12
|
+
in ['exec', path]
|
13
|
+
Akaza.eval(File.read(path))
|
14
|
+
in ['wsrb', path]
|
15
|
+
print Akaza::Ruby2ws.ruby_to_ws(File.read(path), path: path)
|
16
|
+
in ['exec_wsrb', path]
|
17
|
+
ws = Akaza::Ruby2ws.ruby_to_ws(File.read(path), path: path)
|
18
|
+
Akaza.eval(ws)
|
19
|
+
else
|
20
|
+
usage
|
21
|
+
exit 1
|
22
|
+
end
|
data/lib/akaza/ast_ext.rb
CHANGED
@@ -6,12 +6,9 @@ module Akaza
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def traverse(&on_enter)
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
children.each do |child|
|
13
|
-
child.traverse(&on_enter) if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
14
|
-
end
|
9
|
+
on_enter.call self
|
10
|
+
children.each do |child|
|
11
|
+
child.traverse(&on_enter) if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
|
15
12
|
end
|
16
13
|
end
|
17
14
|
|
@@ -36,6 +33,14 @@ module Akaza
|
|
36
33
|
children[1]
|
37
34
|
end
|
38
35
|
|
36
|
+
# :WHEN ext
|
37
|
+
|
38
|
+
def each_when(&block)
|
39
|
+
block.call(self)
|
40
|
+
next_when = self.children[2]
|
41
|
+
next_when&.each_when(&block)
|
42
|
+
end
|
43
|
+
|
39
44
|
def first_index(path)
|
40
45
|
return first_column if first_lineno == 1
|
41
46
|
|
data/lib/akaza/parser.rb
CHANGED
@@ -43,15 +43,15 @@ module Akaza
|
|
43
43
|
private def parse_stack
|
44
44
|
case ch = nextc
|
45
45
|
when SPACE
|
46
|
-
[:
|
46
|
+
[:stack_push, nextint]
|
47
47
|
else
|
48
48
|
case c = [ch, nextc]
|
49
49
|
when [NL, SPACE]
|
50
|
-
[:
|
50
|
+
[:stack_dup]
|
51
51
|
when [NL, TAB]
|
52
|
-
[:
|
52
|
+
[:stack_swap]
|
53
53
|
when [NL, NL]
|
54
|
-
[:
|
54
|
+
[:stack_pop]
|
55
55
|
else
|
56
56
|
raise "unreachable: #{c}"
|
57
57
|
end
|
@@ -61,19 +61,19 @@ module Akaza
|
|
61
61
|
private def parse_flow
|
62
62
|
case c = [nextc, nextc]
|
63
63
|
when [SPACE, SPACE]
|
64
|
-
[:
|
64
|
+
[:flow_def, nextlabel]
|
65
65
|
when [SPACE, TAB]
|
66
|
-
[:
|
66
|
+
[:flow_call, nextlabel]
|
67
67
|
when [SPACE, NL]
|
68
|
-
[:
|
68
|
+
[:flow_jump, nextlabel]
|
69
69
|
when [TAB, SPACE]
|
70
|
-
[:
|
70
|
+
[:flow_jump_if_zero, nextlabel]
|
71
71
|
when [TAB, TAB]
|
72
|
-
[:
|
72
|
+
[:flow_jump_if_neg, nextlabel]
|
73
73
|
when [TAB, NL]
|
74
|
-
[:
|
74
|
+
[:flow_end]
|
75
75
|
when [NL, NL]
|
76
|
-
[:
|
76
|
+
[:flow_exit]
|
77
77
|
else
|
78
78
|
raise "unreachable: #{c}"
|
79
79
|
end
|
@@ -82,15 +82,15 @@ module Akaza
|
|
82
82
|
private def parse_calc
|
83
83
|
case c = [nextc, nextc]
|
84
84
|
when [SPACE, SPACE]
|
85
|
-
[:
|
85
|
+
[:calc_add]
|
86
86
|
when [SPACE, TAB]
|
87
|
-
[:
|
87
|
+
[:calc_sub]
|
88
88
|
when [SPACE, NL]
|
89
|
-
[:
|
89
|
+
[:calc_multi]
|
90
90
|
when [TAB, SPACE]
|
91
|
-
[:
|
91
|
+
[:calc_div]
|
92
92
|
when [TAB, TAB]
|
93
|
-
[:
|
93
|
+
[:calc_mod]
|
94
94
|
else
|
95
95
|
raise "unreachable: #{c}"
|
96
96
|
end
|
@@ -99,9 +99,9 @@ module Akaza
|
|
99
99
|
private def parse_heap
|
100
100
|
case c = nextc
|
101
101
|
when SPACE
|
102
|
-
[:
|
102
|
+
[:heap_save]
|
103
103
|
when TAB
|
104
|
-
[:
|
104
|
+
[:heap_load]
|
105
105
|
else
|
106
106
|
raise "unreachable: #{c}"
|
107
107
|
end
|
@@ -110,13 +110,13 @@ module Akaza
|
|
110
110
|
private def parse_io
|
111
111
|
case c = [nextc, nextc]
|
112
112
|
when [SPACE, SPACE]
|
113
|
-
[:
|
113
|
+
[:io_write_char]
|
114
114
|
when [SPACE, TAB]
|
115
|
-
[:
|
115
|
+
[:io_write_num]
|
116
116
|
when [TAB, SPACE]
|
117
|
-
[:
|
117
|
+
[:io_read_char]
|
118
118
|
when [TAB, TAB]
|
119
|
-
[:
|
119
|
+
[:io_read_num]
|
120
120
|
else
|
121
121
|
raise "unreachable: #{c}"
|
122
122
|
end
|
data/lib/akaza/ruby2ws.rb
CHANGED
@@ -30,96 +30,479 @@ module Akaza
|
|
30
30
|
# x = 10
|
31
31
|
# push x
|
32
32
|
module Ruby2ws
|
33
|
+
MethodDefinition = Struct.new(:name, :lvar_table, :args_count, :body, :klass, keyword_init: true)
|
34
|
+
|
33
35
|
using AstExt
|
36
|
+
|
34
37
|
SPACE = ' '
|
35
38
|
TAB = "\t"
|
36
39
|
NL = "\n"
|
37
40
|
|
41
|
+
NONE_ADDR = 0
|
42
|
+
TMP_ADDR = 1
|
43
|
+
HEAP_COUNT_ADDR = 2
|
44
|
+
|
45
|
+
TYPES = %w[Integer Hash Array]
|
46
|
+
TYPE_BITS = 2
|
47
|
+
|
48
|
+
TYPE_SPECIAL = 0b00
|
49
|
+
TYPE_INT = 0b01
|
50
|
+
TYPE_ARRAY = 0b10
|
51
|
+
TYPE_HASH = 0b11
|
52
|
+
|
53
|
+
HASH_SIZE = 11
|
54
|
+
|
55
|
+
ARRAY_FIRST_CAPACITY = 10
|
56
|
+
|
57
|
+
FALSE = 0 << TYPE_BITS + TYPE_SPECIAL
|
58
|
+
# NONE is for internal. It does not available for user.
|
59
|
+
NONE = 1 << TYPE_BITS + TYPE_SPECIAL
|
60
|
+
TRUE = 2 << TYPE_BITS + TYPE_SPECIAL
|
61
|
+
# NIL is nil
|
62
|
+
NIL = 4 << TYPE_BITS + TYPE_SPECIAL
|
63
|
+
|
64
|
+
# Classes
|
65
|
+
CLASS_SPECIAL = (8 + TYPE_SPECIAL) << TYPE_BITS + TYPE_SPECIAL
|
66
|
+
CLASS_INT = (8 + TYPE_INT) << TYPE_BITS + TYPE_SPECIAL
|
67
|
+
CLASS_ARRAY = (8 + TYPE_ARRAY) << TYPE_BITS + TYPE_SPECIAL
|
68
|
+
CLASS_HASH = (8 + TYPE_HASH) << TYPE_BITS + TYPE_SPECIAL
|
69
|
+
|
70
|
+
# Call when stack top is the target number.
|
71
|
+
UNWRAP_COMMANDS = [
|
72
|
+
[:stack_push, 2 ** TYPE_BITS],
|
73
|
+
[:calc_div],
|
74
|
+
].freeze
|
75
|
+
WRAP_NUMBER_COMMANDS = [
|
76
|
+
[:stack_push, 2 ** TYPE_BITS],
|
77
|
+
[:calc_multi],
|
78
|
+
[:stack_push, TYPE_INT],
|
79
|
+
[:calc_add],
|
80
|
+
].freeze
|
81
|
+
WRAP_ARRAY_COMMANDS = [
|
82
|
+
[:stack_push, 2 ** TYPE_BITS],
|
83
|
+
[:calc_multi],
|
84
|
+
[:stack_push, TYPE_ARRAY],
|
85
|
+
[:calc_add],
|
86
|
+
].freeze
|
87
|
+
WRAP_HASH_COMMANDS = [
|
88
|
+
[:stack_push, 2 ** TYPE_BITS],
|
89
|
+
[:calc_multi],
|
90
|
+
[:stack_push, TYPE_HASH],
|
91
|
+
[:calc_add],
|
92
|
+
].freeze
|
93
|
+
SAVE_TMP_COMMANDS = [
|
94
|
+
[:stack_push, TMP_ADDR],
|
95
|
+
[:stack_swap],
|
96
|
+
[:heap_save],
|
97
|
+
].freeze
|
98
|
+
LOAD_TMP_COMMANDS = [
|
99
|
+
[:stack_push, TMP_ADDR],
|
100
|
+
[:heap_load],
|
101
|
+
].freeze
|
102
|
+
# Allocate heap and push allocated address to the stack
|
103
|
+
ALLOCATE_HEAP_COMMANDS = [
|
104
|
+
[:stack_push, HEAP_COUNT_ADDR],
|
105
|
+
[:heap_load],
|
106
|
+
[:stack_push, 1],
|
107
|
+
[:calc_add],
|
108
|
+
[:stack_dup],
|
109
|
+
[:stack_push, HEAP_COUNT_ADDR],
|
110
|
+
[:stack_swap],
|
111
|
+
[:heap_save],
|
112
|
+
].freeze
|
113
|
+
# Allocate N size heap and push nothing
|
114
|
+
# stack: [N]
|
115
|
+
# return stack: []
|
116
|
+
ALLOCATE_N_HEAP_COMMANDS = [
|
117
|
+
[:stack_push, HEAP_COUNT_ADDR],
|
118
|
+
[:heap_load],
|
119
|
+
[:calc_add],
|
120
|
+
[:stack_push, HEAP_COUNT_ADDR],
|
121
|
+
[:stack_swap],
|
122
|
+
[:heap_save],
|
123
|
+
].freeze
|
124
|
+
# Return an address that will be allocated by ALLOCATE_HEAP_COMMANDS
|
125
|
+
NEXT_HEAP_ADDRESS = [
|
126
|
+
[:stack_push, HEAP_COUNT_ADDR],
|
127
|
+
[:heap_load],
|
128
|
+
[:stack_push, 1],
|
129
|
+
[:calc_add],
|
130
|
+
].freeze
|
131
|
+
ALLOCATE_NEW_HASH_ITEM_COMMANDS = [
|
132
|
+
*ALLOCATE_HEAP_COMMANDS,
|
133
|
+
[:stack_dup],
|
134
|
+
[:stack_push, NONE],
|
135
|
+
[:heap_save],
|
136
|
+
*ALLOCATE_HEAP_COMMANDS,
|
137
|
+
[:stack_pop],
|
138
|
+
*ALLOCATE_HEAP_COMMANDS,
|
139
|
+
[:stack_pop],
|
140
|
+
].freeze
|
141
|
+
|
142
|
+
prelude_path = File.expand_path('./ruby2ws/prelude.rb', __dir__)
|
143
|
+
PRELUDE_AST = RubyVM::AbstractSyntaxTree.parse(File.read(prelude_path))
|
144
|
+
|
38
145
|
class ParseError < StandardError; end
|
39
146
|
|
40
|
-
def self.ruby_to_ws(ruby_code)
|
41
|
-
Transpiler.new(ruby_code).transpile
|
147
|
+
def self.ruby_to_ws(ruby_code, path: '(eval)')
|
148
|
+
Transpiler.new(ruby_code, path: path).transpile
|
42
149
|
end
|
43
150
|
|
44
151
|
class Transpiler
|
45
|
-
|
152
|
+
# @param ruby_code [String]
|
153
|
+
# @param path [String] For debug information
|
154
|
+
def initialize(ruby_code, path:)
|
46
155
|
@ruby_code = ruby_code
|
156
|
+
@path = path
|
157
|
+
|
158
|
+
@variable_addr_index = 2
|
159
|
+
@variable_addrs = {}
|
160
|
+
|
47
161
|
@label_index = 0
|
162
|
+
@labels = {}
|
163
|
+
|
164
|
+
# Method list to compile
|
165
|
+
# Array<Array<Command>>
|
166
|
+
@methods = []
|
167
|
+
|
168
|
+
# For lazy compiling method.
|
169
|
+
# MethodDefinition is inserted to it on :DEFN node.
|
170
|
+
# The definition is compiled on call node, such as :CALL.
|
171
|
+
# Hash{Symbol => Array<MethodDefinition>}
|
172
|
+
@method_definitions = {}
|
173
|
+
|
174
|
+
@method_table = {
|
175
|
+
Array: [:size, :push, :pop, :[], :[]=],
|
176
|
+
Integer: [:<=>],
|
177
|
+
Hash: [:[], :[]=],
|
178
|
+
}
|
179
|
+
|
180
|
+
@lvars_stack = []
|
181
|
+
|
182
|
+
@current_class = nil
|
48
183
|
end
|
49
184
|
|
50
185
|
def transpile
|
186
|
+
commands = []
|
187
|
+
# define built-in functions
|
188
|
+
define_array_size
|
189
|
+
define_array_pop
|
190
|
+
define_array_push
|
191
|
+
define_array_ref
|
192
|
+
define_array_attr_asgn
|
193
|
+
define_hash_ref
|
194
|
+
define_hash_attr_asgn
|
195
|
+
define_op_spaceship
|
196
|
+
|
197
|
+
# Prelude
|
198
|
+
commands.concat compile_expr(PRELUDE_AST)
|
199
|
+
|
51
200
|
ast = RubyVM::AbstractSyntaxTree.parse(@ruby_code)
|
52
|
-
|
201
|
+
body = compile_expr(ast)
|
202
|
+
|
203
|
+
# Save self for top level
|
204
|
+
commands << [:stack_push, variable_name_to_addr(:self)]
|
205
|
+
commands << [:stack_push, NONE]
|
206
|
+
commands << [:heap_save]
|
207
|
+
|
208
|
+
# Reserve heaps for local variables
|
209
|
+
commands << [:stack_push, HEAP_COUNT_ADDR]
|
210
|
+
commands << [:stack_push, @variable_addr_index + 1]
|
211
|
+
commands << [:heap_save]
|
212
|
+
|
213
|
+
commands.concat body
|
214
|
+
commands << [:flow_exit]
|
215
|
+
commands.concat(*@methods)
|
53
216
|
commands_to_ws(commands)
|
54
217
|
end
|
55
218
|
|
56
|
-
private def
|
219
|
+
private def compile_expr(node)
|
57
220
|
commands = []
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
221
|
+
|
222
|
+
case node
|
223
|
+
in [:FCALL, :put_as_number, [:ARRAY, arg, nil]]
|
224
|
+
commands.concat(compile_expr(arg))
|
225
|
+
commands.concat(UNWRAP_COMMANDS)
|
226
|
+
commands << [:io_write_num]
|
227
|
+
commands << [:stack_push, NIL]
|
228
|
+
in [:FCALL, :put_as_char, [:ARRAY, arg, nil]]
|
229
|
+
commands.concat(compile_expr(arg))
|
230
|
+
commands.concat(UNWRAP_COMMANDS)
|
231
|
+
commands << [:io_write_char]
|
232
|
+
commands << [:stack_push, NIL]
|
233
|
+
in [:FCALL, :raise, [:ARRAY, [:STR, str], nil]]
|
234
|
+
commands.concat compile_raise(str, node)
|
235
|
+
in [:VCALL, :get_as_number]
|
236
|
+
commands << [:stack_push, TMP_ADDR]
|
237
|
+
commands << [:io_read_num]
|
238
|
+
commands << [:stack_push, TMP_ADDR]
|
239
|
+
commands << [:heap_load]
|
240
|
+
commands.concat(WRAP_NUMBER_COMMANDS)
|
241
|
+
in [:VCALL, :get_as_char]
|
242
|
+
commands << [:stack_push, TMP_ADDR]
|
243
|
+
commands << [:io_read_char]
|
244
|
+
commands << [:stack_push, TMP_ADDR]
|
245
|
+
commands << [:heap_load]
|
246
|
+
commands.concat(WRAP_NUMBER_COMMANDS)
|
247
|
+
in [:OPCALL, l, :==, [:ARRAY, r, nil]]
|
248
|
+
commands.concat compile_expr(l)
|
249
|
+
commands.concat compile_expr(r)
|
250
|
+
commands << [:flow_call, op_eqeq_label]
|
251
|
+
in [:OPCALL, l, :!=, [:ARRAY, r, nil]]
|
252
|
+
commands.concat compile_expr(l)
|
253
|
+
commands.concat compile_expr(r)
|
254
|
+
commands << [:flow_call, op_eqeq_label]
|
255
|
+
commands << [:flow_call, op_not_label]
|
256
|
+
in [:OPCALL, recv, :!, nil]
|
257
|
+
commands.concat compile_expr(recv)
|
258
|
+
commands << [:flow_call, op_not_label]
|
259
|
+
in [:OPCALL, l, :+ | :- | :* | :/ | :% => sym, [:ARRAY, r, nil]]
|
260
|
+
com = {'+': :calc_add, '-': :calc_sub, '*': :calc_multi, '/': :calc_div, '%': :calc_mod}[sym]
|
261
|
+
commands.concat(compile_expr(l))
|
262
|
+
commands.concat(UNWRAP_COMMANDS)
|
263
|
+
commands.concat(compile_expr(r))
|
264
|
+
commands.concat(UNWRAP_COMMANDS)
|
265
|
+
commands << [com]
|
266
|
+
commands.concat(WRAP_NUMBER_COMMANDS)
|
267
|
+
in [:OPCALL, recv, op, [:ARRAY, *args, nil]]
|
268
|
+
commands.concat compile_expr(recv)
|
269
|
+
commands.concat compile_call_with_recv(op, args, error_target_node: recv, explicit_self: true)
|
270
|
+
in [:VCALL, :exit]
|
271
|
+
commands << [:flow_exit]
|
272
|
+
in [:LASGN, var, arg]
|
273
|
+
commands.concat(compile_expr(arg))
|
274
|
+
commands << [:stack_dup]
|
275
|
+
var_addr = variable_name_to_addr(var)
|
276
|
+
commands << [:stack_push, var_addr]
|
277
|
+
commands << [:stack_swap]
|
278
|
+
commands << [:heap_save]
|
279
|
+
in [:CDECL, var, arg]
|
280
|
+
commands.concat(compile_expr(arg))
|
281
|
+
commands << [:stack_dup]
|
282
|
+
var_addr = variable_name_to_addr(var)
|
283
|
+
commands << [:stack_push, var_addr]
|
284
|
+
commands << [:stack_swap]
|
285
|
+
commands << [:heap_save]
|
286
|
+
in [:ATTRASGN, recv, :[]=, [:ARRAY, index, value, nil]]
|
287
|
+
commands.concat compile_expr(recv)
|
288
|
+
commands.concat compile_call_with_recv(:[]=, [index, value], error_target_node: node, explicit_self: true)
|
289
|
+
in [:DEFN, name, [:SCOPE, lvar_table, [:ARGS, args_count ,*_], body]]
|
290
|
+
(@method_definitions[name] ||= []) << MethodDefinition.new(
|
291
|
+
name: name,
|
292
|
+
lvar_table: lvar_table,
|
293
|
+
args_count: args_count,
|
294
|
+
body: body,
|
295
|
+
klass: @current_class
|
296
|
+
)
|
297
|
+
commands << [:stack_push, NIL] # def foo... returns nil
|
298
|
+
in [:CLASS, [:COLON2, nil, class_name], nil, scope]
|
299
|
+
raise ParseError, "Class cannot be nested, but #{@current_class}::#{class_name} is nested." if @current_class
|
300
|
+
@current_class = class_name
|
301
|
+
commands.concat compile_expr(scope)
|
302
|
+
@current_class = nil
|
303
|
+
in [:SCOPE, lvar_table, _, body]
|
304
|
+
commands.concat update_lvar_commands(lvar_table)
|
305
|
+
commands.concat(compile_expr(body))
|
306
|
+
@lvars_stack.pop
|
307
|
+
in [:BEGIN, nil]
|
308
|
+
# skip
|
309
|
+
# It is available in class definition.
|
310
|
+
commands << [:stack_push, NIL]
|
311
|
+
in [:SELF]
|
312
|
+
commands.concat load_from_self_commands
|
313
|
+
in [:BLOCK, *children]
|
314
|
+
children.each.with_index do |child, index|
|
315
|
+
commands.concat(compile_expr(child))
|
316
|
+
commands << [:stack_pop] unless index == children.size - 1
|
317
|
+
end
|
318
|
+
in [:VCALL, name]
|
319
|
+
commands << [:stack_push, variable_name_to_addr(:self)]
|
320
|
+
commands << [:heap_load]
|
321
|
+
commands.concat compile_call_with_recv(name, [], error_target_node: node, explicit_self: false)
|
322
|
+
in [:FCALL, name, [:ARRAY, *args, nil]]
|
323
|
+
commands << [:stack_push, variable_name_to_addr(:self)]
|
324
|
+
commands << [:heap_load]
|
325
|
+
commands.concat compile_call_with_recv(name, args, error_target_node: node, explicit_self: false)
|
326
|
+
in [:CALL, recv, :is_a?, [:ARRAY, klass, nil]]
|
327
|
+
true_label = ident_to_label(nil)
|
328
|
+
end_label = ident_to_label(nil)
|
329
|
+
commands.concat compile_expr(recv)
|
330
|
+
commands.concat compile_expr(klass)
|
331
|
+
# klass to type
|
332
|
+
commands.concat UNWRAP_COMMANDS
|
333
|
+
commands << [:stack_push, 8]
|
334
|
+
commands << [:calc_sub]
|
335
|
+
|
336
|
+
commands << [:stack_swap]
|
337
|
+
commands << [:flow_call, is_a_label]
|
338
|
+
commands << [:flow_jump_if_zero, true_label]
|
339
|
+
|
340
|
+
commands << [:stack_push, FALSE]
|
341
|
+
commands << [:flow_jump, end_label]
|
342
|
+
|
343
|
+
commands << [:flow_def, true_label]
|
344
|
+
commands << [:stack_push, TRUE]
|
345
|
+
|
346
|
+
commands << [:flow_def, end_label]
|
347
|
+
in [:CALL, recv, name, [:ARRAY, *args, nil]]
|
348
|
+
commands.concat compile_expr(recv)
|
349
|
+
commands.concat compile_call_with_recv(name, args, error_target_node: recv, explicit_self: true)
|
350
|
+
in [:CALL, recv, name, nil]
|
351
|
+
args = []
|
352
|
+
commands.concat compile_expr(recv)
|
353
|
+
commands.concat compile_call_with_recv(name, args, error_target_node: recv, explicit_self: true)
|
354
|
+
in [:IF, cond, if_body, else_body]
|
355
|
+
commands.concat(compile_if(cond, if_body, else_body))
|
356
|
+
in [:UNLESS, cond, else_body, if_body]
|
357
|
+
commands.concat(compile_if(cond, if_body, else_body))
|
358
|
+
in [:CASE, cond, first_when]
|
359
|
+
commands.concat compile_case(cond, first_when)
|
360
|
+
in [:WHILE, cond, body, true]
|
361
|
+
commands.concat(compile_while(cond, body))
|
362
|
+
in [:LIT, num]
|
363
|
+
commands << [:stack_push, with_type(num, TYPE_INT)]
|
364
|
+
in [:STR, str]
|
365
|
+
check_char!(str)
|
366
|
+
commands << [:stack_push, with_type(str.ord, TYPE_INT)]
|
367
|
+
in [:TRUE]
|
368
|
+
commands << [:stack_push, TRUE]
|
369
|
+
in [:FALSE]
|
370
|
+
commands << [:stack_push, FALSE]
|
371
|
+
in [:NIL]
|
372
|
+
commands << [:stack_push, NIL]
|
373
|
+
in [:LVAR, name]
|
374
|
+
commands << [:stack_push, variable_name_to_addr(name)]
|
375
|
+
commands << [:heap_load]
|
376
|
+
in [:CONST, :Integer | :Array | :Hash | :Special => klass]
|
377
|
+
k = {
|
378
|
+
Integer: CLASS_INT,
|
379
|
+
Array: CLASS_ARRAY,
|
380
|
+
Hash: CLASS_HASH,
|
381
|
+
Special: CLASS_SPECIAL,
|
382
|
+
}[klass]
|
383
|
+
commands << [:stack_push, k]
|
384
|
+
in [:CONST, name]
|
385
|
+
commands << [:stack_push, variable_name_to_addr(name)]
|
386
|
+
commands << [:heap_load]
|
387
|
+
in [:ARRAY, *items, nil]
|
388
|
+
commands.concat allocate_array_commands(items.size)
|
389
|
+
# stack: [array]
|
390
|
+
|
391
|
+
commands << [:stack_dup]
|
392
|
+
commands.concat UNWRAP_COMMANDS
|
393
|
+
commands << [:stack_push, 3]
|
394
|
+
commands << [:calc_add]
|
395
|
+
# stack: [array, first_item_addr]
|
396
|
+
|
397
|
+
items.each do |item|
|
398
|
+
commands << [:stack_dup]
|
399
|
+
# stack: [array, item_addr, item_addr]
|
400
|
+
commands.concat compile_expr(item)
|
401
|
+
commands << [:heap_save]
|
402
|
+
commands << [:stack_push, 1]
|
403
|
+
commands << [:calc_add]
|
404
|
+
# stack: [array, next_item_addr]
|
405
|
+
end
|
406
|
+
commands << [:stack_pop]
|
407
|
+
|
408
|
+
in [:ZARRAY]
|
409
|
+
# Allocate array ref
|
410
|
+
commands.concat allocate_array_commands(0)
|
411
|
+
in [:HASH, nil]
|
412
|
+
commands.concat initialize_hash
|
413
|
+
in [:HASH, [:ARRAY, *pairs, nil]]
|
414
|
+
commands.concat initialize_hash
|
415
|
+
commands << [:stack_dup]
|
416
|
+
commands.concat UNWRAP_COMMANDS
|
417
|
+
commands.concat SAVE_TMP_COMMANDS
|
418
|
+
# stack: [hash_object (unwrapped)]
|
419
|
+
# tmp: hash_object (unwrapped)
|
420
|
+
|
421
|
+
pairs.each_slice(2) do |key, value|
|
422
|
+
no_collision_label = ident_to_label(nil)
|
423
|
+
check_collision_label = ident_to_label(nil)
|
424
|
+
when_not_allocated = ident_to_label(nil)
|
425
|
+
|
426
|
+
commands.concat(compile_expr(key))
|
427
|
+
# calc hash
|
428
|
+
commands << [:stack_dup]
|
429
|
+
commands.concat(UNWRAP_COMMANDS)
|
430
|
+
commands << [:stack_push, HASH_SIZE]
|
431
|
+
commands << [:calc_mod]
|
432
|
+
commands << [:stack_push, 3]
|
433
|
+
commands << [:calc_multi]
|
434
|
+
# stack: [key, hash]
|
435
|
+
|
436
|
+
commands.concat LOAD_TMP_COMMANDS
|
437
|
+
commands << [:stack_push, 1]
|
438
|
+
commands << [:calc_add] # hash_addr + 1 is the first item's address.
|
439
|
+
commands << [:calc_add]
|
440
|
+
# stack: [key, key_addr]
|
441
|
+
|
442
|
+
# Check collision
|
443
|
+
commands << [:flow_def, check_collision_label]
|
444
|
+
commands << [:stack_dup]
|
445
|
+
commands << [:heap_load]
|
446
|
+
commands << [:stack_push, NONE]
|
447
|
+
commands << [:calc_sub]
|
448
|
+
# stack: [key, key_addr, is_none]
|
449
|
+
|
450
|
+
commands << [:flow_jump_if_zero, no_collision_label]
|
451
|
+
|
452
|
+
# when collision
|
453
|
+
commands << [:stack_push, 2]
|
454
|
+
commands << [:calc_add]
|
455
|
+
# stack: [key, next_addr]
|
456
|
+
commands << [:stack_dup]
|
457
|
+
commands << [:heap_load]
|
458
|
+
commands << [:stack_push, NONE_ADDR]
|
459
|
+
commands << [:calc_sub]
|
460
|
+
commands << [:flow_jump_if_zero, when_not_allocated]
|
461
|
+
# stack: [key, next_addr]
|
462
|
+
|
463
|
+
# when next field is already allocated
|
464
|
+
commands << [:heap_load]
|
465
|
+
# stack: [key, next_key_addr]
|
466
|
+
commands << [:flow_jump, check_collision_label]
|
467
|
+
|
468
|
+
# when next field is not allocated
|
469
|
+
commands << [:flow_def, when_not_allocated]
|
470
|
+
# stack: [key, next_addr]
|
471
|
+
commands << [:stack_dup]
|
472
|
+
commands.concat ALLOCATE_NEW_HASH_ITEM_COMMANDS
|
473
|
+
commands << [:heap_save]
|
474
|
+
commands << [:heap_load]
|
475
|
+
|
476
|
+
commands << [:flow_jump, check_collision_label]
|
477
|
+
|
478
|
+
commands << [:flow_def, no_collision_label]
|
479
|
+
# End check collision
|
480
|
+
|
481
|
+
# stack: [key, key_addr]
|
482
|
+
# Save value
|
483
|
+
commands << [:stack_dup]
|
484
|
+
commands << [:stack_push, 1]
|
485
|
+
commands << [:calc_add]
|
486
|
+
commands.concat(compile_expr(value))
|
487
|
+
# stack: [key, key_addr, value_addr, value]
|
488
|
+
commands << [:heap_save]
|
489
|
+
# stack: [key, key_addr]
|
490
|
+
|
491
|
+
# Save next addr
|
492
|
+
commands << [:stack_dup]
|
493
|
+
commands << [:stack_push, 2]
|
494
|
+
commands << [:calc_add]
|
495
|
+
# stack: [key, key_addr, next_addr]
|
496
|
+
commands << [:stack_push, NONE_ADDR]
|
497
|
+
commands << [:heap_save]
|
498
|
+
# stack: [key, key_addr]
|
499
|
+
|
500
|
+
# Save key
|
501
|
+
commands << [:stack_swap]
|
502
|
+
commands << [:heap_save]
|
118
503
|
end
|
119
504
|
end
|
120
505
|
|
121
|
-
commands << [:flow, :exit] if main
|
122
|
-
commands.concat(*methods)
|
123
506
|
commands
|
124
507
|
end
|
125
508
|
|
@@ -127,179 +510,1138 @@ module Akaza
|
|
127
510
|
buf = +""
|
128
511
|
commands.each do |command|
|
129
512
|
case command
|
130
|
-
in [:
|
513
|
+
in [:stack_push, num]
|
131
514
|
buf << SPACE << SPACE << num_to_ws(num)
|
132
|
-
in [:
|
515
|
+
in [:stack_pop]
|
516
|
+
buf << SPACE << NL << NL
|
517
|
+
in [:stack_swap]
|
133
518
|
buf << SPACE << NL << TAB
|
134
|
-
in [:
|
519
|
+
in [:stack_dup]
|
520
|
+
buf << SPACE << NL << SPACE
|
521
|
+
in [:heap_save]
|
135
522
|
buf << TAB << TAB << SPACE
|
136
|
-
in [:
|
523
|
+
in [:heap_load]
|
137
524
|
buf << TAB << TAB << TAB
|
138
|
-
in [:
|
525
|
+
in [:io_write_char]
|
139
526
|
buf << TAB << NL << SPACE << SPACE
|
140
|
-
in [:
|
527
|
+
in [:io_write_num]
|
141
528
|
buf << TAB << NL << SPACE << TAB
|
142
|
-
in [:
|
529
|
+
in [:io_read_char]
|
143
530
|
buf << TAB << NL << TAB << SPACE
|
144
|
-
in [:
|
531
|
+
in [:io_read_num]
|
145
532
|
buf << TAB << NL << TAB << TAB
|
146
|
-
in [:
|
533
|
+
in [:flow_exit]
|
147
534
|
buf << NL << NL << NL
|
148
|
-
in [:
|
535
|
+
in [:flow_call, num]
|
149
536
|
buf << NL << SPACE << TAB << num_to_ws(num)
|
150
|
-
in [:
|
537
|
+
in [:flow_def, num]
|
151
538
|
buf << NL << SPACE << SPACE << num_to_ws(num)
|
152
|
-
in [:
|
539
|
+
in [:flow_end]
|
153
540
|
buf << NL << TAB << NL
|
154
|
-
in [:
|
541
|
+
in [:flow_jump_if_zero, label]
|
155
542
|
buf << NL << TAB << SPACE << num_to_ws(label)
|
156
|
-
in [:
|
543
|
+
in [:flow_jump, label]
|
157
544
|
buf << NL << SPACE << NL << num_to_ws(label)
|
158
|
-
in [:
|
545
|
+
in [:flow_jump_if_neg, label]
|
159
546
|
buf << NL << TAB << TAB << num_to_ws(label)
|
160
|
-
in [:
|
547
|
+
in [:calc_add]
|
161
548
|
buf << TAB << SPACE << SPACE << SPACE
|
162
|
-
in [:
|
549
|
+
in [:calc_sub]
|
163
550
|
buf << TAB << SPACE << SPACE << TAB
|
164
|
-
in [:
|
551
|
+
in [:calc_multi]
|
165
552
|
buf << TAB << SPACE << SPACE << NL
|
166
|
-
in [:
|
553
|
+
in [:calc_div]
|
167
554
|
buf << TAB << SPACE << TAB << SPACE
|
168
|
-
in [:
|
555
|
+
in [:calc_mod]
|
169
556
|
buf << TAB << SPACE << TAB << TAB
|
170
557
|
end
|
171
558
|
end
|
172
559
|
buf
|
173
560
|
end
|
174
561
|
|
175
|
-
private def with_storing_lvars(
|
562
|
+
private def with_storing_lvars(commands, &block)
|
176
563
|
lvars.each do |var_addr|
|
177
564
|
# stack.push(addr); stack.push(val)
|
178
|
-
commands << [:
|
179
|
-
commands << [:
|
180
|
-
commands << [:
|
181
|
-
# Fill zero
|
182
|
-
commands << [:stack, :push, var_addr]
|
183
|
-
commands << [:stack, :push, 0]
|
184
|
-
commands << [:heap, :save]
|
565
|
+
commands << [:stack_push, var_addr]
|
566
|
+
commands << [:stack_push, var_addr]
|
567
|
+
commands << [:heap_load]
|
185
568
|
end
|
186
569
|
|
187
570
|
block.call
|
188
571
|
|
189
572
|
lvars.size.times do
|
190
|
-
commands << [:
|
573
|
+
commands << [:heap_save]
|
191
574
|
end
|
192
575
|
end
|
193
576
|
|
194
|
-
|
577
|
+
# stack: [recv]
|
578
|
+
private def compile_call(name, args)
|
195
579
|
commands = []
|
580
|
+
commands.concat SAVE_TMP_COMMANDS
|
581
|
+
with_storing_lvars(commands) do
|
582
|
+
# Update self
|
583
|
+
commands.concat LOAD_TMP_COMMANDS
|
584
|
+
commands.concat save_to_self_commands
|
196
585
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
commands << [:
|
205
|
-
commands << [:
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
586
|
+
|
587
|
+
# push args
|
588
|
+
args.each do |arg|
|
589
|
+
commands.concat(compile_expr(arg))
|
590
|
+
end
|
591
|
+
|
592
|
+
|
593
|
+
commands << [:flow_call, ident_to_label(name)]
|
594
|
+
commands << [:stack_push, TMP_ADDR]
|
595
|
+
commands << [:stack_swap]
|
596
|
+
commands << [:heap_save]
|
597
|
+
end
|
598
|
+
# restore return value
|
599
|
+
commands << [:stack_push, TMP_ADDR]
|
600
|
+
commands << [:heap_load]
|
601
|
+
commands
|
602
|
+
end
|
603
|
+
|
604
|
+
# Compile CALL
|
605
|
+
# stack: [recv]
|
606
|
+
private def compile_call_with_recv(name, args, error_target_node:, explicit_self:)
|
607
|
+
lazy_compile_method(name)
|
608
|
+
|
609
|
+
commands = []
|
610
|
+
|
611
|
+
is_int_label = ident_to_label(nil)
|
612
|
+
is_array_label = ident_to_label(nil)
|
613
|
+
is_hash_label = ident_to_label(nil)
|
614
|
+
is_none_label = ident_to_label(nil)
|
615
|
+
end_label = ident_to_label(nil)
|
616
|
+
|
617
|
+
commands << [:stack_dup]
|
618
|
+
commands.concat SAVE_TMP_COMMANDS
|
619
|
+
|
620
|
+
# is_a?(Integer)
|
621
|
+
commands << [:stack_push, TYPE_INT]
|
622
|
+
commands << [:stack_swap]
|
623
|
+
commands << [:flow_call, is_a_label]
|
624
|
+
commands << [:flow_jump_if_zero, is_int_label]
|
625
|
+
|
626
|
+
# is_a?(Array)
|
627
|
+
commands << [:stack_push, TYPE_ARRAY]
|
628
|
+
commands.concat LOAD_TMP_COMMANDS
|
629
|
+
commands << [:flow_call, is_a_label]
|
630
|
+
commands << [:flow_jump_if_zero, is_array_label]
|
631
|
+
|
632
|
+
# is_a?(Hash)
|
633
|
+
commands << [:stack_push, TYPE_HASH]
|
634
|
+
commands.concat LOAD_TMP_COMMANDS
|
635
|
+
commands << [:flow_call, is_a_label]
|
636
|
+
commands << [:flow_jump_if_zero, is_hash_label]
|
637
|
+
|
638
|
+
# == NONE
|
639
|
+
commands.concat LOAD_TMP_COMMANDS
|
640
|
+
commands << [:stack_push, NONE]
|
641
|
+
commands << [:calc_sub]
|
642
|
+
commands << [:flow_jump_if_zero, is_none_label]
|
643
|
+
|
644
|
+
# Other
|
645
|
+
commands.concat compile_raise("Unknown type of receiver", error_target_node)
|
646
|
+
|
647
|
+
top_level_p = -> (type) { !@method_table[type].include?(name) && !explicit_self }
|
648
|
+
|
649
|
+
commands << [:flow_def, is_int_label]
|
650
|
+
if top_level_p.(:Integer)
|
651
|
+
commands << [:stack_push, NONE]
|
652
|
+
commands.concat compile_call(name, args)
|
653
|
+
else
|
654
|
+
commands.concat LOAD_TMP_COMMANDS
|
655
|
+
commands.concat compile_call(:"Integer##{name}", args)
|
656
|
+
end
|
657
|
+
commands << [:flow_jump, end_label]
|
658
|
+
|
659
|
+
commands << [:flow_def, is_array_label]
|
660
|
+
if top_level_p.(:Array)
|
661
|
+
commands << [:stack_push, NONE]
|
662
|
+
commands.concat compile_call(name, args)
|
663
|
+
else
|
664
|
+
commands.concat LOAD_TMP_COMMANDS
|
665
|
+
commands.concat compile_call(:"Array##{name}", args)
|
666
|
+
end
|
667
|
+
commands << [:flow_jump, end_label]
|
668
|
+
|
669
|
+
commands << [:flow_def, is_hash_label]
|
670
|
+
if top_level_p.(:Hash)
|
671
|
+
commands << [:stack_push, NONE]
|
672
|
+
commands.concat compile_call(name, args)
|
673
|
+
else
|
674
|
+
commands.concat LOAD_TMP_COMMANDS
|
675
|
+
commands.concat compile_call(:"Hash##{name}", args)
|
224
676
|
end
|
677
|
+
commands << [:flow_jump, end_label]
|
678
|
+
|
679
|
+
# If receiver is NONE, it means method is called at the top level
|
680
|
+
commands << [:flow_def, is_none_label]
|
681
|
+
commands << [:stack_push, NONE]
|
682
|
+
commands.concat compile_call(name, args)
|
683
|
+
|
684
|
+
commands << [:flow_def, end_label]
|
685
|
+
|
686
|
+
commands
|
687
|
+
end
|
688
|
+
|
689
|
+
# required stack: [count]
|
690
|
+
# the count in the stack will be modified by this method.
|
691
|
+
private def times(&block)
|
692
|
+
commands = []
|
693
|
+
end_label = ident_to_label(nil)
|
694
|
+
cond_label = ident_to_label(nil)
|
695
|
+
|
696
|
+
commands << [:flow_def, cond_label]
|
697
|
+
commands << [:stack_push, 1]
|
698
|
+
commands << [:calc_sub]
|
699
|
+
commands << [:stack_dup]
|
700
|
+
commands << [:flow_jump_if_neg, end_label]
|
701
|
+
|
702
|
+
commands.concat(block.call)
|
703
|
+
|
704
|
+
commands << [:flow_jump, cond_label]
|
705
|
+
commands << [:flow_def, end_label]
|
225
706
|
|
226
707
|
commands
|
227
708
|
end
|
228
709
|
|
229
710
|
private def compile_if(cond, if_body, else_body)
|
230
711
|
commands = []
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
commands
|
237
|
-
commands.concat(
|
238
|
-
commands << [
|
239
|
-
|
240
|
-
|
241
|
-
|
712
|
+
|
713
|
+
optimized_body = -> (x, sym) do
|
714
|
+
else_label = ident_to_label(nil)
|
715
|
+
end_label = ident_to_label(nil)
|
716
|
+
|
717
|
+
commands.concat(compile_expr(x))
|
718
|
+
commands.concat(UNWRAP_COMMANDS)
|
719
|
+
commands << [sym, else_label]
|
720
|
+
if else_body
|
721
|
+
commands.concat(compile_expr(else_body))
|
722
|
+
else
|
723
|
+
commands << [:stack_push, NIL]
|
724
|
+
end
|
725
|
+
commands << [:flow_jump, end_label]
|
726
|
+
commands << [:flow_def, else_label]
|
727
|
+
if if_body
|
728
|
+
commands.concat(compile_expr(if_body))
|
729
|
+
else
|
730
|
+
commands << [:stack_push, NIL]
|
731
|
+
end
|
732
|
+
commands << [:flow_def, end_label]
|
242
733
|
end
|
243
734
|
|
244
735
|
case cond
|
245
736
|
in [:OPCALL, [:LIT, 0], :==, [:ARRAY, x, nil]]
|
246
|
-
|
737
|
+
optimized_body.(x, :flow_jump_if_zero)
|
247
738
|
in [:OPCALL, x, :==, [:ARRAY, [:LIT, 0], nil]]
|
248
|
-
|
739
|
+
optimized_body.(x, :flow_jump_if_zero)
|
249
740
|
in [:OPCALL, x, :<, [:ARRAY, [:LIT, 0], nil]]
|
250
|
-
|
741
|
+
optimized_body.(x, :flow_jump_if_neg)
|
251
742
|
in [:OPCALL, [:LIT, 0], :<, [:ARRAY, x, nil]]
|
252
|
-
|
743
|
+
optimized_body.(x, :flow_jump_if_neg)
|
744
|
+
else
|
745
|
+
if_label = ident_to_label(nil)
|
746
|
+
end_label = ident_to_label(nil)
|
747
|
+
|
748
|
+
commands.concat compile_expr(cond)
|
749
|
+
commands << [:flow_call, rtest_label]
|
750
|
+
commands << [:flow_jump_if_zero, if_label]
|
751
|
+
|
752
|
+
# when false
|
753
|
+
if else_body
|
754
|
+
commands.concat compile_expr(else_body)
|
755
|
+
else
|
756
|
+
commands << [:stack_push, NIL]
|
757
|
+
end
|
758
|
+
commands << [:flow_jump, end_label]
|
759
|
+
|
760
|
+
# when true
|
761
|
+
commands << [:flow_def, if_label]
|
762
|
+
if if_body
|
763
|
+
commands.concat compile_expr(if_body)
|
764
|
+
else
|
765
|
+
commands << [:stack_push, NIL]
|
766
|
+
end
|
767
|
+
|
768
|
+
commands << [:flow_def, end_label]
|
253
769
|
end
|
254
770
|
|
255
771
|
commands
|
256
772
|
end
|
257
773
|
|
774
|
+
private def compile_case(cond, first_when)
|
775
|
+
commands = []
|
776
|
+
end_label = ident_to_label(nil)
|
777
|
+
|
778
|
+
commands.concat compile_expr(cond)
|
779
|
+
|
780
|
+
bodies = []
|
781
|
+
body_labels = []
|
782
|
+
else_node = nil
|
783
|
+
|
784
|
+
first_when.each_when do |when_node|
|
785
|
+
case when_node
|
786
|
+
in [:WHEN, [:ARRAY, *objs, nil], body, _]
|
787
|
+
bodies << body
|
788
|
+
body_labels << ident_to_label(nil)
|
789
|
+
|
790
|
+
objs.each do |obj|
|
791
|
+
commands << [:stack_dup]
|
792
|
+
commands.concat compile_expr(obj)
|
793
|
+
commands << [:calc_sub]
|
794
|
+
commands << [:flow_jump_if_zero, body_labels.last]
|
795
|
+
end
|
796
|
+
else # When case-else body
|
797
|
+
else_node = when_node
|
798
|
+
end
|
799
|
+
end
|
800
|
+
|
801
|
+
commands << [:stack_pop] # pop cond object
|
802
|
+
if else_node
|
803
|
+
commands.concat compile_expr(else_node)
|
804
|
+
else
|
805
|
+
commands << [:stack_push, NIL]
|
806
|
+
end
|
807
|
+
commands << [:flow_jump, end_label]
|
808
|
+
|
809
|
+
bodies.zip(body_labels).each do |body, label|
|
810
|
+
commands << [:flow_def, label]
|
811
|
+
commands << [:stack_pop] # pop cond object
|
812
|
+
commands.concat compile_expr(body)
|
813
|
+
commands << [:flow_jump, end_label]
|
814
|
+
end
|
815
|
+
|
816
|
+
commands << [:flow_def, end_label]
|
817
|
+
commands
|
818
|
+
end
|
819
|
+
|
258
820
|
private def compile_while(cond, body)
|
259
821
|
commands = []
|
260
|
-
cond_label =
|
261
|
-
body_label =
|
262
|
-
end_label =
|
822
|
+
cond_label = ident_to_label(nil)
|
823
|
+
body_label = ident_to_label(nil)
|
824
|
+
end_label = ident_to_label(nil)
|
263
825
|
|
264
826
|
make_body = -> (x, sym) do
|
265
|
-
commands << [:
|
266
|
-
commands.concat(
|
267
|
-
commands
|
268
|
-
commands << [
|
269
|
-
commands << [:
|
270
|
-
commands
|
271
|
-
commands
|
272
|
-
commands << [:
|
827
|
+
commands << [:flow_def, cond_label]
|
828
|
+
commands.concat(compile_expr(x))
|
829
|
+
commands.concat(UNWRAP_COMMANDS)
|
830
|
+
commands << [sym, body_label]
|
831
|
+
commands << [:flow_jump, end_label]
|
832
|
+
commands << [:flow_def, body_label]
|
833
|
+
commands.concat(compile_expr(body))
|
834
|
+
commands << [:stack_pop]
|
835
|
+
commands << [:flow_jump, cond_label]
|
836
|
+
commands << [:flow_def, end_label]
|
837
|
+
commands << [:stack_push, NIL]
|
273
838
|
end
|
274
839
|
|
275
840
|
case cond
|
841
|
+
in [:TRUE] # Optimized
|
842
|
+
commands << [:flow_def, cond_label]
|
843
|
+
commands.concat compile_expr(body)
|
844
|
+
commands << [:stack_pop]
|
845
|
+
commands << [:flow_jump, cond_label]
|
276
846
|
in [:OPCALL, [:LIT, 0], :==, [:ARRAY, x, nil]]
|
277
|
-
make_body.(x, :
|
847
|
+
make_body.(x, :flow_jump_if_zero)
|
278
848
|
in [:OPCALL, x, :==, [:ARRAY, [:LIT, 0], nil]]
|
279
|
-
make_body.(x, :
|
849
|
+
make_body.(x, :flow_jump_if_zero)
|
280
850
|
in [:OPCALL, x, :<, [:ARRAY, [:LIT, 0], nil]]
|
281
|
-
make_body.(x, :
|
851
|
+
make_body.(x, :flow_jump_if_neg)
|
282
852
|
in [:OPCALL, [:LIT, 0], :<, [:ARRAY, x, nil]]
|
283
|
-
make_body.(x, :
|
853
|
+
make_body.(x, :flow_jump_if_neg)
|
854
|
+
else
|
855
|
+
commands << [:flow_def, cond_label]
|
856
|
+
commands.concat(compile_expr(cond))
|
857
|
+
commands << [:flow_call, rtest_label]
|
858
|
+
commands << [:flow_jump_if_zero, body_label]
|
859
|
+
commands << [:flow_jump, end_label]
|
860
|
+
commands << [:flow_def, body_label]
|
861
|
+
commands.concat(compile_expr(body))
|
862
|
+
commands << [:stack_pop]
|
863
|
+
commands << [:flow_jump, cond_label]
|
864
|
+
commands << [:flow_def, end_label]
|
865
|
+
commands << [:stack_push, NIL]
|
284
866
|
end
|
285
867
|
|
286
868
|
commands
|
287
869
|
end
|
288
870
|
|
289
|
-
private def
|
290
|
-
|
871
|
+
private def compile_raise(str, node)
|
872
|
+
msg = +"#{@path}:"
|
873
|
+
msg << "#{node.first_lineno}:#{node.first_column}"
|
874
|
+
msg << ": #{str} (Error)\n"
|
875
|
+
commands = []
|
876
|
+
|
877
|
+
msg.bytes.each do |byte|
|
878
|
+
commands << [:stack_push, byte]
|
879
|
+
commands << [:io_write_char]
|
880
|
+
end
|
881
|
+
|
882
|
+
commands << [:flow_exit]
|
883
|
+
|
884
|
+
commands
|
291
885
|
end
|
292
886
|
|
293
|
-
private def
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
887
|
+
private def compile_def(name, lvar_table, args_count, body, klass)
|
888
|
+
label = klass ? ident_to_label(:"#{klass}##{name}") : ident_to_label(name)
|
889
|
+
m = [
|
890
|
+
[:flow_def, label],
|
891
|
+
]
|
892
|
+
args = lvar_table[0...args_count].reverse
|
893
|
+
m.concat update_lvar_commands(lvar_table, args: args)
|
894
|
+
args.each do |args_name|
|
895
|
+
addr = variable_name_to_addr(args_name)
|
896
|
+
m << [:stack_push, addr]
|
897
|
+
m << [:stack_swap]
|
898
|
+
m << [:heap_save]
|
899
|
+
end
|
900
|
+
|
901
|
+
m.concat(compile_expr(body))
|
902
|
+
@lvars_stack.pop
|
903
|
+
m << [:flow_end]
|
904
|
+
|
905
|
+
@methods << m
|
906
|
+
|
907
|
+
@method_table[klass] << name if klass
|
908
|
+
end
|
909
|
+
|
910
|
+
private def initialize_hash
|
911
|
+
commands = []
|
912
|
+
# Allocate for Hash
|
913
|
+
commands.concat ALLOCATE_HEAP_COMMANDS
|
914
|
+
|
915
|
+
HASH_SIZE.times do
|
916
|
+
commands.concat ALLOCATE_NEW_HASH_ITEM_COMMANDS
|
917
|
+
commands << [:stack_pop]
|
918
|
+
end
|
919
|
+
|
920
|
+
# stack: [hash_addr]
|
921
|
+
commands << [:stack_dup]
|
922
|
+
commands << [:stack_dup]
|
923
|
+
commands << [:stack_push, 1]
|
924
|
+
commands << [:calc_add]
|
925
|
+
commands << [:heap_save]
|
926
|
+
# stack: [hash_addr]
|
927
|
+
|
928
|
+
commands.concat(WRAP_HASH_COMMANDS)
|
929
|
+
|
930
|
+
commands
|
931
|
+
end
|
932
|
+
|
933
|
+
# stack: [self]
|
934
|
+
# return stack: []
|
935
|
+
private def save_to_self_commands
|
936
|
+
commands = []
|
937
|
+
self_addr = variable_name_to_addr(:self)
|
938
|
+
commands << [:stack_push, self_addr]
|
939
|
+
commands << [:stack_swap]
|
940
|
+
commands << [:heap_save]
|
941
|
+
commands
|
942
|
+
end
|
943
|
+
|
944
|
+
# stack: []
|
945
|
+
# return stack: [self]
|
946
|
+
private def load_from_self_commands
|
947
|
+
commands = []
|
948
|
+
self_addr = variable_name_to_addr(:self)
|
949
|
+
commands << [:stack_push, self_addr]
|
950
|
+
commands << [:heap_load]
|
951
|
+
commands
|
952
|
+
end
|
953
|
+
|
954
|
+
# stack: [addr_of_first_addr]
|
955
|
+
# return stack: []
|
956
|
+
private def realloc_array_label
|
957
|
+
@realloc_array_label ||= (
|
958
|
+
label = ident_to_label(nil)
|
959
|
+
commands = []
|
960
|
+
commands << [:flow_def, label]
|
961
|
+
|
962
|
+
# stack: [addr_of_first_addr]
|
963
|
+
# Get cap addr
|
964
|
+
commands << [:stack_dup]
|
965
|
+
commands << [:stack_push, 2]
|
966
|
+
commands << [:calc_add]
|
967
|
+
commands << [:stack_dup]
|
968
|
+
commands << [:heap_load]
|
969
|
+
# stack: [addr_of_first_addr, cap_addr, cap]
|
970
|
+
|
971
|
+
commands << [:stack_push, 2]
|
972
|
+
commands << [:calc_multi]
|
973
|
+
# stack: [addr_of_first_addr, cap_addr, new_cap]
|
974
|
+
# Update cap
|
975
|
+
commands << [:stack_dup]
|
976
|
+
commands.concat SAVE_TMP_COMMANDS
|
977
|
+
commands << [:heap_save]
|
978
|
+
commands.concat LOAD_TMP_COMMANDS
|
979
|
+
# stack: [addr_of_first_addr, new_cap]
|
980
|
+
commands.concat NEXT_HEAP_ADDRESS
|
981
|
+
commands.concat SAVE_TMP_COMMANDS # new_item_addr
|
982
|
+
commands.concat ALLOCATE_N_HEAP_COMMANDS
|
983
|
+
# stack: [addr_of_first_addr]
|
984
|
+
commands << [:stack_dup]
|
985
|
+
commands << [:heap_load]
|
986
|
+
# stack: [addr_of_first_addr, old_first_addr]
|
987
|
+
# Update first addr
|
988
|
+
commands << [:stack_swap]
|
989
|
+
commands << [:stack_dup]
|
990
|
+
commands.concat LOAD_TMP_COMMANDS
|
991
|
+
# stack: [old_first_addr, addr_of_first_addr, addr_of_first_addr, new_first_addr]
|
992
|
+
commands << [:heap_save]
|
993
|
+
commands << [:stack_swap]
|
994
|
+
# stack: [addr_of_first_addr, old_first_addr]
|
995
|
+
# Load size
|
996
|
+
commands << [:stack_swap]
|
997
|
+
commands << [:stack_push, 1]
|
998
|
+
commands << [:calc_add]
|
999
|
+
commands << [:heap_load]
|
1000
|
+
# stack: [old_first_addr, size]
|
1001
|
+
# Move old items to new addresses
|
1002
|
+
commands.concat(times do
|
1003
|
+
c = []
|
1004
|
+
c << [:stack_swap]
|
1005
|
+
# stack: idx, old_target_addr]
|
1006
|
+
c << [:stack_dup]
|
1007
|
+
c.concat LOAD_TMP_COMMANDS
|
1008
|
+
# stack: [idx, old_target_addr, old_target_addr, new_target_addr]
|
1009
|
+
|
1010
|
+
# Update tmp to new_next_addr
|
1011
|
+
c << [:stack_dup]
|
1012
|
+
c << [:stack_push, 1]
|
1013
|
+
c << [:calc_add]
|
1014
|
+
c.concat SAVE_TMP_COMMANDS
|
1015
|
+
|
1016
|
+
# stack: [idx, old_target_addr, old_target_addr, new_target_addr]
|
1017
|
+
c << [:stack_swap]
|
1018
|
+
c << [:heap_load]
|
1019
|
+
# stack: [idx, old_target_addr, new_target_addr, old_target]
|
1020
|
+
c << [:heap_save]
|
1021
|
+
# stack: [idx, old_target_addr]
|
1022
|
+
c << [:stack_push, 1]
|
1023
|
+
c << [:calc_add]
|
1024
|
+
# stack: [old_next_addr, idx]
|
1025
|
+
c << [:stack_swap]
|
1026
|
+
c
|
1027
|
+
end)
|
1028
|
+
commands << [:stack_pop] # idx
|
1029
|
+
commands << [:stack_pop] # old_next_addr
|
1030
|
+
|
1031
|
+
|
1032
|
+
commands << [:flow_end]
|
1033
|
+
@methods << commands
|
1034
|
+
label
|
1035
|
+
)
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
# stack: [left, right]
|
1039
|
+
# return stack: [TRUE/FALSE]
|
1040
|
+
private def op_eqeq_label
|
1041
|
+
@op_eqeq_label ||= (
|
1042
|
+
label = ident_to_label(nil)
|
1043
|
+
label_if_zero = ident_to_label(nil)
|
1044
|
+
label_end = ident_to_label(nil)
|
1045
|
+
|
1046
|
+
commands = []
|
1047
|
+
commands << [:flow_def, label]
|
1048
|
+
|
1049
|
+
commands << [:calc_sub]
|
1050
|
+
commands << [:flow_jump_if_zero, label_if_zero]
|
1051
|
+
commands << [:stack_push, FALSE]
|
1052
|
+
commands << [:flow_jump, label_end]
|
1053
|
+
|
1054
|
+
commands << [:flow_def, label_if_zero]
|
1055
|
+
commands << [:stack_push, TRUE]
|
1056
|
+
|
1057
|
+
commands << [:flow_def, label_end]
|
1058
|
+
commands << [:flow_end]
|
1059
|
+
@methods << commands
|
1060
|
+
label
|
1061
|
+
)
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
# stack: [obj]
|
1065
|
+
# return stack: [TRUE/FALSE]
|
1066
|
+
private def op_not_label
|
1067
|
+
@op_not_label ||= (
|
1068
|
+
label = ident_to_label(nil)
|
1069
|
+
true_label = ident_to_label(nil)
|
1070
|
+
end_label = ident_to_label(nil)
|
1071
|
+
|
1072
|
+
commands = []
|
1073
|
+
commands << [:flow_def, label]
|
1074
|
+
|
1075
|
+
commands << [:flow_call, rtest_label]
|
1076
|
+
commands << [:flow_jump_if_zero, true_label]
|
1077
|
+
|
1078
|
+
# when obj is falsy
|
1079
|
+
commands << [:stack_push, TRUE]
|
1080
|
+
commands << [:flow_jump, end_label]
|
1081
|
+
|
1082
|
+
# when obj is truthy
|
1083
|
+
commands << [:flow_def, true_label]
|
1084
|
+
commands << [:stack_push, FALSE]
|
1085
|
+
|
1086
|
+
commands << [:flow_def, end_label]
|
1087
|
+
commands << [:flow_end]
|
1088
|
+
@methods << commands
|
1089
|
+
label
|
1090
|
+
)
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
# stack: [target]
|
1094
|
+
# return stack: [0/1] if true then 0, if false then 1.
|
1095
|
+
private def rtest_label
|
1096
|
+
@rtest_label ||= (
|
1097
|
+
truthy = 0
|
1098
|
+
falsy = 1
|
1099
|
+
|
1100
|
+
label = ident_to_label(nil)
|
1101
|
+
when_nil_label = ident_to_label(nil)
|
1102
|
+
when_false_label = ident_to_label(nil)
|
1103
|
+
end_label = ident_to_label(nil)
|
1104
|
+
|
1105
|
+
commands = []
|
1106
|
+
commands << [:flow_def, label]
|
1107
|
+
|
1108
|
+
commands << [:stack_dup]
|
1109
|
+
commands << [:stack_push, NIL]
|
1110
|
+
commands << [:calc_sub]
|
1111
|
+
commands << [:flow_jump_if_zero, when_nil_label]
|
1112
|
+
|
1113
|
+
commands << [:stack_push, FALSE]
|
1114
|
+
commands << [:calc_sub]
|
1115
|
+
commands << [:flow_jump_if_zero, when_false_label]
|
1116
|
+
|
1117
|
+
# when truthy
|
1118
|
+
commands << [:stack_push, truthy]
|
1119
|
+
commands << [:flow_jump, end_label]
|
1120
|
+
|
1121
|
+
# when nil
|
1122
|
+
commands << [:flow_def, when_nil_label]
|
1123
|
+
commands << [:stack_pop]
|
1124
|
+
# when false
|
1125
|
+
commands << [:flow_def, when_false_label]
|
1126
|
+
commands << [:stack_push, falsy]
|
1127
|
+
|
1128
|
+
commands << [:flow_def, end_label]
|
1129
|
+
commands << [:flow_end]
|
1130
|
+
@methods << commands
|
1131
|
+
label
|
1132
|
+
)
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
# stack: [type, val]
|
1136
|
+
# return stack: [int]
|
1137
|
+
# if val is a type then 0
|
1138
|
+
# if not then other int
|
1139
|
+
private def is_a_label
|
1140
|
+
@is_a_label ||= (
|
1141
|
+
label = ident_to_label(nil)
|
1142
|
+
|
1143
|
+
commands = []
|
1144
|
+
commands << [:flow_def, label]
|
1145
|
+
|
1146
|
+
commands << [:stack_push, 2 ** TYPE_BITS]
|
1147
|
+
commands << [:calc_mod]
|
1148
|
+
commands << [:calc_sub]
|
1149
|
+
|
1150
|
+
commands << [:flow_end]
|
1151
|
+
@methods << commands
|
1152
|
+
label
|
1153
|
+
)
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
# stack: [key, hash]
|
1157
|
+
# return stack: [addr_of_prev_key, addr_of_target_key]
|
1158
|
+
private def hash_key_to_addr_label
|
1159
|
+
@hash_key_to_addr_label ||= (
|
1160
|
+
label = ident_to_label(nil)
|
1161
|
+
key_not_collision_label = ident_to_label(nil)
|
1162
|
+
check_key_equivalent_label = ident_to_label(nil)
|
1163
|
+
|
1164
|
+
commands = []
|
1165
|
+
commands << [:flow_def, label]
|
1166
|
+
|
1167
|
+
commands.concat(UNWRAP_COMMANDS)
|
1168
|
+
commands << [:heap_load]
|
1169
|
+
commands << [:stack_swap]
|
1170
|
+
# stack: [addr_of_first_key, key (wrapped)]
|
1171
|
+
commands << [:stack_dup]
|
1172
|
+
commands.concat(SAVE_TMP_COMMANDS)
|
1173
|
+
|
1174
|
+
# calc hash
|
1175
|
+
# stack: [addr_of_first_key, key (wrapped)]
|
1176
|
+
commands.concat(UNWRAP_COMMANDS)
|
1177
|
+
commands << [:stack_push, HASH_SIZE]
|
1178
|
+
commands << [:calc_mod]
|
1179
|
+
commands << [:stack_push, 3]
|
1180
|
+
commands << [:calc_multi]
|
1181
|
+
# stack: [addr_of_first_key, hash]
|
1182
|
+
|
1183
|
+
commands << [:calc_add]
|
1184
|
+
commands << [:stack_push, NONE_ADDR]
|
1185
|
+
commands << [:stack_swap]
|
1186
|
+
# stack: [addr_of_prev_key, addr_of_target_key]
|
1187
|
+
|
1188
|
+
# Check key equivalent
|
1189
|
+
commands << [:flow_def, check_key_equivalent_label]
|
1190
|
+
commands << [:stack_dup]
|
1191
|
+
commands << [:heap_load]
|
1192
|
+
commands.concat(LOAD_TMP_COMMANDS)
|
1193
|
+
# stack: [addr_of_prev_key, addr_of_target_key, target_key, key]
|
1194
|
+
commands << [:calc_sub]
|
1195
|
+
commands << [:flow_jump_if_zero, key_not_collision_label]
|
1196
|
+
# stack: [addr_of_prev_key, addr_of_target_key]
|
1197
|
+
# Check NONE
|
1198
|
+
commands << [:stack_dup]
|
1199
|
+
commands << [:heap_load]
|
1200
|
+
commands << [:stack_push, NONE]
|
1201
|
+
commands << [:calc_sub]
|
1202
|
+
commands << [:flow_jump_if_zero, key_not_collision_label]
|
1203
|
+
|
1204
|
+
# stack: [addr_of_prev_key, addr_of_target_key]
|
1205
|
+
|
1206
|
+
# when collistion
|
1207
|
+
# pop prev key
|
1208
|
+
commands << [:stack_swap]
|
1209
|
+
commands << [:stack_pop]
|
1210
|
+
commands << [:stack_dup]
|
1211
|
+
# stack: [addr_of_target_key, addr_of_target_key]
|
1212
|
+
commands << [:stack_push, 2]
|
1213
|
+
commands << [:calc_add]
|
1214
|
+
# stack: [addr_of_prev_key, addr_of_next_key_addr]
|
1215
|
+
commands << [:heap_load]
|
1216
|
+
# stack: [addr_of_prev_key, next_key_addr]
|
1217
|
+
commands << [:stack_dup]
|
1218
|
+
commands << [:stack_push, NONE_ADDR]
|
1219
|
+
commands << [:calc_sub]
|
1220
|
+
commands << [:flow_jump_if_zero, key_not_collision_label]
|
1221
|
+
commands << [:flow_jump, check_key_equivalent_label]
|
1222
|
+
|
1223
|
+
commands << [:flow_def, key_not_collision_label]
|
1224
|
+
|
1225
|
+
commands << [:flow_end]
|
1226
|
+
@methods << commands
|
1227
|
+
label
|
1228
|
+
)
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
# stack: []
|
1232
|
+
# return stack: [array]
|
1233
|
+
private def allocate_array_commands(size)
|
1234
|
+
commands = []
|
1235
|
+
|
1236
|
+
commands.concat ALLOCATE_HEAP_COMMANDS
|
1237
|
+
commands << [:stack_dup]
|
1238
|
+
commands.concat WRAP_ARRAY_COMMANDS
|
1239
|
+
commands.concat SAVE_TMP_COMMANDS
|
1240
|
+
# stack: [array_addr_1]
|
1241
|
+
|
1242
|
+
# Save first addr
|
1243
|
+
commands << [:stack_dup]
|
1244
|
+
commands << [:stack_push, 3]
|
1245
|
+
commands << [:calc_add]
|
1246
|
+
commands << [:heap_save]
|
1247
|
+
# stack: []
|
1248
|
+
|
1249
|
+
# Allocate size
|
1250
|
+
commands.concat ALLOCATE_HEAP_COMMANDS
|
1251
|
+
commands << [:stack_push, size]
|
1252
|
+
commands << [:heap_save]
|
1253
|
+
|
1254
|
+
# Allocate cap
|
1255
|
+
cap = ARRAY_FIRST_CAPACITY < size ? size * 2 : ARRAY_FIRST_CAPACITY
|
1256
|
+
commands.concat ALLOCATE_HEAP_COMMANDS
|
1257
|
+
commands << [:stack_push, cap]
|
1258
|
+
commands << [:heap_save]
|
1259
|
+
|
1260
|
+
# Allocate cap size heaps
|
1261
|
+
commands << [:stack_push, cap]
|
1262
|
+
commands.concat ALLOCATE_N_HEAP_COMMANDS
|
1263
|
+
|
1264
|
+
commands.concat LOAD_TMP_COMMANDS
|
1265
|
+
# stack: [array]
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
# Array#size
|
1269
|
+
# stack: []
|
1270
|
+
# return stack: [int]
|
1271
|
+
private def define_array_size
|
1272
|
+
label = ident_to_label(:'Array#size')
|
1273
|
+
commands = []
|
1274
|
+
commands << [:flow_def, label]
|
1275
|
+
|
1276
|
+
commands.concat load_from_self_commands
|
1277
|
+
commands.concat UNWRAP_COMMANDS
|
1278
|
+
commands << [:stack_push, 1]
|
1279
|
+
commands << [:calc_add]
|
1280
|
+
commands << [:heap_load]
|
1281
|
+
commands.concat WRAP_NUMBER_COMMANDS
|
1282
|
+
|
1283
|
+
commands << [:flow_end]
|
1284
|
+
# stack: [size]
|
1285
|
+
@methods << commands
|
1286
|
+
end
|
1287
|
+
|
1288
|
+
# Array#pop
|
1289
|
+
# stack: []
|
1290
|
+
# return stack: [obj]
|
1291
|
+
private def define_array_pop
|
1292
|
+
label = ident_to_label(:'Array#pop')
|
1293
|
+
when_empty_label = ident_to_label(nil)
|
1294
|
+
commands = []
|
1295
|
+
commands << [:flow_def, label]
|
1296
|
+
|
1297
|
+
commands.concat load_from_self_commands
|
1298
|
+
commands.concat UNWRAP_COMMANDS
|
1299
|
+
commands << [:stack_push, 1]
|
1300
|
+
commands << [:calc_add]
|
1301
|
+
commands << [:heap_load]
|
1302
|
+
# stack: [size]
|
1303
|
+
# check empty
|
1304
|
+
commands << [:stack_dup]
|
1305
|
+
commands << [:flow_jump_if_zero, when_empty_label]
|
1306
|
+
|
1307
|
+
# when not empty
|
1308
|
+
# Decrease size
|
1309
|
+
commands << [:stack_dup]
|
1310
|
+
commands.concat load_from_self_commands
|
1311
|
+
commands.concat UNWRAP_COMMANDS
|
1312
|
+
commands << [:stack_push, 1]
|
1313
|
+
commands << [:calc_add]
|
1314
|
+
# stack: [size, size, size_addr]
|
1315
|
+
commands << [:stack_swap]
|
1316
|
+
commands << [:stack_push, 1]
|
1317
|
+
commands << [:calc_sub]
|
1318
|
+
commands << [:heap_save]
|
1319
|
+
# Load item
|
1320
|
+
commands.concat load_from_self_commands
|
1321
|
+
commands.concat UNWRAP_COMMANDS
|
1322
|
+
commands << [:heap_load]
|
1323
|
+
# stack: [size, first_addr]
|
1324
|
+
commands << [:stack_push, -1]
|
1325
|
+
commands << [:calc_add]
|
1326
|
+
commands << [:calc_add]
|
1327
|
+
# stack: [addr_of_target_item]
|
1328
|
+
commands << [:heap_load]
|
1329
|
+
|
1330
|
+
commands << [:flow_end]
|
1331
|
+
# stack: [target_item]
|
1332
|
+
|
1333
|
+
commands << [:flow_def, when_empty_label]
|
1334
|
+
commands << [:stack_pop]
|
1335
|
+
commands << [:stack_push, NIL]
|
1336
|
+
commands << [:flow_end]
|
1337
|
+
# stack: [nil]
|
1338
|
+
@methods << commands
|
1339
|
+
end
|
1340
|
+
|
1341
|
+
# Array#push
|
1342
|
+
# stack: [item]
|
1343
|
+
# return stack: [self]
|
1344
|
+
private def define_array_push
|
1345
|
+
label = ident_to_label(:'Array#push')
|
1346
|
+
when_realloc_label = ident_to_label(nil)
|
1347
|
+
when_no_realloc_label = ident_to_label(nil)
|
1348
|
+
commands = []
|
1349
|
+
commands << [:flow_def, label]
|
1350
|
+
|
1351
|
+
commands.concat load_from_self_commands
|
1352
|
+
commands.concat(UNWRAP_COMMANDS)
|
1353
|
+
# stack: [item, addr_of_first_addr]
|
1354
|
+
|
1355
|
+
# Check realloc necessary
|
1356
|
+
commands << [:stack_dup]
|
1357
|
+
commands << [:stack_push, 1]
|
1358
|
+
commands << [:calc_add]
|
1359
|
+
# stack: [item, addr_of_first_addr, addr_of_size]
|
1360
|
+
commands << [:stack_dup]
|
1361
|
+
commands << [:stack_push, 1]
|
1362
|
+
commands << [:calc_add]
|
1363
|
+
# stack: [item, addr_of_first_addr, addr_of_size, addr_of_cap]
|
1364
|
+
commands << [:heap_load]
|
1365
|
+
commands << [:stack_swap]
|
1366
|
+
commands << [:heap_load]
|
1367
|
+
# stack: [item, addr_of_first_addr, cap, size]
|
1368
|
+
commands << [:calc_sub]
|
1369
|
+
commands << [:flow_jump_if_zero, when_realloc_label]
|
1370
|
+
commands << [:flow_jump, when_no_realloc_label]
|
1371
|
+
|
1372
|
+
# Realloc
|
1373
|
+
commands << [:flow_def, when_realloc_label]
|
1374
|
+
commands << [:stack_dup]
|
1375
|
+
commands << [:flow_call, realloc_array_label]
|
1376
|
+
|
1377
|
+
commands << [:flow_def, when_no_realloc_label]
|
1378
|
+
|
1379
|
+
# Push
|
1380
|
+
# stack: [item, addr_of_first_addr]
|
1381
|
+
commands << [:stack_dup]
|
1382
|
+
commands << [:stack_push, 1]
|
1383
|
+
commands << [:calc_add]
|
1384
|
+
commands << [:heap_load]
|
1385
|
+
# stack: [item, addr_of_first_addr, size]
|
1386
|
+
commands << [:stack_swap]
|
1387
|
+
commands << [:heap_load]
|
1388
|
+
# stack: [item, size, first_addr]
|
1389
|
+
commands << [:calc_add]
|
1390
|
+
# stack: [item, addr_of_target]
|
1391
|
+
commands << [:stack_swap]
|
1392
|
+
commands << [:heap_save]
|
1393
|
+
|
1394
|
+
commands.concat load_from_self_commands
|
1395
|
+
# Update size
|
1396
|
+
commands << [:stack_dup]
|
1397
|
+
commands.concat UNWRAP_COMMANDS
|
1398
|
+
# stack: [self, addr_of_first_addr]
|
1399
|
+
commands << [:stack_push, 1]
|
1400
|
+
commands << [:calc_add]
|
1401
|
+
commands << [:stack_dup]
|
1402
|
+
commands << [:heap_load]
|
1403
|
+
# stack: [self, size_addr, size]
|
1404
|
+
commands << [:stack_push, 1]
|
1405
|
+
commands << [:calc_add]
|
1406
|
+
commands << [:heap_save]
|
1407
|
+
|
1408
|
+
commands << [:flow_end]
|
1409
|
+
# stack: [self]
|
1410
|
+
@methods << commands
|
1411
|
+
end
|
1412
|
+
|
1413
|
+
# Array#[]
|
1414
|
+
# stack: [index]
|
1415
|
+
# return stack: [item]
|
1416
|
+
private def define_array_ref
|
1417
|
+
label = ident_to_label(:'Array#[]')
|
1418
|
+
|
1419
|
+
commands = []
|
1420
|
+
commands << [:flow_def, label]
|
1421
|
+
|
1422
|
+
commands.concat(UNWRAP_COMMANDS)
|
1423
|
+
commands.concat load_from_self_commands
|
1424
|
+
# stack: [index, recv]
|
1425
|
+
commands.concat(UNWRAP_COMMANDS)
|
1426
|
+
commands << [:heap_load]
|
1427
|
+
# stack: [addr_of_first_item, index]
|
1428
|
+
commands << [:calc_add]
|
1429
|
+
# TODO: range check and return nil
|
1430
|
+
commands << [:heap_load]
|
1431
|
+
|
1432
|
+
commands << [:flow_end]
|
1433
|
+
@methods << commands
|
1434
|
+
end
|
1435
|
+
|
1436
|
+
# Array#[]=
|
1437
|
+
# stack: [index, value]
|
1438
|
+
# return stack: [value]
|
1439
|
+
private def define_array_attr_asgn
|
1440
|
+
label = ident_to_label(:'Array#[]=')
|
1441
|
+
|
1442
|
+
commands = []
|
1443
|
+
commands << [:flow_def, label]
|
1444
|
+
|
1445
|
+
commands << [:stack_swap]
|
1446
|
+
# stack: [value, index]
|
1447
|
+
commands.concat UNWRAP_COMMANDS
|
1448
|
+
commands.concat load_from_self_commands
|
1449
|
+
commands.concat(UNWRAP_COMMANDS)
|
1450
|
+
commands << [:heap_load]
|
1451
|
+
# stack: [value, index, first_addr]
|
1452
|
+
commands << [:calc_add]
|
1453
|
+
# TODO: range check and realloc
|
1454
|
+
commands << [:stack_swap]
|
1455
|
+
# stack: [target_addr, value]
|
1456
|
+
commands << [:stack_dup]
|
1457
|
+
commands.concat SAVE_TMP_COMMANDS
|
1458
|
+
commands << [:heap_save]
|
1459
|
+
commands.concat LOAD_TMP_COMMANDS
|
1460
|
+
# stack: [value]
|
1461
|
+
|
1462
|
+
commands << [:flow_end]
|
1463
|
+
@methods << commands
|
1464
|
+
end
|
1465
|
+
|
1466
|
+
# Hash#[]
|
1467
|
+
# stack: [key]
|
1468
|
+
private def define_hash_ref
|
1469
|
+
label = ident_to_label(:'Hash#[]')
|
1470
|
+
when_not_found_label = ident_to_label(nil)
|
1471
|
+
|
1472
|
+
commands = []
|
1473
|
+
commands << [:flow_def, label]
|
1474
|
+
|
1475
|
+
commands.concat load_from_self_commands
|
1476
|
+
commands << [:flow_call, hash_key_to_addr_label]
|
1477
|
+
# stack: [addr_of_prev_key, addr_of_target_key]
|
1478
|
+
|
1479
|
+
# pop addr_of_prev_key
|
1480
|
+
commands << [:stack_swap]
|
1481
|
+
commands << [:stack_pop]
|
1482
|
+
|
1483
|
+
# stack: [addr_of_target_key]
|
1484
|
+
# check NONE_ADDR (chained)
|
1485
|
+
commands << [:stack_dup]
|
1486
|
+
commands << [:stack_push, NONE_ADDR]
|
1487
|
+
commands << [:calc_sub]
|
1488
|
+
commands << [:flow_jump_if_zero, when_not_found_label]
|
1489
|
+
|
1490
|
+
# check NONE (not chained)
|
1491
|
+
commands << [:stack_dup]
|
1492
|
+
commands << [:heap_load]
|
1493
|
+
# stack: [addr_of_target_key, target_key]
|
1494
|
+
commands << [:stack_push, NONE]
|
1495
|
+
commands << [:calc_sub]
|
1496
|
+
commands << [:flow_jump_if_zero, when_not_found_label]
|
1497
|
+
|
1498
|
+
# when found
|
1499
|
+
commands << [:stack_push, 1]
|
1500
|
+
commands << [:calc_add]
|
1501
|
+
# stack: [addr_of_target_value]
|
1502
|
+
commands << [:heap_load]
|
1503
|
+
|
1504
|
+
commands << [:flow_end]
|
1505
|
+
|
1506
|
+
# when not found
|
1507
|
+
commands << [:flow_def, when_not_found_label]
|
1508
|
+
commands << [:stack_pop]
|
1509
|
+
commands << [:stack_push, NIL]
|
1510
|
+
commands << [:flow_end]
|
1511
|
+
@methods << commands
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
# Hash#[]
|
1515
|
+
# stack: [key, value]
|
1516
|
+
private def define_hash_attr_asgn
|
1517
|
+
label = ident_to_label(:'Hash#[]=')
|
1518
|
+
when_not_allocated_label = ident_to_label(nil)
|
1519
|
+
when_allocated_label = ident_to_label(nil)
|
1520
|
+
after_allocated_label = ident_to_label(nil)
|
1521
|
+
fill_none_addr_label = ident_to_label(nil)
|
1522
|
+
|
1523
|
+
commands = []
|
1524
|
+
commands << [:flow_def, label]
|
1525
|
+
|
1526
|
+
# stack: [key, value]
|
1527
|
+
commands << [:stack_swap]
|
1528
|
+
commands << [:stack_dup]
|
1529
|
+
commands.concat load_from_self_commands
|
1530
|
+
# stack: [value, key, key, recv]
|
1531
|
+
|
1532
|
+
commands << [:flow_call, hash_key_to_addr_label]
|
1533
|
+
# stack: [value, key, addr_of_prev_key, addr_of_target_key]
|
1534
|
+
|
1535
|
+
# check NONE_ADDR
|
1536
|
+
commands << [:stack_dup]
|
1537
|
+
commands << [:stack_push, NONE_ADDR]
|
1538
|
+
commands << [:calc_sub]
|
1539
|
+
commands << [:flow_jump_if_zero, when_not_allocated_label]
|
1540
|
+
commands << [:flow_jump, when_allocated_label]
|
1541
|
+
|
1542
|
+
# When not allocated
|
1543
|
+
commands << [:flow_def, when_not_allocated_label]
|
1544
|
+
# stack: [value, key, addr_of_prev_key, addr_of_target_key]
|
1545
|
+
commands << [:stack_pop]
|
1546
|
+
commands << [:stack_push, 2]
|
1547
|
+
commands << [:calc_add]
|
1548
|
+
commands.concat ALLOCATE_NEW_HASH_ITEM_COMMANDS
|
1549
|
+
# stack: [value, key, addr_of_prev_key, allocated_addr_of_target_key]
|
1550
|
+
commands << [:stack_dup]
|
1551
|
+
commands.concat SAVE_TMP_COMMANDS
|
1552
|
+
commands << [:heap_save]
|
1553
|
+
commands.concat LOAD_TMP_COMMANDS
|
1554
|
+
# stack: [value, key, allocated_addr_of_target_key]
|
1555
|
+
|
1556
|
+
# Fill NONE_ADDR to next
|
1557
|
+
commands << [:flow_def, fill_none_addr_label]
|
1558
|
+
commands << [:stack_dup]
|
1559
|
+
commands << [:stack_push, 2]
|
1560
|
+
commands << [:calc_add]
|
1561
|
+
# stack: [value, key, allocated_addr_of_target_key, addr_of_next_key_addr]
|
1562
|
+
commands << [:stack_push, NONE_ADDR]
|
1563
|
+
commands << [:heap_save]
|
1564
|
+
# stack: [value, key, allocated_addr_of_target_key]
|
1565
|
+
commands << [:flow_jump, after_allocated_label]
|
1566
|
+
|
1567
|
+
# When allocated
|
1568
|
+
commands << [:flow_def, when_allocated_label]
|
1569
|
+
# stack: [value, key, addr_of_prev_key, addr_of_target_key]
|
1570
|
+
commands << [:stack_swap]
|
1571
|
+
commands << [:stack_pop]
|
1572
|
+
# stack: [value, key, addr_of_target_key]
|
1573
|
+
commands << [:stack_dup]
|
1574
|
+
commands << [:heap_load]
|
1575
|
+
commands << [:stack_push, NONE]
|
1576
|
+
commands << [:calc_sub]
|
1577
|
+
commands << [:flow_jump_if_zero, fill_none_addr_label]
|
1578
|
+
|
1579
|
+
# stack: [value, key, addr_of_target_key]
|
1580
|
+
commands << [:flow_def, after_allocated_label]
|
1581
|
+
# Save key
|
1582
|
+
commands << [:stack_dup]
|
1583
|
+
commands.concat SAVE_TMP_COMMANDS # addr_of_target_key
|
1584
|
+
commands << [:stack_swap]
|
1585
|
+
commands << [:heap_save]
|
1586
|
+
# Save value
|
1587
|
+
commands.concat LOAD_TMP_COMMANDS # addr_of_target_key
|
1588
|
+
# stack: [value, addr_of_target_key]
|
1589
|
+
commands << [:stack_push, 1]
|
1590
|
+
commands << [:calc_add]
|
1591
|
+
# stack: [value, addr_of_target_value]
|
1592
|
+
commands << [:stack_swap]
|
1593
|
+
commands << [:stack_dup]
|
1594
|
+
commands.concat SAVE_TMP_COMMANDS
|
1595
|
+
commands << [:heap_save]
|
1596
|
+
commands.concat LOAD_TMP_COMMANDS
|
1597
|
+
|
1598
|
+
commands << [:flow_end]
|
1599
|
+
@methods << commands
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
# Integer#<=>
|
1603
|
+
# stack: [right]
|
1604
|
+
# return stack: [-1/0/1]
|
1605
|
+
# if left < rigth then -1
|
1606
|
+
# if left == rigth then 0
|
1607
|
+
# if left > rigth then 1
|
1608
|
+
private def define_op_spaceship
|
1609
|
+
label = ident_to_label(:'Integer#<=>')
|
1610
|
+
zero_label = ident_to_label(nil)
|
1611
|
+
end_label = ident_to_label(nil)
|
1612
|
+
neg_label = ident_to_label(nil)
|
1613
|
+
commands = []
|
1614
|
+
commands << [:flow_def, label]
|
1615
|
+
|
1616
|
+
commands.concat load_from_self_commands
|
1617
|
+
commands << [:calc_sub]
|
1618
|
+
commands << [:stack_dup]
|
1619
|
+
commands << [:flow_jump_if_zero, zero_label]
|
1620
|
+
|
1621
|
+
commands << [:flow_jump_if_neg, neg_label]
|
1622
|
+
|
1623
|
+
# if positive
|
1624
|
+
commands << [:stack_push, with_type(-1, TYPE_INT)]
|
1625
|
+
commands << [:flow_jump, end_label]
|
1626
|
+
|
1627
|
+
# if negative
|
1628
|
+
commands << [:flow_def, neg_label]
|
1629
|
+
commands << [:stack_push, with_type(1, TYPE_INT)]
|
1630
|
+
commands << [:flow_jump, end_label]
|
1631
|
+
|
1632
|
+
# if equal
|
1633
|
+
commands << [:flow_def, zero_label]
|
1634
|
+
commands << [:stack_pop]
|
1635
|
+
commands << [:stack_push, with_type(0, TYPE_INT)]
|
1636
|
+
|
1637
|
+
commands << [:flow_def, end_label]
|
1638
|
+
commands << [:flow_end]
|
1639
|
+
@methods << commands
|
1640
|
+
end
|
1641
|
+
|
1642
|
+
|
1643
|
+
private def check_char!(char)
|
1644
|
+
raise ParseError, "String size must be 1, but it's #{char} (#{char.size})" if char.size != 1
|
303
1645
|
end
|
304
1646
|
|
305
1647
|
private def num_to_ws(num)
|
@@ -315,6 +1657,56 @@ module Akaza
|
|
315
1657
|
private def next_label_index
|
316
1658
|
@label_index += 1
|
317
1659
|
end
|
1660
|
+
|
1661
|
+
# @param ident [Symbol | nil]
|
1662
|
+
private def ident_to_label(ident)
|
1663
|
+
if ident
|
1664
|
+
ident = ident.to_sym
|
1665
|
+
@labels[ident] ||= next_label_index
|
1666
|
+
# .tap {|index| p [ident, num_to_ws(index).chop]}
|
1667
|
+
else
|
1668
|
+
next_label_index
|
1669
|
+
# .tap { |index| p [caller[2], num_to_ws(index).chop] }
|
1670
|
+
end
|
1671
|
+
end
|
1672
|
+
|
1673
|
+
private def variable_addr_index
|
1674
|
+
@variable_addr_index += 1
|
1675
|
+
end
|
1676
|
+
|
1677
|
+
private def variable_name_to_addr(ident)
|
1678
|
+
@variable_addrs[ident] ||= variable_addr_index
|
1679
|
+
end
|
1680
|
+
|
1681
|
+
private def with_type(val, type)
|
1682
|
+
(val << TYPE_BITS) + type
|
1683
|
+
end
|
1684
|
+
|
1685
|
+
private def lvars
|
1686
|
+
@lvars_stack.last
|
1687
|
+
end
|
1688
|
+
|
1689
|
+
private def update_lvar_commands(table, args: [])
|
1690
|
+
addr_table = table.map do |var_name|
|
1691
|
+
[var_name, variable_name_to_addr(var_name)]
|
1692
|
+
end
|
1693
|
+
commands = []
|
1694
|
+
addr_table.each do |var_name, addr|
|
1695
|
+
next if args.include?(var_name)
|
1696
|
+
commands << [:stack_push, addr]
|
1697
|
+
commands << [:stack_push, NIL]
|
1698
|
+
commands << [:heap_save]
|
1699
|
+
end
|
1700
|
+
@lvars_stack << addr_table.map{@2}
|
1701
|
+
lvars << variable_name_to_addr(:self)
|
1702
|
+
commands
|
1703
|
+
end
|
1704
|
+
|
1705
|
+
private def lazy_compile_method(name)
|
1706
|
+
@method_definitions.delete(name)&.each do |d|
|
1707
|
+
compile_def(d.name, d.lvar_table, d.args_count, d.body, d.klass)
|
1708
|
+
end
|
1709
|
+
end
|
318
1710
|
end
|
319
1711
|
end
|
320
1712
|
end
|