Skip to content

Commit 5759e03

Browse files
authored
Merge pull request #356 from julia-vscode/sp/juliacon-fixes
Various fixes
2 parents 93225f9 + a6fd41d commit 5759e03

File tree

10 files changed

+111
-36
lines changed

10 files changed

+111
-36
lines changed

src/CSTParser.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ Acceptable starting tokens are:
3838
+ An `@`.
3939
4040
"""
41-
function parse_expression(ps::ParseState, esc_on_error = false; allow_const_field = false)
41+
function parse_expression(ps::ParseState, esc_on_error = false)
4242
if kindof(ps.nt) === Tokens.ENDMARKER
4343
ret = mErrorToken(ps, UnexpectedToken)
4444
elseif (esc_on_error && ps.nt.kind == Tokens.ERROR)
@@ -48,7 +48,7 @@ function parse_expression(ps::ParseState, esc_on_error = false; allow_const_fiel
4848
else
4949
next(ps)
5050
if iskeyword(kindof(ps.t)) && kindof(ps.t) != Tokens.DO
51-
ret = parse_kw(ps; allow_const_field = allow_const_field)
51+
ret = parse_kw(ps)
5252
elseif kindof(ps.t) === Tokens.LPAREN
5353
ret = parse_paren(ps)
5454
elseif kindof(ps.t) === Tokens.LSQUARE

src/components/internals.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ const term_c = (Tokens.RPAREN, Tokens.RSQUARE, Tokens.RBRACE, Tokens.END, Tokens
44
Continue parsing statements until an element of `closers` is hit (usually
55
`end`). Statements are grouped in a `Block` EXPR.
66
"""
7-
function parse_block(ps::ParseState, ret::Vector{EXPR}=EXPR[], closers=(Tokens.END,), docable=false; allow_const_field = false)
7+
function parse_block(ps::ParseState, ret::Vector{EXPR}=EXPR[], closers=(Tokens.END,), docable=false)
88
prevpos = position(ps)
99
while kindof(ps.nt) closers # loop until an expected closer is hit
1010
if kindof(ps.nt) term_c # error handling if an unexpected closer is hit
@@ -17,7 +17,7 @@ function parse_block(ps::ParseState, ret::Vector{EXPR}=EXPR[], closers=(Tokens.E
1717
if docable
1818
a = parse_doc(ps)
1919
else
20-
a = parse_expression(ps; allow_const_field = allow_const_field)
20+
a = parse_expression(ps)
2121
end
2222
push!(ret, a)
2323
end

src/components/keywords.jl

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Dispatch function for when the parser has reached a keyword.
55
"""
6-
function parse_kw(ps::ParseState; allow_const_field = false)
6+
function parse_kw(ps::ParseState)
77
k = kindof(ps.t)
88
if ps.closer.precedence == 20 && ps.lt.kind === Tokens.EX_OR && k !== Tokens.END
99
return EXPR(:IDENTIFIER, ps)
@@ -52,7 +52,7 @@ function parse_kw(ps::ParseState; allow_const_field = false)
5252
elseif k === Tokens.BAREMODULE
5353
return @default ps @closer ps :block parse_blockexpr(ps, :baremodule)
5454
elseif k === Tokens.CONST
55-
return @default ps parse_const(ps; allow_const_field = allow_const_field)
55+
return @default ps parse_const(ps)
5656
elseif k === Tokens.GLOBAL
5757
return @default ps parse_local_global(ps, false)
5858
elseif k === Tokens.LOCAL
@@ -75,7 +75,10 @@ function parse_kw(ps::ParseState; allow_const_field = false)
7575
elseif k === Tokens.TYPE
7676
return EXPR(:IDENTIFIER, ps)
7777
elseif k === Tokens.STRUCT
78-
return @default ps @closer ps :block parse_blockexpr(ps, :struct, allow_const_field = true)
78+
enable!(ps, ParserFlags.AllowConstWithoutAssignment)
79+
ret = @default ps @closer ps :block parse_blockexpr(ps, :struct)
80+
disable!(ps, ParserFlags.AllowConstWithoutAssignment)
81+
return ret
7982
elseif k === Tokens.MUTABLE
8083
return @default ps @closer ps :block parse_mutable(ps)
8184
elseif k === Tokens.OUTER
@@ -85,10 +88,14 @@ function parse_kw(ps::ParseState; allow_const_field = false)
8588
end
8689
end
8790

88-
function parse_const(ps::ParseState; allow_const_field = false)
91+
function parse_const(ps::ParseState)
8992
kw = EXPR(ps)
93+
lt = ps.lt
94+
nt = ps.nt
9095
arg = parse_expression(ps)
91-
if !allow_const_field && !(isassignment(unwrapbracket(arg)) || (headof(arg) === :global && length(arg.args) > 0 && isassignment(unwrapbracket(arg.args[1]))))
96+
allow_no_assignment = has_flag(ps, ParserFlags.AllowConstWithoutAssignment) ||
97+
has_flag(ps, ParserFlags.InQuote) && (kindof(nt) === Tokens.GLOBAL || kindof(lt) === Tokens.GLOBAL)
98+
if !allow_no_assignment && !(isassignment(unwrapbracket(arg)) || (headof(arg) === :global && length(arg.args) > 0 && isassignment(unwrapbracket(arg.args[1]))))
9299
arg = mErrorToken(ps, arg, ExpectedAssignment)
93100
end
94101
ret = EXPR(:const, EXPR[arg], EXPR[kw])
@@ -154,7 +161,11 @@ function parse_mutable(ps::ParseState)
154161
if kindof(ps.nt) === Tokens.STRUCT
155162
kw = EXPR(ps)
156163
next(ps)
157-
ret = parse_blockexpr(ps, :mutable, allow_const_field = true)
164+
165+
enable!(ps, ParserFlags.AllowConstWithoutAssignment)
166+
ret = parse_blockexpr(ps, :mutable)
167+
disable!(ps, ParserFlags.AllowConstWithoutAssignment)
168+
158169
pushfirst!(ret.trivia, setparent!(kw, ret))
159170
update_span!(ret)
160171
else
@@ -308,15 +319,15 @@ function parse_do(ps::ParseState, pre::EXPR)
308319
end
309320

310321
"""
311-
parse_blockexpr(ps::ParseState, head; allow_const_field = false)
322+
parse_blockexpr(ps::ParseState, head)
312323
313324
General function for parsing block expressions comprised of a series of statements
314325
terminated by an `end`.
315326
"""
316-
function parse_blockexpr(ps::ParseState, head; allow_const_field = false)
327+
function parse_blockexpr(ps::ParseState, head)
317328
kw = EXPR(ps)
318329
sig = parse_blockexpr_sig(ps, head)
319-
blockargs = parse_block(ps, EXPR[], (Tokens.END,), docable(head); allow_const_field = allow_const_field)
330+
blockargs = parse_block(ps, EXPR[], (Tokens.END,), docable(head))
320331
if head === :begin
321332
EXPR(:block, blockargs, EXPR[kw, accept_end(ps)])
322333
elseif sig === nothing

src/components/lists.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,7 @@ function parse_array_outer(ps::ParseState, trivia, isref)
118118
max_bp = -typemax(Int)
119119
is_start = true
120120
while kindof(ps.nt) !== Tokens.RSQUARE && kindof(ps.nt) !== Tokens.ENDMARKER
121-
a = @nocloser ps :newline @nocloser ps :newline @closesquare ps @closer ps :insquare @closer ps :ws @closer ps :wsop @closer ps :comma parse_expression(ps)
122-
121+
a = @nocloser ps :semicolon @nocloser ps :newline @closesquare ps @closer ps :insquare @closer ps :ws @closer ps :wsop @closer ps :comma parse_expression(ps)
123122
if is_start
124123
args = EXPR[]
125124
if isref && _do_kw_convert(ps, a)

src/components/operators.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,9 @@ function parse_unary_colon(ps::ParseState, op::EXPR)
249249
ret = op
250250
else
251251
prev_errored = ps.errored
252+
enable!(ps, ParserFlags.InQuote)
252253
arg = @precedence ps 20 @nocloser ps :inref parse_expression(ps)
254+
disable!(ps, ParserFlags.InQuote)
253255
if isbracketed(arg) && headof(arg.args[1]) === :errortoken && errorof(arg.args[1]) === UnexpectedAssignmentOp
254256
ps.errored = prev_errored
255257
arg.args[1] = arg.args[1].args[1]
@@ -471,7 +473,11 @@ function parse_operator(ps::ParseState, ret::EXPR, op::EXPR)
471473
ret = EXPR(op, EXPR[ret], nothing)
472474
elseif is_prime(op)
473475
if isidentifier(ret) || isliteral(ret) ||
474-
headof(ret) in (:call, :tuple, :brackets, :ref, :vect, :vcat, :hcat, :typed_vcat, :typed_hcat, :comprehension, :typed_comprehension, :curly, :braces, :braces_cat) ||
476+
headof(ret) in (
477+
:call, :tuple, :brackets, :ref, :vect, :vcat, :hcat, :ncat, :typed_vcat,
478+
:typed_hcat, :typed_ncat, :comprehension, :typed_comprehension, :curly,
479+
:braces, :braces_cat
480+
) ||
475481
headof(ret) === :do ||
476482
is_dot(headof(ret)) ||
477483
is_prime(headof(ret))

src/iterate.jl

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,20 @@ function _const(x, i)
257257
end
258258

259259
function _global(x, i)
260-
if hastrivia(x) && (headof(first(x.trivia)) === :GLOBAL || headof(first(x.trivia)) === :LOCAL)
261-
oddt_evena(x, i)
260+
if length(x.trivia) > length(x.args) # probably :tuple
261+
if i <= 2
262+
x.trivia[i]
263+
elseif isodd(i)
264+
x.args[div(i - 1, 2)]
265+
else
266+
x.trivia[div(i - 2, 2) + 2]
267+
end
262268
else
263-
odda_event(x, i)
269+
if hastrivia(x) && (headof(first(x.trivia)) === :GLOBAL || headof(first(x.trivia)) === :LOCAL)
270+
oddt_evena(x, i)
271+
else
272+
odda_event(x, i)
273+
end
264274
end
265275
end
266276

src/lexer.jl

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ mutable struct ParseState
4242
nnws::RawToken
4343
closer::Closer
4444
errored::Bool
45+
flags::UInt64
4546
end
4647
function ParseState(str::Union{IOBuffer,String})
47-
ps = ParseState(tokenize(str, RawToken), false, RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), Closer(), false)
48+
ps = ParseState(tokenize(str, RawToken), false, RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), RawToken(), Closer(), false, 0)
4849
return next(next(ps))
4950
end
5051

@@ -58,6 +59,15 @@ function ParseState(str::Union{IOBuffer,String}, loc::Int)
5859
return ps
5960
end
6061

62+
module ParserFlags
63+
const AllowConstWithoutAssignment = 0x1
64+
const InQuote = 0x2
65+
end
66+
67+
enable!(ps::ParseState, flag::Integer) = ps.flags |= flag
68+
disable!(ps::ParseState, flag::Integer) = ps.flags &= ~flag
69+
has_flag(ps::ParseState, flag::Integer) = ps.flags & flag > 0
70+
6171
function Base.show(io::IO, ps::ParseState)
6272
println(io, "ParseState at $(position(ps.l.io))")
6373
println(io, "last : ", kindof(ps.lt), " ($(ps.lt))", " ($(wstype(ps.lws)))")
@@ -66,21 +76,18 @@ function Base.show(io::IO, ps::ParseState)
6676
end
6777
peekchar(ps::ParseState) = peekchar(ps.l)
6878
if !applicable(Base.peek, Tuple{IOBuffer, Char})
69-
function _peek(s::IO, ::Type{T}) where T
70-
mark(s)
71-
try read(s, T)::T
72-
finally
73-
reset(s)
79+
function _peek(s::IO, ::Type{T}) where T
80+
mark(s)
81+
try read(s, T)::T
82+
finally
83+
reset(s)
84+
end
7485
end
75-
end
76-
peekchar(io) = _peek(io, Char)
86+
peekchar(io) = _peek(io, Char)
7787
else
78-
peekchar(io) = peek(io, Char)
88+
peekchar(io) = peek(io, Char)
7989
end
8090

81-
82-
83-
8491
wstype(t::AbstractToken) = kindof(t) == EmptyWS ? "empty" :
8592
kindof(t) == NewLineWS ? "ws w/ newline" :
8693
kindof(t) == SemiColonWS ? "ws w/ semicolon" : "ws"

src/spec.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,8 @@ end
280280
function Base.length(x::EXPR)
281281
headof(x) === :NONSTDIDENTIFIER && return 0
282282
headof(x) === :flatten && return length(Iterating._flatten_lhs(x))
283-
n = x.args isa Nothing ? 0 : length(x.args)
284-
n += x.trivia !== nothing ? length(x.trivia) : 0
283+
n = x.args === nothing ? 0 : length(x.args)
284+
n += x.trivia === nothing ? 0 : length(x.trivia)
285285
x.head isa EXPR && !(x.head.span === 0) && (n += 1)
286286
return n
287287
end

test/iterate.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ end
2929
@test x[1] === x.trivia[1]
3030
@test x[2] === x.args[1]
3131
end
32+
33+
@testset "global tuple" begin
34+
x = cst"global (a = 1,b = 2)"
35+
@test length(x) == 6
36+
@test x[1] === x.trivia[1]
37+
@test x[2] === x.trivia[2]
38+
@test x[3] === x.args[1]
39+
@test x[4] === x.trivia[3]
40+
@test x[5] === x.args[2]
41+
@test x[6] === x.trivia[4]
42+
end
43+
3244
@testset "const" begin
3345
x = cst"const a = 1"
3446
@test length(x) == 2

test/parser.jl

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,14 @@ end
179179
@test ":(;;;)" |> test_expr
180180
end
181181

182+
# this errors during *lowering*, not parsing.
183+
@testset "parse const without assignment in quote" begin
184+
@test ":(global const x)" |> test_expr
185+
@test ":(global const x::Int)" |> test_expr
186+
@test ":(const global x)" |> test_expr
187+
@test ":(const global x::Int)" |> test_expr
188+
end
189+
182190
@testset "where precedence" begin
183191
@test "a = b where c = d" |> test_expr
184192
@test "a = b where c" |> test_expr
@@ -511,11 +519,13 @@ end
511519
@test "[x;;]" |> test_expr
512520
@test "[x;; y;; z]" |> test_expr
513521
@test "[x;;; y;;;z]" |> test_expr
514-
end
515-
516-
@testset "ncat" begin
522+
@test "[x;;; y;;;z]'" |> test_expr
517523
@test "[1 2; 3 4]" |> test_expr
518524
@test "[1;2;;3;4;;5;6;;;;9]" |> test_expr
525+
if VERSION > v"1.7-"
526+
@test "[let; x; end;; y]" |> test_expr
527+
@test "[let; x; end;;;; y]" |> test_expr
528+
end
519529
end
520530

521531
@testset "typed_ncat" begin
@@ -527,20 +537,40 @@ end
527537
@test "t[x;;; y;;;z]" |> test_expr
528538
@test "t[x;;\ny]" |> test_expr
529539
@test "t[x y;;\nz a]" |> test_expr
540+
@test "t[x y;;\nz a]'" |> test_expr
541+
@test "t[let; x; end;; y]" |> test_expr
542+
@test "t[let; x; end;;;; y]" |> test_expr
530543
end
531544
end
532545

533546
@testset "hcat" begin
534547
@test "[x y]" |> test_expr
548+
@test "[let; x; end y]" |> test_expr
549+
@test "[let; x; end; y]" |> test_expr
535550
end
536551

537552
@testset "typed_hcat" begin
538553
@test "t[x y]" |> test_expr
554+
@test "t[let; x; end y]" |> test_expr
555+
@test "t[let; x; end; y]" |> test_expr
539556
end
540557

541558
@testset "Comprehension" begin
542559
@test "[i for i = 1:10]" |> test_expr
543560
@test "Int[i for i = 1:10]" |> test_expr
561+
@test "[let;x;end for x in x]" |> test_expr
562+
@test "[let; x; end for x in x]" |> test_expr
563+
@test "[let x=x; x+x; end for x in x]" |> test_expr
564+
if VERSION > v"1.7-"
565+
@test """[
566+
[
567+
let l = min((d-k),k);
568+
binomial(d-l,l);
569+
end; for k in 1:d-1
570+
] for d in 2:9
571+
]
572+
""" |> test_expr
573+
end
544574
end
545575
end
546576

0 commit comments

Comments
 (0)