溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

怎么用FLex與Bison實現(xiàn)計算器

發(fā)布時間:2021-07-09 16:59:06 來源:億速云 閱讀:745 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容主要講解“怎么用FLex與Bison實現(xiàn)計算器”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學(xué)習(xí)“怎么用FLex與Bison實現(xiàn)計算器”吧!

參考:

    Flex常用規(guī)范

    Bison常用規(guī)范

   flex+bison開發(fā)計算器

1 Unix Lex/YACC 發(fā)展為 Linux FLex/Bison

    Lex是1975年由Mike Lesk和當時尚在AT&T實習(xí)的Eric Schmidt共同完成的(Schmidt做的更多),是一個詞法分析器的生成程序,可以單獨使用也可以與Johnson的yacc協(xié)同工作。lex很有名氣,但是無奈效率太低加上有bug。大概在1987年,Lawrence Berkeley實驗室的Vern Paxson用C重新寫了Lex,并命名為FLex(the Fast Lexical Analyzer Generator),基于伯克利許可證。flex現(xiàn)在是SourceForge的一個項目,依然基于伯克利許可,
[Flex](https://github.com/westes/flex "Flex") 是起初unix版lex的free (but non-GNU) implementation,用于c/c ++ 的詞法掃描生成器。

    (注意:Schmidt曾是google的CEO)

    bison的前身是yacc。yacc是由貝爾實驗室的S.C.Johnson基于Knuth大神的LR語法分析理論,于1975~1978年寫成。大約1985年,UC Berkeley 的研究生Bob Corbett使用改進的內(nèi)部算法實現(xiàn)了伯克利yacc,來自FSF的Richard Stallman改寫了伯克利yacc并將其用于GNU項目,添加了很多特性,形成了今天的GNU Bison。bison現(xiàn)在作為FSF的項目被維護,基于GNU公共許可證發(fā)布,[Bison](http://www.gnu.org/software/bison/manual/)是兼容yacc的free的語法生成器。

    早期Unix的Lex/YACC,發(fā)展為FLex/Bison,新版本的程序是向上兼容的(即兼容老版本),現(xiàn)chang用Flex和Bison。

2 flex-bison工作原理

    使用的角度,F(xiàn)lex和Bison是Linux下用來生成詞法分析器和語法分析器兩個程序的工具,可以處理結(jié)構(gòu)化輸入,一般結(jié)合使用來處理復(fù)雜的文件解析工作。

怎么用FLex與Bison實現(xiàn)計算器

    flex文件是定義pattern(哪是黃豆,哪是綠豆...),通過flex處理(詞法分析)將輸出切分成一段一段的token(將輸入的豆子一個個摘出來),從而執(zhí)行不同的action(黃豆就磨豆?jié){(action),綠豆去做綠豆糕(action))...
    flex 生成的tokens可以喂給Bison處理(更簡便易調(diào)試),當然也可以不喂給bison而直接自己處理就得了(如喜下例)。但是使用bison可以更方便的處理復(fù)雜的邏輯,編寫簡單,調(diào)試方便。

//本例中僅僅使用flex以及少量手寫代碼(main中),來完成字符串統(tǒng)計功能。
Yolandas-MacBook-Pro:flex-bison liuyuanyuan$ cat fb1-1.l
/* 統(tǒng)計輸入字符串*/
%{
  int chars = 0;
  int words = 0;
  int lines =0;

%}

%%
[a-zA-Z]+ {
		words++;
		chars += strlen(yytext);
          }

\n        {     chars++; lines++;}

.         {     chars++;     }

%%


int main(int args, char **argv)
{
	yylex();
	printf("lines=%6d words=%6d chars=%6d\n", lines, words, chars);
	return 0;
}

//Linux 系統(tǒng)上用 -lfl 選項編譯, Mac 的編譯選項是 -ll
Yolandas-MacBook-Pro:flex-bison liuyuanyuan$ gcc -ll lex.yy.c  -o fb1-1
Yolandas-MacBook-Pro:flex-bison liuyuanyuan$ ./fb1-1
hello 
this is yolanda
bye.
lines=     3 words=     5 chars=    28

3 flex(fast lex, scanner)文件內(nèi)容結(jié)構(gòu)(*.l, 分3部分)

/* P1: declarations(定義段) */
%{
  
%}

%%
  /* P2: translation rules(規(guī)則段) */
%%

/* P3: auxiliary functions(用戶輔助程序段,c函數(shù))*/

定義段 包括文字塊、定義、內(nèi)部聲明等。
          C語言的頭文件、函數(shù)和變量的聲明等一般就放在%{…%}之間,這一部分的內(nèi)容會被直接復(fù)制到生成.c文件的開頭部分。

          包含%option選項

%option noyywrap /* 定義段中包含了option選項*/

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

 規(guī)則段 %%...%%之間部分,為一系列匹配模式(正則表達式)和動作(C代碼)。

          當flex掃描程序運行時,它把輸入與規(guī)則段的模式進行匹配,每次發(fā)現(xiàn)一個匹配(被匹配的輸入稱為標記(token))時就執(zhí)行與那種模式相關(guān)的C代碼。

pattern(正則表達式) { action(c代碼) }

example:
[0-9]+ {yylval = atoi(yytest); return NUMBER;}

用戶輔助程序段 為C代碼,會被原樣復(fù)制到c文件中,一般這里定義一些輔助函數(shù)等。

int terror(chr *s)
{
    printf("%s\n", s);
    return 0;
}


4 bison(yacc,  parser)文件內(nèi)容結(jié)構(gòu)(*.y , 分3部分,%%分隔)

/*P1: declarations 定義段*/
%{

%}
 
%%
/*P2: grammar rules 規(guī)則段(rule-action)*/
    
     A: a1  {  語義動作1 }
	  | a2  {  語義動作2 }
	  | …
	  | an  {  語義動作n }
	  | b  //沒有{…},則使用缺省的語義動作       
	; //產(chǎn)生式結(jié)束標記
    //語義動作一般是在產(chǎn)生式右部分析完,歸約動作進行前執(zhí)行。
    A→ a1 | a2 |  … | an | b 
%%

/* P3: supporting C routines 用戶輔助程序段(C函數(shù)) */

定義段 可以分為兩部分:

          1. %{ 和%}之間的部分,C語言編寫的,包括頭文件include、宏定義、全局變量定義、函數(shù)聲明等;

          2. 對文法的終結(jié)符和非終結(jié)符做一些相關(guān)聲明。

             常用的Bison標記聲明有:%token %union %start %type %left %right   %nonassoc等。

%token 定義文法中使用了哪些終結(jié)符。定義形式: %token TOKEN1 TOKEN2
       終結(jié)符一般全大寫;(如 TOKEN1 TOKEN2)
       一行可定義多個終結(jié)符,空格分隔;

%left、%right、%nonassoc也是定義文法中使用了哪些終結(jié)符。定義形式與%token類似。
       先定義的優(yōu)先級低,最后定義的優(yōu)先級最高,同時定義的優(yōu)先級想通過。
       %left表示左結(jié)合,%right表示右結(jié)合;
       %nonassoc 表示不可結(jié)合(即它定義的終結(jié)符不能連續(xù)出現(xiàn)。例如<,如果文法中不允許出現(xiàn)形如a<b<c的句子,則<就是不可結(jié)合的)
       
      %left  AA BB
      %nonassoc CC
      %right DD
      表示優(yōu)先級關(guān)系為:AA=BB<CC<DD,表示結(jié)核性為:AA\BB左結(jié)合, DD右結(jié)合, CC不可結(jié)合。
 
      注意:也可以于%prec來結(jié)合使用來改變token的優(yōu)先級和結(jié)合性 例如:  :'-' expr %prec NEG { $$ = -$2; };

%union 聲明語法符號的語義值類型的集合,
       bison中每個符號包括記號和非終結(jié)符都有一個不同數(shù)據(jù)類型的語義值,并使用yylval變量在移進和歸約中傳遞這些值,YYSTYPE(宏定義)為yylval的類型,默認為int;
       我們使用%union來重新定義符號的類型
       %union
       {
           int iValue;  /* integer value */
           char sIndex; /* symbol table index */
           nodeType *nPtr; /* node pointer */
       };


%type 定義非終結(jié)符其屬性值的類型。
      %type sym1 sym2
      %type <sindex>sym3

%start 指定文法的開始的文法符號(非終結(jié)符),是最終需要規(guī)約而成的符號。
       定義形式為:%start startsym , 其中startsym為文法的開始符號。
       如果不使用%start定義文法開始符號,則默認在第二部分規(guī)則段中定義的第一條生產(chǎn)式規(guī)則的左部非終結(jié)符為開始符號。

規(guī)則段 由rule(語法規(guī)則)和action(包括C代碼)組成。

          bison規(guī)則基本是BNF,做了一點簡化以易于輸入。

          規(guī)則中目標或非終端符放在左邊,后跟一個冒號:然后是產(chǎn)生式的右邊,之后是對應(yīng)的動作(用{}包含)。

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

  expr: INTEGER {$$ = $1; }
           | expr '+' expr { $$ = $1 + $3; }
           | expr '-' expr { $$ = $1 - $3}
  ;
%%

 注意:$1表示右邊的第一個標記的值,$2表示右邊的第二個標記的值,依次類推。$$表示歸約后的值。

怎么用FLex與Bison實現(xiàn)計算器

用戶輔助程序段 為C代碼,會被原樣復(fù)制到c文件中,這里一般自定義一些函數(shù)。

5 flex-bison協(xié)作方式

怎么用FLex與Bison實現(xiàn)計算器

6 macOS下flex/bison安裝

brew install flex
brew install bison
# macos下flex/bison安裝簡單方便,另需安裝gcc用于編譯c語言程序。
brew install gcc

7 flex與bison編寫計算器

flex詞法文件:calc.l

%option noyywrap

%{
    /*
     *  一個簡單計算器的Lex詞法文件
     */
	void yyerror(char*);
	#include "calc.tab.h"
%}

%%

     /* a-z為變量 */   
[a-z]	{
            yylval = *yytext - 'a';
            return VARIABLE;
    	}

    /* 整數(shù) */
[0-9]+	{
            yylval = atoi(yytext);
            return INTEGER;
    	}

    /* 運算符 */
[-+()=/*\n]	{return *yytext;}

    /* 空白被忽略 */
[ \t]    ;

    /* 其他字符都是非法的 */
.    yyerror("invalid input");

%%

bison語法文件:calc.y

%token    INTEGER VARIABLE
%left    '+' '-'
%left    '*' '/'

%{
	#include <stdio.h>   
    void 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;}
    ;
%%

void yyerror(char* s)
{
    fprintf(stderr, "%s\n", s);
}

int main(void)
{
    printf("A simple calculator.\n");
    yyparse();
    return 0;
}

Makefile 文件:

all: clean calc


calc: calc.l calc.y
	bison -d calc.y
	flex calc.l
	cc -o $@ calc.tab.c lex.yy.c -lm

clean:
	rm -f calc \
	lex.yy.c calc.tab.c calc.tab.h

執(zhí)行

(可以直接執(zhí)行make也就是執(zhí)行Makefile中定義的內(nèi)容,也可以單步執(zhí)行)

Yolandas-MBP:calcSimple liuyuanyuan$ ls -l
total 32
-rw-r--r--@ 1 liuyuanyuan  staff  157  8 13 09:53 Makefile
-rw-r--r--@ 1 liuyuanyuan  staff  507  8 13 10:01 calc.l
-rw-r--r--@ 1 liuyuanyuan  staff  731  8 13 23:04 calc.y

Yolandas-MBP:calcSimple liuyuanyuan$ make
rm -f calc \
	lex.yy.c calc.tab.c calc.tab.h
bison -d calc.y
flex calc.l
cc -o calc calc.tab.c lex.yy.c -lm

Yolandas-MBP:calcSimple liuyuanyuan$ ls -l
total 272
-rw-r--r--@ 1 liuyuanyuan  staff    157  8 13 09:53 Makefile
-rwxr-xr-x  1 liuyuanyuan  staff  24600  8 14 12:00 calc
-rw-r--r--@ 1 liuyuanyuan  staff    507  8 13 10:01 calc.l
-rw-r--r--  1 liuyuanyuan  staff  42011  8 14 12:00 calc.tab.c
-rw-r--r--  1 liuyuanyuan  staff   2143  8 14 12:00 calc.tab.h
-rw-r--r--@ 1 liuyuanyuan  staff    731  8 13 23:04 calc.y
-rw-r--r--  1 liuyuanyuan  staff  44590  8 14 12:00 lex.yy.c

Yolandas-MBP:calcSimple liuyuanyuan$ ./calc
A simple calculator.
1+2
3

到此,相信大家對“怎么用FLex與Bison實現(xiàn)計算器”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI