Module: Aura::Diagnostics

Defined in:
lib/aura/diagnostics.rb

Overview

Turns Parslet’s internal failure cause tree into a human-friendly Aura::ParseError with line/column information and a source snippet.

Class Method Summary collapse

Class Method Details

.deepest_cause(cause) ⇒ Object

Walk to the most specific (deepest) cause so the reported position points at the actual offending token rather than the top-level rule.



46
47
48
49
50
51
52
53
54
55
# File 'lib/aura/diagnostics.rb', line 46

def deepest_cause(cause)
  return nil unless cause

  current = cause
  while current.respond_to?(:children) && current.children && !current.children.empty?
    # Prefer the child with the furthest source position.
    current = current.children.max_by { |c| position_of(c) || -1 }
  end
  current
end

.from_parslet(error, source) ⇒ Aura::ParseError

Parameters:

  • error (Parslet::ParseFailed)
  • source (String)

    the original source text

Returns:



33
34
35
36
37
38
39
40
41
42
# File 'lib/aura/diagnostics.rb', line 33

def from_parslet(error, source)
  cause = deepest_cause(error.parse_failure_cause)
  line, column = location_for(cause)
  message = +"Parse error"
  message << " at line #{line}, column #{column}" if line
  message << ": #{cause.to_s}" if cause
  snippet = snippet_for(source, line, column)
  message << "\n\n#{snippet}" if snippet
  ParseError.new(message, line: line, column: column)
end

.location_for(cause) ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
# File 'lib/aura/diagnostics.rb', line 57

def location_for(cause)
  return [nil, nil] unless cause && cause.respond_to?(:source) && cause.source

  pos = cause.respond_to?(:pos) ? cause.pos : nil
  return [nil, nil] unless pos

  line, column = cause.source.line_and_column(pos)
  [line, column]
rescue StandardError
  [nil, nil]
end

.position_of(cause) ⇒ Object



69
70
71
72
73
# File 'lib/aura/diagnostics.rb', line 69

def position_of(cause)
  cause.respond_to?(:pos) && cause.pos ? cause.pos.bytepos : nil
rescue StandardError
  nil
end

.snippet_for(source, line, column) ⇒ Object



75
76
77
78
79
80
81
82
83
84
# File 'lib/aura/diagnostics.rb', line 75

def snippet_for(source, line, column)
  return nil unless line && column

  src_line = source.lines[line - 1]
  return nil unless src_line

  src_line = src_line.chomp
  caret = "#{' ' * (column - 1)}^"
  "  #{src_line}\n  #{caret}"
end