From 7e33d06ca6ddaf6fba20f46d34cf4e3491806d15 Mon Sep 17 00:00:00 2001 From: ydah Date: Sat, 28 Mar 2026 22:59:41 +0900 Subject: [PATCH] Fix compute_follow_kernel_items to properly index bool array per kernel Each kernel was incorrectly mapped to the full `Bitmap.to_bool_array` result (`Array[bool]`) instead of its indexed value. Since arrays are always truthy in Ruby, `follow_kernel_items[goto][kernel]` evaluated to true for every kernel, making the filtering in `goto_follow_set` (Definition 3.39) and `lhs_contributions` (Definition 3.31) ineffective. The fix computes the bool array once and assigns each kernel its corresponding bool value via index, which also eliminates redundant `Bitmap.to_bool_array` calls (previously N calls per goto, now 1). --- lib/lrama/states.rb | 7 ++++--- spec/lrama/states_spec.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/lrama/states.rb b/lib/lrama/states.rb index ddce627df..b59b8a082 100644 --- a/lib/lrama/states.rb +++ b/lib/lrama/states.rb @@ -661,9 +661,10 @@ def compute_follow_kernel_items base_function = compute_goto_bitmaps Digraph.new(set, relation, base_function).compute.each do |goto, follow_kernel_items| state = goto.from_state - state.follow_kernel_items[goto] = state.kernels.map {|kernel| - [kernel, Bitmap.to_bool_array(follow_kernel_items, state.kernels.count)] - }.to_h + bool_array = Bitmap.to_bool_array(follow_kernel_items, state.kernels.count) + state.follow_kernel_items[goto] = state.kernels.each_with_index.to_h {|kernel, i| + [kernel, bool_array[i]] + } end end diff --git a/spec/lrama/states_spec.rb b/spec/lrama/states_spec.rb index 28c217e2a..1285670b1 100644 --- a/spec/lrama/states_spec.rb +++ b/spec/lrama/states_spec.rb @@ -397,6 +397,33 @@ class go to state 5 end end + describe '#compute_follow_kernel_items' do + it 'stores follow kernel items per kernel as booleans' do + path = "common/basic.y" + y = File.read(fixture_path(path)) + grammar = Lrama::Parser.new(y, path).parse + grammar.prepare + grammar.validate! + states = Lrama::States.new(grammar, Lrama::Tracer.new(Lrama::Logger.new)) + states.compute + + state = states.states.find {|s| s.id == 1 } + goto = state.nterm_transitions.find {|transition| transition.next_sym.name == "$@1" } + bool_array = [true, false, true] + + allow(states).to receive(:nterm_transitions).and_return([goto]) + allow(states).to receive(:compute_goto_internal_relation).and_return({ goto => [] }) + allow(states).to receive(:compute_goto_bitmaps).and_return({ goto => Lrama::Bitmap.from_array([0, 2]) }) + + state.follow_kernel_items.clear + states.send(:compute_follow_kernel_items) + + expect(state.follow_kernel_items[goto]).to eq( + state.kernels.each_with_index.to_h {|kernel, i| [kernel, bool_array[i]] } + ) + end + end + describe '#reads_relation' do it do path = "states/reads_relation.y"