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

MacroTools.isexprFunction.
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.

MacroTools.rmlinesFunction.
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)
MacroTools.unblockFunction.
unblock(expr)

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

MacroTools.namifyFunction.

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

MacroTools.inexprFunction.
inexpr(expr, x)

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

inexpr(:(2+2), 2) == true
MacroTools.gensym_idsFunction.
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()
MacroTools.isdefFunction.

Test for function definition expressions.

MacroTools.flattenFunction.
flatten(ex)

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

MacroTools.prettifyFunction.
prettify(ex)

Makes generated code generaly nicer to look at.