Skip to content

Commit

Permalink
Support user define parameterizing rules
Browse files Browse the repository at this point in the history
  • Loading branch information
ydah committed Dec 8, 2023
1 parent b35b7d6 commit 5550ec3
Show file tree
Hide file tree
Showing 16 changed files with 919 additions and 440 deletions.
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
14 changes: 13 additions & 1 deletion lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
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/symbol"
require "lrama/grammar/type"
require "lrama/grammar/union"
Expand Down Expand Up @@ -36,6 +39,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 +133,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 +318,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 Expand Up @@ -400,6 +408,10 @@ def normalize_rules
add_nterm(id: rule._lhs)
end
end

if @parameterizing_resolver.term
add_term(id: @parameterizing_resolver.term)
end
end

# Collect symbols from rules
Expand Down
47 changes: 47 additions & 0 deletions lib/lrama/grammar/parameterizing_rule_builder.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Lrama
class Grammar
class ParameterizingRuleBuilder
attr_reader :name, :args, :rhs, :term

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

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

def build_token(token)
validate_argument_number!(token)
Lrama::Lexer::Token::Ident.new(s_value: "#{name}_#{token.args.first&.s_value}")
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 rhs_term(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
31 changes: 31 additions & 0 deletions lib/lrama/grammar/parameterizing_rule_resolver.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Lrama
class Grammar
class ParameterizingRuleResolver
attr_reader :rules, :tokens, :term

def initialize
@rules = []
@tokens = []
@term = nil
@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, user_code, precedence_sym, line)
@parameterizing_rule_builders.each do |builder|
build_token = builder.build_token(token)
@rules = @rules + builder.build_rules(token, build_token, rule_counter, lhs_tag, user_code, precedence_sym, line)
@tokens << build_token
@term = builder.term
end
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
20 changes: 12 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,15 @@ 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_resolver.build_rules(token, @rule_counter, @lhs_tag, user_code, precedence_sym, line)
@parameterizing_rules = @parameterizing_rules + parameterizing_resolver.rules
@replaced_rhs = @replaced_rhs + parameterizing_resolver.tokens
else
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 +128,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

0 comments on commit 5550ec3

Please sign in to comment.