razyk 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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