delorean_lang 0.3.24 → 0.3.25

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c3b6cbfa5893e3c1fe6e5e8c72808ffb426a9b6e
4
- data.tar.gz: 277eacdb6725be1e1c06c9efe1926b62437ac19c
3
+ metadata.gz: 7a874caf5f4ff132d1358f1200e1bdbe8922d968
4
+ data.tar.gz: 2953cf29508714e114dc2f35b1cec643133fdfdb
5
5
  SHA512:
6
- metadata.gz: efee348e65cd6d9bd2ca26d25815e840122d40a45811aa4e55a73264506b3f57ea079dac101b5c8a93bdee073d7d794311e5d89d1fef39d6a22bf49dc776caee
7
- data.tar.gz: 2743e5e5b12e2165c60546dffcd808e189a6c5acd58efd0b07efba1156a25a692c7d090ebcbcb3c61018efa22cdd3a5991658567c4324f8810a5dd96c4e3ae75
6
+ metadata.gz: fcfc877b4bae858ce9988d371502ea9625b02e93b8046b7861c3f6f505b8b0f57a30878482b5d92bf3ef18935241383f9fddb3c61a243fec328ce0f6a483aef0
7
+ data.tar.gz: e2cfcda4485deca3b94169c26dfd5281778a1850b426fe08d42ad1c8edf3048f0c4452b2e2f262801aee9cde9a8ede2d63b56c99a5b8324bb0a141cf55a36d18
@@ -4,8 +4,9 @@ require 'bigdecimal'
4
4
 
5
5
  module Delorean
6
6
 
7
- # FIXME: add string.gsub; Also, should be able to index regex
8
- # matches. Need string.length.
7
+ DT_TYPES = [Date, Time, ActiveSupport::TimeWithZone]
8
+ NUM_OR_STR = [Numeric, String]
9
+ NUM_OR_NIL = [nil, Fixnum]
9
10
 
10
11
  # FIXME: the whitelist is quite hacky. It's currently difficult to
11
12
  # override it. A user will likely want to directly modify this
@@ -13,12 +14,12 @@ module Delorean
13
14
  # rethought.
14
15
  RUBY_WHITELIST = {
15
16
  attributes: [ActiveRecord::Base],
16
- between?: [[Numeric, String],[Numeric, String],[Numeric, String]],
17
+ between?: [NUM_OR_STR, NUM_OR_STR, NUM_OR_STR],
17
18
  between: "between?",
18
19
  compact: [Array],
19
20
  to_set: [Array],
20
- flatten: [Array, [Fixnum, nil]],
21
- length: [Enumerable],
21
+ flatten: [Array, NUM_OR_NIL],
22
+ length: [[String, Enumerable]],
22
23
  max: [Array],
23
24
  member: "member?",
24
25
  member?: [Enumerable, [Object]],
@@ -33,11 +34,11 @@ module Delorean
33
34
  sum: [Array],
34
35
  transpose: [Array],
35
36
  join: [Array, String],
36
- zip: [Array, [Array, Array, Array]],
37
+ zip: [Array, Array, [Array, nil], [Array, nil]],
37
38
  index: [Array, [Object]],
38
39
  product: [Array, Array],
39
- first: [[ActiveRecord::Relation, Enumerable], [nil, Fixnum]],
40
- last: [[ActiveRecord::Relation, Enumerable], [nil, Fixnum]],
40
+ first: [[ActiveRecord::Relation, Enumerable], NUM_OR_NIL],
41
+ last: [[ActiveRecord::Relation, Enumerable], NUM_OR_NIL],
41
42
  intersection: [Set, Enumerable],
42
43
  union: [Set, Enumerable],
43
44
 
@@ -45,39 +46,30 @@ module Delorean
45
46
  values: [Hash],
46
47
  upcase: [String],
47
48
  downcase: [String],
48
- match: [String, [String], [nil, Fixnum]],
49
-
50
- iso8601: [[Date, Time, ActiveSupport::TimeWithZone]],
51
- hour: [[Date, Time, ActiveSupport::TimeWithZone]],
52
- min: [[Date, Time, ActiveSupport::TimeWithZone, Array]],
53
- sec: [[Date, Time, ActiveSupport::TimeWithZone]],
54
- to_date: [[Date, Time, ActiveSupport::TimeWithZone, String]],
55
-
56
- month: [[Date, Time, ActiveSupport::TimeWithZone]],
57
- day: [[Date, Time, ActiveSupport::TimeWithZone]],
58
- year: [[Date, Time, ActiveSupport::TimeWithZone]],
59
-
60
- next_month: [[Date, Time, ActiveSupport::TimeWithZone],
61
- [nil, Fixnum],
62
- ],
63
- prev_month: [[Date, Time, ActiveSupport::TimeWithZone],
64
- [nil, Fixnum],
65
- ],
66
-
67
- beginning_of_month: [[Date, Time, ActiveSupport::TimeWithZone]],
68
-
69
- end_of_month: [[Date, Time, ActiveSupport::TimeWithZone]],
70
-
71
- next_day: [[Date, Time, ActiveSupport::TimeWithZone],
72
- [nil, Fixnum],
73
- ],
74
- prev_day: [[Date, Time, ActiveSupport::TimeWithZone],
75
- [nil, Fixnum],
76
- ],
77
-
78
- to_i: [[Numeric, String]],
79
- to_f: [[Numeric, String]],
80
- to_d: [[Numeric, String]],
49
+ match: [String, [String], NUM_OR_NIL],
50
+
51
+ iso8601: [DT_TYPES],
52
+ hour: [DT_TYPES],
53
+ min: [DT_TYPES+[Array]],
54
+ sec: [DT_TYPES],
55
+ to_date: [DT_TYPES+[String]],
56
+
57
+ month: [DT_TYPES],
58
+ day: [DT_TYPES],
59
+ year: [DT_TYPES],
60
+
61
+ next_month: [DT_TYPES, NUM_OR_NIL],
62
+ prev_month: [DT_TYPES, NUM_OR_NIL],
63
+
64
+ beginning_of_month: [DT_TYPES],
65
+ end_of_month: [DT_TYPES],
66
+
67
+ next_day: [DT_TYPES, NUM_OR_NIL],
68
+ prev_day: [DT_TYPES, NUM_OR_NIL],
69
+
70
+ to_i: [NUM_OR_STR],
71
+ to_f: [NUM_OR_STR],
72
+ to_d: [NUM_OR_STR],
81
73
  to_s: [Object],
82
74
  to_a: [Object],
83
75
  to_json: [Object],
@@ -97,15 +89,14 @@ module Delorean
97
89
  end
98
90
 
99
91
  def /(args)
100
- raise "non-array/string arg to /" unless
101
- args.is_a?(Array) || args.is_a?(String)
102
-
103
92
  begin
104
93
  case args
105
94
  when Array
106
95
  engine.eval_to_hash(node, args, params.clone)
107
96
  when String
108
97
  engine.evaluate(node, args, params.clone)
98
+ else
99
+ raise "non-array/string arg to /"
109
100
  end
110
101
  rescue => exc
111
102
  Delorean::Engine.grok_runtime_exception(exc)
@@ -134,30 +125,25 @@ module Delorean
134
125
 
135
126
  class BaseClass
136
127
  def self._get_attr(obj, attr, _e)
137
- # FIXME: even Javascript which is superpermissive raises an
138
- # exception on null getattr.
139
- return nil if obj.nil?
140
-
141
128
  # NOTE: should keep this function consistent with _index
142
-
143
- if obj.kind_of? ActiveRecord::Base
144
- klass = obj.class
145
-
146
- return obj.read_attribute(attr) if
147
- klass.attribute_names.member? attr
148
-
149
- return obj.send(attr.to_sym) if
150
- klass.reflect_on_all_associations.map(&:name).member? attr.to_sym
151
- elsif obj.instance_of?(NodeCall)
129
+ case obj
130
+ when nil
131
+ # FIXME: even Javascript which is superpermissive raises an
132
+ # exception on null getattr.
133
+ return nil
134
+ when ActiveRecord::Base
135
+ return obj.read_attribute(attr) if obj.has_attribute?(attr)
136
+ return obj.send(attr.to_sym) if obj.class.reflections[attr]
137
+ when NodeCall
152
138
  return obj.evaluate(attr)
153
- elsif obj.instance_of?(Hash)
139
+ when Hash
154
140
  # FIXME: this implementation doesn't handle something like
155
141
  # {}.length. i.e. length is a whitelisted function, but not
156
142
  # an attr. This implementation returns nil instead of 0.
157
143
  return obj[attr] if obj.member?(attr)
158
144
  return attr.is_a?(String) ? obj[attr.to_sym] : nil
159
- elsif obj.instance_of?(Class) && (obj < BaseClass)
160
- return obj.send((attr + POST).to_sym, _e)
145
+ when Class
146
+ return obj.send((attr + POST).to_sym, _e) if obj < BaseClass
161
147
  end
162
148
 
163
149
  begin
@@ -171,18 +157,19 @@ module Delorean
171
157
  ######################################################################
172
158
 
173
159
  def self._index(obj, args, _e)
174
- return nil if obj.nil?
175
-
176
160
  # NOTE: should keep this function consistent with _get_attr
177
-
178
- if obj.instance_of?(Hash) || obj.kind_of?(ActiveRecord::Base) ||
179
- obj.instance_of?(NodeCall) || obj.instance_of?(Class)
161
+ case obj
162
+ when nil
163
+ # FIXME: even Javascript which is superpermissive raises an
164
+ # exception on null getattr.
165
+ return nil
166
+ when Hash, ActiveRecord::Base, NodeCall, Class
180
167
  raise InvalidIndex unless args.length == 1
181
168
  _get_attr(obj, args[0], _e)
182
- elsif obj.instance_of?(Array) || obj.instance_of?(String)
183
- raise InvalidIndex unless args.length <= 2
184
- raise InvalidIndex unless
185
- args[0].is_a?(Fixnum) && (!args[1] || args[1].is_a?(Fixnum))
169
+ when Array, String, MatchData
170
+ raise InvalidIndex unless args.length <= 2 &&
171
+ args[0].is_a?(Fixnum) &&
172
+ (args[1].nil? || args[1].is_a?(Fixnum))
186
173
  obj[*args]
187
174
  else
188
175
  raise InvalidIndex
@@ -234,11 +221,9 @@ module Delorean
234
221
  return obj.send(msg, *args)
235
222
  end
236
223
 
237
- if obj.class.include?(Delorean::Model)
238
- sig = obj.class.delorean_instance_methods[msg]
239
- end
240
-
241
- sig = RUBY_WHITELIST[msg] unless sig
224
+ cls = obj.class
225
+ sig = (cls < Delorean::Model && cls.delorean_instance_methods[msg]) ||
226
+ RUBY_WHITELIST[msg]
242
227
 
243
228
  raise "no such method #{method}" unless sig
244
229
 
@@ -249,19 +234,15 @@ module Delorean
249
234
 
250
235
  arglist = [obj] + args
251
236
 
252
- sig.each_with_index { |s, i|
237
+ sig.each_with_index do |s, i|
253
238
  s = [s] unless s.is_a?(Array)
254
239
 
255
- ok, ai = false, arglist[i]
240
+ ai = arglist[i]
256
241
 
257
- s.each { |sc|
258
- if (sc.nil? && i>=arglist.length) || (sc && ai.class <= sc)
259
- ok = true
260
- break
261
- end
262
- }
263
- raise "bad arg #{i}, method #{method}: #{ai}/#{ai.class} #{s}" if !ok
264
- }
242
+ raise "bad arg #{i}, method #{method}: #{ai}/#{ai.class} #{s}" unless
243
+ (s.member?(nil) && i>=arglist.length) ||
244
+ s.detect {|sc| sc && ai.class <= sc}
245
+ end
265
246
 
266
247
  res = obj.send(msg, *args)
267
248
  # FIXME: can't freeze AR relations since then we can't chain
@@ -2234,142 +2234,154 @@ module Delorean
2234
2234
  r6 = SyntaxNode.new(input, (index-1)...index) if r6 == true
2235
2235
  r0 = r6
2236
2236
  else
2237
- if (match_len = has_terminal?('>', false, index))
2238
- r7 = true
2237
+ if (match_len = has_terminal?('**', false, index))
2238
+ r7 = instantiate_node(SyntaxNode,input, index...(index + match_len))
2239
2239
  @index += match_len
2240
2240
  else
2241
- terminal_parse_failure('\'>\'')
2241
+ terminal_parse_failure('\'**\'')
2242
2242
  r7 = nil
2243
2243
  end
2244
2244
  if r7
2245
2245
  r7 = SyntaxNode.new(input, (index-1)...index) if r7 == true
2246
2246
  r0 = r7
2247
2247
  else
2248
- if (match_len = has_terminal?('<', false, index))
2248
+ if (match_len = has_terminal?('>', false, index))
2249
2249
  r8 = true
2250
2250
  @index += match_len
2251
2251
  else
2252
- terminal_parse_failure('\'<\'')
2252
+ terminal_parse_failure('\'>\'')
2253
2253
  r8 = nil
2254
2254
  end
2255
2255
  if r8
2256
2256
  r8 = SyntaxNode.new(input, (index-1)...index) if r8 == true
2257
2257
  r0 = r8
2258
2258
  else
2259
- if (match_len = has_terminal?('+', false, index))
2259
+ if (match_len = has_terminal?('<', false, index))
2260
2260
  r9 = true
2261
2261
  @index += match_len
2262
2262
  else
2263
- terminal_parse_failure('\'+\'')
2263
+ terminal_parse_failure('\'<\'')
2264
2264
  r9 = nil
2265
2265
  end
2266
2266
  if r9
2267
2267
  r9 = SyntaxNode.new(input, (index-1)...index) if r9 == true
2268
2268
  r0 = r9
2269
2269
  else
2270
- if (match_len = has_terminal?('-', false, index))
2270
+ if (match_len = has_terminal?('+', false, index))
2271
2271
  r10 = true
2272
2272
  @index += match_len
2273
2273
  else
2274
- terminal_parse_failure('\'-\'')
2274
+ terminal_parse_failure('\'+\'')
2275
2275
  r10 = nil
2276
2276
  end
2277
2277
  if r10
2278
2278
  r10 = SyntaxNode.new(input, (index-1)...index) if r10 == true
2279
2279
  r0 = r10
2280
2280
  else
2281
- if (match_len = has_terminal?('*', false, index))
2281
+ if (match_len = has_terminal?('-', false, index))
2282
2282
  r11 = true
2283
2283
  @index += match_len
2284
2284
  else
2285
- terminal_parse_failure('\'*\'')
2285
+ terminal_parse_failure('\'-\'')
2286
2286
  r11 = nil
2287
2287
  end
2288
2288
  if r11
2289
2289
  r11 = SyntaxNode.new(input, (index-1)...index) if r11 == true
2290
2290
  r0 = r11
2291
2291
  else
2292
- if (match_len = has_terminal?('/', false, index))
2292
+ if (match_len = has_terminal?('*', false, index))
2293
2293
  r12 = true
2294
2294
  @index += match_len
2295
2295
  else
2296
- terminal_parse_failure('\'/\'')
2296
+ terminal_parse_failure('\'*\'')
2297
2297
  r12 = nil
2298
2298
  end
2299
2299
  if r12
2300
2300
  r12 = SyntaxNode.new(input, (index-1)...index) if r12 == true
2301
2301
  r0 = r12
2302
2302
  else
2303
- if (match_len = has_terminal?('%', false, index))
2303
+ if (match_len = has_terminal?('/', false, index))
2304
2304
  r13 = true
2305
2305
  @index += match_len
2306
2306
  else
2307
- terminal_parse_failure('\'%\'')
2307
+ terminal_parse_failure('\'/\'')
2308
2308
  r13 = nil
2309
2309
  end
2310
2310
  if r13
2311
2311
  r13 = SyntaxNode.new(input, (index-1)...index) if r13 == true
2312
2312
  r0 = r13
2313
2313
  else
2314
- if (match_len = has_terminal?('&', false, index))
2314
+ if (match_len = has_terminal?('%', false, index))
2315
2315
  r14 = true
2316
2316
  @index += match_len
2317
2317
  else
2318
- terminal_parse_failure('\'&\'')
2318
+ terminal_parse_failure('\'%\'')
2319
2319
  r14 = nil
2320
2320
  end
2321
2321
  if r14
2322
2322
  r14 = SyntaxNode.new(input, (index-1)...index) if r14 == true
2323
2323
  r0 = r14
2324
2324
  else
2325
- if (match_len = has_terminal?('^', false, index))
2325
+ if (match_len = has_terminal?('&', false, index))
2326
2326
  r15 = true
2327
2327
  @index += match_len
2328
2328
  else
2329
- terminal_parse_failure('\'^\'')
2329
+ terminal_parse_failure('\'&\'')
2330
2330
  r15 = nil
2331
2331
  end
2332
2332
  if r15
2333
2333
  r15 = SyntaxNode.new(input, (index-1)...index) if r15 == true
2334
2334
  r0 = r15
2335
2335
  else
2336
- if (match_len = has_terminal?('|', false, index))
2336
+ if (match_len = has_terminal?('^', false, index))
2337
2337
  r16 = true
2338
2338
  @index += match_len
2339
2339
  else
2340
- terminal_parse_failure('\'|\'')
2340
+ terminal_parse_failure('\'^\'')
2341
2341
  r16 = nil
2342
2342
  end
2343
2343
  if r16
2344
2344
  r16 = SyntaxNode.new(input, (index-1)...index) if r16 == true
2345
2345
  r0 = r16
2346
2346
  else
2347
- i17, s17 = index, []
2348
- if (match_len = has_terminal?('in', false, index))
2349
- r18 = instantiate_node(SyntaxNode,input, index...(index + match_len))
2347
+ if (match_len = has_terminal?('|', false, index))
2348
+ r17 = true
2350
2349
  @index += match_len
2351
2350
  else
2352
- terminal_parse_failure('\'in\'')
2353
- r18 = nil
2354
- end
2355
- s17 << r18
2356
- if r18
2357
- r19 = _nt_sp
2358
- s17 << r19
2359
- end
2360
- if s17.last
2361
- r17 = instantiate_node(SyntaxNode,input, i17...index, s17)
2362
- r17.extend(BinaryOp0)
2363
- else
2364
- @index = i17
2351
+ terminal_parse_failure('\'|\'')
2365
2352
  r17 = nil
2366
2353
  end
2367
2354
  if r17
2368
2355
  r17 = SyntaxNode.new(input, (index-1)...index) if r17 == true
2369
2356
  r0 = r17
2370
2357
  else
2371
- @index = i0
2372
- r0 = nil
2358
+ i18, s18 = index, []
2359
+ if (match_len = has_terminal?('in', false, index))
2360
+ r19 = instantiate_node(SyntaxNode,input, index...(index + match_len))
2361
+ @index += match_len
2362
+ else
2363
+ terminal_parse_failure('\'in\'')
2364
+ r19 = nil
2365
+ end
2366
+ s18 << r19
2367
+ if r19
2368
+ r20 = _nt_sp
2369
+ s18 << r20
2370
+ end
2371
+ if s18.last
2372
+ r18 = instantiate_node(SyntaxNode,input, i18...index, s18)
2373
+ r18.extend(BinaryOp0)
2374
+ else
2375
+ @index = i18
2376
+ r18 = nil
2377
+ end
2378
+ if r18
2379
+ r18 = SyntaxNode.new(input, (index-1)...index) if r18 == true
2380
+ r0 = r18
2381
+ else
2382
+ @index = i0
2383
+ r0 = nil
2384
+ end
2373
2385
  end
2374
2386
  end
2375
2387
  end
@@ -3423,7 +3435,7 @@ module Delorean
3423
3435
  r7 = nil
3424
3436
  terminal_parse_failure('\'"\'', true)
3425
3437
  else
3426
- terminal_failures.pop
3438
+ @terminal_failures.pop
3427
3439
  @index = i7
3428
3440
  r7 = instantiate_node(SyntaxNode,input, index...index)
3429
3441
  end
@@ -98,7 +98,7 @@ grammar Delorean
98
98
  # NOTE: some operations such as << have side-effects (e.g. on
99
99
  # Arrays). So, be cautious about which opertaions are added.
100
100
  rule binary_op
101
- '==' / '!=' / '>=' / '<=' / '&&' / '||' /
101
+ '==' / '!=' / '>=' / '<=' / '&&' / '||' / '**' /
102
102
  '>' / '<' / '+' / '-' / '*' / '/' / '%' /
103
103
  '&' / '^' / '|' / 'in' sp
104
104
  end
@@ -1,3 +1,3 @@
1
1
  module Delorean
2
- VERSION = "0.3.24"
2
+ VERSION = "0.3.25"
3
3
  end
@@ -22,12 +22,15 @@ describe "Delorean" do
22
22
  " x = -(a * 2)",
23
23
  " b = -(a + 1)",
24
24
  " c = -a + 1",
25
+ " d = a ** 3 - 10*0.2",
25
26
  )
26
27
 
27
28
  engine.evaluate_attrs("A", ["a"]).should == [123]
28
29
 
29
30
  r = engine.evaluate_attrs("A", ["x", "b"])
30
31
  r.should == [-246, -124]
32
+
33
+ expect(engine.evaluate("A", "d")).to eq 1860865.0
31
34
  end
32
35
 
33
36
  it "proper unary expression evaluation" do
@@ -118,6 +118,21 @@ describe "Delorean" do
118
118
  engine.evaluate("A", "b").should == x.flatten + x.flatten(1)
119
119
  end
120
120
 
121
+ it "should handle ZIP" do
122
+ a = [1, 2]
123
+ b = [4, 5, 6]
124
+ c = [7, 8]
125
+
126
+ engine.parse defn("A:",
127
+ " a = #{a}",
128
+ " b = #{b}",
129
+ " c = #{c}",
130
+ " d = a.zip(b) + a.zip(b, c)",
131
+ )
132
+
133
+ expect(engine.evaluate("A", "d")).to eq a.zip(b) + a.zip(b, c)
134
+ end
135
+
121
136
  it "should handle ERR" do
122
137
  engine.parse defn("A:",
123
138
  " a = ERR('hello')",
@@ -147,6 +162,7 @@ describe "Delorean" do
147
162
  " l = a.member(5)",
148
163
  " m = [a.member(5), a.member(55)]",
149
164
  " n = {'a':1, 'b':2, 'c':3}.length()",
165
+ " o = 'hello'.length",
150
166
  )
151
167
 
152
168
  engine.evaluate("A", "c").should == x.flatten(1)
@@ -158,6 +174,7 @@ describe "Delorean" do
158
174
  engine.evaluate("A", "gg").should == x.length
159
175
  engine.evaluate("A", "m").should == [x.member?(5), x.member?(55)]
160
176
  engine.evaluate("A", "n").should == 3
177
+ engine.evaluate("A", "o").should == 5
161
178
  end
162
179
 
163
180
  it "should be able to call function on hash" do
@@ -206,4 +223,13 @@ describe "Delorean" do
206
223
  expect(engine.evaluate("A", "b")).to eq([false, true])
207
224
  end
208
225
 
226
+ it "should handle MATCH" do
227
+ engine.parse defn("A:",
228
+ " a = 'this is a test'.match('(.*)( is )(.*)')",
229
+ " b = [a[0], a[1], a[2], a[3], a[4]]",
230
+ )
231
+
232
+ expect(engine.evaluate("A", "b")).
233
+ to eq(["this is a test", "this", " is ", "a test", nil])
234
+ end
209
235
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: delorean_lang
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.24
4
+ version: 0.3.25
5
5
  platform: ruby
6
6
  authors:
7
7
  - Arman Bostani
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-10 00:00:00.000000000 Z
11
+ date: 2017-08-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: treetop