按一定語法寫好規(guī)則,bison則可以自動識別語法,網(wǎng)上關(guān)于Yacc的資料很多,但是幾個關(guān)鍵點講的不夠清晰,這里按自己的理解寫下來,加深記憶。
定義段寫法:
定義段可以分為兩部分:
第一部分以符號%{和%}包裹,里面為以C語法寫的一些定義和聲明:例如,文件包含,宏定義,全局變量定義,函數(shù)聲明等。
第二部分主要是對文法的終結(jié)符和非終結(jié)符做一些相關(guān)聲明。這些聲明主要有如下一些:%token,%left,%right,%nonassoc,%union,%type,%start。下面分別說明它們的用法。
%token定義文法中使用了哪些終結(jié)符,定義形式為:
%token TOKEN1 TOKEN2 TOKEN3 …
%left,%right,%nonassoc也是定義文法中使用的終結(jié)符,定義形式與%token類似,但是他們定義的終結(jié)符具有某種優(yōu)先級和結(jié)合性,%left表示左結(jié)合,%right表示右結(jié)合,%nonassoc表示不可結(jié)合(即它定義的終結(jié)符不能連續(xù)出現(xiàn):例如<,如果文法中不允許出現(xiàn)形如a<b<c的句子,則<就是不可結(jié)合的)。而優(yōu)先級關(guān)系則是以他們定義出現(xiàn)的順序決定的,先定義的優(yōu)先級低,最后定義的優(yōu)先級最高,同時定義的優(yōu)先級相同。例如,如果有如下定義:
%left A B %nonassoc C %right D
則表示優(yōu)先級關(guān)系為: A=B < C < D,而結(jié)合性關(guān)系為:A,B左結(jié)合,C不可結(jié)合,D右結(jié)合。
%union和%type用來處理文法中各符號所帶的屬性。在詞法分析的學(xué)習(xí)中,我們知道記號是由記號名和記號的屬性值兩部分組成的,文法中的終結(jié)符就是記號,他們有屬性值,同樣,非終結(jié)符也是可以有屬性值的。
%union { int num; char * id; }
定義了類型,再將類型和具體的標(biāo)示符進行關(guān)聯(lián),如果是
終結(jié)符:
%token <num> TOKEN1 %token <id> TOKEN2 TOKEN3
非終結(jié)符:
%type <id> sym1 sym2 %type <num> sym3
第二部分規(guī)則段的寫法
第二部分好理解,不作回顧。
第三部分輔助函數(shù)段的寫法
輔助函數(shù)段用C語言語法來寫,輔助函數(shù)一般指在規(guī)則段中用到或者在語法分析器的其他部分用到的函數(shù)。這一部分一般會被直接拷貝到y(tǒng)acc編譯器產(chǎn)生的c源文件中。 一般來說,除規(guī)則段用到的函數(shù)外,輔助函數(shù)段一般包括如下一些例程:yylex(),yyerror(),main()。
int yylex()是詞法分析程序,它返回記號。語法分析驅(qū)動程序yyparse()將會調(diào)用yylex()獲取記號。如果不使用lex生成這個函數(shù),則必須在輔助函數(shù)段用C語言寫這個程序。記號由記號名和屬性值構(gòu)成,記號名一般作為yylex的返回值(注意,記號名是由%token等定義的終結(jié)符名,這些終結(jié)符名在yacc內(nèi)部會被宏定義成一些常數(shù)。),而屬性值則由yacc內(nèi)部定義的變量yylval來傳遞例如,若屬性值棧定義為
%union { int num; char * id; }
而yylex返回記號的屬性值為”myid”(類型為char *)時,yylex在返回之前,應(yīng)使用如下語句將屬性值傳遞給語法分析器:
yylval.id = “myid”;