rubypeg 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README +67 -0
- data/bin/text-peg2ruby-peg +24 -0
- data/examples/arithmetic/arithmetic_peg.rb +57 -0
- data/examples/arithmetic/arithmetic_peg.txt +9 -0
- data/examples/arithmetic/arithmetic_peg_spec.rb +49 -0
- data/examples/arithmetic/calculator.rb +40 -0
- data/examples/arithmetic/calculator_spec.rb +26 -0
- data/examples/excel/excel_peg.txt +35 -0
- data/examples/excel/excel_peg_spec.rb +139 -0
- data/lib/rubypeg.rb +313 -0
- data/lib/textpeg.rb +159 -0
- data/lib/textpeg.txt +29 -0
- data/lib/textpeg2rubypeg.rb +182 -0
- data/spec/any_character_spec.rb +22 -0
- data/spec/lookahead_spec.rb +101 -0
- data/spec/non_terminal_spec.rb +229 -0
- data/spec/peg_leg_spec.rb +63 -0
- data/spec/repetition_spec.rb +74 -0
- data/spec/sequence_spec.rb +50 -0
- data/spec/terminal_spec.rb +118 -0
- data/spec/text_peg2ruby_peg_spec.rb +302 -0
- data/spec/text_peg_spec.rb +123 -0
- data/spec/visit_spec.rb +67 -0
- metadata +95 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'rubypeg'
|
3
|
+
|
4
|
+
class TestNegativeLookahead < RubyPeg
|
5
|
+
def root
|
6
|
+
node :root do
|
7
|
+
one_or_more { negative || word || space }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def negative
|
12
|
+
node :negative do
|
13
|
+
terminal('!') && not_followed_by { terminal('!') }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def word
|
18
|
+
node :word do
|
19
|
+
terminal(/\S+/)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def space
|
24
|
+
ignore { terminal(/\s+/) }
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
describe TestNegativeLookahead do
|
30
|
+
|
31
|
+
it "matches normally" do
|
32
|
+
TestNegativeLookahead.parse("one two three").to_ast.should == [:root,[:word,"one"],[:word,"two"],[:word,"three"]]
|
33
|
+
end
|
34
|
+
|
35
|
+
it "inverts normally" do
|
36
|
+
TestNegativeLookahead.parse("one !two three").to_ast.should == [:root,[:word,"one"],[:negative,"!"],[:word,"two"],[:word,"three"]]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "looks ahead so as to interpret double negatives" do
|
40
|
+
TestNegativeLookahead.parse("one !!two three").to_ast.should == [:root,[:word,"one"],[:word,"!!two"],[:word,"three"]]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class TestPositiveLookahead < RubyPeg
|
45
|
+
def root
|
46
|
+
node :root do
|
47
|
+
one_or_more { noun || verb || adjective || adverb || word || space }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def adjective
|
52
|
+
node :adjective do
|
53
|
+
terminal(/\S+/) && followed_by { space && noun }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def adverb
|
58
|
+
node :adverb do
|
59
|
+
terminal(/\S+/) && followed_by { space && verb }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def word
|
64
|
+
node :word do
|
65
|
+
terminal(/\S+/)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def noun
|
70
|
+
node :noun do
|
71
|
+
terminal('dog') || terminal('fox')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def verb
|
76
|
+
node :verb do
|
77
|
+
terminal('jumped') || terminal('ran')
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def space
|
82
|
+
ignore { terminal(/\s+/) }
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
describe TestPositiveLookahead do
|
89
|
+
|
90
|
+
it "matches normally" do
|
91
|
+
TestPositiveLookahead.parse("black blinking").to_ast.should == [:root,[:word,"black"],[:word,"blinking"]]
|
92
|
+
end
|
93
|
+
|
94
|
+
it "matches when looking ahead" do
|
95
|
+
TestPositiveLookahead.parse("brown dog").to_ast.should == [:root,[:adjective,"brown"],[:noun,"dog"]]
|
96
|
+
end
|
97
|
+
|
98
|
+
it "matches in slighlty more complicated squences" do
|
99
|
+
TestPositiveLookahead.parse("brown dog jumped as it lazily ran").to_ast.should == [:root,[:adjective,"brown"],[:noun,"dog"],[:verb,"jumped"],[:word,'as'],[:word,'it'],[:adverb,'lazily'],[:verb,'ran']]
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'rubypeg'
|
3
|
+
|
4
|
+
class NonTerminalNodeTest < RubyPeg
|
5
|
+
def root
|
6
|
+
node :one do
|
7
|
+
terminal("one")
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ChildlessNonTerminalNodeTest < RubyPeg
|
13
|
+
def root
|
14
|
+
node :one do
|
15
|
+
ignore { terminal("one") }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class MultipleChildNonTerminalNodeTest < RubyPeg
|
21
|
+
def root
|
22
|
+
node :one do
|
23
|
+
terminal("one") && terminal("two")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# class CustomNode
|
29
|
+
# def initialize(children)
|
30
|
+
# # ignored
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# class CustomNodeClassNonTerminalNodeTest < RubyPeg
|
35
|
+
# def root
|
36
|
+
# node CustomNode do
|
37
|
+
# terminal("one")
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# module CustomNodeA; end
|
43
|
+
# module CustomNodeB; end
|
44
|
+
#
|
45
|
+
# class CustomNodeModuleNonTerminalNodeTest < RubyPeg
|
46
|
+
# def root
|
47
|
+
# node CustomNodeA, CustomNodeB do
|
48
|
+
# terminal("one")
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
|
53
|
+
class CreateNonTerminalNodeTest < NonTerminalNodeTest
|
54
|
+
|
55
|
+
def create_non_terminal_node(type,children)
|
56
|
+
[type,*children.map(&:to_s)]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe NonTerminalNodeTest do
|
61
|
+
|
62
|
+
def parse(text)
|
63
|
+
NonTerminalNodeTest.parse(text)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "non terminals are only created if the terminals that they contain match" do
|
67
|
+
parse("two").should == nil
|
68
|
+
end
|
69
|
+
|
70
|
+
it "if a symbol is passed to the node method then non terminals are, by default, instances of Array that have been extended with a NonTerminalNode moduoe" do
|
71
|
+
parse("one").class.should == Array
|
72
|
+
parse("one").should be_kind_of(NonTerminalNode)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "NonTerminalNode instances have a type attribute that returns the symbol used as an argument to the node call" do
|
76
|
+
parse("one").type.should == :one
|
77
|
+
end
|
78
|
+
|
79
|
+
it "NonTerminalNode instances are an array of child nodes" do
|
80
|
+
parse("one").should be_kind_of(Array)
|
81
|
+
parse("one").first.should be_kind_of(TerminalNode)
|
82
|
+
parse("one").first.to_s.should == "one"
|
83
|
+
end
|
84
|
+
|
85
|
+
it "if there are no children, it is an empty array" do
|
86
|
+
ChildlessNonTerminalNodeTest.parse("one").should == []
|
87
|
+
end
|
88
|
+
|
89
|
+
it "NonTerminalNode instances respond to to_ast by returning [:type,*children]" do
|
90
|
+
parse("one").to_ast.should be_kind_of(Array)
|
91
|
+
parse("one").to_ast.should == [:one,"one"]
|
92
|
+
MultipleChildNonTerminalNodeTest.parse("onetwo").to_ast.should == [:one,"one","two"]
|
93
|
+
end
|
94
|
+
|
95
|
+
it "NonTerminalNode instances respond to to_ast by returning [:type] if there are no children" do
|
96
|
+
ChildlessNonTerminalNodeTest.parse("one").to_ast.should be_kind_of(Array)
|
97
|
+
ChildlessNonTerminalNodeTest.parse("one").to_ast.should == [:one]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "NonTerminalNode instances respond to visit(builder) by trying to call a method with the same name on the builder and with its children as arguments" do
|
101
|
+
builder = mock(:TestBuilder)
|
102
|
+
builder.should_receive(:one).with {|a| a.kind_of?(TerminalNode) && a.to_s == "one"}.and_return(1)
|
103
|
+
parse("one").visit(builder).should == 1
|
104
|
+
end
|
105
|
+
|
106
|
+
it "If the builder doesn't have a method with its name, then it calls visit(builder) on its children, returning a string if there is only one child" do
|
107
|
+
builder = mock(:TestBuilder)
|
108
|
+
parse("one").visit(builder).should == "one"
|
109
|
+
end
|
110
|
+
|
111
|
+
it "If the builder doesn't have a method with its name, then it calls visit(builder) on its children, returning a an array of strings if there is more than one child" do
|
112
|
+
builder = mock(:TestBuilder)
|
113
|
+
MultipleChildNonTerminalNodeTest.parse("onetwo").visit(builder).should == ["one","two"]
|
114
|
+
end
|
115
|
+
|
116
|
+
it "the class of the non terminal can be altered by overriding the create_non_terminal_node(type,children) method" do
|
117
|
+
result = CreateNonTerminalNodeTest.parse("one")
|
118
|
+
result.should be_kind_of(Array)
|
119
|
+
result.first.should == :one
|
120
|
+
result.last.should == "one"
|
121
|
+
end
|
122
|
+
|
123
|
+
# it "if a class is passed to the node method then a class of that type is created as the non-terminal. Its initializer must take an array of children as its argument" do
|
124
|
+
# CustomNodeClassNonTerminalNodeTest.parse("one").class.should == CustomNode
|
125
|
+
# end
|
126
|
+
#
|
127
|
+
# it "if one or more modules are passed to the node method then they are used to extend the non-terminal array" do
|
128
|
+
# CustomNodeModuleNonTerminalNodeTest.parse("one").should be_kind_of(Array)
|
129
|
+
# CustomNodeModuleNonTerminalNodeTest.parse("one").should be_kind_of(CustomNodeA)
|
130
|
+
# CustomNodeModuleNonTerminalNodeTest.parse("one").should be_kind_of(CustomNodeB)
|
131
|
+
# end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
class BasketPeg < RubyPeg
|
136
|
+
def root
|
137
|
+
node :basket do
|
138
|
+
one_or_more { items }
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def items
|
143
|
+
node :item do
|
144
|
+
number && optional_space && fruit && optional_space
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def number
|
149
|
+
terminal(/\d+/)
|
150
|
+
end
|
151
|
+
|
152
|
+
def fruit
|
153
|
+
node :fruit do
|
154
|
+
(terminal("apple") || terminal("pear")) && ignore{ optional{ terminal("s") } }
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def optional_space
|
159
|
+
ignore{ optional{ terminal(" ") }}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
describe BasketPeg do
|
164
|
+
|
165
|
+
it "Illustrates NonTerminalNode" do
|
166
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").should be_kind_of(NonTerminalNode)
|
167
|
+
end
|
168
|
+
|
169
|
+
it "Illustrates NonTerminalNode#type" do
|
170
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").type.should == :basket
|
171
|
+
end
|
172
|
+
|
173
|
+
it "Illustrates NonTerminalNode#to_ast" do
|
174
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").to_ast.should == [:basket, [:item, "1", [:fruit, "apple"]], [:item, "2", [:fruit, "apple"]], [:item, "3", [:fruit, "pear"]]]
|
175
|
+
end
|
176
|
+
|
177
|
+
it "Illustrates NonTerminalNode#to_s" do
|
178
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").to_s.should == "1apple2apple3pear"
|
179
|
+
end
|
180
|
+
|
181
|
+
it "Illustrates NonTerminalNode#inspect" do
|
182
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").inspect.should == '[:basket, [:item, "1", [:fruit, "apple"]], [:item, "2", [:fruit, "apple"]], [:item, "3", [:fruit, "pear"]]]'
|
183
|
+
end
|
184
|
+
|
185
|
+
it "Illustrates NonTerminalNode#build" do
|
186
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").visit.should == [["1", "apple"], ["2", "apple"], ["3", "pear"]]
|
187
|
+
class BasketPegBuilderExample
|
188
|
+
attr_accessor :total
|
189
|
+
|
190
|
+
def initialize
|
191
|
+
@total = 0
|
192
|
+
end
|
193
|
+
|
194
|
+
def item(number,kind)
|
195
|
+
@total = @total + (number.to_f * kind.visit(self).to_f)
|
196
|
+
end
|
197
|
+
|
198
|
+
def fruit(kind_of_fruit)
|
199
|
+
case kind_of_fruit
|
200
|
+
when "apple"; 3.0
|
201
|
+
when "pear"; 1.0
|
202
|
+
else 10.0
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
counter = BasketPegBuilderExample.new
|
207
|
+
BasketPeg.parse("1 apple 2 apples 3 pears").visit(counter)
|
208
|
+
counter.total.should == 12.0
|
209
|
+
end
|
210
|
+
|
211
|
+
it "Illustrates NonTerminalNode#children" do
|
212
|
+
basket = BasketPeg.parse("1 apple 2 apples 3 pears")
|
213
|
+
basket.class.should == Array
|
214
|
+
basket.size.should == 3
|
215
|
+
basket.first.should be_kind_of(NonTerminalNode)
|
216
|
+
basket.first.type.should == :item
|
217
|
+
basket.first.class.should == Array
|
218
|
+
basket.first.size.should == 2
|
219
|
+
basket.first.first.should be_kind_of(TerminalNode)
|
220
|
+
basket.first.first.should == "1"
|
221
|
+
basket.first.last.should be_kind_of(NonTerminalNode)
|
222
|
+
basket.first.last.type == :fruit
|
223
|
+
basket.first.last.class.should == Array
|
224
|
+
basket.first.last.size.should == 1
|
225
|
+
basket.first.last.first.should be_kind_of(TerminalNode)
|
226
|
+
basket.first.last.first.should == "apple"
|
227
|
+
end
|
228
|
+
|
229
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'rubypeg'
|
3
|
+
|
4
|
+
class TestLeg1 < RubyPeg
|
5
|
+
def root
|
6
|
+
node :grammar do
|
7
|
+
optional_indent && grammar_keyword && space && grammar_name && newline && ignore {one_or_more { terminal('end') || terminal('END')}}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def optional_indent
|
12
|
+
ignore { terminal(/\s*/) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def grammar_keyword
|
16
|
+
ignore { terminal('grammar') }
|
17
|
+
end
|
18
|
+
|
19
|
+
def space
|
20
|
+
ignore { terminal(/ +/) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def newline
|
24
|
+
ignore { terminal(/\n+\s*/m) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def grammar_name
|
28
|
+
terminal(/[a-zA-Z]+/)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
describe TestLeg1 do
|
34
|
+
|
35
|
+
it "works with simple sequence" do
|
36
|
+
grammar = <<-EOF
|
37
|
+
grammar TestGrammar
|
38
|
+
end
|
39
|
+
EOF
|
40
|
+
TestLeg1.parse(grammar).to_ast.should == [:grammar, "TestGrammar"]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "works with simple alternatives" do
|
44
|
+
grammar = <<-EOF
|
45
|
+
grammar TestGrammar
|
46
|
+
END
|
47
|
+
EOF
|
48
|
+
TestLeg1.parse(grammar).to_ast.should == [:grammar, "TestGrammar"]
|
49
|
+
end
|
50
|
+
|
51
|
+
it "works with one or more" do
|
52
|
+
grammar = <<-EOF
|
53
|
+
grammar TestGrammar
|
54
|
+
endEND
|
55
|
+
EOF
|
56
|
+
TestLeg1.parse(grammar).to_ast.should == [:grammar, "TestGrammar"]
|
57
|
+
grammar = <<-EOF
|
58
|
+
grammar TestGrammar
|
59
|
+
|
60
|
+
EOF
|
61
|
+
TestLeg1.parse(grammar).should == nil
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'rubypeg'
|
3
|
+
|
4
|
+
class OneOrMore < RubyPeg
|
5
|
+
def root
|
6
|
+
node :root do
|
7
|
+
one_or_more { terminal('x') }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe OneOrMore do
|
13
|
+
|
14
|
+
it "matches one" do
|
15
|
+
OneOrMore.parse("x.").to_ast.should == [:root,'x']
|
16
|
+
end
|
17
|
+
|
18
|
+
it "matches more than one" do
|
19
|
+
OneOrMore.parse("xx.").to_ast.should == [:root,'x','x']
|
20
|
+
end
|
21
|
+
|
22
|
+
it "doesn't match none" do
|
23
|
+
OneOrMore.parse(".").should == nil
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
class ZeroOrMore < RubyPeg
|
29
|
+
def root
|
30
|
+
node :root do
|
31
|
+
any_number_of { terminal('x') }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ZeroOrMore do
|
37
|
+
|
38
|
+
it "matches one" do
|
39
|
+
ZeroOrMore.parse("x.").to_ast.should == [:root,'x']
|
40
|
+
end
|
41
|
+
|
42
|
+
it "matches more than one" do
|
43
|
+
ZeroOrMore.parse("xx.").to_ast.should == [:root,'x','x']
|
44
|
+
end
|
45
|
+
|
46
|
+
it "matches none" do
|
47
|
+
ZeroOrMore.parse(".").to_ast.should == [:root]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
class ZeroOrOne < RubyPeg
|
53
|
+
def root
|
54
|
+
node :root do
|
55
|
+
optional { terminal('x') }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe ZeroOrOne do
|
61
|
+
|
62
|
+
it "matches one" do
|
63
|
+
ZeroOrOne.parse("x.").to_ast.should == [:root,'x']
|
64
|
+
end
|
65
|
+
|
66
|
+
it "doesn't match more than one" do
|
67
|
+
ZeroOrOne.parse("xx.").to_ast.should == [:root,'x']
|
68
|
+
end
|
69
|
+
|
70
|
+
it "matches none" do
|
71
|
+
ZeroOrOne.parse(".").to_ast.should == [:root]
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'rubypeg'
|
3
|
+
|
4
|
+
class SimpleSequence < RubyPeg
|
5
|
+
def root
|
6
|
+
node :root do
|
7
|
+
terminal('x') && terminal('y') && terminal('z')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe SimpleSequence do
|
13
|
+
|
14
|
+
it "matches the sequence" do
|
15
|
+
SimpleSequence.parse("xyz").to_ast.should == [:root,'x','y','z']
|
16
|
+
end
|
17
|
+
|
18
|
+
it "doesn't matches anything else" do
|
19
|
+
SimpleSequence.parse("xy.").should == nil
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class RepeatedSequence < RubyPeg
|
25
|
+
def root
|
26
|
+
node :root do
|
27
|
+
any_number_of { terminal('1') && ignore { terminal('2') } && terminal('3') } && one_or_more { terminal('x') && terminal('y') && terminal('z') } && terminal('z')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe RepeatedSequence do
|
33
|
+
|
34
|
+
it "matches the sequence" do
|
35
|
+
RepeatedSequence.parse("xyzz").to_ast.should == [:root,'x','y','z','z']
|
36
|
+
end
|
37
|
+
|
38
|
+
it "matches a repeated sequence" do
|
39
|
+
RepeatedSequence.parse("xyzxyzz").to_ast.should == [:root,'x','y','z','x','y','z','z']
|
40
|
+
end
|
41
|
+
|
42
|
+
it "matches an optional repeated sequence" do
|
43
|
+
RepeatedSequence.parse("123123xyzxyzz").to_ast.should == [:root,'1','3','1','3','x','y','z','x','y','z','z']
|
44
|
+
end
|
45
|
+
|
46
|
+
it "doesn't matches anything else" do
|
47
|
+
RepeatedSequence.parse("xyxy.").should == nil
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
2
|
+
require 'rubypeg'
|
3
|
+
|
4
|
+
class TerminalNodeTest < RubyPeg
|
5
|
+
def root
|
6
|
+
terminal "one"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
describe TerminalNodeTest do
|
11
|
+
|
12
|
+
def parse(text)
|
13
|
+
TerminalNodeTest.parse(text)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "terminals are, by default, Strings" do
|
17
|
+
parse("one").should == "one"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "they have been, by default, extended with the module TerminalNode" do
|
21
|
+
parse("one").should be_kind_of(TerminalNode)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "TerminalNode responds to to_ast by returning itself" do
|
25
|
+
parse("one").to_ast.should be_kind_of(String)
|
26
|
+
parse("one").to_ast.should == "one"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "TerminalNode responds to visit(builder) by returning itself" do
|
30
|
+
class TestBuilder; end
|
31
|
+
parse("one").visit(TestBuilder.new).should be_kind_of(String)
|
32
|
+
parse("one").visit(TestBuilder.new).should == "one"
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
class CreateTerminalNodeTest < RubyPeg
|
38
|
+
def root
|
39
|
+
terminal "one"
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_terminal_node(string)
|
43
|
+
/#{string}/
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe CreateTerminalNodeTest do
|
48
|
+
|
49
|
+
def parse(text)
|
50
|
+
CreateTerminalNodeTest.parse(text)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "the class of the terminal can be altered by overriding the create_terminal_node(string) method" do
|
54
|
+
parse("one").should be_kind_of(Regexp)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class StringTerminalTest < RubyPeg
|
60
|
+
def root
|
61
|
+
terminal "one"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe StringTerminalTest do
|
66
|
+
|
67
|
+
def parse(text)
|
68
|
+
StringTerminalTest.parse(text)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "if given a string, matches that string but nothing else" do
|
72
|
+
parse("one").to_ast.should == 'one'
|
73
|
+
parse("two").should == nil
|
74
|
+
parse("onetwo").to_ast.should == 'one'
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
class RegexpTerminalTest < RubyPeg
|
80
|
+
def root
|
81
|
+
terminal /one|Two/i
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe RegexpTerminalTest do
|
86
|
+
|
87
|
+
def parse(text)
|
88
|
+
RegexpTerminalTest.parse(text)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "if given a regular expression, matches that expression but nothing else" do
|
92
|
+
parse("one").to_ast.should == 'one'
|
93
|
+
parse("two").to_ast.should == 'two'
|
94
|
+
parse("onetwo").to_ast.should == 'one'
|
95
|
+
parse("three").should == nil
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class AnythingTerminalTest < RubyPeg
|
101
|
+
def root
|
102
|
+
terminal 1.0
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe AnythingTerminalTest do
|
107
|
+
|
108
|
+
def parse(text)
|
109
|
+
AnythingTerminalTest.parse(text)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "if given anything else, converts it to astring and tries to match that" do
|
113
|
+
parse("1.0").to_ast.should == '1.0'
|
114
|
+
parse("1").should == nil
|
115
|
+
parse("1.011").to_ast.should == '1.0'
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|