diff --git a/Project.toml b/Project.toml index 889a07d..d04e214 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PikaParser" uuid = "3bbf5609-3e7b-44cd-8549-7c69f321e792" authors = ["The developers of PikaParser.jl"] -version = "0.6.0" +version = "0.6.1" [deps] DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" diff --git a/src/grammar.jl b/src/grammar.jl index fc3219e..3d10e84 100644 --- a/src/grammar.jl +++ b/src/grammar.jl @@ -42,7 +42,15 @@ function make_grammar( end end - all(opened .> 0) || error("some grammar rules not reachable from starts") + # At this point, all rules should be opened at least once. If some grammar + # rules are unreachable from the starts, report them explicitly in the + # error message to ease the debugging. + if !all(opened .> 0) + unreachable_names = map(Base.first, rules)[opened.==0] + error( + "grammar rules not reachable from starts: $(join(repr.(unreachable_names), ", "))", + ) + end reordered = rules[topo_order] diff --git a/test/clauses.jl b/test/clauses.jl index 79f1c3e..e60fb14 100644 --- a/test/clauses.jl +++ b/test/clauses.jl @@ -90,6 +90,27 @@ end @test_throws DomainError P.flatten(rules, Char) end +@testset "Unreachable rules are reported" begin + rules = Dict(:xxx => P.seq(:yyy, P.fail), :yyy => P.epsilon, :zzz => P.epsilon) + + @test_throws ErrorException P.make_grammar([:xxx], P.flatten(rules, Char)) + @test begin + # this should simply not throw anything + P.make_grammar([:xxx, :zzz], P.flatten(rules, Char)) + true + end + + @test try + P.make_grammar([:xxx], P.flatten(rules, Char)) + catch e + b = IOBuffer() + showerror(b, e) + errorstring = String(take!(b)) + + (occursin(":zzz", errorstring) && all(!occursin(errorstring), [":xxx", ":yyy"])) + end +end + @testset "Corner-case epsilon matches" begin str = "whateveρ"