Class: Ibex::LALR::LookaheadPropagation
- Inherits:
-
Object
- Object
- Ibex::LALR::LookaheadPropagation
- Defined in:
- lib/ibex/lalr/lookahead_propagation.rb,
sig/ibex/lalr/lookahead_propagation.rbs
Overview
Recomputes item lookaheads on an already-built LR(0) automaton. This is Phase 4 of direct IELR and is also useful as an independent cross-check for DirectLookaheads.
Constant Summary collapse
- EMPTY =
Array.new(0).freeze
Instance Attribute Summary collapse
- #propagation_edge_count ⇒ Integer readonly
Instance Method Summary collapse
- #add_closure(edges, lookaheads, state_id, production_id, dot) ⇒ void
- #add_transition(edges, state_id, production_id, dot) ⇒ void
- #build ⇒ Array[packed_items]
- #initialize(grammar, sets, states, transitions, seeds:) ⇒ Array[packed_items] constructor
- #propagate(lookaheads, edges) ⇒ void
- #rhs_for(production_id) ⇒ Array[Integer]
- #terminal_ids(bits) ⇒ Array[Integer]
Constructor Details
#initialize(grammar, sets, states, transitions, seeds:) ⇒ Array[packed_items]
20 21 22 23 24 25 26 27 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 20 def initialize(grammar, sets, states, transitions, seeds:) @grammar = grammar @sets = sets @states = states @transitions = transitions @seeds = seeds @productions_by_lhs = grammar.productions.group_by(&:lhs) end |
Instance Attribute Details
#propagation_edge_count ⇒ Integer (readonly)
16 17 18 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 16 def propagation_edge_count @propagation_edge_count end |
Instance Method Details
#add_closure(edges, lookaheads, state_id, production_id, dot) ⇒ void
This method returns an undefined value.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 57 def add_closure(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)) @productions_by_lhs.fetch(symbol.id, []).each do |production| item = [production.id, 0].freeze lookaheads.fetch(state_id).fetch(item).merge(spontaneous) edges[[state_id, production_id, dot]] << [state_id, production.id, 0] if @sets.sequence_nullable?(suffix) end end |
#add_transition(edges, state_id, production_id, dot) ⇒ void
This method returns an undefined value.
48 49 50 51 52 53 54 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 48 def add_transition(edges, state_id, production_id, dot) symbol_id = rhs_for(production_id)[dot] return unless symbol_id target = @transitions.fetch(state_id).fetch(symbol_id) edges[[state_id, production_id, dot]] << [target, production_id, dot + 1] end |
#build ⇒ Array[packed_items]
30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 30 def build lookaheads = @states.map { |items| items.to_h { |item| [item, Set.new] } } edges = Hash.new { |hash, key| hash[key] = [] } @states.each_with_index do |items, state_id| items.each do |production_id, dot| add_transition(edges, state_id, production_id, dot) add_closure(edges, lookaheads, state_id, production_id, dot) end end @seeds.each { |state_id, item, token| lookaheads.fetch(state_id).fetch(item) << token } @propagation_edge_count = edges.values.sum(&:length) propagate(lookaheads, edges) lookaheads end |
#propagate(lookaheads, edges) ⇒ void
This method returns an undefined value.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 73 def propagate(lookaheads, edges) queue = @seeds.map { |state_id, (production_id, dot), _token| [state_id, production_id, dot] } queue.concat(lookaheads.each_with_index.flat_map do |items, state_id| items.filter_map { |(production_id, dot), values| [state_id, production_id, dot] unless values.empty? } end) queued = queue.to_h { |node| [node, true] } cursor = 0 while cursor < queue.length source = queue.fetch(cursor) cursor += 1 queued.delete(source) source_set = lookaheads.fetch(source[0]).fetch([source[1], source[2]]) edges.fetch(source, EMPTY).each do |target| target_set = lookaheads.fetch(target[0]).fetch([target[1], target[2]]) 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 |
#rhs_for(production_id) ⇒ Array[Integer]
103 104 105 106 107 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 103 def rhs_for(production_id) return [@grammar.symbol(@grammar.starts.fetch(-production_id - 1)).id] if production_id.negative? @grammar.productions.fetch(production_id).rhs end |
#terminal_ids(bits) ⇒ Array[Integer]
98 99 100 |
# File 'lib/ibex/lalr/lookahead_propagation.rb', line 98 def terminal_ids(bits) @grammar.terminals.filter_map { |terminal| terminal.id if bits.anybits?(1 << terminal.id) } end |