我试图自己研究编译器构建。我正在读一本书,这是其中一项练习(我想强调的是,这是不是家庭作业,我自己这样做。)
以下语法表示一个简单的算术表达式 类似LISP的前缀表示法
lexp - > 数字 | (op lexp-seq)
op - > + | * | +
lexp-seq - > lexp-seq lexp | lexp
例如,表达式(*( - 2)3 4)的值为-24。写 Yacc / Bison规范,用于计算和打印的程序 此语法中表达式的值。 (提示:这将需要 重写语法,以及使用传递机制 操作符为 lexp-seq
我已经解决了。解决方案如下。但是我对我的解决方案以及问题本身有疑问。他们在这里:
我没有在我的解决方案中修改语法,它似乎完美无缺。将Yacc / Bison规范转换为.c文件时没有冲突。那么为什么作者说我需要重写一个语法?
我的解决方案是使用堆栈作为将操作符传递给 lexp-seq 的机制。有人可以提出一个不同的方法,一个不使用堆栈的方法吗?
这是我对问题的解决方案(我没有发布堆栈操作的代码,因为假设读者熟悉堆栈的工作方式)
%{
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "linkedstack.h"
int yylex();
int yyerror();
node *operatorStack;
%}
%token NUMBER
%%
command : lexp { printf("%d\n", $1); };
lexp : NUMBER { $$ = $1; }
| '(' op lexp_seq ')'
{
int operator;
operatorStack = pop(operatorStack, &operator);
switch(operator) {
default:
yyerror("Unknown operator");
exit(1);
break;
case '+':
case '*':
$$ = $3;
break;
case '-':
$$ = -$3;
break;
}
}
;
op : '+' { operatorStack = push(operatorStack, '+'); }
| '-' { operatorStack = push(operatorStack, '-'); }
| '*' { operatorStack = push(operatorStack, '*'); }
;
lexp_seq : lexp_seq lexp
{
switch(operatorStack->data) {
default:
yyerror("Unrecognized operator");
exit(1);
break;
case '+':
$$ = $1 + $2;
break;
case '-':
$$ = $1 - $2;
break;
case '*':
$$ = $1 * $2;
break;
}
}
| lexp { $$ = $1; }
;
%%
int main(int argc, char** argv) {
int retVal;
init(operatorStack);
if (2 == argc && (0 == strcmp("-g", argv[1])))
yydebug = 1;
retVal = yyparse();
destroy(operatorStack);
return retVal;
}
int yylex() {
int c;
/* eliminate blanks*/
while((c = getchar()) == ' ');
if (isdigit(c)) {
ungetc(c, stdin);
scanf("%d", &yylval);
return (NUMBER);
}
/* makes the parse stop */
if (c == '\n') return 0;
return (c);
}
int yyerror(char * s) {
fprintf(stderr, "%s\n", s);
return 0;
} /* allows for printing of an error message */
答案 0 :(得分:2)
如果你重写语法,就不需要在这里使用堆栈。
一种方法是为每个运营商使用不同的非终端:
command : lexp '\n' { printf("%d\n", $1); }
lexp : NUMBER
| '(' op_exp ')' { $$ = $2; }
op_exp : plus_exp | times_exp | minus_exp
plus_exp: '+' lexp { $$ = $2; }
| plus_exp lexp { $$ = $1 + $2; }
times_exp: '*' lexp { $$ = $2; }
| times_exp lexp { $$ = $1 * $2; }
minus_exp: '-' lexp { $$ = -$2; }
| minus_exp lexp { $$ = $1 - $2; }
我不知道这是你的书作者的想法。当然还有其他可能的实施方式。
在一个真正类似于lisp的语言中,你需要完全不同的方式,因为lexp中的第一个对象可能是一个高阶值(即一个函数),甚至可能是函数调用的结果,因此您无法将操作编码为语法(并且在解析新参数时也不一定能部分地计算表达式。)