llip 0.1.0
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.
- data/History.txt +4 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +45 -0
- data/README.txt +148 -0
- data/Rakefile +66 -0
- data/examples/ariteval/ariteval.rb +132 -0
- data/examples/ariteval/evaluator.rb +61 -0
- data/examples/ariteval/exp.rb +104 -0
- data/lib/llip.rb +6 -0
- data/lib/llip/abstract_parser.rb +170 -0
- data/lib/llip/abstract_scanner.rb +83 -0
- data/lib/llip/buffer.rb +35 -0
- data/lib/llip/llip_error.rb +43 -0
- data/lib/llip/parser.rb +93 -0
- data/lib/llip/production_compiler.rb +168 -0
- data/lib/llip/production_specification.rb +79 -0
- data/lib/llip/recursive_production_compiler.rb +35 -0
- data/lib/llip/regexp_abstract_scanner.rb +116 -0
- data/lib/llip/regexp_parser.rb +197 -0
- data/lib/llip/regexp_scanner.rb +33 -0
- data/lib/llip/regexp_specification.rb +210 -0
- data/lib/llip/token.rb +47 -0
- data/lib/llip/visitable.rb +37 -0
- data/spec/ariteval/ariteval_spec.rb +111 -0
- data/spec/ariteval/evaluator_spec.rb +106 -0
- data/spec/ariteval/exp_spec.rb +232 -0
- data/spec/llip/abstract_parser_spec.rb +273 -0
- data/spec/llip/abstract_scanner_spec.rb +152 -0
- data/spec/llip/buffer_spec.rb +60 -0
- data/spec/llip/llip_error_spec.rb +77 -0
- data/spec/llip/parser_spec.rb +163 -0
- data/spec/llip/production_compiler_spec.rb +271 -0
- data/spec/llip/production_specification_spec.rb +75 -0
- data/spec/llip/recursive_production_compiler_spec.rb +86 -0
- data/spec/llip/regexp_abstract_scanner_spec.rb +320 -0
- data/spec/llip/regexp_parser_spec.rb +265 -0
- data/spec/llip/regexp_scanner_spec.rb +40 -0
- data/spec/llip/regexp_specification_spec.rb +734 -0
- data/spec/llip/token_spec.rb +70 -0
- data/spec/llip/visitable_spec.rb +38 -0
- data/spec/spec_helper.rb +10 -0
- metadata +110 -0
@@ -0,0 +1,232 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'exp'
|
3
|
+
|
4
|
+
describe "A NumExp" do
|
5
|
+
before(:each) do
|
6
|
+
@num = NumExp.new(5)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should return its value" do
|
10
|
+
@num.should respond_to(:value)
|
11
|
+
|
12
|
+
@num.value.should be_equal(5)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should return its value as a String" do
|
16
|
+
@num.to_s.should == "5"
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should call visitor.visit_num_exp in 'accept'" do
|
20
|
+
visitor = mock("Visitor")
|
21
|
+
visitor.should_receive(:visit_num_exp).with(@num)
|
22
|
+
@num.accept(visitor)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "A IdentExp" do
|
27
|
+
|
28
|
+
before(:each) do
|
29
|
+
@ident = IdentExp.new("a")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should return its value" do
|
33
|
+
@ident.should respond_to(:value)
|
34
|
+
|
35
|
+
@ident.value.should == "a"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should be coerced to a String" do
|
39
|
+
@ident.to_s.should == @ident.value
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should call visitor.visit_ident_exp in 'accept'" do
|
43
|
+
visitor = mock("Visitor")
|
44
|
+
visitor.should_receive(:visit_ident_exp).with(@ident)
|
45
|
+
@ident.accept(visitor)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "An AssignIdentExp" do
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
@assign = AssignIdentExp.new("a",5)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should return its name" do
|
57
|
+
@assign.should respond_to(:name)
|
58
|
+
@assign.name.should == "a"
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should be coerced to a String" do
|
62
|
+
@assign.to_s.should == "( a = 5 )"
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should call visitor.visit_assign_ident_exp in 'accept'" do
|
66
|
+
visitor = mock("Visitor")
|
67
|
+
visitor.should_receive(:visit_assign_ident_exp).with(@assign)
|
68
|
+
@assign.accept(visitor)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "OpExp", :shared => true do
|
73
|
+
|
74
|
+
it "should have a left and a right child" do
|
75
|
+
@exp.should respond_to(:left)
|
76
|
+
@exp.should respond_to(:right)
|
77
|
+
|
78
|
+
@exp.left.should be_equal(:left)
|
79
|
+
@exp.right.should be_equal(:right)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should respond to 'op'" do
|
83
|
+
@exp.should respond_to(:op)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should be represented by a String" do
|
87
|
+
@exp.should_receive(:op).and_return("op")
|
88
|
+
|
89
|
+
@exp.to_s.should == "( left op right )"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "An OpExp" do
|
94
|
+
it_should_behave_like "OpExp"
|
95
|
+
|
96
|
+
before(:each) do
|
97
|
+
@exp = OpExp.new(:left,:right)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should not be equal to another one with different values" do
|
101
|
+
@exp.should_not == "ciao"
|
102
|
+
|
103
|
+
new_exp = OpExp.new(:different_left,:different_right)
|
104
|
+
@exp.should_not == new_exp
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should be equal to another one with the same values" do
|
108
|
+
new_exp = OpExp.new(:left,:right)
|
109
|
+
@exp.should == new_exp
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "A PlusExp" do
|
114
|
+
|
115
|
+
it_should_behave_like "OpExp"
|
116
|
+
|
117
|
+
before(:each) do
|
118
|
+
@exp = PlusExp.new(:left,:right)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should have op = '+'" do
|
122
|
+
@exp.op.should == "+"
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should call visitor.visit_plus_exp in 'accept'" do
|
126
|
+
visitor = mock("Visitor")
|
127
|
+
visitor.should_receive(:visit_plus_exp).with(@exp)
|
128
|
+
@exp.accept(visitor)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe "A MinusExp" do
|
133
|
+
|
134
|
+
it_should_behave_like "OpExp"
|
135
|
+
|
136
|
+
before(:each) do
|
137
|
+
@exp = MinusExp.new(:left,:right)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should have op = '-'" do
|
141
|
+
@exp.op.should == "-"
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should call visitor.visit_minus_exp in 'accept'" do
|
145
|
+
visitor = mock("Visitor")
|
146
|
+
visitor.should_receive(:visit_minus_exp).with(@exp)
|
147
|
+
@exp.accept(visitor)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "A MulExp" do
|
152
|
+
|
153
|
+
it_should_behave_like "OpExp"
|
154
|
+
|
155
|
+
before(:each) do
|
156
|
+
@exp = MulExp.new(:left,:right)
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should have op = '*'" do
|
160
|
+
@exp.op.should == "*"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should call visitor.visit_mul_exp in 'accept'" do
|
164
|
+
visitor = mock("Visitor")
|
165
|
+
visitor.should_receive(:visit_mul_exp).with(@exp)
|
166
|
+
@exp.accept(visitor)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe "A DivExp" do
|
171
|
+
|
172
|
+
it_should_behave_like "OpExp"
|
173
|
+
|
174
|
+
before(:each) do
|
175
|
+
@exp = DivExp.new(:left,:right)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should have op = '/'" do
|
179
|
+
@exp.op.should == "/"
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should call visitor.visit_div_exp in 'accept'" do
|
183
|
+
visitor = mock("Visitor")
|
184
|
+
visitor.should_receive(:visit_div_exp).with(@exp)
|
185
|
+
@exp.accept(visitor)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe "Using all the exp you should be able to represent" do
|
190
|
+
it "'3-5'" do
|
191
|
+
exp = MinusExp.new(NumExp.new(3),NumExp.new(5))
|
192
|
+
|
193
|
+
exp.to_s.should == "( 3 - 5 )"
|
194
|
+
end
|
195
|
+
|
196
|
+
it "'2+5*4-1'" do
|
197
|
+
exp = MinusExp.new(
|
198
|
+
PlusExp.new(
|
199
|
+
NumExp.new(2),
|
200
|
+
MulExp.new(NumExp.new(5),NumExp.new(4))
|
201
|
+
),
|
202
|
+
NumExp.new(1)
|
203
|
+
)
|
204
|
+
|
205
|
+
exp.to_s.should == "( ( 2 + ( 5 * 4 ) ) - 1 )"
|
206
|
+
end
|
207
|
+
|
208
|
+
it "'3-5-1*5'" do
|
209
|
+
exp = MinusExp.new(
|
210
|
+
NumExp.new(3),
|
211
|
+
MinusExp.new(
|
212
|
+
NumExp.new(5),
|
213
|
+
MulExp.new(NumExp.new(1),NumExp.new(5))
|
214
|
+
)
|
215
|
+
)
|
216
|
+
|
217
|
+
exp.to_s.should == "( 3 - ( 5 - ( 1 * 5 ) ) )"
|
218
|
+
end
|
219
|
+
|
220
|
+
it "'a = 5'" do
|
221
|
+
exp = AssignIdentExp.new('a',NumExp.new(5))
|
222
|
+
exp.to_s.should == "( a = 5 )"
|
223
|
+
end
|
224
|
+
|
225
|
+
it "'( a = 3 * 2 ) - ( 24 + a )'" do
|
226
|
+
exp = MinusExp.new(
|
227
|
+
AssignIdentExp.new("a", MulExp.new(NumExp.new(3),NumExp.new(2))),
|
228
|
+
PlusExp.new(NumExp.new(24),IdentExp.new("a"))
|
229
|
+
)
|
230
|
+
exp.to_s.should == "( ( a = ( 3 * 2 ) ) - ( 24 + a ) )"
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,273 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'abstract_parser'
|
3
|
+
|
4
|
+
describe "A class descending from AbstractParser" do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@class = Class.new(AbstractParser)
|
8
|
+
if @class.respond_to? :autocompile
|
9
|
+
@class.autocompile(false)
|
10
|
+
end
|
11
|
+
@parser = @class.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should raise an exception if it hasn't been compiled" do
|
15
|
+
lambda { @parser.parse(:mock) }.should raise_error(RuntimeError)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have hash-like capabilities" do
|
19
|
+
@parser.should respond_to(:[])
|
20
|
+
@parser.should respond_to(:[]=)
|
21
|
+
|
22
|
+
@parser[:var]= :foo
|
23
|
+
@parser[:var].should == :foo
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to specify a production with a name (and in case the mode), some tokens and blocks" do
|
27
|
+
@class.should respond_to(:production)
|
28
|
+
|
29
|
+
lambda do
|
30
|
+
@class.production(:name) do |prod|
|
31
|
+
prod.token(:first_token) { "first" }
|
32
|
+
prod.token(:second_token) { "second" }
|
33
|
+
end
|
34
|
+
end.should_not raise_error
|
35
|
+
|
36
|
+
|
37
|
+
l = lambda do
|
38
|
+
@class.production(:name,:mode) do |prod|
|
39
|
+
prod.mode.should == :mode
|
40
|
+
prod.token(:first_token) { "first" }
|
41
|
+
prod.token(:second_token) { "second" }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
l.should_not raise_error
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should memorize its productions in a hash of ProductionSpecification" do
|
48
|
+
@class.should respond_to(:productions)
|
49
|
+
@class.productions.should == {}
|
50
|
+
|
51
|
+
@class.production(:name) do |prod|
|
52
|
+
prod.token(:first_token) { "first" }
|
53
|
+
prod.token(:second_token) { "second" }
|
54
|
+
end
|
55
|
+
|
56
|
+
@class.productions[:name].should be_kind_of(ProductionSpecification)
|
57
|
+
@class.productions[:name].tokens[:first_token].should_not be_nil
|
58
|
+
@class.productions[:name].tokens[:second_token].should_not be_nil
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should memorize the scope" do
|
62
|
+
@class.should respond_to(:scope)
|
63
|
+
@class.scope.should be_nil
|
64
|
+
|
65
|
+
@class.scope(:a_new_scope)
|
66
|
+
@class.scope.should == :a_new_scope
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should have an 'autocompile' attribute" do
|
70
|
+
@class.should respond_to(:autocompile)
|
71
|
+
@class.autocompile.should == false
|
72
|
+
lambda { @class.autocompile(true) }.should_not raise_error
|
73
|
+
@class.autocompile.should == true
|
74
|
+
lambda { @class.autocompile(false) }.should_not raise_error
|
75
|
+
@class.autocompile.should == false
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should have a code attribute" do
|
79
|
+
@class.should respond_to(:code)
|
80
|
+
@class.code.should be_nil
|
81
|
+
@class.scope(:a_scope)
|
82
|
+
@class.production(:a_scope) {}
|
83
|
+
@class.compile
|
84
|
+
@class.code.should_not == ""
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should initialize and fill 'code' when 'autocompile' is true" do
|
88
|
+
@class.autocompile false
|
89
|
+
@class.code.should be_nil
|
90
|
+
@class.autocompile true
|
91
|
+
@class.code.should == ""
|
92
|
+
@class.scope(:a_scope)
|
93
|
+
@class.code.should_not == ""
|
94
|
+
@class.production(:a_scope) {}
|
95
|
+
@class.code.should_not == ""
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should raise if a production has an unknown mode" do
|
99
|
+
l = lambda {
|
100
|
+
@class.scope :first
|
101
|
+
@class.production(:first) { |p| p.mode = :error_mode }
|
102
|
+
@class.compile
|
103
|
+
}
|
104
|
+
l.should raise_error(RuntimeError)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "A class descending from AbstractParser with some productions specified" do
|
109
|
+
|
110
|
+
before(:each) do
|
111
|
+
@class = Class.new(AbstractParser)
|
112
|
+
if @class.respond_to? :autocompile
|
113
|
+
@class.autocompile(false)
|
114
|
+
end
|
115
|
+
@parser = @class.new
|
116
|
+
|
117
|
+
@mock_single = mock "Single"
|
118
|
+
@mock_single.should_receive(:respond_to?).with(:to_ary).and_return(false)
|
119
|
+
@mock_single.should_receive(:respond_to?).with(:call).and_return(true)
|
120
|
+
@class.production(:single) do |prod|
|
121
|
+
prod.token(:single_token,@mock_single)
|
122
|
+
end
|
123
|
+
|
124
|
+
@mock_recursive = mock "Recursive"
|
125
|
+
@mock_recursive.should_receive(:respond_to?).with(:to_ary).and_return(false)
|
126
|
+
@mock_recursive.should_receive(:respond_to?).with(:call).and_return(true)
|
127
|
+
@class.production(:recursive) do |prod|
|
128
|
+
prod.mode=:recursive
|
129
|
+
prod.token(:recursive_token,@mock_recursive)
|
130
|
+
end
|
131
|
+
|
132
|
+
@class.should respond_to(:compile)
|
133
|
+
@class.should respond_to(:compiled)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should be compiled with a scope specified" do
|
137
|
+
@class.scope(:single)
|
138
|
+
|
139
|
+
@class.compiled.should == false
|
140
|
+
lambda { @class.compile }.should_not raise_error
|
141
|
+
@class.compiled.should == true
|
142
|
+
end
|
143
|
+
|
144
|
+
it "shouldn't be compiled without a correct scope" do
|
145
|
+
|
146
|
+
@class.compiled.should == false
|
147
|
+
lambda { @class.compile }.should raise_error(RuntimeError)
|
148
|
+
@class.compiled.should == false
|
149
|
+
|
150
|
+
@class.scope(:fake_scope)
|
151
|
+
@class.compiled.should == false
|
152
|
+
lambda { @class.compile }.should raise_error(RuntimeError)
|
153
|
+
@class.compiled.should == false
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
it "shouldn't be compiled if a production has an invalid mode" do
|
158
|
+
@class.production(:error) { |prod| prod.mode = :this_is_an_invalid_mode }
|
159
|
+
lambda { @class.compile }.should raise_error(RuntimeError)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should mantain an attribute 'code' which mantains the built code" do
|
163
|
+
@class.scope(:single)
|
164
|
+
|
165
|
+
@class.should respond_to(:code)
|
166
|
+
@class.code.should be_nil
|
167
|
+
lambda { @class.compile }.should_not raise_error
|
168
|
+
@class.code.should_not be_nil
|
169
|
+
@class.code.should be_kind_of(String)
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
describe "An instance of a class descending from AbstractParser with some productions specified after being compiled" do
|
175
|
+
|
176
|
+
before(:each) do
|
177
|
+
@class = Class.new(AbstractParser)
|
178
|
+
if @class.respond_to? :autocompile
|
179
|
+
@class.autocompile(false)
|
180
|
+
end
|
181
|
+
@parser = @class.new
|
182
|
+
|
183
|
+
@class.scope :single
|
184
|
+
|
185
|
+
@mock_single = mock "Single"
|
186
|
+
@mock_single.should_receive(:respond_to?).with(:to_ary).and_return(false)
|
187
|
+
@mock_single.should_receive(:respond_to?).with(:call).and_return(true)
|
188
|
+
@class.production(:single) do |prod|
|
189
|
+
prod.token(:single_token,@mock_single)
|
190
|
+
end
|
191
|
+
|
192
|
+
@mock_recursive = mock "Recursive"
|
193
|
+
@mock_recursive.should_receive(:respond_to?).with(:to_ary).and_return(false)
|
194
|
+
@mock_recursive.should_receive(:respond_to?).with(:call).and_return(true)
|
195
|
+
@class.production(:recursive) do |prod|
|
196
|
+
prod.mode = :recursive
|
197
|
+
prod.token(:recursive_token,@mock_recursive)
|
198
|
+
end
|
199
|
+
|
200
|
+
lambda { @class.compile }.should_not raise_error
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should expose parse_NAME methods" do
|
204
|
+
@parser.should respond_to("parse_single")
|
205
|
+
@parser.should respond_to("parse_recursive")
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should have a productions method which alias the class one" do
|
209
|
+
@parser.should respond_to(:productions)
|
210
|
+
@parser.productions.should == @class.productions
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should have a parse method which accept a scanner" do
|
214
|
+
@parser.should respond_to(:parse)
|
215
|
+
|
216
|
+
scanner = mock "Scanner"
|
217
|
+
scanner.should_receive(:next).any_number_of_times
|
218
|
+
scanner.should_receive(:current).twice.and_return(:single_token,nil)
|
219
|
+
|
220
|
+
@mock_single.should_receive(:call).with(nil,scanner,@parser).and_return(:good_result)
|
221
|
+
|
222
|
+
lambda { @parser.parse(scanner).should == :good_result }.should_not raise_error
|
223
|
+
|
224
|
+
scanner = mock "Scanner2"
|
225
|
+
scanner.should_receive(:next).any_number_of_times
|
226
|
+
scanner.should_receive(:current).exactly(5).and_return(:single_token)
|
227
|
+
|
228
|
+
@mock_single.should_receive(:call).with(nil,scanner,@parser).and_return(:good_result)
|
229
|
+
|
230
|
+
lambda { @parser.parse(scanner) }.should raise_error(ParserError)
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
235
|
+
describe "A class descending from AbstractParser should autocompile" do
|
236
|
+
|
237
|
+
before(:each) do
|
238
|
+
@class = Class.new(AbstractParser)
|
239
|
+
@parser = @class.new
|
240
|
+
#@class.autocompile true
|
241
|
+
end
|
242
|
+
|
243
|
+
it "the scope" do
|
244
|
+
@class.scope :scope
|
245
|
+
@parser.should respond_to(:parse)
|
246
|
+
@parser.should_receive(:parse_scope).once
|
247
|
+
scanner = mock "Scanner"
|
248
|
+
scanner.should_receive(:next).once
|
249
|
+
scanner.should_receive(:current).once
|
250
|
+
@parser.parse(scanner)
|
251
|
+
end
|
252
|
+
|
253
|
+
it "a 'single' production" do
|
254
|
+
@mock_single = mock "Single"
|
255
|
+
@mock_single.should_receive(:respond_to?).with(:to_ary).and_return(false)
|
256
|
+
@mock_single.should_receive(:respond_to?).with(:call).and_return(true)
|
257
|
+
@class.production(:single) do |prod|
|
258
|
+
prod.token(:single_token,@mock_single)
|
259
|
+
end
|
260
|
+
@parser.should respond_to(:parse_single)
|
261
|
+
end
|
262
|
+
|
263
|
+
it "a 'recursive' production" do
|
264
|
+
@mock_recursive = mock "Recursive"
|
265
|
+
@mock_recursive.should_receive(:respond_to?).with(:to_ary).and_return(false)
|
266
|
+
@mock_recursive.should_receive(:respond_to?).with(:call).and_return(true)
|
267
|
+
@class.production(:recursive) do |prod|
|
268
|
+
prod.mode = :recursive
|
269
|
+
prod.token(:recursive_token,@mock_recursive)
|
270
|
+
end
|
271
|
+
@parser.should respond_to(:parse_recursive)
|
272
|
+
end
|
273
|
+
end
|