delorean_lang 0.5.1 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.gitlab-ci.yml +1 -2
  3. data/.rubocop.yml +45 -5
  4. data/.rubocop_todo.yml +1 -634
  5. data/Gemfile +3 -1
  6. data/README.md +22 -0
  7. data/Rakefile +3 -1
  8. data/delorean.gemspec +18 -17
  9. data/lib/delorean/abstract_container.rb +4 -2
  10. data/lib/delorean/base.rb +30 -27
  11. data/lib/delorean/cache.rb +2 -0
  12. data/lib/delorean/cache/adapters.rb +2 -0
  13. data/lib/delorean/cache/adapters/base.rb +2 -0
  14. data/lib/delorean/cache/adapters/ruby_cache.rb +5 -0
  15. data/lib/delorean/const.rb +5 -3
  16. data/lib/delorean/debug.rb +6 -5
  17. data/lib/delorean/delorean.rb +466 -147
  18. data/lib/delorean/delorean.treetop +13 -1
  19. data/lib/delorean/engine.rb +61 -50
  20. data/lib/delorean/error.rb +2 -1
  21. data/lib/delorean/model.rb +12 -9
  22. data/lib/delorean/nodes.rb +130 -67
  23. data/lib/delorean/ruby.rb +2 -0
  24. data/lib/delorean/ruby/whitelists.rb +2 -0
  25. data/lib/delorean/ruby/whitelists/base.rb +7 -3
  26. data/lib/delorean/ruby/whitelists/default.rb +6 -6
  27. data/lib/delorean/ruby/whitelists/empty.rb +3 -2
  28. data/lib/delorean/ruby/whitelists/matchers.rb +2 -0
  29. data/lib/delorean/ruby/whitelists/matchers/arguments.rb +2 -0
  30. data/lib/delorean/ruby/whitelists/matchers/method.rb +5 -2
  31. data/lib/delorean/ruby/whitelists/whitelist_error.rb +2 -0
  32. data/lib/delorean/version.rb +3 -1
  33. data/lib/delorean_lang.rb +3 -1
  34. data/spec/cache_spec.rb +4 -2
  35. data/spec/dev_spec.rb +68 -69
  36. data/spec/eval_spec.rb +824 -729
  37. data/spec/func_spec.rb +172 -176
  38. data/spec/parse_spec.rb +516 -522
  39. data/spec/ruby/whitelist_spec.rb +6 -3
  40. data/spec/spec_helper.rb +26 -23
  41. metadata +27 -27
@@ -1,298 +1,294 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ # frozen_string_literal: true
2
2
 
3
- describe "Delorean" do
3
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
4
4
 
5
- let(:engine) {
6
- Delorean::Engine.new("ZZZ")
7
- }
5
+ describe 'Delorean' do
6
+ let(:engine) do
7
+ Delorean::Engine.new('ZZZ')
8
+ end
8
9
 
9
- it "should handle MAX as a node name" do
10
- engine.parse defn("MAX:",
11
- " a = [1, 2, 3, 0, -10].max()",
12
- )
10
+ it 'should handle MAX as a node name' do
11
+ engine.parse defn('MAX:',
12
+ ' a = [1, 2, 3, 0, -10].max()',
13
+ )
13
14
 
14
- r = engine.evaluate("MAX", "a")
15
+ r = engine.evaluate('MAX', 'a')
15
16
  r.should == 3
16
17
  end
17
18
 
18
- it "should handle COMPACT" do
19
- engine.parse defn("A:",
20
- " a = [1, 2, nil, -3, 4].compact",
19
+ it 'should handle COMPACT' do
20
+ engine.parse defn('A:',
21
+ ' a = [1, 2, nil, -3, 4].compact',
21
22
  " b = {'a': 1, 'b': nil, 'c': nil}.compact()",
22
- )
23
+ )
23
24
 
24
- expect(engine.evaluate("A", "a")).to eq([1, 2, -3, 4])
25
- expect(engine.evaluate("A", "b")).to eq({"a" => 1})
25
+ expect(engine.evaluate('A', 'a')).to eq([1, 2, -3, 4])
26
+ expect(engine.evaluate('A', 'b')).to eq('a' => 1)
26
27
  end
27
28
 
28
- it "should handle MIN" do
29
- engine.parse defn("A:",
30
- " a = [1, 2, -3, 4].min()",
31
- )
29
+ it 'should handle MIN' do
30
+ engine.parse defn('A:',
31
+ ' a = [1, 2, -3, 4].min()',
32
+ )
32
33
 
33
- r = engine.evaluate("A", "a")
34
+ r = engine.evaluate('A', 'a')
34
35
  r.should == -3
35
36
  end
36
37
 
37
- it "should handle ROUND" do
38
- engine.parse defn("A:",
39
- " a = 12.3456.round(2)",
40
- " b = 12.3456.round(1)",
41
- " c = 12.3456.round()",
42
- )
38
+ it 'should handle ROUND' do
39
+ engine.parse defn('A:',
40
+ ' a = 12.3456.round(2)',
41
+ ' b = 12.3456.round(1)',
42
+ ' c = 12.3456.round()',
43
+ )
43
44
 
44
- r = engine.evaluate("A", ["a", "b", "c"])
45
+ r = engine.evaluate('A', ['a', 'b', 'c'])
45
46
  r.should == [12.35, 12.3, 12]
46
47
  end
47
48
 
48
- it "should handle TRUNCATE" do
49
- engine.parse defn("A:",
50
- " a = 12.3456.truncate(2)",
51
- " b = 12.3456.truncate(1)",
52
- " c = 12.3456.truncate()",
53
- )
49
+ it 'should handle TRUNCATE' do
50
+ engine.parse defn('A:',
51
+ ' a = 12.3456.truncate(2)',
52
+ ' b = 12.3456.truncate(1)',
53
+ ' c = 12.3456.truncate()',
54
+ )
54
55
 
55
- r = engine.evaluate("A", ["a", "b", "c"])
56
+ r = engine.evaluate('A', ['a', 'b', 'c'])
56
57
  r.should == [12.34, 12.3, 12]
57
58
  end
58
59
 
59
- it "should handle FLOOR" do
60
- engine.parse defn("A:",
61
- " a = [12.3456.floor(), 13.7890.floor()]",
62
- )
60
+ it 'should handle FLOOR' do
61
+ engine.parse defn('A:',
62
+ ' a = [12.3456.floor(), 13.7890.floor()]',
63
+ )
63
64
 
64
- r = engine.evaluate("A", "a")
65
+ r = engine.evaluate('A', 'a')
65
66
  r.should == [12, 13]
66
67
  end
67
68
 
68
- it "should handle TO_F" do
69
- engine.parse defn("A:",
70
- " a = 12.3456.to_f()",
69
+ it 'should handle TO_F' do
70
+ engine.parse defn('A:',
71
+ ' a = 12.3456.to_f()',
71
72
  " b = '12.3456'.to_f()",
72
73
  " c = '12'.to_f()",
73
74
  " d = '2018-05-04 10:56:27 -0700'.to_time.to_f",
74
- )
75
+ )
75
76
 
76
- r = engine.evaluate("A", ["a", "b", "c", "d"])
77
- r.should == [12.3456, 12.3456, 12, 1525456587.0]
77
+ r = engine.evaluate('A', ['a', 'b', 'c', 'd'])
78
+ r.should == [12.3456, 12.3456, 12, 1_525_456_587.0]
78
79
  end
79
80
 
80
- it "should handle ABS" do
81
- engine.parse defn("A:",
82
- " a = (-123).abs()",
83
- " b = (-1.1).abs()",
84
- " c = 2.3.abs()",
85
- " d = 0.abs()",
86
- )
81
+ it 'should handle ABS' do
82
+ engine.parse defn('A:',
83
+ ' a = (-123).abs()',
84
+ ' b = (-1.1).abs()',
85
+ ' c = 2.3.abs()',
86
+ ' d = 0.abs()',
87
+ )
87
88
 
88
- r = engine.evaluate("A", ["a", "b", "c", "d"])
89
+ r = engine.evaluate('A', ['a', 'b', 'c', 'd'])
89
90
  r.should == [123, 1.1, 2.3, 0]
90
91
  end
91
92
 
92
- it "should handle STRING" do
93
- engine.parse defn("A:",
93
+ it 'should handle STRING' do
94
+ engine.parse defn('A:',
94
95
  " a = 'hello'.to_s()",
95
- " b = 12.3456.to_s()",
96
- " c = [1,2,3].to_s()",
97
- )
96
+ ' b = 12.3456.to_s()',
97
+ ' c = [1,2,3].to_s()',
98
+ )
98
99
 
99
- r = engine.evaluate("A", ["a", "b", "c"])
100
- r.should == ["hello", '12.3456', [1,2,3].to_s]
100
+ r = engine.evaluate('A', ['a', 'b', 'c'])
101
+ r.should == ['hello', '12.3456', [1, 2, 3].to_s]
101
102
  end
102
103
 
103
- it "should handle FETCH" do
104
- engine.parse defn("A:",
104
+ it 'should handle FETCH' do
105
+ engine.parse defn('A:',
105
106
  " h = {'a':123, 1:111}",
106
107
  " a = h.fetch('a')",
107
- " b = h.fetch(1)",
108
+ ' b = h.fetch(1)',
108
109
  " c = h.fetch('xxx', 456)",
109
- )
110
+ )
110
111
 
111
- r = engine.evaluate("A", ["a", "b", "c"])
112
+ r = engine.evaluate('A', ['a', 'b', 'c'])
112
113
  r.should == [123, 111, 456]
113
114
  end
114
115
 
115
- it "should handle TIMEPART" do
116
- engine.parse defn("A:",
117
- " p =?",
118
- " h = p.hour()",
119
- " m = p.min()",
120
- " s = p.sec()",
121
- " d = p.to_date()",
122
- " e = p.to_date.to_s.to_date",
123
- )
116
+ it 'should handle TIMEPART' do
117
+ engine.parse defn('A:',
118
+ ' p =?',
119
+ ' h = p.hour()',
120
+ ' m = p.min()',
121
+ ' s = p.sec()',
122
+ ' d = p.to_date()',
123
+ ' e = p.to_date.to_s.to_date',
124
+ )
124
125
 
125
126
  p = Time.now
126
- params = {"p" => p}
127
- r = engine.evaluate("A", %w{h m s d e}, params)
127
+ params = { 'p' => p }
128
+ r = engine.evaluate('A', %w[h m s d e], params)
128
129
  r.should == [p.hour, p.min, p.sec, p.to_date, p.to_date]
129
130
 
130
131
  # Non time argument should raise an error
131
- expect { engine.evaluate("A", ["m"], {"p" => 123}) }.to raise_error
132
-
132
+ expect { engine.evaluate('A', ['m'], 'p' => 123) }.to raise_error
133
133
  end
134
134
 
135
- it "should handle DATEPART" do
136
- engine.parse defn("A:",
137
- " p =?",
138
- " y = p.year()",
139
- " d = p.day()",
140
- " m = p.month()",
141
- )
135
+ it 'should handle DATEPART' do
136
+ engine.parse defn('A:',
137
+ ' p =?',
138
+ ' y = p.year()',
139
+ ' d = p.day()',
140
+ ' m = p.month()',
141
+ )
142
142
 
143
143
  p = Date.today
144
- r = engine.evaluate("A", ["y", "d", "m"], {"p" => p})
144
+ r = engine.evaluate('A', ['y', 'd', 'm'], 'p' => p)
145
145
  r.should == [p.year, p.day, p.month]
146
146
 
147
147
  # Non date argument should raise an error
148
- expect {
149
- engine.evaluate("A", ["y", "d", "m"], {"p" => 123})
150
- }.to raise_error
148
+ expect do
149
+ engine.evaluate('A', ['y', 'd', 'm'], 'p' => 123)
150
+ end.to raise_error
151
151
  end
152
152
 
153
- it "should handle FLATTEN" do
154
- x = [[1,2,[3]], 4, 5, [6]]
153
+ it 'should handle FLATTEN' do
154
+ x = [[1, 2, [3]], 4, 5, [6]]
155
155
 
156
- engine.parse defn("A:",
156
+ engine.parse defn('A:',
157
157
  " a = #{x}",
158
- " b = a.flatten() + a.flatten(1)"
159
- )
158
+ ' b = a.flatten() + a.flatten(1)'
159
+ )
160
160
 
161
- engine.evaluate("A", "b").should == x.flatten + x.flatten(1)
161
+ engine.evaluate('A', 'b').should == x.flatten + x.flatten(1)
162
162
  end
163
163
 
164
- it "should handle ZIP" do
164
+ it 'should handle ZIP' do
165
165
  a = [1, 2]
166
166
  b = [4, 5, 6]
167
167
  c = [7, 8]
168
168
 
169
- engine.parse defn("A:",
169
+ engine.parse defn('A:',
170
170
  " a = #{a}",
171
171
  " b = #{b}",
172
172
  " c = #{c}",
173
- " d = a.zip(b) + a.zip(b, c)",
174
- )
173
+ ' d = a.zip(b) + a.zip(b, c)',
174
+ )
175
175
 
176
- expect(engine.evaluate("A", "d")).to eq a.zip(b) + a.zip(b, c)
176
+ expect(engine.evaluate('A', 'd')).to eq a.zip(b) + a.zip(b, c)
177
177
  end
178
178
 
179
- it "should handle ERR" do
180
- engine.parse defn("A:",
179
+ it 'should handle ERR' do
180
+ engine.parse defn('A:',
181
181
  " a = ERR('hello')",
182
182
  " b = ERR('xx', 1, 2, 3)",
183
- )
183
+ )
184
184
 
185
- expect { engine.evaluate("A", "a") }.to raise_error('hello')
185
+ expect { engine.evaluate('A', 'a') }.to raise_error('hello')
186
186
 
187
187
  lambda {
188
- r = engine.evaluate("A", "b")
189
- }.should raise_error("xx, 1, 2, 3")
188
+ engine.evaluate('A', 'b')
189
+ }.should raise_error('xx, 1, 2, 3')
190
190
  end
191
191
 
192
- it "should handle RUBY" do
192
+ it 'should handle RUBY' do
193
193
  x = [[1, 2, [-3]], 4, 5, [6], -3, 4, 5, 0]
194
194
 
195
- engine.parse defn("A:",
195
+ engine.parse defn('A:',
196
196
  " a = #{x}",
197
- " b = a.flatten()",
198
- " c = a.flatten(1)",
199
- " d = b+c",
200
- " dd = d.flatten()",
201
- " e = dd.sort()",
202
- " f = e.uniq()",
203
- " g = e.length",
204
- " gg = a.length()",
205
- " l = a.member(5)",
206
- " m = [a.member(5), a.member(55)]",
197
+ ' b = a.flatten()',
198
+ ' c = a.flatten(1)',
199
+ ' d = b+c',
200
+ ' dd = d.flatten()',
201
+ ' e = dd.sort()',
202
+ ' f = e.uniq()',
203
+ ' g = e.length',
204
+ ' gg = a.length()',
205
+ ' l = a.member(5)',
206
+ ' m = [a.member(5), a.member(55)]',
207
207
  " n = {'a':1, 'b':2, 'c':3}.length()",
208
208
  " o = 'hello'.length",
209
- )
210
-
211
- engine.evaluate("A", "c").should == x.flatten(1)
212
- d = engine.evaluate("A", "d").should == x.flatten + x.flatten(1)
213
- dd = engine.evaluate("A", "dd")
214
- engine.evaluate("A", "e").should == dd.sort
215
- engine.evaluate("A", "f").should == dd.sort.uniq
216
- engine.evaluate("A", "g").should == dd.length
217
- engine.evaluate("A", "gg").should == x.length
218
- engine.evaluate("A", "m").should == [x.member?(5), x.member?(55)]
219
- engine.evaluate("A", "n").should == 3
220
- engine.evaluate("A", "o").should == 5
209
+ )
210
+
211
+ engine.evaluate('A', 'c').should == x.flatten(1)
212
+ engine.evaluate('A', 'd').should == x.flatten + x.flatten(1)
213
+ dd = engine.evaluate('A', 'dd')
214
+ engine.evaluate('A', 'e').should == dd.sort
215
+ engine.evaluate('A', 'f').should == dd.sort.uniq
216
+ engine.evaluate('A', 'g').should == dd.length
217
+ engine.evaluate('A', 'gg').should == x.length
218
+ engine.evaluate('A', 'm').should == [x.member?(5), x.member?(55)]
219
+ engine.evaluate('A', 'n').should == 3
220
+ engine.evaluate('A', 'o').should == 5
221
221
  end
222
222
 
223
- it "should be able to call function on hash" do
223
+ it 'should be able to call function on hash' do
224
224
  # FIXME: this is actually a Delorean design issue. How do
225
225
  # whitelisted functions interact with attrs? In this case, we
226
226
  # return nil since there is no Delorean 'length' attr in the hash.
227
227
  skip 'Delorean design issue to be resolved'
228
228
 
229
- engine.parse defn("A:",
230
- " n = {}.length",
229
+ engine.parse defn('A:',
230
+ ' n = {}.length',
231
231
  " m = {'length':100}.length",
232
- )
233
- engine.evaluate("A", "n").should == 0
234
- engine.evaluate("A", "m").should == 100
232
+ )
233
+ engine.evaluate('A', 'n').should == 0
234
+ engine.evaluate('A', 'm').should == 100
235
235
  end
236
236
 
237
- it "should be able to call hash except" do
238
- engine.parse defn("A:",
237
+ it 'should be able to call hash except' do
238
+ engine.parse defn('A:',
239
239
  " h = {'a': 1, 'b':2, 'c': 3}",
240
240
  " e = h.except('a', 'c')",
241
- )
242
- expect(engine.evaluate("A", "e")).to eq({"b"=>2})
241
+ )
242
+ expect(engine.evaluate('A', 'e')).to eq('b' => 2)
243
243
  end
244
244
 
245
- it "should handle RUBY slice function" do
245
+ it 'should handle RUBY slice function' do
246
246
  x = [[1, 2, [-3]], 4, [5, 6], -3, 4, 5, 0]
247
247
 
248
- engine.parse defn("A:",
248
+ engine.parse defn('A:',
249
249
  " a = #{x}",
250
- " b = a.slice(0, 4)",
251
- )
252
- engine.evaluate("A", "b").should == x.slice(0,4)
250
+ ' b = a.slice(0, 4)',
251
+ )
252
+ engine.evaluate('A', 'b').should == x.slice(0, 4)
253
253
  end
254
254
 
255
- it "should handle RUBY empty? function" do
256
- engine.parse defn("A:",
257
- " a0 = []",
258
- " b0 = {}",
259
- " c0 = {-}",
260
- " a1 = [1,2,3]",
255
+ it 'should handle RUBY empty? function' do
256
+ engine.parse defn('A:',
257
+ ' a0 = []',
258
+ ' b0 = {}',
259
+ ' c0 = {-}',
260
+ ' a1 = [1,2,3]',
261
261
  " b1 = {'a': 1, 'b':2}",
262
- " c1 = {1,2,3}",
263
- " res = [a0.empty, b0.empty(), c0.empty, a1.empty, b1.empty(), c1.empty]",
264
- )
265
- engine.evaluate("A", "res").should == [true, true, true, false, false, false]
262
+ ' c1 = {1,2,3}',
263
+ ' res = [a0.empty, b0.empty(), c0.empty, a1.empty, b1.empty(), c1.empty]',
264
+ )
265
+ engine.evaluate('A', 'res').should == [true, true, true, false, false, false]
266
266
  end
267
267
 
268
- it "should handle BETWEEN" do
269
- engine.parse defn("A:",
270
- " a = 1.23",
271
- " number_between = [a.between(10,20), a.between(1,3)]",
272
-
268
+ it 'should handle BETWEEN' do
269
+ engine.parse defn('A:',
270
+ ' a = 1.23',
271
+ ' number_between = [a.between(10,20), a.between(1,3)]',
273
272
  " c = 'c'",
274
273
  " string_between = [c.between('a', 'd'), c.between('d', 'e')]",
275
-
276
274
  " types_mismatch1 = [a.between('a', 'd')]",
277
- " types_mismatch2 = [c.between(1, 3)]"
278
- )
275
+ ' types_mismatch2 = [c.between(1, 3)]'
276
+ )
279
277
 
280
- expect(engine.evaluate("A", "number_between")).to eq([false, true])
281
- expect(engine.evaluate("A", "string_between")).to eq([true, false])
278
+ expect(engine.evaluate('A', 'number_between')).to eq([false, true])
279
+ expect(engine.evaluate('A', 'string_between')).to eq([true, false])
282
280
 
283
- expect { engine.evaluate("A", "types_mismatch1") }.to raise_error(/bad arg/)
284
- expect { engine.evaluate("A", "types_mismatch2") }.to raise_error(/bad arg/)
281
+ expect { engine.evaluate('A', 'types_mismatch1') }.to raise_error(/bad arg/)
282
+ expect { engine.evaluate('A', 'types_mismatch2') }.to raise_error(/bad arg/)
285
283
  end
286
284
 
287
-
288
- it "should handle MATCH" do
289
- engine.parse defn("A:",
285
+ it 'should handle MATCH' do
286
+ engine.parse defn('A:',
290
287
  " a = 'this is a test'.match('(.*)( is )(.*)')",
291
- " b = [a[0], a[1], a[2], a[3], a[4]]",
292
- )
288
+ ' b = [a[0], a[1], a[2], a[3], a[4]]',
289
+ )
293
290
 
294
- expect(engine.evaluate("A", "b")).
295
- to eq(["this is a test", "this", " is ", "a test", nil])
291
+ expect(engine.evaluate('A', 'b'))
292
+ .to eq(['this is a test', 'this', ' is ', 'a test', nil])
296
293
  end
297
-
298
294
  end