Class: Parslet::ErrorReporter::Contextual

Inherits:
Deepest
  • Object
show all
Defined in:
lib/parslet/error_reporter/contextual.rb

Overview

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

Instance Attribute Summary

Attributes inherited from Deepest

#deepest_cause

Instance Method Summary collapse

Methods inherited from Deepest

#deepest, #err_at

Constructor Details

#initializeContextual

Returns a new instance of Contextual.



59
60
61
62
# File 'lib/parslet/error_reporter/contextual.rb', line 59

def initialize
  @last_reset_pos = 0
  reset
end

Instance Method Details

#err(atom, source, message, children = nil) ⇒ Cause

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.

Parameters:

  • atom (Parslet::Atoms::Base)

    parslet that failed

  • source (Source)

    Source that we’re using for this parse. (line number information…)

  • message (String, Array)

    Error message at this level.

  • children (Array) (defaults to: nil)

    A list of errors from a deeper level (or nil).

Returns:

  • (Cause)

    An error tree combining children with message.



95
96
97
98
99
100
101
102
# 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

#resetObject

Reset deepest error and its position and sequence index



79
80
81
82
# File 'lib/parslet/error_reporter/contextual.rb', line 79

def reset
  @deepest_cause = nil
  @label_pos = -1
end

#succ(source) ⇒ Object

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)



70
71
72
73
74
75
# 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_label(label, bytepos) ⇒ Object

Update error message label if given label is more relevant. A label is more relevant if the position of the matched source is bigger.

Parameters:

  • label (String)

    label to apply if more relevant

  • bytepos (Integer)

    position in source code of matched source



111
112
113
114
115
116
# 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