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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d6bfb59d34a33d476f312e82821a8daa9c23a766
|
4
|
+
data.tar.gz: 1e17a5d167569095f128e551504e48ccfc5c3f84
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2adf161608625ab5b3a7c7d38cb406df8152d98a55b85fa7d77f338c543f7c21a98df1923d507b52e863a2aeb47f3672dce37b18e3c28cc6226e1664b036b97
|
7
|
+
data.tar.gz: 2949ef6dcb1086f138bdd8059cf8419de98eca82c2054754244050511147c6d1119bda3969397e0b436b8b1a49dde962a470d1d5f299cb1e146b7cdcba9e46cd
|
data/.gitignore
ADDED
data/.rspec
ADDED
File without changes
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
gemspec
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2014 Greg Spurrier
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/PROGRESS.asciidoc
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
Klam Implementation Progress
|
2
|
+
============================
|
3
|
+
|
4
|
+
This document tracks the progress of Klam towards a feature-complete 1.0.0
|
5
|
+
release.
|
6
|
+
|
7
|
+
Implemented
|
8
|
+
-----------
|
9
|
+
|
10
|
+
Language Features
|
11
|
+
~~~~~~~~~~~~~~~~~
|
12
|
+
* Reader
|
13
|
+
* Environment
|
14
|
+
** Global assignment namespace
|
15
|
+
** Global function namespace
|
16
|
+
* Code Generation
|
17
|
+
** Atoms
|
18
|
+
** Abstractions
|
19
|
+
** Functions
|
20
|
+
** Special forms (see marked primimitives below)
|
21
|
+
** Application
|
22
|
+
*** Functions and abstractions:
|
23
|
+
**** Full application
|
24
|
+
**** Partial application
|
25
|
+
**** Currying
|
26
|
+
** Self tail-call optimization
|
27
|
+
|
28
|
+
Primitives
|
29
|
+
~~~~~~~~~~
|
30
|
+
As defined in
|
31
|
+
http://www.shenlanguage.org/learn-shen/shendoc.htm#The%20Primitive%20Functions%20of%20K%20Lambda[The
|
32
|
+
Primitive Functions of Kl]:
|
33
|
+
|
34
|
+
* Boolean Operations
|
35
|
+
** +if+ (special form and normal function)
|
36
|
+
** +and+ (special form and normal function)
|
37
|
+
** +or+ (special form and normal function)
|
38
|
+
** +cond+ (special form)
|
39
|
+
* Symbols
|
40
|
+
** +intern+
|
41
|
+
* Strings
|
42
|
+
** +pos+
|
43
|
+
** +tlstr+
|
44
|
+
** +cn+
|
45
|
+
** +str+
|
46
|
+
** +string?+
|
47
|
+
** +n\->string+
|
48
|
+
** +string\->n+
|
49
|
+
* Assignments
|
50
|
+
** +set+
|
51
|
+
** +value+
|
52
|
+
* Lists
|
53
|
+
** +cons+
|
54
|
+
** +hd+
|
55
|
+
** +tl+
|
56
|
+
** +cons?+
|
57
|
+
* Error Handling
|
58
|
+
** +simple-error+
|
59
|
+
** +trap-error+ (special form)
|
60
|
+
** +error-to-string+
|
61
|
+
* Generic Functions
|
62
|
+
** +defun+ (special form)
|
63
|
+
** +lambda+ (special form)
|
64
|
+
** +let+ (special form)
|
65
|
+
** +=+
|
66
|
+
** +eval-kl+
|
67
|
+
** +freeze+ (special form)
|
68
|
+
** +type+ (special form)
|
69
|
+
* Vectors
|
70
|
+
** +absvector+
|
71
|
+
** +address\->+
|
72
|
+
** +\<-address+
|
73
|
+
** +absvector?+
|
74
|
+
* Streams and I/O
|
75
|
+
** +write-byte+
|
76
|
+
** +read-byte+
|
77
|
+
** +open+
|
78
|
+
** +close+
|
79
|
+
* Time
|
80
|
+
** +get-time+
|
81
|
+
* Arithmetic
|
82
|
+
** +++
|
83
|
+
** +-+
|
84
|
+
** +*+
|
85
|
+
** +/+
|
86
|
+
** +>+
|
87
|
+
** +<+
|
88
|
+
** +>=+
|
89
|
+
** +\<=+
|
90
|
+
** +number?+
|
91
|
+
|
92
|
+
Ruby Interoperation
|
93
|
+
~~~~~~~~~~~~~~~~~~~
|
94
|
+
* Ruby \<\-> Kl converters
|
95
|
+
** Array \<\-> List
|
96
|
+
|
97
|
+
Not Yet Implemented
|
98
|
+
-------------------
|
99
|
+
|
100
|
+
Ruby Interoperation
|
101
|
+
~~~~~~~~~~~~~~~~~~~
|
102
|
+
* Invoking Kl functions from Ruby
|
103
|
+
* Invoking Ruby functions from Kl
|
104
|
+
* Ruby \<\-> Kl converters
|
105
|
+
** Array \<\-> Absvector
|
data/README.asciidoc
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Klam
|
2
|
+
====
|
3
|
+
|
4
|
+
Klam is a Ruby implementation of Kl, the small Lisp on top of which the
|
5
|
+
http://www.shenlanguage.org[Shen] programming language is implemented.
|
6
|
+
|
7
|
+
image:https://travis-ci.org/gregspurrier/klam.png?branch=master["Build Status", link="https://travis-ci.org/gregspurrier/klam"]
|
8
|
+
|
9
|
+
License
|
10
|
+
-------
|
11
|
+
Klam is Copyright (C) 2014-2015 Greg Spurrier. It is distributed under the terms of the MIT License. See link:LICENSE.txt[] for the details.
|
data/Rakefile
ADDED
data/klam.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
lib = File.expand_path('../lib/', __FILE__)
|
2
|
+
$:.unshift lib unless $:.include?(lib)
|
3
|
+
|
4
|
+
require 'klam/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = 'klam'
|
8
|
+
s.version = Klam::VERSION
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.license = 'MIT'
|
11
|
+
s.authors = ['Greg Spurrier']
|
12
|
+
s.email = ['greg@sourcematters.org']
|
13
|
+
s.homepage = 'https://github.com/gregspurrier/klam'
|
14
|
+
s.summary = %q{Klam is a Ruby implementation of the Kl.}
|
15
|
+
s.description = %q{Klam is a Ruby implementation of Kl, the small Lisp on top of which the Shen programming language is implemented.}
|
16
|
+
|
17
|
+
s.required_ruby_version = '>= 1.9.3'
|
18
|
+
|
19
|
+
s.add_development_dependency 'rake', '~> 10.4', '>= 10.4'
|
20
|
+
s.add_development_dependency 'rspec', '~> 3.1', '>= 3.1.0'
|
21
|
+
s.add_development_dependency 'rspec-autotest', '~> 1.0', '>= 1.0.0'
|
22
|
+
s.add_development_dependency 'ZenTest', '~> 4.11', '>= 4.11'
|
23
|
+
|
24
|
+
git_files = `git ls-files`.split("\n") rescue ''
|
25
|
+
s.files = git_files
|
26
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
27
|
+
s.require_paths = ['lib']
|
28
|
+
end
|
data/lib/klam.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
module Klam
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'klam/version'
|
5
|
+
require 'klam/error'
|
6
|
+
require 'klam/template'
|
7
|
+
require 'klam/variable'
|
8
|
+
require 'klam/variable_generator'
|
9
|
+
require 'klam/cons'
|
10
|
+
require 'klam/primitives'
|
11
|
+
require 'klam/converters'
|
12
|
+
require 'klam/compilation_stages'
|
13
|
+
require 'klam/compiler'
|
14
|
+
require 'klam/environment'
|
15
|
+
require 'klam/lexer'
|
16
|
+
require 'klam/reader'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
module ConvertFreezesToLambdas
|
4
|
+
def convert_freezes_to_lambdas(sexp)
|
5
|
+
if sexp.instance_of?(Array)
|
6
|
+
if sexp[0] == :freeze
|
7
|
+
[:lambda, [], convert_freezes_to_lambdas(sexp[1])]
|
8
|
+
else
|
9
|
+
sexp.map { |form| convert_freezes_to_lambdas(form) }
|
10
|
+
end
|
11
|
+
else
|
12
|
+
sexp
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Convert Lexical Variables
|
4
|
+
#
|
5
|
+
# Lexically bound variable names are symbols in Kl. In Ruby, variable and
|
6
|
+
# parameter names are represented differently than symbol literals. The
|
7
|
+
# latter are indicated with a leading ':' (e.g., :foo) and allow an
|
8
|
+
# extended set of characters when using the :"foo-bar" literal syntax.
|
9
|
+
# Ruby variables have no such provision for using additional characters.
|
10
|
+
#
|
11
|
+
# Therefore, if lexical variables remain as symbols, the emission of Ruby
|
12
|
+
# code for symbols would be complicated by the need to differentiate
|
13
|
+
# between symbol literals and lexical variables and to transform Kl
|
14
|
+
# variable names to legal Ruby variable names. Instead, this stage converts
|
15
|
+
# lexical variables to instances of Klam::Variable. This is essentially
|
16
|
+
# alpha conversion and the names of Klam::Variable are generated to be
|
17
|
+
# locally unique and using only allowed Ruby variable names.
|
18
|
+
#
|
19
|
+
# Alpha conversion also avoids potential problems when the let primitive is
|
20
|
+
# used to re-bind an already bound lexical var.
|
21
|
+
module ConvertLexicalVariables
|
22
|
+
def convert_lexical_variables(sexp)
|
23
|
+
convert_lexical_vars(sexp, {})
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def convert_lexical_vars(sexp, var_map)
|
29
|
+
if sexp.instance_of? Array
|
30
|
+
case sexp[0]
|
31
|
+
when :defun
|
32
|
+
convert_lexical_vars_defun(sexp, var_map)
|
33
|
+
when :lambda
|
34
|
+
convert_lexical_vars_lambda(sexp, var_map)
|
35
|
+
when :let
|
36
|
+
convert_lexical_vars_let(sexp, var_map)
|
37
|
+
else
|
38
|
+
sexp.map { |form| convert_lexical_vars(form, var_map) }
|
39
|
+
end
|
40
|
+
else
|
41
|
+
var_map[sexp] || sexp
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def convert_lexical_vars_defun(sexp, var_map)
|
46
|
+
rator, name, params, expr = sexp
|
47
|
+
var_map = extend_var_map(var_map, params)
|
48
|
+
|
49
|
+
params = params.map { |p| var_map[p] }
|
50
|
+
expr = convert_lexical_vars(expr, var_map)
|
51
|
+
[rator, name, params, expr]
|
52
|
+
end
|
53
|
+
|
54
|
+
def convert_lexical_vars_lambda(sexp, var_map)
|
55
|
+
rator, params, expr = sexp
|
56
|
+
var_map = extend_var_map(var_map, params)
|
57
|
+
|
58
|
+
params = params.map { |p| var_map[p] }
|
59
|
+
expr = convert_lexical_vars(expr, var_map)
|
60
|
+
[rator, params, expr]
|
61
|
+
end
|
62
|
+
|
63
|
+
def convert_lexical_vars_let(sexp, var_map)
|
64
|
+
rator, var_sym, value_expr, expr = sexp
|
65
|
+
extended_var_map = extend_var_map(var_map, [var_sym])
|
66
|
+
|
67
|
+
value_expr = convert_lexical_vars(value_expr, var_map)
|
68
|
+
expr = convert_lexical_vars(expr, extended_var_map)
|
69
|
+
[rator, extended_var_map[var_sym], value_expr, expr]
|
70
|
+
end
|
71
|
+
|
72
|
+
def extend_var_map(var_map, syms)
|
73
|
+
new_var_map = Hash.new { |_, k| var_map[k] }
|
74
|
+
syms.each { |sym| new_var_map[sym] = fresh_variable }
|
75
|
+
new_var_map
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
module ConvertPartialApplicationsToLambdas
|
4
|
+
def convert_partial_applications_to_lambdas(sexp)
|
5
|
+
if sexp.instance_of?(Array)
|
6
|
+
rator = sexp[0]
|
7
|
+
if rator.kind_of?(Symbol)
|
8
|
+
rands = sexp[1..-1]
|
9
|
+
converted_rands = rands.map do |rand|
|
10
|
+
convert_partial_applications_to_lambdas(rand)
|
11
|
+
end
|
12
|
+
|
13
|
+
rator_arity = arity(rator)
|
14
|
+
if rator_arity == -1 || rator_arity == rands.length
|
15
|
+
converted_rands.unshift(rator)
|
16
|
+
elsif rator_arity > rands.length
|
17
|
+
# Partial application
|
18
|
+
vars = (rator_arity - rands.length).times.map { fresh_variable }
|
19
|
+
[:lambda, vars, [rator] + converted_rands + vars]
|
20
|
+
else
|
21
|
+
# Uncurrying
|
22
|
+
now_rands = rands[0...rator_arity]
|
23
|
+
deferred_rands = rands[rator_arity..-1]
|
24
|
+
[[rator] + now_rands, *deferred_rands]
|
25
|
+
end
|
26
|
+
else
|
27
|
+
sexp.map { |form| convert_partial_applications_to_lambdas(form) }
|
28
|
+
end
|
29
|
+
else
|
30
|
+
sexp
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module Klam
|
2
|
+
module CompilationStages
|
3
|
+
# Convert Self Tail Calls To Loops
|
4
|
+
#
|
5
|
+
# The Shen specification requires that self tail calls be optimized. This
|
6
|
+
# stage transforms defuns containing self tail calls into a loop/recur form
|
7
|
+
# akin to Clojure's loop/recur. The code emitter knows how to handle these
|
8
|
+
# synthetic special forms.
|
9
|
+
#
|
10
|
+
# For example, this code with tail calls:
|
11
|
+
#
|
12
|
+
# (defun fact-iter (N Accum)
|
13
|
+
# (if (= N 0)
|
14
|
+
# Accum
|
15
|
+
# (fact-iter (- N 1) (* N Accum))))
|
16
|
+
#
|
17
|
+
# is converted to:
|
18
|
+
#
|
19
|
+
# (defun fact-iter (N Accum)
|
20
|
+
# ([LOOP]
|
21
|
+
# (if (= N 0)
|
22
|
+
# Accum
|
23
|
+
# ([RECUR] (N Accum) ((- N 1) (* N Accum))))))
|
24
|
+
#
|
25
|
+
# The variable names are carried along with their new binding expressions
|
26
|
+
# in the [RECUR] form so that they can be rebound before looping.
|
27
|
+
#
|
28
|
+
# Note that this rebinding of variables causes a wrinkle with respect to
|
29
|
+
# closures created in the body. Those closures should close over the value
|
30
|
+
# at the point of closing rather than the ultimate values after rebinding.
|
31
|
+
# To solve this, another sythetic primitive, [FIX-VARS], is used to wrap
|
32
|
+
# these cases and the emitted Ruby code samples those variables.
|
33
|
+
#
|
34
|
+
# This compilation stage must come _after_ SimplifyBooleanOperations and
|
35
|
+
# ConvertFreezesToLambdas.
|
36
|
+
module ConvertSelfTailCallsToLoops
|
37
|
+
def convert_self_tail_calls_to_loops(sexp)
|
38
|
+
# Self tail calls can only be found in functions defined by defun.
|
39
|
+
# defun is only allowed at the top level, so there's no need to
|
40
|
+
# walk the tree if this form is not a defun.
|
41
|
+
if sexp.kind_of?(Array) && sexp[0] == :defun
|
42
|
+
_, name, params, body = sexp
|
43
|
+
if contains_self_tail_calls?(body, name)
|
44
|
+
insert_loop_and_recur_into_defun(fix_vars(sexp, params))
|
45
|
+
else
|
46
|
+
sexp
|
47
|
+
end
|
48
|
+
else
|
49
|
+
sexp
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def contains_self_tail_calls?(body, name)
|
56
|
+
if body.instance_of?(Array)
|
57
|
+
case body[0]
|
58
|
+
when name
|
59
|
+
true
|
60
|
+
when :if
|
61
|
+
_, _, true_expr, false_expr = body
|
62
|
+
contains_self_tail_calls?(true_expr, name) ||
|
63
|
+
contains_self_tail_calls?(false_expr, name)
|
64
|
+
when :let
|
65
|
+
contains_self_tail_calls?(body[3], name)
|
66
|
+
when :do
|
67
|
+
contains_self_tail_calls?(body[2], name)
|
68
|
+
else
|
69
|
+
false
|
70
|
+
end
|
71
|
+
else
|
72
|
+
false
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def insert_loop_and_recur_into_defun(form)
|
77
|
+
rator, name, params, body = form
|
78
|
+
body_with_loop = [:"[LOOP]", name, params,
|
79
|
+
insert_recur_into_expr(body, name, params)]
|
80
|
+
[rator, name, params, body_with_loop]
|
81
|
+
end
|
82
|
+
|
83
|
+
def insert_recur_into_expr(sexp, name, params)
|
84
|
+
if sexp.instance_of?(Array)
|
85
|
+
case sexp[0]
|
86
|
+
when name
|
87
|
+
if sexp.length - 1 == params.length
|
88
|
+
[:"[RECUR]", params, sexp[1..-1]]
|
89
|
+
else
|
90
|
+
sexp
|
91
|
+
end
|
92
|
+
when :if
|
93
|
+
rator, test_expr, true_expr, false_expr = sexp
|
94
|
+
[rator,
|
95
|
+
test_expr,
|
96
|
+
insert_recur_into_expr(true_expr, name, params),
|
97
|
+
insert_recur_into_expr(false_expr, name, params)]
|
98
|
+
when :let
|
99
|
+
rator, var, val_expr, body_expr = sexp
|
100
|
+
[rator,
|
101
|
+
var,
|
102
|
+
val_expr,
|
103
|
+
insert_recur_into_expr(body_expr, name, params)]
|
104
|
+
when :do
|
105
|
+
rator, first_expr, second_expr = sexp
|
106
|
+
[rator, first_expr, insert_recur_into_expr(second_expr, name, params)]
|
107
|
+
else
|
108
|
+
sexp
|
109
|
+
end
|
110
|
+
else
|
111
|
+
sexp
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def fix_vars(sexp, params)
|
116
|
+
if sexp.kind_of?(Array)
|
117
|
+
if sexp[0] == :lambda
|
118
|
+
referenced_vars = vars_referenced_in(sexp, params)
|
119
|
+
if referenced_vars.empty?
|
120
|
+
sexp
|
121
|
+
else
|
122
|
+
[:"[FIX-VARS]", referenced_vars, sexp]
|
123
|
+
end
|
124
|
+
else
|
125
|
+
# Local variables must also be fixed
|
126
|
+
if sexp[0] == :let
|
127
|
+
params = params + [sexp[1]]
|
128
|
+
end
|
129
|
+
sexp.map { |x| fix_vars(x, params) }
|
130
|
+
end
|
131
|
+
else
|
132
|
+
sexp
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def vars_referenced_in(sexp, vars)
|
137
|
+
if sexp.kind_of?(Array)
|
138
|
+
sexp.map { |x| vars_referenced_in(x, vars) }.flatten.uniq
|
139
|
+
elsif vars.include?(sexp)
|
140
|
+
[sexp]
|
141
|
+
else
|
142
|
+
[]
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|