Skip to content

Commit 253d879

Browse files
authored
Bugfix: Always emit a token from parse_atom (#47)
If a closing token is found in parse_atom, we still need to emit a nontrivia token to make the resulting parse tree consistent. We choose an invisible token for this. Also add various minor fixes to error recovory and a few additional tests in parse_atom and elsewhere.
1 parent a6ccaee commit 253d879

File tree

3 files changed

+31
-12
lines changed

3 files changed

+31
-12
lines changed

src/parser.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ function bump_closing_token(ps, closing_kind)
151151
end
152152
# mark as trivia => ignore in AST.
153153
emit(ps, mark, K"error", TRIVIA_FLAG,
154-
error="Expected `$(untokenize(closing_kind))` but got unexpected tokens")
154+
error="Expected `$(untokenize(closing_kind))`")
155155
if peek(ps) == closing_kind
156156
bump(ps, TRIVIA_FLAG)
157157
end
@@ -2798,11 +2798,11 @@ function parse_cat(ps::ParseState, closer, end_is_symbol)
27982798
if k == K"," || (is_closing_token(ps, k) && k != K";")
27992799
if k == K","
28002800
# [x,] ==> (vect x)
2801-
# [x ==> (vect x)
28022801
bump(ps, TRIVIA_FLAG)
28032802
end
28042803
# [x] ==> (vect x)
28052804
# [x \n ] ==> (vect x)
2805+
# [x ==> (vect x (error-t))
28062806
parse_vect(ps, closer)
28072807
elseif k == K"for"
28082808
# [x for a in as] ==> (comprehension (generator x (= a as)))
@@ -2957,6 +2957,9 @@ function parse_brackets(after_parse::Function,
29572957
num_semis += 1
29582958
bump(ps, TRIVIA_FLAG)
29592959
bump_trivia(ps)
2960+
elseif is_closing_token(ps, k)
2961+
# Error; handled below in bump_closing_token
2962+
break
29602963
else
29612964
mark = position(ps)
29622965
eq_pos = parse_eq_star(ps)
@@ -3279,8 +3282,11 @@ function parse_atom(ps::ParseState, check_identifiers=true)
32793282
end
32803283
emit(ps, mark, K"quote")
32813284
elseif leading_kind == K"=" && is_plain_equals(peek_token(ps))
3282-
bump(ps, TRIVIA_FLAG, error="unexpected `=`")
3285+
# = ==> (error =)
3286+
bump(ps, error="unexpected `=`")
32833287
elseif leading_kind == K"Identifier"
3288+
# xx ==> xx
3289+
# x₁ ==> x₁
32843290
bump(ps)
32853291
elseif is_operator(leading_kind)
32863292
if check_identifiers && is_syntactic_operator(leading_kind)
@@ -3375,14 +3381,16 @@ function parse_atom(ps::ParseState, check_identifiers=true)
33753381
parse_string(ps, true)
33763382
emit(ps, mark, K"macrocall")
33773383
elseif is_literal(leading_kind)
3384+
# 42 ==> 42
33783385
bump(ps)
33793386
elseif is_closing_token(ps, leading_kind)
33803387
# Leave closing token in place for other productions to
3381-
# recover with (??)
3388+
# recover with
3389+
# ) ==> error
33823390
msg = leading_kind == K"EndMarker" ?
33833391
"premature end of input" :
33843392
"unexpected closing token"
3385-
emit_diagnostic(ps, error=msg)
3393+
bump_invisible(ps, K"error", error=msg)
33863394
else
33873395
bump(ps, error="invalid syntax atom")
33883396
end

test/parser.jl

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ tests = [
458458
"function f() where T end" => "(function (where (call f) T) (block))"
459459
"function f() \n a \n b end" => "(function (call f) (block a b))"
460460
"function f() end" => "(function (call f) (block))"
461+
# Errors
462+
"function" => "(function (error (error)) (block (error)) (error-t))"
461463
],
462464
JuliaSyntax.parse_try => [
463465
"try \n x \n catch e \n y \n finally \n z end" =>
@@ -560,8 +562,13 @@ tests = [
560562
":foo" => "(quote foo)"
561563
": foo" => "(quote (error-t) foo)"
562564
# Literal colons
563-
":)" => ":"
564-
": end" => ":"
565+
":)" => ":"
566+
": end" => ":"
567+
# plain equals
568+
"=" => "(error =)"
569+
# Identifiers
570+
"xx" => "xx"
571+
"x₁" => "x₁"
565572
# var syntax
566573
"""var"x" """ => "x"
567574
"""var"x"+""" => "x"
@@ -586,14 +593,16 @@ tests = [
586593
":.=" => "(quote .=)"
587594
# Special symbols quoted
588595
":end" => "(quote end)"
589-
":(end)" => "(quote (error (end)))"
596+
":(end)" => "(quote (error-t))"
590597
":<:" => "(quote <:)"
598+
# unexpect =
599+
"=" => "(error =)"
591600
# parse_cat
592601
"[]" => "(vect)"
593602
"[x,]" => "(vect x)"
594603
"[x]" => "(vect x)"
595-
"[x" => "(vect x (error-t))"
596604
"[x \n ]" => "(vect x)"
605+
"[x" => "(vect x (error-t))"
597606
"[x \n\n ]" => "(vect x)"
598607
"[x for a in as]" => "(comprehension (generator x (= a as)))"
599608
"[x \n\n for a in as]" => "(comprehension (generator x (= a as)))"
@@ -625,8 +634,10 @@ tests = [
625634
"``" => "(macrocall :(Core.var\"@cmd\") \"\")"
626635
"`cmd`" => "(macrocall :(Core.var\"@cmd\") \"cmd\")"
627636
"```cmd```" => "(macrocall :(Core.var\"@cmd\") \"cmd\")"
628-
# Errors
629-
": foo" => "(quote (error-t) foo)"
637+
# literals
638+
"42" => "42"
639+
# closing tokens
640+
")" => "(error)"
630641
],
631642
JuliaSyntax.parse_atom => [
632643
# Actually parse_array

test/test_utils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ function itest_parse(production, code; version::VersionNumber=v"1.6")
243243
show(stdout, MIME"text/plain"(), f_ex)
244244

245245
printstyled(stdout, "\n\n# Diff of AST dump:\n", color=:red)
246-
show_expr_text_diff(showfunc, ex, f_ex, context=10)
246+
show_expr_text_diff(show, ex, f_ex, context=10)
247247
# return (ex, f_ex)
248248
# return (code, stream, t, s, ex)
249249
end

0 commit comments

Comments
 (0)