########################################
print("----------");
@macro twice = fun(x) { x; x }; # Hello Hello
twice( print("Hello") );
########################################
print("----------");
@macro maxBad(x,y) {
if x<y then y else x
};
@macro maxGood(x,y) {
var _x = x; # NOTE: no hygenic macro
var _y = y;
if _x < _y then _y else _x
};
def maxFun(x,y) {
if x<y then y else x
};
@macro maxGreat(x,y){@value(
var nx = gensym(); # generate fresh symbol
var ny = gensym();
@macro _x = @macro(nx) {name: nx}; # cheating to create {is:"Var" .. }
@macro _y = @macro(ny) {name: ny}; # at macro layer
{is: "Let", name: nx, layer: "", init: @macro(x), expr:
{is: "Let", name: ny, layer: "", init: @macro(y), expr:
@macro(if _x < _y then _y else _x)}} # the expression
)};
print( maxGood(print(1), print(2)) ); # 1 2 2
print( maxBad(print(1), print(2)) ); # 1 2 2 2
print( maxFun(print(1), print(2)) ); # 1 2 2
print( maxGreat(print(1), print(2)) ); # 1 2 2
print( maxGreat(print(2), print(1)) ); # 2 1 2
########################################
print("----------");
# the variable "it" is inserted to the scope
@macro LetItBe(x, y) { var it = x; y };
print( LetItBe("myself", "when I find " ~ it ~ " in times of trouble") );
########################################
print("----------");
# layerd parameter can also be used for @macro
# you can mix @macro and normal parameters!!
def 3way(n, c1 @macro, c2 @macro, c3 @macro)
{
case n*n
when 0: c1
when 1: c2
when 4: c3
};
3way( rand(3), print("One"), print("Two"), print("Three") );
3way( rand(3), print("One"), print("Two"), print("Three") );
3way( rand(3), print("One"), print("Two"), print("Three") );
########################################
print("----------");
# Conversely, we can mix @value parameter in the macro definition
@macro times(n @value, e) { @value(
if n == 1 then
@macro(e)
else
@macro(e; times(n-1,e))
)};
times(7, print("Ichimen no Nanohana"));
# Here, n is bound to the integer 7, not the AST of it.
########################################
print("----------");
# Explicit manipulation of AST.
# Reverse the order of arguments of the function call expression
@macro reverseArgs(e) {@value(
def rev(xs, acc) {
case xs when {car:x, cdr:xs}: rev(xs, {car:x, cdr:acc}) when {}: acc
};
case @macro(e)
when {is:"App", fun:f, args:as}: {is:"App", fun:f, args:rev(as,{})}
when e : e
)};
print( reverseArgs(1+2) ); # 2+1 == 3
print( reverseArgs(1-2) ); # 2-1 == 1
########################################
print("----------");
# Both prints "original". Macro respects the neutral layer's "let y="
# and "fun(y)". It does not alter the inner scope y
@macro test1(y) { fun(y){y}("original") };
@macro test2(y) { let y = "original" in y };
print( test1("replaced?") );
print( test2("replaced?") );
########################################
print("----------");
# Trick to extract the AST of a function
def foo(x) { x + x };
print( @macro(@value(foo)(arg1)) ); # prints AST for "arg1 + arg1"
# If we wrote @macro(foo(arg1)), it is the AST of "foo(arg1)"
# Here, by @value(foo) we obtain the "usual" behavior of foo,
# not the macro-like bahavior to construct AST "foo(??)".
# But still, by @macro( ... ) layer, the "usual" function is run in
# macro mode.