From fb1b8ba2c552cf873e7233abd09d1a8970f4ecb8 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 | 5 +++-- spec/lrama/states_spec.rb | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/lrama/states.rb b/lib/lrama/states.rb index ddce627df..c3fa1735f 100644 --- a/lib/lrama/states.rb +++ b/lib/lrama/states.rb @@ -661,8 +661,9 @@ 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)] + bool_array = Bitmap.to_bool_array(follow_kernel_items, state.kernels.count) + state.follow_kernel_items[goto] = state.kernels.each_with_index.map {|kernel, i| + [kernel, bool_array[i]] }.to_h end end diff --git a/spec/lrama/states_spec.rb b/spec/lrama/states_spec.rb index 28c217e2a..6aeeb3c77 100644 --- a/spec/lrama/states_spec.rb +++ b/spec/lrama/states_spec.rb @@ -397,6 +397,34 @@ 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" } + + 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[0] => true, + state.kernels[1] => false, + state.kernels[2] => true, + }) + end + end + describe '#reads_relation' do it do path = "states/reads_relation.y"