Static Capturing

Static Capturing

We know that MacroTools.jl has brought about a useful macro @capture to capture specific structures from a given AST.

As the motivation of some contributors, @capture of MacroTools.jl has 3 following shortages.

We can implement several new @capture via MLStyle.jl to get better in all aspects.

RAII-Style

This implementation prevents scope leaking.


function capture(template, ex, action)
    let template = Expr(:quote, template)
        quote
            @match $ex begin 
                $template => $action
                _         => nothing
            end
        end 
    end
end

macro capture(template, ex, action)
    capture(template, ex, action) |> esc
end

node = :(f(1))

@capture f($(x :: T where T <: Number)) node begin
    @info x + 1
end

# info: 2

node2 = :(f(x))

@capture f($(x :: T where T <: Number)) node2 begin
    @info x + 1
end

# do nothing

Regex-Style

This implementation collects captured variables into a dictionary, just like groups in regex but more powerful.

For we have to analyse which variables to be caught, this implementation could be a bit verbose(100 lines about scoping analysis) and might not work with your own patterns(application patterns/recognizers and active-patterns are okay).

Check MLStyle-Playground for implementation codes.

@info @capture f($x) :(f(1))
# Dict(:x=>1)

destruct_fn = @capture function $(fname :: Symbol)(a, $(args...)) $(body...) end

@info destruct_fn(:(
    function f(a, x, y, z)
        x + y + z
    end
))

# Dict{Symbol,Any}(
#     :args => Any[:x, :y, :z],
#     :body=> Any[:(#= StaticallyCapturing.jl:93 =#), :(x + y + z)],
#    :fname=>:f
# )