LMNtal 処理系のモジュール機能と 他言語接続機能の設計 - 早稲田大学

2003 年度 卒業論文
LMNtal 処理系のモジュール機能と
他言語接続機能の設計と実装
提出日: 2004 年 2 月 5 日
指導 : 上田 和紀 教授
早稲田大学理工学部情報学科
学籍番号 : 1G00P102-2
原 耕司
概要
上田研究室により提案された LMNtal は階層的グラフの書換えに基づく並行言
語モデルであり、その基本要素はプロセス、リンク、膜、ルールである。膜はプ
ロセス、膜、ルールの多重集合をもち、階層構造をなしている。また、複数のプ
ロセスをリンクで関連付け、グラフ構造を形成する事ができる。その階層的グラ
フ構造に対してパターンマッチングによるルール(書換え規則)を適用すること
で、処理が進行していく。
LMNtal が掲げる目標は、
(1) 多重集合の書換えに基づく多くの計算モデルの統合
(2) 広域分散計算から極小規模計算までを包含しうる真に汎用なプログラミン
グ言語のベースとなる
(3) 動的データ構造のプログラミングを大幅に平易にする
である。
現存の計算機上の実装を考えた場合、言語の外の世界 (OS) とのやりとりを行
うための仕組みとプログラムを部品化して後で再利用できるような仕組みが必須
である。しかし、現在の LMNtal の処理系には、そのような事を扱う統一的な仕
組みが用意されていない。
本研究では、上記の問題点を解決するために、次の仕組みを LMNtal 言語モデ
ルとの親和性が高い形で設計、実装した。
• インライン方式を用いた他言語インターフェース機能
• 複数のルールをモジュールとしてまとめ、簡単な記述によりモジュールを
使用する機能
• 同一モジュール間で唯一の実体にアクセスするための仕組み
さらに、設計した仕組みを用いて I/O、map コンテナ、セマフォなどの機能を
持つ各ライブラリを記述し、実際に処理系で実行することにより、実装したシス
テムが役に立つ事を確認した。
本研究により、 初めて LMNtal は実用的な言語としての第一歩を踏み出した。
i
目次
第 1 章 研究の目的と背景
1.1 並行言語モデル LMNtal . . . . . . . . .
1.2 既存の LMNtal 処理系に足りない機能 . .
1.2.1 ライブラリの仕組み . . . . . . .
1.2.2 他言語インターフェースの仕組み
1.3 本論文の構成 . . . . . . . . . . . . . . .
第2章
2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
LMNtal 言語解説
Atom とリンク . . . . . . . . .
ルールの適用例 - あみだくじ .
膜と階層化 . . . . . . . . . . .
LMNtal の構文 . . . . . . . . .
プロセス変数 . . . . . . . . . .
2.5.1 プロセス変数の意味 . .
2.5.2 自由リンク制約 . . . . .
2.5.3 プロセス変数の出現規則
ルール変数 . . . . . . . . . . .
型付きプロセス文脈 . . . . . .
現在の処理系の概要 . . . . . .
2.8.1 コンパイル部の機能 . .
2.8.2 ランタイム部の機能 . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
第 3 章 他言語インターフェース機能
3.1 根拠と目的 . . . . . . . . . . . . . . . . .
3.1.1 外界との通信 . . . . . . . . . . . .
3.1.2 処理の高速化 . . . . . . . . . . . .
3.1.3 新機能設計のつなぎとして . . . . .
3.2 他言語インターフェース機能の設計方針 .
3.2.1 Java 言語を書けるようにする . . .
3.2.2 インライン方式を用いる . . . . . .
3.2.3 インラインコードはアトム名とする
3.2.4 インラインアトムの種類 . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
2
2
2
.
.
.
.
.
.
.
.
.
.
.
.
.
4
4
5
6
7
8
8
9
9
10
10
11
11
11
.
.
.
.
.
.
.
.
.
12
12
12
12
13
13
13
13
14
15
ii
3.2.5
3.2.6
インラインコードの具体的な実行方法 . . . . . . . . . . .
対応する中間命令 INLINE . . . . . . . . . . . . . . . . . .
第 4 章 モジュール機能
4.1 根拠と目的 . . . . . . . . . . . . . . . . . . . . .
4.1.1 関連するいくつかのルールのパッケージ化
4.1.2 外部アトムへのアクセス . . . . . . . . . .
4.1.3 システムルールセットのモジュール化 . .
4.2 モジュール機能の設計方針 . . . . . . . . . . . . .
4.2.1 モジュールの宣言に関する設計方針 . . . .
4.2.2 モジュールの使用に関する設計方針 . . . .
4.2.3 外部アトムへのアクセス . . . . . . . . . .
4.2.4 システムルールセットのモジュール化 . .
第 5 章 実装
5.1 Inline クラス . . . . .
5.1.1 フィールド詳細
5.1.2 メソッド詳細 .
5.1.3 アルゴリズム .
5.2 module クラス . . . .
5.2.1 フィールド詳細
5.2.2 メソッド詳細 .
5.2.3 アルゴリズム .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
第 6 章 考察と今後の課題
6.1 実行例 - あみだくじモジュール . . . . . . . . . . . .
6.1.1 amidakuji.lmn - あみだくじモジュールの定義
6.1.2 あみだくじモジュールを使うプログラム . . .
6.2 実行例 - I/O モジュール . . . . . . . . . . . . . . . .
6.2.1 I/O モジュールの定義 . . . . . . . . . . . . .
6.3 実行例 - map モジュール . . . . . . . . . . . . . . . .
6.3.1 map モジュールの定義 . . . . . . . . . . . . .
6.4 実行例 - map モジュールを使うプログラム . . . . . .
6.4.1 map モジュールを使うプログラム . . . . . . .
6.4.2 実行結果 . . . . . . . . . . . . . . . . . . . . .
6.5 実行例 - semaphore モジュール . . . . . . . . . . . .
6.5.1 semaphore モジュールの定義 . . . . . . . . .
6.6 モジュール機能に関する考察 . . . . . . . . . . . . .
6.6.1 膜に名前をつける方法 . . . . . . . . . . . . .
6.6.2 膜に属性を持たせる方法 . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
15
17
.
.
.
.
.
.
.
.
.
19
19
19
20
20
20
20
22
24
31
.
.
.
.
.
.
.
.
32
32
32
32
33
34
34
35
35
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
37
37
37
38
38
38
39
39
40
40
41
41
41
42
42
42
iii
6.6.3
6.6.4
モジュールファイル自動読み込みについて . . . . . . . . .
スタティック呼び出しの仕組みについて . . . . . . . . . .
43
43
謝辞
44
参考文献
45
Appendix.A ソースコード
A.1 Inline クラス . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
A.2 Module クラス . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
46
49
iv
図目次
2.1
2.2
2.3
2.4
図とテキストによるグラフ構造の表現 . . .
グラフ構造に対するルール適用の例 . . . . .
膜によるグラフ構造の階層化とルールの適用
膜から飛び出すリンクが膜の自由リンク . .
.
.
.
.
4
5
7
8
3.1
3.2
インラインアトムは特殊な名前のアトムである . . . . . . . . . . .
インラインコードがアトム名を書き換える例 . . . . . . . . . . . .
14
16
4.1
4.2
4.3
4.4
モジュールの使用例 . . . .
スタティックアトムの遷移図
スタティックアトムの遷移図
スタティックアトムの遷移図
23
27
28
29
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
v
表目次
2.1
2.2
LMNtal 構文 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
自由リンク制約とその説明 . . . . . . . . . . . . . . . . . . . . . .
7
9
3.1
INLINE 中間命令仕様 . . . . . . . . . . . . . . . . . . . . . . . .
18
4.1
LOADMODULE 中間命令仕様 . . . . . . . . . . . . . . . . . . .
24
1
第 1 章 研究の目的と背景
1.1
並行言語モデル LMNtal
LMNtal[1], [2] は論理変数をリンクとする階層的グラフの書換えに基づく簡単
な並行言語モデルであり、計算に関する多様な考え方をできるだけ統合すること
を目標として提案された。
LMNtal のデータ構造は、基本のノードである Atom と、ノードの多重集合で
ある膜、ノード同士をつなぐリンクである。膜自体もノードとして扱えるため、
多重集合を構造化することができる。そしてそれらが形成する階層的グラフは、
ルールと呼ばれる書換え規則により書き換えられる。ルールはノードと共に膜内
に入っており、膜内のノードが形作る特定の構造を書き換える。
これらの LMNtal の基本要素により、動的プロセス構造とデータ構造の両方を
扱うことができる。LMNtal ではリンクするノードの書換えによって、プロセス間
通信とデータ構造の変更を簡潔かつ統一的に扱うことができる。また、LMNtal の
構文は容易に図形化できるように設計されているため、計算の流れや複雑なデー
タ構造を容易かつ直感的に理解することができる。
CHR や gamma など多重集合を扱う言語の多くは、多重集合のサーチにおける
オーバーヘッドが大きく処理効率を上げるのが困難である。そのことが、高速な
手続き型言語の代わりとしてそれらの言語が普及するのを妨げる一因にもなって
いるだろう。LMNtal では、ノードの探索は多くの場合リンクを用いて高速に処
理することができる。LMNtal は手続き型言語に代替しうる実用性と、容易に理
解できる簡潔さを併せ持つ言語を目指して設計されている。
LMNtal 言語の実行効率は、現存する計算機アーキテクチャとの相性という点
では他の手続き型言語より明らかに劣っている。しかし、簡潔な記述は開発効率
を上げ、性能的に不利な部分を十分に補えるはずである。しかも、計算機の性能
は年々良くなり続けているので、一昔(や現在)では時間がかかりすぎるような
処理も、将来は十分実用的な速度で動くということである。
1.2
既存の LMNtal 処理系に足りない機能
この章では、LMNtal に欠けていると思われる機能を指摘し、その機能を実装
したときのメリットを明らかにする。これが本研究の動機であり、それらの設計・
1.3. 本論文の構成
2
実装を行うことによって「実用に耐えうる」処理系にするのが目的とするところ
である。
「実用に耐えうる」とは「何かが簡単に書ける」事であると考えている。
1.2.1
ライブラリの仕組み
ライブラリを扱うための仕組みがない。現在良く使われている言語でライブラ
リの仕組みが備わっていないものは皆無であろう。小さな部品を独立に作ってお
いて後から必要なものを使うための仕組みは必須である。
ライブラリの仕組みがあると、プログラムが大規模になっても信頼性があまり
低下しない。さらに、いろいろな人が考えて作ったライブラリを利用できるため、
資産の共有ができる。車輪の再発明をしなければならない機会が減るはずである。
1.2.2
他言語インターフェースの仕組み
実用言語にする以上、入出力など OS の機能を利用する事が避けて通れない。
したがって、必ずどこかの部分で LMNtal と他言語コードが関連付けられなけれ
ばならない。
現在の LMNtal 処理系には他言語インターフェースが実装されていないので、
OS の機能を呼び出すことができず、従って入出力ができない。
実用プログラミング言語にとってこれは致命的な問題であるので、他の言語と
LMNtal 言語間で通信するための仕組みを作る必要がある。また、ユーザーが記
述した他言語コードと LMNtal 言語を接続したい場合もあるだろう。そのために
柔軟で汎用的な仕組みが必要である。
他言語インターフェースのもう 1 つのメリットとして、とても良く使うライブ
ラリは他言語で書くことによって高速に動作させることができるというものがあ
る。これにより、必要ならば性能の向上も期待できる。
1.3
本論文の構成
本論文では以降、次のような流れに沿って話をすすめる。
• 第2章
LMNtal の言語の概要について解説を行う。
• 第3章
他言語インターフェース機能の設計と具体的な実行イメージを述べる。
• 第4章
モジュール機能の設計と具体的な実行イメージを述べる。加えて、同一モ
1.3. 本論文の構成
3
ジュール間で一意な実体へのアクセスを実現する方法を論じ、実現する。
• 第5章
作成したクラス仕様を述べ、そのクラスが実際の処理系内部ではどのよう
に使われているのかを解説する。
• 第6章
設計した機能を使って書いたサンプルコードを解説し、考察を述べる。並
びに、設計した内容についての問題点や今後の課題を考察する。また、採
用されずに廃案になった設計についても考察を加える。
• Appendix
付録として、Module クラスと Inline クラスのソースコードと第 7 章で取
り上げたサンプルコードを掲載する。
4
第2章
LMNtal 言語解説
本章では、LMNtal(Linked Multisets of Nodes transformation language) 言語仕
様などについて軽く触れる。この章の記述は上田の LMNtal 発表論文 [1][2] 及び
その後の議論に基づき、具体例と共に解説する。また、昨年時の仕様から変わっ
た部分や検討中の仕様についても触れる。
より詳しくは、矢島の卒論 [4] などをご覧頂きたい。
2.1
Atom とリンク
Atom は、LMNtal のグラフ構造の基本ノードであり、Atom の名前と 0 個以上
のリンクを持つ。リンクは Atom を 1 対 1 接続する。例えば、リンク X を持つ名
前 a の Atom は a(X) と表記される。また、Atom のもつリンクには順序があり、
a(X,Y) と a(Y,X) は別物として扱われる。
Atom とリンクによりグラフ構造が形成される。次に例を示す。
h(Neck).
a(Neck, Hand1, Body, Hand2).
h(Hand1). h(Hand2).
b(Body, Foot1, Foot2).
f(Foot1). f(Foot2).
h
Hand1
h
h
ck
Ne
a
Hand2
f
Foot1
Body
b
Foot2
f
図 2.1: 図とテキストによるグラフ構造の表現
この図は LMNtal のグラフ構造の、構文(2.4 参照)に即したテキスト表現と図
示したものの対応を表している。構文がそのままグラフィカルな表現に対応して
いることが分かるだろう。丸が Atom を、丸の中の文字が Atom 名を、Atom を
つなぐ線がリンクを、リンクのそばの文字がリンク変数名を表している。
リンクの実体は論理変数である。しかし、KL1 や CHR のように、リンクが「具
2.2. ルールの適用例 - あみだくじ
5
体化」されることは無い(リンク X に整数3が代入される、といったことは無
い)。また、リンクは方向を持たない無方向リンクである。
2.2
ルールの適用例 - あみだくじ
ルールは、グラフ構造のある特定の部分を書き換える規則である。例を挙げて
説明する。
start1
start2
start3
start2
start1
start3
start1
start2
c
c
start3
you
c
c
c
c
you
c
c
c
goal1
goal2
c
c
goal3
c
c
goal1
goal2
c
c
c
c
you
goal1
goal2
goal3
c
goal3
<< Rule >>
you
c
c
c
c
:you
図 2.2: グラフ構造に対するルール適用の例
これは、あみだくじのグラフ構造を you がたどっていく例であり、LMNtal の
動作の解説によく用いられる。図の左上のグラフが初期状態であり、これにルー
ルが適用されて右上の状態になる。
図 2.2 のテキスト表現を以下に示す。
2.3. 膜と階層化
6
✓
✏
start1(You).
you(You, R10).
start2(R20).
start3(R30).
c(R10, C1, R11).
c(R30, C2, R31).
c(R11, C3, R12).
c(R20, C1, R21).
c(R21, C2, R22).
c(R22, C3, R23).
goal1(R12).
goal2(R23).
goal3(R31).
you(T1,M1), c(M1,C,D1), c(T2,C,D2)
:- c(T1,C,D1), c(T2,C,M2), you(M2,D2).
✒
✑
グラフ構造にすると一目瞭然だが、テキストで見ると複雑である。2 次元で表
されるグラフ構造を 1 次元のテキストに直すのだから、これは致し方ない1 。
Atom c のリンクによるつながりがあみだくじの各節点を表し、スタートとゴー
ルには分かりやすいように別の名前が付けられている。良く見るとグラフ構造と
の対応が分かるだろう。
そのグラフ構造に加えて、「c の上に you が繋がっている時は、you は反対側
の c の下に移動する」という唯一のルールがある2 。
ルール中のリンク変数名は、「こことここが繋がっている」ことを示すために
つけられたものであり、
「リンク変数名がちがうからこのルールは適用できない」
といった事は無い。つまり、a(X), b(X) と a(Y), b(Y) は全く同じグラフ構造を
表す。
2.3
膜と階層化
膜は、ノードの多重集合を保持する仕組みである。膜は膜をノードとしてもつ
ことができるため、膜を用いて階層的なグラフ構造を実現することができる。ま
た、ルールは膜に所属し、膜内のグラフ構造に対して書換えを行う。以下に例を
示す。
このように、膜は任意個の膜、Atom、ルールを持つことができる。膜、Atom、
ルールをあわせて「プロセス」と呼ぶ。また、膜そのもの(テキスト表現におけ
る { } )のことを compound、膜内にある全てのプロセスを含む膜全体のことを
Cell と呼び分ける。
1
とはいえ、もっと直感的なテキスト表現が研究されてしかるべきである。インデントの数が
重要な情報を表している Python 言語のような例もヒントになるのではないかと考えている。
2
LMNtal の記述能力の高さを示す一例であろう。その上、このプログラムは you が増えても
そのまま動く。
2.4. LMNtal の構文
7
{ { {a(X)}, b(X,Y), ({a(X)} :- {d(X)}) }, c(Y) }
a
b
: Cell
c
: Atom
:-
a
d
: Rule
図 2.3: 膜によるグラフ構造の階層化とルールの適用
ちなみに、この図のルールは適用可能である。膜内に適用可能なルールが無い
とき、その膜を「安定 (stable) 状態」にある膜と呼ぶ。この例では、初期状態にお
いては Atom a のある膜のみ安定状態で、b のある膜、c のある膜はどちらも安定
状態でない。ルール適用後は、a,b,c のある膜はどれも安定状態にある。LMNtal
プログラムが実行されるということはルールが適用されるということであり、適
用できるルールが無くなった(全ての膜が安定状態になった)ら実行終了である。
2.4
LMNtal の構文
LMNtal の主な構文は以下のように定義されている。
プロセス P
P
::=
|
|
|
|
|
0
p(X)
P,P
{P}
{P}/
(T :- T)
プロセステンプレート T
T
::=
|
|
|
|
|
|
|
|
0
p(X)
T,T
{T}
{T}/
T:-T
@p
$p[X |A]
p(*X)
(null)
(atom)
(composition)
(cell)
(stable cell)
(rule)
(rule variable)
(process variable)
(aggregate)
A
::=
|
[]
*X
(null)
(bundle)
(null)
(atom)
(composition)
(cell)
(stable cell)
(reaction rule)
表 2.1: LMNtal 構文
構文中の X はリンク(リンク変数)を表す。X は順序のあるリンク X の列を
2.5. プロセス変数
8
表している。X は大文字から始まる文字列で表される。
p は名前を表す。p は Atom 名のほか、ルール変数名、プロセス変数名(後述)
としても使われる。数も名前の一種である(数も Atom として扱われる)。p は大
文字以外から始まる文字列で表される。
リンクは、P 中に1回か2回出現する。1回しか出現しないリンクは、P 中に
リンク先の無いリンクである。2回出現するということは、リンクの両端が共に
P 中にあるということである。P 中に1回しか出現しないリンクを、P の自由リ
ンクと呼ぶ。
膜{P}の自由リンクとは、P の自由リンクのことであるとする。これは、直感
的には膜から飛び出しているリンク、ということができる。
P
図 2.4: 膜から飛び出すリンクが膜の自由リンク
2.5
2.5.1
プロセス変数
プロセス変数の意味
プロセス変数は、
「膜内にある膜、Atom で、ルール中で特定されないもの全て」
を表す。
Node1 : { kake. ue. { { kashi } }. }
というデータ構造を
{ kake. $p. }
というルール左辺にマッチさせたとき、プロセス変数$p が表すものは、
ue. { { kashi } }.
である。kake はルール中で特定されているので、プロセス変数には含まれない。
注意すべき点は、Node1 には { ue } はマッチしないという事である。この
ルール左辺にマッチするのは { ue } だけである。Node1 には ue 以外の要素も
含まれるためマッチしないのである。
2.5. プロセス変数
2.5.2
9
自由リンク制約
プロセス変数 $p[X1 , ...Xm |A] の [X1 , ...Xm |A](自由リンク制約)は、「プ
ロセス変数$p の自由リンク」を表す。膜のもつ自由リンクではなく、「プロセス
変数$p が表す膜、Atom の中で、1回しか現れないリンク」であることに注意す
る3 。
$p[X1 , ...Xm |A] の X1 , ...Xm の部分は、$p が X1 , ...Xm の m 本の自由リン
クを持っていなければならないことを表す。
A は、X1 , ...Xm で指定された以外の自由リンクを規定するものであり、[ ] の
場合は 0 個、*X は 0 個以上の自由リンクを持っていて良い事を表す。また、
|A
が省略された場合、
|[ ] が指定されたものとする。
以下にいくつか例を挙げる。
$p[X,Y]
$p[X|*Y]
$p[|[ ] ]
$p[ ]
$p[|*X]
$p
$p の自由リンクは X,Y のみ。
$p の自由リンクに X が含まれる。その他の自由リンクがあっても良い。
$p は自由リンクを持たない。
同上
$p の自由リンクはあっても無くてもいい。
同上
表 2.2: 自由リンク制約とその説明
2.5.3
プロセス変数の出現規則
ルール左辺に現れるプロセス変数は、膜ごとに高々 1 回出現することができ
る。例えば、ルールの左辺に3つの膜がある場合、その3つの膜の中に1つずつ
プロセス変数を書くことができる。そのとき、それらのプロセス変数名は同じも
のであってはならない。
ルール右辺に現れる場合は、リンクの整合性が保たれる限りいくつ出現しても
良い。例えば、左辺で自由リンクなしと指定されたプロセス変数は自由リンクを
持たないので、右辺に任意の回数出現して良い。つまりこの場合はプロセス変数
で表された内容の消滅や複製ができる。
3
こう定義することによって、{a(X), {b(X)}} というデータ構造に {$p(X),{b(X)}} がマッチ
できるようになる。$p には b(X) が含まれないため、リンク X は a(X) がもつ1つだけ、つまり
X は$p の自由リンクとなる。
2.6. ルール変数
2.6
10
ルール変数
ルール変数は、膜内の全てのルールにマッチする変数である。プロセス変数と
同じように、ルール左辺にある膜ごとに高々 1 回出現できる。また、ルール右辺
には任意の個数だけ出現することができる。
✓
ルール変数によるルールの複製
✏
{ order_area, @rule }, { lawless_area }
:- { order_area, @rule }, { lawless_area, @rule }
✒
✑
このルールは、order のある膜内の全てのルールを、lawless area のある膜内に
コピーするというものである4 。
プロセス変数のときと同じように、ルール変数の無い膜は、ルールをもつ膜に
はマッチできない。逆に、ルール変数のある膜は、ルールを持つ膜にももたない
膜にもマッチできる(ルール変数は空でも良い)。
2.7
型付きプロセス文脈
言語仕様ではないが、1 本のルールで可算個のルールを記述するために「型付
きプロセス文脈」が提案された5 。
ガード中でプロセス変数が満たすべき型や条件を指定すると、そのプロセス変
数が条件を満たす具体値で置換されたようなルールが沢山存在すると考えるとい
う仕組みである6 。
簡単な例を挙げる。
number(N) :- 7 < N | greater_than_seven(N).
このように書くと、次のルール集合
{ number(n) :- greater_than_seven(n) |∀n(n ∈ Z ∧ n > 7)}
すなわちこのようなルールに展開される。
number( 8) :- greater_than_seven( 8).
number( 9) :- greater_than_seven( 9).
4
しかして lawless area のままである :)
加藤による
6
処理系内部で実際にルールが生成される必要はなく、あくまで考え方としてルールがマクロ
展開されるのである。
5
2.8. 現在の処理系の概要
11
number(10) :- greater_than_seven(10).
number(11) :- greater_than_seven(11).
number(12) :- greater_than_seven(12).
:
:
従って
number(5). number(19).
は
number(5). greater_than_seven(19).
に遷移することになる。
2.8
現在の処理系の概要
現在、LMNtal 処理系の開発が上田研言語班の有志により進められている。Java
言語で実装されており、大きく分けてコンパイル部とランタイム部に分かれる。
2.8.1
コンパイル部の機能
LMNtal 言語で書かれたソースファイルを構文解析し、コンパイル時データ構
造に変換する。その構造をルールコンパイラにより内部命令と呼ばれる LMNtal
仮想マシン命令に変換する。
2.8.2
ランタイム部の機能
コンパイル部で生成された内部命令を 1 つずつ解釈実行することで、LMNtal
言語としての動作を実現する。
12
第 3 章 他言語インターフェース機能
本章では、他言語インターフェース機能を実現するための設計を記述する。
まず、この機能を実装する根拠と目的を再確認し、LMNtal ではどういった設
計にするのが良いのかを論ずる。
各クラスの概要及びクラス間の関係は、第 5 章参照。
3.1
根拠と目的
概要で簡単に述べたが、他言語インターフェース機能が必要な根拠とその目的
とする所は次のとおりであった。
3.1.1
外界との通信
実用言語である以上、入出力など OS の機能を利用する事が必須である。した
がって、必ずどこかの部分で LMNtal と他言語コードが関連付けられなければな
らない。他言語インターフェース機能、つまり他言語コードと LMNtal コードが
ある定められた方法で適切に関連付けられるような仕組みがあれば、この条件が
満たされる。
3.1.2
処理の高速化
OS とのやり取り以外の機能は全て LMNtal 言語で表現できるはずだが、現在
の計算機アーキテクチャ上では、ある機能について LMNtal 言語の実行よりも効
率が良いような他の言語も存在する。
そのような機能については、LMNtal 言語の規則に則ってその機能を使うが、
機能そのものは他言語で書かれた効率の良いコードを実行するというのも良い方
法だと考えた。
3.2. 他言語インターフェース機能の設計方針
3.1.3
13
新機能設計のつなぎとして
また他言語インターフェース機能は、言語仕様自体が現在発展中であるような
処理系にとっての利点にもなる。
まだその言語では実現できない機能を処理系記述言語を使って実現し、例題と
なるコードを実行することで、その言語の記述力を確認しながら言語仕様を固め
ていくというやり方ができる。
例えば、LMNtal 言語は型体系の整備がまだなされていないが、型体系を擬似
的に実現するための部分を処理系記述言語で直接書くことにより、理論的に完成
していなくても処理系としては部分的に動作することができる。
実際に動く処理系があることで、エンドユーザからの意見を言語設計現場に
フィードバックすることにより新たな可能性を探る事もできる。
3.2
他言語インターフェース機能の設計方針
前述の動機や目的を達成するための設計を論ずる。決めるべき事柄は次の 4 つ
である。
• どの言語を書けるようにするか
• 他言語コードの記述場所
• LMNtal コードと他言語コードの関連付け方法
• 他言語コードの実行タイミング
以下、詳しい説明と根拠を述べる。
3.2.1
Java 言語を書けるようにする
現処理系は Java 言語で記述されているので、インラインコードも Java 言語
に限る事にするのは自然な選択であろう。実行時にインラインコードを Java コ
ンパイラによりコンパイルした後、生成されたクラスをクラスローダによって処
理系に読み込む。
3.2.2
インライン方式を用いる
他言語コードとの関連付けをインライン機能により実現することのメリットは、
LMNtal コード中における他言語コードの位置情報が利用できる所にある。
3.2. 他言語インターフェース機能の設計方針
14
関連付けたい LMNtal コード付近に他言語コードを書くことで、その位置情報
を関連付け情報として利用できる。
もちろん他言語インターフェースはインラインという形態を取らなくても実現
可能であるが、その場合 LMNtal コードと他言語コードが格納されたファイルと
を関連付けるための適切な対応表を用意しなければならない。
インラインにすれば、ある場所に書いてある他言語コードと、その付近にある
LMNtal コードを関連付けることで、関連付けするにあたって必要となる他の情
報の記述量を減らすことができそうである。
インライン方式を採用するにあたって必要になるのは次のものである。
• 他言語コードがそのまま書ける程度に強力な構文的クォータ
• LMNtal コードと他言語コードを関連付けるための法則
3.2.3
インラインコードはアトム名とする
他言語とのインターフェースは言語仕様にはない。ということは、実装である
処理系が規定すべきであるので、インラインを実現するために言語構文に特殊な
変更を加えるのは最低限にしたいと考えた。
そこで、言語として既に用意されている要素であるアトムを利用する方法を思
いついた。
具体的には、
「アトム名がある文字列で始まるとき、処理系から見てそのアトム
名そのものをインラインコードとして扱う」という方法である。そのようなアト
ムを、インラインアトムと呼ぶことにする。インラインアトムは、言語仕様とし
て見ると単なるアトムと完全に等価であるが、処理系から見るとインラインコー
ドが含まれるアトムとして区別されることになる。
これなら、言語コアに対する変更は一切なしに他言語インターフェースを実現
できる。
例を図で表すと次のようになる。
/*inline*/
System.out.println("test")
print_test
図 3.1: インラインアトムは特殊な名前のアトムである
3.2. 他言語インターフェース機能の設計方針
3.2.4
15
インラインアトムの種類
インラインアトムは 2 種類に分類される。インライン実行アトムとインライン
宣言アトムである。
インライン実行アトム
/*inline*/ で始まる名前を持つアトムを、インライン実行アトムと呼ぶ。
あるルールによってインライン実行アトムが生成されると、そのルールに対応
する実行 (NEWATOM, LINK など) が全て終わってからインラインコードに書か
れた処理が実行される。
ルールによるデータ構造操作が全て終わってからインラインコードを実行しな
いと、インラインコードがアクセスしたいデータ構造がまだ生成されていない可
能性がある。
ちなみに、全て終わってからではなく最初に実行するという方針も考えられる。
最初に実行するようにすると、インラインコードがアクセスできるデータ構造は
ルール左辺に構造がマッチした直後の状態になる。
インラインコード中では、自身を表すインライン実行アトムに対応する Java
オブジェクト runtime.Atom me が使用できる。これによって、インラインコー
ドにて自分に繋がっている 2 番目のアトム名を強制的に書き換えたり、膜を操作
したりする事ができる。
インライン宣言アトム
/*inline_define*/ で始まる名前を持つアトムを、インライン宣言アトムと
呼ぶ。
インライン宣言アトムがソースコード中のどこかに存在すると、対応するイン
ラインコードは Java 言語コードファイルのグローバルな領域にそのまま展開さ
れる。
従って、独自のクラス定義をインライン宣言アトム名として書くことができ、
そのクラスはインライン実行アトムの生成時に参照できる。
インライン宣言アトムを見つけて Java ソースに展開する処理は、コンパイル
時に静的に行われる。
3.2.5
インラインコードの具体的な実行方法
インラインコード自体を表す Java ソースコードを動的に生成・コンパイルし、
それを動的にロードすることによって処理系がコンパイル済みインラインコード
を実行できるようにする。
3.2. 他言語インターフェース機能の設計方針
16
/*inline*/
String s=do_input();
me.nthAtom(0).changeName(s);
me.changeName("done");
null
(You inputted "ABC")
done
ABC
図 3.2: インラインコードがアトム名を書き換える例
動的に生成する MyInlineCode.java は runtime.InlineCode インターフェースを
実装しなければならない。この簡単なインターフェースには図に示す通り、イン
ラインコードを実行するためのメソッド run(Atom a, int codeID) だけが定義
されている。
✓
✏
interface runtime.InlineCode
package runtime;
public interface InlineCode {
public void run(Atom a, int codeID);
}
✒
✑
このメソッドは実行時に呼ばれ、codeID を持つインラインコードを実行しな
ければならない。インラインコードから周辺アトムを操作できるように、そのイ
ンラインコードに対応するインラインアトムオブジェクトも一緒に渡される。
また、実際に生成される MyInlineCode.java は図のような書式になっている。
3.2. 他言語インターフェース機能の設計方針
17
✓
✏
import runtime.*;
/*** ここにインライン宣言コードが埋め込まれる ***/
public class MyInlineCode implements InlineCode {
public void run(Atom me, int codeID) {
switch(codeID) {
case 0: {
/*** コード ID = 0 に対応するインラインコードが
ここに埋め込まれる ***/
break;
}
case 1: {
/*** コード ID = 1 に対応するインラインコードが
ここに埋め込まれる ***/
break;
}
:
:
case N: {
/*** コード ID = N に対応するインラインコードが
ここに埋め込まれる ***/
break;
}
}
}
}
✒
3.2.6
✑
対応する中間命令 INLINE
前述の通り、コンパイラは書き換え規則(ルール)をいったん中間命令に変換
し、ランタイム部がその中間命令を解釈実行することになる。
インライン実行アトムのコードを実行をさせるための中間命令を設ける必要が
あるが、その仕様を示す。
3.2. 他言語インターフェース機能の設計方針
名前
引数
18
INLINE
実行するインライン実行アトムへの参照
インラインコード ID
表 3.1: INLINE 中間命令仕様
✓
インライン機能を使ったプログラム例と、対応する中間命令列
✏
LMNtal プログラム
( a :- [[/*inline*/System.out.println(123);]] ), a
マッチング命令
spec
findatom
react
[2, 0]
[1, 0, a_0]
[( a :- ’/*inline*/...’ ), [0], [1]]
ボディ実行命令
spec
dequeueatom
removeatom
newatom
enqueueatom
inline
freeatom
proceed
[2,
[1]
[1,
[2,
[2]
[2,
[1]
[]
✒
✓
3]
0, a_0]
0, /*inline*/...]
0]
上記プログラムのために動的に生成されたソースコード
✑
✏
import runtime.*;
public class MyInlineCode implements InlineCode {
public void run(Atom me, int codeID) {
switch(codeID) {
case 0: {
/*inline*/System.out.println(123);
break; }
}
}
}
✒
✑
19
第 4 章 モジュール機能
本章では、モジュール機能並びにモジュールのスタティックなアトムへのアクセ
スを実現するための設計を述べる。
まず、この機能を実装する根拠と目的を再確認し、LMNtal ではどういった設
計にするのが良いのかを論ずる。
実現する機能は次の通りである。
• 複数のルールのモジュール化
• モジュールの利用
• 同一モジュール間で唯一な実体へのアクセス
• システムルールセットのモジュール化
各クラスの概要及びクラス間の関係は、第 5 章参照。
4.1
4.1.1
根拠と目的
関連するいくつかのルールのパッケージ化
人間が一度に認識できるコード量はそんなに多くない。一度に認識できない量
のコードを取り扱おうとすると、信頼性が低下することになる。従って、信頼性
を低下させずに大きなプログラムを作るためには一度に取り扱うコード量は適度
に小さくなければならない。
そこで、他の言語でのモジュールと同様に考え、LMNtal プログラムに含まれ
るルール集合をより小さな集合に分割して、関連性の高いルールをモジュールと
してまとめたい。そして、後で使いたい機能のまとまりを一括して利用すること
ができる。これが目的である。
感覚的には、
「ウランの量が臨界量を超えたら核分裂させるなどの一連の核ルー
ル」や「数字として表記されているアトムを実数として扱った上での四則演算に
相当する反応規則群」などをモジュール化しておき、使いたいモジュールを自由
に使えるようにするというのが目的である。
4.2. モジュール機能の設計方針
4.1.2
20
外部アトムへのアクセス
また、複数のモジュール利用者間でモジュールに属するアトムを共有したい時
がある。そのようなアトムはモジュール本体に属している必要があり、Java 言語
で言うところの static なメンバに相当する。
この仕組みは、全ての膜にあるとする「システムルールセット」を使うと実現
できそうである事が分かった。そこで、この仕組みを実現するための方法を論じ、
実際に動かしてみる。
そのスタティックな仕組みを利用した例として、排他制御で使われるセマフォ
と、一意な連番を生成するモジュールを作成してみる。
4.1.3
システムルールセットのモジュール化
システムルールセットとは、全ての膜に存在するものとして処理系が扱うルー
ルセットである。例えば、同一膜にある 2 つのアトムが膜外で単一化された状態
から同一膜の中で 2 つのアトムを直接繋ぐ状態へ書き換えるルールが現在の処理
系でシステムルールセットになっている。
ほかにも、後で述べるスタティックアトムへのアクセスの際にもいくつかのルー
ルをシステムルールセットとして扱うことにする。そこで、このシステムルール
セットについてもモジュールとして定義できるようにすることを考えた。
4.2
モジュール機能の設計方針
ここでは、モジュール機能の実現のために必要な設計を理由等も含めて詳しく
述べる。
4.2.1
モジュールの宣言に関する設計方針
モジュール化の単位は膜
LMNtal には膜という概念がある。膜は、アトム、ルール、膜の多重集合を含
むことができる。これによって、計算の局所化やグラフ構造の階層化が可能にな
るわけだが、この膜という概念をモジュール化のために使うと綺麗にまとまりそ
うだと考えた。
実際 LMNtal には、ある条件にマッチした膜内のルールをまとめて他の場所
にコピーしたり移動したりする機能が言語仕様としてある。モジュールの中身を
膜によりグループ化すれば、このルール移送機能をモジュール単位で使う事がで
きる。
4.2. モジュール機能の設計方針
21
また、LMNtal コードのテキスト表記では、膜は中括弧 {
} として表され
る。この表記方法は、C や Java など既存の多くの言語における関数やブロック
構造、データ構造の表記の一部と同じである。モジュールの単位をこの中括弧で
表すことにより、既存の言語に慣れているユーザにとってモジュールを直感的に
理解することができる。
表記は計算機と人間の間のインターフェースであるので、人間が分かりやすい
表記というのは重要な事であると思う。
膜に名前をつける
しかしそれで十分というわけではない。モジュールを一意に識別するためには、
モジュールとして使う膜になんらかの識別情報を持たせる必要がある。
これを実現するための方法を 2 つ考えた。
案 1 膜直属のアトム name/1 に繋がっている 1 引数アトム名をその膜の名前と
する。現在の言語仕様で説明でき、実処理系でも構文解析系に対する変更が必要
ないが、あまり直感的ではない。
それから、name/1 アトムが複数ある膜は複数個の名前を持ってしまい、膜に
対して名前が一意に定まらないうという性質がある。欠点のようにも思えるが、
ある膜の別名を定義できるエイリアス機能だと思うとまた新たな可能性が見えて
くるかもしれない。
そのようなこともあるので、ひとまずは name/1 が複数あってはならないとい
う制約は設けない事にするが、案 1 では name/1 は膜内に 1 つしかないという前
提があるものとする。
✓
✏
% io という名前の膜
{
name(io),
( fprint(Stream, String) :- ??? )
}
✒
案2
✑
専用の構文を設ける方法である。膜表記の構文を次のように変更する。
膜 ::= [ 名前 : ] ’{’ プロセス ’}’
名前が指定された時、膜はその名前により識別されるものとする。
実処理系の構文解析系に対する変更が必要であるが分かりやすい。また、現在
型体系を研究している理論班からは、膜に名前をつけられるようにしてほしいと
の要望が出てきている。その事情も考慮すると、言語構文を変更するだけの価値
はあると考えた。
4.2. モジュール機能の設計方針
22
案 1 と案 2 は本質的には等価であり、表記が異なるだけである。
したがって、実処理系では案 2 の方法で表記するが、言語コアとしては案 1 の
方法で定義されていると考えることにより、言語仕様の変更は発生しない。
✓
✏
% io という名前の膜
io : {
( fprint(Stream, String) :- ??? )
}
✒
✑
問題点 案 1、案 2 では、ある膜が与えられるとその名前が一意に定まる。しか
し、ある名前が与えられた時に対応する膜が一意に定まるとは限らない。
例えば次のような場合、名前 foo に対応する膜が 2 つ存在する。
✓
✏
% 名前の衝突
foo : { some_atom },
foo : { another_atom }
✒
✑
このような場合、エラーを出すようにする設計も考えられるが、ある名前を持
つ膜が複数あったときに何か面白い挙動を示すかもしれないので、あえてエラー
は出さないようにする。
何か面白い挙動とは、例えば似たような機能をもつモジュールを同じ名前とし
て定義しておくと、確率的にどちらかのモジュールが採用されて実行されるとい
うような事である。
java 言語では、ドメイン名を含む名前で表される名前空間にモジュールを所属
させる方法で衝突を回避している。
4.2.2
モジュールの使用に関する設計方針
図 4.1 に使用例を示す。言語仕様の観点からは、
「モジュールを使う」とは利用
側の膜内にモジュールのルールを読み込んで、勝手に反応させることであるとす
る。たいてい、モジュール内のルール左辺に含まれるアトムを利用側に書くこと
により反応させることになる。
というのは、ある膜内の内容物に反応できるルールは、その膜またはその外側
の膜になければいけないからである。モジュール膜と利用側の膜は親子関係にあ
るとは限らないので、元々の言語仕様では外部の膜であるモジュールのルールを
利用側の内容物に対して反応させることはできない。そこで、モジュールに含ま
れるルールを利用側の膜にコピーすることを着想した。
表記上は「呼ぶ」という能動的な動作に見えるが、意味的には「呼ばれる」と
いう受動的な側面が強い。
4.2. モジュール機能の設計方針
23
<< Module m >>
m.a
m.a :- m.b
(rule N)
(Copy m’s rule to user membrane)
<< Module m >>
m.a
m.a :- m.b
m.a :- m.b
(rule N)
(rule N)
(Apply m.a :- m.b)
<< Module m >>
m.b
m.a :- m.b
m.a :- m.b
(rule N)
(rule N)
図 4.1: モジュールの使用例
4.2. モジュール機能の設計方針
24
ルールをコピーするというと沢山のものがコピーされるかのような印象を受け
るかもしれないが、実際の処理系ではコンパイル済みルール構造への参照が 1 つ
増えるだけなので、問題はない。
以下、モジュールの利用にあたって必要な変更を述べる。
モジュール名つきアトム名
あるアトムがあるモジュールに属することを明示的に表すために、モジュール
名つきアトム名を導入する。
実体は単なるアトムであり、単に モジュール名 ’.’ が従来のアトム名の先頭
に付加されるだけである。
モジュール内のルールを利用側の膜にコピーする
モジュール名つきアトムが生成されるタイミングで、そのモジュール名に対応
するモジュール膜に含まれるルールが膜にコピーされる。
対応する中間命令 LOADMODULE
モジュール名つきアトムが生成されるタイミングはコンパイル時に分かるので、
その時にコピーする中間命令を生成すればいい。その仕様を示す。
名前
引数
LOADMODULE
コピー先の膜を表す番号
モジュール名
表 4.1: LOADMODULE 中間命令仕様
4.2.3
外部アトムへのアクセス
複数のモジュール利用者間で共有されるアトム(以下、共有アトムと呼ぶ)は
モジュール膜に属している必要がある。モジュールの中の実体を利用側で変更し
たり参照したりしたい場合は、利用側とモジュール間で通信する必要がある。通
信するには何らかの方法で両者をリンクで繋げばよい。
利用者側とモジュール膜の特定のアトム間にリンクを張るには、4 つのシステ
ムルールがあれば良い事が分かった。システムルールとは、すべての膜に存在す
る事にするルールである。
4.2. モジュール機能の設計方針
25
{{mv($m),$p,@q},$r,@s}
:- unary($m) | {$r,@s},{mv($m),$p,@q}.
mv アトムがある膜を、1 段階上に持ち上げるルール。
{mv($m1),$p,@q},{name($m2),$r,@s}
:- $m1=$m2 | {name($m2),$p,@q,$r,@s}.
指定した膜が 1 階層下にある場合、mv アトムがある膜の内容を指定した膜に移
動する。
ただし、現在の LMNtal 処理系では特定の名前を持つ膜とのマッチングが出来
ないので、案 1 の name(module_name) を用いた。膜の名前に関するマッチング
が出来るようになったら、name の所をその表記に変えればよい。
{find_and_link($m1,X),ready($m2), $p, @q}
:- $m1=$m2 | {msg(X,$m2), $p, @q}.
指定した名前のアトムとリンクを張るためのルール。これを mv と組み合わせて
使うことで、指定した膜内にあり指定した名前を持つアトムとリンクを張るため
に自ら次々と移動していくエージェントのようなセルとなる。
call($m,$method,X)
:- unary($m),unary($method) | {mv($m).find_and_link($method,X).}.
指定した膜内の指定したスタティック呼び出しを行うためのルール。前述のエー
ジェントを生成する。
一般形
これら 4 つのルールから成るシステムルールセットがあると、外部アトムへの
アクセスは一般的に次のように記述できる。
✓
モジュール定義
✏
module : { name(module).
static.load.
module.static_method(ready).
module.static_method(msg(X))
:- some process..., module.static_method(ready).
}
✒
✓
利用者側
✑
✏
call(module, module.static_method, result)
✒
✑
4.2. モジュール機能の設計方針
26
具体例
図 4.2、4.3、4.4 に具体例でグラフ構造の遷移を示す。この例では、call(m, m.incr,
X) アトムが出現するたびにスタティックな連番を返すモジュール m があり、利
用者側では io.print(X) を用いてその連番を画面に出力している。
その後、同じ遷移をテキスト表記したものも示しておく。
{{{ io.print(call(m, m.incr)) }}}.
m:{ name(m).
id(0). m.incr(ready).
m.incr(msg(X)), id($o) :- int($o),$n=$o+1
| cp($n, C1, C2), id(C1), X=C2, m.incr(ready).
}.
↓ エージェントが生成される
{{{ io.print(X).
{mv(m). find_and_link(m.incr,X).} }}}.
m:{...}.
↓ エージェントが 1 段階上がる(膜から出る)
{{{ io.print(X). }
{mv(m). find_and_link(m.incr,X).} }}.
m:{...}.
↓ エージェントが 1 段階上がる(膜から出る)
{{{ io.print(X). }}
{mv(m). find_and_link(m.incr,X).} }.
m:{...}.
↓ エージェントが 1 段階上がる(膜から出る)
{{{ io.print(X). }}}.
{mv(m). find_and_link(m.incr,X).}.
m:{...}.
↓ 指定された膜が見つかったので、エージェントはその中身を膜に移動し、死ぬ
4.2. モジュール機能の設計方針
27
<< Module m >>
id
0
io.print
m.incr
call
ready
m.incr, ...,
:- cp($n, ....
m
m.incr
Generate agent
<< Module m >>
id
0
m.incr
io.print
find_and_link
ready
m.incr
mv
m
m.incr, ...,
:- cp($n, ....
Agnet go up
<< Module m >>
id
0
io.print
m.incr
find_and_link
ready
m.incr, ...,
:- cp($n, ....
m.incr
mv
図 4.2: スタティックアトムの遷移図
m
4.2. モジュール機能の設計方針
28
Agnet go up
<< Module m >>
id
io.print
0
m.incr
ready
find_and_link
m.incr, ...,
:- cp($n, ....
m.incr
mv
m
Agnet go up
<< Module m >>
id
io.print
0
m.incr
ready
find_and_link
m.incr, ...,
:- cp($n, ....
m.incr
mv
Agnet go down
<< Module m >>
find_and_link
m.incr
io.print
m.incr
id
0
ready
m.incr, ...,
:- cp($n, ....
図 4.3: スタティックアトムの遷移図
m
4.2. モジュール機能の設計方針
29
Find and link
<< Module m >>
msg
m.incr
io.print
id
0
m.incr, ...,
:- cp($n, ....
Apply m’s rule
<< Module m >>
cp
1
id
io.print
m.incr
ready
m.incr, ...,
:- cp($n, ....
Finish
<< Module m >>
1
1
m.incr
id
io.print
ready
m.incr, ...,
:- cp($n, ....
図 4.4: スタティックアトムの遷移図
4.2. モジュール機能の設計方針
30
{{{ io.print(X). }}}.
m:{ name(m).
find_and_link(m.incr, X).
id(0). m.incr(ready).
m.incr(msg(X)), id($o) :- int($o),$n=$o+1
| cp($n, C1, C2), id(C1), X=C2, m.incr(ready).
}.
↓ find_and_link が発動し、指定されたアトムとリンクを張る
{{{ io.print(X). }}}.
m:{ name(m).
id(0). m.incr(msg(X)).
m.incr(msg(X)), id($o) :- int($o),$n=$o+1
| cp($n, C1, C2), id(C1), X=C2, m.incr(ready).
}.
↓ モジュールに定義されているルールが実行される
{{{ io.print(X). }}}.
m:{ name(m).
cp(1, C1, C2). id(C1). X=C2. m.incr(ready).
m.incr(msg(X)), id($o) :- int($o),$n=$o+1
| cp($n, C1, C2), id(C1), X=C2, m.incr(ready).
}.
↓ モジュールに定義されているルールが実行される
{{{ io.print(X). }}}.
m:{ name(m).
id(1). 1(X). m.incr(ready).
m.incr(msg(X)), id($o) :- int($o),$n=$o+1
| cp($n, C1, C2), id(C1), X=C2, m.incr(ready).
}.
これにより io.print と 1 が繋がったので、id(0) が id(1) に変わった事を除け
ばモジュール内は初期状態に戻ったことになる。
次に call(m, m.incr, X) が出現したら、2(X), id(2) になる。これで、連番を生
成することが出来たと言える。
4.2. モジュール機能の設計方針
4.2.4
31
システムルールセットのモジュール化
システムルールセットは、特殊なルールセットである。ルールセットのモジュー
ル化は前述した通り実現できたので、素直に「システムルールセットを特殊なモ
ジュールとして取り扱う」という方法を考えた。
具体的には、モジュール膜内に system_ruleset アトムが存在することでその
モジュールに含まれるルールセットはシステムルールセットである事を表すよう
にした。そのようなアトムを、システムモジュールと呼ぶことにする。
システムモジュールは、ユーザプログラムに対応する膜だけではなく、処理系
のシステムルールセットを保持する部分にも読み込まれる。
処理系内部では、ルール適用する際に必ずシステムルールセットが適用できな
いかをチェックする。これにより、全ての膜に存在することにしている。
32
第 5 章 実装
この章では、実際に作ったコードについて、クラス設計と処理の流れを詳しく説
明する。
5.1
5.1.1
Inline クラス
フィールド詳細
• public static InlineCode inlineCode
インラインクラスが使用可能の時、そのオブジェクトが入る。
• Process cp
コンパイルプロセス。
• public static Map code
インラインコード文字列から一意な連番へのマップ。
• static List defs
インライン宣言コード文字列のリスト。インライン宣言コードはそれぞれ
が識別される必要はないので、Map ではなく List で良い。
• static int codeCount
インラインコード文字列に対応する一意な連番。カウントアップしていく
ことにより一意性を保証する。
5.1.2
メソッド詳細
• public static void initInline()
実行時にインラインを使うための初期化。インラインコードをコンパイル
した結果である MyInline.class をクラスローダにより読み込む。
• public static int getCodeID(String codeStr)
指定したコード文字列を持つコード ID を返す。これはコンパイル時に呼び
出され、実行すべきインラインコードを定数時間で識別するための一意な
整数値を返す。
5.1. Inline クラス
33
• public static void add(String funcName)
パース中にアトムが出てくると呼ばれる。そのアトムがインラインコード
だと判断されると、インライン命令を登録する。登録した後に、整数カウ
ンタを増やす。
• public static void makeCode()
プログラム中に見つかったインラインコードを含む MyInline.java コードを
生成する。コンパイル時に呼ばれる。
• public static void callInline(Atom atom, int codeID)
実際にインライン命令を実行する。実行時に呼ばれ、対応するインライン
アトムへの参照と、インラインコードを定数時間で識別するためのコード
ID が渡される。
5.1.3
アルゴリズム
コンパイルから実行までの流れのうち、インライン機能に関連する部分を説明
する。
構文解析中のアトムの出現
構文解析中にアトムが発見されると、そのアトム名を引数として Inline.add
が呼ばれる。ここで、アトム名が /*inline*/ で始まるなら Map code にコード
と ID を登録する。アトム名が /*inline_define*/ で始まるなら List defs に
コードを登録する。
構文解析直後
構文解析が終わると、全てのインラインアトムに対応するインラインコードが
分かっているので、runtime.Inline.makeCode() を呼び出すことにより実際に
ファイルシステム上に Java ソースコードを生成する。
ルールコンパイル時
ルール右辺でインラインアトムが生成され、一連の構造操作に対応する内部命
令が生成し終わると、インラインコードを実行するための内部命令が生成される。
具体的には、compile.RuleCompiler.addInline メソッドで INLINE 中間命令が
生成される。
5.2. module クラス
✓
34
compile.RuleCompiler.addInline メソッド
✏
/** インライン命令を生成する */
private void addInline() {
Iterator it = rhsatoms.iterator();
while(it.hasNext()) {
Atom atom = (Atom)it.next();
int atomID = rhsatomToPath(atom);
int codeID = Inline.getCodeID(atom.functor.getName());
if(codeID == -1) continue;
body.add( new Instruction
(Instruction.INLINE, atomID, codeID));
}
}
✒
✑
実行時
INLINE 内部命令が実行される。すなわち、MyInlineCode.run が呼ばれ、コン
パイルされたインラインコードが実行される。
✓
✏
INLINE 中間命令の実行部分
case Instruction.INLINE : //[atom, inlineref]
Inline.callInline
( atoms[inst.getIntArg1()], inst.getIntArg2() );
break;
✒
5.2
5.2.1
✑
module クラス
フィールド詳細
• public static String libPath
ライブラリの検索パス。処理系により、このディレクトリからモジュール
が検索される。現在は "../lmntal_lib/" 固定になっているが、近日中に
コマンドライン引数から指定できるようにする予定である。
• public static Map memNameTable
膜の名前から膜そのものへの参照を保持する膜の名表。
5.2. module クラス
5.2.2
35
メソッド詳細
• public static void regMemName(String name, Membrane m)
膜を名表に登録する。
• public static void loadModule(Membrane m, String modName)
指定したモジュールをコンパイルし、膜の名表に登録する。名表がモジュー
ルへの参照を保持しているのでガーベッジコレクタによって清掃されない。
したがって名表に登録するだけで良い。
• public static void resolveModules(Membrane m)
指定した膜について、未定義モジュールを解決する。コンパイルの最後に
呼ばれる。C 言語で言うところの Linker の作業である。
• static void getNeedModules(Membrane m, List need)
指定した膜を再帰的に走査して、未解決モジュールの一覧をリストに入れ
る。need は出力引数である。再帰的に呼ばれるにあたってこの変数が引き
回され、未解決モジュールが追加されていく。
5.2.3
アルゴリズム
コンパイルから実行までの流れのうち、モジュール機能に関連する部分を説明
する。
構文解析時
構文解析中に膜が発見されると、構文解析器は regMemName を呼び出して膜を
名表に登録する。
ルールコンパイル時
コンパイルが終わったら、Module.resolveModules が呼び出され、未解決モ
ジュールのコンパイルが再帰的に行われる。
未解決モジュールとは、モジュール名付きアトム名のうちそのモジュール名に
対応するモジュール膜が存在しないようなモジュールである。コンパイルされた
モジュールの中にも別の未解決モジュールが存在するかもしれないので、膜をコ
ンパイルした後には必ず未解決モジュールの解決が行われる。
前述の通り、LMNtal ではモジュールの使用は受動的に行われるので、未解決
モジュールが結局見つからなかったとしてもルールが適用されないだけなので特
5.2. module クラス
36
に問題はない。したがって、そのような場合には参考までにメッセージを出すが、
エラーとして処理を中断する事はしない。
実行時
LOADMODULE 内部命令が実行される。すなわち、指定された膜に、指定さ
れたモジュール膜に含まれる全てのルールセットをコピーする。
✓
✏
LOADMODULE 中間命令の実行部分
case Instruction.LOADMODULE: //[dstmem, module_name]
// モジュール膜直属のルールセットを全部読み込む
compile.structure.Membrane m = (compile.structure.Membrane)
compile.Module.memNameTable.get(inst.getArg2());
if(m==null) {
Env.e("Undefined module "+inst.getArg2());
} else {
Iterator i = m.rulesets.iterator();
while (i.hasNext()) {
mems[inst.getIntArg1()].loadRuleset
((Ruleset)i.next() );
}
}
break;
✒
✑
37
第 6 章 考察と今後の課題
6.1
6.1.1
実行例 - あみだくじモジュール
amidakuji.lmn - あみだくじモジュールの定義
/*
SYNOPSIS
amidakuji.start_at(start2).
==>
amidakuji.done(goal??).
*/
amidakuji : {
amidakuji.start_at($y),c($s,C,D)
:- $s=$y | you($s,U),c(U,C,D).
you(T1,M1), c(M1,C,D1), c(T2,C,D2)
:- c(T1,C,D1), c(T2,C,M2), you(M2,D2).
c(U,C,M), you(M,$goal)
:- unary($goal) | c(U,C,$goal), amidakuji.done($goal).
}.
解説 2.2 で触れた、あみだくじを実行するプログラムをモジュール化したもの
である。
amidakuji.start_at(スタートする場所) と書くことにより you がスタート
地点に置かれる。すると、グローバルに与えられているあみだ構造に従って順々
に you を動かしていくルールが適用される。末端にたどり着いたらそこがゴール
だとみなし、you を取り除いて amidakuji.done(ゴールした場所) を生成する。
このモジュールはあみだくじのアルゴリズムだけを記述したものなので、予め
あみだくじのデータ構造をグローバルに用意しておく必要がある。
6.2. 実行例 - I/O モジュール
6.1.2
38
あみだくじモジュールを使うプログラム
// Amida data
start1(R10).
c(R10, C1, R11).
c(R11, C3, R12).
goal1(R12).
start2(R20).
c(R20, C1, R21).
c(R21, C2, R22).
c(R22, C3, R23).
goal2(R23).
start3(R30).
c(R30, C2, R31).
goal3(R31).
// Main program
io.input([[This is amidakuji. Where to start today?
Input start1, start2 or start3.]], input_str).
input_str(done(You)) :- amidakuji.start_at(You).
amidakuji.done($goal) :- unary($goal)
| io.print([[You got to ]]), io.print($goal).
解説 このプログラムを実行すると、どこからスタートするかを問い合わせるダ
イアログボックスが開く。start1∼3 のどれかを入力すると、入力した内容を元
にあみだくじモジュールの amidakuji.start_at が呼び出され、予め宣言された
あみだくじの構造に従って you が動いていく。
ゴールにたどり着いたら、たどり着いた場所にあるアトムの名前を標準出力に
印字する。
6.2
6.2.1
実行例 - I/O モジュール
I/O モジュールの定義
io:{
io.print(String) :- unary(String)|[[/*inline*/
System.out.println(me.nth(0));
]](String).
io.input(Message) :- [[/*inline*/
String s = javax.swing.JOptionPane.showInputDialog(null, me.nth(0));
me.changeName(s);
me.nthAtom(0).changeName("done");
]](Message).
io.input(Message, X) :- [[/*inline*/
String s = javax.swing.JOptionPane.showInputDialog(null, me.nth(0));
6.3. 実行例 - map モジュール
39
me.changeName("done");
me.nthAtom(0).changeName(s);
]](Message, X).
io.input :- [[/*inline*/
String s = javax.swing.JOptionPane.showInputDialog(null, "Input text.");
me.changeName(s);
]].
}
解説 入出力を実現するモジュールである。なので、インライン機能によって Java
言語のコードを呼び出している。
io.print は、それに繋がっているアトムが単項アトムの場合にそのアトムの名
前を標準出力に印字する。
io.input は、引数の個数によりいくつかのパターンが存在する。
1 引数の場合は引数で指定されたメッセージを表示して入力を促した後、その
メッセージアトムを done に変え、インライン実行アトム自身を入力された文字
列に変更する。
2 引数の場合はメッセージとハンドルを受け取り、メッセージを表示した後イ
ンライン実行アトム自身を done に変え、メッセージアトムを入力された文字列
に変更する。
LMNtal の言語仕様により、その時点で反応できるルールが適用されていくの
で、自然にオーバーロードの仕組みが実現されていることになる。
6.3
6.3.1
実行例 - map モジュール
map モジュールの定義
// unary map
map : {
map.new($self)
:- unary($self)|{name($self)}.
// override exist key
map.put($self, $k, $v), {name($name), assoc($ok, $ov), $p}
:- unary($k),unary($v),$name=$self,$ok=$k,unary($ov)
| {name($name), assoc($k, $v), $p}.
// add new key
map.put($self, $k, $v), {name($name), $p}
6.4. 実行例 - map モジュールを使うプログラム
40
:- unary($k),unary($v),$name=$self
| {name($name), assoc($k, $v), $p}.
map.get($self, $k, V), {name($name), assoc($mk, $mv), $p}
:- unary($k),$name=$self,$k=$mk,unary($mv)
| V=$mv, {name($name), assoc($mk, $mv), $p}.
}.
解説 key と value を関連付けて保持する入れ物を提供し、必要に応じて追加
(map.put) や key に対応する value の取り出し (map.get) が出来るモジュールで
ある。
利用者は、map.new で新しい map オブジェクトを生成した後、そのオブジェ
クトに対して map.get や map.put を呼び出す事ができる。
オブジェクトの実体は、利用者が map の初期化時に指定した名前を持つ膜で
あり、map.get などに対して第 1 引数にその膜を特定する名前を指定して呼ぶ事
により、特定のオブジェクトに対する操作を実現している。
ここで特筆したい事は、これは正にオブジェクト指向プログラミングが LMNtal
で実現されているということである。
オブジェクトは (bless された) ハッシュであり、メソッド呼び出しはパッケー
ジに属する関数を第 1 引数にそのオブジェクトを指定して呼び出す事により行わ
れるという Perl5 の OOP にとても近いと言える。
Perl5 の OOP 仕様は Java や C++ の OOP 仕様と違って、原理がユーザから
見える形で構成されている。
6.4
6.4.1
実行例 - map モジュールを使うプログラム
map モジュールを使うプログラム
map.new(m1).
map.put(m1, koji, hara).
map.put(m1, koji, imada).
map.put(m1, kojing, harang).
map.put(m1, hong, kong).
map.put(m1, sleepy, [[tired and wanting to sleep]]).
map.put(m1, sleep, [[to be in the state of
rest when your eyes are closed, your body is not active,
and your mind is unconscious]]).
map.get(m1, kojing, io.print).
6.5. 実行例 - semaphore モジュール
41
map.get(m1, hong, io.print).
map.get(m1, sleepy, io.print).
6.4.2
実行結果
$ java runtime.FrontEnd ../sample/map.lmn
harang
tired and wanting to sleep
kong
6.5
6.5.1
実行例 - semaphore モジュール
semaphore モジュールの定義
// Semaphore
sem : { name(sem). static.load.
// public static method interface defs.
sem.new(ready).
sem.up(ready).
sem.new(msg($self))
:- unary($self)
| {sem($self), state(notbusy)}, sem.new(ready).
// up -> state(busy) if state(notbusy)
sem.up(msg($self)), {sem($name), state(notbusy)}
:- $self==$name
| {sem($name), state(busy)}, allowed($self), sem.up(ready).
// down -> state(notbusy)
sem.down(msg($self)), {sem($name), state(busy)}
:- $self==$name
| {sem($name), state(notbusy)}, sem.down(ready).
}.
解説 このモジュールは排他処理で使われるセマフォ機構であり、同一オブジェ
クト間で up 状態になれる利用者が 1 人である事を保証する。本論文により初め
6.6. モジュール機能に関する考察
42
て実現されたスタティックな実体へのアクセス機能を用いて実装されている。
static.load によりスタティックな実体へのアクセスをサポートするためのシ
ステムルールセットが処理系に読み込まれる。
セマフォの利用者は、sem.new により特定の名前で識別されるオブジェクトを
生成する。呼び出し方は map と少し違って、スタティックな実体へのアクセスサ
ポートを利用する call(sem, sem.new, name) により行う。これにより sem モ
ジュールに sem.new メッセージが送られ、モジュール内にオブジェクトが生成さ
れる。
生成されたオブジェクトを使う時にも call を使う。sem.up メッセージが特定
の名前と共に送られると、その名前のオブジェクトの状態が not busy の時に限
り、新しい状態 busy になり、利用者側との間に張られたリンクの先が allowed に
なることで呼び出した利用者がクリティカルセクションの実行権を得られた事を
表す。
6.6
6.6.1
モジュール機能に関する考察
膜に名前をつける方法
module : { ... } の問題点
マッチングの際、膜の名前を指定する方法がない。また、任意の膜の名前にマッ
チして右辺でその名前が使えるような仕組みがない。従って、現状では特定の名
前を持つ膜とのマッチングは { name(module) } により行っている。
膜の名前に関するマッチング機能は今後の課題である。
6.6.2
膜に属性を持たせる方法
システムルールセットのモジュール化に際して、ある特定の膜がシステムルー
ルセットであるという性質を持つという事を記述する必要がある。今後の拡張も
考えると、この他にもいろいろな属性を膜に持たせる必要がある。
現在の所は system_ruleset や name(module) と同様に、膜直下に属性名を持
つアトムを書く事で膜の属性を定義している。しかし、これだと属性でないもの
との衝突の可能性はある。
今後の処理系拡張として、膜の名前付けを階層化すると、属性を記述しておく
膜が専用に作れそうである。例えば、module:{ property:{ atom }. ... } と
書くと module.property.atom として参照できるようになり、属性を表すもので
ないアトムとの衝突が避けられる。
6.6.3
モジュールファイル自動読み込みについて
モジュールの使用宣言が要らず、
「モジュール名. 要素名」と書くだけでモジュー
ルが読み込まれるという仕様は Objective Caml を参考にしたものである。
実際にモジュールを使って LMNtal プログラムを書いてみると、やはりモジュー
ルの使用宣言が不要というのは書きやすいと感じた。
6.6.4
スタティック呼び出しの仕組みについて
言語モデルに即した形で、同一モジュール間で一意な実体へアクセスする方法
が確立でき、実際に処理系として動くことを確認した。
しかし、問題もある。スタティック呼び出しに直接関連したモジュール内のルー
ル実行が終わった後、利用者側のアトムとモジュール内に生成された結果アトム
が複数の膜を隔ててリンクされている状態になるが、現在の処理系が採用してい
る自由リンク管理アトムの仕組みにより、利用者側から見て結果アトムが繋がっ
ているリンク先の構造が型付きプロセス文脈で調べられないという問題がある。
これを解決するには、モジュール内の結果アトムを再度利用者側に引き戻す遷
移が必要である。この機能も、システムルールセットを使えば実現できそうであ
ると考えている。
44
謝辞
本研究を進めるにあたり、御指導を頂きました上田和紀教授に厚く御礼申し上げ
ます。
議論を通じて様々なご意見を頂きました言語班並びに上田研究室の方々に感謝
いたします。特に加藤紀夫氏には、設計方針において多大なご指摘、ご助言を頂
きました。ここに心からの感謝を捧げたいと思います。
また、提出前の追い込み時期には、研究室の愉快な仲間が卒論執筆作業を楽し
くしてくれました。ほんの一部ですが、以下に感謝の意を表します。(順不同)
稲垣氏 私が tgif で図を描くために、席と大画面 LCD を貸してくれました。
森田氏、内藤氏、石井氏 ウィニングイレブンの練習相手を務めてくれました。ナ
イスシュートでした。
石井氏 突然ロードの 4 重奏を流し、心を安らかにさせてくれました。
若槻君 提出 2 週間前に、戸山公園でサッカーの相手をしてくれたり、一緒にさ
くら湯に行きました。
金木氏 歯磨き粉(PC クリーン)を提供してくれました。
石崎君+矢島さん 美味しいご馳走を振舞ってくれました。
増田氏 スパゲッテイを奢ってくれました。
大野君+小川君 深夜番組おおの★ラヂヲ
矢島氏 本論文のテンプレートとなる素晴らしい卒業論文を書いてくれました。
血反吐を吐きながらも研究に燃える著者
2004 年 2 月 原 耕司
45
参考文献
[1] 上田和紀, 加藤紀夫 : Programming with Logical Links, 日本ソフトウェア
科学会第 19 回大会論文集, 2002.
[2] Kazunori Ueda and Norio Kato : Programming with Logical Links, Design
of the LMNtal language, In Proc. 3rd Asian Workshop on Programming
Languages and Systems (APLAS 2002), pp.115-126, 2002.
[3] 関田大吾 : Inside KLIC Version 1.0, KLIC Task Group/AITEC/JIPDEC,
1998.
http://www.klic.org/software/klic/inside/master.html
[4] 矢島伸吾 : LMNtal Runtime の Java による設計と実装, 早稲田大学理工学
部, 卒業論文, 2003.
[5] 金木佑介 : 分散処理系 DKLIC における例外処理の実装, 早稲田大学理工学
部, 卒業論文, 2002.
46
Appendix.A ソースコード
A.1
Inline クラス
package runtime;
import java.io.*;
import java.util.*;
/**
* インラインの方針<BR>
*
* <UL>
* <LI>"/* inline */" で始まるファンクタ名を持つアトムをインラインコードとして扱う。
*
* <LI>"/* inline_define */" で始まるファンクタ名を持つアトムをインライン宣言コード
として扱う。
*
* <LI>インライン宣言コードは、まとめて大域的に宣言される。クラス宣言など、宣言的なも
のはここに書ける。
*
* <LI>あるインラインコードの実行は、
* そのコードが右辺に含まれるルールを適用した直後のタイミングで実行される。
*
* <LI>対応する中間命令は INLINE である。
*
* <LI>リンク先のアトムをいじれるように、すべての NEWATOM, LINK などの操作が終わって
から
* INLINE 命令を発行する。
*
* <LI>中間命令の例
* </UL>
* <PRE>
*
NEWATOM [1, 0, abc_0]
*
... いろいろ
*
INLINE [1, 0]
* </PRE>
*
* @author hara
*/
public class Inline {
/** インラインクラスが使用可能の時、そのオブジェクトが入る。*/
public static InlineCode inlineCode;
/** コンパイルプロセス。 */
A.1. Inline クラス
47
static Process cp;
/** Hash { インラインコード文字列 -> 一意な連番 } */
public static Map code = new HashMap();
/** List インライン宣言コード文字列 */
public static List defs = new ArrayList();
/** 一意な連番 */
static int codeCount = 0;
/**
* インラインを使うための初期化。実行時に呼ぶ。
*
*/
public static void initInline() {
try {
if(cp!=null) {
// コンパイルしてるプロセスのエラー出力を取得。
// これをしないと、エラーがたくさんあるときデッドロックになって止まる!
BufferedReader br = new BufferedReader(new InputStreamReader(cp.getErrorStream()
String el;
while( (el=br.readLine())!=null ) {
System.err.println(el);
}
cp.waitFor();
Env.d("Compile result : "+cp.exitValue());
cp = null;
}
// jar で処理系を起動すると、勝手なファイルからクラスをロードすることがで
きないみたい。
ClassLoader cl = new FileClassLoader();
Object o = cl.loadClass("MyInlineCode").newInstance();
if (o instanceof InlineCode) {
inlineCode = (InlineCode)o;
}
//inlineCode = (InlineCode)Class.forName("MyInlineCode").newInstance();
//Env.d(Class.forName("MyInlineCode").getField("version"));
} catch (Exception e) {
//Env.e("!! catch !! "+e.getMessage()+"\n"+Env.parray(Arrays.asList(e.getStackTrace(
}
if (inlineCode != null) { Env.d("MyInlineCode Loaded"); }
else if (inlineCode == null) { Env.d("Failed in loading MyInlineCode"); }
}
/**
* 指定したファンクタ名を持つコード ID を返す。
* @param name
* @return codeID
*/
public static int getCodeID(String name) {
try {
A.1. Inline クラス
48
return ((Integer)code.get(name)).intValue();
} catch (Exception e) {
return -1;
}
}
/**
* パース中にアトムが出てくると呼ばれる。
* ここで必要に応じてインライン命令を登録する。
* @param atom
*/
public static void add(String src) {
if(src.startsWith("/*inline*/")) {
//if(src.startsWith("a")) {
//登録
Env.d("Register inlineCode : "+src);
code.put(src, new Integer(codeCount++));
} else if(src.startsWith("/*inline_define*/")) {
//登録
Env.d("Register inlineDefineCode : "+src);
defs.add(src);
}
}
/**
* コードを生成する。
*/
public static void makeCode() {
try {
if(code.isEmpty() && defs.isEmpty()) return;
Iterator i;
PrintWriter p = new PrintWriter(new FileOutputStream("MyInlineCode.java"));
Env.d("make inline code "+code);
//p.println("package runtime;");
p.println("import runtime.*;");
i = defs.iterator();
while(i.hasNext()) {
String s = (String)i.next();
p.println(s);
}
p.println("public class MyInlineCode implements InlineCode {");
p.println("\tpublic static String version=\"static string.\";");
p.println("\tpublic void run(Atom me, int codeID) {");
//p.println("\t\tEnv.p(\"-------------------------- \");");
//p.println("\t\tEnv.d(\"Exec Inline \"+me+codeID);");
p.println("\t\tswitch(codeID) {");
i = code.keySet().iterator();
while(i.hasNext()) {
String s = (String)i.next();
int codeID = ((Integer)(code.get(s))).intValue();
p.println("\t\tcase "+codeID+": ");
A.2. Module クラス
49
//p.println("\t\t\t/*"+s.replaceAll("\\*\\/", "* /").replaceAll("\\/\\*", "/ *")
p.println("\t\t\t"+s);
p.println("\t\t\tbreak;");
}
p.println("\t\t}");
p.println("\t}");
p.println("}");
p.close();
// 非同期。別プロセスでコンパイルしながら、現在のプロセスでほかの事をやる。
cp = Runtime.getRuntime().exec("javac -classpath .;lmntal.jar MyInlineCode.java");
} catch (Exception e) {
Env.d("!!! "+e.getMessage()+Arrays.asList(e.getStackTrace()));
}
}
/**
* インライン命令を実行する。
* @param atom 実行すべきアトム名を持つアトム
*/
public static void callInline(Atom atom, int codeID) {
//Env.d(atom+" "+codeID);
if(inlineCode==null) return;
//Env.d("=> call Inline "+atom.getName());
inlineCode.run(atom, codeID);
}
}
A.2
Module クラス
package compile;
import
import
import
import
java.io.BufferedReader;
java.io.FileInputStream;
java.io.InputStreamReader;
java.util.*;
//import runtime.Functor;
import runtime.Env;
import compile.parser.LMNParser;
import compile.structure.*;
/**
* モジュールシステムを実現するクラス。
*
* @author hara
*/
public class Module {
public static String libPath = "../lmntal_lib/";
A.2. Module クラス
50
public static Map memNameTable = new HashMap();
/**
* 膜を名表に登録する。
* @param m
*/
public static void regMemName(String name, Membrane m) {
memNameTable.put(name, m);
}
/**
* 指定したモジュールを指定した膜に読み込む
* @param m
読み込まれる膜
* @param mod_name モジュール名
*/
public static void loadModule(Membrane m, String mod_name) {
String filename = libPath+mod_name+".lmn";
StringBuffer sb = new StringBuffer("Loading Module "+mod_name+" ...");
try {
LMNParser lp = new LMNParser(new BufferedReader(new InputStreamReader(new FileInputS
Membrane nm = RulesetCompiler.runStartWithNull(lp.parse());
//memNameTable がモジュール膜への参照を保持しているので、GC されない。
//m.add(nm);
sb.append(" [ OK ] ");
} catch (Exception e) {
Env.e("!! catch !! "+e.getMessage()+"\n"+Env.parray(Arrays.asList(e.getStackTrace())
sb.append(" [ FAILED ] ");
}
Env.d(sb.toString());
}
/**
* 指定した膜について、未定義モジュールを解決する。
* @param m
*/
public static void resolveModules(Membrane m) {
List need = new ArrayList();
getNeedModules(m, need);
Iterator i = need.iterator();
while(i.hasNext()) {
loadModule(m, (String)i.next());
}
}
/**
* 指定した膜について、必要なモジュール一覧をつくる。
* @param m
* @param need 出力引数。モジュール一覧が入る。
*/
static void getNeedModules(Membrane m, List need) {
Iterator i;
i = m.atoms.listIterator();
while(i.hasNext()) {
A.2. Module クラス
Atom a = (Atom)i.next();
String path = a.getPath();
if(path == null) continue;
if(path.equals(m.name)) continue;
if(!memNameTable.containsKey(path)) {
need.add(path);
}
}
i = m.rules.listIterator();
while(i.hasNext()) {
RuleStructure rs = (RuleStructure)i.next();
getNeedModules(rs.leftMem, need);
getNeedModules(rs.rightMem, need);
}
i = m.mems.listIterator();
while(i.hasNext()) {
getNeedModules((Membrane)i.next(), need);
}
}
51