flex和bison实例分析

        最近在学习编译原理,利用flex和bison编写一个基于文本识别的简单计算器程序,参考《flex于bison》中内容,对程序进行一些简单的修改,加入Makefile。该计算器程序主要实现识别文本中的整数和运算符,进行加减乘除四则运算,暂不考虑括号对运算顺序的影响。
        flex用于词法分析器的构建,bison用于语法分析器构建,两者可以结合使用,利用bison生成源程序可以直接调用flex生成源程序中yylex()函数,提取标准输入文本中的目标字符,虽然main函数可以放到flex或bison的源码文件的用户自定义部分,但是不便于阅读和理解。因此将main函数单独放到一个cpp文件中,调用bison源码文件中的yyparse()函数对输入内容进行解析和处理。将计算器的运算实现放到bison源码的规则部分。
       创建test.l文件,编写词法分析规则(数字和符号识别)

%{
    #include 
    #include "test_yacc.h" 
    //因为使用了bison在test_yacc.h中自动生成的token枚举

    extern int yylval; 
    //yylval定义在bison库中
%}

number [0-9]

%%
{number}+ {
    yylval = atoi(yytext);
    printf("number: %d  ", yylval);
    return INT;
}

"+" {
    printf("operator: +  ");
    return ADD; 
}

"-" {
    printf("operator: -  ");
    return SUB;
}

"*" {
    printf("operator: *  ");
    return MUL;
}

"/" {
    printf("operator: /  ");
    return DIV;
}

\n { return EOL; }

[ \t] {}

. { //该条规则匹配除以上字符外的剩余字符,并且报错
    printf("error: input illegal character '%s'.\n", yytext);
}
%%

void yyerror(const char *msg) 
//该函数的重写可以放在任意位置,bison的yyparse()会调用该函数,需在.y文件中声明此函数
//传参必须为const,不然编译会报warning
{
    fprintf(stderr, "error: %s.\n", msg);
}

        创建test.y文件,编写语法分析规则(计算器的运算处理),cal为创建的空规则,专门用于判断是否有换行,如果有换行,完成一次cal的规约,exp属性栈弹出,即exp清零。根据BNF表示方法,将首处理符号INT放在最底层规则,其次优先匹配乘除法,在处理乘除法之后再匹配加减法。

%{
    #include 

    extern int yylex();
    extern void yyerror(const char *);
%}

%token INT
%token ADD SUB MUL DIV
%token EOL
%%
cal : 
    | cal exp EOL { printf("\n caculation result: %d\n", $2); }
    ;

exp : factor
    | exp ADD factor { $$ = $1 + $3; } 
    | exp SUB factor { $$ = $1 - $3; } 
    ;

factor : term
    | factor MUL term { $$ = $1 * $3; } 
    | factor DIV term { $$ = $1 / $3; } 
    ;

term : INT
    ;
%%

        创建testMain.cpp,编写main函数内容:

#include 

//extern int yylex();
extern int yyparse();

int main(int argc, char ** args) 
{
    //yylex(); 
    yyparse(); //调用bison的yyparse()函数

    return 0;
}

        编写编译Makefile脚本

ECHO = echo
CXX  = g++

TARGET = ./testDemo

LIBS   = -lfl

CFLAGS = -Wall
CFLAGS += -std=c++11

OBJECTS =  testMain.o 
OBJECTS += test_yacc.o
OBJECTS += test_lex.o

$(TARGET) : $(OBJECTS)
    $(CXX) -o $(TARGET) $(OBJECTS) $(LIBS) $(CFLAGS) 

.cpp.o :
    $(CXX) -o $@ -c $<

#生成test_yacc.cpp、test_yacc.h
#test_yacc.h会生成token的相关枚举
test_yacc.cpp : test.y
    bison -d $< -b test_yacc
    mv test_yacc.tab.c test_yacc.cpp
    mv test_yacc.tab.h test_yacc.h

#生成test_lex.cpp
test_lex.cpp : test.l
    flex -o $@ $<

.PHONY : clean

clean:
    rm $(OBJECTS) $(TARGET) test_yacc.h test_yacc.cpp test_lex.cpp

        编译:

flex和bison实例分析_第1张图片

     yylex()返回记号token标志,标志0-255被保留作为字符值,可以自己定义token标志的值,一般由bison产生的token标志默认从258开始。本例中,由bison生成的test_yacc.h文件,其中token枚举定义:

flex和bison实例分析_第2张图片

       调试结果:

你可能感兴趣的:(编译原理,程序编译,编译器)