マニュアルページセク ション 1: ユーザーコマンド

印刷ビューの終了

更新: 2014 年 7 月
 
 

lex(1)

名前

lex - 字句解析プログラムの生成

形式

lex [-cntv] [-e | -w] [-V -Q [y | n]] [file]...

説明

lex ユーティリティーは、文字入力の字句解析に用いる C プログラムを生成します。このプログラムは、yacc へのインタフェースとして使用できます。lex ソースコードから生成された C プログラムは、ISO C 標準に準拠しています。通常 lex ユーティリティーは、生成したプログラムを lex.yy.c ファイルに書き出します。lex の終了ステータスがゼロでない場合、このファイルの状態は不確定です。lex の入力言語に関する詳細は、「拡張機能説明」の項を参照してください。

オプション

サポートしているオプションは、次のとおりです。

–c

C 言語の動作 (デフォルトのオプション) を示します。

–e

EUC 文字を扱えるプログラムを生成します。本オプションは –w と同時に指定することはできません。yytext[ ] の型は unsigned char[ ] となります。

–n

–v によって出力される合計情報の出力を抑止します。lex ソースコード中にテーブルのサイズが指定されず、–v オプションを省略した場合には、この –n が指定されたものと見なされます。

–t

生成したプログラムを、lex.yy.c ファイルではなく標準出力上に書き出します。

–v

lex 統計のサマリーを標準エラー出力に書き出します (「lex の定義」の項にある lex テーブルサイズの説明を参照してください)。lex ソースコード中にテーブルサイズが指定され、–n オプションが指定されなかった場合、–v オプションを有効にできます。

–w

EUC 文字を扱えるプログラムを生成します。本オプションは –e と同時に指定することはできません。–yytext[ ] の型は wchar_t[ ] となります。この点が e 指定と異なります。

–V

標準エラー出力上にバージョン情報を書き出します。

–Q[y|n]

–Qy は、ファイル lex.yy.c にバージョン情報を書き出します。–Qn はバージョン情報を出力しません。デフォルト指定は Qn です。

オペランド

次のオペランドを指定できます。

file

入力ファイルのパス名。このような複数の file を指定すると、すべてのファイルが連結されて 1 つの lex プログラムが生成されます。file オペランドを 1 つも指定しない場合、または file オペランドに を指定した場合には、標準入力が用いられます。

出力

出力

lex の出力ファイルについては下記を参照してください。

Stdout

–t オプションが指定されると、lex の C ソースコード出力のテキストファイルが標準出力に書き出されます。

Stderr

–t オプションが指定されると、lex ソースコード入力の内容に関する情報メッセージ、エラーメッセージ、および警告メッセージが標準エラー出力に書き出されます。

–t オプションが指定されなければ、次のようになります。

  1. lex ソースコード入力の内容に関する情報メッセージ、エラーメッセージ、および警告メッセージが標準出力または標準エラー出力に書き出されます。

  2. –v オプションが指定され、–n オプションが指定されなかった場合は、lex 統計情報も標準エラー出力に書き出されます。–n オプションが指定されていないかぎり、lex定義のセクション (「拡張機能説明」を参照) 内にテーブルサイズが % 演算子で指定された場合も、この統計情報が生成されます。

出力ファイル

C ソースコードが入ったテキストファイルが lex.yy.c に、または –t オプションが指定された場合は標準出力に書き出されます。

詳細説明

各入力ファイルには lex ソースコードが入っています。これは、C プログラムフラグメントの形式で対応するアクションを定義した正規表現のテーブルです。

lex.yy.c がコンパイルされ lex ライブラリにリンクされると (–c89 または ccl l オペランドを使って)、生成されたプログラムは標準入力から文字入力を読み込み、与えられた式と一致する文字列に分割します。

式が一致すると、次の処理が行われます。

  • 一致した入力文字列は、NULL で終わっている文字列として yytext に残されます。yytext は、外部文字配列または文字列へのポインタのどちらかです。lex の定義のセクション で説明するように、%array または %pointer 宣言を使えば型を明示選択できますが、デフォルトは %array です。

  • 照合文字列の長さが外部変数 int yyleng に設定されます。

  • 式に対応するプログラムフラグメントまたはアクションが実行されます。

パターン照合の間、lex はパターンセットを検索し、一致するもっとも長い文字列を見つけ出します。複数のルールが同じ長さの文字列に一致する場合は、最初のルールが選択されます。

lex ソースの一般形式は、次のとおりです。

Definitions
%%
Rules
%%
User Subroutines

最初の %% はルール (正規表現とアクション) の開始を示すために必要なもので、2 番目の %% はユーザーサブルーチンが続く場合にのみ必要なものです。

lexDefinitions で、空白文字で始まる行は、C プログラムフラグメントとみなされ、lex.yy.c ファイルの外部定義域にコピーされます。同様に、%{%} だけの区切り行に囲まれた部分もそのまま、lex.yy.c ファイルの外部定義域にコピーされます。

Rules セクションの始めに、ほかのルールが指定される前にこのような入力 (空白文字で始まる、または %{%} だけの区切り行に囲まれる) がある場合、lex.yy.cyylex 関数の変数宣言のあと、および yylex 内の最初のコード行の前に書き出されます。そのため yylex に入ったときに実行されるアプリケーションコードだけでなく、yylex にローカルなユーザー変数をここで宣言できます。

Rules セクションで、いくつかのルールのあとに空白文字で始まる、または %{%} だけの区切り行に囲まれる入力があったときに lex が実行するアクションは未定義です。このような入力があったとき、yylex 関数の定義にエラーが生じることがあります。

lex 内の定義

lex 定義は最初の %% 区切り行より前にあります。このセクション内で %{%} 区切り行に囲まれていない行で、しかも空白文字以外で始まっている行は、 lex 置換文字列を定義するものとみなされます。これらの行の形式は、次のとおりです。

name   substitute

name が ISO C 標準の識別子の条件を満たしていない場合、結果は未定義です。substitute 文字列は、ルール内で使用されると { name } 文字列を置き換えます。この状況で name 文字列が認識されるのは、中括弧が使用されていて、大括弧や二重引用符で囲まれていないときだけです。

lex 定義セクションでは、% (パーセント記号) で始まりその後ろに s または S で始まる英数字語が続く行は、開始条件セットを定義します。% (パーセント記号) で始まりその後ろに x または X で始まる英数字語が続く行は、排他的な開始条件セットを定義します。生成されたスキャナが %s 状態のときは、状態が指定されていないパターンもアクティブになります。%x 状態では、このようなパターンはアクティブになりません。最初の語を除いた行の残りは、空白文字で区切られた開始条件名とみなされます。開始条件名は、定義名と同じ方法で作成されます。「lex の正規表現」の項で説明するように、開始条件は、正規表現の照合をいくつかの状態に制限するときに使用できます。

lex 定義セクションで、次の排他的な宣言の 2 つのうちどちらかを使用します。

%array

yytext の型を NULL で終わる文字配列と宣言します。

%pointer

yytext の型を NULL で終わる文字配列へのポインタと宣言します。

yytext を変更するために、%pointer オプションと同時に yyless 関数を使用することはできません。

%array はデフォルトです。%array が指定されている (または %array%pointer の 2 つとも指定されていない) 場合に、外部参照先を yyext にするには次のように書式を宣言します。

extern char yytext[ ]

%pointer が指定されている場合、外部参照先の書式は次のとおりです。

extern char *yytext;

lex では、lex 内の定義セクションの特定の内部テーブルサイズの設定を宣言できます。次の表に、宣言を示します。

lex でのテーブルサイズ宣言

宣言
説明
デフォルト
%pn
ポジションの数
2500
%nn
状態の数
500
%a n
遷移の数
2000
%en
解析ツリーのノード数
1000
%kn
パック文字クラスの数
10000
%on
出力配列のサイズ
3000

lex により生成されたプログラムは、補助コードセットの EUC 文字を含む入力データを処理するため、–e または –w のいずれかのオプションの指定を必要とします。この両オプションがともに省略されると、yytext の型は char[ ] となり、生成されたプログラムは ASCII 以外のコードセットを扱うことはできません。

–e オプションを使用すると、yytext の型は unsigned char[ ] となり、yyleng は一致した文字列の合計バイト数を示すことになります。マクロ input()unput(c)、および output(c) は、通常の ASCII lex と同様に、バイトを基準とした I/O を実行しなければなりません。–e オプションで使用できる変数はほかに 2 つあります。それは yywtextyywleng で、それぞれ –w オプション指定時の yytext および yyleng と同じ動きをします。

–w オプションを指定すると、yytext の型は wchar_t[ ] となり、yyleng には一致した文字列の文字数が記録されます。ユーザーがこのオプションを使って独自の input()unput(c)、または output(c) マクロを指定するのであれば、それらはワイド文字 (wchar_t) の形式で EUC 文字を返したり受け取ったりするよう設計しなければなりません。これによって、ユーザーのプログラムと lex 内部との間に別のインタフェースを設け、ほかのプログラムの処理速度を上げることが可能となります。

lex 内のルール

lex ソースファイルルールは、左のカラムには正規表現、右のカラムにはその正規表現が認識されたときに実行されるアクション (C プログラムフラグメント) が入ったテーブルです。

ERE action
ERE action
...

行の拡張正規表現 (ERE) 部分と action は、1 つ以上の空白文字で区切られます。空白文字が入った正規表現は、次の条件の 1 つが満たされる場合に認識されます。

  • 表現全体が二重引用符で囲まれている

  • 二重引用符または大括弧内に空白文字がある

  • 各空白文字の前にバックスラッシュがある

lex 内のユーザーサブルーチン

ユーザーサブルーチンセクション内のものはすべて、lex.yy.cyylex のあとにコピーされます。

lex 内の正規表現

lex ユーティリティーは、regex(5) で記述されている拡張正規表現 (ERE) セットをサポートします。ただし次のように追加された構文と、例外となる構文があります。

 . . .

二重引用符で囲まれた文字列は、二重引用符内の文字そのものを表します。ただし、バックスラッシュエスケープは認識されます (次の表を参照してください)。バックスラッシュエスケープシーケンスは、閉じる引用符で終端されます。たとえば " \ 01""1" は、8 進数の 1 と後続の文字 1 という単一の文字列を表します。

<state>r

<state1, state2,  . . . >r

正規表現 r が照合されるのは、プログラムが statestate1 などによって示される開始条件の 1 つにあるときだけです。詳細については、「lex 内のアクション」の項を参照してください。 (以降本書では、印刷上の規則の例外として、たとえば <state> はメタ変数を表さず、記号を囲むリテラル山括弧文字を表します)。このように開始条件が認識されるのは、正規表現の始めだけです。

r/x

正規表現 r が照合されるのは、その後ろに正規表現 x が続いているときだけです。yytext で戻されるトークンは、r とだけ照合されます。r の末尾部分が x の先頭部分と一致する場合の結果は不確定です。r 式には末尾コンテキストや $ (行の終わり) 演算子は含めることができません。x には &; (行の始まり) 演算子、末尾コンテキスト、$ 演算子は含めることができません。つまり lex 正規表現には 1 つの末尾コンテキストしか含めず、^ 演算子が使用できるのはこのような式の始めだけです。末尾コンテキスト演算子 / (スラッシュ) は、括弧でグループ化できないよう制限されています。

{name}

nameDefinitions セクションからの置換記号の 1 つである場合、左右の中括弧も含めた文字列が substitute 値で置き換えられます。拡張正規表現内では、substitute 値は括弧で囲まれているものとして扱われます。{name} が大括弧や二重引用符で囲まれている場合、置換は行われません。

ERE 内では、バックスラッシュ文字 ( \\, \ a, \ b, \ f, \ n, \ r, \ t, \ v) をエスケープシーケンスの始まりとみなします。さらに次の表のエスケープシーケンスが認識されます。

リテラル復帰改行文字 (NEWLINE) は、ERE 内ではありえません。復帰改行文字を表現するには、エスケープシーケンス \ n が使用されます。復帰改行文字は、ピリオド演算子とは照合できません。

lex 内のエスケープシーケンス

lex 内のエスケープシーケンス
エスケープシーケンス
説明
意味
\ digits
バックスラッシュ文字とそのあとの 1、2、または 3 桁の 8 進数の最長シーケンス (01234567)。すべての桁が 0 なら (つまり NULL 文字なら)、動作は未定義
エンコーディングが 1、2、または 3 桁の 8 進数で表現される文字。複数バイト文字は、この種のエスケープシーケンスがいくつか連続したものを必要とします。そのとき、各バイトの先頭に \ が必要です。
\ xdigits
バックスラッシュ文字と、そのあとに続く 16 進文字の最長シーケンス (01234567abcdefABCDEF)。すべての桁が 0 (つまり NULL 文字) の場合、動作は未定義です。
エンコーディングが 16 進整数で表現される文字。
\ c
バックスラッシュ文字と、そのあとに続くこの表に記載されていない文字。( \\, \ a,\ b, \ f,\e n, \ r,\ t, \ v)
文字 c、変更なし。

lex 用の拡張正規表現の優先順位は、下の表に示すとおりです。優先度の高い順に並んでいます。

エスケープ文字エントリは、これらが演算子であることを意味しませんが、真の演算子との関係を示すため、表に組み込まれています。この項で説明した配置方法の制限のため、開始条件、末尾コンテキスト、およびアンカー指定は省略されています。これらは ERE の先頭または末尾にだけ指定できます。

lex 内での ERE 優先順位
照合関連の括弧記号
[= =] [: :] [. .]
エスケープ文字
\<special character>
大括弧表現
[ ]
引用
". . ."
グループ化
()
定義
{name}
単一文字 RE 重複
* + ?
連結
インターバル表現
{m,n}
択一
|

ERE アンカー演算子 ( ^$ ) は、表にはありません。lex 正規表現では、これらの演算子の使用が次のように制限されています。^ 演算子は正規表現の始めにしか使用できず、$ 演算子は最後にしか使用できません。演算子は正規表現全体に適用されます。そのためたとえば、(^abc)|(def$) などのパターンは未定義です。これは 2 つの別々のルールとして書くことができます。正規表現 ^abc と正規表現 def$ です。この 2 つは、特殊な | アクション (下記を参照のこと) を介して共通なアクションを共有します。パターンが ^abc|def$ と書かれたら、abc または def だけの行と一致します。

一般の ERE ルールと異なり、従来のほとんどの lex の実装では組み込み型アンカーは許可されていません。組み込み型アンカーの例としては、foo が完全な語として存在するときに foo と照合させるための (^)foo($) などのパターン用があります。これは、次の既存の lex 機能を使えば可能です。

^foo/[ \ n]|
" foo"/[ \ n]    /* found foo as a separate word */

なお $ も末尾コンテキストの形式であり (/\ n と等価)、その演算子の別のインスタンスを含む正規表現では 使用できません (上の末尾コンテキストの説明を参照してください) 。

追加の正規表現末尾コンテキスト演算子 / (スラッシュ) は、二重引用符で囲む (" / ")、前にバックスラッシュを付ける (\ /)、または大括弧で囲めば ([ / ])、通常の文字として使用できます。開始条件 <> 演算子は、正規表現の始めの開始条件においてだけ特別とみなされます。正規表現のその他の場所では、通常の文字として扱われます。

次の例を見れば、lex 正規表現と本書に出てくるその他の正規表現との違いがわかります。r/x 形式の正規表現の場合、r と一致する文字列が常に返されます。x の先頭が r の末尾部分と一致する場合に混乱が生じることがあります。たとえば、正規表現 a*b/cc と入力 aaabcc が与えられると、yytext にはこの照合結果として aaab が入ります。しかし正規表現 x*/xy と入力 xxxy が与えられると、xxx は x* に一致するため、xx ではなく xxx が返されます。

ルール ab*/bc では、r の最後の b* が末尾コンテキストの先頭まで一致するため、結果は不確定です。ただしこのルールが ab/bc なら、テキスト ab にテキスト bc が続く場合に一致します。この場合 rx の先頭の b までに一致しないので、結果は確定的です。

lex 内のアクション

ERE が一致したときに実行されるアクションは、C プログラムフラグメントまたは次に説明する特殊なアクションです。プログラムフラグメントには 1 つ以上の C ステートメントと特殊なアクションが入っています。空の C ステートメント ; は、有効なアクションです。このようなルールのパターン部分に一致する lex.yy.c 入力内の文字列は、実際には無視またはスキップされます。ただしアクションの不在は無効であり、このような状況で lex が実行するアクションは未定義です。

C ステートメントと特殊アクションも含め、アクションについての仕様は、 中括弧で囲まれていれば複数行にわたることができます。

ERE <one or more blanks> { program statement
program statement }

lex.yy.c プログラムへの入力内の文字列がどの表現とも一致しないときは、デフォルトアクションとして、文字列が出力にコピーされます。lex によって生成されるプログラムのデフォルトの動作は、入力を読み込んで出力へコピーするだけなので、 %% だけの最小 lex ソースプログラムは、入力をそのまま出力へコピーするだけの C プログラムを生成します。

4 つの特殊アクションが使用可能です。

|       ECHO;      REJECT;      BEGIN
|

アクション | は、次のルールのアクションがこのルールのアクションであることを意味します。ほかの 3 つのアクションとは異なり、| は中括弧で囲んだりセミコロンで終わることができません。それは、ほかの動作なしで、単独で指定する必要があります。

ECHO;

文字列 yytext の内容を出力に書き出します。

REJECT;

1 つの式だけを入力内の文字列と一致させるのが普通です。REJECT は「現在の入力と一致する次の式まで継続する」という意味で、現在のルールが実行されたあと、2 番目のルールが何であれ同じ入力に対して実行されます。そのため、1 つの入力文字列または重なる入力文字列に対して複数のルールを一致させて実行できます。たとえば、正規表現 xyz 、正規表現 xy、および入力 xyz が与えられると、通常は正規表現 xyz だけが一致します。次の照合の試行は z のあとから開始されます。xyz ルールの最後のアクションが REJECT なら、このルールと xy ルールの両方が実行されますyylex の別の部分への goto と同じように、制御の流れが継続しないような方法で、REJECT アクションを実装できます。REJECT を使用すると、スキャナはある程度大きくなり、実行が遅くなります。

BEGIN

アクション

BEGIN newstate;

は、状態 (開始条件) を newstate に切り替えます。文字列 newstatelex 定義セクションに開始条件として宣言されていないと、結果は不確定です。初期状態は、数字の 0 または INITIAL トークンで示されます。

次に説明する関数やマクロは、lex 入力内のユーザーコードをアクセスできます。lex の C コード出力に示されるかどうか、および c89cc への –l l オペランドを通してだけアクセス可能かどうかは不確定です (lex ライブラリ)。

int yylex(void)

入力の字句を解析します。これは lex ユーティリティーによって生成される主な関数です。この関数は、入力の終わりに達するとゼロを返します。その他の場合は、選択されたアクションによって決定されたゼロ以外の値 (トークン) を返します。

int yymore(void)

呼び出されると、次の入力文字列がいつ認識されるかを示します。置き換えるのではなく、yytext の現在の値に付加されます。これに合わせて yyleng の値が調整されます。

intyyless(int n)

NULL で終わっている yytext 内の最初の n 文字を記憶し、残りの文字は読み取っていないものとします。これに合わせて yyleng の値が調整されます。

int input(void)

入力から次の文字を返します。ファイルが終わりのときはゼロを返します。ストリームポインタ yyin から、おそらく中間バッファーを介して入力を得ます。そのためスキャニングの開始後に yyin の値を変更した場合の影響は未定義です。読み込まれた文字列は、スキャナの入力ストリームからそのまま取り除かれます。

int unput(int c)

文字 c を入力に返します。次の式が一致するまで yytextyyleng は未定義です。入力された文字より多い文字に対して unput を使用すると、結果は不確定です。

次の関数は –l l オペランドを通してアクセス可能な lex ライブラリ内にだけ出てきます。そのためこれらの関数は移植性のあるアプリケーションによって再定義可能です。

int yywrap(void)

ファイルの終わりで yylex によって呼び出されます。デフォルトの yywrap は常に 1 を返します。アプリケーションが yylex に別の入力ソースで処理を継続させたい場合、アプリケーションは関数 yywrap を組み込めます。これは別のファイルと外部変数 FILE *yyin を関連付け、ゼロの値を返します。

int main(int argc, char *argv[ ])

字句解析のために yylex を呼び出してから、終了します。ユーザーコードにはアプリケーションに固有な動作を実行する main を組み込んだり、必要なら yylex を呼び出したりできます。

移植性のあるアプリケーションによって確実に再定義可能なのは libl.a の関数だけだという理由によって、前述の関数は 2 つのグループに分割されています。

lex によって生成される名前で、inputunputmain を除くすべての外部およびスタティック名には、接頭辞 yy または YY が付きます。

使用法

lex 内のルールセクションではアクションのない ERE は受け入れられないことが、移植性のあるアプリケーションに警告されますが、lex がエラーとして検出する必要はありません。これはコンパイルまたは実行時エラーを引き起こすことがあります。

input の目的は、字句分析に関して、入力ストリームから文字を 取り出し破棄することです。コメントの先頭を検出したら、コメント全体を破棄するという使い方が一般的です。

lex ユーティリティーは、lex ソースコードや生成された字句アナライザにおける正規表現の扱いが完全には国際化されていません。字句アナライザの実行時に、指定された環境に応じて lex ソース内の正規表現を解析することが理想とされますが、現在の lex テクノロジではこれは不可能です。さらに lex によって生成される字句アナライザの特徴は、ロケール固有なことが多い入力言語の字句要件に密接に結びついている必要があります。(たとえば、フランス語に使用するアナライザを作成しても、自動的にその他の言語の処理に役立つことはありません)。

使用例 1 lex の使用

次の例は、Pascal に似た構文用の簡単なスキャナを実装する lex プログラムです。


%{
/* need this for the call to atof() below */
#include <math.h>
/* need this for printf(), fopen() and stdin below */
#include <stdio.h>
%}

DIGIT    [0-9]
ID       [a-z][a-z0-9]*
%%

{DIGIT}+	{
                       printf("An integer: %s (%d)\n", yytext,
                       atoi(yytext));
                       }

{DIGIT}+"."{DIGIT}*    {
                       printf("A float: %s (%g)\n", yytext,
                       atof(yytext));
                       }

if|then|begin|end|procedure|function        {
                       printf("A keyword: %s\n", yytext);
                       }

{ID}                   printf("An identifier: %s\n", yytext);

"+"|"-"|"*"|"/"        printf("An operator: %s\n", yytext);

"{"[^}\n]*"}"         /* eat up one-line comments */

[ \t\n]+               /* eat up white space */

.                      printf("Unrecognized character: %s\n", yytext);

%%

int main(int argc, char *argv[ ])
{
                      ++argv, --argc;  /* skip over program name */
                      if (argc > 0)
		                  yyin = fopen(argv[0], "r");
                      else
                      yyin = stdin;
	
                      yylex();
}

環境変数

lex の実行に影響を与える次の環境変数についての詳細は、environ(5) を参照してください。LANG、LC_ALL 、LC_COLLATE、LC_CTYPE、LC_MESSAGES、および NLSPATH。

終了ステータス

次の終了ステータスが返されます。

0

正常終了。

>0

エラーが発生した。

属性

属性についての詳細は、マニュアルページの attributes(5) を参照してください。

属性タイプ
属性値
使用条件
developer/base-developer-utilities
インタフェースの安定性
確実
標準
standards(5) を参照してください。

関連項目

yacc(1), attributes(5), environ(5), regex(5), standards(5)

 .l ファイルの yyback()yywrap()yylock() などのルーチンが外部 C 関数となる場合には、C++ プログラムをコンパイルするコマンド行で __EXTERN_C__ マクロを定義する必要があります。例:

example%  CC –D__EXTERN_C__ ... file