Index: doc/_common.html ================================================================== --- doc/_common.html +++ doc/_common.html @@ -24,11 +24,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:43 2010 + on Sat Nov 27 01:17:56 2010 </td></tr> </table> </div> <script> Index: doc/ast.html ================================================================== --- doc/ast.html +++ doc/ast.html @@ -592,11 +592,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:44 2010 + on Sat Nov 27 01:17:56 2010 </td></tr> </table> </div> <script> Index: doc/eval.html ================================================================== --- doc/eval.html +++ doc/eval.html @@ -38,20 +38,10 @@ <dd>Objects for maitaining global environment and evaluation of expression on it<br><br> <script>explorer.outline.incSymbolLevel();</script> <dl> -<script>explorer.outline.writeEnabled = true;</script> -<dt><span class="decl">this(); -</span></dt> -<script>explorer.outline.writeEnabled = false;</script> - - -<dd>Initialize evaluator with empty context<br><br> - -</dd> - <script>explorer.outline.writeEnabled = true;</script> <dt><span class="decl">Value <span class="currsymbol">evalAST</span> <script>explorer.outline.addDecl('evalAST');</script> @@ -103,10 +93,20 @@ <dd>Get the global context<br><br> </dd> + +<script>explorer.outline.writeEnabled = true;</script> +<dt><span class="decl">this(); +</span></dt> +<script>explorer.outline.writeEnabled = false;</script> + + +<dd>Initialize evaluator with empty context<br><br> + +</dd> <script>explorer.outline.writeEnabled = true;</script> <dt><span class="decl">void <span class="currsymbol">addPrimitive</span> <script>explorer.outline.addDecl('addPrimitive');</script> @@ -130,11 +130,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:45 2010 + on Sat Nov 27 01:17:57 2010 </td></tr> </table> </div> <script> Index: doc/failure.html ================================================================== --- doc/failure.html +++ doc/failure.html @@ -180,11 +180,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:45 2010 + on Sat Nov 27 01:17:58 2010 </td></tr> </table> </div> <script> Index: doc/fresh.html ================================================================== --- doc/fresh.html +++ doc/fresh.html @@ -48,11 +48,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:46 2010 + on Sat Nov 27 01:17:58 2010 </td></tr> </table> </div> <script> Index: doc/index.html ================================================================== --- doc/index.html +++ doc/index.html @@ -18,14 +18,23 @@ k.inaba<br><br> <b>License:</b><br> NYSL 0.9982 (http://www.kmonos.net/nysl/)<br><br> <p> -このファイルは、言語仕様などの簡単な説明です。 +左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 </p> <p> -あとついでに、左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 +このファイルは、言語仕様などの、やや辞書的な説明です。<br /> +もっとざっくりとした、言語デザインの方向性の魂的なものについては、 +「メタプログラミングの会」の発表スライドをご覧下さい。 +</p> +<p> +あと、 やたらとマクロの章が長くなっていますが、 この部分は、 +レイヤ機能を入れたら自動的にすごく自然にマクロが入るなーと思って、 +おまけで実装してみた程度のものです。 +あんまり重要ではないので、適当にスルーして下さいませ。 +単に、適当に入れたら適当で微妙な部分が多く残ってしまったので注意書きが増えているだけで…。 </p> <script>explorer.outline.incSymbolLevel();</script> <dl> @@ -238,11 +247,11 @@ let-in を縦にチェインしたときだけ、同名変数を破壊的に上書きします (再帰関数の定義が"うまく"いっているのはこの上書きのためです)。 なんでこんなことになっているかというと、 後で説明する「レイヤ」を使ったときに <tt>let foo = ... in @lay foo = ... in ...</tt> -で他レイヤに重ね書きするためであります。 +で他レイヤに重ね書きするため、のつもりです。詳しくは後で。 </p> </dd> </dl> <script>explorer.outline.decSymbolLevel();</script> @@ -290,11 +299,11 @@ <ul> <li>整数: <tt>0</tt>, <tt>123</tt>, <tt>456666666666666666666666666666666666666789</tt>, ...</li> <li>文字列: <tt>"hello, world!"</tt>, ...</li> <li>関数: <tt>fun(x){x+1}</tt></li> <li>テーブル: <tt>{car: 1, cdr: {car: 2, cdr: {}}}</tt></li> - <li>未定義値: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li> + <li>ボトム: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li> </ul> <p> 関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。 テーブルはいわゆるプロトタイプチェーンを持っていて、 自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、 @@ -558,12 +567,11 @@ 動きとしてはこうです。 </p> <ol> <li>関数呼び出し時(とトップレベル環境の実行開始時)に、 まず、<code>@macro</code> レイヤでコードを実行。</li> -<li>返ってきた構文木を、<code>@value</code> レイヤ、 - またはその関数を呼び出したときのレイヤで実行。</li> +<li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li> </ol> <p> <code>@macro</code> レイヤも所詮ただのレイヤですので、 上で説明した方法で <code>@macro</code> レイヤに関数などを登録しておくことで、 構文木の生成をいじることが可能です。まさにマクロ。 @@ -657,12 +665,12 @@ </pre> <p> <tt>reverseArgs</tt> は、関数呼び出しの構文木の、引数の順番を逆転する関数です。 <tt>@macro(e)</tt> によってマクロレイヤにセットされている構文木引数を取り出し、 それを <tt>@value</tt> レイヤによる普通の計算プログラムで操作しています。 -要は、<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 -<tt>@value(...)</tt> は「逆クオート (unquote)」に近い働きをします。 +<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 +<tt>@value(...)</tt> は「逆クオート (unquote)」にちょっと近いかもしれません。 </p> <p> <tt>@layer(...)</tt> だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、 一部の引数は <tt>@macro</tt>、一部の引数は <tt>@value</tt> レイヤで受け取る関数を書くなど、 さらに色々面白いことが可能です。 @@ -698,60 +706,108 @@ </p> </dd> <script>explorer.outline.writeEnabled = true;</script> <dt><span class="decl"> -<span class="currsymbol">微妙なところ</span> -<script>explorer.outline.addDecl('微妙なところ');</script> +<span class="currsymbol">微妙なところ1</span> +<script>explorer.outline.addDecl('微妙なところ1');</script> </span></dt> <script>explorer.outline.writeEnabled = false;</script> <dd><p> -ここまで、<tt>@macro</tt> が本当にただの1レイヤであるかのように説明してきましたが、 -実はちょっと幾つかのトリックが潜んでいます。 +ここまで、<tt>@macro</tt> が本当にただの1レイヤと説明してきましたが、 +実はちょっとトリックが潜んでいます。 </p> <pre> >> @macro twice(x) {x; x} in twice(<b>@value</b>(print("Hello"))) Hello Hello Hello </pre> <p> 先ほどの例に <tt>@value</tt> を増やしたものですが、これでもやはり、Hello -が2回 print されるようになります。 +が2回 print されるようになります。これは本来はおかしな話で、<tt>print("Hello")</tt> +は <tt>@value</tt> レイヤで実行されて値に落ちるはずなので、1回しか print されないはず。 +</p> +<p> +実は、Polemy の中では、<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> +レイヤという二つの異なるマクロ用レイヤが動いています。 +</p> +<ul> + <li><tt>(rawmacro)</tt> も <tt>@macro</tt> も、コードを動かすとその構文木を返す意味論。</li> + <li>ただし、<tt>(rawmacro)</tt> も <tt>@macro</tt> も、 + <tt>@macro</tt> レイヤに値のセットされた変数をみつけたときは、 + その変数という構文木を作るのではなく、変数の内容を展開。</li> + <li>また <tt>@macro</tt> は、 + レイヤ指定式を見ると実行レイヤを切り替て、構文木生成モードをやめてしまう。</li> + <li><tt>(rawmacro)</tt> は、 + レイヤ指定式を見ても実行レイヤを切り替えないで構文木にする。</li> +</ul> +<p> +ユーザーから直接 <tt>(rawmacro)</tt> は呼べませんが、 +「関数やトップレベル実行開始前のマクロ処理は <tt>(rawmacro)</tt> で実行開始」 +「<tt>@macro</tt> レイヤ以外で呼び出した関数の仮引数に <tt>@macro</tt> がついていたら、 +その実引数は <tt>(rawmacro)</tt> で実行」 +という2つのタイミングで <tt>(rawmacro)</tt> が動き出します。 +<tt>(rawmacro)</tt> が <tt>@macro</tt> レイヤから変数を見つけてマクロし始める時に、 +そこで <tt>@macro</tt> に動作が移ります。 +</p> +<p> +こうなっているのは、全部がレイヤ指定式に反応する <tt>@macro</tt> の動作だと、 +レイヤを使ったプログラムが全て <tt>@value</tt> 実行時ではなく、 +マクロ展開の時点で動き始めてしまって、おかしなことになるためです。 +色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。 +</p> +</dd> + +<script>explorer.outline.writeEnabled = true;</script> +<dt><span class="decl"> +<span class="currsymbol">微妙なところ2</span> +<script>explorer.outline.addDecl('微妙なところ2');</script> + +</span></dt> +<script>explorer.outline.writeEnabled = false;</script> + + <dd><p> +「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、 +何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。 +これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。 +</p> +</dd> + +<script>explorer.outline.writeEnabled = true;</script> +<dt><span class="decl"> +<span class="currsymbol">微妙なところ3</span> +<script>explorer.outline.addDecl('微妙なところ3');</script> + +</span></dt> +<script>explorer.outline.writeEnabled = false;</script> + + <dd><p> +これはエラーになります。 </p> <pre> -<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> レイヤという二つが協調して動作しています。 - (rawmacro) レイヤの話 - - [[limitations]] - - This @macro layer is a very primitive one, and not a perfect macro language. - Two major limitations are seen in the following "it" example. - - >> @macro LetItBe(x, y) { let it = x in y }; - - The variable name is not hygenic, and so without any effort, the syntax tree "y" - can access the outer variable "it". - - >> def foo() { LetItBe( 1+2+3, it*it ) } - >> foo() - 36 - - Of course, this is not just a limitation; it can sometimes allow us to write - many interesting macros. - - The other problem is that the macro expansion is only done at function startup. - So - - >> LetItBe( 1+2+3, it*it ) - ...\value.d(173): [<REPL>:24:1] variable LetItBe is not set in layer @value - - you cannot directly use the macro in the same scope as the definition. - You need to wrap it up in a function (like the foo() in the above example). + >> let _ = (@macro twice(x) {x;x} in twice(print("Hello"))) + polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\value.d(109): + [<REPL>:2:35] 'twice' is not set in @value layer </pre> +<p> +どういうことかというと、<tt>@macro</tt> で定義したマクロはいつから使えるようになるかという話で、 +この <tt>@macro twice(x) {x;x} in ...</tt> の部分は <tt>@value</tt> レイヤの式なので、 +まずこの式全体のマクロ展開が終わったあとにしか実行されないのです。<tt>twice</tt> +がマクロと見なされはじめるのは、<tt>@macro</tt> 実行が終わった後。 +なので、 +例えば <tt>twice(print("Hello"))</tt> の部分を無名関数にラップしてやれば、 +マクロ展開を遅らせられて、 ちゃんと実行ができます。 +</p> +<p> +これだと余りにも不便なので、関数のトップレベルの変数宣言式の列についてだけは、 +<tt>@macro</tt> と <tt>@value</tt> の評価を交互にインターリーブするようにしました。 +「関数やREPLのトップレベルの最初に宣言したマクロだけは、その関数内で即座に使える」わけです。 +これも Scheme の let-syntax などなどの動きを調べて勉強しないと…。 +</p> </dd> </dl> <script>explorer.outline.decSymbolLevel();</script> @@ -879,11 +935,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:52 2010 + on Sat Nov 27 01:54:49 2010 </td></tr> </table> </div> <script> Index: doc/layer.html ================================================================== --- doc/layer.html +++ doc/layer.html @@ -165,11 +165,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:46 2010 + on Sat Nov 27 01:17:58 2010 </td></tr> </table> </div> <script> Index: doc/lex.html ================================================================== --- doc/lex.html +++ doc/lex.html @@ -309,11 +309,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:47 2010 + on Sat Nov 27 01:17:59 2010 </td></tr> </table> </div> <script> Index: doc/main.html ================================================================== --- doc/main.html +++ doc/main.html @@ -64,11 +64,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:42 2010 + on Sat Nov 27 01:17:56 2010 </td></tr> </table> </div> <script> Index: doc/parse.html ================================================================== --- doc/parse.html +++ doc/parse.html @@ -58,11 +58,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:48 2010 + on Sat Nov 27 01:17:59 2010 </td></tr> </table> </div> <script> Index: doc/repl.html ================================================================== --- doc/repl.html +++ doc/repl.html @@ -158,11 +158,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:48 2010 + on Sat Nov 27 01:18:00 2010 </td></tr> </table> </div> <script> Index: doc/runtime.html ================================================================== --- doc/runtime.html +++ doc/runtime.html @@ -44,11 +44,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:49 2010 + on Sat Nov 27 01:18:00 2010 </td></tr> </table> </div> <script> Index: doc/test.html ================================================================== --- doc/test.html +++ doc/test.html @@ -145,11 +145,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:51 2010 + on Sat Nov 27 01:18:02 2010 </td></tr> </table> </div> <script> Index: doc/tricks.html ================================================================== --- doc/tricks.html +++ doc/tricks.html @@ -248,11 +248,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:52 2010 + on Sat Nov 27 01:18:02 2010 </td></tr> </table> </div> <script> Index: doc/value.html ================================================================== --- doc/value.html +++ doc/value.html @@ -65,12 +65,12 @@ <dd><br><br> </dd> <script>explorer.outline.writeEnabled = true;</script> <dt><span class="decl">class -<span class="currsymbol">UndefinedValue</span> -<script>explorer.outline.addDecl('UndefinedValue');</script> +<span class="currsymbol">BottomValue</span> +<script>explorer.outline.addDecl('BottomValue');</script> : polemy.value.Value; </span></dt> <script>explorer.outline.writeEnabled = false;</script> @@ -261,11 +261,11 @@ </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:50 2010 + on Sat Nov 27 01:18:01 2010 </td></tr> </table> </div> <script> Index: doc/valueconv.html ================================================================== --- doc/valueconv.html +++ doc/valueconv.html @@ -77,20 +77,34 @@ <script>explorer.outline.writeEnabled = false;</script> <dd>Convert AST to Table so that it can be used in Polemy<br><br> +</dd> + +<script>explorer.outline.writeEnabled = true;</script> +<dt><span class="decl">Value +<span class="currsymbol">ast2table</span> +<script>explorer.outline.addDecl('ast2table');</script> + +(T)(T <span class="funcparam">e</span>); +</span></dt> +<script>explorer.outline.writeEnabled = false;</script> + + +<dd>No hook version<br><br> + </dd> </dl> <script>explorer.outline.decSymbolLevel();</script> </td></tr> <tr><td id="docfooter"> Page was generated with <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px"> - on Fri Nov 26 16:41:51 2010 + on Sat Nov 27 01:18:01 2010 </td></tr> </table> </div> <script> Index: index.dd ================================================================== --- index.dd +++ index.dd @@ -1,14 +1,23 @@ Ddoc $(DDOC_AUTHORS k.inaba) $(DDOC_LICENSE NYSL 0.9982 (http://www.kmonos.net/nysl/)) <p> -このファイルは、言語仕様などの簡単な説明です。 +左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 </p> <p> -あとついでに、左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。 +このファイルは、言語仕様などの、やや辞書的な説明です。<br /> +もっとざっくりとした、言語デザインの方向性の魂的なものについては、 +「メタプログラミングの会」の発表スライドをご覧下さい。 +</p> +<p> +あと、 やたらとマクロの章が長くなっていますが、 この部分は、 +レイヤ機能を入れたら自動的にすごく自然にマクロが入るなーと思って、 +おまけで実装してみた程度のものです。 +あんまり重要ではないので、適当にスルーして下さいませ。 +単に、適当に入れたら適当で微妙な部分が多く残ってしまったので注意書きが増えているだけで…。 </p> $(DDOC_MEMBERS $(SECTION Syntax, $(SECBODY @@ -173,11 +182,11 @@ let-in を縦にチェインしたときだけ、同名変数を破壊的に上書きします (再帰関数の定義が"うまく"いっているのはこの上書きのためです)。 なんでこんなことになっているかというと、 後で説明する「レイヤ」を使ったときに <tt>let foo = ... in @lay foo = ... in ...</tt> -で他レイヤに重ね書きするためであります。 +で他レイヤに重ね書きするため、のつもりです。詳しくは後で。 </p> )) ) )) @@ -205,11 +214,11 @@ <ul> <li>整数: <tt>0</tt>, <tt>123</tt>, <tt>456666666666666666666666666666666666666789</tt>, ...</li> <li>文字列: <tt>"hello, world!"</tt>, ...</li> <li>関数: <tt>fun(x){x+1}</tt></li> <li>テーブル: <tt>{car: 1, cdr: {car: 2, cdr: {}}}</tt></li> - <li>未定義値: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li> + <li>ボトム: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li> </ul> <p> 関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。 テーブルはいわゆるプロトタイプチェーンを持っていて、 自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、 @@ -446,12 +455,11 @@ 動きとしてはこうです。 </p> <ol> <li>関数呼び出し時(とトップレベル環境の実行開始時)に、 まず、<code>@macro</code> レイヤでコードを実行。</li> -<li>返ってきた構文木を、<code>@value</code> レイヤ、 - またはその関数を呼び出したときのレイヤで実行。</li> +<li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li> </ol> <p> <code>@macro</code> レイヤも所詮ただのレイヤですので、 上で説明した方法で <code>@macro</code> レイヤに関数などを登録しておくことで、 構文木の生成をいじることが可能です。まさにマクロ。 @@ -528,12 +536,12 @@ </pre> <p> <tt>reverseArgs</tt> は、関数呼び出しの構文木の、引数の順番を逆転する関数です。 <tt>@macro(e)</tt> によってマクロレイヤにセットされている構文木引数を取り出し、 それを <tt>@value</tt> レイヤによる普通の計算プログラムで操作しています。 -要は、<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 -<tt>@value(...)</tt> は「逆クオート (unquote)」に近い働きをします。 +<tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、 +<tt>@value(...)</tt> は「逆クオート (unquote)」にちょっと近いかもしれません。 </p> <p> <tt>@layer(...)</tt> だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、 一部の引数は <tt>@macro</tt>、一部の引数は <tt>@value</tt> レイヤで受け取る関数を書くなど、 さらに色々面白いことが可能です。 @@ -558,55 +566,87 @@ クラス名が <tt>is</tt> フィールドに、メンバ変数はそのままの名前で入ります。 配列メンバは cons リストになって入ってきます。 自分で構文木を作る時は、<tt>pos</tt> フィールドだけは省略しても構いません。 </p> )) -$(SECTION 微妙なところ, $(SECBODY +$(SECTION 微妙なところ1, $(SECBODY <p> -ここまで、<tt>@macro</tt> が本当にただの1レイヤであるかのように説明してきましたが、 -実はちょっと幾つかのトリックが潜んでいます。 +ここまで、<tt>@macro</tt> が本当にただの1レイヤと説明してきましたが、 +実はちょっとトリックが潜んでいます。 </p> <pre> >> @macro twice(x) {x; x} in twice($(B @value)(print("Hello"))) Hello Hello Hello </pre> <p> 先ほどの例に <tt>@value</tt> を増やしたものですが、これでもやはり、Hello -が2回 print されるようになります。 +が2回 print されるようになります。これは本来はおかしな話で、<tt>print("Hello")</tt> +は <tt>@value</tt> レイヤで実行されて値に落ちるはずなので、1回しか print されないはず。 +</p> +<p> +実は、Polemy の中では、<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> +レイヤという二つの異なるマクロ用レイヤが動いています。 +</p> +<ul> + <li><tt>(rawmacro)</tt> も <tt>@macro</tt> も、コードを動かすとその構文木を返す意味論。</li> + <li>ただし、<tt>(rawmacro)</tt> も <tt>@macro</tt> も、 + <tt>@macro</tt> レイヤに値のセットされた変数をみつけたときは、 + その変数という構文木を作るのではなく、変数の内容を展開。</li> + <li>また <tt>@macro</tt> は、 + レイヤ指定式を見ると実行レイヤを切り替て、構文木生成モードをやめてしまう。</li> + <li><tt>(rawmacro)</tt> は、 + レイヤ指定式を見ても実行レイヤを切り替えないで構文木にする。</li> +</ul> +<p> +ユーザーから直接 <tt>(rawmacro)</tt> は呼べませんが、 +「関数やトップレベル実行開始前のマクロ処理は <tt>(rawmacro)</tt> で実行開始」 +「<tt>@macro</tt> レイヤ以外で呼び出した関数の仮引数に <tt>@macro</tt> がついていたら、 +その実引数は <tt>(rawmacro)</tt> で実行」 +という2つのタイミングで <tt>(rawmacro)</tt> が動き出します。 +<tt>(rawmacro)</tt> が <tt>@macro</tt> レイヤから変数を見つけてマクロし始める時に、 +そこで <tt>@macro</tt> に動作が移ります。 +</p> +<p> +こうなっているのは、全部がレイヤ指定式に反応する <tt>@macro</tt> の動作だと、 +レイヤを使ったプログラムが全て <tt>@value</tt> 実行時ではなく、 +マクロ展開の時点で動き始めてしまって、おかしなことになるためです。 +色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。 +</p> +)) +$(SECTION 微妙なところ2, $(SECBODY +<p> +「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、 +何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。 +これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。 +</p> +)) +$(SECTION 微妙なところ3, $(SECBODY +<p> +これはエラーになります。 </p> <pre> -<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt> レイヤという二つが協調して動作しています。 - (rawmacro) レイヤの話 - - [[limitations]] - - This @macro layer is a very primitive one, and not a perfect macro language. - Two major limitations are seen in the following "it" example. - - >> @macro LetItBe(x, y) { let it = x in y }; - - The variable name is not hygenic, and so without any effort, the syntax tree "y" - can access the outer variable "it". - - >> def foo() { LetItBe( 1+2+3, it*it ) } - >> foo() - 36 - - Of course, this is not just a limitation; it can sometimes allow us to write - many interesting macros. - - The other problem is that the macro expansion is only done at function startup. - So - - >> LetItBe( 1+2+3, it*it ) - ...\value.d(173): [<REPL>:24:1] variable LetItBe is not set in layer @value - - you cannot directly use the macro in the same scope as the definition. - You need to wrap it up in a function (like the foo() in the above example). + >> let _ = (@macro twice(x) {x;x} in twice(print("Hello"))) + polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\value.d(109): + [<REPL>:2:35] 'twice' is not set in @value layer </pre> +<p> +どういうことかというと、<tt>@macro</tt> で定義したマクロはいつから使えるようになるかという話で、 +この <tt>@macro twice(x) {x;x} in ...</tt> の部分は <tt>@value</tt> レイヤの式なので、 +まずこの式全体のマクロ展開が終わったあとにしか実行されないのです。<tt>twice</tt> +がマクロと見なされはじめるのは、<tt>@macro</tt> 実行が終わった後。 +なので、 +例えば <tt>twice(print("Hello"))</tt> の部分を無名関数にラップしてやれば、 +マクロ展開を遅らせられて、 ちゃんと実行ができます。 +</p> +<p> +これだと余りにも不便なので、関数のトップレベルの変数宣言式の列についてだけは、 +<tt>@macro</tt> と <tt>@value</tt> の評価を交互にインターリーブするようにしました。 +「関数やREPLのトップレベルの最初に宣言したマクロだけは、その関数内で即座に使える」わけです。 +これも Scheme の let-syntax などなどの動きを調べて勉強しないと…。 +</p> )) ) )) Index: polemy/eval.d ================================================================== --- polemy/eval.d +++ polemy/eval.d @@ -17,71 +17,76 @@ /// Objects for maitaining global environment and evaluation of expression on it class Evaluator { public: - /// Initialize evaluator with empty context - this() { theContext = new Table; } - /// Evaluate the AST Value evalAST(AST e) - { - return macroAndEval(e, ValueLayer, theContext, OverwriteCtx); - } + { return getLayerEval(ValueLayer).macroAndEval(e, theContext, LayerEval.OverwriteCtx); } /// Evaluate the string Value evalString(S,T...)(S str, T fn_ln_cn) - { - return evalAST(parseString(str,fn_ln_cn)); - } + { return evalAST(parseString(str,fn_ln_cn)); } /// Evaluate the file Value evalFile(S,T...)(S filename, T ln_cn) - { - return evalAST(parseFile(filename,ln_cn)); - } + { return evalAST(parseFile(filename,ln_cn)); } /// Get the global context Table globalContext() + { return theContext; } + + /// Initialize evaluator with empty context + this() { - return theContext; + theContext = new Table; + theLayers[ValueLayer] = new ValueLayerEval; + theLayers[MacroLayer] = new MacroLayerEval; + theLayers[RawMacroLayer] = new RawMacroLayerEval; } private: - Table theContext; + Table theContext; + LayerEval[Layer] theLayers; - enum : bool { CascadeCtx=false, OverwriteCtx=true }; - - LayerEval getLayerEvaluator(Layer lay) + /// Get the layer evaluator from layer ID + LayerEval getLayerEval(Layer lay) { - if( lay == ValueLayer ) - return new ValueLayerEval; - if( lay == RawMacroLayer ) - return new RawMacroLayerEval; - if( lay == MacroLayer ) - return new MacroLayerEval; - return new UserDefinedLayerEval(lay); + if(auto p = lay in theLayers) + return *p; + return theLayers[lay] = new UserDefinedLayerEval(lay); } + /// Interface of layers abstract class LayerEval { + enum : bool { CascadeCtx=false, OverwriteCtx=true }; + /// Concrete layers should implement these - Layer currentLayer(); - Value eval_( Die e, Table ctx, bool ctxMod );/// - Value eval_( Str e, Table ctx, bool ctxMod );/// - Value eval_( Int e, Table ctx, bool ctxMod );/// - Value eval_( Var e, Table ctx, bool ctxMod );/// - Value eval_( Lay e, Table ctx, bool ctxMod );/// - Value eval_( Let e, Table ctx, bool ctxMod );/// - Value eval_( App e, Table ctx, bool ctxMod );/// - Value eval_( Fun e, Table ctx, bool ctxMod );/// + protected Layer layerID(); + protected Value eval_( Die e, Table ctx, bool ctxMod );/// + protected Value eval_( Str e, Table ctx, bool ctxMod );/// + protected Value eval_( Int e, Table ctx, bool ctxMod );/// + protected Value eval_( Var e, Table ctx, bool ctxMod );/// + protected Value eval_( Lay e, Table ctx, bool ctxMod );/// + protected Value eval_( Let e, Table ctx, bool ctxMod );/// + protected Value eval_( App e, Table ctx, bool ctxMod );/// + protected Value eval_( Fun e, Table ctx, bool ctxMod );/// + + /// Should override this also, if the layer may return null for optimization + Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx ) + { return eval(e,ctx,ctxMod); } + + /// Do macro expansion first, and then eval. Should override this also if it is NOT macro layer. + Value macroAndEval( AST e, Table ctx, bool ctxMod = CascadeCtx ) + { return evalToNonNull(e,ctx,ctxMod); } /// dynamic-overload-resolution - Value eval( AST e, Table ctx, bool ctxMod ) + Value eval( AST e, Table ctx, bool ctxMod = CascadeCtx ) { - enum funName = "eval_"; // modify here to customize - alias TypeTuple!(e,ctx,ctxMod) params; // modify here to customize + enum funName = "eval_"; // modify here for customization + alias TypeTuple!(e,ctx,ctxMod) params; // modify here for customization alias typeof(__traits(getOverloads, this, funName)) ovTypes; alias staticMap!(firstParam, ovTypes) fstTypes; alias DerivedToFront!(fstTypes) fstTypes_sorted; foreach(i, T; fstTypes_sorted) @@ -90,77 +95,71 @@ // modify here to customize the default behavior assert(false, text("eval() for ",typeid(e)," [",e.pos,"] is not defined")); } - /// - Value invokeFunction(Value _f, AST[] args, Table ctx, LexPosition pos, string callstackmsg) + /// Function calling convention. + /// Run all arugment AST in the appropriate layer and invoke the function. + Value invokeFunction(Value f_, AST[] args, Table ctx, LexPosition pos, string callstackmsg) { - if(auto f = cast(FunValue)_f) - { - Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); - foreach(i,p; f.params()) - if( p.layers.empty ) { - Value v = this.eval(args[i], ctx, CascadeCtx); - if(v is null) v = ast2table(args[i]); - newCtx.set(p.name, currentLayer(), v); + FunValue f = cast(FunValue)f_; + if( f is null ) + throw genex!RuntimeException(pos, text("tried to call non-function: ",f)); + if( f.params().length != args.length ) + throw genex!RuntimeException(pos, + sprintf!("%d arguments required but %d passed")(f.params().length, args.length)); + + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); + foreach(i,p; f.params()) + if( p.layers.empty ) + newCtx.set(p.name, layerID(), this.evalToNonNull(args[i], ctx)); + else + foreach(argLay; p.layers) + { + Layer ll = argLay; + if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) ) + ll = RawMacroLayer; // explicit @macro invokes (rawmacro) + newCtx.set(p.name, argLay, getLayerEval(ll).evalToNonNull(args[i], ctx)); } - else - foreach(argLay; p.layers) { - Layer ll = argLay; - if( isMacroLayer(argLay) && typeid(this)!=typeid(MacroLayerEval) ) - ll = RawMacroLayer; // explicit @macro invokes (rawmacro) - Value v = getLayerEvaluator(ll).eval(args[i], ctx, CascadeCtx); - if(v is null) v = ast2table(args[i]); - newCtx.set(p.name, argLay, v); - } - scope _ = new PushCallStack(pos, callstackmsg); - return f.invoke(currentLayer(), newCtx, pos); - } - throw genex!RuntimeException(pos, text("tried to call non-function: ",_f)); + scope _ = new PushCallStack(pos, callstackmsg); + return f.invoke(layerID(), newCtx, pos); } - /// + /// Lift the value v to the current layer Value lift(Value v, Table ctx, LexPosition pos) { - Layer lay = currentLayer(); - - // functions are automatically lifterd + // functions are automatically lifted by itself if( cast(FunValue) v ) return v; + Layer lay = layerID(); + + // load the lift function if( !ctx.has(lay, LiftLayer) ) throw genex!RuntimeException(pos, "lift function for "~lay~" is not registered" ); - // similar to invokeFunction, but with only one argument bound to ValueLayer - auto _f = ctx.get(lay, LiftLayer, pos); - if(auto f = cast(FunValue)_f) - { - Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); - auto ps = f.params(); - if( ps.length != 1 ) - throw genex!RuntimeException(pos, - text("lift function for", lay, " must take exactly one argument of ", ValueLayer)); - if( ps[0].layers.length==0 || ps[0].layers.length==1 && ps[0].layers[0]==ValueLayer ) - { - newCtx.set(ps[0].name, ValueLayer, v); - scope _ = new PushCallStack(pos, lay); - return f.invoke(ValueLayer, newCtx, pos); - } - else - throw genex!RuntimeException(pos, - text("lift function for", lay, " must take exactly one argument of ", ValueLayer)); - } - throw genex!RuntimeException(pos, - text("non-function ", _f, " is registered as the lift function for ", lay)); + Value f_ = ctx.get(lay, LiftLayer, pos); + FunValue f = cast(FunValue) f_; + if( f is null ) + throw genex!RuntimeException(pos, + text("non-function ", f_, " is registered as the lift function for ", lay)); + if( f.params().length != 1 || f.params()[0].layers.length>=1 ) + throw genex!RuntimeException(pos, + text("lift function must take exactly 1 neutral parameter:",lay)); + + // invokeFunction + Table newCtx = new Table(f.definitionContext(), Table.Kind.NotPropagateSet); + newCtx.set(f.params()[0].name, ValueLayer, v); + scope _ = new PushCallStack(pos, "lift to "~lay); + return f.invoke(ValueLayer, newCtx, pos); } } /// Evaluator for standard @value semantics class ValueLayerEval : LayerEval { - override Layer currentLayer() + override Layer layerID() { return ValueLayer; } override Value eval_( Die e, Table ctx, bool ctxMod ) { @@ -174,28 +173,23 @@ { return new IntValue(e.data); } override Value eval_( Var e, Table ctx, bool ctxMod ) { - return ctx.get(e.name, currentLayer(), e.pos); + return ctx.get(e.name, layerID(), e.pos); } override Value eval_( Lay e, Table ctx, bool ctxMod ) { - auto le = getLayerEvaluator(e.layer); - auto v = le.eval(e.expr,ctx,CascadeCtx); - if( (v is null) && (cast(MacroLayerEval)le !is null) ) - return ast2table(e.expr); - else - return v; + return getLayerEval(e.layer).evalToNonNull(e.expr, ctx); } override Value eval_( Let e, Table ctx, bool ctxMod ) { - Table newCtx = ctxMod ? ctx : new Table(ctx, Table.Kind.NotPropagateSet); - Value ri = this.eval(e.init, newCtx, CascadeCtx); - if(e.name!="_") - newCtx.set(e.name, e.layer.empty ? currentLayer() : e.layer, ri); - return this.eval(e.expr, newCtx, OverwriteCtx); + if( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + Value ri = this.eval(e.init, ctx); + ctx.set(e.name, e.layer.empty ? layerID(): e.layer, ri); + return this.eval(e.expr, ctx, OverwriteCtx); } override Value eval_( App e, Table ctx, bool ctxMod ) { Value f = this.eval( e.fun, ctx, CascadeCtx ); return this.invokeFunction(f, e.args, ctx, e.pos, getNameIfPossible(e.fun)); @@ -202,49 +196,70 @@ } override Value eval_( Fun e, Table ctx, bool ctxMod ) { return createNewFunction(e, ctx); } + override Value macroAndEval( AST e, Table ctx, bool ctxMod ) + { + // incremental execution of let-expressions + if(auto le = cast(Let)e) + { + AST ai = runMacro(le.init, ctx); + + if( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + + Value vi = this.eval(ai, ctx); + ctx.set(le.name, le.layer.empty ? layerID() : le.layer, vi); + return this.macroAndEval(le.expr, ctx, OverwriteCtx); + } + else + return this.eval(runMacro(e,ctx,ctxMod), ctx, ctxMod); + } } /// Evaluator for user-defined layer class UserDefinedLayerEval : ValueLayerEval { - Layer layerID; + Layer theID; mixin SimpleConstructor; - override Layer currentLayer() + override Layer layerID() { - return layerID; + return theID; } override Value eval_( Die e, Table ctx, bool ctxMod ) { - return new UndefinedValue; + return new BottomValue; } override Value eval_( Str e, Table ctx, bool ctxMod ) { - return this.lift(new StrValue(e.data), ctx, e.pos); + return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos); } override Value eval_( Int e, Table ctx, bool ctxMod ) { - return this.lift(new IntValue(e.data), ctx, e.pos); + return this.lift(super.eval_(e,ctx,ctxMod), ctx, e.pos); } override Value eval_( Var e, Table ctx, bool ctxMod ) { - if( ctx.has(e.name, currentLayer()) ) - return ctx.get(e.name, currentLayer()); + if( ctx.has(e.name, layerID()) ) + return ctx.get(e.name, layerID()); return this.lift(ctx.get(e.name, ValueLayer, e.pos), ctx, e.pos); } } - // Convention!! - // returns null if never used macro-like feature + // Macro layer. For optimization, if AST didn't change it returns null class MacroLayerEval : LayerEval { - override Layer currentLayer() + override Layer layerID() { return MacroLayer; + } + override Value evalToNonNull( AST e, Table ctx, bool ctxMod = CascadeCtx ) + { + Value v = this.eval(e, ctx, ctxMod); + return v is null ? ast2table(e) : v; } override Value eval_( Die e, Table ctx, bool ctxMod ) { return null; } @@ -256,45 +271,48 @@ { return null; } override Value eval_( Var e, Table ctx, bool ctxMod ) { - if( ctx.has(e.name, currentLayer()) ) - return ctx.get(e.name, currentLayer(), e.pos); - else - return null; + if( ctx.has(e.name, layerID()) ) + return ctx.get(e.name, layerID(), e.pos); + return null; } override Value eval_( Lay e, Table ctx, bool ctxMod ) { - auto le = getLayerEvaluator(e.layer); - return le.eval(e.expr,ctx,CascadeCtx); + return getLayerEval(e.layer).eval(e.expr,ctx); } override Value eval_( Let e, Table ctx, bool ctxMod ) { - Table newCtx = ctxMod ? ctx : new Table(ctx, Table.Kind.NotPropagateSet); - Value ai = this.eval(e.init, newCtx, CascadeCtx); - newCtx.set(e.name, NoopLayer, null); - Value ae = this.eval(e.expr, newCtx, OverwriteCtx); - if( ai is null && ae is null ) - return null; + if( !ctxMod ) + ctx = new Table(ctx, Table.Kind.NotPropagateSet); + + Value ai = this.eval(e.init, ctx); + ctx.set(e.name, NoopLayer, null); + Value ae = this.eval(e.expr, ctx, OverwriteCtx); + + if( ai is null && ae is null ) return null; if( ai is null ) ai = ast2table(e.init); if( ae is null ) ae = ast2table(e.expr); + return ast2table(e, delegate Value (AST _){ if(_ is e.init) { return ai; } if(_ is e.expr) { return ae; } assert(false); }); } override Value eval_( App e, Table ctx, bool ctxMod ) { - Value f = this.eval( e.fun, ctx, CascadeCtx ); + Value f = this.eval( e.fun, ctx ); if(auto ff = cast(FunValue)f) return this.invokeFunction(ff, e.args, ctx, e.pos, getNameIfPossible(e.fun)); - else { + else + { bool allNull = (f is null); Value[] vas; - foreach(a; e.args) { + foreach(a; e.args) + { Value va = this.eval(a, ctx, CascadeCtx); if(va !is null) allNull = false; vas ~= va; } if( allNull ) @@ -306,64 +324,47 @@ }); } } override Value eval_( Fun e, Table ctx, bool ctxMod ) { - Table newCtx = new Table(ctx, Table.Kind.NotPropagateSet); + ctx = new Table(ctx, Table.Kind.NotPropagateSet); foreach(p; e.params) - newCtx.set(p.name, NoopLayer, null); - Value af = this.eval(e.funbody, newCtx, CascadeCtx); + ctx.set(p.name, NoopLayer, null); + Value af = this.eval(e.funbody, ctx); if( af is null ) return null; return ast2table(e, (AST _){if(_ is e.funbody)return af; assert(false);}); } } + /// (rawmacro) layer. almost same as @macro, but the Layer expression becomes AST, too. class RawMacroLayerEval : MacroLayerEval { override Value eval_( Lay e, Table ctx, bool ctxMod ) { - Value ae = this.eval(e.expr, ctx, CascadeCtx); + Value ae = this.eval(e.expr, ctx); return ae is null ? null : ast2table(e, delegate Value (AST _){if(_ is e.expr)return ae; assert(false);}); } } -private: - Value macroAndEval( AST e_, Layer lay, Table ctx, bool ctxMod ) - { - assert( !isASTLayer(lay) ); - if(auto e = cast(Let)e_) - { - Value vai = getLayerEvaluator(RawMacroLayer).eval(e.init, ctx, CascadeCtx); - AST ai = (vai is null ? e.init : polemy2d!(AST)(vai, e.pos)); +private: // short utils - if( !ctxMod ) - ctx = new Table(ctx, Table.Kind.NotPropagateSet); - - Value vi = getLayerEvaluator(lay).eval(ai, ctx, CascadeCtx); - string theLayer = e.layer.empty ? lay : e.layer; - ctx.set(e.name, theLayer, vi); - - return macroAndEval( e.expr, lay, ctx, OverwriteCtx ); - } - else - { - Value va = getLayerEvaluator(RawMacroLayer).eval(e_, ctx, ctxMod); - AST a = (va is null ? e_ : polemy2d!(AST)(va, e_.pos)); - return getLayerEvaluator(lay).eval(a, ctx, ctxMod); - } - } - -private: string getNameIfPossible(AST e) { if(auto v = cast(Var)e) return v.name; return ""; } + AST runMacro(AST e, Table ctx, bool ctxMod=LayerEval.CascadeCtx) + { + Value v = getLayerEval(RawMacroLayer).eval(e, ctx, ctxMod); + return (v is null ? e : polemy2d!(AST)(v, e.pos)); + } + +private: Value createNewFunction(Fun e, Table ctx) { class UserDefinedFunValue : FunValue { Fun ast; @@ -387,67 +388,41 @@ } override hash_t toHash() { return (cast(hash_t)cast(void*)ast) + (cast(hash_t)cast(void*)defCtx); } - AST macroCache; AST[void*] mandeCache; static class MemokeyType { - void* a; Layer b; Tuple!(string,Layer,Value)[] c; - hash_t toHash() { - hash_t h = structuralHash(a) + structuralHash(b); - foreach(e; c) - h += structuralHash(e[0])+structuralHash(e[1])+structuralHash(e[2]); - return h; - } - mixin SimpleToString; - mixin SimpleConstructor; - mixin SimpleCompareWithoutToHash; + void* a; Layer b; Table c; + mixin SimpleClass; } static Tuple!(Value,int)[MemokeyType] memo; override Value invoke(Layer lay, Table ctx, LexPosition pos) { - if( isASTLayer(lay) ) { - Value v = getLayerEvaluator(lay).eval(ast.funbody, ctx, CascadeCtx); - if( v is null ) v = ast2table(ast.funbody); - return v; - } - return macroAndEval(ast.funbody, lay, ctx, CascadeCtx); -/* - auto nonMemoizedRun = (){ - if( macroCache is null ) - { - auto va = macroAndEval(e.funbody, lay, ctx, CascadeCtx, mandeCache); - macroCache = va[1]; - return va[0]; - } - else - return eval(macroCache, lay, ctx); - }; + auto evlay = getLayerEval(lay); + auto nonMemoizedRun = (){ return evlay.macroAndEval(ast.funbody, ctx); }; if( !isUserDefinedLayer(lay) ) return nonMemoizedRun(); - MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx.direct_entries()); - + // automatic memoized co-recursive execution + MemokeyType memokey = new MemokeyType(cast(void*)ast, lay, ctx); if(auto p = memokey in memo) { (*p)[1] ++; return (*p)[0]; } else - memo[memokey] = tuple(lift(new UndefinedValue, lay, ctx, pos), 0); + memo[memokey] = tuple(evlay.lift(new BottomValue, ctx, pos), 0); Value r = nonMemoizedRun(); int touched = memo[memokey][1]; memo[memokey] = tuple(r, 12345678); - //if(touched) {DBG("rerun :: ",r);r = nonMemoizedRun();} // twice!! return r; -*/ } } return new UserDefinedFunValue(e,ctx); } @@ -501,11 +476,13 @@ } theContext.set(name, defLay, new NativeFunValue(dg)); } } -version(unittest) import polemy.runtime; +version(unittest) + import polemy.runtime; + unittest { auto e = new Evaluator; enrollRuntimeLibrary(e); auto r = assert_nothrow( e.evalString(`var x = 21; x + x*x;`) ); @@ -512,10 +489,11 @@ assert_eq( r, new IntValue(BigInt(21+21*21)) ); assert_eq( e.globalContext.get("x",ValueLayer), new IntValue(BigInt(21)) ); assert_nothrow( e.globalContext.get("x",ValueLayer) ); assert_throw!RuntimeException( e.globalContext.get("y",ValueLayer) ); } + unittest { auto e = new Evaluator; enrollRuntimeLibrary(e); auto r = assert_nothrow( e.evalString(`var x = 21; var x = x + x*x;`) ); @@ -595,7 +573,7 @@ unittest { auto e = new Evaluator; enrollRuntimeLibrary(e); assert_throw!RuntimeException( e.evalString(`...`) ); - assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new UndefinedValue ); + assert_eq( e.evalString(`@@foo(x){x}; @foo(...)`), new BottomValue ); } Index: polemy/runtime.d ================================================================== --- polemy/runtime.d +++ polemy/runtime.d @@ -59,11 +59,11 @@ e.addPrimitive("_isstr", ValueLayer, (Value v){return new IntValue(cast(StrValue)v !is null);} ); e.addPrimitive("_isfun", ValueLayer, (Value v){return new IntValue(cast(FunValue)v !is null);} ); e.addPrimitive("_isundefined", ValueLayer, - (Value v){return new IntValue(cast(UndefinedValue)v !is null);} ); + (Value v){return new IntValue(cast(BottomValue)v !is null);} ); e.addPrimitive("_istable", ValueLayer, (Value v){return new IntValue(cast(Table)v !is null);} ); // table e.addPrimitive(".", ValueLayer, (Table t, StrValue s){ if( t.has(s.data, ValueLayer) ) Index: polemy/value.d ================================================================== --- polemy/value.d +++ polemy/value.d @@ -50,14 +50,14 @@ } mixin SimpleToHash; } /// -class UndefinedValue : Value +class BottomValue : Value { mixin SimpleConstructor; - override string toString() const { return "<undefined>"; } + override string toString() const { return "_|_"; } override int opCmp(Object rhs) { if(auto r = cast(StrValue)rhs) return 0; if(auto r = cast(Value)rhs) return typeid(this).opCmp(typeid(r)); throw genex!RuntimeException("comparison with value and somithing other"); } Index: sample/fib.pmy ================================================================== --- sample/fib.pmy +++ sample/fib.pmy @@ -1,5 +1,6 @@ +# Standard Fibonacci function def fib(x) { if x<2 then 1 else fib(x-1) + fib(x-2) }; @@ -9,6 +10,24 @@ }; var compose = fun(f,g){ fun(x){f(g(x))} }; var "<<" = compose; -upto(16, print<<fib); +upto(18, print<<fib); + + +###################################### +# Omake. Feel the automatic memoization! + +# Set up a mirror layer (do-the-same-thing layer) of @value +@@m(x){x}; +@m "+" (x,y) { @value(@m(x)+@m(y)) }; +@m "-" (x,y) { @value(@m(x)-@m(y)) }; +@m "<" (x,y) { @value(@m(x)<@m(y)) }; +@m "if" (c,t,e) { @value(if @m(c) then @m(t()) else @m(e())) }; + +# Helper function to call fib in layer @m +def fibm(x @value) { @m(fib(@value(x))) }; + +# User defined layers are automatically memoized. +# So this is much faster. +upto(18, print<<fibm); Index: sample/helloworld.pmy ================================================================== --- sample/helloworld.pmy +++ sample/helloworld.pmy @@ -1,12 +1,11 @@ -# omake. how to use argv +print( "Hello, World" ); + +# Omake. How to use argv def print_list(xs) { case xs when {car: x, cdr: xs}: ( print(x); print_list(xs) ) }; print_list(argv); - -# here is the helloworld -print( "Hello, World" );