klam 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rspec +0 -0
- data/.travis.yml +8 -0
- data/Gemfile +1 -0
- data/LICENSE.txt +19 -0
- data/PROGRESS.asciidoc +105 -0
- data/README.asciidoc +11 -0
- data/Rakefile +5 -0
- data/klam.gemspec +28 -0
- data/lib/klam.rb +16 -0
- data/lib/klam/compilation_stages.rb +4 -0
- data/lib/klam/compilation_stages/convert_freezes_to_lambdas.rb +17 -0
- data/lib/klam/compilation_stages/convert_lexical_variables.rb +79 -0
- data/lib/klam/compilation_stages/convert_partial_applications_to_lambdas.rb +35 -0
- data/lib/klam/compilation_stages/convert_self_tail_calls_to_loops.rb +147 -0
- data/lib/klam/compilation_stages/curry_abstraction_applications.rb +33 -0
- data/lib/klam/compilation_stages/emit_ruby.rb +232 -0
- data/lib/klam/compilation_stages/kl_to_internal_representation.rb +21 -0
- data/lib/klam/compilation_stages/make_abstractions_monadic.rb +26 -0
- data/lib/klam/compilation_stages/make_abstractions_variadic.rb +23 -0
- data/lib/klam/compilation_stages/simplify_boolean_operations.rb +74 -0
- data/lib/klam/compilation_stages/strip_type_declarations.rb +24 -0
- data/lib/klam/compiler.rb +63 -0
- data/lib/klam/cons.rb +18 -0
- data/lib/klam/converters.rb +4 -0
- data/lib/klam/converters/.list.rb.swp +0 -0
- data/lib/klam/converters/list.rb +29 -0
- data/lib/klam/environment.rb +61 -0
- data/lib/klam/error.rb +4 -0
- data/lib/klam/lexer.rb +185 -0
- data/lib/klam/primitives.rb +4 -0
- data/lib/klam/primitives/arithmetic.rb +49 -0
- data/lib/klam/primitives/assignments.rb +13 -0
- data/lib/klam/primitives/boolean_operations.rb +22 -0
- data/lib/klam/primitives/error_handling.rb +19 -0
- data/lib/klam/primitives/generic_functions.rb +19 -0
- data/lib/klam/primitives/lists.rb +23 -0
- data/lib/klam/primitives/streams.rb +32 -0
- data/lib/klam/primitives/strings.rb +58 -0
- data/lib/klam/primitives/symbols.rb +16 -0
- data/lib/klam/primitives/time.rb +19 -0
- data/lib/klam/primitives/vectors.rb +26 -0
- data/lib/klam/reader.rb +46 -0
- data/lib/klam/template.rb +38 -0
- data/lib/klam/variable.rb +21 -0
- data/lib/klam/variable_generator.rb +12 -0
- data/lib/klam/version.rb +3 -0
- data/spec/functional/application_spec.rb +94 -0
- data/spec/functional/atoms_spec.rb +56 -0
- data/spec/functional/extensions/do_spec.rb +22 -0
- data/spec/functional/primitives/assignments_spec.rb +38 -0
- data/spec/functional/primitives/boolean_operations_spec.rb +133 -0
- data/spec/functional/primitives/error_handling_spec.rb +22 -0
- data/spec/functional/primitives/generic_functions_spec.rb +82 -0
- data/spec/functional/tail_call_optimization_spec.rb +71 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/unit/klam/compilation_stages/convert_lexical_variables_spec.rb +58 -0
- data/spec/unit/klam/compilation_stages/convert_self_tail_calls_to_loops_spec.rb +33 -0
- data/spec/unit/klam/compilation_stages/curry_abstraction_applications_spec.rb +19 -0
- data/spec/unit/klam/compilation_stages/make_abstractions_variadic_spec.rb +12 -0
- data/spec/unit/klam/converters/list_spec.rb +57 -0
- data/spec/unit/klam/lexer_spec.rb +149 -0
- data/spec/unit/klam/primitives/arithmetic_spec.rb +153 -0
- data/spec/unit/klam/primitives/boolean_operations_spec.rb +39 -0
- data/spec/unit/klam/primitives/error_handling_spec.rb +19 -0
- data/spec/unit/klam/primitives/lists_spec.rb +49 -0
- data/spec/unit/klam/primitives/strings_spec.rb +53 -0
- data/spec/unit/klam/primitives/symbols_spec.rb +19 -0
- data/spec/unit/klam/primitives/time_spec.rb +16 -0
- data/spec/unit/klam/primitives/vectors_spec.rb +55 -0
- data/spec/unit/klam/reader_spec.rb +47 -0
- data/spec/unit/klam/template_spec.rb +28 -0
- data/spec/unit/klam/variable_spec.rb +22 -0
- data/spec/unit/klam/version_spec.rb +7 -0
- metadata +225 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Curry Abstraction Applications
|
4
|
+
#
|
5
|
+
# This stage converts all applications of abstractions (i.e., any
|
6
|
+
# application that does not have a symbol in rator position) to
|
7
|
+
# a curried form in which only one argument is applied at a time.
|
8
|
+
# For example:
|
9
|
+
#
|
10
|
+
# ((foo) 1 2 3)
|
11
|
+
#
|
12
|
+
# becomes:
|
13
|
+
#
|
14
|
+
# ((((foo) 1) 2) 3)
|
15
|
+
module CurryAbstractionApplications
|
16
|
+
def curry_abstraction_applications(sexp)
|
17
|
+
if sexp.instance_of?(Array)
|
18
|
+
if !sexp[0].instance_of?(Symbol) && sexp.length > 2
|
19
|
+
[curry_abstraction_applications(sexp[0..-2]),
|
20
|
+
curry_abstraction_applications(sexp[-1])]
|
21
|
+
elsif sexp[0] == :defun
|
22
|
+
sexp[0,3] +
|
23
|
+
sexp[3..-1].map { |form| curry_abstraction_applications(form) }
|
24
|
+
else
|
25
|
+
sexp.map { |form| curry_abstraction_applications(form) }
|
26
|
+
end
|
27
|
+
else
|
28
|
+
sexp
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Emit Ruby
|
4
|
+
#
|
5
|
+
# This is the final stage in the compilation pipeline. It is responsible
|
6
|
+
# for converting the compiler's internal representation to a string
|
7
|
+
# containing Ruby code that is suitable for execution via instance_eval in
|
8
|
+
# the context of a Klam::Environment object.
|
9
|
+
#
|
10
|
+
# By the time compilation reaches this stage, all of the heavy lifting
|
11
|
+
# should be complete. Emitting Ruby is simply a matter of transliterating
|
12
|
+
# the simplified s-expression into Ruby.
|
13
|
+
module EmitRuby
|
14
|
+
include Klam::Template
|
15
|
+
|
16
|
+
PRIMITIVE_TEMPLATES = {
|
17
|
+
:+ => [2, '($1 + $2)'],
|
18
|
+
:- => [2, '($1 - $2)'],
|
19
|
+
:* => [2, '($1 * $2)'],
|
20
|
+
:< => [2, '($1 < $2)'],
|
21
|
+
:> => [2, '($1 > $2)'],
|
22
|
+
:<= => [2, '($1 <= $2)'],
|
23
|
+
:>= => [2, '($1 >= $2)'],
|
24
|
+
:"=" => [2, '($1 == $2)'],
|
25
|
+
:absvector => [1, '::Array.new($1)'],
|
26
|
+
:absvector? => [1, '$1.instance_of?(::Array)'],
|
27
|
+
:"<-address" => [2, '$1[$2]'],
|
28
|
+
:cons => [2, '::Klam::Cons.new($1, $2)'],
|
29
|
+
:cons? => [1, '$1.instance_of?(::Klam::Cons)'],
|
30
|
+
:hd => [1, '$1.hd'],
|
31
|
+
:set => [2, '(@assignments[$1] = $2)'],
|
32
|
+
:tl => [1, '$1.tl'],
|
33
|
+
:value => [1, '@assignments[$1]']
|
34
|
+
}
|
35
|
+
|
36
|
+
def emit_ruby(sexp)
|
37
|
+
case sexp
|
38
|
+
when Symbol
|
39
|
+
emit_symbol(sexp)
|
40
|
+
when String
|
41
|
+
emit_string(sexp)
|
42
|
+
when Klam::Variable, Numeric, true, false
|
43
|
+
sexp.to_s
|
44
|
+
when Array
|
45
|
+
if sexp.empty?
|
46
|
+
Klam::Primitives::Lists::EMPTY_LIST.inspect
|
47
|
+
else
|
48
|
+
emit_compound_form(sexp)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def emit_compound_form(form)
|
56
|
+
# Handle special forms and fall back to normal function application
|
57
|
+
case form[0]
|
58
|
+
when :defun
|
59
|
+
emit_defun(form)
|
60
|
+
when :if
|
61
|
+
emit_if(form)
|
62
|
+
when :lambda
|
63
|
+
emit_lambda(form)
|
64
|
+
when :let
|
65
|
+
emit_let(form)
|
66
|
+
when :"trap-error"
|
67
|
+
emit_trap_error(form)
|
68
|
+
when :do
|
69
|
+
emit_do(form)
|
70
|
+
when :"[FIX-VARS]"
|
71
|
+
emit_fix_vars(form)
|
72
|
+
when :"[LOOP]"
|
73
|
+
emit_loop(form)
|
74
|
+
when :"[RECUR]"
|
75
|
+
emit_recur(form)
|
76
|
+
else
|
77
|
+
if full_primitive_form?(form)
|
78
|
+
emit_primitive(form)
|
79
|
+
else
|
80
|
+
emit_application(form)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def full_primitive_form?(form)
|
86
|
+
num_args, template = PRIMITIVE_TEMPLATES[form[0]]
|
87
|
+
num_args && (num_args == form.size - 1)
|
88
|
+
end
|
89
|
+
|
90
|
+
def emit_primitive(form)
|
91
|
+
_, template = PRIMITIVE_TEMPLATES[form[0]]
|
92
|
+
rands_rb = form[1..-1].map { |rand| emit_ruby(rand) }
|
93
|
+
render_string(template, *rands_rb)
|
94
|
+
end
|
95
|
+
|
96
|
+
def emit_application(form)
|
97
|
+
rator = form[0]
|
98
|
+
rands = form[1..-1]
|
99
|
+
rator_rb = emit_ruby(rator)
|
100
|
+
rands_rb = rands.map { |rand| emit_ruby(rand) }
|
101
|
+
|
102
|
+
if rator.kind_of?(Symbol)
|
103
|
+
# Application of a function defined in the environment. At this point
|
104
|
+
# partial application and currying has been taken care of, so a
|
105
|
+
# simple send suffices.
|
106
|
+
render_string('__send__($1)', [rator_rb] + rands_rb)
|
107
|
+
else
|
108
|
+
render_string('__apply($1)', [rator_rb] + rands_rb)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def emit_defun(form)
|
113
|
+
_, name, params, body = form
|
114
|
+
name_rb = emit_ruby(name)
|
115
|
+
params_rb = params.map { |param| emit_ruby(param) }
|
116
|
+
body_rb = emit_ruby(body)
|
117
|
+
|
118
|
+
# Some valid Kl function names (e.g. foo-bar) are not valid when used
|
119
|
+
# with Ruby's def syntax. They will work with define_method, but the
|
120
|
+
# resulting methods are slower than if they had been defined via def.
|
121
|
+
# To maximize performance, methods are defined with def and then
|
122
|
+
# renamed to their intended name afterwards.
|
123
|
+
mangled_name = '__klam_fn_' + name.to_s.gsub(/[^a-zA-Z0-9]/, '_')
|
124
|
+
render_string(<<-EOT, name_rb, params_rb, body_rb, mangled_name, params.size)
|
125
|
+
def $4($2)
|
126
|
+
$3
|
127
|
+
end
|
128
|
+
@eigenclass.rename_method(:$4, $1)
|
129
|
+
@arities[$1] = $5
|
130
|
+
@curried_methods.delete($1)
|
131
|
+
@loop_cache.delete($1)
|
132
|
+
$1
|
133
|
+
EOT
|
134
|
+
end
|
135
|
+
|
136
|
+
def emit_do(form)
|
137
|
+
rands = form[1,2]
|
138
|
+
rands_rb = rands.map { |rand| emit_ruby(rand) }
|
139
|
+
'(' + rands_rb.join(';') + ')'
|
140
|
+
end
|
141
|
+
|
142
|
+
def emit_fix_vars(form)
|
143
|
+
_, params, expr = form
|
144
|
+
params_rb = params.map { |param| emit_ruby(param) }
|
145
|
+
expr_rb = emit_ruby(expr)
|
146
|
+
|
147
|
+
render_string('(::Kernel.lambda { |$1| $2 }).call($1)',
|
148
|
+
params_rb, expr_rb)
|
149
|
+
end
|
150
|
+
|
151
|
+
def emit_if(form)
|
152
|
+
args = form[1..3].map { |sexp| emit_ruby(sexp) }
|
153
|
+
render_string('($1 ? $2 : $3)', *args)
|
154
|
+
end
|
155
|
+
|
156
|
+
def emit_lambda(form)
|
157
|
+
_, params, body = form
|
158
|
+
params_rb = params.map { |param| emit_ruby(param) }
|
159
|
+
body_rb = emit_ruby(body)
|
160
|
+
|
161
|
+
render_string('(::Kernel.lambda { |$1| $2 })', params_rb, body_rb)
|
162
|
+
end
|
163
|
+
|
164
|
+
def emit_let(form)
|
165
|
+
_, var, value_expr, body_expr = form
|
166
|
+
|
167
|
+
var_rb = emit_ruby(var)
|
168
|
+
value_expr_rb = emit_ruby(value_expr)
|
169
|
+
body_expr_rb = emit_ruby(body_expr)
|
170
|
+
|
171
|
+
render_string('($1 = $2; $3)', var_rb, value_expr_rb, body_expr_rb)
|
172
|
+
end
|
173
|
+
|
174
|
+
def emit_loop(form)
|
175
|
+
name_rb = emit_ruby(form[1])
|
176
|
+
params_rb = form[2].map {|v| emit_ruby(v)}
|
177
|
+
expr_rb = emit_ruby(form[3])
|
178
|
+
render_string('(@loop_cache[$1] ||= ::Kernel.lambda { |$2| $3 }).call($2)', name_rb,
|
179
|
+
params_rb, expr_rb)
|
180
|
+
end
|
181
|
+
|
182
|
+
def emit_recur(form)
|
183
|
+
_, params, new_value_exprs = form
|
184
|
+
if params.size > 0
|
185
|
+
params_rb = params.map { |param| emit_ruby(param) }
|
186
|
+
new_value_exprs_rb = new_value_exprs.map { |expr| emit_ruby(expr) }
|
187
|
+
|
188
|
+
render_string('(($1 = $2); redo)', params_rb, new_value_exprs_rb)
|
189
|
+
else
|
190
|
+
'redo'
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
def emit_string(str)
|
195
|
+
"'" + escape_string(str) + "'"
|
196
|
+
end
|
197
|
+
|
198
|
+
def emit_symbol(sym)
|
199
|
+
':"' + sym.to_s + '"'
|
200
|
+
end
|
201
|
+
|
202
|
+
def emit_trap_error(form)
|
203
|
+
_, expr, handler = form
|
204
|
+
err_var = fresh_variable
|
205
|
+
|
206
|
+
expr_rb = emit_ruby(expr)
|
207
|
+
apply_handler_rb = emit_application([handler, err_var])
|
208
|
+
err_var_rb = emit_ruby(err_var)
|
209
|
+
|
210
|
+
render_string('(begin; $2; rescue => $1; $3; end)', err_var_rb,
|
211
|
+
expr_rb, apply_handler_rb)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Escape single quotes and backslashes
|
215
|
+
def escape_string(str)
|
216
|
+
new_str = ""
|
217
|
+
str.each_char do |c|
|
218
|
+
if c == "'"
|
219
|
+
new_str << "\\"
|
220
|
+
new_str << "'"
|
221
|
+
elsif c == '\\'
|
222
|
+
new_str << '\\'
|
223
|
+
new_str << '\\'
|
224
|
+
else
|
225
|
+
new_str << c
|
226
|
+
end
|
227
|
+
end
|
228
|
+
new_str
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Kl to Internal Represenation
|
4
|
+
#
|
5
|
+
# To simplify coding and improve performance, the compiler uses arrays
|
6
|
+
# rather than Kl lists to represent nested s-expressions. This stage
|
7
|
+
# performs the conversion.
|
8
|
+
module KlToInternalRepresentation
|
9
|
+
include Klam::Primitives::Lists
|
10
|
+
include Klam::Converters::List
|
11
|
+
|
12
|
+
def kl_to_internal_representation(kl)
|
13
|
+
if cons?(kl) || kl == Klam::Primitives::Lists::EMPTY_LIST
|
14
|
+
listToArray(kl)
|
15
|
+
else
|
16
|
+
kl
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Make Abstractions Monadic
|
4
|
+
#
|
5
|
+
# By the time the Ruby code is emitted, we want abstractions to take
|
6
|
+
# no more than one parameter. This simplifies code generation and
|
7
|
+
# avoids the need for using Proc#curry, which is slow.
|
8
|
+
#
|
9
|
+
# This stage converts (lambda [X Y] form) to (lambda [X] (lambda [Y] form))
|
10
|
+
module MakeAbstractionsMonadic
|
11
|
+
def make_abstractions_monadic(sexp)
|
12
|
+
if sexp.instance_of?(Array)
|
13
|
+
if sexp[0] == :lambda && sexp[1].length > 1
|
14
|
+
params = sexp[1]
|
15
|
+
[:lambda, [params[0]],
|
16
|
+
make_abstractions_monadic([:lambda, params[1..-1], sexp[2]])]
|
17
|
+
else
|
18
|
+
sexp.map { |form| make_abstractions_monadic(form) }
|
19
|
+
end
|
20
|
+
else
|
21
|
+
sexp
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Make Abstractions Variadic
|
4
|
+
#
|
5
|
+
# Kl's lambda special form only accepts a single parameter for the
|
6
|
+
# abstraction. It is useful internally, however, to allow abstractions to
|
7
|
+
# have zero or more parameters.
|
8
|
+
module MakeAbstractionsVariadic
|
9
|
+
def make_abstractions_variadic(sexp)
|
10
|
+
if sexp.instance_of?(Array)
|
11
|
+
if sexp[0] == :lambda
|
12
|
+
rator, param, form = sexp
|
13
|
+
[rator, [param], make_abstractions_variadic(form)]
|
14
|
+
else
|
15
|
+
sexp.map { |form| make_abstractions_variadic(form) }
|
16
|
+
end
|
17
|
+
else
|
18
|
+
sexp
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Simplify Boolean Operations
|
4
|
+
#
|
5
|
+
# Kl defines four special forms for boolean operations: if, or, and, and
|
6
|
+
# cond. Having if or cond alone is sufficient for implementing the complete
|
7
|
+
# set. This compilation stages recasts or, and, and cond in terms of if.
|
8
|
+
module SimplifyBooleanOperations
|
9
|
+
def simplify_boolean_operations(sexp)
|
10
|
+
if sexp.instance_of?(Array)
|
11
|
+
case sexp[0]
|
12
|
+
when :and
|
13
|
+
simplify_and(sexp)
|
14
|
+
when :cond
|
15
|
+
simplify_cond(sexp)
|
16
|
+
when :or
|
17
|
+
simplify_or(sexp)
|
18
|
+
else
|
19
|
+
sexp.map { |form| simplify_boolean_operations(form) }
|
20
|
+
end
|
21
|
+
else
|
22
|
+
sexp
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def simplify_and(sexp)
|
29
|
+
# Only convert the fully-applied case
|
30
|
+
if sexp.length == 3
|
31
|
+
_, expr1, expr2 = sexp
|
32
|
+
expr1 = simplify_boolean_operations(expr1)
|
33
|
+
expr2 = simplify_boolean_operations(expr2)
|
34
|
+
|
35
|
+
[:if, expr1, expr2, false]
|
36
|
+
else
|
37
|
+
sexp.map { |form| simplify_boolean_operations(form) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def simplify_cond(sexp)
|
42
|
+
# Cond expressions are of the form:
|
43
|
+
# (cond (Test1 Expr1) ... (TestN ExprN))
|
44
|
+
clauses = sexp[1..-1]
|
45
|
+
simplify_cond_clauses(clauses)
|
46
|
+
end
|
47
|
+
|
48
|
+
def simplify_cond_clauses(clauses)
|
49
|
+
if clauses.empty?
|
50
|
+
# An error is raised if none of the clauses match.
|
51
|
+
[:"simple-error", 'cond failure']
|
52
|
+
else
|
53
|
+
test, expr = clauses[0]
|
54
|
+
test = simplify_boolean_operations(test)
|
55
|
+
expr = simplify_boolean_operations(expr)
|
56
|
+
[:if, test, expr, simplify_cond_clauses(clauses[1..-1])]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def simplify_or(sexp)
|
61
|
+
# Only convert the fully-applied case
|
62
|
+
if sexp.length == 3
|
63
|
+
_, expr1, expr2 = sexp
|
64
|
+
expr1 = simplify_boolean_operations(expr1)
|
65
|
+
expr2 = simplify_boolean_operations(expr2)
|
66
|
+
|
67
|
+
[:if, expr1, true, expr2]
|
68
|
+
else
|
69
|
+
sexp.map { |form| simplify_boolean_operations(form) }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Strip Type Declarations
|
4
|
+
#
|
5
|
+
# The Kl type primitive is provided to pass type hints from the Shen
|
6
|
+
# compiler to the Kl compiler. Klam does not make use of them, so they are
|
7
|
+
# removed in this stage to simplify the s-expression and avoid having to
|
8
|
+
# take the type primitive into account when optimizing self tail calls.
|
9
|
+
module StripTypeDeclarations
|
10
|
+
def strip_type_declarations(sexp)
|
11
|
+
if sexp.instance_of?(Array)
|
12
|
+
if sexp[0] == :type
|
13
|
+
_, form, _ = sexp
|
14
|
+
strip_type_declarations(form)
|
15
|
+
else
|
16
|
+
sexp.map { |form| strip_type_declarations(form) }
|
17
|
+
end
|
18
|
+
else
|
19
|
+
sexp
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Klam
|
2
|
+
class Compiler
|
3
|
+
# Compiliation is implmented as a series of stages, each defined in
|
4
|
+
# its own module. The first stage converts Kl s-expressions to the
|
5
|
+
# the compiler's internal represenation. The final stage converts
|
6
|
+
# the internal representation of a string containing Ruby code
|
7
|
+
# suitable for use with instance_eval in the context of a
|
8
|
+
# Klam::Environment instance.
|
9
|
+
|
10
|
+
include Klam::CompilationStages::KlToInternalRepresentation
|
11
|
+
include Klam::CompilationStages::StripTypeDeclarations
|
12
|
+
include Klam::CompilationStages::MakeAbstractionsVariadic
|
13
|
+
include Klam::CompilationStages::ConvertLexicalVariables
|
14
|
+
include Klam::CompilationStages::ConvertFreezesToLambdas
|
15
|
+
include Klam::CompilationStages::SimplifyBooleanOperations
|
16
|
+
include Klam::CompilationStages::ConvertPartialApplicationsToLambdas
|
17
|
+
include Klam::CompilationStages::CurryAbstractionApplications
|
18
|
+
include Klam::CompilationStages::MakeAbstractionsMonadic
|
19
|
+
include Klam::CompilationStages::ConvertSelfTailCallsToLoops
|
20
|
+
include Klam::CompilationStages::EmitRuby
|
21
|
+
|
22
|
+
def initialize(environment)
|
23
|
+
@environment = environment
|
24
|
+
@generator = Klam::VariableGenerator.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def compile(kl)
|
28
|
+
stages = [
|
29
|
+
:kl_to_internal_representation,
|
30
|
+
:strip_type_declarations,
|
31
|
+
:make_abstractions_variadic,
|
32
|
+
:convert_lexical_variables,
|
33
|
+
:convert_freezes_to_lambdas,
|
34
|
+
:simplify_boolean_operations,
|
35
|
+
:convert_partial_applications_to_lambdas,
|
36
|
+
:curry_abstraction_applications,
|
37
|
+
:make_abstractions_monadic,
|
38
|
+
:convert_self_tail_calls_to_loops,
|
39
|
+
:emit_ruby
|
40
|
+
]
|
41
|
+
apply_stages(stages, kl)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def apply_stages(stages, kl)
|
47
|
+
stages.reduce(kl) do |exp, stage|
|
48
|
+
send(stage, exp)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def arity(sym)
|
53
|
+
@environment.__arity(sym)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns a new Klam::Variable object that is unique within this
|
57
|
+
# instance of the compiler. Variables are never used in a global
|
58
|
+
# context in Kl, so this is sufficient to avoid collisions.
|
59
|
+
def fresh_variable
|
60
|
+
@generator.next
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|