Warn on shift/reduce conflicts

This commit is contained in:
Josh Holtrop 2024-07-13 21:35:53 -04:00
parent 69cc8fa67d
commit 4ae5ab79b3
4 changed files with 33 additions and 4 deletions

View File

@ -669,7 +669,7 @@ typedef struct
/** Parser shift table. */
static const shift_t parser_shift_table[] = {
<% @parser.shift_table.each do |shift| %>
{<%= shift[:symbol_id] %>u, <%= shift[:state_id] %>u},
{<%= shift[:symbol].id %>u, <%= shift[:state_id] %>u},
<% end %>
};

View File

@ -817,7 +817,7 @@ private struct state_value_t
/** Parser shift table. */
private immutable shift_t[] parser_shift_table = [
<% @parser.shift_table.each do |shift| %>
shift_t(<%= shift[:symbol_id] %>u, <%= shift[:state_id] %>u),
shift_t(<%= shift[:symbol].id %>u, <%= shift[:state_id] %>u),
<% end %>
];

View File

@ -13,6 +13,7 @@ class Propane
@log = log
@item_sets = []
@item_sets_set = {}
@sr_conflicts = Set.new
start_item = Item.new(grammar.rules.first, 0)
eval_item_sets = Set[ItemSet.new([start_item])]
@ -38,8 +39,8 @@ class Propane
build_reduce_actions!
build_follow_sets!
write_log!
build_tables!
write_log!
end
private
@ -57,10 +58,17 @@ class Propane
item_set.next_item_set[next_symbol].id
end
{
symbol_id: next_symbol.id,
symbol: next_symbol,
state_id: state_id,
}
end
if item_set.reduce_actions
shift_entries.each do |shift_entry|
if item_set.reduce_actions.include?(shift_entry[:symbol])
@sr_conflicts << [shift_entry[:symbol], item_set.reduce_actions[shift_entry[:symbol]]]
end
end
end
reduce_entries =
if rule = item_set.reduce_rule
[{token_id: @grammar.invalid_token_id, rule_id: rule.id, rule: rule,
@ -310,6 +318,13 @@ class Propane
end
end
end
if @sr_conflicts.size > 0
@log.puts
@log.puts "Shift/Reduce Conflicts:"
@sr_conflicts.each do |sr_conflict|
@log.puts " Shift/Reduce conflict between #{sr_conflict[0].name} and #{sr_conflict[1].name}"
end
end
end
end

View File

@ -184,6 +184,20 @@ EOF
expect(results.status).to_not eq 0
end
it "warns on shift/reduce conflicts" do
write_grammar <<EOF
token a;
token b;
Start -> As? b?;
As -> a As2?;
As2 -> b a As2?;
EOF
results = run_propane(capture: true)
expect(results.stderr).to eq ""
expect(results.status).to eq 0
expect(File.binread("spec/run/testparser.log")).to match %r{Shift/Reduce conflict between b and As2}
end
%w[d c].each do |language|
context "#{language.upcase} language" do