詞法分析器采取的操作
當(dāng)詞法分析器與說(shuō)明文件規(guī)則部分中的一個(gè)擴(kuò)展正則表達(dá)式匹配時(shí),它執(zhí)行與擴(kuò)展正則表達(dá)式相對(duì)應(yīng)的操作。沒有足夠的規(guī)則匹配輸入流中的所有字符串,詞法分析器則將輸入復(fù)制到標(biāo)準(zhǔn)輸出。因此,不要?jiǎng)?chuàng)建僅將輸入復(fù)制到輸出的規(guī)則。缺省的輸出能夠幫助在規(guī)則中查找間隔。
當(dāng)使用 lex 命令處理由 yacc 命令產(chǎn)生的解析器的輸入時(shí),請(qǐng)?zhí)峁┡c所有輸入字符串匹配的規(guī)則。那些規(guī)則必須生成 yacc 命令能夠解釋的輸出。
空操作
要忽略與擴(kuò)展正則表達(dá)式關(guān)聯(lián)的輸入,請(qǐng)使用 ;(C 語(yǔ)言空語(yǔ)句)作為操作。下面的示例忽略了三個(gè)間隔字符(空白、制表符和換行):
[ \t\n] ;
與下一個(gè)操作相同
要避免反復(fù)寫相同的操作,請(qǐng)使用 |(管道符號(hào))。此字符指示此規(guī)則的操作與下一條規(guī)則的操作相同。例如,先前忽略空白、制表符和換行字符的示例也可寫成:
" " |
"\t" |
"\n" ;
\n 和 \t 兩邊的引號(hào)并不需要。
打印匹配字符串
要確定哪個(gè)文本與說(shuō)明文件的規(guī)則部分的表達(dá)式匹配,您可以包擴(kuò) C 語(yǔ)言 printf 子例程調(diào)用作為該表達(dá)式的一個(gè)操作。當(dāng)詞法分析器在輸入流中找到匹配,程序?qū)⑵ヅ渥址湃胪獠孔址?strong>char)和寬字符(wchar_t)數(shù)組中,分別稱為 yytext 和 yywtext。例如,您能使用下面的規(guī)則打印匹配字符串:
[a-z]+ printf("%s",yytext);
C 語(yǔ)言 printf 子例程接受格式參數(shù)和要打印的數(shù)據(jù)。在此示例中,printf 子例程的參數(shù)具有下面的含義:
%s
|
在打印之前將數(shù)據(jù)轉(zhuǎn)換為類型字符串的符號(hào) |
%S
|
在打印之前將數(shù)據(jù)轉(zhuǎn)換為寬字符串(wchar_t)的符號(hào) |
yytext
|
包含要打印的數(shù)據(jù)的數(shù)組的名稱 |
yywtext
|
包含要打印的多字節(jié)類型(wchar_t)數(shù)據(jù)的數(shù)組名稱 |
lex 命令定義 ECHO;作為要打印 yytext 的內(nèi)容的特殊操作。例如,下面的兩條規(guī)則是等價(jià)的:
[a-z]+ ECHO;
[a-z]+ printf("%s",yytext);
您可以在 lex 說(shuō)明文件的定義部分使用 %array 或者 %pointer 如下更改 yytext 的說(shuō)明:
%array
|
將 yytext 定義為以 null 結(jié)束的字符數(shù)組。這是缺省操作。 |
%pointer
|
將 yytext 定義為指向以 null 結(jié)束的字符串的指針。 |
查找匹配字符串的長(zhǎng)度
要查找詞法分析器與特定的擴(kuò)展正則表達(dá)式所匹配的字符數(shù),請(qǐng)使用 yyleng 或者 yywleng 外部變量。
yyleng
|
跟蹤匹配的字節(jié)數(shù)。 |
yywleng
|
跟蹤匹配字符串中的寬字符數(shù)。多字節(jié)字符的長(zhǎng)度大于 1。 |
要對(duì)輸入的字?jǐn)?shù)和字中的字符數(shù)進(jìn)行計(jì)數(shù),請(qǐng)使用下面的操作:
[a-zA-Z]+ {words++;chars += yyleng;}
此操作總計(jì)匹配的字中的字符數(shù),并將該數(shù)字賦予 chars。
下面的表達(dá)式在匹配字符串中查找最后一個(gè)字符:
yytext[yyleng-1]
匹配字符串中的字符串
lex 命令對(duì)輸入流進(jìn)行分區(qū),并不搜索每個(gè)表達(dá)式的所有可能的匹配字符串。每個(gè)字符僅計(jì)算一次。要覆蓋此選項(xiàng)并搜索可能重疊或者互相包含的項(xiàng),請(qǐng)使用 REJECT 操作。例如,要對(duì) she 和 he 的所有實(shí)例(包括包含在 she 中的 he)計(jì)數(shù),請(qǐng)使用下面的操作:
she {s++; REJECT;}
he {h++}
\n |. ;
在對(duì) she 的出現(xiàn)次數(shù)進(jìn)行計(jì)數(shù)后,lex 命令拒絕輸入字符串,然后對(duì) he 的出現(xiàn)次數(shù)進(jìn)行計(jì)數(shù)。因?yàn)?he 并不包括 she,所以 REJECT 操作不必在 he 上。
將結(jié)果添加到 yytext 數(shù)組
典型情況下,來(lái)自輸入流的下一個(gè)字符串覆蓋 yytext 數(shù)組中的當(dāng)前項(xiàng)。如果您使用 yymore 子例程,來(lái)自輸入流的下一個(gè)字符串將被添加到 yytext 數(shù)組的當(dāng)前項(xiàng)的尾部。
例如,下面的詞法分析器搜索字符串:
%s instring
%%
<INITIAL>\" { /* start of string */
BEGIN instring;
yymore();
}
<instring>\" { /* end of string */
printf("matched %s\n", yytext);
BEGIN INITIAL;
}
<instring>. {
yymore();
}
<instring>\n {
printf("Error, new line in string\n");
BEGIN INITIAL;
}
盡管通過匹配多個(gè)規(guī)則,字符串可能被識(shí)別,但是反復(fù)調(diào)用 yymore 子例程可以確保 yytext 數(shù)組包含整個(gè)字符串。
將字符返回到輸入流
要將字符返回給輸入流,請(qǐng)使用下面的調(diào)用:
yyless(n)
其中 n 是當(dāng)前字符串中要保持的字符數(shù)。字符串中超過此數(shù)目的字符被返回到輸入流。yyless 子例程提供的先行函數(shù)類型與 /(斜杠)運(yùn)算符所使用的相同,但是它允許更多對(duì)其用法的控制。
不止一次使用 yyless 子例程處理文本。例如,當(dāng)語(yǔ)法分析 C 語(yǔ)言程序時(shí),諸如 x=-a 之類的表達(dá)式很難理解。它表示 x等于-a,還是 x -= a 的舊的表述形式(意味著將 x減去值a)?要將此表達(dá)式作為 x等于-a 處理,但是要打印警告消息則請(qǐng)使用如下的規(guī)則:
=-[a-zA-Z] {
printf("Operator (=-) ambiguous\n");
yyless(yyleng-1);
... action for = ...
}
輸入/輸出子例程
lex 程序允許程序使用下述輸入/輸出(I/O)子例程:
input()
|
返回下一個(gè)輸入字符 |
output(c)
|
將字符 c 寫到輸出 |
unput(c)
|
將字符 c 推回輸入流,稍后再通過 input 子例程讀出 |
winput()
|
返回下一個(gè)多字節(jié)輸入字符 |
woutput(C)
|
將多字節(jié)字符 C 寫回輸出流 |
wunput(C)
|
將多字節(jié)字符 C 推回輸入流,以通過 winput 子例程讀出 |
lex 程序提供這些子例程作為宏定義。子例程的代碼在 lex.yy.c 文件中。您能覆蓋它們并提供其他版本。
定義 winput、wunput 和 woutput 宏以使用 yywinput、yywunput 和 yywoutput 子例程??紤]到兼容性,yy 子例程隨后使用 input、unput 和 output 子例程來(lái)讀、寫和替換完全多字節(jié)字符中需要數(shù)目的字節(jié)。
這些子例程定義外部文件和內(nèi)部字符之間的關(guān)系。如果您更改子例程,請(qǐng)以相同的方式將它們?nèi)扛摹_@些子例程應(yīng)該遵循這些規(guī)則:
- 所有的子例程必須使用相同的字符集。
-
input 子例程必須返回 0 值以指示文件的末尾。
- 不要更改 unput 子例程和 input 子例程的關(guān)系,否則先行函數(shù)會(huì)不起作用。
lex.yy.c 文件允許詞法分析器最多備份 200 個(gè)字符。
要讀包含 NULL 的文件,請(qǐng)創(chuàng)建不同版本的 input 子例程。在 input 子例程的正常版本中,(從空字符)返回的值 0 表明這是文件的末尾,且將終止輸入。
字符集
lex 命令生成的詞法分析器通過 input、output 和 unput 子例程處理字符 I/O。因此,要在 yytext 子例程中返回值,lex 命令使用這些子例程使用的字符說(shuō)明。但是,在內(nèi)部 lex 命令使用小整數(shù)代表每一個(gè)字符。當(dāng)使用標(biāo)準(zhǔn)庫(kù)時(shí),此整數(shù)是計(jì)算機(jī)用來(lái)表示字符的位模式的值。正常情況下,字母 a 用與字符常量 a 相同的格式表示。如果您使用不同的 I/O 子例程更改此解釋,請(qǐng)將轉(zhuǎn)換表放到說(shuō)明文件的定義部分。轉(zhuǎn)換表在包含下述條目的行開始和結(jié)束:
%T
轉(zhuǎn)換表包含指示與每個(gè)字符關(guān)聯(lián)的值的其他行。例如:
%T
{integer} {character string}
{integer} {character string}
{integer} {character string}
%T
文件末尾處理
當(dāng)詞法分析器到達(dá)文件末尾時(shí),它調(diào)用 yywrap 庫(kù)子例程,此調(diào)用返回值 1,指示詞法分析器應(yīng)該繼續(xù)在輸入末尾正常結(jié)束。
但是,如果詞法分析器從多個(gè)源接收到輸入,請(qǐng)更改 yywrap 子例程。新的函數(shù)必須獲取新的輸入并將值 0 返回給詞法分析器。返回值 0 指示程序應(yīng)該繼續(xù)處理。
您也可以包含代碼,以在詞法分析器在新版本的 yywrap 子例程中終止時(shí),打印摘要報(bào)告和表。yywrap 子例程是強(qiáng)制 yylex 子例程識(shí)別輸入末尾的唯一途徑。