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 Attribute Summary collapse
- #final_partition_count ⇒ Integer? readonly
- #initial_partition_count ⇒ Integer? readonly
Instance Method Summary collapse
- #action_contributions(state_id) ⇒ Hash[Integer, Set[contribution_action]]
- #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, profile: false) ⇒ 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, profile: false) ⇒ IELRPartition
Returns a new instance of IELRPartition.
27 28 29 30 31 32 33 34 35 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 27 def initialize(grammar, states, transitions, profile: false) @grammar = grammar @states = states @transitions = transitions @contributions = Array.new(states.length) { |state_id| action_contributions(state_id) } @initial_partition_count = nil @final_partition_count = nil @profile = profile end |
Instance Attribute Details
#final_partition_count ⇒ Integer? (readonly)
24 25 26 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 24 def final_partition_count @final_partition_count end |
#initial_partition_count ⇒ Integer? (readonly)
23 24 25 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 23 def initial_partition_count @initial_partition_count end |
Instance Method Details
#action_contributions(state_id) ⇒ Hash[Integer, Set[contribution_action]]
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 132 def action_contributions(state_id) actions = Hash.new { |hash, key| hash[key] = Set.new } #: Hash[Integer, Set[contribution_action]] @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] #: contribution_action actions[lookahead] << action end actions end |
#build ⇒ [ Array[packed_items], transitions ]
38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 38 def build partitions = initial_partitions @initial_partition_count = partitions.length if @profile partitions = refine_transitions(partitions) @final_partition_count = partitions.length if @profile 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.
78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 78 def compatible?(members) merged = Hash.new { |hash, key| hash[key] = Set.new } #: Hash[Integer, Set[contribution_action]] 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]
66 67 68 69 70 71 72 73 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 66 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]
159 160 161 162 163 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 159 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]
56 57 58 59 60 61 62 63 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 56 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
121 122 123 124 125 126 127 128 129 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 121 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]
112 113 114 115 116 117 118 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 112 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]
92 93 94 95 96 97 98 99 100 101 102 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 92 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]
148 149 150 151 152 153 154 155 156 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 148 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 ]]
105 106 107 108 109 |
# File 'lib/ibex/lalr/ielr_partition.rb', line 105 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 |