Skip to content

Commit 947359c

Browse files
authored
Fix for operator-named macros: parse @+x as @+ x (#197)
Allow operator-named macros to be used without spaces.
1 parent 0747ea0 commit 947359c

File tree

2 files changed

+25
-30
lines changed

2 files changed

+25
-30
lines changed

src/parser.jl

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1466,7 +1466,13 @@ function parse_identifier_or_interpolate(ps::ParseState)
14661466
end
14671467

14681468
# Parses a chain of sufficies at function call precedence, leftmost binding
1469-
# tightest.
1469+
# tightest. This handles
1470+
# * Bracketed calls like a() b[] c{}
1471+
# * Field access like a.b.c
1472+
# - Various dotted syntax like f.() and f.:x
1473+
# * Adjoint suffix like a'
1474+
# * String macros like a"str" b"""str""" c`str` d```str```
1475+
#
14701476
# f(a).g(b) ==> (call (. (call f a) (quote g)) b)
14711477
#
14721478
# flisp: parse-call-chain, parse-call-with-initial-ex
@@ -1487,15 +1493,23 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
14871493
maybe_strmac_1 = false
14881494
t = peek_token(ps)
14891495
k = kind(t)
1490-
if is_macrocall && (preceding_whitespace(t) || is_closing_token(ps, k))
1496+
if !is_macrocall && ps.space_sensitive && preceding_whitespace(t) &&
1497+
k in KSet"( [ { \" \"\"\" ` ```"
1498+
# [f (x)] ==> (hcat f x)
1499+
# [f x] ==> (hcat f x)
1500+
break
1501+
elseif is_macrocall && (preceding_whitespace(t) || !(k in KSet"( [ { ' ."))
14911502
# Macro calls with space-separated arguments
14921503
# @foo a b ==> (macrocall @foo a b)
14931504
# @foo (x) ==> (macrocall @foo x)
14941505
# @foo (x,y) ==> (macrocall @foo (tuple x y))
14951506
# [@foo x] ==> (vect (macrocall @foo x))
1507+
# [@foo] ==> (vect (macrocall @foo))
14961508
# @var"#" a ==> (macrocall (var @#) a)
14971509
# A.@x y ==> (macrocall (. A (quote @x)) y)
14981510
# A.@var"#" a ==> (macrocall (. A (quote (var @#))) a)
1511+
# @+x y ==> (macrocall @+ x y)
1512+
# [email protected] ==> (macrocall (. A (quote @.)) x)
14991513
fix_macro_name_kind!(ps, macro_name_position)
15001514
let ps = with_space_sensitive(ps)
15011515
# Space separated macro arguments
@@ -1523,11 +1537,6 @@ function parse_call_chain(ps::ParseState, mark, is_macrocall=false)
15231537
emit(ps, mark, K"macrocall")
15241538
end
15251539
break
1526-
elseif (ps.space_sensitive && preceding_whitespace(t) &&
1527-
k in KSet"( [ { \ Char \" \"\"\" ` ```")
1528-
# [f (x)] ==> (hcat f x)
1529-
# [f x] ==> (hcat f x)
1530-
break
15311540
elseif k == K"("
15321541
# f(a,b) ==> (call f a b)
15331542
# f(a=1; b=2) ==> (call f (= a 1) (parameters (= b 2)))

test/parser.jl

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -306,16 +306,22 @@ tests = [
306306
"f(a).g(b)" => "(call (. (call f a) (quote g)) b)"
307307
"\$A.@x" => "(macrocall (. (\$ A) (quote @x)))"
308308

309+
# non-errors in space sensitive contexts
310+
"[f (x)]" => "(hcat f x)"
311+
"[f x]" => "(hcat f x)"
309312
# space separated macro calls
310313
"@foo a b" => "(macrocall @foo a b)"
311314
"@foo (x)" => "(macrocall @foo x)"
312315
"@foo (x,y)" => "(macrocall @foo (tuple x y))"
313316
"A.@foo a b" => "(macrocall (. A (quote @foo)) a b)"
314317
"@A.foo a b" => "(macrocall (. A (quote @foo)) a b)"
315318
"[@foo x]" => "(vect (macrocall @foo x))"
319+
"[@foo]" => "(vect (macrocall @foo))"
316320
"@var\"#\" a" => "(macrocall (var @#) a)" => Expr(:macrocall, Symbol("@#"), LineNumberNode(1), :a)
317321
"A.@x y" => "(macrocall (. A (quote @x)) y)"
318322
"A.@var\"#\" a"=> "(macrocall (. A (quote (var @#))) a)" => Expr(:macrocall, Expr(:., :A, QuoteNode(Symbol("@#"))), LineNumberNode(1), :a)
323+
"@+x y" => "(macrocall @+ x y)"
324+
"[email protected]" => "(macrocall (. A (quote @.)) x)"
319325
# Macro names
320326
"@! x" => "(macrocall @! x)"
321327
"@.. x" => "(macrocall @.. x)"
@@ -329,9 +335,6 @@ tests = [
329335
"@doc x\n\ny" => "(macrocall @doc x)"
330336
"@doc x\nend" => "(macrocall @doc x)"
331337

332-
# non-errors in space sensitive contexts
333-
"[f (x)]" => "(hcat f x)"
334-
"[f x]" => "(hcat f x)"
335338
# calls with brackets
336339
"f(a,b)" => "(call f a b)"
337340
"f(a=1; b=2)" => "(call f (= a 1) (parameters (= b 2)))" =>
@@ -941,26 +944,9 @@ parseall_test_specs = [
941944
end
942945
end
943946

944-
# Known bugs / incompatibilities
945-
broken_tests = [
946-
JuliaSyntax.parse_atom => [
947-
"""var""\"x""\"""" => "x"
948-
# Operator-named macros without spaces
949-
"@!x" => "(macrocall @! x)"
950-
"@..x" => "(macrocall @.. x)"
951-
"@.x" => "(macrocall @__dot__ x)"
952-
]
953-
]
954-
955-
@testset "Broken $production" for (production, test_specs) in broken_tests
956-
@testset "$(repr(input))" for (input,output) in test_specs
957-
if !(input isa AbstractString)
958-
opts,input = input
959-
else
960-
opts = NamedTuple()
961-
end
962-
@test_broken parse_to_sexpr_str(production, input; opts...) == output
963-
end
947+
@testset "Broken tests" begin
948+
# Technically broken. But do we even want this behavior?
949+
@test_broken parse_to_sexpr_str(JuliaSyntax.parse_eq, "var\"\"\"x\"\"\"") == "(var x)"
964950
end
965951

966952
@testset "Trivia attachment" begin

0 commit comments

Comments
 (0)