SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

MASMとなでしこによるオリジナルコンパイラの制作

MASMとなでしこによるオリジナルコンパイラの制作 2

はじめてのなでしこ

トークン分割

トークンとは

 トークンを説明するに当たって、まずは、C言語に置き換えて考えてみましょう。

C言語でHello Word!
#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」へのトークン分割プログラムの書き込みは後で実行します。

 まず、先ほど述べたように、文字列分解を使って次のプログラムを記述します。

VEFBLでのHello Word!を分解
「print "Hello Word!"」を文字列分解して、言う。
 //なでしこでは配列変数を参照した時、
 //1要素ごとにその最後に改行を付けて表示されます
実行結果
実行結果

 実行結果は、1要素ごとに改行されたものが表示されました。スペースも、立派な一つの要素になります。

 ここで、トークン分割として「print」を認識し、「print」を検出したら成功の旨を表示するプログラムを作ってみましょう。

トークン分割でprint - その1
バッファは空。 //「空」は、標準で用意されている何も入っていない変数です

「print "Hello Word!"」を文字列分解して、反復
 バッファは「{バッファ}{対象}」。
        //バッファの最後に取り出した1文字を加えていきます
 もし、バッファが「print」ならば、
  「printを検出しました」と言う。
実行結果
実行結果

 だいたい分かるでしょうか。コメントが読み辛いところですが、なでしこエディタに貼り付ければ色分けされるので、そちらで読むとよいでしょう。

 なでしこでは、文字列を指定する時、「」でくくります。つまり、「(文字列)」となるわけです。「」の中で変数の内容を埋め込みたい時は、変数名を{}でくくります。

 次に、printの引数に指定した内容を表示してみましょう。

トークン分割でprint - その2
バッファは空。
文字列中は、いいえ。 //現在の位置が文字列の中かどうか
最新命令は、空。   //最後に読み込んだ命令

「print "Hello Word!"」を文字列分解して、反復
 バッファは「{バッファ}{対象}」。
 もし、バッファが「print」ならば、
  「printを検出しました」と言う。
  最新命令はバッファ。
  バッファは空。
   //引数を読み込むために、バッファを一度空に

 違えば、もし、対象が「"」ならば、
  //「"」は一般的な言語では文字列の最初を表す
  もし、文字列中がはいならば、
   文字列中は、いいえ。
   もし、最新命令が「print」ならば、
    バッファを表示。
    バッファは空。
  違えば、
   文字列中は、はい。

 記述が大幅に変わりましたので、一から書きなおしです。面倒だという方はコピーしても結構です。

 アルゴリズムはなんとなくお分かりでしょうか。よく読むと今までの経験で分かると思います。それでは、実行してみましょう。予想では「Hello Word!」だけ表示されるはずです。

実行結果1
実行結果1
実行結果2
実行結果2

 残念なことに、実行結果は、「"」がついたり、余分な半角スペースが入ったりしてしまいました。どこが悪かったのでしょうか。筆者が一番気になるのは、反復の直後の次の一文です。

バッファは「{バッファ}{対象}」。

 なぜなら、文字列分解で、プログラム部分のprintの後に返された文字列がすべて表示されているからです。上のプログラムでは、printが検出された後、常に変数バッファの文字列の最後に現在反復されている文字がつくようになっています。ということで、この一文を最後に持って行きます。

トークン分割でprint - その3
バッファは空。
文字列中は、いいえ。
最新命令は、空。

「print "Hello Word!"」を文字列分解して、反復
 もし、バッファが「print」ならば、
  「printを検出しました」と言う。
  最新命令はバッファ。
  バッファは空。

 違えば、もし、対象が「"」ならば、
  もし、文字列中がはいならば、
   文字列中は、いいえ。
   もし、最新命令が「print」ならば、
    バッファを表示。
    バッファは空。
  違えば、
   文字列中は、はい。
   バッファは空。 //文字列の最初についてしまう「"」対策
 違えば、            //ここは対象が「"」じゃない時の処理です
  バッファは「{バッファ}{対象}」。
   //上記以外の場合
   //printの後の半角スペースとかは
   //この行から5行上で対処してしまっています
実行結果
実行結果

 ようやく成功です。予想通りの実行結果を得ることができました。このソースは次回も使いますので、VEFBLフォルダに「tokentest.nako」という名前で保存しておいてください。

まとめ

 今回は、ようやく、はじめてなでしこに触った上で、インタプリタ風に、トークン分割を使って「Hello Word!」の表示を実装しました。

 次回は「Hello Word!」以外の文字列の表示について検討してみます。また、余裕があればアセンブリ変換も行いたいと思います。

次の記事

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
MASMとなでしこによるオリジナルコンパイラの制作連載記事一覧

もっと読む

この記事の著者

KMY(かみぃ)

とあるごく平凡な中学生。兵庫県在住。日本語プログラミング言語「なでしこ」に惹かれ、たくさんのプログラムを作り利用している。なでしこ以外にも、他の言語を心得ており、HTMLがなでしこの次に得意であり、Perlが第3位(かも)。ベクターにも多彩な作品を残している。作文がど苦手。KMYpage

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/686 2007/02/20 10:23

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー