我正在使用Bison和Flex开发类似C语言的编译器。目前,编译器能够识别具有声明,赋值和打印语句以及算术和逻辑表达式的语言(仅使用int变量)。它会生成一个3AC(以及一些用于管理内存的指令)。这是我的野牛代码:
%{
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "list.h"
int yylex();
void yyerror(char *s);
TList list = NULL;
int i=0;
char* tmp() {
char* t = (char*)malloc(sizeof(char*));
sprintf(t, "t%d", i);
i++;
return t;
}
%}
%union {
int number;
char* identifier;
}
%token <number> NUM
%token <identifier> ID
%token PRINT INT ENDFILE
%left '+' '-'
%left '*' '/'
%right UMINUS
%left OR
%left AND
%right NOT
%nonassoc EQ LT GT LE GE NE
%type <identifier> expr
%type <identifier> comp
%type <identifier> bexpr
%%
program : lstmt ENDFILE { return 0; }
;
lstmt : lstmt stmt ';'
| stmt ';'
| lstmt openb lstmt closeb
| openb lstmt closeb
;
openb : '{' { printf("list = insertElement(list);\n"); }
;
closeb : '}' { printf("list = removeElement(list);\n"); }
;
stmt : INT ID { printf("addVar(\"%s\", &list->table);\n", $2); }
| INT ID '=' NUM {
printf("addVar(\"%s\", &list->table);\n", $2);
printf("setVarList(\"%s\", %d, list);\n", $2, $4);
}
| ID '=' expr { printf("setVarList(\"%s\", %s, list);\n", $1, $3); }
| PRINT '(' ID ')' { printf("printf(\"%%s: %%d\\n\", \"%s\", getVarList(\"%s\", list));\n", $3, $3); }
| ID '=' bexpr { printf("setVarList(\"%s\", %s, list);\n", $1, $3); }
;
bexpr : bexpr OR bexpr {
$$ = tmp();
printf("%s = %s || %s;\n", $$, $1, $3);
}
| bexpr AND bexpr {
$$ = tmp();
printf("%s = %s && %s;\n", $$, $1, $3);
}
| expr OR bexpr {
$$ = tmp();
printf("%s = %s || %s;\n", $$, $1, $3);
}
| expr AND bexpr {
$$ = tmp();
printf("%s = %s && %s;\n", $$, $1, $3);
}
| bexpr OR expr {
$$ = tmp();
printf("%s = %s || %s;\n", $$, $1, $3);
}
| bexpr AND expr {
$$ = tmp();
printf("%s = %s && %s;\n", $$, $1, $3);
}
| NOT bexpr {
$$ = tmp();
printf("%s = !%s;\n", $$, $2);
}
| '(' bexpr ')' { $$ = $2; }
| comp { $$ = $1; }
;
comp : expr LT expr {
$$ = tmp();
printf("%s = %s < %s;\n", $$, $1, $3);
}
| expr LE expr {
$$ = tmp();
printf("%s = %s <= %s;\n", $$, $1, $3);
}
| expr GT expr {
$$ = tmp();
printf("%s = %s > %s;\n", $$, $1, $3);
}
| expr GE expr {
$$ = tmp();
printf("%s = %s >= %s;\n", $$, $1, $3);
}
| expr EQ expr {
$$ = tmp();
printf("%s = %s == %s;\n", $$, $1, $3);
}
| expr NE expr {
$$ = tmp();
printf("%s = %s != %s;\n", $$, $1, $3);
}
| expr AND expr {
$$ = tmp();
printf("%s = %s && %s;\n", $$, $1, $3);
}
| expr OR expr {
$$ = tmp();
printf("%s = %s || %s;\n", $$, $1, $3);
}
| NOT expr {
$$ = tmp();
printf("%s = !%s;\n", $$, $2);
}
;
expr : expr '+' expr {
$$ = tmp();
printf("%s = %s + %s;\n", $$, $1, $3);
}
| expr '-' expr {
$$ = tmp();
printf("%s = %s - %s;\n", $$, $1, $3);
}
| expr '*' expr {
$$ = tmp();
printf("%s = %s * %s;\n", $$, $1, $3);
}
| expr '/' expr {
$$ = tmp();
printf("%s = %s / %s;\n", $$, $1, $3);
}
| '(' expr ')' { $$ = $2; }
| '-' expr %prec UMINUS {
$$ = tmp();
printf("%s = -%s;\n", $$, $2);
}
| ID {
$$ = tmp();
printf("%s = getVarList(\"%s\", list);\n", $$, $1);
}
| NUM {
$$ = tmp();
printf("%s = %d;\n", $$, $1);
}
;
%%
int main () {
list = insertElement(list);
if(yyparse() !=0)
fprintf(stderr, "Abonormal exit\n");
fprintf(fopen("temp.h", "w"), "#include \"list.h\"\n\nTList list = NULL;\nint t" );
for(int j=0; j<i-1; j++) {
fprintf(fopen("temp.h", "a"), "%d, t", j);
}
fprintf(fopen("temp.h", "a"), "%d;", i-1);
return 0;
}
void yyerror (char *s) {
fprintf(stderr, "Error: %s\n", s);
}
您可以看到逻辑表达式的语法有些复杂,但是编译器会执行应有的操作。该行为类似于C,因此可以在AND / OR / NOT中使用整数值。
我对语法的想法是这样的:
bexpr : bexpr OR bexpr
| bexpr AND bexpr
| NOT bexpr
| '(' bexpr ')'
| comp
| expr
;
comp : expr LT expr
| expr LE expr
| expr GT expr
| expr GE expr
| expr EQ expr
| expr NE expr
;
但是以这种方式,我遇到两个冲突,1个移位/减少和1个减少/减少。有没有一种方法可以简化语法?
我的建议是不要尝试在语法上区分bexpr
和expr
。由于您允许将变量用作布尔值,因此无法真正准确地做到这一点。当然,您当前的语法是一种英勇的工作,但是当您在语法中添加条件语句时,您会发现
if ((a)) ...
将被标记为语法错误(假设条件语句的语法类似于C:"if" '(' bexpr ')' stmt | "if" '(' bexpr ')' stmt "else" stmt
)。由于a AND (1 + -1)
是有效的bexpr
,因此很容易规避禁止将算术表达式用作布尔运算符的参数的尝试。可能还会有人问为什么(a < b) == (b < c)
不应该是有效的语法。当然,这有点令人困惑,但这是写“ b
在a
和c
之间”的便捷方法。
[如果您确实想禁止将算术运算用作布尔运算符AND
,OR
和NOT
的参数,则可以通过创建两个并行的层次结构或通过简单地标记操作符的类型来改进语法。表达式作为其语义值的一部分,并在每个语义动作中进行检查。第二个选项的优点是双重的:它简化了语法,消除了重复,并且提供了更精确的错误消息的可能性(“尝试将算术表达式用作布尔值”而不是“语法错误”)。