Utility macros and functions for Julia
## Some useful and/or interesting Julia macros. I haven't seen many | |
## examples online, so I hope this might be useful for people trying | |
## to understand macros. Caveat Emptor; I don't understand them that | |
## well myself. Additions and improvements are more than welcome. | |
## | |
## Everything here is available under the MIT license (see bottom of | |
## file) | |
## | |
## - Gray Calhoun (@grayclhn) 10/17/2014 | |
using Base.Meta | |
## Basic functions for working with loops | |
isloop(e) = false | |
isloop(e::Expr) = isexpr(e, [:comprehension, :for, :while]) | |
function loophead!(e::Expr, s::Expr) | |
if isexpr(e, :comprehension) | |
e.args[2] = s | |
elseif isexpr(e, [:for, :while]) | |
e.args[1] = s | |
else | |
stop("$(e.head) is not supported yet.") | |
end | |
end | |
function loopbody!(e::Expr, s::Expr) | |
if isexpr(e, :comprehension) | |
e.args[1] = s | |
elseif isexpr(e, [:for, :while]) | |
return e.args[2] = s | |
else | |
stop("$(e.head) is not supported yet.") | |
end | |
end | |
function loophead(e::Expr) | |
isexpr(e, :comprehension) && return e.args[2] | |
isexpr(e, [:for, :while]) && return e.args[1] | |
stop("$(e.head) is not supported yet.") | |
end | |
function loopbody(e::Expr) | |
isexpr(e, :comprehension) && return e.args[1] | |
isexpr(e, [:for, :while]) && return e.args[2] | |
stop("$(e.head) is not supported yet.") | |
end | |
## Helper function for the @any and @all macros; this function takes a | |
## list of expressions and chains them together with the symbol s, so | |
## chain_exprs(:&&, [:(x == 1), :(y == 2), (z == 3)]) | |
## becomes | |
## :(x == 1 && y == 2 && z == 3 | |
function chain_exprs(s::Symbol, e...) | |
length(e) <= 1 && return e[1] | |
length(e) == 2 && return Expr(s, e[1], e[2]) | |
return make_chained(s, Expr(s, e[1], e[2]), e[3:end]...) | |
end | |
## Helper function for the @any and @all macros; this function turns | |
## an array comprehension or a for loop into a loop that breaks | |
## _immediately_ when its body returns `value`. | |
shortcircuit_loop(loop::Expr, value::Bool) = | |
Expr(:block, :(v = !$value), Expr(:for, loophead(loop), quote | |
if ($value == $(loopbody(loop))) | |
v = $value | |
break | |
end end), :v) | |
## Short-circuit `any` macro that ends when the first element is | |
## true. This is designed to be used in array comprehensions: | |
## @any [x < 2 for x in 1:1000000000000000000000000000000000] | |
## should immediately return `true` | |
macro any(e...) | |
length(e) >= 2 && return chain_exprs(:||, e...) | |
isexpr(e[1], :comparison) && return e[1] | |
isexpr(e[1], :comprehension) && return shortcircuit_loop(e[1], true) | |
error("@any doesn't support $(e[1].head) yet") | |
end | |
## Short-circuit `any` macro that ends when the first element is | |
## true. This is designed to be used in array comprehensions: | |
## @all [x < 2 for x in 1:1000000000000000000000000000000000] | |
## should return `false` in its second check | |
macro all(e...) | |
isexpr(e, :comparison) && return e | |
length(e) >= 2 && return chain_exprs(:&&, e...) | |
isexpr(e[1], :comprehension) && return shortcircuit_loop(e[1], false) | |
error("@all doesn't support $(e[1].head) yet") | |
end | |
## Copyright (c) 2014 Gray Calhoun | |
## | |
## Permission is hereby granted, free of charge, to any person | |
## obtaining a copy of this software and associated documentation | |
## files (the "Software"), to deal in the Software without | |
## restriction, including without limitation the rights to use, copy, | |
## modify, merge, publish, distribute, sublicense, and/or sell copies | |
## of the Software, and to permit persons to whom the Software is | |
## furnished to do so, subject to the following conditions: | |
## | |
## The above copyright notice and this permission notice shall be | |
## included in all copies or substantial portions of the Software. | |
## | |
## THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |
## EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
## MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
## NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
## BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
## ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
## CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
## SOFTWARE. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
all
is already shortcircuit-enabled: