詞法分析器采取的操作
當詞法分析器與說明文件規則部分中的一個擴展正則表達式匹配時,它執行與擴展正則表達式相對應的操作。沒有足夠的規則匹配輸入流中的所有字符串,詞法分析器則將輸入復制到標準輸出。因此,不要創建僅將輸入復制到輸出的規則。缺省的輸出能夠幫助在規則中查找間隔。
當使用 lex 命令處理由 yacc 命令產生的解析器的輸入時,請提供與所有輸入字符串匹配的規則。那些規則必須生成 yacc 命令能夠解釋的輸出。
空操作
要忽略與擴展正則表達式關聯的輸入,請使用 ;(C 語言空語句)作為操作。下面的示例忽略了三個間隔字符(空白、制表符和換行):
[ \t\n] ;
與下一個操作相同
要避免反復寫相同的操作,請使用 |(管道符號)。此字符指示此規則的操作與下一條規則的操作相同。例如,先前忽略空白、制表符和換行字符的示例也可寫成:
" " |
"\t" |
"\n" ;
\n 和 \t 兩邊的引號并不需要。
打印匹配字符串
要確定哪個文本與說明文件的規則部分的表達式匹配,您可以包擴 C 語言 printf 子例程調用作為該表達式的一個操作。當詞法分析器在輸入流中找到匹配,程序將匹配字符串放入外部字符(char)和寬字符(wchar_t)數組中,分別稱為 yytext 和 yywtext。例如,您能使用下面的規則打印匹配字符串:
[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 操作。例如,要對 she 和 he 的所有實例(包括包含在 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 文件中。您能覆蓋它們并提供其他版本。
定義 winput、wunput 和 woutput 宏以使用 yywinput、yywunput 和 yywoutput 子例程。考慮到兼容性,yy 子例程隨后使用 input、unput 和 output 子例程來讀、寫和替換完全多字節字符中需要數目的字節。
這些子例程定義外部文件和內部字符之間的關系。如果您更改子例程,請以相同的方式將它們全部更改。這些子例程應該遵循這些規則:
- 所有的子例程必須使用相同的字符集。
-
input 子例程必須返回 0 值以指示文件的末尾。
- 不要更改 unput 子例程和 input 子例程的關系,否則先行函數會不起作用。
lex.yy.c 文件允許詞法分析器最多備份 200 個字符。
要讀包含 NULL 的文件,請創建不同版本的 input 子例程。在 input 子例程的正常版本中,(從空字符)返回的值 0 表明這是文件的末尾,且將終止輸入。
字符集
lex 命令生成的詞法分析器通過 input、output 和 unput 子例程處理字符 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 子例程識別輸入末尾的唯一途徑。