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