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
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'extension: (do X Y)', :type => :functional do
|
4
|
+
it 'evaluates X before Y' do
|
5
|
+
eval_kl('(set foo "a")')
|
6
|
+
eval_kl('(do (set foo (cn (value foo) "b")) (set foo (cn (value foo) "c")))')
|
7
|
+
expect_kl('(value foo)').to eq("abc")
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns the normal form of Y' do
|
11
|
+
expect_kl('(do 37 (+ 40 2))').to eq(42)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'treats Y as being in tail position' do
|
15
|
+
eval_kl('(defun count-down (X) (if (> X 0) (do ignore-me (count-down (- X 1))) true))')
|
16
|
+
expect_kl('(count-down 20000)').to be(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'allows nesting calls' do
|
20
|
+
expect_kl('(do (+ 1 0) (do (+ 1 1) (+ 1 2)))').to eq(3)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Assignments primitives', :type => :functional do
|
4
|
+
describe '(set Name Value)' do
|
5
|
+
it 'associates Value with Name' do
|
6
|
+
eval_kl('(set foo bar)')
|
7
|
+
expect_kl('(value foo)').to eq(:bar)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'returns Value' do
|
11
|
+
expect_kl('(set foo bar)').to eq(:bar)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'overwrites the previous value when called again with same Name' do
|
15
|
+
eval_kl('(set foo bar)')
|
16
|
+
expect_kl('(set foo baz)').to eq(:baz)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'does not interfere with the function namespace' do
|
20
|
+
eval_kl('(set value bar)')
|
21
|
+
expect_kl('(value value)').to eq(:bar)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '(value Name)' do
|
26
|
+
# Duplicated for documentation purposes
|
27
|
+
it 'returns the value associated with Name' do
|
28
|
+
eval_kl('(set foo bar)')
|
29
|
+
expect_kl('(value foo)').to eq(:bar)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'raises an error if Name has not previously been set' do
|
33
|
+
expect {
|
34
|
+
eval_kl('(value an-unset-symobl)')
|
35
|
+
}.to raise_error(Klam::Error)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Boolean operation primitives', :type => :functional do
|
4
|
+
describe '(and Expr1 Expr2)' do
|
5
|
+
describe 'when Expr1 evaluates to true' do
|
6
|
+
it 'returns the result of evaluating Expr2' do
|
7
|
+
expect_kl('(and (< 1 2) (> 3 3))').to be(false)
|
8
|
+
expect_kl('(and (< 1 2) (>= 3 3))').to be(true)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'when Expr1 evaluates to false' do
|
13
|
+
it 'returns false' do
|
14
|
+
expect_kl('(and (> 1 2) true)').to be(false)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'does not evaluate Expr2' do
|
18
|
+
eval_kl('(set success true)')
|
19
|
+
eval_kl('(and (> 1 2) (set success false))')
|
20
|
+
expect_kl('(value success)').to be(true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'is available for partial application' do
|
25
|
+
expect_kl('((and true) true)').to be(true)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '(cond Test1 Expr1 ... TestN ExprN)' do
|
30
|
+
it 'returns the normal form of the Expr for the first true Test' do
|
31
|
+
kl = <<-EOKL
|
32
|
+
(cond
|
33
|
+
((< 3 3) 1)
|
34
|
+
((= 3 3) 2)
|
35
|
+
((= 1 1) 3))
|
36
|
+
EOKL
|
37
|
+
|
38
|
+
expect_kl(kl).to eq(2)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not evaluate the other Exprs' do
|
42
|
+
eval_kl('(set expr1-was-evaluated false)')
|
43
|
+
eval_kl('(set expr2-was-evaluated false)')
|
44
|
+
eval_kl('(set expr3-was-evaluated false)')
|
45
|
+
|
46
|
+
kl = <<-EOKL
|
47
|
+
(cond
|
48
|
+
((< 3 3) (set expr1-was-evaluated true))
|
49
|
+
((<= 3 3) (set expr2-was-evaluated true))
|
50
|
+
((<= 1 1) (set expr3-was-evaluated true)))
|
51
|
+
EOKL
|
52
|
+
eval_kl(kl)
|
53
|
+
|
54
|
+
expect_kl('(value expr1-was-evaluated)').to be(false)
|
55
|
+
expect_kl('(value expr2-was-evaluated)').to be(true)
|
56
|
+
expect_kl('(value expr3-was-evaluated)').to be(false)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'does not evaluate the subsequent tests' do
|
60
|
+
eval_kl('(set test3-was-evaluated false)')
|
61
|
+
|
62
|
+
kl = <<-EOKL
|
63
|
+
(cond
|
64
|
+
((< 3 3) 1)
|
65
|
+
((<= 3 3) 2)
|
66
|
+
((set test3-was-evaluated true) 3))
|
67
|
+
EOKL
|
68
|
+
eval_kl(kl)
|
69
|
+
|
70
|
+
expect_kl('(value test3-was-evaluated)').to be(false)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'raises an error if none of the tests evaluate to true' do
|
74
|
+
expect_kl('(trap-error (cond (false false)) error-to-string)')
|
75
|
+
.to eq('cond failure')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe '(if Test TrueExpr FalseExpr)' do
|
80
|
+
describe 'when Test evaluates to true' do
|
81
|
+
it 'returns the value of TrueExpr' do
|
82
|
+
expect_kl('(if (< 1 2) yes no)').to eq(:yes)
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'does not evaluate FalseExpr' do
|
86
|
+
eval_kl('(set success true)')
|
87
|
+
eval_kl('(if (< 1 2) yes (set success false))')
|
88
|
+
expect_kl('(value success)').to be(true)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe 'when Test evaluates to false' do
|
93
|
+
it 'returns the value of FalseExpr' do
|
94
|
+
expect_kl('(if (> 1 2) yes no)').to eq(:no)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'does not evaluate TrueExpr' do
|
98
|
+
eval_kl('(set success true)')
|
99
|
+
eval_kl('(if (> 1 2) (set success false) no)')
|
100
|
+
expect_kl('(value success)').to be(true)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'is available for partial application' do
|
105
|
+
expect_kl('(((if false) 1) 37)').to eq(37)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '(or Expr1 Expr2)' do
|
110
|
+
describe 'when Expr1 evaluates to true' do
|
111
|
+
it 'returns true' do
|
112
|
+
expect_kl('(or (< 1 2) false)').to be(true)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'does not evaluate Expr2' do
|
116
|
+
eval_kl('(set success true)')
|
117
|
+
eval_kl('(or (< 1 2) (set success false))')
|
118
|
+
expect_kl('(value success)').to be(true)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'when Expr1 evaluates to false' do
|
123
|
+
it 'returns the result of evaluating Expr2' do
|
124
|
+
expect_kl('(or (> 1 2) (> 3 3))').to be(false)
|
125
|
+
expect_kl('(or (> 1 2) (>= 3 3))').to be(true)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'is available for partial application' do
|
130
|
+
expect_kl('((or false) false)').to be(false)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Error handling primitives', :type => :functional do
|
4
|
+
describe '(trap-error Expr Handler)' do
|
5
|
+
describe 'when evaluating Expr succeeds' do
|
6
|
+
it 'returns the normal form of Expr' do
|
7
|
+
expect_kl('(trap-error (+ 1 2) error-to-string)').to eq(3)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'when evaluating Expr raises an error' do
|
12
|
+
it 'applies Handler to the error' do
|
13
|
+
expect_kl('(trap-error (simple-error "oops!") error-to-string)')
|
14
|
+
.to eq('oops!')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'may be used as an argument to another function' do
|
19
|
+
expect_kl('(+ (trap-error 2 error-to-string) 3)').to eq(5)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Generic function primitives', :type => :functional do
|
4
|
+
describe '(defun Name Params Expr)' do
|
5
|
+
it 'returns Name' do
|
6
|
+
expect_kl('(defun foo (X) X)').to eq(:foo)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'does not evaluate Expr' do
|
10
|
+
eval_kl('(set success true)')
|
11
|
+
eval_kl('(defun foo (X) (set success false))')
|
12
|
+
expect_kl('(value success)').to be(true)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'installs Name as a global function' do
|
16
|
+
eval_kl('(defun foo (X) X)')
|
17
|
+
expect_kl('(foo 37)').to eq(37)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'allows names that are not allowed with the Ruby define syntax' do
|
21
|
+
eval_kl('(defun f-o-o (X) X)')
|
22
|
+
expect_kl('(f-o-o 37)').to eq(37)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'allows parameter names that are not valid Ruby parameter names' do
|
26
|
+
eval_kl('(defun foo (A!B?-C) A!B?-C)')
|
27
|
+
expect_kl('(foo 37)').to eq(37)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'allows params to be empty' do
|
31
|
+
eval_kl('(defun foo () 37)')
|
32
|
+
expect_kl('(foo)').to eq(37)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '(lambda Var Expr)' do
|
37
|
+
it 'returns a function' do
|
38
|
+
expect_kl('(lambda X X)').to be_kind_of(Proc)
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'does not evaluate Expr' do
|
42
|
+
eval_kl('(set success true)')
|
43
|
+
eval_kl('(lambda X (set success false))')
|
44
|
+
expect_kl('(value success)').to be(true)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '(let Var ValueExpr BodyExpr)' do
|
49
|
+
it 'evaluates BodyExpr with Var bound to the normal form of ValueExpr' do
|
50
|
+
expect_kl('(let X (+ 1 2) (* X 2))').to eq(6)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'uses the inner-most binding when Var is shadowed' do
|
54
|
+
expect_kl('(let X 1 (let X 2 X))').to eq(2)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '(freeze Expr)' do
|
59
|
+
it 'returns a function' do
|
60
|
+
expect_kl('(freeze (+ 30 7))').to be_kind_of(Proc)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'does not evaluate Expr' do
|
64
|
+
eval_kl('(set success true)')
|
65
|
+
eval_kl('(freeze (set success false))')
|
66
|
+
expect_kl('(value success)').to be(true)
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'when thawed' do
|
70
|
+
it 'evaluates the frozen Expr and returns the result' do
|
71
|
+
eval_kl('(defun thaw (Thunk) (Thunk))')
|
72
|
+
expect_kl('(thaw (freeze (+ 30 7)))').to eq(37)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '(type Expr Type)' do
|
78
|
+
it 'returns the normal form of Expr' do
|
79
|
+
expect_kl('(type (+ 1 2) expr)').to eq(3)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Tail call optimization', :type => :functional do
|
4
|
+
it 'optimizes self tail calls in an if true clause' do
|
5
|
+
eval_kl('(defun count-down (X) (if (> X 0) (count-down (- X 1)) true))')
|
6
|
+
expect_kl('(count-down 20000)').to be(true)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'optimizes self tail calls in an if false clause' do
|
10
|
+
eval_kl('(defun count-down (X) (if (<= X 0) true (count-down (- X 1))))')
|
11
|
+
expect_kl('(count-down 20000)').to be(true)
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'optimizes self tail calls in a let body' do
|
15
|
+
eval_kl('(defun count-down (X) (if (<= X 0) true (let F 1 (count-down (- X 1)))))')
|
16
|
+
expect_kl('(count-down 20000)').to be(true)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'uses the current values of the params when calculating the new ones' do
|
20
|
+
eval_kl <<-EOKL
|
21
|
+
(defun fact-iter (N Accum)
|
22
|
+
(if (= N 1)
|
23
|
+
Accum
|
24
|
+
(fact-iter (- N 1) (* N Accum))))
|
25
|
+
EOKL
|
26
|
+
expect_kl('(fact-iter 5 1)').to eq(120)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'preserves the value of closed-over function parameters' do
|
30
|
+
eval_kl('(set foo (absvector 3))')
|
31
|
+
eval_kl <<-EOKL
|
32
|
+
(defun do-it (N)
|
33
|
+
(if (= N 3)
|
34
|
+
true
|
35
|
+
(let _ (address-> (value foo) N (freeze N))
|
36
|
+
(do-it (+ N 1)))))
|
37
|
+
EOKL
|
38
|
+
eval_kl('(do-it 0)')
|
39
|
+
expect_kl('((<-address (value foo) 0))').to eq(0)
|
40
|
+
expect_kl('((<-address (value foo) 1))').to eq(1)
|
41
|
+
expect_kl('((<-address (value foo) 2))').to eq(2)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'preserves the value of closed-over local variables' do
|
45
|
+
eval_kl('(set foo (absvector 3))')
|
46
|
+
eval_kl <<-EOKL
|
47
|
+
(defun do-it (N)
|
48
|
+
(if (= N 3)
|
49
|
+
true
|
50
|
+
(let X N
|
51
|
+
(let _ (address-> (value foo) N (freeze X))
|
52
|
+
(do-it (+ N 1))))))
|
53
|
+
EOKL
|
54
|
+
eval_kl('(do-it 0)')
|
55
|
+
expect_kl('((<-address (value foo) 0))').to eq(0)
|
56
|
+
expect_kl('((<-address (value foo) 1))').to eq(1)
|
57
|
+
expect_kl('((<-address (value foo) 2))').to eq(2)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'supports functions with zero arguments' do
|
61
|
+
eval_kl('(set counter 0)')
|
62
|
+
eval_kl <<-EOKL
|
63
|
+
(defun count-down ()
|
64
|
+
(if (= 20000 (set counter (+ (value counter) 1)))
|
65
|
+
true
|
66
|
+
(count-down)))
|
67
|
+
EOKL
|
68
|
+
expect_kl('(count-down)').to be(true)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'klam'
|
3
|
+
|
4
|
+
module EvalKl
|
5
|
+
def read_kl(str)
|
6
|
+
stream = StringIO.new(str)
|
7
|
+
reader = Klam::Reader.new(stream)
|
8
|
+
reader.next
|
9
|
+
end
|
10
|
+
|
11
|
+
def eval_kl(str)
|
12
|
+
form = read_kl(str)
|
13
|
+
@env.__send__(:"eval-kl", form)
|
14
|
+
end
|
15
|
+
|
16
|
+
def expect_kl(str)
|
17
|
+
expect(eval_kl(str))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
RSpec.configure do |cfg|
|
22
|
+
# Support eval_kl and friends in functional specs
|
23
|
+
cfg.include EvalKl, :type => :functional
|
24
|
+
cfg.before(:each, :type => :functional) do
|
25
|
+
@env = Klam::Environment.new
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Klam::CompilationStages::ConvertLexicalVariables do
|
4
|
+
include Klam::CompilationStages::ConvertLexicalVariables
|
5
|
+
|
6
|
+
# ConvertLexicalVariables requires this function to be defined
|
7
|
+
# by its including class.
|
8
|
+
def fresh_variable
|
9
|
+
@generator.next
|
10
|
+
end
|
11
|
+
|
12
|
+
def reset_generator
|
13
|
+
@generator = Klam::VariableGenerator.new
|
14
|
+
end
|
15
|
+
|
16
|
+
before(:each) do
|
17
|
+
reset_generator
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'with defun' do
|
21
|
+
it 'converts formal param symbols to variables' do
|
22
|
+
sexp = [:defun, :foo, [:X, :Y], [:+, :X, :Y]]
|
23
|
+
converted = convert_lexical_variables(sexp)
|
24
|
+
|
25
|
+
reset_generator
|
26
|
+
v1, v2 = fresh_variable, fresh_variable
|
27
|
+
expected = [:defun, :foo, [v1, v2], [:+, v1, v2]]
|
28
|
+
|
29
|
+
expect(converted).to eq(expected)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe 'with lambda' do
|
34
|
+
it 'converts formal param symbols to variables' do
|
35
|
+
sexp = [:lambda, [:X, :Y], [:+, :X, :Y]]
|
36
|
+
converted = convert_lexical_variables(sexp)
|
37
|
+
|
38
|
+
reset_generator
|
39
|
+
v1, v2 = fresh_variable, fresh_variable
|
40
|
+
expected = [:lambda, [v1, v2], [:+, v1, v2]]
|
41
|
+
|
42
|
+
expect(converted).to eq(expected)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe 'with let' do
|
47
|
+
it 'converts var param to a variable' do
|
48
|
+
sexp = [:let, :X, [:+, :X, 1], [:+, :X, 2]]
|
49
|
+
converted = convert_lexical_variables(sexp)
|
50
|
+
|
51
|
+
reset_generator
|
52
|
+
v1 = fresh_variable
|
53
|
+
expected = [:let, v1, [:+, :X, 1], [:+, v1, 2]]
|
54
|
+
|
55
|
+
expect(converted).to eq(expected)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|