多練習,才是王道!底下來熟悉lex語法代表的意義吧!
像雷米這樣對抽象化沒太多慧根的人,有個具體的例子學起來就輕鬆多了。
同樣引用自此網站:http://epaperpress.com/lexandyacc/
Lex Practice
表格一、樣式(Pattern)比對原始值
Metacharacter | Matches | |
---|---|---|
. |
any character except newline | 除換行以外的任何型別 |
\n |
newline | 新行 |
* |
zero or more copies of the preceding expression | 零或多個複製前面的表達式 |
+ |
one or more copies of the preceding expression | 一或多個複製前面的表達式 |
? |
zero or one copy of the preceding expression | 零或一個複製前面的表達式 |
^ |
beginning of line | 起始行 |
$ |
end of line | 結束行 |
a|b |
a or b |
a或b |
(ab)+ |
one or more copies of ab (grouping) |
一或多個複製ab(合併) |
"a+b" |
literal “a+b ” (C escapes still work) |
文字”a+b” 這句我看不懂Orz |
[] |
character class | 型別類別 |
表格二、樣式比對範例 雷米碎碎念:看例子果然清楚多了!
Expression | Matches | 酷喔!用表情符號超方便! |
---|---|---|
abc |
abc |
XD XD |
abc* |
ab abc abcc abccc ... |
XD* X, XD, XDD, XDDD… (可以少c) |
abc+ |
abc, abcc, abccc, abcccc, ... |
Orz+ Orz, Orzz, Orzzz, Orzzz…(不可少c) |
a(bc)+ |
abc, abcbc, abcbcbc, ... |
O(rz)+ Orz,Orzrz, Orzrzrz (後面兩個重複) |
a(bc)? |
a, abc |
O(rz)? O, Orz (意思是有bc或沒bc二選一) |
[abc] |
one of: a, b, c |
[Orz]O, r, z 其中一個 |
[a-z] |
any letter, a through z | 比對英文字母a~z |
[a\-z] |
one of: a, -, z |
比對a,-,z三者之一(加一個右斜線差很多!) |
[-az] |
one of: - a z |
比對-,a,z三者之一 |
[A-Za-z0-9]+ |
one or more alphanumeric characters | 一個或多個英文與數字 |
[ \t\n]+ |
whitespace | 白鍵 |
[^ab] |
anything except: a, b |
任何值,除了a,b之外(所以可以用來去括號囉!?) |
[a^b] |
a, ^, b |
意思是將^放到中間以後,會當作符號來判別。 |
[a|b] |
a, |, b |
將|放在[ ] 類別中,會被當作符號來判別。 |
a|b |
a, b |
將|直接使用,則代表兩者。 |
雷米碎碎念:
嗯嗯!(點頭點頭~)所以,依照上面這堆例子,我們大概可以知道樣式(pattern)要怎麼定義了。
接著我們開始步入真正的重點,lex file的寫法囉!副檔名為*.l。
lex正規表示式的組成方式在表一,樣式比對的範例在表二。
在一般的型別類別[ ]中,一般運算子會失去它們的意義。
其中兩個運算子hyphen (“-
“)和circumflex (“^
“)允許在一個型別類別[ ]中,
當hyphen (“-
“)使用在兩個型別之間表示型別的範圍,當circumflex (“^
“)放在第一個位置表示減掉後頭的敘述。
如果兩個樣式比對到相同的字串,則會以長的字串為主。
如果發生比對到兩個相同長度的情況,則會以第一個樣式為主。
… definitions …%% … rules … %% … subroutines … |
輸入給lex的內容分為三個段落,利用%%來分段。
第一個例子可能是最簡短lex檔案:
%%
輸入一次複製到輸出一個字元。
第一個%%是必須用來做為規則段落。
然而,如果我們沒有指定規則,那麼這個預設的段落會去比對任何東西並且複製到輸出。
預設的輸入與輸出分別是stdin和stdout,這裡用相同的例子來表示預設的編碼。
%% /* match everything except newline */ . ECHO; /* match newline */ \n ECHO; %% int yywrap(void) { return 1; } int main(void) { yylex(); return 0; }
兩個樣式(patterns)已經指定在規則段落。
每個樣式必須開始從第一列開始,接著是空白(whitespace)如白鍵、標籤、換行(space, tab, newline) 和一個與樣式相關的操作。
這些操作可能是一行C語言或是多行C語言,寫在括號內。任何沒有在一開始逐字複製到C檔案的內容,我們可以使用這個方法去指定一些指令在lex檔案裡。
在這個範例中,有兩個樣式”.”和”\n”,伴隨著ECHO這個關聯動作。
許多巨集(macros)和變數(variables)是由lex預先定義的。
ECHO是一個巨集(macros)代表的是由這個樣式(pattern)去寫程式去做比對。
對任何一個尚未比對的字串來說,這是預設的行為。
一般情況下, ECHO
被定義為…
#define ECHO fwrite(yytext, yyleng, 1, yyout)
變數yytext是一個被用來比對字串的指標
(NULL-terminated),yyleng
是用來比對字串的長度。yyout
是輸出檔且預設為 stdout 標準輸出。函式
yywrap會被lex呼叫當輸入已經耗盡,當你已經做完時回傳1,如果還有更多的存取則回傳0。
每個C程式都有一個main 函式,在這種情況下,我們簡單的使用yylex做為main 函式對lex的輸入口。
lex有些實作方式包含在函式庫中複製main與yywrap,因此不需要明確的程式碼。
這就是為什麼第一個範例程式可以正常運作。