Diff
Not logged in

Differences From Artifact [caf492581281c25d]:

To Artifact [80b16f2bd31d3d7f]:


450 450 <p> 451 451 <code>@macro</code> レイヤも所詮ただのレイヤですので、 452 452 上で説明した方法で <code>@macro</code> レイヤに関数などを登録しておくことで、 453 453 構文木の生成をいじることが可能です。まさにマクロ。 454 454 </p> 455 455 456 456 $(DDOC_MEMBERS 457 -$(SECTION 使い方, $(SECBODY 457 +$(SECTION 概要, $(SECBODY 458 +<p> 459 +samples/macro.pmy にいくつか使い方サンプルが置いてありますので、詳しくはそちらをどうぞ。 460 +</p> 461 +<pre> 462 + &gt;&gt; @macro( twice(print("Hello")) ) 463 + { 464 + pos: {lineno:1, column:9, filename:<REPL>}, 465 + args: [ { pos: {lineno:1, column:15, filename:<REPL>}, 466 + args: [{pos:{lineno:1, column:21, filename:<REPL>}, 467 + is:Str, 468 + data:Hello}], 469 + is: App, 470 + fun: {pos:{lineno:1, column:15, filename:<REPL>}, is:Var, name:print}} 471 + ], 472 + is: App, 473 + fun: {pos:{lineno:1, column:9, filename:<REPL>}, is:Var, name:twice} 474 + } 475 +</pre> 476 +<p> 477 +詳細は気にしなくて構いませんが、とにかく、<tt>@macro</tt> レイヤでは、 478 +基本的には、コードを実行するとそのコードの構文木がでてきます。 479 +この挙動は <tt>@macro</tt> レイヤの変数をセットすることで、カスタマイズできます。 480 +</p> 481 +<pre> 482 + &gt;&gt; @macro twice(x) { x; x } in twice(print("Hello")) 483 + Hello 484 + Hello 485 + Hello 486 +</pre> 487 +<p> 488 +(3回出力されてますが、3個目は <tt>print(x)</tt> の返値は <tt>x</tt> なので、 489 +それがREPLによって印字されているだけです。) 490 +<tt>@macro</tt> レイヤで <tt>in</tt> 以降を実行すると、<tt>print("Hello")</tt> という式を表す構文木が作られ、 491 +それが <tt>twice</tt> 関数に渡されます。<tt>twice</tt> の中身も <tt>@macro</tt> レイヤで実行されるので、 492 +構文木を作ろうとしますが、変数 <tt>x</tt> には <tt>@macro</tt> レイヤで値が入っているので、 493 +その値を読み取って構文木を作成します。 494 +結果として、2回 <tt>print("Hello")</tt> する構文木が作られて、 495 +その後で、それが <tt>@value</tt> レイヤで実行されています。 496 +</p> 497 +<p> 498 +本当にベタに構文木を作るだけなので、変数名の衝突などなどは気にしません。「衛生的でない」マクロです。 499 +</p> 500 +<pre> 501 + @macro LetItBe(x, y) { var $(B it) = x; y }; $(D_COMMENT # y の中で変数 it が使える) 502 + print( LetItBe("myself", "when I find " ~ $(B it) ~ " in times of trouble") ); 503 +</pre> 504 +<p> 505 +変数名に気をつけるには、組み込み関数 <tt>gensym()</tt> を使って頑張って下さい。 506 +</p> 507 +)) 508 +$(SECTION レイヤ切り替え, $(SECBODY 509 +<p> 510 +他のレイヤ同様、<tt>@macro</tt> レイヤを実行中に <tt>@layer( ... )</tt> 構文を使うことで、 511 +別のレイヤでコードを動かすこともできます。よく使う例は、<tt>@value</tt> 512 +レイヤに移ることで構文木を普通に計算して色々プログラム的にいじる用途です。 513 +</p> 458 514 <pre> 459 - When function is invoked, it first run in the @macro layer, and after that, 460 - it run in the neutral layer. Here is an example. 461 - 462 - >> @macro twice(x) { x; x } 463 - >> def f() { twice(print("Hello")); 999 } 464 - (function:173b6a0:1789720) 465 - >> f() 466 - Hello 467 - Hello 468 - 999 469 - 470 - When the interpreter evaluates f(), it first executes 471 - "twice(print("Hello")); 999" 472 - in the @macro layer. Basically what it does is to just construct its syntax tree. 473 - But, since we have defined the "twice" function in the @macro layer, it is 474 - execute as a function. Resulting syntax tree is 475 - "print("Hello"); print("Hello"); 999" 476 - and this is executed on the neutral (in this example, @value) layer. 477 - This is the reason why you see two "Hello"s. 478 - 479 - [[quote and unquote]] 480 - 481 - Here is more involved example of code genration. 482 - From "x", it generates "x*x*x*x*x*x*x*x*x*x". 483 - 484 - @macro pow10(x) { 485 - @value( 486 - def pow(x, n) { 487 - if( n == 1 ) { x } 488 - else { 489 - @macro( @value(x) * @value(pow(x,n-1)) ) 490 - } 491 - } 492 - in 493 - pow(@macro(x),10) 494 - ) 495 - }; 496 - 497 - Here, x is a syntax tree but n is an actual integer. If you read carefully, 498 - you should get what is going on. Basically, @macro can be considered like 499 - quasiquoting and @value to be an escape from it. 515 + @macro reverseArgs(e) {$(B @value)( 516 + def rev(xs, acc) { 517 + case xs when {car:x, cdr:xs}: rev(xs, {car:x, cdr:acc}) when {}: acc 518 + }; 519 + case @macro(e) 520 + when {is:"App", fun:f, args:as}: {is:"App", fun:f, args:rev(as,{})} 521 + when e: e 522 + )}; 523 + print( reverseArgs(1-2) ); $(D_COMMENT # 2-1 == 1) 500 524 </pre> 525 +<p> 526 +<tt>reverseArgs</tt> は、関数呼び出しの構文木の、引数の順番を逆転する関数です。 527 +<tt>@macro(e)</tt> によってマクロレイヤにセットされている構文木引数を取り出し、 528 +それを <tt>@value</tt> レイヤによる普通の計算プログラムで操作しています。 529 +要は、<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 530 +<tt>@value(...)</tt> は「逆クオート (unquote)」に近い働きをします。 531 +</p> 532 +<p> 533 +<tt>@layer(...)</tt> だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、 534 +一部の引数は <tt>@macro</tt>、一部の引数は <tt>@value</tt> レイヤで受け取る関数を書くなど、 535 +さらに色々面白いことが可能です。 536 +</p> 537 +)) 538 +$(SECTION 構文木の構造, $(SECBODY 501 539 <p> 502 540 構文木がどのようなテーブルで渡されてくるかについては、ソースドキュメントの 503 541 <a href="http://www.kmonos.net/repos/polemy/doc/tip/doc/ast.html">polemy.ast</a> 504 542 のページをご覧下さい。例えば変数名を表す <code>Var</code> クラスには、 505 543 継承の分も合わせて 506 544 <tt><a href="http://www.kmonos.net/repos/polemy/doc/tip/doc/failure.html">LexPosition</a> pos;</tt> 507 545 と <tt>string name;</tt> の2つのメンバがあるので ................................................................................ 511 549 pos: {filename:"foo.pmy", lineno:123, column:45}, 512 550 name: "x" } 513 551 </pre> 514 552 <p> 515 553 こんな感じのテーブルになります。 516 554 クラス名が <tt>is</tt> フィールドに、メンバ変数はそのままの名前で入ります。 517 555 配列メンバは cons リストになって入ってきます。 556 +自分で構文木を作る時は、<tt>pos</tt> フィールドだけは省略しても構いません。 518 557 </p> 519 558 )) 520 -$(SECTION 微妙な挙動, $(SECBODY 559 +$(SECTION 微妙なところ, $(SECBODY 560 +<p> 561 +ここまで、<tt>@macro</tt> が本当にただの1レイヤであるかのように説明してきましたが、 562 +実はちょっと幾つかのトリックが潜んでいます。 563 +</p> 564 +<pre> 565 + &gt;&gt; @macro twice(x) {x; x} in twice($(B @value)(print("Hello"))) 566 + Hello 567 + Hello 568 + Hello 569 +</pre> 570 +<p> 571 +先ほどの例に <tt>@value</tt> を増やしたものですが、これでもやはり、Hello 572 +が2回 print されるようになります。 573 +</p> 521 574 <pre> 575 +<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> レイヤという二つが協調して動作しています。 522 576 (rawmacro) レイヤの話 523 577 524 578 [[limitations]] 525 579 526 580 This @macro layer is a very primitive one, and not a perfect macro language. 527 581 Two major limitations are seen in the following "it" example. 528 582