Cyanのマクロの基本

マクロで式を作る時、Lispと同じく準クオートとアンクオートを使うのが簡単だけど、もう1つPorter.new()やMessenger.new()を使う方法がある。

cyan> p = '(a b c)
 => a(b(c))
cyan> p.slots()
 => %{ callee => a, args => &(b(c)), parent => #<Porter> }
cyan> m = '(a + b + c)
 => a.(+)(b).(+)(c)
cyan> m.slots()
 => %{ receiver => a.(+)(b), args => &(c), message => (+), parent => #<Messenger> }

上の関数適用の形の式がPorterで、下のメソッド呼び出しの形の式がMessenger。
生成するには、Porter.new(callee, args), Messenger(receiver, message, args)とする。

def(mkblock)^(list):
  `begin(?Block.new(list))

mac(p2m)^(body):
  mkblock body.list.map^(p):
    m := Messenger.new(p.callee, p.args[0].callee, &(p.args[0].args[0]))
    `(say ?p.to_s() + " => " + ?m.to_s() + " = " + (?m).to_s())

def(test)^:
  p2m:
    10 rem 4
    20 rem 5
    28 rem 6

Block.newは、式のリスト[exp1, exp2, ... , expN]を受け取ってブロック{ exp1, exp2, ... ,expN }を返す。で、begin関数はブロックを受け取って、中の式を順番に評価する。

実行

cyan> test()
10(rem(4)) => 10.rem(4) = 2
20(rem(5)) => 20.rem(5) = 0
28(rem(6)) => 28.rem(6) = 4
 => "28(rem(6)) => 28.rem(6) = 4"

メソッド呼び出しの式(Messenger)を作る時、messageを可変にしたい場合に準クオート&アンクオートを使おうとしても

cyan> mac(bad)^(mes): `(5.?mes(3))
(stdin):1: error: syntax error: unexpected Unquote, expecting Symbol
cyan> mac(bad)^(mes): `(5.?(mes)(3))
(stdin):1: error: syntax error: unexpected Unquote, expecting Symbol
cyan> mac(bad)^(mes): `(5.(?mes)(3))
(stdin):1: error: syntax error: unexpected Unquote, expecting Operator

書きようがないのでMessenger.new()を使いましょう、ということでよいのかな。

参考: 擬似クラス