trxl 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|