trxl 0.1.5
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.
- data/.gitignore +24 -0
- data/README +143 -0
- data/Rakefile +41 -0
- data/VERSION +1 -0
- data/lib/trxl.rb +5 -0
- data/lib/trxl/trxl.rb +585 -0
- data/lib/trxl/trxl_grammar.rb +8583 -0
- data/lib/trxl/trxl_grammar.treetop +1394 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/trxl/arithmetics_spec.rb +391 -0
- data/spec/trxl/arrays_spec.rb +163 -0
- data/spec/trxl/booleans_spec.rb +138 -0
- data/spec/trxl/builtins_spec.rb +268 -0
- data/spec/trxl/closures_spec.rb +244 -0
- data/spec/trxl/comments_spec.rb +35 -0
- data/spec/trxl/common_spec.rb +22 -0
- data/spec/trxl/conditionals_spec.rb +454 -0
- data/spec/trxl/constants_spec.rb +23 -0
- data/spec/trxl/environment_spec.rb +117 -0
- data/spec/trxl/hashes_spec.rb +62 -0
- data/spec/trxl/numbers_spec.rb +27 -0
- data/spec/trxl/ranges_spec.rb +81 -0
- data/spec/trxl/require_spec.rb +50 -0
- data/spec/trxl/stdlib_spec.rb +370 -0
- data/spec/trxl/strings_spec.rb +1 -0
- data/spec/trxl/variables_spec.rb +45 -0
- data/trxl.gemspec +90 -0
- metadata +119 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "For writing boolean expressions, the language" do
|
4
|
+
|
5
|
+
include Trxl::SpecHelper
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@parser = Trxl::Calculator.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow a logical AND operation by using '&&'" do
|
12
|
+
eval("TRUE && TRUE").should be_true
|
13
|
+
eval("FALSE && FALSE").should be_false
|
14
|
+
eval("TRUE && FALSE").should be_false
|
15
|
+
eval("FALSE && TRUE").should be_false
|
16
|
+
|
17
|
+
eval("TRUE && NULL").should be_false
|
18
|
+
eval("NULL && TRUE").should be_false
|
19
|
+
eval("NULL && NULL").should be_false
|
20
|
+
|
21
|
+
eval("(2 == 2) && (2 == 2)").should be_true
|
22
|
+
|
23
|
+
lambda { eval("TRUE && TRUE && TRUE") }.should_not raise_error
|
24
|
+
lambda { eval("TRUE && TRUE && FALSE") }.should_not raise_error
|
25
|
+
lambda { eval("(2 == 2) && (2 == 2) && (2 == 2)") }.should_not raise_error
|
26
|
+
lambda { eval("(2 == 2) && (2 == 2) && (2 == 3)") }.should_not raise_error
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should allow a logical OR operation by using '||'" do
|
30
|
+
eval("TRUE || TRUE").should be_true
|
31
|
+
eval("TRUE || FALSE").should be_true
|
32
|
+
eval("FALSE || TRUE").should be_true
|
33
|
+
eval("FALSE || FALSE").should be_false
|
34
|
+
eval("(2 == 2) || (2 == 2)").should be_true
|
35
|
+
eval("(2 == 2) || (2 == 3)").should be_true
|
36
|
+
eval("(2 == 3) || (2 == 3)").should be_false
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should allow a logical NOT operation by using '!'" do
|
40
|
+
eval("!TRUE").should be_false
|
41
|
+
eval("!FALSE").should be_true
|
42
|
+
eval("!1").should be_false
|
43
|
+
eval("!(1+1)").should be_false
|
44
|
+
eval("!(TRUE || FALSE)").should be_false
|
45
|
+
eval("!(TRUE && FALSE)").should be_true
|
46
|
+
eval("!(2 == 2)").should be_false
|
47
|
+
eval("!(2 == 3)").should be_true
|
48
|
+
eval("a = TRUE; !a").should be_false
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
describe "For comparing expressions, the language" do
|
55
|
+
|
56
|
+
include Trxl::SpecHelper
|
57
|
+
|
58
|
+
before(:each) do
|
59
|
+
@parser = Trxl::Calculator.new
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should allow to test if two expressions are equal by using '=='" do
|
63
|
+
eval("(TRUE == TRUE)").should be_true
|
64
|
+
eval("(FALSE == FALSE)").should be_true
|
65
|
+
eval("(TRUE == FALSE)").should be_false
|
66
|
+
eval("(NULL == NULL)").should be_true
|
67
|
+
|
68
|
+
eval("2.0 == 1.999999999").should == (2.0 == 1.999999999)
|
69
|
+
eval("2.0 == 2.000000001").should == (2.0 == 2.000000001)
|
70
|
+
eval("1.333333333 == 1.333333333").should == (1.333333333 == 1.333333333)
|
71
|
+
|
72
|
+
eval("'foo' == 'foo'").should be_true
|
73
|
+
eval("'foo' == 'bar'").should be_false
|
74
|
+
|
75
|
+
eval("'foo' != 'foo'").should be_false
|
76
|
+
eval("'foo' != 'bar'").should be_true
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should allow to test if two expressions are not equal by using '!='" do
|
80
|
+
eval("(TRUE != TRUE)").should be_false
|
81
|
+
eval("(TRUE != FALSE)").should be_true
|
82
|
+
eval("(FALSE != FALSE)").should be_false
|
83
|
+
eval("(NULL != NULL)").should be_false
|
84
|
+
|
85
|
+
eval("2.0 != 1.999999999").should == (2.0 != 1.999999999)
|
86
|
+
eval("2.0 != 2.000000001").should == (2.0 != 2.000000001)
|
87
|
+
eval("1.333333333 != 1.333333334").should == (1.333333333 != 1.333333334)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should allow to compare two expressions by using '<'" do
|
91
|
+
eval("(1 < 1)").should be_false
|
92
|
+
eval("(1 < 2)").should be_true
|
93
|
+
eval("(2 < 1)").should be_false
|
94
|
+
|
95
|
+
eval("2.0 < 1.999999999").should == (2.0 < 1.999999999)
|
96
|
+
eval("2.0 < 2.000000001").should == (2.0 < 2.000000001)
|
97
|
+
eval("1.333333333 < 1.333333332").should == (1.333333333 < 1.333333332)
|
98
|
+
eval("1.333333333 < 1.333333333").should == (1.333333333 < 1.333333333)
|
99
|
+
eval("1.333333333 < 1.333333334").should == (1.333333333 < 1.333333334)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should allow to compare two expressions by using '>'" do
|
103
|
+
eval("(1 > 1)").should be_false
|
104
|
+
eval("(2 > 1)").should be_true
|
105
|
+
eval("(1 > 2)").should be_false
|
106
|
+
|
107
|
+
eval("2.0 > 1.999999999").should == (2.0 > 1.999999999)
|
108
|
+
eval("2.0 > 2.000000001").should == (2.0 > 2.000000001)
|
109
|
+
eval("1.333333333 > 1.333333332").should == (1.333333333 > 1.333333332)
|
110
|
+
eval("1.333333333 > 1.333333333").should == (1.333333333 > 1.333333333)
|
111
|
+
eval("1.333333333 > 1.333333334").should == (1.333333333 > 1.333333334)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should allow to compare two expressions by using '<='" do
|
115
|
+
eval("(1 <= 1)").should be_true
|
116
|
+
eval("(1 <= 2)").should be_true
|
117
|
+
eval("(2 <= 1)").should be_false
|
118
|
+
|
119
|
+
eval("2.0 <= 1.999999999").should == (2.0 <= 1.999999999)
|
120
|
+
eval("2.0 <= 2.000000001").should == (2.0 <= 2.000000001)
|
121
|
+
eval("1.333333333 <= 1.333333332").should == (1.333333333 <= 1.333333332)
|
122
|
+
eval("1.333333333 <= 1.333333333").should == (1.333333333 <= 1.333333333)
|
123
|
+
eval("1.333333333 <= 1.333333334").should == (1.333333333 <= 1.333333334)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should allow to compare two expressions by using '>='" do
|
127
|
+
eval("(1 >= 1)").should be_true
|
128
|
+
eval("(2 >= 1)").should be_true
|
129
|
+
eval("(1 >= 2)").should be_false
|
130
|
+
|
131
|
+
eval("2.0 >= 1.999999999").should == (2.0 >= 1.999999999)
|
132
|
+
eval("2.0 >= 2.000000001").should == (2.0 >= 2.000000001)
|
133
|
+
eval("1.333333333 >= 1.333333332").should == (1.333333333 >= 1.333333332)
|
134
|
+
eval("1.333333333 >= 1.333333333").should == (1.333333333 >= 1.333333333)
|
135
|
+
eval("1.333333333 >= 1.333333334").should == (1.333333333 >= 1.333333334)
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "The language" do
|
4
|
+
|
5
|
+
include Trxl::SpecHelper
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@parser = Trxl::Calculator.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should allow to access the current environment by calling ENV" do
|
12
|
+
env = eval("ENV", {})
|
13
|
+
env.should be_kind_of(Trxl::Environment)
|
14
|
+
env.should be_empty
|
15
|
+
|
16
|
+
env = eval("ENV", { :a => 1, :b => 2 })
|
17
|
+
env.should be_kind_of(Trxl::Environment)
|
18
|
+
env.should have_key(:a)
|
19
|
+
env.should have_key(:b)
|
20
|
+
env[:a].should == 1
|
21
|
+
env[:b].should == 2
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should allow to access variables by name by calling ENV['foo']" do
|
25
|
+
eval("ENV['a']", {}).should == nil
|
26
|
+
eval("ENV['a']", { :a => 1 }).should == 1
|
27
|
+
eval("ENV['a']", { :a => "yes" }).should == "yes"
|
28
|
+
eval("ENV['a']", { :a => [ 1, 2, 3 ] }).should == [ 1, 2, 3 ]
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should allow to access ranges in the current environment by calling ENV['a'..'z']" do
|
32
|
+
eval("ENV['a'..'a']", { :a => 1, :b => 2, :c => 3 }).should == [ 1 ]
|
33
|
+
eval("ENV['a'..'b']", { :a => 1, :b => 2, :c => 3 }).should include(1)
|
34
|
+
eval("ENV['a'..'b']", { :a => 1, :b => 2, :c => 3 }).should include(2)
|
35
|
+
eval("ENV['a'..'c']", { :a => 1, :b => 2, :c => 3 }).should include(1)
|
36
|
+
eval("ENV['a'..'c']", { :a => 1, :b => 2, :c => 3 }).should include(2)
|
37
|
+
eval("ENV['a'..'c']", { :a => 1, :b => 2, :c => 3 }).should include(3)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should be able to PRINT and PRINT_LINE to STDOUT" do
|
41
|
+
eval("PRINT('- This is output from PRINT. ')").should be_nil
|
42
|
+
eval("PRINT_LINE('And this is output from PRINT_LINE')").should be_nil
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be able to split strings by calling SPLIT(some_string, split_char)" do
|
46
|
+
eval("SPLIT('foo/bar', '/')").should == [ 'foo', 'bar' ]
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should be able to convert values into integers by calling TO_INT(alpha_numeric_string)" do
|
50
|
+
eval("TO_INT('1')").should == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be able to convert values into floats by calling TO_FLOAT(alpha_numeric_string)" do
|
54
|
+
eval("TO_FLOAT('1.12')").should == 1.12
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should be able to convert values into arrays by calling TO_ARRAY(some_value)" do
|
58
|
+
eval("TO_ARRAY(1)").should == [ 1 ]
|
59
|
+
eval("TO_ARRAY('1')").should == [ '1' ]
|
60
|
+
eval("TO_ARRAY([1])").should == [ 1 ]
|
61
|
+
eval("TO_ARRAY({ 1 => 2 })").should == [ [1, 2] ]
|
62
|
+
eval("TO_ARRAY({ 1 => 2, 3 => 4 })").should == [ [1, 2], [3, 4] ]
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should be able to round numbers to a given number of decimal places by calling ROUND(num, digits)" do
|
66
|
+
program = "ROUND(1.2222,0)"
|
67
|
+
eval(program).should == 1
|
68
|
+
program = "ROUND(1.6666,0)"
|
69
|
+
eval(program).should == 2
|
70
|
+
program = "ROUND(1.2222,1)"
|
71
|
+
eval(program).should == 1.2
|
72
|
+
program = "ROUND(1.2222,2)"
|
73
|
+
eval(program).should == 1.22
|
74
|
+
program = "ROUND(1.2782,2)"
|
75
|
+
eval(program).should == 1.28
|
76
|
+
program = "ROUND(fun(){1.1234567}(),4)"
|
77
|
+
eval(program).should == 1.1235
|
78
|
+
program = "ROUND(NULL,2)"
|
79
|
+
eval(program).should be_nil
|
80
|
+
program = "ROUND(FALSE,2)"
|
81
|
+
eval(program).should be_nil
|
82
|
+
program = "ROUND(TRUE,2)"
|
83
|
+
eval(program).should be_nil
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should be able to calculate SUM for an arbitrary number of argument expressions" do
|
87
|
+
program = "SUM()"
|
88
|
+
eval(program).should == 0
|
89
|
+
program = "SUM(NULL)"
|
90
|
+
eval(program).should == 0
|
91
|
+
program = "SUM(0)"
|
92
|
+
eval(program).should == 0
|
93
|
+
program = "SUM(1)"
|
94
|
+
eval(program).should == 1
|
95
|
+
program = "SUM(1,2,3,4)"
|
96
|
+
eval(program).should == 10
|
97
|
+
program = "SUM(1,2,3,NULL)"
|
98
|
+
eval(program).should == 6
|
99
|
+
program = "SUM(fun(){4}(),fun(x){x}(4),fun(x,y){x+y}(2,2))"
|
100
|
+
eval(program).should == 12
|
101
|
+
program = "SUM(x)"
|
102
|
+
eval(program, { :x => [ 1, 2, 3 ] }).should == 6
|
103
|
+
|
104
|
+
program = "SUM([2,2,2],[4,4,4],[3,3,3])"
|
105
|
+
eval(program).should == 27
|
106
|
+
|
107
|
+
program = "SUM([ [2,2,2], [4,4,4], [3,3,3] ])"
|
108
|
+
eval(program).should == 27
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should be able to calculate MULT for an arbitrary number of argument expressions" do
|
112
|
+
program = "MULT()"
|
113
|
+
eval(program).should == 0
|
114
|
+
program = "MULT(NULL)"
|
115
|
+
eval(program).should == 0
|
116
|
+
program = "MULT(0)"
|
117
|
+
eval(program).should == 0
|
118
|
+
program = "MULT(1)"
|
119
|
+
eval(program).should == 1
|
120
|
+
program = "MULT(1,2,3,4)"
|
121
|
+
eval(program).should == 24
|
122
|
+
program = "MULT(1,2,3,NULL)"
|
123
|
+
eval(program).should == 6
|
124
|
+
program = "MULT(fun(){4}(),fun(x){x}(4),fun(x,y){x+y}(2,2))"
|
125
|
+
eval(program).should == 64
|
126
|
+
program = "MULT(x)"
|
127
|
+
eval(program, { :x => [ 1, 2, 3 ] }).should == 6
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should be able to calculate AVG for an arbitrary number of argument expressions" do
|
131
|
+
program = "AVG()"
|
132
|
+
eval(program).should == 0
|
133
|
+
program = "AVG(NULL)"
|
134
|
+
eval(program).should be_nil
|
135
|
+
program = "AVG(0)"
|
136
|
+
eval(program).should == 0
|
137
|
+
program = "AVG(1)"
|
138
|
+
eval(program).should == 1
|
139
|
+
program = "AVG(3,3,3)"
|
140
|
+
eval(program).should == 3
|
141
|
+
program = "AVG(3,3,3)"
|
142
|
+
eval(program).should == 3
|
143
|
+
program = "AVG(3,5,0)"
|
144
|
+
eval(program).should be_close(2.66666666666, 0.01)
|
145
|
+
program = "AVG(FALSE, 3,5,0)"
|
146
|
+
eval(program).should == 4
|
147
|
+
program = "AVG(TRUE, 3,5,0)"
|
148
|
+
eval(program).should be_close(2.66666666666, 0.01)
|
149
|
+
program = "AVG(3,NULL,3)"
|
150
|
+
eval(program).should == 3
|
151
|
+
program = "AVG(3.3,3.3,3.3)"
|
152
|
+
eval(program).should be_close(3.3, 0.01)
|
153
|
+
program = "AVG(3,5,4)"
|
154
|
+
eval(program).should == 4
|
155
|
+
program = "AVG(fun(){4}(),fun(x){x}(4),fun(x,y){x+y}(2,2))"
|
156
|
+
eval(program).should == 4
|
157
|
+
|
158
|
+
program = "AVG([1,2,3],[4,4,4],[6,6,6])"
|
159
|
+
eval(program).should == 4
|
160
|
+
program = "AVG([2,2],[4,4,4],[6,6])"
|
161
|
+
eval(program).should == 4
|
162
|
+
end
|
163
|
+
|
164
|
+
it "should be able to calculate AVG_SUM for an arbitrary number of argument expressions" do
|
165
|
+
program = "AVG_SUM([1,2,3],[4,4,4],[6,6,6])"
|
166
|
+
eval(program).should == 12
|
167
|
+
program = "AVG_SUM([2,2],[4,4,4],[6,6])"
|
168
|
+
eval(program).should == 12
|
169
|
+
|
170
|
+
env = {
|
171
|
+
:ap => [138542.0, 136795.0, 134256.0], # 136531
|
172
|
+
:aq => [124580.0, 21458.0, 34560.0], # 60199.3333333333
|
173
|
+
:ar => [1200.0, 1200.0, 1200.0], # 1200
|
174
|
+
:as => [1235.0, 1265.0, 5620.0], # 2706.66666666667
|
175
|
+
:at => [5201.0, 4263.0, 8452.0], # 5972
|
176
|
+
:au => [250.0, 250.0, 250.0] # 250
|
177
|
+
# ------------------
|
178
|
+
# 206859
|
179
|
+
|
180
|
+
}
|
181
|
+
eval("AVG_SUM(ENV['ap'..'au'])", env).should == 206859
|
182
|
+
|
183
|
+
env = {
|
184
|
+
:ap => [138542.0, 136795.0, 134256.0], # 136531
|
185
|
+
:aq => [124580.0, 21458.0, 34560.0], # 60199.3333333333
|
186
|
+
:ar => [1200.0, 1200.0, 1200.0], # 1200
|
187
|
+
:as => [1235.0, 1265.0, 5620.0], # 2706.66666666667
|
188
|
+
:at => [5201.0, 4263.0, 8452.0], # 5972
|
189
|
+
:au => [0, 0, 0] # 0
|
190
|
+
# ------------------
|
191
|
+
# 206609
|
192
|
+
|
193
|
+
}
|
194
|
+
eval("AVG_SUM(ENV['ap'..'au'])", env).should == 206609
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should be able to calculate MIN for an arbitrary number of argument expressions" do
|
199
|
+
program = "MIN()"
|
200
|
+
eval(program).should == 0
|
201
|
+
program = "MIN(0)"
|
202
|
+
eval(program).should == 0
|
203
|
+
program = "MIN(1)"
|
204
|
+
eval(program).should == 1
|
205
|
+
program = "MIN(1,2,3)"
|
206
|
+
eval(program).should == 1
|
207
|
+
program = "MIN(1.0, 2.0, 3.0)"
|
208
|
+
eval(program).should == 1.0
|
209
|
+
program = "MIN(fun(){4}(),fun(x){x}(4),fun(x,y){x+y}(2,2))"
|
210
|
+
eval(program).should == 4
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should be able to calculate MAX for an arbitrary number of argument expressions" do
|
214
|
+
program = "MAX()"
|
215
|
+
eval(program).should == 0
|
216
|
+
program = "MAX(0)"
|
217
|
+
eval(program).should == 0
|
218
|
+
program = "MAX(1)"
|
219
|
+
eval(program).should == 1
|
220
|
+
program = "MAX(1,2,3)"
|
221
|
+
eval(program).should == 3
|
222
|
+
program = "MAX(1.0, 2.0, 3.0)"
|
223
|
+
eval(program).should == 3.0
|
224
|
+
program = "MAX(fun(){4}(),fun(x){x}(4),fun(x,y){x+y}(2,2))"
|
225
|
+
eval(program).should == 4
|
226
|
+
end
|
227
|
+
|
228
|
+
it "should be able to map all hash ids that match a given value by calling MATCHING_IDS" do
|
229
|
+
env = { :foo => { :a => "bar", :b => "bar", :c => "baz" } }
|
230
|
+
eval("MATCHING_IDS('bar', foo)", env).should include(:a)
|
231
|
+
eval("MATCHING_IDS('bar', foo)", env).should include(:b)
|
232
|
+
eval("MATCHING_IDS('baz', foo)", env).should == [ :c ]
|
233
|
+
eval("MATCHING_IDS('bam', foo)", env).should == []
|
234
|
+
end
|
235
|
+
|
236
|
+
it "should define a VALUES_OF_TYPE function" do
|
237
|
+
env = {
|
238
|
+
:all_types => { :a => "bar", :b => "bar", :c => "baz" },
|
239
|
+
:all_values => { :a => 100, :b => 200, :c => 300 }
|
240
|
+
}
|
241
|
+
eval("VALUES_OF_TYPE('bar', all_types, all_values)", env).should include(100)
|
242
|
+
eval("VALUES_OF_TYPE('bar', all_types, all_values)", env).should include(200)
|
243
|
+
eval("VALUES_OF_TYPE('baz', all_types, all_values)", env).should == [ 300 ]
|
244
|
+
eval("VALUES_OF_TYPE('bam', all_types, all_values)", env).should == []
|
245
|
+
|
246
|
+
env = {
|
247
|
+
:all_types => { :a => "bar", :b => "bar", :c => "baz" },
|
248
|
+
:all_values => { :a => [100,200,300], :b => [400,500,600], :c => [700,800,900] }
|
249
|
+
}
|
250
|
+
eval("VALUES_OF_TYPE('bar', all_types, all_values)", env).should include([100,200,300])
|
251
|
+
eval("VALUES_OF_TYPE('bar', all_types, all_values)", env).should include([400,500,600])
|
252
|
+
eval("VALUES_OF_TYPE('baz', all_types, all_values)", env).should == [ [700,800,900] ]
|
253
|
+
eval("VALUES_OF_TYPE('bam', all_types, all_values)", env).should == []
|
254
|
+
|
255
|
+
env = {
|
256
|
+
:all_types => { :a => "bar", :b => "bar", :c => "baz" },
|
257
|
+
:all_values => []
|
258
|
+
}
|
259
|
+
lambda { eval("VALUES_OF_TYPE('bar', all_types, all_values)", env) }.should raise_error(Trxl::InvalidArgumentException)
|
260
|
+
|
261
|
+
env = {
|
262
|
+
:all_types => [],
|
263
|
+
:all_values => { :a => [100,200,300], :b => [400,500,600], :c => [700,800,900] }
|
264
|
+
}
|
265
|
+
lambda { eval("VALUES_OF_TYPE('bar', all_types, all_values)", env) }.should raise_error(Trxl::InvalidArgumentException)
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
@@ -0,0 +1,244 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "For defining closures, the language" do
|
4
|
+
|
5
|
+
include Trxl::SpecHelper
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@parser = Trxl::Calculator.new
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should parse lambdas with no parameters" do
|
12
|
+
parse("fun() {1}").eval.to_s.should == "fun() {1}"
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should parse lambdas with one parameter" do
|
16
|
+
parse("fun(x) {x}").eval.to_s.should == "fun(x) {x}"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should parse lambdas with multiple parameters" do
|
20
|
+
parse("fun(x,y) {x + y}").eval.to_s.should == "fun(x,y) {x + y}"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should parse lambdas that return another lambda" do
|
24
|
+
program = "fun(x) {fun(y) {x + y}}(5)"
|
25
|
+
eval(program).to_s.should == "fun(y) {x + y}"
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
it "should be able to apply lambdas with no parameters" do
|
30
|
+
parse("fun() {2 * 2} ()").eval.should == 4
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should be able to apply lambdas with one parameter" do
|
34
|
+
parse("fun(x) {x} (1)").eval.should == 1
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should be able to apply lambdas with multiple parameters" do
|
38
|
+
parse("fun(x,y) {x + y} (5,5)").eval.should == 10
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should be able to store lambdas in a variable and apply them later" do
|
42
|
+
eval("a = fun(x,y) {x + y}; a(5,5);").should == 10
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should be able to return closures, store them in variables and apply them later" do
|
46
|
+
program = "foo = 2;"
|
47
|
+
program << "adder = fun(x) {fun(y) {x + y}}(5);"
|
48
|
+
program << "adder(2);"
|
49
|
+
eval(program).should == 7
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should have read access to the enclosing scope in closures" do
|
53
|
+
program = "a = 1;"
|
54
|
+
program << "fun(){a}();"
|
55
|
+
eval(program).should == 1
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should have write access to the enclosing scope in closures" do
|
59
|
+
program = "a = 0;"
|
60
|
+
program << "fun(x){a = x}(1);"
|
61
|
+
program << "a;"
|
62
|
+
eval(program).should == 1
|
63
|
+
|
64
|
+
program = <<-PROGRAM
|
65
|
+
foo = fun() {
|
66
|
+
a = 0;
|
67
|
+
fun(y) { a = y }(1);
|
68
|
+
a;
|
69
|
+
};
|
70
|
+
foo();
|
71
|
+
PROGRAM
|
72
|
+
eval(program).should == 1
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
it "should allow multiple statements in a function body" do
|
77
|
+
program = <<-PROGRAM
|
78
|
+
foo = fun(x) {
|
79
|
+
a = 3;
|
80
|
+
x + a;
|
81
|
+
};
|
82
|
+
foo(3)
|
83
|
+
PROGRAM
|
84
|
+
eval(program).should == 6
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should be able to perform arithmetics in function bodies" do
|
88
|
+
eval("fun(x,y) {x+y} (5,5)").should == 10
|
89
|
+
eval("fun(x,y) {x-y} (5,5)").should == 0
|
90
|
+
eval("fun(x,y) {x*y} (5,5)").should == 25
|
91
|
+
eval("fun(x,y) {x/y} (10,5)").should == 2
|
92
|
+
eval("fun(x,y) {x%y} (10,2)").should == 0
|
93
|
+
|
94
|
+
eval("fun(x,y) {x+y*3} (5,5)").should == 20
|
95
|
+
eval("fun(x,y) {(x-y)*3} (5,5)").should == 0
|
96
|
+
eval("fun(x,y) {(x*y)+5} (5,5)").should == 30
|
97
|
+
eval("fun(x,y) {x/(y-3)} (10,5)").should == 5
|
98
|
+
eval("fun(x,y) {(x+y)%2} (10,2)").should == 0
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should be able to perform recursive calls" do
|
102
|
+
program = <<-PROGRAM
|
103
|
+
fact = fun(x) { if(x == 0) 1 else fact(x - 1) * x end};
|
104
|
+
fact(5);
|
105
|
+
PROGRAM
|
106
|
+
eval(program).should == 5*4*3*2*1
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
it "should allow nested applications of recursive functions" do
|
111
|
+
program = <<-PROGRAM
|
112
|
+
require 'stdlib/inject';
|
113
|
+
inject(0, [[1,2],[3,4]], fun(m, arr) {
|
114
|
+
m + inject(0, arr, fun(sum, v) {
|
115
|
+
sum + v
|
116
|
+
})
|
117
|
+
})
|
118
|
+
PROGRAM
|
119
|
+
lambda { eval(program).should == 10 }.should_not raise_error
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should evaluate actual parameters that are function applications" do
|
123
|
+
program = "a = 5;"
|
124
|
+
program << "b = 10;"
|
125
|
+
program << "sum = fun(x,y) {x + y};"
|
126
|
+
program << "div = fun(x,y) {x / y};"
|
127
|
+
program << "div(sum(a,b),a);"
|
128
|
+
eval(program).should == 3
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should be able to apply previously defined closures inside function bodies" do
|
132
|
+
program = "a = 5;"
|
133
|
+
program << "b = 10;"
|
134
|
+
program << "sum = fun(x,y) {x + y};"
|
135
|
+
program << "foo = fun(x,y) {sum(x,y)};"
|
136
|
+
program << "foo(a,b);"
|
137
|
+
eval(program).should == 15
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should perform left associative chained applications" do
|
141
|
+
program = "fun(x) {fun(y) {x + y}}(5)(5);"
|
142
|
+
eval(program).should == 10
|
143
|
+
program = "fun(a,b) {fun(c,d) {a * b + c * d}}(5,5)(5,5);"
|
144
|
+
eval(program).should == 50
|
145
|
+
program = "sum = fun(x,y) {x + y};"
|
146
|
+
program << "fun(a,b) {fun(c,d) {a * b + c * d}}(sum(5,5),sum(5,5))(5,5);"
|
147
|
+
eval(program).should == 125
|
148
|
+
program = "sum = fun(x,y) {x + y};"
|
149
|
+
program << "mult = fun(x,y) {x * y};"
|
150
|
+
program << "fun(a,b) {fun(c,d) {a * b + c * d}}(sum(mult(5,5),5),sum(5,5))(sum(5,5),mult(5,5));"
|
151
|
+
eval(program).should == 550
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should be able to use function applications as left operand in binary operations" do
|
155
|
+
program = "sum = fun(x,y) {x + y};"
|
156
|
+
program << "sum(2,2) + 1"
|
157
|
+
eval(program).should == 5
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should be able to use function applications as right operand in binary operations" do
|
161
|
+
program = "sum = fun(x,y) {x + y};"
|
162
|
+
program << "1 + sum(2,2);"
|
163
|
+
eval(program).should == 5
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should be able to use function applications as operands in binary operations" do
|
167
|
+
program = "sum = fun(x,y) {x + y};"
|
168
|
+
program << "sum(1,2) + sum(3,4)"
|
169
|
+
eval(program).should == 10
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should be able to use function applications as left operands in binary expressions inside function bodies" do
|
173
|
+
program = "sum = fun(x,y) {x + y};"
|
174
|
+
program << "foo = fun(x,y) {sum(x,y) + x};"
|
175
|
+
program << "foo(5,10);"
|
176
|
+
eval(program).should == 20
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should be able to use function applications as right operands in binary expressions inside function bodies" do
|
180
|
+
program = "sum = fun(x,y) {x + y};"
|
181
|
+
program << "foo = fun(x,y) {x + sum(x,y)};"
|
182
|
+
program << "foo(5,10);"
|
183
|
+
eval(program).should == 20
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should be able to use function applications as operands in binary expressions inside function bodies" do
|
187
|
+
program = "sum = fun(x,y) {x + y};"
|
188
|
+
program << "foo = fun(x,y) {sum(x,y) + sum(x,y)};"
|
189
|
+
program << "foo(5,10);"
|
190
|
+
eval(program).should == 30
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should be able to use predefined function applications as operands in binary expressions" do
|
194
|
+
program = "AVG(10,15,20) / AVG(3,3,3)"
|
195
|
+
eval(program).should == 5
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should be able to use predefined function applications as operands in binary expressions acting as actual parameters to another predefined function" do
|
199
|
+
program = "ROUND( AVG(10,15,20) / AVG(3,3,3) , 2 )"
|
200
|
+
eval(program).should == 5
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should be able to use predefined and regular function applications as operands in binary expressions acting as a parameter to another predefined function" do
|
204
|
+
program = "ROUND( AVG(fun(x){x}(10),15,fun(x,y){x+y}(10,10)) / AVG(fun(x){x}(3),3,fun(x,y){x+y}(1,2)) , 2 )"
|
205
|
+
eval(program).should == 5
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should have the arguments variable available in function bodies" do
|
209
|
+
program = <<-PROGRAM
|
210
|
+
require 'stdlib/inject';
|
211
|
+
sum = fun() { inject(0, arguments, fun(memo, i) { memo + i }); };
|
212
|
+
sum(1,2,3,4,5);
|
213
|
+
PROGRAM
|
214
|
+
eval(program).should == 15
|
215
|
+
|
216
|
+
program = <<-PROGRAM
|
217
|
+
require 'stdlib/inject';
|
218
|
+
sum = fun(x,y) { inject(0, arguments, fun(memo, i) { memo + i }); };
|
219
|
+
sum(1,2);
|
220
|
+
PROGRAM
|
221
|
+
eval(program).should == 3
|
222
|
+
|
223
|
+
program = <<-PROGRAM
|
224
|
+
require 'stdlib/inject';
|
225
|
+
sum = fun(x,y) { inject(0, arguments, fun(memo, i) { memo + i }); };
|
226
|
+
sum(1,2,3,4,5);
|
227
|
+
PROGRAM
|
228
|
+
eval(program).should == 15
|
229
|
+
|
230
|
+
program = <<-PROGRAM
|
231
|
+
require 'stdlib/inject';
|
232
|
+
sum = fun(x,y,z) { inject(0, arguments, fun(memo, i) { memo + i }); };
|
233
|
+
sum(1,2);
|
234
|
+
PROGRAM
|
235
|
+
lambda { eval(program) }.should raise_error(Trxl::WrongNumberOfArgumentsException)
|
236
|
+
|
237
|
+
begin
|
238
|
+
eval(program)
|
239
|
+
rescue Trxl::WrongNumberOfArgumentsException => e
|
240
|
+
e.message.should == "2 instead of 3"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
end
|