First!
lex程序的結(jié)構(gòu)是這樣的!
定義
%%
規(guī)則
%%
用戶代碼
一個(gè) Lex 程序分為三個(gè)段:第一段是 C 和 Lex 的全局聲明,第二段包括模式(C 代碼),第三段是補(bǔ)充的 C 函數(shù)。 這些段以%%來分界。 下面是一個(gè)行數(shù)與字?jǐn)?shù)的統(tǒng)計(jì)工具。
int num_lines = 0, num_chars = 0;
%%
\n ++num_lines; ++num_chars;
. ++num_chars;
%%
main()
{
yylex();
printf( "# of lines = %d, # of chars = %d\n",
num_lines, num_chars );
}
Second!
對(duì)First內(nèi)容的回顧
C 和 Lex 的全局聲明
這一段中我們可以增加 C 變量聲明。這里我們將為字?jǐn)?shù)統(tǒng)計(jì)程序聲明一個(gè)整型變量,來保存程序統(tǒng)計(jì)出來的字?jǐn)?shù)。我們還將進(jìn)行 Lex 的標(biāo)記聲明。
字?jǐn)?shù)統(tǒng)計(jì)程序的聲明
%{
int wordCount = 0;
%}
chars [A-za-z\_\'\.\"]
numbers ([0-9])+
delim [" "\n\t]
whitespace {delim}+
words {chars}+
%%
兩個(gè)百分號(hào)標(biāo)記指出了 Lex 程序中這一段的結(jié)束和三段中第二段的開始。
Lex 的模式匹配規(guī)則
讓我們看一下 Lex 描述我們所要匹配的標(biāo)記的規(guī)則。(我們將使用 C 來定義標(biāo)記匹配后的動(dòng)作。)繼續(xù)看我們的字?jǐn)?shù)統(tǒng)計(jì)程序,下面是標(biāo)記匹配的規(guī)則。
字?jǐn)?shù)統(tǒng)計(jì)程序中的 Lex 規(guī)則
{words} { wordCount++; /*
increase the word count by one*/ }
{whitespace} { /* do
nothing*/ }
{numbers} { /* one may
want to add some processing here*/ }
%%
C 代碼
Lex 編程的第三段,也就是最后一段覆蓋了 C 的函數(shù)聲明(有時(shí)是主函數(shù))。注意這一段必須包括 yywrap() 函數(shù)。 Lex 有一套可供使用的函數(shù)和變量。 其中之一就是 yywrap。一般來說,yywrap() 的定義如下例。我們將在 高級(jí) Lex 中探討這一問題。
字?jǐn)?shù)統(tǒng)計(jì)程序的 C 代碼段
void main()
{
yylex(); /* start the
analysis*/
printf(" No of words:
%d\n", wordCount);
}
int yywrap()
{
return 1;
}
Lex 編程的基本元素就這樣搞定了,它將幫助你編寫簡單的詞法分析程序。
Third
高級(jí)Lex
Lex 有幾個(gè)函數(shù)和變量提供了不同的信息,可以用來編譯實(shí)現(xiàn)復(fù)雜函數(shù)的程序。下表中列出了一些變量和函數(shù),以及它們的使用。 詳盡的列表請(qǐng)參考 Lex 手冊(cè)。
Lex 變量
yyin FILE* 類型。 它指向 lexer 正在解析的當(dāng)前文件。
yyout FILE* 類型。 它指向記錄 lexer 輸出的位置。 缺省情況下,yyin 和 yyout 都指向標(biāo)準(zhǔn)輸入和輸出。
yytext 匹配模式的文本存儲(chǔ)在這一變量中(char*)。
yyleng 給出匹配模式的長度。
yylineno 提供當(dāng)前的行數(shù)信息。(lexer不一定支持。)
Lex 函數(shù)
yylex() 這一函數(shù)開始分析。 它由 Lex 自動(dòng)生成。
yywrap() 這一函數(shù)在文件(或輸入)的末尾調(diào)用。如果函數(shù)的返回值是1,就停止解析。 因此它可以用來解析多個(gè)文件。代碼可以寫在第三段,這就能夠解析多個(gè)文件。 方法是使用 yyin 文件指針(見上表)指向不同的文件,直到所有的文件都被解析。最后,yywrap() 可以返回 1 來表示解析的結(jié)束。
yyless(int n) 這一函數(shù)可以用來送回除了前 n? 個(gè)字符外的所有讀出標(biāo)記。
yymore() 這一函數(shù)告訴 Lexer 將下一個(gè)標(biāo)記附加到當(dāng)前標(biāo)記后。
到此為止,可能你看到lex程序還會(huì)范暈,沒關(guān)系,下面我們接著來,分析一個(gè)類pascal語法的極簡析器!
/* 這個(gè)就是注釋了*/
/* scanner for a toy Pascal-like language */
申明部分開始
%{ 內(nèi)的東西會(huì)原封不動(dòng)地出現(xiàn)在輸出文件中 }%
%{
/* need this for the call to atof() below */
#include <math.h>
%}
DIGIT [0-9]
ID [a-z][a-z0-9]*
%%
模式部分開始
{DIGIT}+ {
printf( "An integer: %s (%d)\n", yytext,
atoi( yytext ) );
}
{DIGIT}+"."{DIGIT}* {
printf( "A float: %s (%g)\n", yytext,
atof( yytext ) );
}
if|then|begin|end|procedure|function {
printf( "A keyword: %s\n", yytext );
}
{ID} printf( "An identifier: %s\n", yytext );
"+"|"-"|"*"|"/" printf( "An operator: %s\n", yytext );
"{"[^}\n]*"}" /* eat up one-line comments */
[ \t\n]+ /* eat up whitespace */
. printf( "Unrecognized character: %s\n", yytext );
%%
補(bǔ)充部分開始
main( argc, argv )
int argc;
char **argv;
{
++argv, --argc; /* skip over program name */
if ( argc > 0 )
yyin = fopen( argv[0], "r" );
else
yyin = stdin;
yylex();
}
想要真正了解lex, [[正則表達(dá)式]] 是關(guān)鍵!
Four
yytext 匹配模式的文本存儲(chǔ)變量, 可以通過在申明階段使用%pointer或%array來控制是一個(gè)字符指針還是一個(gè)字符數(shù)組。指針模式與數(shù)組模式各有特點(diǎn),導(dǎo)致在yytex申明上也不一樣,具體請(qǐng)參考lex手冊(cè)!
在模式階段中
模式 動(dòng)作
[ \t]+ putchar( ' ' );
[ \t]+$ /* ignore this token */
模式部分是正則表達(dá)式,動(dòng)作部分是處理方法,動(dòng)作部分如果時(shí){開頭,那么,動(dòng)作將會(huì)持續(xù)到},如果動(dòng)作中出現(xiàn)了括號(hào){},開始采用 %{ %}來表示動(dòng)作去區(qū)段。動(dòng)作部分如果時(shí) |,就表示與下一條規(guī)則執(zhí)行相同的動(dòng)作。
好的,我們來看一個(gè)更為實(shí)用一點(diǎn)的lex程序。
我們先定義三個(gè)動(dòng)作:
ECHO 將yytext輸出
BEGIN 開始一個(gè)條件處理塊
REJECT 指示簡析器對(duì)當(dāng)前規(guī)則不做處理,而是采用第二匹配規(guī)則。
int word_count = 0;
%%
frob special(); REJECT;
[^ \t\n]+ ++word_count;
如果frob沒有REJECT動(dòng)作,frob將不會(huì)被計(jì)數(shù),因?yàn)榻馕銎髟谕ǔG闆r下,每個(gè)被匹配的對(duì)象只會(huì)對(duì)一個(gè)動(dòng)作生效,多個(gè)REJECT也是允許的,會(huì)尋找下一個(gè)最配的規(guī)則來做處理。所以,下面的規(guī)則會(huì)把輸入的"abcd"處理后輸出"abcdabcaba".
%%
a |
ab |
abc |
abcd ECHO; REJECT;
.|\n /* eat up any unmatched character */
`yymore()' 告訴解析器下一次匹配的規(guī)則,滿足的部分將會(huì)添加到當(dāng)前yytext值得后面而不是替換它。 例如,指定的輸入"mega-kludge"經(jīng)過下面的程序處理后將會(huì)輸出"mega-mega-kludge"。
%%
mega- ECHO; yymore();
kludge ECHO;
第一個(gè) "mega-" 被滿足并且輸出. 然后 "kludge" 滿足, 但是并沒有替換之前的"mega-"而是"kludge"附加到他的后面,然后輸出的其實(shí)是"mega-kludge".
yymore()需要兩件事情需要注意。第一,yymnore()依賴于表現(xiàn)當(dāng)前匹配項(xiàng)的長度yyleng的值,所以使用yymore不允許改變yyleng的值。第二,yymore()的使用會(huì)使解析器付出一點(diǎn)點(diǎn)性能的代價(jià)。
有yymore()就有yyless()
yyless(n) 返回當(dāng)前匹配項(xiàng)除了開始的n個(gè)字符內(nèi)的所有的內(nèi)容到輸入緩存區(qū),解析器處理下一個(gè)匹配時(shí),它們將會(huì)被重新解析。yyless將會(huì)導(dǎo)致yytext與yyleng的調(diào)整。(yyleng將會(huì)等于=n) 如輸入"foobar"被下面的程序處理后,將會(huì)輸出"boobarbar". 因?yàn)榍皀=3個(gè)字符foo外的字符bar被重新返回到輸入緩存區(qū)了。
%%
foobar ECHO; yyless(3);
[a-z]+ ECHO;
參數(shù)0對(duì)于yyless將會(huì)導(dǎo)致整個(gè)當(dāng)前匹配將會(huì)被重新解析。除非你改變了解析器本來的處理流程(如使用begin),這將會(huì)導(dǎo)致循環(huán)結(jié)束。需要注意的是,yyless是一個(gè)宏,并且在flex輸入文件中使用,不能在其他源文件中使用。
unput(c) 將字符c放回到輸入流中,該字符可以重新被解析。下面的動(dòng)作將當(dāng)前的匹配值附上括號(hào)后重新進(jìn)行匹配。
{
int i;
/* Copy yytext because unput() trashes yytext */
char *yycopy = strdup( yytext );
unput( ')' );
for ( i = yyleng - 1; i >= 0; --i )
unput( yycopy[i] );
unput( '(' );
free( yycopy );
}
注意: 由于每次unput()將指定的字符添加到輸入源的開頭,所以將字符串添加到輸入源開頭必須從后道前處理。一個(gè)比較重要的潛在問題是使用unput()的時(shí)候,如果采用了%pointer指針模式保存yytext,unput會(huì)破壞yytext的內(nèi)容,從最右邊的字符開始將會(huì)破壞左邊的一個(gè)字符。如果在unput()后要用到y(tǒng)ytext,你首先必須復(fù)制一份yytext,或者用%array模式來保存yytext. 最后你不能放一個(gè)EOF去試圖標(biāo)志輸入流的結(jié)束。
input 從輸入源中讀取下一個(gè)字符。例如,下面有的例子將會(huì)吃掉C語言注釋
%%
"/*" {
register int c;
for ( ; ; )
{
while ( (c = input()) != '*' &&
c != EOF )
; /* eat up text of comment */
if ( c == '*' )
{
while ( (c = input()) == '*' )
;
if ( c == '/' )
break; /* found the end */
}
if ( c == EOF )
{
error( "EOF in comment" );
break;
}
}
}
注意: 如果簡析器采用用C++編譯,input()被yyinput()的替代,因?yàn)閕nput()與C++中的流名稱input沖突。
YY_FLUSH_BUFFER 刷新解析器內(nèi)部緩存以便于下一次的匹配工作,首先它會(huì)使用YY_INPUT填充緩存區(qū)。這是通用yy_flush_buffer()的一個(gè)特例,將會(huì)在多輸入緩存中描述。
yyterminate()可以在動(dòng)作內(nèi)部返回描述區(qū)域中使用,它將終止解析器并返回0給解析器調(diào)用者,表示操作完成。缺省情況下,到達(dá)文件結(jié)束位置也會(huì)被調(diào)用,它是一個(gè)宏,并且可能重定義。
Lex進(jìn)階
模式
模式在第一階段或第二個(gè)階段使用,也就是在申明或規(guī)則階段中出現(xiàn),模式定義了匹配的目標(biāo),目標(biāo)被匹配后將會(huì)執(zhí)行動(dòng)作。
對(duì)于模式不想做太多說明,使用正則表達(dá)式定義,可以參看 regex 或 pcre.
開始條件
lex提供了根據(jù)條件激活規(guī)則的機(jī)制。在<sc>前綴的規(guī)則將會(huì)在解析器在"sc"的開始條件下被匹配。
<STRING>[^"]* { /* eat up the string body ... */ ... }
將會(huì)在啟動(dòng)條件"STRING"的情況下被激活。
<INITIAL,STRING,QUOTE>\. { /* handle an escape ... */ ... }
將會(huì)在 "INITIAL", "STRING", "QUOTE"三者之一的條件下被激活。
開始條件在輸入源的定義(第一)部分被申明,在‘%s' 或 ’%x'后跟隨著名字列表。 %s申明了包含的開始條件,%x申明了排他的開始條件。開始條件被BEGIN動(dòng)作激活。直到下一個(gè)BEGIN動(dòng)作,滿足開始條件名稱的規(guī)則將會(huì)被規(guī)則,不滿足啟動(dòng)條件的規(guī)則將不會(huì)被執(zhí)行。
如果是包含條件,沒有開始條件的規(guī)則也會(huì)被激活執(zhí)行,如果時(shí)排他條件,只有滿足開始條件的規(guī)則才會(huì)被執(zhí)行。
具有相同排他條件的規(guī)則的集合可以使解析器獨(dú)立于其他的規(guī)則。因此,排他條件可以容易地創(chuàng)建微型解析器處理輸入源中的獨(dú)立與其他部分的一部分(如,注釋)。如果對(duì)于包含與排他條件還有混淆,可以看下面的例子。
%s example%%<example>foo do_something();bar something_else();
等同于
%x example%%<example>foo do_something();<INITIAL,example>bar something_else();
上面的程序中如果沒有<INITIAL,example>,在example條件下bar規(guī)則將永遠(yuǎn)不會(huì)被激活。如果使用<example>,將會(huì)導(dǎo)致只能在exmaple開始條件下激活,而INITIAL條件下不會(huì)被激活。而第一個(gè)程序中在任何條件下bar都被會(huì)激活。因?yàn)榈谝粋€(gè)程序用example時(shí)%s,時(shí)包含條件。頁可以通過特殊開始條件<*>來配置任何開始條件,上面的程序還可以寫為:
%x example%%<example>foo do_something();<*>bar something_else();
缺省規(guī)則(顯示任何未被匹配的字符)在開始條件下仍然生效。等同于:
<*>.|\\n ECHO;
‘BEGIN(0)’在無開始條件的規(guī)則激活條件下返回原始狀態(tài),這個(gè)狀態(tài)同于開始條件下的'INITIAL',所以‘BEGIN(INITIAL)'等同于’BEGIN(0)'。
BEGIN行為在規(guī)則部分的開頭是默認(rèn)的代碼(BEGIN actions can also be given as indented code at the beginning of the rules section.請(qǐng)翻譯)例如,下面的代碼將會(huì)僅需SPECIAL開始條件,不管合適yylex()被調(diào)用并且全局變量enter_special是true。
int enter_special;%x SPECIAL%% if ( enter_special ) BEGIN(SPECIAL);<SPECIAL>blahblahblah...more rules follow...
為了說明開始條件,我們用兩種方法處理"123.456".缺省將會(huì)被解析為 '123','.','456'三個(gè)標(biāo)記,如果expect-floats后面將會(huì)被解析為浮點(diǎn)數(shù) 123.456
%{#include <math.h>%}%s expect%%expect-floats BEGIN(expect);<expect>[0-9]+"."[0-9]+ { printf( "found a float, = %f\n", atof( yytext ) ); }<expect>\n { /* that's the end of the line, so * we need another "expect-number" * before we'll recognize any more * numbers */ BEGIN(INITIAL); }[0-9]+ { printf( "found an integer, = %d\n", atoi( yytext ) ); }"." printf( "found a dot\n" );
下面的代碼能夠是被C語言注釋并且統(tǒng)計(jì)行數(shù)。
%x comment%% int line_num = 1;"/*" BEGIN(comment);<comment>[^*\n]* /* eat anything that's not a '*' */<comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */<comment>\n ++line_num;<comment>"*"+"/" BEGIN(INITIAL);
實(shí)際上,編寫高速解析程序的辦法時(shí)在每個(gè)規(guī)則中做盡可能多的匹配。
This scanner goes to a bit of trouble to match as much text as possible with each rule. In general, when attempting to write a high-speed scanner try to match as much possible in each rule, as it's a big win.
注意: 開始條件的名字實(shí)際上時(shí)一個(gè)整形值并且能夠被保存,所以,上面的代碼可以擴(kuò)展為:
%x comment foo%% int line_num = 1; int comment_caller;"/*" { comment_caller = INITIAL; BEGIN(comment); }...<foo>"/*" { comment_caller = foo; BEGIN(comment); }<comment>[^*\n]* /* eat anything that's not a '*' */<comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */<comment>\n ++line_num;<comment>"*"+"/" BEGIN(comment_caller);
而且,可能易使用YY_START宏來訪問當(dāng)前的開始條件。如上面的賦值條件可以改寫為
comment_caller = YY_START
YYSTATE是YY_START的別名(因?yàn)锳T&T lex使用了YYSTATE)。
注意 開始條件沒有他們的名字空間; %s 與 %x 申明與 #define形式一樣。
到這里,時(shí)一個(gè)使用排他開始條件如何匹配C風(fēng)格的引用字符串的處理。包含的擴(kuò)展的轉(zhuǎn)義,但不包括檢查,因?yàn)榇a太長。
%x str%% char string_buf[MAX_STR_CONST]; char *string_buf_ptr;\" string_buf_ptr = string_buf; BEGIN(str);<str>\" { /* saw closing quote - all done */ BEGIN(INITIAL); *string_buf_ptr = '\0'; /* return string constant token type and * value to parser */ }<str>\n { /* error - unterminated string constant */ /* generate error message */ }<str>\\[0-7]{1,3} { /* octal escape sequence */ int result; (void) sscanf( yytext + 1, "%o", &result ); if ( result > 0xff ) /* error, constant is out-of-bounds */ *string_buf_ptr++ = result; }<str>\\[0-9]+ { /* generate error - bad escape sequence; something * like '\48' or '\0777777' */ }<str>\\n *string_buf_ptr++ = '\n';<str>\\t *string_buf_ptr++ = '\t';<str>\\r *string_buf_ptr++ = '\r';<str>\\b *string_buf_ptr++ = '\b';<str>\\f *string_buf_ptr++ = '\f';<str>\\(.|\n) *string_buf_ptr++ = yytext[1];<str>[^\\\n\"]+ { char *yptr = yytext; while ( *yptr ) *string_buf_ptr++ = *yptr++; }
通常,如上面的例子中所看到你,會(huì)有許多相同開始條件的處理。開始條件范圍可以簡化重復(fù)操作。
<SCs>{}
SCs 是一個(gè)或開始條件的列表。在這個(gè)開始條件范圍內(nèi),每個(gè)規(guī)則將會(huì)自動(dòng)具有前綴 `<SCs>' 直到 `}' 與開始的 `{' 匹配. 例如
<ESC>{ "\\n" return '\n'; "\\r" return '\r'; "\\f" return '\f'; "\\0" return '\0';}
等價(jià)于
<ESC>"\\n" return '\n';<ESC>"\\r" return '\r';<ESC>"\\f" return '\f';<ESC>"\\0" return '\0';
開始條件頁可以嵌套,下面時(shí)三個(gè)管理開始條件堆棧的參數(shù)。
`void yy_push_state(int new_state)'
將當(dāng)前的開始條件壓棧,切換到 new_state 與使用 `BEGIN new_state'類似。
`void yy_pop_state()'
從棧頂彈出,類似于 BEGIN.
`int yy_top_state()'
返回棧頂值,不改變棧內(nèi)容。
開始條件棧動(dòng)態(tài)增長,沒有固定限制,如果內(nèi)容用盡,程序竟會(huì)終止。
為了使用開始條件棧,需要使用 `%option stack' 指令。
多輸入緩存區(qū)
一些允許include文件解析器的解析器要求從幾個(gè)輸入流中讀取內(nèi)容。YY_INPUT只在結(jié)束緩存時(shí)被調(diào)用,碰到 include 后需要切換輸入源,而解析一個(gè)描述也許需要很長時(shí)間。為了解決此類問題,解析器提供了創(chuàng)建并在多個(gè)輸入緩存中創(chuàng)建的機(jī)制。輸入緩存可以通過下面的方式創(chuàng)建:
YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
參數(shù)為與緩存關(guān)聯(lián)的輸入文件指針,以及足夠的可維持size字符(如果不確定,size可以使用YY_BUF_SIZE)。返回一個(gè)YY_BUFFER_STATE,可以傳遞到其他的處理過程。YY_BUFFER_STATE是一個(gè)不可見結(jié)構(gòu)yy_buffer_state的指針,所以可以安全地使用`((YY_BUFFER_STATE) 0)'來初始化YY_BUFFER_STATE,如果你愿意,你可以在解析器之外的源程序中引用這個(gè)不透明結(jié)構(gòu)來正確的申明輸入緩存。可以通過下面的參數(shù)來選擇一個(gè)緩存區(qū)。
void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
切換解析器的輸入緩存將會(huì)導(dǎo)致記接下來的匹配項(xiàng)來自于新的緩存中。yy_switch_to_buffer可能出現(xiàn)在yywrap中為繼續(xù)解析做準(zhǔn)備,替換打開一個(gè)新的文件并執(zhí)行yyin. 通過yy_switch_to_buffer 或 yywrap切換輸入源不改變開始條件。
void yy_delete_buffer( YY_BUFFER_STATE buffer )
用于收回與緩存關(guān)聯(lián)的空間。你可以使用下面的函數(shù)清空當(dāng)前內(nèi)容:
void yy_flush_buffer( YY_BUFFER_STATE buffer )
此函數(shù)廢棄緩存內(nèi)容,下一個(gè)解析器試圖匹配一個(gè)內(nèi)容時(shí)將會(huì)使用YY_INPUT來更新緩存區(qū)。
`yy_new_buffer()' 是 `yy_create_buffer()' 的一個(gè)別名,用于提供C++使用new 與 delete操作創(chuàng)建與銷毀動(dòng)態(tài)對(duì)象的兼容性。
最后, YY_CURRENT_BUFFER 宏返回 YY_BUFFER_STATE 指針,表示當(dāng)前的緩存。
這里是一個(gè)擴(kuò)展include使用的一個(gè)解析器 (`<<EOF>>' 特性將會(huì)在以后討論):
/* "incl" 狀態(tài)用于獲取include的文件名 */
%x incl
%{
#define MAX_INCLUDE_DEPTH 10
YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH];
int include_stack_ptr = 0;
%}
%%
include BEGIN(incl);
[a-z]+ ECHO;
[^a-z\n]*\n? ECHO;
<incl>[ \t]* /* eat the whitespace */
<incl>[^ \t\n]+ { /* got the include file name */
if ( include_stack_ptr >= MAX_INCLUDE_DEPTH )
{
fprintf( stderr, "Includes nested too deeply" );
exit( 1 );
}
include_stack[include_stack_ptr++] =
YY_CURRENT_BUFFER;
yyin = fopen( yytext, "r" );
if ( ! yyin )
error( ... );
yy_switch_to_buffer(
yy_create_buffer( yyin, YY_BUF_SIZE ) );
BEGIN(INITIAL);
}
<<EOF>> {
if ( --include_stack_ptr < 0 )
{
yyterminate();
}
else
{
yy_delete_buffer( YY_CURRENT_BUFFER );
yy_switch_to_buffer(
include_stack[include_stack_ptr] );
}
}
提供三個(gè)過程來實(shí)現(xiàn)內(nèi)存字符串而不是文件輸入緩存的解析。它們都要?jiǎng)?chuàng)建一個(gè)輸入緩存來解析字符串,并且返回YY_BUFFER_STATE (可以在完成解析后用 `yy_delete_buffer()' 刪除).,也可以通過`yy_switch_to_buffer()'來切換, 下一次調(diào)用`yylex()' 將會(huì)解析字符串。
`yy_scan_string(const char *str)' 解析0結(jié)尾字符串。
`yy_scan_bytes(const char *bytes, int len)' 解析bytes開始的len個(gè)字符(可能包含 0 字符)
注意,上面的兩個(gè)函數(shù)會(huì)創(chuàng)建字符串或字節(jié)串的副本。(這也許時(shí)期望的,因?yàn)閌yylex()' 會(huì)修改被解析緩存的內(nèi)容) 可以使用下面的方式來拒絕使用副本:
`yy_scan_buffer(char *base, yy_size_t size)'
將會(huì)從base開始解析,包含size個(gè)字節(jié), 最后的兩個(gè)字節(jié)必須是 YY_END_OF_BUFFER_CHAR (ASCII NUL)。他們不會(huì)被解析, 解析范圍從 `base[0]' 到 `base[size-2]'(包含)。如果你沒能按照這種規(guī)定使用base(如,忘記了最后的兩個(gè)YY_END_OF_BUFFER_CHAR字節(jié)), `yy_scan_buffer()' 將會(huì)返回空指針而不創(chuàng)建YY_BUFFER_STATE。yy_size_t類型是個(gè)整型,可以轉(zhuǎn)化為整數(shù)來反映buffer的長度。
文件結(jié)束規(guī)則
特殊規(guī)則 "<<EOF>>" 只是規(guī)則在文件結(jié)束位置發(fā)生且yywrap()返回非0值。(如,沒有更多的文件要處理). 這個(gè)動(dòng)作必須完成下面四件事情之一:
賦值給yyin一個(gè)新的文件 (早期版本的flex, 此操作后必須調(diào)用特殊動(dòng)作 YY_NEW_FILE; 這個(gè)操作已經(jīng)不需要了);
執(zhí)行一個(gè)返回申明;
執(zhí)行一個(gè)特殊的`yyterminate()' 動(dòng)作;
或者使用`yy_switch_to_buffer()' 切換到一個(gè)新的輸入緩存區(qū).
<<EOF>> 不能與其他模式一起使用;它也許僅在開始條件列表申明。如果指定了不合法 <<EOF>> 規(guī)則, 它將會(huì)應(yīng)用到所有的開始條件而不僅是 <<EOF>> 動(dòng)作. 指定 <<EOF>> 規(guī)則僅在 initial 開始條件下匹配,就是用:
<INITIAL><<EOF>>
下面的規(guī)則可以發(fā)現(xiàn)象不關(guān)閉的注釋類的問題。
%x quote
%%
...other rules for dealing with quotes...
<quote><<EOF>> {
error( "unterminated quote" );
yyterminate();
}
<<EOF>> {
if ( *++filelist )
yyin = fopen( *filelist, "r" );
else
yyterminate();
}