小白说编译原理-3-yacc基本用法

前言

这个编译原理是一个系列,系列地址为: https://blog.csdn.net/lpstudy/article/category/937055
考虑到很多小伙伴咨询代码的问题,现把链接发出来:https://github.com/lpstudy/compile
这个链接里面具有这个系列所有的VS工程和代码,工程是按照系列中的一个教程环境配置6来配置的,不过lib我好像没有上传到github。
如果大家发现任何问题,可以在github或者csdn,我有空的时候完善一下,争取做到下载github工程即可跑。

前提说明

前面lex的例子说明了可以通过定义正规式的方式,实现一个词法分析器。 它主要是通过lex编译器将用户定义的正规式以及相应的action编译成对应的c++代码,交给c++编译器生成可执行程序exe。
同样的,应该可以定义语法规则,并由语法编译器将语法规则以及action编译成相应的c++代码,并执行。
这就是我们这节要说明的yacc。

yacc规则

声明部分
C++头文件,函数声明
文法记号的声明(类似于lex中词法token的声明),例如%token DIGIT
%%
翻译规则
%%
翻译规则主要文法产生式和对应的动作组成。
例如 expr : expr ‘+’ term {$$ = $1 + $2}
其他c++语言的代码

#yacc示例,简单计算器
%{

%}



%name yacc
// class definition
{
	// place any extra class members here
	virtual int yygettoken();
}
// constructor
{
	// place any extra initialisation code here
}

// destructor
{
	// place any extra cleanup code here
}

// place any declarations here
%include {
#ifndef YYSTYPE
#define YYSTYPE double
#endif
}

%%

lines : lines expr '\n' {printf("%f\n", $2);}
	  | lines '\n'
	  |//empty line
	  ;

expr : expr '+' term {$$ = $1 + $3;}	
        | expr '-' term {$$ = $1 - $3;}	
        | term
        ;
term : term '*' factor {$$ = $1 * $3;}    
        | term '/' factor {$$ = $1 / $3;}        
        | factor {$$=$1;}
        ;
factor : '(' expr ')'   {$$=$2;}        
         | DIGIT
         ;
DIGIT	:	'0'				{ $$ = 0.0; }
		|	'1'				{ $$ = 1.0; }
		|	'2'				{ $$ = 2.0; }
		|	'3'				{ $$ = 3.0; }
		|	'4'				{ $$ = 4.0; }
		|	'5'				{ $$ = 5.0; }
		|	'6'				{ $$ = 6.0; }
		|	'7'				{ $$ = 7.0; }
		|	'8'				{ $$ = 8.0; }
		|	'9'				{ $$ = 9.0; }
		;      
%%


int YYPARSERNAME::yygettoken()
{
	return getchar();
}
int main()
{
//create a lexer, and call the lex function.
//it will read from stdin and parser the tokens.
	int n = 1;
	
	YYPARSERNAME parser;
	if (parser.yycreate()) 
	{
		n = parser.yyparse();
	}
	return n;
}

计算器解释

  1. 添加gettoken函数
    virtual int yygettoken();
  2. expr : expr ‘+’ term {$$ = $1 + $3;}
    计算加法,$$表示当返回值,$1表示第一个参数,$3表示第三个参数
  3. main
    创建parser语法分析器,调用gettoken分析,即可。

巧妙的地方

1,支持多行
多行的关键在于lines的表示,lines既可以表示为空,又可以表示为lines expr ‘\n’
2,支持算符优先级
term和factor的使用,先使用term,再使用factor
3,支持括号
最终的factor因子的表示,既可以表示单个数字,还可以表示(expr)。
3,只支持单个数字的加减乘除
这是由于对于DIGIT的解释只支持单个数字

更新版本支持浮点数

%{
#include <stdio.h>
%}



%name yacc
// class definition
{
	// place any extra class members here
	virtual int yygettoken();
	double yyvalue;
}
// constructor
{
	// place any extra initialisation code here
}

// destructor
{
	// place any extra cleanup code here
}

// place any declarations here
%include {
#ifndef YYSTYPE
#define YYSTYPE double
#endif
}

%token NUMBER

%%

lines : lines expr '\n' {printf("%f\n", $2);}
	  | lines '\n'
	  |//empty line
	  ;

expr : expr '+' term {$$ = $1 + $3;}	
        | expr '-' term {$$ = $1 - $3;}	
        | term
        ;
term : term '*' factor {$$ = $1 * $3;}    
        | term '/' factor {$$ = $1 / $3;}        
        | factor {$$=$1;}
        ;
factor : '(' expr ')'   {$$=$2;}        
         | NUMBER {$$=yyvalue;}
         ;     
%%

int YYPARSERNAME::yygettoken()
{
    int c;
    double value;
    while((c = getchar())  == ' ');//jump the empty
    
    //here not the blank
    if(isdigit(c) || c == '.'){
        ungetc(c, stdin);
		cin>>value;
		yyvalue = value;
		return NUMBER;
    }
	
	return c;
}
int main()
{
//create a lexer, and call the lex function.
//it will read from stdin and parser the tokens.
	int n = 1;
	
	YYPARSERNAME parser;
	if (parser.yycreate()) 
	{
		n = parser.yyparse();
	}
	return n;
}

修改的地方

  1. 删除了DIGIT采用语法分析器进行的表达,过去gettoken直接返回char型字符,由DIGIT或者+,-等进行语法翻译。现在使用一个token标记NUMBER, 它代表一个数字。这个NUMBER标记是由对应的词法分析gettoken进行返回的。
  2. gettoken中,如果遇到数字,则使用cin方式读取double型变量,保存到变量yylvalue中,这样当语法分析的过程中,遇到number,将yylvalue赋值给当前返回即可,即可以读取到浮点值。
  3. 为了更好的适应性,词法分析中还进行了跳过空格的操作,对于非数字不进行解析,直接返回。

本人lpstudy,转载请注明出处: http://blog.csdn.net/lpstudy/article/details/51225953

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值