pegarus 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. data/LICENSE +22 -0
  2. data/README +24 -0
  3. data/Rakefile +70 -0
  4. data/lib/pegarus/ast/always.rb +7 -0
  5. data/lib/pegarus/ast/any.rb +13 -0
  6. data/lib/pegarus/ast/any_range.rb +13 -0
  7. data/lib/pegarus/ast/character.rb +13 -0
  8. data/lib/pegarus/ast/character_range.rb +13 -0
  9. data/lib/pegarus/ast/choice.rb +7 -0
  10. data/lib/pegarus/ast/concatenation.rb +7 -0
  11. data/lib/pegarus/ast/difference.rb +7 -0
  12. data/lib/pegarus/ast/grammar.rb +56 -0
  13. data/lib/pegarus/ast/if.rb +9 -0
  14. data/lib/pegarus/ast/never.rb +7 -0
  15. data/lib/pegarus/ast/pattern.rb +127 -0
  16. data/lib/pegarus/ast/product.rb +12 -0
  17. data/lib/pegarus/ast/set.rb +13 -0
  18. data/lib/pegarus/ast/unless.rb +9 -0
  19. data/lib/pegarus/ast/variable.rb +15 -0
  20. data/lib/pegarus/ast.rb +16 -0
  21. data/lib/pegarus/compatibility.rb +25 -0
  22. data/lib/pegarus/evaluator.rb +93 -0
  23. data/lib/pegarus/machine/compiler.rb +18 -0
  24. data/lib/pegarus/machine/generator.rb +8 -0
  25. data/lib/pegarus/machine/instructions.rb +65 -0
  26. data/lib/pegarus/machine/interpreter.rb +31 -0
  27. data/lib/pegarus/machine/state.rb +29 -0
  28. data/lib/pegarus/machine.rb +15 -0
  29. data/lib/pegarus/parser/parse_error.rb +3 -0
  30. data/lib/pegarus/parser.rb +1 -0
  31. data/lib/pegarus/rubinius/compiler.rb +220 -0
  32. data/lib/pegarus/rubinius/generator.rb +48 -0
  33. data/lib/pegarus/rubinius.rb +4 -0
  34. data/lib/pegarus/version.rb +3 -0
  35. data/lib/pegarus.rb +3 -0
  36. data/spec/ast/choice_spec.rb +9 -0
  37. data/spec/ast/concatenation_spec.rb +9 -0
  38. data/spec/ast/difference_spec.rb +9 -0
  39. data/spec/ast/grammar_spec.rb +54 -0
  40. data/spec/ast/if_spec.rb +8 -0
  41. data/spec/ast/pattern_spec.rb +123 -0
  42. data/spec/ast/product_spec.rb +28 -0
  43. data/spec/ast/unless_spec.rb +8 -0
  44. data/spec/ast/variable_spec.rb +4 -0
  45. data/spec/custom/guards/engine.rb +27 -0
  46. data/spec/machine/instructions/call_spec.rb +21 -0
  47. data/spec/machine/instructions/capture_spec.rb +15 -0
  48. data/spec/machine/instructions/char_spec.rb +34 -0
  49. data/spec/machine/instructions/choice_spec.rb +15 -0
  50. data/spec/machine/instructions/commit_spec.rb +21 -0
  51. data/spec/machine/instructions/fail_spec.rb +34 -0
  52. data/spec/machine/instructions/jump_spec.rb +15 -0
  53. data/spec/machine/instructions/return_spec.rb +16 -0
  54. data/spec/matching/evaluator/any_spec.rb +6 -0
  55. data/spec/matching/evaluator/character_spec.rb +6 -0
  56. data/spec/matching/evaluator/choice_spec.rb +6 -0
  57. data/spec/matching/evaluator/concatenation_spec.rb +6 -0
  58. data/spec/matching/evaluator/difference_spec.rb +6 -0
  59. data/spec/matching/evaluator/if_spec.rb +6 -0
  60. data/spec/matching/evaluator/product_spec.rb +6 -0
  61. data/spec/matching/evaluator/setup.rb +4 -0
  62. data/spec/matching/evaluator/unless_spec.rb +6 -0
  63. data/spec/matching/machine/any_spec.rb +6 -0
  64. data/spec/matching/machine/character_spec.rb +6 -0
  65. data/spec/matching/machine/choice_spec.rb +6 -0
  66. data/spec/matching/machine/concatenation_spec.rb +6 -0
  67. data/spec/matching/machine/difference_spec.rb +6 -0
  68. data/spec/matching/machine/if_spec.rb +6 -0
  69. data/spec/matching/machine/product_spec.rb +6 -0
  70. data/spec/matching/machine/setup.rb +4 -0
  71. data/spec/matching/machine/unless_spec.rb +6 -0
  72. data/spec/matching/rubinius/any_spec.rb +8 -0
  73. data/spec/matching/rubinius/character_spec.rb +8 -0
  74. data/spec/matching/rubinius/choice_spec.rb +8 -0
  75. data/spec/matching/rubinius/concatenation_spec.rb +8 -0
  76. data/spec/matching/rubinius/difference_spec.rb +8 -0
  77. data/spec/matching/rubinius/if_spec.rb +8 -0
  78. data/spec/matching/rubinius/product_spec.rb +8 -0
  79. data/spec/matching/rubinius/setup.rb +4 -0
  80. data/spec/matching/rubinius/unless_spec.rb +8 -0
  81. data/spec/matching/shared/any.rb +13 -0
  82. data/spec/matching/shared/character.rb +17 -0
  83. data/spec/matching/shared/choice.rb +16 -0
  84. data/spec/matching/shared/concatenation.rb +16 -0
  85. data/spec/matching/shared/difference.rb +2 -0
  86. data/spec/matching/shared/if.rb +16 -0
  87. data/spec/matching/shared/product.rb +2 -0
  88. data/spec/matching/shared/unless.rb +16 -0
  89. data/spec/spec_helper.rb +12 -0
  90. 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,3 @@
1
+ module Pegarus
2
+ class ParseError < Exception; end
3
+ end
@@ -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
@@ -0,0 +1,4 @@
1
+ require 'pegarus/rubinius/generator'
2
+ require 'pegarus/rubinius/compiler'
3
+
4
+ Pegarus::Pattern.select_engine Pegarus::Rubinius
@@ -0,0 +1,3 @@
1
+ module Pegarus
2
+ VERSION = "0.1.0"
3
+ end
data/lib/pegarus.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'pegarus/ast'
2
+ require 'pegarus/parser'
3
+ require 'pegarus/compatibility'
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "If.new" do
4
+ it "converts the input to a pattern" do
5
+ pattern = Pegarus::If.new "a"
6
+ pattern.pattern.should be_an_instance_of(Pegarus::Character)
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,8 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "Unless.new" do
4
+ it "converts the input to a pattern" do
5
+ pattern = Pegarus::Unless.new "a"
6
+ pattern.pattern.should be_an_instance_of(Pegarus::Character)
7
+ end
8
+ end
@@ -0,0 +1,4 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe "Pegarus.variable" do
4
+ end