akaza 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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