Skip to content

Commit

Permalink
Add support for adjusting Index to Inline
Browse files Browse the repository at this point in the history
  • Loading branch information
ydah committed Apr 9, 2024
1 parent 300456a commit 926b345
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 25 deletions.
13 changes: 13 additions & 0 deletions lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ def epilogue=(epilogue)
end

def prepare
resolve_inline_rules
normalize_rules
collect_symbols
set_lhs_and_rhs
Expand Down Expand Up @@ -289,6 +290,18 @@ def append_special_symbols
@accept_symbol = term
end

def resolve_inline_rules
while @rule_builders.any? {|r| r.has_inline_rules?(@parameterizing_rule_resolver) } do
@rule_builders.map! do |builder|
if builder.has_inline_rules?(@parameterizing_rule_resolver)
builder.resolve_inline_rules(@parameterizing_rule_resolver)
else
builder
end
end.flatten!
end
end

def normalize_rules
# Add $accept rule to the top of rules
lineno = @rule_builders.first ? @rule_builders.first.line : 0
Expand Down
53 changes: 28 additions & 25 deletions lib/lrama/grammar/rule_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,38 @@ def complete_input

def setup_rules(parameterizing_rule_resolver)
preprocess_references unless @skip_preprocess_references
if rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) }
resolve_inline(parameterizing_rule_resolver)
else
process_rhs(parameterizing_rule_resolver)
end
process_rhs(parameterizing_rule_resolver)
build_rules
end

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

def has_inline_rules?(parameterizing_rule_resolver)
rhs.any? { |token| parameterizing_rule_resolver.find_inline(token) }
end

def resolve_inline_rules(parameterizing_rule_resolver)
resolved_builders = []
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, 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)
resolved_builders << rule_builder
end
break
end
end
resolved_builders
end

private

def freeze_rhs
Expand Down Expand Up @@ -172,23 +192,6 @@ 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
Expand All @@ -204,6 +207,9 @@ def replace_inline_user_code(inline_rhs, index)
return user_code if user_code.nil?

code = user_code.s_value.gsub(/\$#{index + 1}/, inline_rhs.user_code.s_value)
user_code.references[(index+1)..-1]&.each do |ref|
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

Expand Down Expand Up @@ -238,9 +244,6 @@ def numberize_references
end

if ref.number
# TODO: When Inlining is implemented, for example, if `$1` is expanded to multiple RHS tokens,
# `$2` needs to access `$2 + n` to actually access it. So, after the Inlining implementation,
# it needs resolves from number to index.
ref.index = ref.number
end

Expand Down
42 changes: 42 additions & 0 deletions spec/fixtures/inlining/resolve_index.y
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* This is comment for this file.
*/

%{
// Prologue
static int yylex(YYSTYPE *val, YYLTYPE *loc);
static int yyerror(YYLTYPE *loc, const char *str);
%}

%union {
int i;
}

%token <i> NUM
%type <i> expression

%rule %inline op : '+' { + }
| '+' '=' { += }
;

%%

expression : NUM
| expression op expression { $$ = $1 $2 $3; }
;

%%

static int yylex(YYSTYPE *yylval, YYLTYPE *loc)
{
return 0;
}

static int yyerror(YYLTYPE *loc, const char *str)
{
return 0;
}

int main(int argc, char *argv[])
{
}
64 changes: 64 additions & 0 deletions spec/lrama/parser_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1988,6 +1988,70 @@
])
end
end

context 'when inline with resolve index' do
let(:path) { "inlining/resolve_index.y" }

it "expands inlining rules" do
expect(grammar.nterms.sort_by(&:number)).to match_symbols([
Sym.new(id: T::Ident.new(s_value: "$accept"), alias_name: nil, number: 6, tag: nil, term: false, token_id: 0, nullable: false),
Sym.new(id: T::Ident.new(s_value: "expression"), alias_name: nil, number: 7, tag: T::Tag.new(s_value: "<i>"), term: false, token_id: 1, nullable: false),
])

expect(grammar.rules).to eq([
Rule.new(
id: 2,
lhs: grammar.find_symbol_by_s_value!("$accept"),
rhs: [
grammar.find_symbol_by_s_value!("expression"),
grammar.find_symbol_by_s_value!("YYEOF"),
],
token_code: nil,
nullable: false,
precedence_sym: grammar.find_symbol_by_s_value!("YYEOF"),
lineno: 24,
),
Rule.new(
id: 3,
lhs: grammar.find_symbol_by_s_value!("expression"),
rhs: [
grammar.find_symbol_by_s_value!("NUM"),
],
token_code: nil,
nullable: false,
precedence_sym: grammar.find_symbol_by_s_value!("NUM"),
lineno: 24,
),
Rule.new(
id: 4,
lhs: grammar.find_symbol_by_s_value!("expression"),
rhs: [
grammar.find_symbol_by_s_value!("expression"),
grammar.find_symbol_by_s_value!("'+'"),
grammar.find_symbol_by_s_value!("expression"),
],
token_code: T::UserCode.new(s_value: " $$ = $1 + $3; "),
nullable: false,
precedence_sym: grammar.find_symbol_by_s_value!("'+'"),
lineno: 25,
),
Rule.new(
id: 5,
lhs: grammar.find_symbol_by_s_value!("expression"),
rhs: [
grammar.find_symbol_by_s_value!("expression"),
grammar.find_symbol_by_s_value!("'+'"),
grammar.find_symbol_by_s_value!("'='"),
grammar.find_symbol_by_s_value!("expression"),
],
token_code: T::UserCode.new(s_value: " $$ = $1 += $4; "),
nullable: false,
precedence_sym: grammar.find_symbol_by_s_value!("'='"),
lineno: 25,
),
])
end
end
end

it "; for rules is optional" do
Expand Down

0 comments on commit 926b345

Please sign in to comment.