Skip to content

Commit 4818362

Browse files
authored
Merge pull request #77 from JuliaLang/sp/ternary-unexpected-kw
Better "unexpected kw" handling in ternary parsing
2 parents 82b9705 + c606e9d commit 4818362

File tree

5 files changed

+84
-18
lines changed

5 files changed

+84
-18
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
/Manifest.toml
2+
/tools/pkgs
3+
/tools/logs.txt

src/kinds.jl

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,10 @@ const _kind_names =
2727
"baremodule"
2828
"begin"
2929
"break"
30-
"catch"
3130
"const"
3231
"continue"
3332
"do"
34-
"else"
35-
"elseif"
36-
"end"
3733
"export"
38-
"finally"
3934
"for"
4035
"function"
4136
"global"
@@ -51,6 +46,13 @@ const _kind_names =
5146
"try"
5247
"using"
5348
"while"
49+
"BEGIN_BLOCK_CONTINUATION_KEYWORDS"
50+
"catch"
51+
"finally"
52+
"else"
53+
"elseif"
54+
"end"
55+
"END_BLOCK_CONTINUATION_KEYWORDS"
5456
"BEGIN_CONTEXTUAL_KEYWORDS"
5557
# contextual keywords
5658
"abstract"
@@ -1046,6 +1048,7 @@ end
10461048
is_contextual_keyword(k::Kind) = K"BEGIN_CONTEXTUAL_KEYWORDS" < k < K"END_CONTEXTUAL_KEYWORDS"
10471049
is_error(k::Kind) = K"BEGIN_ERRORS" < k < K"END_ERRORS"
10481050
is_keyword(k::Kind) = K"BEGIN_KEYWORDS" < k < K"END_KEYWORDS"
1051+
is_block_continuation_keyword(k::Kind) = K"BEGIN_BLOCK_CONTINUATION_KEYWORDS" < k < K"END_BLOCK_CONTINUATION_KEYWORDS"
10491052
is_literal(k::Kind) = K"BEGIN_LITERAL" < k < K"END_LITERAL"
10501053
is_operator(k::Kind) = K"BEGIN_OPS" < k < K"END_OPS"
10511054
is_word_operator(k::Kind) = (k == K"in" || k == K"isa" || k == K"where")
@@ -1099,5 +1102,3 @@ end
10991102
function is_whitespace(x)
11001103
kind(x) in (K"Whitespace", K"NewlineWs")
11011104
end
1102-
1103-

src/parser.jl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -637,12 +637,27 @@ function parse_cond(ps::ParseState)
637637
# a ? b c ==> (? a b (error-t) c)
638638
bump_invisible(ps, K"error", TRIVIA_FLAG, error="`:` expected in `?` expression")
639639
end
640-
t = peek_token(ps)
640+
t = peek_token(ps; skip_newlines = true)
641641
if !preceding_whitespace(t)
642642
# a ? b :c ==> (? a b (error-t) c)
643643
bump_invisible(ps, K"error", TRIVIA_FLAG,
644644
error="space required after `:` in `?` expression")
645645
end
646+
647+
# FIXME: This is a very specific case. Error recovery should be handled more
648+
# generally elsewhere.
649+
if is_block_continuation_keyword(kind(t))
650+
# a "continuaton keyword" is likely to belong to the surrounding code, so
651+
# we abort early
652+
653+
# if true; x ? true elseif true end ==> (if true (block (if x true (error-t) (error-t))) (elseif true (block)))
654+
# if true; x ? true end ==> (if true (block (if x true (error-t) (error-t))))
655+
# if true; x ? true\n end ==> (if true (block (if x true (error-t) (error-t))))
656+
# if true; x ? true : elseif true end ==> (if true (block (if x true (error-t))) (elseif true (block)))
657+
bump_invisible(ps, K"error", TRIVIA_FLAG, error="unexpected `$(kind(t))`")
658+
emit(ps, mark, K"if")
659+
return
660+
end
646661
parse_eq_star(ps)
647662
emit(ps, mark, K"?")
648663
end

test/parser.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,10 @@ tests = [
403403
"if a xx elseif b yy end" => "(if a (block xx) (elseif b (block yy)))"
404404
"if a xx else if b yy end" => "(if a (block xx) (error-t) (elseif b (block yy)))"
405405
"if a xx else yy end" => "(if a (block xx) (block yy))"
406+
"if true; x ? true elseif true end" => "(if true (block (if x true (error-t) (error-t))) (elseif true (block)))"
407+
"if true; x ? true end" => "(if true (block (if x true (error-t) (error-t))))"
408+
"if true; x ? true\nend" => "(if true (block (if x true (error-t) (error-t))))"
409+
"if true; x ? true : elseif true end" => "(if true (block (if x true (error-t))) (elseif true (block)))"
406410
],
407411
JuliaSyntax.parse_const_local_global => [
408412
"global x" => "(global x)"

tools/check_all_packages.jl

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@
55

66
using JuliaSyntax, Logging
77

8+
# like Meta.parseall, but throws
9+
function parseall_throws(str)
10+
pos = firstindex(str)
11+
exs = []
12+
while pos <= lastindex(str)
13+
ex, pos = Meta.parse(str, pos)
14+
push!(exs, ex)
15+
end
16+
if length(exs) == 0
17+
throw(Meta.ParseError("end of input"))
18+
elseif length(exs) == 1
19+
return exs[1]
20+
else
21+
return Expr(:toplevel, exs...)
22+
end
23+
end
24+
825
logio = open(joinpath(@__DIR__, "logs.txt"), "w")
926
logger = Logging.ConsoleLogger(logio)
1027

@@ -35,27 +52,54 @@ Logging.with_logger(logger) do
3552
t = time()
3653
i = 0
3754
iob = IOBuffer()
55+
ex_count = 0
3856
for (r, _, files) in walkdir(pkgspath)
3957
for f in files
4058
endswith(f, ".jl") || continue
4159
fpath = joinpath(r, f)
42-
try
43-
JuliaSyntax.parse(Expr, read(fpath, String))
44-
catch err
45-
err isa InterruptException && rethrow()
46-
ex = (err, catch_backtrace())
47-
push!(exceptions, ex)
48-
@error "parsing failed for $(fpath)" ex
49-
flush(logio)
60+
if isfile(fpath)
61+
file = read(fpath, String)
62+
try
63+
e1 = JuliaSyntax.parse(Expr, file)
64+
catch err
65+
err isa InterruptException && rethrow()
66+
ex_count += 1
67+
ex = (err, catch_backtrace())
68+
push!(exceptions, ex)
69+
meta_parse = "success"
70+
try
71+
parseall_throws(file)
72+
catch err2
73+
meta_parse = "fail"
74+
ex_count -= 1
75+
end
76+
parse_to_syntax = "success"
77+
try
78+
JuliaSyntax.parse(JuliaSyntax.SyntaxNode, file)
79+
catch err2
80+
parse_to_syntax = "fail"
81+
end
82+
severity = parse_to_syntax == "fail" ? "error" :
83+
meta_parse == "fail" ? "warn" : "error"
84+
println(logio, """
85+
[$(severity)] $(fpath)
86+
parse-to-expr: fail
87+
parse-to-syntaxtree: $(parse_to_syntax)
88+
reference: $(meta_parse)
89+
""")
90+
@error "" exception = ex
91+
flush(logio)
92+
end
5093
end
5194
i += 1
5295
if i % 100 == 0
5396
runtime = time() - t
5497
avg = round(runtime/i*1000, digits = 2)
5598
print(iob, "\e[2J\e[0;0H")
5699
println(iob, "$i files parsed")
57-
println(iob, " $(length(exceptions)) failures")
58-
println(iob, " $(avg)ms per file, $(round(Int, runtime))s in total")
100+
println(iob, "> $(ex_count) failures compared to Meta.parse")
101+
println(iob, "> $(length(exceptions)) errors in total")
102+
println(iob, "> $(avg)ms per file, $(round(Int, runtime))s in total")
59103
println(stderr, String(take!(iob)))
60104
end
61105
end

0 commit comments

Comments
 (0)