コマンドの解析と解釈
コマンドラインに非常に複雑なパーサーを持たせる場合もありますが、これはそうした種類のコマンドラインではありません。単純にスペースで文字列を個別の単語に分割します。従って、g command lineというコマンドは、"g"、"command"、および"line"に分割されます。最初の"g"という単語はコマンドと解釈されます。残りの単語はコマンドの引数と解釈されます。これは従来のコマンドラインの構造に従っています。
しかし、g iコマンドは違います。コマンドライン全体はg i command lineです。この場合は、最初の2つの単語、つまり"g"と"i"がコマンドで、残りがコマンドの引数になります。すると、コマンドと引数をどのように区別するのかという疑問が浮かびます。つまり、"i"はg iコマンドの一部であって、"i command line"を検索するための最初の単語ではないことをどのように見分けるのかという疑問です。
コマンドハンドラの定義
各コマンドは関数で実装されています。例えば、gコマンドを定義するには、次のようにgという関数を作成します。
function g() { // Do a Google search... }
これにより、新しいコマンドの定義が非常に容易になります。例えば、Wikipedia検索用のコマンドが必要であれば、検索を行うw()関数を作成するだけで、すぐにw command lineというコマンドを実行できるようになります。
次にg iコマンドを見てみましょう。このコマンドは、次のようにg_iという関数で定義されています。
function g_i() { // Do a Google image search... }
g i command lineというコマンドを入力すると、コードは次の関数が存在するかどうかを確認します。
g() g_i() g_i_command()
g_i_command()が存在しないことが分かるとコードは確認をやめて、その時点でg_i()が目的のコマンドであると判断します。
このような命名規則を使うことで、いくつもの変種を持つコマンドを簡単に定義できるようになります。確かに、イメージ検索コマンドとしてgiを定義することも可能です。しかし、g iをgのサブコマンドと考えれば、今後実装する数々のコマンドを覚えやすくなります(スペースバーが重要な鍵です。スペースバーを活用するキーボードベースのインターフェイスを設計することが、人間工学的に優れた選択となります)。
各コマンド関数は、一種のプラグインになっています。このコマンドラインは、新しいコマンドを非常に簡単に作成できるように設計されています。特別なインターフェイスを持つオブジェクトを作成する必要も、「プラグイン」を特別な名前のファイルに配置する必要も、それをインストールするコードを書く必要もありません。単に関数を定義するだけで完成です。このコードは利便性を念頭に置いて設計されています。
それでは、ハンドラがどのような仕組みで動いているかを見てみましょう。
コマンドハンドラの例
ハンドラの内部には、必要な処理を何でも入れることができます。これはコマンドライン自体の実装とはあまり関係がありません。それでも、簡単なハンドラでどのようなことができるかを見るのは意味のあることです。
Google検索関数は次のようになっています。
function g() { var search_string = arguments_to_array( arguments ).join( " " ); bcl_jump_cgi( "http://www.google.com/search", { "q": search_string } ); }
1行目では引数を受け取り、それらを1つの文字列に連結しています。これはパーサーと逆の処理ですが、コマンドと引数を区別するために、やはり先に解析を行う必要があります。また、すべてのハンドラが単語を1つの文字列に連結するわけではありません。
arguments_to_array()を使用して、argumentsオブジェクトを正規のJavaScript配列に変換する必要があることに注意してください。argumentsオブジェクトは、すべての関数呼び出しのスコープ内で定義され、すべての引数のリストが格納されます。しかし、これは実際には配列オブジェクトではないので、join()メソッドを持っていません。そこで、argumentsを変換して、join()メソッドを持つ配列にしています。arguments_to_array()関数は、コードの別の場所で定義されています。
Google検索ハンドラの2行目では、Google検索URLを作成し、そのURLにアクセスするようにブラウザに指示しています。これにはbcl_jump_cgi()というヘルパー関数が使われています。この関数は、与えられたURLと一連のパラメータを使ってCGI呼び出しを行います。この例では、URLが"http://www.google.com/search"で、単独のパラメータ"q"が検索文字列です。bcl_jump_cgi()の2番目の引数は、CGIパラメータが格納された配列です。
g iイメージ検索コマンドは、Google検索コマンドとよく似ています。
function g_i() { var search_string = arguments_to_array( arguments ).join( " " ); bcl_jump_cgi( "http://www.google.com/images", { "q": search_string } ); }
実際、URLの違いを除けば、コードはまったく同じです。Wikipedia検索も似ています。
function w() { var search_string = arguments_to_array( arguments ).join( " " ); bcl_jump_cgi( "http://en.wikipedia.org/wiki/Special:Search", { "search": search_string } ); }
この検索ではURLが異なるのと、検索文字列パラメータが"q"ではなく"search"になっています。
これらの検索関数はすべて、不確定数の引数を取ります。検索では任意の単語のリストが指定されるからです。しかし警告を表示するal()関数は違います。この関数は次のように1つの引数を取ります。
function al( o )
{
alert( o );
}
引数はパラメータ"o"として渡されます。
CGI呼び出しの実行
bcl_jump_cgi()関数は非常に役に立つので見ておく価値があります。
function bcl_jump_cgi( url, kvs ) { var url = url+"?"; for (var k in kvs) { var v = kvs[k]; url += k+"="+escape( v ); } location = url; }
このコードはURLの末尾にCGIパラメータを追加します。escape()関数を使って各パラメータ値をエスケープし、値をHTTP標準に従ってURLで安全に使用できるようにしています。
関数の最後の行で、疑似変数"location"にURLを代入しています。"location"は、ブラウザにURLへのアクセスを指示します。
