Module: Ibex::Codegen::Report

Defined in:
lib/ibex/codegen/report.rb,
sig/ibex/codegen/report.rbs

Overview

Renders a human-readable state and conflict report from Automaton IR.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.append_counterexample(lines, example, grammar, labels) ⇒ Object



90
91
92
93
94
95
96
97
98
99
# File 'lib/ibex/codegen/report.rb', line 90

def append_counterexample(lines, example, grammar, labels)
  label = example[:unifying] ? "unifying counterexample" : "nonunifying witness"
  sentence = example[:sentence].map { |name| tree_label(grammar, labels, name) }
  sentence = sentence.dup.insert(example[:lookahead_index], "").join(" ")
  lines << "  #{label}: #{sentence}"
  example[:interpretations].each do |interpretation|
    lines << "    #{interpretation[:kind]} derivation:"
    append_tree(lines, interpretation[:tree], "      ", grammar, labels)
  end
end

.append_state(lines, state, grammar, examples, labels) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/ibex/codegen/report.rb', line 74

def append_state(lines, state, grammar, examples, labels)
  lines << "State #{state.id}"
  state.items.each { |item| lines << "  #{format_item(item, grammar, labels)}" }
  state.actions.each do |token_id, action|
    lines << "  on #{symbol_name(labels, token_id)}: #{format_action(action)}"
  end
  lines << "  default: #{format_action(state.default_action)}" if state.default_action
  state.gotos.each { |symbol_id, target| lines << "  goto #{symbol_name(labels, symbol_id)}: #{target}" }
  state.conflicts.each do |conflict|
    lines << "  conflict: #{format_value(display_conflict(conflict, grammar, labels))}"
  end
  examples.each { |example| append_counterexample(lines, example, grammar, labels) }
  lines << ""
end

.append_tree(lines, tree, indentation, grammar, labels) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/ibex/codegen/report.rb', line 102

def append_tree(lines, tree, indentation, grammar, labels)
  unless tree.is_a?(Hash)
    lines << "#{indentation}#{tree_label(grammar, labels, tree)}"
    return
  end

  symbol = tree_label(grammar, labels, tree[:symbol] || tree[:token])
  unless tree[:children]
    lines << "#{indentation}#{symbol}"
    return
  end

  production = tree[:production] ? " (production #{tree[:production]})" : ""
  lines << "#{indentation}#{symbol}#{production}"
  tree[:children].each { |child| append_tree(lines, child, "#{indentation}  ", grammar, labels) }
end

.display_conflict(conflict, grammar, labels) ⇒ Object



156
157
158
159
160
161
# File 'lib/ibex/codegen/report.rb', line 156

def display_conflict(conflict, grammar, labels)
  symbol = grammar.symbol(conflict[:symbol])
  return conflict unless symbol

  conflict.merge(symbol: symbol_name(labels, symbol.id))
end

.format_action(action) ⇒ Object



136
137
138
139
140
141
142
# File 'lib/ibex/codegen/report.rb', line 136

def format_action(action)
  case action[:type]
  when :shift then "shift #{action[:state]}"
  when :reduce then "reduce #{action[:production]}"
  else action[:type].to_s
  end
end

.format_item(item, grammar, labels) ⇒ Object



120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/ibex/codegen/report.rb', line 120

def format_item(item, grammar, labels)
  if item.production == LALR::Builder::AUGMENTED_PRODUCTION
    start = grammar.symbol(grammar.start)
    rhs = [grammar.starts.one? && start ? symbol_name(labels, start.id) : "<entry>"]
    lhs = "$accept"
  else
    production = grammar.productions.fetch(item.production)
    rhs = production.rhs.map { |id| symbol_name(labels, id) }
    lhs = symbol_name(labels, production.lhs)
  end
  rhs = rhs.dup.insert(item.dot, "")
  lookaheads = item.lookaheads.map { |id| symbol_name(labels, id) }.join(", ")
  "#{lhs} -> #{rhs.join(' ')} [#{lookaheads}]"
end

.format_value(value) ⇒ Object

Keep reports byte-identical across Ruby versions whose Hash#inspect formatting differs for symbol keys.



166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/ibex/codegen/report.rb', line 166

def format_value(value)
  case value
  when Hash
    entries = value.map do |key, item|
      label = key.is_a?(Symbol) ? "#{key}:" : "#{format_value(key)} =>"
      "#{label} #{format_value(item)}"
    end
    "{#{entries.join(', ')}}"
  when Array
    "[#{value.map { |item| format_value(item) }.join(', ')}]"
  else
    value.inspect
  end
end

.render(automaton, max_tokens: LALR::Counterexample::DEFAULT_MAX_TOKENS, max_configurations: LALR::Counterexample::DEFAULT_MAX_CONFIGURATIONS) ⇒ Object

RBS:

  • (IR::Automaton automaton, ?max_tokens: Integer, ?max_configurations: Integer) -> String



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/ibex/codegen/report.rb', line 50

def render(automaton, max_tokens: LALR::Counterexample::DEFAULT_MAX_TOKENS,
           max_configurations: LALR::Counterexample::DEFAULT_MAX_CONFIGURATIONS)
  grammar = automaton.grammar
  labels = SymbolLabels.build(grammar)
  entries = automaton.entry_states.map { |name, state| "#{name}=#{state}" }.join(", ")
  lines = ["Algorithm: #{automaton.algorithm}", "States: #{automaton.states.length}"]
  lines << "Entries: #{entries}" if automaton.entry_states.length > 1
  lines << ""
  examples = LALR::Counterexample.new(
    automaton, max_tokens: max_tokens, max_configurations: max_configurations
  ).all.group_by { |example| example[:state] }
  automaton.states.each do |state|
    append_state(lines, state, grammar, examples.fetch(state.id, Array.new(0)), labels)
  end
  summary = automaton.conflict_summary
  lines << "Conflicts: #{summary[:sr]} shift/reduce, #{summary[:rr]} reduce/reduce"
  "#{lines.join("\n")}\n"
end

.symbol_name(labels, id) ⇒ Object



145
146
147
# File 'lib/ibex/codegen/report.rb', line 145

def symbol_name(labels, id)
  labels.fetch(id) { raise Ibex::Error, "missing grammar symbol id #{id}" }
end

.tree_label(grammar, labels, value) ⇒ Object



150
151
152
153
# File 'lib/ibex/codegen/report.rb', line 150

def tree_label(grammar, labels, value)
  symbol = grammar.symbol(value.to_s)
  symbol ? symbol_name(labels, symbol.id) : value
end

Instance Method Details

#self?.renderString

RBS:

  • (IR::Automaton automaton, ?max_tokens: Integer, ?max_configurations: Integer) -> String

Parameters:

  • automaton (IR::Automaton)
  • max_tokens: (Integer)
  • max_configurations: (Integer)

Returns:

  • (String)


44
# File 'sig/ibex/codegen/report.rbs', line 44

def self?.render: (IR::Automaton automaton, ?max_tokens: Integer, ?max_configurations: Integer) -> String