klam 0.0.1
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 +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
|