shen-ruby 0.10.0 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|