rubypeg 0.0.2
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/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
|