前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >理解YACC中符号的优先级和结合性

理解YACC中符号的优先级和结合性

作者头像
mingjie
发布2022-12-11 09:18:17
1.2K0
发布2022-12-11 09:18:17
举报
文章被收录于专栏:Postgresql源码分析

1 什么时候需要优先级和结合性?

代码语言:javascript
复制
expr:
  expr '-' expr
| expr '*' expr
| expr '<' expr
| '(' expr ')'
…
;

1.1 场景一:不同token如何决定计算的先后顺序?(需要优先级)

当输入1 - 2 * 3时,上面语法无法决定(1 - 2) * 3 or 1 - ( 2 * 3)

这时需要定义不同token的优先级,来决定先reduce 1-2还是reduce 2*3。

情况一:

代码语言:javascript
复制
|   |
| 3 | --- shift
|   |
| 2 | ---\
|   |     reduce: expr '-' expr
| 1 | ---/ 
|---|

情况二:

代码语言:javascript
复制
|   |
| 3 | ---\
|   |     reduce: expr '*' expr
| 2 | ---/
|   |     
| 1 | --- shift
|---|

1.2 场景二:当token优先级相同时,如何决定运算顺序?(需要结合性)

当输入1 - 2 - 5时,上面语法无法决定:(1 - 2) - 5 or 1 - (2 - 5)

这时优先级相同,需要定义结合性的方向,来决定是先reduce 1-2还是先reduce 2-5。

2 如何声明优先级与结合性?

结合性声明方式:

  • 左结合:%left
  • 右结合:%right
  • 不能结合:%nonassoc
    • 连续发现两次运算符会会报语法错误。

优先级的声明方式:

  • 不同运算符的相对优先级由声明它们的顺序控制。文件中的第一个优先级/关联性声明声明优先级最低的运算符,下一个此类声明声明优先级稍高的运算符,依此类推。

3 局部提升优先级

有些符号的优先级与上下文强绑定,例如负号

  • 作为一元运算符时有很高的优先级:-4 * 5
  • 作为二元运算符时只有中等优先级:3 - 4 * 5

yacc or bison允许临时修改优先级,使用prec语法修饰运算符:

代码语言:javascript
复制
…
%left '+' '-'
%left '*'
%left UMINUS

%%

exp:
  …
| exp '-' exp
  …
| '-' exp %prec UMINUS    // 临时提升优先级为UMINUS,UMINUS具有比*更高的优先级。

4 一个计算器实例

效果:

代码语言:javascript
复制
[mingjie@x ~/proj/lex1]$ make
bison -t -v -d calc.y
flex calc.l
gcc -o calc calc.tab.c lex.yy.c

[mingjie@x ~/proj/lex1]$ ./calc 
3-5*6
        Result: -27

calc.l

代码语言:javascript
复制
%option noyywrap

%{
#include <stdio.h>

#define YY_DECL int yylex()

#include "calc.tab.h"

%}

whitespace		[ \t]
newline			[\n]
digit			[0-9]
integer			{digit}+
decimal			(({digit}*\.{digit}+)|({digit}+\.{digit}*))

%%

{whitespace}	{
					/* ignore */
				}

{newline}		{
					return T_NEWLINE;
				}

{integer}		{
					yylval.ival = atoi(yytext);
					return T_INT;
				}

{decimal}		{
					yylval.fval = atof(yytext); 
					return T_FLOAT;
				}


"+"				{return T_PLUS;}
"-"				{return T_MINUS;}
"*"				{return T_MULTIPLY;}
"/"				{return T_DIVIDE;}
"("				{return T_LEFT;}
")"				{return T_RIGHT;}
"exit"			{return T_QUIT;}
"quit"			{return T_QUIT;}

%%

calc.y

代码语言:javascript
复制
%{

#include <stdio.h>
#include <stdlib.h>

extern int yylex();
extern int yyparse();
extern FILE* yyin;

void yyerror(const char* s);
%}

%union
{
	int ival;
	float fval;
}

%token<ival>	T_INT
%token<fval>	T_FLOAT

%token		T_PLUS T_MINUS T_MULTIPLY T_DIVIDE T_LEFT T_RIGHT
%token		T_NEWLINE T_QUIT

%left		T_PLUS T_MINUS
%left		T_MULTIPLY T_DIVIDE
%nonassoc	UMINUS

%type<ival> expr
%type<fval> fexpr

%start calculation

%%

calculation:
			| calculation line
			;

line: T_NEWLINE
			| fexpr T_NEWLINE
				{
					printf("\tResult: %f\n", $1);
				}
			| expr T_NEWLINE
				{
					printf("\tResult: %i\n", $1);
				}
			| T_QUIT T_NEWLINE
				{
					printf("bye!\n"); exit(0);
				}
			;

fexpr: T_FLOAT								{ $$ = $1; }
			| fexpr T_PLUS fexpr			{ $$ = $1 + $3; }
			| fexpr T_MINUS fexpr			{ $$ = $1 - $3; }
			| fexpr T_MULTIPLY fexpr		{ $$ = $1 * $3; }
			| fexpr T_DIVIDE fexpr			{ $$ = $1 / $3; }
			| T_LEFT fexpr T_RIGHT			{ $$ = $2; }
			| expr T_PLUS fexpr				{ $$ = $1 + $3; }
			| expr T_MINUS fexpr			{ $$ = $1 - $3; }
			| expr T_MULTIPLY fexpr			{ $$ = $1 * $3; }
			| expr T_DIVIDE fexpr			{ $$ = $1 / $3; }
			| fexpr T_PLUS expr				{ $$ = $1 + $3; }
			| fexpr T_MINUS expr			{ $$ = $1 - $3; }
			| fexpr T_MULTIPLY expr			{ $$ = $1 * $3; }
			| fexpr T_DIVIDE expr			{ $$ = $1 / $3; }
			| expr T_DIVIDE expr			{ $$ = $1 / (float)$3; }
			;

expr: T_INT							{ $$ = $1; }
			| expr T_PLUS expr		{ $$ = $1 + $3; }
			| expr T_MINUS expr		{ $$ = $1 - $3; }
			| expr T_MULTIPLY expr	{ $$ = $1 * $3; }
			| T_LEFT expr T_RIGHT	{ $$ = $2; }
			| T_PLUS expr			%prec UMINUS	{ $$ = $2; }
			| T_MINUS expr			%prec UMINUS	{ $$ = -$2; }
			;

%%

int main() 
{
	yyin = stdin;

	do
	{
		yyparse();
	} while(!feof(yyin));

	return 0;
}

void yyerror(const char* s)
{
	fprintf(stderr, "Parse error: %s\n", s);
	exit(1);
}

makefile

代码语言:javascript
复制
all: calc

calc.tab.c calc.tab.h:	calc.y
	bison -t -v -d calc.y

lex.yy.c: calc.l calc.tab.h
	flex calc.l

calc: lex.yy.c calc.tab.c calc.tab.h
	gcc -o calc calc.tab.c lex.yy.c

clean:
	rm calc calc.tab.c lex.yy.c calc.tab.h calc.output
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-12-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1 什么时候需要优先级和结合性?
    • 1.1 场景一:不同token如何决定计算的先后顺序?(需要优先级)
      • 1.2 场景二:当token优先级相同时,如何决定运算顺序?(需要结合性)
        • calc.l
        • calc.y
        • makefile
    • 2 如何声明优先级与结合性?
    • 3 局部提升优先级
    • 4 一个计算器实例
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档