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.
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