Skip to content

Commit 2720980

Browse files
authored
Merge pull request #220 from JuliaLang/c42f/bump-invisible-cleanup
Various AST cleanups related to removing invisible tokens
2 parents e4f08c4 + 9eb28b2 commit 2720980

File tree

9 files changed

+130
-89
lines changed

9 files changed

+130
-89
lines changed

src/expr.jl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
278278
pushfirst!(args[2].args, loc)
279279
end
280280
elseif headsym === :module
281+
pushfirst!(args, !has_flags(node, BARE_MODULE_FLAG))
281282
pushfirst!(args[3].args, loc)
282283
elseif headsym == :inert || (headsym == :quote && length(args) == 1 &&
283284
!(a1 = only(args); a1 isa Expr || a1 isa QuoteNode ||
@@ -303,6 +304,13 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
303304
args[1] = Expr(headsym, args[1].args...)
304305
headsym = :const
305306
end
307+
elseif headsym === :return && isempty(args)
308+
push!(args, nothing)
309+
elseif headsym === :juxtapose
310+
headsym = :call
311+
pushfirst!(args, :*)
312+
elseif headsym === :struct
313+
pushfirst!(args, has_flags(node, MUTABLE_FLAG))
306314
end
307315
return Expr(headsym, args...)
308316
end

src/hooks.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ function _incomplete_tag(n::SyntaxNode)
6262
elseif kp in KSet"for while function if"
6363
return i == 1 ? :other : :block
6464
elseif kp in KSet"module struct"
65-
return i == 2 ? :other : :block
65+
return i == 1 ? :other : :block
6666
elseif kp == K"do"
6767
return i < 3 ? :other : :block
6868
else

src/kinds.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ const _kind_names =
8383
"CmdString"
8484
"true"
8585
"false"
86-
"nothing" # A literal `nothing`
8786
"END_LITERAL"
8887

8988
"BEGIN_DELIMITERS"
@@ -880,6 +879,7 @@ const _kind_names =
880879
"comparison"
881880
"curly"
882881
"inert" # QuoteNode; not quasiquote
882+
"juxtapose" # Numeric juxtaposition like 2x
883883
"string" # A string interior node (possibly containing interpolations)
884884
"cmdstring" # A cmd string node (containing delimiters plus string)
885885
"char" # A char string node (containing delims + char data)

src/parse_stream.jl

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,38 @@
55
# TODO: Use `primitive type SyntaxFlags 16 end` rather than an alias?
66
const RawFlags = UInt16
77
const EMPTY_FLAGS = RawFlags(0)
8-
# Applied to tokens which are syntax trivia after parsing
8+
9+
# Set for tokens or ranges which are syntax trivia after parsing
910
const TRIVIA_FLAG = RawFlags(1<<0)
1011

11-
# Record whether operators are dotted
12+
# Token flags - may be set for operator kinded tokens
13+
# Operator is dotted
1214
const DOTOP_FLAG = RawFlags(1<<1)
13-
# Record whether operator has a suffix
14-
const SUFFIXED_FLAG = RawFlags(1<<2)
15+
# Operator has a suffix
16+
const SUFFIXED_FLAG = RawFlags(1<<2)
1517

18+
# Set for K"call", K"dotcall" or any syntactic operator heads
1619
# Distinguish various syntaxes which are mapped to K"call"
1720
const PREFIX_CALL_FLAG = RawFlags(0<<3)
1821
const INFIX_FLAG = RawFlags(1<<3)
1922
const PREFIX_OP_FLAG = RawFlags(2<<3)
2023
const POSTFIX_OP_FLAG = RawFlags(3<<3)
2124

22-
# The next two bits could overlap with the previous two if necessary
23-
# Set when kind == K"String" was triple-delimited as with """ or ```
25+
# The following flags are quite head-specific and may overlap
26+
27+
# Set when K"string" or K"cmdstring" was triple-delimited as with """ or ```
2428
const TRIPLE_STRING_FLAG = RawFlags(1<<5)
25-
# Set when a string or identifier needs "raw string" unescaping
29+
# Set when a K"string", K"cmdstring" or K"Identifier" needs raw string unescaping
2630
const RAW_STRING_FLAG = RawFlags(1<<6)
2731

28-
const PARENS_FLAG = RawFlags(1<<7)
32+
# Set for K"tuple", K"block" or K"macrocall" which are delimited by parentheses
33+
const PARENS_FLAG = RawFlags(1<<5)
34+
35+
# Set for K"struct" when mutable
36+
const MUTABLE_FLAG = RawFlags(1<<5)
37+
38+
# Set for K"module" when it's not bare (`module`, not `baremodule`)
39+
const BARE_MODULE_FLAG = RawFlags(1<<5)
2940

3041
# Flags holding the dimension of an nrow or other UInt8 not held in the source
3142
const NUMERIC_FLAGS = RawFlags(RawFlags(0xff)<<8)
@@ -78,10 +89,18 @@ function untokenize(head::SyntaxHead; unique=true, include_flag_suff=true)
7889
is_infix_op_call(head) && (str = str*"-i")
7990
is_prefix_op_call(head) && (str = str*"-pre")
8091
is_postfix_op_call(head) && (str = str*"-post")
81-
has_flags(head, TRIPLE_STRING_FLAG) && (str = str*"-s")
82-
has_flags(head, RAW_STRING_FLAG) && (str = str*"-r")
83-
has_flags(head, PARENS_FLAG) && (str = str*"-p")
84-
is_suffixed(head) && (str = str*"-S")
92+
93+
if kind(head) in KSet"string cmdstring Identifier"
94+
has_flags(head, TRIPLE_STRING_FLAG) && (str = str*"-s")
95+
has_flags(head, RAW_STRING_FLAG) && (str = str*"-r")
96+
elseif kind(head) in KSet"tuple block macrocall"
97+
has_flags(head, PARENS_FLAG) && (str = str*"-p")
98+
elseif kind(head) == K"struct"
99+
has_flags(head, MUTABLE_FLAG) && (str = str*"-mut")
100+
elseif kind(head) == K"module"
101+
has_flags(head, BARE_MODULE_FLAG) && (str = str*"-bare")
102+
end
103+
is_suffixed(head) && (str = str*"-suf")
85104
n = numeric_flags(head)
86105
n != 0 && (str = str*"-"*string(n))
87106
end

src/parser.jl

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -763,7 +763,7 @@ function parse_comparison(ps::ParseState, subtype_comparison=false)
763763
mark = position(ps)
764764
if subtype_comparison && is_reserved_word(peek(ps))
765765
# Recovery
766-
# struct try end ==> (struct false (error (try)) (block))
766+
# struct try end ==> (struct (error (try)) (block))
767767
name = untokenize(peek(ps))
768768
bump(ps)
769769
emit(ps, mark, K"error", error="Invalid type name `$name`")
@@ -1105,13 +1105,13 @@ function is_juxtapose(ps, prev_k, t)
11051105
!is_initial_reserved_word(ps, k)
11061106
end
11071107

1108-
# Juxtoposition. Ugh!
1108+
# Juxtoposition. Ugh! But so useful for units and Field identities like `im`
11091109
#
1110-
# 2x ==> (call-i 2 * x)
1111-
# 2(x) ==> (call-i 2 * x)
1112-
# (2)(3)x ==> (call-i 2 * 3 x)
1113-
# (x-1)y ==> (call-i (call-i x - 1) * y)
1114-
# x'y ==> (call-i (call-post x ') * y)
1110+
# 2x ==> (juxtapose 2 x)
1111+
# 2(x) ==> (juxtapose 2 x)
1112+
# (2)(3)x ==> (juxtapose 2 3 x)
1113+
# (x-1)y ==> (juxtapose (call-i x - 1) y)
1114+
# x'y ==> (juxtapose (call-post x ') y)
11151115
#
11161116
# flisp: parse-juxtapose
11171117
function parse_juxtapose(ps::ParseState)
@@ -1124,15 +1124,12 @@ function parse_juxtapose(ps::ParseState)
11241124
if !is_juxtapose(ps, prev_kind, t)
11251125
break
11261126
end
1127-
if n_terms == 1
1128-
bump_invisible(ps, K"*")
1129-
end
11301127
if prev_kind == K"string" || is_string_delim(t)
11311128
# issue #20575
11321129
#
1133-
# "a""b" ==> (call-i (string "a") * (error-t) (string "b"))
1134-
# "a"x ==> (call-i (string "a") * (error-t) x)
1135-
# "$y"x ==> (call-i (string (string y)) * (error-t) x)
1130+
# "a""b" ==> (juxtapose (string "a") (error-t) (string "b"))
1131+
# "a"x ==> (juxtapose (string "a") (error-t) x)
1132+
# "$y"x ==> (juxtapose (string (string y)) (error-t) x)
11361133
bump_invisible(ps, K"error", TRIVIA_FLAG,
11371134
error="cannot juxtapose string literal")
11381135
end
@@ -1144,7 +1141,7 @@ function parse_juxtapose(ps::ParseState)
11441141
n_terms += 1
11451142
end
11461143
if n_terms > 1
1147-
emit(ps, mark, K"call", INFIX_FLAG)
1144+
emit(ps, mark, K"juxtapose")
11481145
end
11491146
end
11501147

@@ -1767,8 +1764,8 @@ function parse_struct_field(ps::ParseState)
17671764
parse_eq(ps)
17681765
if const_field
17691766
# Const fields https://github.com/JuliaLang/julia/pull/43305
1770-
#v1.8: struct A const a end ==> (struct false A (block (const x)))
1771-
#v1.7: struct A const a end ==> (struct false A (block (error (const x))))
1767+
#v1.8: struct A const a end ==> (struct A (block (const x)))
1768+
#v1.7: struct A const a end ==> (struct A (block (error (const x))))
17721769
emit(ps, mark, K"const")
17731770
min_supported_version(v"1.8", ps, mark, "`const` struct field")
17741771
end
@@ -1823,23 +1820,21 @@ function parse_resword(ps::ParseState)
18231820
emit(ps, mark, K"for")
18241821
elseif word == K"let"
18251822
bump(ps, TRIVIA_FLAG)
1826-
if peek(ps) KSet"NewlineWs ;"
1827-
# let x=1\n end ==> (let (block (= x 1)) (block))
1828-
# let x=1 ; end ==> (let (block (= x 1)) (block))
1829-
m = position(ps)
1830-
n_subexprs = parse_comma_separated(ps, parse_eq_star)
1831-
kb = peek_behind(ps).kind
1823+
m = position(ps)
1824+
if peek(ps) in KSet"NewlineWs ;"
1825+
# let end ==> (let (block) (block))
1826+
# let ; end ==> (let (block) (block))
1827+
# let ; body end ==> (let (block) (block body))
1828+
else
1829+
# let x=1\n end ==> (let (block (= x 1)) (block))
1830+
# let x=1 ; end ==> (let (block (= x 1)) (block))
18321831
# let x::1 ; end ==> (let (block (::-i x 1)) (block))
18331832
# let x ; end ==> (let (block x) (block))
18341833
# let x=1,y=2 ; end ==> (let (block (= x 1) (= y 2) (block)))
18351834
# let x+=1 ; end ==> (let (block (+= x 1)) (block))
1836-
emit(ps, m, K"block")
1837-
else
1838-
# let end ==> (let (block) (block))
1839-
# let ; end ==> (let (block) (block))
1840-
# let ; body end ==> (let (block) (block body))
1841-
bump_invisible(ps, K"block")
1835+
parse_comma_separated(ps, parse_eq_star)
18421836
end
1837+
emit(ps, m, K"block")
18431838
k = peek(ps)
18441839
if k in KSet"NewlineWs ;"
18451840
bump(ps, TRIVIA_FLAG)
@@ -1934,24 +1929,23 @@ function parse_resword(ps::ParseState)
19341929
bump_closing_token(ps, K"end")
19351930
emit(ps, mark, K"abstract")
19361931
elseif word in KSet"struct mutable"
1937-
# struct A <: B \n a::X \n end ==> (struct false (<: A B) (block (::-i a X)))
1938-
# struct A \n a \n b \n end ==> (struct false A (block a b))
1939-
#v1.7: struct A const a end ==> (struct false A (block (error (const a))))
1940-
#v1.8: struct A const a end ==> (struct false A (block (const a)))
1941-
if word == K"mutable"
1942-
# mutable struct A end ==> (struct true A (block))
1932+
# struct A <: B \n a::X \n end ==> (struct (<: A B) (block (::-i a X)))
1933+
# struct A \n a \n b \n end ==> (struct A (block a b))
1934+
#v1.7: struct A const a end ==> (struct A (block (error (const a))))
1935+
#v1.8: struct A const a end ==> (struct A (block (const a)))
1936+
is_mut = word == K"mutable"
1937+
if is_mut
1938+
# mutable struct A end ==> (struct-mut A (block))
19431939
bump(ps, TRIVIA_FLAG)
1944-
bump_invisible(ps, K"true")
19451940
else
1946-
# struct A end ==> (struct false A (block))
1947-
bump_invisible(ps, K"false")
1941+
# struct A end ==> (struct A (block))
19481942
end
19491943
@check peek(ps) == K"struct"
19501944
bump(ps, TRIVIA_FLAG)
19511945
parse_subtype_spec(ps)
19521946
parse_block(ps, parse_struct_field)
19531947
bump_closing_token(ps, K"end")
1954-
emit(ps, mark, K"struct")
1948+
emit(ps, mark, K"struct", is_mut ? MUTABLE_FLAG : EMPTY_FLAGS)
19551949
elseif word == K"primitive"
19561950
# primitive type A 32 end ==> (primitive A 32)
19571951
# primitive type A 32 ; end ==> (primitive A 32)
@@ -1973,9 +1967,8 @@ function parse_resword(ps::ParseState)
19731967
bump(ps, TRIVIA_FLAG)
19741968
k = peek(ps)
19751969
if k == K"NewlineWs" || is_closing_token(ps, k)
1976-
# return\nx ==> (return nothing)
1977-
# return) ==> (return nothing)
1978-
bump_invisible(ps, K"nothing")
1970+
# return\nx ==> (return)
1971+
# return) ==> (return)
19791972
else
19801973
# return x ==> (return x)
19811974
# return x,y ==> (return (tuple x y))
@@ -1992,22 +1985,22 @@ function parse_resword(ps::ParseState)
19921985
error="unexpected token after $(untokenize(word))")
19931986
end
19941987
elseif word in KSet"module baremodule"
1995-
# module A end ==> (module true A (block))
1996-
# baremodule A end ==> (module false A (block))
1988+
# module A end ==> (module A (block))
1989+
# baremodule A end ==> (module-bare A (block))
19971990
bump(ps, TRIVIA_FLAG)
1998-
bump_invisible(ps, (word == K"module") ? K"true" : K"false")
19991991
if is_reserved_word(peek(ps))
2000-
# module do \n end ==> (module true (error do) (block))
1992+
# module do \n end ==> (module (error do) (block))
20011993
bump(ps, error="Invalid module name")
20021994
else
2003-
# module $A end ==> (module true ($ A) (block))
1995+
# module $A end ==> (module ($ A) (block))
20041996
parse_unary_prefix(ps)
20051997
end
2006-
# module A \n a \n b \n end ==> (module true A (block a b))
2007-
# module A \n "x"\na \n end ==> (module true A (block (doc (string "x") a)))
1998+
# module A \n a \n b \n end ==> (module A (block a b))
1999+
# module A \n "x"\na \n end ==> (module A (block (doc (string "x") a)))
20082000
parse_block(ps, parse_docstring)
20092001
bump_closing_token(ps, K"end")
2010-
emit(ps, mark, K"module")
2002+
emit(ps, mark, K"module",
2003+
word == K"baremodule" ? BARE_MODULE_FLAG : EMPTY_FLAGS)
20112004
elseif word == K"export"
20122005
# export a ==> (export a)
20132006
# export @a ==> (export @a)

src/syntax_tree.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,6 @@ function SyntaxNode(source::SourceFile, raw::GreenNode{SyntaxHead}, position::In
107107
isempty(val_range) ?
108108
Symbol(untokenize(k)) : # synthetic invisible tokens
109109
Symbol(normalize_identifier(val_str))
110-
elseif k == K"nothing"
111-
nothing
112110
elseif k == K"error"
113111
ErrorVal()
114112
elseif k == K"MacroName"

test/expr.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,34 @@
296296
Expr(:block, LineNumberNode(1), :z))
297297
end
298298

299+
@testset "juxtapose" begin
300+
@test parse(Expr, "2x") == Expr(:call, :*, 2, :x)
301+
@test parse(Expr, "(2)(3)x") == Expr(:call, :*, 2, 3, :x)
302+
end
303+
299304
@testset "Core.@doc" begin
300305
@test parse(Expr, "\"x\" f") ==
301306
Expr(:macrocall, GlobalRef(Core, Symbol("@doc")), LineNumberNode(1), "x", :f)
302307
@test parse(Expr, "\n\"x\" f") ==
303308
Expr(:macrocall, GlobalRef(Core, Symbol("@doc")), LineNumberNode(2), "x", :f)
304309
end
310+
311+
@testset "return" begin
312+
@test parse(Expr, "return x") == Expr(:return, :x)
313+
@test parse(Expr, "return") == Expr(:return, nothing)
314+
end
315+
316+
@testset "struct" begin
317+
@test parse(Expr, "struct A end") ==
318+
Expr(:struct, false, :A, Expr(:block, LineNumberNode(1)))
319+
@test parse(Expr, "mutable struct A end") ==
320+
Expr(:struct, true, :A, Expr(:block, LineNumberNode(1)))
321+
end
322+
323+
@testset "module" begin
324+
@test parse(Expr, "module A end") ==
325+
Expr(:module, true, :A, Expr(:block, LineNumberNode(1), LineNumberNode(1)))
326+
@test parse(Expr, "baremodule A end") ==
327+
Expr(:module, false, :A, Expr(:block, LineNumberNode(1), LineNumberNode(1)))
328+
end
305329
end

test/hooks.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838

3939
@testset "Expr(:incomplete)" begin
4040
JuliaSyntax.enable_in_core!()
41-
4241
@test Meta.isexpr(Meta.parse("[x"), :incomplete)
4342

4443
for (str, tag) in [

0 commit comments

Comments
 (0)