From 44be665ae279f785e1b9203573614bca06213650 Mon Sep 17 00:00:00 2001 From: ydah Date: Sat, 12 Oct 2024 15:50:01 +0900 Subject: [PATCH] Fix an error when `str.length` is greater than `length` This PR fix an error when `str.length` is greater than `length`. Reproduced by parse.y below: https://gist.github.com/ydah/70186de8a1ff14fa2cd16b338b9fc0e0 --- lib/lrama/counterexamples/derivation.rb | 2 +- spec/lrama/counterexamples_spec.rb | 86 +++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/lib/lrama/counterexamples/derivation.rb b/lib/lrama/counterexamples/derivation.rb index 423e58b8..b5c2fcba 100644 --- a/lib/lrama/counterexamples/derivation.rb +++ b/lib/lrama/counterexamples/derivation.rb @@ -44,7 +44,7 @@ def _render_for_report(derivation, offset, strings, index) str << "#{item.next_sym.display_name}" length = _render_for_report(derivation.left, len, strings, index + 1) # I want String#ljust! - str << " " * (length - str.length) + str << " " * (length - str.length) if length > str.length else str << " • #{item.symbols_after_dot.map(&:display_name).join(" ")} " return str.length diff --git a/spec/lrama/counterexamples_spec.rb b/spec/lrama/counterexamples_spec.rb index 2be7240a..14980bf1 100644 --- a/spec/lrama/counterexamples_spec.rb +++ b/spec/lrama/counterexamples_spec.rb @@ -299,6 +299,92 @@ 4: digit '+' digit • STR end + + context "when the grammar has a long rule name" do + let(:y) do + <<~STR + %{ + // Prologue + %} + + %union { + int i; + } + + %token digit + + %type stmt + %type expr1 + %type long_long_long_name_expr2 + + %% + + stmt : expr1 + | long_long_long_name_expr2 + ; + + expr1 : digit '+' digit + ; + + long_long_long_name_expr2 : digit '+' digit + ; + + %% + + STR + end + + it "build counterexamples of R/R conflicts" do + grammar = Lrama::Parser.new(y, "parse.y").parse + grammar.prepare + grammar.validate! + states = Lrama::States.new(grammar) + states.compute + counterexamples = Lrama::Counterexamples.new(states) + + # State 7 + # + # 3 expr1: digit '+' digit • ["end of file"] + # 4 long_long_long_name_expr2: digit '+' digit • ["end of file"] + # + # "end of file" reduce using rule 3 (expr1) + # "end of file" reduce using rule 4 (long_long_long_name_expr2) + state_7 = states.states[7] + examples = counterexamples.compute(state_7) + expect(examples.count).to eq 1 + example = examples[0] + + expect(example.type).to eq :reduce_reduce + # Reduce Conflict + expect(example.path1.map(&:to).map(&:item).map(&:to_s)).to eq([ + "$accept: • stmt \"end of file\" (rule 0)", + "stmt: • expr1 (rule 1)", + "expr1: • digit '+' digit (rule 3)", + "expr1: digit • '+' digit (rule 3)", + "expr1: digit '+' • digit (rule 3)", + "expr1: digit '+' digit • (rule 3)" + ]) + expect(example.derivations1.render_for_report).to eq(<<~STR.chomp) + 0: stmt "end of file" + 1: expr1 + 3: digit '+' digit • + STR + # Reduce Conflict + expect(example.path2.map(&:to).map(&:item).map(&:to_s)).to eq([ + "$accept: • stmt \"end of file\" (rule 0)", + "stmt: • long_long_long_name_expr2 (rule 2)", + "long_long_long_name_expr2: • digit '+' digit (rule 4)", + "long_long_long_name_expr2: digit • '+' digit (rule 4)", + "long_long_long_name_expr2: digit '+' • digit (rule 4)", + "long_long_long_name_expr2: digit '+' digit • (rule 4)" + ]) + expect(example.derivations2.render_for_report).to eq(<<~STR.chomp) + 0: stmt "end of file" + 2: long_long_long_name_expr2 + 4: digit '+' digit • + STR + end + end end describe "target state item will be start item when finding shift conflict shortest state items" do