rb-scheme 0.3.5
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.
- checksums.yaml +7 -0
- data/.gitignore +37 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +65 -0
- data/Rakefile +11 -0
- data/circle.yml +6 -0
- data/examples/nqueen.scm +120 -0
- data/examples/y_combinator.scm +12 -0
- data/exe/rb-scheme +8 -0
- data/lib/rb-scheme.rb +18 -0
- data/lib/rb-scheme/compiler.rb +280 -0
- data/lib/rb-scheme/evaluator.rb +26 -0
- data/lib/rb-scheme/executer.rb +55 -0
- data/lib/rb-scheme/extension.rb +17 -0
- data/lib/rb-scheme/extension/procedures.scm +7 -0
- data/lib/rb-scheme/global.rb +25 -0
- data/lib/rb-scheme/helpers.rb +42 -0
- data/lib/rb-scheme/lisp-objects.rb +105 -0
- data/lib/rb-scheme/parser.rb +146 -0
- data/lib/rb-scheme/primitive.rb +111 -0
- data/lib/rb-scheme/primitive/procedure.rb +42 -0
- data/lib/rb-scheme/printer.rb +108 -0
- data/lib/rb-scheme/symbol.rb +14 -0
- data/lib/rb-scheme/version.rb +3 -0
- data/lib/rb-scheme/vm.rb +281 -0
- data/lib/rb-scheme/vm/box.rb +17 -0
- data/lib/rb-scheme/vm/stack.rb +45 -0
- data/rb-scheme.gemspec +27 -0
- metadata +118 -0
@@ -0,0 +1,42 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class Primitive
|
3
|
+
class Procedure
|
4
|
+
attr_reader :func, :name, :required_arg_num, :arg_list
|
5
|
+
|
6
|
+
def initialize(attrs = {})
|
7
|
+
@name = attrs[:name]
|
8
|
+
@func = attrs[:func]
|
9
|
+
parse_parameter_info(attrs[:func])
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(args)
|
13
|
+
check_arg_num!(args)
|
14
|
+
func.call(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def parse_parameter_info(fn)
|
20
|
+
@required_arg_num = 0
|
21
|
+
fn.parameters.each do |p|
|
22
|
+
param_type = p[0]
|
23
|
+
case param_type
|
24
|
+
when :req
|
25
|
+
@required_arg_num += 1
|
26
|
+
when :rest
|
27
|
+
@arg_list = true
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_arg_num!(args)
|
33
|
+
if required_arg_num > args.count
|
34
|
+
message = arg_list ?
|
35
|
+
"primitive procedure #{name}: required at least #{required_arg_num} arguments, got #{args.count}" :
|
36
|
+
"primitive procedure #{name}: required #{required_arg_num} arguments, got #{args.count}"
|
37
|
+
raise ArgumentError, message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end # Procedure
|
41
|
+
end # Primitive
|
42
|
+
end # RbScheme
|
@@ -0,0 +1,108 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class Printer
|
3
|
+
def puts_lisp_object(obj)
|
4
|
+
print_lisp_object(obj)
|
5
|
+
print "\n"
|
6
|
+
end
|
7
|
+
|
8
|
+
def print_lisp_object(obj)
|
9
|
+
case obj
|
10
|
+
when LInt
|
11
|
+
print obj.value
|
12
|
+
when LSymbol
|
13
|
+
print obj.name
|
14
|
+
when LTrue
|
15
|
+
print "#t"
|
16
|
+
when LFalse
|
17
|
+
print "#f"
|
18
|
+
when primitive_procedure
|
19
|
+
print "#<subr>"
|
20
|
+
when compound_procedure
|
21
|
+
print "#<closure>"
|
22
|
+
when LCell
|
23
|
+
if obj.null?
|
24
|
+
print("()")
|
25
|
+
return
|
26
|
+
end
|
27
|
+
|
28
|
+
print "("
|
29
|
+
loop do
|
30
|
+
print_lisp_object(obj.car)
|
31
|
+
case obj.cdr
|
32
|
+
when LCell
|
33
|
+
if obj.cdr.null?
|
34
|
+
print(")")
|
35
|
+
return
|
36
|
+
end
|
37
|
+
|
38
|
+
print(" ")
|
39
|
+
obj = obj.cdr
|
40
|
+
else
|
41
|
+
print(" . ")
|
42
|
+
print_lisp_object(obj.cdr)
|
43
|
+
print(")")
|
44
|
+
return
|
45
|
+
end
|
46
|
+
end
|
47
|
+
else
|
48
|
+
raise "bug - error unexpected type #{obj}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def compound_procedure
|
55
|
+
# expression (lambda ...) is compiled into Array
|
56
|
+
Array
|
57
|
+
end
|
58
|
+
|
59
|
+
def primitive_procedure
|
60
|
+
Primitive::Procedure
|
61
|
+
end
|
62
|
+
|
63
|
+
# for debug
|
64
|
+
def print_debug(obj)
|
65
|
+
case obj
|
66
|
+
when LInt
|
67
|
+
print obj.value
|
68
|
+
when LSymbol
|
69
|
+
print obj.name
|
70
|
+
when LTrue
|
71
|
+
print "#t"
|
72
|
+
when LFalse
|
73
|
+
print "#f"
|
74
|
+
when primitive_procedure
|
75
|
+
print "#<subr>"
|
76
|
+
when compound_procedure
|
77
|
+
print "#<closure>"
|
78
|
+
when LCell
|
79
|
+
if obj.null?
|
80
|
+
print("()")
|
81
|
+
return
|
82
|
+
end
|
83
|
+
|
84
|
+
print "("
|
85
|
+
loop do
|
86
|
+
print_debug(obj.car)
|
87
|
+
case obj.cdr
|
88
|
+
when LCell
|
89
|
+
if obj.cdr.null?
|
90
|
+
print(")")
|
91
|
+
return
|
92
|
+
end
|
93
|
+
|
94
|
+
print(" ")
|
95
|
+
obj = obj.cdr
|
96
|
+
else
|
97
|
+
print(" . ")
|
98
|
+
print_debug(obj.cdr)
|
99
|
+
print(")")
|
100
|
+
return
|
101
|
+
end
|
102
|
+
end
|
103
|
+
else
|
104
|
+
print ("ruby(#{obj})")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end # Printer
|
108
|
+
end # RbScheme
|
data/lib/rb-scheme/vm.rb
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
module RbScheme
|
2
|
+
class VM
|
3
|
+
extend Forwardable
|
4
|
+
include Helpers
|
5
|
+
include Symbol
|
6
|
+
|
7
|
+
def_delegators :@stack, :push, :index, :index_set!, :save_stack, :restore_stack
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@stack = Stack.new
|
11
|
+
end
|
12
|
+
|
13
|
+
def exec(acc, exp, frame_p, cls, stack_p)
|
14
|
+
loop do
|
15
|
+
case exp.car
|
16
|
+
when intern("halt")
|
17
|
+
check_length!(exp.cdr, 0, "halt")
|
18
|
+
return acc
|
19
|
+
when intern("refer-local")
|
20
|
+
check_length!(exp.cdr, 2, "refer-local")
|
21
|
+
n, x = exp.cdr.to_a
|
22
|
+
|
23
|
+
acc = index(frame_p, n)
|
24
|
+
exp = x
|
25
|
+
when intern("refer-free")
|
26
|
+
check_length!(exp.cdr, 2, "refer-free")
|
27
|
+
n, x = exp.cdr.to_a
|
28
|
+
|
29
|
+
acc = index_closure(cls, n)
|
30
|
+
exp = x
|
31
|
+
when intern("refer-global")
|
32
|
+
check_length!(exp.cdr, 2, "refer-free")
|
33
|
+
key, x = exp.cdr.to_a
|
34
|
+
|
35
|
+
acc = Global.get(key)
|
36
|
+
exp = x
|
37
|
+
when intern("indirect")
|
38
|
+
check_length!(exp.cdr, 1, "indirect")
|
39
|
+
x = exp.cadr
|
40
|
+
|
41
|
+
acc = acc.unbox
|
42
|
+
exp = x
|
43
|
+
when intern("constant")
|
44
|
+
check_length!(exp.cdr, 2, "constant")
|
45
|
+
obj, x = exp.cdr.to_a
|
46
|
+
|
47
|
+
acc = obj
|
48
|
+
exp = x
|
49
|
+
when intern("close")
|
50
|
+
check_length!(exp.cdr, 5, "close")
|
51
|
+
param_count, variadic, free_count, body, x = exp.cdr.to_a
|
52
|
+
|
53
|
+
acc = closure(body, param_count, variadic, free_count, stack_p)
|
54
|
+
exp = x
|
55
|
+
stack_p = stack_p - free_count
|
56
|
+
when intern("box")
|
57
|
+
check_length!(exp.cdr, 2, "box")
|
58
|
+
n, x = exp.cdr.to_a
|
59
|
+
|
60
|
+
index_set!(stack_p, n, Box.new(index(stack_p, n)))
|
61
|
+
exp = x
|
62
|
+
when intern("test")
|
63
|
+
check_length!(exp.cdr, 2, "test")
|
64
|
+
thenx, elsex = exp.cdr.to_a
|
65
|
+
|
66
|
+
exp = LFalse === acc ? elsex : thenx
|
67
|
+
when intern("assign-local")
|
68
|
+
check_length!(exp.cdr, 2, "assign-local")
|
69
|
+
n, x = exp.cdr.to_a
|
70
|
+
|
71
|
+
index(frame_p, n).set_box!(acc)
|
72
|
+
exp = x
|
73
|
+
when intern("assign-free")
|
74
|
+
check_length!(exp.cdr, 2, "assign-free")
|
75
|
+
n, x = exp.cdr.to_a
|
76
|
+
|
77
|
+
index_closure(cls, n).set_box!(acc)
|
78
|
+
exp = x
|
79
|
+
when intern("assign-global")
|
80
|
+
check_length!(exp.cdr, 2, "assign-global")
|
81
|
+
key, x = exp.cdr.to_a
|
82
|
+
|
83
|
+
Global.put(key, acc)
|
84
|
+
exp = x
|
85
|
+
when intern("conti")
|
86
|
+
check_length!(exp.cdr, 1, "conti")
|
87
|
+
x = exp.cadr
|
88
|
+
|
89
|
+
acc = continuation(stack_p)
|
90
|
+
exp = x
|
91
|
+
when intern("nuate")
|
92
|
+
check_length!(exp.cdr, 2, "nuate")
|
93
|
+
saved_stack, x = exp.cdr.to_a
|
94
|
+
|
95
|
+
exp = x
|
96
|
+
stack_p = restore_stack(saved_stack)
|
97
|
+
when intern("frame")
|
98
|
+
check_length!(exp.cdr, 2, "frame")
|
99
|
+
ret, x = exp.cdr.to_a
|
100
|
+
|
101
|
+
exp = x
|
102
|
+
stack_p = push(ret, push(frame_p, push(cls, stack_p)))
|
103
|
+
when intern("argument")
|
104
|
+
check_length!(exp.cdr, 1, "argument")
|
105
|
+
x = exp.cadr
|
106
|
+
|
107
|
+
exp = x
|
108
|
+
stack_p = push(acc, stack_p)
|
109
|
+
when intern("shift")
|
110
|
+
check_length!(exp.cdr, 3, "shift")
|
111
|
+
n, m, x = exp.cdr.to_a
|
112
|
+
|
113
|
+
exp = x
|
114
|
+
stack_p = shift_args(n, m, stack_p)
|
115
|
+
when intern("apply")
|
116
|
+
check_length!(exp.cdr, 1, "apply")
|
117
|
+
arg_count = exp.cadr
|
118
|
+
|
119
|
+
if primitive_procedure?(acc)
|
120
|
+
acc = apply_primitive(acc, arg_count, stack_p)
|
121
|
+
exp, frame_p, cls, stack_p = return_primitive(stack_p, arg_count)
|
122
|
+
elsif compound_procedure?(acc)
|
123
|
+
check_parameter!(closure_param_count(acc), arg_count, variadic_closure?(acc))
|
124
|
+
if variadic_closure?(acc)
|
125
|
+
stack_p = collect_arguments(stack_p, closure_param_count(acc), arg_count)
|
126
|
+
end
|
127
|
+
exp, frame_p, cls = apply_compound(acc, stack_p)
|
128
|
+
else
|
129
|
+
raise "invalid application"
|
130
|
+
end
|
131
|
+
when intern("return")
|
132
|
+
check_length!(exp.cdr, 1, "return")
|
133
|
+
n = exp.cadr
|
134
|
+
s = stack_p - n
|
135
|
+
|
136
|
+
exp = index(s, 0)
|
137
|
+
frame_p = index(s, 1)
|
138
|
+
cls = index(s, 2)
|
139
|
+
stack_p = s - 3
|
140
|
+
else
|
141
|
+
raise "Unknown instruction - #{exp.car}"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def collect_arguments(stack_p, cls_param_count, arg_count)
|
147
|
+
req = cls_param_count - 1
|
148
|
+
list_length = arg_count - req
|
149
|
+
unless list_length == 0
|
150
|
+
collect_arguments_as_list(stack_p, arg_count, list_length)
|
151
|
+
shift_required_variables(stack_p, req, arg_count)
|
152
|
+
stack_p - arg_count + cls_param_count
|
153
|
+
else
|
154
|
+
add_empty_list_as_argument(stack_p, arg_count)
|
155
|
+
stack_p + 1
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def collect_arguments_as_list(stack_p, arg_count, length)
|
160
|
+
lst = list
|
161
|
+
i = arg_count
|
162
|
+
length.times do
|
163
|
+
lst = cons(index(stack_p, i - 1), lst)
|
164
|
+
i -= 1
|
165
|
+
end
|
166
|
+
index_set!(stack_p, arg_count - 1, lst)
|
167
|
+
end
|
168
|
+
|
169
|
+
def shift_required_variables(stack_p, required_count, arg_count)
|
170
|
+
j = required_count
|
171
|
+
k = arg_count - 2
|
172
|
+
required_count.times do
|
173
|
+
index_set!(stack_p, k, index(stack_p, j - 1))
|
174
|
+
j -= 1
|
175
|
+
k -= 1
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def add_empty_list_as_argument(stack_p, arg_count)
|
180
|
+
last = arg_count - 1
|
181
|
+
0.upto(last) do |n|
|
182
|
+
v = index(stack_p, n)
|
183
|
+
index_set!(stack_p, n - 1, v)
|
184
|
+
end
|
185
|
+
index_set!(stack_p, last, list)
|
186
|
+
end
|
187
|
+
|
188
|
+
def apply_primitive(prim_proc, arg_count, stack_p)
|
189
|
+
i = 0
|
190
|
+
args = []
|
191
|
+
arg_count.times do
|
192
|
+
args.push(index(stack_p, i))
|
193
|
+
i += 1
|
194
|
+
end
|
195
|
+
prim_proc.call(args)
|
196
|
+
end
|
197
|
+
|
198
|
+
def return_primitive(stack_p, arg_count)
|
199
|
+
s = stack_p - arg_count
|
200
|
+
# [exp, frame_p, cls, stack_p]
|
201
|
+
[index(s, 0), index(s, 1), index(s, 2), s -3]
|
202
|
+
end
|
203
|
+
|
204
|
+
def apply_compound(acc, stack_p)
|
205
|
+
# [exp, frame_p, cls]
|
206
|
+
[closure_body(acc), stack_p, acc]
|
207
|
+
end
|
208
|
+
|
209
|
+
def primitive_procedure?(procedure)
|
210
|
+
procedure.is_a?(Primitive::Procedure)
|
211
|
+
end
|
212
|
+
|
213
|
+
def compound_procedure?(procedure)
|
214
|
+
procedure.is_a?(Array)
|
215
|
+
end
|
216
|
+
|
217
|
+
def shift_args(n, m, s)
|
218
|
+
i = n - 1
|
219
|
+
until i < 0
|
220
|
+
index_set!(s, i + m, index(s, i))
|
221
|
+
i -= 1
|
222
|
+
end
|
223
|
+
s - m
|
224
|
+
end
|
225
|
+
|
226
|
+
CLOSURE_OFFSET = 3
|
227
|
+
def closure(body, param_count, variadic, free_count, stack_p)
|
228
|
+
v = Array.new(free_count + CLOSURE_OFFSET)
|
229
|
+
v[0] = body
|
230
|
+
v[1] = param_count
|
231
|
+
v[2] = variadic
|
232
|
+
|
233
|
+
i = 0
|
234
|
+
until i == free_count
|
235
|
+
v[i + CLOSURE_OFFSET] = index(stack_p, i)
|
236
|
+
i += 1
|
237
|
+
end
|
238
|
+
v
|
239
|
+
end
|
240
|
+
|
241
|
+
def closure_body(cls)
|
242
|
+
cls[0]
|
243
|
+
end
|
244
|
+
|
245
|
+
def closure_param_count(cls)
|
246
|
+
cls[1]
|
247
|
+
end
|
248
|
+
|
249
|
+
def variadic_closure?(cls)
|
250
|
+
# 1: true, 0: false
|
251
|
+
cls[2] == 1
|
252
|
+
end
|
253
|
+
|
254
|
+
def index_closure(cls, n)
|
255
|
+
cls[n + CLOSURE_OFFSET]
|
256
|
+
end
|
257
|
+
|
258
|
+
def check_parameter!(expect, got, variadic)
|
259
|
+
if variadic
|
260
|
+
unless (expect - 1) <= got
|
261
|
+
raise ArgumentError,
|
262
|
+
"closure: required at least #{expect} arguments, got #{got}"
|
263
|
+
end
|
264
|
+
else
|
265
|
+
unless expect == got
|
266
|
+
raise ArgumentError,
|
267
|
+
"closure: required #{expect} arguments, got #{got}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def continuation(stack_p)
|
273
|
+
body = list(intern("refer-local"),
|
274
|
+
0,
|
275
|
+
list(intern("nuate"),
|
276
|
+
save_stack(stack_p),
|
277
|
+
list(intern("return"), 0)))
|
278
|
+
closure(body, 1, 0, 0, stack_p)
|
279
|
+
end
|
280
|
+
end # VM
|
281
|
+
end # RbScheme
|