日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

基于MSYS的 Flex & Bison

 啟_明_星 2010-04-24

使用說(shuō)明

1.介紹

編譯器是軟件開(kāi)發(fā)中的核心部件,其作用是其他 任何軟件所不能取代的。編譯器在工作過(guò)程中,往往完成如下的任務(wù):

  1. 讀取源代碼并且獲得程序的結(jié)構(gòu)描述
  2. 分析程序結(jié)構(gòu),并且生成相應(yīng)的目標(biāo)代碼
在UNIX早期時(shí)代,編寫(xiě)一個(gè)編譯器是一件非常耗時(shí)的工作。人們?yōu)榱撕?jiǎn)化開(kāi)發(fā)過(guò)程,開(kāi)發(fā)了Lex和YACC程序來(lái)解決第一 個(gè)任務(wù),根據(jù)用戶(hù)描述的語(yǔ)言,生成能夠解決問(wèn)題的C/C++語(yǔ)言代碼,供開(kāi)發(fā)者使用。
  1. 將源代碼文件分解為各種詞匯(Lex)
  2. 找到這些詞匯的組成方式(YACC)
GNU軟件協(xié)會(huì)開(kāi)發(fā)了Flex和BISON,其功能與LEX和YACC基本兼容,并且在Lex和YACC提供的功能的基礎(chǔ) 上進(jìn)行了各種擴(kuò)展。

2.Flex 入門(mén)

Lex能夠用來(lái)編寫(xiě)那些輸入數(shù)據(jù)流(字符串)能夠用正則表達(dá)式描述的程序,它可以根據(jù)正則表達(dá) 式的描述,將輸入數(shù)據(jù)流分類(lèi)為各類(lèi)詞匯,為后來(lái)的語(yǔ)法分析做準(zhǔn)備。

2.1.正則表達(dá)式

正則表達(dá)式是通過(guò)對(duì)各種詞組類(lèi)型所包含的字符類(lèi)型的歸納,描述所需詞組組成格式的方法,比如下 面的例子描述了所有數(shù)字類(lèi)型的字符串,表明無(wú)論在何時(shí)起,只要有0-9字符出現(xiàn),就進(jìn)入該狀態(tài),說(shuō)明是字符,直到非0-9字符結(jié)束:

[0123456789]+

為了簡(jiǎn)化書(shū)寫(xiě)起見(jiàn),也可以寫(xiě)成如下的格式:

[0-9]+

對(duì)于任意單詞,其中只能包含字母,所以單詞的正則表達(dá)式為:

[a-zA-Z]+

Flex支持如下類(lèi)型的正則表達(dá)式:

x          符合字符串"x"
.          除了換行以外的任何字符
[xyz]      一個(gè)字符類(lèi),在這個(gè)例子中,輸入必須符合要么是'x’要么是'y'要么是'z'
[abj-oZ]   一個(gè)帶范圍的字符類(lèi),符合任何滿(mǎn)足'a', 'b', 從'j'到'o'還有'Z'
[^A-Z]     一個(gè)取反的字符類(lèi),比如任何字母除了大寫(xiě)字母。
[^A-Z\n]   任何字符除了大寫(xiě)字母和換行
r*         零個(gè)或更多r,r可以是任意正則表達(dá)式
r+         一個(gè)或更多r
r?         零個(gè)或最多一個(gè)r
r{2,5}     任何2到5個(gè)r
r{2,}      2個(gè)或更多r
r{4}       正好4個(gè)r
{name}     對(duì)name的擴(kuò)展
"[xyz]\"foo"
           符合正則表達(dá)式 [xyz]"foo 的字符串
\X         如果X是一個(gè)'a', 'b', 'f', 'n', 'r', 't', 或者'v',
             則按照ASCII碼\x轉(zhuǎn)義符進(jìn)行處理,否則,其他的'X'將用來(lái)做取消處理符處理
\0         一個(gè)ASCII碼空字符
\123       內(nèi)容為八進(jìn)制123的char型
\x2a       內(nèi)容為十六進(jìn)制0x2a的char型
(r)        符合一個(gè)r,括號(hào)是用來(lái)越過(guò)優(yōu)先級(jí)的
rs         正則表達(dá)式r,緊跟著一個(gè)
r|s        要么是r要么是s
^r         一個(gè)r,但是必須是在一行的開(kāi)始
r$         一個(gè)r,但是必須是在行末
<s>r       一個(gè)r,但是之前字符串必須符合條件s
<s1,s2,s3>r
           同上,但是必須之前字符串符合s1或者s2或者s3
<*>r       不考慮開(kāi)始字符串類(lèi)型,只符合r
<<EOF>>    文件末尾
<s1,s2><<EOF>>
           前面符合s1或者s2的文件末尾

2.2.第一個(gè)Lex代碼

按照上一節(jié)我們講述的正則表達(dá)式例子,我們嘗試第一次使用Lex來(lái)產(chǎn)生我們所需要的程序,實(shí)踐 一遍L(zhǎng)ex的使用以及gcc編譯器如何編譯和生成所需的二進(jìn)制。

Flex甚至Bison代碼都有如下的編寫(xiě)格式:

/* 定義段 */
%%
/* Flex、Bison代碼段(規(guī)則) */
%%
/* 輔助代碼段,C語(yǔ)言 */

首先,使用vi編譯器,輸入以下代碼(test.l):

// 定義段代碼
%{                                  // 這種括號(hào)說(shuō)明內(nèi)部的代碼不許flex處理,直接進(jìn)入.C文件
#include <stdio.h>
%}
%%
// 詞法規(guī)則段代碼
[0123456789]+    printf("NUMBER");  // 數(shù)字類(lèi)型字符串
[a-zA-Z]+        {
                   printf("WO");    // 單詞類(lèi)型字符串(WORD)
                   printf("RD");
                 }
%%
// 輔助C語(yǔ)言函數(shù)代碼(直接寫(xiě)C語(yǔ)言,無(wú)須括號(hào),我們這里無(wú)內(nèi)容)

下面我們首先使用lex程序生成所需的狀態(tài)機(jī)代碼:

flex -otest.c test.l    # 從正則表達(dá)式聲稱(chēng)對(duì)應(yīng)的C語(yǔ)言代碼,注意-o后不要有空格(flex bug?)
gcc test.c -o test -lfl # 從C語(yǔ)言代碼生成二進(jìn)制,從而運(yùn)行,-lfl說(shuō)明鏈接libfl.a庫(kù)文件
./test                  # 運(yùn)行剛剛生成的二進(jìn)制

下面我們來(lái)對(duì)剛生成的二進(jìn)制進(jìn)行試驗(yàn),以弄清楚flex到底是做什么的,在test程序中輸入 以下內(nèi)容,按下Ctrl-D可以退出test程序:

3505
hello
what is 3505

通過(guò)這些試驗(yàn),相信您已經(jīng)明白了Flex的用途,每當(dāng)一個(gè)正則表達(dá)式符合時(shí),flex生成的代 碼就會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的函數(shù),或者運(yùn)行對(duì)應(yīng)的程序。下面我們對(duì)這個(gè)例子進(jìn)行一些深入研究,處理一個(gè)類(lèi)似C語(yǔ)言的程序配置腳本代碼。首先,一個(gè)演示腳本如下:

logging {
    category lame-servers { null; };
    category cname { null; };
};
zone "." {
    type hint;
    file "/etc/bind/db.root";
}

在這個(gè)腳本中,我們可以看到一系列的詞匯類(lèi)型:

  • 單詞(WORD),比如'zone', 'type'
  • 文件名(FILENAME),比如'/etc/bind/db.root'
  • 引號(hào)(QUOTE),比如文件名兩邊的
  • 左花括號(hào)(OBRACE),'{'
  • 右花括號(hào)(EBRACE),'}'
  • 分號(hào)(SEMICOLON),';'
我們修改后的Lex代碼如下:

 

%{
#include <stdio.h>
%}
%%
[a-zA-Z][a-zA-Z0-9]*  printf("WORD ");       /* 字符串 */
[a-zA-Z0-9\/.-]+      printf("FILENAME ");   /* 文件名 */
\"                    printf("QUOTE ");      /* 引號(hào)" */
\{                    printf("OBRACE ");     /* 左花括號(hào) */
\}                    printf("EBRACE ");     /* 右花括號(hào) */
;                     printf("SEMICOLON ");  /* 分號(hào) */
\n                    printf("\n");          /* 換行 */
[ \t]+                /* 忽略空白字符 */
%%
int yywrap(void)      /* 當(dāng)詞法分析器到了文件末尾做什么 */
{
        return 1;     /* 返回1,說(shuō)明停止前進(jìn),0則繼續(xù) */
}

int yyerror(char *s) /* 錯(cuò)誤信息打印函數(shù) */
{
        fprintf(stderr, "%s\n", s);
        return 0;
}

int main(int argc, char *argv[])
{
        FILE *fp;
        fp = fopen(argv[1], "r"); /* 首先打開(kāi)要被處理的文件(參數(shù)1) */

        yyin = fp;                /* yyin是lex默認(rèn)的文件輸入指針,則不處理控制臺(tái)輸入 */

        yylex();                  /* 調(diào)用lex的工作函數(shù),從這里開(kāi)始,lex的詞法分析器開(kāi)始工作 */

        fclose(fp);
        return 0;
}

到這里,我們已經(jīng)完全可以對(duì)一個(gè)包含各種類(lèi)型詞組的源代碼文件進(jìn)行分析,得出其中各類(lèi)型詞組的 排列順序。在一個(gè)規(guī)范的基于語(yǔ)法的源代碼中,詞組的順序從一定意義上來(lái)說(shuō),就是語(yǔ)法。對(duì)于源代碼,lex的處理能力也就到這里為止,但是我們并沒(méi)有完全展 示lex的所有功能,讀者如果有興趣,可以繼續(xù)深入閱讀本文提供的參考文獻(xiàn)。如何進(jìn)行語(yǔ)法分析?我們?cè)谙旅娴恼鹿?jié)中講開(kāi)始講述Bison的基本使用方法, 以及如何使用Bison進(jìn)行語(yǔ)法分析。

3.Bison 入門(mén)

3.1.基礎(chǔ)理論

Bison采用與YACC相同的描述語(yǔ)言,這種語(yǔ)言是BNF語(yǔ)法(Backus Naur Form)的一種,最早被John Backus和Peter Naur用于ALGOL60語(yǔ)言的開(kāi)發(fā)工作。BNF語(yǔ)法可以用來(lái)表達(dá)與內(nèi)容無(wú)關(guān)的,基于語(yǔ)法的語(yǔ)言,幾乎所有的現(xiàn)代編程語(yǔ)言都可以用BNF進(jìn)行描述。作為 一個(gè)例子,一個(gè)進(jìn)行加法和乘法的數(shù)學(xué)表達(dá)式語(yǔ)法可以如下表達(dá):

E : E '+' E
E : E '*' E
E : id

在這三句中,E在Bison語(yǔ)言中代表表達(dá)式,一個(gè)表達(dá)式可以是一個(gè)數(shù)字,也可以是一個(gè)變量名 稱(chēng),也可以是幾個(gè)變量名稱(chēng)的組合,比如:

1
1+2
a
a+b*c

而以上三句Bison語(yǔ)言就能夠表達(dá)這些語(yǔ)法結(jié)構(gòu)。

3.2.Bison初探

我們?cè)谙旅孀饕粋€(gè)計(jì)算器,通過(guò)這個(gè)實(shí)例讓大家明白Flex和Bison的相互關(guān)系和如何共同工 作。首先,建立test2ll.l文件,并輸入以下內(nèi)容:

/* 計(jì)算器的詞法分析器描述,F(xiàn)lex語(yǔ)言 */
%{                        /* 直接翻譯為C語(yǔ)言 */
#include <stdlib.h>       /* 包含標(biāo)準(zhǔn)庫(kù)文件 */
int yyerror(char *);      /* 這是我們上面用到的錯(cuò)誤報(bào)告函數(shù) */
#include "test2yy.h"      /* 這個(gè)頭文件由Bison自動(dòng)生成 */
%}
%%
[0-9]+    {
              yylval = atoi(yytext);    /* yytext是flex內(nèi)部用于指向當(dāng)前詞匯的字符串指針 */
              return INTEGER;           /* INTEGER是從test2yy.h中包含過(guò)來(lái)的,在Bison中定義 */
          }
[-+\n]    return *yytext;
[ \t]     ; /* 跳過(guò)空白字符 */
.         yyerror("invalid character"); /* 產(chǎn)生一個(gè)錯(cuò)誤,說(shuō)明是無(wú)效字符 */
%%
int yywrap(void)
{
    return 1;                           /* 文件結(jié)束時(shí)退出 */
}

以上就是計(jì)算器使用的Flex語(yǔ)言,描述了我們 將會(huì)遇到的各種詞匯的格式,比如0-9

/* 注:您先抄寫(xiě),注解見(jiàn)下文 */
%{
#include <stdio.h>
int yylex(void);
int yyerror(char *);
%}
%token INTEGER                          /* Flex語(yǔ)言中INTEGER定義在此 */
%%
program:
        program expr '\n'    { printf("%d\n", $2); }
        |
        ;
expr:
        INTEGER              { $$ = $1; }
        | expr '+' expr      { $$ = $1 + $3; }  /* (1) */
        | expr '-' expr      { $$ = $1 - $3; }  /* (2) */
        ;
%%
int yyerror(char *s)
{
    fprintf(stderr, "%s\n", s);
}
int main(void)
{
    yyparse();
    return 0;
}

編譯過(guò)程:

flex -otest2ll.c test2ll.l
bison -otest2yy.c test2yy.y -d      # 注意-d,用于產(chǎn)生對(duì)應(yīng)的頭文件
gcc test2yy.c test2ll.c -o test2

在這個(gè)例子中,我們遇到了許多沒(méi)有見(jiàn)過(guò)的用法,Bison的書(shū)寫(xiě)格式基本與Flex的相同,只 是規(guī)則的定義語(yǔ)法不同。其中,$N(N為數(shù)字)代表語(yǔ)法描述中,第N個(gè)詞匯的內(nèi)容,比如(1)中的$1代表'+'左邊的expr,$3代表右邊expr的 內(nèi)容,其中的N是指當(dāng)前語(yǔ)法的詞匯順序,從1開(kāi)始計(jì)數(shù)。而$$則代表這一段語(yǔ)法處理完后的結(jié)果,每一個(gè)語(yǔ)法對(duì)應(yīng)的處理都有輸入$N和輸出$$。 該例子中還有一個(gè)特殊的地方,就是第歸調(diào)用,在Bison中,語(yǔ)法規(guī)則可以是第歸的,這也是Bison之處(或者說(shuō)是YACC的強(qiáng)大之處)。舉個(gè)例子:

program:
        program expr '\n'    { printf("%d\n", $2); }
        |
        ;

這里,program的定義就是任何符合program的表達(dá)式后面緊接著expr和 '\n',掃描到該表達(dá)式后,將expr處理的結(jié)果打印到屏幕。最后的|是“或者”的意思,也就是說(shuō)program也可以是空的,什么都不寫(xiě),分號(hào)代表該 語(yǔ)義定義結(jié)束。 有了第歸之后,Bison才可以說(shuō)是一個(gè)能夠應(yīng)對(duì)任何狀況的語(yǔ)法分析器,當(dāng)然,這里還需要讀者對(duì)以上所提供代碼進(jìn)行深入的研究和分析,考慮清楚后,會(huì)發(fā)現(xiàn) 無(wú)論是C語(yǔ)言,還是Bison,第歸永遠(yuǎn)是一個(gè)神奇的解決方案。

3.3.計(jì)算器程序的深入研究

以上我們?cè)O(shè)計(jì)的計(jì)算器只能進(jìn)行加減計(jì)算,并且里面還有一些軟件缺陷,對(duì)于一個(gè)實(shí)用的計(jì)算器來(lái) 說(shuō),我們必須能夠支持:

  1. 變量定義
  2. 變量賦值
  3. 變量與數(shù)字立即處理
于是我們假想設(shè)計(jì)出來(lái)的計(jì)算器能有如下的操作過(guò)程(輸入和輸出):

 

輸入:3 * (4 + 5)
結(jié)果:27
輸入:x = 3 * (4 + 5)
輸入:y = 5
輸入:x
結(jié)果:27
輸入:y
結(jié)果:5
輸入:x + 2 * y
結(jié)果:37

這樣看,這個(gè)計(jì)算器的功能已經(jīng)相當(dāng)強(qiáng)大了,下面我們著手實(shí)現(xiàn),首先修改上面例子的Flex文件 為如下:

%{
    #include <stdlib.h>
    int yyerror(char *);
    #include "test2yy.h"
%}
%%
[a-z]        {                            /* 變量類(lèi)型詞匯,限制:變量只能用一個(gè)字符 */
                 yylval = *yytext - 'a';
                 return VARIABLE;
             }
[0-9]+       {                            /* 整數(shù) */
                 yylval = atoi(yytext);
                 return INTEGER;
             }
[-+()=/*\n]  { return *yytext; }          /* 數(shù)學(xué)計(jì)算符號(hào) */
[ \t]        ;                            /* 跳過(guò)空白符號(hào) */
%%
int yywrap(void)
{
    return 1;
}

下面是我們新的Bison文件:

%token INTEGER VARIABLE
%left '+' '-'
%left '*' '/'
%{
    #include <stdio.h>
    int yyerror(char *);
    int yylex(void);
    int sym[26];
%}
%%
program:
    program statement '\n'
    |
    ;
statement:
    expr                       { printf("%d\n", $1); }
    | VARIABLE '=' expr        { sym[$1] = $3; }
    ;
expr:
    INTEGER
    | VARIABLE                 { $$ = sym[$1]; }
    | expr '+' expr            { $$ = $1 + $3; }
    | expr '-' expr            { $$ = $1 - $3; }
    | expr '*' expr            { $$ = $1 * $3; }
    | expr '/' expr            { $$ = $1 / $3; }
    | '(' expr ')'             { $$ = $2; }
    ;
%%
int yyerror(char *s)
{
    fprintf(stderr, "%s\n", s);
    return 0;
}
int main(void)
{
    yyparse();
    return 0;
}

現(xiàn)在我們對(duì)該例子中引入的新功能介紹,%left,%right,%token都是用來(lái)聲明詞 匯的,區(qū)別在于,%token聲明的詞匯與左右優(yōu)先級(jí)無(wú)關(guān),而%left的處理時(shí),先處理左邊的,%right先處理右邊的,例如遇到字符串"1 - 2 - 5",到底是處理為"(1 - 2) - 5",還是處理為"1 - (2 - 5)"?%left處理為前者,而%right處理為后者(注:第一個(gè)計(jì)算器代碼中就有這個(gè)缺陷,比如執(zhí)行1-2+3,得到的結(jié)果就是-4,作為一個(gè)練 習(xí),讀者可以使用這里講解的%left自行更正)。

4.Flex和Bison高級(jí)應(yīng)用

在下面的例子中,我們便寫(xiě)了一個(gè)更高級(jí)的計(jì)算器程序,其操作實(shí)例如下:

x = 0;
while(x < 3) {
    print(x);
    x = x + 1;
}

例子中得到的輸出結(jié)果如下:

0
1
2

首先是我們的全局頭文件test3.h:

typedef enum {
        typeCon,
        typeId,
        typeOpr
} nodeEnum;

/* constants */
typedef struct {
        int value;      /* value of constant */
} conNodeType;

/* identifiers */
typedef struct {
        int i;          /* subscript to sym array */
} idNodeType;

/* operators */
typedef struct {
        int oper;       /* operator */
        int nops;       /* number of operands */
        struct nodeTypeTag *op[1];      /* operands (expandable) */
} oprNodeType;

typedef struct nodeTypeTag {
        nodeEnum type;  /* type of node */

        /* union must be last entry in nodeType */
        /* because operNodeType may dynamically increase */
        union {
                conNodeType con;        /* constants */
                idNodeType id;          /* identifiers */
                oprNodeType opr;        /* operators */
        };
} nodeType;

extern int sym[26];

下面是Flex語(yǔ)言文件test3ll.l:

%{
#include <stdlib.h>
#include "test3.h"
#include "test3yy.h"
int yyerror(char *);
%}

%%

[a-z]           {
                        yylval.sIndex = *yytext - 'a';
                        return VARIABLE;
                }

[0-9]+          {
                        yylval.iValue = atoi(yytext);
                        return INTEGER;
                }

[-()<>=+*/;{}.] {
                        return *yytext;
                }

">="            return GE;
"<="            return LE;
"=="            return EQ;
"!="            return NE;
"while"         return WHILE;
"if"            return IF;
"else"          return ELSE;
"print"         return PRINT;

[ \t\n]+        ;       /* ignore whitespace */
.               yyerror("Unknown character");

%%

int yywrap(void)
{
        return 1;
}

然后是我們的Bison文件test3yy.y:

%{
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "test3.h"

/* prototypes */
nodeType *opr(int oper, int nops, ...);
nodeType *id(int i);
nodeType *con(int value);
void freeNode(nodeType *p);
int yylex(void);

int yyerror(char *s);
int sym[26];
%}

%union {
        int iValue;     /* integer value */
        char sIndex;    /* symbol table index */
        nodeType *nPtr; /* node pointer */
};

%token <iValue> INTEGER
%token <sIndex> VARIABLE
%token WHILE IF PRINT
%nonassoc IFX
%nonassoc ELSE

%left GE LE EQ NE '>' '<'
%left '+' '-'
%left '*' '/'
%nonassoc UMINUS

%type <nPtr> stmt expr stmt_list

%%

program:
        function                        { exit(0); }
        ;

function:
        function stmt                   { ex($2); freeNode($2); }
        |
        ;

stmt:
        ';'                             { $$ = opr(';', 2, NULL, NULL); }
        | expr ';'                      { $$ = $1; }
        | PRINT expr ';'                { $$ = opr(PRINT, 1, $2); }
        | VARIABLE '=' expr ';'         { $$ = opr('=', 2, id($1), $3); }
        | WHILE '(' expr ')' stmt       { $$ = opr(WHILE, 2, $3, $5); }
        | IF '(' expr ')' stmt %prec IFX{ $$ = opr(IF, 2, $3, $5); }
        | IF '(' expr ')' stmt ELSE stmt{ $$ = opr(IF, 3, $3, $5, $7); }
        | '{' stmt_list '}'             { $$ = $2; }
        ;

stmt_list:
        stmt                            { $$ = $1; }
        | stmt_list stmt                { $$ = opr(';', 2, $1, $2); }
        ;

expr:
        INTEGER                         { $$ = con($1); }
        | VARIABLE                      { $$ = id($1); }
        | '-' expr %prec UMINUS         { $$ = opr(UMINUS, 1, $2); }
        | expr '+' expr                 { $$ = opr('+', 2, $1, $3); }
        | expr '-' expr                 { $$ = opr('-', 2, $1, $3); }
        | expr '*' expr                 { $$ = opr('*', 2, $1, $3); }
        | expr '/' expr                 { $$ = opr('/', 2, $1, $3); }
        | expr '<' expr                 { $$ = opr('<', 2, $1, $3); }
        | expr '>' expr                 { $$ = opr('>', 2, $1, $3); }
        | expr GE  expr                 { $$ = opr(GE , 2, $1, $3); }
        | expr LE  expr                 { $$ = opr(LE , 2, $1, $3); }
        | expr NE  expr                 { $$ = opr(NE , 2, $1, $3); }
        | expr EQ  expr                 { $$ = opr(EQ , 2, $1, $3); }
        | '(' expr ')'                  { $$ = $2; }
        ;

%%

#define SIZEOF_NODETYPE ((char*)&p->con - (char*)p)

nodeType *con(int value) {
        nodeType *p;
        size_t nodeSize;

        /* allocate node */
        nodeSize = SIZEOF_NODETYPE + sizeof(conNodeType);
        if((p = malloc(nodeSize)) == NULL)
                yyerror("out of memory");

        /* copy information */
        p->type = typeCon;
        p->con.value = value;

        return p;
}

nodeType *id(int i) {
        nodeType *p;
        size_t nodeSize;

        /* allocate node */
        nodeSize = SIZEOF_NODETYPE + sizeof(idNodeType);
        if((p = malloc(nodeSize)) == NULL)
                yyerror("out of memory");

        /* copy information */
        p->type = typeId;
        p->id.i = i;

        return p;
}

nodeType *opr(int oper, int nops, ...) {
        va_list ap;
        nodeType *p;
        size_t nodeSize;
        int i;

        /* allocate node */
        nodeSize = SIZEOF_NODETYPE + sizeof(oprNodeType) +
                (nops - 1) * sizeof(nodeType*);
        if((p = malloc(nodeSize)) == NULL)
                yyerror("out of memory");

        /* copy information */
        p->type = typeOpr;
        p->opr.oper = oper;
        p->opr.nops = nops;
        va_start(ap, nops);
        for(i = 0; i < nops; i++)
                p->opr.op[i] = va_arg(ap, nodeType*);
        va_end(ap);
        return p;
}

void freeNode(nodeType *p) {
        int i;

        if(!p) return;
        if(p->type == typeOpr) {
                for(i=0; i<p->opr.nops; i++)
                        freeNode(p->opr.op[i]);
        }
        free(p);
}

int yyerror(char *s) {
        fprintf(stdout, "%s\n", s);
}

int main(void) {
        yyparse();
        return 0;
}

上面的Flex和Bison代碼所作的工作是根據(jù)語(yǔ)法建立語(yǔ)意描述樹(shù)結(jié)構(gòu)。延續(xù)我們上面計(jì)算器 的例子,我們寫(xiě)出如何翻譯這些語(yǔ)言的實(shí)現(xiàn)部分(對(duì)生成的樹(shù)進(jìn)行第歸分析):

#include <stdio.h>
#include "test3.h"
#include "test3yy.h"

int ex(nodeType *p) {
        if(!p) return 0;
        switch(p->type) {
        case typeCon:
                return p->con.value;
        case typeId:
                return sym[p->id.i];
        case typeOpr:
                switch(p->opr.oper) {
                case WHILE:
                        while(ex(p->opr.op[0]))
                                ex(p->opr.op[1]);
                        return 0;
                case IF:
                        if(ex(p->opr.op[0]))
                                ex(p->opr.op[1]);
                        else if(p->opr.nops > 2)
                                ex(p->opr.op[2]);
                        return 0;
                case PRINT:
                        printf("%d\n", ex(p->opr.op[0]));
                        return 0;
                case ';':
                        ex(p->opr.op[0]);
                        return ex(p->opr.op[1]);
                case '=':
                        return sym[p->opr.op[0]->id.i] = ex(p->opr.op[1]);
                case UMINUS:
                        return -ex(p->opr.op[0]);
                case '+':
                        return ex(p->opr.op[0]) + ex(p->opr.op[1]);
                case '-':
                        return ex(p->opr.op[0]) - ex(p->opr.op[1]);
                case '*':
                        return ex(p->opr.op[0]) * ex(p->opr.op[1]);
                case '/':
                        return ex(p->opr.op[0]) / ex(p->opr.op[1]);
                case '<':
                        return ex(p->opr.op[0]) < ex(p->opr.op[1]);
                case '>':
                        return ex(p->opr.op[0]) > ex(p->opr.op[1]);
                case GE:
                        return ex(p->opr.op[0]) >= ex(p->opr.op[1]);
                case LE:
                        return ex(p->opr.op[0]) <= ex(p->opr.op[1]);
                case NE:
                        return ex(p->opr.op[0]) != ex(p->opr.op[1]);
                case EQ:
                        return ex(p->opr.op[0]) == ex(p->opr.op[1]);
                       
                }
        }
}

一般實(shí)際的編譯器都是以匯編代碼輸出的,所以我們?cè)谶@里進(jìn)行一些深入研究,得出了另一個(gè)版本的 ex函數(shù)實(shí)現(xiàn),能夠?qū)崿F(xiàn)匯編代碼的輸出(compiler.c):

#include <stdio.h>
#include "test3.h"
#include "test3yy.h"

static int lbl;

int ex(nodeType *p) {
        int lbl1, lbl2;

        if(!p) return 0;
        switch(p->type) {
        case typeCon:
                printf("\tpush\t%d\n", p->con.value);
                break;

        case typeId:
                printf("\tpush\t%c\n", p->id.i + 'a');
                break;

        case typeOpr:
                switch(p->opr.oper) {
                case WHILE:
                        printf("L%03d:\n", lbl1 = lbl++);
                        ex(p->opr.op[0]);
                        printf("\tjs\tL%03d\n", lbl2 = lbl++);
                        ex(p->opr.op[1]);
                        printf("\tjz\tL%03d\n", lbl1);
                        printf("L%03d:\n", lbl2);
                        break;

                case IF:
                        ex(p->opr.op[0]);
                        if(p->opr.nops > 2) {
                                /* if else */
                                printf("\tjs\tL%03d\n", lbl1 = lbl++);
                                ex(p->opr.op[1]);
                                printf("\tjmp\tL%03d\n", lbl2 = lbl++);
                                printf("L%03d:\n", lbl1);
                                ex(p->opr.op[2]);
                                printf("L%03d:\n", lbl2);
                        } else {
                                /* if */
                                printf("\tjs\tL%03d\n", lbl1 = lbl++);
                                ex(p->opr.op[1]);
                                printf("L%03d:\n", lbl1);
                        }
                        break;

                case PRINT:
                        ex(p->opr.op[0]);
                        printf("\tprint\n");
                        break;

                case '=':
                        ex(p->opr.op[1]);
                        printf("\tpop\t%c\n", p->opr.op[0]->id.i + 'a');
                        break;

                case UMINUS:
                        ex(p->opr.op[0]);
                        printf("\tneg\n");
                        break;

                default:
                        ex(p->opr.op[0]);
                        ex(p->opr.op[1]);
                        switch(p->opr.oper) {
                        case '+':       printf("\tadd\n"); break;
                        case '-':       printf("\tsub\n"); break;
                        case '*':       printf("\tmul\n"); break;
                        case '/':       printf("\tdiv\n"); break;
                        case '<':       printf("\tcompLT\n"); break;
                        case '>':       printf("\tcompGT\n"); break;
                        case GE:        printf("\tcompGE\n"); break;
                        case LE:        printf("\tcompLE\n"); break;
                        case NE:        printf("\tcompNE\n"); break;
                        case EQ:        printf("\tcompEQ\n"); break;
                        }
                }
        }
        return 0;
}

使用方法:取消interpreter.c的編譯,取而代之用compiler.c即可。關(guān)于 這個(gè)計(jì)算器的代碼分析,請(qǐng)等待后續(xù),(未完待續(xù))

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多