Class: Ibex::RaccMigration::Checker

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/racc_migration/checker.rb,
sig/ibex/racc_migration/checker.rbs

Overview

Checks syntax, normalization, superclass, and runtime-coupling hazards. Semantic action code is treated as opaque data and never executed.

Constant Summary collapse

RACC_REQUIRE =

Signature:

  • Regexp

Returns:

  • (Regexp)
%r{(?:require|require_relative)\s*\(?\s*["']racc(?:/parser)?["']}
RACC_CONSTANT =

Signature:

  • Regexp

Returns:

  • (Regexp)
/\bRacc::[A-Za-z_]\w*/

Instance Method Summary collapse

Instance Method Details

#check(source, file:) ⇒ Report

RBS:

  • (String source, file: String) -> Report

Parameters:

  • source (String)
  • file: (String)

Returns:



13
14
15
16
17
18
19
20
21
22
23
# File 'lib/ibex/racc_migration/checker.rb', line 13

def check(source, file:)
  result = Frontend::Parser.new(source, file: file, mode: :default).parse_with_diagnostics
  findings = result.diagnostics.map { |diagnostic| syntax_finding(diagnostic) }
  ast = result.ast
  return Report.new(file: file, class_name: nil, findings: findings) unless ast

  findings.concat(superclass_findings(ast))
  findings.concat(user_code_findings(ast))
  findings.concat(normalization_findings(ast))
  Report.new(file: file, class_name: ast.class_name, findings: sort_findings(findings))
end

#error_location(message, fallback) ⇒ [ Frontend::Location, String ]

RBS:

  • (String message, Frontend::Location fallback) -> [Frontend::Location, String]

Parameters:

Returns:



98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/ibex/racc_migration/checker.rb', line 98

def error_location(message, fallback)
  match = message.match(/\A(.+):(\d+):(\d+): (.*)\z/m)
  return [fallback, message] unless match

  [
    Frontend::Location.new(
      file: match[1].to_s,
      line: Integer(match[2].to_s, 10),
      column: Integer(match[3].to_s, 10)
    ),
    match[4].to_s
  ]
end

#normalization_findings(ast) ⇒ Array[Finding]

RBS:

  • (Frontend::AST::Root ast) -> Array[Finding]

Parameters:

Returns:



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/ibex/racc_migration/checker.rb', line 81

def normalization_findings(ast)
  Normalizer.new(ast, mode: :default).normalize
  []
rescue Ibex::Error => e
  location, message = error_location(e.message, ast.loc)
  [
    Finding.new(
      code: "racc.normalization",
      severity: :error,
      message: message,
      location: location,
      suggestion: "apply the diagnostic before generating either parser"
    )
  ]
end

#sort_findings(findings) ⇒ Array[Finding]

RBS:

  • (Array[Finding] findings) -> Array[Finding]

Parameters:

Returns:



113
114
115
116
117
118
119
# File 'lib/ibex/racc_migration/checker.rb', line 113

def sort_findings(findings)
  rank = { error: 0, warning: 1, info: 2 }
  findings.sort_by do |finding|
    location = finding.location
    [location&.file || "", location&.line || 0, location&.column || 0, rank.fetch(finding.severity), finding.code]
  end
end

#superclass_findings(ast) ⇒ Array[Finding]

RBS:

  • (Frontend::AST::Root ast) -> Array[Finding]

Parameters:

Returns:



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/ibex/racc_migration/checker.rb', line 40

def superclass_findings(ast)
  return [] unless ast.superclass&.start_with?("Racc::")

  [
    Finding.new(
      code: "racc.runtime_superclass",
      severity: :error,
      message: "generated class inherits #{ast.superclass}",
      location: ast.loc,
      suggestion: "remove the explicit Racc superclass or generate with `--superclass=Ibex::Runtime::Parser`"
    )
  ]
end

#syntax_finding(diagnostic) ⇒ Finding

RBS:

  • (Frontend::Diagnostic diagnostic) -> Finding

Parameters:

Returns:



28
29
30
31
32
33
34
35
36
37
# File 'lib/ibex/racc_migration/checker.rb', line 28

def syntax_finding(diagnostic)
  Finding.new(
    code: "racc.syntax",
    severity: :error,
    message: diagnostic.message,
    location: diagnostic.location,
    suggestion: "keep the grammar in default mode, or adopt the reported extension explicitly " \
                "with `pragma extended`"
  )
end

#user_code_findings(ast) ⇒ Array[Finding]

RBS:

  • (Frontend::AST::Root ast) -> Array[Finding]

Parameters:

Returns:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/ibex/racc_migration/checker.rb', line 55

def user_code_findings(ast)
  ast.user_code.values.flatten.flat_map do |chunk|
    findings = [] #: Array[Finding]
    if chunk.code.match?(RACC_REQUIRE)
      findings << Finding.new(
        code: "racc.runtime_require",
        severity: :warning,
        message: "user code requires the racc runtime",
        location: chunk.loc,
        suggestion: "remove this require after switching generated parsers to the Ibex runtime"
      )
    end
    if chunk.code.match?(RACC_CONSTANT)
      findings << Finding.new(
        code: "racc.runtime_constant",
        severity: :warning,
        message: "user code references a Racc runtime constant",
        location: chunk.loc,
        suggestion: "replace the runtime-specific constant or keep an explicit compatibility adapter"
      )
    end
    findings
  end
end