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.
Files changed (128) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -0
  3. data/.travis.yml +9 -3
  4. data/Gemfile +1 -4
  5. data/HISTORY.md +16 -0
  6. data/MIT_LICENSE.txt +1 -1
  7. data/README.md +25 -26
  8. data/Rakefile +3 -11
  9. data/bin/shen_test_suite.rb +15 -3
  10. data/bin/srrepl +6 -8
  11. data/lib/shen_ruby.rb +6 -1
  12. data/lib/shen_ruby/converters.rb +23 -0
  13. data/lib/shen_ruby/version.rb +1 -1
  14. data/shen-ruby.gemspec +4 -1
  15. data/shen/lib/shen_ruby/shen.rb +49 -33
  16. data/shen/release/benchmarks/N_queens.shen +45 -45
  17. data/shen/release/benchmarks/README.shen +14 -14
  18. data/shen/release/benchmarks/benchmarks.shen +52 -52
  19. data/shen/release/benchmarks/einstein.shen +32 -32
  20. data/shen/release/benchmarks/interpreter.shen +219 -219
  21. data/shen/release/benchmarks/jnk.shen +193 -193
  22. data/shen/release/benchmarks/powerset.shen +10 -10
  23. data/shen/release/benchmarks/prime.shen +10 -10
  24. data/shen/release/benchmarks/short.shen +129 -129
  25. data/shen/release/k_lambda/core.kl +181 -181
  26. data/shen/release/k_lambda/declarations.kl +131 -131
  27. data/shen/release/k_lambda/load.kl +84 -84
  28. data/shen/release/k_lambda/macros.kl +112 -112
  29. data/shen/release/k_lambda/prolog.kl +252 -252
  30. data/shen/release/k_lambda/reader.kl +222 -222
  31. data/shen/release/k_lambda/sequent.kl +166 -166
  32. data/shen/release/k_lambda/sys.kl +271 -271
  33. data/shen/release/k_lambda/t-star.kl +139 -139
  34. data/shen/release/k_lambda/toplevel.kl +135 -135
  35. data/shen/release/k_lambda/track.kl +103 -103
  36. data/shen/release/k_lambda/types.kl +324 -324
  37. data/shen/release/k_lambda/writer.kl +105 -105
  38. data/shen/release/k_lambda/yacc.kl +113 -113
  39. data/shen/release/test_programs/Chap13/problems.txt +26 -26
  40. data/shen/release/test_programs/README.shen +52 -52
  41. data/shen/release/test_programs/TinyLispFunctions.txt +15 -15
  42. data/shen/release/test_programs/TinyTypes.shen +55 -55
  43. data/shen/release/test_programs/binary.shen +24 -24
  44. data/shen/release/test_programs/bubble_version_1.shen +28 -28
  45. data/shen/release/test_programs/bubble_version_2.shen +22 -22
  46. data/shen/release/test_programs/calculator.shen +21 -21
  47. data/shen/release/test_programs/cartprod.shen +23 -23
  48. data/shen/release/test_programs/change.shen +25 -25
  49. data/shen/release/test_programs/classes-defaults.shen +94 -94
  50. data/shen/release/test_programs/classes-inheritance.shen +100 -100
  51. data/shen/release/test_programs/classes-typed.shen +74 -74
  52. data/shen/release/test_programs/classes-untyped.shen +46 -46
  53. data/shen/release/test_programs/depth_.shen +14 -14
  54. data/shen/release/test_programs/einstein.shen +34 -34
  55. data/shen/release/test_programs/fruit_machine.shen +46 -46
  56. data/shen/release/test_programs/interpreter.shen +217 -217
  57. data/shen/release/test_programs/metaprog.shen +85 -85
  58. data/shen/release/test_programs/minim.shen +192 -192
  59. data/shen/release/test_programs/mutual.shen +11 -11
  60. data/shen/release/test_programs/n_queens.shen +45 -45
  61. data/shen/release/test_programs/newton_version_1.shen +33 -33
  62. data/shen/release/test_programs/newton_version_2.shen +24 -24
  63. data/shen/release/test_programs/parse.prl +14 -14
  64. data/shen/release/test_programs/parser.shen +51 -51
  65. data/shen/release/test_programs/powerset.shen +10 -10
  66. data/shen/release/test_programs/prime.shen +10 -10
  67. data/shen/release/test_programs/prolog.shen +78 -78
  68. data/shen/release/test_programs/proof_assistant.shen +80 -80
  69. data/shen/release/test_programs/proplog_version_1.shen +25 -25
  70. data/shen/release/test_programs/proplog_version_2.shen +27 -27
  71. data/shen/release/test_programs/qmachine.shen +66 -66
  72. data/shen/release/test_programs/red-black.shen +54 -54
  73. data/shen/release/test_programs/search.shen +55 -55
  74. data/shen/release/test_programs/semantic_net.shen +44 -44
  75. data/shen/release/test_programs/spreadsheet.shen +34 -34
  76. data/shen/release/test_programs/stack.shen +27 -27
  77. data/shen/release/test_programs/streams.shen +20 -20
  78. data/shen/release/test_programs/strings.shen +57 -57
  79. data/shen/release/test_programs/structures-typed.shen +71 -71
  80. data/shen/release/test_programs/structures-untyped.shen +41 -41
  81. data/shen/release/test_programs/tests.shen +232 -232
  82. data/shen/release/test_programs/types.shen +11 -11
  83. data/shen/release/test_programs/whist.shen +239 -239
  84. data/shen/release/test_programs/yacc.shen +132 -132
  85. data/spec/shen_ruby/converters_spec.rb +48 -0
  86. data/spec/spec_helper.rb +1 -2
  87. metadata +55 -60
  88. data/k_lambda_spec/atom_spec.rb +0 -85
  89. data/k_lambda_spec/primitives/arithmetic_spec.rb +0 -175
  90. data/k_lambda_spec/primitives/assignments_spec.rb +0 -44
  91. data/k_lambda_spec/primitives/boolean_operations_spec.rb +0 -136
  92. data/k_lambda_spec/primitives/generic_functions_spec.rb +0 -120
  93. data/k_lambda_spec/primitives/lists_spec.rb +0 -40
  94. data/k_lambda_spec/primitives/strings_spec.rb +0 -77
  95. data/k_lambda_spec/primitives/symbols_spec.rb +0 -24
  96. data/k_lambda_spec/primitives/vectors_spec.rb +0 -92
  97. data/k_lambda_spec/spec_helper.rb +0 -29
  98. data/k_lambda_spec/support/shared_examples.rb +0 -124
  99. data/k_lambda_spec/tail_recursion_spec.rb +0 -30
  100. data/lib/kl.rb +0 -7
  101. data/lib/kl/absvector.rb +0 -12
  102. data/lib/kl/compiler.rb +0 -360
  103. data/lib/kl/cons.rb +0 -51
  104. data/lib/kl/empty_list.rb +0 -12
  105. data/lib/kl/environment.rb +0 -163
  106. data/lib/kl/error.rb +0 -4
  107. data/lib/kl/internal_error.rb +0 -7
  108. data/lib/kl/lexer.rb +0 -186
  109. data/lib/kl/primitives/arithmetic.rb +0 -60
  110. data/lib/kl/primitives/assignments.rb +0 -15
  111. data/lib/kl/primitives/booleans.rb +0 -21
  112. data/lib/kl/primitives/error_handling.rb +0 -13
  113. data/lib/kl/primitives/extensions.rb +0 -12
  114. data/lib/kl/primitives/generic_functions.rb +0 -29
  115. data/lib/kl/primitives/lists.rb +0 -23
  116. data/lib/kl/primitives/streams.rb +0 -28
  117. data/lib/kl/primitives/strings.rb +0 -63
  118. data/lib/kl/primitives/symbols.rb +0 -18
  119. data/lib/kl/primitives/time.rb +0 -17
  120. data/lib/kl/primitives/vectors.rb +0 -36
  121. data/lib/kl/reader.rb +0 -46
  122. data/spec/kl/cons_spec.rb +0 -12
  123. data/spec/kl/environment_spec.rb +0 -282
  124. data/spec/kl/interop_spec.rb +0 -68
  125. data/spec/kl/lexer_spec.rb +0 -149
  126. data/spec/kl/primitives/generic_functions_spec.rb +0 -29
  127. data/spec/kl/primitives/symbols_spec.rb +0 -21
  128. 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
@@ -1,12 +0,0 @@
1
- require 'singleton'
2
-
3
- module Kl
4
- class EmptyList
5
- include Singleton
6
- include Enumerable
7
-
8
- def each
9
- # no-op
10
- end
11
- end
12
- end
@@ -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