Class: Kumi::Parser::Source

Inherits:
Object
  • Object
show all
Defined in:
lib/kumi/parser/source.rb

Overview

The text being parsed, plus the bookkeeping needed to turn a byte offset into a 1-based line/column and to render a caret-annotated code frame for error messages. Owning this here keeps location math in one place instead of being recomputed in the lexer and the parser.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(text, file: 'schema') ⇒ Source

Returns a new instance of Source.



12
13
14
15
16
# File 'lib/kumi/parser/source.rb', line 12

def initialize(text, file: 'schema')
  @text = text
  @file = file
  @line_starts = compute_line_starts(text)
end

Instance Attribute Details

#fileObject (readonly)

Returns the value of attribute file.



10
11
12
# File 'lib/kumi/parser/source.rb', line 10

def file
  @file
end

#textObject (readonly)

Returns the value of attribute text.



10
11
12
# File 'lib/kumi/parser/source.rb', line 10

def text
  @text
end

Instance Method Details

#code_frame(offset, context: 2) ⇒ Object

A two-line-of-context code frame with a caret under the offending column, in the same shape kumi-core’s text frontend already renders.



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/kumi/parser/source.rb', line 33

def code_frame(offset, context: 2)
  line, col = line_col(offset)
  lines = text.lines
  from = [line - 1 - context, 0].max
  to   = [line - 1 + context, lines.length - 1].min
  return '' if lines.empty?

  out = []
  (from..to).each do |i|
    marker = i + 1 == line ? '' : ' '
    out << format('%s %4d | %s', marker, i + 1, lines[i].to_s.chomp)
    out << format('       | %s^', ' ' * (col - 1)) if i + 1 == line
  end
  out.join("\n")
end

#line_col(offset) ⇒ Object

1-based [line, column] for a 0-based byte offset.



19
20
21
22
23
24
# File 'lib/kumi/parser/source.rb', line 19

def line_col(offset)
  offset = text.length if offset > text.length
  line = upper_bound(@line_starts, offset) - 1
  col = offset - @line_starts[line] + 1
  [line + 1, col]
end

#location(offset) ⇒ Object



26
27
28
29
# File 'lib/kumi/parser/source.rb', line 26

def location(offset)
  line, col = line_col(offset)
  Kumi::Syntax::Location.new(file: file, line: line, column: col)
end