llip 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/History.txt +4 -0
  2. data/MIT-LICENSE +21 -0
  3. data/Manifest.txt +45 -0
  4. data/README.txt +148 -0
  5. data/Rakefile +66 -0
  6. data/examples/ariteval/ariteval.rb +132 -0
  7. data/examples/ariteval/evaluator.rb +61 -0
  8. data/examples/ariteval/exp.rb +104 -0
  9. data/lib/llip.rb +6 -0
  10. data/lib/llip/abstract_parser.rb +170 -0
  11. data/lib/llip/abstract_scanner.rb +83 -0
  12. data/lib/llip/buffer.rb +35 -0
  13. data/lib/llip/llip_error.rb +43 -0
  14. data/lib/llip/parser.rb +93 -0
  15. data/lib/llip/production_compiler.rb +168 -0
  16. data/lib/llip/production_specification.rb +79 -0
  17. data/lib/llip/recursive_production_compiler.rb +35 -0
  18. data/lib/llip/regexp_abstract_scanner.rb +116 -0
  19. data/lib/llip/regexp_parser.rb +197 -0
  20. data/lib/llip/regexp_scanner.rb +33 -0
  21. data/lib/llip/regexp_specification.rb +210 -0
  22. data/lib/llip/token.rb +47 -0
  23. data/lib/llip/visitable.rb +37 -0
  24. data/spec/ariteval/ariteval_spec.rb +111 -0
  25. data/spec/ariteval/evaluator_spec.rb +106 -0
  26. data/spec/ariteval/exp_spec.rb +232 -0
  27. data/spec/llip/abstract_parser_spec.rb +273 -0
  28. data/spec/llip/abstract_scanner_spec.rb +152 -0
  29. data/spec/llip/buffer_spec.rb +60 -0
  30. data/spec/llip/llip_error_spec.rb +77 -0
  31. data/spec/llip/parser_spec.rb +163 -0
  32. data/spec/llip/production_compiler_spec.rb +271 -0
  33. data/spec/llip/production_specification_spec.rb +75 -0
  34. data/spec/llip/recursive_production_compiler_spec.rb +86 -0
  35. data/spec/llip/regexp_abstract_scanner_spec.rb +320 -0
  36. data/spec/llip/regexp_parser_spec.rb +265 -0
  37. data/spec/llip/regexp_scanner_spec.rb +40 -0
  38. data/spec/llip/regexp_specification_spec.rb +734 -0
  39. data/spec/llip/token_spec.rb +70 -0
  40. data/spec/llip/visitable_spec.rb +38 -0
  41. data/spec/spec_helper.rb +10 -0
  42. 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