Skip to content

Commit

Permalink
Refactor inline rule handling and introduce InlinedRuleBuilder for be…
Browse files Browse the repository at this point in the history
…tter structure
  • Loading branch information
ydah committed Jan 2, 2025
1 parent 214c511 commit fb13465
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 57 deletions.
5 changes: 3 additions & 2 deletions lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
require_relative "grammar/counter"
require_relative "grammar/destructor"
require_relative "grammar/error_token"
require_relative "grammar/inlined_rule_builder"
require_relative "grammar/parameterizing_rule"
require_relative "grammar/percent_code"
require_relative "grammar/precedence"
Expand Down Expand Up @@ -294,10 +295,10 @@ def append_special_symbols
end

def resolve_inline_rules
while @rule_builders.any? {|r| r.has_inline_rules? } do
while @rule_builders.any?(&:has_inline_rules?) do
@rule_builders = @rule_builders.flat_map do |builder|
if builder.has_inline_rules?
builder.resolve_inline_rules
builder.build_inlined_builders
else
builder
end
Expand Down
15 changes: 15 additions & 0 deletions lib/lrama/grammar/binding.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ def resolve_symbol(symbol)
end
end

def concatenated_args_str(token)
"#{token.rule_name}_#{token_to_args_s_values(token).join('_')}"
end

private

def parameter_to_arg(symbol)
Expand All @@ -30,6 +34,17 @@ def parameter_to_arg(symbol)
end
arg
end

def token_to_args_s_values(token)
token.args.map do |arg|
resolved = resolve_symbol(arg)
if resolved.is_a?(Lexer::Token::InstantiateRule)
[resolved.s_value, resolved.args.map(&:s_value)]
else
resolved.s_value
end
end
end
end
end
end
77 changes: 77 additions & 0 deletions lib/lrama/grammar/inlined_rule_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# frozen_string_literal: true

module Lrama
class Grammar
class InlinedRuleBuilder
def initialize(rule_counter, midrule_action_counter, parameterizing_rule_resolver, lhs, lhs_tag, rhs, user_code, precedence_sym, line)
@rule_counter = rule_counter
@midrule_action_counter = midrule_action_counter
@parameterizing_rule_resolver = parameterizing_rule_resolver
@lhs = lhs
@lhs_tag = lhs_tag
@rhs = rhs
@user_code = user_code
@precedence_sym = precedence_sym
@line = line
end

def build_builders(rule, token, index)
binding = create_binding(rule, token)

rule.rhs_list.map do |rhs|
rule_builder = initialize_builder
resolve_rhs(rule_builder, rhs, index, binding)
finalize_builder(rule_builder, rhs, index)
rule_builder
end
end

private

def initialize_builder
RuleBuilder.new(
@rule_counter,
@midrule_action_counter,
@parameterizing_rule_resolver,
lhs_tag: @lhs_tag
)
end

def create_binding(rule, token)
return unless token.is_a?(Lexer::Token::InstantiateRule)

Binding.new(rule, token.args)
end

def resolve_rhs(rule_builder, rhs, index, bindings)
@rhs.each_with_index do |token, i|
if index == i
rhs.symbols.each { |sym| rule_builder.add_rhs(bindings.nil? ? sym : bindings.resolve_symbol(sym)) }
else
rule_builder.add_rhs(token)
end
end
end

def finalize_builder(rule_builder, rhs, index)
rule_builder.lhs = @lhs
rule_builder.line = @line
rule_builder.precedence_sym = @precedence_sym
rule_builder.user_code = replace_user_code(rhs, index)
end

def replace_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
end
end
end
62 changes: 7 additions & 55 deletions lib/lrama/grammar/rule_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,27 +66,14 @@ def has_inline_rules?
rhs.any? { |token| @parameterizing_rule_resolver.find_inline(token) }
end

def resolve_inline_rules
resolved_builders = [] #: Array[RuleBuilder]
def build_inlined_builders
inlined_rule_builder = Lrama::Grammar::InlinedRuleBuilder.new(@rule_counter, @midrule_action_counter, @parameterizing_rule_resolver, @lhs, @lhs_tag, @rhs, @user_code, @precedence_sym, @line)
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
next unless (inline_rule = @parameterizing_rule_resolver.find_inline(token))

return inlined_rule_builder.build_builders(inline_rule, token, i)
end
resolved_builders
[]
end

private
Expand Down Expand Up @@ -136,7 +123,7 @@ def process_rhs
raise "Unexpected token. #{token}" unless parameterizing_rule

bindings = Binding.new(parameterizing_rule, token.args)
lhs_s_value = lhs_s_value(token, bindings)
lhs_s_value = bindings.concatenated_args_str(token)
if (created_lhs = @parameterizing_rule_resolver.created_lhs(lhs_s_value))
@replaced_rhs << created_lhs
else
Expand Down Expand Up @@ -174,41 +161,6 @@ def process_rhs
end
end

def lhs_s_value(token, bindings)
s_values = token.args.map do |arg|
resolved = bindings.resolve_symbol(arg)
if resolved.is_a?(Lexer::Token::InstantiateRule)
[resolved.s_value, resolved.args.map(&:s_value)]
else
resolved.s_value
end
end
"#{token.rule_name}_#{s_values.join('_')}"
end

def resolve_inline_rhs(rule_builder, inline_rhs, index, bindings = nil)
rhs.each_with_index do |token, i|
if index == i
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

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
(rhs + [user_code]).compact.each.with_index(1) do |token, i|
Expand Down

0 comments on commit fb13465

Please sign in to comment.