Utilities

# Utilities

## Function definitions

Function definitions pose a problem to pattern matching, since there are a lot of different ways to define a function. For example, a pattern that captures `f(x) = 2x` will not match

``````function f(x)
return 2x
end``````

There are a couple of ways to handle this. One way is to use `longdef` or `shortdef` to normalise function definitions to short form, before matching it.

``````julia> ex = :(function f(x) 2x end)
:(function f(x)
#= none:1 =#
2x
end)

julia> MacroTools.shortdef(ex)
:(f(x) = begin
#= none:1 =#
2x
end)``````

More generally it's also possible to use `splitdef` and `combinedef` to handle the full range of function syntax.

`splitdef(def)` matches a function definition of the form

``````function name(args; kwargs)::rtype where {whereparams}
body
end``````

and returns `Dict(:name=>..., :args=>..., etc.)`. The definition can be rebuilt by calling `MacroTools.combinedef(dict)`, or explicitly with

``````rtype = get(dict, :rtype, :Any)
:(function \$(dict[:name])(\$(dict[:args]...);
\$(dict[:kwargs]...))::\$rtype where {\$(dict[:whereparams]...)}
\$(dict[:body].args...)
end)``````

`splitarg(arg)` matches function arguments (whether from a definition or a function call) such as `x::Int=2` and returns `(arg_name, arg_type, slurp, default)`. `default` is `nothing` when there is none. For example:

``````> map(splitarg, (:(f(y, a=2, x::Int=nothing, args...))).args[2:end])
4-element Array{Tuple{Symbol,Symbol,Bool,Any},1}:
(:y, :Any, false, nothing)
(:a, :Any, false, 2)
(:x, :Int, false, :nothing)
(:args, :Any, true, nothing)``````

## Other Utilities

``isexpr(x, ts...)``

Convenient way to test the type of a Julia expression. Expression heads and types are supported, so for example you can call

``isexpr(expr, String, :string)``

to pick up on all string-like expressions.

``rmlines(x)``

Remove the line nodes from a block or array of expressions.

Compare `quote end` vs `rmlines(quote end)`

Examples

To work with nested blocks:

``prewalk(rmlines, ex)``
``unblock(expr)``

Remove outer `begin` blocks from an expression, if the block is redundant (i.e. contains only a single expression).

An easy way to get the (function/type) name out of expressions like `foo{T}` or `Bar{T} <: Vector{T}`.

``inexpr(expr, x)``

Simple expression match; will return `true` if the expression `x` can be found inside `expr`.

``inexpr(:(2+2), 2) == true``
``gensym_ids(expr)``

Replaces gensyms with unique ids (deterministically).

``````julia> x, y = gensym("x"), gensym("y")
(Symbol("##x#363"), Symbol("##y#364"))

julia> MacroTools.gensym_ids(:(\$x+\$y))
:(x_1 + y_2)``````
``alias_gensyms(expr)``

Replaces gensyms with animal names. This makes gensym'd code far easier to follow.

``````julia> x, y = gensym("x"), gensym("y")
(Symbol("##x#363"), Symbol("##y#364"))

julia> MacroTools.alias_gensyms(:(\$x+\$y))
:(porcupine + gull)``````

More convenient macro expansion, e.g.

``@expand @time foo()``

Test for function definition expressions.

``flatten(ex)``

Flatten any redundant blocks into a single block, over the whole expression.

``prettify(ex)``

Makes generated code generaly nicer to look at.