delorean_lang 0.0.33

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.
@@ -0,0 +1,3 @@
1
+ module Delorean
2
+ VERSION = "0.0.33"
3
+ end
@@ -0,0 +1,12 @@
1
+ require "delorean/version"
2
+
3
+ require 'treetop'
4
+ require 'delorean/delorean'
5
+ require 'delorean/nodes'
6
+ require 'delorean/engine'
7
+ require 'delorean/functions'
8
+ require 'delorean/base'
9
+ require 'delorean/error'
10
+ require 'delorean/container'
11
+ require 'delorean/model'
12
+
data/spec/dev_spec.rb ADDED
@@ -0,0 +1,98 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Delorean" do
4
+
5
+ let(:engine) {
6
+ Delorean::Engine.new("YYY")
7
+ }
8
+
9
+ it "can enumerate nodes" do
10
+ engine.parse defn("X:",
11
+ " a = 123",
12
+ " b = a",
13
+ "Y: X",
14
+ "A:",
15
+ "XX: Y",
16
+ " a = 11",
17
+ " c =?",
18
+ " d = 456",
19
+ )
20
+ engine.enumerate_nodes.should == SortedSet.new(["A", "X", "XX", "Y"])
21
+ end
22
+
23
+ it "can enumerate all attrs" do
24
+ engine.parse defn("X:",
25
+ " a = 123",
26
+ " b = a",
27
+ "Y: X",
28
+ "Z:",
29
+ "XX: Y",
30
+ " a = 11",
31
+ " c =?",
32
+ " d = 456",
33
+ )
34
+ engine.enumerate_attrs.should == {
35
+ "X"=>["a", "b"],
36
+ "Y"=>["a", "b"],
37
+ "Z"=>[],
38
+ "XX"=>["a", "c", "d", "b"],
39
+ }
40
+ end
41
+
42
+ it "can enumerate attrs by node" do
43
+ engine.parse defn("X:",
44
+ " a = 123",
45
+ " b = a",
46
+ "Y: X",
47
+ "Z:",
48
+ "XX: Y",
49
+ " a = 11",
50
+ " c =?",
51
+ " d = 456",
52
+ )
53
+ engine.enumerate_attrs_by_node("X").should == { "X"=>["a", "b"] }
54
+ engine.enumerate_attrs_by_node("Y").should == { "Y"=>["a", "b"] }
55
+ engine.enumerate_attrs_by_node("Z").should == { "Z"=>[] }
56
+ engine.enumerate_attrs_by_node("XX").should == { "XX"=>["a", "c", "d", "b"] }
57
+ engine.enumerate_attrs_by_node("UNK").should == { }
58
+ end
59
+
60
+ it "can enumerate params" do
61
+ engine.parse defn("X:",
62
+ " a =? 123",
63
+ " b = a",
64
+ "Y: X",
65
+ "Z:",
66
+ "XX: Y",
67
+ " a = 11",
68
+ " c =?",
69
+ " d = 123",
70
+ "YY: XX",
71
+ " c =? 22",
72
+ " e =? 11",
73
+ )
74
+
75
+ engine.enumerate_params.should == Set.new(["a", "c", "e"])
76
+ end
77
+
78
+ it "can enumerate params by node" do
79
+ engine.parse defn("X:",
80
+ " a =? 123",
81
+ " b = a",
82
+ "Y: X",
83
+ "Z:",
84
+ "XX: Y",
85
+ " a = 11",
86
+ " c =?",
87
+ " d = 123",
88
+ "YY: XX",
89
+ " c =? 22",
90
+ " e =? 11",
91
+ )
92
+ engine.enumerate_params_by_node("X").should == Set.new(["a"])
93
+ engine.enumerate_params_by_node("XX").should == Set.new(["a", "c"])
94
+ engine.enumerate_params_by_node("YY").should == Set.new(["a", "c", "e"])
95
+ engine.enumerate_params_by_node("Z").should == Set.new([])
96
+ engine.enumerate_params_by_node("UNK").should == Set.new([])
97
+ end
98
+ end
data/spec/eval_spec.rb ADDED
@@ -0,0 +1,609 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Delorean" do
4
+
5
+ let(:engine) {
6
+ Delorean::Engine.new "XXX"
7
+ }
8
+
9
+ let(:sset) {
10
+ TestContainer.new({
11
+ ["AAA", "0001"] =>
12
+ defn("X:",
13
+ " a =? 123",
14
+ " b = a*2",
15
+ )
16
+ })
17
+ }
18
+
19
+ it "evaluate simple expressions" do
20
+ engine.parse defn("A:",
21
+ " a = 123",
22
+ " x = -(a * 2)",
23
+ " b = -(a + 1)",
24
+ " c = -a + 1",
25
+ )
26
+
27
+ engine.evaluate_attrs("A", ["a"]).should == [123]
28
+
29
+ r = engine.evaluate_attrs("A", ["x", "b"])
30
+ r.should == [-246, -124]
31
+ end
32
+
33
+ it "proper unary expression evaluation" do
34
+ engine.parse defn("A:",
35
+ " a = 123",
36
+ " c = -a + 1",
37
+ )
38
+
39
+ r = engine.evaluate("A", "c")
40
+ r.should == -122
41
+ end
42
+
43
+ it "should be able to evaluate multiple node attrs" do
44
+ engine.parse defn("A:",
45
+ " a =? 123",
46
+ " b = a % 11",
47
+ " c = a / 4.0",
48
+ )
49
+
50
+ h = {"a" => 16}
51
+ r = engine.evaluate_attrs("A", ["c", "b"], h)
52
+ r.should == [4, 5]
53
+ end
54
+
55
+ it "should give error when accessing undefined attr" do
56
+ engine.parse defn("A:",
57
+ " a = 1",
58
+ " c = a.to_s",
59
+ )
60
+
61
+ lambda {
62
+ r = engine.evaluate("A", "c")
63
+ }.should raise_error(Delorean::InvalidGetAttribute)
64
+ end
65
+
66
+ it "should handle default param values" do
67
+ engine.parse defn("A:",
68
+ " a =? 123",
69
+ " c = a / 123.0",
70
+ )
71
+
72
+ r = engine.evaluate("A", "c")
73
+ r.should == 1
74
+ end
75
+
76
+ it "order of attr evaluation should not matter" do
77
+ engine.parse defn("A:",
78
+ " a =? 1",
79
+ "B:",
80
+ " a =? 2",
81
+ " c = A.a",
82
+ )
83
+ engine.evaluate_attrs("B", %w{c a}).should == [1, 2]
84
+ engine.evaluate_attrs("B", %w{a c}).should == [2, 1]
85
+ end
86
+
87
+ it "params should behave properly with inheritance" do
88
+ engine.parse defn("A:",
89
+ " a =? 1",
90
+ "B: A",
91
+ " a =? 2",
92
+ "C: B",
93
+ " a =? 3",
94
+ " b = B.a",
95
+ " c = A.a",
96
+ )
97
+ engine.evaluate_attrs("C", %w{a b c}).should == [3, 2, 1]
98
+ engine.evaluate_attrs("C", %w{a b c}, {"a" => 4}).should == [4, 4, 4]
99
+ engine.evaluate_attrs("C", %w{c b a}).should == [1, 2, 3]
100
+ end
101
+
102
+ it "should give error when param is undefined for eval" do
103
+ engine.parse defn("A:",
104
+ " a =?",
105
+ " c = a / 123.0",
106
+ )
107
+
108
+ lambda {
109
+ r = engine.evaluate("A", "c")
110
+ }.should raise_error(Delorean::UndefinedParamError)
111
+ end
112
+
113
+ it "should handle simple param computation" do
114
+ engine.parse defn("A:",
115
+ " a =?",
116
+ " c = a / 123.0",
117
+ )
118
+
119
+ r = engine.evaluate("A", "c", {"a" => 123})
120
+ r.should == 1
121
+ end
122
+
123
+ it "should give error on unknown node" do
124
+ engine.parse defn("A:",
125
+ " a = 1",
126
+ )
127
+
128
+ lambda {
129
+ r = engine.evaluate("B", "a")
130
+ }.should raise_error(Delorean::UndefinedNodeError)
131
+ end
132
+
133
+ it "should handle runtime errors and report module/line number" do
134
+ engine.parse defn("A:",
135
+ " a = 1/0",
136
+ " b = 10 * a",
137
+ )
138
+
139
+ begin
140
+ engine.evaluate("A", "b")
141
+ rescue => exc
142
+ res = engine.parse_runtime_exception(exc)
143
+ end
144
+
145
+ res.should == ["divided by 0", [["XXX", 2, "/"], ["XXX", 2, "a"], ["XXX", 3, "b"]]]
146
+ end
147
+
148
+ it "should handle runtime errors 2" do
149
+ engine.parse defn("A:",
150
+ " b = Dummy.call_me_maybe('a', 'b')",
151
+ )
152
+
153
+ begin
154
+ engine.evaluate("A", "b")
155
+ rescue => exc
156
+ res = engine.parse_runtime_exception(exc)
157
+ end
158
+
159
+ res[1].should == [["XXX", 2, "b"]]
160
+ end
161
+
162
+ it "should handle operator precedence properly" do
163
+ engine.parse defn("A:",
164
+ " b = 3+2*4-1",
165
+ " c = b*3+5",
166
+ " d = b*2-c*2",
167
+ " e = if (d < -10) then -123-1 else -456+1",
168
+ )
169
+
170
+ r = engine.evaluate("A", "d")
171
+ r.should == -50
172
+
173
+ r = engine.evaluate("A", "e")
174
+ r.should == -124
175
+ end
176
+
177
+ it "should handle if/else" do
178
+ text = defn("A:",
179
+ " d =? -10",
180
+ ' e = if d < -10 then "gungam"+"style" else "korea"'
181
+ )
182
+
183
+ engine.parse text
184
+ r = engine.evaluate("A", "e", {"d" => -100})
185
+ r.should == "gungam"+"style"
186
+
187
+ r = engine.evaluate("A", "e")
188
+ r.should == "korea"
189
+ end
190
+
191
+ it "should be able to access specific node attrs " do
192
+ engine.parse defn("A:",
193
+ " b = 123",
194
+ " c =?",
195
+ "B: A",
196
+ " b = 111",
197
+ " c = A.b * 123",
198
+ "C:",
199
+ " c = A.c + B.c",
200
+ )
201
+
202
+ r = engine.evaluate("B", "c")
203
+ r.should == 123*123
204
+ r = engine.evaluate("C", "c", {"c" => 5})
205
+ r.should == 123*123 + 5
206
+ end
207
+
208
+ it "should be able to call class methods on ActiveRecord classes" do
209
+ engine.parse defn("A:",
210
+ " b = Dummy.call_me_maybe(1, 2, 3, 4)",
211
+ " c = Dummy.call_me_maybe()",
212
+ " d = Dummy.call_me_maybe(5) + b + c",
213
+ )
214
+ r = engine.evaluate_attrs("A", ["b", "c", "d"])
215
+ r.should == [10, 0, 15]
216
+ end
217
+
218
+ it "should be able to get attr on ActiveRecord objects using a.b syntax" do
219
+ engine.parse defn("A:",
220
+ ' b = Dummy.i_just_met_you("this is crazy", 0.404)',
221
+ " c = b.number",
222
+ " d = b.name",
223
+ " e = b.foo",
224
+ )
225
+ r = engine.evaluate("A", "c")
226
+ r.should == 0.404
227
+
228
+ r = engine.evaluate("A", "d")
229
+ r.should == "this is crazy"
230
+
231
+ lambda {
232
+ r = engine.evaluate("A", "e")
233
+ }.should raise_error(Delorean::InvalidGetAttribute)
234
+ end
235
+
236
+ it "should be able to get attr on ActiveRecord objects using Class.method().attr syntax" do
237
+ engine.parse defn("A:",
238
+ ' b = Dummy.i_just_met_you("CRJ", 1.234).name',
239
+ )
240
+ r = engine.evaluate("A", "b")
241
+ r.should == "CRJ"
242
+ end
243
+
244
+ it "should be able to get attr on Hash objects using a.b syntax" do
245
+ engine.parse defn("A:",
246
+ ' b = Dummy.i_threw_a_hash_in_the_well()',
247
+ " c = b.a",
248
+ " d = b.b",
249
+ " e = b.this_is_crazy",
250
+ )
251
+ engine.evaluate_attrs("A", %w{c d e}).should == [456, 789, nil]
252
+ end
253
+
254
+ it "get attr on nil should return nil" do
255
+ engine.parse defn("A:",
256
+ ' b = Dummy.i_just_met_you("CRJ", 1.234).dummy',
257
+ ' c = b.gaga',
258
+ ' d = b.gaga || 55',
259
+ )
260
+ r = engine.evaluate_attrs("A", ["b", "c", "d"])
261
+ r.should == [nil, nil, 55]
262
+ end
263
+
264
+ it "should be able to get assoc attr on ActiveRecord objects" do
265
+ engine.parse defn("A:",
266
+ ' b = Dummy.miss_you_so_bad()',
267
+ ' c = b.dummy',
268
+ )
269
+ r = engine.evaluate("A", "c")
270
+ r.name.should == "hello"
271
+ end
272
+
273
+ it "should be able to call class methods on ActiveRecord classes in modules" do
274
+ engine.parse defn("A:",
275
+ " b = M::LittleDummy.heres_my_number(867, 5309)",
276
+ )
277
+ r = engine.evaluate("A", "b")
278
+ r.should == 867 + 5309
279
+ end
280
+
281
+ it "should not eval inside strings" do
282
+ engine.parse defn("A:",
283
+ ' d = "#{this is a test}"',
284
+ )
285
+
286
+ r = engine.evaluate("A", "d")
287
+ r.should == '#{this is a test}'
288
+ end
289
+
290
+ it "should ignore undeclared params sent to eval which match attr names" do
291
+ engine.parse defn("A:",
292
+ " d = 12",
293
+ )
294
+ r = engine.evaluate("A", "d", {"d" => 5, "e" => 6})
295
+ r.should == 12
296
+ end
297
+
298
+ it "should handle different param defaults on nodes" do
299
+ engine.parse defn("A:",
300
+ " p =? 1",
301
+ " c = p * 123",
302
+ "B: A",
303
+ " p =? 2",
304
+ "C: A",
305
+ " p =? 3",
306
+ )
307
+
308
+ r = engine.evaluate("C", "c", {"p" => 5})
309
+ r.should == 5*123
310
+
311
+ r = engine.evaluate("B", "c", {"p" => 10})
312
+ r.should == 10*123
313
+
314
+ r = engine.evaluate("A", "c")
315
+ r.should == 1*123
316
+
317
+ r = engine.evaluate("B", "c")
318
+ r.should == 2*123
319
+
320
+ r = engine.evaluate("C", "c")
321
+ r.should == 3*123
322
+ end
323
+
324
+ it "should allow overriding of attrs as params" do
325
+ engine.parse defn("A:",
326
+ " a = 2",
327
+ " b = a*3",
328
+ "B: A",
329
+ " a =?",
330
+ )
331
+
332
+ r = engine.evaluate("A", "b", {"a" => 10})
333
+ r.should == 2*3
334
+
335
+ r = engine.evaluate("B", "b", {"a" => 10})
336
+ r.should == 10*3
337
+
338
+ lambda {
339
+ r = engine.evaluate("B", "b")
340
+ }.should raise_error(Delorean::UndefinedParamError)
341
+
342
+ end
343
+
344
+ sample_script = <<eof
345
+ A:
346
+ a = 2
347
+ p =?
348
+ c = a * 2
349
+ pc = p + c
350
+
351
+ C: A
352
+ p =? 3
353
+
354
+ B: A
355
+ p =? 5
356
+ eof
357
+
358
+ it "should allow overriding of attrs as params" do
359
+ engine.parse sample_script
360
+
361
+ r = engine.evaluate("C", "c")
362
+ r.should == 4
363
+
364
+ r = engine.evaluate("B", "pc")
365
+ r.should == 4 + 5
366
+
367
+ r = engine.evaluate("C", "pc")
368
+ r.should == 4 + 3
369
+
370
+ lambda {
371
+ r = engine.evaluate("A", "pc")
372
+ }.should raise_error(Delorean::UndefinedParamError)
373
+ end
374
+
375
+ it "engines of same name should be independent" do
376
+ engin2 = Delorean::Engine.new(engine.module_name)
377
+
378
+ engine.parse defn("A:",
379
+ " a = 123",
380
+ " b = a*3",
381
+ "B: A",
382
+ " c = b*2",
383
+ )
384
+
385
+ engin2.parse defn("A:",
386
+ " a = 222.0",
387
+ " b = a/5",
388
+ "B: A",
389
+ " c = b*3",
390
+ "C:",
391
+ " d = 111",
392
+ )
393
+
394
+ engine.evaluate_attrs("A", ["a", "b"]).should == [123, 123*3]
395
+ engin2.evaluate_attrs("A", ["a", "b"]).should == [222.0, 222.0/5]
396
+
397
+ engine.evaluate_attrs("B", ["a", "b", "c"]).should == [123, 123*3, 123*3*2]
398
+ engin2.evaluate_attrs("B", ["a", "b", "c"]).should == [222.0, 222.0/5, 222.0/5*3]
399
+
400
+ engin2.evaluate("C", "d").should == 111
401
+ lambda {
402
+ engine.evaluate("C", "d")
403
+ }.should raise_error(Delorean::UndefinedNodeError)
404
+ end
405
+
406
+ it "should handle invalid expression evaluation" do
407
+ # Should handle errors on expression such as -[] or -"xxx" or ("x"
408
+ # + []) better. Currently, it raised NoMethodError.
409
+ pending
410
+ end
411
+
412
+ it "should eval lists" do
413
+ engine.parse defn("A:",
414
+ " b = []",
415
+ " c = [1,2,3]",
416
+ " d = [b, c, b, c, 1, 2, '123', 1.1, -1.23]",
417
+ " e = [1, 1+1, 1+1+1, 1*2*4]",
418
+ )
419
+
420
+ engine.evaluate_attrs("A", %w{b c d e}).should ==
421
+ [[],
422
+ [1, 2, 3],
423
+ [[], [1, 2, 3], [], [1, 2, 3], 1, 2, "123", 1.1, -1.23],
424
+ [1, 2, 3, 8],
425
+ ]
426
+ end
427
+
428
+ it "should eval list expressions" do
429
+ engine.parse defn("A:",
430
+ " b = []+[]",
431
+ " c = [1,2,3]+b",
432
+ " d = c*2",
433
+ )
434
+
435
+ engine.evaluate_attrs("A", %w{b c d}).should ==
436
+ [[],
437
+ [1, 2, 3],
438
+ [1, 2, 3]*2,
439
+ ]
440
+ end
441
+
442
+ it "should eval list comprehension" do
443
+ engine.parse defn("A:",
444
+ " b = [i*5 for i in [1,2,3]]",
445
+ )
446
+ engine.evaluate("A", "b").should == [5, 10, 15]
447
+ end
448
+
449
+ it "should eval nested list comprehension" do
450
+ engine.parse defn("A:",
451
+ " b = [[a+c for c in [4,5]] for a in [1,2,3]]",
452
+ )
453
+ engine.evaluate("A", "b").should == [[5, 6], [6, 7], [7, 8]]
454
+
455
+ end
456
+
457
+ it "should eval list comprehension variable override" do
458
+ engine.parse defn("A:",
459
+ " b = [b/2.0 for b in [1,2,3]]",
460
+ )
461
+ engine.evaluate("A", "b").should == [0.5, 1.0, 1.5]
462
+ end
463
+
464
+ it "should eval list comprehension variable override (2)" do
465
+ engine.parse defn("A:",
466
+ " a = 1",
467
+ " b = [a+1 for a in [1,2,3]]",
468
+ )
469
+ engine.evaluate("A", "b").should == [2, 3, 4]
470
+ end
471
+
472
+ it "should eval conditional list comprehension" do
473
+ engine.parse defn("A:",
474
+ " b = [i*5 for i in [1,2,3,4,5] if i%2 == 1]",
475
+ )
476
+ engine.evaluate("A", "b").should == [5, 15, 25]
477
+ end
478
+
479
+ it "should eval hashes" do
480
+ engine.parse defn("A:",
481
+ " b = {}",
482
+ " c = {'a':1, 'b': 2,'c':3}",
483
+ " d = {123*2: -123, 'b_b': 1+1}",
484
+ " e = {'x': 1, 'y': 1+1, 'z': 1+1+1, 'zz': 1*2*4}",
485
+ " f = {'a': nil, 'b': [1, nil, 2]}",
486
+ " g = {b:b, [b]:[1,23], []:345}",
487
+ )
488
+
489
+ engine.evaluate_attrs("A", %w{b c d e f g}).should ==
490
+ [{},
491
+ {"a"=>1, "b"=>2, "c"=>3},
492
+ {123*2=>-123, "b_b"=>2},
493
+ {"x"=>1, "y"=>2, "z"=>3, "zz"=>8},
494
+ {"a"=>nil, "b"=>[1, nil, 2]},
495
+ {{}=>{}, [{}]=>[1, 23], []=>345},
496
+ ]
497
+ end
498
+
499
+ it "should eval module calls" do
500
+ engine.parse defn("A:",
501
+ " a = 123",
502
+ " b = 456 + a",
503
+ " n = 'A'",
504
+ " c = @('a', 'b', x: 123, y: 456)",
505
+ " d = @n('a', 'b', x: 123, y: 456)",
506
+ " e = @('b')",
507
+ )
508
+
509
+ engine.evaluate_attrs("A", %w{n c d e}).should ==
510
+ ["A", {"a"=>123, "b"=>579}, {"a"=>123, "b"=>579}, 579]
511
+ end
512
+
513
+ it "should be possible to implement recursive calls" do
514
+ engine.parse defn("A:",
515
+ " n =?",
516
+ " factorial = if n <= 1 then 1 else n * @('factorial', n: n-1)",
517
+ )
518
+
519
+ engine.evaluate("A", "factorial", "n" => 10).should == 3628800
520
+ end
521
+
522
+ it "should eval module calls by node name" do
523
+ engine.parse defn("A:",
524
+ " a = 123",
525
+ " b = @A('a')",
526
+ )
527
+ engine.evaluate("A", "b").should == 123
528
+ end
529
+
530
+ it "should eval multiline expressions" do
531
+ engine.parse defn("A:",
532
+ " a = 1",
533
+ " b = [a+1",
534
+ " for a in [1,2,3]",
535
+ " ]",
536
+ )
537
+ engine.evaluate("A", "b").should == [2, 3, 4]
538
+ end
539
+
540
+ it "should eval multiline expressions" do
541
+ engine.parse defn("A:",
542
+ " a = 123",
543
+ " b = 456 + ",
544
+ " a",
545
+ " n = 'A'",
546
+ " c = @('a', ",
547
+ " 'b', ",
548
+ " x: 123, ",
549
+ " y: 456)",
550
+ " d = @n('a', ",
551
+ " 'b', ",
552
+ " x: 123, y: 456)",
553
+ " e = @(",
554
+ " 'b'",
555
+ " )",
556
+ )
557
+
558
+ engine.evaluate_attrs("A", %w{n c d e}).should ==
559
+ ["A", {"a"=>123, "b"=>579}, {"a"=>123, "b"=>579}, 579]
560
+ end
561
+
562
+ it "should eval imports" do
563
+ engine.parse defn("import AAA 0001",
564
+ "A:",
565
+ " b = 456",
566
+ "B: AAA::X",
567
+ " a = 111",
568
+ " c = @AAA::X('b', a: 456)",
569
+ ), sset
570
+ engine.evaluate_attrs("B", ["a", "b", "c"], {}).should == [111, 222, 456*2]
571
+ end
572
+
573
+ it "should eval imports (2)" do
574
+ sset.merge({
575
+ ["BBB", "0002"] =>
576
+ defn("import AAA 0001",
577
+ "B: AAA::X",
578
+ " a = 111",
579
+ " c = @AAA::X('b', a: -1)",
580
+ " d = a * 2",
581
+ ),
582
+ ["CCC", "0003"] =>
583
+ defn("import BBB 0002",
584
+ "import AAA 0001",
585
+ "B: BBB::B",
586
+ " e = d * 3",
587
+ "C: AAA::X",
588
+ " d = b * 3",
589
+ ),
590
+ })
591
+
592
+ e2 = sset.get_engine("BBB", "0002")
593
+
594
+ e2.evaluate_attrs("B", ["a", "b", "c", "d"]).should == [111, 222, -2, 222]
595
+
596
+ engine.parse defn("import BBB 0002",
597
+ "B: BBB::B",
598
+ " e = d + 3",
599
+ ), sset
600
+
601
+ engine.evaluate_attrs("B", ["a", "b", "c", "d", "e"]).should == [111, 222, -2, 222, 225]
602
+
603
+ e4 = sset.get_engine("CCC", "0003")
604
+
605
+ e4.evaluate_attrs("B", ["a", "b", "c", "d", "e"]).should == [111, 222, -2, 222, 666]
606
+ e4.evaluate_attrs("C", ["a", "b", "d"]).should == [123, 123*2, 123*3*2]
607
+ end
608
+
609
+ end