lib/lrama/state.rb in lrama-0.6.11 vs lib/lrama/state.rb in lrama-0.7.0
- old
+ new
@@ -8,11 +8,11 @@
module Lrama
class State
attr_reader :id, :accessing_symbol, :kernels, :conflicts, :resolved_conflicts,
:default_reduction_rule, :closure, :items
- attr_accessor :shifts, :reduces
+ attr_accessor :shifts, :reduces, :ielr_isocores, :lalr_isocore
def initialize(id, accessing_symbol, kernels)
@id = id
@accessing_symbol = accessing_symbol
@kernels = kernels.freeze
@@ -21,10 +21,16 @@
# to resolve next state
@items_to_state = {}
@conflicts = []
@resolved_conflicts = []
@default_reduction_rule = nil
+ @predecessors = []
+ @lalr_isocore = self
+ @ielr_isocores = [self]
+ @internal_dependencies = {}
+ @successor_dependencies = {}
+ @always_follows = {}
end
def closure=(closure)
@closure = closure
@items = @kernels + @closure
@@ -82,10 +88,22 @@
def transitions
@transitions ||= shifts.map {|shift| [shift, @items_to_state[shift.next_items]] }
end
+ def update_transition(shift, next_state)
+ set_items_to_state(shift.next_items, next_state)
+ next_state.append_predecessor(self)
+ clear_transitions_cache
+ end
+
+ def clear_transitions_cache
+ @nterm_transitions = nil
+ @term_transitions = nil
+ @transitions = nil
+ end
+
def selected_term_transitions
term_transitions.reject do |shift, next_state|
shift.not_selected
end
end
@@ -139,8 +157,277 @@
def rr_conflicts
@conflicts.select do |conflict|
conflict.type == :reduce_reduce
end
+ end
+
+ def propagate_lookaheads(next_state)
+ next_state.kernels.map {|item|
+ lookahead_sets =
+ if item.position == 1
+ goto_follow_set(item.lhs)
+ else
+ kernel = kernels.find {|k| k.predecessor_item_of?(item) }
+ item_lookahead_set[kernel]
+ end
+
+ [item, lookahead_sets & next_state.lookahead_set_filters[item]]
+ }.to_h
+ end
+
+ def lookaheads_recomputed
+ !@item_lookahead_set.nil?
+ end
+
+ def compatible_lookahead?(filtered_lookahead)
+ !lookaheads_recomputed ||
+ @lalr_isocore.annotation_list.all? {|token, actions|
+ a = dominant_contribution(token, actions, item_lookahead_set)
+ b = dominant_contribution(token, actions, filtered_lookahead)
+ a.nil? || b.nil? || a == b
+ }
+ end
+
+ def lookahead_set_filters
+ kernels.map {|kernel|
+ [kernel,
+ @lalr_isocore.annotation_list.select {|token, actions|
+ token.term? && actions.any? {|action, contributions|
+ !contributions.nil? && contributions.key?(kernel) && contributions[kernel]
+ }
+ }.map {|token, _| token }
+ ]
+ }.to_h
+ end
+
+ def dominant_contribution(token, actions, lookaheads)
+ a = actions.select {|action, contributions|
+ contributions.nil? || contributions.any? {|item, contributed| contributed && lookaheads[item].include?(token) }
+ }.map {|action, _| action }
+ return nil if a.empty?
+ a.reject {|action|
+ if action.is_a?(State::Shift)
+ action.not_selected
+ elsif action.is_a?(State::Reduce)
+ action.not_selected_symbols.include?(token)
+ end
+ }
+ end
+
+ def inadequacy_list
+ return @inadequacy_list if @inadequacy_list
+
+ shift_contributions = shifts.map {|shift|
+ [shift.next_sym, [shift]]
+ }.to_h
+ reduce_contributions = reduces.map {|reduce|
+ (reduce.look_ahead || []).map {|sym|
+ [sym, [reduce]]
+ }.to_h
+ }.reduce(Hash.new([])) {|hash, cont|
+ hash.merge(cont) {|_, a, b| a | b }
+ }
+
+ list = shift_contributions.merge(reduce_contributions) {|_, a, b| a | b }
+ @inadequacy_list = list.select {|token, actions| token.term? && actions.size > 1 }
+ end
+
+ def annotation_list
+ return @annotation_list if @annotation_list
+
+ @annotation_list = annotate_manifestation
+ @annotation_list = @items_to_state.values.map {|next_state| next_state.annotate_predecessor(self) }
+ .reduce(@annotation_list) {|result, annotations|
+ result.merge(annotations) {|_, actions_a, actions_b|
+ if actions_a.nil? || actions_b.nil?
+ actions_a || actions_b
+ else
+ actions_a.merge(actions_b) {|_, contributions_a, contributions_b|
+ if contributions_a.nil? || contributions_b.nil?
+ next contributions_a || contributions_b
+ end
+
+ contributions_a.merge(contributions_b) {|_, contributed_a, contributed_b|
+ contributed_a || contributed_b
+ }
+ }
+ end
+ }
+ }
+ end
+
+ def annotate_manifestation
+ inadequacy_list.transform_values {|actions|
+ actions.map {|action|
+ if action.is_a?(Shift)
+ [action, nil]
+ elsif action.is_a?(Reduce)
+ if action.rule.empty_rule?
+ [action, lhs_contributions(action.rule.lhs, inadequacy_list.key(actions))]
+ else
+ contributions = kernels.map {|kernel| [kernel, kernel.rule == action.rule && kernel.end_of_rule?] }.to_h
+ [action, contributions]
+ end
+ end
+ }.to_h
+ }
+ end
+
+ def annotate_predecessor(predecessor)
+ annotation_list.transform_values {|actions|
+ token = annotation_list.key(actions)
+ actions.transform_values {|inadequacy|
+ next nil if inadequacy.nil?
+ lhs_adequacy = kernels.any? {|kernel|
+ inadequacy[kernel] && kernel.position == 1 && predecessor.lhs_contributions(kernel.lhs, token).nil?
+ }
+ if lhs_adequacy
+ next nil
+ else
+ predecessor.kernels.map {|pred_k|
+ [pred_k, kernels.any? {|k|
+ inadequacy[k] && (
+ pred_k.predecessor_item_of?(k) && predecessor.item_lookahead_set[pred_k].include?(token) ||
+ k.position == 1 && predecessor.lhs_contributions(k.lhs, token)[pred_k]
+ )
+ }]
+ }.to_h
+ end
+ }
+ }
+ end
+
+ def lhs_contributions(sym, token)
+ shift, next_state = nterm_transitions.find {|sh, _| sh.next_sym == sym }
+ if always_follows(shift, next_state).include?(token)
+ nil
+ else
+ kernels.map {|kernel| [kernel, follow_kernel_items(shift, next_state, kernel) && item_lookahead_set[kernel].include?(token)] }.to_h
+ end
+ end
+
+ def follow_kernel_items(shift, next_state, kernel)
+ queue = [[self, shift, next_state]]
+ until queue.empty?
+ st, sh, next_st = queue.pop
+ return true if kernel.next_sym == sh.next_sym && kernel.symbols_after_transition.all?(&:nullable)
+ st.internal_dependencies(sh, next_st).each {|v| queue << v }
+ end
+ false
+ end
+
+ def item_lookahead_set
+ return @item_lookahead_set if @item_lookahead_set
+
+ kernels.map {|item|
+ value =
+ if item.lhs.accept_symbol?
+ []
+ elsif item.position > 1
+ prev_items = predecessors_with_item(item)
+ prev_items.map {|st, i| st.item_lookahead_set[i] }.reduce([]) {|acc, syms| acc |= syms }
+ elsif item.position == 1
+ prev_state = @predecessors.find {|p| p.shifts.any? {|shift| shift.next_sym == item.lhs } }
+ shift, next_state = prev_state.nterm_transitions.find {|shift, _| shift.next_sym == item.lhs }
+ prev_state.goto_follows(shift, next_state)
+ end
+ [item, value]
+ }.to_h
+ end
+
+ def item_lookahead_set=(k)
+ @item_lookahead_set = k
+ end
+
+ def predecessors_with_item(item)
+ result = []
+ @predecessors.each do |pre|
+ pre.items.each do |i|
+ result << [pre, i] if i.predecessor_item_of?(item)
+ end
+ end
+ result
+ end
+
+ def append_predecessor(prev_state)
+ @predecessors << prev_state
+ @predecessors.uniq!
+ end
+
+ def goto_follow_set(nterm_token)
+ return [] if nterm_token.accept_symbol?
+ shift, next_state = @lalr_isocore.nterm_transitions.find {|sh, _| sh.next_sym == nterm_token }
+
+ @kernels
+ .select {|kernel| follow_kernel_items(shift, next_state, kernel) }
+ .map {|kernel| item_lookahead_set[kernel] }
+ .reduce(always_follows(shift, next_state)) {|result, terms| result |= terms }
+ end
+
+ def goto_follows(shift, next_state)
+ queue = internal_dependencies(shift, next_state) + predecessor_dependencies(shift, next_state)
+ terms = always_follows(shift, next_state)
+ until queue.empty?
+ st, sh, next_st = queue.pop
+ terms |= st.always_follows(sh, next_st)
+ st.internal_dependencies(sh, next_st).each {|v| queue << v }
+ st.predecessor_dependencies(sh, next_st).each {|v| queue << v }
+ end
+ terms
+ end
+
+ def always_follows(shift, next_state)
+ return @always_follows[[shift, next_state]] if @always_follows[[shift, next_state]]
+
+ queue = internal_dependencies(shift, next_state) + successor_dependencies(shift, next_state)
+ terms = []
+ until queue.empty?
+ st, sh, next_st = queue.pop
+ terms |= next_st.term_transitions.map {|sh, _| sh.next_sym }
+ st.internal_dependencies(sh, next_st).each {|v| queue << v }
+ st.successor_dependencies(sh, next_st).each {|v| queue << v }
+ end
+ @always_follows[[shift, next_state]] = terms
+ end
+
+ def internal_dependencies(shift, next_state)
+ return @internal_dependencies[[shift, next_state]] if @internal_dependencies[[shift, next_state]]
+
+ syms = @items.select {|i|
+ i.next_sym == shift.next_sym && i.symbols_after_transition.all?(&:nullable) && i.position == 0
+ }.map(&:lhs).uniq
+ @internal_dependencies[[shift, next_state]] = nterm_transitions.select {|sh, _| syms.include?(sh.next_sym) }.map {|goto| [self, *goto] }
+ end
+
+ def successor_dependencies(shift, next_state)
+ return @successor_dependencies[[shift, next_state]] if @successor_dependencies[[shift, next_state]]
+
+ @successor_dependencies[[shift, next_state]] =
+ next_state.nterm_transitions
+ .select {|next_shift, _| next_shift.next_sym.nullable }
+ .map {|transition| [next_state, *transition] }
+ end
+
+ def predecessor_dependencies(shift, next_state)
+ state_items = []
+ @kernels.select {|kernel|
+ kernel.next_sym == shift.next_sym && kernel.symbols_after_transition.all?(&:nullable)
+ }.each do |item|
+ queue = predecessors_with_item(item)
+ until queue.empty?
+ st, i = queue.pop
+ if i.position == 0
+ state_items << [st, i]
+ else
+ st.predecessors_with_item(i).each {|v| queue << v }
+ end
+ end
+ end
+
+ state_items.map {|state, item|
+ sh, next_st = state.nterm_transitions.find {|shi, _| shi.next_sym == item.lhs }
+ [state, sh, next_st]
+ }
end
end
end