13-05.字句解析(Flexファイルの記述)
この項からは
13-03.ファイル構成の設定と、関数定義で記述した内容について細かく見ていきます。
まず手始めに
字句解析をになう
Flex用のファイル
CLangProject.lから解説します。
字句解析とはなにか
プログラムソースファイルは
テキストファイルです。中には
アスキーコードが並んでいます。コメントや、文字列の中には日本語も使えますが、それ以外は
日本語コードは記述できません。
まず、言語を作成するには
ソースコードをトークンに分割しなければなりません。
トークンというのは文法上意味のある
文字列です。
それは
+ のように
記号であることもあるし
intといった予約語であることもあります。それらに分割します。
その役割を担うのが
Flexです。
Flexの定義ファイルである
CLangProject.lに
トークンを定義します。
まず、冒頭
%{
#include <stdio.h>
#include "CLangProject.tab.h"
#define YY_SKIP_YYWRAP 1
int gLine = 1;
int yywrap(void){ return 1; }
%}
の部分は
お決まりの部分なので、あんまり気にしないでください。
続く
%s COMMENT
%s LINE_COMMENT
は
状態を定義します。
Flexは、解析を行う際
状態というのを持っています。デフォルトは
INITIALという状態です。
INITIAL状態は、ここで定義しなくても使用できます。
では
INITIAL状態から見ていきます。
Flexの解析ブロックは
ではじまり
で終わります。
INITIAL状態は
<INITIAL>{
"(" return LP;
")" return RP;
"{" return LC;
"}" return RC;
";" return SEMICOLON;
"dump" return DMP;
"int" return INT_TYPE;
"/*" { BEGIN(COMMENT);}
[/][/]+ { BEGIN(LINE_COMMENT);}
[1-9][0-9]* {
return INT_LITERAL;
}
[A-Za-z_][A-Za-z_0-9]* {
return IDENTIFIER;
}
"\n" {gLine++;}
[ \t] ;
. { return 0;}
}
のように
<と
>で状態名を挟んで定義します。
まず
状態の変更から見ていきます。
というのは、
"/*"というトークンが現れたら
COMMENTという状態に推移しなさい、という意味です。
/* は
複数行コメントです。
複数行コメントが始まると
COMMENTという状態の定義にのっとって解析します。
<COMMENT>{
"\n" {gLine++;}
"*/" {BEGIN(INITIAL);}
. ;
}
は
改行が来たら、行数変数をインクリメントと
*/ が現れたら、
状態を
INITIALにもどします。それ以外は
のように何もしません(つまり無視します)。
INITIAL状態で
//が現れたら
LINE_COMMENT状態に推移します。その定義は
[/][/]+ { BEGIN(LINE_COMMENT);}
です。
Flexは
正規表現という文法にのっとって、トークンを解析します。
[/][/]+というのは
/ が二つ以上つつくって意味です。
LINE_COMMENT状態の定義は以下になります。
<LINE_COMMENT>{
"\n" { gLine++; BEGIN(INITIAL);}
. ;
}
ここでは、改行が来たときに行数変数をインクリメントして、
状態を
INITIALにもどします。それ以外は何もしません。
それでは
INITIAL状態を詳しく見ていきましょう。重複しますがもう一度記します。
<INITIAL>{
"(" return LP;
")" return RP;
"{" return LC;
"}" return RC;
";" return SEMICOLON;
"dump" return DMP;
"int" return INT_TYPE;
"/*" { BEGIN(COMMENT);}
[/][/]+ { BEGIN(LINE_COMMENT);}
[1-9][0-9]* {
return INT_LITERAL;
}
[A-Za-z_][A-Za-z_0-9]* {
return IDENTIFIER;
}
"\n" {gLine++;}
[ \t] ;
. { return 0;}
}
ここで
は
( が現れたらLPをリターンしなさいという意味です。
何に対してリターンするのかというと、次項に説明する
Bisonに対してです。
ここでは
Flexを先に説明していますが、
Flexの定義というのは、
Bisonの定義から呼ばれます。それで、
トークンに分けたらBisonに返すという形になります。
ここで
LPというのは
Bisonの定義で、
トークンとして命名されている名前です。その命名方法は次項で述べます。
Flex定義ではこのように
LP、RP、LC、RC、SEMICOLONのように、記号に対してトークン分けしてますが、そのほかに
dump、intといった文字列に対してもトークンに分けます。それぞれ
DMP、INT_TYPEという名前を返しています。
dumpというのは、C言語の予約語にはない文字列です。この文字列は
デバッグ出力をするために用意しました。
通常、C言語では
出力は
printf関数を使いますが、
printf関数の定義はもう少し後で行いたいので、気軽に出力できる
dumpという予約語を作成しました。
このほかに
[1-9][0-9]* {
return INT_LITERAL;
}
というのは
10進整数リテラルをあらわします。
1から9で始まり、次からは
0から9の文字列が続くトークンを
10進整数リテラルと定義します。
[A-Za-z_][A-Za-z_0-9]* {
return IDENTIFIER;
}
は
識別子です。
識別子は
関数名や変数名に使われる文字列です。ここでは
アルファベットか、_(アンダースコア)ではじまり、アルファベットか、_ か、数字が続くのを
識別子としました。
は
タブや空白は無視で、それ以外は
意味なしをあらわす
return 0;とします。
このように定義された
Flexの定義は
Bisonから
次のトークンをくれという要求にしたがって、実行されます。
次項では
構文解析をになう
Bisonの定義を解説します。