手写编译器-尾递归

手写编译器

谈谈尾递归在变编译器中的实现

这一篇主要讲述生产式到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");
        }
    }

}


你可能感兴趣的:(编译原理,响应式编程,2020-01,编译器,java,编程语言)