lib/lrama/grammar/rule_builder.rb in lrama-0.6.9 vs lib/lrama/grammar/rule_builder.rb in lrama-0.6.10

- old
+ new

@@ -1,14 +1,17 @@ +# frozen_string_literal: true + module Lrama class Grammar class RuleBuilder attr_accessor :lhs, :line attr_reader :lhs_tag, :rhs, :user_code, :precedence_sym - def initialize(rule_counter, midrule_action_counter, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false) + def initialize(rule_counter, midrule_action_counter, parameterizing_rule_resolver, position_in_original_rule_rhs = nil, lhs_tag: nil, skip_preprocess_references: false) @rule_counter = rule_counter @midrule_action_counter = midrule_action_counter + @parameterizing_rule_resolver = parameterizing_rule_resolver @position_in_original_rule_rhs = position_in_original_rule_rhs @skip_preprocess_references = skip_preprocess_references @lhs = nil @lhs_tag = lhs_tag @@ -17,30 +20,24 @@ @precedence_sym = nil @line = nil @rules = [] @rule_builders_for_parameterizing_rules = [] @rule_builders_for_derived_rules = [] - @rule_builders_for_inline_rules = [] @parameterizing_rules = [] - @inline_rules = [] @midrule_action_rules = [] end def add_rhs(rhs) - if !@line - @line = rhs.line - end + @line ||= rhs.line flush_user_code @rhs << rhs end def user_code=(user_code) - if !@line - @line = user_code&.line - end + @line ||= user_code&.line flush_user_code @user_code = user_code end @@ -53,24 +50,47 @@ def complete_input freeze_rhs end - def setup_rules(parameterizing_rule_resolver) + def setup_rules 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 build_rules end def rules - @parameterizing_rules + @inline_rules + @midrule_action_rules + @rules + @parameterizing_rules + @midrule_action_rules + @rules end + def has_inline_rules? + rhs.any? { |token| @parameterizing_rule_resolver.find_inline(token) } + end + + def resolve_inline_rules + 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, @parameterizing_rule_resolver, lhs_tag: lhs_tag) + if token.is_a?(Lexer::Token::InstantiateRule) + resolve_inline_rhs(rule_builder, inline_rhs, i, Binding.new(inline_rule, token.args)) + else + resolve_inline_rhs(rule_builder, inline_rhs, i) + end + rule_builder.lhs = lhs + rule_builder.line = line + rule_builder.precedence_sym = precedence_sym + rule_builder.user_code = replace_inline_user_code(inline_rhs, i) + resolved_builders << rule_builder + end + break + end + end + resolved_builders + end + private def freeze_rhs @rhs.freeze end @@ -80,35 +100,29 @@ end def build_rules tokens = @replaced_rhs - if tokens - rule = Rule.new( - id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, - position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line - ) - @rules = [rule] - @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| - rule_builder.rules - end.flatten - @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| - rule_builder.rules - end.flatten - @midrule_action_rules.each do |r| - r.original_rule = rule - end - else - @inline_rules = @rule_builders_for_inline_rules.map do |rule_builder| - rule_builder.rules - end.flatten + rule = Rule.new( + id: @rule_counter.increment, _lhs: lhs, _rhs: tokens, lhs_tag: lhs_tag, token_code: user_code, + position_in_original_rule_rhs: @position_in_original_rule_rhs, precedence_sym: precedence_sym, lineno: line + ) + @rules = [rule] + @parameterizing_rules = @rule_builders_for_parameterizing_rules.map do |rule_builder| + rule_builder.rules + end.flatten + @midrule_action_rules = @rule_builders_for_derived_rules.map do |rule_builder| + rule_builder.rules + end.flatten + @midrule_action_rules.each do |r| + r.original_rule = rule end end # rhs is a mixture of variety type of tokens like `Ident`, `InstantiateRule`, `UserCode` and so on. # `#process_rhs` replaces some kind of tokens to `Ident` so that all `@replaced_rhs` are `Ident` or `Char`. - def process_rhs(parameterizing_rule_resolver) + def process_rhs return if @replaced_rhs @replaced_rhs = [] rhs.each_with_index do |token, i| @@ -116,44 +130,44 @@ when Lrama::Lexer::Token::Char @replaced_rhs << token when Lrama::Lexer::Token::Ident @replaced_rhs << token when Lrama::Lexer::Token::InstantiateRule - parameterizing_rule = parameterizing_rule_resolver.find_rule(token) + parameterizing_rule = @parameterizing_rule_resolver.find_rule(token) raise "Unexpected token. #{token}" unless parameterizing_rule bindings = Binding.new(parameterizing_rule, token.args) lhs_s_value = lhs_s_value(token, bindings) - if (created_lhs = parameterizing_rule_resolver.created_lhs(lhs_s_value)) + if (created_lhs = @parameterizing_rule_resolver.created_lhs(lhs_s_value)) @replaced_rhs << created_lhs else lhs_token = Lrama::Lexer::Token::Ident.new(s_value: lhs_s_value, location: token.location) @replaced_rhs << lhs_token - parameterizing_rule_resolver.created_lhs_list << lhs_token + @parameterizing_rule_resolver.created_lhs_list << lhs_token parameterizing_rule.rhs_list.each do |r| - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, lhs_tag: token.lhs_tag || parameterizing_rule.tag) + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, lhs_tag: token.lhs_tag || parameterizing_rule.tag) rule_builder.lhs = lhs_token r.symbols.each { |sym| rule_builder.add_rhs(bindings.resolve_symbol(sym)) } rule_builder.line = line rule_builder.precedence_sym = r.precedence_sym rule_builder.user_code = r.resolve_user_code(bindings) rule_builder.complete_input - rule_builder.setup_rules(parameterizing_rule_resolver) + rule_builder.setup_rules @rule_builders_for_parameterizing_rules << rule_builder end end when Lrama::Lexer::Token::UserCode prefix = token.referred ? "@" : "$@" tag = token.tag || lhs_tag new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s) @replaced_rhs << new_token - rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, i, lhs_tag: tag, skip_preprocess_references: true) + rule_builder = RuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, i, lhs_tag: tag, skip_preprocess_references: true) rule_builder.lhs = new_token rule_builder.user_code = token rule_builder.complete_input - rule_builder.setup_rules(parameterizing_rule_resolver) + rule_builder.setup_rules @rule_builders_for_derived_rules << rule_builder else raise "Unexpected token. #{token}" end @@ -170,31 +184,14 @@ end end "#{token.rule_name}_#{s_values.join('_')}" end - def resolve_inline(parameterizing_rule_resolver) + def resolve_inline_rhs(rule_builder, inline_rhs, index, bindings = nil) 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 - inline_rhs.symbols.each { |sym| rule_builder.add_rhs(sym) } + inline_rhs.symbols.each { |sym| rule_builder.add_rhs(bindings.nil? ? sym : bindings.resolve_symbol(sym)) } else rule_builder.add_rhs(token) end end end @@ -202,10 +199,15 @@ def replace_inline_user_code(inline_rhs, index) return user_code if inline_rhs.user_code.nil? return user_code if user_code.nil? code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value) + user_code.references.each do |ref| + next if ref.index.nil? || ref.index <= index # nil is a case for `$$` + code = code.gsub(/\$#{ref.index}/, "$#{ref.index + (inline_rhs.symbols.count-1)}") + 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 def numberize_references # Bison n'th component is 1-origin @@ -236,12 +238,9 @@ end end 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 # TODO: Need to check index of @ too? next if ref.type == :at