用途
生成一個與輸入流的簡單語法分析相匹配的 C 或 C++ 語言程序。
語法
lex [ -C ] [ -t ] [ -v| -n ] [ File... ]
描述
lex 命令讀取 File 或標(biāo)準(zhǔn)輸入,生成 C 語言程序并將它寫到一個名為 lex.yy.c 的文件中。這個文件,lex.yy.c ,是一個兼容的 C 語言的程序。一個 C++ 編譯器也能夠編譯 lex 命令的輸出。-C 標(biāo)志將輸出文件重命名為 lex.yy.C 供 C++ 編譯器使用。
由 lex 命令生成的 C++ 程序可使用 STDIO 或 IOSTREAMS。如果在 C++ 編譯中,cpp 定義 _CPP_IOSTREAMS 是真,程序為所有 I/O 使用 IOSTREAMS。否則,使用 STDIO。
lex 命令使用包含在 File 中的規(guī)則和操作來生成一個程序,lex.yy.c,這個程序可用 cc 命令編譯。這個編譯過的 lex.yy.c 然后能接受輸入,將輸入分成為由在 File 文件中的規(guī)則定義的邏輯片,并運行包含在 File 文件中的操作的程序片斷。
這個生成的程序是一個稱為 yylex 的 C 語言函數(shù)。lex 命令將 yylex 函數(shù)存儲在一個名為 lex.yy.c 的文件中。可單獨用 yylex 函數(shù)來識別簡單的一個單詞的輸入,或能用它和其他 C 語言程序一起來執(zhí)行更困難的輸入分析函數(shù)。例如,您能用 lex 命令來生成一個程序。這個程序能在將輸入流發(fā)送到一個由 yacc 命令生成的解析器程序之前簡化輸入流。
yylex 函數(shù)用稱為有限自動機的程序結(jié)構(gòu)來分析輸入流。這個結(jié)構(gòu)在一個時間允許程序僅在一個狀態(tài)(或條件)下退出。允許有有限個數(shù)目的狀態(tài)。在 File 中的規(guī)則確定程序怎樣從一個狀態(tài)移動到另一個狀態(tài)。
如果不指定一個 File,lex 命令讀取標(biāo)準(zhǔn)輸入。它將多個文件作為一個單個的文件對待。
注: 由于 lex 命令為中間和輸出文件使用固定的名稱,您可僅有一個由 lex 在給定目錄中生成的程序。
lex 規(guī)范文件
輸入文件文件包含三部分:定義、規(guī)則和用戶子例程。每部分必須用僅含定界符 %%(雙百分號)的行和其他部分分開。格式是:
下面描述了各自的用途和格式。
定義
如果想在您的規(guī)則中應(yīng)用變量,必須在這個部分定義它們。變量組成左邊的列,它們的定義組成右邊的列。例如,如果想定義 D 作為數(shù)字,應(yīng)該這樣寫:
D [0-9]
您可用一個在 {} (大括號)內(nèi)圍住變量名的規(guī)則部分定義的變量。
{D}
在以空格開頭或由 %{, %} 定界符行中括住的定義部分中的行被復(fù)制到 lex.yy.c 文件。能用這個構(gòu)造聲明 C 語言變量用在 lex 操作或包含頭文件,例如:
%{
#include <math.h>
int count;
%}
這些行也可出現(xiàn)在規(guī)則部分的開頭部分,僅在第一個 %% 定界符之后,但它們不應(yīng)當(dāng)用在規(guī)則部分的其他地方。如果這行在 File 的定義部分,lex 命令將它復(fù)制到 lex.yy.c 文件的外部聲明部分。如果這行出現(xiàn)在規(guī)則部分,在第一個規(guī)則前,lex 命令將它復(fù)制到 lex.yy.c 文件的 yylex 子例程的本地聲明部分。那些行不能在第一個規(guī)則后出現(xiàn)。
lex 外部的類型,yytext,能通過在定義部分指定以下之一來設(shè)置為以空結(jié)束的字符數(shù)組(缺?。┗蛘呤且钥战Y(jié)束字符串的指針:
%array (缺省) %pointer
在定義部分,可為生成的有限狀態(tài)機設(shè)置表的大小。缺省大小對小程序足夠大??赡芟霝楦鼜?fù)雜的程序設(shè)置更大的大小。
%an |
轉(zhuǎn)變數(shù)是 n(缺省 5000) |
%en |
語法分析樹節(jié)點數(shù)是 n(缺省 2000) |
%hn |
多字節(jié)字符輸出槽數(shù)(缺省 0) |
%kn |
壓縮字符類數(shù)(缺省 1000) |
%mn |
多字節(jié)字符類輸出槽數(shù)(缺省 0) |
%nn |
狀態(tài)數(shù)是 n(缺省 2500) |
%on |
輸出槽數(shù)(缺省 5000,最小 257) |
%pn |
位置數(shù)是 n(缺省 5000) |
%vp |
在由 %h 和 %m 控制的散列表中的空槽百分比(缺省 20,范圍 0 <= P < 100) |
%zn |
多字節(jié)字符類輸出槽數(shù)(缺省 0) |
如果多字節(jié)字符出現(xiàn)在擴展的正則表達式字符串中,可能需要用 %o 參數(shù)復(fù)位輸出數(shù)組大?。赡艿臄?shù)組大小在 10,000 到 20,000 的范圍內(nèi))。這個復(fù)位反映相對于單字節(jié)字符數(shù)大得多的字符數(shù)。
如果多字節(jié)字符出現(xiàn)在一個擴展的正則表達式中,必須用 %h 和 %m 參數(shù)設(shè)置多字節(jié)散列表大小為一個比包含在 lex 文件中的多字節(jié)字符總數(shù)更大的大小。
如果沒有多字節(jié)字符出現(xiàn)在擴展的規(guī)則表達式中,但是您想 '.' 來匹配多字節(jié)字符,必須設(shè)置 %z 大于零。類似的,對逆字符類(例如,[^abc])來匹配多字節(jié)字符,必須設(shè)置 %h 和 %m 大于零。
當(dāng)用多字節(jié)字符時,lex.yy.c 文件必須用 -qmbcs 編譯選項來編譯。
規(guī)則
一旦定義了條件,就可寫規(guī)則部分。它包含由 yylex 子例程來匹配的字符串和表達式,和當(dāng)匹配時要執(zhí)行的 C 命令。需要這一部分,這一部分必須由定界符 %%(雙百分號)開頭,不論是否有一個定義部分。lex 命令不識別沒有定界符的規(guī)則。
在這個部分,左邊列包含擴展正則表達式形式的模式。這些表達式可由在到 yylex 子例程的輸入文件中被識別。右邊的列包含一個當(dāng)這個模式被識別時執(zhí)行的 C 程序段,稱為一個操作。
當(dāng)詞法分析程序發(fā)現(xiàn)一個擴展的正則表達式的匹配,詞法分析程序執(zhí)行與那個擴展正則表達式相關(guān)聯(lián)的操作。
模式可包含擴展的字符。如果多字節(jié)語言環(huán)境在系統(tǒng)中安裝,模式也可包含屬于安裝代碼集一部分的多字節(jié)字符。
列由跳格或空格分開。例如,如果想搜索關(guān)鍵字為 KEY 的文件,可輸入如下內(nèi)容:
(KEY) printf ("found KEY");
如果在 File 文件中包含這個規(guī)則,yylex 詞法分析程序匹配模式 KEY 并運行 printf 子例程。
每個模式可有一個對應(yīng)操作,既,當(dāng)一個模式匹配時,一個 C 命令來執(zhí)行。每個語句必須以 ;(分號)結(jié)束。如果在一個操作中用多于一條的語句,必須將它們包含在 { } (大括號)中。如果有個用戶子例程部分,第二個定界符 %%,必須跟著這個規(guī)則部分。如果沒有一個指定操作的模式匹配,詞法分析程序?qū)⒃诓桓妮斎肽J降那闆r下將之復(fù)制到輸出。
當(dāng) yylex 詞法分析程序在匹配一個輸入流中的一個字符串時,在它執(zhí)行規(guī)則部分的任何命令前,它會將這個匹配的字符串復(fù)制到一個外部字符數(shù)組(或指向字符串的指針),yytext。類似的,外部的 int,yyleng,被設(shè)置為以字節(jié)表示的匹配字符串的長度(因此,多字節(jié)字符的大小大于 1)。
如想獲得如何形成擴展正則表達式的信息,請參閱在 《AIX V6.1 通用編程概念:編寫并調(diào)試程序》 中的『lex 命令中的擴展正則表達式』。
用戶子例程
lex 庫定義下列子例程作為能在 lex 規(guī)范文件的規(guī)則部分用的宏。
input |
從 yyin 讀取字節(jié)。 |
unput |
在讀取后替換一個字節(jié)。 |
output |
寫一個輸出字節(jié)到 yyout。 |
winput |
從 yyin 讀取多字節(jié)字符。 |
wunput |
在讀取后替換一個多字節(jié)字符。 |
woutput |
寫一個多字節(jié)輸出字符到 yyout。 |
yysetlocale |
調(diào)用 setlocale (LC_ALL、 " " ); 子例程來確定當(dāng)前語言環(huán)境。 |
winput、wunput 和 woutput 宏被定義來使用在 lex.yy.c 文件中編碼的 yywinput、yywunput 和 yywoutput 子例程。為了兼容性,那些 yy 子例程順序地使用 input、unput 和 output 子例程用完全多字節(jié)字符來讀取、替換和寫必要的字節(jié)數(shù)。
能通過為在用戶子例程部分的例程寫自己的代碼來覆蓋那些宏。但是如果寫自己的,必須如下那樣在定義部分取消那些宏的定義:
%{
#undef input
#undef unput
#undef unput
#undef output
#undef winput
#undef wunput
#undef woutput
#undef yysetlocale
%}
在 lex.yy.c 中沒有 main 子例程,因為 lex 庫包含 main 子例程,而這個子例程調(diào)用 yylex 詞法分析程序和由 yylex() 在 File 結(jié)束處調(diào)用的 yywrap 子例程。因此,如果在用戶子例程部分不包含 main() 或 yywrap() 或兩者都不包含,當(dāng)編譯 lex.yy.c 時,必須在 ll 調(diào)用 lex 庫的地方輸入 cclex.yy.c-ll。
由 lex 命令生成的外部名稱都以 yy 開始,象在 yyin、yyout、 yylex 和 yytext 中那樣。
有限狀態(tài)機
有限狀態(tài)機的缺省骨架在 /usr/ccs/lib/lex/ncform 中定義。用戶可通過設(shè)置一個環(huán)境變量 LEXER=PATH. 使用一個個人配置的有限狀態(tài)機。PATH 變量指定用戶定義的有限狀態(tài)機路徑和文件名。lex 命令為變量檢查環(huán)境,如果它被設(shè)置,那么用補充的路徑。
在表達式中放置空格
一般的,空格或跳格結(jié)束一個規(guī)則,接著結(jié)束定義一個規(guī)則的表達式。然而,可在 " "(引號)內(nèi)包括空格和跳格字符來在表達式中包含它們。用引號括住沒有在 [ ] (括號)集合中的表達式中的所有空格。
其他特殊字符
lex 程序識別許多正常的 C 語言特殊字符。這些字符序列是:
序列 |
含義 |
\a |
提醒 |
\b |
退格 |
\f |
反饋表單 |
\n |
換行符(在表達式中不用實際的換行符。) |
\r |
返回 |
\t |
跳格 |
\v |
縱向跳格 |
\\ |
反斜杠 |
\digits |
通過由 digits 指定 1、2、3 位的八進制整數(shù)表示的帶編碼的字符。 |
\xdigits |
通過由 digits 指定的十六進制字符的序列表示的帶編碼的字符。 |
\c |
在 c 不是上面列出的字符的情況下,表示這個 c 字符未改變。 |
注: 在 lex 規(guī)則中不使用 \0 或 \x0。
當(dāng)在一個表達式中用這些特殊字符,不必將它們括到引號中。除了在 《AIX V6.1 通用編程概念:編寫并調(diào)試程序》 中的『lex 命令中的擴展正則表達式』中描述的特殊字符和運算符符號,所有字符總是一個文本字符。
匹配規(guī)則
當(dāng)多于一個表達式可匹配當(dāng)前輸入,lex 命令先選擇最長的匹配。當(dāng)幾個規(guī)則匹配相同數(shù)目的字符,lex 命令選擇先出現(xiàn)的那個。例如,如果規(guī)則
integer keyword action...;
[a-z]+ identifier action...;
以這個順序給出,integers 是輸入單詞,lex 匹配輸入作為一個標(biāo)識,因為 [a-z]+ 匹配八個字符然而 integer 僅匹配七個字符。然而,輸入是 integer,兩個規(guī)則匹配七個字符。lex 選擇這個關(guān)鍵字規(guī)則因為它先出現(xiàn)。一個更短的輸入,如 int,不匹配整數(shù)表達式,所以 lex 選擇標(biāo)識規(guī)則。
用通配符匹配一個字符串
因為 lex 先選擇最長的匹配,所以不使用包含像 .* 的表達式。例如:
'.*'
可能象是一個在單引號中識別一個字符串的好方法。然而,詞法分析程序讀取源頭,來查找一個遠(yuǎn)的單引號來完成長匹配。如果帶這樣規(guī)則的詞法分析規(guī)則得到以下輸入:
'first' quoted string here, 'second' here
它匹配:
'first' quoted string here, 'second'
為了發(fā)現(xiàn)更短的字符串,first 和 second,使用以下規(guī)則:
'[^'\n]*'
這個規(guī)則在 'first' 后停止。
這個類型的錯誤不是遠(yuǎn)到達,因為 .(句點)運算符不匹配換行字符。因此,像 .*(句號,星號)的表達式在當(dāng)前行停止。不要試圖用像 [.\n]+ 這樣的表達式來使它失敗。詞法分析程序試圖讀取整個輸入文件,并且發(fā)生一個內(nèi)部緩沖區(qū)溢出。
在字符串中查找字符串
lex 程序分割輸入流同時不搜索每個表達式的所有可能匹配。每個字符計算一次且僅一次。例如,計算 she 和 he 在輸入文本中的出現(xiàn)次數(shù),嘗試以下規(guī)則:
she s++
he h++
\n |. ;
在這里最后兩個規(guī)則忽略除了 he 和 she 的所有東西。然而,因為 she 包含 he,lex 不識別包含在 she 中的 he 的情況。
為覆蓋這個選擇,用 REJECT 操作。這個偽指令告訴 lex 轉(zhuǎn)到下一個規(guī)則。lex 然后在第一個規(guī)則被執(zhí)行前調(diào)整輸入指針的位置到它在的地方,并執(zhí)行第二個選擇規(guī)則。例如,計算包含 he 的實例,用以下規(guī)則:
she {s++;REJECT;}
he {h++;REJECT;}
\n |. ;
在計算完 she 的發(fā)生次數(shù),lex 拒絕輸入流然后計算 he 的發(fā)生次數(shù)。因為在這種情況下,she 包含 he 但反之不然,可在 he 上省略 REJECT 操作。在其他情況下,確定哪個輸入字符在兩個類中可能較困難。
總之,無論何時 lex 的目的不是分割輸入流而是檢測在輸入中的某些項的所有示例,REJECT 總是有用的,并且這些項的實例可交迭或互相包含。
標(biāo)志
-C |
生成 lex.yy.C 文件而不是 lex.yy.c 以和 C++ 編譯器一起使用。為得到 I/O 流庫,使用宏 _CPP_IOSTREAMS。 |
-n |
禁止統(tǒng)計摘要。當(dāng)為有限狀態(tài)機設(shè)置自己的表的大小時,如果您不選該標(biāo)志,lex 命令自動生成這個摘要。 |
-t |
寫 lex.yy.c 到標(biāo)準(zhǔn)輸出而不是到一個文件。 |
-v |
提供一個生成的有限狀態(tài)機統(tǒng)計的一行摘要。 |
退出狀態(tài)
此命令返回以下退出值:
示例
- 從 lexcommands 文件提取 lex 指令,并在 lex.yy.c 中放置輸出,用下列命令:
lex lexcommands
- 創(chuàng)建一個 lex 程序,它將大寫轉(zhuǎn)換為小寫,刪除行尾空格,并用一個空格代替多個空格,在 lex 命令文件中包括下列內(nèi)容:
%%
[A-Z] putchar(yytext[0]+ 'a'-'A');
[ ]+$ ;
[ ]+ putchar(' ');
文件
/usr/ccs/lib/libl.a |
包含運行時庫。 |
/usr/ccs/lib/lex/ncform |
定義一個有限狀態(tài)機。 |