詞法分析器采取的操作

當詞法分析器與說明文件規則部分中的一個擴展正則表達式匹配時,它執行與擴展正則表達式相對應的操作。沒有足夠的規則匹配輸入流中的所有字符串,詞法分析器則將輸入復制到標準輸出。因此,不要創建僅將輸入復制到輸出的規則。缺省的輸出能夠幫助在規則中查找間隔。

當使用 lex 命令處理由 yacc 命令產生的解析器的輸入時,請提供與所有輸入字符串匹配的規則。那些規則必須生成 yacc 命令能夠解釋的輸出。

空操作

要忽略與擴展正則表達式關聯的輸入,請使用 ;(C 語言空語句)作為操作。下面的示例忽略了三個間隔字符(空白、制表符和換行):

[ \t\n] ;

與下一個操作相同

要避免反復寫相同的操作,請使用 |(管道符號)。此字符指示此規則的操作與下一條規則的操作相同。例如,先前忽略空白、制表符和換行字符的示例也可寫成:

" "                     |
"\t"                    |
"\n"                    ;

\n\t 兩邊的引號并不需要。

打印匹配字符串

要確定哪個文本與說明文件的規則部分的表達式匹配,您可以包擴 C 語言 printf 子例程調用作為該表達式的一個操作。當詞法分析器在輸入流中找到匹配,程序將匹配字符串放入外部字符(char)和寬字符(wchar_t)數組中,分別稱為 yytextyywtext。例如,您能使用下面的規則打印匹配字符串:

[a-z]+            printf("%s",yytext);

C 語言 printf 子例程接受格式參數和要打印的數據。在此示例中,printf 子例程的參數具有下面的含義:

%s 在打印之前將數據轉換為類型字符串的符號
%S 在打印之前將數據轉換為寬字符串(wchar_t)的符號
yytext 包含要打印的數據的數組的名稱
yywtext 包含要打印的多字節類型(wchar_t)數據的數組名稱

lex 命令定義 ECHO;作為要打印 yytext 的內容的特殊操作。例如,下面的兩條規則是等價的:

[a-z]+       ECHO;
[a-z]+       printf("%s",yytext);

您可以在 lex 說明文件的定義部分使用 %array 或者 %pointer 如下更改 yytext 的說明:

%array yytext 定義為以 null 結束的字符數組。這是缺省操作。
%pointer yytext 定義為指向以 null 結束的字符串的指針。

查找匹配字符串的長度

要查找詞法分析器與特定的擴展正則表達式所匹配的字符數,請使用 yyleng 或者 yywleng 外部變量。

yyleng 跟蹤匹配的字節數。
yywleng 跟蹤匹配字符串中的寬字符數。多字節字符的長度大于 1。

要對輸入的字數和字中的字符數進行計數,請使用下面的操作:

[a-zA-Z]+       {words++;chars += yyleng;}

此操作總計匹配的字中的字符數,并將該數字賦予 chars

下面的表達式在匹配字符串中查找最后一個字符:

yytext[yyleng-1]

匹配字符串中的字符串

lex 命令對輸入流進行分區,并不搜索每個表達式的所有可能的匹配字符串。每個字符僅計算一次。要覆蓋此選項并搜索可能重疊或者互相包含的項,請使用 REJECT 操作。例如,要對 shehe 的所有實例(包括包含在 she 中的 he)計數,請使用下面的操作:

she              {s++; REJECT;}
he               {h++}
\n          |.           ;

在對 she 的出現次數進行計數后,lex 命令拒絕輸入字符串,然后對 he 的出現次數進行計數。因為 he 并不包括 she,所以 REJECT 操作不必在 he 上。

將結果添加到 yytext 數組

典型情況下,來自輸入流的下一個字符串覆蓋 yytext 數組中的當前項。如果您使用 yymore 子例程,來自輸入流的下一個字符串將被添加到 yytext 數組的當前項的尾部。

例如,下面的詞法分析器搜索字符串:

%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;
        }

盡管通過匹配多個規則,字符串可能被識別,但是反復調用 yymore 子例程可以確保 yytext 數組包含整個字符串。

將字符返回到輸入流

要將字符返回給輸入流,請使用下面的調用:

yyless(n)

其中 n 是當前字符串中要保持的字符數。字符串中超過此數目的字符被返回到輸入流。yyless 子例程提供的先行函數類型與 /(斜杠)運算符所使用的相同,但是它允許更多對其用法的控制。

不止一次使用 yyless 子例程處理文本。例如,當語法分析 C 語言程序時,諸如 x=-a 之類的表達式很難理解。它表示 x等于-a,還是 x -= a 的舊的表述形式(意味著將 x減去a)?要將此表達式作為 x等于-a 處理,但是要打印警告消息則請使用如下的規則:

=-[a-zA-Z]      {
                printf("Operator (=-) ambiguous\n");
                yyless(yyleng-1);
                ... action for = ...
                }

輸入/輸出子例程

lex 程序允許程序使用下述輸入/輸出(I/O)子例程:

input() 返回下一個輸入字符
output(c) 將字符 c 寫到輸出
unput(c) 將字符 c 推回輸入流,稍后再通過 input 子例程讀出
winput() 返回下一個多字節輸入字符
woutput(C) 將多字節字符 C 寫回輸出流
wunput(C) 將多字節字符 C 推回輸入流,以通過 winput 子例程讀出

lex 程序提供這些子例程作為宏定義。子例程的代碼在 lex.yy.c 文件中。您能覆蓋它們并提供其他版本。

定義 winputwunputwoutput 宏以使用 yywinputyywunputyywoutput 子例程。考慮到兼容性,yy 子例程隨后使用 inputunputoutput 子例程來讀、寫和替換完全多字節字符中需要數目的字節。

這些子例程定義外部文件和內部字符之間的關系。如果您更改子例程,請以相同的方式將它們全部更改。這些子例程應該遵循這些規則:

  • 所有的子例程必須使用相同的字符集。
  • input 子例程必須返回 0 值以指示文件的末尾。
  • 不要更改 unput 子例程和 input 子例程的關系,否則先行函數會不起作用。

lex.yy.c 文件允許詞法分析器最多備份 200 個字符。

要讀包含 NULL 的文件,請創建不同版本的 input 子例程。在 input 子例程的正常版本中,(從空字符)返回的值 0 表明這是文件的末尾,且將終止輸入。

字符集

lex 命令生成的詞法分析器通過 input、outputunput 子例程處理字符 I/O。因此,要在 yytext 子例程中返回值,lex 命令使用這些子例程使用的字符說明。但是,在內部 lex 命令使用小整數代表每一個字符。當使用標準庫時,此整數是計算機用來表示字符的位模式的值。正常情況下,字母 a 用與字符常量 a 相同的格式表示。如果您使用不同的 I/O 子例程更改此解釋,請將轉換表放到說明文件的定義部分。轉換表在包含下述條目的行開始和結束:

%T

轉換表包含指示與每個字符關聯的值的其他行。例如:

%T
{integer}       {character string}
{integer}       {character string}
{integer}       {character string}
%T

文件末尾處理

當詞法分析器到達文件末尾時,它調用 yywrap 庫子例程,此調用返回值 1,指示詞法分析器應該繼續在輸入末尾正常結束。

但是,如果詞法分析器從多個源接收到輸入,請更改 yywrap 子例程。新的函數必須獲取新的輸入并將值 0 返回給詞法分析器。返回值 0 指示程序應該繼續處理。

您也可以包含代碼,以在詞法分析器在新版本的 yywrap 子例程中終止時,打印摘要報告和表。yywrap 子例程是強制 yylex 子例程識別輸入末尾的唯一途徑。