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,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
|