Class: Ibex::LALR::DirectLookaheads
- Inherits:
-
Object
- Object
- Ibex::LALR::DirectLookaheads
- Defined in:
- lib/ibex/lalr/direct_lookaheads.rb,
sig/ibex/lalr/direct_lookaheads.rbs
Overview
Builds an LR(0) collection and propagates LALR(1) lookaheads directly over item occurrences. Canonical LR(1) states are never materialized.
Constant Summary collapse
- AUGMENTED_PRODUCTION =
-1 #: Integer
- EMPTY_PRODUCTIONS =
Array.new(0).freeze
- EMPTY_NODES =
Array.new(0).freeze
Instance Method Summary collapse
- #add_closure_relations(edges, lookaheads, state_id, production_id, dot) ⇒ void
- #add_transition_edge(edges, transitions, state_id, production_id, dot) ⇒ void
- #build ⇒ [ Array[packed_items], transitions ]
- #closure(seed) ⇒ core_set
- #empty_lookaheads(states) ⇒ Array[packed_items]
-
#initialize(grammar, sets) ⇒ DirectLookaheads
constructor
A new instance of DirectLookaheads.
- #item_core(production_id, dot) ⇒ item_core
- #item_cores_for(production_id, rhs_length) ⇒ Array[item_core]
- #item_key(items) ⇒ Array[item_core]
- #lr0_collection ⇒ [ Array[core_set], transitions ]
- #node_set(lookaheads, node) ⇒ Set[Integer]
- #propagate(lookaheads, edges) ⇒ void
- #propagation_graph(states, transitions, lookaheads) ⇒ Hash[lookahead_node, Array[lookahead_node]]
- #rhs_for(production_id) ⇒ Array[Integer]
- #shifted_kernels(items) ⇒ Hash[Integer, core_set]
- #terminal_ids(bits) ⇒ Array[Integer]
Constructor Details
#initialize(grammar, sets) ⇒ DirectLookaheads
Returns a new instance of DirectLookaheads.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 27 def initialize(grammar, sets) @grammar = grammar @sets = sets @productions_by_lhs = grammar.productions.group_by(&:lhs) start = grammar.symbol(grammar.start) || raise(Ibex::Error, "missing start symbol") @augmented_rhs = [start.id].freeze @production_rhs = grammar.productions.map(&:rhs).freeze @augmented_item_cores = item_cores_for(AUGMENTED_PRODUCTION, @augmented_rhs.length) @production_item_cores = grammar.productions.map do |production| item_cores_for(production.id, production.rhs.length) end.freeze @terminal_ids = grammar.terminals.map(&:id).freeze @terminal_masks = @terminal_ids.map { |id| 1 << id }.freeze end |
Instance Method Details
#add_closure_relations(edges, lookaheads, state_id, production_id, dot) ⇒ void
This method returns an undefined value.
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 144 def add_closure_relations(edges, lookaheads, state_id, production_id, dot) rhs = rhs_for(production_id) symbol = @grammar.symbol_by_id(rhs[dot]) return unless symbol&.nonterminal? suffix = rhs.drop(dot + 1) spontaneous = terminal_ids(@sets.first_of_sequence(suffix)) source = [state_id, production_id, dot] #: lookahead_node @productions_by_lhs.fetch(symbol.id, EMPTY_PRODUCTIONS).each do |production| target_item = item_core(production.id, 0) lookaheads.fetch(state_id).fetch(target_item).merge(spontaneous) if @sets.sequence_nullable?(suffix) target = [state_id, production.id, 0] #: lookahead_node edges[source] << target end end end |
#add_transition_edge(edges, transitions, state_id, production_id, dot) ⇒ void
This method returns an undefined value.
132 133 134 135 136 137 138 139 140 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 132 def add_transition_edge(edges, transitions, state_id, production_id, dot) symbol_id = rhs_for(production_id)[dot] return unless symbol_id target_state = transitions.fetch(state_id).fetch(symbol_id) source = [state_id, production_id, dot] #: lookahead_node target = [target_state, production_id, dot + 1] #: lookahead_node edges[source] << target end |
#build ⇒ [ Array[packed_items], transitions ]
43 44 45 46 47 48 49 50 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 43 def build states, transitions = lr0_collection lookaheads = empty_lookaheads(states) propagation = propagation_graph(states, transitions, lookaheads) lookaheads.fetch(0).fetch(item_core(AUGMENTED_PRODUCTION, 0)) << 0 propagate(lookaheads, propagation) [lookaheads, transitions] end |
#closure(seed) ⇒ core_set
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 79 def closure(seed) items = seed.dup queue = seed.to_a cursor = 0 while cursor < queue.length production_id, dot = queue.fetch(cursor) cursor += 1 symbol = @grammar.symbol_by_id(rhs_for(production_id)[dot]) next unless symbol&.nonterminal? @productions_by_lhs.fetch(symbol.id, EMPTY_PRODUCTIONS).each do |production| item = item_core(production.id, 0) queue << item if items.add?(item) end end items end |
#empty_lookaheads(states) ⇒ Array[packed_items]
110 111 112 113 114 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 110 def empty_lookaheads(states) states.map do |items| items.to_h { |item| [item, Set.new] } #: packed_items end end |
#item_core(production_id, dot) ⇒ item_core
222 223 224 225 226 227 228 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 222 def item_core(production_id, dot) raise IndexError, "invalid item dot: #{dot}" if dot.negative? return @augmented_item_cores.fetch(dot) if production_id == AUGMENTED_PRODUCTION raise IndexError, "invalid production id: #{production_id}" if production_id.negative? @production_item_cores.fetch(production_id).fetch(dot) end |
#item_cores_for(production_id, rhs_length) ⇒ Array[item_core]
215 216 217 218 219 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 215 def item_cores_for(production_id, rhs_length) Array.new(rhs_length + 1) do |dot| [production_id, dot].freeze #: item_core end.freeze end |
#item_key(items) ⇒ Array[item_core]
231 232 233 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 231 def item_key(items) items.to_a.sort end |
#lr0_collection ⇒ [ Array[core_set], transitions ]
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 55 def lr0_collection seed = Set[item_core(AUGMENTED_PRODUCTION, 0)] #: core_set states = [closure(seed)] transitions = [] #: transitions indexes = { item_key(states.first) => 0 } cursor = 0 while cursor < states.length transitions[cursor] = {} kernels = shifted_kernels(states.fetch(cursor)) kernels.keys.sort.each do |symbol_id| target = closure(kernels.fetch(symbol_id)) key = item_key(target) target_id = indexes[key] ||= begin states << target states.length - 1 end transitions.fetch(cursor)[symbol_id] = target_id end cursor += 1 end [states, transitions] end |
#node_set(lookaheads, node) ⇒ Set[Integer]
191 192 193 194 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 191 def node_set(lookaheads, node) state_id, production_id, dot = node lookaheads.fetch(state_id).fetch(item_core(production_id, dot)) end |
#propagate(lookaheads, edges) ⇒ void
This method returns an undefined value.
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 163 def propagate(lookaheads, edges) queue = lookaheads.each_with_index.flat_map do |items, state_id| items.filter_map do |(production_id, dot), tokens| next if tokens.empty? [state_id, production_id, dot] #: lookahead_node end end #: Array[lookahead_node] queued = queue.to_h { |node| [node, true] } #: Hash[lookahead_node, bool] cursor = 0 while cursor < queue.length source = queue.fetch(cursor) cursor += 1 queued.delete(source) source_set = node_set(lookaheads, source) edges.fetch(source, EMPTY_NODES).each do |target| target_set = node_set(lookaheads, target) previous_size = target_set.size target_set.merge(source_set) next if target_set.size == previous_size || queued[target] queue << target queued[target] = true end end end |
#propagation_graph(states, transitions, lookaheads) ⇒ Hash[lookahead_node, Array[lookahead_node]]
118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 118 def propagation_graph(states, transitions, lookaheads) edges = Hash.new { |hash, key| hash[key] = [] } #: Hash[lookahead_node, Array[lookahead_node]] states.each_with_index do |items, state_id| items.each do |production_id, dot| add_transition_edge(edges, transitions, state_id, production_id, dot) add_closure_relations(edges, lookaheads, state_id, production_id, dot) end end edges.each_value(&:uniq!) edges end |
#rhs_for(production_id) ⇒ Array[Integer]
208 209 210 211 212 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 208 def rhs_for(production_id) return @augmented_rhs if production_id == AUGMENTED_PRODUCTION @production_rhs.fetch(production_id) end |
#shifted_kernels(items) ⇒ Hash[Integer, core_set]
98 99 100 101 102 103 104 105 106 107 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 98 def shifted_kernels(items) kernels = {} #: Hash[Integer, core_set] items.each do |production_id, dot| symbol_id = rhs_for(production_id)[dot] next unless symbol_id (kernels[symbol_id] ||= Set.new) << item_core(production_id, dot + 1) end kernels end |
#terminal_ids(bits) ⇒ Array[Integer]
197 198 199 200 201 202 203 204 205 |
# File 'lib/ibex/lalr/direct_lookaheads.rb', line 197 def terminal_ids(bits) selected = [] #: Array[Integer] index = 0 while index < @terminal_ids.length selected << @terminal_ids[index] if bits.anybits?(@terminal_masks[index]) index += 1 end selected end |