shen-ruby 0.10.0 → 0.11.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.
- checksums.yaml +4 -4
- data/.rspec +1 -0
- data/.travis.yml +9 -3
- data/Gemfile +1 -4
- data/HISTORY.md +16 -0
- data/MIT_LICENSE.txt +1 -1
- data/README.md +25 -26
- data/Rakefile +3 -11
- data/bin/shen_test_suite.rb +15 -3
- data/bin/srrepl +6 -8
- data/lib/shen_ruby.rb +6 -1
- data/lib/shen_ruby/converters.rb +23 -0
- data/lib/shen_ruby/version.rb +1 -1
- data/shen-ruby.gemspec +4 -1
- data/shen/lib/shen_ruby/shen.rb +49 -33
- data/shen/release/benchmarks/N_queens.shen +45 -45
- data/shen/release/benchmarks/README.shen +14 -14
- data/shen/release/benchmarks/benchmarks.shen +52 -52
- data/shen/release/benchmarks/einstein.shen +32 -32
- data/shen/release/benchmarks/interpreter.shen +219 -219
- data/shen/release/benchmarks/jnk.shen +193 -193
- data/shen/release/benchmarks/powerset.shen +10 -10
- data/shen/release/benchmarks/prime.shen +10 -10
- data/shen/release/benchmarks/short.shen +129 -129
- data/shen/release/k_lambda/core.kl +181 -181
- data/shen/release/k_lambda/declarations.kl +131 -131
- data/shen/release/k_lambda/load.kl +84 -84
- data/shen/release/k_lambda/macros.kl +112 -112
- data/shen/release/k_lambda/prolog.kl +252 -252
- data/shen/release/k_lambda/reader.kl +222 -222
- data/shen/release/k_lambda/sequent.kl +166 -166
- data/shen/release/k_lambda/sys.kl +271 -271
- data/shen/release/k_lambda/t-star.kl +139 -139
- data/shen/release/k_lambda/toplevel.kl +135 -135
- data/shen/release/k_lambda/track.kl +103 -103
- data/shen/release/k_lambda/types.kl +324 -324
- data/shen/release/k_lambda/writer.kl +105 -105
- data/shen/release/k_lambda/yacc.kl +113 -113
- data/shen/release/test_programs/Chap13/problems.txt +26 -26
- data/shen/release/test_programs/README.shen +52 -52
- data/shen/release/test_programs/TinyLispFunctions.txt +15 -15
- data/shen/release/test_programs/TinyTypes.shen +55 -55
- data/shen/release/test_programs/binary.shen +24 -24
- data/shen/release/test_programs/bubble_version_1.shen +28 -28
- data/shen/release/test_programs/bubble_version_2.shen +22 -22
- data/shen/release/test_programs/calculator.shen +21 -21
- data/shen/release/test_programs/cartprod.shen +23 -23
- data/shen/release/test_programs/change.shen +25 -25
- data/shen/release/test_programs/classes-defaults.shen +94 -94
- data/shen/release/test_programs/classes-inheritance.shen +100 -100
- data/shen/release/test_programs/classes-typed.shen +74 -74
- data/shen/release/test_programs/classes-untyped.shen +46 -46
- data/shen/release/test_programs/depth_.shen +14 -14
- data/shen/release/test_programs/einstein.shen +34 -34
- data/shen/release/test_programs/fruit_machine.shen +46 -46
- data/shen/release/test_programs/interpreter.shen +217 -217
- data/shen/release/test_programs/metaprog.shen +85 -85
- data/shen/release/test_programs/minim.shen +192 -192
- data/shen/release/test_programs/mutual.shen +11 -11
- data/shen/release/test_programs/n_queens.shen +45 -45
- data/shen/release/test_programs/newton_version_1.shen +33 -33
- data/shen/release/test_programs/newton_version_2.shen +24 -24
- data/shen/release/test_programs/parse.prl +14 -14
- data/shen/release/test_programs/parser.shen +51 -51
- data/shen/release/test_programs/powerset.shen +10 -10
- data/shen/release/test_programs/prime.shen +10 -10
- data/shen/release/test_programs/prolog.shen +78 -78
- data/shen/release/test_programs/proof_assistant.shen +80 -80
- data/shen/release/test_programs/proplog_version_1.shen +25 -25
- data/shen/release/test_programs/proplog_version_2.shen +27 -27
- data/shen/release/test_programs/qmachine.shen +66 -66
- data/shen/release/test_programs/red-black.shen +54 -54
- data/shen/release/test_programs/search.shen +55 -55
- data/shen/release/test_programs/semantic_net.shen +44 -44
- data/shen/release/test_programs/spreadsheet.shen +34 -34
- data/shen/release/test_programs/stack.shen +27 -27
- data/shen/release/test_programs/streams.shen +20 -20
- data/shen/release/test_programs/strings.shen +57 -57
- data/shen/release/test_programs/structures-typed.shen +71 -71
- data/shen/release/test_programs/structures-untyped.shen +41 -41
- data/shen/release/test_programs/tests.shen +232 -232
- data/shen/release/test_programs/types.shen +11 -11
- data/shen/release/test_programs/whist.shen +239 -239
- data/shen/release/test_programs/yacc.shen +132 -132
- data/spec/shen_ruby/converters_spec.rb +48 -0
- data/spec/spec_helper.rb +1 -2
- metadata +55 -60
- data/k_lambda_spec/atom_spec.rb +0 -85
- data/k_lambda_spec/primitives/arithmetic_spec.rb +0 -175
- data/k_lambda_spec/primitives/assignments_spec.rb +0 -44
- data/k_lambda_spec/primitives/boolean_operations_spec.rb +0 -136
- data/k_lambda_spec/primitives/generic_functions_spec.rb +0 -120
- data/k_lambda_spec/primitives/lists_spec.rb +0 -40
- data/k_lambda_spec/primitives/strings_spec.rb +0 -77
- data/k_lambda_spec/primitives/symbols_spec.rb +0 -24
- data/k_lambda_spec/primitives/vectors_spec.rb +0 -92
- data/k_lambda_spec/spec_helper.rb +0 -29
- data/k_lambda_spec/support/shared_examples.rb +0 -124
- data/k_lambda_spec/tail_recursion_spec.rb +0 -30
- data/lib/kl.rb +0 -7
- data/lib/kl/absvector.rb +0 -12
- data/lib/kl/compiler.rb +0 -360
- data/lib/kl/cons.rb +0 -51
- data/lib/kl/empty_list.rb +0 -12
- data/lib/kl/environment.rb +0 -163
- data/lib/kl/error.rb +0 -4
- data/lib/kl/internal_error.rb +0 -7
- data/lib/kl/lexer.rb +0 -186
- data/lib/kl/primitives/arithmetic.rb +0 -60
- data/lib/kl/primitives/assignments.rb +0 -15
- data/lib/kl/primitives/booleans.rb +0 -21
- data/lib/kl/primitives/error_handling.rb +0 -13
- data/lib/kl/primitives/extensions.rb +0 -12
- data/lib/kl/primitives/generic_functions.rb +0 -29
- data/lib/kl/primitives/lists.rb +0 -23
- data/lib/kl/primitives/streams.rb +0 -28
- data/lib/kl/primitives/strings.rb +0 -63
- data/lib/kl/primitives/symbols.rb +0 -18
- data/lib/kl/primitives/time.rb +0 -17
- data/lib/kl/primitives/vectors.rb +0 -36
- data/lib/kl/reader.rb +0 -46
- data/spec/kl/cons_spec.rb +0 -12
- data/spec/kl/environment_spec.rb +0 -282
- data/spec/kl/interop_spec.rb +0 -68
- data/spec/kl/lexer_spec.rb +0 -149
- data/spec/kl/primitives/generic_functions_spec.rb +0 -29
- data/spec/kl/primitives/symbols_spec.rb +0 -21
- data/spec/kl/reader_spec.rb +0 -42
data/lib/kl/compiler.rb
DELETED
@@ -1,360 +0,0 @@
|
|
1
|
-
module Kl
|
2
|
-
module Compiler
|
3
|
-
# The K Lambda primitives are all Ruby functions and never use
|
4
|
-
# trampolines. They are all regarded as System Functions and
|
5
|
-
# therefore are not allowed to be redefined by the user at
|
6
|
-
# run time. Therefore, if all of their arguments have been
|
7
|
-
# supplied, it is safe to directly invoke them rather than going
|
8
|
-
# incurring the overhead of using __apply. This table holds the
|
9
|
-
# arities of the primitives and is used to determine whether
|
10
|
-
# direct invocation is possible.
|
11
|
-
# Kl::Primitives::Extensions is purposely omitted from this list
|
12
|
-
# so that it may be overridden by Shen.
|
13
|
-
PRIMITIVE_ARITIES = {}
|
14
|
-
[Kl::Primitives::Arithmetic, Kl::Primitives::Assignments,
|
15
|
-
Kl::Primitives::Booleans, Kl::Primitives::ErrorHandling,
|
16
|
-
Kl::Primitives::GenericFunctions, Kl::Primitives::Lists,
|
17
|
-
Kl::Primitives::Streams, Kl::Primitives::Strings,
|
18
|
-
Kl::Primitives::Symbols, Kl::Primitives::Time,
|
19
|
-
Kl::Primitives::Vectors].each do |prim_mod|
|
20
|
-
prim_mod.instance_methods.each do |name|
|
21
|
-
PRIMITIVE_ARITIES[name] = prim_mod.instance_method(name).arity
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class << self
|
26
|
-
def compile(form, lexical_vars, in_tail_pos)
|
27
|
-
case form
|
28
|
-
when Symbol
|
29
|
-
if lexical_vars.has_key?(form)
|
30
|
-
lexical_vars[form]
|
31
|
-
else
|
32
|
-
escape_symbol(form)
|
33
|
-
end
|
34
|
-
when String
|
35
|
-
# Emit non-interpolating strings
|
36
|
-
"'" + escape_string(form) + "'"
|
37
|
-
when Kl::Cons
|
38
|
-
compile_form(form, lexical_vars, in_tail_pos)
|
39
|
-
when Kl::EmptyList
|
40
|
-
"::Kl::EmptyList.instance"
|
41
|
-
when Numeric
|
42
|
-
form.to_s
|
43
|
-
when true
|
44
|
-
'true'
|
45
|
-
when false
|
46
|
-
'false'
|
47
|
-
else
|
48
|
-
raise Kl::InternalError, "unexpected form: #{form}"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
def compile_form(form, lexical_vars, in_tail_pos)
|
54
|
-
case form.hd
|
55
|
-
when :defun
|
56
|
-
compile_defun(form, lexical_vars)
|
57
|
-
when :lambda
|
58
|
-
compile_lambda(form, lexical_vars)
|
59
|
-
when :let
|
60
|
-
compile_let(form, lexical_vars, in_tail_pos)
|
61
|
-
when :freeze
|
62
|
-
compile_freeze(form, lexical_vars, in_tail_pos)
|
63
|
-
when :type
|
64
|
-
compile_type(form, lexical_vars, in_tail_pos)
|
65
|
-
when :if
|
66
|
-
compile_if(form, lexical_vars, in_tail_pos)
|
67
|
-
when :and
|
68
|
-
compile_and(form, lexical_vars, in_tail_pos)
|
69
|
-
when :or
|
70
|
-
compile_or(form, lexical_vars, in_tail_pos)
|
71
|
-
when :cond
|
72
|
-
compile_cond(form, lexical_vars, in_tail_pos)
|
73
|
-
when :do
|
74
|
-
compile_do(form, lexical_vars, in_tail_pos)
|
75
|
-
when :"trap-error"
|
76
|
-
compile_trap_error(form, lexical_vars, in_tail_pos)
|
77
|
-
# cons, hd, tl, and cons? are crucial to performance and are inlined
|
78
|
-
# when all of their arguments are available.
|
79
|
-
when :cons
|
80
|
-
compile_cons(form, lexical_vars, in_tail_pos)
|
81
|
-
when :"cons?"
|
82
|
-
compile_consp(form, lexical_vars, in_tail_pos)
|
83
|
-
else
|
84
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# (defun NAME ARGS BODY)
|
89
|
-
def compile_defun(form, lexical_vars)
|
90
|
-
name, arglist, body = destructure_form(form, 3)
|
91
|
-
unless name.kind_of? Symbol
|
92
|
-
raise Kl::Error, "#{name} is not a symbol"
|
93
|
-
end
|
94
|
-
unless [Kl::Cons, Kl::EmptyList].include? arglist.class
|
95
|
-
raise Kl::Error, "#{arglist} is not a list"
|
96
|
-
end
|
97
|
-
arglist.each do |arg|
|
98
|
-
unless arg.kind_of? Symbol
|
99
|
-
raise Kl::Error, "#{arg} is not a symbol"
|
100
|
-
end
|
101
|
-
end
|
102
|
-
if PRIMITIVE_ARITIES.has_key?(name)
|
103
|
-
raise Kl::Error, "#{name} is primitive and may not be redefined"
|
104
|
-
end
|
105
|
-
|
106
|
-
extended_vars = add_vars(lexical_vars, arglist.to_a)
|
107
|
-
|
108
|
-
fn_name = escape_symbol(name)
|
109
|
-
fn_args = arglist.map { |arg| extended_vars[arg] }.join(",")
|
110
|
-
fn_body = compile(body, extended_vars, true)
|
111
|
-
|
112
|
-
"(@functions[#{fn_name}] = ::Kernel.lambda { |#{fn_args}| #{fn_body}}; #{fn_name})"
|
113
|
-
end
|
114
|
-
|
115
|
-
# (lambda VAR BODY)
|
116
|
-
def compile_lambda(form, lexical_vars)
|
117
|
-
var, body = destructure_form(form, 2)
|
118
|
-
unless var.kind_of? Symbol
|
119
|
-
raise Kl::Error, "#{var} is not a symbol"
|
120
|
-
end
|
121
|
-
|
122
|
-
extended_vars = add_var(lexical_vars, var)
|
123
|
-
fn_arg = extended_vars[var]
|
124
|
-
fn_body = compile(body, extended_vars, true)
|
125
|
-
|
126
|
-
"::Kernel.lambda { |#{fn_arg}| #{fn_body}}"
|
127
|
-
end
|
128
|
-
|
129
|
-
# (let VAR EXPR BODY)
|
130
|
-
def compile_let(form, lexical_vars, in_tail_pos)
|
131
|
-
var, expr, body = destructure_form(form, 3)
|
132
|
-
unless var.kind_of? Symbol
|
133
|
-
raise Kl::Error, 'first argument to let must be a symbol'
|
134
|
-
end
|
135
|
-
|
136
|
-
extended_vars = add_var(lexical_vars, var)
|
137
|
-
bound_var = extended_vars[var]
|
138
|
-
# The bound expression is evaluated in the original lexical scope
|
139
|
-
bound_expr = compile(expr, lexical_vars, false)
|
140
|
-
let_body = compile(body, extended_vars, in_tail_pos)
|
141
|
-
|
142
|
-
"(#{bound_var} = #{bound_expr}; #{let_body})"
|
143
|
-
end
|
144
|
-
|
145
|
-
# (freeze EXPR)
|
146
|
-
def compile_freeze(form, lexical_vars, in_tail_pos)
|
147
|
-
if form.count == 2
|
148
|
-
expr = destructure_form(form, 1).first
|
149
|
-
|
150
|
-
body = compile(expr, lexical_vars, true)
|
151
|
-
|
152
|
-
"::Kernel.lambda { #{body} }"
|
153
|
-
else
|
154
|
-
# Partial application falls back to normal application
|
155
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
# (type EXPR T)
|
160
|
-
def compile_type(form, lexical_vars, in_tail_pos)
|
161
|
-
# Just ignore the type information for now
|
162
|
-
expr, _ = destructure_form(form, 2)
|
163
|
-
compile(expr, lexical_vars, in_tail_pos)
|
164
|
-
end
|
165
|
-
|
166
|
-
# (if TEST_EXPR TRUE_EXPR FALSE_EXPR)
|
167
|
-
def compile_if(form, lexical_vars, in_tail_pos)
|
168
|
-
if form.count == 4
|
169
|
-
test_expr, on_true_expr, on_false_expr = destructure_form(form, 3)
|
170
|
-
|
171
|
-
test_clause = compile(test_expr, lexical_vars, false)
|
172
|
-
true_clause = compile(on_true_expr, lexical_vars, in_tail_pos)
|
173
|
-
false_clause = compile(on_false_expr, lexical_vars, in_tail_pos)
|
174
|
-
|
175
|
-
"(#{test_clause} ? #{true_clause} : #{false_clause})"
|
176
|
-
else
|
177
|
-
# Partial application falls back to normal application
|
178
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
# (and EXPR1 EXPR2)
|
183
|
-
def compile_and(form, lexical_vars, in_tail_pos)
|
184
|
-
if form.count == 3
|
185
|
-
# This is the special form case
|
186
|
-
expr1, expr2 = destructure_form(form, 2)
|
187
|
-
compile_if(Kl::Cons.list([:if, expr1, expr2, false]),
|
188
|
-
lexical_vars, in_tail_pos)
|
189
|
-
else
|
190
|
-
# Partial application falls back to normal application
|
191
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
# (or EXPR1 EXPR2)
|
196
|
-
def compile_or(form, lexical_vars, in_tail_pos)
|
197
|
-
if form.count == 3
|
198
|
-
expr1, expr2 = destructure_form(form, 2)
|
199
|
-
compile_if(Kl::Cons.list([:if, expr1, true, expr2]),
|
200
|
-
lexical_vars, in_tail_pos)
|
201
|
-
else
|
202
|
-
# Partial application falls back to normal application
|
203
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
def compile_cond(form, lexical_vars, in_tail_pos)
|
208
|
-
clauses = form.tl
|
209
|
-
if clauses.kind_of? Kl::EmptyList
|
210
|
-
'raise(::Kl::Error, "condition failure")'
|
211
|
-
else
|
212
|
-
clause = clauses.hd
|
213
|
-
test_expr = clause.hd
|
214
|
-
true_expr = clause.tl.hd
|
215
|
-
false_expr = Kl::Cons.new(:cond, clauses.tl)
|
216
|
-
compile_if(Kl::Cons.list([:if, test_expr, true_expr, false_expr]),
|
217
|
-
lexical_vars,
|
218
|
-
in_tail_pos)
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
# (do EXPR1 EXPR2)
|
223
|
-
# 'do' is not a K Lambda primitive, and is defined in the Shen sources as
|
224
|
-
# a function that receives two arguments, and returns the last one.
|
225
|
-
# 'do' being a function means that the compiler will not see EXPR2 as
|
226
|
-
# being in tail-position, inhibiting TCO.
|
227
|
-
# To work around this, calls to 'do' are compiled to a sequence of
|
228
|
-
# expressions instead of calls to a 'do' function, by doing this, EXPR2
|
229
|
-
# has the potential to be in tail position and optimized as such.
|
230
|
-
def compile_do(form, lexical_vars, in_tail_pos)
|
231
|
-
if form.count == 3
|
232
|
-
expr1, expr2 = destructure_form(form, 2)
|
233
|
-
body1 = compile(expr1, lexical_vars, false)
|
234
|
-
body2 = compile(expr2, lexical_vars, in_tail_pos)
|
235
|
-
"(#{body1}; #{body2})"
|
236
|
-
else
|
237
|
-
# Partial application falls back to normal application
|
238
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
# (trap-error EXPR ERR_HANDLER)
|
243
|
-
def compile_trap_error(form, lexical_vars, in_tail_pos)
|
244
|
-
expr, err_handler = destructure_form(form, 2)
|
245
|
-
|
246
|
-
# TODO: TCO for try and catch clauses
|
247
|
-
try_clause = compile(expr, lexical_vars, false)
|
248
|
-
extended_vars = add_var(lexical_vars, :err)
|
249
|
-
err_var = extended_vars[:err]
|
250
|
-
catch_clause = compile_application(
|
251
|
-
Kl::Cons.list([err_handler, :err]),
|
252
|
-
extended_vars,
|
253
|
-
false)
|
254
|
-
|
255
|
-
"(begin; #{try_clause}; rescue ::Kl::Error => #{err_var}; " +
|
256
|
-
"#{catch_clause}; end)"
|
257
|
-
end
|
258
|
-
|
259
|
-
# Inlined version of (cons HD TL)
|
260
|
-
def compile_cons(form, lexical_vars, in_tail_pos)
|
261
|
-
if form.count == 3
|
262
|
-
hd, tl = destructure_form(form, 2)
|
263
|
-
hd_expr = compile(hd, lexical_vars, false)
|
264
|
-
tl_expr = compile(tl, lexical_vars, false)
|
265
|
-
"::Kl::Cons.new(#{hd_expr}, #{tl_expr})"
|
266
|
-
else
|
267
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
268
|
-
end
|
269
|
-
end
|
270
|
-
|
271
|
-
# Inlined version of (cons? X)
|
272
|
-
def compile_consp(form, lexical_vars, in_tail_pos)
|
273
|
-
if form.count == 2
|
274
|
-
expr = compile(form.tl.hd, lexical_vars, false)
|
275
|
-
"(#{expr}).kind_of?(::Kl::Cons)"
|
276
|
-
else
|
277
|
-
compile_application(form, lexical_vars, in_tail_pos)
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
# Normal function application
|
282
|
-
def compile_application(form, lexical_vars, in_tail_pos)
|
283
|
-
f = form.hd
|
284
|
-
args = form.tl
|
285
|
-
|
286
|
-
if PRIMITIVE_ARITIES[f] == args.count
|
287
|
-
# This is a non-partial primitive application. No need for __apply
|
288
|
-
# or trampolines.
|
289
|
-
send_args = form.map {|f| compile(f, lexical_vars, false)}.join(',')
|
290
|
-
"send(#{send_args})"
|
291
|
-
else
|
292
|
-
rator = compile(f, lexical_vars, false)
|
293
|
-
rands = args.map { |arg| compile(arg, lexical_vars, false) }.join(',')
|
294
|
-
|
295
|
-
if in_tail_pos
|
296
|
-
tfn = gen_sym
|
297
|
-
targs = gen_sym
|
298
|
-
|
299
|
-
"(
|
300
|
-
#{tfn} = #{rator};
|
301
|
-
#{targs} = [#{rands}];
|
302
|
-
@tramp_fn = #{tfn};
|
303
|
-
@tramp_args = #{targs}
|
304
|
-
)"
|
305
|
-
else
|
306
|
-
"__apply(#{rator}, [#{rands}])"
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
310
|
-
|
311
|
-
# Escape single quotes and backslashes
|
312
|
-
def escape_string(str)
|
313
|
-
new_str = ""
|
314
|
-
str.each_char do |c|
|
315
|
-
if c == "'"
|
316
|
-
new_str << "\\"
|
317
|
-
new_str << "'"
|
318
|
-
elsif c == '\\'
|
319
|
-
new_str << '\\'
|
320
|
-
new_str << '\\'
|
321
|
-
else
|
322
|
-
new_str << c
|
323
|
-
end
|
324
|
-
end
|
325
|
-
new_str
|
326
|
-
end
|
327
|
-
|
328
|
-
def escape_symbol(sym)
|
329
|
-
':"' + sym.to_s + '"'
|
330
|
-
end
|
331
|
-
|
332
|
-
def destructure_form(form, expected_arg_count)
|
333
|
-
array = form.to_a
|
334
|
-
unless array.length == expected_arg_count + 1
|
335
|
-
raise Kl::Error, "#{form.first} expects #{expected_arg_count} " +
|
336
|
-
"arguments but was given #{array.length - 1}"
|
337
|
-
end
|
338
|
-
array[1..-1]
|
339
|
-
end
|
340
|
-
|
341
|
-
def add_var(scope, var)
|
342
|
-
add_vars(scope, [var])
|
343
|
-
end
|
344
|
-
|
345
|
-
def add_vars(scope, vars)
|
346
|
-
scope.dup.tap do |new_scope|
|
347
|
-
vars.each do |var|
|
348
|
-
new_scope[var] = gen_sym
|
349
|
-
end
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
def gen_sym
|
354
|
-
@sym_count ||= 0
|
355
|
-
@sym_count += 1
|
356
|
-
"__kl_VAR_#{@sym_count}"
|
357
|
-
end
|
358
|
-
end
|
359
|
-
end
|
360
|
-
end
|
data/lib/kl/cons.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
require 'kl/empty_list'
|
2
|
-
|
3
|
-
module Kl
|
4
|
-
class Cons
|
5
|
-
include Enumerable
|
6
|
-
|
7
|
-
attr_reader :hd, :tl
|
8
|
-
|
9
|
-
def initialize(hd, tl)
|
10
|
-
@hd = hd
|
11
|
-
@tl = tl
|
12
|
-
end
|
13
|
-
|
14
|
-
def ==(other)
|
15
|
-
other.kind_of?(Kl::Cons) && hd == other.hd && tl == other.tl
|
16
|
-
end
|
17
|
-
|
18
|
-
def each
|
19
|
-
cell = self
|
20
|
-
while !cell.kind_of? Kl::EmptyList
|
21
|
-
yield cell.hd
|
22
|
-
cell = cell.tl
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
class << self
|
27
|
-
def list(array)
|
28
|
-
if array.empty?
|
29
|
-
Kl::EmptyList.instance
|
30
|
-
else
|
31
|
-
index = array.size - 1
|
32
|
-
head = new(array[index], Kl::EmptyList.instance)
|
33
|
-
index = index - 1
|
34
|
-
while index >= 0
|
35
|
-
head = new(array[index], head)
|
36
|
-
index = index - 1
|
37
|
-
end
|
38
|
-
head
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def list_to_string(f)
|
43
|
-
if f.kind_of? Kl::Cons
|
44
|
-
'(' + f.map {|x| list_to_string(x)}.join(' ') + ')'
|
45
|
-
else
|
46
|
-
f.to_s
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
data/lib/kl/empty_list.rb
DELETED
data/lib/kl/environment.rb
DELETED
@@ -1,163 +0,0 @@
|
|
1
|
-
require 'kl/primitives/booleans'
|
2
|
-
require 'kl/primitives/symbols'
|
3
|
-
require 'kl/primitives/strings'
|
4
|
-
require 'kl/primitives/assignments'
|
5
|
-
require 'kl/primitives/error_handling'
|
6
|
-
require 'kl/primitives/lists'
|
7
|
-
require 'kl/primitives/generic_functions'
|
8
|
-
require 'kl/primitives/vectors'
|
9
|
-
require 'kl/primitives/streams'
|
10
|
-
require 'kl/primitives/time'
|
11
|
-
require 'kl/primitives/arithmetic'
|
12
|
-
require 'kl/primitives/extensions'
|
13
|
-
require 'kl/compiler'
|
14
|
-
|
15
|
-
module Kl
|
16
|
-
class Environment
|
17
|
-
include ::Kl::Primitives::Booleans
|
18
|
-
include ::Kl::Primitives::Symbols
|
19
|
-
include ::Kl::Primitives::Strings
|
20
|
-
include ::Kl::Primitives::Assignments
|
21
|
-
include ::Kl::Primitives::ErrorHandling
|
22
|
-
include ::Kl::Primitives::Lists
|
23
|
-
include ::Kl::Primitives::GenericFunctions
|
24
|
-
include ::Kl::Primitives::Vectors
|
25
|
-
include ::Kl::Primitives::Streams
|
26
|
-
include ::Kl::Primitives::Time
|
27
|
-
include ::Kl::Primitives::Arithmetic
|
28
|
-
include ::Kl::Primitives::Extensions
|
29
|
-
|
30
|
-
def initialize
|
31
|
-
@dump_code = false
|
32
|
-
@tramp_fn = @tramp_args = nil
|
33
|
-
@variables = Hash.new do |_, k|
|
34
|
-
raise Kl::Error, "#{k} is not a symbol" unless k.kind_of? Symbol
|
35
|
-
raise Kl::Error, "variable #{k} has no value"
|
36
|
-
end
|
37
|
-
@functions = Hash.new do |h, k|
|
38
|
-
if respond_to? k
|
39
|
-
fn = method(k).to_proc
|
40
|
-
h[k] = fn
|
41
|
-
else
|
42
|
-
raise Kl::Error, "The function #{k} is undefined"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
@eigenklass = class << self; self; end
|
46
|
-
end
|
47
|
-
|
48
|
-
# Trampoline-aware function application
|
49
|
-
def __apply(fn, args)
|
50
|
-
while fn
|
51
|
-
@tramp_fn = nil
|
52
|
-
fn = @functions[fn] if fn.kind_of? Symbol
|
53
|
-
arity = fn.arity
|
54
|
-
if arity == args.size || arity == -1
|
55
|
-
result = fn.call(*args)
|
56
|
-
elsif arity > args.size
|
57
|
-
# Partial application
|
58
|
-
result = fn.curry.call(*args)
|
59
|
-
else
|
60
|
-
# Uncurrying. Apply fn to its expected number of arguments
|
61
|
-
# and hope that the result is a function that can be applied
|
62
|
-
# to the remainder.
|
63
|
-
fn = __apply(fn, args[0, arity])
|
64
|
-
unless fn.kind_of?(Proc) || fn.kind_of?(Symbol)
|
65
|
-
raise ::Kl::Error,
|
66
|
-
"The value #{str(fn)} is neither a function nor a symbol."
|
67
|
-
end
|
68
|
-
args = args[arity..-1]
|
69
|
-
next
|
70
|
-
end
|
71
|
-
|
72
|
-
if fn = @tramp_fn
|
73
|
-
# Bounce on the trampoline
|
74
|
-
args = @tramp_args
|
75
|
-
@tramp_args = nil
|
76
|
-
end
|
77
|
-
end
|
78
|
-
result
|
79
|
-
rescue SystemStackError
|
80
|
-
raise ::Kl::Error, 'maximum stack depth exceeded'
|
81
|
-
end
|
82
|
-
|
83
|
-
def __eval(form)
|
84
|
-
if @dump_code
|
85
|
-
puts "=" * 70
|
86
|
-
puts "Compiling:"
|
87
|
-
puts Kl::Cons.list_to_string(form)
|
88
|
-
puts '-----'
|
89
|
-
end
|
90
|
-
code = ::Kl::Compiler.compile(form, {}, true)
|
91
|
-
if @dump_code
|
92
|
-
puts code
|
93
|
-
puts "=" * 70
|
94
|
-
end
|
95
|
-
@tramp_fn = nil
|
96
|
-
result = instance_eval(code)
|
97
|
-
# Handle top-level trampolines
|
98
|
-
if @tramp_fn
|
99
|
-
fn = @tramp_fn
|
100
|
-
args = @tramp_args
|
101
|
-
@tramp_fn = nil
|
102
|
-
@tramp_args = nil
|
103
|
-
__apply(fn, args)
|
104
|
-
else
|
105
|
-
result
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def method_missing(f, *args)
|
110
|
-
if @functions.has_key?(f)
|
111
|
-
fn = @functions[f]
|
112
|
-
else
|
113
|
-
coerced_f = Kl::Environment.ruby_to_kl(f)
|
114
|
-
if @functions.has_key?(coerced_f)
|
115
|
-
fn = @functions[coerced_f]
|
116
|
-
else
|
117
|
-
super
|
118
|
-
end
|
119
|
-
end
|
120
|
-
coerced_args = args.map { |arg| Kl::Environment.ruby_to_kl(arg)}
|
121
|
-
Kl::Environment.kl_to_ruby(__apply(fn, coerced_args))
|
122
|
-
end
|
123
|
-
|
124
|
-
def respond_to?(f)
|
125
|
-
@functions.has_key?(f) ||
|
126
|
-
@functions.has_key?(Kl::Environment.ruby_to_kl(f)) ||
|
127
|
-
super
|
128
|
-
end
|
129
|
-
|
130
|
-
class << self
|
131
|
-
def load_file(env, path)
|
132
|
-
File.open(path, 'r') do |file|
|
133
|
-
reader = Kl::Reader.new(file)
|
134
|
-
while form = reader.next
|
135
|
-
env.__eval(form)
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# Coerce Ruby types and conventions to K Lambda
|
141
|
-
def kl_to_ruby(x)
|
142
|
-
if x.kind_of? Kl::Cons
|
143
|
-
x.to_a.map { |y| kl_to_ruby(y) }
|
144
|
-
elsif x.kind_of? Symbol
|
145
|
-
x.to_s.gsub(/-/, '_').to_sym
|
146
|
-
else
|
147
|
-
x
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# Coerce K Lambda types and conventions to Ruby
|
152
|
-
def ruby_to_kl(x)
|
153
|
-
if x.kind_of? Array
|
154
|
-
Kl::Cons.list(x.map {|x| ruby_to_kl(x)})
|
155
|
-
elsif x.kind_of? Symbol
|
156
|
-
x.to_s.gsub(/_/, '-').to_sym
|
157
|
-
else
|
158
|
-
x
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|