Split many kinds of expressions into detailed parts, play with them, and put them back together again.
Think of it as a really souped-up version of splitdef()
and splitarg()
from MacroTools.jl:
using CodeSculptor
expr = :( f(i::Int) = i )
def = SplitFunction(expr)
# Make the function generic
def.args[1].type = :I
push!(def.where_params, SplitType(:( I<:Integer )))
# Modify metadata
def.doc_string = "Does something with any integer"
def.inline = true
# Put it back together and evaluate
eval(combine_expr(def))
# Generate a function call of f
def.body = nothing
def.args[1] = SplitArg(5, false)
@assert eval(combine_expr(def)) === 5
# Check syntax by looking for a null result
is_type_expr(e) = (SplitType(e) !== nothing)
@assert is_type_expr(:( C <: Integer ))
@assert is_type_expr(:D)
@assert !is_type_expr(:( 3 + 8 ))
SplitFunction
for function calls and definitionsSplitMacro
for macro invocationsSplitMeta
for metadata around a definition, such as a docstring or@inline
SplitType
for type declarations (likeC{R, T<:Integer} <: B
)SplitArg
for function arguments (likea::Int...
)
Each field on the SplitX
types is documented with comments.
Each split type can return nothing
when constructed, if the expression fails to parse.
The constructors can also run in a less strict mode by passing false
into the constructor,
for example to stop SplitFunction
from verifying that a function name has valid syntax.
Each of them can be deep-copied by constructing with a source instance to copy from. Some of them offer extra flags to skip copying particular parts of the AST.
is_scopable_name(expr)
checks that the expression is either a Symbol or a series of Symbols separated by a.
expression. Optionally allows type parameters at the end, likeA.B{C}
.expr_deepcopy(e)
works like deepcopy, but without copying special literals/interpolated references (like aModule
).visit_exprs(e)
is likeMacroTools.postwalk()
, but without modifying anything and with extra provided data that tells you exactly where you are in the original AST.unescape(e)
andunescape_deep(e)
help removeesc()
from expressions.
There are some tools to map between operators and assignments (e.x. +
and +=
).
ASSIGNMENT_INNER_OP
maps assignment to operatorASSIGNMENT_WITH_OP
maps operator to assignmentcompute_op(s::Union{Symbol, Val}, a, b)
computes an assignment as an operator- For example,
compute_op(:+=, 3, 4)
returnscompute_op(Val(:+=), 3, 4)
returns7
.
- For example,
SplitField
for struct fields (optionally supporting assigned initial values)- Break apart
SplitFunction
intoSplitCall
,SplitSignature
, andSplitFuncDef
.