llip 0.1.0 → 0.2.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 +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
|