语法树能使程序具有层次性,使要翻译的程序更具有语义特点。比如,对于同一段程序中不同地方都出现的代码int a;在语法树中可以体现出其不同的状态和性质。
设计语法树中的结点的数据结构如下:
typedef struct ParseNode //语法树结点数据结构 { int NodeID; //结点序号 char lexval[20]; //用来存储结点信息 int val; //用来存储结点值 int NodeType; //结点类型 PTOKENLIST NodeTokenList;//指向结点的符号表项地址 struct ParseNode* children; //孩子指针 struct ParseNode* sublings; //兄弟指针 }PARSENODE,*PPARSENODE; |
该语法树拥有的操作有:
void printparsetree(PPARSENODE); //打印语法树 PPARSENODE makeleaf(int, int, char*); //创建叶结点 PPARSENODE makenode(PPARSENODE, PPARSENODE, int, int, char*); //创建枝结点 void printparsenode(PPARSENODE); //打印结点 void printnodechildren(PPARSENODE); //打印孩子结点的序号 |
2、翻译模式的设计
根据语法树的结构,设计上下无关文法如下:
lines : lines cstmt { printparsetree($2); displaytokenlist(head); } | ; cstmt : ID '(' ')' '{' sstmts '}' { $$=$5; $1->NodeTokenList=storetoken($1->lexval,$1->val); } ; sstmts : sstmts sstmt { $$=makenode($1,$2,COMPOUND,0,"compound"); } | sstmt { $$=$1; } ; sstmt : type idlist ';' { addtype($2,$1->NodeType); $$=makenode($1,$2,VAR,0,"var"); } | IF '(' expr ')' expr ';' { $$=makenode($3,$5,IF,0,"if"); } ; type : INT { $$=makeleaf(INT,0,"integer"); } ; idlist : idlist',' ID { $1->sublings=makeleaf(ID,$3->val,$3->lexval); $1->sublings->NodeTokenList=storetoken($3->lexval,$3->val);$$=$1; } | ID { $$=makeleaf(ID,$1->val,$1->lexval); $$->NodeTokenList=storetoken($$->lexval,$$->val); } ;
expr : expr DUPEQU expr{ $$=makenode($1,$3,EXPR,0,"=="); } | expr EQU expr { flushtokenvalue($1,$3->val); $$=makenode($1,$3,EXPR,0,"="); } | expr ADD expr { $$=makenode($1,$3,EXPR,$1->val+$3->val,"+"); } | expr SUB expr { $$=makenode($1,$3,EXPR,$1->val-$3->val,"-"); } | expr MUL expr { $$=makenode($1,$3,EXPR,$1->val*$3->val,"*"); } | expr DIV expr { $$=makenode($1,$3,EXPR,$1->val/$3->val,"/"); } | '(' expr ')' { $$=makenode($2,NULL,EXPR,$2->val,"-"); } | SUB expr %prec UMINUS { $$=makenode($2,NULL,EXPR,0-$2->val,$2->lexval); } | NUMBER { $$=makeleaf(NUMBER,$1->val,$1->lexval); } | ID { $$=makeleaf(ID,$1->val,$1->lexval); $$->NodeTokenList=storetoken($$->lexval,$$->val); } ; |
符号表
1、 符号表的数据结构
typedef struct tokenlist //符号表结构 { char token[20]; //符号名 int type; //符号类型 int value; //符号值 struct tokenlist * next ; //指向下一个符号表项 }TOKENLIST,*PTOKENLIST; |
2、 符号表上的操作
PTOKENLIST storetoken(char*, int); //存储符号 PTOKENLIST findtoken(char*); //寻找符号值 void deletetoken(char*); //删除符号表项,一般发生在符号的生命周期结束时 void addtype(PPARSENODE,int); //为符号表项添加类型 void flushtokenvalue(PPARSENODE,int); //更新符号表项的值 void displaytokenlist(PTOKENLIST); //显示符号表 |
Yacc 与Lex 程序的交互
1、变量的交互
通过Yacc程序里已经的定义的变量yylval来作为Yacc与Lex程序的信使。
并定义
#ifndef YYSTYPE
#define YYSTYPE PPARSENODE
#endif
这里的YYSTYPE是yylval的类型,在Yacc程序编译出来的.c文件里有这样的一段代码:
YYSTYPE YYNEAR yylval;
然后,在Lex程序中有这样的代码:
extern YYSTYPE yylval;
即引用外部程序的yylval,因此Yacc与Lex程序就这样联系起来了。
2、 程序的交互
Lex程序中引用由Yacc程序生成的.h程序,而Yacc程序引用Lex程序生成的.h程序。
比如,在myparsetree_yacc.y中引用了:
#include "myparsetree_lex.h"
而在myparsetree_lex.l中引用了:
#include "myparsetree_yacc.h"
而myparsetree_lex.h是在Lex程序中编写的,myparsetree_yacc.h是在Yacc程序中编写的,都是在下面的这段代码里:
%include{
// .h文件里包含的代码
}
结果如下: