编译器实战 消除左递归

328 阅读1分钟

常见的成员表达式写法

const o={a:1,b:2};
o.a;
o.b;

这里o.a是一个成员表达式,由两部分组成object o 和property a

compiler.png

写成巴克斯扩展范式,先匹配一个 ID 再匹配一个“.”成员运算符,再接一个ID。

memberExpr:ID '.' ID;

O_A.png o.a成功解析出来。

可该范式无法解决a.b.c.d这样的成员表达式嵌套问题

chain.png 因为一个成员运算符 允许跟在ID后,也可以跟在一个成员表达式后,如果我们把巴克斯扩展范式写成这样,那么就遇到了著名的左递归问题。

memberExpr:(ID|memberExpr) '.' ID

这条规则在递归下降解析中,写成如下形式:

function matchMemberExpr(){
    if(matchID()||matchMemberExpr()){
        if(matchDot()&& matchID();){
            return new MemberExpr();
        }
     }
}

matchMemberExpr被不断调用,最终导致栈溢出。好在我们由固定的套路来解决这个问题--左递归文法改写。 文法改写由两个要点,1.把左递归转为右递归

memberExpr: memberExpr '.' ID
           |ID '.' ID;