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;}
}
 のように<>で状態名を挟んで定義します。
 まず状態の変更から見ていきます。
    "/*"    { BEGIN(COMMENT);}
 というのは、"/*"というトークンが現れたら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;}
}
 ここで
    "("     return LP;
 は ( が現れたら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;
    }
 は識別子です。識別子関数名や変数名に使われる文字列です。ここではアルファベットか、_(アンダースコア)ではじまり、アルファベットか、_ か、数字が続くのを識別子としました。
    [ \t]   ;
    .   { return 0;}
 はタブや空白は無視で、それ以外は意味なしをあらわすreturn 0;とします。
 このように定義されたFlexの定義Bisonから次のトークンをくれという要求にしたがって、実行されます。
 次項では構文解析をになうBisonの定義を解説します。