Always compute lookahead tokens for reduce rules

Even if they won't be needed for the generated parser, they'll be useful
to detect shift/reduce conflicts.
This commit is contained in:
Josh Holtrop 2024-07-13 21:01:44 -04:00
parent 7f3eb8f315
commit 69cc8fa67d
2 changed files with 25 additions and 24 deletions

View File

@ -62,13 +62,12 @@ class Propane
} }
end end
reduce_entries = reduce_entries =
case ra = item_set.reduce_actions if rule = item_set.reduce_rule
when Rule [{token_id: @grammar.invalid_token_id, rule_id: rule.id, rule: rule,
[{token_id: @grammar.invalid_token_id, rule_id: ra.id, rule: ra, rule_set_id: rule.rule_set.id, n_states: rule.components.size,
rule_set_id: ra.rule_set.id, n_states: ra.components.size, propagate_optional_target: rule.optional? && rule.components.size == 1}]
propagate_optional_target: ra.optional? && ra.components.size == 1}] elsif reduce_actions = item_set.reduce_actions
when Hash reduce_actions.map do |token, rule|
ra.map do |token, rule|
{token_id: token.id, rule_id: rule.id, rule: rule, {token_id: token.id, rule_id: rule.id, rule: rule,
rule_set_id: rule.rule_set.id, n_states: rule.components.size, rule_set_id: rule.rule_set.id, n_states: rule.components.size,
propagate_optional_target: rule.optional? && rule.components.size == 1} propagate_optional_target: rule.optional? && rule.components.size == 1}
@ -111,10 +110,8 @@ class Propane
# @param item_set [ItemSet] # @param item_set [ItemSet]
# ItemSet (parser state) # ItemSet (parser state)
# #
# @return [nil, Rule, Hash] # @return [nil, Hash]
# If no reduce actions are possible for the given item set, nil. # If no reduce actions are possible for the given item set, nil.
# If only one reduce action is possible for the given item set, the Rule
# to reduce.
# Otherwise, a mapping of lookahead Tokens to the Rules to reduce. # Otherwise, a mapping of lookahead Tokens to the Rules to reduce.
def build_reduce_actions_for_item_set(item_set) def build_reduce_actions_for_item_set(item_set)
# To build the reduce actions, we start by looking at any # To build the reduce actions, we start by looking at any
@ -123,16 +120,16 @@ class Propane
# reduction in the current ItemSet. # reduction in the current ItemSet.
reduce_rules = Set.new(item_set.items.select(&:complete?).map(&:rule)) reduce_rules = Set.new(item_set.items.select(&:complete?).map(&:rule))
# If there are no rules to reduce for this ItemSet, we're done here. if reduce_rules.size == 1
return nil if reduce_rules.size == 0 item_set.reduce_rule = reduce_rules.first
end
# If there is exactly one rule to reduce for this ItemSet, then do not if reduce_rules.size == 0
# figure out the lookaheads; just reduce it. nil
return reduce_rules.first if reduce_rules.size == 1 else
# Otherwise, we have more than one possible rule to reduce.
build_lookahead_reduce_actions_for_item_set(item_set) build_lookahead_reduce_actions_for_item_set(item_set)
end end
end
# Build the reduce actions for a single item set (parser state). # Build the reduce actions for a single item set (parser state).
# #
@ -305,10 +302,9 @@ class Propane
end end
@log.puts @log.puts
@log.puts " Reduce actions:" @log.puts " Reduce actions:"
case item_set.reduce_actions if item_set.reduce_rule
when Rule @log.puts " * => rule #{item_set.reduce_rule.id}, rule set #{@rule_sets[item_set.reduce_rule.name].id} (#{item_set.reduce_rule.name})"
@log.puts " * => rule #{item_set.reduce_actions.id}, rule set #{@rule_sets[item_set.reduce_actions.name].id} (#{item_set.reduce_actions.name})" elsif item_set.reduce_actions
when Hash
item_set.reduce_actions.each do |token, rule| item_set.reduce_actions.each do |token, rule|
@log.puts " lookahead #{token.name} => #{rule.name} (#{rule.id}), rule set ##{rule.rule_set.id}" @log.puts " lookahead #{token.name} => #{rule.name} (#{rule.id}), rule set ##{rule.rule_set.id}"
end end

View File

@ -21,8 +21,13 @@ class Propane
# ItemSets leading to this item set. # ItemSets leading to this item set.
attr_reader :in_sets attr_reader :in_sets
# @return [nil, Rule, Hash] # @return [nil, Rule]
# Reduce actions, mapping lookahead tokens to rules. # Rule to reduce if there is only one possibility.
attr_accessor :reduce_rule
# @return [nil, Hash]
# Reduce actions, mapping lookahead tokens to rules, if there is
# more than one rule that could be reduced.
attr_accessor :reduce_actions attr_accessor :reduce_actions
# @return [Set<Token>] # @return [Set<Token>]