Lex和Yacc使用教程(五).再识语法树

来源:互联网 发布:黑客免杀攻防python 编辑:程序博客网 时间:2024/05/04 15:14

Lex和Yacc应用方法(五).再识语法树

草木瓜  20070524

一、序

  在《Lex和Yacc应用教程(四).语法树》一文已对语法树有了初步的概念,本文主要目的
是巩固语法树的概念,并做进一步的扩展分析。闲说少说,首先给出完整示例,本例在Redhat Linux 9
下调试通过,可放心使用。
    另外系列文章的标题,有的叫“lex和yacc应用方法”,有的叫“lex和yacc应用教程”,
还有的叫“lex和yacc使用教程”等等,概念都是一样的。之所以起这么多名字是便于大家通
过搜索引擎能迅速查到。
   
    <本站文章难免有错误疏漏之处。Lex,Yacc系列文章 http://blog.csdn.net/liwei_cmg/category/207528.aspx>

二、示例全代码

本示例包括四个文件
node.h(头文件),lexya_e.l(lex文件),lexya_e.y(yacc文件),parser.c(外部分析文件)

--------------------------------------
A.头文件 node.h
  

/* 定义树结点的权举类型 */
typedef enum { TYPE_CONTENT, TYPE_INDEX, TYPE_OP } NodeEnum;


/* 操作符 */
typedef struct {
int name; /* 操作符名称 */
int num; /* 操作元个数 */
struct NodeTag * node[1]; /* 操作元地址 可扩展 */
} OpNode;

typedef struct NodeTag {
 NodeEnum type; /* 树结点类型 */
 /* Union 必须是最后一个成员 */
 union {
  float content; /* 内容 */
  int index; /* 索引 */
  OpNode op; /* 操作符对象 */
 };

} Node;

struct VarIndex
{
 float val;
 char mark[10];
};

struct VarDefine
{
 int index;
 char * name;
};

#define USER_DEF_NUM 259 /* Yacc编译的保留字开始索引 */

#define MAX_VARS 100     /* 最多变量数 */
#define MAX_DEFS 20      /* 最多保留字数 */

#define MAX_BUFF_COLS 40   /* 分析语句最多行数 */
#define MAX_BUFF_ROWS 40   /* 分析语句每行最多字符数 */

extern struct VarIndex G_Var[MAX_VARS];  /* 存储的变量数组 */
extern struct VarDefine G_Def[MAX_DEFS]; /* 系统保留字变量 */

extern int G_iVarMaxIndex;   /* 变量目前总数 */
extern int G_iVarCurIndex;   /* 当前操作变量索引 */

extern char G_sBuff[MAX_BUFF_ROWS][MAX_BUFF_COLS];  /* 存储分析语句 */
extern int G_iBuffRowCount;  /* 当前语句行数 */
extern int G_iBuffColCount;  /* 当前语句列数 */

/* 是否打印调试信息的开关 */
// #define PARSE_DEBUG   

 


--------------------------------------
B.lexya_e.l lex文件
  

%{
#include <stdlib.h>
#include "node.h"
#include "lexya_e.tab.h"

struct VarDefine G_Def[MAX_DEFS];             /* 存储的变量数组 */
char G_sBuff[MAX_BUFF_ROWS][MAX_BUFF_COLS];   /* 存储分析语句   */
int G_iBuffRowCount=0;       /* 当前语句行数 */
int G_iBuffColCount=0;       /* 当前语句列数 */

extern void add_var(char *);  /* 在内存中添加变量 */
void add_buff(char *); /* 在内存中添加语句 */
void yyerror(char *);
%}

/* 使用代变量表示任意字符 */
any  .
%%


#{any}*[/n]  {
 add_buff(yytext);
 G_iBuffColCount=0;
 G_iBuffRowCount++;
} /* 单行注释 */


[/n]  {
 G_iBuffColCount=0;
 G_iBuffRowCount++;
} /* 回车 */

"for"   {
 yylval.index = FOR - USER_DEF_NUM;  
 G_Def[yylval.index].name="for";
 add_buff(yytext);  
 return FOR;  
}
"while" {
 yylval.index = WHILE -USER_DEF_NUM; 
 G_Def[yylval.index].name="while";
 add_buff(yytext);  
 return WHILE;
}
"if"    {
 yylval.index = IF - USER_DEF_NUM;   
 G_Def[yylval.index].name="if";
 add_buff(yytext);    
  return IF;
}
"else"  {
 yylval.index = ELSE - USER_DEF_NUM; 
 G_Def[yylval.index].name="else"; 
 add_buff(yytext);  
 return ELSE;
}
"print" {
 yylval.index = PRINT -USER_DEF_NUM ;
 G_Def[yylval.index].name="print";
 add_buff(yytext);
 return PRINT;
}

[a-zA-Z][a-zA-Z0-9]* {
 add_var(yytext);
 yylval.index = G_iVarCurIndex;
 add_buff(yytext);
 return VARIABLE;
}

[0-9]+ {
 yylval.val = atof(yytext);
 add_buff(yytext);
 return NUMBER;
}

[0-9]*/.[0-9]+ {
 yylval.val = atof(yytext);
 add_buff(yytext);
 return NUMBER;
}

"++" { yylval.index = ADD_T-USER_DEF_NUM; G_Def[yylval.index].name="++"; G_Def[yylval.index+1].name="++";  add_buff(yytext); return ADD_T; }
"--" { yylval.index = MUS_T-USER_DEF_NUM; G_Def[yylval.index].name="--"; G_Def[yylval.index+1].name="++";  add_buff(yytext); return MUS_T; }

">=" { yylval.index = GE - USER_DEF_NUM;  G_Def[yylval.index].name=">=";  add_buff(yytext); return GE;}
"<=" { yylval.index = LE - USER_DEF_NUM;  G_Def[yylval.index].name="<=";  add_buff(yytext); return LE;}
"==" { yylval.index = EQ - USER_DEF_NUM;  G_Def[yylval.index].name="==";  add_buff(yytext); return EQ;}
"!=" { yylval.index = NE - USER_DEF_NUM;  G_Def[yylval.index].name="!=";  add_buff(yytext); return NE;}

"&&" { yylval.index = AND - USER_DEF_NUM; G_Def[yylval.index].name="&&";  add_buff(yytext); return AND;}
"||" { yylval.index = OR - USER_DEF_NUM;  G_Def[yylval.index].name="||";  add_buff(yytext); return OR; }

[()<>=+/-*/;{}.] {
 yylval.index = *yytext;  /* 存储运算符 */
 add_buff(yytext);
 return *yytext;
}

                                                                                 

[/t]    { add_buff(yytext); }  /* 去除TAB  */
[ ]     { add_buff(yytext); }  /* 去除空格 */
{any}   { printf("Ignore Unknow Symbol:[%s]/n",yytext); }
%%

void add_buff(char * buff) {
 strcat(G_sBuff[G_iBuffRowCount], buff);
 G_iBuffColCount=G_iBuffColCount+strlen(buff);
}
int yywrap(void) {
 return 1;
}

 

--------------------------------------
C.lexya_e.y yacc文件


%{
 
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include "node.h"

/* 属性操作类型 */
Node *opr(int name, int num, ...);
Node *set_index(int value);
Node *set_content(float value);

/* 树结点操作 */
void NodeFree(Node *p);
float NodeExecute(Node *p);

typedef union {
float val;  /* 变量值 */
int index;  /* 用于存放 变量数组索引 或 一元操作符值 或 多元操作符索引 */
Node *node; /* 结点地址 */
}yystype;
#define YYSTYPE yystype

/* 打印分析调试信息 */
void debug_vsp(YYSTYPE , char * ,YYSTYPE *, char * );
void print_stmt();

 /* 在内存中添加变量 */
void add_var(char *);

int G_iVarMaxIndex = 0;  /* 变量最大个数 */
int G_iVarCurIndex = -1; /* 变量当前索引 */
struct VarIndex G_Var[MAX_VARS];  /* 变量内存数组 */


void yyerror(char *s);
%}

%union {
float val; /* 变量值 */
int index; /* 变量数组索引 */
Node *node; /* 结点地址 */
};

%token <val> NUMBER
%token <index> VARIABLE
%token PRINT
%token FOR WHILE
%nonassoc IF
%nonassoc ELSE
%left AND OR
%left GE LE EQ NE '>' '<'
%left '+' '-'
%left '*' '/'
%left ADD_T ADD_TT MUS_T MUS_TT
%nonassoc UMINUS
%type <node> stmt stmt_list expr_set expr_setself expr_comp expr
%%
program:
function { exit(0); }
;
function:
function stmt { NodeExecute($2); NodeFree($2); }
| /* NULL */
;
stmt:
';'                 { $$ = opr(';', 2, NULL, NULL); debug_vsp(yyval,";",yyvsp,"0"); }
| expr_set ';'      { $$ = $1; debug_vsp(yyval,"es;",yyvsp,"01");                   }
| PRINT expr ';'    { $$ = opr(PRINT, 1, $2); debug_vsp(yyval,"p(e);",yyvsp,"401"); }
| PRINT expr_set ';'    { $$ = opr(PRINT, 1, $2); debug_vsp(yyval,"p(es);",yyvsp,"401"); }
| FOR '(' expr_set ';' expr_comp ';' expr_set ')' stmt { $$ = opr(FOR, 4, $3, $5, $7, $9); debug_vsp(yyval,"for(es;ec;es) st",yyvsp,"410101010"); }
| WHILE '(' expr_comp ')' stmt       { $$ = opr(WHILE, 2, $3, $5); debug_vsp(yyval,"while(ec) st",yyvsp,"41010"); }
| IF '(' expr_comp ')' stmt %prec IF { $$ = opr(IF, 2, $3, $5);    debug_vsp(yyval,"if(ec) st",yyvsp,"41010");    }
| IF '(' expr_comp ')' stmt ELSE stmt %prec ELSE       { $$ = opr(IF, 3, $3, $5, $7);      debug_vsp(yyval,"if(ec)else st",yyvsp,"4101040");      }
| '{' stmt_list '}' { $$ = $2; debug_vsp(yyval,"{stl}",yyvsp,"101"); }
;

stmt_list:
stmt              { $$ = $1;  debug_vsp(yyval,"st",yyvsp,"0");  }
| stmt_list stmt  { $$ = opr(';', 2, $1, $2); debug_vsp(yyval,"stl st",yyvsp,"00"); }
;

expr_set:
VARIABLE '=' expr { $$ = opr('=', 2, set_index($1), $3); debug_vsp(yyval,"v=e",yyvsp,"210"); }
| VARIABLE '=' expr_setself { $$ = opr('=', 2, set_index($1), $3); debug_vsp(yyval,"v=ess",yyvsp,"210"); }
| expr_setself
;

expr_setself:
  ADD_T VARIABLE  { $$ = opr(ADD_T, 1, set_index($2));  debug_vsp(yyval,"++v",yyvsp,"42");   }
| MUS_T VARIABLE  { $$ = opr(MUS_T, 1, set_index($2));  debug_vsp(yyval,"--v",yyvsp,"42");   }
| VARIABLE ADD_T  { $$ = opr(ADD_TT, 1, set_index($1));  debug_vsp(yyval,"v++",yyvsp,"24");  }
| VARIABLE MUS_T  { $$ = opr(MUS_TT, 1, set_index($1));  debug_vsp(yyval,"v--",yyvsp,"24");  }
| '(' expr_setself ')' { $$ = $2; debug_vsp(yyval,"(ess)",yyvsp,"101");   }
;

expr_comp:
  expr '<' expr   { $$ = opr('<', 2, $1, $3); debug_vsp(yyval,"e<e",yyvsp,"010");    }
| expr '>' expr   { $$ = opr('>', 2, $1, $3); debug_vsp(yyval,"e>e",yyvsp,"010");    }
| expr GE expr    { $$ = opr(GE, 2, $1, $3);  debug_vsp(yyval,"e>=e",yyvsp,"040");   }
| expr LE expr    { $$ = opr(LE, 2, $1, $3);  debug_vsp(yyval,"e<=e",yyvsp,"040");   }
| expr NE expr    { $$ = opr(NE, 2, $1, $3);  debug_vsp(yyval,"e!=e",yyvsp,"040");   }
| expr EQ expr    { $$ = opr(EQ, 2, $1, $3);  debug_vsp(yyval,"e==e",yyvsp,"040");   }
| expr_comp AND expr_comp { $$ = opr(AND, 2, $1, $3); debug_vsp(yyval,"ec&&ec",yyvsp,"040"); }
| expr_comp OR expr_comp  { $$ = opr(OR, 2, $1, $3);  debug_vsp(yyval,"ec||ec",yyvsp,"040"); }
| '(' expr_comp ')'       { $$ = $2;                  debug_vsp(yyval,"(ec)",yyvsp,"101");   }
;

expr:
NUMBER            { $$ = set_content($1);      debug_vsp(yyval,"f",yyvsp,"3");     }
| VARIABLE        { $$ = set_index($1);        debug_vsp(yyval,"v",yyvsp,"2");     }
| '-' NUMBER %prec UMINUS { $$ = set_content(-$2);   debug_vsp(yyval,"-e", yyvsp,"13"); }
| expr '+' expr   { $$ = opr('+', 2, $1, $3);  debug_vsp(yyval,"e+e",yyvsp,"010"); }
| expr '-' expr   { $$ = opr('-', 2, $1, $3);  debug_vsp(yyval,"e-e",yyvsp,"010"); }
| expr '*' expr   { $$ = opr('*', 2, $1, $3);  debug_vsp(yyval,"e*e",yyvsp,"010"); }
| expr '/' expr   { $$ = opr('/', 2, $1, $3);  debug_vsp(yyval,"e/e",yyvsp,"010"); }
| '(' expr ')'    { $$ = $2;                   debug_vsp(yyval,"(e)",yyvsp,"101"); }
;

//| '(' expr error        { $$ = $2; printf("ERROR"); exit(0); }

%%
#define SIZE_OF_NODE ((char *)&p->content - (char *)p)

Node *set_content(float value) {
 
 Node *p;
 
 size_t sizeNode;
 /* 分配结点空间 */
 sizeNode = SIZE_OF_NODE + sizeof(float);
 
 if ((p = malloc(sizeNode)) == NULL)
  yyerror("out of memory");
  
 /* 复制内容 */
 p->type = TYPE_CONTENT;
 p->content = value;
 
 return p;
 
}

Node *set_index(int value) {
 
 Node *p;
 size_t sizeNode;
 /* 分配结点空间 */
 sizeNode = SIZE_OF_NODE + sizeof(int);
 
 if ((p = malloc(sizeNode)) == NULL)
  yyerror("out of memory");
  
 /* 复制内容 */
 p->type = TYPE_INDEX;
 p->index = value;
 
 return p;
}

Node *opr(int name, int num, ...) {

 va_list valist;
 Node *p;
 size_t sizeNode;
 int i;
 /* 分配结点空间 */
 sizeNode = SIZE_OF_NODE + sizeof(OpNode) + (num - 1) * sizeof(Node*);
 
 if ((p = malloc(sizeNode)) == NULL)
  yyerror("out of memory");
  
 /* 复制内容 */
 
 p->type = TYPE_OP;
 p->op.name = name;
 p->op.num = num;
 
 va_start(valist, num);

 for (i = 0; i < num; i++)
 p->op.node[i] = va_arg(valist, Node*);
 
 va_end(valist);
 return p;
}

/**/
void debug_vsp(YYSTYPE yval, char * info, YYSTYPE * vsp, char * mark) {

#ifdef PARSE_DEBUG
 
  printf("/n -RULE  0x%x  %s /n ", yval.node, info  );
  int i;
  int ilen=strlen(mark);
  for(i=1-ilen;i<=0;i++) {
   
  switch(mark[ilen+i-1]){
   case '0':
    printf(" [ 0x%x ",vsp[i].node);//「」
    switch(vsp[i].node->type) {
     case TYPE_CONTENT:
      printf("%g ] ",vsp[i].node->content);
      break;
     case TYPE_INDEX:
      printf("%s ] ",G_Var[vsp[i].node->index].mark);
      break;
     case TYPE_OP:
      if(vsp[i].node->op.name<USER_DEF_NUM)
       printf("%c ] ",vsp[i].node->op.name);
      else
       printf("%s ] ",G_Def[vsp[i].node->op.name-USER_DEF_NUM].name);
      break;      
    }
    break;
   case '1':
    printf(" %c ",vsp[i].index);   /* 打印运算符 */
    break;
   case '2':
    printf(" %s ",G_Var[vsp[i].index].mark);
    break;
   case '3':
    printf(" %g ",vsp[i].val);
    break;
   case '4':
    printf(" %s ",G_Def[vsp[i].index].name);
    break;   
    }
    
  }
  printf("/n");
  print_stmt();

#endif
 
}
void add_var(char *mark) {

 if(G_iVarMaxIndex==0){
  strcpy(G_Var[0].mark,mark);
  G_iVarMaxIndex++;
  G_iVarCurIndex=0;
  return;
 }

 int i;
 for(i=0;i<=G_iVarMaxIndex-1;i++) {
  if(strcmp(G_Var[i].mark,mark)==0) {
   G_iVarCurIndex=i;
   return;
  }
 }

 strcpy(G_Var[G_iVarMaxIndex].mark,mark); 
 G_iVarCurIndex=G_iVarMaxIndex;
 G_iVarMaxIndex++;

}
void print_stmt() {

 printf(" -STMT: /n");
 /*
 int i;
 for(i=0;i<=G_iBuffRowCount;i++)
  printf("%s /n",G_sBuff[i]);
 */
 if(G_iBuffColCount==0)
  printf("  %s /n",G_sBuff[G_iBuffRowCount-1]);
 else
  printf("  %s /n",G_sBuff[G_iBuffRowCount]);
  
 printf("/n");
 
}

void NodeFree(Node *p) {
 int i;
 if (!p) return;
 if (p->type == TYPE_OP) {
  for (i = 0; i < p->op.num; i++)
  NodeFree(p->op.node[i]);
 }
 free (p);
}
void yyerror(char *s) {
 //fprintf(stdout, "%s/n", s);
 printf("<Parser Error> Line %d ,Col %d /n",G_iBuffRowCount+1,G_iBuffColCount+1);
 printf(" %s/n",G_sBuff[G_iBuffRowCount]);
}

int main(void) {
 yyparse();
 return 0;
}

 

--------------------------------------
D. parser.c(外部分析文件)

#include <stdio.h>
#include "node.h"
#include "lexya_e.tab.h"

float NodeExecute(Node *p) {
 if (!p) return 0;
 switch(p->type) {
  case TYPE_CONTENT: return p->content;
  case TYPE_INDEX:   return G_Var[p->index].val;
  case TYPE_OP:
   switch(p->op.name) {
    
    case WHILE:  while(NodeExecute(p->op.node[0]))NodeExecute(p->op.node[1]);
                return 0;
               
     case FOR:    NodeExecute(p->op.node[0]);
                 while(NodeExecute(p->op.node[1])) {
                    NodeExecute(p->op.node[3]);
                    NodeExecute(p->op.node[2]);
                  }
                  return 0;
    
    case IF:     if (NodeExecute(p->op.node[0]))
                  NodeExecute(p->op.node[1]);
                else
                  if (p->op.num>2)
                    NodeExecute(p->op.node[2]);
                return 0;
               
    case PRINT:  printf("%g/n", NodeExecute(p->op.node[0]));
                return 0;
               
    case ';':    NodeExecute(p->op.node[0]);
                return NodeExecute(p->op.node[1]);
               
    case '=':    return G_Var[p->op.node[0]->index].val = NodeExecute(p->op.node[1]);
    case UMINUS: return NodeExecute(p->op.node[0]);
    case '+':    return NodeExecute(p->op.node[0]) + NodeExecute(p->op.node[1]);
    case '-':    return NodeExecute(p->op.node[0]) - NodeExecute(p->op.node[1]);
    case '*':    return NodeExecute(p->op.node[0]) * NodeExecute(p->op.node[1]);
    case '/':    return NodeExecute(p->op.node[0]) / NodeExecute(p->op.node[1]);
    case '<':    return NodeExecute(p->op.node[0]) < NodeExecute(p->op.node[1]);
    case '>':    return NodeExecute(p->op.node[0]) > NodeExecute(p->op.node[1]);
    case GE:     return NodeExecute(p->op.node[0]) >= NodeExecute(p->op.node[1]);
    case LE:     return NodeExecute(p->op.node[0]) <= NodeExecute(p->op.node[1]);
    case NE:     return NodeExecute(p->op.node[0]) != NodeExecute(p->op.node[1]);
    case EQ:     return NodeExecute(p->op.node[0]) == NodeExecute(p->op.node[1]);
    case AND:    return NodeExecute(p->op.node[0]) && NodeExecute(p->op.node[1]);
    case OR:     return NodeExecute(p->op.node[0]) || NodeExecute(p->op.node[1]);   
    case ADD_T:  return ++G_Var[p->op.node[0]->index].val;
    case MUS_T:  return --G_Var[p->op.node[0]->index].val; 
    case ADD_TT: return G_Var[p->op.node[0]->index].val++;
    case MUS_TT: return G_Var[p->op.node[0]->index].val--; 
    }
   
 }
 
 return 0;
 
}

 

三、示例功能说明


  以上示例显然是根据《Lex和Yacc应用教程(四).语法树》文中的示例扩展而来。主要演示
C语言类似的语法编译方法。支持的功能如下:

    1. 支持整型和浮点型
    2. 支持变量存储,变量名可为多个字符
    3. 支持+-*/()=运算法则
    4. 支持负数及负数运算
    5. 支持变量的自加(++)和自减运算(--),区分前自加减和后自加减
    6. 支持print打印值和变量
  7. 支持for while if else控制结构,并支持控制结构的嵌套
    8. 支持>= <= != ==四种比较运算
    9. 支持&& ||的复合比较运算
    10. 支持对空格和TAB的忽略处理
    11. 支持#的单行注释
    12. 支持{}多重组合
    13. 支持编译错误的具体显示
    14. 支持编译过程的变量堆栈信息打印,便于调试分析
    15. 支持保留字的存储显示。
    16. 支持语法树打印(将在下一篇文章着重说明)
   
    示例文件:
   
  k=9;
  if((1>1)||(-9>-1))
    for(i=0;i<=9;i=i+1)
      print(i);
  else
    if(3>1&&2>1) {
      for(j=-1.1;j<=3;j++)
        print(j);
      for(jdd=1;jdd<=3;++jdd)
        print(jdd);
      while(k<=9) {
        print(k++);
        print(++k);
      }
    }
  #test 
  
  关闭调试信息的输出:
  
  -1.1
  -0.1
  0.9
  1.9
  2.9
  1
  2
  3
  9
  11
  
四、思路分析

    示例已经包括了一些注释,这里只对一些难点做些说明。
  
  1.定义的规则和递归的语法树
  
    我们第一步需要划分数值(1,2.2,99 ...)和变量(ab,d)的概念。即lex文件中的
  NUMBER和VARIABLE。然后划分一元运算符,多元运算符和保留字。一元运算符可以用
  int来表示,多元必须依靠token去标记。
    这里要注意的是,使用了代变量any,为得是成功描述#{any}*[/n]这个规则,否
   则是拼不出合法规则符的。
    Lex的规则划分难度在于逻辑优先级,这个也在于自已把握,总的依据是复杂规则
  在前,简单规则在后。
    
       有了Lex的规则定义,就可以进行语法的递归划分。如下图:
      
       program     #未做定义 表示整个文件的内容
          |
       function    #未做定义 表示整个文件的内容  可有多个stmt组成
          |
        stmt       #包含赋值,打印,分支 的组合语句,须以;或者{}结尾
          |
 expr_set          #赋值句句
      |      stmt_list  #多个stmt
      |           |         ...    #其他打印分支语句
expr_setself     stmt         |
      |                      |
    expr              expr_set  expr_comp  expr
                        |          |
                    expr_setself  expr
                        |
                       expr
       
        想用简单图描述出复杂的递归思路,还是比较难。下面逐一进行补充说明。
       
        expr设计用于基本的数值运算,并可以无限扩展递归,表示了“普通运算表达式”的
    所有可能。
        expr_comp设计用于“普通运算表达式”的比较,对于&& ||支持无限扩展递归,包括
    了大部分的比较运算可能。
        expr_setself设计用于“变量自身加减”,只支持()形式的递归
        expr_set设计用于“赋值运算”,即两种可能,一是将“普通运算表达式”结果赋值
    给变量,另一是将“变量自身加减”结果赋值。
        stmt其实就是大杂烩,融合了多种保留字的运算法则。stmt将随着功能扩展不断细分。
        stmt_list和stmt用了一个循环递归,但不是死循环,因为BNF范式本身就是有序的,
    归并顺序自顶而上,自复杂至简单,归并后的规则会越来越少。递归的方向是有序的,就
    不存在死循环的问题。
       
   
   2.构造内存中的语法树
   
        这部分内容在上文已有些许说明。即定义一个Union保存三种类型的树结点,对每种类
    型的树结点提供可递归的规则。yacc文件中定义的各类动作便是为了构造内存的语法树。
        总体概念是利用lex,yacc解析文件,同时构造了一棵完整的语法树,在归并到stmt时,
    进行遍历处理。至于每步的操作细节可结合打印信息和语法树进行全面分析。
   
   
   3.调试分析信息的打印
   
        系统定义了G_sBuff存储已分析的字符,G_Var存储所有变量,G_Def存储所有保留字。
    在yacc的每步归并规则中,通过debug_vsp和预定义的文法规则打印vsp的堆栈信息。对于
    特殊信息,须查找全局内存变量。
        调试信息需要lex和yacc配合起来实现,需要提出的是yystype的index是一值多用,
    可以通过gdb跟踪调试,加深理解。
       
       
   4.前自加减和后自加减
   
        这个例子其实对++做了两个token,并在实现操作中直接使用C的规则。
 
       
五、重要提示

  1.写C的时候,要严防内存越界。本例大多通过宏定义,定义了一些有界的数组,一旦出现
  字符越界会造成奇怪的错误,而且很难调试发现。换句话说,一旦遇到奇怪的问题,首先
  想到得是内存越界。写本例的自加自减功能时,忽略了MAX_DEFS(当初为10),遇到ADD_T
  MUS_T,数值运算总是出错,在打印信息中也发现乱码,G_Var无缘无故被写,逐步跟踪调
  试半天,也没有发现具体问题,后来最终发现是G_Def越界,写脏了G_Var。将MAX_DEFS改
  为20即可。
  
  
   2.对于不能理解的概念,必须单步跟踪调试,这是最为快捷的解决方法。本例需要重点理解
   opr的内存构造和NodeExecute执行过程。
  
六、结束

    这篇文章的示例已是初步成形了,具备了一定的应用价值,但是与C语言的编译体系相比仍
有相当大的距离,更不用说编译优化了。
    随着对lex yacc的研究深入,会逐渐发现这套理论的强大和精妙所在。lex,yacc比较原始,
也只有原始才是真实的。研究计算机就需要从0开始。
    在下篇文章中将详细介绍语法树的打印。