Skip to content

Commit 9c318e1

Browse files
committed
Fix allow_no_assignment logic for quoted global consts
1 parent 79d5733 commit 9c318e1

File tree

6 files changed

+52
-24
lines changed

6 files changed

+52
-24
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_no_assignment = 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_no_assignm
4848
else
4949
next(ps)
5050
if iskeyword(kindof(ps.t)) && kindof(ps.t) != Tokens.DO
51-
ret = parse_kw(ps; allow_no_assignment = allow_no_assignment)
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_no_assignment = 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_no_assignment = allow_no_assignment)
20+
a = parse_expression(ps)
2121
end
2222
push!(ret, a)
2323
end

src/components/keywords.jl

Lines changed: 19 additions & 8 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_no_assignment = 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_no_assignment = 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_no_assignment = allow_no_assignment)
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_no_assignment = 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_no_assignment = 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,9 +88,13 @@ function parse_kw(ps::ParseState; allow_no_assignment = false)
8588
end
8689
end
8790

88-
function parse_const(ps::ParseState; allow_no_assignment = false)
91+
function parse_const(ps::ParseState)
8992
kw = EXPR(ps)
93+
lt = ps.lt
94+
nt = ps.nt
9095
arg = parse_expression(ps)
96+
allow_no_assignment = has_flag(ps, ParserFlags.AllowConstWithoutAssignment) ||
97+
has_flag(ps, ParserFlags.InQuote) && (kindof(nt) === Tokens.GLOBAL || kindof(lt) === Tokens.GLOBAL)
9198
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
@@ -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_no_assignment = 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_no_assignment = 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_no_assignment = 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_no_assignment = allow_no_assignment)
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/operators.jl

Lines changed: 2 additions & 0 deletions
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]

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"

test/parser.jl

Lines changed: 8 additions & 0 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

0 commit comments

Comments
 (0)