yacc程序怎麼編譯
① 編譯原理入門之 lex, flex,yacc,bison等工具了解
Lex,Flex,Yacc,bison是編譯原理中常用的工具,分別用於詞法分析和語法分析。Lex(或Flex)生成詞法分析器,將字元流轉換為標記;Yacc(或bison)生成語法分析器,執行語法規則解析。使用場景主要在編譯器前端階段,分別進行詞法和語法分析。工作原理分別是通過正則表達式和BNF來描述規則並生成代碼。
Lex與Flex相似,後者生成的掃描器具有可重入性,適用於多線程環境。Yacc與bison等效,後者具備更多功能與優化的錯誤報告,同樣支持多線程,通過BNF描述語法規則生成代碼。
綜上,Lex和Flex用於生成詞法分析器,Yacc和bison用於生成語法分析器,共同構成編譯器的核心部分。這些工具通過將詞法或語法規則轉化為C語言代碼,實現源代碼到目標代碼的轉換。
拓展內容:Lex文件通常包含三部分:定義、規則和C代碼。以下是一個簡單的Lex文件示例,用於將輸入文本分割成單詞和數字,並輸出它們。將此文件保存為`lexer.l`,使用Lex工具生成詞法分析器。步驟如下:編寫Lex文件,使用`lex lexer.l`生成C文件`lex.yy.c`,通過C編譯器編譯文件`gcc lex.yy.c -o lexer`,最後運行生成的程序`./lexer`。
② Lex與YACC詳解
只要你在Unix環境中寫過程序,你必定會邂逅神秘的Lex&YACC,就如GNU/Linux用戶所熟知的Flex&Bison,這里的Flex就是由Vern Paxon實現的一個Lex,Bison則是GNU版本的YACC。這些程序實用性極廣,但如同你的C編譯器一樣,在其主頁上並沒有描述它們,也沒有關於怎樣使用的信息。然而,Lex和YACC可以讓你輕易的解析復雜的語言,當你需要讀取一個配置文件時,或者你需要編寫一個你自己使用的語言的編譯器時,這對於你來說是莫大的裨益。
Lex會生成一個叫做『詞法分析器』的程序。這是一個函數,它帶有一個字元流傳入參數,詞法分析器函數看到一組字元就會去匹配一個關鍵字(key),採取相應措施。一個非常簡單的例子如下:
詞法分析器會等待輸入一些數據,每次輸入一些不匹配的命令(非』stop』和』start』),它會將你輸入的字元再次輸出。你若輸入』stop』,它將輸出』Stop command received』。用一個EOF(^D)來結束程序。
Lex的常規表達式是一種使用元語言的模式描述。表達式由符號組成。符號一般是字元和數字,還有一些具有特殊含義的其他標記。例如,常規表達式可以匹配「[0123456789]+」或「[a-zA-Z][a-zA-Z0-9]*」。其中,後者匹配一個變數名,必須以字母開頭,可以在後續字元中用數字。
YACC可以解析輸入流中的標識符(token),這就清楚的描述了YACC和LEX的關系。YACC並不知道『輸入流』為何物,它需要事先就將輸入流預加工成標識符,雖然你可以自己手工寫一個Tokenizer,但我們將這些工作留給LEX來做。YACC用來為編譯器解析輸入數據,即程序代碼。這些用編程語言寫成的程序代碼一點也不模稜兩可——它們只有一個意思。正因為如此,YACC才不會去對付那些有歧義的語法,並且會抱怨shift/rece或者rece/rece沖突。
Lex和YACC可以生成C++代碼的解析器。雖然LEX和YACC的歷史要早於C++,但是還是可以用它們來生成一個C++解析器。我們用LEX來生成C++的詞法分析器,YACC並不知道如何直接來處理這些,所以我們不打算這么做。比較好的做法是,要做一個C++解析器,就需要LEX生成一個C文件,並且讓YACC來生成C++代碼。然而,在這個過程中,你會遇到一些問題,因為C++代碼默認情況下並不能找到C的函數,除非你將那些函數定義為extern 「C」。為解決此問題,我們在YACC代碼中編寫一個C開頭。
在YACC文件中,你定義了你自己的main()函數,它在某個點上調用了yyparse()。YACC會創建你的yyparse()函數,並在y.tab.c中結束該函數。yyparse()函數讀取一個『標識符/值對』(token/value pairs)流,這些流需要事先就提供,這些流可以是你自己手寫的代碼提供的,也可以是LEX生成的。在我們的示例中,我們把這個工作丟給了LEX。LEX生成的yylex()函數從文件參數FILE *file中讀取字元(文件名為yyin)。
遞歸是YACC一個極其重要的特性。沒有遞歸的話,你就確定一個文件是由一系列獨立的命令組成還是由語句組成。由於YACC自身的特性,它只對第一個規則或那個你將其設計為『起始規則』的規則感興趣。起始規則用』%start』符號標記。YACC中的遞歸以兩種形式出現,左遞歸和右遞歸。左遞歸是你應該經常使用的,它們看起來如下:
在解析長的語句時,務必使用左遞歸,例如整個文件。但有時難以避免右遞歸,不過,如果你的語句並不太長,你就沒有必要越軌使用左遞歸。正確的做法是使用左遞歸。
為了匹配所有丟給它的東西,避免了將那些不匹配的輸入輸出到標准輸出的默認行為,我們可以通過字元作為速記法來作為標識符的數字ID,我們可以這樣來重寫我們的詞法分析器。
我們需要定義yylval的類型。但是這並不一直恰如其當。我們可能會多次這樣做,因為需要處理多種數據類型。例如,我們需要定義yylval為一個union,它可以存儲字元串,也可以存儲整數,但並不是同時存儲。我們可以定義YYSTYPE為一個union,YACC中有一種簡便的方法來實現,即%union語句。
我們不希望從標准輸入解析,而希望解析給定的字元串。實現方法是自定義實現YY_INPUT。
YACC中有許多調試反饋信息。這些調試信息的代價有點高,所以你需要提供一些開關來打開它。當你運行那個生成的二進制文件,它將輸出很多運行時信息。裡麵包含當前所運行的狀態機以及讀取到的一些標識符。你可以用Emacs中那個非常好的『pinfo』工具閱讀.info文件。
YACC解析器在內部運行的是一個『狀態機』,該狀態機可以有多種轉台。接著有多個規則來管制狀態間的相互轉化。任何內容都是從『root』規則開始。狀態機不斷遞減演化,直到它遇到某些它能理解的東西。