akaza 0.1.0 → 0.2.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: f8f268d8e1d526b8e7d63dba375322624e27a5af9c281f91101aa60c3ba5efc6
4
- data.tar.gz: 561b42e5bac690f8c983cb03f992558ee5a35d24c81da8025973ff1a11b332a1
3
+ metadata.gz: 7a887b99455c8e17fb594fe02a570c5ea0251c57b5d3e0a73030aeff7c7d5c35
4
+ data.tar.gz: 5f57819daa32f8560816bb36a45930acef8aeecb11d9bc1acf7f247d75c9484f
5
5
  SHA512:
6
- metadata.gz: 250e590b52669d99cbd3bac3c942c38558f9fb270b60a273507d4695c249afdd238011013b364db491c9945c7b383446563533b18ee39ff71af1c26b5047d948
7
- data.tar.gz: 2f6bac299e64ecaaa42a21d04de0c20393b9caab95cc1a01c5e9bdb3c486453f8fb53ea2aa68379758d3226c1a45299d8733c8dddfb15553c1bc0814dba3f246
6
+ metadata.gz: 4bbe86bad2ad3096bd1d6845bd6a88f831977022c7b31cf4bd0aa8b35f6f89e68907bb1cd813fd25b0f656e2c4e2be936d77a51efef554245ebae3ed05349584
7
+ data.tar.gz: f426f4188fdb40d9632042941c2633922df440bf9fea1f08338e2ebdc43b86bf6029bad3986e8cd2431df1bc1258e75d83bee6edb9d67459e1471ce591abe953
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ /Gemfile.lock
data/README.md CHANGED
@@ -66,8 +66,7 @@ class A
66
66
  Akaza::Body
67
67
 
68
68
 
69
-
70
-
69
+
71
70
  output.string.to_i
72
71
 
73
72
  end
@@ -112,8 +111,7 @@ This code is never evaluated.
112
111
  So you can write any
113
112
  sentences as comments!
114
113
 
115
-
116
-
114
+
117
115
 
118
116
 
119
117
  end
@@ -123,7 +121,7 @@ a = A.new
123
121
  input = StringIO.new("20\n22\n")
124
122
  output = StringIO.new
125
123
  a.sum(input, output)
126
- p output.string
124
+ p output.string # => "42"
127
125
  ```
128
126
 
129
127
  ## Development
@@ -27,7 +27,7 @@ Gem::Specification.new do |spec|
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
29
 
30
- spec.add_development_dependency "bundler", "~> 2.1.a"
30
+ spec.add_development_dependency "bundler"
31
31
  spec.add_development_dependency "rake", "~> 12.0"
32
32
  spec.add_development_dependency "minitest", ">= 5"
33
33
  end
@@ -3,6 +3,7 @@ require 'akaza/ast_ext'
3
3
  require 'akaza/annotation'
4
4
  require 'akaza/parser'
5
5
  require 'akaza/vm'
6
+ require 'akaza/ruby2ws'
6
7
 
7
8
  module Akaza
8
9
  def self.eval(code, input: $stdin, output: $stdout)
@@ -5,10 +5,13 @@ module Akaza
5
5
  file_content(path)[first_index(path)..last_index(path)]
6
6
  end
7
7
 
8
- def traverse(&block)
9
- block.call self
10
- children.each do |child|
11
- child.traverse(&block) if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
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
12
15
  end
13
16
  end
14
17
 
@@ -115,7 +115,7 @@ module Akaza
115
115
  [:io, :write_num]
116
116
  when [TAB, SPACE]
117
117
  [:io, :read_char]
118
- when [TAB, NL]
118
+ when [TAB, TAB]
119
119
  [:io, :read_num]
120
120
  else
121
121
  raise "unreachable: #{c}"
@@ -0,0 +1,320 @@
1
+ module Akaza
2
+ # Convert Ruby like script to Whitespace.
3
+ # The syntax is a subset of Ruby,
4
+ # but it has different semantics with Ruby.
5
+ #
6
+ # # sample code
7
+ # # output
8
+ # put_as_number n
9
+ # put_as_char ch
10
+ # put_as_number 42
11
+ # put_as_char 'a'
12
+ #
13
+ # # input
14
+ # num = get_as_number
15
+ # char = get_as_char
16
+ #
17
+ # # flow
18
+ # def foo
19
+ # end
20
+ #
21
+ # exit
22
+ #
23
+ # if x == 0
24
+ # end
25
+ #
26
+ # if x < 0
27
+ # end
28
+ #
29
+ # # heap
30
+ # x = 10
31
+ # push x
32
+ module Ruby2ws
33
+ using AstExt
34
+ SPACE = ' '
35
+ TAB = "\t"
36
+ NL = "\n"
37
+
38
+ class ParseError < StandardError; end
39
+
40
+ def self.ruby_to_ws(ruby_code)
41
+ Transpiler.new(ruby_code).transpile
42
+ end
43
+
44
+ class Transpiler
45
+ def initialize(ruby_code)
46
+ @ruby_code = ruby_code
47
+ @label_index = 0
48
+ end
49
+
50
+ def transpile
51
+ ast = RubyVM::AbstractSyntaxTree.parse(@ruby_code)
52
+ commands = ast_to_commands(ast, main: true)
53
+ commands_to_ws(commands)
54
+ end
55
+
56
+ private def ast_to_commands(ast, main:)
57
+ 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
118
+ end
119
+ end
120
+
121
+ commands << [:flow, :exit] if main
122
+ commands.concat(*methods)
123
+ commands
124
+ end
125
+
126
+ private def commands_to_ws(commands)
127
+ buf = +""
128
+ commands.each do |command|
129
+ case command
130
+ in [:stack, :push, num]
131
+ buf << SPACE << SPACE << num_to_ws(num)
132
+ in [:stack, :swap]
133
+ buf << SPACE << NL << TAB
134
+ in [:heap, :save]
135
+ buf << TAB << TAB << SPACE
136
+ in [:heap, :load]
137
+ buf << TAB << TAB << TAB
138
+ in [:io, :write_char]
139
+ buf << TAB << NL << SPACE << SPACE
140
+ in [:io, :write_num]
141
+ buf << TAB << NL << SPACE << TAB
142
+ in [:io, :read_char]
143
+ buf << TAB << NL << TAB << SPACE
144
+ in [:io, :read_num]
145
+ buf << TAB << NL << TAB << TAB
146
+ in [:flow, :exit]
147
+ buf << NL << NL << NL
148
+ in [:flow, :call, num]
149
+ buf << NL << SPACE << TAB << num_to_ws(num)
150
+ in [:flow, :def, num]
151
+ buf << NL << SPACE << SPACE << num_to_ws(num)
152
+ in [:flow, :end]
153
+ buf << NL << TAB << NL
154
+ in [:flow, :jump_if_zero, label]
155
+ buf << NL << TAB << SPACE << num_to_ws(label)
156
+ in [:flow, :jump, label]
157
+ buf << NL << SPACE << NL << num_to_ws(label)
158
+ in [:flow, :jump_if_neg, label]
159
+ buf << NL << TAB << TAB << num_to_ws(label)
160
+ in [:calc, :add]
161
+ buf << TAB << SPACE << SPACE << SPACE
162
+ in [:calc, :sub]
163
+ buf << TAB << SPACE << SPACE << TAB
164
+ in [:calc, :multi]
165
+ buf << TAB << SPACE << SPACE << NL
166
+ in [:calc, :div]
167
+ buf << TAB << SPACE << TAB << SPACE
168
+ in [:calc, :mod]
169
+ buf << TAB << SPACE << TAB << TAB
170
+ end
171
+ end
172
+ buf
173
+ end
174
+
175
+ private def with_storing_lvars(lvars, commands, &block)
176
+ lvars.each do |var_addr|
177
+ # 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]
185
+ end
186
+
187
+ block.call
188
+
189
+ lvars.size.times do
190
+ commands << [:heap, :save]
191
+ end
192
+ end
193
+
194
+ private def push_value(ast)
195
+ commands = []
196
+
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]
224
+ end
225
+
226
+ commands
227
+ end
228
+
229
+ private def compile_if(cond, if_body, else_body)
230
+ 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]
242
+ end
243
+
244
+ case cond
245
+ in [:OPCALL, [:LIT, 0], :==, [:ARRAY, x, nil]]
246
+ body.(x, :jump_if_zero)
247
+ in [:OPCALL, x, :==, [:ARRAY, [:LIT, 0], nil]]
248
+ body.(x, :jump_if_zero)
249
+ in [:OPCALL, x, :<, [:ARRAY, [:LIT, 0], nil]]
250
+ body.(x, :jump_if_neg)
251
+ in [:OPCALL, [:LIT, 0], :<, [:ARRAY, x, nil]]
252
+ body.(x, :jump_if_neg)
253
+ end
254
+
255
+ commands
256
+ end
257
+
258
+ private def compile_while(cond, body)
259
+ 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)
263
+
264
+ 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]
273
+ end
274
+
275
+ case cond
276
+ in [:OPCALL, [:LIT, 0], :==, [:ARRAY, x, nil]]
277
+ make_body.(x, :jump_if_zero)
278
+ in [:OPCALL, x, :==, [:ARRAY, [:LIT, 0], nil]]
279
+ make_body.(x, :jump_if_zero)
280
+ in [:OPCALL, x, :<, [:ARRAY, [:LIT, 0], nil]]
281
+ make_body.(x, :jump_if_neg)
282
+ in [:OPCALL, [:LIT, 0], :<, [:ARRAY, x, nil]]
283
+ make_body.(x, :jump_if_neg)
284
+ end
285
+
286
+ commands
287
+ end
288
+
289
+ private def check_char!(char)
290
+ raise ParserError, "String size must be 1, but it's #{char} (#{char.size})" if char.size != 1
291
+ end
292
+
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 }
303
+ end
304
+
305
+ private def num_to_ws(num)
306
+ sign =
307
+ if num < 0
308
+ TAB
309
+ else
310
+ SPACE
311
+ end
312
+ sign + num.abs.to_s(2).gsub("1", TAB).gsub('0', SPACE) + NL
313
+ end
314
+
315
+ private def next_label_index
316
+ @label_index += 1
317
+ end
318
+ end
319
+ end
320
+ end
@@ -1,3 +1,3 @@
1
1
  module Akaza
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -25,15 +25,25 @@ module Akaza
25
25
  in [:stack, :pop]
26
26
  @stack.pop
27
27
  in [:calc, :add]
28
- @stack.push @stack.pop + @stack.pop
28
+ r = @stack.pop
29
+ l = @stack.pop
30
+ @stack.push l + r
29
31
  in [:calc, :sub]
30
- @stack.push @stack.pop - @stack.pop
32
+ r = @stack.pop
33
+ l = @stack.pop
34
+ @stack.push l - r
31
35
  in [:calc, :multi]
32
- @stack.push @stack.pop * @stack.pop
36
+ r = @stack.pop
37
+ l = @stack.pop
38
+ @stack.push l * r
33
39
  in [:calc, :div]
34
- @stack.push @stack.pop / @stack.pop
40
+ r = @stack.pop
41
+ l = @stack.pop
42
+ @stack.push l / r
35
43
  in [:calc, :mod]
36
- @stack.push @stack.pop % @stack.pop
44
+ r = @stack.pop
45
+ l = @stack.pop
46
+ @stack.push l % r
37
47
  in [:heap, :save]
38
48
  val = @stack.pop
39
49
  addr = @stack.pop
@@ -42,13 +52,13 @@ module Akaza
42
52
  addr = @stack.pop
43
53
  @stack.push @heap[addr]
44
54
  in [:flow, :def, label]
45
- # does nothing
55
+ # skip
46
56
  in [:flow, :call, label]
47
- raise "unknwon label:#{label}" unless @labels.key?(label)
57
+ raise "unknwon label:#{label.inspect}" unless @labels.key?(label)
48
58
  @call_stack.push @index
49
59
  @index = @labels[label]
50
60
  in [:flow, :jump, label]
51
- raise "unknwon label:#{label}" unless @labels.key?(label)
61
+ raise "unknwon label:#{label.inspect}" unless @labels.key?(label)
52
62
  @index = @labels[label]
53
63
  in [:flow, :jump_if_zero, label]
54
64
  @index = @labels[label] if @stack.pop == 0
@@ -77,7 +87,7 @@ module Akaza
77
87
  @commands.each.with_index do |command, index|
78
88
  case command
79
89
  in [:flow, :def, label]
80
- @labels[label] = index - 1
90
+ @labels[label] = index
81
91
  else
82
92
  # skip
83
93
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: akaza
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masataka Pocke Kuwabara
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-28 00:00:00.000000000 Z
11
+ date: 2019-04-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.1.a
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 2.1.a
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -61,7 +61,6 @@ extra_rdoc_files: []
61
61
  files:
62
62
  - ".gitignore"
63
63
  - Gemfile
64
- - Gemfile.lock
65
64
  - README.md
66
65
  - Rakefile
67
66
  - akaza.gemspec
@@ -72,6 +71,7 @@ files:
72
71
  - lib/akaza/annotation.rb
73
72
  - lib/akaza/ast_ext.rb
74
73
  - lib/akaza/parser.rb
74
+ - lib/akaza/ruby2ws.rb
75
75
  - lib/akaza/version.rb
76
76
  - lib/akaza/vm.rb
77
77
  homepage: https://github.com/pocke/akaza
@@ -1,22 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- akaza (0.1.0)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- minitest (5.11.3)
10
- rake (12.3.2)
11
-
12
- PLATFORMS
13
- ruby
14
-
15
- DEPENDENCIES
16
- akaza!
17
- bundler (~> 2.1.a)
18
- minitest (>= 5)
19
- rake (~> 12.0)
20
-
21
- BUNDLED WITH
22
- 2.1.0.pre.1