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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a887b99455c8e17fb594fe02a570c5ea0251c57b5d3e0a73030aeff7c7d5c35
4
- data.tar.gz: 5f57819daa32f8560816bb36a45930acef8aeecb11d9bc1acf7f247d75c9484f
3
+ metadata.gz: 2acfb98593d59509b9e04d79bc2caad1a69ad0a73608b0046b401b3417e8a8a5
4
+ data.tar.gz: a7cc5518798a5ee863cf1e34e09ef95f5b8143ed47a0f9bc36b66a9d010b358e
5
5
  SHA512:
6
- metadata.gz: 4bbe86bad2ad3096bd1d6845bd6a88f831977022c7b31cf4bd0aa8b35f6f89e68907bb1cd813fd25b0f656e2c4e2be936d77a51efef554245ebae3ed05349584
7
- data.tar.gz: f426f4188fdb40d9632042941c2633922df440bf9fea1f08338e2ebdc43b86bf6029bad3986e8cd2431df1bc1258e75d83bee6edb9d67459e1471ce591abe953
6
+ metadata.gz: 80bf448885f9b9b49bf730945a9d0eceff3ae51c01c922fd6551f2a5a4bb8cdd159936bc09f2747eb23ac13772095915cdb5ef2aaedb884133d88a9fe80658c3
7
+ data.tar.gz: a147e1a53eee6f41d65238fdf1edefc656fb470e322b723f9bb07e25ec097f9c37b82f279ded9df3bf020d8d2e236c178f2c905e48e59af94fafef460fc6aa6a
data/.gitignore CHANGED
@@ -2,7 +2,6 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /coverage/
5
- /doc/
6
5
  /pkg/
7
6
  /spec/reports/
8
7
  /tmp/
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.
@@ -30,4 +30,5 @@ Gem::Specification.new do |spec|
30
30
  spec.add_development_dependency "bundler"
31
31
  spec.add_development_dependency "rake", "~> 12.0"
32
32
  spec.add_development_dependency "minitest", ">= 5"
33
+ spec.add_development_dependency "m"
33
34
  end
@@ -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.
@@ -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
@@ -6,12 +6,9 @@ module Akaza
6
6
  end
7
7
 
8
8
  def traverse(&on_enter)
9
- opt = {}
10
- on_enter.call self, opt
11
- unless opt[:skip_children]
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
 
@@ -43,15 +43,15 @@ module Akaza
43
43
  private def parse_stack
44
44
  case ch = nextc
45
45
  when SPACE
46
- [:stack, :push, nextint]
46
+ [:stack_push, nextint]
47
47
  else
48
48
  case c = [ch, nextc]
49
49
  when [NL, SPACE]
50
- [:stack, :dup]
50
+ [:stack_dup]
51
51
  when [NL, TAB]
52
- [:stack, :swap]
52
+ [:stack_swap]
53
53
  when [NL, NL]
54
- [:stack, :pop]
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
- [:flow, :def, nextlabel]
64
+ [:flow_def, nextlabel]
65
65
  when [SPACE, TAB]
66
- [:flow, :call, nextlabel]
66
+ [:flow_call, nextlabel]
67
67
  when [SPACE, NL]
68
- [:flow, :jump, nextlabel]
68
+ [:flow_jump, nextlabel]
69
69
  when [TAB, SPACE]
70
- [:flow, :jump_if_zero, nextlabel]
70
+ [:flow_jump_if_zero, nextlabel]
71
71
  when [TAB, TAB]
72
- [:flow, :jump_if_neg, nextlabel]
72
+ [:flow_jump_if_neg, nextlabel]
73
73
  when [TAB, NL]
74
- [:flow, :end]
74
+ [:flow_end]
75
75
  when [NL, NL]
76
- [:flow, :exit]
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
- [:calc, :add]
85
+ [:calc_add]
86
86
  when [SPACE, TAB]
87
- [:calc, :sub]
87
+ [:calc_sub]
88
88
  when [SPACE, NL]
89
- [:calc, :multi]
89
+ [:calc_multi]
90
90
  when [TAB, SPACE]
91
- [:calc, :div]
91
+ [:calc_div]
92
92
  when [TAB, TAB]
93
- [:calc, :mod]
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
- [:heap, :save]
102
+ [:heap_save]
103
103
  when TAB
104
- [:heap, :load]
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
- [:io, :write_char]
113
+ [:io_write_char]
114
114
  when [SPACE, TAB]
115
- [:io, :write_num]
115
+ [:io_write_num]
116
116
  when [TAB, SPACE]
117
- [:io, :read_char]
117
+ [:io_read_char]
118
118
  when [TAB, TAB]
119
- [:io, :read_num]
119
+ [:io_read_num]
120
120
  else
121
121
  raise "unreachable: #{c}"
122
122
  end
@@ -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
- def initialize(ruby_code)
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
- commands = ast_to_commands(ast, main: true)
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 ast_to_commands(ast, main:)
219
+ private def compile_expr(node)
57
220
  commands = []
58
- methods = []
59
- lvars = []
60
-
61
- ast.traverse do |node, opt|
62
- case node
63
- in [:FCALL, :put_as_number, [:ARRAY, arg, nil]]
64
- commands.concat(push_value(arg))
65
- commands << [:io, :write_num]
66
- opt[:skip_children] = true
67
- in [:FCALL, :put_as_char, [:ARRAY, arg, nil]]
68
- commands.concat(push_value(arg))
69
- commands << [:io, :write_char]
70
- opt[:skip_children] = true
71
- in [:VCALL, :exit]
72
- commands << [:flow, :exit]
73
- in [:LASGN, var, arg]
74
- var_addr = str_to_int(var, type: :variable)
75
- commands << [:stack, :push, var_addr]
76
- commands.concat(push_value(arg))
77
- commands << [:heap, :save]
78
- opt[:skip_children] = true
79
- lvars << var_addr
80
- in [:DEFN, name, [:SCOPE, lvar_table, [:ARGS, args_count ,*_], body]]
81
- m = [
82
- [:flow, :def, str_to_int(name, type: :method)],
83
- ]
84
- lvar_table[0...args_count].reverse.each do |args_name|
85
- m << [:stack, :push, str_to_int(args_name, type: :variable)]
86
- m << [:stack, :swap]
87
- m << [:heap, :save]
88
- end
89
- m.concat(ast_to_commands(body, main: false))
90
- m << [:flow, :end]
91
-
92
- methods << m
93
- opt[:skip_children] = true
94
- in [:SCOPE, *_] | [:BLOCK, *_]
95
- # skip
96
- in [:VCALL, name]
97
- with_storing_lvars(lvars, commands) do
98
- commands << [:flow, :call, str_to_int(name, type: :method)]
99
- end
100
- opt[:skip_children] = true
101
- in [:FCALL, name, [:ARRAY, *args, nil]]
102
- with_storing_lvars(lvars, commands) do
103
- args.each do |arg|
104
- commands.concat(push_value(arg))
105
- end
106
- commands << [:flow, :call, str_to_int(name, type: :method)]
107
- end
108
- opt[:skip_children] = true
109
- in [:IF, cond, if_body, else_body]
110
- commands.concat(compile_if(cond, if_body, else_body))
111
- opt[:skip_children] = true
112
- in [:UNLESS, cond, else_body, if_body]
113
- commands.concat(compile_if(cond, if_body, else_body))
114
- opt[:skip_children] = true
115
- in [:WHILE, cond, body]
116
- commands.concat(compile_while(cond, body))
117
- opt[:skip_children] = true
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 [:stack, :push, num]
513
+ in [:stack_push, num]
131
514
  buf << SPACE << SPACE << num_to_ws(num)
132
- in [:stack, :swap]
515
+ in [:stack_pop]
516
+ buf << SPACE << NL << NL
517
+ in [:stack_swap]
133
518
  buf << SPACE << NL << TAB
134
- in [:heap, :save]
519
+ in [:stack_dup]
520
+ buf << SPACE << NL << SPACE
521
+ in [:heap_save]
135
522
  buf << TAB << TAB << SPACE
136
- in [:heap, :load]
523
+ in [:heap_load]
137
524
  buf << TAB << TAB << TAB
138
- in [:io, :write_char]
525
+ in [:io_write_char]
139
526
  buf << TAB << NL << SPACE << SPACE
140
- in [:io, :write_num]
527
+ in [:io_write_num]
141
528
  buf << TAB << NL << SPACE << TAB
142
- in [:io, :read_char]
529
+ in [:io_read_char]
143
530
  buf << TAB << NL << TAB << SPACE
144
- in [:io, :read_num]
531
+ in [:io_read_num]
145
532
  buf << TAB << NL << TAB << TAB
146
- in [:flow, :exit]
533
+ in [:flow_exit]
147
534
  buf << NL << NL << NL
148
- in [:flow, :call, num]
535
+ in [:flow_call, num]
149
536
  buf << NL << SPACE << TAB << num_to_ws(num)
150
- in [:flow, :def, num]
537
+ in [:flow_def, num]
151
538
  buf << NL << SPACE << SPACE << num_to_ws(num)
152
- in [:flow, :end]
539
+ in [:flow_end]
153
540
  buf << NL << TAB << NL
154
- in [:flow, :jump_if_zero, label]
541
+ in [:flow_jump_if_zero, label]
155
542
  buf << NL << TAB << SPACE << num_to_ws(label)
156
- in [:flow, :jump, label]
543
+ in [:flow_jump, label]
157
544
  buf << NL << SPACE << NL << num_to_ws(label)
158
- in [:flow, :jump_if_neg, label]
545
+ in [:flow_jump_if_neg, label]
159
546
  buf << NL << TAB << TAB << num_to_ws(label)
160
- in [:calc, :add]
547
+ in [:calc_add]
161
548
  buf << TAB << SPACE << SPACE << SPACE
162
- in [:calc, :sub]
549
+ in [:calc_sub]
163
550
  buf << TAB << SPACE << SPACE << TAB
164
- in [:calc, :multi]
551
+ in [:calc_multi]
165
552
  buf << TAB << SPACE << SPACE << NL
166
- in [:calc, :div]
553
+ in [:calc_div]
167
554
  buf << TAB << SPACE << TAB << SPACE
168
- in [:calc, :mod]
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(lvars, commands, &block)
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 << [:stack, :push, var_addr]
179
- commands << [:stack, :push, var_addr]
180
- commands << [:heap, :load]
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 << [:heap, :save]
573
+ commands << [:heap_save]
191
574
  end
192
575
  end
193
576
 
194
- private def push_value(ast)
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
- case ast
198
- in [:LIT, num]
199
- commands << [:stack, :push, num]
200
- in [:STR, str]
201
- check_char!(str)
202
- commands << [:stack, :push, str.ord]
203
- in [:LVAR, name]
204
- commands << [:stack, :push, str_to_int(name, type: :variable)]
205
- commands << [:heap, :load]
206
- in [:VCALL, :get_as_number]
207
- tmp_var = str_to_int("__tmp", type: :variable)
208
- commands << [:stack, :push, tmp_var]
209
- commands << [:io, :read_num]
210
- commands << [:stack, :push, tmp_var]
211
- commands << [:heap, :load]
212
- in [:VCALL, :get_as_char]
213
- tmp_var = str_to_int("__tmp", type: :variable)
214
- commands << [:stack, :push, tmp_var]
215
- commands << [:io, :read_char]
216
- commands << [:stack, :push, tmp_var]
217
- commands << [:heap, :load]
218
- in [:OPCALL, l, sym, [:ARRAY, r, nil]]
219
- com = {'+': :add, '-': :sub, '*': :multi, '/': :div, '%': :mod}[sym]
220
- raise ParserError, "Unknown symbol: #{sym}" unless com
221
- commands.concat(push_value(l))
222
- commands.concat(push_value(r))
223
- commands << [:calc, com]
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
- else_label = str_to_int("else_#{next_label_index}", type: :condition)
232
- end_label = str_to_int("end_#{next_label_index}", type: :condition)
233
-
234
- body = -> (x, sym) do
235
- commands.concat(push_value(x))
236
- commands << [:flow, sym, else_label]
237
- commands.concat(ast_to_commands(else_body, main: false)) if else_body
238
- commands << [:flow, :jump, end_label]
239
- commands << [:flow, :def, else_label]
240
- commands.concat(ast_to_commands(if_body, main: false)) if if_body
241
- commands << [:flow, :def, end_label]
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
- body.(x, :jump_if_zero)
737
+ optimized_body.(x, :flow_jump_if_zero)
247
738
  in [:OPCALL, x, :==, [:ARRAY, [:LIT, 0], nil]]
248
- body.(x, :jump_if_zero)
739
+ optimized_body.(x, :flow_jump_if_zero)
249
740
  in [:OPCALL, x, :<, [:ARRAY, [:LIT, 0], nil]]
250
- body.(x, :jump_if_neg)
741
+ optimized_body.(x, :flow_jump_if_neg)
251
742
  in [:OPCALL, [:LIT, 0], :<, [:ARRAY, x, nil]]
252
- body.(x, :jump_if_neg)
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 = str_to_int("cond_#{next_label_index}", type: :condition)
261
- body_label = str_to_int("body_#{next_label_index}", type: :condition)
262
- end_label = str_to_int("end_#{next_label_index}", type: :condition)
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 << [:flow, :def, cond_label]
266
- commands.concat(push_value(x))
267
- commands << [:flow, sym, body_label]
268
- commands << [:flow, :jump, end_label]
269
- commands << [:flow, :def, body_label]
270
- commands.concat(ast_to_commands(body, main: false))
271
- commands << [:flow, :jump, cond_label]
272
- commands << [:flow, :def, end_label]
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, :jump_if_zero)
847
+ make_body.(x, :flow_jump_if_zero)
278
848
  in [:OPCALL, x, :==, [:ARRAY, [:LIT, 0], nil]]
279
- make_body.(x, :jump_if_zero)
849
+ make_body.(x, :flow_jump_if_zero)
280
850
  in [:OPCALL, x, :<, [:ARRAY, [:LIT, 0], nil]]
281
- make_body.(x, :jump_if_neg)
851
+ make_body.(x, :flow_jump_if_neg)
282
852
  in [:OPCALL, [:LIT, 0], :<, [:ARRAY, x, nil]]
283
- make_body.(x, :jump_if_neg)
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 check_char!(char)
290
- raise ParserError, "String size must be 1, but it's #{char} (#{char.size})" if char.size != 1
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 str_to_int(str, type:)
294
- prefix =
295
- case type
296
- when :variable then 'v'
297
- when :method then 'f'
298
- when :condition then 'c'
299
- else
300
- raise "Unknown type: #{type}"
301
- end
302
- "#{prefix}_#{str}".bytes.inject { |a, b| (a << 8) + b }
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