Lex & Yacc 學(xué)習(xí)筆記(2)- 簡單計算器

一、背景

構(gòu)建一個簡單計算器,識別輸入的計算表達(dá)式并計算結(jié)果。通過計算器程序 來說明 lex & yacc 的開發(fā)過程 和 Lex 的結(jié)構(gòu)規(guī)范。

二、環(huán)境

? 系統(tǒng): CentOS 7.5
? 編譯器: gcc - 4.8.5
? lex: flex 2.5.37
? Yacc: bison (GNU Bison) 3.0.4

安裝 flex 和 bison 。
yum install flex bison

如果在編譯鏈接過程中出現(xiàn)以下錯誤:
/usr/bin/ld: cannot find -lfl
請重新安裝flex:
yum remove flex
yum install flex

三、簡單計算器實現(xiàn)

計算器實現(xiàn)整數(shù)的 +、-、*、/、% 五種簡單運(yùn)算。

詞法分析程序 cal.l

%{
#include "cal.tab.h"
extern int yylval;
%}

%%
[0-9]+  { yylval = atoi(yytext); return NUMBER; }
[ \t]   ;       /* ignore white space */
\n  return 0;   /* logical EOF */
.   return yytext[0];
%%

代碼中定義了四條規(guī)則,前面的部分就是模式,處于一行的開始位置,后面部分是動作,也就是,輸入中匹配到了這個模式的時候,對應(yīng)進(jìn)行什么動作(就像機(jī)器人接受到了什么樣的指令,然后會執(zhí)行相應(yīng)的動作一樣)

第一個模式,匹配連續(xù)一個或者多個數(shù)字,匹配到之后就返回標(biāo)簽NUMBER。
第二個模式,匹配空格,沒有任何操作,忽略所有空格。
第三個模式,匹配一個換行符,匹配到之后結(jié)束匹配。
第四個模式,匹配出了\n之外的字符,返回該字符。

總體來說,匹配到連續(xù)數(shù)字,則返回NUMBER;忽略空格;換行結(jié)束;匹配到任何其他字符返回字符。

語法分析程序 cal.y

%{
#include <stdio.h>
%}
%token NAME NUMBER
%%
statement:  NAME '=' expression
    |   expression      { printf("= %d\n", $1); }
    ;

expression: expression '+' NUMBER   { $$ = $1 + $3; }
    |   expression '-' NUMBER   { $$ = $1 - $3; }
    |   expression '*' NUMBER   { $$ = $1 * $3; }
    |   expression '/' NUMBER   { $$ = $1 / $3; }
    |   expression '%' NUMBER   { $$ = $1 % $3; }
    |   NUMBER          { $$ = $1; }
    ;
%%
int main()
{
    yyparse();
    return 0;
}

int yyerror(char *s)
{
    printf("%s/n",s);
    return 0;
}

編譯運(yùn)行過程

[appusr@postgre cal]$ ls -l
total 8
-rw-rw-r--. 1 appusr appusr 200 Aug 16 11:27 cal.l
-rw-rw-r--. 1 appusr appusr 533 Aug 16 14:11 cal.y

/* 1.  編譯lex文件,生成lex.yy.c文件 */
[appusr@postgre cal]$ flex cal.l
[appusr@postgre cal]$ ls -l
total 52
-rw-rw-r--. 1 appusr appusr   200 Aug 16 11:27 cal.l
-rw-rw-r--. 1 appusr appusr   533 Aug 16 14:11 cal.y
-rw-rw-r--. 1 appusr appusr 44068 Aug 16 15:58 lex.yy.c

/* 2. 編譯yacc文件,生成cal.tab.h 與cal.tab.c文件 */
[appusr@postgre cal]$ bison -d cal.y
[appusr@postgre cal]$ ls -l
total 100
-rw-rw-r--. 1 appusr appusr   200 Aug 16 11:27 cal.l
-rw-rw-r--. 1 appusr appusr 44255 Aug 16 15:58 cal.tab.c
-rw-rw-r--. 1 appusr appusr  2063 Aug 16 15:58 cal.tab.h
-rw-rw-r--. 1 appusr appusr   533 Aug 16 14:11 cal.y
-rw-rw-r--. 1 appusr appusr 44068 Aug 16 15:58 lex.yy.c

/* 3. 鏈接生成的.c 文件,并生成相應(yīng)的可執(zhí)行文件 cal */
[appusr@postgre cal]$ gcc -o cal cal.tab.c lex.yy.c -lfl
[appusr@postgre cal]$ ls -l
total 128
-rwxrwxr-x. 1 appusr appusr 28632 Aug 16 15:58 cal
-rw-rw-r--. 1 appusr appusr   200 Aug 16 11:27 cal.l
-rw-rw-r--. 1 appusr appusr 44255 Aug 16 15:58 cal.tab.c
-rw-rw-r--. 1 appusr appusr  2063 Aug 16 15:58 cal.tab.h
-rw-rw-r--. 1 appusr appusr   533 Aug 16 14:11 cal.y
-rw-rw-r--. 1 appusr appusr 44068 Aug 16 15:58 lex.yy.c

/* 4. 運(yùn)行可執(zhí)行文件cal,計算簡單表達(dá)式  */
[appusr@postgre cal]$ ./cal
22*33
= 726
[appusr@postgre cal]$

四、Lex 的結(jié)構(gòu)規(guī)范

lex是用來生成詞法分析器的
lex源文件擴(kuò)展名.l
分為三個段:定義段、規(guī)則段、用戶子程序段

/* 定義段 */
%{
...
%}
...

%%
    /* 規(guī)則段 */
...

%%
    /* 用戶子程序段 */
...

三個段用 %% 進(jìn)行分隔:

1. 定義段

定義段包括文字塊、定義、內(nèi)部表聲明、起始條件和轉(zhuǎn)換。
C語言的注釋、頭文件包含等一般就放在%{%}之間,這一部分的內(nèi)容會被直接復(fù)制到輸出文件的開頭部分。
例如:

%{
#include "cal.tab.h"
extern int yylval;
%}

2. 規(guī)則段

規(guī)則段為一系列匹配模式和動作,模式一般使用正則表達(dá)式書寫,動作部分為C代碼:

模式1 {動作1 (C代碼)}

例如:
[0-9]+  { yylval = atoi(yytext); return NUMBER; }

????在輸入和模式1匹配的時候,執(zhí)行動作部分的代碼。
????C代碼被逐字拷貝到生成的C文件中。

當(dāng)lex掃描程序運(yùn)行時,它把輸入與規(guī)則段的模式進(jìn)行匹配。
? 每次發(fā)現(xiàn)一個匹配(被匹配的輸入稱為標(biāo)記(token))時就執(zhí)行與那種模式相關(guān)的C代碼。
? 如果模式后面跟著 | 符號,則該模式將使用與文件中下一個模式相同的C代碼。
? 當(dāng)輸入字符不匹配模式時,詞法分析程序的動作就好像它匹配上了代碼ECHO的模式,ECHO將標(biāo)記的拷貝寫到輸出。

3. 用戶子程序段

這里為C代碼,會被原樣復(fù)制到c文件中,一般這里定義一些輔助函數(shù)等,如動作代碼中使用到的輔助函數(shù)。
如果重新定義input()、unput()、output()、或者yywrap(),新的版本或者支持子程序,都可以放在這里。

詞法分析器所做的,就是在輸入中尋找字符的模式(pattern)。
在詞法分析器中,我們要給定我們需要識別的模式,因此需要使用一種方式來描述模式,這就是常用的正則表達(dá)式。

五、總結(jié)

本文通過親自動手構(gòu)建一個計算器實現(xiàn)來示范 lex & yacc 的開發(fā)過程 和 Lex 的結(jié)構(gòu)規(guī)范,加深 lex & yacc 的編程理解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容