Skip to content

Commit 3906436

Browse files
authored
Parse keyword args with = head rather than kw (#103)
For Expr there's various cases where = is parsed into a kw head, but this is inconsistent especially when named tuples come into play. That is, the `=` parses to different heads: * as `=` in `(a=1, b=2)` * as `kw` in `f(a=1, b=2)` This causes extra complexity in the parser, and is visually confusing for macro writers. Instead, this change always parses `=` to the K"=" head, converting to the :kw head when lowering to Expr. This all seems to work fairly well, with one fairly obscure but awkward exception - in infix notation such as `(x = 1) != y` the equality is assignment, not a keyword argument.
1 parent 9f416bd commit 3906436

File tree

5 files changed

+148
-139
lines changed

5 files changed

+148
-139
lines changed

src/expr.jl

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ function is_stringchunk(node)
1111
return k == K"String" || k == K"CmdString"
1212
end
1313

14-
function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
14+
function _to_expr(node::SyntaxNode; iteration_spec=false, need_linenodes=true,
15+
eq_to_kw=false, inside_dot_expr=false, inside_vect_or_braces=false)
1516
if !haschildren(node)
1617
val = node.val
1718
if val isa Union{Int128,UInt128,BigInt}
@@ -20,7 +21,6 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
2021
# representation of these.
2122
str = replace(sourcetext(node), '_'=>"")
2223
headsym = :macrocall
23-
k = kind(node)
2424
macname = val isa Int128 ? Symbol("@int128_str") :
2525
val isa UInt128 ? Symbol("@uint128_str") :
2626
Symbol("@big_str")
@@ -29,12 +29,15 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
2929
return val
3030
end
3131
end
32-
if kind(node) == K"?"
32+
nodekind = kind(node)
33+
if nodekind == K"?"
3334
headsym = :if
35+
elseif nodekind == K"=" && !is_decorated(node) && eq_to_kw
36+
headsym = :kw
3437
else
3538
headstr = untokenize(head(node), include_flag_suff=false)
3639
headsym = !isnothing(headstr) ? Symbol(headstr) :
37-
error("Can't untokenize head of kind $(kind(node))")
40+
error("Can't untokenize head of kind $(nodekind)")
3841
end
3942
node_args = children(node)
4043
if headsym == :string || headsym == :cmdstring
@@ -89,26 +92,38 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
8992
args = Vector{Any}(undef, length(node_args)*(insert_linenums ? 2 : 1))
9093
if headsym == :for && length(node_args) == 2
9194
# No line numbers in for loop iteration spec
92-
args[1] = _to_expr(node_args[1], true, false)
95+
args[1] = _to_expr(node_args[1], iteration_spec=true, need_linenodes=false)
9396
args[2] = _to_expr(node_args[2])
9497
elseif headsym == :let && length(node_args) == 2
9598
# No line numbers in let statement binding list
96-
args[1] = _to_expr(node_args[1], false, false)
99+
args[1] = _to_expr(node_args[1], need_linenodes=false)
97100
args[2] = _to_expr(node_args[2])
98101
else
102+
eq_to_kw = headsym == :call && !has_flags(node, INFIX_FLAG) ||
103+
headsym == :ref ||
104+
(headsym == :parameters && !inside_vect_or_braces) ||
105+
(headsym == :tuple && inside_dot_expr)
106+
in_dot = headsym == :.
107+
in_vb = headsym == :vect || headsym == :braces
99108
if insert_linenums
100109
if isempty(node_args)
101110
push!(args, source_location(LineNumberNode, node.source, node.position))
102111
else
103112
for i in 1:length(node_args)
104113
n = node_args[i]
105114
args[2*i-1] = source_location(LineNumberNode, n.source, n.position)
106-
args[2*i] = _to_expr(n)
115+
args[2*i] = _to_expr(n,
116+
eq_to_kw=eq_to_kw,
117+
inside_dot_expr=in_dot,
118+
inside_vect_or_braces=in_vb)
107119
end
108120
end
109121
else
110122
for i in 1:length(node_args)
111-
args[i] = _to_expr(node_args[i])
123+
args[i] = _to_expr(node_args[i],
124+
eq_to_kw=eq_to_kw,
125+
inside_dot_expr=in_dot,
126+
inside_vect_or_braces=in_vb)
112127
end
113128
end
114129
end
@@ -118,8 +133,9 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
118133
if is_infix(node.raw)
119134
args[2], args[1] = args[1], args[2]
120135
end
136+
137+
# Special cases for various expression heads
121138
loc = source_location(LineNumberNode, node.source, node.position)
122-
# Convert elements
123139
if headsym == :macrocall
124140
insert!(args, 2, loc)
125141
elseif headsym in (:call, :ref)
@@ -128,7 +144,7 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
128144
insert!(args, 2, args[end])
129145
pop!(args)
130146
end
131-
elseif headsym in (:tuple, :parameters, :vect)
147+
elseif headsym in (:tuple, :parameters, :vect, :braces)
132148
# Move parameters blocks to args[1]
133149
if length(args) > 1 && Meta.isexpr(args[end], :parameters)
134150
pushfirst!(args, args[end])
@@ -181,7 +197,7 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
181197
# elseif headsym == :string && length(args) == 1 && version <= (1,5)
182198
# Strip string from interpolations in 1.5 and lower to preserve
183199
# "hi$("ho")" ==> (string "hi" "ho")
184-
elseif headsym == :(=)
200+
elseif headsym == :(=) && !is_decorated(node)
185201
if is_eventually_call(args[1]) && !iteration_spec && !Meta.isexpr(args[2], :block)
186202
# Add block for short form function locations
187203
args[2] = Expr(:block, loc, args[2])
@@ -191,10 +207,7 @@ function _to_expr(node::SyntaxNode, iteration_spec=false, need_linenodes=true)
191207
args[1] = Expr(:block, loc, args[1])
192208
elseif headsym == :(->)
193209
if Meta.isexpr(args[2], :block)
194-
parent = node.parent
195-
if parent isa SyntaxNode && kind(parent) != K"do"
196-
pushfirst!(args[2].args, loc)
197-
end
210+
pushfirst!(args[2].args, loc)
198211
else
199212
# Add block for source locations
200213
args[2] = Expr(:block, loc, args[2])

src/kinds.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -875,7 +875,6 @@ const _kind_names =
875875
"string" # A string interior node (possibly containing interpolations)
876876
"cmdstring" # A cmd string node (containing delimiters plus string)
877877
"macrocall"
878-
"kw" # the = in f(a=1)
879878
"parameters" # the list after ; in f(; a=1)
880879
"toplevel"
881880
"tuple"

0 commit comments

Comments
 (0)