Skip to content
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
30 changes: 30 additions & 0 deletions lib/lrama/grammar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,8 @@ def append_special_symbols

# @rbs () -> void
def resolve_inline_rules
validate_inline_rules!

while @rule_builders.any?(&:has_inline_rules?) do
@rule_builders = @rule_builders.flat_map do |builder|
if builder.has_inline_rules?
Expand All @@ -451,6 +453,34 @@ def resolve_inline_rules
end
end

# @rbs () -> void
def validate_inline_rules!
visiting = {} #: Hash[Parameterized::Rule, bool]
visited = {} #: Hash[Parameterized::Rule, bool]

parameterized_rules.select(&:inline?).each do |rule|
validate_inline_rule!(rule, visiting, visited)
end
end

# @rbs (Parameterized::Rule rule, Hash[Parameterized::Rule, bool] visiting, Hash[Parameterized::Rule, bool] visited) -> void
def validate_inline_rule!(rule, visiting, visited)
return if visited[rule]
raise "Recursive inline rule: #{rule.name}" if visiting[rule]

visiting[rule] = true
rule.rhs.each do |rhs|
rhs.symbols.each do |symbol|
next if rule.parameters.any? { |parameter| parameter.s_value == symbol.s_value }

inline_rule = @parameterized_resolver.find_inline(symbol)
validate_inline_rule!(inline_rule, visiting, visited) if inline_rule
end
end
visiting.delete(rule)
visited[rule] = true
end

# @rbs () -> void
def normalize_rules
add_accept_rule
Expand Down
6 changes: 6 additions & 0 deletions sig/generated/lrama/grammar.rbs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions spec/lrama/parser_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "tempfile"
require "timeout"

RSpec.describe Lrama::Parser do
T ||= Lrama::Lexer::Token
Expand Down Expand Up @@ -3040,6 +3041,21 @@
end
end

context 'when inline is recursive' do
let(:path) { "recursive_inline.y" }
let(:y) do
<<~GRAMMAR
%rule %inline a : 'x' a | 'y' ;
%%
program : a ;
GRAMMAR
end

it "raises an error" do
expect { Timeout.timeout(1) { grammar } }.to raise_error(RuntimeError, "Recursive inline rule: a")
end
end

context 'when inline have rhs include parameterized rule' do
let(:path) { "inlining/rhs_include_parameterized.y" }

Expand Down