delorean_lang 0.5.1 → 0.5.2

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