pegarus 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.
- data/LICENSE +22 -0
- data/README +24 -0
- data/Rakefile +70 -0
- data/lib/pegarus/ast/always.rb +7 -0
- data/lib/pegarus/ast/any.rb +13 -0
- data/lib/pegarus/ast/any_range.rb +13 -0
- data/lib/pegarus/ast/character.rb +13 -0
- data/lib/pegarus/ast/character_range.rb +13 -0
- data/lib/pegarus/ast/choice.rb +7 -0
- data/lib/pegarus/ast/concatenation.rb +7 -0
- data/lib/pegarus/ast/difference.rb +7 -0
- data/lib/pegarus/ast/grammar.rb +56 -0
- data/lib/pegarus/ast/if.rb +9 -0
- data/lib/pegarus/ast/never.rb +7 -0
- data/lib/pegarus/ast/pattern.rb +127 -0
- data/lib/pegarus/ast/product.rb +12 -0
- data/lib/pegarus/ast/set.rb +13 -0
- data/lib/pegarus/ast/unless.rb +9 -0
- data/lib/pegarus/ast/variable.rb +15 -0
- data/lib/pegarus/ast.rb +16 -0
- data/lib/pegarus/compatibility.rb +25 -0
- data/lib/pegarus/evaluator.rb +93 -0
- data/lib/pegarus/machine/compiler.rb +18 -0
- data/lib/pegarus/machine/generator.rb +8 -0
- data/lib/pegarus/machine/instructions.rb +65 -0
- data/lib/pegarus/machine/interpreter.rb +31 -0
- data/lib/pegarus/machine/state.rb +29 -0
- data/lib/pegarus/machine.rb +15 -0
- data/lib/pegarus/parser/parse_error.rb +3 -0
- data/lib/pegarus/parser.rb +1 -0
- data/lib/pegarus/rubinius/compiler.rb +220 -0
- data/lib/pegarus/rubinius/generator.rb +48 -0
- data/lib/pegarus/rubinius.rb +4 -0
- data/lib/pegarus/version.rb +3 -0
- data/lib/pegarus.rb +3 -0
- data/spec/ast/choice_spec.rb +9 -0
- data/spec/ast/concatenation_spec.rb +9 -0
- data/spec/ast/difference_spec.rb +9 -0
- data/spec/ast/grammar_spec.rb +54 -0
- data/spec/ast/if_spec.rb +8 -0
- data/spec/ast/pattern_spec.rb +123 -0
- data/spec/ast/product_spec.rb +28 -0
- data/spec/ast/unless_spec.rb +8 -0
- data/spec/ast/variable_spec.rb +4 -0
- data/spec/custom/guards/engine.rb +27 -0
- data/spec/machine/instructions/call_spec.rb +21 -0
- data/spec/machine/instructions/capture_spec.rb +15 -0
- data/spec/machine/instructions/char_spec.rb +34 -0
- data/spec/machine/instructions/choice_spec.rb +15 -0
- data/spec/machine/instructions/commit_spec.rb +21 -0
- data/spec/machine/instructions/fail_spec.rb +34 -0
- data/spec/machine/instructions/jump_spec.rb +15 -0
- data/spec/machine/instructions/return_spec.rb +16 -0
- data/spec/matching/evaluator/any_spec.rb +6 -0
- data/spec/matching/evaluator/character_spec.rb +6 -0
- data/spec/matching/evaluator/choice_spec.rb +6 -0
- data/spec/matching/evaluator/concatenation_spec.rb +6 -0
- data/spec/matching/evaluator/difference_spec.rb +6 -0
- data/spec/matching/evaluator/if_spec.rb +6 -0
- data/spec/matching/evaluator/product_spec.rb +6 -0
- data/spec/matching/evaluator/setup.rb +4 -0
- data/spec/matching/evaluator/unless_spec.rb +6 -0
- data/spec/matching/machine/any_spec.rb +6 -0
- data/spec/matching/machine/character_spec.rb +6 -0
- data/spec/matching/machine/choice_spec.rb +6 -0
- data/spec/matching/machine/concatenation_spec.rb +6 -0
- data/spec/matching/machine/difference_spec.rb +6 -0
- data/spec/matching/machine/if_spec.rb +6 -0
- data/spec/matching/machine/product_spec.rb +6 -0
- data/spec/matching/machine/setup.rb +4 -0
- data/spec/matching/machine/unless_spec.rb +6 -0
- data/spec/matching/rubinius/any_spec.rb +8 -0
- data/spec/matching/rubinius/character_spec.rb +8 -0
- data/spec/matching/rubinius/choice_spec.rb +8 -0
- data/spec/matching/rubinius/concatenation_spec.rb +8 -0
- data/spec/matching/rubinius/difference_spec.rb +8 -0
- data/spec/matching/rubinius/if_spec.rb +8 -0
- data/spec/matching/rubinius/product_spec.rb +8 -0
- data/spec/matching/rubinius/setup.rb +4 -0
- data/spec/matching/rubinius/unless_spec.rb +8 -0
- data/spec/matching/shared/any.rb +13 -0
- data/spec/matching/shared/character.rb +17 -0
- data/spec/matching/shared/choice.rb +16 -0
- data/spec/matching/shared/concatenation.rb +16 -0
- data/spec/matching/shared/difference.rb +2 -0
- data/spec/matching/shared/if.rb +16 -0
- data/spec/matching/shared/product.rb +2 -0
- data/spec/matching/shared/unless.rb +16 -0
- data/spec/spec_helper.rb +12 -0
- metadata +166 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
module Pegarus
|
2
|
+
module Machine
|
3
|
+
module Instructions
|
4
|
+
def self.[](opcode)
|
5
|
+
instructions[opcode]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.instructions
|
9
|
+
@instructions ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.instruction(name, &block)
|
13
|
+
instructions[name] = [block.arity, block]
|
14
|
+
end
|
15
|
+
|
16
|
+
instruction :char do |state, char|
|
17
|
+
if state.subject[state.index] == char
|
18
|
+
state.index += 1
|
19
|
+
else
|
20
|
+
state.failure
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
instruction :jump do |state, label|
|
25
|
+
state.ip = label
|
26
|
+
end
|
27
|
+
|
28
|
+
instruction :choice do |state, label|
|
29
|
+
state.stack << label << state.index << state.captures.dup
|
30
|
+
end
|
31
|
+
|
32
|
+
instruction :call do |state, label|
|
33
|
+
state.stack << state.ip + 2
|
34
|
+
state.ip = label
|
35
|
+
end
|
36
|
+
|
37
|
+
instruction :return do |state|
|
38
|
+
state.ip = state.stack.pop
|
39
|
+
end
|
40
|
+
|
41
|
+
instruction :commit do |state, label|
|
42
|
+
state.stack[-3..-1] = nil
|
43
|
+
state.ip = label
|
44
|
+
end
|
45
|
+
|
46
|
+
instruction :capture do |state, data|
|
47
|
+
state.captures << [state.index, data]
|
48
|
+
end
|
49
|
+
|
50
|
+
instruction :fail do |state|
|
51
|
+
until state.stack.empty?
|
52
|
+
item = state.stack.pop
|
53
|
+
next unless item.kind_of? Array
|
54
|
+
|
55
|
+
state.captures.replace item
|
56
|
+
state.index = state.stack.pop
|
57
|
+
state.ip = state.stack.pop
|
58
|
+
|
59
|
+
state.continue
|
60
|
+
break
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Pegarus
|
2
|
+
module Machine
|
3
|
+
def self.execute(program, subject)
|
4
|
+
state = State.new subject
|
5
|
+
_, fail = Instructions[:fail]
|
6
|
+
|
7
|
+
total = program.size
|
8
|
+
while state.ip < total
|
9
|
+
break if state.fail?
|
10
|
+
|
11
|
+
width, code = Instructions[program[state.ip]]
|
12
|
+
case width
|
13
|
+
when 1
|
14
|
+
code[state]
|
15
|
+
when 2
|
16
|
+
code[state, program[state.ip+1]]
|
17
|
+
when 3
|
18
|
+
code[state, program[state.ip+1], program[state.ip+2]]
|
19
|
+
end
|
20
|
+
|
21
|
+
if state.fail?
|
22
|
+
fail[state]
|
23
|
+
else
|
24
|
+
state.ip += width
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
return state.failure? ? nil : state.index
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Pegarus
|
2
|
+
module Machine
|
3
|
+
class State
|
4
|
+
attr_accessor :ip, :index
|
5
|
+
attr_reader :subject, :stack, :captures
|
6
|
+
|
7
|
+
def initialize(subject)
|
8
|
+
@subject = subject
|
9
|
+
@ip = 0
|
10
|
+
@index = 0
|
11
|
+
@stack = []
|
12
|
+
@captures = []
|
13
|
+
@failure = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def failure?
|
17
|
+
@failure
|
18
|
+
end
|
19
|
+
|
20
|
+
def failure
|
21
|
+
@failure = true
|
22
|
+
end
|
23
|
+
|
24
|
+
def continue
|
25
|
+
@failure = false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'pegarus/machine/instructions'
|
2
|
+
require 'pegarus/machine/compiler'
|
3
|
+
require 'pegarus/machine/state'
|
4
|
+
require 'pegarus/machine/interpreter'
|
5
|
+
|
6
|
+
module Pegarus
|
7
|
+
module Machine
|
8
|
+
def self.new_executor(pattern, subject)
|
9
|
+
Compiler.new.compile pattern
|
10
|
+
pattern.match subject
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
Pegarus::Pattern.select_engine Pegarus::Machine
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'pegarus/parser/parse_error'
|
@@ -0,0 +1,220 @@
|
|
1
|
+
module Pegarus
|
2
|
+
module Rubinius
|
3
|
+
def self.new_executor(pattern, subject)
|
4
|
+
Compiler.new.compile pattern
|
5
|
+
pattern.match subject
|
6
|
+
end
|
7
|
+
|
8
|
+
class Compiler
|
9
|
+
attr_reader :g
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@g = Generator.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def compile(pattern)
|
16
|
+
g.name = :match
|
17
|
+
g.file = :"(pegarus)"
|
18
|
+
g.set_line 1
|
19
|
+
|
20
|
+
g.required_args = 1
|
21
|
+
g.total_args = 1
|
22
|
+
g.splat_index = nil
|
23
|
+
|
24
|
+
g.local_count = 2
|
25
|
+
g.local_names = [:subject, :index]
|
26
|
+
|
27
|
+
g.push 0
|
28
|
+
g.set_index
|
29
|
+
|
30
|
+
pattern.visit self
|
31
|
+
g.finish
|
32
|
+
|
33
|
+
g.encode
|
34
|
+
cm = g.package ::Rubinius::CompiledMethod
|
35
|
+
puts cm.decode if $DEBUG
|
36
|
+
|
37
|
+
ss = ::Rubinius::StaticScope.new Object
|
38
|
+
::Rubinius.attach_method :match, cm, ss, pattern
|
39
|
+
end
|
40
|
+
|
41
|
+
def failure
|
42
|
+
g.push :nil
|
43
|
+
g.goto g.fail
|
44
|
+
end
|
45
|
+
|
46
|
+
def always(pattern)
|
47
|
+
failure
|
48
|
+
end
|
49
|
+
|
50
|
+
def any(pattern)
|
51
|
+
failure
|
52
|
+
end
|
53
|
+
|
54
|
+
def any_range(pattern)
|
55
|
+
failure
|
56
|
+
end
|
57
|
+
|
58
|
+
def character(pattern)
|
59
|
+
failure
|
60
|
+
end
|
61
|
+
|
62
|
+
def character_range(pattern)
|
63
|
+
failure
|
64
|
+
end
|
65
|
+
|
66
|
+
def choice(pattern)
|
67
|
+
failure
|
68
|
+
end
|
69
|
+
|
70
|
+
def concatenation(pattern)
|
71
|
+
failure
|
72
|
+
end
|
73
|
+
|
74
|
+
def difference(pattern)
|
75
|
+
failure
|
76
|
+
end
|
77
|
+
|
78
|
+
def if(pattern)
|
79
|
+
failure
|
80
|
+
end
|
81
|
+
|
82
|
+
def never(pattern)
|
83
|
+
failure
|
84
|
+
end
|
85
|
+
|
86
|
+
def set(pattern)
|
87
|
+
failure
|
88
|
+
end
|
89
|
+
|
90
|
+
def unless(pattern)
|
91
|
+
failure
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def any(g, count)
|
96
|
+
g.push_index
|
97
|
+
g.push count
|
98
|
+
g.send :+, 1, false
|
99
|
+
|
100
|
+
g.push_subject
|
101
|
+
g.send :size, 0, false
|
102
|
+
g.send :<=, 1, false
|
103
|
+
g.gif g.fail
|
104
|
+
|
105
|
+
g.push count
|
106
|
+
g.inc_index
|
107
|
+
end
|
108
|
+
|
109
|
+
def char(g, string)
|
110
|
+
fail = g.new_label
|
111
|
+
done = g.new_label
|
112
|
+
|
113
|
+
g.push_literal string
|
114
|
+
g.dup
|
115
|
+
g.send :size, 0, false
|
116
|
+
g.dup
|
117
|
+
g.move_down 2
|
118
|
+
|
119
|
+
g.push_subject
|
120
|
+
g.swap
|
121
|
+
g.push_index
|
122
|
+
g.swap
|
123
|
+
g.send :substring, 2, false
|
124
|
+
g.send :==, 1, false
|
125
|
+
g.gif fail
|
126
|
+
|
127
|
+
g.inc_index
|
128
|
+
g.goto done
|
129
|
+
|
130
|
+
fail.set!
|
131
|
+
g.pop
|
132
|
+
g.goto g.fail
|
133
|
+
|
134
|
+
done.set!
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class Any
|
139
|
+
def bytecode(g)
|
140
|
+
any g, @count
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class Character
|
145
|
+
def bytecode(g)
|
146
|
+
char g, @string
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class Choice
|
151
|
+
def bytecode(g)
|
152
|
+
g.push_index # store index, so that we can restart
|
153
|
+
old_fail = g.fail
|
154
|
+
done = g.new_label
|
155
|
+
|
156
|
+
choice_fail = g.new_label
|
157
|
+
g.fail = choice_fail
|
158
|
+
@first.bytecode(g)
|
159
|
+
g.pop # success, pop index
|
160
|
+
g.fail = old_fail # reset fail
|
161
|
+
g.goto done
|
162
|
+
|
163
|
+
choice_fail.set!
|
164
|
+
g.fail = old_fail
|
165
|
+
g.set_index # reset index to the stored index, this pops the value
|
166
|
+
@second.bytecode(g)
|
167
|
+
|
168
|
+
done.set!
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
class Concatenation
|
173
|
+
def bytecode(g)
|
174
|
+
@first.bytecode(g)
|
175
|
+
@second.bytecode(g)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
class If
|
180
|
+
def bytecode(g)
|
181
|
+
g.push_index # store index, so that we can restart
|
182
|
+
old_fail = g.fail
|
183
|
+
done = g.new_label
|
184
|
+
|
185
|
+
pattern_fail = g.new_label
|
186
|
+
g.fail = pattern_fail
|
187
|
+
@pattern.bytecode(g)
|
188
|
+
# success
|
189
|
+
g.set_index # reset index to the stored index, this pops the value
|
190
|
+
g.fail = old_fail # reset fail
|
191
|
+
g.goto done
|
192
|
+
# failure
|
193
|
+
pattern_fail.set!
|
194
|
+
g.set_index # reset index to the stored index, this pops the value
|
195
|
+
g.fail = old_fail
|
196
|
+
g.goto g.fail
|
197
|
+
|
198
|
+
done.set!
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class Unless
|
203
|
+
def bytecode(g)
|
204
|
+
g.push_index # store index, so that we can restart
|
205
|
+
old_fail = g.fail
|
206
|
+
|
207
|
+
pattern_fail = g.new_label
|
208
|
+
g.fail = pattern_fail
|
209
|
+
@pattern.bytecode(g)
|
210
|
+
# success => failure
|
211
|
+
g.set_index # reset index to the stored index, this pops the value
|
212
|
+
g.fail = old_fail # reset fail
|
213
|
+
g.goto g.fail
|
214
|
+
# failure => success
|
215
|
+
pattern_fail.set!
|
216
|
+
g.set_index # reset index to the stored index, this pops the value
|
217
|
+
g.fail = old_fail
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Pegarus
|
2
|
+
module Rubinius
|
3
|
+
class Generator < ::Rubinius::Generator
|
4
|
+
attr_reader :done
|
5
|
+
attr_accessor :fail
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
super
|
9
|
+
@fail = new_label
|
10
|
+
@done = new_label
|
11
|
+
end
|
12
|
+
|
13
|
+
def push_subject
|
14
|
+
push_local 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def push_index
|
18
|
+
push_local 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_index
|
22
|
+
set_local 1
|
23
|
+
pop
|
24
|
+
end
|
25
|
+
|
26
|
+
def inc_index
|
27
|
+
push_local 1
|
28
|
+
send :+, 1, false
|
29
|
+
set_local 1
|
30
|
+
pop
|
31
|
+
end
|
32
|
+
|
33
|
+
def finish
|
34
|
+
# end
|
35
|
+
push_index
|
36
|
+
goto done
|
37
|
+
|
38
|
+
# fail
|
39
|
+
fail.set!
|
40
|
+
push :nil
|
41
|
+
|
42
|
+
done.set!
|
43
|
+
ret
|
44
|
+
close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/pegarus.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "Choice.new" do
|
4
|
+
it "converts both inputs to patterns" do
|
5
|
+
pattern = Pegarus::Choice.new 1, "a"
|
6
|
+
pattern.first.should be_an_instance_of(Pegarus::Any)
|
7
|
+
pattern.second.should be_an_instance_of(Pegarus::Character)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "Concatenation.new" do
|
4
|
+
it "converts both inputs to patterns" do
|
5
|
+
pattern = Pegarus::Concatenation.new 1, "a"
|
6
|
+
pattern.first.should be_an_instance_of(Pegarus::Any)
|
7
|
+
pattern.second.should be_an_instance_of(Pegarus::Character)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "Difference.new" do
|
4
|
+
it "converts the input to a pattern" do
|
5
|
+
pattern = Pegarus::Difference.new 1, "a"
|
6
|
+
pattern.first.should be_an_instance_of(Pegarus::Any)
|
7
|
+
pattern.second.should be_an_instance_of(Pegarus::Character)
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "Pegarus.grammar" do
|
4
|
+
it "returns a Grammar with empty start rule when passed no arguments" do
|
5
|
+
grammar = Pegarus.grammar
|
6
|
+
|
7
|
+
grammar.should be_an_instance_of(Pegarus::Grammar)
|
8
|
+
grammar.start.should be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
it "returns a Grammar with start rule :S corresponding to the Pattern passed" do
|
12
|
+
pattern = Pegarus.pattern 1
|
13
|
+
grammar = Pegarus.grammar pattern
|
14
|
+
|
15
|
+
grammar.should be_an_instance_of(Pegarus::Grammar)
|
16
|
+
grammar.start.should == :S
|
17
|
+
grammar.S.pattern.should equal(pattern)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns a Grammar with start symbol corresponding to the Symbol passed" do
|
21
|
+
grammar = Pegarus.grammar :token
|
22
|
+
|
23
|
+
grammar.should be_an_instance_of(Pegarus::Grammar)
|
24
|
+
grammar.start.should == :token
|
25
|
+
grammar.token.pattern.should be_nil
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns a Grammar with start rule corresponding to the Symbol and Pattern passed" do
|
29
|
+
pattern = Pegarus.pattern 1
|
30
|
+
grammar = Pegarus.grammar :token, pattern
|
31
|
+
|
32
|
+
grammar.should be_an_instance_of(Pegarus::Grammar)
|
33
|
+
grammar.start.should == :token
|
34
|
+
grammar.token.pattern.should equal(pattern)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "returns a Grammar with start rule corresponding to the Variable passed" do
|
38
|
+
variable = Pegarus.variable :token, "abc"
|
39
|
+
grammar = Pegarus.grammar variable
|
40
|
+
|
41
|
+
grammar.should be_an_instance_of(Pegarus::Grammar)
|
42
|
+
grammar.start.should == :token
|
43
|
+
grammar.token.pattern.should equal(variable.pattern)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "Pegarus::Grammar" do
|
48
|
+
it "returns a Variable instance with the name of the method invoked" do
|
49
|
+
grammar = Pegarus.grammar
|
50
|
+
variable = grammar.pattern
|
51
|
+
variable.name.should == :pattern
|
52
|
+
variable.pattern.should be_nil
|
53
|
+
end
|
54
|
+
end
|
data/spec/ast/if_spec.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "Pegarus.pattern" do
|
4
|
+
it "returns an Any pattern when passed an Integer" do
|
5
|
+
Pegarus.pattern(5).should be_an_instance_of(Pegarus::Any)
|
6
|
+
end
|
7
|
+
|
8
|
+
it "returns a Set pattern when passed an Array" do
|
9
|
+
Pegarus.pattern(["abc"]).should be_an_instance_of(Pegarus::Set)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "retuns an Always pattern when passed true" do
|
13
|
+
Pegarus.pattern(true).should be_an_instance_of(Pegarus::Always)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns a Never pattern when passed false" do
|
17
|
+
Pegarus.pattern(false).should be_an_instance_of(Pegarus::Never)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns an Exact pattern when passed a String" do
|
21
|
+
Pegarus.pattern("abc").should be_an_instance_of(Pegarus::Character)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns a AnyRange pattern when passed a Range of Integers" do
|
25
|
+
Pegarus.pattern(1..2).should be_an_instance_of(Pegarus::AnyRange)
|
26
|
+
Pegarus.pattern(1...2).should be_an_instance_of(Pegarus::AnyRange)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns a CharRange pattern when passed a Range of Strings" do
|
30
|
+
Pegarus.pattern("a".."z").should be_an_instance_of(Pegarus::CharacterRange)
|
31
|
+
Pegarus.pattern("a"..."z").should be_an_instance_of(Pegarus::CharacterRange)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns the instance unmodified when passed a Pattern" do
|
35
|
+
pattern = Pegarus::Pattern.new
|
36
|
+
Pegarus.pattern(pattern).should equal(pattern)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns a Variable when passed a Symbol" do
|
40
|
+
Pegarus.pattern(:S).should be_an_instance_of(Pegarus::Variable)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns nil when passed nil" do
|
44
|
+
Pegarus.pattern(nil).should be_nil
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises a Pegarus::ParseError when passed a Object" do
|
48
|
+
lambda { Pegarus.pattern(Object.new) }.should raise_error(Pegarus::ParseError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises a Pegarus::ParseError when passed a Hash" do
|
52
|
+
lambda { Pegarus.pattern({}) }.should raise_error(Pegarus::ParseError)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "raises a Pegarus::ParseError when passed a Float" do
|
56
|
+
lambda { Pegarus.pattern(4.2) }.should raise_error(Pegarus::ParseError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "Pattern#+@" do
|
61
|
+
it "returns an If pattern" do
|
62
|
+
p1 = Pegarus.pattern(1)
|
63
|
+
pattern = +p1
|
64
|
+
|
65
|
+
pattern.should be_an_instance_of(Pegarus::If)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "Pattern#-@" do
|
70
|
+
it "returns an Unless pattern" do
|
71
|
+
p1 = Pegarus.pattern(1)
|
72
|
+
pattern = -p1
|
73
|
+
|
74
|
+
pattern.should be_an_instance_of(Pegarus::Unless)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe "Pattern#/" do
|
79
|
+
it "returns a Choice of two patterns" do
|
80
|
+
p1 = Pegarus.pattern(1)
|
81
|
+
p2 = Pegarus.pattern("a")
|
82
|
+
pattern = p1 / p2
|
83
|
+
|
84
|
+
pattern.should be_an_instance_of(Pegarus::Choice)
|
85
|
+
pattern.first.should equal(p1)
|
86
|
+
pattern.second.should equal(p2)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "Pattern#+" do
|
91
|
+
it "returns a Concatenation of two patterns" do
|
92
|
+
p1 = Pegarus.pattern(1)
|
93
|
+
p2 = Pegarus.pattern("a")
|
94
|
+
pattern = p1 + p2
|
95
|
+
|
96
|
+
pattern.should be_an_instance_of(Pegarus::Concatenation)
|
97
|
+
pattern.first.should equal(p1)
|
98
|
+
pattern.second.should equal(p2)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "Pegarus#-" do
|
103
|
+
it "returns a Difference of two patterns" do
|
104
|
+
p1 = Pegarus.pattern(1)
|
105
|
+
p2 = Pegarus.pattern("a")
|
106
|
+
pattern = p1 - p2
|
107
|
+
|
108
|
+
pattern.should be_an_instance_of(Pegarus::Difference)
|
109
|
+
pattern.first.should equal(p1)
|
110
|
+
pattern.second.should equal(p2)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "Pegarus#*" do
|
115
|
+
it "returns a Product of two patterns" do
|
116
|
+
p1 = Pegarus.pattern("a")
|
117
|
+
pattern = p1 * 2
|
118
|
+
|
119
|
+
pattern.should be_an_instance_of(Pegarus::Product)
|
120
|
+
pattern.first.should equal(p1)
|
121
|
+
pattern.second.should == 2
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
describe "Product.new" do
|
4
|
+
it "converts the first input to a pattern" do
|
5
|
+
pattern = Pegarus::Product.new "a", 1
|
6
|
+
pattern.first.should be_an_instance_of(Pegarus::Character)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "calls #to_int to convert the second input" do
|
10
|
+
obj = mock("multiplier")
|
11
|
+
obj.should_receive(:to_int).and_return(2)
|
12
|
+
|
13
|
+
pattern = Pegarus::Product.new "a", obj
|
14
|
+
pattern.second.should == 2
|
15
|
+
end
|
16
|
+
|
17
|
+
it "raises a TypeError if the second input is nil" do
|
18
|
+
lambda { Pegarus::Product.new "a", nil }.should raise_error(TypeError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "raises a TypeError if the second input is a String" do
|
22
|
+
lambda { Pegarus::Product.new "a", "b" }.should raise_error(TypeError)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "raises a TypeError if the second input is an Object" do
|
26
|
+
lambda { Pegarus::Product.new "a", Object.new }.should raise_error(TypeError)
|
27
|
+
end
|
28
|
+
end
|