Bison^Flex=语法分析生成中

上古时代的时候(前一段时间参加了些些小竞赛,状态有点迷糊,五一又浪里个浪,所以…虽迟但到!),我们有讲解过借助Flex实现SysY词法分析。词法分析是编译的第一阶段,仅仅只是将代码进行切块归类,得到的只是些零散的tokens。现在我们进入第二阶段,对这些token进行整理排序,以便后面可以方便地解读每句code的含义,即语义分析——构建抽象语法树AST

本文将阐述如何使用Bison联合Flex来生成语法分析器,并输出抽象语法树。


抽象语法树用树的数据结构将token们进行关联,非常抽象地展现了整体与部分的关系,就好似为一句话断句,使其结构一览无余。既然要为代码块进行“断句”,那么我们势必需要先清楚代码块拥有哪些结构层次。但结构层次并不是死板的,例如英语中正常的句子结构都是主谓宾,但并不是所有句子只有这种模式,句子中还可以有状语、补语等。代码也是如此,那么如何来描述一种编程语言所有可能的结构层次呢,就是所谓的文法。

栗子胜千言,例如下面这个简单的文法

S → aSb | aAb
A → bAa | ba

其中是开始符号,即文法推导中的第一个非终结符,也就是说这个文法描述的所有句子都可以由推导而来。一般大写字母表示的都是非终结符,即还可以继续推导的符号,小写字母就相对应的终结符。那么很显然,这个文法描述的所有句子可以用集合来表示。

可以看到有限的推导式足以描述无穷的句子,但这些句子是具有一定语法规则的。而一种编程语言本身就是一些具有特定语法规则的句子,因此也可以用一个文法来表示,只不过推导式的数量稍微多了亿些。

在不借助任何工具的情况下,要实现一个语法分析器就会有两种思路,其一是自底向上,就是将读入的token不断归约(推导的逆过程)为非终结符,直到剩下开始符号;另一种则是自顶向下,即从开始符号找到生成当前代码的推导过程,这里其实就是选择哪一条产生式的问题。两种思路本质上就是选择树的根还是叶子结点作为分析的起点来考虑的,但这并不是本文的重点,你只需知道Bison生成的语法分析器代码采用的是自底向上的思路,而我们只要提供给Bison相应的文法即可得到该文法对应语言的语法分析器。

不过,带着一点语法分析原理的学习来阅读本文体验会更好哦


GNU Bison是一款用于自动生成语法分析器的程序,基于并兼容yacc,可用于所有常见的操作系统。Bison把LALR形式的上下文无关文法描述转换为可做语法分析的C或C++程序。

Bison的输入文件*.y的基本格式如下,使用者需要将文法的一些基本信息写入该文件

%start CompUnit
%expect 1

%{
    #include "parser.h"
    ...
%}

%union
{
    int     num;
    char*   str;
}

/*声明Flex产生的token有三种格式,其中<type>不是必须的*/
%token <type1> tokens1 /*类型为type1的token们*/
%right <type2> tokens2 /*类型为type2的右结合token们*/
%left <type3> tokens3  /*类型为type3的左结合token们*/

/*声明非终结符的类型*/
%type<num> Number CompUnit Decl ...

%%
/*Grammar Productions and Corresponding Actions*/
CompUnit:       CompUnit Decl                     {$$=$1+$2;}
                | CompUnit FuncDef                {$$=$1-$2;}
Decl:           ConstDecl    %prec    token3      {$$=$1*10;}
                | /*ε*/                                {}
...
%%

int main()
{
    yyparse();
    ...
}
  1. 第一句%start ...是声明文法的开始符号,后面紧跟一个非终结符

  2. %expect n表示你知道这个文法中存在n个移入/归约冲突(在某一个时刻既可以选择归约为非终结符也可以选择读入下一个token),并且认同Bison默认的处理方式,那么在生成语法分析器时若Bison确实发现了n个冲突就不会提醒你,否则会有提示(不加也没有关系,这句不是必要的)

    *.y: conflicts: n shift/reduce
  3. %{...}%中间则和Flex类似,可以放一些头文件,这一部分会被直接添入生成代码的靠前部分

  4. %union{...}则是对之后需要用到的类型声明,即后面token非终结符的类型都只能取自union中声明的符号,在上面的文件中只能用numstr来声明类型

  5. 对Flex生成的token进行声明,使Bison知道有哪些token存在,另外可以通过<type>对token的类型进行声明(默认为int),这样当Flex返回token时我们可以认为返回的是一个type类型的变量,而对于token的赋值则是在Flex中完成的,如此可以使Bison和Flex之间存在信息交互

  6. %right%left也是token的一种声明方式,分别表示右结合和左结合,多用于运算符token的声明上

  7. token的声明顺序决定了语法优先级,从上至下优先级逐渐升高,同一行的token优先级相同,多用于运算符token的声明,优先级高的token先进行归约

  8. %type<> ...则是声明非终结符的类型,类型依然只能取自%union{...}中,token的值是在Flex与Bison之间传递,而非终结符的值则是在产生式之间传递,即当一个非终结符A被归约出来后,我们可以对其进行相应类型的赋值,随后在A被归约时则会带着之前的赋值从产生式左部转移到另一个产生式的右部,那么我们就可以用A的值来定义新归约出来的非终结符的值

  9. %%...%%之间则是最重要的语法规则以及相应的动作,只不过产生式中的【→】变成了【:】

  10. 每一个产生式的右部可能有好几种推导结果(由【|】进行分隔),我们认为不同的右部代表着不同的产生式,而每一个产生式后面都可以跟相应的一系列操作【{…}】,表示发生这一个产生式的归约时会执行的相应动作

  11. 操作中我们可以用【$】来引用产生式中的每一个token以及终结符,其中【$$】表示产生式左部的非终结符(冒号左边的非终结符),【$n】表示产生式右部第n个非终结符/token/字符串的值

    ⚠️当【$n】是一个字符串常量时,Bison会提示这个【$n】没有声明类型,但不会影响程序的运行,当然你觉得难受的话可以直接将其替换成字符串的字面值

  12. 产生式的右部是可以为空的,这就相当于文法中的空串

  13. 产生式中%prec token_name可以使%prec左边的产生式具有和token_name相同的优先级

  14. 最下面的部分也跟Flex类似,会被直接复制到生成代码的底部,函数yyparse()会执行语法分析过程,接下来你可以增加一些输出分析结果的代码

  15. 以上只是Bison最基础的规则,希望更加全面系统地学习可以参阅《flex与bison》.


∫ “parser.h”

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>

extern int yylineno;
extern char* yytext;
extern int yylex();

typedef enum {NUM,TEXT} TYPE;

void yyerror(const char* msg)
{
    printf("\033[1;31m%s at line %d\033[0m: %s\n",msg,yylineno,yytext);
}

int OCT2DEC(char* text)
{
    int sum=0;
    for(int i=1;i<strlen(text);++i)
        sum=sum*8+(text[i]-'0');
    return sum;
}

int HEX2DEC(char* text)
{
    int sum=0;
    for(int i=2;i<strlen(text);++i)
    {
        if(text[i]>='0'&&text[i]<='9')
            sum=sum*16+(text[i]-'0');
        else if(text[i]>='a'&&text[i]<='f')
            sum=sum*16+(text[i]-'a'+10);
        else
            sum=sum*16+(text[i]-'A'+10);
    }
    return sum;
}

typedef struct ASTnode
{
    TYPE type;
    char* text;
    int num;
    struct ASTnode* next;
    struct ASTnode* child;
}ASTnode;

ASTnode* ASThead=NULL;

//generate new node
ASTnode* newASTnode(TYPE type, char* text, int num, ASTnode* nxt, ASTnode* child)
{
    ASTnode* node=(ASTnode*)malloc(sizeof(ASTnode)); //不是sizeof(ASTnode*)!!!
    node->type=type;
    if(type==TEXT)
        node->text=strdup(text);
    else
        node->num=num;
    node->next=nxt;
    node->child=child;
    return node;
}

//connect sibling nodes
void connectASTnode(int num,...)
{
    va_list valist;
    va_start(valist,num);
    ASTnode* node=va_arg(valist,ASTnode*);
    for(int i=1;i<num;++i)
    {
        ASTnode* next=va_arg(valist,ASTnode*);
        node->next=next;
        node=next;
    }
    node->next=NULL;
    va_end(valist);
}

void outputAST(ASTnode* head,int tab)
{
    switch (head->type)
    {
        case NUM:
            printf("\033[1;35m%d\033[0m\n",head->num);
            break;
        case TEXT:
            if(head->child!=NULL)
                printf("\033[1;32m%s\033[0m\n",head->text);
            else
                printf("\033[1;34m%s\033[0m\n",head->text);
            break;
    }
    ASTnode* node=head->child;
    while(node!=NULL)
    {
        for(int i=0;i<tab;++i)
            printf("\t");
        printf("└───────");
        outputAST(node,tab+1);
        node=node->next;
    }
}

void freeAST(ASTnode *head)
{
    ASTnode *node=head->child;
    while (node!=NULL)
    {
        freeAST(node);
        node=node->next;
    }
    if(head->type==TEXT)
        free(head->text);
    free(head);
}
  1. 这个头文件中主要包括了AST的构建、输出、释放等函数,还有8进制、16进制转十进制数的函数

  2. 节点ASTnode中既可以存字符串也可以存整型,TYPE type指明了这个节点存储的数据类型,其中TYPE是一个枚举类型

  3. 前面3个extern声明了Flex(lex.yy.c)中自带的三个变量,从而可以在Bison中使用

  4. yyerror是Bison无法归约时会调用的报错函数,这里它会输出出错信息、出错行数以及出错字符串

  5. connectASTnode函数使用了C语言中多参数函数的机制,可以连接任意个数的兄弟节点,每天一个make bug小魔法

    #include <stdarg.h>
    // num is number of parameters in ...
    type func_name(int num,...)
    {
      // 声明参数列表
        va_list valist;
        va_start(valist,num);
      // 以TYPE类型取出参数
        TYPE val=va_arg(valist,TYPE);
        for(int i=1;i<num;++i)
        {
            TYPE next=va_arg(valist,TYPE);
            ...
        }
        va_end(valist);
    }
  6. C语言不知道怎么输出树,所以采用了文件树的形式,以tab数表示该节点所处的深度,每个节点的父节点都是其上方最近的tab-1节点

  7. 用全局变量ASThead来存储AST的头节点,我们只需要在每次执行归约到开始符号(即左部是开始符号)的产生式时将开始符号的节点赋值给ASThead,那么最后一次赋值(即最后一次归约)的必然是AST的头节点


∬ SysY.l

%option yylineno

%{
    #include "SysY.tab.h"
    #define UNEXPECTED 0
%}

INT                 int
VOID                void
CONST               const
IF                  if
ELSE                else
WHILE               while
BREAK               break
CONTINUE            continue
RETURN              return
MULDIVSUR           "*"|"/"|"%"
ADDSUB              "+"|"-"
CMP                 "<"|">"|"<="|">="
EQNEQ               "=="|"!="
ASSIGN              "="
NONZERO             [1-9]
DIGIT               [0-9]
LETTER              [A-Za-z]
OCTAL_DIGIT         [0-7]
OCTAL_CONST         0{OCTAL_DIGIT}*
ILLEGAL_OCTAL_CONST 0[0-9a-wy-zA-WY-Z]({LETTER}|{DIGIT})*
HEX_PREFIX          0x|0X
HEX_DIGIT           [0-9a-fA-F]
HEX_CONST           {HEX_PREFIX}{HEX_DIGIT}+
ILLEGAL_HEX_CONST   {HEX_PREFIX}({LETTER}|{DIGIT})*
NONDIGIT            {LETTER}|"_"
ID                  {NONDIGIT}({DIGIT}|{NONDIGIT})*
DEC_CONST           {NONZERO}{DIGIT}*  
COMMENT1            "/*"[^*]*"*"+([^*/][^*]*"*"+)*"/"
COMMENT2            "//".*                                                                                

%%

{INT}                   { yylval.str=strdup(yytext); return INT; }
{VOID}                  { yylval.str=strdup(yytext); return VOID; }
{OCTAL_CONST}           { yylval.str=strdup(yytext); return OCTAL_CONST; }
{ILLEGAL_OCTAL_CONST}   { yylval.str=strdup(yytext); return HEX_CONST; }
{HEX_CONST}             { yylval.str=strdup(yytext); return HEX_CONST; }
{ILLEGAL_HEX_CONST}     { yylval.str=strdup(yytext); return DEC_CONST; }
{DEC_CONST}             { yylval.str=strdup(yytext); return DEC_CONST; }
{CONST}                 { yylval.str=strdup(yytext); return CONST; }
{IF}                    { yylval.str=strdup(yytext); return IF; }
{ELSE}                  { yylval.str=strdup(yytext); return ELSE; }
{WHILE}                 { yylval.str=strdup(yytext); return WHILE; }
{BREAK}                 { yylval.str=strdup(yytext); return BREAK; }
{CONTINUE}              { yylval.str=strdup(yytext); return CONTINUE; }
{RETURN}                { yylval.str=strdup(yytext); return RETURN; }
{MULDIVSUR}             { yylval.str=strdup(yytext); return MULDIVSUR; } 
{ADDSUB}                { yylval.str=strdup(yytext); return ADDSUB; } 
{CMP}                   { yylval.str=strdup(yytext); return CMP; } 
{EQNEQ}                 { yylval.str=strdup(yytext); return EQNEQ; } 
{ASSIGN}                { yylval.str=strdup(yytext); return ASSIGN; }
{ID}                    { yylval.str=strdup(yytext); return ID; }
"("                     { yylval.str=strdup(yytext); return yytext[0]; }
")"                     { yylval.str=strdup(yytext); return yytext[0]; }
"["                     { yylval.str=strdup(yytext); return yytext[0]; }
"]"                     { yylval.str=strdup(yytext); return yytext[0]; }
"{"                     { yylval.str=strdup(yytext); return yytext[0]; }
"}"                     { yylval.str=strdup(yytext); return yytext[0]; }
";"                     { yylval.str=strdup(yytext); return yytext[0]; }
","                     { yylval.str=strdup(yytext); return yytext[0]; }
"&&"                    { yylval.str=strdup(yytext); return AND; }
"||"                    { yylval.str=strdup(yytext); return OR; }
{COMMENT1}|{COMMENT2}   { }
[ \t\n]                 { }
.                       { yylval.str=strdup(yytext); return UNEXPECTED; }
%%

int yywrap(void) 
{ 
    return 1;
}
  1. “SysY.tab.h”是bison -d SysY.y产生的,Bison会将你声明的token进行enum放在生成的头文件”SysY.tab.h”中,所以你无需自己再写一个”translator.h”
  2. 注意在Flex中需要完成对token的赋值,如果token的类型为TYPE(取自%union),那么你需要以yylval.TYPE=...的形式进行赋值,然后return token_name;
  3. char* strdup(char *s)函数会开辟一段新的空间并复制字符串s的值,然后返回新空间的指针(需要free)

∭ SysY.y

%start CompUnit
%expect 1
 
%{
    #include "parser.h"
%}
 
%union
{
    int     num;
    char*   str;
    struct ASTnode* node; /*"struct" is indispensable*/
}
 
%token <str> INT VOID CONST IF ELSE WHILE BREAK CONTINUE RETURN ID OCTAL_CONST HEX_CONST DEC_CONST
%right <str> ASSIGN
%left <str> OR
%left <str> AND
%left <str> EQNEQ
%left <str> CMP
%left <str> ADDSUB
%left <str> MULDIVSUR
 
%type<node> Number CompUnit Decl FuncDef ConstDecl VarDecl ConstDef ConstDefBlock ConstExpBlock ConstInitVal ConstExp ConstInitFlag ConstValBlock VarDef
    VarDefFlag InitVal Exp InitValFlag InitValBlock FuncFParams Block FuncFParam FuncFParamBlock ExpBlockFlag ExpBlock BlockItemBlock BlockItem 
    Stmt LVal ExpFlag StmtFlag Cond AddExp LOrExp PrimaryExp UnaryExp FuncFParamsFlag FuncRParams UNARYOP CommaExpBlock MulExp RelExp EqExp LAndExp
 
%%
CompUnit:       CompUnit Decl                           {
                                                            connectASTnode(2,$1,$2);
                                                            ASThead=$$=newASTnode(TEXT,"CompUnit",0,NULL,$1);
                                                        }
                | CompUnit FuncDef                      {
                                                            connectASTnode(2,$1,$2);
                                                            ASThead=$$=newASTnode(TEXT,"CompUnit",0,NULL,$1);
                                                        }
                | Decl                                  {ASThead=$$=newASTnode(TEXT,"CompUnit",0,NULL,$1);}
                | FuncDef                               {ASThead=$$=newASTnode(TEXT,"CompUnit",0,NULL,$1);}
Decl:           ConstDecl                               {$$=newASTnode(TEXT,"Decl",0,NULL,$1);}
                | VarDecl                               {$$=newASTnode(TEXT,"Decl",0,NULL,$1);}
ConstDecl:      CONST INT ConstDef ConstDefBlock ';'    {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,$2,0,NULL,NULL),
                                                                    *n5=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(5,n1,n2,$3,$4,n5);
                                                            $$=newASTnode(TEXT,"ConstDecl",0,NULL,n1);
                                                        }
ConstDefBlock:  ConstDefBlock ',' ConstDef              {
                                                            ASTnode *n=newASTnode(TEXT,",",0,$3,NULL);
                                                            connectASTnode(3,$1,n,$3);
                                                            $$=newASTnode(TEXT,"ConstDefBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ConstDefBlock",0,NULL,n);
                                                        }
ConstDef:       ID ConstExpBlock ASSIGN ConstInitVal    {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,$3,0,NULL,NULL);
                                                            connectASTnode(4,n1,$2,n3,$4);
                                                            $$=newASTnode(TEXT,"ConstDef",0,NULL,n1);
                                                        }
ConstExpBlock:  ConstExpBlock '[' ConstExp ']'          {
                                                            ASTnode *n2=newASTnode(TEXT,"[",0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,"]",0,NULL,NULL);
                                                            connectASTnode(4,$1,n2,$3,n4);
                                                            $$=newASTnode(TEXT,"ConstExpBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ConstExpBlock",0,NULL,n);
                                                        }
ConstInitVal:   ConstExp                                {$$=newASTnode(TEXT,"ConstInitVal",0,NULL,$1);}
                |'{'ConstInitFlag'}'                    {
                                                            ASTnode *n1=newASTnode(TEXT,"{",0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,"}",0,NULL,NULL);
                                                            connectASTnode(3,n1,$2,n3);
                                                            $$=newASTnode(TEXT,"ConstInitVal",0,NULL,n1);
                                                        }
ConstInitFlag:  ConstInitVal ConstValBlock              {
                                                            connectASTnode(2,$1,$2);
                                                            $$=newASTnode(TEXT,"ConstInitFlag",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ConstInitFlag",0,NULL,n);
                                                        }
ConstValBlock:  ConstValBlock ',' ConstInitVal          {
                                                            ASTnode *n2=newASTnode(TEXT,",",0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"ConstValBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ConstValBlock",0,NULL,n);
                                                        }
VarDecl:        INT VarDef VarDefFlag ';'               {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(4,n1,$2,$3,n4);
                                                            $$=newASTnode(TEXT,"VarDecl",0,NULL,n1);
                                                        }
VarDefFlag:    ',' VarDef VarDefFlag                    {
                                                            ASTnode *n1=newASTnode(TEXT,",",0,NULL,NULL);
                                                            connectASTnode(3,n1,$2,$3);
                                                            $$=newASTnode(TEXT,"VarDefFlag",0,NULL,n1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"VarDefFlag",0,NULL,n);
                                                        }
VarDef:         ID ConstExpBlock                        {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL);
                                                            connectASTnode(2,n1,$2);
                                                            $$=newASTnode(TEXT,"VarDef",0,NULL,n1);
                                                        }
                | ID ConstExpBlock ASSIGN InitVal       {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,$3,0,NULL,NULL);
                                                            connectASTnode(4,n1,$2,n3,$4);
                                                            $$=newASTnode(TEXT,"VarDef",0,NULL,n1);
                                                        }
InitVal:          Exp                                   {$$=newASTnode(TEXT,"InitVal",0,NULL,$1);}
                | '{'InitValFlag'}'                     {
                                                            ASTnode *n1=newASTnode(TEXT,"{",0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,"}",0,NULL,NULL);
                                                            connectASTnode(3,n1,$2,n3);
                                                            $$=newASTnode(TEXT,"InitVal",0,NULL,n1);
                                                        }
InitValFlag:    InitVal InitValBlock                    {
                                                            connectASTnode(2,$1,$2);
                                                            $$=newASTnode(TEXT,"InitValFlag",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"InitValFlag",0,NULL,n);
                                                        }
InitValBlock:   InitValBlock ',' InitVal                {
                                                            ASTnode *n2=newASTnode(TEXT,",",0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"InitValBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"InitValFlag",0,NULL,n);
                                                        }
FuncDef:        INT ID '(' FuncFParams')' Block         {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,$2,0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,"(",0,NULL,NULL),
                                                                    *n5=newASTnode(TEXT,")",0,NULL,NULL);
                                                            connectASTnode(6,n1,n2,n3,$4,n5,$6);
                                                            $$=newASTnode(TEXT,"FuncDef",0,NULL,n1);
                                                        }
                | VOID ID '(' FuncFParams')' Block      {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,$2,0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,"(",0,NULL,NULL),
                                                                    *n5=newASTnode(TEXT,")",0,NULL,NULL);
                                                            connectASTnode(6,n1,n2,n3,$4,n5,$6);
                                                            $$=newASTnode(TEXT,"FuncDef",0,NULL,n1);
                                                        }
FuncFParams:    FuncFParam FuncFParamBlock              {
                                                            connectASTnode(2,$1,$2);
                                                            $$=newASTnode(TEXT,"FuncFParams",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"FuncFParams",0,NULL,n);
                                                        }
FuncFParamBlock:FuncFParamBlock ',' FuncFParam          {
                                                            ASTnode *n2=newASTnode(TEXT,",",0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"FuncFParamBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"FuncFParamBlock",0,NULL,n);
                                                        }
FuncFParam:     INT ID ExpBlockFlag                     {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,n1,n2,$3);
                                                            $$=newASTnode(TEXT,"FuncFParam",0,NULL,n1);
                                                        }
ExpBlockFlag:   '['']' ExpBlock                         {
                                                            ASTnode *n1=newASTnode(TEXT,"[",0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,"]",0,NULL,NULL);
                                                            connectASTnode(3,n1,n2,$3);
                                                            $$=newASTnode(TEXT,"ExpBlockFlag",0,NULL,n1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ExpBlockFlag",0,NULL,n);
                                                        }
ExpBlock:       ExpBlock '['Exp']'                      {
                                                            ASTnode *n2=newASTnode(TEXT,"[",0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,"]",0,NULL,NULL);
                                                            connectASTnode(4,$1,n2,$3,n4);
                                                            $$=newASTnode(TEXT,"FuncDef",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ExpBlock",0,NULL,n);
                                                        }
Block:          '{' BlockItemBlock '}'                  {
                                                            ASTnode *n1=newASTnode(TEXT,"{",0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,"}",0,NULL,NULL);
                                                            connectASTnode(3,n1,$2,n3);
                                                            $$=newASTnode(TEXT,"Block",0,NULL,n1);
                                                        }
BlockItemBlock: BlockItemBlock BlockItem                {
                                                            connectASTnode(2,$1,$2);
                                                            $$=newASTnode(TEXT,"BlockItemBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"BlockItemBlock",0,NULL,n);
                                                        }
BlockItem:        Decl                                  {$$=newASTnode(TEXT,"BlockItem",0,NULL,$1);}
                | Stmt                                  {$$=newASTnode(TEXT,"BlockItem",0,NULL,$1);}
Stmt:           LVal ASSIGN Exp ';' %prec ASSIGN        {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(4,$1,n2,$3,n4);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,$1);
                                                        }
                | ExpFlag';'                            {
                                                            ASTnode *n2=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(2,$1,n2);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,$1);
                                                        }
                | Block                                 {$$=newASTnode(TEXT,"Stmt",0,NULL,$1);}
                | IF'('Cond')' Stmt StmtFlag            {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,"(",0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,")",0,NULL,NULL);
                                                            connectASTnode(6,n1,n2,$3,n4,$5,$6);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,n1);
                                                        }
                | WHILE'('Cond')' Stmt                  {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,"(",0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,")",0,NULL,NULL);
                                                            connectASTnode(5,n1,n2,$3,n4,$5);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,n1);
                                                        }
                | BREAK';'                              {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(2,n1,n2);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,n1);
                                                        }
                | CONTINUE';'                           {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(2,n1,n2);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,n1);
                                                        }
                | RETURN ExpFlag';'                     {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,";",0,NULL,NULL);
                                                            connectASTnode(3,n1,$2,n3);
                                                            $$=newASTnode(TEXT,"Stmt",0,NULL,n1);
                                                        }
ExpFlag:          Exp                                   {$$=newASTnode(TEXT,"ExpFlag",0,NULL,$1);}
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"ExpFlag",0,NULL,n);
                                                        }
StmtFlag:       ELSE Stmt                               {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL);
                                                            connectASTnode(2,n1,$2);
                                                            $$=newASTnode(TEXT,"StmtFlag",0,NULL,n1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"StmtFlag",0,NULL,n);
                                                        }
Exp:            AddExp                                  {$$=newASTnode(TEXT,"Exp",0,NULL,$1);}
Cond:           LOrExp                                  {$$=newASTnode(TEXT,"Cond",0,NULL,$1);}
LVal:           ID ExpBlock                             {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL);
                                                            connectASTnode(2,n1,$2);
                                                            $$=newASTnode(TEXT,"LVal",0,NULL,n1);
                                                        }
PrimaryExp:     '('Exp')'                               {
                                                            ASTnode *n1=newASTnode(TEXT,"(",0,NULL,NULL),
                                                                    *n3=newASTnode(TEXT,")",0,NULL,NULL);
                                                            connectASTnode(3,n1,$2,n3);
                                                            $$=newASTnode(TEXT,"PrimaryExp",0,NULL,n1);
                                                        }
                | LVal                                  {$$=newASTnode(TEXT,"PrimaryExp",0,NULL,$1);}
                | Number                                {$$=newASTnode(TEXT,"PrimaryExp",0,NULL,$1);}
Number:         OCTAL_CONST                             {
                                                            ASTnode *n=newASTnode(NUM,NULL,OCT2DEC($1),NULL,NULL);
                                                            $$=newASTnode(TEXT,"Number",0,NULL,n);
                                                        }
                | HEX_CONST                             {
                                                            ASTnode *n=newASTnode(NUM,NULL,HEX2DEC($1),NULL,NULL);
                                                            $$=newASTnode(TEXT,"Number",0,NULL,n);
                                                        }
                | DEC_CONST                             {
                                                            ASTnode *n=newASTnode(NUM,NULL,atoi($1),NULL,NULL);
                                                            $$=newASTnode(TEXT,"Number",0,NULL,n);
                                                        }
UnaryExp:       PrimaryExp                              {$$=newASTnode(TEXT,"UnaryExp",0,NULL,$1);}
                | ID '(' FuncFParamsFlag ')'            {
                                                            ASTnode *n1=newASTnode(TEXT,$1,0,NULL,NULL),
                                                                    *n2=newASTnode(TEXT,"(",0,NULL,NULL),
                                                                    *n4=newASTnode(TEXT,")",0,NULL,NULL);
                                                            connectASTnode(4,n1,n2,$3,n4);
                                                            $$=newASTnode(TEXT,"PrimaryExp",0,NULL,n1);
                                                        }
                | UNARYOP UnaryExp                      {
                                                            connectASTnode(2,$1,$2);
                                                            $$=newASTnode(TEXT,"UnaryExp",0,NULL,$1);
                                                        }
FuncFParamsFlag:FuncRParams                             {$$=newASTnode(TEXT,"FuncFParamsFlag",0,NULL,$1);}
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"FuncFParamsFlag",0,NULL,n);
                                                        }
FuncRParams:    Exp CommaExpBlock                       {
                                                            connectASTnode(2,$1,$2);
                                                            $$=newASTnode(TEXT,"FuncRParams",0,NULL,$1);
                                                        }
CommaExpBlock:  CommaExpBlock ',' Exp                   {
                                                            ASTnode *n2=newASTnode(TEXT,",",0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"CommaExpBlock",0,NULL,$1);
                                                        }
                | /*ε*/                                 {
                                                            ASTnode *n=newASTnode(TEXT,"ε",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"CommaExpBlock",0,NULL,n);
                                                        }
UNARYOP:        ADDSUB                                  {
                                                            ASTnode *n=newASTnode(TEXT,$1,0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"UNARYOP",0,NULL,n);
                                                        }
                | '!'                                   {
                                                            ASTnode *n=newASTnode(TEXT,"!",0,NULL,NULL);
                                                            $$=newASTnode(TEXT,"UNARYOP",0,NULL,n);
                                                        }
MulExp:         UnaryExp                                {$$=newASTnode(TEXT,"MulExp",0,NULL,$1);}
                | MulExp MULDIVSUR UnaryExp             {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"MulExp",0,NULL,$1);
                                                        }
AddExp:         MulExp                                  {$$=newASTnode(TEXT,"AddExp",0,NULL,$1);}
                | AddExp ADDSUB MulExp                  {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"MulExp",0,NULL,$1);
                                                        }
RelExp:         AddExp                                  {$$=newASTnode(TEXT,"RelExp",0,NULL,$1);}
                | RelExp CMP AddExp                     {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"RelExp",0,NULL,$1);
                                                        }
EqExp:          RelExp                                  {$$=newASTnode(TEXT,"EqExp",0,NULL,$1);}
                | EqExp EQNEQ RelExp                    {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"EqExp",0,NULL,$1);
                                                        }
LAndExp:        EqExp                                   {$$=newASTnode(TEXT,"LAndExp",0,NULL,$1);}
                | LAndExp AND EqExp                     {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"LAndExp",0,NULL,$1);
                                                        }
LOrExp:         LAndExp                                 {$$=newASTnode(TEXT,"LOrExp",0,NULL,$1);}
                | LOrExp OR LAndExp                     {
                                                            ASTnode *n2=newASTnode(TEXT,$2,0,NULL,NULL);
                                                            connectASTnode(3,$1,n2,$3);
                                                            $$=newASTnode(TEXT,"LOrExp",0,NULL,$1);
                                                        }
ConstExp:       AddExp                                  {$$=newASTnode(TEXT,"ConstExp",0,NULL,$1);}
%%
 
int main()
{
    yyparse();
    outputAST(ASThead,0);
    freeAST(ASThead);
    return 0;
}

Test.c

int main()
{
    int bb = 0x12d23;
    if (abc >= bb) // hello
            return 8;
    /*This is a test
    */
}

Cast Magic 🪄

flex SysY.l
bison -d SysY.y
clang SysY.tab.c lex.yy.c
cat test.c|./a.out

Output

  • 7
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值