razyk 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/razyk/node.rb CHANGED
@@ -54,15 +54,54 @@ module RazyK
54
54
  self.class.disconnect(self, a)
55
55
  self.class.connect(self, b)
56
56
  end
57
+
58
+ def integer?
59
+ @label.is_a?(Integer) or (/\A\d+\z/ =~ @label.to_s)
60
+ end
61
+
62
+ def integer
63
+ unless integer?
64
+ raise "#{self} is not a integer"
65
+ end
66
+ case @label
67
+ when Integer
68
+ @label
69
+ else
70
+ Integer(@label.to_s)
71
+ end
72
+ end
73
+
74
+ def as_json
75
+ {name: @label.to_s}
76
+ end
77
+
78
+ def self.list(*args, terminator: nil, memory: {})
79
+ cons = lambda{|x, y| Pair.cons(x, y, memory) }
80
+ term = (terminator || cons[:K, 256])
81
+ args.reverse.each do |i|
82
+ term = cons[cons[:S, cons[cons[:S, :I], cons[:K, i]]], cons[:K, term]]
83
+ end
84
+ term
85
+ end
57
86
  end
58
87
 
59
88
  class Combinator < Node
89
+ def self.get(comb, mem)
90
+ n = comb.to_s
91
+ mem[n] ||= self.new(comb)
92
+ end
93
+
60
94
  def initialize(comb)
61
95
  super(comb)
62
96
  end
63
97
 
64
98
  def to_s
65
- @label.to_s
99
+ case l = @label.to_s
100
+ when "S", "K", "I"
101
+ l
102
+ else
103
+ "$" + l
104
+ end
66
105
  end
67
106
  def inspect
68
107
  to_s
@@ -73,6 +112,11 @@ module RazyK
73
112
  # Pair has only two child node (car and cdr).
74
113
  # It represent term of combinators or terms.
75
114
  class Pair < Node
115
+ def self.cons(car, cdr, mem)
116
+ n = "(#{car} #{cdr})"
117
+ mem[n] ||= self.new(car, cdr)
118
+ end
119
+
76
120
  def initialize(car, cdr)
77
121
  car = Combinator.new(car) unless car.is_a?(Node)
78
122
  cdr = Combinator.new(cdr) unless cdr.is_a?(Node)
@@ -113,5 +157,9 @@ module RazyK
113
157
  def inspect
114
158
  to_s
115
159
  end
160
+
161
+ def as_json
162
+ {name: "", children: [@car.as_json, @cdr.as_json]}
163
+ end
116
164
  end
117
165
  end
data/lib/razyk/parser.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  #
2
2
  # DO NOT MODIFY!!!!
3
- # This file is automatically generated by Racc 1.4.6
3
+ # This file is automatically generated by Racc 1.4.13
4
4
  # from Racc grammer file "".
5
5
  #
6
6
 
@@ -15,12 +15,7 @@ module RazyK
15
15
  module_eval(<<'...end parser.y/module_eval...', 'parser.y', 114)
16
16
 
17
17
  def str2list(str)
18
- # (K 256) means End-of-stream. RazyK String adopt it as null terminator
19
- head = Pair.new(:K, 256)
20
- str.unpack("C*").reverse_each do |ch|
21
- head = Pair.new(Pair.new(:CONS, ch), head)
22
- end
23
- head
18
+ Node.list(*str.unpack("C*"), memory: @memory)
24
19
  end
25
20
 
26
21
  def scan
@@ -121,6 +116,7 @@ end
121
116
  def parse(str, opt={})
122
117
  @buf = str
123
118
  @jot = []
119
+ @memory = opt[:memory] || {}
124
120
  yyparse self, :scan
125
121
  end
126
122
 
@@ -269,7 +265,7 @@ Racc_debug_parser = false
269
265
 
270
266
  module_eval(<<'.,.,', 'parser.y', 16)
271
267
  def _reduce_1(val, _values, result)
272
- result = val[0] || Combinator.new(:I)
268
+ result = val[0] || Combinator.get(:I, @memory)
273
269
 
274
270
  result
275
271
  end
@@ -288,7 +284,7 @@ module_eval(<<'.,.,', 'parser.y', 26)
288
284
  if val[0].nil?
289
285
  result = val[1]
290
286
  else
291
- result = Pair.new(val[0], val[1])
287
+ result = Pair.cons(val[0], val[1], @memory)
292
288
  end
293
289
 
294
290
  result
@@ -297,7 +293,7 @@ module_eval(<<'.,.,', 'parser.y', 26)
297
293
 
298
294
  module_eval(<<'.,.,', 'parser.y', 36)
299
295
  def _reduce_4(val, _values, result)
300
- result = Combinator.new(:I)
296
+ result = Combinator.get(:I, @memory)
301
297
 
302
298
  result
303
299
  end
@@ -307,7 +303,7 @@ module_eval(<<'.,.,', 'parser.y', 36)
307
303
 
308
304
  module_eval(<<'.,.,', 'parser.y', 43)
309
305
  def _reduce_6(val, _values, result)
310
- result = Combinator.new(:IOTA)
306
+ result = Combinator.get(:IOTA, @memory)
311
307
 
312
308
  result
313
309
  end
@@ -317,7 +313,7 @@ module_eval(<<'.,.,', 'parser.y', 43)
317
313
 
318
314
  module_eval(<<'.,.,', 'parser.y', 50)
319
315
  def _reduce_8(val, _values, result)
320
- result = Combinator.new(:I)
316
+ result = Combinator.get(:I, @memory)
321
317
 
322
318
  result
323
319
  end
@@ -325,7 +321,7 @@ module_eval(<<'.,.,', 'parser.y', 50)
325
321
 
326
322
  module_eval(<<'.,.,', 'parser.y', 54)
327
323
  def _reduce_9(val, _values, result)
328
- result = Combinator.new(:K)
324
+ result = Combinator.get(:K, @memory)
329
325
 
330
326
  result
331
327
  end
@@ -333,7 +329,7 @@ module_eval(<<'.,.,', 'parser.y', 54)
333
329
 
334
330
  module_eval(<<'.,.,', 'parser.y', 58)
335
331
  def _reduce_10(val, _values, result)
336
- result = Combinator.new(:S)
332
+ result = Combinator.get(:S, @memory)
337
333
 
338
334
  result
339
335
  end
@@ -341,13 +337,13 @@ module_eval(<<'.,.,', 'parser.y', 58)
341
337
 
342
338
  module_eval(<<'.,.,', 'parser.y', 62)
343
339
  def _reduce_11(val, _values, result)
344
- comb = Combinator.new(:I)
340
+ comb = Combinator.get(:I, @memory)
345
341
  @jot.reverse_each do |i|
346
342
  case i
347
343
  when 0
348
- comb = Pair.new(Pair.new(comb, :S), :K)
344
+ comb = Pair.cons(Pair.cons(comb, :S, @memory), :K, @memory)
349
345
  when 1
350
- comb = Pair.new(:S, Pair.new(:K, comb))
346
+ comb = Pair.cons(:S, Pair.cons(:K, comb, @memory), @memory)
351
347
  end
352
348
  end
353
349
  @jot.clear
@@ -359,7 +355,7 @@ module_eval(<<'.,.,', 'parser.y', 62)
359
355
 
360
356
  module_eval(<<'.,.,', 'parser.y', 76)
361
357
  def _reduce_12(val, _values, result)
362
- result = Pair.new(val[1], val[2])
358
+ result = Pair.cons(val[1], val[2], @memory)
363
359
 
364
360
  result
365
361
  end
@@ -367,7 +363,7 @@ module_eval(<<'.,.,', 'parser.y', 76)
367
363
 
368
364
  module_eval(<<'.,.,', 'parser.y', 80)
369
365
  def _reduce_13(val, _values, result)
370
- result = Pair.new(val[1], val[2])
366
+ result = Pair.cons(val[1], val[2], @memory)
371
367
 
372
368
  result
373
369
  end
@@ -383,7 +379,7 @@ module_eval(<<'.,.,', 'parser.y', 84)
383
379
 
384
380
  module_eval(<<'.,.,', 'parser.y', 88)
385
381
  def _reduce_15(val, _values, result)
386
- result = Combinator.new(val[0].to_sym)
382
+ result = Combinator.get(val[0].to_sym, @memory)
387
383
 
388
384
  result
389
385
  end
data/lib/razyk/parser.y CHANGED
@@ -14,7 +14,7 @@ rule
14
14
 
15
15
  program : ccexpr
16
16
  {
17
- result = val[0] || Combinator.new(:I)
17
+ result = val[0] || Combinator.get(:I, @memory)
18
18
  }
19
19
  ;
20
20
 
@@ -27,46 +27,46 @@ ccexpr : /* epsilon */
27
27
  if val[0].nil?
28
28
  result = val[1]
29
29
  else
30
- result = Pair.new(val[0], val[1])
30
+ result = Pair.cons(val[0], val[1], @memory)
31
31
  end
32
32
  }
33
33
  ;
34
34
 
35
35
  expr : SMALL_I
36
36
  {
37
- result = Combinator.new(:I)
37
+ result = Combinator.get(:I, @memory)
38
38
  }
39
39
  | expr2
40
40
  ;
41
41
 
42
42
  iotaexpr: SMALL_I
43
43
  {
44
- result = Combinator.new(:IOTA)
44
+ result = Combinator.get(:IOTA, @memory)
45
45
  }
46
46
  | expr2
47
47
  ;
48
48
 
49
49
  expr2 : I
50
50
  {
51
- result = Combinator.new(:I)
51
+ result = Combinator.get(:I, @memory)
52
52
  }
53
53
  | K
54
54
  {
55
- result = Combinator.new(:K)
55
+ result = Combinator.get(:K, @memory)
56
56
  }
57
57
  | S
58
58
  {
59
- result = Combinator.new(:S)
59
+ result = Combinator.get(:S, @memory)
60
60
  }
61
61
  | no_empty_jot_expr
62
62
  {
63
- comb = Combinator.new(:I)
63
+ comb = Combinator.get(:I, @memory)
64
64
  @jot.reverse_each do |i|
65
65
  case i
66
66
  when 0
67
- comb = Pair.new(Pair.new(comb, :S), :K)
67
+ comb = Pair.cons(Pair.cons(comb, :S, @memory), :K, @memory)
68
68
  when 1
69
- comb = Pair.new(:S, Pair.new(:K, comb))
69
+ comb = Pair.cons(:S, Pair.cons(:K, comb, @memory), @memory)
70
70
  end
71
71
  end
72
72
  @jot.clear
@@ -74,11 +74,11 @@ expr2 : I
74
74
  }
75
75
  | BACKSLASH expr expr
76
76
  {
77
- result = Pair.new(val[1], val[2])
77
+ result = Pair.cons(val[1], val[2], @memory)
78
78
  }
79
79
  | ASTAR iotaexpr iotaexpr
80
80
  {
81
- result = Pair.new(val[1], val[2])
81
+ result = Pair.cons(val[1], val[2], @memory)
82
82
  }
83
83
  | LPAR ccexpr RPAR
84
84
  {
@@ -86,7 +86,7 @@ expr2 : I
86
86
  }
87
87
  | LITERAL
88
88
  {
89
- result = Combinator.new(val[0].to_sym)
89
+ result = Combinator.get(val[0].to_sym, @memory)
90
90
  }
91
91
  | STRING
92
92
  {
@@ -113,12 +113,7 @@ require "razyk/node"
113
113
  ---- inner
114
114
 
115
115
  def str2list(str)
116
- # (K 256) means End-of-stream. RazyK String adopt it as null terminator
117
- head = Pair.new(:K, 256)
118
- str.unpack("C*").reverse_each do |ch|
119
- head = Pair.new(Pair.new(:CONS, ch), head)
120
- end
121
- head
116
+ Node.list(*str.unpack("C*"), memory: @memory)
122
117
  end
123
118
 
124
119
  def scan
@@ -219,6 +214,7 @@ end
219
214
  def parse(str, opt={})
220
215
  @buf = str
221
216
  @jot = []
217
+ @memory = opt[:memory] || {}
222
218
  yyparse self, :scan
223
219
  end
224
220
 
@@ -0,0 +1,3 @@
1
+ module RazyK
2
+ VERSION = "0.1.0"
3
+ end
data/lib/razyk/vm.rb CHANGED
@@ -12,12 +12,13 @@ module RazyK
12
12
  class VM
13
13
  class StackUnderflow < StandardError; end
14
14
 
15
- def initialize(tree, input=$stdin, output=$stdout, recursive=false)
15
+ def initialize(tree, input=$stdin, output=$stdout, recursive: false, statistics: nil)
16
16
  @root = Node.new(:root, [], [tree])
17
17
  @generator = nil
18
18
  @input = input
19
19
  @output = output
20
20
  @recursive = recursive
21
+ @statistics = statistics
21
22
  end
22
23
 
23
24
  def tree
@@ -54,21 +55,22 @@ module RazyK
54
55
  stack.push(new_root)
55
56
  end
56
57
 
57
- def evaluate(root, gen=nil)
58
+ def evaluate(root, &blk)
58
59
  stack = [root]
59
- until step(stack, gen).nil?
60
- if gen
61
- gen.yield(self)
60
+ until step(stack, &blk).nil?
61
+ if blk
62
+ blk.call(self)
62
63
  end
63
64
  end
64
65
  if @recursive and stack.last.is_a?(Pair)
65
- evaluate(stack.last.cdr, gen)
66
+ evaluate(stack.last.cdr, &blk)
66
67
  end
67
68
  nil
68
69
  end
69
70
 
70
- def step(stack, gen=nil)
71
+ def step(stack, &blk)
71
72
  return nil if stack.empty?
73
+ @statistics[:count] += 1 if @statistics
72
74
  while stack.last.is_a?(Pair)
73
75
  stack.push(stack.last.car)
74
76
  end
@@ -92,40 +94,19 @@ module RazyK
92
94
  root, x = pop_pairs(stack, 1)
93
95
  new_root = Pair.new(Pair.new(x, :S), :K)
94
96
  replace_root(stack, root, new_root)
95
- when Integer
96
- # (<N> f x) -> x (N == 0)
97
- # -> (f (<N-1> f x)) (N > 0)
98
- root, f, x = pop_pairs(stack, 2)
99
- num = comb.label
100
- if num == 0
101
- replace_root(stack, root, x)
102
- else
103
- # shortcut
104
- if f.label == :INC and x.label.is_a?(Integer)
105
- replace_root(stack, root, Combinator.new(num + x.label))
106
- else
107
- dec_pair = Pair.new(Combinator.new(num-1), f)
108
- new_root = Pair.new(f, Pair.new(dec_pair, x))
109
- replace_root(stack, root, new_root)
110
- end
111
- end
112
97
  when :CONS
113
98
  # (CONS a d f) -> (f a d)
114
99
  root, a, d, f = pop_pairs(stack, 3)
115
100
  new_root = Pair.new(Pair.new(f, a), d)
116
101
  replace_root(stack, root, new_root)
117
102
  when :IN
118
- # (IN f) -> (CONS <CH> IN f) where <CH> is a byte from stdin
119
- ch = @input.read(1)
103
+ # (IN f) -> (S (S I (K <CH>)) (K IN)) where <CH> is a byte from stdin
104
+ ch = @input.getbyte
120
105
  if ch.nil?
121
106
  ch = 256
122
- else
123
- ch = ch.unpack("C")[0]
124
107
  end
125
- new_root = Pair.new(Pair.new(:CONS, Combinator.new(ch)),
126
- :DUMMY) # reuse :IN combinator
108
+ new_root = Node.list(Combinator.new(ch), terminator: Combinator.new(:IN))
127
109
  comb.replace(new_root)
128
- new_root.cdr = comb
129
110
  stack.push(new_root)
130
111
  when :CAR
131
112
  # (CAR x) -> (x K) (CAR = (Lx.x TRUE), TRUE = (Lxy.x) = K)
@@ -142,16 +123,16 @@ module RazyK
142
123
  root, f = pop_pairs(stack, 1)
143
124
  new_root = Pair.new(
144
125
  Pair.new(:PUTC,
145
- Pair.new(Pair.new(Pair.new(:CAR, f), :INC), 0)),
126
+ Pair.new(Pair.new(Pair.new(f, :K), :INC), 0)),
146
127
  Pair.new(comb, # reuse :OUT combinator
147
- Pair.new(:CDR, f)))
128
+ Pair.new(f, Pair.new(:S, :K))))
148
129
  replace_root(stack, root, new_root)
149
130
  when :INC
150
131
  # (INC n) -> n+1 : increment church number
151
132
  raise StackUnderflow if stack.empty?
152
- evaluate(stack.last.cdr, gen)
133
+ evaluate(stack.last.cdr, &blk)
153
134
  root, n = pop_pairs(stack, 1)
154
- unless n.label.is_a?(Integer)
135
+ unless n.integer?
155
136
  begin
156
137
  msg = "argument of INC combinator is not a church number but #{n.inspect}"
157
138
  rescue
@@ -159,13 +140,13 @@ module RazyK
159
140
  end
160
141
  raise msg
161
142
  end
162
- replace_root(stack, root, Combinator.new(n.label + 1))
143
+ replace_root(stack, root, Combinator.new(n.integer + 1))
163
144
  when :PUTC
164
145
  # (PUTC x y) -> y : evaluate x and putchar it
165
146
  raise StackUnderflow if stack.size < 2
166
147
  x = stack.pop
167
- evaluate(x.cdr, gen)
168
- unless x.cdr.label.is_a?(Integer)
148
+ evaluate(x.cdr, &blk)
149
+ unless x.cdr.integer?
169
150
  begin
170
151
  msg = "output is not church number but #{x.cdr.inspect}"
171
152
  rescue
@@ -173,16 +154,38 @@ module RazyK
173
154
  end
174
155
  raise msg
175
156
  end
176
- num = x.cdr.label
157
+ num = x.cdr.integer
177
158
  if num >= 256
159
+ if @output != $stdout and @output != STDOUT
160
+ @output.close_write
161
+ end
178
162
  return nil
179
163
  end
180
164
  @output.write([num].pack("C"))
181
165
  root = stack.pop
182
166
  y = root.cut_cdr
183
167
  replace_root(stack, root, y)
184
- else # unknown combinator... treat as combinator without enough arguments
185
- raise StackUnderflow
168
+ else
169
+ if comb.integer?
170
+ # (<N> f x) -> x (N == 0)
171
+ # -> (f (<N-1> f x)) (N > 0)
172
+ root, f, x = pop_pairs(stack, 2)
173
+ num = comb.integer
174
+ if num == 0
175
+ replace_root(stack, root, x)
176
+ else
177
+ # shortcut
178
+ if f.label == :INC and x.integer?
179
+ replace_root(stack, root, Combinator.new(num + x.integer))
180
+ else
181
+ dec_pair = Pair.new(Combinator.new(num-1), f)
182
+ new_root = Pair.new(f, Pair.new(dec_pair, x))
183
+ replace_root(stack, root, new_root)
184
+ end
185
+ end
186
+ else # unknown combinator... treat as combinator without enough arguments
187
+ raise StackUnderflow
188
+ end
186
189
  end
187
190
  true
188
191
  rescue StackUnderflow
@@ -190,22 +193,17 @@ module RazyK
190
193
  end
191
194
 
192
195
  def reduce
193
- @generator ||= Enumerator.new{|e| evaluate(self.tree, e) }
194
- begin
195
- @generator.next
196
- self
197
- rescue StopIteration
198
- nil
199
- end
196
+ evaluate(self.tree) {|vm| return vm }
197
+ nil
200
198
  end
201
199
 
202
200
  def run(&blk)
203
- if blk
204
- while reduce
205
- blk.call(self)
206
- end
207
- else
208
- evaluate(self.tree, nil)
201
+ if @statistics
202
+ @statistics[:started_at] = Time.now
203
+ end
204
+ evaluate(self.tree, &blk)
205
+ if @statistics
206
+ @statistics[:finished_at] = Time.now
209
207
  end
210
208
  end
211
209
  end