class Parslet::ErrorReporter::Contextual
A reporter that tries to improve on the deepest error reporter by using heuristics to find the most relevant error and provide more context. The heuristic chooses the deepest error when parsing a sequence for which no alternative parsed successfully.
Given the following parser:
root(:call)
rule(:call, label: 'call') {
identifier >> str('.') >> method
}
rule(:method, label: 'method call') {
identifier >> str('(') >> arguments.maybe >> str(')')
}
rule(:identifier, label: 'identifier') {
match['[:alnum:]'].repeat(1)
}
rule(:arguments, label: 'method call arguments') {
argument >> str(',') >> arguments | argument
}
rule(:argument) {
call | identifier
}
and the following source:
foo.bar(a,goo.baz(),c,)
The contextual reporter returns the following causes:
0: Failed to match sequence (identifier '.' method call) at line 1 char 5
when parsing method call arguments.
1: Failed to match sequence (identifier '(' method call arguments? ')') at
line 1 char 22 when parsing method call arguments.
2: Failed to match [[:alnum:]] at line 1 char 23 when parsing method call
arguments.
(where 2 is a child cause of 1 and 1 a child cause of 0)
The last piece used by the reporter is the (newly introduced) ability to attach a label to rules that describe a sequence in the grammar. The labels are used in two places:
- In the "to_s" of Atom::Base so that any error message uses labels to refer to atoms - In the cause error messages to give information about which expression failed to parse
Public Class Methods
# File lib/parslet/error_reporter/contextual.rb, line 59 def initialize @last_reset_pos = 0 reset end
Public Instance Methods
Produces an error cause that combines the message at the current level with the errors that happened at a level below (children). Compute and set label used by Cause
to produce error message.
@param atom [Parslet::Atoms::Base] parslet that failed @param source [Source] Source
that we're using for this parse. (line
number information...)
@param message [String, Array] Error message at this level. @param children [Array] A list of errors from a deeper level (or nil). @return [Cause] An error tree combining children with message.
# File lib/parslet/error_reporter/contextual.rb, line 95 def err(atom, source, message, children=nil) cause = super(atom, source, message, children) if (label = atom.respond_to?(:label) && atom.label) update_label(label, source.pos.bytepos) cause.set_label(@label) end cause end
Reset deepest error and its position and sequence index
# File lib/parslet/error_reporter/contextual.rb, line 79 def reset @deepest_cause = nil @label_pos = -1 end
A sequence expression successfully parsed, reset all errors reported for previous expressions in the sequence (an alternative matched) Only reset errors if the position of the source that matched is higher than the position of the source that was last successful (so we keep errors that are the “deepest” but for which no alternative succeeded)
# File lib/parslet/error_reporter/contextual.rb, line 70 def succ(source) source_pos = source.pos.bytepos return if source_pos < @last_reset_pos @last_reset_pos = source_pos reset end
Update error message label if given label is more relevant. A label is more relevant if the position of the matched source is bigger.
@param label [String] label to apply if more relevant @param bytepos [Integer] position in source code of matched source
# File lib/parslet/error_reporter/contextual.rb, line 111 def update_label(label, bytepos) if bytepos >= @label_pos @label_pos = bytepos @label = label end end