Differences From Artifact [7883429c0af25534]:
- File
doc/index.html
- 2010-11-27 14:23:54 - part of checkin [005474ba5b] on branch trunk - changed: not to lift _|_ (user: kinaba) [annotate]
To Artifact [d0516fddd5213f2d]:
- File
doc/index.html
- 2010-11-27 15:49:30 - part of checkin [6de3d8df3a] on branch trunk - reference manual completed (user: kinaba) [annotate]
417 417 Welcome to Polemy 0.1.0
418 418 >> 1 + 2
419 419 3
420 420 </pre>
421 421 この、普通に、数字の 1 は数字の 1 として、2 は 2 として、足し算は足し算として実行するのが、
422 422 「<tt>@value</tt> レイヤ」です。
423 423 レイヤを明示的に指定するには、<tt>レイヤ名( ... )</tt> という構文を使います。
424 -なので、以下のように書いても同じ意味です。
424 +<font color=red><b>レイヤ指定式</b></font> と読んでいます。
425 +つまり、さっきのコードは以下のようにも書けます。
425 426 <pre>
426 427 >> @value( 1 + 2 )
427 428 3
428 429 </pre>
429 430 他のレイヤで動かしてみましょう。適当に。「<tt>@hoge</tt> レイヤ」で。
430 431 <pre>
431 432 >> @hoge( 3 )
................................................................................
434 435 </pre>
435 436 <p>
436 437 エラーになりました。Polemy のインタプリタは、起動時には、<tt>@value</tt>
437 438 レイヤでのコードの意味しか知りません。<tt>@hoge</tt> レイヤでは <tt>3</tt>
438 439 というのがどんな意味なのか、わかりません!というエラーが出ています。
439 440 </p>
440 441 <p>
441 -これを教えてあげるためには、<tt>@hoge</tt> レイヤの <font color=red><b>リフト関数</b></font> を定義します。
442 +これを教えてあげるためには、<tt>@hoge</tt> レイヤの <font color=red><b>リフト関数</b></font> というものを定義します。
442 443 </p>
443 444 <pre>
444 445 >> @@hoge = fun(x){ x*2 }
445 446 (function:1bdc5c0:1ba8580)
446 447 </pre>
447 448 <p>
448 -「<tt>@ レイヤ名 = ...</tt>」文で、
449 -</p>
450 -<pre>
451 -[Layers :: Overview]
452 -
453 - Polemy's runtime environment has many "layer"s.
454 - Usual execution run in the @value layer.
455 -
456 - >> 1 + 2
457 - 3
458 - >> @value( 1 + 2 )
459 - 3
460 -
461 - Here you can see that @LayerName( Expression ) executes the inner Expression in
462 - the @LayerName layer. Other than @value, one other predefined layer exists: @macro.
463 -
464 - >> @macro( 1+2 )
465 - {pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
466 - is@value:app,
467 - args@value:{car@value:{pos@value:{lineno@value:3, column@value:9, filename@value:<REPL>},
468 - is@value:int,
469 - data@value:1},
470 - cdr@value:{
471 - car@value:{pos@value:{lineno@value:3, column@value:11, filename@value:<REPL>},
472 - is@value:int,
473 - data@value:2},
474 - cdr@value:{}}},
475 - fun@value:{pos@value:{lineno@value:3, column@value:10, filename@value:<REPL>},
476 - is@value:var,
477 - name@value:+}}
478 -
479 - (Sorry, this pretty printing is not available on the actual interpreter...)
480 - This evaluates the expression 1+2 in the @macro layer. In this layer, the meaning of
481 - the program is its abstract syntax tree.
482 -
483 - You can interleave layers.
484 - The root node of the abstract syntax tree is function "app"lication.
485 -
486 - >> @value(@macro( 1+2 ).is)
487 - app
488 -
489 -
490 -
491 -[Layers :: Defining a new layer]
492 -
493 - To define a new layer, you should first tell how to "lift" existing values two the new layer.
494 - Let us define the "@type" layer, where the meaning of programs is their static type.
495 -
496 - >> @@type = fun(x) {
497 - >> if( _isint(x) ) { "int" } else {
498 - >> if( _isfun(x) ) { x } else { "unknown" } }
499 - >> }
500 - (Note: polemy REPL may warn some exception here but please ignore)
501 -
502 - For simplicity, I here deal only with integers.
503 - _isint is a primitive function of Polemy that checks the dynamic type of a value.
504 - For function, leaving it untouched works well for almost all layers.
505 -
449 +<tt>@hoge</tt> レイヤでは、<tt>1</tt> というコードの意味は <tt>2</tt>、
450 +<tt>2</tt> というコードの意味は <tt>4</tt>、…、という、全部「2倍した意味」を持っていることにします。
451 +「<tt>@ レイヤ名 = ...</tt>」 という構文を使います。
452 +ここには、「<tt>@value</tt> レイヤでの値 <tt>x</tt> は <tt>@hoge</tt> レイヤではどういう意味になるか?」
453 +を計算して返す関数を登録します。
454 +これで、Polemy にも、<tt>@hoge</tt> レイヤの意味がわかるようになりました。
455 +</p>
456 +<pre>
457 + >> @hoge( 3 )
458 + 6
459 +</pre>
460 +<p>
461 +では、1+2 を <tt>@hoge</tt> レイヤで動かしてみましょう。
462 +</p>
463 +<pre>
464 + >> @hoge( 1 + 2 )
465 + polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\eval.d(466):
466 + [<REPL>:3:7] only @value layer can call native function: +
467 + [<REPL>:3:7] +
468 +</pre>
469 +<p>
470 +まだエラーですね。これは要するに "+" の意味がわからない、と言っています。
471 +<font color=red><b>レイヤ指定変数定義式</b></font> で、"+" の意味を教えてあげます。
472 +</p>
473 +<pre>
474 + >> @hoge "+" = fun(x, y) {x}
475 + (function:182eca0:18435e0)
476 + >> @hoge( 3 + 4 )
477 + 6
478 +</pre>
479 +<p>
480 +できました。
481 +</p>
482 +<p>
483 +他の組み込み関数の意味も決めてみましょう。この <tt>@hoge</tt> レイヤでは、
484 +引き算のつもりで書いたコードが、掛け算になってしまうのだ!
485 +</p>
486 +<pre>
487 + >> @hoge "-" = fun(x, y) {x * y}
488 + (function:1b4c6a0:1b4fbe0)
489 + >> @hoge( 5 - 6 )
490 + polemy.failure.RuntimeException@C:\Develop\Projects\Polemy\polemy\eval.d(469):
491 + [<REPL>:3:24] only @value layer can call native function: *
492 + [<REPL>:3:24] *
493 + [<REPL>:4:8] -
494 +</pre>
495 +<p>
496 +5、の意味は 10 で 6 の意味は 12 なので、10 - 12 と見せかけて掛け算して 120 が返るのだ!
497 +と思いきや、エラーになってしまいました。なぜでしょう。それは、この "-" の定義、
498 +<code>fun(x, y) {x * y}</code> 自体が、<tt>@hoge</tt> レイヤで実行されるからです。
499 +掛け算はまだ定義していません。
500 +</p>
501 +<p>
502 +ここは、「普通の」意味の掛け算を使いたいのです。
503 +この部分については、<tt>@value</tt> レイヤで計算して欲しい。
504 +そんなときは、レイヤ指定式を使います。
505 +</p>
506 +<pre>
507 + >> @hoge "-" = fun(x, y) {<b>@value(@hoge(x) * @hoge(y))</b>}
508 + (function:1b086c0:1b4fbe0)
509 + >> @hoge( 5 - 6 )
510 + 120
511 +</pre>
512 +<p>
513 +できました。掛け算は、<tt>@value</tt> レイヤの意味で実行します。
514 +各変数は、<tt>@hoge</tt> レイヤで計算された意味を使います、という意味になります。
515 +</p>
516 +</dd>
517 +
518 +<script>explorer.outline.writeEnabled = true;</script>
519 +<dt><span class="decl">
520 +<span class="currsymbol">関数の自動リフト</span>
521 +<script>explorer.outline.addDecl('関数の自動リフト');</script>
522 +
523 +</span></dt>
524 +<script>explorer.outline.writeEnabled = false;</script>
525 +
526 + <dd><p>
527 +続きです。ちょっと関数を定義してみました。
528 +</p>
529 +<pre>
530 + >> def twoMinus(x,y,z) { x - y - z }
531 + (function:1b26420:1b4fbe0)
532 + >> twoMinus(1,2,3)
533 + -4
534 +</pre>
535 +<p>
536 +<tt>@value</tt> レイヤで実行すると、当然、1 から 2 と 3 を引いて、-4 です。
537 +</p>
538 +<pre>
539 + >> @hoge( twoMinus(1,2,3) )
540 + 48
541 +</pre>
542 +<p>
543 +<tt>@hoge</tt> レイヤだと、2 と 4 と 6 を掛け算するので、結果は 48 です。
544 +</p>
545 +<p>
546 +1, 2, 3 のような値と、+ や - のような組み込み関数については、
547 +「<tt>@hoge</tt> レイヤでの意味」をレイヤを定義する人が決めてやる必要があります。
548 +でも、それさえ決めれば、あとはプログラム中で自分で定義した関数はすべて、
549 +Polemy 側で自動的にそのレイヤでの意味で実行できるようになります。
550 +</p>
551 +<p>
552 +レイヤ指定変数定義を使って、変数の意味をそのレイヤでだけ上書きして、
553 +違う意味を与えてやっても構いません。
554 +</p>
555 +<pre>
556 + >> def twoMinus(x,y,z) { x - y - z } <font color=green># @value レイヤでの定義</font>
557 + >> @hoge twoMinus(x,y,z) { 21 } <font color=green># @hoge レイヤでの定義</font>
558 + >> twoMinus(1,2,3)
559 + -4
560 + >> @hoge( twoMinus(1,2,3) )
561 + 42
562 +</pre>
563 +<p>
564 +こんな感じで。
565 +</p>
566 +</dd>
567 +
568 +<script>explorer.outline.writeEnabled = true;</script>
569 +<dt><span class="decl">
570 +<span class="currsymbol">レイヤ指定引数</span>
571 +<script>explorer.outline.addDecl('レイヤ指定引数');</script>
572 +
573 +</span></dt>
574 +<script>explorer.outline.writeEnabled = false;</script>
575 +
576 + <dd><p>
577 +ここまでのサンプルでは、コードを書いた人が、レイヤ指定式で明示的にレイヤを切り替えていました。
578 +<font color=red><b>レイヤ指定引数</b></font> を使うと、ライブラリ関数などを書くときに、
579 +「この関数の第2引数は <tt>@hoge</tt> レイヤで計算して欲しい」
580 +といった指定ができます。
581 +</p>
582 +<pre>
583 + >> def f(x, y <b>@hoge</b>) { x + @hoge(y) }
584 + >> f(1, 2)
585 + 5
586 +</pre>
587 +<p>
588 +f の第2引数は、必ず <tt>@hoge</tt> レイヤで解釈されます。
589 +</p>
590 +<pre>
591 + >> def ff(x, y <b>@hoge @value</b>) { x + @hoge(y) + @value(y) }
592 + >> ff(1, 2)
593 + 7
594 +</pre>
595 +<p>
596 +<tt>@hoge</tt> と <tt>@value</tt> の両方のレイヤで解釈して欲しい、という欲張りな人は、
597 +レイヤ指定を複数並べて下さい。
598 +</p>
599 +<p>
600 +なにもレイヤ指定がないと、<font color=red><b>ニュートラルレイヤ指定</b></font> と呼ばれ、
601 +その関数の呼び出し側が解釈されていたレイヤと同じところにセットされます。
602 +<tt>let</tt>, <tt>var</tt>, <tt>def</tt> による変数定義も同じで、
603 +<tt>@hoge x = ...</tt> とレイヤを明示するとそのレイヤでの変数の意味が定義されますが、
604 +<tt>let x = ...</tt> とレイヤ指定しないで書くと、現在解釈中のレイヤに定義、という動作をします。
605 +</p>
606 +</dd>
607 +
608 +<script>explorer.outline.writeEnabled = true;</script>
609 +<dt><span class="decl">
610 +<span class="currsymbol">ボトムと自動メモ化</span>
611 +<script>explorer.outline.addDecl('ボトムと自動メモ化');</script>
612 +
613 +</span></dt>
614 +<script>explorer.outline.writeEnabled = false;</script>
615 +
616 + <dd><p>
617 +パターンマッチ失敗時と、"..." という式を実行したときと、再帰が無限に止まらなくなったとき、
618 +には、Polemy のコードは実行時エラーで終了します……<tt>@value</tt> レイヤならば。
619 +</p>
620 +<p>
621 +ユーザー定義レイヤでは、このような時にも実行時エラーにならず、
622 +「<font color=red><b>ボトム</b></font>」という特別な値がリフト関数に渡されます。
623 +組み込みの <tt>_isbot</tt> 関数で、ボトムかどうか判定できます。
624 +</p>
625 +<p>
626 +「再帰が無限に止まらなくなったとき」は、
627 +ある引数で呼び出された関数が、return するよりも前にまた同じ引数で呼び出されたら、
628 +ループしていると見なすことで判定しています。
629 +これを判定する実装の副作用として、ユーザー定義のレイヤでは、関数は全てメモ化されています。
630 +つまり、ある関数が2回同じ引数同じ環境で呼び出されたら、1回目の答えをキャッシュしておいて、
631 +2回目は計算をせずに即座にキャッシュをひいて答えを返します。
632 +</p>
633 +</dd>
634 +
635 +<script>explorer.outline.writeEnabled = true;</script>
636 +<dt><span class="decl">
637 +<span class="currsymbol">まとめ</span>
638 +<script>explorer.outline.addDecl('まとめ');</script>
639 +
640 +</span></dt>
641 +<script>explorer.outline.writeEnabled = false;</script>
642 +
643 + <dd><p>
644 +まとめると、以下の機能があります。
645 +</p>
646 +<ul>
647 + <li><tt>@@layer = fun(x) { ... } in ...</tt> で、
648 + <tt>@value</tt> レイヤの値に別のレイヤでの意味を与えるリフト関数を定義</li>
649 + <li><tt>@layer x = ... in ...</tt> で、そのレイヤでのその変数の意味を定義</li>
650 + <li>どちらも let/var/def 式の特殊形なので、<tt>@@layer(x) { ... } in ...</tt> などの略記も可。</li>
651 + <li>式の途中で @layer( ... ) と書くと、レイヤを明示的に切り替えられる</li>
652 + <li>関数の仮引数に fun(x @layer){ ... } とレイヤを指定すると、
653 + 対応する実引数はそのレイヤで解釈される。</li>
654 +</ul>
655 +<p>
656 +</p>
657 +</dd>
658 +
659 +<script>explorer.outline.writeEnabled = true;</script>
660 +<dt><span class="decl">
661 +<span class="currsymbol">例</span>
662 +<script>explorer.outline.addDecl('例');</script>
663 +
664 +</span></dt>
665 +<script>explorer.outline.writeEnabled = false;</script>
666 +
667 + <dd><p>
668 +具体的な「値」のかわりに、その「メタ情報」を取り出して、それが処理によってどう変化するか、
669 +といった情報を解析するのを主な用途として、この機能を作ってみました。
670 +プログラムでよく使われる代表的なメタ情報は、「型」です。
671 +サンプルとしては、sample/type.pmy をご覧下さい。以下、簡単な概略。
672 +</p>
673 +<pre>
674 + @@type = fun(x) {
675 + if( _isint(x) ) then "int"
676 + else if( _isstr(x) ) then "str"
677 + else if( _isbot(x) ) then "runtime error"
678 + else "type error"
679 + }
680 +</pre>
681 +<pre>
506 682 >> @type( 1 )
507 683 int
508 684 >> @type( 2 )
509 685 int
510 686 >> @type( "foo" )
511 - unknown
512 -
513 - Fine! Let's try to type 1+2.
514 -
515 - >> @type( 1 + 2 )
516 - ...\value.d(119): [<REPL>:6:8] only @value layer can call native function
517 -
518 - Note that the behavior of this program is
519 - - run 1+2 in the @type layer
520 - and NOT
521 - - run 1+2 in @value and obtain 3 and run 3 in the @type.
522 - The problem is, the variable "+" is defined only in the @value layer.
523 - To carry out computation in the @type layer. We need to define it also
524 - in the @type layer.
525 -
526 - To define some variable in a specific layer, use @LayerName in place of
527 - (let|var|def)s.
528 -
529 - >> let x = 2
530 - >> @value x = 2
531 - >> @type x = "int"
532 - >> @hoge x = "fuga"
533 -
534 - For "+", do it like this.
535 -
536 - >> @type "+" = fun(x,y) {@value(
537 - >> if( @type(x)=="int" && @type(y)=="int" ) { "int" } else { "typeerror" }
538 - >> )}
539 - polemy.value.native!(IntValue,IntValue,IntValue).native.__anonclass24
540 -
541 - It is just computing the return type from the input type.
542 - Not here that the intended "meaning" of if-then-else is the runtime-branching,
543 - and the meaning of "==" is the value-comparison. These are the @value layer
544 - behavior. So we have defined the function body inside @value layer.
545 - But when we refer the variables x and y, we need its @type layer meaning.
546 - Hence we use @type() there.
547 -
548 - Now we get it.
549 -
687 + str
688 +</pre>
689 +<p>
690 +こんな風に、値をメタ情報へ抽象化するのが、リフト関数です。
691 +</p>
692 +<p>
693 +型に抽象化したレイヤでの、組み込み関数の意味を考えましょう。
694 +"+" は、"int" と "int" を足したら "int" を返す関数です。
695 +それ以外なら"型エラー"を返します。そういう関数です。
696 +</p>
697 +<pre>
698 + var int_int_int = fun (x, y) {@value(
699 + var tx = @type(x);
700 + var ty = @type(y);
701 + if tx=="runtime error" then ty
702 + else if ty=="runtime error" then tx
703 + else if tx=="int" && ty=="int" then "int"
704 + else "type error"
705 + )};
706 +
707 + @type "+" = int_int_int;
708 + @type "-" = int_int_int;
709 + @type "<" = int_int_int;
710 +</pre>
711 +<pre>
550 712 >> @type( 1 + 2 )
551 713 int
552 -
553 - Well, but do we have to define the @type layer meaning for every variables???
554 - No. After you defined @type "+", you'll automatically get the following:
555 -
556 - >> def double(x) { x + x }
557 - (function:17e4740:1789720)
558 -
559 - >> @type( double(123) )
560 - int
561 -
562 - Every user-defined functions are automatically "lift"ed to the appropriate layer.
563 - Only primitive functions like "+" requires @yourNewLayer annotation.
564 -
565 -
566 -
567 -[Layers :: neutral-layer]
568 -
569 - let|var|def is to define a variable in the "current" layer.
570 - Not necessary to the @value layer.
571 -
572 - >> @value( let x = 1 in @value(x) )
573 - 1
574 -
575 - >> @macro( let x = 1 in @value(x) )
576 - polemy.failure.RuntimeException: [<REPL>:14:29] variable x not found
577 -
578 - >> @macro( let x = 1 in @macro(x) )
579 - {pos@value:{lineno@value:15, ...
580 -
581 -
582 -
583 -[Layers :: Layered-Parameters]
584 -
585 - >> def foo(x @macro @value) { {fst: x, snd: @macro(x)} }
586 - (function:1730360:1789720)
587 -
588 - If you annotate function parameters by @LayerNames, when you invoke the function...
589 -
590 - >> foo(1+2)
591 - {snd@value: {pos@value:{lineno@value:17, column@value:5, filename@value:<REPL>},
592 - is@value:app, arg@value:{...
593 - /fst@value:3
594 - /}
595 -
596 - its corresponding arguments are evaluated in the layer and passed to it.
597 - If you specify multiple layers, the argument expression is run multiple times.
598 - If you do not specify any layer for a parameter, it works in the neutral layer.
714 + >> @type( 1 + "foo" )
715 + type error
716 +</pre>
717 +<p>
718 +「実行時エラーについては、それが起きなければ返すはずの型」を計算するという定義に、
719 +ここではしています。さらに(ちょっと手抜きで int 以外を考えていない)if の型定義を考えると、
720 +こんな雰囲気。
721 +</p>
722 +<pre>
723 + @type "if" (c, t, e) {@value(
724 + if( @type(c)=="int" || @type(c)=="runtime error" ) then
725 + @type( int_int_int(t(), e()) )
726 + else
727 + "type error"
728 + )}
729 +</pre>
730 +<p>
731 +関数が自動リフトされるので、フィボナッチ関数の型を調べることができます。
732 +</p>
733 +<pre>
734 + >> def fib(x) { if x<2 then 1 else fib(x-1)+fib(x-2) };
735 + >> @type( fib(100000000000000) )
736 + int
737 + >> def gib(x) { if x<2 then 1 else gib(x-1)+gib(x-"str") };
738 + >> @type( gib(100000000000000) )
739 + type error
740 +</pre>
741 +<p>
742 +この定義で <tt>fib(100000000000000)</tt> を <tt>@value</tt> レイヤで普通に計算して、
743 +結果の型を見る、というのでは時間がいくらあっても足りません。
744 +いったん <tt>@type</tt> のメタ情報の世界に移ってから計算できるのが、レイヤ機能の肝です。
745 +</p>
746 +<p>
747 +正確には、この定義で <tt>@type</tt> レイヤに移ると fib("int") を無限に呼び出し続けて止まらなくなるのですが、
748 +そこは、自動メモ化による再帰検出でボトム値を返す機能によって、うまく止まっています。
749 +</p>
750 +<p>
751 +それでも上手く型計算ができない(あるいはすごく遅くなる)ような複雑な関数があるかもしれません。
752 +仕方がないので、型情報をアノテーションとしてつけてあげることも可能です。
753 +</p>
754 +<pre>
755 + @type f = int_int_int;
756 + def f(x,y) { ...とても型を計算できないくらい複雑な定義... };
599 757 </pre>
758 +<p>
759 +これが、レイヤ指定変数定義の典型的な使い道です。
760 +</p>
600 761 </dd>
601 762 </dl>
602 763 <script>explorer.outline.decSymbolLevel();</script>
603 764
604 765
605 766 </dd>
606 767
................................................................................
617 778 <dd><p>
618 779 Polemy 言語組み込みのレイヤは <code>@value</code> と <code>@macro</code> の二つです。
619 780 (内部的にはもういくつかありますが、ユーザから直接は使えません。)
620 781 <code>@value</code> は、「普通に」普通のセマンティクスでプログラムを実行するレイヤでした。
621 782 <code>@macro</code> は、実は、<code>@value</code> よりも前に実行されるレイヤで、
622 783 「プログラムを実行するとその構文木を返す」というセマンティクスで動きます。
623 784 </p>
624 -<pre>
625 - (ここに例)
626 -</pre>
627 785 <p>
628 786 動きとしてはこうです。
629 787 </p>
630 788 <ol>
631 789 <li>関数呼び出し時(とトップレベル環境の実行開始時)に、
632 790 まず、<code>@macro</code> レイヤでコードを実行。</li>
633 791 <li>返ってきた構文木を、その関数を呼び出したときのレイヤで実行。</li>
................................................................................
976 1134
977 1135 </span></dt>
978 1136 <script>explorer.outline.writeEnabled = false;</script>
979 1137
980 1138 <dd> <table> <tr><th>_isint</th> <td>(a)</td> <td>a が整数なら 1、でなければ 0</td></tr>
981 1139 <tr><th>_isstr</th> <td>(a)</td> <td>a が文字列なら 1、でなければ 0</td></tr>
982 1140 <tr><th>_isfun</th> <td>(a)</td> <td>a が関数なら 1、でなければ 0</td></tr>
983 - <tr><th>_istable</th> <td>(a)</td> <td>a がテーブルなら 1、でなければ 0</td></tr>
984 - <tr><th>_isundefined</th> <td>(a)</td> <td>a が未定義値なら 1、でなければ 0</td></tr>
1141 + <tr><th>_istbl</th> <td>(a)</td> <td>a がテーブルなら 1、でなければ 0</td></tr>
1142 + <tr><th>_isbot</th> <td>(a)</td> <td>a が未定義値なら 1、でなければ 0</td></tr>
985 1143 </table>
986 1144 </dd>
987 1145 </dl>
988 1146 <script>explorer.outline.decSymbolLevel();</script>
989 1147
990 1148
991 1149 </dd>
................................................................................
994 1152 <script>explorer.outline.decSymbolLevel();</script>
995 1153
996 1154
997 1155 </td></tr>
998 1156 <tr><td id="docfooter">
999 1157 Page was generated with
1000 1158 <img src="candydoc/img/candydoc.gif" style="vertical-align:middle; position:relative; top:-1px">
1001 - on Sat Nov 27 22:01:28 2010
1159 + on Sun Nov 28 00:45:59 2010
1002 1160
1003 1161 </td></tr>
1004 1162 </table>
1005 1163 </div>
1006 1164 <script>
1007 1165 explorer.packageExplorer.addModule("index");
1008 1166 explorer.packageExplorer.addModule("main");