Class: Ibex::LALR::IELRPartition
- Inherits:
-
Object
- Object
- Ibex::LALR::IELRPartition
- Defined in:
- lib/ibex/lalr/ielr_partition.rb,
sig/ibex/lalr/ielr_partition.rbs
Overview
Deterministically merges compatible canonical LR(1) states and splits partitions until their outgoing transitions are congruent.
Constant Summary collapse
- AUGMENTED_PRODUCTION =
-1 #: Integer
Instance Method Summary collapse
- #action_contributions(state_id) ⇒ Hash[Integer, Set[Array[untyped]]]
- #build ⇒ [ Array[packed_items], transitions ]
-
#compatible?(members) ⇒ Boolean
A canonical member may gain an action only where it previously had no action.
- #compatible_partitions(members) ⇒ Array[state_partition]
- #core_key(items) ⇒ Array[item_core]
- #initial_partitions ⇒ Array[state_partition]
-
#initialize(grammar, states, transitions) ⇒ IELRPartition
constructor
A new instance of IELRPartition.
- #merge_items(members) ⇒ packed_items
- #partition_indexes(partitions) ⇒ Hash[Integer, Integer]
- #refine_transitions(partitions) ⇒ Array[state_partition]
- #rhs_for(production_id) ⇒ Array[Integer]
- #transition_signature(state_id, indexes) ⇒ Array[[ Integer, Integer ]]
Constructor Details
#initialize(grammar, states, transitions) ⇒ IELRPartition
Returns a new instance of IELRPartition.
19 20 21 22 23 24 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 19 def initialize(grammar, states, transitions) @grammar = grammar @states = states @transitions = transitions @contributions = Array.new(states.length) { |state_id| action_contributions(state_id) } end |
Instance Method Details
#action_contributions(state_id) ⇒ Hash[Integer, Set[Array[untyped]]]
118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 118 def action_contributions(state_id) actions = Hash.new { |hash, key| hash[key] = Set.new } #: Hash[Integer, Set[Array[untyped]]] @transitions.fetch(state_id).each_key do |symbol_id| symbol = @grammar.symbol_by_id(symbol_id) actions[symbol_id] << [:shift] if symbol&.terminal? end @states.fetch(state_id).each do |production_id, dot, lookahead| next unless dot == rhs_for(production_id).length action = production_id.negative? ? [:accept] : [:reduce, production_id] actions[lookahead] << action end actions end |
#build ⇒ [ Array[packed_items], transitions ]
27 28 29 30 31 32 33 34 35 36 37 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 27 def build partitions = refine_transitions(initial_partitions) indexes = partition_indexes(partitions) items = partitions.map { |members| merge_items(members) } transitions = partitions.map do |members| @transitions.fetch(members.first).to_h do |symbol_id, target| [symbol_id, indexes.fetch(target)] end end [items, transitions] end |
#compatible?(members) ⇒ Boolean
A canonical member may gain an action only where it previously had no action. Every nonempty member cell must equal the merged cell.
64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 64 def compatible?(members) merged = Hash.new { |hash, key| hash[key] = Set.new } #: Hash[Integer, Set[Array[untyped]]] members.each do |state_id| @contributions.fetch(state_id).each { |token_id, actions| merged[token_id].merge(actions) } end merged.all? do |token_id, actions| members.all? do |state_id| member_actions = @contributions.fetch(state_id)[token_id] member_actions.nil? || member_actions.empty? || member_actions == actions end end end |
#compatible_partitions(members) ⇒ Array[state_partition]
52 53 54 55 56 57 58 59 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 52 def compatible_partitions(members) partitions = [] #: Array[state_partition] members.each do |state_id| partition = partitions.find { |candidate| compatible?(candidate + [state_id]) } partition ? partition << state_id : partitions << [state_id] end partitions end |
#core_key(items) ⇒ Array[item_core]
145 146 147 148 149 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 145 def core_key(items) items.map do |production_id, dot, _lookahead| [production_id, dot] #: item_core end.uniq.sort end |
#initial_partitions ⇒ Array[state_partition]
42 43 44 45 46 47 48 49 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 42 def initial_partitions cores = {} #: Hash[Array[item_core], Array[Integer]] @states.each_with_index do |items, state_id| cores[core_key(items)] ||= [] cores.fetch(core_key(items)) << state_id end cores.values.flat_map { |members| compatible_partitions(members) } end |
#merge_items(members) ⇒ packed_items
107 108 109 110 111 112 113 114 115 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 107 def merge_items(members) merged = Hash.new { |hash, key| hash[key] = Set.new } #: packed_items members.each do |state_id| @states.fetch(state_id).each do |production_id, dot, lookahead| merged[[production_id, dot]] << lookahead end end merged end |
#partition_indexes(partitions) ⇒ Hash[Integer, Integer]
98 99 100 101 102 103 104 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 98 def partition_indexes(partitions) indexes = {} #: Hash[Integer, Integer] partitions.each_with_index do |members, partition_id| members.each { |state_id| indexes[state_id] = partition_id } end indexes end |
#refine_transitions(partitions) ⇒ Array[state_partition]
78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 78 def refine_transitions(partitions) loop do indexes = partition_indexes(partitions) refined = partitions.flat_map do |members| members.group_by { |state_id| transition_signature(state_id, indexes) }.values end return partitions if refined == partitions partitions = refined end end |
#rhs_for(production_id) ⇒ Array[Integer]
134 135 136 137 138 139 140 141 142 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 134 def rhs_for(production_id) if production_id.negative? name = @grammar.starts.fetch(-production_id - 1) start = @grammar.symbol(name) || raise(Ibex::Error, "missing start symbol #{name}") return [start.id] end @grammar.productions.fetch(production_id).rhs end |
#transition_signature(state_id, indexes) ⇒ Array[[ Integer, Integer ]]
91 92 93 94 95 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 91 def transition_signature(state_id, indexes) @transitions.fetch(state_id).map do |symbol_id, target| [symbol_id, indexes.fetch(target)] #: [Integer, Integer] end.sort end |