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

Support user define parameterizing rules #285

Merged
merged 7 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ target :lib do
check "lib/lrama/grammar/code.rb"
check "lib/lrama/grammar/counter.rb"
check "lib/lrama/grammar/error_token.rb"
check "lib/lrama/grammar/parameterizing_rule_builder.rb"
check "lib/lrama/grammar/parameterizing_rule_resolver.rb"
check "lib/lrama/grammar/parameterizing_rule_rhs_builder.rb"
check "lib/lrama/grammar/parameterizing_rules"
check "lib/lrama/grammar/percent_code.rb"
check "lib/lrama/grammar/precedence.rb"
Expand Down
11 changes: 10 additions & 1 deletion lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
require "lrama/grammar/reference"
require "lrama/grammar/rule"
require "lrama/grammar/rule_builder"
require "lrama/grammar/parameterizing_rule_builder"
require "lrama/grammar/parameterizing_rule_resolver"
require "lrama/grammar/parameterizing_rule_rhs_builder"
require "lrama/grammar/parameterizing_rule"
require "lrama/grammar/symbol"
require "lrama/grammar/type"
require "lrama/grammar/union"
Expand Down Expand Up @@ -36,6 +40,7 @@ def initialize(rule_counter)
@rule_builders = []
@rules = []
@sym_to_rules = {}
@parameterizing_resolver = ParameterizingRuleResolver.new
@empty_symbol = nil
@eof_symbol = nil
@error_symbol = nil
Expand Down Expand Up @@ -129,6 +134,10 @@ def add_rule_builder(builder)
@rule_builders << builder
end

def add_parameterizing_rule_builder(builder)
@parameterizing_resolver.add_parameterizing_rule_builder(builder)
end

def prologue_first_lineno=(prologue_first_lineno)
@aux.prologue_first_lineno = prologue_first_lineno
end
Expand Down Expand Up @@ -310,7 +319,7 @@ def compute_first_set

def setup_rules
@rule_builders.each do |builder|
builder.setup_rules
builder.setup_rules(@parameterizing_resolver)
end
end

Expand Down
6 changes: 6 additions & 0 deletions lib/lrama/grammar/parameterizing_rule.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module Lrama
class Grammar
class ParameterizingRule < Struct.new(:rules, :token, keyword_init: true)
end
end
end
45 changes: 45 additions & 0 deletions lib/lrama/grammar/parameterizing_rule_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
module Lrama
class Grammar
class ParameterizingRuleBuilder
attr_reader :name, :args, :rhs

def initialize(name, args, rhs)
@name = name
@args = args
@rhs = rhs
@required_args_count = args.count
end

def build_rules(token, rule_counter, lhs_tag, line)
validate_argument_number!(token)
lhs = lhs_token(token)
rules = []
@rhs.each do |rhs|
rules << Rule.new(id: rule_counter.increment, _lhs: lhs, _rhs: [rhs_token(token, rhs)].compact, lhs_tag: lhs_tag, token_code: rhs.user_code, precedence_sym: rhs.precedence_sym, lineno: line)
end
ParameterizingRule.new(rules: rules, token: lhs)
end

private

def validate_argument_number!(token)
unless @required_args_count == token.args.count
raise "Invalid number of arguments. expect: #{@required_args_count} actual: #{token.args.count}"
end
end

def lhs_token(token)
Lrama::Lexer::Token::Ident.new(s_value: "#{name}_#{token.args.map(&:s_value).join('_')}")
end

def rhs_token(token, rhs)
return nil unless rhs.symbol
term = rhs.symbol
@args.each_with_index do |arg, index|
term = token.args[index] if arg.s_value == rhs.symbol.s_value
end
term
end
end
end
end
24 changes: 24 additions & 0 deletions lib/lrama/grammar/parameterizing_rule_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Lrama
class Grammar
class ParameterizingRuleResolver
def initialize
@parameterizing_rule_builders = []
end

def add_parameterizing_rule_builder(builder)
@parameterizing_rule_builders << builder
end

def defined?(name)
@parameterizing_rule_builders.any? { |builder| builder.name == name }
end

def build_rules(token, rule_counter, lhs_tag, line)
builder = @parameterizing_rule_builders.select { |b| b.name == token.s_value }.last
raise "Unknown parameterizing rule #{token.s_value} at line #{token.line}" unless builder

builder.build_rules(token, rule_counter, lhs_tag, line)
end
end
end
end
13 changes: 13 additions & 0 deletions lib/lrama/grammar/parameterizing_rule_rhs_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Lrama
class Grammar
class ParameterizingRuleRhsBuilder
attr_accessor :symbol, :user_code, :precedence_sym

def initialize
@symbol = nil
@user_code = nil
@precedence_sym = nil
end
end
end
end
21 changes: 13 additions & 8 deletions lib/lrama/grammar/rule_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ def complete_input
freeze_rhs
end

def setup_rules
def setup_rules(parameterizing_resolver)
preprocess_references unless @skip_preprocess_references
process_rhs
process_rhs(parameterizing_resolver)
build_rules
end

Expand Down Expand Up @@ -97,7 +97,7 @@ def build_rules

# rhs is a mixture of variety type of tokens like `Ident`, `Parameterizing`, `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
def process_rhs(parameterizing_resolver)
return if @replaced_rhs

@replaced_rhs = []
Expand All @@ -110,11 +110,16 @@ def process_rhs
when Lrama::Lexer::Token::Ident
@replaced_rhs << token
when Lrama::Lexer::Token::Parameterizing
parameterizing = ParameterizingRules::Builder.new(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
parameterizing.build.each do |r|
@parameterizing_rules << r
if parameterizing_resolver.defined?(token.s_value)
parameterizing = parameterizing_resolver.build_rules(token, @rule_counter, @lhs_tag, line)
@parameterizing_rules = @parameterizing_rules + parameterizing.rules
@replaced_rhs << parameterizing.token
else
# TODO: Delete when the standard library will defined as a grammar file.
parameterizing = ParameterizingRules::Builder.new(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
@parameterizing_rules = @parameterizing_rules + parameterizing.build
@replaced_rhs << parameterizing.build_token
end
@replaced_rhs << parameterizing.build_token
when Lrama::Lexer::Token::UserCode
prefix = token.referred ? "@" : "$@"
new_token = Lrama::Lexer::Token::Ident.new(s_value: prefix + @midrule_action_counter.increment.to_s)
Expand All @@ -124,7 +129,7 @@ def process_rhs
rule_builder.lhs = new_token
rule_builder.user_code = token
rule_builder.complete_input
rule_builder.setup_rules
rule_builder.setup_rules(parameterizing_resolver)

@rule_builders_for_derived_rules << rule_builder
else
Expand Down
1 change: 1 addition & 0 deletions lib/lrama/lexer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Lexer
%error-token
%empty
%code
%rule
)

def initialize(text)
Expand Down
Loading