Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Inlining #369

Merged
merged 11 commits into from
Apr 2, 2024
11 changes: 10 additions & 1 deletion lib/lrama/grammar/parameterizing_rule/resolver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,22 @@ def add_parameterizing_rule(rule)
@rules << rule
end

def find(token)
def find_rule(token)
select_rules(token).last
end

def find_inline(token)
@rules.select { |rule| rule.name == token.s_value && rule.is_inline }.last
end

def created_lhs(lhs_s_value)
@created_lhs_list.reverse.find { |created_lhs| created_lhs.s_value == lhs_s_value }
end

private

def select_rules(token)
rules = select_not_inline_rules
rules = select_rules_by_name(token.rule_name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems the result o line 31 is not used by line 32 and overwrote by line 32. Is something wrong?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😨 My apologies... I updated it.

rules = rules.select { |rule| rule.required_parameters_count == token.args_count }
if rules.empty?
Expand All @@ -33,6 +38,10 @@ def select_rules(token)
end
end

def select_not_inline_rules
@rules.select { |rule| !rule.is_inline }
end

def select_rules_by_name(rule_name)
rules = @rules.select { |rule| rule.name == rule_name }
if rules.empty?
Expand Down
5 changes: 3 additions & 2 deletions lib/lrama/grammar/parameterizing_rule/rule.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ module Lrama
class Grammar
class ParameterizingRule
class Rule
attr_reader :name, :parameters, :rhs_list, :required_parameters_count
attr_reader :name, :parameters, :rhs_list, :required_parameters_count, :is_inline

def initialize(name, parameters, rhs_list)
def initialize(name, parameters, rhs_list, is_inline: false)
@name = name
@parameters = parameters
@rhs_list = rhs_list
@is_inline = is_inline
@required_parameters_count = parameters.count
end
end
Expand Down
82 changes: 66 additions & 16 deletions lib/lrama/grammar/rule_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ def initialize(rule_counter, midrule_action_counter, position_in_original_rule_r
@user_code = nil
@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)
Expand Down Expand Up @@ -52,12 +57,16 @@ def complete_input

def setup_rules(parameterizing_rule_resolver)
preprocess_references unless @skip_preprocess_references
process_rhs(parameterizing_rule_resolver)
if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[note] In the future, we need to change this condition otherwise lrama can not handle production rules whose RHS include both parameterizing rules and inline rules.

resolve_inline(parameterizing_rule_resolver)
else
process_rhs(parameterizing_rule_resolver)
end
build_rules
end

def rules
@parameterizing_rules + @midrule_action_rules + @rules
@parameterizing_rules + @inline_rules + @midrule_action_rules + @rules
end

private
Expand All @@ -73,19 +82,25 @@ def preprocess_references
def build_rules
tokens = @replaced_rhs

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
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
end
end

Expand All @@ -103,7 +118,7 @@ def process_rhs(parameterizing_rule_resolver)
when Lrama::Lexer::Token::Ident
@replaced_rhs << token
when Lrama::Lexer::Token::InstantiateRule
parameterizing_rule = parameterizing_rule_resolver.find(token)
parameterizing_rule = parameterizing_rule_resolver.find_rule(token)
raise "Unexpected token. #{token}" unless parameterizing_rule

bindings = Binding.new(parameterizing_rule, token.args)
Expand Down Expand Up @@ -157,6 +172,41 @@ 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
inline_rhs.symbols.each { |sym| rule_builder.add_rhs(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)
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
1 change: 1 addition & 0 deletions lib/lrama/lexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class Lexer
%code
%rule
%no-stdlib
%inline
)

def initialize(grammar_file)
Expand Down
Loading