diff --git a/lib/lrama/grammar/inline/resolver.rb b/lib/lrama/grammar/inline/resolver.rb index aca689ccf..c3761e450 100644 --- a/lib/lrama/grammar/inline/resolver.rb +++ b/lib/lrama/grammar/inline/resolver.rb @@ -66,11 +66,21 @@ def replace_user_code(rhs, index) user_code = @rule_builder.user_code return user_code if rhs.user_code.nil? || user_code.nil? - code = user_code.s_value.gsub(/\$#{index + 1}/, rhs.user_code.s_value) - user_code.references.each do |ref| - next if ref.index.nil? || ref.index <= index # nil は $$ の場合 - code = code.gsub(/\$#{ref.index}/, "$#{ref.index + (rhs.symbols.count - 1)}") - code = code.gsub(/@#{ref.index}/, "@#{ref.index + (rhs.symbols.count - 1)}") + inline_index = index + 1 + index_offset = rhs.symbols.count - 1 + code = user_code.s_value.dup + + user_code.references.reverse_each do |ref| + next if ref.index.nil? || ref.index < inline_index # nil は $$ の場合 + + reference = code[ref.first_column...ref.last_column] + replacement = + if ref.type == :dollar && ref.index == inline_index + rhs.user_code.s_value + else + reference.sub(/\d+\z/, (ref.index + index_offset).to_s) + end + code[ref.first_column...ref.last_column] = replacement end Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location) end diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index 6478fef4d..0581316e5 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -3448,6 +3448,28 @@ ]) end end + + it "renumbers each positional reference once when expanding an inline rule" do + y = <<~'GRAMMAR' + %union { int i; } + %token N + %type expression + + %rule %inline op : '+' '=' { "\1" } ; + + %% + + expression : op N N N N N N N N N + { $$ = f($1, $2, $3, $10, @1, @2, @10); } + ; + GRAMMAR + + grammar = Lrama::Parser.new(y, "parse.y").parse + grammar.prepare + rule = grammar.rules.last + + expect(rule.token_code.s_value).to eq(%q{ $$ = f( "\1" , $3, $4, $11, @2, @3, @11); }) + end end it "; for rules is optional" do