Skip to content

Commit bd4ae55

Browse files
authored
Use importpath kind rather than K"." (#244)
In `Expr` trees, the internal structure of children of `Expr(:.)` is different within an import statement vs outside. For example, `x.y` parses as * `(. x (quote y))` in normal code * `(. x y)` inside `import` and `using`. This causes awkwardness when writing expression manipulation code because import paths * Need to be distinguished from normal `.` but * Can be nested several layers deep in `:` and `as` nodes within an `import` or `using` parent node. To avoid this situation, here I've created a new K"importpath" kind specifically for import paths.
1 parent 73766d7 commit bd4ae55

File tree

4 files changed

+76
-82
lines changed

4 files changed

+76
-82
lines changed

src/expr.jl

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -334,20 +334,13 @@ function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
334334
pushfirst!(args, :*)
335335
elseif headsym === :struct
336336
pushfirst!(args, has_flags(node, MUTABLE_FLAG))
337-
elseif headsym === :import || headsym == :using
338-
# Permit nonsense additional quoting such as
339-
# import A.(:b).:c
340-
if !isempty(args) && Meta.isexpr(args[1], Symbol(":"))
341-
imports = args[1].args
342-
else
343-
imports = args
344-
end
345-
for imp in imports
346-
imp_path = Meta.isexpr(imp, :as) ? imp.args[1].args : imp.args
347-
for i = 1:length(imp_path)
348-
if imp_path[i] isa QuoteNode
349-
imp_path[i] = imp_path[i].value
350-
end
337+
elseif headsym === :importpath
338+
headsym = :.
339+
for i = 1:length(args)
340+
if args[i] isa QuoteNode
341+
# Permit nonsense additional quoting such as
342+
# import A.(:b).:c
343+
args[i] = args[i].value
351344
end
352345
end
353346
end

src/kinds.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -890,6 +890,7 @@ const _kind_names =
890890
"ref"
891891
"vect"
892892
"parens"
893+
"importpath"
893894
# Concatenation syntax
894895
"braces"
895896
"bracescat"

src/parser.jl

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2374,21 +2374,21 @@ function parse_atsym(ps::ParseState, allow_quotes=true)
23742374
# export ($f) ==> (export ($ f))
23752375
mark = position(ps)
23762376
if allow_quotes && peek(ps) == K":"
2377-
# import A.:+ ==> (import (. A (quote +)))
2377+
# import A.:+ ==> (import (importpath A (quote +)))
23782378
emit_diagnostic(ps, warning="quoting with `:` is not required here")
23792379
end
23802380
parse_unary_prefix(ps)
23812381
pos = position(ps)
23822382
warn_parens = false
23832383
if peek_behind(ps, pos).kind == K"parens"
2384-
# import A.(:+) ==> (import (. A (parens (quote +))))
2384+
# import A.(:+) ==> (import (importpath A (parens (quote +))))
23852385
pos = first_child_position(ps, pos)
23862386
warn_parens = true
23872387
end
23882388
if allow_quotes && peek_behind(ps, pos).kind == K"quote"
23892389
pos = first_child_position(ps, pos)
23902390
if peek_behind(ps, pos).kind == K"parens"
2391-
# import A.:(+) ==> (import (. A (quote (parens +))))
2391+
# import A.:(+) ==> (import (importpath A (quote (parens +))))
23922392
pos = first_child_position(ps, pos)
23932393
warn_parens = true
23942394
end
@@ -2423,22 +2423,22 @@ function parse_imports(ps::ParseState)
24232423
bump(ps, TRIVIA_FLAG)
24242424
has_import_prefix = true
24252425
if initial_as
2426-
# import A as B: x ==> (import (: (error (as (. A) B)) (. x)))
2426+
# import A as B: x ==> (import (: (error (as (importpath A) B)) (importpath x)))
24272427
emit(ps, emark, K"error", error="`as` before `:` in import/using")
24282428
end
24292429
elseif k == K","
24302430
bump(ps, TRIVIA_FLAG)
24312431
has_comma = true
24322432
end
24332433
if has_import_prefix || has_comma
2434-
# import A, y ==> (import (. A) (. y))
2435-
# import A: x, y ==> (import (: (. A) (. x) (. y)))
2436-
# import A: +, == ==> (import (: (. A) (. +) (. ==)))
2434+
# import A, y ==> (import (importpath A) (importpath y))
2435+
# import A: x, y ==> (import (: (importpath A) (importpath x) (importpath y)))
2436+
# import A: +, == ==> (import (: (importpath A) (importpath +) (importpath ==)))
24372437
has_import_prefix_ = has_import_prefix
24382438
parse_comma_separated(ps, ps1->parse_import(ps1, word, has_import_prefix_))
24392439
if peek(ps) == K":"
24402440
# Error recovery
2441-
# import A: x, B: y ==> (import (: (. A) (. x) (. B) (error-t (. y))))
2441+
# import A: x, B: y ==> (import (: (importpath A) (importpath x) (importpath B) (error-t (importpath y))))
24422442
emark = position(ps)
24432443
bump(ps, TRIVIA_FLAG)
24442444
parse_comma_separated(ps, ps1->parse_import(ps1, word, has_import_prefix_))
@@ -2447,11 +2447,11 @@ function parse_imports(ps::ParseState)
24472447
end
24482448
end
24492449
if has_import_prefix
2450-
# import A: x ==> (import (: (. A) (. x)))
2450+
# import A: x ==> (import (: (importpath A) (importpath x)))
24512451
emit(ps, mark, K":")
24522452
end
2453-
# using A ==> (using (. A))
2454-
# import A ==> (import (. A))
2453+
# using A ==> (using (importpath A))
2454+
# import A ==> (import (importpath A))
24552455
emit(ps, mark, word)
24562456
end
24572457

@@ -2461,21 +2461,21 @@ end
24612461
function parse_import(ps::ParseState, word, has_import_prefix)
24622462
mark = position(ps)
24632463
parse_import_path(ps)
2464-
# import A: x, y ==> (import (: (. A) (. x) (. y)))
2464+
# import A: x, y ==> (import (: (importpath A) (importpath x) (importpath y)))
24652465
if peek(ps) == K"as"
2466-
# import A as B ==> (import (as (. A) B))
2467-
# import A: x as y ==> (import (: (. A) (as (. x) y)))
2468-
# using A: x as y ==> (using (: (. A) (as (. x) y)))
2466+
# import A as B ==> (import (as (importpath A) B))
2467+
# import A: x as y ==> (import (: (importpath A) (as (importpath x) y)))
2468+
# using A: x as y ==> (using (: (importpath A) (as (importpath x) y)))
24692469
bump(ps, TRIVIA_FLAG)
24702470
parse_atsym(ps)
24712471
emit(ps, mark, K"as")
24722472
if word == K"using" && !has_import_prefix
2473-
# using A as B ==> (using (error (as (. A) B)))
2474-
# using A, B as C ==> (using (. A) (error (as (. B) C)))
2473+
# using A as B ==> (using (error (as (importpath A) B)))
2474+
# using A, B as C ==> (using (importpath A) (error (as (importpath B) C)))
24752475
emit(ps, mark, K"error",
24762476
error="`using` with `as` renaming requires a `:` and context module")
24772477
end
2478-
#v1.5: import A as B ==> (import (error (as (. A) B)))
2478+
#v1.5: import A as B ==> (import (error (as (importpath A) B)))
24792479
min_supported_version(v"1.6", ps, mark, "`import ... as`")
24802480
return true
24812481
else
@@ -2489,12 +2489,12 @@ function parse_import_path(ps::ParseState)
24892489
bump_trivia(ps)
24902490
# The tokenizer produces conjoined dotted tokens .. and ...
24912491
# When parsing import we must split these into single dots
2492-
# import .A ==> (import (. . A))
2493-
# import ..A ==> (import (. . . A))
2494-
# import ...A ==> (import (. . . . A))
2495-
# import ....A ==> (import (. . . . . A))
2492+
# import .A ==> (import (importpath . A))
2493+
# import ..A ==> (import (importpath . . A))
2494+
# import ...A ==> (import (importpath . . . A))
2495+
# import ....A ==> (import (importpath . . . . A))
24962496
# Dots with spaces are allowed (a misfeature?)
2497-
# import . .A ==> (import (. . . A))
2497+
# import . .A ==> (import (importpath . . A))
24982498
first_dot = true
24992499
while true
25002500
t = peek_token(ps)
@@ -2516,50 +2516,50 @@ function parse_import_path(ps::ParseState)
25162516
end
25172517
if is_dotted(peek_token(ps))
25182518
# Modules with operator symbol names
2519-
# import .⋆ ==> (import (. . ⋆))
2519+
# import .⋆ ==> (import (importpath . ⋆))
25202520
bump_trivia(ps)
25212521
bump_split(ps, (1,K".",EMPTY_FLAGS), (1,peek(ps),EMPTY_FLAGS))
25222522
else
2523-
# import @x ==> (import (. @x))
2524-
# import $A ==> (import (. ($ A)))
2523+
# import @x ==> (import (importpath @x))
2524+
# import $A ==> (import (importpath ($ A)))
25252525
parse_atsym(ps)
25262526
end
25272527
while true
25282528
t = peek_token(ps)
25292529
k = kind(t)
25302530
if k == K"."
2531-
# import A.B ==> (import (. A B))
2532-
# import $A.@x ==> (import (. ($ A) @x))
2533-
# import A.B.C ==> (import (. A B C))
2531+
# import A.B ==> (import (importpath A B))
2532+
# import $A.@x ==> (import (importpath ($ A) @x))
2533+
# import A.B.C ==> (import (importpath A B C))
25342534
bump_disallowed_space(ps)
25352535
bump(ps, TRIVIA_FLAG)
25362536
parse_atsym(ps)
25372537
elseif is_dotted(t)
25382538
# Resolve tokenization ambiguity: In imports, dots are part of the
25392539
# path, not operators
2540-
# import A.== ==> (import (. A ==))
2541-
# import A.⋆.f ==> (import (. A ⋆ f))
2540+
# import A.== ==> (import (importpath A ==))
2541+
# import A.⋆.f ==> (import (importpath A ⋆ f))
25422542
if preceding_whitespace(t)
25432543
# Whitespace in import path allowed but discouraged
2544-
# import A .== ==> (import (. A ==))
2544+
# import A .== ==> (import (importpath A ==))
25452545
emit_diagnostic(ps, whitespace=true,
25462546
warning="space between dots in import path")
25472547
end
25482548
bump_trivia(ps)
25492549
bump_split(ps, (1,K".",TRIVIA_FLAG), (1,k,EMPTY_FLAGS))
25502550
elseif k == K"..."
25512551
# Import the .. operator
2552-
# import A... ==> (import (. A ..))
2552+
# import A... ==> (import (importpath A ..))
25532553
bump_split(ps, (1,K".",TRIVIA_FLAG), (2,K"..",EMPTY_FLAGS))
25542554
elseif k in KSet"NewlineWs ; , : EndMarker"
2555-
# import A; B ==> (import (. A))
2555+
# import A; B ==> (import (importpath A))
25562556
break
25572557
else
25582558
# Could we emit a more comprehensible error here?
25592559
break
25602560
end
25612561
end
2562-
emit(ps, mark, K".")
2562+
emit(ps, mark, K"importpath")
25632563
end
25642564

25652565
# parse comma-separated assignments, like "i=1:n,j=1:m,..."

test/parser.jl

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -615,46 +615,46 @@ tests = [
615615
"try x end" => "(try (block x) (error-t))"
616616
],
617617
JuliaSyntax.parse_imports => [
618-
"import A as B: x" => "(import (: (error (as (. A) B)) (. x)))"
619-
"import A, y" => "(import (. A) (. y))"
620-
"import A: +, ==" => "(import (: (. A) (. +) (. ==)))"
621-
"import A: x, y" => "(import (: (. A) (. x) (. y)))"
622-
"import A: x, B: y" => "(import (: (. A) (. x) (. B) (error-t (. y))))"
623-
"import A: x" => "(import (: (. A) (. x)))"
624-
"using A" => "(using (. A))"
625-
"import A" => "(import (. A))"
618+
"import A as B: x" => "(import (: (error (as (importpath A) B)) (importpath x)))"
619+
"import A, y" => "(import (importpath A) (importpath y))"
620+
"import A: +, ==" => "(import (: (importpath A) (importpath +) (importpath ==)))"
621+
"import A: x, y" => "(import (: (importpath A) (importpath x) (importpath y)))"
622+
"import A: x, B: y" => "(import (: (importpath A) (importpath x) (importpath B) (error-t (importpath y))))"
623+
"import A: x" => "(import (: (importpath A) (importpath x)))"
624+
"using A" => "(using (importpath A))"
625+
"import A" => "(import (importpath A))"
626626
# parse_import
627-
"import A: x, y" => "(import (: (. A) (. x) (. y)))"
628-
"import A as B" => "(import (as (. A) B))"
629-
"import A: x as y" => "(import (: (. A) (as (. x) y)))"
630-
"using A: x as y" => "(using (: (. A) (as (. x) y)))"
631-
((v=v"1.5",), "import A as B") => "(import (error (as (. A) B)))"
632-
"using A as B" => "(using (error (as (. A) B)))"
633-
"using A, B as C" => "(using (. A) (error (as (. B) C)))"
627+
"import A: x, y" => "(import (: (importpath A) (importpath x) (importpath y)))"
628+
"import A as B" => "(import (as (importpath A) B))"
629+
"import A: x as y" => "(import (: (importpath A) (as (importpath x) y)))"
630+
"using A: x as y" => "(using (: (importpath A) (as (importpath x) y)))"
631+
((v=v"1.5",), "import A as B") => "(import (error (as (importpath A) B)))"
632+
"using A as B" => "(using (error (as (importpath A) B)))"
633+
"using A, B as C" => "(using (importpath A) (error (as (importpath B) C)))"
634634
# parse_import_path
635635
# When parsing import we must split initial dots into nontrivial
636636
# leading dots for relative paths
637-
"import .A" => "(import (. . A))"
638-
"import ..A" => "(import (. . . A))"
639-
"import ...A" => "(import (. . . . A))"
640-
"import ....A" => "(import (. . . . . A))"
637+
"import .A" => "(import (importpath . A))"
638+
"import ..A" => "(import (importpath . . A))"
639+
"import ...A" => "(import (importpath . . . A))"
640+
"import ....A" => "(import (importpath . . . . A))"
641641
# Dots with spaces are allowed (a misfeature?)
642-
"import . .A" => "(import (. . . A))"
642+
"import . .A" => "(import (importpath . . A))"
643643
# Modules with operator symbol names
644-
"import .⋆" => "(import (. . ⋆))"
644+
"import .⋆" => "(import (importpath . ⋆))"
645645
# Expressions allowed in import paths
646-
"import @x" => "(import (. @x))"
647-
"import \$A" => "(import (. (\$ A)))"
648-
"import \$A.@x" => "(import (. (\$ A) @x))"
649-
"import A.B" => "(import (. A B))"
650-
"import A.B.C" => "(import (. A B C))"
651-
"import A.:+" => "(import (. A (quote +)))"
652-
"import A.(:+)"=> "(import (. A (parens (quote +))))"
653-
"import A.:(+)" => "(import (. A (quote (parens +))))"
654-
"import A.==" => "(import (. A ==))"
655-
"import A.⋆.f" => "(import (. A ⋆ f))"
656-
"import A..." => "(import (. A ..))"
657-
"import A; B" => "(import (. A))"
646+
"import @x" => "(import (importpath @x))"
647+
"import \$A" => "(import (importpath (\$ A)))"
648+
"import \$A.@x" => "(import (importpath (\$ A) @x))"
649+
"import A.B" => "(import (importpath A B))"
650+
"import A.B.C" => "(import (importpath A B C))"
651+
"import A.:+" => "(import (importpath A (quote +)))"
652+
"import A.(:+)" => "(import (importpath A (parens (quote +))))"
653+
"import A.:(+)" => "(import (importpath A (quote (parens +))))"
654+
"import A.==" => "(import (importpath A ==))"
655+
"import A.⋆.f" => "(import (importpath A ⋆ f))"
656+
"import A..." => "(import (importpath A ..))"
657+
"import A; B" => "(import (importpath A))"
658658
],
659659
JuliaSyntax.parse_iteration_spec => [
660660
"i = rhs" => "(= i rhs)"

0 commit comments

Comments
 (0)