diff --git a/lib/lrama/parser.rb b/lib/lrama/parser.rb index 6be3f2b9..0541baf6 100644 --- a/lib/lrama/parser.rb +++ b/lib/lrama/parser.rb @@ -2129,6 +2129,12 @@ def _reduce_103(val, _values, result) module_eval(<<'.,.,', 'parser.y', 447) def _reduce_106(val, _values, result) result = val[3].map do |rhs| + if rhs.reject {|sym| sym.is_a?(Grammar::Node::RuleRhs::Action) || sym.is_a?(Grammar::Node::RuleRhs::Prec) }.count > 1 + empties = rhs.select { |sym| sym.is_a?(Grammar::Node::RuleRhs::Empty) } + empties.each do |empty| + on_action_error("%empty on non-empty rule", empty) + end + end Grammar::Node::Rule.new( id: val[0], alias_name: val[1], @@ -2141,15 +2147,9 @@ def _reduce_106(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 460) +module_eval(<<'.,.,', 'parser.y', 466) def _reduce_107(val, _values, result) - if val[0].reject {|sym| sym.is_a?(Grammar::Node::RuleRhs::Action) || sym.is_a?(Grammar::Node::RuleRhs::Prec) }.count > 1 - empties = val[0].select { |sym| sym.is_a?(Grammar::Node::RuleRhs::Empty) } - empties.each do |empty| - on_action_error("%empty on non-empty rule", empty) - end - end - result = [val[0]] + result = [val[0]] result end diff --git a/parser.y b/parser.y index 2250abb3..237dcfc0 100644 --- a/parser.y +++ b/parser.y @@ -446,6 +446,12 @@ rule IDENT_COLON named_ref? ":" rhs_list { result = val[3].map do |rhs| + if rhs.reject {|sym| sym.is_a?(Grammar::Node::RuleRhs::Action) || sym.is_a?(Grammar::Node::RuleRhs::Prec) }.count > 1 + empties = rhs.select { |sym| sym.is_a?(Grammar::Node::RuleRhs::Empty) } + empties.each do |empty| + on_action_error("%empty on non-empty rule", empty) + end + end Grammar::Node::Rule.new( id: val[0], alias_name: val[1], @@ -458,12 +464,6 @@ rule rhs_list: rhs { - if val[0].reject {|sym| sym.is_a?(Grammar::Node::RuleRhs::Action) || sym.is_a?(Grammar::Node::RuleRhs::Prec) }.count > 1 - empties = val[0].select { |sym| sym.is_a?(Grammar::Node::RuleRhs::Empty) } - empties.each do |empty| - on_action_error("%empty on non-empty rule", empty) - end - end result = [val[0]] } | rhs_list "|" rhs diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index cbccdc13..0b44b05b 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -4601,6 +4601,31 @@ class : keyword_class tSTRING keyword_end { code 1 } ERROR end end + + context "when %empty is misused in a later alternative" do + [ + ["A | B %empty", 13], + ["A | %empty B", 11], + ["A | %empty %empty", 11], + ["A | A | B %empty", 17], + ].each do |rhs, column| + it "rejects #{rhs}" do + y = <<~INPUT + %token A B + %% + start: #{rhs}; + INPUT + + parser = Lrama::Parser.new(y, "error_messages/parse.y") + + expect { parser.parse }.to raise_error(ParseError, <<~ERROR) + error_messages/parse.y:3:#{column}: %empty on non-empty rule + 3 | start: #{rhs}; + | #{' ' * column}^~~~~~ + ERROR + end + end + end end end