このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

CatyScriptで記述するCatyシェル

CatyScript2.0を企画してますが、実際に使えるようになるのはだいぶ先かな。

プログラミング言語がある程度の表現能力を持っていることの基準として、「自分で自分を記述できる」ことがあります。CatyScript2.0は、Catyのメカニズムを記述できる能力を持たせるつもりです。とはいえ、アルゴリズムの詳細を書くような用途にはまったく不向きです。概略を説明する擬似コードのように使えるが、擬似じゃなくて実際に動く、というのが目標。

CatyScriptを使ってCaty自身を記述するとはどういうことか、Catyの対話的シェルを題材にして実際にやってみます。その過程で、CatyScript2.0の(予定されている)機能も紹介します。

タグと分岐

Catyの扱うデータ構造はJSONをわずかに拡張したもの(XJSON)です。拡張した点はタグが付けられることです。タグはアットマークで始まる名前で、@yen 1200 や @person {"name" : "坂東トン吉", "age" : 25} がその例です。"hello" とか [2, 3] にはタグが付いてませんが、これらは @string "hello"、@array [2, 3] とみなされます。

タグは分岐構文で使われます。型による振り分け(type dispatch)は次のような感じ。


when {
number => <number型データの処理>,
string => <string型データの処理>,
array => <array型データの処理>,
object => <object型データの処理>
}

タグの用途は型の目印だけとは限りません。非常によく使うのは、処理が成功した目印に@OK、失敗したら@NGを付けたデータを返す方法です。


some-command |
when {
OK => <成功時の処理>,
NG => <失敗時の処理>
}

if-then-elseのように使うときは、述語コマンド(predicate command)を使います。述語コマンドは、入力に対して@True, @Falseというタグを付けて出力します。


pred-command |
when {
True => <述語が真のときの処理>,
False => <述語が偽のときの処理>
}

これらの機能は現状で使えます。

例外処理

CatyScript2.0の例外処理の予約語は、世間で一番よく使われているであろう「try」にしようと思います。try {...} というブロック内で発生した例外は捕捉されます。例外には@#exceptという特殊なタグが付けられ、正常終了すれば@#normalという特殊なタグが付けられます。つまり、try式の出力は、#exceptまたは#normalという特殊なシンボルでタグ付けされた値となります。

例外処理は次のように書けます。


try {<例外が起きるかもしれないコード>} |
when {
#except => <例外ハンドリングコード>,
#nromal => <正常処理の続き>
}

色付きの絵で描くと次のよう。


原寸大

繰り返し

繰り返りループは、begin/repeat構文を使います。begin {...} というbeginブロック内でrepeatが実行されると、beginブロックの先頭から実行が繰り返されます。repeatに出会わないなら、beginブロックを抜けます。

begin/repeatを使って、Catyの対話的シェルは次のように書けます。


"Caty interactive shell" | cout; // オープニングバナー
/* トップレベル・ループ */
begin {
"> " | cout --no-nl; // プロンプト、改行しない
cin | eval | xjson:pretty | cout; // read-eval-print
repeat
}

使われているコマンドは:

  1. cout -- 入力をコンソールに(副作用として)書き出す。出力はvoid。
  2. cin -- キーボードから1行入力して、出力とする。
  3. eval -- 入力をCatyScriptとして評価して、値(XJSONデータ)を出力。
  4. xjson:pretty -- XJSONデータを整形したテキストとして出力。

これだと永久に終われないので、文字列が "quit" であるかどうかを判定する述語コマンドを quit-p として、終了できるようにしましょう。


"Caty interactive shell" | cout;
begin {
"> " | cout --no-nl;
cin | quit-p |
when {
True => void, // ブロックを抜けて終了
False => eval | xjson:pretty | cout; repeat
}
}

evalは例外を起こす可能性があるので、次の形で使ったほうがいいでしょう。


try {eval} |
when {
#except => print-error, // エラーを適切に表示するコマンド
#normal => xjson:pretty | cout
}; repeat

スクリプトによるshellコマンドの定義

現状のCatyでは、スクリプトをコマンドとして使うには、どんな短いスクリプトでもファイルにする必要があります。これは不便なので、次の形のコマンド定義もサポートしようかと:


command <名前> <コマンド宣言頭部> {<スクリプトコード>};

例えば、次のようになります。


command eval-print :: string -> void {
try {eval} |
when {
#except => print-error,
#normal => xjson:pretty | cout
}
};

command shell :: void -> void {
"Caty interactive shell" | cout;
begin {
"> " | cout --no-nl;
cin | quit-p |
when {
True => void, // ブロックを抜けて終了
False => eval-print; repeat
}
}
};

Catyシェル内でshellコマンドを実行すると、CatyScriptで書かれたシェルが動き出します(そうなる予定)。