Class: Ibex::LALR::IELR::Annotator
- Inherits:
-
Object
- Object
- Ibex::LALR::IELR::Annotator
- Defined in:
- lib/ibex/lalr/ielr/annotator.rb,
sig/ibex/lalr/ielr/annotator.rbs
Overview
Finds grammar-relative inadequacies and traces their possible manifestations through predecessor states.
Instance Attribute Summary collapse
- #annotation_lists ⇒ Array[Array[Annotation]] readonly
- #inadequacies ⇒ Array[Array[Inadequacy]] readonly
- #item_lookaheads ⇒ ItemLookaheads readonly
- #split_stable_discarded ⇒ Integer readonly
Instance Method Summary collapse
- #always_via_lhs?(state_id, cores, row, token) ⇒ Boolean
- #annotate_manifestation(state_id, inadequacy) ⇒ Annotation
- #annotate_predecessor(state_id, successor_id, annotation) ⇒ Annotation
- #build ⇒ Array[Array[Annotation]]
- #build_inadequacies(state_id) ⇒ Array[Inadequacy]
- #completed_kernel_mask(state_id, production_id, length) ⇒ Integer
- #compute_lhs_contributions(state_id, lhs_id, token_id) ⇒ Integer?
- #contributions_for(state_id, token_id) ⇒ Array[[ Symbol, Integer? ]]
-
#initialize(grammar, states, transitions, items, goto_follows, resolver: nil) ⇒ Annotator
constructor
A new instance of Annotator.
- #kernel_index_of(state_id, production_id, dot) ⇒ Integer?
- #kernel_items(items) ⇒ Array[item_core]
- #lhs_for(production_id) ⇒ Integer
- #project_row(state_id, cores, row, token) ⇒ Integer
- #register?(state_id, annotation) ⇒ Boolean
- #rhs_for(production_id) ⇒ Array[Integer]
Constructor Details
#initialize(grammar, states, transitions, items, goto_follows, resolver: nil) ⇒ Annotator
Returns a new instance of Annotator.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 28 def initialize(grammar, states, transitions, items, goto_follows, resolver: nil) @grammar = grammar @states = states @transitions = transitions @items = items @goto_follows = goto_follows @resolver = resolver || ConflictResolver.new(grammar) @kernel_cores = states.map { |state| kernel_items(state) } @item_lookaheads = ItemLookaheads.new( grammar, goto_follows, @kernel_cores, goto_follows.predecessors, start_states: (0...grammar.starts.length).to_a ) @annotation_lists = Array.new(states.length) { [] } @keys = Array.new(states.length) { Set.new } @inadequacies = Array.new(states.length) { [] } @next_id = 0 @split_stable_discarded = 0 end |
Instance Attribute Details
#annotation_lists ⇒ Array[Array[Annotation]] (readonly)
21 22 23 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 21 def annotation_lists @annotation_lists end |
#inadequacies ⇒ Array[Array[Inadequacy]] (readonly)
22 23 24 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 22 def inadequacies @inadequacies end |
#item_lookaheads ⇒ ItemLookaheads (readonly)
23 24 25 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 23 def item_lookaheads @item_lookaheads end |
#split_stable_discarded ⇒ Integer (readonly)
24 25 26 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 24 def split_stable_discarded @split_stable_discarded end |
Instance Method Details
#always_via_lhs?(state_id, cores, row, token) ⇒ Boolean
127 128 129 130 131 132 133 134 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 127 def always_via_lhs?(state_id, cores, row, token) Bits.each_set_bit(row).any? do |index| production_id, dot = cores.fetch(index) next false unless dot == 1 compute_lhs_contributions(state_id, lhs_for(production_id), token).nil? end end |
#annotate_manifestation(state_id, inadequacy) ⇒ Annotation
99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 99 def annotate_manifestation(state_id, inadequacy) matrix = inadequacy.contributions.map do |kind, production_id| next nil if kind == :shift rhs = rhs_for(production_id) if rhs.empty? compute_lhs_contributions(state_id, lhs_for(production_id), inadequacy.token) else completed_kernel_mask(state_id, production_id, rhs.length) end end Annotation.new(inadequacy: inadequacy, matrix: matrix) end |
#annotate_predecessor(state_id, successor_id, annotation) ⇒ Annotation
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 114 def annotate_predecessor(state_id, successor_id, annotation) token = annotation.inadequacy.token cores = @kernel_cores.fetch(successor_id) matrix = annotation.matrix.map do |row| next nil if row.nil? next nil if always_via_lhs?(state_id, cores, row, token) project_row(state_id, cores, row, token) end Annotation.new(inadequacy: annotation.inadequacy, matrix: matrix) end |
#build ⇒ Array[Array[Annotation]]
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 48 def build @states.each_index do |state_id| @inadequacies[state_id] = build_inadequacies(state_id) @inadequacies.fetch(state_id).each do |inadequacy| register?(state_id, annotate_manifestation(state_id, inadequacy)) end end worklist = @annotation_lists.each_with_index.flat_map do |annotations, state_id| annotations.map { |annotation| [state_id, annotation] } end cursor = 0 while cursor < worklist.length state_id, annotation = worklist.fetch(cursor) cursor += 1 @goto_follows.predecessors.fetch(state_id).each do |previous| derived = annotate_predecessor(previous, state_id, annotation) worklist << [previous, derived] if register?(previous, derived) end end @annotation_lists end |
#build_inadequacies(state_id) ⇒ Array[Inadequacy]
73 74 75 76 77 78 79 80 81 82 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 73 def build_inadequacies(state_id) @grammar.terminals.filter_map do |terminal| contributions = contributions_for(state_id, terminal.id) next if contributions.length < 2 inadequacy = Inadequacy.new(state: state_id, token: terminal.id, contributions: contributions, id: @next_id) @next_id += 1 inadequacy end end |
#completed_kernel_mask(state_id, production_id, length) ⇒ Integer
153 154 155 156 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 153 def completed_kernel_mask(state_id, production_id, length) index = kernel_index_of(state_id, production_id, length) index ? (1 << index) : 0 end |
#compute_lhs_contributions(state_id, lhs_id, token_id) ⇒ Integer?
159 160 161 162 163 164 165 166 167 168 169 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 159 def compute_lhs_contributions(state_id, lhs_id, token_id) goto_id = @goto_follows.goto_for(state_id, lhs_id) raise Ibex::Error, "missing goto for #{lhs_id} from #{state_id}" unless goto_id return nil if @goto_follows.always_follows.fetch(goto_id).anybits?(1 << token_id) mask = 0 Bits.each_set_bit(@goto_follows.follow_kernel_items.fetch(goto_id)) do |index| mask |= 1 << index if @item_lookaheads.fetch(state_id, index).anybits?(1 << token_id) end mask end |
#contributions_for(state_id, token_id) ⇒ Array[[ Symbol, Integer? ]]
85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 85 def contributions_for(state_id, token_id) reductions = @items.fetch(state_id).filter_map do |(production_id, dot), lookaheads| next unless dot == rhs_for(production_id).length && lookaheads.include?(token_id) production_id end.sort contributions = reductions.select(&:negative?).map { |production_id| [:accept, production_id] } symbol = @grammar.symbol_by_id(token_id) contributions << [:shift, nil] if symbol&.terminal? && @transitions.fetch(state_id).key?(token_id) contributions.concat(reductions.reject(&:negative?).map { |production_id| [:reduce, production_id] }) contributions end |
#kernel_index_of(state_id, production_id, dot) ⇒ Integer?
205 206 207 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 205 def kernel_index_of(state_id, production_id, dot) @kernel_cores.fetch(state_id).index([production_id, dot]) end |
#kernel_items(items) ⇒ Array[item_core]
200 201 202 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 200 def kernel_items(items) items.select { |production_id, dot| production_id.negative? || dot.positive? }.to_a.sort end |
#lhs_for(production_id) ⇒ Integer
186 187 188 189 190 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 186 def lhs_for(production_id) return @grammar.symbol(@grammar.starts.fetch(-production_id - 1)).id if production_id.negative? @grammar.productions.fetch(production_id).lhs end |
#project_row(state_id, cores, row, token) ⇒ Integer
137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 137 def project_row(state_id, cores, row, token) Bits.each_set_bit(row).reduce(0) do |mask, index| production_id, dot = cores.fetch(index) if dot > 1 previous = kernel_index_of(state_id, production_id, dot - 1) next mask unless previous && item_lookaheads.fetch(state_id, previous).anybits?(1 << token) mask | (1 << previous) else lhs_mask = compute_lhs_contributions(state_id, lhs_for(production_id), token) mask | lhs_mask.to_i end end end |
#register?(state_id, annotation) ⇒ Boolean
172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 172 def register?(state_id, annotation) if SplitStability.split_stable?(annotation, @resolver) @split_stable_discarded += 1 return false end key = annotation.key return false unless @keys.fetch(state_id).add?(key) @annotation_lists.fetch(state_id) << annotation true end |
#rhs_for(production_id) ⇒ Array[Integer]
193 194 195 196 197 |
# File 'lib/ibex/lalr/ielr/annotator.rb', line 193 def rhs_for(production_id) return [lhs_for(production_id)] if production_id.negative? @grammar.productions.fetch(production_id).rhs end |