From be3763d0b6f26fbda696e5257dd9b7a59579b273 Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 21 Oct 2026 20:07:10 +0900 Subject: [PATCH 1/2] Accept %param declarations for both lexer and parser --- lib/lrama/grammar.rb | 6 + lib/lrama/grammar/node.rb | 1 + lib/lrama/grammar/node/param_decl.rb | 18 ++ lib/lrama/lexer.rb | 1 + lib/lrama/parser.rb | 191 +++++++++--------- parser.y | 6 + .../lrama/grammar/node/param_decl.rbs | 14 ++ spec/fixtures/integration/params.y | 3 +- spec/lrama/integration_spec.rb | 2 +- spec/lrama/parser_spec.rb | 13 ++ 10 files changed, 161 insertions(+), 94 deletions(-) create mode 100644 lib/lrama/grammar/node/param_decl.rb create mode 100644 sig/generated/lrama/grammar/node/param_decl.rbs diff --git a/lib/lrama/grammar.rb b/lib/lrama/grammar.rb index 02ee9afcb..974f49eab 100644 --- a/lib/lrama/grammar.rb +++ b/lib/lrama/grammar.rb @@ -332,6 +332,12 @@ def _evaluate_nodes self.epilogue = node.code.s_value when Node::RequireDecl @required = true + when Node::ParamDecl + node.params.each do |param| + code = Grammar::Code::NoReferenceCode.new(type: :param, token_code: param).token_code.s_value + self.lex_param = code + self.parse_param = code + end when Node::LexParamDecl node.params.each do |param| self.lex_param = Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: param).token_code.s_value diff --git a/lib/lrama/grammar/node.rb b/lib/lrama/grammar/node.rb index 8166755e5..2739950c8 100644 --- a/lib/lrama/grammar/node.rb +++ b/lib/lrama/grammar/node.rb @@ -18,6 +18,7 @@ require_relative "node/locations_decl" require_relative "node/no_stdlib_decl" require_relative "node/nterm_decl" +require_relative "node/param_decl" require_relative "node/parameterized_rule" require_relative "node/parse_param_decl" require_relative "node/precedence_decl" diff --git a/lib/lrama/grammar/node/param_decl.rb b/lib/lrama/grammar/node/param_decl.rb new file mode 100644 index 000000000..1129c8dbb --- /dev/null +++ b/lib/lrama/grammar/node/param_decl.rb @@ -0,0 +1,18 @@ +# rbs_inline: enabled +# frozen_string_literal: true + +module Lrama + class Grammar + module Node + class ParamDecl < Base + attr_reader :params #: Array[Lexer::Token::UserCode] + + # @rbs (params: Array[Lexer::Token::UserCode], location: Lexer::Location) -> void + def initialize(params:, location:) + @params = params + super(location: location) + end + end + end + end +end diff --git a/lib/lrama/lexer.rb b/lib/lrama/lexer.rb index c70b91ce2..65d2ab44c 100644 --- a/lib/lrama/lexer.rb +++ b/lib/lrama/lexer.rb @@ -42,6 +42,7 @@ class Lexer %require %printer %destructor + %param %lex-param %parse-param %initial-action diff --git a/lib/lrama/parser.rb b/lib/lrama/parser.rb index 6be3f2b9d..cdedbe632 100644 --- a/lib/lrama/parser.rb +++ b/lib/lrama/parser.rb @@ -655,7 +655,7 @@ def token_to_str(t) module Lrama class Parser < Racc::Parser -module_eval(<<'...end parser.y/module_eval...', 'parser.y', 598) +module_eval(<<'...end parser.y/module_eval...', 'parser.y', 604) include Lrama::Tracer::Duration @@ -965,7 +965,7 @@ def merge_locations(*locations) 2, 70, :_reduce_20, 3, 70, :_reduce_21, 5, 70, :_reduce_22, - 2, 70, :_reduce_none, + 2, 70, :_reduce_23, 2, 70, :_reduce_24, 2, 70, :_reduce_25, 3, 70, :_reduce_26, @@ -1400,14 +1400,14 @@ def _reduce_15(val, _values, result) # reduce 17 omitted -module_eval(<<'.,.,', 'parser.y', 108) +module_eval(<<'.,.,', 'parser.y', 114) def _reduce_18(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 108) +module_eval(<<'.,.,', 'parser.y', 114) def _reduce_19(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result @@ -1449,9 +1449,18 @@ def _reduce_22(val, _values, result) end .,., -# reduce 23 omitted +module_eval(<<'.,.,', 'parser.y', 66) + def _reduce_23(val, _values, result) + result = Grammar::Node::ParamDecl.new( + params: val[1], + location: merge_locations(val[0].loc, val[1].last.loc) + ) + + result + end +.,., -module_eval(<<'.,.,', 'parser.y', 67) +module_eval(<<'.,.,', 'parser.y', 73) def _reduce_24(val, _values, result) result = Grammar::Node::LexParamDecl.new( params: val[1], @@ -1462,7 +1471,7 @@ def _reduce_24(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 74) +module_eval(<<'.,.,', 'parser.y', 80) def _reduce_25(val, _values, result) result = Grammar::Node::ParseParamDecl.new( params: val[1], @@ -1473,7 +1482,7 @@ def _reduce_25(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 81) +module_eval(<<'.,.,', 'parser.y', 87) def _reduce_26(val, _values, result) result = Grammar::Node::CodeDecl.new( id: val[1], @@ -1485,7 +1494,7 @@ def _reduce_26(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 89) +module_eval(<<'.,.,', 'parser.y', 95) def _reduce_27(val, _values, result) result = Grammar::Node::InitialActionDecl.new( code: val[1], @@ -1496,7 +1505,7 @@ def _reduce_27(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 96) +module_eval(<<'.,.,', 'parser.y', 102) def _reduce_28(val, _values, result) result = Grammar::Node::NoStdlibDecl.new( location: val[0].loc @@ -1506,7 +1515,7 @@ def _reduce_28(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 102) +module_eval(<<'.,.,', 'parser.y', 108) def _reduce_29(val, _values, result) result = Grammar::Node::LocationsDecl.new( location: val[0].loc @@ -1516,28 +1525,28 @@ def _reduce_29(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 179) +module_eval(<<'.,.,', 'parser.y', 185) def _reduce_30(val, _values, result) result = val result end .,., -module_eval(<<'.,.,', 'parser.y', 179) +module_eval(<<'.,.,', 'parser.y', 185) def _reduce_31(val, _values, result) result = val result end .,., -module_eval(<<'.,.,', 'parser.y', 179) +module_eval(<<'.,.,', 'parser.y', 185) def _reduce_32(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 179) +module_eval(<<'.,.,', 'parser.y', 185) def _reduce_33(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result @@ -1550,7 +1559,7 @@ def _reduce_33(val, _values, result) # reduce 36 omitted -module_eval(<<'.,.,', 'parser.y', 113) +module_eval(<<'.,.,', 'parser.y', 119) def _reduce_37(val, _values, result) result = Grammar::Node::UnionDecl.new( code: val[1], @@ -1561,7 +1570,7 @@ def _reduce_37(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 120) +module_eval(<<'.,.,', 'parser.y', 126) def _reduce_38(val, _values, result) result = Grammar::Node::DestructorDecl.new( ident_or_tags: val[2].flatten, @@ -1573,7 +1582,7 @@ def _reduce_38(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 128) +module_eval(<<'.,.,', 'parser.y', 134) def _reduce_39(val, _values, result) result = Grammar::Node::PrinterDecl.new( ident_or_tags: val[2].flatten, @@ -1585,7 +1594,7 @@ def _reduce_39(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 136) +module_eval(<<'.,.,', 'parser.y', 142) def _reduce_40(val, _values, result) result = Grammar::Node::ErrorTokenDecl.new( ident_or_tags: val[2].flatten, @@ -1597,7 +1606,7 @@ def _reduce_40(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 144) +module_eval(<<'.,.,', 'parser.y', 150) def _reduce_41(val, _values, result) result = Grammar::Node::AfterShiftDecl.new( id: val[1], @@ -1608,7 +1617,7 @@ def _reduce_41(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 151) +module_eval(<<'.,.,', 'parser.y', 157) def _reduce_42(val, _values, result) result = Grammar::Node::BeforeReduceDecl.new( id: val[1], @@ -1619,7 +1628,7 @@ def _reduce_42(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 158) +module_eval(<<'.,.,', 'parser.y', 164) def _reduce_43(val, _values, result) result = Grammar::Node::AfterReduceDecl.new( id: val[1], @@ -1630,7 +1639,7 @@ def _reduce_43(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 165) +module_eval(<<'.,.,', 'parser.y', 171) def _reduce_44(val, _values, result) result = Grammar::Node::AfterShiftErrorTokenDecl.new( id: val[1], @@ -1641,7 +1650,7 @@ def _reduce_44(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 172) +module_eval(<<'.,.,', 'parser.y', 178) def _reduce_45(val, _values, result) result = Grammar::Node::AfterPopStackDecl.new( id: val[1], @@ -1652,7 +1661,7 @@ def _reduce_45(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 181) +module_eval(<<'.,.,', 'parser.y', 187) def _reduce_46(val, _values, result) result = Grammar::Node::TokenDecl.new(tokens: val[1], location: merge_locations(val[0].loc, val[1].last.loc)) @@ -1660,7 +1669,7 @@ def _reduce_46(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 185) +module_eval(<<'.,.,', 'parser.y', 191) def _reduce_47(val, _values, result) result = Grammar::Node::TypeDecl.new( symbols: val[1], @@ -1671,7 +1680,7 @@ def _reduce_47(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 192) +module_eval(<<'.,.,', 'parser.y', 198) def _reduce_48(val, _values, result) result = Grammar::Node::NtermDecl.new( symbols: val[1], @@ -1682,7 +1691,7 @@ def _reduce_48(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 199) +module_eval(<<'.,.,', 'parser.y', 205) def _reduce_49(val, _values, result) result = Grammar::Node::PrecedenceDecl.new( type: :left, tokens: val[1], number: @precedence_number, @@ -1694,7 +1703,7 @@ def _reduce_49(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 207) +module_eval(<<'.,.,', 'parser.y', 213) def _reduce_50(val, _values, result) result = Grammar::Node::PrecedenceDecl.new( type: :right, tokens: val[1], number: @precedence_number, @@ -1706,7 +1715,7 @@ def _reduce_50(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 215) +module_eval(<<'.,.,', 'parser.y', 221) def _reduce_51(val, _values, result) result = Grammar::Node::PrecedenceDecl.new( type: :precedence, tokens: val[1], number: @precedence_number, @@ -1718,7 +1727,7 @@ def _reduce_51(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 223) +module_eval(<<'.,.,', 'parser.y', 229) def _reduce_52(val, _values, result) result = Grammar::Node::PrecedenceDecl.new( type: :nonassoc, tokens: val[1], number: @precedence_number, @@ -1730,7 +1739,7 @@ def _reduce_52(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 231) +module_eval(<<'.,.,', 'parser.y', 237) def _reduce_53(val, _values, result) result = Grammar::Node::StartDecl.new( id: val[1], @@ -1745,21 +1754,21 @@ def _reduce_53(val, _values, result) # reduce 55 omitted -module_eval(<<'.,.,', 'parser.y', 254) +module_eval(<<'.,.,', 'parser.y', 260) def _reduce_56(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 254) +module_eval(<<'.,.,', 'parser.y', 260) def _reduce_57(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 240) +module_eval(<<'.,.,', 'parser.y', 246) def _reduce_58(val, _values, result) val[1].each {|token| token.tag = val[0] @@ -1770,7 +1779,7 @@ def _reduce_58(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 247) +module_eval(<<'.,.,', 'parser.y', 253) def _reduce_59(val, _values, result) val[2].each {|token| token.tag = val[1] @@ -1785,7 +1794,7 @@ def _reduce_59(val, _values, result) # reduce 61 omitted -module_eval(<<'.,.,', 'parser.y', 255) +module_eval(<<'.,.,', 'parser.y', 261) def _reduce_62(val, _values, result) result = Grammar::Node::Token.new( id: val[0], token_id: val[1]&.s_value, alias_name: val[2]&.s_value, @@ -1796,7 +1805,7 @@ def _reduce_62(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 264) +module_eval(<<'.,.,', 'parser.y', 270) def _reduce_63(val, _values, result) result = Grammar::Node::ParameterizedRule.new( name: val[1].s_value, parameters: val[3], tag: val[5], @@ -1808,7 +1817,7 @@ def _reduce_63(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 274) +module_eval(<<'.,.,', 'parser.y', 280) def _reduce_64(val, _values, result) result = Grammar::Node::ParameterizedRule.new( name: val[2].s_value, parameters: [], @@ -1820,7 +1829,7 @@ def _reduce_64(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 282) +module_eval(<<'.,.,', 'parser.y', 288) def _reduce_65(val, _values, result) result = Grammar::Node::ParameterizedRule.new( name: val[2].s_value, parameters: val[4], @@ -1832,21 +1841,21 @@ def _reduce_65(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 290) +module_eval(<<'.,.,', 'parser.y', 296) def _reduce_66(val, _values, result) result = [val[0]] result end .,., -module_eval(<<'.,.,', 'parser.y', 291) +module_eval(<<'.,.,', 'parser.y', 297) def _reduce_67(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 296) +module_eval(<<'.,.,', 'parser.y', 302) def _reduce_68(val, _values, result) result = [val[0]] @@ -1854,7 +1863,7 @@ def _reduce_68(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 300) +module_eval(<<'.,.,', 'parser.y', 306) def _reduce_69(val, _values, result) result = val[0].append(val[2]) @@ -1870,7 +1879,7 @@ def _reduce_69(val, _values, result) # reduce 73 omitted -module_eval(<<'.,.,', 'parser.y', 306) +module_eval(<<'.,.,', 'parser.y', 312) def _reduce_74(val, _values, result) reset_precs result = [] @@ -1879,7 +1888,7 @@ def _reduce_74(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 311) +module_eval(<<'.,.,', 'parser.y', 317) def _reduce_75(val, _values, result) on_action_error("intermediate %prec in a rule", val[1]) if @trailing_prec_seen node = Grammar::Node::RuleRhs::Symbol.new( @@ -1892,7 +1901,7 @@ def _reduce_75(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 320) +module_eval(<<'.,.,', 'parser.y', 326) def _reduce_76(val, _values, result) on_action_error("intermediate %prec in a rule", val[1]) if @trailing_prec_seen node = Grammar::Node::RuleRhs::InstantiateRule.new( @@ -1905,7 +1914,7 @@ def _reduce_76(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 329) +module_eval(<<'.,.,', 'parser.y', 335) def _reduce_77(val, _values, result) on_action_error("intermediate %prec in a rule", val[1]) if @trailing_prec_seen node = Grammar::Node::RuleRhs::InstantiateRule.new( @@ -1919,7 +1928,7 @@ def _reduce_77(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 339) +module_eval(<<'.,.,', 'parser.y', 345) def _reduce_78(val, _values, result) node = Grammar::Node::RuleRhs::Action.new( code: val[1], alias_name: val[2], @@ -1931,7 +1940,7 @@ def _reduce_78(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 347) +module_eval(<<'.,.,', 'parser.y', 353) def _reduce_79(val, _values, result) on_action_error("multiple %prec in a rule", val[0]) if prec_seen? @prec_seen = true @@ -1951,21 +1960,21 @@ def _reduce_79(val, _values, result) # reduce 82 omitted -module_eval(<<'.,.,', 'parser.y', 380) +module_eval(<<'.,.,', 'parser.y', 386) def _reduce_83(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 380) +module_eval(<<'.,.,', 'parser.y', 386) def _reduce_84(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 361) +module_eval(<<'.,.,', 'parser.y', 367) def _reduce_85(val, _values, result) result = val[1].map do |id| Grammar::Node::Symbol.new( @@ -1978,7 +1987,7 @@ def _reduce_85(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 370) +module_eval(<<'.,.,', 'parser.y', 376) def _reduce_86(val, _values, result) syms = val[2].map do |id| Grammar::Node::Symbol.new( @@ -1996,7 +2005,7 @@ def _reduce_86(val, _values, result) # reduce 88 omitted -module_eval(<<'.,.,', 'parser.y', 386) +module_eval(<<'.,.,', 'parser.y', 392) def _reduce_89(val, _values, result) begin_c_declaration("}") @@ -2004,7 +2013,7 @@ def _reduce_89(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 390) +module_eval(<<'.,.,', 'parser.y', 396) def _reduce_90(val, _values, result) end_c_declaration @@ -2012,7 +2021,7 @@ def _reduce_90(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 394) +module_eval(<<'.,.,', 'parser.y', 400) def _reduce_91(val, _values, result) result = val[2] @@ -2020,42 +2029,42 @@ def _reduce_91(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 437) +module_eval(<<'.,.,', 'parser.y', 443) def _reduce_92(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 437) +module_eval(<<'.,.,', 'parser.y', 443) def _reduce_93(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 437) +module_eval(<<'.,.,', 'parser.y', 443) def _reduce_94(val, _values, result) result = val result end .,., -module_eval(<<'.,.,', 'parser.y', 437) +module_eval(<<'.,.,', 'parser.y', 443) def _reduce_95(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 437) +module_eval(<<'.,.,', 'parser.y', 443) def _reduce_96(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 400) +module_eval(<<'.,.,', 'parser.y', 406) def _reduce_97(val, _values, result) result = val[0].map do |id| Grammar::Node::Token.new( @@ -2068,7 +2077,7 @@ def _reduce_97(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 409) +module_eval(<<'.,.,', 'parser.y', 415) def _reduce_98(val, _values, result) result = val[0].flat_map do |tag, ids| ids.map do |id| @@ -2084,7 +2093,7 @@ def _reduce_98(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 421) +module_eval(<<'.,.,', 'parser.y', 427) def _reduce_99(val, _values, result) # result = [{tag: nil, tokens: val[0]}, {tag: val[1], tokens: val[2]}] result = val[0].map do |id| @@ -2108,14 +2117,14 @@ def _reduce_99(val, _values, result) # reduce 101 omitted -module_eval(<<'.,.,', 'parser.y', 445) +module_eval(<<'.,.,', 'parser.y', 451) def _reduce_102(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result end .,., -module_eval(<<'.,.,', 'parser.y', 445) +module_eval(<<'.,.,', 'parser.y', 451) def _reduce_103(val, _values, result) result = val[1] ? val[1].unshift(val[0]) : val result @@ -2126,7 +2135,7 @@ def _reduce_103(val, _values, result) # reduce 105 omitted -module_eval(<<'.,.,', 'parser.y', 447) +module_eval(<<'.,.,', 'parser.y', 453) def _reduce_106(val, _values, result) result = val[3].map do |rhs| Grammar::Node::Rule.new( @@ -2141,7 +2150,7 @@ 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) } @@ -2155,7 +2164,7 @@ def _reduce_107(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 470) +module_eval(<<'.,.,', 'parser.y', 476) def _reduce_108(val, _values, result) result = val[0].append(val[2]) @@ -2163,7 +2172,7 @@ def _reduce_108(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 476) +module_eval(<<'.,.,', 'parser.y', 482) def _reduce_109(val, _values, result) reset_precs result = [] @@ -2172,7 +2181,7 @@ def _reduce_109(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 481) +module_eval(<<'.,.,', 'parser.y', 487) def _reduce_110(val, _values, result) node = Grammar::Node::RuleRhs::Empty.new(location: val[1].loc) result.append(node) @@ -2181,7 +2190,7 @@ def _reduce_110(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 486) +module_eval(<<'.,.,', 'parser.y', 492) def _reduce_111(val, _values, result) on_action_error("intermediate %prec in a rule", val[1]) if @trailing_prec_seen node = Grammar::Node::RuleRhs::Symbol.new( @@ -2194,7 +2203,7 @@ def _reduce_111(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 495) +module_eval(<<'.,.,', 'parser.y', 501) def _reduce_112(val, _values, result) on_action_error("intermediate %prec in a rule", val[1]) if @trailing_prec_seen node = Grammar::Node::RuleRhs::InstantiateRule.new( @@ -2208,7 +2217,7 @@ def _reduce_112(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 505) +module_eval(<<'.,.,', 'parser.y', 511) def _reduce_113(val, _values, result) on_action_error("intermediate %prec in a rule", val[1]) if @trailing_prec_seen node = Grammar::Node::RuleRhs::InstantiateRule.new( @@ -2222,7 +2231,7 @@ def _reduce_113(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 515) +module_eval(<<'.,.,', 'parser.y', 521) def _reduce_114(val, _values, result) node = Grammar::Node::RuleRhs::Action.new( code: val[1], alias_name: val[2], tag: val[3], @@ -2234,7 +2243,7 @@ def _reduce_114(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 523) +module_eval(<<'.,.,', 'parser.y', 529) def _reduce_115(val, _values, result) on_action_error("multiple %prec in a rule", val[0]) if prec_seen? if val[0].empty? @@ -2253,21 +2262,21 @@ def _reduce_115(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 538) +module_eval(<<'.,.,', 'parser.y', 544) def _reduce_116(val, _values, result) result = "option" result end .,., -module_eval(<<'.,.,', 'parser.y', 539) +module_eval(<<'.,.,', 'parser.y', 545) def _reduce_117(val, _values, result) result = "nonempty_list" result end .,., -module_eval(<<'.,.,', 'parser.y', 540) +module_eval(<<'.,.,', 'parser.y', 546) def _reduce_118(val, _values, result) result = "list" result @@ -2278,7 +2287,7 @@ def _reduce_118(val, _values, result) # reduce 120 omitted -module_eval(<<'.,.,', 'parser.y', 545) +module_eval(<<'.,.,', 'parser.y', 551) def _reduce_121(val, _values, result) result = if val[1] [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[1].s_value, location: @lexer.location, args: val[0])] @@ -2290,28 +2299,28 @@ def _reduce_121(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 551) +module_eval(<<'.,.,', 'parser.y', 557) def _reduce_122(val, _values, result) result = val[0].append(val[2]) result end .,., -module_eval(<<'.,.,', 'parser.y', 552) +module_eval(<<'.,.,', 'parser.y', 558) def _reduce_123(val, _values, result) result = val[0].append(Lrama::Lexer::Token::InstantiateRule.new(s_value: val[2].s_value, location: @lexer.location, args: val[4])) result end .,., -module_eval(<<'.,.,', 'parser.y', 553) +module_eval(<<'.,.,', 'parser.y', 559) def _reduce_124(val, _values, result) result = [Lrama::Lexer::Token::InstantiateRule.new(s_value: val[0].s_value, location: @lexer.location, args: val[2])] result end .,., -module_eval(<<'.,.,', 'parser.y', 558) +module_eval(<<'.,.,', 'parser.y', 564) def _reduce_125(val, _values, result) if prec_seen? on_action_error("multiple User_code after %prec", val[0]) if @code_after_prec @@ -2323,7 +2332,7 @@ def _reduce_125(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 566) +module_eval(<<'.,.,', 'parser.y', 572) def _reduce_126(val, _values, result) end_c_declaration @@ -2331,7 +2340,7 @@ def _reduce_126(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 570) +module_eval(<<'.,.,', 'parser.y', 576) def _reduce_127(val, _values, result) result = val[2] @@ -2339,14 +2348,14 @@ def _reduce_127(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 573) +module_eval(<<'.,.,', 'parser.y', 579) def _reduce_128(val, _values, result) result = val[1].s_value result end .,., -module_eval(<<'.,.,', 'parser.y', 578) +module_eval(<<'.,.,', 'parser.y', 584) def _reduce_129(val, _values, result) begin_c_declaration('\Z') @@ -2354,7 +2363,7 @@ def _reduce_129(val, _values, result) end .,., -module_eval(<<'.,.,', 'parser.y', 582) +module_eval(<<'.,.,', 'parser.y', 588) def _reduce_130(val, _values, result) end_c_declaration result = Grammar::Node::EpilogueDecl.new(code: val[2], location: merge_locations(val[0].loc, val[2].loc)) @@ -2373,7 +2382,7 @@ def _reduce_130(val, _values, result) # reduce 135 omitted -module_eval(<<'.,.,', 'parser.y', 593) +module_eval(<<'.,.,', 'parser.y', 599) def _reduce_136(val, _values, result) result = Lrama::Lexer::Token::Ident.new(s_value: val[0].s_value, location: val[0].location) result diff --git a/parser.y b/parser.y index 2250abb34..332e492eb 100644 --- a/parser.y +++ b/parser.y @@ -63,6 +63,12 @@ rule ) } | "%param" param+ + { + result = Grammar::Node::ParamDecl.new( + params: val[1], + location: merge_locations(val[0].loc, val[1].last.loc) + ) + } | "%lex-param" param+ { result = Grammar::Node::LexParamDecl.new( diff --git a/sig/generated/lrama/grammar/node/param_decl.rbs b/sig/generated/lrama/grammar/node/param_decl.rbs new file mode 100644 index 000000000..9393c8a43 --- /dev/null +++ b/sig/generated/lrama/grammar/node/param_decl.rbs @@ -0,0 +1,14 @@ +# Generated from lib/lrama/grammar/node/param_decl.rb with RBS::Inline + +module Lrama + class Grammar + module Node + class ParamDecl < Base + attr_reader params: Array[Lexer::Token::UserCode] + + # @rbs (params: Array[Lexer::Token::UserCode], location: Lexer::Location) -> void + def initialize: (params: Array[Lexer::Token::UserCode], location: Lexer::Location) -> void + end + end + end +end diff --git a/spec/fixtures/integration/params.y b/spec/fixtures/integration/params.y index d9ad53873..256d50516 100644 --- a/spec/fixtures/integration/params.y +++ b/spec/fixtures/integration/params.y @@ -14,8 +14,7 @@ static int yyerror(YYLTYPE *loc, int parse_param, const char *str); %} -%lex-param {int parse_param} -%parse-param {int parse_param} +%param {int parse_param} %union { int val; diff --git a/spec/lrama/integration_spec.rb b/spec/lrama/integration_spec.rb index c0bba17f4..e07581c3e 100644 --- a/spec/lrama/integration_spec.rb +++ b/spec/lrama/integration_spec.rb @@ -100,7 +100,7 @@ def generate_object(grammar_file_path, c_path, obj_path, command_args: []) test_parser("contains_at_reference", "", "") end - describe "YYDEBUG, %lex-param, %parse-param option are enabled" do + describe "YYDEBUG and %param are enabled" do it "returns 9 for '(1+2)*3'" do test_parser("params", "(1+2)*3", "=> 9") end diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index cbccdc13a..2366cb32b 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -118,6 +118,19 @@ end describe '#parse' do + it 'uses %param for both lexer and parser parameters' do + grammar = Lrama::Parser.new(<<~'GRAMMAR', "param.y").parse + %param {struct context *ctx} + %% + start: ; + GRAMMAR + grammar.prepare + grammar.validate! + + expect(grammar.lex_param).to eq("struct context *ctx") + expect(grammar.parse_param).to eq("struct context *ctx") + end + it "no_union" do path = "common/no_union.y" y = File.read(fixture_path(path)) From 48dfb08df20dfcf63f4f188e4c717ad3917b7908 Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 21 Oct 2026 20:09:35 +0900 Subject: [PATCH 2/2] Preserve all lexer and parser parameters --- lib/lrama/grammar.rb | 14 ++-- lib/lrama/output.rb | 36 +++------- sig/generated/lrama/grammar.rbs | 4 +- spec/fixtures/integration/params.l | 4 +- spec/fixtures/integration/params.y | 30 ++++++--- spec/lrama/integration_spec.rb | 6 +- spec/lrama/output_spec.rb | 101 ++++++++++++++++++++--------- spec/lrama/parser_spec.rb | 8 +-- 8 files changed, 121 insertions(+), 82 deletions(-) diff --git a/lib/lrama/grammar.rb b/lib/lrama/grammar.rb index 974f49eab..2005fbc21 100644 --- a/lib/lrama/grammar.rb +++ b/lib/lrama/grammar.rb @@ -85,8 +85,8 @@ class Grammar attr_accessor :expect #: Integer attr_accessor :printers #: Array[Printer] attr_accessor :error_tokens #: Array[ErrorToken] - attr_accessor :lex_param #: String - attr_accessor :parse_param #: String + attr_reader :lex_params #: Array[String] + attr_reader :parse_params #: Array[String] attr_accessor :initial_action #: Grammar::Code::InitialActionCode attr_accessor :after_shift #: Lexer::Token::Base attr_accessor :before_reduce #: Lexer::Token::Base @@ -119,6 +119,8 @@ def initialize(rule_counter, midrule_action_counter, locations, define = {}) @printers = [] @destructors = [] @error_tokens = [] + @lex_params = [] + @parse_params = [] @symbols_resolver = Grammar::Symbols::Resolver.new @types = [] @rule_builders = [] @@ -335,16 +337,16 @@ def _evaluate_nodes when Node::ParamDecl node.params.each do |param| code = Grammar::Code::NoReferenceCode.new(type: :param, token_code: param).token_code.s_value - self.lex_param = code - self.parse_param = code + lex_params << code + parse_params << code end when Node::LexParamDecl node.params.each do |param| - self.lex_param = Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: param).token_code.s_value + lex_params << Grammar::Code::NoReferenceCode.new(type: :lex_param, token_code: param).token_code.s_value end when Node::ParseParamDecl node.params.each do |param| - self.parse_param = Grammar::Code::NoReferenceCode.new(type: :parse_param, token_code: param).token_code.s_value + parse_params << Grammar::Code::NoReferenceCode.new(type: :parse_param, token_code: param).token_code.s_value end when Node::UnionDecl self.set_union( diff --git a/lib/lrama/output.rb b/lib/lrama/output.rb index 24cf725c7..a6038a7b4 100644 --- a/lib/lrama/output.rb +++ b/lib/lrama/output.rb @@ -265,24 +265,16 @@ def omit_blanks(param) # b4_parse_param def parse_param - if @grammar.parse_param - omit_blanks(@grammar.parse_param) - else - "" - end + @grammar.parse_params.map { |param| omit_blanks(param) }.join(", ") end def lex_param - if @grammar.lex_param - omit_blanks(@grammar.lex_param) - else - "" - end + @grammar.lex_params.map { |param| omit_blanks(param) }.join(", ") end # b4_user_formals def user_formals - if @grammar.parse_param + if @grammar.parse_params.any? ", #{parse_param}" else "" @@ -291,7 +283,7 @@ def user_formals # b4_user_args def user_args - if @grammar.parse_param + if @grammar.parse_params.any? ", #{parse_param_name}" else "" @@ -303,19 +295,11 @@ def extract_param_name(param) end def parse_param_name - if @grammar.parse_param - extract_param_name(parse_param) - else - "" - end + @grammar.parse_params.map { |param| extract_param_name(param) }.join(", ") end def lex_param_name - if @grammar.lex_param - extract_param_name(lex_param) - else - "" - end + @grammar.lex_params.map { |param| extract_param_name(param) }.join(", ") end # b4_parse_param_use @@ -325,9 +309,7 @@ def parse_param_use(val, loc) YY_USE (#{loc}); STR - if @grammar.parse_param - str << " YY_USE (#{parse_param_name});" - end + str << @grammar.parse_params.map { |param| " YY_USE (#{extract_param_name(param)});" }.join("\n") str end @@ -337,7 +319,7 @@ def yylex_formals ary = ["&yylval"] ary << "&yylloc" if @grammar.locations - if @grammar.lex_param + if @grammar.lex_params.any? ary << lex_param_name end @@ -357,7 +339,7 @@ def table_value_equals(table, value, literal, symbol) def yyerror_args ary = ["&yylloc"] - if @grammar.parse_param + if @grammar.parse_params.any? ary << parse_param_name end diff --git a/sig/generated/lrama/grammar.rbs b/sig/generated/lrama/grammar.rbs index 5852949a7..067e13e64 100644 --- a/sig/generated/lrama/grammar.rbs +++ b/sig/generated/lrama/grammar.rbs @@ -104,9 +104,9 @@ module Lrama attr_accessor error_tokens: Array[ErrorToken] - attr_accessor lex_param: String + attr_reader lex_params: Array[String] - attr_accessor parse_param: String + attr_reader parse_params: Array[String] attr_accessor initial_action: Grammar::Code::InitialActionCode diff --git a/spec/fixtures/integration/params.l b/spec/fixtures/integration/params.l index 5fc6c60f2..fdaf1e3dd 100644 --- a/spec/fixtures/integration/params.l +++ b/spec/fixtures/integration/params.l @@ -6,7 +6,7 @@ #include #include "params.h" -#define YY_DECL int yylex (YYSTYPE *yylval, YYLTYPE *yylloc, int parser_params) +#define YY_DECL int yylex (YYSTYPE *yylval, YYLTYPE *yylloc, int lexer_value, int shared, int offset, int *errors) int yycolumn = 0; @@ -23,6 +23,8 @@ NUMBER [0-9]+ %% {NUMBER} { + if (lexer_value != 7 || shared != 5 || offset != 11 || *errors) + return YYUNDEF; yylval->val = atoi(yytext); return NUM; } diff --git a/spec/fixtures/integration/params.y b/spec/fixtures/integration/params.y index 256d50516..2d194f22e 100644 --- a/spec/fixtures/integration/params.y +++ b/spec/fixtures/integration/params.y @@ -2,19 +2,23 @@ #define YYDEBUG 1 #include -#define YY_LOCATION_PRINT(File, loc, p) ((void) 0) +#define YY_LOCATION_PRINT(File, loc, ...) ((void) 0) -#define YY_DECL yylex (YYSTYPE *lval, YYLTYPE *yylloc, int parser_params) +#define YY_DECL yylex (YYSTYPE *lval, YYLTYPE *yylloc, int lexer_value, int shared, int offset, int *errors) #include "params.h" #include "params-lexer.h" -extern int yylex(YYSTYPE *lval, YYLTYPE *yylloc, int parser_params); -static int yyerror(YYLTYPE *loc, int parse_param, const char *str); +static int lexer_value = 7; +extern int yylex(YYSTYPE *lval, YYLTYPE *yylloc, int lexer_value, int shared, int offset, int *errors); +static int yyerror(YYLTYPE *loc, int parser_value, int shared, int offset, int *errors, const char *str); %} -%param {int parse_param} +%lex-param {int lexer_value} +%parse-param {int parser_value} +%param {int shared} {int offset} +%param {int *errors} %union { int val; @@ -30,7 +34,11 @@ static int yyerror(YYLTYPE *loc, int parse_param, const char *str); %% program : /* empty */ - | expr { printf("=> %d", $1); } + | expr { + if (parser_value != 3 || shared != 5 || offset != 11 || *errors) + YYABORT; + printf("=> %d", $1); + } ; expr : NUM | expr '+' expr { $$ = $1 + $3; } @@ -42,19 +50,23 @@ expr : NUM %% -static int yyerror(YYLTYPE *loc, int parse_param, const char *str) { +static int yyerror(YYLTYPE *loc, int parser_value, int shared, int offset, int *errors, const char *str) { + if (parser_value == 3 && shared == 5 && offset == 11) + ++*errors; fprintf(stderr, "parse error: %s\\n", str); return 0; } int main(int argc, char *argv[]) { + int errors = 0; if (argc == 2) { yy_scan_string(argv[1]); } - if (yyparse(0)) { + if (yyparse(3, 5, 11, &errors)) { + printf("errors: %d", errors); fprintf(stderr, "syntax error\n"); return 1; } - return 0; + return errors != 0; } diff --git a/spec/lrama/integration_spec.rb b/spec/lrama/integration_spec.rb index e07581c3e..651d4e926 100644 --- a/spec/lrama/integration_spec.rb +++ b/spec/lrama/integration_spec.rb @@ -100,10 +100,14 @@ def generate_object(grammar_file_path, c_path, obj_path, command_args: []) test_parser("contains_at_reference", "", "") end - describe "YYDEBUG and %param are enabled" do + describe "YYDEBUG and multiple parameters are enabled" do it "returns 9 for '(1+2)*3'" do test_parser("params", "(1+2)*3", "=> 9") end + + it "passes all parser parameters to yyerror" do + test_parser("params", "(1+)", "errors: 1", expect_success: false) + end end describe "named references" do diff --git a/spec/lrama/output_spec.rb b/spec/lrama/output_spec.rb index 5fa1d04f0..46bee7a97 100644 --- a/spec/lrama/output_spec.rb +++ b/spec/lrama/output_spec.rb @@ -29,24 +29,61 @@ let(:grammar_file_path) { fixture_path("common/basic.y") } let(:header_file_path) { "y.tab.h" } + context 'with multiple parameter declarations' do + let(:text) do + <<~'GRAMMAR' + %lex-param {int lex_only} {char *input} + %parse-param {int parse_only} + %param {int shared} {void *context} + %parse-param {int *result} + %param {int last} + %% + start: ; + GRAMMAR + end + + it 'preserves declaration order in function signatures and calls' do + expect(output.lex_param).to eq("int lex_only, char *input, int shared, void *context, int last") + expect(output.parse_param).to eq("int parse_only, int shared, void *context, int *result, int last") + expect(output.lex_param_name).to eq("lex_only, input, shared, context, last") + expect(output.parse_param_name).to eq("parse_only, shared, context, result, last") + expect(output.user_formals).to eq(", int parse_only, int shared, void *context, int *result, int last") + expect(output.user_args).to eq(", parse_only, shared, context, result, last") + expect(output.yylex_formals).to eq("(&yylval, lex_only, input, shared, context, last)") + expect(output.yyerror_args).to eq("&yylloc, parse_only, shared, context, result, last") + end + + it 'marks each parser parameter as used separately' do + expect(output.parse_param_use("yyvaluep", "yylocationp")).to eq(<<-C.chomp) + YY_USE (yyvaluep); + YY_USE (yylocationp); + YY_USE (parse_only); + YY_USE (shared); + YY_USE (context); + YY_USE (result); + YY_USE (last); + C + end + end + describe "#parse_param" do it "returns declaration of parse param without blanks" do - allow(grammar).to receive(:parse_param).and_return("struct parser_params *p") + allow(grammar).to receive(:parse_params).and_return(["struct parser_params *p"]) expect(output.parse_param).to eq("struct parser_params *p") - allow(grammar).to receive(:parse_param).and_return(" struct parser_params *p ") + allow(grammar).to receive(:parse_params).and_return([" struct parser_params *p "]) expect(output.parse_param).to eq("struct parser_params *p") - allow(grammar).to receive(:parse_param).and_return("int i") + allow(grammar).to receive(:parse_params).and_return(["int i"]) expect(output.parse_param).to eq("int i") - allow(grammar).to receive(:parse_param).and_return(" int i ") + allow(grammar).to receive(:parse_params).and_return([" int i "]) expect(output.parse_param).to eq("int i") - allow(grammar).to receive(:parse_param).and_return("int parse_param") + allow(grammar).to receive(:parse_params).and_return(["int parse_param"]) expect(output.parse_param).to eq("int parse_param") - allow(grammar).to receive(:parse_param).and_return(" int parse_param ") + allow(grammar).to receive(:parse_params).and_return([" int parse_param "]) expect(output.parse_param).to eq("int parse_param") end end @@ -54,29 +91,29 @@ describe "#user_formals" do context "when parse_param exists" do it "returns declaration of parse param without blanks with a leading comma" do - allow(grammar).to receive(:parse_param).and_return("struct parser_params *p") + allow(grammar).to receive(:parse_params).and_return(["struct parser_params *p"]) expect(output.user_formals).to eq(", struct parser_params *p") - allow(grammar).to receive(:parse_param).and_return(" struct parser_params *p ") + allow(grammar).to receive(:parse_params).and_return([" struct parser_params *p "]) expect(output.user_formals).to eq(", struct parser_params *p") - allow(grammar).to receive(:parse_param).and_return("int i") + allow(grammar).to receive(:parse_params).and_return(["int i"]) expect(output.user_formals).to eq(", int i") - allow(grammar).to receive(:parse_param).and_return(" int i ") + allow(grammar).to receive(:parse_params).and_return([" int i "]) expect(output.user_formals).to eq(", int i") - allow(grammar).to receive(:parse_param).and_return("int parse_param") + allow(grammar).to receive(:parse_params).and_return(["int parse_param"]) expect(output.user_formals).to eq(", int parse_param") - allow(grammar).to receive(:parse_param).and_return(" int parse_param ") + allow(grammar).to receive(:parse_params).and_return([" int parse_param "]) expect(output.user_formals).to eq(", int parse_param") end end context "when parse_param does not exist" do it "returns blank" do - allow(grammar).to receive(:parse_param).and_return(nil) + allow(grammar).to receive(:parse_params).and_return([]) expect(output.user_formals).to eq("") end end @@ -85,29 +122,29 @@ describe "#user_args" do context "when parse_param exists" do it "returns name of parse param without blanks with a leading comma" do - allow(grammar).to receive(:parse_param).and_return("struct parser_params *p") + allow(grammar).to receive(:parse_params).and_return(["struct parser_params *p"]) expect(output.user_args).to eq(", p") - allow(grammar).to receive(:parse_param).and_return(" struct parser_params *p ") + allow(grammar).to receive(:parse_params).and_return([" struct parser_params *p "]) expect(output.user_args).to eq(", p") - allow(grammar).to receive(:parse_param).and_return("int i") + allow(grammar).to receive(:parse_params).and_return(["int i"]) expect(output.user_args).to eq(", i") - allow(grammar).to receive(:parse_param).and_return(" int i ") + allow(grammar).to receive(:parse_params).and_return([" int i "]) expect(output.user_args).to eq(", i") - allow(grammar).to receive(:parse_param).and_return("int parse_param") + allow(grammar).to receive(:parse_params).and_return(["int parse_param"]) expect(output.user_args).to eq(", parse_param") - allow(grammar).to receive(:parse_param).and_return(" int parse_param ") + allow(grammar).to receive(:parse_params).and_return([" int parse_param "]) expect(output.user_args).to eq(", parse_param") end end context "when parse_param does not exist" do it "returns blank" do - allow(grammar).to receive(:parse_param).and_return(nil) + allow(grammar).to receive(:parse_params).and_return([]) expect(output.user_args).to eq("") end end @@ -115,44 +152,44 @@ describe "#parse_param_name" do it "returns name of parse param" do - allow(grammar).to receive(:parse_param).and_return("struct parser_params *p") + allow(grammar).to receive(:parse_params).and_return(["struct parser_params *p"]) expect(output.parse_param_name).to eq("p") - allow(grammar).to receive(:parse_param).and_return(" struct parser_params *p ") + allow(grammar).to receive(:parse_params).and_return([" struct parser_params *p "]) expect(output.parse_param_name).to eq("p") - allow(grammar).to receive(:parse_param).and_return("int i") + allow(grammar).to receive(:parse_params).and_return(["int i"]) expect(output.parse_param_name).to eq("i") - allow(grammar).to receive(:parse_param).and_return(" int i ") + allow(grammar).to receive(:parse_params).and_return([" int i "]) expect(output.parse_param_name).to eq("i") - allow(grammar).to receive(:parse_param).and_return("int parse_param") + allow(grammar).to receive(:parse_params).and_return(["int parse_param"]) expect(output.parse_param_name).to eq("parse_param") - allow(grammar).to receive(:parse_param).and_return(" int parse_param ") + allow(grammar).to receive(:parse_params).and_return([" int parse_param "]) expect(output.parse_param_name).to eq("parse_param") end end describe "#lex_param_name" do it "returns name of lex param" do - allow(grammar).to receive(:lex_param).and_return("struct parser_params *p") + allow(grammar).to receive(:lex_params).and_return(["struct parser_params *p"]) expect(output.lex_param_name).to eq("p") - allow(grammar).to receive(:lex_param).and_return(" struct parser_params *p ") + allow(grammar).to receive(:lex_params).and_return([" struct parser_params *p "]) expect(output.lex_param_name).to eq("p") - allow(grammar).to receive(:lex_param).and_return("int i") + allow(grammar).to receive(:lex_params).and_return(["int i"]) expect(output.lex_param_name).to eq("i") - allow(grammar).to receive(:lex_param).and_return(" int i ") + allow(grammar).to receive(:lex_params).and_return([" int i "]) expect(output.lex_param_name).to eq("i") - allow(grammar).to receive(:lex_param).and_return("int lex_param") + allow(grammar).to receive(:lex_params).and_return(["int lex_param"]) expect(output.lex_param_name).to eq("lex_param") - allow(grammar).to receive(:lex_param).and_return(" int lex_param ") + allow(grammar).to receive(:lex_params).and_return([" int lex_param "]) expect(output.lex_param_name).to eq("lex_param") end end diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index 2366cb32b..95874fd3c 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -127,8 +127,8 @@ grammar.prepare grammar.validate! - expect(grammar.lex_param).to eq("struct context *ctx") - expect(grammar.parse_param).to eq("struct context *ctx") + expect(grammar.lex_params).to eq(["struct context *ctx"]) + expect(grammar.parse_params).to eq(["struct context *ctx"]) end it "no_union" do @@ -191,8 +191,8 @@ lineno: 19 ), ]) - expect(grammar.lex_param).to eq("struct lex_params *p") - expect(grammar.parse_param).to eq("struct parse_params *p") + expect(grammar.lex_params).to eq(["struct lex_params *p"]) + expect(grammar.parse_params).to eq(["struct parse_params *p"]) expect(grammar.initial_action).to eq(Code::InitialActionCode.new(type: :initial_action, token_code: T::UserCode.new(s_value: "\n initial_action_func(@$);\n"))) expect(grammar.symbols.sort_by(&:number)).to match_symbols([ Sym.new(id: T::Ident.new(s_value: "EOI"), alias_name: "\"EOI\"", number: 0, tag: nil, term: true, token_id: 0, nullable: false, precedence: nil, printer: nil),