rltk 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +12 -11
- data/lib/rltk/parser.rb +23 -3
- data/lib/rltk/version.rb +1 -1
- data/test/tc_parser.rb +30 -6
- metadata +2 -2
data/README.md
CHANGED
@@ -312,28 +312,29 @@ Calls to {RLTK::Parser.parse} may raise one of four exceptions:
|
|
312
312
|
|
313
313
|
**Warning: this is the lest tested feature of RLTK. If you encounter any problems while using it, please let me know so I can fix any bugs as soon as possible.**
|
314
314
|
|
315
|
-
When an RLTK parser encounters a token for which there are no more valid actions (and it is on the last parse stack / possible parse-tree path) it will enter error handling mode. In this mode the parser pops states and input off of the parse stack (the parser is a pushdown automaton after all) until it finds a state that has a shift action for the `ERROR` terminal. A dummy `ERROR` terminal is then placed onto the parse stack and the shift action is taken. This error token will have the position information of the token that caused the parser to enter error handling mode.
|
315
|
+
When an RLTK parser encounters a token for which there are no more valid actions (and it is on the last parse stack / possible parse-tree path) it will enter error handling mode. In this mode the parser pops states and input off of the parse stack (the parser is a pushdown automaton after all) until it finds a state that has a shift action for the `ERROR` terminal. A dummy `ERROR` terminal is then placed onto the parse stack and the shift action is taken. This error token will have the position information of the token that caused the parser to enter error handling mode. Additional tokens may have been discarded after this token.
|
316
316
|
|
317
|
-
If the input (including the `ERROR` token) can be reduced immediately the associated error handling proc is evaluated and we continue parsing. If
|
317
|
+
If the input (including the `ERROR` token) can be reduced immediately the associated error handling proc is evaluated and we continue parsing. If no shift or reduce action is available the parser will being shifting tokens off of the input stack until a token appears with a valid action in the current state, in which case parsing resumes as normal.
|
318
318
|
|
319
|
-
The
|
319
|
+
The value of an `ERROR` non-terminal will be an array containing all of the tokens that were discarded while the parser was searching for a valid action.
|
320
320
|
|
321
|
-
|
322
|
-
class AfterSubError < StandardError; end
|
321
|
+
The example below, based on one of the unit tests, shows a very basic usage of error productions:
|
323
322
|
|
324
323
|
class ErrorCalc < RLTK::Parser
|
324
|
+
left :ERROR
|
325
|
+
right :PLS, :SUB, :MUL, :DIV, :NUM
|
326
|
+
|
325
327
|
production(:e) do
|
326
|
-
clause('NUM') {
|
327
|
-
|
328
|
+
clause('NUM') {|n| n}
|
329
|
+
|
328
330
|
clause('e PLS e') { |e0, _, e1| e0 + e1 }
|
329
331
|
clause('e SUB e') { |e0, _, e1| e0 - e1 }
|
330
332
|
clause('e MUL e') { |e0, _, e1| e0 * e1 }
|
331
333
|
clause('e DIV e') { |e0, _, e1| e0 / e1 }
|
332
|
-
|
333
|
-
clause('e PLS ERROR') { |_, _, _| raise AfterPlsError }
|
334
|
-
clause('e SUB ERROR') { |_, _, _| raise AfterSubError }
|
335
|
-
end
|
336
334
|
|
335
|
+
clause('e PLS ERROR e') { |e0, _, err, e1| error("#{err.len} tokens skipped."); e0 + e1 }
|
336
|
+
end
|
337
|
+
|
337
338
|
finalize
|
338
339
|
end
|
339
340
|
|
data/lib/rltk/parser.rb
CHANGED
@@ -835,6 +835,13 @@ module RLTK # :nodoc:
|
|
835
835
|
# If we are already in error mode and there
|
836
836
|
# are no actions we skip this token.
|
837
837
|
if error_mode
|
838
|
+
v.puts("Discarding token: #{token.type}#{if token.value then "(#{token.value})" end}") if v
|
839
|
+
|
840
|
+
# Add the current token to the array
|
841
|
+
# that corresponds to the output value
|
842
|
+
# for the ERROR token.
|
843
|
+
stack.output_stack.last << token
|
844
|
+
|
838
845
|
moving_on << stack
|
839
846
|
next
|
840
847
|
end
|
@@ -842,6 +849,16 @@ module RLTK # :nodoc:
|
|
842
849
|
# We would be dropping the last stack so we
|
843
850
|
# are going to go into error mode.
|
844
851
|
if accepted.empty? and moving_on.empty? and processing.empty?
|
852
|
+
|
853
|
+
if v
|
854
|
+
v.puts
|
855
|
+
v.puts('Current stack:')
|
856
|
+
v.puts("\tID: #{stack.id}")
|
857
|
+
v.puts("\tState stack:\t#{stack.state_stack.inspect}")
|
858
|
+
v.puts("\tOutput Stack:\t#{stack.output_stack.inspect}")
|
859
|
+
v.puts
|
860
|
+
end
|
861
|
+
|
845
862
|
# Try and find a valid error state.
|
846
863
|
while stack.state
|
847
864
|
if (actions = @states[stack.state].on?(:ERROR)).empty?
|
@@ -850,7 +867,7 @@ module RLTK # :nodoc:
|
|
850
867
|
stack.pop
|
851
868
|
else
|
852
869
|
# Enter the found error state.
|
853
|
-
stack.push(actions.first.id,
|
870
|
+
stack.push(actions.first.id, [token], :ERROR, token.position)
|
854
871
|
|
855
872
|
break
|
856
873
|
end
|
@@ -860,9 +877,12 @@ module RLTK # :nodoc:
|
|
860
877
|
# We found a valid error state.
|
861
878
|
error_mode = reduction_guard = true
|
862
879
|
opts[:env].he = true
|
863
|
-
|
880
|
+
moving_on << stack
|
864
881
|
|
865
|
-
|
882
|
+
if v
|
883
|
+
v.puts('Invalid input encountered. Entering error handling mode.')
|
884
|
+
v.puts("Discarding token: #{token.type}#{if token.value then "(#{token.value})" end}")
|
885
|
+
end
|
866
886
|
else
|
867
887
|
# No valid error states could be
|
868
888
|
# found. Time to print a message
|
data/lib/rltk/version.rb
CHANGED
data/test/tc_parser.rb
CHANGED
@@ -127,6 +127,9 @@ class ParserTester < Test::Unit::TestCase
|
|
127
127
|
class DummyError2 < StandardError; end
|
128
128
|
|
129
129
|
class ErrorCalc < RLTK::Parser
|
130
|
+
left :ERROR
|
131
|
+
right :PLS, :SUB, :MUL, :DIV, :NUM
|
132
|
+
|
130
133
|
production(:e) do
|
131
134
|
clause('NUM') {|n| n}
|
132
135
|
|
@@ -135,8 +138,7 @@ class ParserTester < Test::Unit::TestCase
|
|
135
138
|
clause('e MUL e') { |e0, _, e1| e0 * e1 }
|
136
139
|
clause('e DIV e') { |e0, _, e1| e0 / e1 }
|
137
140
|
|
138
|
-
clause('e PLS ERROR') { |
|
139
|
-
clause('e SUB ERROR') { |_, _, _| raise DummyError2 }
|
141
|
+
clause('e PLS ERROR e') { |e0, _, ts, e1| error(ts); e0 + e1 }
|
140
142
|
end
|
141
143
|
|
142
144
|
finalize
|
@@ -159,7 +161,7 @@ class ParserTester < Test::Unit::TestCase
|
|
159
161
|
clause('NEWLINE') { |_| nil }
|
160
162
|
|
161
163
|
clause('WORD+ SEMI NEWLINE') { |w, _, _| w }
|
162
|
-
clause('WORD+ ERROR
|
164
|
+
clause('WORD+ ERROR') { |w, e| error(pos(1).line_number); w }
|
163
165
|
end
|
164
166
|
|
165
167
|
finalize
|
@@ -277,8 +279,8 @@ class ParserTester < Test::Unit::TestCase
|
|
277
279
|
end
|
278
280
|
|
279
281
|
def test_error_productions
|
280
|
-
|
281
|
-
|
282
|
+
|
283
|
+
# Test to see if error reporting is working correctly.
|
282
284
|
|
283
285
|
test_string = "first line;\n"
|
284
286
|
test_string += "second line\n"
|
@@ -289,8 +291,30 @@ class ParserTester < Test::Unit::TestCase
|
|
289
291
|
|
290
292
|
begin
|
291
293
|
ErrorLine.parse(ELLexer.lex(test_string))
|
294
|
+
|
295
|
+
rescue RLTK::HandledError => e
|
296
|
+
assert_equal([2,4], e.errors)
|
297
|
+
end
|
298
|
+
|
299
|
+
# Test to see if we can continue parsing after errors are encounterd.
|
300
|
+
|
301
|
+
begin
|
302
|
+
ErrorCalc.parse(RLTK::Lexers::Calculator.lex('1 + + 1'))
|
303
|
+
|
304
|
+
rescue RLTK::HandledError => e
|
305
|
+
assert_equal(1, e.errors.first.length)
|
306
|
+
assert_equal(2, e.result)
|
307
|
+
end
|
308
|
+
|
309
|
+
# Test to see if we pop tokens correctly after an error is
|
310
|
+
# encountered.
|
311
|
+
|
312
|
+
begin
|
313
|
+
ErrorCalc.parse(RLTK::Lexers::Calculator.lex('1 + + + + + + 1'))
|
314
|
+
|
292
315
|
rescue RLTK::HandledError => e
|
293
|
-
assert_equal(e.errors
|
316
|
+
assert_equal(5, e.errors.first.length)
|
317
|
+
assert_equal(2, e.result)
|
294
318
|
end
|
295
319
|
end
|
296
320
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rltk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2.
|
4
|
+
version: 2.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-09-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|