Skip to content

Instantly share code, notes, and snippets.

@zeeshanlakhani
Forked from zachallaun/macros-in.jl
Created March 4, 2014 23:33
  • Select an option

Select an option

Revisions

  1. @zachallaun zachallaun created this gist Apr 11, 2013.
    211 changes: 211 additions & 0 deletions macros-in.jl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,211 @@
    # time
    # ====

    macro time(ex)
    quote
    local t0 = time_ns()
    local val = $(esc(ex))
    local t1 = time_ns()
    println("elapsed time: ", (t1-t0)/1e9, " seconds")
    val
    end
    end



    # let*
    # ====

    function make_let_star(body, bindings)
    if isempty(bindings)
    body
    else
    sym, exp = bindings[1].args
    :(let $sym = $exp
    $(make_let_star(body, bindings[2:]))
    end)
    end
    end

    macro star(letex)
    body, bindings = letex.args[1], letex.args[2:]
    make_let_star(body, bindings)
    end




    # bindto
    # ======

    macro bindto(sym, ifex)
    if is(ifex.head, :if)
    sym = esc(sym)
    quote
    local $sym
    $(transform_ifex(sym, ifex))
    end
    else
    error("@bindto must be passed a symbol and an if expression")
    end
    end

    function transform_ifex(sym, ifex)
    # assuming an else clause
    pred, thenex, elseex = ifex.args
    elseex = elseex.args[2]
    elseex = elseex.head == :if ? transform_ifex(sym, elseex) : esc(elseex)
    quote
    $sym = $(esc(pred))
    if $sym != false
    $(esc(thenex))
    else
    $elseex
    end
    end
    end

    maybe_inc(n::Int) = n == 0 ? false : n + 1

    @bindto x if maybe_inc(0)
    println("never")
    elseif maybe_inc(1)
    println("success: $x")
    else
    println("fail")
    end

    begin
    local x
    x = maybe_inc(0)
    if x != false
    println("never")
    else
    x = maybe_inc(1)
    if x != false
    println("success: $x")
    else
    println("fail")
    end
    end
    end


    # anaphoric bind
    # ==============

    macro abind(ifex)
    :(@bindto $(esc(:it)) $ifex)
    end


    # multimethods
    # ===========

    # What do we want our macro to look like?
    # =======================================

    # @multi numbercrunch(x::Int, y::Int) = (x >= 0, y >= 0)

    # @method function numbercrunch(x::Int, y::Int)::(true, true)
    # x + y
    # end

    # @method function numbercrunch(x::Int, y::Int)::(true, false)
    # x - y
    # end

    # @method function numbercrunch(x::Int, y::Int)::(false, true)
    # x / y
    # end

    # @method function numbercrunch(x::Int, y::Int)::(false, false)
    # x * y
    # end

    # How could we implement it without macros?
    # =========================================

    # numbercrunch = multidispatch((x::Int, y::Int) -> (x >= 0, y >= 0))

    # addmethod!(numbercrunch, (true, true), function (x::Int, y::Int)
    # x + y
    # end)

    # const dispatchtable = Dict{Function, Dict{Any, Function}}()

    # function multidispatch(fn::Function)
    # methodtable = Dict{Any, Function}()
    # function dispatchfn(args...)
    # val = fn(args...)
    # @bindto method if get(methodtable, val, false)
    # method(args...)
    # else
    # error("No dispatch function for dispatch value $val")
    # end
    # end

    # dispatchtable[dispatchfn] = methodtable
    # dispatchfn
    # end

    # function addmethod!(dispatchfn::Function, val, method::Function)
    # @bindto methodtable if get(dispatchtable, dispatchfn, false)
    # methodtable[val] = method
    # else
    # error("$dispatchfn is not a multimethod dispatch function")
    # end
    # end

    # Macroify
    # ========

    const dispatchtable = Dict{Function, Dict{Any, Function}}()

    macro multi(ex)
    name = ex.args[1].args[1]
    name = isa(name, Symbol) ? esc(name) : esc(name.args[1])
    params = tuple(ex.args[1].args[2:end]...)
    args = map((x) -> isa(x, Symbol) ? x : x.args[1], params)
    pred = ex.args[2]

    @gensym method

    quote
    methodtable = Dict{Any, Function}()
    function $name($(params...))
    val = $pred
    @bindto $method if get(methodtable, val, false)
    $(Expr(:call, method, args...))
    else
    error("No implementation for dispatch value $val")
    end
    end
    dispatchtable[$name] = methodtable
    $name
    end
    end

    function addmethod!(dispatchfn::Function, val, method::Function)
    @bindto methodtable if get(dispatchtable, dispatchfn, false)
    methodtable[val] = method
    else
    error("$dispatchfn is not a multimethod dispatch function")
    end
    dispatchfn
    end

    macro method(ex)
    is(ex.head, :function) || error("@method only supports `function` syntax")

    # Capture and remove the name from the function expression
    name = esc(ex.args[1].args[1])
    ex.args[1] = Expr(:tuple, ex.args[1].args[2:end]...)

    # Capture and remove the dispatch value
    dispatchval = esc(ex.args[2].args[2].args[1])
    delete!(ex.args[2].args, 1:2)

    :(addmethod!($name, $dispatchval, $(esc(ex))))
    end