[追記]このエントリーは、Catyスクリプトの最新の仕様には追従していません。ご注意ください。[/追記]
Catyはどんなソフトウェアかというと、「コマンド・インタプリタだ」と言うのが一番適切かな、と思います*1。キーボードから入力するコマンドラインの構文と、それをファイルに収めたスクリプトの構文は同じです。それをプログラミング言語とみなして、Catyスクリプトと呼ぶことにします。
Catyスクリプトは、基本的にコマンド言語なので、通常のシェル(bashとかCMD.EXEとか)のコマンドラインに類似の構文としました。が、そのうち式言語(expression language)の性格が強くなってきて、JSONと似た複合データ表現を入れました。結果的に、なんかミョウチクリンな構文ができたのですが、折衷案/妥協案としてはこんなもんだろう、と思っています。
内容:
スカラーリテラル
Catyスクリプトは、JSONリテラルを含みます。特にスカラーリテラルはトークンの最初の1文字で判断可能です。
- 数字(digit)またはマイナスからはじまるなら数値(number)
- 二重引用符からはじまるなら文字列(string)
- true, false なら真偽値(boolean)
- null はヌル型データ(null)
trueやnullは名前なので、コマンド名のようにも見えます。実は、名前(true, false, null)に限らず、スカラー定数はすべてコマンドと解釈できます。コマンドとは、入力(コマンドライン引数ではありません、OSの標準入力と同様)を受け取って出力(OSの標準出力と同様)を返すものなので、ある種の関数と解釈できます。定数そのものと、引数なしの定数値関数は同一視できます。よって、スカラー定数はコマンドと解釈されるのです。
例えば、true∈Boolean は、true()∈Boolean と考えてもいいのです。引数のない(0引数の)関数trueは、単元集合(singleton set)からの関数ですから、true:Void→Booleanです。ここで、Voidは選ばれた(distinguished)単元集合です。Void = {null} と約束すると、true = true() = true(null) となります。つまり、関数としてのtrueは、値nullを受け取って値trueを返すわけです。
コマンドの入出力仕様は、プロファイルと呼びますが、スカラーリテラルをコマンドとみなしてプロファイルを書いてみると:
- "hello" : null → string
- 100 : null → number
- null : null → null
なんだか分かりにくいかもしれませんね。特に「null : null → null」は意味不明かも。実は、「null」という表記が多義的に使用されているのです。次のように書き分けることにしましょう。
- 値としてのnullを nullvalue
- 型名としてのnullを nulltype
- 関数としてのnullを nullfunction
- 型の領域としてのnullを Null (Null = {nullvalue})
すると、プロファイル「null : null → null」の意味は、
- nullfunction : nulltype → nulltype
「null(null) = null」、「null(null)∈null」の意味は、それぞれ次のようなことです。
また、null() は nullfunction(nullvalue) の略記です。そして、null() と null は同義なのです。
コマンド呼び出し
コマンド呼び出し構文は、通常のOSシェルのそれと同じと思ってください。コマンド呼び出しは、必ずコマンド名からはじまります。そのコマンド名は、名前トークンであるか、またはファイル名です。実行可能ファイル名がコマンド名として使える点はOSシェルと同じです。設定によっては、実行可能でないデータファイル名もコマンド名として使えます(データの“実行”)。
ファイル名の場合、空白や特殊文字が含まれる可能性があるので、クォーティングやエスケープが必要になります。二重引用符は文字列リテラル開始記号に使われているので、シングルクォートが引用符の候補でしょう。ただし、ファイル名構文を制限して、クォーティングなしにできるかもしれないので、現状で詳細は未定です。
コマンド呼び出しを、関数呼び出し形式(例えば、cmd(otp=val, arg1, arg2) )にしようと考えなくもなかったのですが、キーボードから手で打ち込むときの快適さは、クラシカルなコマンド構文(例えば、cmd --opt=val arg1 arg2 )が上だろう、という判断で、関数形式は採用しませんでした。
コマンド呼び出しは、コマンド名に引数が指定された状態を意味します。各コマンド呼び出しは、プロファイル(入出力の仕様)を持ちます。例を挙げると:
- cat : T→T
- cat mypage.html : null→T
ここで、「cat」と「cat mypage.html」がコマンド呼び出しで、それぞれのコマンド呼び出しごとにプロファイルが決まります。プロファイルは型パラメータを含む可能性があります。上の例のTは型パラメータで、任意の型に具体化されます。「cat mypage.html」のプロファイルに出てくるTは、実際には mypage.htmlに依存して決まるので、「任意の型」と言うのは少しおかしいですけどね。正確にTを記述するなら、ファイルシステムを表現するコモナド族を使うことができますが、まー、いいとしましょう。
単一のコマンドを起動する構文について今これ以上は述べませんが、詳細を「コマンド呼び出し」というプレイスホルダ(文法の非終端記号)に押し込めてしまい、以下では、コマンド呼び出しを複合する構文に焦点を当てることにします。以下、構文記述にはBNFを用います。
パイプラインとコマンド式
Catyスクリプトは、コマンド言語と式言語のハイブリッドな性格を持ちます。トップレベルの構文要素(ルート非終端記号)を「パイプライン」、セカンドレベルの構文要素を「コマンド式」とします。うーん、ハイブリッドですなー。
まずは次の定義をしましょう。
単純コマンド式 ::= スカラーリテラル | コマンド呼び出し | '(' パイプライン ')'
単純コマンド式は、コマンド式のビルディングブロックです。スカラーリテラルとコマンド呼び出しは文字通り単純です。また、どんな複雑なパイプラインでも、丸括弧で囲ってしまえば単純コマンド式として扱えます。丸括弧は単に構文的なグルーピング機能で、OSシェルのようにプロセス起動を意味したりはしません。
パイプラインとコマンド式は次のようなものです。
パイプライン ::= コマンド式 ('|' コマンド式)*
コマンド式 ::= 単純コマンド式
| 配列式
| オブジェクト式
| 分岐式
| タグ付き式
タグ付き式(タグ付け演算子)は実験的な仕様です。
区切り記号
話が前後しますが、区切り記号を一覧にしておきます。二重引用符のような、トークンレベルの区切り記号は入れてません。
- '(' と ')' : 任意のグルーピング
- ',' : 各種の区切り
- '[' と ']' : 配列の構成
- '{' と '}' : オブジェクトの構成、分岐の記述
- ':' : オブジェクト内のキー/バリュー区切り
- '>:' : 図式順のときのバリュー/キー区切り
- '=>' : 分岐選択肢の接頭辞/アクション区切り
- '=>>' : => と同じだが、タグを削り落とす
- '|' : パイプ
- '@' : タギング演算子、直後にタグ名を伴う
- '>@' : 図式順タギング演算子 直後にタグ名を伴う
タギング演算子は、@name, >@name をトークンとする案もあります。
配列式
配列データの構築をします。
配列式 ::= '[' 項目並び ']'
項目並び ::= 空 | パイプライン (',' パイプライン)*
オブジェクト式
オブジェクトデータの構築をします。
オブジェクト式 ::= 標準オブジェクト式 | 図式順オブジェクト式標準オブジェクト式 ::= '{' プロパティ並び '}'
プロパティ並び ::= 空 | プロパティ名 ':' パイプライン ( ',' プロパティ名 ':' パイプライン)*図式順オブジェクト式 ::= '{' 図式順プロパティ並び '}'
図式順プロパティ並び ::= 空 | パイプライン '>:' プロパティ名 ( ',' パイプライン '>:' プロパティ名)*
図式順記法を入れたのは、僕(檜山)の好みが影響していますが、パイプラインの流れが「左から右」の方向ですから、全体も「左から右」に書けたほうが混乱が少ないと思うからです。標準オブジェクト式と図式順オブジェクト式は、意味的にはまったく等価です。
分岐式
Catyスクリプトの唯一の制御構造(?)です。
分岐式 ::= '{' 選択肢 (',' 選択肢)* '}'
選択肢 ::= 接頭辞 ('=>' | '=>>') パイプライン
接頭辞は、タグ名に加えて次の予約語が使えます: number, string, boolean, null, array, object。bagも接頭辞に使えますが、これは単なるタグ名に分類されます -- このへんの事情はユーザー定義型に関係します。
タグ付き式
多用しそうなので、タギングを前置演算子、後置演算子として入れてみましたが、コマンドで代用できるので必須ではありません。タギング演算子の優先順順位が高いので、(括弧で囲んでいない)パイプライン全体をタグ付け演算子で修飾することはできません。
標準タグ付き式 ::= '@' タグ名 コマンド式
図式順タグ付き式 ::= コマンド式 '>@' タグ名
その他
- 変数参照が使えます。変数参照が'$'からはじまることは確実*2ですが、詳細はまだです。
- プロパティ名、タグ名、接頭辞として何を許すか? 細かい問題がまだあります。
- できれば入れたくないのですが、値による分岐としてswitch文が必要かもしれません。