diff --git a/lib/lrama/grammar/rule_builder.rb b/lib/lrama/grammar/rule_builder.rb index 34fdca6c8..b1a1d9d6e 100644 --- a/lib/lrama/grammar/rule_builder.rb +++ b/lib/lrama/grammar/rule_builder.rb @@ -245,13 +245,12 @@ def numberize_references ref.index = ref.number end - # TODO: Need to check index of @ too? - next if ref.type == :at - if ref.index # TODO: Prohibit $0 even so Bison allows it? # See: https://www.gnu.org/software/bison/manual/html_node/Actions.html token.invalid_ref(ref, "Can not refer following component. #{ref.index} >= #{i}.") if ref.index >= i + next if ref.type == :at + rhs[ref.index - 1].referred = true end end diff --git a/spec/fixtures/integration/contains_at_reference.y b/spec/fixtures/integration/contains_at_reference.y index eadc25dee..2af065f8c 100644 --- a/spec/fixtures/integration/contains_at_reference.y +++ b/spec/fixtures/integration/contains_at_reference.y @@ -14,7 +14,7 @@ static int yyerror(YYLTYPE *loc, const char *str); %% -program : /* empty */ { (void)@1; } +program : /* empty */ { (void)@0; } ; %% diff --git a/spec/lrama/grammar/rule_builder_spec.rb b/spec/lrama/grammar/rule_builder_spec.rb index 7293f7b05..00f43bf45 100644 --- a/spec/lrama/grammar/rule_builder_spec.rb +++ b/spec/lrama/grammar/rule_builder_spec.rb @@ -283,6 +283,41 @@ end end + context "location variables in mid action rule refer to following component" do + let(:text) { "class : keyword_class { @3; } tSTRING keyword_end { $class = $1; }" } + let(:grammar_file) { Lrama::Lexer::GrammarFile.new(path, text) } + let(:location_1) { Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 0, last_line: 1, last_column: 5) } + let(:location_2) { Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 8, last_line: 1, last_column: 21) } + let(:location_3) { Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 23, last_line: 1, last_column: 28) } + let(:location_4) { Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 30, last_line: 1, last_column: 37) } + let(:location_5) { Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 38, last_line: 1, last_column: 49) } + let(:location_6) { Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 51, last_line: 1, last_column: 65) } + let(:token_1) { Lrama::Lexer::Token::Ident.new(s_value: "class", location: location_1) } + let(:token_2) { Lrama::Lexer::Token::Ident.new(s_value: "keyword_class", location: location_2) } + let(:token_3) { Lrama::Lexer::Token::UserCode.new(s_value: " @3; ", location: location_3) } + let(:token_4) { Lrama::Lexer::Token::Ident.new(s_value: "tSTRING", location: location_4) } + let(:token_5) { Lrama::Lexer::Token::Ident.new(s_value: "keyword_end", location: location_5) } + let(:token_6) { Lrama::Lexer::Token::UserCode.new(s_value: " $class = $1; ", location: location_6) } + + it "raises error" do + rule_builder.lhs = token_1 + rule_builder.add_rhs(token_2) + rule_builder.user_code = token_3 + rule_builder.add_rhs(token_4) + rule_builder.add_rhs(token_5) + rule_builder.user_code = token_6 + rule_builder.complete_input + + expected = <<~TEXT + parse.y:1:24: Can not refer following component. 3 >= 2. + 1 | class : keyword_class { @3; } tSTRING keyword_end { $class = $1; } + | ^~ + TEXT + + expect { rule_builder.send(:preprocess_references) }.to raise_error(expected) + end + end + context "variables refer with wrong name" do let(:text) { "class : keyword_class tSTRING keyword_end { $classes = $1; }" } let(:grammar_file) { Lrama::Lexer::GrammarFile.new(path, text) }