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,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists'
2
4
 
3
5
  module Delorean
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists/default'
2
4
 
3
5
  module Delorean
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists/whitelist_error'
2
4
  require 'delorean/ruby/whitelists/matchers'
3
5
 
@@ -11,9 +13,11 @@ module Delorean
11
13
  return method_name_error unless method_name.is_a?(Symbol)
12
14
  return block_and_match_error if !match_to.nil? && block_given?
13
15
 
14
- return add_matched_method(
15
- method_name: method_name, match_to: match_to
16
- ) unless match_to.nil?
16
+ unless match_to.nil?
17
+ return add_matched_method(
18
+ method_name: method_name, match_to: match_to
19
+ )
20
+ end
17
21
 
18
22
  matchers[method_name.to_sym] = method_matcher_class.new(
19
23
  method_name: method_name, &block
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists/base'
2
4
 
3
5
  module Delorean
4
6
  module Ruby
5
7
  module Whitelists
6
8
  class Default < ::Delorean::Ruby::Whitelists::Base
7
- TI_TYPES = [Time, ActiveSupport::TimeWithZone]
9
+ TI_TYPES = [Time, ActiveSupport::TimeWithZone].freeze
8
10
  DT_TYPES = [Date] + TI_TYPES
9
- NUM_OR_STR = [Numeric, String]
10
- NUM_OR_NIL = [nil, Integer]
11
+ NUM_OR_STR = [Numeric, String].freeze
12
+ NUM_OR_NIL = [nil, Integer].freeze
11
13
 
12
14
  def initialize_hook
13
15
  _add_default_methods
@@ -63,7 +65,7 @@ module Delorean
63
65
  end
64
66
 
65
67
  add_method :except do |method|
66
- method.called_on Hash, with: [String] + [[nil, String]]*9
68
+ method.called_on Hash, with: [String] + [[nil, String]] * 9
67
69
  end
68
70
 
69
71
  add_method :reverse do |method|
@@ -132,8 +134,6 @@ module Delorean
132
134
  method.called_on Set, with: [Enumerable]
133
135
  end
134
136
 
135
-
136
-
137
137
  add_method :keys do |method|
138
138
  method.called_on Hash
139
139
  end
@@ -1,11 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists/base'
2
4
 
3
5
  module Delorean
4
6
  module Ruby
5
7
  module Whitelists
6
8
  class Empty < ::Delorean::Ruby::Whitelists::Base
7
- def initialize_hook
8
- end
9
+ def initialize_hook; end
9
10
  end
10
11
  end
11
12
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists/matchers/method'
2
4
 
3
5
  module Delorean
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delorean
2
4
  module Ruby
3
5
  module Whitelists
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'delorean/ruby/whitelists/matchers/arguments'
2
4
 
3
5
  module Delorean
@@ -24,11 +26,12 @@ module Delorean
24
26
  end
25
27
 
26
28
  def matcher(klass:)
27
- matcher = arguments_matchers.find do
28
- |matcher_object| klass <= matcher_object.called_on
29
+ matcher = arguments_matchers.find do |matcher_object|
30
+ klass <= matcher_object.called_on
29
31
  end
30
32
 
31
33
  raise "no such method #{method_name} for #{klass}" if matcher.nil?
34
+
32
35
  matcher
33
36
  end
34
37
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delorean
2
4
  module Ruby
3
5
  module Whitelists
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Delorean
2
- VERSION = "0.5.1"
4
+ VERSION = '0.5.2'.freeze
3
5
  end
@@ -1,4 +1,6 @@
1
- require "delorean/version"
1
+ # frozen_string_literal: true
2
+
3
+ require 'delorean/version'
2
4
 
3
5
  require 'treetop'
4
6
  require 'delorean/abstract_container'
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
4
 
3
- describe "Delorean cache" do
5
+ describe 'Delorean cache' do
4
6
  before do
5
7
  Dummy.clear_lookup_cache!
6
8
  end
@@ -60,6 +62,6 @@ describe "Delorean cache" do
60
62
  )
61
63
 
62
64
  expect(item_10).to be_a(OpenStruct)
63
- expect(item_10["10"]).to eq(10)
65
+ expect(item_10['10']).to eq(10)
64
66
  end
65
67
  end
@@ -1,90 +1,89 @@
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("YYY")
7
- }
5
+ describe 'Delorean' do
6
+ let(:engine) do
7
+ Delorean::Engine.new('YYY')
8
+ end
8
9
 
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"])
10
+ it 'can enumerate nodes' do
11
+ engine.parse defn('X:',
12
+ ' a = 123',
13
+ ' b = a',
14
+ 'Y: X',
15
+ 'A:',
16
+ 'XX: Y',
17
+ ' a = 11',
18
+ ' c =?',
19
+ ' d = 456',
20
+ )
21
+ engine.enumerate_nodes.should == SortedSet.new(['A', 'X', 'XX', 'Y'])
21
22
  end
22
23
 
23
- it "can enumerate attrs by node" 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
- )
24
+ it 'can enumerate attrs by node' do
25
+ engine.parse defn('X:',
26
+ ' a = 123',
27
+ ' b = a',
28
+ 'Y: X',
29
+ 'Z:',
30
+ 'XX: Y',
31
+ ' a = 11',
32
+ ' c =?',
33
+ ' d = 456',
34
+ )
34
35
 
35
36
  exp = {
36
- "X" => ["a", "b"],
37
- "Y" => ["a", "b"],
38
- "Z" => [],
39
- "XX" => ["a", "b", "c", "d"],
37
+ 'X' => ['a', 'b'],
38
+ 'Y' => ['a', 'b'],
39
+ 'Z' => [],
40
+ 'XX' => ['a', 'b', 'c', 'd'],
40
41
  }
41
42
  res = engine.enumerate_attrs
42
43
 
43
44
  res.keys.sort.should == exp.keys.sort
44
45
 
45
- exp.each {
46
- |k, v|
47
-
46
+ exp.each do |k, v|
48
47
  engine.enumerate_attrs_by_node(k).sort.should == v
49
48
  res[k].sort.should == v
50
- }
49
+ end
51
50
  end
52
51
 
53
- it "can enumerate params" do
54
- engine.parse defn("X:",
55
- " a =? 123",
56
- " b = a",
57
- "Y: X",
58
- "Z:",
59
- "XX: Y",
60
- " a = 11",
61
- " c =?",
62
- " d = 123",
63
- "YY: XX",
64
- " c =? 22",
65
- " e =? 11",
66
- )
52
+ it 'can enumerate params' do
53
+ engine.parse defn('X:',
54
+ ' a =? 123',
55
+ ' b = a',
56
+ 'Y: X',
57
+ 'Z:',
58
+ 'XX: Y',
59
+ ' a = 11',
60
+ ' c =?',
61
+ ' d = 123',
62
+ 'YY: XX',
63
+ ' c =? 22',
64
+ ' e =? 11',
65
+ )
67
66
 
68
- engine.enumerate_params.should == Set.new(["a", "c", "e"])
67
+ engine.enumerate_params.should == Set.new(['a', 'c', 'e'])
69
68
  end
70
69
 
71
- it "can enumerate params by node" do
72
- engine.parse defn("X:",
73
- " a =? 123",
74
- " b = a",
75
- "Y: X",
76
- "Z:",
77
- "XX: Y",
78
- " a = 11",
79
- " c =?",
80
- " d = 123",
81
- "YY: XX",
82
- " c =? 22",
83
- " e =? 11",
84
- )
85
- engine.enumerate_params_by_node("X").should == Set.new(["a"])
86
- engine.enumerate_params_by_node("XX").should == Set.new(["a", "c"])
87
- engine.enumerate_params_by_node("YY").should == Set.new(["a", "c", "e"])
88
- engine.enumerate_params_by_node("Z").should == Set.new([])
70
+ it 'can enumerate params by node' do
71
+ engine.parse defn('X:',
72
+ ' a =? 123',
73
+ ' b = a',
74
+ 'Y: X',
75
+ 'Z:',
76
+ 'XX: Y',
77
+ ' a = 11',
78
+ ' c =?',
79
+ ' d = 123',
80
+ 'YY: XX',
81
+ ' c =? 22',
82
+ ' e =? 11',
83
+ )
84
+ engine.enumerate_params_by_node('X').should == Set.new(['a'])
85
+ engine.enumerate_params_by_node('XX').should == Set.new(['a', 'c'])
86
+ engine.enumerate_params_by_node('YY').should == Set.new(['a', 'c', 'e'])
87
+ engine.enumerate_params_by_node('Z').should == Set.new([])
89
88
  end
90
89
  end
@@ -1,316 +1,317 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- describe "Delorean" do
3
-
4
- let(:sset) {
5
- TestContainer.new({
6
- "AAA" =>
7
- defn("X:",
8
- " a =? 123",
9
- " b = a*2",
10
- )
11
- })
12
- }
13
-
14
- let(:engine) {
15
- Delorean::Engine.new "XXX", sset
16
- }
17
-
18
- it "evaluate simple expressions" do
19
- engine.parse defn("A:",
20
- " a = 123",
21
- " x = -(a * 2)",
22
- " b = -(a + 1)",
23
- " c = -a + 1",
24
- " d = a ** 3 - 10*0.2",
25
- )
26
-
27
- engine.evaluate("A", ["a"]).should == [123]
28
-
29
- r = engine.evaluate("A", ["x", "b"])
4
+ describe 'Delorean' do
5
+ let(:sset) do
6
+ TestContainer.new(
7
+ 'AAA' =>
8
+ defn('X:',
9
+ ' a =? 123',
10
+ ' b = a*2',
11
+ )
12
+ )
13
+ end
14
+
15
+ let(:engine) do
16
+ Delorean::Engine.new 'XXX', sset
17
+ end
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
+ ' d = a ** 3 - 10*0.2',
26
+ )
27
+
28
+ engine.evaluate('A', ['a']).should == [123]
29
+
30
+ r = engine.evaluate('A', ['x', 'b'])
30
31
  r.should == [-246, -124]
31
32
 
32
- expect(engine.evaluate("A", "d")).to eq 1860865.0
33
+ expect(engine.evaluate('A', 'd')).to eq 1_860_865.0
33
34
  end
34
35
 
35
- it "proper unary expression evaluation" do
36
- engine.parse defn("A:",
37
- " a = 123",
38
- " c = -a + 1",
39
- )
36
+ it 'proper unary expression evaluation' do
37
+ engine.parse defn('A:',
38
+ ' a = 123',
39
+ ' c = -a + 1',
40
+ )
40
41
 
41
- r = engine.evaluate("A", "c")
42
+ r = engine.evaluate('A', 'c')
42
43
  r.should == -122
43
44
  end
44
45
 
45
- it "proper string interpolation" do
46
- engine.parse defn("A:",
46
+ it 'proper string interpolation' do
47
+ engine.parse defn('A:',
47
48
  ' a = "\n123\n"',
48
- )
49
+ )
49
50
 
50
- r = engine.evaluate("A", "a")
51
+ r = engine.evaluate('A', 'a')
51
52
  r.should == "\n123\n"
52
53
  end
53
54
 
54
- it "should handle getattr in expressions" do
55
- engine.parse defn("A:",
55
+ it 'should handle getattr in expressions' do
56
+ engine.parse defn('A:',
56
57
  " a = {'x':123, 'y':456, 'z':789}",
57
- " b = A.a.x * A.a.y - A.a.z",
58
- )
59
- engine.evaluate("A", ["b"]).should == [123*456-789]
58
+ ' b = A.a.x * A.a.y - A.a.z',
59
+ )
60
+ engine.evaluate('A', ['b']).should == [123 * 456 - 789]
60
61
  end
61
62
 
62
- it "should handle numeric getattr" do
63
- engine.parse defn("A:",
63
+ it 'should handle numeric getattr' do
64
+ engine.parse defn('A:',
64
65
  " a = {1:123, 0:456, 'z':789, 2: {'a':444}}",
65
- " b = A.a.1 * A.a.0 - A.a.z - A.a.2.a",
66
- )
67
- engine.evaluate("A", ["b"]).should == [123*456-789-444]
66
+ ' b = A.a.1 * A.a.0 - A.a.z - A.a.2.a',
67
+ )
68
+ engine.evaluate('A', ['b']).should == [123 * 456 - 789 - 444]
68
69
  end
69
70
 
70
- it "should be able to evaluate multiple node attrs" do
71
- engine.parse defn("A:",
72
- " a =? 123",
73
- " b = a % 11",
74
- " c = a / 4.0",
75
- )
71
+ it 'should be able to evaluate multiple node attrs' do
72
+ engine.parse defn('A:',
73
+ ' a =? 123',
74
+ ' b = a % 11',
75
+ ' c = a / 4.0',
76
+ )
76
77
 
77
- h = {"a" => 16}
78
- r = engine.evaluate("A", ["c", "b"], h)
78
+ h = { 'a' => 16 }
79
+ r = engine.evaluate('A', ['c', 'b'], h)
79
80
  r.should == [4, 5]
80
81
  end
81
82
 
82
- it "should give error when accessing undefined attr" do
83
- engine.parse defn("A:",
84
- " a = 1",
85
- " c = a.to_ss",
86
- )
83
+ it 'should give error when accessing undefined attr' do
84
+ engine.parse defn('A:',
85
+ ' a = 1',
86
+ ' c = a.to_ss',
87
+ )
87
88
 
88
89
  lambda {
89
- r = engine.evaluate("A", "c")
90
+ engine.evaluate('A', 'c')
90
91
  }.should raise_error(Delorean::InvalidGetAttribute)
91
92
  end
92
93
 
93
- it "should be able to call 0-ary functions without ()" do
94
- engine.parse defn("A:",
95
- " a = 1",
96
- " d = a.to_s",
97
- )
94
+ it 'should be able to call 0-ary functions without ()' do
95
+ engine.parse defn('A:',
96
+ ' a = 1',
97
+ ' d = a.to_s',
98
+ )
98
99
 
99
- engine.evaluate("A", "d").should == "1"
100
+ engine.evaluate('A', 'd').should == '1'
100
101
  end
101
102
 
102
- it "should handle default param values" do
103
- engine.parse defn("A:",
104
- " a =? 123",
105
- " c = a / 123.0",
106
- )
103
+ it 'should handle default param values' do
104
+ engine.parse defn('A:',
105
+ ' a =? 123',
106
+ ' c = a / 123.0',
107
+ )
107
108
 
108
- r = engine.evaluate("A", "c")
109
+ r = engine.evaluate('A', 'c')
109
110
  r.should == 1
110
111
  end
111
112
 
112
- it "order of attr evaluation should not matter" do
113
- engine.parse defn("A:",
114
- " a =? 1",
115
- "B:",
116
- " a =? 2",
117
- " c = A.a",
118
- )
119
- engine.evaluate("B", %w{c a}).should == [1, 2]
120
- engine.evaluate("B", %w{a c}).should == [2, 1]
121
- end
122
-
123
- it "params should behave properly with inheritance" do
124
- engine.parse defn("A:",
125
- " a =? 1",
126
- "B: A",
127
- " a =? 2",
128
- "C: B",
129
- " a =? 3",
130
- " b = B.a",
131
- " c = A.a",
132
- )
133
- engine.evaluate("C", %w{a b c}).should == [3, 2, 1]
134
- engine.evaluate("C", %w{a b c}, {"a" => 4}).should == [4, 4, 4]
135
- engine.evaluate("C", %w{c b a}).should == [1, 2, 3]
136
- end
137
-
138
- it "should give error when param is undefined for eval" do
139
- engine.parse defn("A:",
140
- " a =?",
141
- " c = a / 123.0",
142
- )
113
+ it 'order of attr evaluation should not matter' do
114
+ engine.parse defn('A:',
115
+ ' a =? 1',
116
+ 'B:',
117
+ ' a =? 2',
118
+ ' c = A.a',
119
+ )
120
+ engine.evaluate('B', %w[c a]).should == [1, 2]
121
+ engine.evaluate('B', %w[a c]).should == [2, 1]
122
+ end
123
+
124
+ it 'params should behave properly with inheritance' do
125
+ engine.parse defn('A:',
126
+ ' a =? 1',
127
+ 'B: A',
128
+ ' a =? 2',
129
+ 'C: B',
130
+ ' a =? 3',
131
+ ' b = B.a',
132
+ ' c = A.a',
133
+ )
134
+ engine.evaluate('C', %w[a b c]).should == [3, 2, 1]
135
+ engine.evaluate('C', %w[a b c], 'a' => 4).should == [4, 4, 4]
136
+ engine.evaluate('C', %w[c b a]).should == [1, 2, 3]
137
+ end
138
+
139
+ it 'should give error when param is undefined for eval' do
140
+ engine.parse defn('A:',
141
+ ' a =?',
142
+ ' c = a / 123.0',
143
+ )
143
144
 
144
145
  lambda {
145
- r = engine.evaluate("A", "c")
146
+ engine.evaluate('A', 'c')
146
147
  }.should raise_error(Delorean::UndefinedParamError)
147
148
  end
148
149
 
149
- it "should handle simple param computation" do
150
- engine.parse defn("A:",
151
- " a =?",
152
- " c = a / 123.0",
153
- )
150
+ it 'should handle simple param computation' do
151
+ engine.parse defn('A:',
152
+ ' a =?',
153
+ ' c = a / 123.0',
154
+ )
154
155
 
155
- r = engine.evaluate("A", "c", {"a" => 123})
156
+ r = engine.evaluate('A', 'c', 'a' => 123)
156
157
  r.should == 1
157
158
  end
158
159
 
159
- it "should give error on unknown node" do
160
- engine.parse defn("A:",
161
- " a = 1",
162
- )
160
+ it 'should give error on unknown node' do
161
+ engine.parse defn('A:',
162
+ ' a = 1',
163
+ )
163
164
 
164
165
  lambda {
165
- r = engine.evaluate("B", "a")
166
+ engine.evaluate('B', 'a')
166
167
  }.should raise_error(Delorean::UndefinedNodeError)
167
168
  end
168
169
 
169
- it "should handle runtime errors and report module/line number" do
170
- engine.parse defn("A:",
171
- " a = 1/0",
172
- " b = 10 * a",
173
- )
170
+ it 'should handle runtime errors and report module/line number' do
171
+ engine.parse defn('A:',
172
+ ' a = 1/0',
173
+ ' b = 10 * a',
174
+ )
174
175
 
175
176
  begin
176
- engine.evaluate("A", "b")
177
- rescue => exc
177
+ engine.evaluate('A', 'b')
178
+ rescue StandardError => exc
178
179
  res = Delorean::Engine.grok_runtime_exception(exc)
179
180
  end
180
181
 
181
182
  res.should == {
182
- "error" => "divided by 0",
183
- "backtrace" => [["XXX", 2, "/"], ["XXX", 2, "a"], ["XXX", 3, "b"]],
183
+ 'error' => 'divided by 0',
184
+ 'backtrace' => [['XXX', 2, '/'], ['XXX', 2, 'a'], ['XXX', 3, 'b']],
184
185
  }
185
186
  end
186
187
 
187
- it "should handle runtime errors 2" do
188
- engine.parse defn("A:",
188
+ it 'should handle runtime errors 2' do
189
+ engine.parse defn('A:',
189
190
  " b = Dummy.call_me_maybe('a', 'b')",
190
- )
191
+ )
191
192
 
192
193
  begin
193
- engine.evaluate("A", "b")
194
- rescue => exc
194
+ engine.evaluate('A', 'b')
195
+ rescue StandardError => exc
195
196
  res = Delorean::Engine.grok_runtime_exception(exc)
196
197
  end
197
198
 
198
- res["backtrace"].should == [["XXX", 2, "b"]]
199
+ res['backtrace'].should == [['XXX', 2, 'b']]
199
200
  end
200
201
 
201
- it "should handle optional args to external fns" do
202
- engine.parse defn("A:",
202
+ it 'should handle optional args to external fns' do
203
+ engine.parse defn('A:',
203
204
  " b = Dummy.one_or_two(['a', 'b'])",
204
205
  " c = Dummy.one_or_two([1,2,3], ['a', 'b'])",
205
- )
206
+ )
206
207
 
207
- engine.evaluate("A", "b").should == [['a', 'b'], nil]
208
- engine.evaluate("A", "c").should == [[1,2,3], ['a', 'b']]
208
+ engine.evaluate('A', 'b').should == [['a', 'b'], nil]
209
+ engine.evaluate('A', 'c').should == [[1, 2, 3], ['a', 'b']]
209
210
  end
210
211
 
211
- it "should handle operator precedence properly" do
212
- engine.parse defn("A:",
213
- " b = 3+2*4-1",
214
- " c = b*3+5",
215
- " d = b*2-c*2",
216
- " e = if (d < -10) then -123-1 else -456+1",
217
- )
212
+ it 'should handle operator precedence properly' do
213
+ engine.parse defn('A:',
214
+ ' b = 3+2*4-1',
215
+ ' c = b*3+5',
216
+ ' d = b*2-c*2',
217
+ ' e = if (d < -10) then -123-1 else -456+1',
218
+ )
218
219
 
219
- r = engine.evaluate("A", "d")
220
+ r = engine.evaluate('A', 'd')
220
221
  r.should == -50
221
222
 
222
- r = engine.evaluate("A", "e")
223
+ r = engine.evaluate('A', 'e')
223
224
  r.should == -124
224
225
  end
225
226
 
226
- it "should handle if/else" do
227
- text = defn("A:",
228
- " d =? -10",
227
+ it 'should handle if/else' do
228
+ text = defn('A:',
229
+ ' d =? -10',
229
230
  ' e = if d < -10 then "gungam"+"style" else "korea"'
230
- )
231
+ )
231
232
 
232
233
  engine.parse text
233
- r = engine.evaluate("A", "e", {"d" => -100})
234
- r.should == "gungamstyle"
235
-
236
- r = engine.evaluate("A", "e")
237
- r.should == "korea"
238
- end
239
-
240
- it "should be able to access specific node attrs " do
241
- engine.parse defn("A:",
242
- " b = 123",
243
- " c =?",
244
- "B: A",
245
- " b = 111",
246
- " c = A.b * 123",
247
- "C:",
248
- " c = A.c + B.c",
249
- )
250
-
251
- r = engine.evaluate("B", "c")
252
- r.should == 123*123
253
- r = engine.evaluate("C", "c", {"c" => 5})
254
- r.should == 123*123 + 5
255
- end
256
-
257
- it "should be able to access nodes and node attrs dynamically " do
258
- engine.parse defn("A:",
259
- " b = 123",
260
- "B:",
261
- " b = A",
262
- " c = b.b * 456",
263
- )
264
-
265
- r = engine.evaluate("B", "c")
266
- r.should == 123*456
267
- end
268
-
269
- it "should be able to call class methods on ActiveRecord classes" do
270
- engine.parse defn("A:",
271
- " b = Dummy.call_me_maybe(1, 2, 3, 4)",
272
- " c = Dummy.call_me_maybe()",
273
- " d = Dummy.call_me_maybe(5) + b + c",
274
- )
275
- r = engine.evaluate("A", ["b", "c", "d"])
234
+ r = engine.evaluate('A', 'e', 'd' => -100)
235
+ r.should == 'gungamstyle'
236
+
237
+ r = engine.evaluate('A', 'e')
238
+ r.should == 'korea'
239
+ end
240
+
241
+ it 'should be able to access specific node attrs ' do
242
+ engine.parse defn('A:',
243
+ ' b = 123',
244
+ ' c =?',
245
+ 'B: A',
246
+ ' b = 111',
247
+ ' c = A.b * 123',
248
+ 'C:',
249
+ ' c = A.c + B.c',
250
+ )
251
+
252
+ r = engine.evaluate('B', 'c')
253
+ r.should == 123 * 123
254
+ r = engine.evaluate('C', 'c', 'c' => 5)
255
+ r.should == 123 * 123 + 5
256
+ end
257
+
258
+ it 'should be able to access nodes and node attrs dynamically ' do
259
+ engine.parse defn('A:',
260
+ ' b = 123',
261
+ 'B:',
262
+ ' b = A',
263
+ ' c = b.b * 456',
264
+ )
265
+
266
+ r = engine.evaluate('B', 'c')
267
+ r.should == 123 * 456
268
+ end
269
+
270
+ it 'should be able to call class methods on ActiveRecord classes' do
271
+ engine.parse defn('A:',
272
+ ' b = Dummy.call_me_maybe(1, 2, 3, 4)',
273
+ ' c = Dummy.call_me_maybe()',
274
+ ' d = Dummy.call_me_maybe(5) + b + c',
275
+ )
276
+ r = engine.evaluate('A', ['b', 'c', 'd'])
276
277
  r.should == [10, 0, 15]
277
278
  end
278
279
 
279
- it "should be able to access ActiveRecord whitelisted fns using .x syntax" do
280
- engine.parse defn("A:",
280
+ it 'should be able to access ActiveRecord whitelisted fns using .x syntax' do
281
+ engine.parse defn('A:',
281
282
  ' b = Dummy.i_just_met_you("CRJ", 1.234).name2',
282
- )
283
- r = engine.evaluate("A", "b")
284
- r.should == "CRJ-1.234"
283
+ )
284
+ r = engine.evaluate('A', 'b')
285
+ r.should == 'CRJ-1.234'
285
286
  end
286
287
 
287
- it "should be able to get attr on Hash objects using a.b syntax" do
288
- engine.parse defn("A:",
288
+ it 'should be able to get attr on Hash objects using a.b syntax' do
289
+ engine.parse defn('A:',
289
290
  ' b = Dummy.i_threw_a_hash_in_the_well()',
290
- " c = b.a",
291
- " d = b.b",
292
- " e = b.this_is_crazy",
293
- )
294
- engine.evaluate("A", %w{c d e}).should == [456, 789, nil]
291
+ ' c = b.a',
292
+ ' d = b.b',
293
+ ' e = b.this_is_crazy',
294
+ )
295
+ engine.evaluate('A', %w[c d e]).should == [456, 789, nil]
295
296
  end
296
297
 
297
- it "get attr on nil should return nil" do
298
- engine.parse defn("A:",
298
+ it 'get attr on nil should return nil' do
299
+ engine.parse defn('A:',
299
300
  ' b = nil',
300
301
  ' c = b.gaga',
301
302
  ' d = b.gaga || 55',
302
- )
303
- r = engine.evaluate("A", ["b", "c", "d"])
303
+ )
304
+ r = engine.evaluate('A', ['b', 'c', 'd'])
304
305
  r.should == [nil, nil, 55]
305
306
  end
306
307
 
307
- it "should be able to get attr on node" do
308
- engine.parse defn("A:",
309
- " a = 123",
310
- " b = A",
311
- " c = b.a * 2",
312
- )
313
- engine.evaluate("A", %w{a c}).should == [123, 123*2]
308
+ it 'should be able to get attr on node' do
309
+ engine.parse defn('A:',
310
+ ' a = 123',
311
+ ' b = A',
312
+ ' c = b.a * 2',
313
+ )
314
+ engine.evaluate('A', %w[a c]).should == [123, 123 * 2]
314
315
  end
315
316
 
316
317
  getattr_code = <<eoc
@@ -326,80 +327,87 @@ E:
326
327
  xx = [n.x for n in D.xs]
327
328
  eoc
328
329
 
329
- it "should be able to get attr on node 2" do
330
+ it 'should be able to get attr on node 2' do
330
331
  engine.parse getattr_code
331
- engine.evaluate("E", "xx").should == [1,2,3]
332
+ engine.evaluate('E', 'xx').should == [1, 2, 3]
332
333
  end
333
334
 
334
- it "should be able to call class methods on AR classes in modules" do
335
- engine.parse defn("A:",
336
- " b = M::LittleDummy.heres_my_number(867, 5309)",
337
- )
338
- r = engine.evaluate("A", "b")
335
+ it 'should be able to call class methods on AR classes in modules' do
336
+ engine.parse defn('A:',
337
+ ' b = M::LittleDummy.heres_my_number(867, 5309)',
338
+ ' c = M::N::NestedDummy.heres_my_number(867, 5309)',
339
+ )
340
+ r = engine.evaluate('A', 'b')
341
+ r.should == 867 + 5309
342
+
343
+ r = engine.evaluate('A', 'c')
339
344
  r.should == 867 + 5309
340
345
  end
341
346
 
342
- it "should be able to use AR classes as values and call their methods" do
343
- engine.parse defn("A:",
344
- " a = M::LittleDummy",
345
- " b = a.heres_my_number(867, 5309)",
346
- )
347
- r = engine.evaluate("A", "b")
347
+ it 'should be able to use AR classes as values and call their methods' do
348
+ engine.parse defn('A:',
349
+ ' a = M::LittleDummy',
350
+ ' b = a.heres_my_number(867, 5309)',
351
+ ' c = M::N::NestedDummy.heres_my_number(867, 5309)',
352
+ )
353
+ r = engine.evaluate('A', 'b')
354
+ r.should == 867 + 5309
355
+
356
+ r = engine.evaluate('A', 'c')
348
357
  r.should == 867 + 5309
349
358
  end
350
359
 
351
- it "should ignore undeclared params sent to eval which match attr names" do
352
- engine.parse defn("A:",
353
- " d = 12",
354
- )
355
- r = engine.evaluate("A", "d", {"d" => 5, "e" => 6})
360
+ it 'should ignore undeclared params sent to eval which match attr names' do
361
+ engine.parse defn('A:',
362
+ ' d = 12',
363
+ )
364
+ r = engine.evaluate('A', 'd', 'd' => 5, 'e' => 6)
356
365
  r.should == 12
357
366
  end
358
367
 
359
- it "should handle different param defaults on nodes" do
360
- engine.parse defn("A:",
361
- " p =? 1",
362
- " c = p * 123",
363
- "B: A",
364
- " p =? 2",
365
- "C: A",
366
- " p =? 3",
367
- )
368
+ it 'should handle different param defaults on nodes' do
369
+ engine.parse defn('A:',
370
+ ' p =? 1',
371
+ ' c = p * 123',
372
+ 'B: A',
373
+ ' p =? 2',
374
+ 'C: A',
375
+ ' p =? 3',
376
+ )
368
377
 
369
- r = engine.evaluate("C", "c", {"p" => 5})
370
- r.should == 5*123
378
+ r = engine.evaluate('C', 'c', 'p' => 5)
379
+ r.should == 5 * 123
371
380
 
372
- r = engine.evaluate("B", "c", {"p" => 10})
373
- r.should == 10*123
381
+ r = engine.evaluate('B', 'c', 'p' => 10)
382
+ r.should == 10 * 123
374
383
 
375
- r = engine.evaluate("A", "c")
376
- r.should == 1*123
384
+ r = engine.evaluate('A', 'c')
385
+ r.should == 1 * 123
377
386
 
378
- r = engine.evaluate("B", "c")
379
- r.should == 2*123
387
+ r = engine.evaluate('B', 'c')
388
+ r.should == 2 * 123
380
389
 
381
- r = engine.evaluate("C", "c")
382
- r.should == 3*123
390
+ r = engine.evaluate('C', 'c')
391
+ r.should == 3 * 123
383
392
  end
384
393
 
385
- it "should allow overriding of attrs as params" do
386
- engine.parse defn("A:",
387
- " a = 2",
388
- " b = a*3",
389
- "B: A",
390
- " a =?",
391
- )
394
+ it 'should allow overriding of attrs as params' do
395
+ engine.parse defn('A:',
396
+ ' a = 2',
397
+ ' b = a*3',
398
+ 'B: A',
399
+ ' a =?',
400
+ )
392
401
 
393
- r = engine.evaluate("A", "b", {"a" => 10})
394
- r.should == 2*3
402
+ r = engine.evaluate('A', 'b', 'a' => 10)
403
+ r.should == 2 * 3
395
404
 
396
- r = engine.evaluate("B", "b", {"a" => 10})
397
- r.should == 10*3
405
+ r = engine.evaluate('B', 'b', 'a' => 10)
406
+ r.should == 10 * 3
398
407
 
399
408
  lambda {
400
- r = engine.evaluate("B", "b")
409
+ r = engine.evaluate('B', 'b')
401
410
  }.should raise_error(Delorean::UndefinedParamError)
402
-
403
411
  end
404
412
 
405
413
  sample_script = <<eof
@@ -416,663 +424,750 @@ B: A
416
424
  p =? 5
417
425
  eof
418
426
 
419
- it "should allow overriding of attrs as params" do
427
+ it 'should allow overriding of attrs as params' do
420
428
  engine.parse sample_script
421
429
 
422
- r = engine.evaluate("C", "c")
430
+ r = engine.evaluate('C', 'c')
423
431
  r.should == 4
424
432
 
425
- r = engine.evaluate("B", "pc")
433
+ r = engine.evaluate('B', 'pc')
426
434
  r.should == 4 + 5
427
435
 
428
- r = engine.evaluate("C", "pc")
436
+ r = engine.evaluate('C', 'pc')
429
437
  r.should == 4 + 3
430
438
 
431
439
  lambda {
432
- r = engine.evaluate("A", "pc")
440
+ r = engine.evaluate('A', 'pc')
433
441
  }.should raise_error(Delorean::UndefinedParamError)
434
442
  end
435
443
 
436
- it "engines of same name should be independent" do
444
+ it 'engines of same name should be independent' do
437
445
  engin2 = Delorean::Engine.new(engine.module_name)
438
446
 
439
- engine.parse defn("A:",
440
- " a = 123",
441
- " b = a*3",
442
- "B: A",
443
- " c = b*2",
444
- )
445
-
446
- engin2.parse defn("A:",
447
- " a = 222.0",
448
- " b = a/5",
449
- "B: A",
450
- " c = b*3",
451
- "C:",
452
- " d = 111",
453
- )
454
-
455
- engine.evaluate("A", ["a", "b"]).should == [123, 123*3]
456
- engin2.evaluate("A", ["a", "b"]).should == [222.0, 222.0/5]
457
-
458
- engine.evaluate("B", ["a", "b", "c"]).should == [123, 123*3, 123*3*2]
459
- engin2.evaluate("B", ["a", "b", "c"]).should ==
460
- [222.0, 222.0/5, 222.0/5*3]
461
-
462
- engin2.evaluate("C", "d").should == 111
447
+ engine.parse defn('A:',
448
+ ' a = 123',
449
+ ' b = a*3',
450
+ 'B: A',
451
+ ' c = b*2',
452
+ )
453
+
454
+ engin2.parse defn('A:',
455
+ ' a = 222.0',
456
+ ' b = a/5',
457
+ 'B: A',
458
+ ' c = b*3',
459
+ 'C:',
460
+ ' d = 111',
461
+ )
462
+
463
+ engine.evaluate('A', ['a', 'b']).should == [123, 123 * 3]
464
+ engin2.evaluate('A', ['a', 'b']).should == [222.0, 222.0 / 5]
465
+
466
+ engine.evaluate('B', ['a', 'b', 'c']).should == [123, 123 * 3, 123 * 3 * 2]
467
+ engin2.evaluate('B', ['a', 'b', 'c']).should ==
468
+ [222.0, 222.0 / 5, 222.0 / 5 * 3]
469
+
470
+ engin2.evaluate('C', 'd').should == 111
463
471
  lambda {
464
- engine.evaluate("C", "d")
472
+ engine.evaluate('C', 'd')
465
473
  }.should raise_error(Delorean::UndefinedNodeError)
466
474
  end
467
475
 
468
- it "should handle invalid expression evaluation" do
476
+ it 'should handle invalid expression evaluation' do
469
477
  # Should handle errors on expression such as -[] or -"xxx" or ("x"
470
478
  # + []) better. Currently, it raises NoMethodError.
471
479
  skip 'handle errors on expressions such as -[] or -"xxx"'
472
480
  end
473
481
 
474
- it "should eval lists" do
475
- engine.parse defn("A:",
476
- " b = []",
477
- " c = [1,2,3]",
482
+ it 'should eval lists' do
483
+ engine.parse defn('A:',
484
+ ' b = []',
485
+ ' c = [1,2,3]',
478
486
  " d = [b, c, b, c, 1, 2, '123', 1.1, -1.23]",
479
- " e = [1, 1+1, 1+1+1, 1*2*4]",
480
- )
487
+ ' e = [1, 1+1, 1+1+1, 1*2*4]',
488
+ )
481
489
 
482
- engine.evaluate("A", %w{b c d e}).should ==
490
+ engine.evaluate('A', %w[b c d e]).should ==
483
491
  [[],
484
492
  [1, 2, 3],
485
- [[], [1, 2, 3], [], [1, 2, 3], 1, 2, "123", 1.1, -1.23],
493
+ [[], [1, 2, 3], [], [1, 2, 3], 1, 2, '123', 1.1, -1.23],
486
494
  [1, 2, 3, 8],
487
495
  ]
488
496
  end
489
497
 
490
- it "should eval list expressions" do
491
- engine.parse defn("A:",
492
- " b = []+[]",
493
- " c = [1,2,3]+b",
494
- " d = c*2",
495
- )
498
+ it 'should eval list expressions' do
499
+ engine.parse defn('A:',
500
+ ' b = []+[]',
501
+ ' c = [1,2,3]+b',
502
+ ' d = c*2',
503
+ )
496
504
 
497
- engine.evaluate("A", %w{b c d}).should ==
505
+ engine.evaluate('A', %w[b c d]).should ==
498
506
  [[],
499
507
  [1, 2, 3],
500
- [1, 2, 3]*2,
508
+ [1, 2, 3] * 2,
501
509
  ]
502
510
  end
503
511
 
504
- it "should eval sets and set comprehension" do
505
- engine.parse defn("A:",
506
- " a = {-}",
507
- " b = {i*5 for i in {1,2,3}}",
508
- " c = {1,2,3} | {4,5}",
509
- )
510
- engine.evaluate("A", ["a", "b", "c"]).should ==
511
- [Set[], Set[5,10,15], Set[1,2,3,4,5]]
512
+ it 'should eval sets and set comprehension' do
513
+ engine.parse defn('A:',
514
+ ' a = {-}',
515
+ ' b = {i*5 for i in {1,2,3}}',
516
+ ' c = {1,2,3} | {4,5}',
517
+ )
518
+ engine.evaluate('A', ['a', 'b', 'c']).should ==
519
+ [Set[], Set[5, 10, 15], Set[1, 2, 3, 4, 5]]
512
520
  end
513
521
 
514
- it "should eval list comprehension" do
515
- engine.parse defn("A:",
516
- " b = [i*5 for i in [1,2,3]]",
517
- " c = [a-b for a, b in [[1,2],[4,3]]]"
518
- )
519
- engine.evaluate("A", "b").should == [5, 10, 15]
520
- engine.evaluate("A", "c").should == [-1, 1]
522
+ it 'should eval list comprehension' do
523
+ engine.parse defn('A:',
524
+ ' b = [i*5 for i in [1,2,3]]',
525
+ ' c = [a-b for a, b in [[1,2],[4,3]]]'
526
+ )
527
+ engine.evaluate('A', 'b').should == [5, 10, 15]
528
+ engine.evaluate('A', 'c').should == [-1, 1]
521
529
  end
522
530
 
523
- it "should eval nested list comprehension" do
524
- engine.parse defn("A:",
525
- " b = [[a+c for c in [4,5]] for a in [1,2,3]]",
526
- )
527
- engine.evaluate("A", "b").should == [[5, 6], [6, 7], [7, 8]]
528
-
531
+ it 'should eval nested list comprehension' do
532
+ engine.parse defn('A:',
533
+ ' b = [[a+c for c in [4,5]] for a in [1,2,3]]',
534
+ )
535
+ engine.evaluate('A', 'b').should == [[5, 6], [6, 7], [7, 8]]
529
536
  end
530
537
 
531
- it "should eval list comprehension variable override" do
532
- engine.parse defn("A:",
533
- " b = [b/2.0 for b in [1,2,3]]",
534
- )
535
- engine.evaluate("A", "b").should == [0.5, 1.0, 1.5]
538
+ it 'should eval list comprehension variable override' do
539
+ engine.parse defn('A:',
540
+ ' b = [b/2.0 for b in [1,2,3]]',
541
+ )
542
+ engine.evaluate('A', 'b').should == [0.5, 1.0, 1.5]
536
543
  end
537
544
 
538
- it "should eval list comprehension variable override (2)" do
539
- engine.parse defn("A:",
540
- " a = 1",
541
- " b = [a+1 for a in [1,2,3]]",
542
- )
543
- engine.evaluate("A", "b").should == [2, 3, 4]
545
+ it 'should eval list comprehension variable override (2)' do
546
+ engine.parse defn('A:',
547
+ ' a = 1',
548
+ ' b = [a+1 for a in [1,2,3]]',
549
+ )
550
+ engine.evaluate('A', 'b').should == [2, 3, 4]
544
551
  end
545
552
 
546
- it "should eval conditional list comprehension" do
547
- engine.parse defn("A:",
548
- " b = [i*5 for i in [1,2,3,4,5] if i%2 == 1]",
549
- " c = [i/10.0 for i in [1,2,3,4,5] if i>4]",
550
- )
551
- engine.evaluate("A", "b").should == [5, 15, 25]
552
- engine.evaluate("A", "c").should == [0.5]
553
+ it 'should eval conditional list comprehension' do
554
+ engine.parse defn('A:',
555
+ ' b = [i*5 for i in [1,2,3,4,5] if i%2 == 1]',
556
+ ' c = [i/10.0 for i in [1,2,3,4,5] if i>4]',
557
+ )
558
+ engine.evaluate('A', 'b').should == [5, 15, 25]
559
+ engine.evaluate('A', 'c').should == [0.5]
553
560
  end
554
561
 
555
- it "should handle list comprehension unpacking" do
556
- engine.parse defn("A:",
557
- " b = [a-b for a, b in [[1,2],[20,10]]]",
558
- )
559
- engine.evaluate("A", "b").should == [-1, 10]
562
+ it 'should handle list comprehension unpacking' do
563
+ engine.parse defn('A:',
564
+ ' b = [a-b for a, b in [[1,2],[20,10]]]',
565
+ )
566
+ engine.evaluate('A', 'b').should == [-1, 10]
560
567
  end
561
568
 
562
- it "should handle list comprehension with conditions using loop var" do
563
- skip "need to fix"
564
- engine.parse defn("A:",
569
+ it 'should handle list comprehension with conditions using loop var' do
570
+ skip 'need to fix'
571
+ engine.parse defn('A:',
565
572
  " b = [n for n in {'pt' : 1} if n[1]+1]",
566
- )
567
- engine.evaluate("A", "b").should == [['pt', 1]]
573
+ )
574
+ engine.evaluate('A', 'b').should == [['pt', 1]]
568
575
  end
569
576
 
570
- it "should eval hashes" do
571
- engine.parse defn("A:",
572
- " b = {}",
577
+ it 'should eval hashes' do
578
+ engine.parse defn('A:',
579
+ ' b = {}',
573
580
  " c = {'a':1, 'b': 2,'c':3}",
574
581
  " d = {123*2: -123, 'b_b': 1+1}",
575
582
  " e = {'x': 1, 'y': 1+1, 'z': 1+1+1, 'zz': 1*2*4}",
576
583
  " f = {'a': nil, 'b': [1, nil, 2]}",
577
- " g = {b:b, [b]:[1,23], []:345}",
578
- )
584
+ ' g = {b:b, [b]:[1,23], []:345}',
585
+ )
579
586
 
580
- engine.evaluate("A", %w{b c d e f g}).should ==
587
+ engine.evaluate('A', %w[b c d e f g]).should ==
581
588
  [{},
582
- {"a"=>1, "b"=>2, "c"=>3},
583
- {123*2=>-123, "b_b"=>2},
584
- {"x"=>1, "y"=>2, "z"=>3, "zz"=>8},
585
- {"a"=>nil, "b"=>[1, nil, 2]},
586
- {{}=>{}, [{}]=>[1, 23], []=>345},
589
+ { 'a' => 1, 'b' => 2, 'c' => 3 },
590
+ { 123 * 2 => -123, 'b_b' => 2 },
591
+ { 'x' => 1, 'y' => 2, 'z' => 3, 'zz' => 8 },
592
+ { 'a' => nil, 'b' => [1, nil, 2] },
593
+ { {} => {}, [{}] => [1, 23], [] => 345 },
587
594
  ]
588
595
  end
589
596
 
590
- it "handles literal hashes with conditionals" do
591
- engine.parse defn("A:",
597
+ it 'handles literal hashes with conditionals' do
598
+ engine.parse defn('A:',
592
599
  " a = {'a':1 if 123, 'b':'x' if nil}",
593
600
  " b = {'a':a if a, 2: a if true, 'c':nil if 2*2}",
594
- " c = 1>2",
595
- " d = {1: {1: 2 if b}, 3: 3 if c, 2: {2: 3 if a}}",
596
- )
597
-
598
- engine.evaluate("A", %w{a b d}).should == [
599
- {"a"=>1},
600
- {"a"=>{"a"=>1}, 2=>{"a"=>1}, "c"=>nil},
601
- {1=>{1=>2}, 2=>{2=>3}},
601
+ ' c = 1>2',
602
+ ' d = {1: {1: 2 if b}, 3: 3 if c, 2: {2: 3 if a}}',
603
+ )
604
+
605
+ engine.evaluate('A', %w[a b d]).should == [
606
+ { 'a' => 1 },
607
+ { 'a' => { 'a' => 1 }, 2 => { 'a' => 1 }, 'c' => nil },
608
+ { 1 => { 1 => 2 }, 2 => { 2 => 3 } },
602
609
  ]
603
610
  end
604
611
 
605
- it "should eval hash comprehension" do
606
- engine.parse defn("A:",
607
- " b = {i*5 :i for i in [1,2,3]}",
608
- " c = [kv for kv in {1:11, 2:22}]",
609
- )
610
- engine.evaluate("A", "b").should == {5=>1, 10=>2, 15=>3}
611
- engine.evaluate("A", "c").should == [[1, 11], [2, 22]]
612
+ it 'should eval hash comprehension' do
613
+ engine.parse defn('A:',
614
+ ' b = {i*5 :i for i in [1,2,3]}',
615
+ ' c = [kv for kv in {1:11, 2:22}]',
616
+ )
617
+ engine.evaluate('A', 'b').should == { 5 => 1, 10 => 2, 15 => 3 }
618
+ engine.evaluate('A', 'c').should == [[1, 11], [2, 22]]
612
619
  end
613
620
 
614
- it "for-in-hash should iterate over key/value pairs" do
615
- engine.parse defn("A:",
616
- " b = {1: 11, 2: 22}",
617
- " c = [kv[0]-kv[1] for kv in b]",
618
- " d = {kv[0] : kv[1] for kv in b}",
619
- " e = [kv for kv in b if kv[1]]",
620
- " f = [k-v for k, v in b if k>1]",
621
- )
622
- engine.evaluate("A", "c").should == [-10, -20]
623
- engine.evaluate("A", "d").should == {1=>11, 2=>22}
624
- engine.evaluate("A", "f").should == [-20]
621
+ it 'for-in-hash should iterate over key/value pairs' do
622
+ engine.parse defn('A:',
623
+ ' b = {1: 11, 2: 22}',
624
+ ' c = [kv[0]-kv[1] for kv in b]',
625
+ ' d = {kv[0] : kv[1] for kv in b}',
626
+ ' e = [kv for kv in b if kv[1]]',
627
+ ' f = [k-v for k, v in b if k>1]',
628
+ )
629
+ engine.evaluate('A', 'c').should == [-10, -20]
630
+ engine.evaluate('A', 'd').should == { 1 => 11, 2 => 22 }
631
+ engine.evaluate('A', 'f').should == [-20]
625
632
 
626
633
  # FIXME: this is a known bug in Delorean caused by the strange way
627
634
  # that select iterates over hashes and provides args to the block.
628
635
  # engine.evaluate("A", "e").should == [[1,11], [2,22]]
629
636
  end
630
637
 
631
- it "should eval nested hash comprehension" do
632
- engine.parse defn("A:",
633
- " b = { a:{a+c:a-c for c in [4,5]} for a in [1,2,3]}",
634
- )
635
- engine.evaluate("A", "b").should ==
636
- {1=>{5=>-3, 6=>-4}, 2=>{6=>-2, 7=>-3}, 3=>{7=>-1, 8=>-2}}
638
+ it 'should eval nested hash comprehension' do
639
+ engine.parse defn('A:',
640
+ ' b = { a:{a+c:a-c for c in [4,5]} for a in [1,2,3]}',
641
+ )
642
+ engine.evaluate('A', 'b').should == {
643
+ 1 => { 5 => -3, 6 => -4 },
644
+ 2 => { 6 => -2, 7 => -3 },
645
+ 3 => { 7 => -1, 8 => -2 }
646
+ }
637
647
  end
638
648
 
639
- it "should eval conditional hash comprehension" do
640
- engine.parse defn("A:",
641
- " b = {i*5:i+5 for i in [1,2,3,4,5] if i%2 == 1}",
642
- " c = {i/10.0:i*10 for i in [1,2,3,4,5] if i>4}",
643
- )
644
- engine.evaluate("A", "b").should == {5=>6, 15=>8, 25=>10}
645
- engine.evaluate("A", "c").should == {0.5=>50}
649
+ it 'should eval conditional hash comprehension' do
650
+ engine.parse defn('A:',
651
+ ' b = {i*5:i+5 for i in [1,2,3,4,5] if i%2 == 1}',
652
+ ' c = {i/10.0:i*10 for i in [1,2,3,4,5] if i>4}',
653
+ )
654
+ engine.evaluate('A', 'b').should == { 5 => 6, 15 => 8, 25 => 10 }
655
+ engine.evaluate('A', 'c').should == { 0.5 => 50 }
646
656
  end
647
657
 
648
- it "should eval node calls as intermediate results" do
649
- engine.parse defn("A:",
650
- " a =?",
651
- " e = A(a=13)",
652
- " d = e.a * 2",
653
- " f = e.d / e.a",
654
- )
658
+ it 'should eval node calls as intermediate results' do
659
+ engine.parse defn('A:',
660
+ ' a =?',
661
+ ' e = A(a=13)',
662
+ ' d = e.a * 2',
663
+ ' f = e.d / e.a',
664
+ )
655
665
 
656
- engine.evaluate("A", ["d", "f"]).should == [26, 2]
666
+ engine.evaluate('A', ['d', 'f']).should == [26, 2]
657
667
  end
658
668
 
659
- it "allows node calls from attrs" do
660
- engine.parse defn("A:",
661
- " a =?",
662
- " c =?",
663
- " b = a**2",
664
- " e = A(a=13)",
669
+ it 'allows node calls from attrs' do
670
+ engine.parse defn('A:',
671
+ ' a =?',
672
+ ' c =?',
673
+ ' b = a**2',
674
+ ' e = A(a=13)',
665
675
  " d = e(a=4, **{'c': 5})",
666
- " f = d.b + d.c + e().a",
667
- )
676
+ ' f = d.b + d.c + e().a',
677
+ )
668
678
 
669
- engine.evaluate("A", ["f"]).should == [16+5+13]
679
+ engine.evaluate('A', ['f']).should == [16 + 5 + 13]
670
680
  end
671
681
 
672
- it "should eval multi-var hash comprehension" do
673
- engine.parse defn("A:",
674
- " b = {k*5 : v+1 for k, v in {1:2, 7:-30}}",
675
- " c = [k-v for k, v in {1:2, 7:-30}]",
676
- )
677
- engine.evaluate("A", "b").should == {5=>3, 35=>-29}
678
- engine.evaluate("A", "c").should == [-1, 37]
682
+ it 'should eval multi-var hash comprehension' do
683
+ engine.parse defn('A:',
684
+ ' b = {k*5 : v+1 for k, v in {1:2, 7:-30}}',
685
+ ' c = [k-v for k, v in {1:2, 7:-30}]',
686
+ )
687
+ engine.evaluate('A', 'b').should == { 5 => 3, 35 => -29 }
688
+ engine.evaluate('A', 'c').should == [-1, 37]
679
689
  end
680
690
 
681
- it "should be able to amend node calls" do
682
- engine.parse defn("A:",
683
- " a =?",
684
- " aa = a*2",
685
- " c = A(a=12)",
691
+ it 'should be able to amend node calls' do
692
+ engine.parse defn('A:',
693
+ ' a =?',
694
+ ' aa = a*2',
695
+ ' c = A(a=12)',
686
696
  " d = c+{'a':3}",
687
697
  " f = c+{'a':4}",
688
- " g = d.aa + f.aa",
689
- " h = c(a=5).aa",
690
- " j = d(a=6).aa",
691
- )
698
+ ' g = d.aa + f.aa',
699
+ ' h = c(a=5).aa',
700
+ ' j = d(a=6).aa',
701
+ )
692
702
 
693
- engine.evaluate("A", ["g", "h", "j"]).should ==
694
- [3*2 + 4*2, 5*2, 6*2]
703
+ engine.evaluate('A', ['g', 'h', 'j']).should ==
704
+ [3 * 2 + 4 * 2, 5 * 2, 6 * 2]
695
705
  end
696
706
 
697
- it "should be able to amend node calls 2" do
698
- engine.parse defn("A:",
699
- " a =?",
700
- " d = A(a=3)",
701
- " e = [d.a, d(a=4).a]",
702
- )
707
+ it 'should be able to amend node calls 2' do
708
+ engine.parse defn('A:',
709
+ ' a =?',
710
+ ' d = A(a=3)',
711
+ ' e = [d.a, d(a=4).a]',
712
+ )
703
713
 
704
- engine.evaluate("A", ["e"]).should == [[3,4]]
714
+ engine.evaluate('A', ['e']).should == [[3, 4]]
705
715
  end
706
716
 
707
- it "should eval module calls 1" do
708
- engine.parse defn("A:",
709
- " a = 123",
710
- " n = A",
711
- " d = n().a",
712
- )
717
+ it 'should eval module calls 1' do
718
+ engine.parse defn('A:',
719
+ ' a = 123',
720
+ ' n = A',
721
+ ' d = n().a',
722
+ )
713
723
 
714
- engine.evaluate("A", %w{d}).should == [123]
724
+ engine.evaluate('A', %w[d]).should == [123]
715
725
  end
716
726
 
717
- it "should eval module calls 2" do
718
- engine.parse defn("A:",
719
- " a = 123",
720
- " b = 456 + a",
727
+ it 'should eval module calls 2' do
728
+ engine.parse defn('A:',
729
+ ' a = 123',
730
+ ' b = 456 + a',
721
731
  " n = 'A'",
722
732
  " c = nil(x = 123, y = 456) % ['a', 'b']",
723
733
  " d = n(x = 123, y = 456) % ['a', 'b']",
724
734
  " e = nil() % ['b']",
725
- )
735
+ )
726
736
 
727
- engine.evaluate("A", %w{n c d e}).should ==
728
- ["A", {"a"=>123, "b"=>579}, {"a"=>123, "b"=>579}, {"b"=>579}]
737
+ engine.evaluate('A', %w[n c d e]).should == [
738
+ 'A',
739
+ { 'a' => 123, 'b' => 579 },
740
+ { 'a' => 123, 'b' => 579 },
741
+ { 'b' => 579 }
742
+ ]
729
743
  end
730
744
 
731
- it "should eval module calls 3" do
732
- engine.parse defn("A:",
733
- " a = 123",
734
- "B:",
745
+ it 'should eval module calls 3' do
746
+ engine.parse defn('A:',
747
+ ' a = 123',
748
+ 'B:',
735
749
  " n = 'A'",
736
- " d = n().a",
737
- )
750
+ ' d = n().a',
751
+ )
738
752
 
739
- engine.evaluate("B", %w{d}).should == [123]
753
+ engine.evaluate('B', %w[d]).should == [123]
740
754
  end
741
755
 
742
- it "should be possible to implement recursive calls" do
743
- engine.parse defn("A:",
744
- " n =?",
745
- " fact = if n <= 1 then 1 else n * A(n=n-1).fact",
746
- )
756
+ it 'should be possible to implement recursive calls' do
757
+ engine.parse defn('A:',
758
+ ' n =?',
759
+ ' fact = if n <= 1 then 1 else n * A(n=n-1).fact',
760
+ )
747
761
 
748
- engine.evaluate("A", "fact", "n" => 10).should == 3628800
762
+ engine.evaluate('A', 'fact', 'n' => 10).should == 3_628_800
749
763
  end
750
764
 
751
- it "should eval module calls by node name" do
752
- engine.parse defn("A:",
753
- " a = 123",
754
- " b = A().a",
755
- )
756
- engine.evaluate("A", "b").should == 123
765
+ it 'should eval module calls by node name' do
766
+ engine.parse defn('A:',
767
+ ' a = 123',
768
+ ' b = A().a',
769
+ )
770
+ engine.evaluate('A', 'b').should == 123
757
771
  end
758
772
 
759
- it "should eval multiline expressions" do
760
- engine.parse defn("A:",
761
- " a = 1",
762
- " b = [a+1",
763
- " for a in [1,2,3]",
764
- " ]",
765
- )
766
- engine.evaluate("A", "b").should == [2, 3, 4]
773
+ it 'should eval multiline expressions' do
774
+ engine.parse defn('A:',
775
+ ' a = 1',
776
+ ' b = [a+1',
777
+ ' for a in [1,2,3]',
778
+ ' ]',
779
+ )
780
+ engine.evaluate('A', 'b').should == [2, 3, 4]
767
781
  end
768
782
 
769
- it "should eval multiline expressions (2)" do
770
- engine.parse defn("A:",
771
- " a = 123",
772
- " b = 456 + ",
773
- " a",
783
+ it 'should eval multiline expressions (2)' do
784
+ engine.parse defn('A:',
785
+ ' a = 123',
786
+ ' b = 456 + ',
787
+ ' a',
774
788
  " n = 'A'",
775
- " c = nil(x = 123,",
789
+ ' c = nil(x = 123,',
776
790
  " y = 456) % ['a', 'b']",
777
- " d = n(",
791
+ ' d = n(',
778
792
  " x = 123, y = 456) % ['a', 'b']",
779
- " e = nil(",
793
+ ' e = nil(',
780
794
  " ) % ['b']",
781
- )
795
+ )
782
796
 
783
- engine.evaluate("A", %w{n c d e}).should ==
784
- ["A", {"a"=>123, "b"=>579}, {"a"=>123, "b"=>579}, {"b"=>579}]
797
+ engine.evaluate('A', %w[n c d e]).should == [
798
+ 'A',
799
+ { 'a' => 123, 'b' => 579 },
800
+ { 'a' => 123, 'b' => 579 },
801
+ { 'b' => 579 }
802
+ ]
785
803
  end
786
804
 
787
- it "should eval in expressions" do
788
- engine.parse defn("A:",
789
- " a = [1,2,3,33,44]",
790
- " s = {22,33,44}",
791
- " b = (1 in a) && (2 in {22,44})",
792
- " c = (2 in a) && (22 in s)",
793
- " d = [i*2 for i in s if i in a]",
794
- )
805
+ it 'should eval in expressions' do
806
+ engine.parse defn('A:',
807
+ ' a = [1,2,3,33,44]',
808
+ ' s = {22,33,44}',
809
+ ' b = (1 in a) && (2 in {22,44})',
810
+ ' c = (2 in a) && (22 in s)',
811
+ ' d = [i*2 for i in s if i in a]',
812
+ )
795
813
 
796
- engine.evaluate("A", %w{b c d}).should ==
814
+ engine.evaluate('A', %w[b c d]).should ==
797
815
  [false, true, [66, 88]]
798
816
  end
799
817
 
800
- it "should eval imports" do
801
- engine.parse defn("import AAA",
802
- "A:",
803
- " b = 456",
804
- "B: AAA::X",
805
- " a = 111",
806
- " c = AAA::X(a=456).b",
807
- )
808
- engine.evaluate("B", ["a", "b", "c"], {}).should ==
809
- [111, 222, 456*2]
810
- end
811
-
812
- it "should eval imports (2)" do
813
- sset.merge({
814
- "BBB" =>
815
- defn("import AAA",
816
- "B: AAA::X",
817
- " a = 111",
818
- " c = AAA::X(a=-1).b",
819
- " d = a * 2",
820
- ),
821
- "CCC" =>
822
- defn("import BBB",
823
- "import AAA",
824
- "B: BBB::B",
825
- " e = d * 3",
826
- "C: AAA::X",
827
- " d = b * 3",
828
- ),
829
- })
830
-
831
- e2 = sset.get_engine("BBB")
832
-
833
- e2.evaluate("B", ["a", "b", "c", "d"]).should ==
818
+ it 'should eval imports' do
819
+ engine.parse defn('import AAA',
820
+ 'A:',
821
+ ' b = 456',
822
+ 'B: AAA::X',
823
+ ' a = 111',
824
+ ' c = AAA::X(a=456).b',
825
+ )
826
+ engine.evaluate('B', ['a', 'b', 'c'], {}).should ==
827
+ [111, 222, 456 * 2]
828
+ end
829
+
830
+ it 'should eval imports (2)' do
831
+ sset.merge(
832
+ 'BBB' =>
833
+ defn('import AAA',
834
+ 'B: AAA::X',
835
+ ' a = 111',
836
+ ' c = AAA::X(a=-1).b',
837
+ ' d = a * 2',
838
+ ),
839
+ 'CCC' =>
840
+ defn('import BBB',
841
+ 'import AAA',
842
+ 'B: BBB::B',
843
+ ' e = d * 3',
844
+ 'C: AAA::X',
845
+ ' d = b * 3',
846
+ ),
847
+ )
848
+
849
+ e2 = sset.get_engine('BBB')
850
+
851
+ e2.evaluate('B', ['a', 'b', 'c', 'd']).should ==
834
852
  [111, 222, -2, 222]
835
853
 
836
- engine.parse defn("import BBB",
837
- "B: BBB::B",
838
- " e = d + 3",
839
- )
854
+ engine.parse defn('import BBB',
855
+ 'B: BBB::B',
856
+ ' e = d + 3',
857
+ )
840
858
 
841
- engine.evaluate("B", ["a", "b", "c", "d", "e"]).should ==
859
+ engine.evaluate('B', ['a', 'b', 'c', 'd', 'e']).should ==
842
860
  [111, 222, -2, 222, 225]
843
861
 
844
- e4 = sset.get_engine("CCC")
862
+ e4 = sset.get_engine('CCC')
845
863
 
846
- e4.evaluate("B", ["a", "b", "c", "d", "e"]).should ==
864
+ e4.evaluate('B', ['a', 'b', 'c', 'd', 'e']).should ==
847
865
  [111, 222, -2, 222, 666]
848
866
 
849
- e4.evaluate("C", ["a", "b", "d"]).should == [123, 123*2, 123*3*2]
850
- end
851
-
852
- it "should eval imports (3)" do
853
- sset.merge({
854
- "BBB" => getattr_code,
855
- "CCC" =>
856
- defn("import BBB",
857
- "X:",
858
- " xx = [n.x for n in BBB::D().xs]",
859
- " yy = [n.x for n in BBB::D.xs]",
860
- ),
861
- })
862
-
863
- e4 = sset.get_engine("CCC")
864
- e4.evaluate("X", "xx").should == [1,2,3]
865
- e4.evaluate("X", "yy").should == [1,2,3]
866
- end
867
-
868
- it "can eval indexing" do
869
- engine.parse defn("A:",
870
- " a = [1,2,3]",
871
- " b = a[1]",
872
- " c = a[-1]",
867
+ e4.evaluate('C', ['a', 'b', 'd']).should == [123, 123 * 2, 123 * 3 * 2]
868
+ end
869
+
870
+ it 'should eval imports (3)' do
871
+ sset.merge(
872
+ 'BBB' => getattr_code,
873
+ 'CCC' =>
874
+ defn('import BBB',
875
+ 'X:',
876
+ ' xx = [n.x for n in BBB::D().xs]',
877
+ ' yy = [n.x for n in BBB::D.xs]',
878
+ ),
879
+ )
880
+
881
+ e4 = sset.get_engine('CCC')
882
+ e4.evaluate('X', 'xx').should == [1, 2, 3]
883
+ e4.evaluate('X', 'yy').should == [1, 2, 3]
884
+ end
885
+
886
+ it 'should eval imports (4) - with ::' do
887
+ sset.merge(
888
+ 'BBB' => getattr_code,
889
+ 'BBB::A' => defn(
890
+ 'X:',
891
+ ' xx = [1, 2, 3]'
892
+ ),
893
+ 'BBB::A::CC' => defn(
894
+ 'X:',
895
+ ' xx = [1, 2, 3]'
896
+ ),
897
+ 'DDD__Ef__Gh' => defn(
898
+ 'import BBB',
899
+ 'import BBB::A',
900
+ 'import BBB::A::CC',
901
+ 'EfNode:',
902
+ ' g = BBB::D.xs',
903
+ ' gh = BBB::A::X.xx',
904
+ ),
905
+ 'CCC' =>
906
+ defn('import BBB',
907
+ 'import DDD__Ef__Gh',
908
+ 'X:',
909
+ ' xx = [n.x for n in BBB::D().xs]',
910
+ ' yy = [n.x for n in BBB::D.xs]',
911
+ ' zz = [n * 2 for n in DDD__Ef__Gh::EfNode.gh]',
912
+ ),
913
+ )
914
+
915
+ e4 = sset.get_engine('CCC')
916
+ e4.evaluate('X', 'xx').should == [1, 2, 3]
917
+ e4.evaluate('X', 'zz').should == [2, 4, 6]
918
+ end
919
+
920
+ it 'should eval imports (4) - inheritance - with ::' do
921
+ sset.merge(
922
+ 'BBB' => getattr_code,
923
+ 'BBB::A' => defn(
924
+ 'X:',
925
+ ' xx = [1, 2, 3]'
926
+ ),
927
+ 'BBB::A::CC' => defn(
928
+ 'X:',
929
+ ' xx = [1, 2, 3]'
930
+ ),
931
+ 'DDD__Ef__Gh' => defn(
932
+ 'import BBB',
933
+ 'import BBB::A',
934
+ 'import BBB::A::CC',
935
+ 'EfNode: BBB::A::CC::X',
936
+ ' g = xx',
937
+ ),
938
+ 'CCC' =>
939
+ defn('import BBB',
940
+ 'import DDD__Ef__Gh',
941
+ 'X:',
942
+ ' zz = [n * 2 for n in DDD__Ef__Gh::EfNode.g]',
943
+ ),
944
+ )
945
+
946
+ e4 = sset.get_engine('CCC')
947
+ e4.evaluate('X', 'zz').should == [2, 4, 6]
948
+ end
949
+
950
+ it 'can eval indexing' do
951
+ engine.parse defn('A:',
952
+ ' a = [1,2,3]',
953
+ ' b = a[1]',
954
+ ' c = a[-1]',
873
955
  " d = {'a' : 123, 'b': 456}",
874
956
  " e = d['b']",
875
- " f = a[1,2]",
876
- )
877
- r = engine.evaluate("A", ["b", "c", "e", "f"])
878
- r.should == [2, 3, 456, [2,3]]
957
+ ' f = a[1,2]',
958
+ )
959
+ r = engine.evaluate('A', ['b', 'c', 'e', 'f'])
960
+ r.should == [2, 3, 456, [2, 3]]
879
961
  end
880
962
 
881
- it "can eval indexing 2" do
882
- engine.parse defn("A:",
883
- " a = 1",
963
+ it 'can eval indexing 2' do
964
+ engine.parse defn('A:',
965
+ ' a = 1',
884
966
  " b = {'x' : 123, 'y': 456}",
885
967
  " c = A() % ['a', 'b']",
886
968
  " d = c['b'].x * c['a'] - c['b'].y",
887
- )
888
- r = engine.evaluate("A", ["a", "b", "c", "d"])
889
- r.should ==
890
- [1, {"x"=>123, "y"=>456}, {"a"=>1, "b"=>{"x"=>123, "y"=>456}}, -333]
969
+ )
970
+ r = engine.evaluate('A', ['a', 'b', 'c', 'd'])
971
+ r.should == [
972
+ 1,
973
+ { 'x' => 123, 'y' => 456 },
974
+ { 'a' => 1, 'b' => { 'x' => 123, 'y' => 456 } },
975
+ -333
976
+ ]
891
977
  end
892
978
 
893
- it "can handle exceptions with / syntax" do
894
- engine.parse defn("A:",
895
- " a = 1",
979
+ it 'can handle exceptions with / syntax' do
980
+ engine.parse defn('A:',
981
+ ' a = 1',
896
982
  " b = {'x' : 123, 'y': 456}",
897
983
  " e = ERR('hello')",
898
984
  " c = A() / ['a', 'b']",
899
985
  " d = A() / ['a', 'e']",
900
986
  " f = A() / 'a'",
901
- )
902
- r = engine.evaluate("A", ["a", "b", "c"])
903
- r.should ==
904
- [1, {"x"=>123, "y"=>456}, {"a"=>1, "b"=>{"x"=>123, "y"=>456}}]
987
+ )
988
+ r = engine.evaluate('A', ['a', 'b', 'c'])
989
+ r.should == [
990
+ 1,
991
+ { 'x' => 123, 'y' => 456 },
992
+ { 'a' => 1, 'b' => { 'x' => 123, 'y' => 456 } }
993
+ ]
905
994
 
906
- r = engine.evaluate("A", ["a", "d"])
907
- r.should ==
908
- [1, {"error"=>"hello", "backtrace"=>[["XXX", 4, "e"], ["XXX", 6, "d"]]}]
995
+ r = engine.evaluate('A', ['a', 'd'])
996
+ r.should == [
997
+ 1,
998
+ { 'error' => 'hello', 'backtrace' => [['XXX', 4, 'e'], ['XXX', 6, 'd']] }
999
+ ]
909
1000
 
910
- r = engine.evaluate("A", ["f"])
1001
+ r = engine.evaluate('A', ['f'])
911
1002
  r.should == [1]
912
1003
  end
913
1004
 
914
- it "should properly eval overridden attrs" do
915
- engine.parse defn("A:",
916
- " a = 5",
917
- " b = a",
918
- "B: A",
919
- " a = 2",
920
- " x = A.b - B.b",
921
- " k = [A.b, B.b]",
922
- " l = [x.b for x in [A, B]]",
923
- " m = [x().b for x in [A, B]]",
924
- )
925
-
926
- engine.evaluate("A", "b").should == 5
927
- engine.evaluate("B", "b").should == 2
928
- engine.evaluate("B", "x").should == 3
929
- engine.evaluate("B", "k").should == [5, 2]
930
- engine.evaluate("B", "l").should == [5, 2]
931
- engine.evaluate("B", "m").should == [5, 2]
932
- end
933
-
934
- it "implements simple version of self (_)" do
935
- engine.parse defn("B:",
936
- " a =?",
937
- " b =?",
938
- " x = a - b",
939
- "A:",
940
- " a =?",
941
- " b =?",
942
- " x = _.a * _.b",
943
- " y = a && _",
944
- " z = (B() + _).x",
945
- " w = B(**_).x",
1005
+ it 'should properly eval overridden attrs' do
1006
+ engine.parse defn('A:',
1007
+ ' a = 5',
1008
+ ' b = a',
1009
+ 'B: A',
1010
+ ' a = 2',
1011
+ ' x = A.b - B.b',
1012
+ ' k = [A.b, B.b]',
1013
+ ' l = [x.b for x in [A, B]]',
1014
+ ' m = [x().b for x in [A, B]]',
1015
+ )
1016
+
1017
+ engine.evaluate('A', 'b').should == 5
1018
+ engine.evaluate('B', 'b').should == 2
1019
+ engine.evaluate('B', 'x').should == 3
1020
+ engine.evaluate('B', 'k').should == [5, 2]
1021
+ engine.evaluate('B', 'l').should == [5, 2]
1022
+ engine.evaluate('B', 'm').should == [5, 2]
1023
+ end
1024
+
1025
+ it 'implements simple version of self (_)' do
1026
+ engine.parse defn('B:',
1027
+ ' a =?',
1028
+ ' b =?',
1029
+ ' x = a - b',
1030
+ 'A:',
1031
+ ' a =?',
1032
+ ' b =?',
1033
+ ' x = _.a * _.b',
1034
+ ' y = a && _',
1035
+ ' z = (B() + _).x',
1036
+ ' w = B(**_).x',
946
1037
  " v = {**_, 'a': 123}",
947
- )
948
-
949
- engine.evaluate("A", "x", {"a"=>3, "b"=>5}).should == 15
950
- h = {"a"=>1, "b"=>2, "c"=>3}
951
- engine.evaluate("A", "y", {"a"=>1, "b"=>2, "c"=>3}).should == h
952
- engine.evaluate("A", "z", {"a"=>1, "b"=>2, "c"=>3}).should == -1
953
- engine.evaluate("A", "w", {"a"=>4, "b"=>5, "c"=>3}).should == -1
954
- engine.evaluate("A", "v", {"a"=>4, "b"=>5, "c"=>3}).should == {
955
- "a"=>123, "b"=>5, "c"=>3}
956
- end
957
-
958
- it "implements positional args in node calls" do
959
- engine.parse defn("B:",
960
- " a =?",
961
- " b =?",
962
- " x = (_.0 - _.1) * (a - b)",
963
- " y = [_.0, _.1, _.2]",
964
- "A:",
965
- " a = _.0 - _.1",
966
- " z = B(10, 20, a=3, b=7).x",
1038
+ )
1039
+
1040
+ engine.evaluate('A', 'x', 'a' => 3, 'b' => 5).should == 15
1041
+ h = { 'a' => 1, 'b' => 2, 'c' => 3 }
1042
+ engine.evaluate('A', 'y', 'a' => 1, 'b' => 2, 'c' => 3).should == h
1043
+ engine.evaluate('A', 'z', 'a' => 1, 'b' => 2, 'c' => 3).should == -1
1044
+ engine.evaluate('A', 'w', 'a' => 4, 'b' => 5, 'c' => 3).should == -1
1045
+ engine.evaluate('A', 'v', 'a' => 4, 'b' => 5, 'c' => 3).should == {
1046
+ 'a' => 123, 'b' => 5, 'c' => 3
1047
+ }
1048
+ end
1049
+
1050
+ it 'implements positional args in node calls' do
1051
+ engine.parse defn('B:',
1052
+ ' a =?',
1053
+ ' b =?',
1054
+ ' x = (_.0 - _.1) * (a - b)',
1055
+ ' y = [_.0, _.1, _.2]',
1056
+ 'A:',
1057
+ ' a = _.0 - _.1',
1058
+ ' z = B(10, 20, a=3, b=7).x',
967
1059
  " y = B('x', 'y').y",
968
- )
969
- engine.evaluate("A", ["a", "z", "y"], {0 => 123, 1 => 456}).should ==
970
- [123-456, 40, ["x", "y", nil]]
1060
+ )
1061
+ engine.evaluate('A', ['a', 'z', 'y'], 0 => 123, 1 => 456).should ==
1062
+ [123 - 456, 40, ['x', 'y', nil]]
971
1063
  end
972
1064
 
973
- it "can call 0-arity functions in list comprehension" do
974
- engine.parse defn("A:",
1065
+ it 'can call 0-arity functions in list comprehension' do
1066
+ engine.parse defn('A:',
975
1067
  ' b = [x.name for x in Dummy.all_of_me]',
976
- )
977
- r = engine.evaluate("A", "b")
978
- expect(r).to eq ["hello"]
1068
+ )
1069
+ r = engine.evaluate('A', 'b')
1070
+ expect(r).to eq ['hello']
979
1071
  end
980
1072
 
981
- it "node calls are not memoized/cached" do
982
- engine.parse defn("A:",
983
- " x = Dummy.side_effect",
984
- "B: A",
985
- " x = (A() + _).x + (A() + _).x"
1073
+ it 'node calls are not memoized/cached' do
1074
+ engine.parse defn('A:',
1075
+ ' x = Dummy.side_effect',
1076
+ 'B: A',
1077
+ ' x = (A() + _).x + (A() + _).x'
986
1078
  )
987
- r = engine.evaluate("B", "x")
1079
+ r = engine.evaluate('B', 'x')
988
1080
  expect(r).to eq 3
989
1081
  end
990
1082
 
991
- it "node calls with double splats" do
992
- engine.parse defn("A:",
993
- " a =?",
994
- " b =?",
995
- " c = a+b",
1083
+ it 'node calls with double splats' do
1084
+ engine.parse defn('A:',
1085
+ ' a =?',
1086
+ ' b =?',
1087
+ ' c = a+b',
996
1088
  " h = {'a': 123}",
997
1089
  " k = {'b': 456}",
998
- " x = A(**h, **k).c"
1090
+ ' x = A(**h, **k).c'
999
1091
  )
1000
- r = engine.evaluate("A", "x")
1092
+ r = engine.evaluate('A', 'x')
1001
1093
  expect(r).to eq 579
1002
1094
  end
1003
1095
 
1004
- it "hash literal with double splats" do
1005
- engine.parse defn("A:",
1006
- " a =?",
1007
- " b =?",
1096
+ it 'hash literal with double splats' do
1097
+ engine.parse defn('A:',
1098
+ ' a =?',
1099
+ ' b =?',
1008
1100
  " h = {'a': 123, **a}",
1009
1101
  " k = {'b': 456, **h, **a, **b}",
1010
- " l = {**k}",
1011
- " m = {**k, 1:1, 2:2, 3:33}",
1012
- " n = {**k if false, 1:1, 2:2, 3:33}",
1102
+ ' l = {**k}',
1103
+ ' m = {**k, 1:1, 2:2, 3:33}',
1104
+ ' n = {**k if false, 1:1, 2:2, 3:33}',
1013
1105
  )
1014
- r = engine.evaluate("A", ["h", "k", "l", "m", "n"],
1015
- {"a" => {3=>3, 4=>4}, "b" => {5=>5, "a" => "aa"}})
1106
+ r = engine.evaluate(
1107
+ 'A',
1108
+ ['h', 'k', 'l', 'm', 'n'],
1109
+ 'a' => { 3 => 3, 4 => 4 }, 'b' => { 5 => 5, 'a' => 'aa' }
1110
+ )
1111
+
1016
1112
  expect(r).to eq [
1017
- {"a"=>123, 3=>3, 4=>4},
1018
- {"b"=>456, "a"=>"aa", 3=>3, 4=>4, 5=>5},
1019
- {"b"=>456, "a"=>"aa", 3=>3, 4=>4, 5=>5},
1020
- {"b"=>456, "a"=>"aa", 3=>33, 4=>4, 5=>5, 1=>1, 2=>2},
1021
- {1=>1, 2=>2, 3=>33},
1022
- ]
1023
- end
1024
-
1025
- it "understands openstructs" do
1026
- engine.parse defn("A:",
1027
- " os = Dummy.returns_openstruct",
1028
- " abc = os.abc",
1029
- " not_found = os.not_found"
1030
- )
1031
- r = engine.evaluate("A", ["os", "abc", "not_found"])
1032
- expect(r[0].abc).to eq("def")
1033
- expect(r[1]).to eq("def")
1034
- expect(r[2]).to be_nil
1113
+ { 'a' => 123, 3 => 3, 4 => 4 },
1114
+ { 'b' => 456, 'a' => 'aa', 3 => 3, 4 => 4, 5 => 5 },
1115
+ { 'b' => 456, 'a' => 'aa', 3 => 3, 4 => 4, 5 => 5 },
1116
+ { 'b' => 456, 'a' => 'aa', 3 => 33, 4 => 4, 5 => 5, 1 => 1, 2 => 2 },
1117
+ { 1 => 1, 2 => 2, 3 => 33 },
1118
+ ]
1035
1119
  end
1036
1120
 
1037
- it "can use nodes as continuations" do
1121
+ it 'understands openstructs' do
1122
+ engine.parse defn('A:',
1123
+ ' os = Dummy.returns_openstruct',
1124
+ ' abc = os.abc',
1125
+ ' not_found = os.not_found'
1126
+ )
1127
+ r = engine.evaluate('A', ['os', 'abc', 'not_found'])
1128
+ expect(r[0].abc).to eq('def')
1129
+ expect(r[1]).to eq('def')
1130
+ expect(r[2]).to be_nil
1131
+ end
1038
1132
 
1133
+ it 'can use nodes as continuations' do
1039
1134
  # FIME: This is actually a trivial exmaple. Ideally we should be
1040
1135
  # able to pass arguments to the nodes when evaluating ys. If the
1041
1136
  # arguments do not change the computation of "x" then "x" should
1042
1137
  # not be recomputed. This would need some flow analysis though.
1043
1138
 
1044
- engine.parse defn("A:",
1045
- " a =?",
1046
- " x = Dummy.side_effect",
1047
- " y = x*a",
1048
- "B:",
1049
- " ns = [A(a=a) for a in [1, 1, 1]]",
1050
- " xs = [n.x for n in ns]",
1051
- " ys = [n.y for n in ns]",
1052
- " res = [xs, ys]",
1053
- )
1054
- r = engine.evaluate("B", "res")
1139
+ engine.parse defn('A:',
1140
+ ' a =?',
1141
+ ' x = Dummy.side_effect',
1142
+ ' y = x*a',
1143
+ 'B:',
1144
+ ' ns = [A(a=a) for a in [1, 1, 1]]',
1145
+ ' xs = [n.x for n in ns]',
1146
+ ' ys = [n.y for n in ns]',
1147
+ ' res = [xs, ys]',
1148
+ )
1149
+ r = engine.evaluate('B', 'res')
1055
1150
  expect(r[1]).to eq r[0]
1056
1151
  end
1057
1152
 
1058
- it "can use nodes as continuations -- simple" do
1059
- engine.parse defn("A:",
1060
- " x = Dummy.side_effect",
1061
- " y = x",
1062
- "B:",
1063
- " ns = A()",
1064
- " res = [ns.x, ns.y]",
1153
+ it 'can use nodes as continuations -- simple' do
1154
+ engine.parse defn('A:',
1155
+ ' x = Dummy.side_effect',
1156
+ ' y = x',
1157
+ 'B:',
1158
+ ' ns = A()',
1159
+ ' res = [ns.x, ns.y]',
1065
1160
  " res2 = ns % ['x', 'y']",
1066
1161
  )
1067
- r = engine.evaluate("B", "res")
1162
+ r = engine.evaluate('B', 'res')
1068
1163
  expect(r[1]).to eq r[0]
1069
1164
 
1070
1165
  # this one works as expected
1071
- r2 = engine.evaluate("B", "res2")
1166
+ r2 = engine.evaluate('B', 'res2')
1072
1167
  expect(r2.values.uniq.length).to eq 1
1073
1168
  end
1074
1169
 
1075
- it "Implements ability to use overridden superclass attrs" do
1170
+ it 'Implements ability to use overridden superclass attrs' do
1076
1171
  code = <<-DELOREAN
1077
1172
  A:
1078
1173
  x = 123
@@ -1086,7 +1181,7 @@ eof
1086
1181
 
1087
1182
  engine.parse code.gsub(/^ /, '')
1088
1183
 
1089
- r = engine.evaluate("B", ["x", "y", "xx", "yy"])
1184
+ r = engine.evaluate('B', ['x', 'y', 'xx', 'yy'])
1090
1185
  expect(r).to eq [5, 2460, 128, 1230]
1091
1186
  end
1092
1187
  end