whittle 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -91,9 +91,7 @@ be. We use `#as` to provide an action that actually does something meaningful w
91
91
  inputs.
92
92
 
93
93
  We can optionally use the Hash notation to map a name with a pattern (or a fixed string) when
94
- we declare terminal rules too, as we have done with the `:int` rule above. Note that the
95
- longer way around defining terminal rules is to do like we have done for `:expr` and define a
96
- block, but since this is such a common use-case, Whittle offers the shorthand.
94
+ we declare terminal rules too, as we have done with the `:int` rule above.
97
95
 
98
96
  As the input string is parsed, it *must* match the start rule `:expr`.
99
97
 
@@ -438,6 +436,17 @@ end
438
436
  The following would return the array `["a", "b", "c"]` given the input string "a, b, c", or
439
437
  given the input string "" (nothing) it would return the empty array.
440
438
 
439
+ ## You can use a different start rule on-demand
440
+
441
+ While this is not advised in production (requiring such a thing in production would suggest
442
+ you need to re-think your grammar), during development you may wish to specify any of your
443
+ smaller rules as the start rule for a parse. This is particularly useful in debugging, and
444
+ in writing unit tests.
445
+
446
+ ``` ruby
447
+ parser.parse(input_string, :rule => :something_else)
448
+ ```
449
+
441
450
  ## Parse errors
442
451
 
443
452
  ### The default error reporting
@@ -464,10 +473,23 @@ class ListParser < Whittle::Parser
464
473
  start(:list)
465
474
  end
466
475
 
467
- ListParser.new.parse("a, \nb, \nc- \nd")
476
+ str = <<-END
477
+ one, two, three, four, five,
478
+ six, seven, eight, nine, ten,
479
+ eleven, twelve, thirteen,
480
+ fourteen, fifteen - sixteen, seventeen
481
+ END
482
+
483
+ ListParser.new.parse(str)
468
484
 
469
485
  # =>
470
- # Parse error: expected "," but got "-" on line 3
486
+ # Parse error: expected "," but got "-" on line 4. (Whittle::ParseError)
487
+ #
488
+ # Exact region marked...
489
+ #
490
+ # fourteen, fifteen - sixteen, seventeen
491
+ # ... ^ ... right here
492
+ #
471
493
  ```
472
494
 
473
495
  You can also access `#line`, `#expected` and `#received` if you catch the exception.
@@ -500,6 +522,16 @@ rule("keyword")
500
522
  rule(:name => /pattern/)
501
523
  ```
502
524
 
525
+ ### Matching with case-insenstivity
526
+
527
+ You can use the Hash notation from above, with a String as the key, mapping to a Regexp.
528
+
529
+ ``` ruby
530
+ rule("function" => /function/i)
531
+ ```
532
+
533
+ Now in all rules that allow case-insensitive "function", just use the String `"function"`.
534
+
503
535
  ### Providing a semantic action for a terminal rule
504
536
 
505
537
  ``` ruby
@@ -580,14 +612,20 @@ rule("+") ^ 1
580
612
  rule("*") ^ 2
581
613
  ```
582
614
 
583
- ### I have two types of expression: binary and function call. How can I allow a binary expression in a function call argument, and a function call in a binary expression?
615
+ ### How do I make two expresions mutually reference each other?
616
+
617
+ Let's say you have two types of expression, `:binary_expr` (like "a + b") and `:invcation_expr` (like "foo(bar)").
618
+
619
+ What you're saying is that any argument in the invocation expression should support either another invocation, or
620
+ a `:binary_expr`. Likewise, you want any operand of `:binary_expr` to support either another `:binary_expr` or
621
+ an `:invoation_expr`.
584
622
 
585
623
  If you can explain it this simply on paper, you can explain it formally in your grammar. If `:binary_expr`
586
624
  allows `:invocation_expr` as an operand, and if `:invocation_expr` allows `:binary_expr` as an argument, then
587
625
  what you're saying is they can be used in place of each other; thus, define a rule that represents the two of them
588
626
  and use that new rule where you want to support both types of expression.
589
627
 
590
- Assuming your grammar looked something like this:
628
+ Assuming your grammar looked something like this pseudo example.
591
629
 
592
630
  ``` ruby
593
631
  rule("+")
@@ -648,6 +686,10 @@ Now we can parse the more complex expression "1 + foo(2, 3) + 4" without any iss
648
686
 
649
687
  ### How do I track state to store variables etc with Whittle?
650
688
 
689
+ In general you build an complete AST to be interpreted if you're writing a program, rather than interpret the input as
690
+ it is parsed (what would happen if something had written to disk and then a parse error occurred?). That said, in
691
+ simple cases it may be useful to simply interpret the input as it is read.
692
+
651
693
  One of the goals of making Whittle all ruby was that I wouldn't have to tie people into any particular way of doing
652
694
  something. Your blocks can call any ruby code they like, so create an object of some sort those blocks can reference
653
695
  and do as you need during the parse. For example, you could add a method to the class called something like `runtime`,
@@ -57,3 +57,13 @@ p calculator.parse("5 - -2").to_f
57
57
 
58
58
  p calculator.parse("5 * 2 - -2").to_f
59
59
  # => 12
60
+
61
+
62
+ p calculator.parse(
63
+ <<-END
64
+ 5 * 2 - -2
65
+ + 3 / 7 * 90
66
+ / 45 * 8 * 8 * 8 - 6 * (1 - 2 - 3 ) (45 / 5)) / (7 * 3)
67
+ - 6
68
+ END
69
+ ).to_f
@@ -0,0 +1,92 @@
1
+ # Whittle: A little LALR(1) parser in pure ruby, without a generator.
2
+ #
3
+ # Copyright (c) Chris Corbyn, 2011
4
+ module Whittle
5
+ # Since parse error diagram the region where the error occured,
6
+ # this logic is split out from the main Parser
7
+ class ParseErrorBuilder
8
+ class << self
9
+ # Generates a ParseError for the given set of error conditions
10
+ #
11
+ # A ParseError always specifies the line nunber, the expected inputs and
12
+ # the received input.
13
+ #
14
+ # If possible, it also draw a diagram indicating the point where the
15
+ # error occurred.
16
+ #
17
+ # @param [Hash] state
18
+ # all the instructions for the current parser state
19
+ #
20
+ # @param [Hash] token
21
+ # the unexpected input token
22
+ #
23
+ # @param [Hash] context
24
+ # the current parser context, providing line number, input string + stack etc
25
+ #
26
+ # @return [ParseError]
27
+ # a detailed Exception to be raised
28
+ def exception(state, token, context)
29
+ region = extract_error_region(token[:offset], context[:input])
30
+ expected = extract_expected_tokens(state)
31
+ message = <<-ERROR.gsub(/\n(?!\n)\s+/, " ").strip
32
+ Parse error:
33
+ #{expected.count > 1 ? 'expected one of' : 'expected'}
34
+ #{expected.map { |k| k.inspect }.join(", ")}
35
+ but got
36
+ #{token[:name].inspect}
37
+ on line
38
+ #{token[:line]}.
39
+ ERROR
40
+
41
+ unless region.nil?
42
+ region = "\n\nExact region marked...\n\n#{region}"
43
+ end
44
+
45
+ ParseError.new(message + region.to_s, token[:line], expected, token[:name])
46
+ end
47
+
48
+ private
49
+
50
+ def extract_error_region(offset, input)
51
+ return if offset.nil?
52
+
53
+ # FIXME: If anybody has a cleaner way to insert the ^ marker, please do :-)
54
+ width = 100
55
+ start_offset = [offset - width, 0].max
56
+ end_offset = offset + width
57
+ before = input[start_offset, [offset, width].min]
58
+ after = input[offset, width]
59
+ before_lines = "~#{before}~".lines.to_a
60
+ after_lines = "~#{after}~".lines.to_a
61
+
62
+ before_lines.first.slice!(0)
63
+ before_lines.last.chop!
64
+
65
+ after_lines.first.slice!(0)
66
+ after_lines.last.chop!
67
+
68
+ region_before = before_lines.pop
69
+ region_after = after_lines.shift
70
+ error_line = region_before + region_after
71
+
72
+ padding = if region_before.length > 5
73
+ (" " * (region_before.length - 5)) + " ... "
74
+ else
75
+ " " * region_before.length
76
+ end
77
+
78
+ marker = "#{padding}^ ... right here\n\n"
79
+
80
+ unless error_line =~ /[\r\n]\Z/
81
+ marker = "\n#{marker}"
82
+ end
83
+
84
+ "#{error_line}#{marker}"
85
+ end
86
+
87
+ def extract_expected_tokens(state)
88
+ state.select { |s, i| [:shift, :accept].include?(i[:action]) }.keys
89
+ end
90
+ end
91
+ end
92
+ end
@@ -107,19 +107,6 @@ module Whittle
107
107
  @start
108
108
  end
109
109
 
110
- # Returns the numeric value for the initial state (the state ID associated with the start
111
- # rule).
112
- #
113
- # In most LALR(1) parsers, this would be zero, but for implementation reasons, this will
114
- # be an unpredictably large (or small) number.
115
- #
116
- # @return [Fixnum]
117
- # the ID for the initial state in the parse table
118
- def initial_state
119
- prepare_start_rule
120
- [rules[start], 0].hash
121
- end
122
-
123
110
  # Returns the entire parse table used to interpret input into the parser.
124
111
  #
125
112
  # You should not need to call this method, though you may wish to inspect its contents
@@ -133,34 +120,38 @@ module Whittle
133
120
  # @return [Hash]
134
121
  # a 2-dimensional Hash representing states with actions to perform for a given lookahead
135
122
  def parse_table
136
- @parse_table ||= begin
137
- prepare_start_rule
138
- rules[start].build_parse_table(
139
- {},
140
- self,
141
- {
142
- :initial => true,
143
- :state => initial_state,
144
- :seen => [],
145
- :offset => 0,
146
- :prec => 0
147
- }
148
- )
149
- end
123
+ @parse_table ||= parse_table_for_rule(start)
150
124
  end
151
125
 
152
- private
153
-
154
- def prepare_start_rule
155
- raise GrammarError, "Undefined start rule #{start.inspect}" unless rules.key?(start)
156
-
157
- if rules[start].terminal?
158
- rule(:$start) do |r|
159
- r[start].as { |prog| prog }
160
- end
126
+ # Prepare the parse table for a given rule instead of the start rule.
127
+ #
128
+ # Warning: this method does not memoize the result, so you should not use it in production.
129
+ #
130
+ # @param [Symbol, String] name
131
+ # the name of the Rule to use as the start rule
132
+ #
133
+ # @return [Hash]
134
+ # the complete parse table for this rule
135
+ def parse_table_for_rule(name)
136
+ raise GrammarError, "Undefined start rule #{name.inspect}" unless rules.key?(name)
161
137
 
162
- start(:$start)
138
+ rule = if rules[name].terminal?
139
+ RuleSet.new(:$start, false).tap { |r| r[name].as { |prog| prog } }
140
+ else
141
+ rules[name]
163
142
  end
143
+
144
+ rule.build_parse_table(
145
+ {},
146
+ self,
147
+ {
148
+ :initial => true,
149
+ :state => [rule, 0].hash,
150
+ :seen => [],
151
+ :offset => 0,
152
+ :prec => 0
153
+ }
154
+ )
164
155
  end
165
156
  end
166
157
 
@@ -185,14 +176,28 @@ module Whittle
185
176
  #
186
177
  # A successful parse returns the result of evaluating the start rule, whatever that may be.
187
178
  #
179
+ # It is possible to specify a different start rule during development.
180
+ #
181
+ # @example Using a different start rule
182
+ #
183
+ # parser.parse(str, :rule => :another_rule)
184
+ #
188
185
  # @param [String] input
189
186
  # a complete input string to parse according to the grammar
190
187
  #
188
+ # @param [Hash] options
189
+ # currently the only supported option is :rule, to specify a different once-off start rule
190
+ #
191
191
  # @return [Object]
192
192
  # whatever the grammar defines
193
- def parse(input)
194
- table = self.class.parse_table
195
- states = [self.class.initial_state]
193
+ def parse(input, options = {})
194
+ table = if options.key?(:rule)
195
+ self.class.parse_table_for_rule(options[:rule])
196
+ else
197
+ self.class.parse_table
198
+ end
199
+
200
+ states = [table.keys.first]
196
201
  args = []
197
202
  line = 1
198
203
 
@@ -223,7 +228,7 @@ module Whittle
223
228
  end
224
229
  end
225
230
 
226
- error(state, token, :states => states, :args => args)
231
+ error(state, token, :input => input, :states => states, :args => args)
227
232
  end
228
233
  end
229
234
  end
@@ -246,13 +251,14 @@ module Whittle
246
251
  raise UnconsumedInputError,
247
252
  "Unmatched input #{input[offset..-1].inspect} on line #{line}" if token.nil?
248
253
 
249
- offset += token[:value].length
254
+ token[:offset] = offset
250
255
  line, token[:line] = token[:line], line
256
+ offset += token[:value].length
251
257
  yield token unless token[:discarded]
252
258
  end
253
259
  end
254
260
 
255
- yield ({ :name => :$end, :line => line, :value => nil })
261
+ yield ({ :name => :$end, :line => line, :value => nil, :offset => offset })
256
262
  end
257
263
 
258
264
  # Invoked when the parser detects an error.
@@ -267,30 +273,21 @@ module Whittle
267
273
  # @param [Hash] state
268
274
  # the possible actions for the current parser state
269
275
  #
270
- # @param [Hash] input
271
- # the received token (or, unlikely, a nonterminal symbol)
276
+ # @param [Hash] token
277
+ # the received token
272
278
  #
273
- # @param [Hash] stack
274
- # the current parse context (arg stack + state stack)
275
- def error(state, input, stack)
276
- expected = extract_expected_tokens(state)
277
- message = <<-ERROR.gsub(/\n\s+/, " ").strip
278
- Parse error:
279
- expected
280
- #{expected.map { |k| k.inspect }.join("; or ")}
281
- but got
282
- #{input[:name].inspect}
283
- on line
284
- #{input[:line]}
285
- ERROR
286
-
287
- raise ParseError.new(message, input[:line], expected, input[:name])
279
+ # @param [Hash] context
280
+ # the current parse context (input + arg stack + state stack)
281
+ def error(state, token, context)
282
+ raise ParseErrorBuilder.exception(state, token, context)
288
283
  end
289
284
 
290
285
  private
291
286
 
292
287
  def next_token(source, offset, line)
293
288
  rules.each do |name, rule|
289
+ next unless rule.terminal?
290
+
294
291
  if token = rule.scan(source, offset, line)
295
292
  token[:name] = name
296
293
  return token
@@ -299,9 +296,5 @@ module Whittle
299
296
 
300
297
  nil
301
298
  end
302
-
303
- def extract_expected_tokens(state)
304
- state.select { |s, i| [:shift, :accept].include?(i[:action]) }.keys
305
- end
306
299
  end
307
300
  end
data/lib/whittle/rule.rb CHANGED
@@ -36,16 +36,6 @@ module Whittle
36
36
  raise ArgumentError, "Unsupported rule component #{c.class}"
37
37
  end
38
38
  end
39
-
40
- pattern = @components.first
41
-
42
- if terminal?
43
- @pattern = if pattern.kind_of?(Regexp)
44
- Regexp.new("\\G#{pattern}")
45
- else
46
- Regexp.new("\\G#{Regexp.escape(pattern)}")
47
- end
48
- end
49
39
  end
50
40
 
51
41
  # Predicate check for whether or not the Rule represents a terminal symbol.
@@ -213,35 +203,6 @@ module Whittle
213
203
  tap { @prec = prec.to_i }
214
204
  end
215
205
 
216
- # Invoked for terminal rules during lexing, ignored for nonterminal rules.
217
- #
218
- # @param [String] source
219
- # the input String the scan
220
- #
221
- # @param [Fixnum] offset
222
- # the current index in the search
223
- #
224
- # @param [Fixnum] line
225
- # the line the lexer was up to when the previous token was matched
226
- #
227
- # @return [Hash]
228
- # a Hash representing the token, containing :rule, :value, :line and
229
- # :discarded, if the token is to be skipped.
230
- #
231
- # Returns nil if nothing is matched.
232
- def scan(source, offset, line)
233
- return nil unless terminal?
234
-
235
- if match = source.match(@pattern, offset)
236
- {
237
- :rule => self,
238
- :value => match[0],
239
- :line => line + match[0].count("\r\n", "\n"),
240
- :discarded => @action.equal?(NULL_ACTION)
241
- }
242
- end
243
- end
244
-
245
206
  private
246
207
 
247
208
  def resolve_conflicts(instructions)
@@ -5,8 +5,54 @@
5
5
  module Whittle
6
6
  # Represents an terminal Rule, matching a pattern in the input String
7
7
  class Terminal < Rule
8
+ # Hard-coded to always return true
8
9
  def terminal?
9
10
  true
10
11
  end
12
+
13
+ # Invoked for terminal rules during lexing, ignored for nonterminal rules.
14
+ #
15
+ # @param [String] source
16
+ # the input String the scan
17
+ #
18
+ # @param [Fixnum] offset
19
+ # the current index in the search
20
+ #
21
+ # @param [Fixnum] line
22
+ # the line the lexer was up to when the previous token was matched
23
+ #
24
+ # @return [Hash]
25
+ # a Hash representing the token, containing :rule, :value, :line and
26
+ # :discarded, if the token is to be skipped.
27
+ #
28
+ # Returns nil if nothing is matched.
29
+ def scan(source, offset, line)
30
+ if match = source.match(@pattern, offset)
31
+ {
32
+ :rule => self,
33
+ :value => match[0],
34
+ :line => line + match[0].count("\r\n", "\n"),
35
+ :discarded => @action.equal?(NULL_ACTION)
36
+ }
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def initialize(name, *components)
43
+ raise ArgumentError, \
44
+ "Rule #{name.inspect} is terminal and can only have one rule component" \
45
+ unless components.length == 1
46
+
47
+ super
48
+
49
+ pattern = components.first
50
+
51
+ @pattern = if pattern.kind_of?(Regexp)
52
+ Regexp.new("\\G#{pattern}")
53
+ else
54
+ Regexp.new("\\G#{Regexp.escape(pattern)}")
55
+ end
56
+ end
11
57
  end
12
58
  end
@@ -3,5 +3,5 @@
3
3
  # Copyright (c) Chris Corbyn, 2011
4
4
 
5
5
  module Whittle
6
- VERSION = "0.0.6"
6
+ VERSION = "0.0.7"
7
7
  end
data/lib/whittle.rb CHANGED
@@ -7,6 +7,7 @@ require "whittle/error"
7
7
  require "whittle/errors/unconsumed_input_error"
8
8
  require "whittle/errors/parse_error"
9
9
  require "whittle/errors/grammar_error"
10
+ require "whittle/parse_error_builder"
10
11
  require "whittle/rule"
11
12
  require "whittle/terminal"
12
13
  require "whittle/non_terminal"
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+
3
+ describe Whittle::ParseErrorBuilder do
4
+ let(:context) do
5
+ {
6
+ :input => "one two three four five\nsix seven eight nine ten\neleven twelve"
7
+ }
8
+ end
9
+
10
+ let(:state) do
11
+ {
12
+ "gazillion" => { :action => :shift, :state => 7 }
13
+ }
14
+ end
15
+
16
+ context "given an error region in the middle of a line" do
17
+ let(:token) do
18
+ {
19
+ :name => "eight",
20
+ :value => "eight",
21
+ :offset => 34
22
+ }
23
+ end
24
+
25
+ let(:indicator) do
26
+ Regexp.escape(
27
+ "six seven eight nine ten\n" <<
28
+ " ... ^ ..."
29
+ )
30
+ end
31
+
32
+ it "indicates the exact region" do
33
+ Whittle::ParseErrorBuilder.exception(state, token, context).message.should =~ /#{indicator}/
34
+ end
35
+ end
36
+
37
+ context "given an error region near the start of a line" do
38
+ let(:token) do
39
+ {
40
+ :name => "two",
41
+ :value => "two",
42
+ :offset => 4
43
+ }
44
+ end
45
+
46
+ let(:indicator) do
47
+ Regexp.escape(
48
+ "one two three four five\n" <<
49
+ " ^ ..."
50
+ )
51
+ end
52
+
53
+ it "indicates the exact region" do
54
+ Whittle::ParseErrorBuilder.exception(state, token, context).message.should =~ /#{indicator}/
55
+ end
56
+ end
57
+
58
+ context "given an error region near the end of a line" do
59
+ let(:token) do
60
+ {
61
+ :name => "five",
62
+ :value => "five",
63
+ :offset => 19
64
+ }
65
+ end
66
+
67
+ let(:indicator) do
68
+ Regexp.escape(
69
+ "one two three four five\n" <<
70
+ " ... ^ ..."
71
+ )
72
+ end
73
+
74
+ it "indicates the exact region" do
75
+ Whittle::ParseErrorBuilder.exception(state, token, context).message.should =~ /#{indicator}/
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,26 @@
1
+ require "spec_helper"
2
+
3
+ describe "parsing according to a different start rule" do
4
+ let(:parser) do
5
+ Class.new(Whittle::Parser) do
6
+ rule("+")
7
+ rule("-")
8
+
9
+ rule(:int => /[0-9]+/).as { |i| Integer(i) }
10
+
11
+ rule(:sum) do |r|
12
+ r[:int, "+", :int].as { |a, _, b| a + b }
13
+ end
14
+
15
+ rule(:sub) do |r|
16
+ r[:sum, "-", :sum].as { |a, _, b| a - b }
17
+ end
18
+
19
+ start(:sub)
20
+ end
21
+ end
22
+
23
+ it "ignores the defined start rule and uses the specified one" do
24
+ parser.new.parse("1+2", :rule => :sum).should == 3
25
+ end
26
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whittle
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-02 00:00:00.000000000 Z
12
+ date: 2011-12-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
16
- requirement: &70361567853620 !ruby/object:Gem::Requirement
16
+ requirement: &70312516285540 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '2.6'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70361567853620
24
+ version_requirements: *70312516285540
25
25
  description: ! "Write powerful parsers by defining a series of very simple rules\n
26
26
  \ and operations to perform as those rules are matched. Whittle\n
27
27
  \ parsers are written in pure ruby and as such are extremely
@@ -47,12 +47,14 @@ files:
47
47
  - lib/whittle/errors/parse_error.rb
48
48
  - lib/whittle/errors/unconsumed_input_error.rb
49
49
  - lib/whittle/non_terminal.rb
50
+ - lib/whittle/parse_error_builder.rb
50
51
  - lib/whittle/parser.rb
51
52
  - lib/whittle/rule.rb
52
53
  - lib/whittle/rule_set.rb
53
54
  - lib/whittle/terminal.rb
54
55
  - lib/whittle/version.rb
55
56
  - spec/spec_helper.rb
57
+ - spec/unit/parse_error_builder_spec.rb
56
58
  - spec/unit/parser/empty_rule_spec.rb
57
59
  - spec/unit/parser/empty_string_spec.rb
58
60
  - spec/unit/parser/error_reporting_spec.rb
@@ -60,6 +62,7 @@ files:
60
62
  - spec/unit/parser/multiple_precedence_spec.rb
61
63
  - spec/unit/parser/non_terminal_ambiguity_spec.rb
62
64
  - spec/unit/parser/noop_spec.rb
65
+ - spec/unit/parser/one_off_start_rule_spec.rb
63
66
  - spec/unit/parser/pass_through_parser_spec.rb
64
67
  - spec/unit/parser/precedence_spec.rb
65
68
  - spec/unit/parser/premature_eof_spec.rb
@@ -96,6 +99,7 @@ specification_version: 3
96
99
  summary: An efficient, easy to use, LALR parser for Ruby
97
100
  test_files:
98
101
  - spec/spec_helper.rb
102
+ - spec/unit/parse_error_builder_spec.rb
99
103
  - spec/unit/parser/empty_rule_spec.rb
100
104
  - spec/unit/parser/empty_string_spec.rb
101
105
  - spec/unit/parser/error_reporting_spec.rb
@@ -103,6 +107,7 @@ test_files:
103
107
  - spec/unit/parser/multiple_precedence_spec.rb
104
108
  - spec/unit/parser/non_terminal_ambiguity_spec.rb
105
109
  - spec/unit/parser/noop_spec.rb
110
+ - spec/unit/parser/one_off_start_rule_spec.rb
106
111
  - spec/unit/parser/pass_through_parser_spec.rb
107
112
  - spec/unit/parser/precedence_spec.rb
108
113
  - spec/unit/parser/premature_eof_spec.rb