flex/bison设计一个简单的语法分析器 能够将输入的单词序列分析成语法树,支持语法规则、优先级等特性
时间: 2024-03-15 13:44:56 浏览: 99
基于C语言使用flex和bison对Cminus进行词法分析和语法分析(编译原理大作业)【100012265】
5星 · 资源好评率100%
好的,以下是一个稍微复杂一些的示例,使用flex进行词法分析,使用bison进行语法分析,能够将输入的单词序列分析成语法树,支持语法规则、优先级等特性。
首先,我们需要定义一个简单的语言,支持加减乘除四则运算、函数调用、变量赋值、变量引用等操作。语法规则如下:
```
program: stmt_list
;
stmt_list: stmt
| stmt_list stmt
;
stmt: expr_stmt
| assign_stmt
| func_call_stmt
;
expr_stmt: expr ';'
;
assign_stmt: ID '=' expr ';'
;
func_call_stmt: ID '(' arg_list ')' ';'
;
arg_list: expr
| arg_list ',' expr
;
expr: expr '+' term
| expr '-' term
| term
;
term: term '*' factor
| term '/' factor
| factor
;
factor: ID
| NUMBER
| '-' factor %prec UMINUS
| '(' expr ')'
| func_call_expr
;
func_call_expr: ID '(' arg_list ')'
;
ID: [a-zA-Z_][a-zA-Z0-9_]*
;
NUMBER: [0-9]+('.'[0-9]+)? ;
```
其中,program表示整个程序,由stmt_list组成;stmt_list表示语句列表,由stmt组成;stmt表示语句,可以是表达式语句、变量赋值语句、函数调用语句;expr_stmt表示表达式语句,由expr和分号组成;assign_stmt表示变量赋值语句,由ID、等号、expr和分号组成;func_call_stmt表示函数调用语句,由ID、左括号、arg_list、右括号和分号组成;arg_list表示参数列表,由expr组成,多个参数用逗号隔开;expr表示表达式,可以是两个expr相加、相减,或者一个term;term表示项,可以是两个term相乘、相除,或者一个factor;factor表示因子,可以是一个ID、一个数字、一个负号和一个factor、一个括号内的expr,或者一个函数调用表达式;func_call_expr表示函数调用表达式,由ID、左括号、arg_list和右括号组成;ID表示标识符,由字母、数字和下划线组成,必须以字母或下划线开头;NUMBER表示数字,包括整数和浮点数。
接着,我们使用flex进行词法分析,定义一个lexer.l文件,代码如下:
```
%{
#include "y.tab.h"
%}
%%
[a-zA-Z_][a-zA-Z0-9_]* { yylval.strval = strdup(yytext); return ID; }
[0-9]+('.'[0-9]+)? { yylval.numval = atof(yytext); return NUMBER; }
"+" { return '+'; }
"-" { return '-'; }
"*" { return '*'; }
"/" { return '/'; }
"=" { return '='; }
"(" { return '('; }
")" { return ')'; }
"," { return ','; }
";" { return ';'; }
" " { /* ignore whitespace */ }
. { fprintf(stderr, "Invalid character: %s\n", yytext); }
%%
int yywrap() { return 1; }
```
其中,我们使用yylval来保存字符串和数字的值,在识别到ID和NUMBER时,使用strdup和atof将字符串复制和转换成浮点数,并返回对应的token。其他的运算符、括号、逗号和分号直接返回对应的token即可。
接下来,我们使用bison进行语法分析,定义一个parser.y文件,代码如下:
```
%{
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node {
char type[32]; // 节点类型
double numval; // 数字值
char strval[32]; // 字符串值
struct node *left; // 左子树
struct node *right; // 右子树
struct node *args; // 参数列表
} Node;
void print_tree(Node *node, int depth);
%}
%token ID
%token NUMBER
%left '+' '-'
%left '*' '/'
%left UMINUS
%%
program: stmt_list { $$ = $1; }
;
stmt_list: stmt { $$ = $1; }
| stmt_list stmt { $$ = $2; }
stmt: expr_stmt { $$ = $1; }
| assign_stmt { $$ = $1; }
| func_call_stmt { $$ = $1; }
expr_stmt: expr ';' { $$ = $1; }
assign_stmt: ID '=' expr ';' { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "assign"); strcpy($$->strval, $1->strval); $$->left = $1; $$->right = $3; }
func_call_stmt: ID '(' arg_list ')' ';' { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "func_call"); strcpy($$->strval, $1->strval); $$->args = $3; }
arg_list: expr { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "arg_list"); $$->left = $1; $$->right = NULL; }
| arg_list ',' expr { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "arg_list"); $$->left = $1; $$->right = (Node*)malloc(sizeof(Node)); $$->right->left = $3; $$->right->right = NULL; }
expr: expr '+' term { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "add"); $$->left = $1; $$->right = $3; }
| expr '-' term { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "sub"); $$->left = $1; $$->right = $3; }
| term { $$ = $1; }
term: term '*' factor { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "mul"); $$->left = $1; $$->right = $3; }
| term '/' factor { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "div"); $$->left = $1; $$->right = $3; }
| factor { $$ = $1; }
factor: ID { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "id"); strcpy($$->strval, $1->strval); }
| NUMBER { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "num"); $$->numval = $1->numval; }
| '-' factor %prec UMINUS { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "neg"); $$->left = $2; $$->right = NULL; }
| '(' expr ')' { $$ = $2; }
| func_call_expr { $$ = $1; }
func_call_expr: ID '(' arg_list ')' { $$ = (Node*)malloc(sizeof(Node)); strcpy($$->type, "func_call"); strcpy($$->strval, $1->strval); $$->args = $3; }
%%
int main() {
yyparse();
return 0;
}
void yyerror(const char *s) {
fprintf(stderr, "%s\n", s);
}
void print_tree(Node *node, int depth) {
int i;
if (node == NULL) return;
for (i = 0; i < depth; i++) printf(" ");
printf("%s", node->type);
if (strcmp(node->type, "num") == 0) printf(": %g", node->numval);
if (strcmp(node->type, "id") == 0) printf(": %s", node->strval);
if (strcmp(node->type, "assign") == 0) printf(": %s", node->strval);
printf("\n");
print_tree(node->left, depth + 1);
print_tree(node->right, depth + 1);
print_tree(node->args, depth + 1);
}
```
其中,%token用于定义词法分析器返回的token类型,%left用于定义运算符的优先级,UMINUS用于处理负号的优先级。在expr、term和factor的各个规则中,我们使用$$来表示当前表达式的值,$1、$2、$3分别表示第1、2、3个子表达式的值,这些值都是由词法分析器返回的token组成的。在assign_stmt、func_call_stmt和arg_list的规则中,我们使用malloc动态分配一个Node节点,并将其类型、字符串值、左子树和右子树进行赋值。在func_call_expr的规则中,我们也使用malloc动态分配一个Node节点,并将其类型、字符串值和参数列表进行赋值。在main函数中,我们打印出语法树,并使用print_tree函数进行递归打印。
最后,我们需要编译和链接这两个文件,命令如下:
```
flex lexer.l
bison -d parser.y
gcc -o parser lex.yy.c parser.tab.c -lm
```
这样,我们就完成了一个能够将输入的单词序列分析成语法树的解释器的设计。可以通过输入表达式、变量赋值、函数调用等操作,来构建语法树,并打印语法树进行调试和验证。
阅读全文