|
|
@@ -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 |
|
|
|