1. 程式人生 > >現代編譯原理——第三章:抽象語法樹以及原始碼

現代編譯原理——第三章:抽象語法樹以及原始碼

  轉自: 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檔案中定義的。(其實在實際編寫過程中,應該是先寫語法再寫詞法,書上的順序是返的,便於大家理解而已。)