詞法分析器采取的操作

當(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ù)組中,分別稱為 yytextyywtext。例如,您能使用下面的規(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ì) shehe 的所有實(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、wunputwoutput 宏以使用 yywinput、yywunputyywoutput 子例程??紤]到兼容性,yy 子例程隨后使用 input、unputoutput 子例程來(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、outputunput 子例程處理字符 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í)別輸入末尾的唯一途徑。