llip 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +10 -0
- data/Manifest.txt +1 -0
- data/examples/ariteval/ariteval.rb +18 -12
- data/examples/ariteval/ariteval_repl.rb +24 -0
- data/examples/ariteval/evaluator.rb +1 -0
- data/examples/ariteval/exp.rb +4 -6
- data/lib/llip.rb +2 -1
- data/lib/llip/parser.rb +1 -1
- data/lib/llip/production_specification.rb +10 -2
- data/lib/llip/regexp_parser.rb +63 -4
- data/lib/llip/regexp_scanner.rb +3 -1
- data/lib/llip/regexp_specification.rb +7 -1
- data/spec/ariteval/ariteval_spec.rb +4 -0
- data/spec/ariteval/evaluator_spec.rb +25 -21
- data/spec/llip/abstract_parser_spec.rb +0 -1
- data/spec/llip/abstract_scanner_spec.rb +0 -1
- data/spec/llip/llip_error_spec.rb +0 -1
- data/spec/llip/parser_spec.rb +3 -3
- data/spec/llip/production_compiler_spec.rb +11 -2
- data/spec/llip/production_specification_spec.rb +5 -1
- data/spec/llip/regexp_abstract_scanner_spec.rb +0 -1
- data/spec/llip/regexp_parser_spec.rb +86 -5
- data/spec/llip/regexp_scanner_spec.rb +12 -2
- data/spec/llip/regexp_specification_spec.rb +8 -4
- data/spec/spec_helper.rb +8 -6
- metadata +4 -3
data/History.txt
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
== 0.2.0 / 2007-07-17
|
2
|
+
|
3
|
+
* 2 major enhacement
|
4
|
+
* added the '[a-Z]' syntax to regular expression support
|
5
|
+
* added the possibility to define two token in which final states of the first are internal states of the second
|
6
|
+
* 1 minor enhacement
|
7
|
+
* spec loading fixed
|
8
|
+
* added a simple REPL based on Ariteval
|
9
|
+
* aliased :recursive with :iterative because it's more intuitive
|
10
|
+
|
1
11
|
== 0.1.0 / 2007-06-09
|
2
12
|
|
3
13
|
* 1 major enhancement
|
data/Manifest.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require File.dirname(__FILE__) + '/evaluator'
|
2
|
+
require File.dirname(__FILE__) + '/exp'
|
3
3
|
|
4
4
|
# It's a simple arithmetical evaluator. It's able to parse expressions like these:
|
5
5
|
# * ( a = 3 * 2 ) - ( 24 + a ),
|
@@ -25,7 +25,7 @@ require 'evaluator'
|
|
25
25
|
# FACTOR ::= ( EXP )
|
26
26
|
# }
|
27
27
|
#
|
28
|
-
class Ariteval < Parser
|
28
|
+
class Ariteval < LLIP::Parser
|
29
29
|
|
30
30
|
def initialize
|
31
31
|
super
|
@@ -39,8 +39,7 @@ class Ariteval < Parser
|
|
39
39
|
|
40
40
|
# tokens definitions
|
41
41
|
|
42
|
-
|
43
|
-
token :number, "(#{numbers})+ *"
|
42
|
+
token :number, "[0-9]+ *"
|
44
43
|
|
45
44
|
token :plus, '\+ *'
|
46
45
|
|
@@ -54,19 +53,27 @@ class Ariteval < Parser
|
|
54
53
|
|
55
54
|
token ")".to_sym, '\) *'
|
56
55
|
|
57
|
-
|
58
|
-
token :ident, "(#{identifiers}) *"
|
56
|
+
token :ident, "[a-Z] *"
|
59
57
|
|
60
58
|
token :assign, "= *"
|
61
59
|
|
60
|
+
token :space, " "
|
61
|
+
|
62
62
|
# production definitions
|
63
63
|
|
64
64
|
lookahead(true)
|
65
65
|
|
66
66
|
scope :exp
|
67
67
|
|
68
|
-
production(:exp,:
|
69
|
-
prod.default
|
68
|
+
production(:exp,:iterative) do |prod|
|
69
|
+
prod.default do |scanner,parser|
|
70
|
+
#this is needed to trim spaces from the beginning of the string to parse
|
71
|
+
while scanner.current == :space
|
72
|
+
scanner.next
|
73
|
+
end
|
74
|
+
|
75
|
+
parser.parse_term
|
76
|
+
end
|
70
77
|
|
71
78
|
prod.token(:plus) do |term_seq,scanner,parser|
|
72
79
|
scanner.next
|
@@ -79,10 +86,9 @@ class Ariteval < Parser
|
|
79
86
|
next_term = parser.parse_term
|
80
87
|
MinusExp.new(term_seq,next_term)
|
81
88
|
end
|
82
|
-
|
83
89
|
end
|
84
90
|
|
85
|
-
production(:term,:
|
91
|
+
production(:term,:iterative) do |prod|
|
86
92
|
prod.default { |scanner,parser| parser.parse_factor }
|
87
93
|
|
88
94
|
prod.token(:mul) do |factor_seq,scanner,parser|
|
@@ -129,4 +135,4 @@ class Ariteval < Parser
|
|
129
135
|
|
130
136
|
end
|
131
137
|
|
132
|
-
end
|
138
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#! /usr/bin/ruby
|
2
|
+
|
3
|
+
unless Object.const_defined? :LLIP
|
4
|
+
require File.join(File.dirname(__FILE__), "/../../lib/llip")
|
5
|
+
end
|
6
|
+
|
7
|
+
require File.dirname(__FILE__) + "/ariteval"
|
8
|
+
|
9
|
+
Signal.trap("INT") do
|
10
|
+
puts "Goodbye!"
|
11
|
+
exit(0)
|
12
|
+
end
|
13
|
+
|
14
|
+
parser = Ariteval.new
|
15
|
+
|
16
|
+
loop do
|
17
|
+
print "> "
|
18
|
+
string = readline.strip
|
19
|
+
begin
|
20
|
+
puts "=> " + parser.evaluate(string).to_s
|
21
|
+
rescue Exception => er
|
22
|
+
puts "Exception raised: \"#{er.message}\""
|
23
|
+
end
|
24
|
+
end
|
data/examples/ariteval/exp.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
|
-
require 'visitable'
|
2
|
-
|
3
1
|
class NumExp
|
4
2
|
|
5
|
-
include Visitable
|
3
|
+
include LLIP::Visitable
|
6
4
|
|
7
5
|
attr_reader :value
|
8
6
|
|
@@ -16,7 +14,7 @@ class NumExp
|
|
16
14
|
end
|
17
15
|
|
18
16
|
class IdentExp
|
19
|
-
include Visitable
|
17
|
+
include LLIP::Visitable
|
20
18
|
|
21
19
|
attr_reader :value
|
22
20
|
|
@@ -31,7 +29,7 @@ end
|
|
31
29
|
|
32
30
|
class AssignIdentExp
|
33
31
|
|
34
|
-
include Visitable
|
32
|
+
include LLIP::Visitable
|
35
33
|
|
36
34
|
attr_reader :name
|
37
35
|
attr_reader :value
|
@@ -49,7 +47,7 @@ end
|
|
49
47
|
|
50
48
|
class OpExp
|
51
49
|
|
52
|
-
include Visitable
|
50
|
+
include LLIP::Visitable
|
53
51
|
|
54
52
|
attr_reader :op
|
55
53
|
attr_reader :left
|
data/lib/llip.rb
CHANGED
data/lib/llip/parser.rb
CHANGED
@@ -27,8 +27,8 @@ module LLIP
|
|
27
27
|
# They are specified through ProductionSpecification#token.
|
28
28
|
attr_reader :tokens
|
29
29
|
|
30
|
-
# The mode of the production. It can be :single or :recursive.
|
31
|
-
|
30
|
+
# The mode of the production. It can be :single or :recursive (:iterative is just an alias for :recursive).
|
31
|
+
attr_reader :mode
|
32
32
|
|
33
33
|
# This attribute specifies if the production should raise an exception if the current token hasn't been recognized.
|
34
34
|
# It's important only for :single productions.
|
@@ -75,5 +75,13 @@ module LLIP
|
|
75
75
|
@default
|
76
76
|
end
|
77
77
|
|
78
|
+
# see ProductionSpecification#mode
|
79
|
+
def mode=(value)
|
80
|
+
if value == :iterative
|
81
|
+
value = :recursive
|
82
|
+
end
|
83
|
+
@mode = value
|
84
|
+
end
|
85
|
+
|
78
86
|
end
|
79
87
|
end
|
data/lib/llip/regexp_parser.rb
CHANGED
@@ -6,11 +6,11 @@ module LLIP
|
|
6
6
|
#
|
7
7
|
# === Grammar
|
8
8
|
#
|
9
|
-
# VN = { EXP , ELEMENT}
|
9
|
+
# VN = { EXP , ELEMENT , META}
|
10
10
|
#
|
11
11
|
# char = every charachter
|
12
12
|
#
|
13
|
-
# symb = { ( , ) , . , * , + , \ , |}
|
13
|
+
# symb = { ( , ) , . , * , + , \ , | , [ , ] }
|
14
14
|
#
|
15
15
|
# VT = char U symb
|
16
16
|
#
|
@@ -24,7 +24,12 @@ module LLIP
|
|
24
24
|
# META -> ELEMENT+
|
25
25
|
# META -> ELEMENT
|
26
26
|
# ELEMENT -> char or . or \symb
|
27
|
+
# ELEMENT -> [CLASS]
|
27
28
|
# ELEMENT -> (EXP)
|
29
|
+
# CLASS -> char
|
30
|
+
# CLASS -> char CLASS
|
31
|
+
# CLASS -> char - char
|
32
|
+
# CLASS -> char - char CLASS
|
28
33
|
# }
|
29
34
|
#
|
30
35
|
# or in EBNF format
|
@@ -32,7 +37,8 @@ module LLIP
|
|
32
37
|
# P' = {
|
33
38
|
# EXP ::= META{[|]EXP}
|
34
39
|
# META ::= ELEMENT[* or +]
|
35
|
-
# ELEMENT ::= char or . or \symb or (EXP)
|
40
|
+
# ELEMENT ::= char or . or \symb or (EXP) or [CLASS]
|
41
|
+
# CLASS ::= char{ - char or - char char or char}
|
36
42
|
# }
|
37
43
|
#
|
38
44
|
class LLIP::RegexpParser < LLIP::AbstractParser
|
@@ -67,7 +73,6 @@ module LLIP
|
|
67
73
|
end
|
68
74
|
|
69
75
|
p.token("|") do |result,scanner,parser|
|
70
|
-
result
|
71
76
|
scanner.next
|
72
77
|
parser[:last] = result
|
73
78
|
parser.parse_meta.last
|
@@ -88,6 +93,11 @@ module LLIP
|
|
88
93
|
result
|
89
94
|
end
|
90
95
|
|
96
|
+
p.token("[") do |result,scanner,parser|
|
97
|
+
parser.parse_meta
|
98
|
+
result
|
99
|
+
end
|
100
|
+
|
91
101
|
p.token("\\") do |result,scanner,parser|
|
92
102
|
parser.parse_meta
|
93
103
|
result
|
@@ -171,6 +181,55 @@ module LLIP
|
|
171
181
|
parser[:last] = first_state.last
|
172
182
|
first_state.keys
|
173
183
|
end
|
184
|
+
|
185
|
+
p.token("[") do |result, scanner, parser|
|
186
|
+
scanner.next
|
187
|
+
|
188
|
+
chars = parser.parse_class
|
189
|
+
unless scanner.current == "]"
|
190
|
+
raise "Every '[' must be followed by a ']'"
|
191
|
+
end
|
192
|
+
scanner.next
|
193
|
+
|
194
|
+
state = parser[:regexp].add_state
|
195
|
+
chars.each do |char|
|
196
|
+
parser[:last].each { |s| s[char] = state }
|
197
|
+
end
|
198
|
+
|
199
|
+
parser[:last] = [state]
|
200
|
+
chars
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
production :class, :recursive do |prod|
|
205
|
+
prod.default do |scanner,parser|
|
206
|
+
[]
|
207
|
+
end
|
208
|
+
|
209
|
+
prod.token(:char) do |result, scanner, parser|
|
210
|
+
result << scanner.current.value
|
211
|
+
scanner.next
|
212
|
+
result
|
213
|
+
end
|
214
|
+
|
215
|
+
prod.token(:char,"-",:char) do |result, scanner, parser|
|
216
|
+
first_char = scanner.current.value
|
217
|
+
scanner.next
|
218
|
+
last_char = scanner.next.value
|
219
|
+
|
220
|
+
block = lambda do |char|
|
221
|
+
result << char
|
222
|
+
end
|
223
|
+
|
224
|
+
if first_char =~ /[a-z]/ and last_char =~ /[A-Z]/
|
225
|
+
(first_char.."z").to_a.each(&block)
|
226
|
+
("A"..last_char).to_a.each(&block)
|
227
|
+
else
|
228
|
+
(first_char..last_char).to_a.each(&block)
|
229
|
+
end
|
230
|
+
scanner.next
|
231
|
+
result
|
232
|
+
end
|
174
233
|
end
|
175
234
|
|
176
235
|
def add_char(parser, scanner, char=scanner.current.value)
|
data/lib/llip/regexp_scanner.rb
CHANGED
@@ -15,7 +15,7 @@ module LLIP
|
|
15
15
|
|
16
16
|
add_regexp(CHAR)
|
17
17
|
|
18
|
-
# It represents the regular expression '(.|*|+|\(|\)
|
18
|
+
# It represents the regular expression '(.|*|+|\(|\)|\\|\|\[|\]-)' so it matches the chars: . * + ( ) \ | [ ] -
|
19
19
|
SYMBOL = LLIP::RegexpSpecification.new(:symbol)
|
20
20
|
|
21
21
|
SYMBOL.add_state
|
@@ -27,6 +27,8 @@ module LLIP
|
|
27
27
|
SYMBOL.init[')'] = final
|
28
28
|
SYMBOL.init['\\'] = final
|
29
29
|
SYMBOL.init['|'] = final
|
30
|
+
SYMBOL.init['['] = final
|
31
|
+
SYMBOL.init[']'] = final
|
30
32
|
|
31
33
|
add_regexp(SYMBOL)
|
32
34
|
end
|
@@ -177,8 +177,14 @@ module LLIP
|
|
177
177
|
examined[new_first] ||= state
|
178
178
|
examined[new_second] ||= state
|
179
179
|
|
180
|
-
if new_first.final?
|
180
|
+
if new_first.final? and new_second.final?
|
181
181
|
raise "It's impossible to mix two regexp with final states in common."
|
182
|
+
elsif new_first.final?
|
183
|
+
state.regexp = new_first.regexp
|
184
|
+
state.final = true
|
185
|
+
elsif new_second.final?
|
186
|
+
state.regexp = new_second.regexp
|
187
|
+
state.final = true
|
182
188
|
end
|
183
189
|
mix_accessor(new_first,new_second,regexp,state,examined)
|
184
190
|
end
|
@@ -80,6 +80,10 @@ describe "An Ariteval should be able to parse" do
|
|
80
80
|
it "'( a = 3 * 2 ) - ( 24 + a )'" do
|
81
81
|
@parser.parse('( a = 3 * 2 ) - ( 24 + a )').to_s.should == "( ( a = ( 3 * 2 ) ) - ( 24 + a ) )"
|
82
82
|
end
|
83
|
+
|
84
|
+
it "' 3 + 2'" do
|
85
|
+
@parser.parse(' 3 + 2').to_s.should == "( 3 + 2 )"
|
86
|
+
end
|
83
87
|
end
|
84
88
|
|
85
89
|
describe "An Ariteval shouldn't be able to parse" do
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
require 'ariteval'
|
2
3
|
require 'evaluator'
|
3
|
-
require 'buffer'
|
4
4
|
|
5
5
|
|
6
6
|
describe 'An Evaluator' do
|
@@ -61,26 +61,26 @@ describe "An Evaluator should be able to eval" do
|
|
61
61
|
expression = "3 * (4 - 2) + 5*(4/2)/(3-2)"
|
62
62
|
|
63
63
|
exp = PlusExp.new(
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
64
|
+
MulExp.new(
|
65
|
+
NumExp.new(3),
|
66
|
+
MinusExp.new(
|
67
|
+
NumExp.new(4),
|
68
|
+
NumExp.new(2)
|
69
|
+
)
|
70
|
+
),
|
71
|
+
DivExp.new(
|
72
|
+
MulExp.new(
|
73
|
+
NumExp.new(5),
|
74
|
+
DivExp.new(
|
75
|
+
NumExp.new(4),
|
76
|
+
NumExp.new(2)
|
77
|
+
)
|
78
|
+
),
|
79
|
+
DivExp.new(
|
80
|
+
NumExp.new(3),
|
81
|
+
NumExp.new(2)
|
82
|
+
)
|
83
|
+
)
|
84
84
|
)
|
85
85
|
|
86
86
|
exp.accept(@eval)
|
@@ -103,4 +103,8 @@ describe "An Evaluator should be able to eval" do
|
|
103
103
|
@parser.parse("a").accept(@eval)
|
104
104
|
@eval.result.should == @eval.ident_table["a"]
|
105
105
|
end
|
106
|
+
|
107
|
+
it "an IdentExp with an unknown name" do
|
108
|
+
lambda { @parser.parse("a").accept(@eval) }.should raise_error
|
109
|
+
end
|
106
110
|
end
|
data/spec/llip/parser_spec.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
-
require 'parser'
|
3
2
|
require 'stringio'
|
4
3
|
|
5
4
|
describe "A class that descend from Parser" do
|
@@ -38,7 +37,8 @@ describe "A class that descend from Parser" do
|
|
38
37
|
@class.should respond_to(:regexp_scanner)
|
39
38
|
|
40
39
|
@class.regexp_parser.should be_kind_of(RegexpParser)
|
41
|
-
@class.regexp_scanner.should be_kind_of(
|
40
|
+
@class.regexp_scanner.should be_kind_of(Buffer)
|
41
|
+
@class.regexp_scanner.scanner.should be_kind_of(RegexpScanner)
|
42
42
|
end
|
43
43
|
|
44
44
|
it "should have a token method which parse a regexp and calls :add_regexp to the scanner" do
|
@@ -135,7 +135,7 @@ describe "The instance of a class descending from Parser with a simple grammar s
|
|
135
135
|
@instance = @class.new
|
136
136
|
|
137
137
|
@class.token(:plus,"\\+")
|
138
|
-
@class.token(:number,
|
138
|
+
@class.token(:number,"[0-9]")
|
139
139
|
|
140
140
|
@class.scope(:exp)
|
141
141
|
|
@@ -137,12 +137,13 @@ describe "A ProductionCompiler" do
|
|
137
137
|
|
138
138
|
@production = mock "Production"
|
139
139
|
@production.should_receive(:name).and_return(:fake)
|
140
|
-
@production.should_receive(:tokens).and_return({
|
140
|
+
@production.should_receive(:tokens).and_return({[:look,"3","2"] => nil, :look => nil, [:look,"1"] => nil, [:look, "2"] => nil })
|
141
141
|
@production.should_receive(:raise_on_error).and_return(true)
|
142
142
|
|
143
143
|
@compiler.should_receive(:start).with(:fake)
|
144
|
-
@compiler.should_receive(:token).with([:look,"3"])
|
144
|
+
@compiler.should_receive(:token).with([:look,"3","2"])
|
145
145
|
@compiler.should_receive(:token).with([:look,"1"])
|
146
|
+
@compiler.should_receive(:token).with([:look,"2"])
|
146
147
|
@compiler.should_receive(:token).with(:look)
|
147
148
|
@compiler.should_receive(:end).with(true)
|
148
149
|
|
@@ -156,6 +157,14 @@ describe "A ProductionCompiler" do
|
|
156
157
|
@production.should_receive(:tokens).and_return({ [:look,"1"] => nil, [:look,"3","2"] => nil, :everything => nil, :look => nil})
|
157
158
|
|
158
159
|
@compiler.sort_production(@production).should == [[:look,"3","2"],[:look,"1"], :look, :everything]
|
160
|
+
|
161
|
+
@production = mock "Production"
|
162
|
+
tokens = mock "Tokens"
|
163
|
+
tokens.should_receive(:keys).twice.and_return([[:look,"3","2"],[:look,"1"]])
|
164
|
+
tokens.should_receive(:has_key?).twice.with(:look).and_return(false)
|
165
|
+
@production.should_receive(:tokens).and_return(tokens)
|
166
|
+
|
167
|
+
@compiler.sort_production(@production).should == [[:look,"3","2"],[:look,"1"]]
|
159
168
|
end
|
160
169
|
end
|
161
170
|
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
-
require 'production_specification'
|
3
2
|
|
4
3
|
describe "A ProductionSpecification" do
|
5
4
|
|
@@ -72,4 +71,9 @@ describe "A ProductionSpecification" do
|
|
72
71
|
@production.raise_on_error=false
|
73
72
|
@production.raise_on_error.should == false
|
74
73
|
end
|
74
|
+
|
75
|
+
it "should alias :recursive with :iterative" do
|
76
|
+
@production.mode = :iterative
|
77
|
+
@production.mode.should == :recursive
|
78
|
+
end
|
75
79
|
end
|
@@ -1,14 +1,11 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
-
require 'regexp_parser'
|
3
|
-
require 'regexp_specification'
|
4
|
-
require 'token'
|
5
2
|
|
6
3
|
module RegexpMockScannerBuilder
|
7
4
|
|
8
5
|
def mock_scanner(*tokens)
|
9
6
|
@scanner = mock "Scanner"
|
10
7
|
tokens.map! do |t|
|
11
|
-
if t =~ /[\.\+\*\|\(\)
|
8
|
+
if t =~ /[\.\+\*\|\(\)\\\-\[\]]/
|
12
9
|
Token.new(:symbol,t)
|
13
10
|
else
|
14
11
|
Token.new(:char,t)
|
@@ -19,7 +16,7 @@ module RegexpMockScannerBuilder
|
|
19
16
|
t = nil
|
20
17
|
@scanner.should_receive(:next).exactly(tokens.size).and_return { t = @tokens.shift }
|
21
18
|
@scanner.should_receive(:current).any_number_of_times.and_return { t }
|
22
|
-
@scanner
|
19
|
+
Buffer.new(@scanner)
|
23
20
|
end
|
24
21
|
|
25
22
|
end
|
@@ -245,6 +242,85 @@ describe "A RegexpParser should parse" do
|
|
245
242
|
regexp.init['a']['c'].final?.should == false
|
246
243
|
regexp.init['a']['c']['e'].final?.should == false
|
247
244
|
end
|
245
|
+
|
246
|
+
it "[a-zD]" do
|
247
|
+
@scanner = mock_scanner("[","a","-","z","D","]")
|
248
|
+
regexp = @parser.parse(@scanner)
|
249
|
+
|
250
|
+
keys = regexp.init.keys
|
251
|
+
expected_keys = ("a".."z").to_a + ["D"]
|
252
|
+
keys.sort!
|
253
|
+
expected_keys.sort!
|
254
|
+
keys.should == expected_keys
|
255
|
+
keys.each do |key|
|
256
|
+
regexp[key].should be_final
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
it "[a-Z]" do
|
261
|
+
@scanner = mock_scanner("[","a","-","Z","]")
|
262
|
+
regexp = @parser.parse(@scanner)
|
263
|
+
|
264
|
+
keys = regexp.init.keys
|
265
|
+
expected_keys = ("a".."z").to_a + ("A".."Z").to_a
|
266
|
+
keys.sort!
|
267
|
+
expected_keys.sort!
|
268
|
+
keys.should == expected_keys
|
269
|
+
keys.each do |key|
|
270
|
+
regexp[key].should be_final
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
|
275
|
+
it "a[bc]+d" do
|
276
|
+
@scanner = mock_scanner("a","[","b","c","]","+","d")
|
277
|
+
regexp = @parser.parse(@scanner)
|
278
|
+
|
279
|
+
regexp.init['a'].keys.should == ['b','c']
|
280
|
+
|
281
|
+
regexp['a']['b'].keys.should == ['b','c','d']
|
282
|
+
regexp['a']['c'].keys.should == ['b','c','d']
|
283
|
+
|
284
|
+
regexp.init['a']['b']['d'].should be_final
|
285
|
+
regexp.init['a']['c']['d'].should be_final
|
286
|
+
|
287
|
+
regexp.init['a'].should_not == regexp.init['a']['b']
|
288
|
+
regexp.init['a'].should_not == regexp.init['a']['c']
|
289
|
+
|
290
|
+
regexp.init['a']['b']['b'].should == regexp.init['a']['b']
|
291
|
+
regexp.init['a']['c']['b'].should == regexp.init['a']['b']
|
292
|
+
regexp.init['a']['b']['c'].should == regexp['a']['c']
|
293
|
+
|
294
|
+
regexp['a']['b'].final?.should == false
|
295
|
+
regexp.init['a']['c'].final?.should == false
|
296
|
+
regexp.init['a']['c'].final?.should == false
|
297
|
+
end
|
298
|
+
|
299
|
+
|
300
|
+
it "a[bc]*d" do
|
301
|
+
@scanner = mock_scanner("a","[","b","c","]","*","d")
|
302
|
+
regexp = @parser.parse(@scanner)
|
303
|
+
|
304
|
+
regexp.init['a'].keys.should == ['b','c','d']
|
305
|
+
regexp['a']['d'].should be_final
|
306
|
+
|
307
|
+
regexp['a']['b'].keys.should == ['b','c','d']
|
308
|
+
regexp['a']['c'].keys.should == ['b','c','d']
|
309
|
+
|
310
|
+
regexp.init['a']['b']['d'].should be_final
|
311
|
+
regexp.init['a']['c']['d'].should be_final
|
312
|
+
|
313
|
+
regexp.init['a'].should == regexp.init['a']['b']
|
314
|
+
regexp.init['a'].should == regexp.init['a']['c']
|
315
|
+
|
316
|
+
regexp.init['a']['b']['b'].should == regexp.init['a']['b']
|
317
|
+
regexp.init['a']['c']['b'].should == regexp.init['a']['b']
|
318
|
+
regexp.init['a']['b']['c'].should == regexp['a']['c']
|
319
|
+
|
320
|
+
regexp['a']['b'].final?.should == false
|
321
|
+
regexp.init['a']['c'].final?.should == false
|
322
|
+
regexp.init['a']['c'].final?.should == false
|
323
|
+
end
|
248
324
|
|
249
325
|
end
|
250
326
|
|
@@ -261,5 +337,10 @@ describe "A RegexpParser should not parse" do
|
|
261
337
|
@scanner = mock_scanner("a","(","b","c","d","e","f")
|
262
338
|
lambda { @parser.parse(@scanner) }.should raise_error(RuntimeError)
|
263
339
|
end
|
340
|
+
|
341
|
+
it "a[cdef" do
|
342
|
+
@scanner = mock_scanner("a","[","b","c","d","e","f")
|
343
|
+
lambda { @parser.parse(@scanner) }.should raise_error(RuntimeError)
|
344
|
+
end
|
264
345
|
|
265
346
|
end
|
@@ -1,6 +1,4 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
-
require 'regexp_scanner'
|
3
|
-
require 'token'
|
4
2
|
require 'stringio'
|
5
3
|
|
6
4
|
describe "A RegexpScanner should scan" do
|
@@ -37,4 +35,16 @@ describe "A RegexpScanner should scan" do
|
|
37
35
|
@scanner.next.should == :symbol
|
38
36
|
@scanner.next.should be_nil
|
39
37
|
end
|
38
|
+
|
39
|
+
it "'[a-zA-Z]'" do
|
40
|
+
@scanner.scan('[a-zA-Z]')
|
41
|
+
@scanner.next.should == :symbol
|
42
|
+
@scanner.next.should == 'a'
|
43
|
+
@scanner.next.should == '-'
|
44
|
+
@scanner.next.should == 'z'
|
45
|
+
@scanner.next.should == 'A'
|
46
|
+
@scanner.next.should == '-'
|
47
|
+
@scanner.next.should == 'Z'
|
48
|
+
@scanner.next.should == :symbol
|
49
|
+
end
|
40
50
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
-
require 'regexp_specification'
|
3
2
|
|
4
3
|
describe "A RegexpSpecification" do
|
5
4
|
|
@@ -637,7 +636,7 @@ describe "The :mix method of the RegexpSpecification class" do
|
|
637
636
|
second['a'].error.should == s3
|
638
637
|
end
|
639
638
|
|
640
|
-
it "should
|
639
|
+
it "should be able to mix: 'ab' and 'abc'" do
|
641
640
|
r1 = RegexpSpecification.new("first")
|
642
641
|
r2 = RegexpSpecification.new("second")
|
643
642
|
|
@@ -654,8 +653,13 @@ describe "The :mix method of the RegexpSpecification class" do
|
|
654
653
|
s4['a'] = s5
|
655
654
|
s5['b'] = s6
|
656
655
|
s6['c'] = s7
|
657
|
-
|
658
|
-
|
656
|
+
|
657
|
+
result = RegexpSpecification.mix(r1,r2)
|
658
|
+
result.name.should == :"mix between 'first' and 'second'"
|
659
|
+
result['a']['b'].should be_final
|
660
|
+
result['a']['b'].regexp.should == r1
|
661
|
+
result['a']['b']['c'].should be_final
|
662
|
+
result['a']['b']['c'].regexp.should == r2
|
659
663
|
end
|
660
664
|
|
661
665
|
it "should be able to mix: '(a|b)c' and '(b|e)d'" do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
unless Object.const_defined? :LLIP
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), "/../lib/llip"))
|
3
3
|
|
4
|
-
|
4
|
+
require File.join(File.dirname(__FILE__), "/../lib/llip")
|
5
5
|
|
6
|
-
|
6
|
+
include LLIP
|
7
7
|
|
8
|
-
|
8
|
+
require 'rubygems'
|
9
|
+
require 'spec'
|
9
10
|
|
10
|
-
$: << File.dirname(__FILE__) + "/../examples/ariteval"
|
11
|
+
$: << File.dirname(__FILE__) + "/../examples/ariteval"
|
12
|
+
end
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: llip
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2007-07-17 00:00:00 +02:00
|
8
8
|
summary: LLIP is a tool to geneate a LL(k) parser.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- Rakefile
|
37
37
|
- examples/ariteval
|
38
38
|
- examples/ariteval/ariteval.rb
|
39
|
+
- examples/ariteval/ariteval_repl.rb
|
39
40
|
- examples/ariteval/evaluator.rb
|
40
41
|
- examples/ariteval/exp.rb
|
41
42
|
- lib/llip
|