From 926b345790f89d07aabd6c692210174ff553962e Mon Sep 17 00:00:00 2001 From: ydah Date: Wed, 10 Apr 2024 05:19:58 +0900 Subject: [PATCH] Add support for adjusting Index to Inline --- lib/lrama/grammar.rb | 13 ++++++ lib/lrama/grammar/rule_builder.rb | 53 +++++++++++---------- spec/fixtures/inlining/resolve_index.y | 42 +++++++++++++++++ spec/lrama/parser_spec.rb | 64 ++++++++++++++++++++++++++ 4 files changed, 147 insertions(+), 25 deletions(-) create mode 100644 spec/fixtures/inlining/resolve_index.y diff --git a/lib/lrama/grammar.rb b/lib/lrama/grammar.rb index a816b826..229f757b 100644 --- a/lib/lrama/grammar.rb +++ b/lib/lrama/grammar.rb @@ -141,6 +141,7 @@ def epilogue=(epilogue) end def prepare + resolve_inline_rules normalize_rules collect_symbols set_lhs_and_rhs @@ -289,6 +290,18 @@ def append_special_symbols @accept_symbol = term end + def resolve_inline_rules + while @rule_builders.any? {|r| r.has_inline_rules?(@parameterizing_rule_resolver) } do + @rule_builders.map! do |builder| + if builder.has_inline_rules?(@parameterizing_rule_resolver) + builder.resolve_inline_rules(@parameterizing_rule_resolver) + else + builder + end + end.flatten! + end + end + def normalize_rules # Add $accept rule to the top of rules lineno = @rule_builders.first ? @rule_builders.first.line : 0 diff --git a/lib/lrama/grammar/rule_builder.rb b/lib/lrama/grammar/rule_builder.rb index 10c44b34..6415af04 100644 --- a/lib/lrama/grammar/rule_builder.rb +++ b/lib/lrama/grammar/rule_builder.rb @@ -57,11 +57,7 @@ def complete_input def setup_rules(parameterizing_rule_resolver) preprocess_references unless @skip_preprocess_references - if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) } - resolve_inline(parameterizing_rule_resolver) - else - process_rhs(parameterizing_rule_resolver) - end + process_rhs(parameterizing_rule_resolver) build_rules end @@ -69,6 +65,30 @@ def rules @parameterizing_rules + @inline_rules + @midrule_action_rules + @rules end + def has_inline_rules?(parameterizing_rule_resolver) + rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) } + end + + def resolve_inline_rules(parameterizing_rule_resolver) + resolved_builders = [] + rhs.each_with_index do |token, i| + if inline_rule = parameterizing_rule_resolver.find_inline(token) + inline_rule.rhs_list.each do |inline_rhs| + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: lhs_tag, skip_preprocess_references: true) + resolve_inline_rhs(rule_builder, inline_rhs, i) + rule_builder.lhs = lhs + rule_builder.line = line + rule_builder.user_code = replace_inline_user_code(inline_rhs, i) + rule_builder.complete_input + rule_builder.setup_rules(parameterizing_rule_resolver) + resolved_builders << rule_builder + end + break + end + end + resolved_builders + end + private def freeze_rhs @@ -172,23 +192,6 @@ def lhs_s_value(token, bindings) "#{token.rule_name}_#{s_values.join('_')}" end - def resolve_inline(parameterizing_rule_resolver) - rhs.each_with_index do |token, i| - if inline_rule = parameterizing_rule_resolver.find_inline(token) - inline_rule.rhs_list.each_with_index do |inline_rhs| - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: lhs_tag, skip_preprocess_references: true) - resolve_inline_rhs(rule_builder, inline_rhs, i) - rule_builder.lhs = lhs - rule_builder.line = line - rule_builder.user_code = replace_inline_user_code(inline_rhs, i) - rule_builder.complete_input - rule_builder.setup_rules(parameterizing_rule_resolver) - @rule_builders_for_inline_rules << rule_builder - end - end - end - end - def resolve_inline_rhs(rule_builder, inline_rhs, index) rhs.each_with_index do |token, i| if index == i @@ -204,6 +207,9 @@ def replace_inline_user_code(inline_rhs, index) return user_code if user_code.nil? code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value) + user_code.references[(index+1)..-1]&.each do |ref| + code = code.gsub(/\$#{ref.index}/, "$#{ref.index + (inline_rhs.symbols.count-1)}") + end Lrama::Lexer::Token::UserCode.new(s_value: code, location: user_code.location) end @@ -238,9 +244,6 @@ def numberize_references end if ref.number - # TODO: When Inlining is implemented, for example, if `$1` is expanded to multiple RHS tokens, - # `$2` needs to access `$2 + n` to actually access it. So, after the Inlining implementation, - # it needs resolves from number to index. ref.index = ref.number end diff --git a/spec/fixtures/inlining/resolve_index.y b/spec/fixtures/inlining/resolve_index.y new file mode 100644 index 00000000..827f7964 --- /dev/null +++ b/spec/fixtures/inlining/resolve_index.y @@ -0,0 +1,42 @@ +/* + * This is comment for this file. + */ + +%{ +// Prologue +static int yylex(YYSTYPE *val, YYLTYPE *loc); +static int yyerror(YYLTYPE *loc, const char *str); +%} + +%union { + int i; +} + +%token NUM +%type expression + +%rule %inline op : '+' { + } + | '+' '=' { += } + ; + +%% + +expression : NUM + | expression op expression { $$ = $1 $2 $3; } + ; + +%% + +static int yylex(YYSTYPE *yylval, YYLTYPE *loc) +{ + return 0; +} + +static int yyerror(YYLTYPE *loc, const char *str) +{ + return 0; +} + +int main(int argc, char *argv[]) +{ +} diff --git a/spec/lrama/parser_spec.rb b/spec/lrama/parser_spec.rb index a32f6b42..099eb4b9 100644 --- a/spec/lrama/parser_spec.rb +++ b/spec/lrama/parser_spec.rb @@ -1988,6 +1988,70 @@ ]) end end + + context 'when inline with resolve index' do + let(:path) { "inlining/resolve_index.y" } + + it "expands inlining rules" do + expect(grammar.nterms.sort_by(&:number)).to match_symbols([ + Sym.new(id: T::Ident.new(s_value: "$accept"), alias_name: nil, number: 6, tag: nil, term: false, token_id: 0, nullable: false), + Sym.new(id: T::Ident.new(s_value: "expression"), alias_name: nil, number: 7, tag: T::Tag.new(s_value: ""), term: false, token_id: 1, nullable: false), + ]) + + expect(grammar.rules).to eq([ + Rule.new( + id: 2, + lhs: grammar.find_symbol_by_s_value!("$accept"), + rhs: [ + grammar.find_symbol_by_s_value!("expression"), + grammar.find_symbol_by_s_value!("YYEOF"), + ], + token_code: nil, + nullable: false, + precedence_sym: grammar.find_symbol_by_s_value!("YYEOF"), + lineno: 24, + ), + Rule.new( + id: 3, + lhs: grammar.find_symbol_by_s_value!("expression"), + rhs: [ + grammar.find_symbol_by_s_value!("NUM"), + ], + token_code: nil, + nullable: false, + precedence_sym: grammar.find_symbol_by_s_value!("NUM"), + lineno: 24, + ), + Rule.new( + id: 4, + lhs: grammar.find_symbol_by_s_value!("expression"), + rhs: [ + grammar.find_symbol_by_s_value!("expression"), + grammar.find_symbol_by_s_value!("'+'"), + grammar.find_symbol_by_s_value!("expression"), + ], + token_code: T::UserCode.new(s_value: " $$ = $1 + $3; "), + nullable: false, + precedence_sym: grammar.find_symbol_by_s_value!("'+'"), + lineno: 25, + ), + Rule.new( + id: 5, + lhs: grammar.find_symbol_by_s_value!("expression"), + rhs: [ + grammar.find_symbol_by_s_value!("expression"), + grammar.find_symbol_by_s_value!("'+'"), + grammar.find_symbol_by_s_value!("'='"), + grammar.find_symbol_by_s_value!("expression"), + ], + token_code: T::UserCode.new(s_value: " $$ = $1 += $4; "), + nullable: false, + precedence_sym: grammar.find_symbol_by_s_value!("'='"), + lineno: 25, + ), + ]) + end + end end it "; for rules is optional" do