手寫編譯器-尾遞歸

手寫編譯器

談談尾遞歸在變編譯器中的實現

這一篇主要講述生產式到java代碼的生成過程;

何爲生產式

expr ->   expr + term {print('+')}
        | expr - term {print('-')}
        | term
term ->  0   {print('0')}
        |1  {print('1')}
        |2  {print('2')}
         ...
        |9  {print('9')}

expr : 表示no ternimal (非終結符號,可以繼續解析)
term : 表示no ternimal (非終結符號,可以繼續解析)
0,1 … 9 : 數字(terminal 終結符號)
+ - : 操作符(terminal 終結符號)
| : 或者的意思,表示當前非終結符號可以解析的可能性
就expr 解析來說 有3種解析路勁,但是有的解析路徑有可以又包含expr , 又可以繼續解析,這是一個遞歸過程;
關於生產式的大致講到這,總體概念是這樣,想要更多細節還得深入。

生產式的解析

生產式的解析需要藉助抽象語法樹(Abstract and concrete tree)。比如9-5+2是符合上述生產式的,其抽象語法樹

            +
        /       \
        -       2
    /       \
    9       5

我們知道上述的生生產式解析其實有左遞歸問題的(LR),可以就看成是 A代表expr。則 A->Aa|Aß|y 就是一個左遞歸產常見的例子,和上面的生產式類似;

消除該左遞歸之後可得到(消除方法見另一篇手寫編譯器-消除左遞歸

expr ->   term rest
rest -> + term {print('-')} rest
        | - term {print('-')} rest
        | £
term ->  0   {print('0')}
        |1  {print('1')}
        |2  {print('2')}
         ...
        |9  {print('9')}

該解析樹用代碼java僞代碼實現

void expr(){
    term();reset();
}
void rest(){
    if(lookahead == '+'){
        match('+');term();print('+');rest();
    }else if(lookahead == '-'){
        match('-');term();print('-');rest();
    }else{
        //do nothing
    }
}
void term(){
    if (lookahead is a digit){
        t=lookahead;match(lookahead);print(t)
    }
    else{
        report("Syntax error);
    }
}

上述代碼是正常將生產式轉換而成的。但是採用帶了遞歸的方法;

  • 遞歸比較複雜,不夠簡化
  • 遞歸會當數據量比較大的時候,會加劇深棧操作,我們顯示寫代碼中應該儘量避免
    深棧操作的避免也體現在Spring reactor對鏈路的優化,相反Rxjava採用了深棧操作(後續再給源碼大家看)
  • 編譯器避免了遞歸而導致的深棧操作,尾遞歸

故可進一步簡化上述代碼的rest方法,用循環替換,亦可以達到上述方法的實現

void rest(){
    while(true){
        if(lookahead == '+'){
            match('+');term();print('+');continue;
        }else if(lookahead == '-'){
            match('-');term();print('-');continue;
        }
        break;
    }
}

下面將完整的代碼實現貼上,爲大家理解javac的源碼助一臂之力。


import java.io.*;
class Parser{
    static int lookahead;//輸入位置,當前遊標
    public Parser(){
        lookahead = System.in.read();
    }
    void expr() throw IoException{
        term();
        while(true){
        if(lookahead == '+'){
            match('+');term();System.out.print('+');continue;
        }else if(lookahead == '-'){
            match('-');term();System.out.print('-');continue;
        }
        break;
        }
    }
    void term() throw IoException {
        if(Character.isDigit((char)lookahead)){
            System.out.print(lookahead);match(lookahead);
        }
        throw new IoException("Syntax error");
    }
    void match(int t){
        if(lookahead == t){
            lookahead =System.in.read(); 
        }
        throw new IoException("Syntax error");
    }

    public class PostFix{
        public static void main(String[] args){
            Parser parser = new Parser();
            parser.expr();
            System.out.print("\n");
        }
    }

}


發佈了24 篇原創文章 · 獲贊 20 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章