第1关:用Bison构建逆波兰计算器

文章介绍了如何利用YACC或Bison工具创建一个语法分析器,特别是用于处理逆波兰表达式的计算器。它涉及到BNF范式来描述产生式,Bison的语法结构,并提供了代码示例来实现加、减、乘、除、乘方和取负运算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

任务描述
相信大家通过flex的实验已经掌握了如何构建一个词法分析器,但是为了创建一个完整的编译程序,我们还需要一个语法分析器。同样的,我们可以使用现有的工具来节省开发的时间,也就是Unix下的YACC和GNU/Linux下的Bison。
相关知识
逆波兰表达式
逆波兰表达式,它的语法规定,表达式必须以逆波兰表达式的方式给出。逆波兰表达式又叫做后缀表达式。下面是一些例子:

a+b —> a b +
a+(b-c) —> a b c - +
a+(b-c)d —> a b c - d * +
a+d
(b-c) —> a d b c - * +
a=1+3 —> a 1 3 + =
逆波兰表达式的逻辑实现是通过栈来完成的,如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个(一个)元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。

BNF范式
由于Bison中使用的是BNF范式来描述产生式,这里先简单介绍一下BNF范式。
BNF规定是推导规则(产生式)的集合,写为:
<符号> ::= <使用符号的表达式>
这里的 <符号> 是非终结符,而表达式由一个符号序列,或用指示选择的竖杠’|'分隔的多个符号序列构成,每个符号序列整体都是左端的符号的一种可能的替代。从未在左端出现的符号叫做终结符。

BNF类似一种数学游戏:从一个符号开始(叫做起始标志,实例中常用S表示),然后给出替换前面符号的规则。BNF语法定义的语言只不过是一个字符串集合,你可以按照下述规则书写,这些规则叫做书写规范(生产式规则),形式如下:
symbol := alternative1 | alternative2 …

Bison语法结构
Bison的语法结构和lex/flex类似,也是分为三个部分

/* 定义段 /
%{
C/C++头文件、全局文件、全局变量、类型定义
词法分析器yylex(采用lex进行词法分析)和错误打印函数
%}
Bison声明区间。定义之后用到的终结符、非终结符、操作符优先级
%%
/
规则段 /
Bison语法规则定义
%%
/
用户子程序段 */
C/C++代码 需要定义prologue区域函数,或者其他代码,生成的c/c++文件会完全拷贝这份代码。
终结符、非终结符定义
Token用于定义终结符,type定义非终结符,操作符也属于终结符,left表示左关联运算符,right表示右关联运算符,下面是一些例子:

%token NUM
%nonassoc ‘<’ /表示该终结符无结合性 不能出现a<b<c/
%left ‘+’ ‘-’ /左结合 后面接操作符 下方的操作符比上方的优先级高/
%left ‘’ ‘/’
%right NEG NEG表示非
%right ‘^’
本关任务是利用YACC/Bison构建一个逆波兰符号计算器。
编程要求
根据提示,在右侧编辑器补充代码,实现加法(+)、减法(-)、乘法(
)、除法(/)、乘方(^)以及取负运算(n)!

测试说明
平台会对你编写的代码进行测试:

测试输入:4 5 +,3 4 ^,2 7 + 3 /,16 4 / 12 * n;
预期输出:
9
81
3
-48

 /* 逆波兰符号计算器 */
 /* 功能:能够计算出合法的后缀表达式的值,包括加法、减法、乘法、除法、乘方、取反等运算 */
 /* 说明:在下面的begin和end之间添加代码,加油吧! */
 /* 提示: */

%{
  #include <ctype.h>
  #include <stdio.h>
  #include <math.h>
  int yylex (void);
  void yyerror (char const *);
%}


%define api.value.type {double}
%token NUM

%% 

/* Grammar rules and actions follow.  */

input:
  %empty
| input line
;


line:
  '\n'
| exp '\n'      { printf ("%.10g\n", $1); }
;


exp:
  NUM           { $$ = $1;           }
 /* begin */
| exp exp '+'  { $$ = $1 + $2;      }
| exp exp '-'  { $$ = $1 - $2;      }
| exp exp '*'  { $$ = $1 * $2;      }
| exp exp '/'  { $$ = $1 / $2;      }
| exp exp '^'  { $$ = pow($1,$2);   }
| exp 'n'      { $$ = -1 * $1;      }
 /* end */
;

%%

/* The lexical analyzer returns a double floating point
   number on the stack and the token NUM, or the numeric code
   of the character read if not a number.  It skips all blanks
   and tabs, and returns 0 for end-of-input.  */


int yylex (void)
{
  int c;

  /* Skip white space.  */
  while ((c = getchar ()) == ' ' || c == '\t')
    continue;

  /* Process numbers.  */
  if (c == '.' || isdigit (c))
    {
      ungetc (c, stdin);
      scanf ("%lf", &yylval);
      return NUM;
    }

  /* Return end-of-input.  */
  if (c == EOF)
    return 0;
  if (c == '!')
  	return 0;
  /* Return a single char.  */
  return c;
}

int main (int argc, char** argv)
{
  return yyparse();
}


/* Called by yyparse on error.  */
void yyerror (char const *s)
{
  fprintf (stderr, "%s\n", s);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值