OONCP(オブジェクト指向NScriptコンパイラ by Perl)

NScripterオブジェクト指向プログラミングを可能にする方法を考えた。
……オリジナルのオブジェクト指向言語を作って、それNScriptに変換するのと同じくらいの手間じゃないのか? orz

NScripterの変数の扱いはかなり特殊で、変数名は全て番号になっている。
numalias命令を使って文字列と数値の対応表を自作することで、スクリプトの可読性を上げている。
変数の型は三種類あり、

  • 数値変数。%で始まる。
  • 文字列変数。$で始まる。
  • 配列変数。define節での定義が必須。?で始まる。

となり、%0と$0と?0[n]は同じ0番の変数ではあるが、内容物は区別される。
この特徴を使うことで、オブジェクトの基礎としたい。
番号を一つ指定してオブジェクトとする。その番号の数値・文字列・配列変数をオブジェクトが持つメンバー変数と規定し、外部からアクセスしないようにする。
こうすることで、オブジェクトのカプセル化を行う。
そして指定した番号をnumaliasで文字列化し、それをもって(擬似的な)オブジェクトとみなす。
クラスは、別途クラス定義書を作成する。同時にこれは、スクリプトが始まった時点でいくつのインスタンスを作成するかも記述していなければならない。numaliasを使う必要性があるため、start節が始まった時点でインスタンスの生成は終わっていなければならないからだ。
各クラス定義書で記述されたメソッドは、NScript内ではサブルーチンとして定義されるため、スクリプト中のどこからでも参照できるグローバルなものである。これをプライベートなものにするため、メソッドの第一引数に必ずオブジェクト名を要求し、オブジェクト名からクラス名を確認、クラス名から可能なメソッドを確認するフェーズをはさみ、不正なメソッド起動を検出してエラーストップを出すようにする。
クラス定義書の書式には継承を含める。ただし、単純継承のみ。

このように定義されたクラスから発生したインスタンスをNScript内で扱うためには、(オブジェクト指向の文法からは少々外れるけれども)method objectalias[,other num]と言うように扱う。
正直な予想をするならば、スクリプト言語においてこのような冗長な記述を行うと、てきめんに実行速度に影響しそうだが、NScripter本来の使用目的であるノベルゲームにはそれほど重要な要素ではないと切り捨てることにする。

サンプルメソッド(二つの数値引数を与えると、その合計を返す(=%class_method_returnに結果を代入する)メソッド)
*class_method
mov %class_method_error,0 ; エラーコードの初期化
getparam %class_method_this,%class_method_num1,%class_method_num2 ; 引数の取得
if %class_method_this >= class_lower and %class_method_this =< class_higher goto *class_method_main ; 第一引数に与えられたオブジェクト番号が、そのメソッドに正当なものであれば、そのメソッドのメインルーチンへ行く。
; このオブジェクト正当性チェックルーチンは、クラスが継承される度に増える。
mov %class_method_error,1 ;正しくないメソッド呼び出しであったことの証拠。
mov %class_method_return,0 ; 結果が無効であったことの証拠。
return
*class_method_main
mov %class_method_return,%class_method_num1
add %class_method_return,%class_method_num2
return

変数のローカライズができないのがNScripterの難点であるが、メソッドごとに変数を用意することで一応の解決策とする。非常に変数領域を圧迫するので、ローカル変数とグローバル変数の境界は大幅に変更する必要がある。それでも、同じメソッドを(同じスレッドで)何度も実行すると「何が起こるかわからない」ので、回帰的なメソッド呼び出しができない。これは将来に投げたい。
上記のサンプルメソッドを見ればわかるが、実際のサブルーチンでは、classの部分にクラス名が入り、methodの部分にはメソッド名が入る。
一つのメソッドには、最低でthis, error, returnの変数が定義され、それ以上の変数はそれぞれのメソッドで定義される。
なお上記のNScriptは、下記のクラス定義書をコンパイラに読み込ませることで(define節での大量のnumalias, defsubとともに)生成される。

class(クラス名)
method (num1, num2) {(使う変数名は必ずここで定義しなければならない)
mov return,num1 (実際のNScriptを記述する。ただし、変数名は大幅に省略できる)
add return,num2
(return変数が暗黙的に返されると考えて欲しい)
}

また、グローバル変数領域を使うことでオブジェクトを永続化できる。ただし、それでもオブジェクトの初期化メソッドは作るべきだろう。クラス定義書を書き換えれば、オブジェクトのマッピングが変わる可能性があるからだ。
また、グローバル変数を使うため、このオブジェクトでは配列変数を使用することができない(保存できない)。


※2005/04/22 Fri 11:07:57 に再編集されました。