From ba76b0312b855aa7712b16a752ca00f4efcecf30 Mon Sep 17 00:00:00 2001 From: ydah Date: Sat, 5 Sep 2026 15:11:07 +0900 Subject: [PATCH 1/2] Report undefined start symbols --- lib/lrama/grammar/symbols/resolver.rb | 2 +- lib/lrama/parser.rb | 2 +- parser.y | 2 +- spec/lrama/grammar/symbols/resolver_spec.rb | 10 +++++++--- spec/lrama/parser_spec.rb | 19 +++++++++++++++++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/lrama/grammar/symbols/resolver.rb b/lib/lrama/grammar/symbols/resolver.rb index 085a835d2..94831461d 100644 --- a/lib/lrama/grammar/symbols/resolver.rb +++ b/lib/lrama/grammar/symbols/resolver.rb @@ -112,7 +112,7 @@ def find_symbol_by_id(id) # @rbs (Lexer::Token::Base id) -> Grammar::Symbol def find_symbol_by_id!(id) - find_symbol_by_id(id) || (raise "Symbol not found. #{id}") + find_symbol_by_id(id) || (raise id.location.generate_error_message("symbol #{id.s_value} is used, but is not defined as a token and has no rules")) end # @rbs (Integer token_id) -> Grammar::Symbol? diff --git a/lib/lrama/parser.rb b/lib/lrama/parser.rb index 04632cbae..508d3ca98 100644 --- a/lib/lrama/parser.rb +++ b/lib/lrama/parser.rb @@ -2272,7 +2272,7 @@ def _reduce_130(val, _values, result) module_eval(<<'.,.,', 'parser.y', 500) def _reduce_136(val, _values, result) - result = Lrama::Lexer::Token::Ident.new(s_value: val[0].s_value) + result = Lrama::Lexer::Token::Ident.new(s_value: val[0].s_value, location: val[0].location) result end .,., diff --git a/parser.y b/parser.y index f256d5330..797c5a337 100644 --- a/parser.y +++ b/parser.y @@ -498,7 +498,7 @@ rule | STRING | "{...}" - string_as_id: STRING { result = Lrama::Lexer::Token::Ident.new(s_value: val[0].s_value) } + string_as_id: STRING { result = Lrama::Lexer::Token::Ident.new(s_value: val[0].s_value, location: val[0].location) } end ---- inner diff --git a/spec/lrama/grammar/symbols/resolver_spec.rb b/spec/lrama/grammar/symbols/resolver_spec.rb index a817de2c0..6edf4f210 100644 --- a/spec/lrama/grammar/symbols/resolver_spec.rb +++ b/spec/lrama/grammar/symbols/resolver_spec.rb @@ -132,10 +132,14 @@ end it "raises error if symbol not found" do - grammar_file = Lrama::Lexer::GrammarFile.new("foo/basic.y", "") - location = Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 2, last_line: 3, last_column: 4) + grammar_file = Lrama::Lexer::GrammarFile.new("foo/basic.y", " alias") + location = Lrama::Lexer::Location.new(grammar_file: grammar_file, first_line: 1, first_column: 2, last_line: 1, last_column: 7) symbol = Lrama::Grammar::Symbol.new(id: Lrama::Lexer::Token::Ident.new(s_value: "alias", location: location), alias_name: "alias", term: true) - expect { resolver.find_symbol_by_id!(symbol.id) }.to raise_error("Symbol not found. value: `alias`, location: foo/basic.y (1,2)-(3,4)") + expect { resolver.find_symbol_by_id!(symbol.id) }.to raise_error(<<~ERROR) + foo/basic.y:1:2: symbol alias is used, but is not defined as a token and has no rules + 1 | alias + | ^~~~~ + ERROR end end diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index 1a1d05a97..cbccdc13a 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -4434,6 +4434,25 @@ class : keyword_class tSTRING keyword_end { code 1 } end describe "error messages" do + context "when the start symbol is not defined" do + it "reports the symbol and its location" do + y = <<~INPUT + %start nosuch + %% + program: "a" ; + INPUT + + expect do + grammar = Lrama::Parser.new(y, "parse.y").parse + grammar.prepare + end.to raise_error(<<~ERROR) + parse.y:1:7: symbol nosuch is used, but is not defined as a token and has no rules + 1 | %start nosuch + | ^~~~~~ + ERROR + end + end + context "error_value has line number and column" do it "contains line number and column" do y = <<~INPUT From 00fcf977cb4b1b0ae06721fffc44cc71edbecb07 Mon Sep 17 00:00:00 2001 From: ydah Date: Sat, 5 Sep 2026 16:32:49 +0900 Subject: [PATCH 2/2] Handle unresolved symbols without locations --- lib/lrama/grammar/symbols/resolver.rb | 6 +++++- spec/lrama/grammar/symbols/resolver_spec.rb | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/lrama/grammar/symbols/resolver.rb b/lib/lrama/grammar/symbols/resolver.rb index 94831461d..ffa049832 100644 --- a/lib/lrama/grammar/symbols/resolver.rb +++ b/lib/lrama/grammar/symbols/resolver.rb @@ -112,7 +112,11 @@ def find_symbol_by_id(id) # @rbs (Lexer::Token::Base id) -> Grammar::Symbol def find_symbol_by_id!(id) - find_symbol_by_id(id) || (raise id.location.generate_error_message("symbol #{id.s_value} is used, but is not defined as a token and has no rules")) + symbol = find_symbol_by_id(id) + return symbol if symbol + + message = "symbol #{id.s_value} is used, but is not defined as a token and has no rules" + raise(id.location&.generate_error_message(message) || message) end # @rbs (Integer token_id) -> Grammar::Symbol? diff --git a/spec/lrama/grammar/symbols/resolver_spec.rb b/spec/lrama/grammar/symbols/resolver_spec.rb index 6edf4f210..ea7a4e38c 100644 --- a/spec/lrama/grammar/symbols/resolver_spec.rb +++ b/spec/lrama/grammar/symbols/resolver_spec.rb @@ -141,6 +141,13 @@ | ^~~~~ ERROR end + + it "raises error if symbol has no location" do + id = Lrama::Lexer::Token::Ident.new(s_value: "alias") + + expect { resolver.find_symbol_by_id!(id) } + .to raise_error("symbol alias is used, but is not defined as a token and has no rules") + end end describe "#find_symbol_by_token_id" do