Lex

名称

Lexはレキシカルアナライザジェネレータである。すなわちレキシカルアナライザ（字句解析プログラム、字句解析器）の生成ツールであり、Lexの名称も英語のLexical analysisからきている。

用途

字句解析はテキスト中の文字列の変換、カウント、抽出などさまざまな目的に使われ、その応用領域は、コンパイラやコンバータの作成を筆頭に、自然言語処理や簡単な整形まで幅広い。
このうち、コンパイラにおけるレキシカルアナライザの位置づけを、以下に説明する。
プログラムを中間言語あるいは機械語に変換するコンパイラは、一般的にソースを入力し構文木を出力する構文解析部(1)と、その構文木を入力し中間言語コードまたは機械語を出力するコード生成部(2)からなる。
コンパイラ
　├構文解析部(1)
　│　├字句解析器(1.1)　←〔ソース〕
　│　│呼出し↑↓
　│　│　　　↑〔トークン列〕
　│　│　　　↑↓
　│　└構文解析器(1.2)
　│　　　　　↓
　│　　〔構文木〕
　│　　　　　↓
　└コード生成部(2)　　　→〔中間言語コードまたは機械語〕
このうち(1)の前半は、ソースを入力しトークン（語彙素）列を出力する字句解析器（レキシカルアナライザ、トークナイザ、スキャナ）(1.1)である。
後半は、そのトークン列を入力し、構文規則にしたがって構文解析をし、構文木を出力する構文解析器（パーサ、パーザ）(1.2)である。
(1.1)のレキシカルアナライザを生成するのが、レキシカルアナライザジェネレータである。

機能概要

構文解析器(1.2)に解析を数字や英字や空白などの1文字単位で行わせると、複雜になりすぎる。しかし、人間が英文から英単語や数字などの記号列を、区切り文字（たとえば空白、タブ、改行、コンマ、終止符、カッコ）やその列を目印に抽出して、意味を判断しているのと、同様の発想ができる。すなわち、区切り記号列でソースを切っていくと、「print」のような語、「1999」のような10進数、「"Hello, world"」といった文字リテラル、「++」といった演算子、「}」や「;」など意味のある区切り文字など、各種の文字列が取り出せる。これをトークンという。ここまでの下位の文法処理を上記字句解析器(1.1)に行わせ、一方、構文解析器(1.2)はトークンから出発して句、文、ブロック、プログラムなどを認識する上位の文法処理に専念させる。この分業化により、それぞれの定義と処理を簡潔にできる。
この字句解析器(1.1)の合理的な開発を目的とし、機械可読にした規則定義を与えれば字句解析器を自動生成してくれる便利なツールがレキシカルアナライザジェネレータであり、LexやFlexなどがそれに属する。
〔規則定義〕
　　↓
レキシカルアナライザジェネレータ（Lex, Flexなど）
　　↓
〔字句解析器(1.1)〕
同様に、構文解析器(1.2)の合理的な開発を目的とし、構文規則定義を与えれば構文解析器を自動生成してくれる便利なツールとして、Yacc(Yet Another Compiler Compiler)などのパーサジェネレータ（コンパイラコンパイラ）がある。

構文解析器ジェネレータとの関係

Yaccなどの構文解析器生成ツールを利用するなら、Lexに字句文法定義を与えて生成させたC言語ソースである字句解析器が（Yaccがトークンをユーザから得るための）yylex関数を含んでいるので、CコンパイラでYacc出力と一緒にこれをリンクして組み込む。これにより、ソーステキストの字句解析と構文解析を両方行って、規則のアクション部（あるいはさらにそれに呼ばれるユーザ作成のC言語関数）に書かれた計算の結果や、コンパイルの生成に使われる抽象構文木の構造体データ、あるいは各種表示が出力される変換プログラムが完成する。
YaccとLexはよく似た文法定義をもち、セットで使われ、セットで解説されることが多い。LexとYaccの機能はIEEE POSIX 1003.1-2008（かつては1003.2）で標準化されている。

配布と派生

YaccはほとんどのUNIXシステムで、デフォルトのレキスカルアナライザジェネレータとして利用可能だった。
Lexは多くのシステム、多くのプログラミング言語に移植されている。たとえばJava言語による字句解析器を生成するLex系ツールに、JLex、それを書き直したJFlexがある。このJFlexとJava言語用構文解析器CUPと組みあわせて用いることも行われている。

Lex のファイル構造

Lexのファイル構造は意識的にyaccのそれに似せて定義されている。
ファイルは3つの部分に分割されており、それぞれ定義領域、規則領域、Cコード領域である。各領域はパーセント記号2つ(%%)のみを含んだ行で区切られる。
定義領域は正規表現を用いてマクロを定義するところであり、かつCのヘッダーファイルを取り込む場所でもある。
規則領域は最も重要な領域でありCの命令との関連付けを行う。Lexの規則と一致するパターンがあるとそれに関連付けされたCコードを実行する。
Cコード領域には生成したソースにそのままコピーされるCの命令や関数が含まれている。
これらの命令は規則領域での規則により呼ばれたコードを含む場合もある。大規模なプログラムではここに分割しておきコンパイル時にリンクするほうが便利である。

Flex用のサンプルファイル

次の例はFlex版の入力ファイルのサンプルである。これは入力中の整数値を認識する。
"abc123z.!&*24ghj6"という入力があると次のような表示を行う。
Saw an integer: 123
Saw an integer: 24
Saw an integer: 6
/* 
 * flex用字句解析　例
 *
 * 数値（整数値）を入力を取り出す.
 */

/*** Definition section ***/

%{

/*
 * C コードにはCの標準I/O ライブラリを使うものがある.
 * %{と %} で囲まれた部分はそのまま生成ファイルに * 取り込まれる.
 */
#include <stdio.h>

%}

/* マクロ;  正規表現 */
DIGIT       [0-9]
INTEGER     {DIGIT}+

/* これはflexに入力ファイルが一つであることを示す. */
%option noyywrap

%%
    /*
     * 規則領域     *
     * コメントはインデントしなければならない.
     * そうしないと正規表現と誤認識してしまう.
     */

{INTEGER}   {
                /*
                 * この規則は入力から整数を表示する.
                 * yytextには一致した文字列が含まれる.
                 */
                printf("Saw an integer: %s\n", yytext); 
            }

.           { /* それ以外の文字は無視. */ }

%%
/*** Cコード領域 ***/

/*
 * メインプログラム.
 *
 * 字句解析を呼び出し、処理が済むと終了する. 
 */
int main(void)
{
    /* yyin はlexが読むファイルでここでは標準入力になっている. */
    FILE *yyin = stdin;

    /* 字句解析の呼び出し. */

    yylex();
    return 0;
}