jacksplwxy

现代编译原理——第三章:抽象语法树以及源码

  转自: http://www.cnblogs.com/BlackWalnut/p/4508093.html

  这是flxe的文件,文件名称为tiger.l

  

复制代码
%{
#include <string.h>
#include "util.h"
#include "tokens.h"
#include "errormsg.h"
#include "iostream"
#include "tiger.tab.h"
int charPos=1;
int count =  0 ;

#ifdef __cplusplus
extern "C" int yywrap (void )
#else
extern int yywrap (void )
#endif
{
    charPos = 1 ;
    return 1 ;
}

void adjust(void)
{
 EM_tokPos=charPos;
 charPos+=yyleng;
}

%}


%state  COMMENT
%state  CONST_STRING

%%
<INITIAL>[\"]   {adjust();  BEGIN CONST_STRING;}
<INITIAL>" "     {adjust();  continue;}
<INITIAL>\n      {adjust(); lineNum++; continue;}
<INITIAL>\t   {adjust(); continue;}
<INITIAL>","  {adjust(); std::cout << "COMMA" << std::endl ; return COMMA;}
<INITIAL>:    {adjust(); std::cout << "COLON" << std::endl ; return COLON;}
<INITIAL>;    {adjust(); std::cout << "SEMICOLON" << std::endl ; return SEMICOLON;}
<INITIAL>"("  {adjust(); std::cout << "LPAREN" << std::endl ; return LPAREN;}
<INITIAL>")"  {adjust(); std::cout << "RPAREN" << std::endl ; return RPAREN;}
<INITIAL>"["  {adjust(); std::cout << "LBRACK" << std::endl ; return LBRACK;}
<INITIAL>"]"  {adjust(); std::cout << "RBRACK" << std::endl ; return RBRACK;}
<INITIAL>"{"  {adjust(); std::cout << "LBRACE" << std::endl ; return LBRACE;}
<INITIAL>"}"  {adjust(); std::cout << "RBRACE" << std::endl ; return RBRACE;}
<INITIAL>"."  {adjust(); std::cout << "DOT" << std::endl ; return DOT;}
<INITIAL>"+"  {adjust(); std::cout << "PLUS" << std::endl ; return PLUS;}
<INITIAL>"-"  {adjust(); std::cout << "MINUS" << std::endl ; return MINUS;}
<INITIAL>"*"  {adjust(); std::cout << "TIMES" << std::endl ; return TIMES;}
<INITIAL>"/"  {adjust(); std::cout << "DIVIDE" << std::endl ; return DIVIDE;}
<INITIAL>"="  {adjust(); std::cout << "EQ" << std::endl ; return EQ;}
<INITIAL>"<>" {adjust(); std::cout << "NEQ" << std::endl ;  return NEQ;}
<INITIAL>"<"  {adjust(); std::cout << "LT" << std::endl ; return LT;}
<INITIAL>"<=" {adjust(); std::cout << "LE" << std::endl ; return LE;}
<INITIAL>">"  {adjust(); std::cout << "GT" << std::endl ; return GT;}
<INITIAL>">=" {adjust(); std::cout << "GE" << std::endl ; return GE;}
<INITIAL>"&"  {adjust(); std::cout << "AND" << std::endl ; return AND;}
<INITIAL>"|"  {adjust(); std::cout << "OR" << std::endl ; return OR;}
<INITIAL>:=   {adjust(); std::cout << "ASSIGN" << std::endl ; return ASSIGN;}
<INITIAL>for  {adjust(); std::cout << "FOR" << std::endl ; return FOR;}
<INITIAL>array {adjust();std::cout << "ARRAY" << std::endl ;  return ARRAY;}
<INITIAL>if   {adjust(); std::cout << "IF" << std::endl ; return IF;}
<INITIAL>then {adjust(); std::cout << "THEN" << std::endl ; return THEN;} 
<INITIAL>else {adjust(); std::cout << "ELSE" << std::endl ; return ELSE;}  
<INITIAL>while {adjust(); std::cout << "WHILE" << std::endl ; return WHILE;}
<INITIAL>to   {adjust(); std::cout << "TO" << std::endl ; return TO;}
<INITIAL>do   {adjust();std::cout << "DO" << std::endl ;  return DO;}
<INITIAL>let  {adjust();std::cout << "LET" << std::endl ;  return LET;}
<INITIAL>in   {adjust();std::cout << "IN" << std::endl ;  return IN;}
<INITIAL>end  {adjust();std::cout << "END" << std::endl ;  return END;}
<INITIAL>of   {adjust(); std::cout << "OF" << std::endl ; return OF;}
<INITIAL>break {adjust(); std::cout << "BREAK" << std::endl ; return BREAK;}
<INITIAL>nil  {adjust(); std::cout << "NIL" << std::endl ; return NIL;}
<INITIAL>function {adjust();std::cout << "FUNCTION" << std::endl ;  return FUNCTION;}
<INITIAL>var  {adjust(); std::cout << "VAR" << std::endl ; return VAR;}
<INITIAL>type {adjust();std::cout << "TYPE" << std::endl ;  return TYPE;}
<INITIAL>[0-9]+[a-zA-Z]+  {adjust();  /*error!!!!*/ }
<INITIAL>[0-9]+     {adjust(); std::cout << "INT" << std::endl ; yylval.ival=atoi(yytext); return INT;}
<INITIAL>[a-zA-Z][a-zA-Z0-9]*   {adjust(); std::cout << "ID" << std::endl ; yylval.sval=string(yytext);return ID;}
<INITIAL>"/*"  {adjust();count++; BEGIN COMMENT;}
<CONST_STRING>[\"]  {adjust();  BEGIN INITIAL ; }
<CONST_STRING>[^\"]*  {adjust(); yylval.sval = string(yytext);std::cout << "STRING" << std::endl ;  return STRING; }
<COMMENT>"*/"   {adjust();count--; if(count == 0) BEGIN INITIAL;}
<COMMENT>. {adjust();}
<COMMENT>\n {adjust();}
复制代码

  这里需要注意的是,使用了形如:

  

复制代码
#ifdef __cplusplus
static int yyinput (void );
#else
static int input (void );
#endif


#ifdef __cplusplus
extern "C" int yywrap (void )
#else
extern int yywrap (void )
#endif
{
    charPos = 1 ;
    return 1 ;
}
复制代码

  这样的标示,因为我想使用c++,但是flex生成的是c,所以这里要特别声明一下。

  以上使用flex后得到的.c文件直接改为.cpp,然后找到文件中的#include <unistd.h> ,使用 #include <io.h>  和 #include <process.h>替换就可以了。

  一下是tiger的bison文件,文件名为tiger.y

复制代码
%{
#include <stdio.h>
#include "util.h"
#include "errormsg.h"
#include "absyn.h"
#include "symbol.h"
#include "iostream"

int pos ;
extern int yylex(void); /* function prototype */
A_exp absyn_root ;
void yyerror(char *s)
{
 EM_error(EM_tokPos, "%s", s);
}

%}

  // ’‚∏ˆunion æÕ «”Ô∑®∑÷Œˆ∆˜÷–µƒ$∑˚∫≈∫ÕÕ‚ΩÁ¡™œµµƒΩËø⁄ ’‚¿Ô÷ª”√–¥“ª–©∑«÷’Ω·∑˚±Ì¥Ô Ω “‘º∞ ÷’Ω·∑˚±Ì¥Ô Ω÷–”–»∑∂®÷µµƒ≤ø∑÷ œÒ≤Ÿ◊˜∑˚÷Æ¿‡µƒæÕ≤ª”√¡À
%union {
    int ival;
    float fval ;
    string sval;
    S_symbol symbol;
    A_var var ;
    A_exp exp ;
    A_dec dec ;
    A_ty  ty ;
    A_decList decs ;
    A_expList expList;
    A_field   tyfield;
    A_fieldList tyfields;
    A_fundec  fundec;
    A_fundecList fundecList;
    A_namety  tydec;
    A_nametyList tydecList;
    A_efield  fild;
    A_efieldList  fildlist ;
    }



%type <var> lvalue 
%type <exp> exp
%type <dec> dec
%type <ty>  ty
%type <decs> decs
%type <expList> expseq 
%type <expList> expList
%type <tyfield> tyfield
%type <tyfields> tyfields
%type <fundec>  fundec
%type <fundecList> fundecList
%type <tydec> tydec 
%type <tydecList> tydecList
%type <fild> fild
%type <fildlist>  fildlist

%token <sval>  ID
%token <sval>  STRING
%token <ival>  INT 


%token 
  COMMA COLON SEMICOLON LPAREN RPAREN LBRACK RBRACK 
  LBRACE RBRACE DOT 
  PLUS MINUS TIMES DIVIDE EQ NEQ LT LE GT GE
  AND OR ASSIGN
  ARRAY IF THEN ELSE WHILE FOR TO DO LET IN END OF 
  BREAK NIL
  FUNCTION VAR TYPE UMINUS

%start program

%right FUNCTION TYPE 
%right OF
%right DO ELSE THEN 
%nonassoc ASSIGN 
%left  OR 
%left  AND
%nonassoc EQ NEQ LT LE GT GE 
%left PLUS MINUS 
%left TIMES DIVIDE  
%left UMINUS

%%

/* This is a skeleton grammar file, meant to illustrate what kind of
 * declarations are necessary above the %% mark.  Students are expected
 *  to replace the two dummy productions below with an actual grammar. 
 */

program:    exp      { absyn_root = $1 ; }


decs :  dec         { $$ = A_DecList($1 , NULL) ; }
       | dec decs   { $$ = A_DecList($1 , $2) ; }
       
dec  :  tydecList    { $$ = A_TypeDec(pos , $1) ;  }
       | VAR ID ASSIGN exp             { $$ = A_VarDec(pos , S_Symbol($2) , NULL , $4) ; } 
       | VAR ID COLON ID  ASSIGN exp   { $$ = A_VarDec(pos , S_Symbol($2) , S_Symbol($4) , $6) ;   }
       | fundecList  { $$ = A_FunctionDec(pos , $1) ; }


tydecList :  tydec                  { $$ = A_NametyList($1 , NULL); }
             | tydec tydecList      { $$ = A_NametyList($1 , $2) ; }
tydec :      TYPE ID EQ ty          { $$ = A_Namety(S_Symbol($2) , $4 ) ;}

ty :         LBRACE tyfields RBRACE { $$ = A_RecordTy(pos , $2); }
             | LBRACE RBRACE        { $$ = A_RecordTy(pos , NULL);  }
             | ARRAY OF ID          { $$ =  A_ArrayTy(pos , S_Symbol($3)); }
             | ID                   { $$ = A_NameTy(pos , S_Symbol($1)) ; }
            

tyfield   :  ID  COLON ID                { $$ = A_Field(pos, S_Symbol($1) , S_Symbol($3));   }
tyfields  :  tyfield                     { $$ = A_FieldList($1 , NULL) ; }
             | tyfield COMMA tyfields    { $$ = A_FieldList($1 , $3);    }




fundecList : fundec             { $$ = A_FundecList($1 , NULL); }
             | fundec fundecList { $$ = A_FundecList($1 , $2);   }
             
fundec : FUNCTION ID LPAREN tyfields RPAREN COLON ID  EQ exp { $$ = A_Fundec( pos, S_Symbol($2) , $4 , S_Symbol($7) , $9 ) ;     }
         | FUNCTION ID LPAREN RPAREN COLON ID EQ exp         { $$ = A_Fundec( pos, S_Symbol($2) , NULL , S_Symbol($6) , $8 ) ;   }
         | FUNCTION ID LPAREN tyfields RPAREN EQ exp         { $$ = A_Fundec( pos, S_Symbol($2) , $4 , NULL , $7 ) ;   }
         | FUNCTION ID LPAREN RPAREN EQ exp                  { $$ = A_Fundec( pos, S_Symbol($2) , NULL , NULL , $6 ) ; }
 
 
 
lvalue:     ID                     { $$ = A_SimpleVar(pos , S_Symbol($1) ) ;       }
            | lvalue DOT ID        { $$ = A_FieldVar(pos, $1 ,S_Symbol($3) );     }
            | ID LBRACK exp RBRACK { $$ = A_SubscriptVar(pos ,  A_SimpleVar(pos , S_Symbol($1) ) , $3); }  

fild     :  ID EQ exp                     { $$ =  A_Efield(  S_Symbol($1) ,$3 ) ; }
fildlist :  fild                          { $$ = A_EfieldList( $1 , NULL ); }
           | fild COMMA fildlist           { $$ = A_EfieldList( $1 ,$3 ) ; }
           
//expseq ∫Õ explist ∑÷±”√”⁄±Ì æ ±Ì¥Ô Ω∂”¡– ∫Õ   ∫Ø ˝µ˜”√  µ´ «º«¬ºÀ˚√«µƒ ˝æ›Ω·π𠱓ª—˘µƒ “ÚŒ™À˚√«÷ª”–÷–º‰µƒ÷’Ω·∑˚∫≈≤ª“ª—˘
expseq :    exp                     { $$ = A_ExpList( $1 , NULL ); }
           | exp SEMICOLON  expseq  { $$ = A_ExpList( $1 , $3 ) ;  }

expList  :  exp                  { $$ = A_ExpList($1 , NULL ) ; }
           | exp COMMA expList   { $$ = A_ExpList($1 , $3) ; }
         // - exp
exp :    MINUS exp       {  $$ = A_OpExp( pos , A_oper::A_minusOp , A_IntExp(pos , 0) , $2 ); }%prec UMINUS  
        //var
        |lvalue         { $$ = A_VarExp(pos , $1 ); }
        //nil 
        | NIL           { $$ = A_NilExp(pos); }
        | LPAREN RPAREN { $$ = A_NilExp(pos); }
        //const int
        | INT           { $$ = A_IntExp(pos , $1) ; } 
        //const string 
        | STRING        { $$ = A_StringExp(pos , $1); }
        // expression sequence
        | LPAREN expseq RPAREN    { $$ = A_SeqExp( pos , $2 ) ; }
        //CALL FUNCTION
        |ID LPAREN RPAREN         { $$ = A_CallExp( pos, S_Symbol($1) ,NULL ); }
        |ID LPAREN expList RPAREN { $$ = A_CallExp(pos , S_Symbol($1) ,$3) ; }
        //OPERATOR 
        |exp PLUS exp       { $$ = A_OpExp( pos , A_oper::A_plusOp , $1 , $3 ); }
        |exp MINUS exp      { $$ = A_OpExp( pos , A_oper::A_minusOp , $1 , $3 ); }
        |exp TIMES exp      { $$ = A_OpExp( pos , A_oper::A_timesOp , $1 , $3 ); }
        |exp DIVIDE exp     { $$ = A_OpExp( pos , A_oper::A_divideOp , $1 , $3 ); }
        |exp EQ exp         { $$ = A_OpExp( pos , A_oper::A_eqOp , $1 , $3 ); }
        |exp NEQ exp        { $$ = A_OpExp( pos , A_oper::A_neqOp , $1 , $3 ); }
        |exp LT exp         { $$ = A_OpExp( pos , A_oper::A_ltOp , $1 , $3 ); }
        |exp LE exp         { $$ = A_OpExp( pos , A_oper::A_leOp , $1 , $3 ); }
        |exp GT exp         { $$ = A_OpExp( pos , A_oper::A_gtOp , $1 , $3 ); }
        |exp GE exp         { $$ = A_OpExp( pos , A_oper::A_geOp , $1 , $3 ); }
        |exp AND exp        { $$ = A_IfExp( pos , $1 , $3 ,  A_IntExp(pos ,0)); }
        |exp OR exp         { $$ = A_IfExp( pos , $1 , A_IntExp(pos , 1) ,$3 ); }
        //RECORD
        |ID LBRACE RBRACE   { $$ = A_RecordExp(pos, S_Symbol($1) , NULL); }
        |ID LBRACE fildlist RBRACE  { $$ = A_RecordExp(pos,S_Symbol($1) , $3); } 
        //ARRAY
        |ID LBRACK exp RBRACK OF exp {  $$ = A_ArrayExp(pos , S_Symbol($1) , $3 ,$6) ; }
        //assign
        |lvalue ASSIGN exp  { $$ = A_AssignExp(pos , $1 , $3) ; }
        //if...then....else  
        |IF exp THEN exp ELSE exp {  $$ = A_IfExp( pos , $2 , $4 , $6 ) ;}
        |IF exp THEN exp  {  $$ = A_IfExp( pos , $2 , $4 , NULL) ;}
        //while
        |WHILE exp DO  exp { $$ = A_WhileExp(pos, $2 , $4 );} 
        //for 
        |FOR ID ASSIGN exp TO exp DO exp { $$ =  A_ForExp( pos, S_Symbol($2) , $4 , $6 , $8); }
        //break
        | BREAK                    { $$ = A_BreakExp( pos ) ; }
        //let
        | LET decs IN expseq  END  { $$ = A_LetExp( pos , $2 , A_SeqExp( pos , $4 ));}
        | LET decs IN END       { $$ = A_LetExp( pos , $2 ,NULL) ;}
        
        
复制代码

  对于这个文件,虎书给的是.grm文件后缀,但是只有使用.y文件后缀才可以生成相应的.c和.h文件。同样,为了使用c++,将文件后缀改为.cpp。

  这里提一些要点:

  1.使用bison生成文件时,输入-b 生成的文件可以进行debug,使用-v可以得到一个.output文件,这个文件里就记录了所有的状态信息,方便查看各种移进规约冲突等的位置。

  2.当出现$$ 未定义标示符的时候,说明你的bison没有加union 和 %type 语句,如果再加上%token 就可以生成.h文件中定义了一个枚举和一个联合,因为在联合中的类型都是自己定义的,所以在生成的.h文件中还要加得到的.h文件还要加上在程序中使用的相应的头文件 如#include "symbol.h" ,#include "absyn.h"。生成的这个.h文件很重要这两个是实现bison生成的语法分析器和flex生成的词法分析器交互的数据结构。

     联合有两个作用,第一个定义一个栈,将语法分析过程中的数据都存放到这个栈中,使用这个栈生成最后的抽象语法分析树。第二个作用是定义了一个变量 yyval,这个变量 yylval 的类型是YYSTYPE(这个类型就是联合)。这个变量将词法分析过程中的值记录下来放入栈中,如{adjust(); std::cout << "ID" << std::endl ; yylval.sval=string(yytext);return ID;}中记录ID的实际命名时什么,还有一个作用就是使用它记录了语法分析器中的动作(每条规则后面的c++程序)中的值返回值。找到这个变量的定义,发现上面有一个注释:该变量返回动作中的值,也就是说它里面记录了动作(每条规则后面的c++程序)中的值,并压入栈中。看看bison生成的代码可以知道,yyval也就是$$,他的每个动作都是使用栈顶的内容生成一个新的内容,老的内容被移出去,然后赋值给yylval,最后由这个yylval值在赋值给栈顶。从而完成了抽象语法数的构建。这样可以知道,在整个过程中,词法分析器中使用的内容是由.h文件定义的类型,以及在.c文件中定义的变量来生成的。

  在联合的下面定义了一些终结符和非终结符的具体类型,可以看出都是联合中包含的类型,这样就保证了可以使用一个类型(联合)来创建一个栈,这个栈可以包含多种类型。

     词法分析器向语法分析器发送的是一个个的终结符,如 中的return <INITIAL>:= {adjust(); std::cout << "ASSIGN" << std::endl ; return ASSIGN;} 由上面的分析可以知道,这句话中的ASSIGN在语法分析器的.h文件中定义的。(其实在实际编写过程中,应该是先写语法再写词法,书上的顺序是返的,便于大家理解而已。)

posted on 2018-12-02 11:45  jacksplwxy  阅读(1440)  评论(0编辑  收藏  举报

导航