Skip to content

Commit

Permalink
Merge pull request #285 from ydah/support-user-define
Browse files Browse the repository at this point in the history
Support user define parameterizing rules
  • Loading branch information
yui-knk authored Dec 11, 2023
2 parents cf6cc82 + 74f43ae commit 7b93130
Show file tree
Hide file tree
Showing 18 changed files with 1,007 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
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

0 comments on commit 7b93130

Please sign in to comment.