在編譯過程中,詞法分析和語法分析是兩個重要階段。lex和yacc是unix環境下非常著名的兩個工具,可以生成分別完成詞法分析和語法分析功能 的c**。在學習編譯原理過程中,可以善加利用這兩個工具,加深對兩個階段的理解。在平時的工作中,這兩個工具也會起到重要的作用。 lex是lexical compiler的縮寫,主要功能是生成乙個詞法分析器(scanner)的c原始碼。描述詞法分析器的檔案,經過lex編譯後,生成乙個lex.yy.c 的檔案,然後由c編譯器編譯生成乙個詞法分析器。詞法分析器,簡單來說,其任務就是將輸入的各種符號,轉化成相應的識別符號(token),轉化後的識別符號 很容被後續階段處理。
先讓我們來看乙個簡單的例子:
int num_lines = 0, num_chars = 0;
%% \n
. %%
main()
然後編譯,輸入乙個文字試試:
$ flex sample1.l$ mv lex.yy.c sample1.c$ gcc sample1.c -o sample1 -ll$ ./sample1
#include "y.tab.h"
typedef char * yystype;
char * yylval;
% }
正規表示式宣告如下
/* regular definitions */
delim [ \t\n]
ws +
letter [a-za-z]
digit [0-9]
id ()*
number +(\.+)?(e[+\-]?+}?
這段正規表示式描述識別數(number)、識別符號(id)的"規則"。過一會我們再細說正規表示式。
規則段是由正規表示式和相應的動作組成的。
p1
p2
…… pn
值得注意的是,lex 依次嘗試每乙個規則,盡可能地匹配最長的輸入流。如果有一些內容根本不匹配任何規則,那麼 lex 將只是把它拷貝到標準輸出。比如
%% a
aa
aaaa
%% 編譯後執行一下,
$ ./sample3
aaaaaaa
i love you
可以看出lex的確按照最長的規則匹配。
程式段部分放一些掃瞄器的其它模組,比如一些動作執行時需要的模組。也可以在另乙個程式檔案中編寫,最後再鏈結到一起。 生成c**後,需用c的編譯器編譯。連線時需要指定鏈結庫。gcc的連線引數為 -ll。
[編輯]正規表示式
正規表示式(regular expression)可以描述有窮狀態自動機(finite automata)接受的語言,也就是定義乙個可以接受的串的集合。限於篇幅,我們就不展開關於這方面的話題了。有興趣的請參考[4]。 這裡只介紹一下lex中用到的正規表示式的一些規則。
轉義字元(也稱操作符):
" \ [ ] ^ - ? . * + | ( ) $ / % 這些符號有特殊含義,不能用來匹配自身。如果需要匹配的話,可以通過引號(")或者轉義符號(\)來指示。比如
c"++" c\+\+都可以匹配c++。
非轉義字元:所有除了轉義字元之外的字元都是非轉義字元。乙個非轉義字元可以匹配自身。比如
integer匹配文字中出現的integer。
萬用字元:萬用字元就是.(dot),可以匹配任何乙個字元。
字符集:用一對指定的字元構成乙個字符集。比如[abc]表示乙個字符集,可以匹配a、b、c中的任意乙個字元。使用 可以指定範圍。比如[a-z]表示可以匹配所有小寫字母的字符集。
重複:
* 任意次重複+ 至少一次的重複,相當於xx*? 零次或者一次選擇和分組:|符號表示選擇,二者則一;括號表示分組,括號內的組合被看作是乙個原子。比如(ab|cd)匹配ab或者cd。
簡單來說,yacc(yet another compiler-compiler)就是編譯器的編譯器。yacc是乙個通用的工具,能夠根據使用者指定的規則,生成乙個詞法分析程式。yacc能識別 lalr(1)且無歧義的文法,它的輸入是詞法分析器的輸出。我們知道,生成詞法分析器是lex分內的事,因此lex和yacc常常珠聯璧合。
先讓我們看一下yacc檔案的格式。和前面介紹的lex的格式類似:
declarations(宣告)
%%rules(規則)%%
programs(**)
俗話說"沒有規矩,不成方圓"。 規則段描述規則,自然是重中之重了。規則段的結構是如下,
a : body ;
a表示非終結符名,body表示產生式和動作。產生式包括非終結符和終結符,終結符用''引用。一些轉義字元,比如'\r','\n'等,和c裡面 的表示是一樣的。動作(action)則是在輸入被當前規則識別出來時而執行的。動作實際上就是c的**,寫在中。為了溝通詞法分析器和動作,yacc引入了形式變數,以$開頭。如果希望獲得詞法分析器和前面的動作返回的值,我們可以使用$1,$2,…。$i表 示一條規則右側第i個單元的值。比如有這樣的一條規則,
a : b c d ;c的返回值為$2,d為$3。依此類推。
程式段放一些其它的程式,也可以省略,連%%都可以不要。
連線時需要指定連線庫,gcc的引數為-ly。
[編輯]舉例
讓我們看乙個典型的例子,它實現乙個簡單的計算器: %
%start list
%token digit letter
%left '|'
%left '&'
%left '+' '-'
%left '*' '/' '%'
%left uminus /* supplies precedence for unary minus */
%% /* beginning of rules section
*/list :
/* empty */
| list stat '\n'
| list error '\n'
; stat : expr
| letter '=' expr
; expr : '(' expr ')'
| expr '+' expr
| expr '-' expr
| expr '*' expr
| expr '/' expr
| expr '%' expr
| expr '&' expr
| expr '|' expr
| '-' expr %prec uminus
| letter
| number ; number : digit | number digit ; %% /* start of programs */ yylex() /* c is now nonblank */ if( islower( c ) ) if( isdigit( c ) ) return( c ); } 編譯、執行:
$bison example.y
$gcc example.tab.c ly -o example
$./example
20+30*50=1520
小結 lex是詞法分析器的生成工具,yacc是文法分析器的生成工具。lex的描述規則採用正規表示式,關於正規表示式的詳細討論,參見文獻[1]; yacc的描述規則採用無歧異文法。在gnu中有相應lex和yacc工具:flex和bison,與lex和 yacc相容。
C語言的lex和yacc工具說明
一,lex工具 lex工具是一種詞法分析程式生成器,它可以根據詞法規則說明書的要求來生成單詞識別程式,由該程式識別出輸入文字中的各個單詞。1 lex程式的結構 定義部分 規則部分 使用者子程式部分 其中規則部分是必須的,定義和使用者子程式部分是任選的。1 定義部分 定義部分起始於 符號,其間可以是包...
lex和yacc格式入門
lex和yacc格式入門 lex檔案 hi oi n tchau bye n int main void int yywrap void yacc檔案 token hi bye program hi bye hi hi bye bye int yyerror char msg 會發現它們的結構都很相...
初步學習lex和yacc
因為是非計算機本科,所以沒有學編譯原理,進來想補補課,於是買了本 自製程式語言 裡面介紹了lex和yacc工具,於是裝起來試了下。原來用工具來解析字串還是挺方便的,以前知道正則以後,就覺得這東西很好,現在有了lex和yacc,把正則能做的事情又放大了,能夠做更豐富的事情。例如,寫乙個簡單的把字串裡的...