parslet 1.7.0 → 1.7.1
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 +4 -4
- data/README +1 -1
- data/lib/parslet/context.rb +1 -1
- data/lib/parslet/parser.rb +1 -1
- data/parslet.gemspec +18 -0
- data/spec/acceptance/examples_spec.rb +37 -0
- data/spec/acceptance/infix_parser_spec.rb +112 -0
- data/spec/acceptance/regression_spec.rb +314 -0
- data/spec/acceptance/repetition_and_maybe_spec.rb +42 -0
- data/spec/acceptance/unconsumed_input_spec.rb +21 -0
- data/spec/parslet/atom_results_spec.rb +39 -0
- data/spec/parslet/atoms/alternative_spec.rb +26 -0
- data/spec/parslet/atoms/base_spec.rb +126 -0
- data/spec/parslet/atoms/capture_spec.rb +21 -0
- data/spec/parslet/atoms/combinations_spec.rb +5 -0
- data/spec/parslet/atoms/dsl_spec.rb +25 -0
- data/spec/parslet/atoms/entity_spec.rb +77 -0
- data/spec/parslet/atoms/infix_spec.rb +5 -0
- data/spec/parslet/atoms/lookahead_spec.rb +22 -0
- data/spec/parslet/atoms/named_spec.rb +4 -0
- data/spec/parslet/atoms/re_spec.rb +14 -0
- data/spec/parslet/atoms/repetition_spec.rb +24 -0
- data/spec/parslet/atoms/scope_spec.rb +26 -0
- data/spec/parslet/atoms/sequence_spec.rb +28 -0
- data/spec/parslet/atoms/str_spec.rb +15 -0
- data/spec/parslet/atoms/visitor_spec.rb +80 -0
- data/spec/parslet/atoms_spec.rb +429 -0
- data/spec/parslet/convenience_spec.rb +48 -0
- data/spec/parslet/error_reporter/contextual_spec.rb +115 -0
- data/spec/parslet/error_reporter/deepest_spec.rb +73 -0
- data/spec/parslet/error_reporter/tree_spec.rb +7 -0
- data/spec/parslet/export_spec.rb +67 -0
- data/spec/parslet/expression/treetop_spec.rb +74 -0
- data/spec/parslet/minilisp.citrus +29 -0
- data/spec/parslet/minilisp.tt +29 -0
- data/spec/parslet/parser_spec.rb +31 -0
- data/spec/parslet/parslet_spec.rb +38 -0
- data/spec/parslet/pattern_spec.rb +272 -0
- data/spec/parslet/position_spec.rb +14 -0
- data/spec/parslet/rig/rspec_spec.rb +54 -0
- data/spec/parslet/scope_spec.rb +45 -0
- data/spec/parslet/slice_spec.rb +144 -0
- data/spec/parslet/source/line_cache_spec.rb +74 -0
- data/spec/parslet/source_spec.rb +168 -0
- data/spec/parslet/transform/context_spec.rb +35 -0
- data/spec/parslet/transform_spec.rb +165 -0
- data/spec/spec_helper.rb +38 -0
- metadata +46 -4
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::Slice do
|
4
|
+
def cslice string, offset, cache=nil
|
5
|
+
described_class.new(
|
6
|
+
Parslet::Position.new(string, offset),
|
7
|
+
string, cache)
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "construction" do
|
11
|
+
it "should construct from an offset and a string" do
|
12
|
+
cslice('foobar', 40)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
context "('foobar', 40, 'foobar')" do
|
16
|
+
let(:slice) { cslice('foobar', 40) }
|
17
|
+
describe "comparison" do
|
18
|
+
it "should be equal to other slices with the same attributes" do
|
19
|
+
other = cslice('foobar', 40)
|
20
|
+
slice.should == other
|
21
|
+
other.should == slice
|
22
|
+
end
|
23
|
+
it "should be equal to other slices (offset is irrelevant for comparison)" do
|
24
|
+
other = cslice('foobar', 41)
|
25
|
+
slice.should == other
|
26
|
+
other.should == slice
|
27
|
+
end
|
28
|
+
it "should be equal to a string with the same content" do
|
29
|
+
slice.should == 'foobar'
|
30
|
+
end
|
31
|
+
it "should be equal to a string (inversed operands)" do
|
32
|
+
'foobar'.should == slice
|
33
|
+
end
|
34
|
+
it "should not be equal to a string" do
|
35
|
+
slice.should_not equal('foobar')
|
36
|
+
end
|
37
|
+
it "should not be eql to a string" do
|
38
|
+
slice.should_not eql('foobar')
|
39
|
+
end
|
40
|
+
it "should not hash to the same number" do
|
41
|
+
slice.hash.should_not == 'foobar'.hash
|
42
|
+
end
|
43
|
+
end
|
44
|
+
describe "offset" do
|
45
|
+
it "should return the associated offset" do
|
46
|
+
slice.offset.should == 6
|
47
|
+
end
|
48
|
+
it "should fail to return a line and column" do
|
49
|
+
lambda {
|
50
|
+
slice.line_and_column
|
51
|
+
}.should raise_error(ArgumentError)
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when constructed with a source" do
|
55
|
+
let(:slice) { cslice(
|
56
|
+
'foobar', 40,
|
57
|
+
flexmock(:cache, :line_and_column => [13, 14])) }
|
58
|
+
it "should return proper line and column" do
|
59
|
+
slice.line_and_column.should == [13, 14]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
describe "string methods" do
|
64
|
+
describe "matching" do
|
65
|
+
it "should match as a string would" do
|
66
|
+
slice.should match(/bar/)
|
67
|
+
slice.should match(/foo/)
|
68
|
+
|
69
|
+
md = slice.match(/f(o)o/)
|
70
|
+
md.captures.first.should == 'o'
|
71
|
+
end
|
72
|
+
end
|
73
|
+
describe "<- #size" do
|
74
|
+
subject { slice.size }
|
75
|
+
it { should == 6 }
|
76
|
+
end
|
77
|
+
describe "<- #length" do
|
78
|
+
subject { slice.length }
|
79
|
+
it { should == 6 }
|
80
|
+
end
|
81
|
+
describe "<- #+" do
|
82
|
+
let(:other) { cslice('baz', 10) }
|
83
|
+
subject { slice + other }
|
84
|
+
|
85
|
+
it "should concat like string does" do
|
86
|
+
subject.size.should == 9
|
87
|
+
subject.should == 'foobarbaz'
|
88
|
+
subject.offset.should == 6
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
describe "conversion" do
|
93
|
+
describe "<- #to_slice" do
|
94
|
+
it "should return self" do
|
95
|
+
slice.to_slice.should eq(slice)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
describe "<- #to_sym" do
|
99
|
+
it "should return :foobar" do
|
100
|
+
slice.to_sym.should == :foobar
|
101
|
+
end
|
102
|
+
end
|
103
|
+
describe "cast to Float" do
|
104
|
+
it "should return a float" do
|
105
|
+
Float(cslice('1.345', 11)).should == 1.345
|
106
|
+
end
|
107
|
+
end
|
108
|
+
describe "cast to Integer" do
|
109
|
+
it "should cast to integer as a string would" do
|
110
|
+
s = cslice('1234', 40)
|
111
|
+
Integer(s).should == 1234
|
112
|
+
s.to_i.should == 1234
|
113
|
+
end
|
114
|
+
it "should fail when Integer would fail on a string" do
|
115
|
+
lambda { Integer(slice) }.should raise_error(ArgumentError, /invalid value/)
|
116
|
+
end
|
117
|
+
it "should turn into zero when a string would" do
|
118
|
+
slice.to_i.should == 0
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
describe "inspection and string conversion" do
|
123
|
+
describe "#inspect" do
|
124
|
+
subject { slice.inspect }
|
125
|
+
it { should == '"foobar"@6' }
|
126
|
+
end
|
127
|
+
describe "#to_s" do
|
128
|
+
subject { slice.to_s }
|
129
|
+
it { should == 'foobar' }
|
130
|
+
end
|
131
|
+
end
|
132
|
+
describe "serializability" do
|
133
|
+
it "should serialize" do
|
134
|
+
Marshal.dump(slice)
|
135
|
+
end
|
136
|
+
context "when storing a line cache" do
|
137
|
+
let(:slice) { cslice('foobar', 40, Parslet::Source::LineCache.new()) }
|
138
|
+
it "should serialize" do
|
139
|
+
Marshal.dump(slice)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::Source::RangeSearch do
|
4
|
+
describe "<- #lbound" do
|
5
|
+
context "for a simple array" do
|
6
|
+
let(:ary) { [10, 20, 30, 40, 50] }
|
7
|
+
before(:each) { ary.extend Parslet::Source::RangeSearch }
|
8
|
+
|
9
|
+
it "should return correct answers for numbers not in the array" do
|
10
|
+
ary.lbound(5).should == 0
|
11
|
+
ary.lbound(15).should == 1
|
12
|
+
ary.lbound(25).should == 2
|
13
|
+
ary.lbound(35).should == 3
|
14
|
+
ary.lbound(45).should == 4
|
15
|
+
end
|
16
|
+
it "should return correct answers for numbers in the array" do
|
17
|
+
ary.lbound(10).should == 1
|
18
|
+
ary.lbound(20).should == 2
|
19
|
+
ary.lbound(30).should == 3
|
20
|
+
ary.lbound(40).should == 4
|
21
|
+
end
|
22
|
+
it "should cover right edge case" do
|
23
|
+
ary.lbound(50).should be_nil
|
24
|
+
ary.lbound(51).should be_nil
|
25
|
+
end
|
26
|
+
it "should cover left edge case" do
|
27
|
+
ary.lbound(0).should == 0
|
28
|
+
end
|
29
|
+
end
|
30
|
+
context "for an empty array" do
|
31
|
+
let(:ary) { [] }
|
32
|
+
before(:each) { ary.extend Parslet::Source::RangeSearch }
|
33
|
+
|
34
|
+
it "should return nil" do
|
35
|
+
ary.lbound(1).should be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe Parslet::Source::LineCache do
|
42
|
+
describe "<- scan_for_line_endings" do
|
43
|
+
context "calculating the line_and_columns" do
|
44
|
+
let(:str) { "foo\nbar\nbazd" }
|
45
|
+
|
46
|
+
it "should return the first line if we have no line ends" do
|
47
|
+
subject.scan_for_line_endings(0, nil)
|
48
|
+
subject.line_and_column(3).should == [1, 4]
|
49
|
+
|
50
|
+
subject.scan_for_line_endings(0, "")
|
51
|
+
subject.line_and_column(5).should == [1, 6]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should find the right line starting from pos 0" do
|
55
|
+
subject.scan_for_line_endings(0, str)
|
56
|
+
subject.line_and_column(5).should == [2, 2]
|
57
|
+
subject.line_and_column(9).should == [3, 2]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should find the right line starting from pos 5" do
|
61
|
+
subject.scan_for_line_endings(5, str)
|
62
|
+
subject.line_and_column(11).should == [2, 3]
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should find the right line if scannning the string multiple times" do
|
66
|
+
subject.scan_for_line_endings(0, str)
|
67
|
+
subject.scan_for_line_endings(0, "#{str}\nthe quick\nbrown fox")
|
68
|
+
subject.line_and_column(10).should == [3,3]
|
69
|
+
subject.line_and_column(24).should == [5,2]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# Encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Parslet::Source do
|
6
|
+
describe "using simple input" do
|
7
|
+
let(:str) { "a"*100 + "\n" + "a"*100 + "\n" }
|
8
|
+
let(:source) { described_class.new(str) }
|
9
|
+
|
10
|
+
describe "<- #read(n)" do
|
11
|
+
it "should not raise error when the return value is nil" do
|
12
|
+
described_class.new('').consume(1)
|
13
|
+
end
|
14
|
+
it "should return 100 'a's when reading 100 chars" do
|
15
|
+
source.consume(100).should == 'a'*100
|
16
|
+
end
|
17
|
+
end
|
18
|
+
describe "<- #chars_left" do
|
19
|
+
subject { source.chars_left }
|
20
|
+
|
21
|
+
it { should == 202 }
|
22
|
+
context "after depleting the source" do
|
23
|
+
before(:each) { source.consume(10000) }
|
24
|
+
|
25
|
+
it { should == 0 }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
describe "<- #pos" do
|
29
|
+
subject { source.pos.charpos }
|
30
|
+
|
31
|
+
it { should == 0 }
|
32
|
+
context "after reading a few bytes" do
|
33
|
+
it "should still be correct" do
|
34
|
+
pos = 0
|
35
|
+
10.times do
|
36
|
+
pos += (n = rand(10)+1)
|
37
|
+
source.consume(n)
|
38
|
+
|
39
|
+
source.pos.charpos.should == pos
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
describe "<- #pos=(n)" do
|
45
|
+
subject { source.pos.charpos }
|
46
|
+
10.times do
|
47
|
+
pos = rand(200)
|
48
|
+
context "setting position #{pos}" do
|
49
|
+
before(:each) { source.bytepos = pos }
|
50
|
+
|
51
|
+
it { should == pos }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
describe '#chars_until' do
|
56
|
+
it 'should return 100 chars before line end' do
|
57
|
+
source.chars_until("\n").should == 100
|
58
|
+
end
|
59
|
+
end
|
60
|
+
describe "<- #column & #line" do
|
61
|
+
subject { source.line_and_column }
|
62
|
+
|
63
|
+
it { should == [1,1] }
|
64
|
+
|
65
|
+
context "on the first line" do
|
66
|
+
it "should increase column with every read" do
|
67
|
+
10.times do |i|
|
68
|
+
source.line_and_column.last.should == 1+i
|
69
|
+
source.consume(1)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
context "on the second line" do
|
74
|
+
before(:each) { source.consume(101) }
|
75
|
+
it { should == [2, 1]}
|
76
|
+
end
|
77
|
+
context "after reading everything" do
|
78
|
+
before(:each) { source.consume(10000) }
|
79
|
+
|
80
|
+
context "when seeking to 9" do
|
81
|
+
before(:each) { source.bytepos = 9 }
|
82
|
+
it { should == [1, 10] }
|
83
|
+
end
|
84
|
+
context "when seeking to 100" do
|
85
|
+
before(:each) { source.bytepos = 100 }
|
86
|
+
it { should == [1, 101] }
|
87
|
+
end
|
88
|
+
context "when seeking to 101" do
|
89
|
+
before(:each) { source.bytepos = 101 }
|
90
|
+
it { should == [2, 1] }
|
91
|
+
end
|
92
|
+
context "when seeking to 102" do
|
93
|
+
before(:each) { source.bytepos = 102 }
|
94
|
+
it { should == [2, 2] }
|
95
|
+
end
|
96
|
+
context "when seeking beyond eof" do
|
97
|
+
it "should not throw an error" do
|
98
|
+
source.bytepos = 1000
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
context "reading char by char, storing the results" do
|
103
|
+
attr_reader :results
|
104
|
+
before(:each) {
|
105
|
+
@results = {}
|
106
|
+
while source.chars_left>0
|
107
|
+
pos = source.pos.charpos
|
108
|
+
@results[pos] = source.line_and_column
|
109
|
+
source.consume(1)
|
110
|
+
end
|
111
|
+
|
112
|
+
@results.entries.size.should == 202
|
113
|
+
@results
|
114
|
+
}
|
115
|
+
|
116
|
+
context "when using pos argument" do
|
117
|
+
it "should return the same results" do
|
118
|
+
results.each do |pos, result|
|
119
|
+
source.line_and_column(pos).should == result
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
it "should give the same results when seeking" do
|
124
|
+
results.each do |pos, result|
|
125
|
+
source.bytepos = pos
|
126
|
+
source.line_and_column.should == result
|
127
|
+
end
|
128
|
+
end
|
129
|
+
it "should give the same results when reading" do
|
130
|
+
cur = source.bytepos = 0
|
131
|
+
while source.chars_left>0
|
132
|
+
source.line_and_column.should == results[cur]
|
133
|
+
cur += 1
|
134
|
+
source.consume(1)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "reading encoded input" do
|
143
|
+
let(:source) { described_class.new("éö変わる") }
|
144
|
+
|
145
|
+
def r str
|
146
|
+
Regexp.new(Regexp.escape(str))
|
147
|
+
end
|
148
|
+
|
149
|
+
it "should read characters, not bytes" do
|
150
|
+
source.should match(r("é"))
|
151
|
+
source.consume(1)
|
152
|
+
source.pos.charpos.should == 1
|
153
|
+
source.bytepos.should == 2
|
154
|
+
|
155
|
+
source.should match(r("ö"))
|
156
|
+
source.consume(1)
|
157
|
+
source.pos.charpos.should == 2
|
158
|
+
source.bytepos.should == 4
|
159
|
+
|
160
|
+
source.should match(r("変"))
|
161
|
+
source.consume(1)
|
162
|
+
|
163
|
+
source.consume(2)
|
164
|
+
source.chars_left.should == 0
|
165
|
+
source.chars_left.should == 0
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Parslet::Context do
|
4
|
+
def context(*args)
|
5
|
+
described_class.new(*args)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "binds hash keys as variable like things" do
|
9
|
+
context(:a => 'value').instance_eval { a }.
|
10
|
+
should == 'value'
|
11
|
+
end
|
12
|
+
describe 'when a method in BlankSlate is inherited from the environment somehow' do
|
13
|
+
before(:each) { BlankSlate.send(:define_method, :a) { 'c' } }
|
14
|
+
after(:each) { BlankSlate.send(:undef_method, :a) }
|
15
|
+
|
16
|
+
it "masks what is already on blank slate" do
|
17
|
+
context(:a => 'b').instance_eval { a }.
|
18
|
+
should == 'b'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
it "should not reveal define_singleton_method for all users of blankslate, just for us" do
|
22
|
+
expect {
|
23
|
+
BlankSlate.new.instance_eval {
|
24
|
+
define_singleton_method(:foo) { 'foo' }
|
25
|
+
}
|
26
|
+
}.to raise_error(NoMethodError)
|
27
|
+
end
|
28
|
+
it "one contexts variables aren't the next ones" do
|
29
|
+
ca = context(:a => 'b')
|
30
|
+
cb = context(:b => 'c')
|
31
|
+
|
32
|
+
ca.methods.should_not include(:b)
|
33
|
+
cb.methods.should_not include(:a)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'parslet'
|
4
|
+
|
5
|
+
describe Parslet::Transform do
|
6
|
+
include Parslet
|
7
|
+
|
8
|
+
let(:transform) { Parslet::Transform.new }
|
9
|
+
attr_reader :transform
|
10
|
+
before(:each) do
|
11
|
+
@transform = Parslet::Transform.new
|
12
|
+
end
|
13
|
+
|
14
|
+
class A < Struct.new(:elt); end
|
15
|
+
class B < Struct.new(:elt); end
|
16
|
+
class C < Struct.new(:elt); end
|
17
|
+
class Bi < Struct.new(:a, :b); end
|
18
|
+
|
19
|
+
describe "delayed construction" do
|
20
|
+
context "given simple(:x) => A.new(x)" do
|
21
|
+
before(:each) do
|
22
|
+
transform.rule(simple(:x)) { |d| A.new(d[:x]) }
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should transform 'a' into A.new('a')" do
|
26
|
+
transform.apply('a').should == A.new('a')
|
27
|
+
end
|
28
|
+
it "should transform ['a', 'b'] into [A.new('a'), A.new('b')]" do
|
29
|
+
transform.apply(['a', 'b']).should ==
|
30
|
+
[A.new('a'), A.new('b')]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
context "given rules on {:a => simple(:x)} and {:b => :_x}" do
|
34
|
+
before(:each) do
|
35
|
+
transform.rule(:a => simple(:x)) { |d| A.new(d[:x]) }
|
36
|
+
transform.rule(:b => simple(:x)) { |d| B.new(d[:x]) }
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should transform {:d=>{:b=>'c'}} into d => B('c')" do
|
40
|
+
transform.apply({:d=>{:b=>'c'}}).should == {:d => B.new('c')}
|
41
|
+
end
|
42
|
+
it "should transform {:a=>{:b=>'c'}} into A(B('c'))" do
|
43
|
+
transform.apply({:a=>{:b=>'c'}}).should == A.new(B.new('c'))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
describe "pulling out subbranches" do
|
47
|
+
before(:each) do
|
48
|
+
transform.rule(:a => {:b => simple(:x)}, :d => {:e => simple(:y)}) { |d|
|
49
|
+
Bi.new(*d.values_at(:x, :y))
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should yield Bi.new('c', 'f')" do
|
54
|
+
transform.apply(:a => {:b => 'c'}, :d => {:e => 'f'}).should ==
|
55
|
+
Bi.new('c', 'f')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
describe "dsl construction" do
|
60
|
+
let(:transform) { Parslet::Transform.new do
|
61
|
+
rule(simple(:x)) { A.new(x) }
|
62
|
+
end
|
63
|
+
}
|
64
|
+
|
65
|
+
it "should still evaluate rules correctly" do
|
66
|
+
transform.apply('a').should == A.new('a')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
describe "class construction" do
|
70
|
+
class OptimusPrime < Parslet::Transform
|
71
|
+
rule(:a => simple(:x)) { A.new(x) }
|
72
|
+
rule(:b => simple(:x)) { B.new(x) }
|
73
|
+
end
|
74
|
+
let(:transform) { OptimusPrime.new }
|
75
|
+
|
76
|
+
it "should evaluate rules" do
|
77
|
+
transform.apply(:a => 'a').should == A.new('a')
|
78
|
+
end
|
79
|
+
|
80
|
+
context "with inheritance" do
|
81
|
+
class OptimusPrimeJunior < OptimusPrime
|
82
|
+
rule(:b => simple(:x)) { B.new(x.upcase) }
|
83
|
+
rule(:c => simple(:x)) { C.new(x) }
|
84
|
+
end
|
85
|
+
let(:transform) { OptimusPrimeJunior.new }
|
86
|
+
|
87
|
+
it "should inherit rules from its parent" do
|
88
|
+
transform.apply(:a => 'a').should == A.new('a')
|
89
|
+
end
|
90
|
+
|
91
|
+
it "should be able to override rules from its parent" do
|
92
|
+
transform.apply(:b => 'b').should == B.new('B')
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should be able to define new rules" do
|
96
|
+
transform.apply(:c => 'c').should == C.new('c')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
describe "<- #call_on_match" do
|
101
|
+
let(:bindings) { { :foo => 'test' } }
|
102
|
+
context "when given a block of arity 1" do
|
103
|
+
it "should call the block" do
|
104
|
+
called = false
|
105
|
+
transform.call_on_match(bindings, lambda do |dict|
|
106
|
+
called = true
|
107
|
+
end)
|
108
|
+
|
109
|
+
called.should == true
|
110
|
+
end
|
111
|
+
it "should yield the bindings" do
|
112
|
+
transform.call_on_match(bindings, lambda do |dict|
|
113
|
+
dict.should == bindings
|
114
|
+
end)
|
115
|
+
end
|
116
|
+
it "should execute in the current context" do
|
117
|
+
foo = 'test'
|
118
|
+
transform.call_on_match(bindings, lambda do |dict|
|
119
|
+
foo.should == 'test'
|
120
|
+
end)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
context "when given a block of arity 0" do
|
124
|
+
it "should call the block" do
|
125
|
+
called = false
|
126
|
+
transform.call_on_match(bindings, proc do
|
127
|
+
called = true
|
128
|
+
end)
|
129
|
+
|
130
|
+
called.should == true
|
131
|
+
end
|
132
|
+
it "should have bindings as local variables" do
|
133
|
+
transform.call_on_match(bindings, proc do
|
134
|
+
foo.should == 'test'
|
135
|
+
end)
|
136
|
+
end
|
137
|
+
it "should execute in its own context" do
|
138
|
+
@bar = 'test'
|
139
|
+
transform.call_on_match(bindings, proc do
|
140
|
+
@bar.should_not == 'test'
|
141
|
+
end)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
context "various transformations (regression)" do
|
147
|
+
context "hashes" do
|
148
|
+
it "are matched completely" do
|
149
|
+
transform.rule(:a => simple(:x)) { fail }
|
150
|
+
transform.apply(:a => 'a', :b => 'b')
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
context "when not using the bindings as hash, but as local variables" do
|
156
|
+
it "should access the variables" do
|
157
|
+
transform.rule(simple(:x)) { A.new(x) }
|
158
|
+
transform.apply('a').should == A.new('a')
|
159
|
+
end
|
160
|
+
it "should allow context as local variable" do
|
161
|
+
transform.rule(simple(:x)) { foo }
|
162
|
+
transform.apply('a', :foo => 'bar').should == 'bar'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|