現代編譯原理--第三章(抽象語法樹以及源碼)


 (轉載請表明出處 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文件中定義的。(其實在實際編寫過程中,應該是先寫語法再寫詞法,書上的順序是返的,便於大家理解而已。)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM