concatenative 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +16 -0
- data/README.rdoc +5 -5
- data/examples/benchmarks.rb +5 -5
- data/examples/concatenative_cli.rb +2 -2
- data/lib/concatenative.rb +84 -20
- data/lib/concatenative/kernel.rb +419 -0
- data/lib/concatenative/system_extensions.rb +56 -18
- data/spec/concatenative_spec.rb +22 -4
- data/spec/kernel_spec.rb +149 -0
- data/spec/system_extensions_spec.rb +24 -14
- metadata +6 -7
- data/lib/concatenative/definitions.rb +0 -12
- data/lib/concatenative/system.rb +0 -334
- data/spec/definitions_spec.rb +0 -44
- data/spec/system_spec.rb +0 -107
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
== Version 0.2.0
|
2
|
+
|
3
|
+
* Implemented new combinators:
|
4
|
+
* :binrec
|
5
|
+
* :split
|
6
|
+
* :twodip
|
7
|
+
* :threedip
|
8
|
+
* Moved combinators to kernel.rb.
|
9
|
+
* Removed definitions.rb (all words are now defined in kernel.rb).
|
10
|
+
* Performance improvement: stack is never copied.
|
11
|
+
* Performance improvement: no symbol/string conversion when processing words.
|
12
|
+
* Added ~ operator (call, unquote) for Symbols and Arrays. Removed Array#unquote and Symbol#unquote
|
13
|
+
* Added Symbol#<= to define words and Symbol#/ to concatenate them.
|
14
|
+
* Added Symbol#/ to concatenate symbols and simulate namespaces.
|
15
|
+
* Removed Symbol#define (use Symbol#<= instead)
|
16
|
+
|
1
17
|
== Version 0.1.0
|
2
18
|
|
3
19
|
First preview release of Concatenative, implementing all the most basic operators and
|
data/README.rdoc
CHANGED
@@ -22,16 +22,16 @@ Execute a Concatenative program:
|
|
22
22
|
10,
|
23
23
|
[0, :==],
|
24
24
|
[1, :+],
|
25
|
-
[:
|
25
|
+
[:dup, 1, :-],
|
26
26
|
[:*],
|
27
|
-
:
|
27
|
+
:linrec
|
28
28
|
)
|
29
29
|
|
30
|
-
The program above returns the factorial of 10, computed using the linrec combinator. It is also possible to execute arrays directly and define concatenative programs as symbols
|
30
|
+
The program above returns the factorial of 10, computed using the linrec combinator. It is also possible to execute arrays directly and define concatenative programs as symbols, as folows:
|
31
31
|
|
32
32
|
|
33
|
-
:
|
34
|
-
[5, :
|
33
|
+
:factorial <= [0, :==], [:pop, 1], [:dup, 1, :- , :factorial, :*], :if
|
34
|
+
[5, :factorial].execute
|
35
35
|
|
36
36
|
The program above calculates the factorial of 5, using explicit recursion.
|
37
37
|
|
data/examples/benchmarks.rb
CHANGED
@@ -4,7 +4,7 @@ require 'benchmark'
|
|
4
4
|
dir = File.dirname(File.expand_path(__FILE__))+'/../lib/'
|
5
5
|
require dir+"concatenative"
|
6
6
|
|
7
|
-
n =
|
7
|
+
n = 3_000
|
8
8
|
|
9
9
|
def factorial(n)
|
10
10
|
(n == 0) ? 1 : factorial(n-1)
|
@@ -22,9 +22,9 @@ puts "=====> Factorial of #{n}"
|
|
22
22
|
puts "======================================================================"
|
23
23
|
Benchmark.bmbm(20) do |x|
|
24
24
|
x.report("Standard Ruby Code:") { factorial n }
|
25
|
-
x.report("Concatenative (times):") { concatenate(n, 1, 1, :
|
26
|
-
x.report("Concatenative (linrec):") { concatenate(n, [0, :==], [1, :+], [:
|
27
|
-
x.report("Concatenative (primrec):") { concatenate(n, [1], [:*], :
|
25
|
+
x.report("Concatenative (times):") { concatenate(n, 1, 1, :rolldown, [:dup, [:*], :dip, :succ], :times, :pop) }
|
26
|
+
x.report("Concatenative (linrec):") { concatenate(n, [0, :==], [1, :+], [:dup, 1, :-], [:*], :linrec) }
|
27
|
+
x.report("Concatenative (primrec):") { concatenate(n, [1], [:*], :primrec) }
|
28
28
|
end
|
29
29
|
puts
|
30
30
|
puts
|
@@ -33,6 +33,6 @@ puts "=====> Fibonacci number for #{n}"
|
|
33
33
|
puts "======================================================================"
|
34
34
|
Benchmark.bmbm(20) do |x|
|
35
35
|
x.report("Standard Ruby Code:") { fibonacci n }
|
36
|
-
x.report("Concatenative (times):") { concatenate(n, 0, 1, :
|
36
|
+
x.report("Concatenative (times):") { concatenate(n, 0, 1, :rolldown, [:dup, [:+], :dip, :swap], :times, :pop) }
|
37
37
|
end
|
38
38
|
|
@@ -11,7 +11,7 @@ puts " ========================================="
|
|
11
11
|
loop do
|
12
12
|
print " => "
|
13
13
|
begin
|
14
|
-
Concatenative
|
14
|
+
Concatenative.process(instance_eval(gets))
|
15
15
|
rescue Exception => e
|
16
16
|
if e.is_a? SystemExit then
|
17
17
|
puts " Exiting."
|
@@ -21,5 +21,5 @@ loop do
|
|
21
21
|
puts e.message
|
22
22
|
end
|
23
23
|
print " STACK: "
|
24
|
-
pp Concatenative::
|
24
|
+
pp Concatenative::STACK
|
25
25
|
end
|
data/lib/concatenative.rb
CHANGED
@@ -1,27 +1,24 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
1
3
|
require 'pp'
|
2
4
|
|
3
5
|
libdir = File.dirname(File.expand_path(__FILE__))+'/concatenative/'
|
4
6
|
|
5
7
|
class EmptyStackError < RuntimeError; end
|
6
8
|
|
7
|
-
require libdir+'
|
9
|
+
require libdir+'kernel'
|
8
10
|
require libdir+'system_extensions'
|
9
|
-
require libdir+'definitions'
|
10
11
|
|
11
|
-
# The Concatenative module (included automatically when required) defines
|
12
|
-
# some constants, the <tt>concatenate</tt> method and the RubyMessage class.
|
13
12
|
module Concatenative
|
14
13
|
|
14
|
+
extend Concatenative::Kernel
|
15
|
+
|
16
|
+
STACK = []
|
15
17
|
ARITIES = {}
|
16
18
|
DEBUG = false
|
17
19
|
|
18
|
-
#
|
19
|
-
|
20
|
-
ARITIES[meth] = arity
|
21
|
-
end
|
22
|
-
|
23
|
-
# RubyMessage objects wrap a symbol and its arity
|
24
|
-
# (returned by Symbol#|).
|
20
|
+
# RubyMessage objects wrap a symbol and its arity
|
21
|
+
# (returned by Symbol#|).
|
25
22
|
class RubyMessage
|
26
23
|
attr_reader :name, :arity
|
27
24
|
def initialize(name, arity)
|
@@ -30,19 +27,86 @@ module Concatenative
|
|
30
27
|
end
|
31
28
|
end
|
32
29
|
|
33
|
-
|
34
|
-
|
35
|
-
System.execute program
|
30
|
+
class << self
|
31
|
+
attr_accessor :frozen, :popped, :pushed
|
36
32
|
end
|
37
33
|
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
@frozen = nil
|
35
|
+
@popped = nil
|
36
|
+
@pushed = nil
|
37
|
+
|
38
|
+
# Pushes an item on the stack.
|
39
|
+
def self.push(element)
|
40
|
+
STACK.push element
|
41
|
+
@pushed += 1 if @frozen
|
42
|
+
element
|
41
43
|
end
|
42
|
-
|
43
|
-
end
|
44
44
|
|
45
|
-
|
45
|
+
# Saves the stack state
|
46
|
+
def self.save_stack
|
47
|
+
@frozen = STACK.length
|
48
|
+
@popped = 0
|
49
|
+
@pushed = 0
|
50
|
+
end
|
51
|
+
|
52
|
+
# Restored the previously saved stack state
|
53
|
+
def self.restore_stack
|
54
|
+
diff = STACK.length - @frozen
|
55
|
+
@frozen = nil
|
56
|
+
diff.times { _pop }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Executes an array as a concatenative program (clears the stack first).
|
60
|
+
def self.execute(array)
|
61
|
+
STACK.clear
|
62
|
+
array.each { |e| process e }
|
63
|
+
(STACK.length == 1) ? STACK[0] : STACK
|
64
|
+
end
|
65
|
+
|
66
|
+
# Processes an item (without clearning the stack).
|
67
|
+
def self.process(item)
|
68
|
+
case
|
69
|
+
when item.is_a?(Symbol) then
|
70
|
+
if item.defined?
|
71
|
+
~item.definition
|
72
|
+
else
|
73
|
+
case item.namespace
|
74
|
+
when :kernel then
|
75
|
+
Concatenative.send(item.name) rescue raise(RuntimeError, "Kernel word '#{item.name}' is not defined.")
|
76
|
+
when :ruby then
|
77
|
+
push ruby_method(item.name)
|
78
|
+
else
|
79
|
+
return Concatenative.send(item.name) if Concatenative::Kernel.method_defined? item.name
|
80
|
+
push ruby_method(item.name)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
when item.is_a?(RubyMessage) then
|
84
|
+
push ruby_method(item.name, item.arity)
|
85
|
+
else
|
86
|
+
push item
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Calls a Ruby method, consuming elements from the stack according to its
|
91
|
+
# explicit or implicit arity.
|
92
|
+
def self.ruby_method(message, arity=nil)
|
93
|
+
raise EmptyStackError, "Empty stack" if STACK.empty?
|
94
|
+
n = arity || ARITIES[message] || 0
|
95
|
+
method = message
|
96
|
+
elements = []
|
97
|
+
(n+1).times { elements << pop }
|
98
|
+
receiver = elements.pop
|
99
|
+
args = []
|
100
|
+
(elements.length).times { args << elements.pop }
|
101
|
+
begin
|
102
|
+
(args.length == 0) ? receiver.send(method) : receiver.send(method, *args)
|
103
|
+
rescue Exception => e
|
104
|
+
raise RuntimeError,
|
105
|
+
"Error when calling: #{receiver}##{method}(#{args.join(', ')}) [#{receiver.class}##{method}]"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
46
110
|
|
47
111
|
# Setting some default arities
|
48
112
|
set_arity :+, 1
|
@@ -0,0 +1,419 @@
|
|
1
|
+
#!usr/bin/env ruby
|
2
|
+
|
3
|
+
module Concatenative
|
4
|
+
|
5
|
+
module Kernel
|
6
|
+
|
7
|
+
# Clears the stack.
|
8
|
+
def clear
|
9
|
+
STACK.clear
|
10
|
+
end
|
11
|
+
|
12
|
+
# Pops an item out of the stack.
|
13
|
+
#
|
14
|
+
# <tt>A =></tt>
|
15
|
+
def pop
|
16
|
+
raise EmptyStackError, "Empty stack" if STACK.empty?
|
17
|
+
return STACK.pop unless @frozen
|
18
|
+
if @pushed > 0 then
|
19
|
+
@pushed -= 1
|
20
|
+
STACK.pop
|
21
|
+
else
|
22
|
+
raise EmptyStackError, "Empty stack" if @frozen <= 0
|
23
|
+
@popped += 1
|
24
|
+
STACK[@frozen-@popped]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Prints the top stack item.
|
29
|
+
def put
|
30
|
+
puts STACK.last
|
31
|
+
end
|
32
|
+
|
33
|
+
# Pushes a user-entered string on the stack.
|
34
|
+
def get
|
35
|
+
push gets
|
36
|
+
end
|
37
|
+
|
38
|
+
# Duplicates the top stack item.
|
39
|
+
#
|
40
|
+
# <tt>A => A A</tt>
|
41
|
+
def dup
|
42
|
+
raise EmptyStackError, "Empty stack" if STACK.empty?
|
43
|
+
push STACK.last
|
44
|
+
end
|
45
|
+
|
46
|
+
# Swaps the first two elements on the stack.
|
47
|
+
#
|
48
|
+
# <tt>A B => B A</tt>
|
49
|
+
def swap
|
50
|
+
a = pop
|
51
|
+
b = pop
|
52
|
+
push a
|
53
|
+
push b
|
54
|
+
end
|
55
|
+
|
56
|
+
# Prepends an element to an Array.
|
57
|
+
#
|
58
|
+
# <tt>[A] B => [A B]</tt>
|
59
|
+
def cons
|
60
|
+
array = pop
|
61
|
+
element = pop
|
62
|
+
raise ArgumentError, "CONS: first element is not an Array." unless array.is_a? Array
|
63
|
+
push array.insert(0, element)
|
64
|
+
end
|
65
|
+
|
66
|
+
def swons
|
67
|
+
swap
|
68
|
+
cons
|
69
|
+
end
|
70
|
+
|
71
|
+
# Removes the first element of an array and puts it on the stack, along with the
|
72
|
+
# new array
|
73
|
+
#
|
74
|
+
# <tt>[A] => B [C]</tt>
|
75
|
+
def uncons
|
76
|
+
array = pop
|
77
|
+
raise ArgumentError, "UNCONS: first element is not an Array." unless array.is_a? Array
|
78
|
+
push array.first
|
79
|
+
push array.drop 1
|
80
|
+
end
|
81
|
+
|
82
|
+
def unswons
|
83
|
+
uncons
|
84
|
+
swap
|
85
|
+
end
|
86
|
+
|
87
|
+
# Concatenates two arrays.
|
88
|
+
#
|
89
|
+
# <tt>[A] [B] => [A B]</tt>
|
90
|
+
def cat
|
91
|
+
array1 = pop
|
92
|
+
array2 = pop
|
93
|
+
raise ArgumentError, "CAT: first element is not an Array." unless array1.is_a? Array
|
94
|
+
raise ArgumentError, "CAT: first element is not an Array." unless array2.is_a? Array
|
95
|
+
push array2.concat(array1)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Returns the first element of an array.
|
99
|
+
#
|
100
|
+
# <tt>[A B] => A</tt>
|
101
|
+
def first
|
102
|
+
array = pop
|
103
|
+
raise ArgumentError, "FIRST: first element is not an Array." unless array.is_a? Array
|
104
|
+
raise ArgumentError, "FIRST: empty array." if array.length == 0
|
105
|
+
push array.first
|
106
|
+
end
|
107
|
+
|
108
|
+
# Returns everything but the first element of an array.
|
109
|
+
#
|
110
|
+
# <tt>[A B C] => [B C]</tt>
|
111
|
+
def rest
|
112
|
+
array = pop
|
113
|
+
raise ArgumentError, "REST: first element is not an Array." unless array.is_a? Array
|
114
|
+
raise ArgumentError, "REST: empty array." if array.length == 0
|
115
|
+
array.delete_at 0
|
116
|
+
push array
|
117
|
+
end
|
118
|
+
|
119
|
+
alias drop pop
|
120
|
+
alias zap pop
|
121
|
+
alias concat cat
|
122
|
+
|
123
|
+
# Saves A, executes P, restores A.
|
124
|
+
#
|
125
|
+
# <tt>A [P] => A</tt>
|
126
|
+
def dip
|
127
|
+
program = pop
|
128
|
+
raise ArgumentError, "DIP: first element is not an Array." unless program.is_a? Array
|
129
|
+
item = pop
|
130
|
+
~program
|
131
|
+
push item
|
132
|
+
end
|
133
|
+
|
134
|
+
# Saves A and B, executes P, restores A and B.
|
135
|
+
#
|
136
|
+
# <tt>A B [P] => A B</tt>
|
137
|
+
def twodip
|
138
|
+
program = pop
|
139
|
+
raise ArgumentError, "2DIP: first element is not an Array." unless program.is_a? Array
|
140
|
+
items = []
|
141
|
+
2.times { items << pop }
|
142
|
+
~program
|
143
|
+
items.reverse.each {|i| push i }
|
144
|
+
end
|
145
|
+
|
146
|
+
# Saves A, B and C, executes P, restores A, B and C.
|
147
|
+
#
|
148
|
+
# <tt>A B C [P] => A B C</tt>
|
149
|
+
def threedip
|
150
|
+
program = pop
|
151
|
+
raise ArgumentError, "2DIP: first element is not an Array." unless program.is_a? Array
|
152
|
+
items = []
|
153
|
+
3.times { items << pop }
|
154
|
+
~program
|
155
|
+
items.reverse.each {|i| push i }
|
156
|
+
end
|
157
|
+
|
158
|
+
# Removes the second item on the stack.
|
159
|
+
#
|
160
|
+
# <tt>A B => B</tt>
|
161
|
+
def popd
|
162
|
+
push [:pop]
|
163
|
+
dip
|
164
|
+
end
|
165
|
+
|
166
|
+
# Duplicates the second item on the stack
|
167
|
+
#
|
168
|
+
# <tt>A B => A A B</tt>
|
169
|
+
def dupd
|
170
|
+
push [:dup]
|
171
|
+
dip
|
172
|
+
end
|
173
|
+
|
174
|
+
# Swaps the second and third items on the stack
|
175
|
+
#
|
176
|
+
# <tt>A B C => B A C</tt>
|
177
|
+
def swapd
|
178
|
+
push [:swap]
|
179
|
+
dip
|
180
|
+
end
|
181
|
+
|
182
|
+
#
|
183
|
+
# <tt>A B C => B C A</tt>
|
184
|
+
def rollup
|
185
|
+
swap
|
186
|
+
push [:swap]
|
187
|
+
dip
|
188
|
+
end
|
189
|
+
|
190
|
+
#
|
191
|
+
# <tt>A B C => C A B</tt>
|
192
|
+
def rolldown
|
193
|
+
push [:swap]
|
194
|
+
dip
|
195
|
+
swap
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# <tt>A B C => C B A</tt>
|
200
|
+
def rotate
|
201
|
+
swap
|
202
|
+
push [:swap]
|
203
|
+
dip
|
204
|
+
swap
|
205
|
+
end
|
206
|
+
|
207
|
+
# Executes a quoted program.
|
208
|
+
#
|
209
|
+
# <tt>[P] => </tt>
|
210
|
+
def i
|
211
|
+
program = pop
|
212
|
+
raise ArgumentError, "I: first element is not an Array." unless program.is_a? Array
|
213
|
+
~program
|
214
|
+
end
|
215
|
+
|
216
|
+
# Executes THEN if IF is true, otherwise executes ELSE.
|
217
|
+
#
|
218
|
+
# <tt>[IF] [THEN] [ELSE] =></tt>
|
219
|
+
def ifte
|
220
|
+
_else = pop
|
221
|
+
_then = pop
|
222
|
+
_if = pop
|
223
|
+
raise ArgumentError, "IFTE: first element is not an Array." unless _if.is_a? Array
|
224
|
+
raise ArgumentError, "IFTE: second element is not an Array." unless _then.is_a? Array
|
225
|
+
raise ArgumentError, "IFTE: third element is not an Array." unless _else.is_a? Array
|
226
|
+
save_stack
|
227
|
+
~_if
|
228
|
+
condition = pop
|
229
|
+
restore_stack
|
230
|
+
if condition then
|
231
|
+
~_then
|
232
|
+
else
|
233
|
+
~_else
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
alias if ifte
|
238
|
+
|
239
|
+
# Quotes the top stack element.
|
240
|
+
#
|
241
|
+
# <tt>A => [A]</tt>
|
242
|
+
def unit
|
243
|
+
push [pop]
|
244
|
+
end
|
245
|
+
|
246
|
+
# Executes P for each element of A, pushes an array containing the results on the stack.
|
247
|
+
#
|
248
|
+
# <tt>[A] [P] => [B]</tt>
|
249
|
+
def map
|
250
|
+
program = pop
|
251
|
+
list = pop
|
252
|
+
raise ArgumentError, "MAP: first element is not an Array." unless program.is_a? Array
|
253
|
+
raise ArgumentError, "MAP: second element is not an array." unless list.is_a? Array
|
254
|
+
push []
|
255
|
+
list.map do |e|
|
256
|
+
push e
|
257
|
+
~program
|
258
|
+
unit
|
259
|
+
cat
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Executes P for each element of A, pushes the results on the stack.
|
264
|
+
#
|
265
|
+
# <tt>[A] [P] => B</tt>
|
266
|
+
def step
|
267
|
+
program = pop
|
268
|
+
list = pop
|
269
|
+
raise ArgumentError, "STEP: first element is not an Array." unless program.is_a? Array
|
270
|
+
raise ArgumentError, "STEP: second element is not an array." unless list.is_a? Array
|
271
|
+
list.map do |e|
|
272
|
+
push e
|
273
|
+
~program
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# If IF is true, executes THEN. Otherwise, executes REC1, recurses and then executes REC2.
|
278
|
+
#
|
279
|
+
# <tt>[IF] [THEN] [REC1] [REC2] =></tt>
|
280
|
+
def linrec
|
281
|
+
rec2 = pop
|
282
|
+
rec1 = pop
|
283
|
+
_then = pop
|
284
|
+
_if = pop
|
285
|
+
raise ArgumentError, "LINREC: first element is not an Array." unless _if.is_a? Array
|
286
|
+
raise ArgumentError, "LINREC: second element is not an Array." unless _then.is_a? Array
|
287
|
+
raise ArgumentError, "LINREC: third element is not an Array." unless rec1.is_a? Array
|
288
|
+
raise ArgumentError, "LINREC: fourth element is not an Array." unless rec2.is_a? Array
|
289
|
+
save_stack
|
290
|
+
~_if
|
291
|
+
condition = pop
|
292
|
+
restore_stack
|
293
|
+
if condition then
|
294
|
+
~_then
|
295
|
+
else
|
296
|
+
~rec1
|
297
|
+
[_if, _then, rec1, rec2].each {|e| push e }
|
298
|
+
linrec
|
299
|
+
~rec2
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# Same as linrec, but it is only necessary to specify THEN and REC2.
|
304
|
+
# * REC1 = a program to reduce A to its zero value (0, [], "").
|
305
|
+
# * IF = a condition to verify if A is its zero value (0, [], "") or not.
|
306
|
+
#
|
307
|
+
# <tt>A [THEN] [REC2] =><tt>
|
308
|
+
def primrec
|
309
|
+
rec2 = pop
|
310
|
+
_then = [:pop, pop, :i]
|
311
|
+
arg = pop
|
312
|
+
# Guessing IF
|
313
|
+
case
|
314
|
+
when arg.respond_to?(:blank?) then
|
315
|
+
_if = [:blank?]
|
316
|
+
when arg.respond_to?(:empty?) then
|
317
|
+
_if = [:empty]
|
318
|
+
when arg.is_a?(Numeric) then
|
319
|
+
_if = [0, :==]
|
320
|
+
when arg.is_a?(String) then
|
321
|
+
_if = ["", :==]
|
322
|
+
else
|
323
|
+
raise ArgumentError, "PRIMREC: Unable to create IF element for #{arg} (#{arg.class})"
|
324
|
+
end
|
325
|
+
# Guessing REC1
|
326
|
+
case
|
327
|
+
when arg.respond_to?(:length) && arg.respond_to?(:slice) then
|
328
|
+
rec1 = [0, (arg.length-2), :slice|2]
|
329
|
+
when arg.respond_to?(:-) then
|
330
|
+
rec1 = [:dup, 1, :-]
|
331
|
+
else
|
332
|
+
raise ArgumentError, "PRIMREC: Unable to create REC1 element for #{arg} (#{arg.class})"
|
333
|
+
end
|
334
|
+
[arg, _if, _then, rec1, rec2].each {|e| push e }
|
335
|
+
linrec
|
336
|
+
end
|
337
|
+
|
338
|
+
# Executes P N times.
|
339
|
+
#
|
340
|
+
# <tt>N [P] => </tt>
|
341
|
+
def times
|
342
|
+
program = pop
|
343
|
+
n = pop
|
344
|
+
raise ArgumentError, "TIMEs: second element is not an Array." unless program.is_a? Array
|
345
|
+
n.times { ~program.dup }
|
346
|
+
end
|
347
|
+
|
348
|
+
# While COND is true, executes P
|
349
|
+
#
|
350
|
+
# <tt>[P] [COND] => </tt>
|
351
|
+
def while
|
352
|
+
program = pop
|
353
|
+
cond = pop
|
354
|
+
raise ArgumentError, "WHILE: first element is not an Array." unless cond.is_a? Array
|
355
|
+
raise ArgumentError, "WHILE: second element is not an Array." unless program.is_a? Array
|
356
|
+
save_stack
|
357
|
+
~cond
|
358
|
+
res = pop
|
359
|
+
restore_stack
|
360
|
+
if res then
|
361
|
+
~program
|
362
|
+
[cond, program].each {|e| push e }
|
363
|
+
self.while
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
# Splits an Array into two parts, depending on which element satisfy a condition
|
368
|
+
#
|
369
|
+
# <tt>[A] [P] => [B] [C]</tt>
|
370
|
+
def split
|
371
|
+
cond = pop
|
372
|
+
array = pop
|
373
|
+
raise ArgumentError, "SPLIT: first element is not an Array." unless cond.is_a? Array
|
374
|
+
raise ArgumentError, "SPLIT: second element is not an Array." unless array.is_a? Array
|
375
|
+
yes, no = [], []
|
376
|
+
array.each do |e|
|
377
|
+
save_stack
|
378
|
+
push e
|
379
|
+
~cond.dup
|
380
|
+
pop ? yes << e : no << e
|
381
|
+
restore_stack
|
382
|
+
end
|
383
|
+
push yes
|
384
|
+
push no
|
385
|
+
end
|
386
|
+
|
387
|
+
# If IF is true, executes THEN. Otherwise, executes REC1 (which must return two elements),
|
388
|
+
# recurses twice and then executes REC2.
|
389
|
+
#
|
390
|
+
# <tt>[IF] [THEN] [REC1] [REC2] =></tt>
|
391
|
+
def binrec
|
392
|
+
rec2 = pop
|
393
|
+
rec1 = pop
|
394
|
+
_then = pop
|
395
|
+
_if = pop
|
396
|
+
raise ArgumentError, "BINREC: first element is not an Array." unless _if.is_a? Array
|
397
|
+
raise ArgumentError, "BINREC: second element is not an Array." unless _then.is_a? Array
|
398
|
+
raise ArgumentError, "BINREC: third element is not an Array." unless rec1.is_a? Array
|
399
|
+
raise ArgumentError, "BINREC: fourth element is not an Array." unless rec2.is_a? Array
|
400
|
+
save_stack
|
401
|
+
~_if
|
402
|
+
condition = pop
|
403
|
+
restore_stack
|
404
|
+
if condition then
|
405
|
+
~_then
|
406
|
+
else
|
407
|
+
~rec1
|
408
|
+
a = pop
|
409
|
+
b = pop
|
410
|
+
[b, _if, _then, rec1, rec2].each {|e| push e }
|
411
|
+
binrec
|
412
|
+
[a, _if, _then, rec1, rec2].each {|e| push e }
|
413
|
+
binrec
|
414
|
+
~rec2
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
end
|
419
|
+
end
|