トークン分割
トークンとは
トークンを説明するに当たって、まずは、C言語に置き換えて考えてみましょう。
#include <stdio.h> void main() { printf("Hello Word!"); return; }
このプログラム自体の説明は、まず1行目が「stdio.h」というファイルを取り込むという意味です。3行目の「main」は、プログラムの最初を表します。5行目の「printf」は、コンソール画面に文字を出力する関数(命令)です。突き詰めるとこんなに簡単な説明ではないのですが、これはC言語の講座ではありませんので詳細は割愛します。
では、これをトークン分割してみましょう。「そもそもトークンとは何か」と疑問を持つ方もいらっしゃるでしょうが、実際に見てから説明したいと思います。
#
include
<
stdio.h
>
void
main
(
)
{
printf
(
"Hello Word!"
)
;
return
;
}
トークンとは、プログラムを構成する最小の単位をいいます。ちなみに、キャプションに示しましたが、これは筆者の想像です。実際のCのコンパイルの時の動きとはちょっと異なると思いますが、とりあえずこの状態を前提に話を進めます。
プログラムを構成する最小の単位は、今まで「printf("Hello Word!");」などの関数(命令)単位だと思っていた方もいらっしゃると思いますが、それだと、次のようなプログラムがきちんとコンパイルできる説明ができなくなります。
//仮に、printfのトークンが「printf("【何でもよい】");」だった場合 1:printf("Hello Word!"); // コンパイルOK 2:char chars[] = "Hello Word!"; printf(chars); // コンパイルNG? 3:printf("Hello" "Word!"); // コンパイルNG?
実際は、すべて正しくコンパイルされます。もしもコンパイラが「printf("【何でもよい】");」の単位で変換していたとしたら、2、3の二つは必然的にエラーを出すでしょう。「printf("*(何でもよい)");」の考え方は、ワイルドカードに近い考え方です。
なでしこからトークン分割を実装する
なでしこからトークン分割を実装するにはとうすればいいのでしょうか。それには、指定した文字列を一つずつ分けて配列として返す「文字列分解」命令を使います(筆者はよく「文字列解体」と間違えてしまいエラーが出ます。似ているので要注意)。なでしこエディタで[新規]を選択し、新たに次のプログラムを実行してみましょう。
「文字列hEべ。」を文字列分解して、反復 対象を言う。

この中で、「反復」と「配列」はどのようなものでしょうか。実行結果を見て、勘の鋭い方は、「反復は配列の要素を一つずつ繰り返し、現在読んでいる文字列を対象という変数に格納している」とお分かりだと思います。ここで、配列と反復について説明をしておきます。この説明が終わったら、いよいよトークン分割です。
配列
配列は複数の要素を一まとまりにして扱う仕組みです。
配列へアクセスするには、次のように記述します。改行が要素の区切りを表し、順番に配列へデータが格納されます。任意の場所へアクセスする方法もありますが、それは、次回以降で後述します。
テスト配列は「文 字 列 h E べ 。」。 テスト配列を表示。
配列名を指定して表示すると、改行区切りで各要素が一度に表示されます。
文 字 列 h E べ 。
文字列分解
「文字列分解」は、指定した文字列を1文字ずつ分解して、順番に配列へ格納します。例えば、次のように記述します。
「あえいうえおあお」を文字列分解して、言う。

反復
「反復」は、指定されたデータに対して1要素ずつ処理を実行します。また、反復で繰り返したい対象は「対象」に格納されます。
この節、冒頭の例では、文字列分解で分解された各文字をメッセージボックスで表示する処理を繰り返しています。
ついにトークン分割
ここで再度、なでしこのファイルを新規作成します。「build.nako」へのトークン分割プログラムの書き込みは後で実行します。
まず、先ほど述べたように、文字列分解を使って次のプログラムを記述します。
「print "Hello Word!"」を文字列分解して、言う。 //なでしこでは配列変数を参照した時、 //1要素ごとにその最後に改行を付けて表示されます

実行結果は、1要素ごとに改行されたものが表示されました。スペースも、立派な一つの要素になります。
ここで、トークン分割として「print」を認識し、「print」を検出したら成功の旨を表示するプログラムを作ってみましょう。
バッファは空。 //「空」は、標準で用意されている何も入っていない変数です 「print "Hello Word!"」を文字列分解して、反復 バッファは「{バッファ}{対象}」。 //バッファの最後に取り出した1文字を加えていきます もし、バッファが「print」ならば、 「printを検出しました」と言う。

だいたい分かるでしょうか。コメントが読み辛いところですが、なでしこエディタに貼り付ければ色分けされるので、そちらで読むとよいでしょう。
なでしこでは、文字列を指定する時、「」でくくります。つまり、「(文字列)」となるわけです。「」の中で変数の内容を埋め込みたい時は、変数名を{}でくくります。
次に、printの引数に指定した内容を表示してみましょう。
バッファは空。 文字列中は、いいえ。 //現在の位置が文字列の中かどうか 最新命令は、空。 //最後に読み込んだ命令 「print "Hello Word!"」を文字列分解して、反復 バッファは「{バッファ}{対象}」。 もし、バッファが「print」ならば、 「printを検出しました」と言う。 最新命令はバッファ。 バッファは空。 //引数を読み込むために、バッファを一度空に 違えば、もし、対象が「"」ならば、 //「"」は一般的な言語では文字列の最初を表す もし、文字列中がはいならば、 文字列中は、いいえ。 もし、最新命令が「print」ならば、 バッファを表示。 バッファは空。 違えば、 文字列中は、はい。
記述が大幅に変わりましたので、一から書きなおしです。面倒だという方はコピーしても結構です。
アルゴリズムはなんとなくお分かりでしょうか。よく読むと今までの経験で分かると思います。それでは、実行してみましょう。予想では「Hello Word!」だけ表示されるはずです。

残念なことに、実行結果は、「"」がついたり、余分な半角スペースが入ったりしてしまいました。どこが悪かったのでしょうか。筆者が一番気になるのは、反復の直後の次の一文です。
バッファは「{バッファ}{対象}」。
なぜなら、文字列分解で、プログラム部分のprintの後に返された文字列がすべて表示されているからです。上のプログラムでは、printが検出された後、常に変数バッファの文字列の最後に現在反復されている文字がつくようになっています。ということで、この一文を最後に持って行きます。
バッファは空。 文字列中は、いいえ。 最新命令は、空。 「print "Hello Word!"」を文字列分解して、反復 もし、バッファが「print」ならば、 「printを検出しました」と言う。 最新命令はバッファ。 バッファは空。 違えば、もし、対象が「"」ならば、 もし、文字列中がはいならば、 文字列中は、いいえ。 もし、最新命令が「print」ならば、 バッファを表示。 バッファは空。 違えば、 文字列中は、はい。 バッファは空。 //文字列の最初についてしまう「"」対策 違えば、 //ここは対象が「"」じゃない時の処理です バッファは「{バッファ}{対象}」。 //上記以外の場合 //printの後の半角スペースとかは //この行から5行上で対処してしまっています
ようやく成功です。予想通りの実行結果を得ることができました。このソースは次回も使いますので、VEFBLフォルダに「tokentest.nako」という名前で保存しておいてください。
まとめ
今回は、ようやく、はじめてなでしこに触った上で、インタプリタ風に、トークン分割を使って「Hello Word!」の表示を実装しました。
次回は「Hello Word!」以外の文字列の表示について検討してみます。また、余裕があればアセンブリ変換も行いたいと思います。


