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.
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