Module: Kumi::Frontends::Text

Defined in:
lib/kumi/frontends/text.rb

Class Method Summary collapse

Class Method Details

.code_frame(src, line, col, context: 2) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/kumi/frontends/text.rb', line 59

def self.code_frame(src, line, col, context: 2)
  return "" unless line

  lines  = src.lines
  from   = [line - 1 - context, 0].max
  to     = [line - 1 + context, lines.length - 1].min
  out    = []

  (from..to).each do |i|
    prefix = i + 1 == line ? "➤" : " "
    out << format("#{prefix} %4d | %s", i + 1, lines[i].rstrip)
    out << format("       | %s^", " " * (col - 1)) if i + 1 == line && col
  end

  out.join("\n")
end

.extract_line_column(exception) ⇒ Object



40
41
42
43
44
45
46
47
48
49
# File 'lib/kumi/frontends/text.rb', line 40

def self.extract_line_column(exception)
  # Try to access Location object from exception
  if exception.respond_to?(:location) && exception.location
    loc = exception.location
    return [loc.line, loc.column] if loc.respond_to?(:line) && loc.respond_to?(:column)
  end

  # Fall back to parsing error message if no Location object
  extract_line_column_from_message(exception.message)
end

.extract_line_column_from_message(message) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/kumi/frontends/text.rb', line 51

def self.extract_line_column_from_message(message)
  if message =~ /line=(\d+)\s+column=(\d+)/
    [::Regexp.last_match(1).to_i, ::Regexp.last_match(2).to_i]
  else
    [nil, nil]
  end
end

.load(path: nil, src: nil, inputs: {}) ⇒ Object

Load from a file path or a raw string. Usage:

Text.load(path: "schema.kumi", inputs: {...})
Text.load(src: "...raw text...", inputs: {...})

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/kumi/frontends/text.rb', line 12

def load(path: nil, src: nil, inputs: {})
  raise ArgumentError, "provide either :path or :src" if (path.nil? && src.nil?) || (path && src)

  src ||= File.read(path)
  file_label = path || "schema"

  begin
    require "kumi-parser"
    ast = Kumi::Parser::TextParser.parse(src, source_file: path || "schema")
    Core::Analyzer::Debug.info(:parse, kind: :text, file: file_label, ok: true) if Core::Analyzer::Debug.enabled?
    [ast, inputs]
  rescue LoadError
    raise "kumi-parser gem not available. Install: gem install kumi-parser"
  rescue StandardError => e
    # Try to extract line/column from exception object first
    line, col = extract_line_column(e)
    snippet = code_frame(src, line, col)

    # Strip file:line:col prefix from e.message if it exists (from parser)
    # Also strip embedded "at FILE line=N column=M" to avoid duplication
    error_message = e.message
                     .sub(/^\S+:\d+:\d+:\s+/, "")
                     .gsub(/\s+at\s+\S+\s+line=\d+\s+column=\d+/, "")
                     .strip
    raise StandardError, "#{file_label}:#{line || '?'}:#{col || '?'}: #{error_message}\n#{snippet}"
  end
end