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 ))
SplitFunctionfor function calls and definitionsSplitMacrofor macro invocationsSplitMetafor metadata around a definition, such as a docstring or@inlineSplitTypefor type declarations (likeC{R, T<:Integer} <: B)SplitArgfor 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_OPmaps assignment to operatorASSIGNMENT_WITH_OPmaps 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,
SplitFieldfor struct fields (optionally supporting assigned initial values)- Break apart
SplitFunctionintoSplitCall,SplitSignature, andSplitFuncDef.