lex与yacc(二)计算器的实现

构建一个c语言的编译器并不是一件容易的事,我想每个人在学习编译原理的时候并不会常见得它非常简单.
下面将会学习编译器的两个重要组成部分:词法分析器flex和语法分析器yacc
flex是gun实现的fast lex(lexical anslysis)
yacc实现有gun的bison和berkeley的byacc
flex与yacc程序是由下面三部分组成
%{
/*注释用空白缩进
 */
%}
%%标记这一部分结束
%%第二部分与第三部分分割
第一部分
定义段,拷贝到最终程序中的原始的c程序代码,如头文件.
第二部分
规则段,由两部分组成:模式和动作,空白分开,记法分析程序识别出某个模式时,将执行相应的动作
模式为unix样式的正则表达式
第三部分
用户子程序段,合法c代码组成.在lex生成代码结束之后复制到c文件.
calc.l
%{
#include "y.tab.h"
#include "ch3hdr2.h"
#include <math.h>
%}


%%
([0-9]+|([0-9]*\.[0-9]+)([eE][-+]?[0-9]+)?) {
                yylval.dval = atof(yytext);
                return NUMBER;
        }


[ \t]   ;                /* ignore white space */


[A-Za-z][A-Za-z0-9]*    {       /* return symbol pointer */
                struct symtab *sp = symlook(yytext);


                yylval.symp = sp;
                return NAME;
        }


"{1}quot;     { return 0; }


\n      |
.       return yytext[0];
%%
在编写好规则后,调用yylex来根据进行规则词法分析.
而这些规则是由正则表达式写成的.
calc.y
%{
#include "ch3hdr2.h"
#include <string.h>
#include <math.h>
%}


%union {
        double dval;
        struct symtab *symp;
}
%token <symp> NAME
%token <dval> NUMBER
%left '-' '+'
%left '*' '/'
%nonassoc UMINUS


%type <dval> expression
%%
statement_list: statement '\n'
        |       statement_list statement '\n'
        ;


statement:      NAME '=' expression     { $1->value = $3; }
        |       expression              { printf("= %g\n", $1); }
        ;


expression:     expression '+' expression { $ = $1 + $3; }
        |       expression '-' expression { $ = $1 - $3; }
        |       expression '*' expression { $ = $1 * $3; }
        |       expression '/' expression
                                {       if($3 == 0.0)
                                                yyerror("divide by zero");
                                        else
                                                $ = $1 / $3;
                                }
        |       '-' expression %prec UMINUS     { $ = -$2; }
        |       '(' expression ')'      { $ = $2; }
        |       NUMBER
        |       NAME                    { $ = $1->value; }
        |       NAME '(' expression ')' {
                        if($1->funcptr)
                                $ = ($1->funcptr)($3);
                        else {
                                printf("%s not a function\n", $1->name);
                                $ = 0.0;
                        }
                }
        ;
%%
/* look up a symbol table entry, add if not present */
struct symtab *
symlook(s)
char *s;
{
        char *p;
        struct symtab *sp;


        for(sp = symtab; sp < &symtab[NSYMS]; sp++) {
                /* is it already here? */
                if(sp->name && !strcmp(sp->name, s))
                        return sp;


                /* is it free */
                if(!sp->name) {
                        sp->name = strdup(s);
                        return sp;
                }
                /* otherwise continue to next */
        }
        yyerror("Too many symbols");
        exit(1);        /* cannot continue */
} /* symlook */


addfunc(name, func)
char *name;
double (*func)();
{
        struct symtab *sp = symlook(name);
        sp->funcptr = func;
}


main()
{
        extern double sqrt(), exp(), log();


        addfunc("sqrt", sqrt);
        addfunc("exp", exp);
        addfunc("log", log);
        yyparse();
}
lex将输入流分成块,然后yacc取得这些块并将它们逻辑性地归组到一起.
yacc语法分析器yyparse()重复尝试分析句子直到输入结束.

需要输入时调用词法分析程序yylex(),把匹配的标记返回给程序.要注意的是这里的递归规则.

编译:

lex calc.l
yacc -d calc.y
gcc y.tab.c lex.yy.c -ly -ll
语法分析程序分析过程移动与归约

yacc语法分析程序是通过寻找可以匹配目前为止所看到的标记的规则来工作.yacc处理语法分析程序时创建了一组状态,每个状态反映一个或多个部分地被分析的规则中的一个可能位置.当语法分析程序读取标记时,每次它读取一个没完成规则的标记,就把它压入内部堆栈中并切换到一种反映它刚刚读取的标记的新状态,这个动作称为移进(shift).当它发现组成某条规则右侧的全部符号时,它就把右侧符号弹出堆栈,而将左侧符号压入堆栈中,并切换到反映堆栈上新符号的新状态,这个动作被称为归约(reduction).因为它通常减少堆栈上项目的数目.无论yacc什么时候归约规则,它都执行与这条规则有关的用户代码.

参考资料:<lex与yacc>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值