delorean_lang 0.0.43 → 0.1.00
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/delorean/base.rb +105 -12
- data/lib/delorean/delorean.rb +626 -958
- data/lib/delorean/delorean.treetop +24 -37
- data/lib/delorean/engine.rb +7 -4
- data/lib/delorean/nodes.rb +120 -75
- data/lib/delorean/version.rb +1 -1
- data/lib/delorean_lang.rb +0 -1
- data/spec/eval_spec.rb +54 -26
- data/spec/func_spec.rb +37 -111
- data/spec/parse_spec.rb +33 -7
- metadata +2 -3
- data/lib/delorean/functions.rb +0 -200
data/spec/func_spec.rb
CHANGED
@@ -8,60 +8,27 @@ describe "Delorean" do
|
|
8
8
|
|
9
9
|
it "should handle MAX as a node name" do
|
10
10
|
engine.parse defn("MAX:",
|
11
|
-
" a =
|
11
|
+
" a = [1, 2, 3, 0, -10].max()",
|
12
12
|
)
|
13
13
|
|
14
14
|
r = engine.evaluate("MAX", "a")
|
15
15
|
r.should == 3
|
16
16
|
end
|
17
17
|
|
18
|
-
it "should handle MAX" do
|
19
|
-
engine.parse defn("A:",
|
20
|
-
" a = MAX(1, 2, 3)",
|
21
|
-
)
|
22
|
-
|
23
|
-
r = engine.evaluate("A", "a")
|
24
|
-
r.should == 3
|
25
|
-
end
|
26
|
-
|
27
|
-
it "should handle insufficient args" do
|
28
|
-
lambda {
|
29
|
-
engine.parse defn("A:",
|
30
|
-
" a = MAX(1)",
|
31
|
-
)
|
32
|
-
}.should raise_error(Delorean::BadCallError)
|
33
|
-
end
|
34
|
-
|
35
18
|
it "should handle MIN" do
|
36
19
|
engine.parse defn("A:",
|
37
|
-
" a =
|
20
|
+
" a = [1, 2, -3, 4].min()",
|
38
21
|
)
|
39
22
|
|
40
23
|
r = engine.evaluate("A", "a")
|
41
24
|
r.should == -3
|
42
25
|
end
|
43
26
|
|
44
|
-
it "should handle MAXLIST" do
|
45
|
-
engine.parse defn("A:",
|
46
|
-
" a = MAXLIST([1, 2, 3])",
|
47
|
-
)
|
48
|
-
|
49
|
-
engine.evaluate("A", "a").should == 3
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should handle MINLIST" do
|
53
|
-
engine.parse defn("A:",
|
54
|
-
" a = MINLIST([1, 10, -3])",
|
55
|
-
)
|
56
|
-
|
57
|
-
engine.evaluate("A", "a").should == -3
|
58
|
-
end
|
59
|
-
|
60
27
|
it "should handle ROUND" do
|
61
28
|
engine.parse defn("A:",
|
62
|
-
" a =
|
63
|
-
" b =
|
64
|
-
" c =
|
29
|
+
" a = 12.3456.round(2)",
|
30
|
+
" b = 12.3456.round(1)",
|
31
|
+
" c = 12.3456.round()",
|
65
32
|
)
|
66
33
|
|
67
34
|
r = engine.evaluate_attrs("A", ["a", "b", "c"])
|
@@ -70,9 +37,9 @@ describe "Delorean" do
|
|
70
37
|
|
71
38
|
it "should handle NUMBER" do
|
72
39
|
engine.parse defn("A:",
|
73
|
-
" a =
|
74
|
-
" b =
|
75
|
-
" c =
|
40
|
+
" a = 12.3456.to_f()",
|
41
|
+
" b = '12.3456'.to_f()",
|
42
|
+
" c = '12'.to_f()",
|
76
43
|
)
|
77
44
|
|
78
45
|
r = engine.evaluate_attrs("A", ["a", "b", "c"])
|
@@ -81,10 +48,10 @@ describe "Delorean" do
|
|
81
48
|
|
82
49
|
it "should handle ABS" do
|
83
50
|
engine.parse defn("A:",
|
84
|
-
" a =
|
85
|
-
" b =
|
86
|
-
" c =
|
87
|
-
" d =
|
51
|
+
" a = (-123).abs()",
|
52
|
+
" b = (-1.1).abs()",
|
53
|
+
" c = 2.3.abs()",
|
54
|
+
" d = 0.abs()",
|
88
55
|
)
|
89
56
|
|
90
57
|
r = engine.evaluate_attrs("A", ["a", "b", "c", "d"])
|
@@ -93,9 +60,9 @@ describe "Delorean" do
|
|
93
60
|
|
94
61
|
it "should handle STRING" do
|
95
62
|
engine.parse defn("A:",
|
96
|
-
" a =
|
97
|
-
" b =
|
98
|
-
" c =
|
63
|
+
" a = 'hello'.to_s()",
|
64
|
+
" b = 12.3456.to_s()",
|
65
|
+
" c = [1,2,3].to_s()",
|
99
66
|
)
|
100
67
|
|
101
68
|
r = engine.evaluate_attrs("A", ["a", "b", "c"])
|
@@ -105,21 +72,16 @@ describe "Delorean" do
|
|
105
72
|
it "should handle TIMEPART" do
|
106
73
|
engine.parse defn("A:",
|
107
74
|
" p =?",
|
108
|
-
"
|
109
|
-
"
|
110
|
-
"
|
111
|
-
"
|
112
|
-
" d = TIMEPART(p, 'd')",
|
113
|
-
" d2 = TIMEPART(p2, 'd')",
|
114
|
-
" h2 = TIMEPART(p2, 'h')",
|
75
|
+
" h = p.hour()",
|
76
|
+
" m = p.min()",
|
77
|
+
" s = p.sec()",
|
78
|
+
" d = p.to_date()",
|
115
79
|
)
|
116
80
|
|
117
81
|
p = Time.now
|
118
|
-
params = {"p" => p
|
119
|
-
r = engine.evaluate_attrs("A", %w{h m s d
|
120
|
-
r.should == [p.hour, p.min, p.sec, p.to_date
|
121
|
-
|
122
|
-
expect { engine.evaluate_attrs("A", ["h2"], params) }.to raise_error
|
82
|
+
params = {"p" => p}
|
83
|
+
r = engine.evaluate_attrs("A", %w{h m s d}, params)
|
84
|
+
r.should == [p.hour, p.min, p.sec, p.to_date]
|
123
85
|
|
124
86
|
# Non time argument should raise an error
|
125
87
|
expect { engine.evaluate_attrs("A", ["m"], {"p" => 123}) }.to raise_error
|
@@ -129,9 +91,9 @@ describe "Delorean" do
|
|
129
91
|
it "should handle DATEPART" do
|
130
92
|
engine.parse defn("A:",
|
131
93
|
" p =?",
|
132
|
-
" y =
|
133
|
-
" d =
|
134
|
-
" m =
|
94
|
+
" y = p.year()",
|
95
|
+
" d = p.day()",
|
96
|
+
" m = p.month()",
|
135
97
|
)
|
136
98
|
|
137
99
|
p = Date.today
|
@@ -140,45 +102,6 @@ describe "Delorean" do
|
|
140
102
|
|
141
103
|
# Non date argument should raise an error
|
142
104
|
expect { engine.evaluate_attrs("A", ["y", "d", "m"], {"p" => 123}) }.to raise_error
|
143
|
-
# Invalid part argument should raise an error
|
144
|
-
engine.reset
|
145
|
-
engine.parse defn("A:",
|
146
|
-
" p =?",
|
147
|
-
" x = DATEPART(p, 'x')",
|
148
|
-
)
|
149
|
-
expect { engine.evaluate_attrs("A", ["x"], {"p" => p}) }.to raise_error
|
150
|
-
end
|
151
|
-
|
152
|
-
it "should handle DATEADD" do
|
153
|
-
engine.parse defn("A:",
|
154
|
-
" p =?",
|
155
|
-
" y = DATEADD(p, 1, 'y')",
|
156
|
-
" d = DATEADD(p, 30, 'd')",
|
157
|
-
" m = DATEADD(p, 2, 'm')",
|
158
|
-
)
|
159
|
-
|
160
|
-
p = Date.today
|
161
|
-
r = engine.evaluate_attrs("A", ["y", "d", "m"], {"p" => p})
|
162
|
-
r.should == [p + 1.years, p + 30.days, p + 2.months]
|
163
|
-
|
164
|
-
# Non date argument should raise an error
|
165
|
-
expect { engine.evaluate_attrs("A", ["y", "d", "m"], {"p" => 123}) }.to raise_error
|
166
|
-
|
167
|
-
# Invalid interval argument should raise an error
|
168
|
-
engine.reset
|
169
|
-
engine.parse defn("A:",
|
170
|
-
" p =?",
|
171
|
-
" m = DATEADD(p, 1.3, 'm')",
|
172
|
-
)
|
173
|
-
expect { engine.evaluate_attrs("A", ["m"], {"p" => p}) }.to raise_error
|
174
|
-
|
175
|
-
# Invalid part argument should raise an error
|
176
|
-
engine.reset
|
177
|
-
engine.parse defn("A:",
|
178
|
-
" p =?",
|
179
|
-
" x = DATEADD(p, 1, 'x')",
|
180
|
-
)
|
181
|
-
expect { engine.evaluate_attrs("A", ["x"], {"p" => p}) }.to raise_error
|
182
105
|
end
|
183
106
|
|
184
107
|
it "should handle FLATTEN" do
|
@@ -186,7 +109,7 @@ describe "Delorean" do
|
|
186
109
|
|
187
110
|
engine.parse defn("A:",
|
188
111
|
" a = #{x}",
|
189
|
-
" b =
|
112
|
+
" b = a.flatten() + a.flatten(1)"
|
190
113
|
)
|
191
114
|
|
192
115
|
engine.evaluate("A", "b").should == x.flatten + x.flatten(1)
|
@@ -210,14 +133,16 @@ describe "Delorean" do
|
|
210
133
|
|
211
134
|
engine.parse defn("A:",
|
212
135
|
" a = #{x}",
|
213
|
-
" b =
|
214
|
-
" c =
|
136
|
+
" b = a.flatten()",
|
137
|
+
" c = a.flatten(1)",
|
215
138
|
" d = b+c",
|
216
|
-
" dd =
|
217
|
-
" e =
|
218
|
-
" f =
|
219
|
-
" g =
|
220
|
-
" gg =
|
139
|
+
" dd = d.flatten()",
|
140
|
+
" e = dd.sort()",
|
141
|
+
" f = e.uniq()",
|
142
|
+
" g = e.length()",
|
143
|
+
" gg = a.length()",
|
144
|
+
" l = a.member(5)",
|
145
|
+
" m = [a.member(5), a.member(55)]",
|
221
146
|
)
|
222
147
|
|
223
148
|
engine.evaluate("A", "c").should == x.flatten(1)
|
@@ -227,6 +152,7 @@ describe "Delorean" do
|
|
227
152
|
engine.evaluate("A", "f").should == dd.sort.uniq
|
228
153
|
engine.evaluate("A", "g").should == dd.length
|
229
154
|
engine.evaluate("A", "gg").should == x.length
|
155
|
+
engine.evaluate("A", "m").should == [x.member?(5), x.member?(55)]
|
230
156
|
end
|
231
157
|
|
232
158
|
it "should handle RUBY slice function" do
|
@@ -234,7 +160,7 @@ describe "Delorean" do
|
|
234
160
|
|
235
161
|
engine.parse defn("A:",
|
236
162
|
" a = #{x}",
|
237
|
-
" b =
|
163
|
+
" b = a.slice(0, 4)",
|
238
164
|
)
|
239
165
|
engine.evaluate("A", "b").should == x.slice(0,4)
|
240
166
|
end
|
data/spec/parse_spec.rb
CHANGED
@@ -50,6 +50,13 @@ describe "Delorean" do
|
|
50
50
|
)
|
51
51
|
end
|
52
52
|
|
53
|
+
it "can parse indexing with getattr" do
|
54
|
+
engine.parse defn("A:",
|
55
|
+
" a = {'x': [1,2,3]}",
|
56
|
+
" b = a.x[1]",
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
53
60
|
it "should accept default param definitions" do
|
54
61
|
lambda {
|
55
62
|
engine.parse defn("A:",
|
@@ -290,6 +297,15 @@ describe "Delorean" do
|
|
290
297
|
}.should_not raise_error
|
291
298
|
end
|
292
299
|
|
300
|
+
it "should parse calls followed by getattr" do
|
301
|
+
lambda {
|
302
|
+
engine.parse defn("A:",
|
303
|
+
" a = -1",
|
304
|
+
" b = A().a",
|
305
|
+
)
|
306
|
+
}.should_not raise_error
|
307
|
+
end
|
308
|
+
|
293
309
|
it "should be able to chain method calls on model functions" do
|
294
310
|
lambda {
|
295
311
|
engine.parse defn("A:",
|
@@ -304,6 +320,14 @@ describe "Delorean" do
|
|
304
320
|
)
|
305
321
|
end
|
306
322
|
|
323
|
+
it "should get exception on arg count to class method call" do
|
324
|
+
lambda {
|
325
|
+
engine.parse defn("A:",
|
326
|
+
' b = Dummy.i_just_met_you("CRJ")',
|
327
|
+
)
|
328
|
+
}.should raise_error(Delorean::BadCallError)
|
329
|
+
end
|
330
|
+
|
307
331
|
it "shouldn't be able to call ActiveRecord methods without signature" do
|
308
332
|
lambda {
|
309
333
|
engine.parse defn("A:",
|
@@ -328,10 +352,6 @@ describe "Delorean" do
|
|
328
352
|
)
|
329
353
|
end
|
330
354
|
|
331
|
-
it "should raise error on node attr access without all needed params" do
|
332
|
-
pending
|
333
|
-
end
|
334
|
-
|
335
355
|
it "should be able to access derived attrs" do
|
336
356
|
engine.parse defn("A:",
|
337
357
|
" b =? 22",
|
@@ -614,15 +634,21 @@ describe "Delorean" do
|
|
614
634
|
" a = 123",
|
615
635
|
" b = 456 + a",
|
616
636
|
" n = 'A'",
|
617
|
-
" c =
|
618
|
-
" d =
|
637
|
+
" c = nil(x: 123, y: 456)",
|
638
|
+
" d = n(x: 123, y: 456)",
|
619
639
|
)
|
620
640
|
end
|
621
641
|
|
622
642
|
it "should parse module calls by node name" do
|
623
643
|
engine.parse defn("A:",
|
624
644
|
" a = 123",
|
625
|
-
" d =
|
645
|
+
" d = A()",
|
646
|
+
)
|
647
|
+
end
|
648
|
+
|
649
|
+
it "should parse instance calls" do
|
650
|
+
engine.parse defn("A:",
|
651
|
+
" a = [1,2,[4]].flatten(1)",
|
626
652
|
)
|
627
653
|
end
|
628
654
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delorean_lang
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.00
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: treetop
|
@@ -96,7 +96,6 @@ files:
|
|
96
96
|
- lib/delorean/delorean.treetop
|
97
97
|
- lib/delorean/engine.rb
|
98
98
|
- lib/delorean/error.rb
|
99
|
-
- lib/delorean/functions.rb
|
100
99
|
- lib/delorean/model.rb
|
101
100
|
- lib/delorean/nodes.rb
|
102
101
|
- lib/delorean/version.rb
|
data/lib/delorean/functions.rb
DELETED
@@ -1,200 +0,0 @@
|
|
1
|
-
module Delorean
|
2
|
-
module Functions
|
3
|
-
|
4
|
-
######################################################################
|
5
|
-
|
6
|
-
def MAX(_e, *args)
|
7
|
-
args.max
|
8
|
-
end
|
9
|
-
|
10
|
-
MAX_SIG = [ 2, Float::INFINITY ]
|
11
|
-
|
12
|
-
######################################################################
|
13
|
-
|
14
|
-
def MIN(_e, *args)
|
15
|
-
args.min
|
16
|
-
end
|
17
|
-
|
18
|
-
MIN_SIG = MAX_SIG
|
19
|
-
|
20
|
-
######################################################################
|
21
|
-
|
22
|
-
def MAXLIST(_e, arg)
|
23
|
-
raise "argument must be list" unless arg.is_a? Array
|
24
|
-
arg.max
|
25
|
-
end
|
26
|
-
|
27
|
-
MAXLIST_SIG = [ 1, 1 ]
|
28
|
-
|
29
|
-
######################################################################
|
30
|
-
|
31
|
-
def MINLIST(_e, arg)
|
32
|
-
raise "argument must be list" unless arg.is_a? Array
|
33
|
-
arg.min
|
34
|
-
end
|
35
|
-
|
36
|
-
MINLIST_SIG = [ 1, 1 ]
|
37
|
-
|
38
|
-
######################################################################
|
39
|
-
|
40
|
-
def ROUND(_e, number, *args)
|
41
|
-
number.round(*args)
|
42
|
-
end
|
43
|
-
|
44
|
-
ROUND_SIG = [ 1, 2 ]
|
45
|
-
|
46
|
-
######################################################################
|
47
|
-
|
48
|
-
def ABS(_e, n)
|
49
|
-
raise "#{n} is not a number" unless
|
50
|
-
n.is_a?(Float) || n.is_a?(Fixnum) || n.is_a?(BigDecimal)
|
51
|
-
n.abs
|
52
|
-
end
|
53
|
-
|
54
|
-
ABS_SIG = [ 1, 1 ]
|
55
|
-
|
56
|
-
######################################################################
|
57
|
-
|
58
|
-
def NUMBER(_e, s)
|
59
|
-
# FIXME: handle BigDecimal
|
60
|
-
return s if s.is_a?(Float) || s.is_a?(Fixnum) || s.is_a?(BigDecimal)
|
61
|
-
raise "Can't convert #{s} to number" unless
|
62
|
-
s =~ /^\d+(\.\d+)?$/
|
63
|
-
|
64
|
-
s.to_f
|
65
|
-
end
|
66
|
-
|
67
|
-
NUMBER_SIG = [ 1, 1 ]
|
68
|
-
|
69
|
-
######################################################################
|
70
|
-
|
71
|
-
def STRING(_e, obj)
|
72
|
-
obj.to_s
|
73
|
-
end
|
74
|
-
|
75
|
-
STRING_SIG = [ 1, 1 ]
|
76
|
-
|
77
|
-
######################################################################
|
78
|
-
|
79
|
-
def TIMEPART(_e, time, part)
|
80
|
-
if time == Float::INFINITY
|
81
|
-
return time if part == "d"
|
82
|
-
raise "Can only access date part of Infinity"
|
83
|
-
end
|
84
|
-
|
85
|
-
raise "non-time arg to TIMEPART" unless
|
86
|
-
time.is_a?(DateTime) || time.is_a?(Time)
|
87
|
-
|
88
|
-
case part
|
89
|
-
when "h" then time.hour
|
90
|
-
when "m" then time.min
|
91
|
-
when "s" then time.sec
|
92
|
-
when "d" then time.to_date
|
93
|
-
else
|
94
|
-
raise "unknown part arg to TIMEPART"
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
TIMEPART_SIG = [ 2, 2 ]
|
99
|
-
|
100
|
-
######################################################################
|
101
|
-
|
102
|
-
def DATEPART(_e, date, part)
|
103
|
-
raise "non-date arg to DATEPART" unless date.is_a?(Date)
|
104
|
-
|
105
|
-
case part
|
106
|
-
when "m" then date.month
|
107
|
-
when "d" then date.day
|
108
|
-
when "y" then date.year
|
109
|
-
else
|
110
|
-
raise "unknown part arg to DATEPART"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
DATEPART_SIG = [ 2, 2 ]
|
115
|
-
|
116
|
-
######################################################################
|
117
|
-
|
118
|
-
def DATEADD(_e, date, interval, part)
|
119
|
-
raise "non-date arg to DATEADD" unless date.is_a?(Date)
|
120
|
-
raise "non-integer interval arg to DATEADD" unless interval.is_a?(Fixnum)
|
121
|
-
|
122
|
-
case part
|
123
|
-
when "m" then date >> interval
|
124
|
-
when "d" then date + interval
|
125
|
-
when "y" then date >> (interval * 12)
|
126
|
-
else
|
127
|
-
raise "unknown part arg to DATEADD"
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
DATEADD_SIG = [ 3, 3 ]
|
132
|
-
|
133
|
-
######################################################################
|
134
|
-
|
135
|
-
def FLATTEN(_e, array, *args)
|
136
|
-
raise "non-array arg to FLATTEN" unless array.is_a?(Array)
|
137
|
-
raise "non-integer flatten on call to FLATTEN" unless
|
138
|
-
(args.empty? || args[0].is_a?(Fixnum))
|
139
|
-
array.flatten(*args)
|
140
|
-
end
|
141
|
-
|
142
|
-
FLATTEN_SIG = [ 1, 2 ]
|
143
|
-
|
144
|
-
######################################################################
|
145
|
-
|
146
|
-
def ERR(_e, *args)
|
147
|
-
str = args.map(&:to_s).join(", ")
|
148
|
-
raise str
|
149
|
-
end
|
150
|
-
|
151
|
-
ERR_SIG = [ 1, Float::INFINITY ]
|
152
|
-
|
153
|
-
######################################################################
|
154
|
-
|
155
|
-
RUBY_METHODS = {
|
156
|
-
sort: [Array],
|
157
|
-
reverse: [Array],
|
158
|
-
min: [Array],
|
159
|
-
max: [Array],
|
160
|
-
uniq: [Array],
|
161
|
-
length: [[Array, String]],
|
162
|
-
flatten: [Array, [Fixnum, nil]],
|
163
|
-
slice: [Array, Fixnum, Fixnum],
|
164
|
-
member?: [Array, [Fixnum, String]],
|
165
|
-
compact: [Array],
|
166
|
-
split: [String, String],
|
167
|
-
}
|
168
|
-
|
169
|
-
def RUBY(_e, method, *args)
|
170
|
-
raise "method must be a string" unless method.class.name=="String"
|
171
|
-
msg = method.to_sym
|
172
|
-
|
173
|
-
raise "no such method #{method}" unless RUBY_METHODS.member? msg
|
174
|
-
|
175
|
-
sig = RUBY_METHODS[msg]
|
176
|
-
raise "too many args to #{method}" if args.length>sig.length
|
177
|
-
|
178
|
-
sig.each_with_index { |s, i|
|
179
|
-
s = [s] unless s.is_a?(Array)
|
180
|
-
|
181
|
-
ok = false
|
182
|
-
s.each { |sc|
|
183
|
-
if (sc.nil? && i>=args.length) || (sc && args[i].class <= sc)
|
184
|
-
ok = true
|
185
|
-
break
|
186
|
-
end
|
187
|
-
}
|
188
|
-
|
189
|
-
raise "bad arg #{i} to method #{method}: #{args[i]}/#{args[i].class}" unless ok
|
190
|
-
}
|
191
|
-
|
192
|
-
args[0].send(msg, *args[1, args.length])
|
193
|
-
end
|
194
|
-
|
195
|
-
RUBY_SIG = [ 1, Float::INFINITY ]
|
196
|
-
|
197
|
-
######################################################################
|
198
|
-
|
199
|
-
end
|
200
|
-
end
|