1 Ddoc
2 $(DDOC_AUTHORS k.inaba)
3 $(DDOC_LICENSE NYSL 0.9982 (http://www.kmonos.net/nysl/))
4
5 <p>
6 左のサイドバーの "Package" タブをクリックすると実装のソースのドキュメントが読めます。
7 </p>
8 <p>
9 このファイルは、言語仕様などの、やや辞書的な説明です。<br />
10 もっとざっくりとした、言語デザインの方向性の魂的なものについては、
11 「メタプログラミングの会」の発表スライドをご覧下さい。
12 </p>
13 <p>
14 あと、 やたらとマクロの章が長くなっていますが、 この部分は、
15 レイヤ機能を入れたら自動的にすごく自然にマクロが入るなーと思って、
16 おまけで実装してみた程度のものです。
17 あんまり重要ではないので、適当にスルーして下さいませ。
18 単に、適当に入れたら適当で微妙な部分が多く残ってしまったので注意書きが増えているだけで…。
19 </p>
20
21 $(DDOC_MEMBERS
22
23 $(SECTION Syntax, $(SECBODY
24 <p>
25 文法について。
26 字句解析がわりと適当なので、
27 変数宣言の変数名のところに、数字を変数名として使えて参照できない変数が作れたり、
28 予約語は予約語として解釈され得ないところでは普通に変数名として使えちゃったりして、
29 偶にとんでもない見かけのソースが構文解析通りますが、気にしないで適当に使って下さい。
30 </p>
31
32 $(DDOC_MEMBERS
33
34 $(SECTION 文字コード, $(SECBODY
35 <p>
36 UTF-8 のみ対応です。
37 </p>
38 ))
39
40 $(SECTION コメント, $(SECBODY
41 <p>
42 行コメントは <tt>#</tt> から改行までです。
43 </p>
44 <p>
45 ブロックコメントはありません。
46 </p>
47 ))
48
49 $(SECTION BNF, $(SECBODY
50 <pre>
51 ID ::= 適当に識別子っぽい文字列
52 LAYER ::= "@" ID
53
54 E ::=
55 $(D_COMMENT # 変数宣言)
56 | DECL "=" E (";"|"in") E
57 | DECL "(" PARAMS ")" "{" E "}" (";"|"in") E
58 | DECL "=" E
59 | DECL "(" PARAMS ")" "{" E "}"
60
61 where DECL ::= ("var"|"let"|"def"|LAYER) ID | "@" LAYER
62
63 $(D_COMMENT # リテラル)
64 | INTEGER $(D_COMMENT # 非負整数)
65 | STRING $(D_COMMENT # "" でくくった文字列。\" と \\ は使える)
66 | "{" ENTRYS "}" $(D_COMMENT # テーブル)
67 | "fun" "(" PARAMS ")" "{" E "}" $(D_COMMENT # 無名関数)
68 | "λ" "(" PARAMS ")" "{" E "}" $(D_COMMENT # 無名関数)
69
70 $(D_COMMENT # 関数呼び出し)
71 | E "(" ARGS")"
72
73 where ARGS ::= E "," ... "," E
74 PARAMS ::= (ID|LAYER)+ "," ... "," (ID|LAYER)+
75 ENTRYS ::= ID ":" E "," ... "," ID ":" E
76
77 $(D_COMMENT # 演算子など)
78 | "(" E ")" $(D_COMMENT # ただの括弧)
79 | "..." $(D_COMMENT # これを実行するとdie)
80 | E BINOP E $(D_COMMENT # 二項演算子いろいろ)
81 | E "." ID $(D_COMMENT # テーブルのフィールドアクセス)
82 | E ".?" ID $(D_COMMENT # テーブルにフィールドがあるか否か)
83 | E "{" ENTRYS "}" $(D_COMMENT # テーブル拡張)
84 | "if" E ("then"|":"|"then" ":") E
85 | "if" E ("then"|":"|"then" ":") E "else" ":"? E
86
87 $(D_COMMENT # パターンマッチ)
88 | "case" E ("when" PATTERN ":" E )*
89
90 where PATTERN ::= 式がだいたいなんでも書ける気がする
91
92 $(D_COMMENT # レイヤ指定実行)
93 | LAYER "(" E ")"
94 </pre>
95 ))
96
97 $(SECTION 糖衣構文, $(SECBODY
98 <p>
99 演算子というものはありません。内部的には全て関数呼び出し構文に書き換えられています。<tt>if</tt> もです。
100 <br/>
101 パターンマッチも全部 <tt>if</tt> と <tt>==</tt> と <tt>&&</tt> と
102 <tt>.</tt> と <tt>.?</tt> を使った関数呼び出し式に書き換えられていますが、
103 規則の詳細を説明するのが面倒なので適当に想像して下さい。
104 他の書き換えはこんな感じです。
105 </p>
106 <pre>
107 if E then E ⇒ if( E, fun(){E}, fun(){} )
108 if E then E else E ⇒ if( E, fun(){E}, fun(){E} )
109 E BINOP E ⇒ BINOP(E, E)
110 { ENTRIES } ⇒ {}{ ENTRIES }
111 {} ⇒ {}()
112 E {ID:E, ...} ⇒ .=(E, ID, E) { ... }
113 </pre>
114 <p>
115 変数宣言に色々ありますが、<tt>let</tt> と <tt>var</tt> と <tt>def</tt> は同じ扱いで、
116 <tt>in</tt> と <tt>;</tt> は同じ扱いです。つまり
117 </p>
118 <pre>
119 let x = E in E
120 var x = E in E
121 def x = E in E
122 let x = E ; E
123 var x = E ; E
124 def x = E ; E
125 </pre>
126 <p>
127 以上のどれも同じ意味なので、なんとなく関数型っぽく書きたい気分の日は <tt>let in</tt> を、
128 手続き型っぽく書きたい気分の日は <tt>var ;</tt> を使うとよいと思います。
129 <tt>if then else</tt> も微妙にコロンがあったりなかったりバリエーションがありますが好みで使います。
130 </p>
131 <p>
132 関数を宣言するときは、<tt>fun</tt> や <tt>λ</tt> を省略できます。
133 以下の書き換えが行われます。
134 </p>
135 <pre>
136 def f( ARGS ) { E }; E ⇒ def f = fun(ARGS){E}; E
137 </pre>
138 <p>
139 他に、もっと手続き型っぽくための書き換え色々
140 </p>
141 <pre>
142 fun () { E; E; E } ⇒ fun () { let _ = E in let _ = E in E }
143 fun () { var x = 100 } ⇒ fun () { var x = 100; x }
144 fun () { var x = 100; } ⇒ fun () { var x = 100; x }
145 fun () { } ⇒ fun () { "(empty function body)" }
146 </pre>
147 <p>
148 中身が空の関数に何を返させるかは適当です。今はとりあえず適当に文字列返してます。
149 </p>
150 ))
151
152 $(SECTION 変数のスコープ規則, $(SECBODY
153 <p>
154 基本的には、let によって常識的な感じに変数のスコープがネストします。
155 </p>
156 <pre>
157 let x=21 in let x=x+x in x $(D_COMMENT # 42)
158 </pre>
159 <p>
160 一方で、"let rec" のような特別な構文はありませんが、
161 </p>
162 <pre>
163 let f = fun(x) { if x==0 then 1 else x*f(x-1) } in f(10) $(D_COMMENT # 3628800)
164 </pre>
165 <p>
166 再帰的な関数定義なども、おそらく意図されたとおりに動きます。
167 内部の詳細は、諸般の事情により、
168 マジカルで破壊的なスコープ規則になっているのですが、
169 同名の変数を激しく重ねて使ったりしなければ、
170 だいたい自然な動きをすると思います、たぶん、はい。
171 </p>
172 <p>
173 ひとつだけ不可思議な動きをするのは、以下のケースです。
174 </p>
175 <pre>
176 let x = 1 in
177 let f = fun() {x} in
178 let x = 2 in
179 f() $(D_COMMENT # 2!!)
180 </pre>
181 <p>
182 let-in を縦にチェインしたときだけ、同名変数を破壊的に上書きします
183 (再帰関数の定義が"うまく"いっているのはこの上書きのためです)。
184 なんでこんなことになっているかというと、
185 後で説明する「レイヤ」を使ったときに
186 <tt>let foo = ... in @lay foo = ... in ...</tt>
187 で他レイヤに重ね書きするため、のつもりです。詳しくは後で。
188 </p>
189 ))
190 )
191 ))
192
193
194
195
196 $(SECTION Basic Features, $(SECBODY
197 <p>
198 特に特徴的でもない部分を簡単にまとめ。
199 </p>
200 <ul>
201 <li>静的型システムはありません。</li>
202 <li>"ほぼ" 純粋関数型言語です。変数やテーブルのフィールドの破壊的な書き換えはできません。<br/>
203 ただし、組み込み関数(<tt>print</tt>)と、変数のスコープ規則のマジカルな片隅に副作用があります。</li>
204 </ul>
205 <p>
206 静的型システムがないのは意図的ですが、破壊的代入がないのは、単に実装がめんどかっただけなので、
207 今後何か増えるかもしれません。増えないかもしれません。
208 </p>
209 $(DDOC_MEMBERS
210 $(SECTION データ型, $(SECBODY
211 <p>
212 以下のデータ型があります。
213 </p>
214 <ul>
215 <li>整数: <tt>0</tt>, <tt>123</tt>, <tt>456666666666666666666666666666666666666789</tt>, ...</li>
216 <li>文字列: <tt>"hello, world!"</tt>, ...</li>
217 <li>関数: <tt>fun(x){x+1}</tt></li>
218 <li>テーブル: <tt>{car: 1, cdr: {car: 2, cdr: {}}}</tt></li>
219 <li>ボトム: (特殊なケースで作られます。「レイヤ」の説明参照のこと。)</li>
220 </ul>
221 <p>
222 関数はいわゆる「クロージャ」です。静的スコープで外側の環境にアクセスできます。
223 テーブルはいわゆるプロトタイプチェーンを持っていて、
224 自分にないフィールドの場合は親に問い合わせが行く感じになっていますが、
225 フィールドの書き換えがないので、これは特に意味ないかもしれない…。
226 </p>
227 <p>
228 また、リストを扱うために、いわゆる「cons リスト」を使います。
229 空リストを <tt>{}</tt>、1個以上要素があるものを <tt>{car: 先頭要素, cdr: 二番目以降のリスト}</tt>
230 という形で。この形でリストを扱わなければならないという決まりはありませんが、
231 この形は特別扱いされて <tt>print</tt> で綺麗に出力されたりします。
232 </p>
233 ))
234 $(SECTION パターンマッチ, $(SECBODY
235 <p>
236 適当に実装されたパターンマッチがあります。
237 リストの 2n 番目と 2n+1 番目を足して長さを半分にする関数:
238 </p>
239 <pre>
240 def adjSum(lst)
241 {
242 case lst
243 when {car:x, cdr:{car: y, cdr:z}}: {car: x+y, cdr: adjSum(z)}
244 when {car:x, cdr:{}}: lst
245 when {}: {}
246 }
247 </pre>
248 <p>
249 動かすときには、処理系がそれっぽい if-then-else に展開しています。
250 <tt>when</tt> を上から試していって、最初にマッチしたところを実行します。
251 どれにもマッチしないとエラーでプログラム終了します。
252 </p>
253 <pre>
254 PAT ::= "_" $(D_COMMENT # ワイルドカード)
255 | ID $(D_COMMENT # 変数パターン)
256 | "{" ID ":" PAT "," ... "," ID : PAT "}" $(D_COMMENT # テーブルパターン)
257 | E $(D_COMMENT # 値パターン)
258 </pre>
259 <p>
260 変数パターンは常にマッチして、値をその変数に束縛します。
261 ワイルドカードも常にマッチしますが、変数束縛しません。
262 値パターンは、任意の式が書けます。その式を評価した結果と <tt>==</tt> ならマッチします。
263 外で束縛された変数を値パターンとして配置、は直接はできないので
264 </p>
265 <pre>
266 var x = 123;
267 case foo
268 when {val: x+0}: ... $(D_COMMENT # これは {val:123} と同じ)
269 when {val: x}: ... $(D_COMMENT # これは任意の foo.?val なら常にマッチ)
270 </pre>
271 <p>
272 適当にちょっと複雑な式にしてやるとよいかも(裏技)。
273 </p>
274 <p>
275 テーブルパターンは、書かれたキーが全てあればマッチします。
276 <tt>{a: _}</tt> は、<tt>.a</tt> を持ってさえいればマッチするので、
277 <tt>{a: 123, b: 456}</tt> なんかにもマッチします。
278 なので、リストに対するパターンを書くときには、car/cdr の場合を先に書かないと
279 <tt>when {}</tt> を上に書くと全部マッチしちゃいます。注意。
280 </p>
281 ))
282 )
283 ))
284
285
286
287
288
289 $(SECTION Layers, $(SECBODY
290 <p>
291 この言語の唯一の特徴的な部分は、「レイヤ」機能です。
292 </p>
293 $(DDOC_MEMBERS
294 $(SECTION Layers, $(SECBODY
295 <pre>
296 [Layers :: Overview]
297
298 Polemy's runtime environment has many "layer"s.
299 Usual execution run in the @value layer.
300
301 >> 1 + 2
302 3
303 >> @value( 1 + 2 )
304 3
305
306 Here you can see that @LayerName( Expression ) executes the inner Expression in
307 the @LayerName layer. Other than @value, one other predefined layer exists: @macro.
308
309 >> @macro( 1+2 )
310 {pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
311 is@value:app,
312 args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
313 is@value:int,
314 data@value:1},
315 cdr@value:{
316 car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:<REPL>},
317 is@value:int,
318 data@value:2},
319 cdr@value:{}}},
320 fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:<REPL>},
321 is@value:var,
322 name@value:+}}
323
324 (Sorry, this pretty printing is not available on the actual interpreter...)
325 This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of
326 the program is its abstract syntax tree.
327
328 You can interleave layers.
329 The root node of the abstract syntax tree is function "app"lication.
330
331 >> @value(@macro( 1+2 ).is)
332 app
333
334
335
336 [Layers :: Defining a new layer]
337
338 To define a new layer, you should first tell how to "lift" existing values two the new layer.
339 Let us define the "@type" layer, where the meaning of programs is their static type.
340
341 >> @@type = fun(x) {
342 >> if( _isint(x) ) { "int" } else {
343 >> if( _isfun(x) ) { x } else { "unknown" } }
344 >> }
345 (Note: polemy REPL may warn some exception here but please ignore)
346
347 For simplicity, I here deal only with integers.
348 _isint is a primitive function of Polemy that checks the dynamic type of a value.
349 For function, leaving it untouched works well for almost all layers.
350
351 >> @type( 1 )
352 int
353 >> @type( 2 )
354 int
355 >> @type( "foo" )
356 unknown
357
358 Fine! Let's try to type 1+2.
359
360 >> @type( 1 + 2 )
361 ...\value.d(119): [<REPL>:6:8] only @value layer can call native function
362
363 Note that the behavior of this program is
364 - run 1+2 in the @type layer
365 and NOT
366 - run 1+2 in @value and obtain 3 and run 3 in the @type.
367 The problem is, the variable "+" is defined only in the @value layer.
368 To carry out computation in the @type layer. We need to define it also
369 in the @type layer.
370
371 To define some variable in a specific layer, use @LayerName in place of
372 (let|var|def)s.
373
374 >> let x = 2
375 >> @value x = 2
376 >> @type x = "int"
377 >> @hoge x = "fuga"
378
379 For "+", do it like this.
380
381 >> @type "+" = fun(x,y) {@value(
382 >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" }
383 >> )}
384 polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24
385
386 It is just computing the return type from the input type.
387 Not here that the intended "meaning" of if-then-else is the runtime-branching,
388 and the meaning of "==" is the value-comparison. These are the @value layer
389 behavior. So we have defined the function body inside @value layer.
390 But when we refer the variables x and y, we need its @type layer meaning.
391 Hence we use @type() there.
392
393 Now we get it.
394
395 >> @type( 1 + 2 )
396 int
397
398 Well, but do we have to define the @type layer meaning for every variables???
399 No. After you defined @type "+", you'll automatically get the following:
400
401 >> def double(x) { x + x }
402 (function:17e4740:1789720)
403
404 >> @type( double(123) )
405 int
406
407 Every user-defined functions are automatically "lift"ed to the appropriate layer.
408 Only primitive functions like "+" requires @yourNewLayer annotation.
409
410
411
412 [Layers :: neutral-layer]
413
414 let|var|def is to define a variable in the "current" layer.
415 Not necessary to the @value layer.
416
417 >> @value( let x = 1 in @value(x) )
418 1
419
420 >> @macro( let x = 1 in @value(x) )
421 polemy.failure.RuntimeException: [<REPL>:14:29] variable x not found
422
423 >> @macro( let x = 1 in @macro(x) )
424 {pos@value:{lineno@value:15, ...
425
426
427
428 [Layers :: Layered-Parameters]
429
430 >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} }
431 (function:1730360:1789720)
432
433 If you annotate function parameters by @LayerNames, when you invoke the function...
434
435 >> foo(1+2)
436 {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:<REPL>},
437 is@value:app, arg@value:{...
438 /fst@value:3
439 /}
440
441 its corresponding arguments are evaluated in the layer and passed to it.
442 If you specify multiple layers, the argument expression is run multiple times.
443 If you do not specify any layer for a parameter, it works in the neutral layer.
444 </pre>
445 ))
446 )
447 ))
448
449
450 $(SECTION Macro Layers, $(SECBODY
451 <p>
452 Polemy 言語組み込みのレイヤは <code>@value</code> と <code>@macro</code> の二つです。
453 (内部的にはもういくつかありますが、ユーザから直接は使えません。)
454 <code>@value</code> は、「普通に」普通のセマンティクスでプログラムを実行するレイヤでした。
455 <code>@macro</code> は、実は、<code>@value</code> よりも前に実行されるレイヤで、
456 「プログラムを実行するとその構文木を返す」というセマンティクスで動きます。
457 </p>
458 <pre>
459 (ここに例)
460 </pre>
461 <p>
462 動きとしてはこうです。
463 </p>
464 <ol>
465 <li>関数呼び出し時(とトップレベル環境の実行開始時)に、
466 まず、<code>@macro</code> レイヤでコードを実行。</li>
467 <li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li>
468 </ol>
469 <p>
470 <code>@macro</code> レイヤも所詮ただのレイヤですので、
471 上で説明した方法で <code>@macro</code> レイヤに関数などを登録しておくことで、
472 構文木の生成をいじることが可能です。まさにマクロ。
473 </p>
474
475 $(DDOC_MEMBERS
476 $(SECTION 概要, $(SECBODY
477 <p>
478 samples/macro.pmy にいくつか使い方サンプルが置いてありますので、詳しくはそちらをどうぞ。
479 </p>
480 <pre>
481 >> @macro( twice(print("Hello")) )
482 {
483 pos: {lineno:1, column:9, filename:<REPL>},
484 args: [ { pos: {lineno:1, column:15, filename:<REPL>},
485 args: [{pos:{lineno:1, column:21, filename:<REPL>},
486 is:Str,
487 data:Hello}],
488 is: App,
489 fun: {pos:{lineno:1, column:15, filename:<REPL>}, is:Var, name:print}}
490 ],
491 is: App,
492 fun: {pos:{lineno:1, column:9, filename:<REPL>}, is:Var, name:twice}
493 }
494 </pre>
495 <p>
496 詳細は気にしなくて構いませんが、とにかく、<tt>@macro</tt> レイヤでは、
497 基本的には、コードを実行するとそのコードの構文木がでてきます。
498 この挙動は <tt>@macro</tt> レイヤの変数をセットすることで、カスタマイズできます。
499 </p>
500 <pre>
501 >> @macro twice(x) { x; x } in twice(print("Hello"))
502 Hello
503 Hello
504 Hello
505 </pre>
506 <p>
507 (3回出力されてますが、3個目は <tt>print(x)</tt> の返値は <tt>x</tt> なので、
508 それがREPLによって印字されているだけです。)
509 <tt>@macro</tt> レイヤで <tt>in</tt> 以降を実行すると、<tt>print("Hello")</tt> という式を表す構文木が作られ、
510 それが <tt>twice</tt> 関数に渡されます。<tt>twice</tt> の中身も <tt>@macro</tt> レイヤで実行されるので、
511 構文木を作ろうとしますが、変数 <tt>x</tt> には <tt>@macro</tt> レイヤで値が入っているので、
512 その値を読み取って構文木を作成します。
513 結果として、2回 <tt>print("Hello")</tt> する構文木が作られて、
514 その後で、それが <tt>@value</tt> レイヤで実行されています。
515 </p>
516 <p>
517 本当にベタに構文木を作るだけなので、変数名の衝突などなどは気にしません。「衛生的でない」マクロです。
518 </p>
519 <pre>
520 @macro LetItBe(x, y) { var $(B it) = x; y }; $(D_COMMENT # y の中で変数 it が使える)
521 print( LetItBe("myself", "when I find " ~ $(B it) ~ " in times of trouble") );
522 </pre>
523 <p>
524 変数名に気をつけるには、組み込み関数 <tt>gensym()</tt> を使って頑張って下さい。
525 </p>
526 ))
527 $(SECTION レイヤ切り替え, $(SECBODY
528 <p>
529 他のレイヤ同様、<tt>@macro</tt> レイヤを実行中に <tt>@layer( ... )</tt> 構文を使うことで、
530 別のレイヤでコードを動かすこともできます。よく使う例は、<tt>@value</tt>
531 レイヤに移ることで構文木を普通に計算して色々プログラム的にいじる用途です。
532 </p>
533 <pre>
534 @macro reverseArgs(e) {$(B @value)(
535 def rev(xs, acc) {
536 case xs when {car:x, cdr:xs}: rev(xs, {car:x, cdr:acc}) when {}: acc
537 };
538 case @macro(e)
539 when {is:"App", fun:f, args:as}: {is:"App", fun:f, args:rev(as,{})}
540 when e: e
541 )};
542 print( reverseArgs(1-2) ); $(D_COMMENT # 2-1 == 1)
543 </pre>
544 <p>
545 <tt>reverseArgs</tt> は、関数呼び出しの構文木の、引数の順番を逆転する関数です。
546 <tt>@macro(e)</tt> によってマクロレイヤにセットされている構文木引数を取り出し、
547 それを <tt>@value</tt> レイヤによる普通の計算プログラムで操作しています。
548 <tt>@macro(...)</tt> はいわゆる「準クオート (quasiquote)」、
549 <tt>@value(...)</tt> は「逆クオート (unquote)」にちょっと近いかもしれません。
550 </p>
551 <p>
552 <tt>@layer(...)</tt> だけでなく、関数のレイヤ指定引数なども同様に使うことができるので、
553 一部の引数は <tt>@macro</tt>、一部の引数は <tt>@value</tt> レイヤで受け取る関数を書くなど、
554 さらに色々面白いことが可能です。
555 </p>
556 ))
557 $(SECTION 構文木の構造, $(SECBODY
558 <p>
559 構文木がどのようなテーブルで渡されてくるかについては、ソースドキュメントの
560 <a href="http://www.kmonos.net/repos/polemy/doc/tip/doc/ast.html">polemy.ast</a>
561 のページをご覧下さい。例えば変数名を表す <code>Var</code> クラスには、
562 継承の分も合わせて
563 <tt><a href="http://www.kmonos.net/repos/polemy/doc/tip/doc/failure.html">LexPosition</a> pos;</tt>
564 と <tt>string name;</tt> の2つのメンバがあるので
565 </p>
566 <pre>
567 { is: "Var",
568 pos: {filename:"foo.pmy", lineno:123, column:45},
569 name: "x" }
570 </pre>
571 <p>
572 こんな感じのテーブルになります。
573 クラス名が <tt>is</tt> フィールドに、メンバ変数はそのままの名前で入ります。
574 配列メンバは cons リストになって入ってきます。
575 自分で構文木を作る時は、<tt>pos</tt> フィールドだけは省略しても構いません。
576 </p>
577 ))
578 $(SECTION 微妙なところ1, $(SECBODY
579 <p>
580 ここまで、<tt>@macro</tt> が本当にただの1レイヤと説明してきましたが、
581 実はちょっとトリックが潜んでいます。
582 </p>
583 <pre>
584 >> @macro twice(x) {x; x} in twice($(B @value)(print("Hello")))
585 Hello
586 Hello
587 Hello
588 </pre>
589 <p>
590 先ほどの例に <tt>@value</tt> を増やしたものですが、これでもやはり、Hello
591 が2回 print されるようになります。これは本来はおかしな話で、<tt>print("Hello")</tt>
592 は <tt>@value</tt> レイヤで実行されて値に落ちるはずなので、1回しか print されないはず。
593 </p>
594 <p>
595 実は、Polemy の中では、<tt>@macro</tt> レイヤと <tt>(rawmacro)</tt>
596 レイヤという二つの異なるマクロ用レイヤが動いています。
597 </p>
598 <ul>
599 <li><tt>(rawmacro)</tt> も <tt>@macro</tt> も、コードを動かすとその構文木を返す意味論。</li>
600 <li>ただし、<tt>(rawmacro)</tt> も <tt>@macro</tt> も、
601 <tt>@macro</tt> レイヤに値のセットされた変数をみつけたときは、
602 その変数という構文木を作るのではなく、変数の内容を展開。</li>
603 <li>また <tt>@macro</tt> は、
604 レイヤ指定式を見ると実行レイヤを切り替て、構文木生成モードをやめてしまう。</li>
605 <li><tt>(rawmacro)</tt> は、
606 レイヤ指定式を見ても実行レイヤを切り替えないで構文木にする。</li>
607 </ul>
608 <p>
609 ユーザーから直接 <tt>(rawmacro)</tt> は呼べませんが、
610 「関数やトップレベル実行開始前のマクロ処理は <tt>(rawmacro)</tt> で実行開始」
611 「<tt>@macro</tt> レイヤ以外で呼び出した関数の仮引数に <tt>@macro</tt> がついていたら、
612 その実引数は <tt>(rawmacro)</tt> で実行」
613 という2つのタイミングで <tt>(rawmacro)</tt> が動き出します。
614 <tt>(rawmacro)</tt> が <tt>@macro</tt> レイヤから変数を見つけてマクロし始める時に、
615 そこで <tt>@macro</tt> に動作が移ります。
616 </p>
617 <p>
618 こうなっているのは、全部がレイヤ指定式に反応する <tt>@macro</tt> の動作だと、
619 レイヤを使ったプログラムが全て <tt>@value</tt> 実行時ではなく、
620 マクロ展開の時点で動き始めてしまって、おかしなことになるためです。
621 色々考えた結果、とりあえずこの中途半端な混合が具合がよいのではないかということになりました。
622 </p>
623 ))
624 $(SECTION 微妙なところ2, $(SECBODY
625 <p>
626 「関数実行開始時に、まずマクロレイヤを実行」と書きましたが、この時、関数内関数まで辿りにいくので、
627 何重にもネストした関数を使っていると、内側の関数は、何重にもマクロ展開が走ってしまいます。
628 これはなにかおかしい気がしますね。Scheme などはどうなっているのか調べないと…。
629 </p>
630 ))
631 $(SECTION 微妙なところ3, $(SECBODY
632 <p>
633 これはエラーになります。
634 </p>
635 <pre>
636 >> let _ = (@macro twice(x) {x;x} in twice(print("Hello")))
637 polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\value.d(109):
638 [<REPL>:2:35] 'twice' is not set in @value layer
639 </pre>
640 <p>
641 どういうことかというと、<tt>@macro</tt> で定義したマクロはいつから使えるようになるかという話で、
642 この <tt>@macro twice(x) {x;x} in ...</tt> の部分は <tt>@value</tt> レイヤの式なので、
643 まずこの式全体のマクロ展開が終わったあとにしか実行されないのです。<tt>twice</tt>
644 がマクロと見なされはじめるのは、<tt>@macro</tt> 実行が終わった後。
645 なので、
646 例えば <tt>twice(print("Hello"))</tt> の部分を無名関数にラップしてやれば、
647 マクロ展開を遅らせられて、 ちゃんと実行ができます。
648 </p>
649 <p>
650 これだと余りにも不便なので、関数のトップレベルの変数宣言式の列についてだけは、
651 <tt>@macro</tt> と <tt>@value</tt> の評価を交互にインターリーブするようにしました。
652 「関数やREPLのトップレベルの最初に宣言したマクロだけは、その関数内で即座に使える」わけです。
653 これも Scheme の let-syntax などなどの動きを調べて勉強しないと…。
654 </p>
655 ))
656 )
657 ))
658
659
660 $(SECTION Built-in Primitives, $(SECBODY
661 <p>
662 組み込み関数・変数の一覧。
663 </p>
664 $(DDOC_MEMBERS
665
666 $(SECTION テーブル操作, $(SECBODY
667 $(TABLE
668 $(TR $(TH {}) $(TD ()) $(TD 空のテーブルを作る))
669 $(TR $(TH .) $(TD (t, s)) $(TD テーブル t の名前 s のフィールドの値を取得。なければ <tt>undefined</tt>))
670 $(TR $(TH .?) $(TD (t, s)) $(TD テーブル t に名前 s のフィールドがあれば 1、なければ 0))
671 $(TR $(TH .=) $(TD (t, s, v)) $(TD テーブル t を親に持ち、名前 s のフィールドに v が入ったテーブルを作る))
672 )
673 ))
674 <br />
675
676 $(SECTION 制御フロー, $(SECBODY
677 $(TABLE
678 $(TR $(TH if) $(TD (n, ft, fe)) $(TD n が非 0 なら <tt>ft()</t>、0 なら <tt>fe()</tt> を実行))
679 )
680 ))
681 <br />
682
683 $(SECTION 演算, $(SECBODY
684 $(TABLE
685 $(TR $(TH +) $(TD (n, m)) $(TD 整数 n と整数 m を足して返す))
686 $(TR $(TH -) $(TD (n, m)) $(TD 整数の引き算))
687 $(TR $(TH *) $(TD (n, m)) $(TD 整数の掛け算))
688 $(TR $(TH /) $(TD (n, m)) $(TD 整数の割り算))
689 $(TR $(TH %) $(TD (n, m)) $(TD 整数の剰余))
690 $(TR $(TH &&) $(TD (n, m)) $(TD 整数 n と m が両方非 0 なら 1、それ以外では 0))
691 $(TR $(TH ||) $(TD (n, m)) $(TD 整数 n と m がどちらか非 0 なら 1、それ以外では 0))
692 $(TR $(TH ~) $(TD (a, b)) $(TD a と b を文字列化して結合))
693 $(TR $(TH <) $(TD (a, b)) $(TD a と b を比較))
694 $(TR $(TH <=) $(TD (a, b)) $(TD a と b を比較))
695 $(TR $(TH >) $(TD (a, b)) $(TD a と b を比較))
696 $(TR $(TH >=) $(TD (a, b)) $(TD a と b を比較))
697 $(TR $(TH ==) $(TD (a, b)) $(TD a と b を比較))
698 $(TR $(TH !=) $(TD (a, b)) $(TD a と b を比較))
699 )
700 <p>
701 注意点として、作者の趣味の問題で、<tt>&&</tt> と <tt>||</tt> は short-circuit 評価をしません。
702 整数演算の種類が少ないのは、D 言語の std.bigint がビット演算などをサポートしてないためです。
703 文字列が結合しかできないのは、単に手抜きです。
704 </p>
705 ))
706
707 $(SECTION 外部とのやりとり, $(SECBODY
708 $(TABLE
709 $(TR $(TH print) $(TD (a)) $(TD a を文字列化標準出力に改行付きで表示して、a を返す))
710 $(TR $(TH argv) $(TD ) $(TD スクリプトに渡された引数文字列のconsリスト))
711 $(TR $(TH gensym) $(TD ()) $(TD エセgensym。変数名として他とかぶらなそうな文字列を返します))
712 $(TR $(TH rand) $(TD (n)) $(TD 0 以上 n 未満の自然数を31bit以内でランダムに生成します))
713 )
714 ))
715 <br />
716
717 $(SECTION データ型判定, $(SECBODY
718 $(TABLE
719 $(TR $(TH _isint) $(TD (a)) $(TD a が整数なら 1、でなければ 0))
720 $(TR $(TH _isstr) $(TD (a)) $(TD a が文字列なら 1、でなければ 0))
721 $(TR $(TH _isfun) $(TD (a)) $(TD a が関数なら 1、でなければ 0))
722 $(TR $(TH _istable) $(TD (a)) $(TD a がテーブルなら 1、でなければ 0))
723 $(TR $(TH _isundefined) $(TD (a)) $(TD a が未定義値なら 1、でなければ 0))
724 )
725 ))
726 )
727 ))
728
729 )
730 Macros:
731 TITLE=Polemy Reference Manual
732 DOCFILENAME=index.html
733 SECTION=$(DDOC_DECL $(DDOC_PSYMBOL $1)) $(DDOC_DECL_DD $2)
734 SECBODY=$0