前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Stanford公开课《编译原理》学习笔记(2)递归下降法

Stanford公开课《编译原理》学习笔记(2)递归下降法

作者头像
大史不说话
发布于 2019-10-08 02:46:50
发布于 2019-10-08 02:46:50
1.1K00
代码可运行
举报
运行总次数:0
代码可运行

目录

课程里涉及到的内容讲的还是很清楚的,但个别地方有点脱节,建议课下自己配合经典著作《Compilers-priciples, Techniques and Tools》(也就是大名鼎鼎的龙书)作为补充阅读。

一. Parse阶段

词法分析阶段的任务是将字符串转为Token组,而Parse阶段的目标是将Token变为Parse Tree,本篇只是这部分内容最基础的一部分。

CFG

CFGcontext free grammer,定义一种CFG语法规则需要声明如下特征:

  • 一组终止符号集,也称为“词法单元”
  • 一组非终止符号集,也称为“语法变量”
  • 一个开始符号集
  • 若干产生式规则(产生式则就是指在当前CFG的语法下,产生符号->左右两侧可以互相替代)

CFG的基本转换流程如下:

从隶属于开始集S开始,尝试将字符串中的非终止符X替换为终止集的形式(X->Y1Y2...Yn),重复这个步骤直到字符串序列中不再有非终止符。这个过程被称为Derivation(派生),它是一系列变换过程的序列,可以转换为树的形式,树的根节点即为起始集合S中的成员,转换后的每个终止集以子节点的形式挂载在根节点下,这棵生成的树就被称为Parse Tree,可以看出最后的结果实际上就是Parse Tree的叶节点遍历结果。

当需要转换的非终结字符有多个时,需要按照一定的顺序来逐个推导,派生过程可以按照left-mostright-most进行,但有时会得到不同的合法的转换树,通常会通过修改转换集语法或设定优先级来解决。

Recursive Descent(递归下降遍历)

Recursive Descent是一种遍历parse tree的策略,是一种典型的递归回溯算法,从树的根节点开始,逐个尝试当前父节点上记录的非终止字符能够支持的产生规则,并判断其子节点是否符合这样的形式,直到子节点符合某个特定的产生式规则,然后再继续递归进行深度遍历,如果在某个非终止节点上尝试完所有的产生式规则都无法继续向下进行使得子树的叶节点都符合终止符号集,则需要通过回溯到上一节点并尝试父节点的下一个产生式规则,使得循环程序可以继续向后进行。课程里用了很多的数学符号定义和伪代码来描述递归遍历的过程,如果觉得太抽象不好理解可以暂时略过。需要注意左递归文法会使得递归下降遍历进入死循环,在文法设计时应该避免,龙书中也提供了一种通用的拆分方法来解决这个问题。

二. 递归下降遍历

【声明】由于课程中并没有看到从tokensparse tree的全貌,只能先逐步消化基础知识。下文的过程只是笔者自己的理解(尤其是逐行分析的形式,因为尚未涉及任何结构性语法,所以通用性还有待考量),仅供参考,也欢迎交流指正。但对于直观理解递归下降法而言是足够的。

2.1 预备知识

本节中使用JavaScript来实现递归下降遍历,目标代码仍是上一篇博文中的示例代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var b3 = 2;
a = 1 + ( b3 + 4);
return a;

经过上一节的分词器后可以得到下面的词素序列:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[ 'keywords', 'var' ],
[ 'id', 'b3' ],
[ 'assign', '=' ],
[ 'num', '2' ],
[ 'semicolon', ';' ],
[ 'id', 'a' ],
[ 'assign', '=' ],
[ 'num', '1' ],
[ 'plus', '+' ],
[ 'lparen', '(' ],
[ 'id', 'b3' ],
[ 'plus', '+' ],
[ 'num', '4' ],
[ 'rparen', ')' ],
[ 'semicolon', ';' ],
[ 'keywords', 'return' ],
[ 'id', 'a' ],
[ 'semicolon', ';' ]

语法分析是基于语法规则的,所谓语法规则,通常是指一系列CFG表示的产生式,大多数开发者并不具备设计一套语法规则的能力,此处直接借鉴Mozilla中的Javascript引擎SpiderMonkey中的文法定义来进行基本产生式,由于Javascript语言中涉及的文法非常多,本节只筛选出与目标解析式相关的一部分简化的语法规则(图中标记为蓝色的部分):

完整的语法规则可以查看【SpiderMonkey_ParserAPI】进行了解。

2.2 多行语句的处理思路

我们把上面的目标解析代码当做是一段Javascript代码,自顶向下分析时,根节点的类型是Program,它可以由多个Statement节点(语句节点)构成,所以本例中进行简化后以semicolon(分号)作为词素批量处理的分界点,每次将两个分号之间的部分读入缓冲区进行分析,由于上例中均为单行语句,所以理解起来比较简单。

在更为复杂的情况中,代码中包含条件语句,循环语句等一些结构化的关键词时可能会存在跨行的语句,此时可以在递归下降之前先对缓冲区的词素队列进行基本的结构分析,如果发现匹配的结构化模式,就从tokens序列中将下一行(或多行)也读入缓冲区,直到缓冲区中的所有tokens放在一起符合了某些特定的结构,再开始进行递归下降。

2.3 简易的文法定义

为方便理解,本例中均使用关键词缩写来表示可能的语法规则集,如果你对Javascript语言有一定了解,它们是非常容易理解的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 文法定义-生产规则
 * Program -> Statement
 * P -> S
 * 
 * 语句 -> 块状语句 | if语句 | return语句 | 声明 | 表达式 |......
 * Statement -> BlockStatement | IfStatement | ReturnStatement | Declaration | Expression |......
 * S -> B | I | R | D | E
 * 
 * B -> { Statement }
 * 
 * I -> if ( ExpressionStatement ) { Statement }
 * 
 * R -> return Expression | null
 * 
 * 声明 -> 函数声明 | 变量声明
 * Declaration -> FunctionDeclaration | VariableDeclaration
 * D -> F | V
 * 
 * F -> function ID ( SequenceExpression ) { ... }
 * 
 * V -> 'var | let | const' ID [= Expression | Null] ?
 * 
 * 表达式 -> 赋值表达式 | 序列表达式 | 一元运算表达式 | 二元运算表达式 |......
 * Expression -> AssignmentExpression | SequenceExpression | UnaryExpression | BinaryExpression | BracketExpression......
 * E -> A | Seq | U | BI | BRA |...
 * 
 * A -> E = E //赋值表达式
 * 
 * Seq -> ID,ID,ID//类似形式
 * 
 * //一元表达式
 * U -> "-" | "+" | "!" | "~" | "typeof" | "void" | "delete" E
 * 
 * //二元表达式
 * BI -> E "==" | "!=" | "===" | "!=="
         | "<" | "<=" | ">" | ">="
         | "<<" | ">>" | ">>>"
         | "+" | "-" | "*" | "/" | "%"
         | "|" | "^" | "&" | "in"
         | "instanceof" | ".."  E
 * 
 * //括号表达式
 * BRA -> ( E )
 * 
 * N -> null  
 */

需要额外注意的是表达式Expression到赋值表达式AssignmentExpression的产生式,E的判断规则里需要判断A,而A的逻辑里又再次调用了E,这里就是一种左递归,如果不进行任何处理,在代码运行时就会陷入死循环然后爆栈,这也就是前文强调的需要在语法产生式设计时消除左递归的场景。这里并不是说spiderMonkeyparserAPI是错的,因为消除左递归的语法改造只是一种等价形式的转换,是为了防止产生式产生无限递推(或者说程序实现时进入无限递归的死循环)而做的一种形式处理,改造的过程可能只是引入了某个中间集合来消除这种场景的影响,对于最终的语法表意并不会产生影响。

下文示例代码中并没有进行严谨的"左递归消除",而是简单地使用了一个E_集合,与原本的E进行一些微小的差异区分,从而避免了死循环。

2.4 文法产生式的代码转换

下面将上一小节的语法规则进行代码翻译(只包含部分产生式的推导,本例中的完整代码可以从demo或代码仓中获取):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//判断是否为Statement
function S(tokens) {
    //把结尾的分号全部去除
    while(tokens[tokens.length - 1][0] === TT.semicolon){
        tokens.pop();
    }
    return B(tokens) || I(tokens) || R(tokens) || D(tokens) || E(tokens);
}

//判断是否为BlockStatement  B -> { Statement } (本例中并不涉及本方法,故暂不考虑末尾分号和文法递归的情况)
function B(tokens) {
     //本例中不涉及,直接返回false
    return false;
}

//判断是否为IfStatement I -> if ( ExpressionStatement ) { Statement }
function I(tokens) {
    //本例中不涉及,直接返回false
    return false;
}
//判断是否为ReturnStatement  R -> return Expression | null
function R(tokens) {
    return isReturn(tokens[0]) && (E(tokens.slice(1)) || N(tokens.slice(1)[0]));
}

//判断是否为声明语句 Declaration -> FunctionDeclaration | VariableDeclaration
function D(tokens) {
    return F(tokens) || V(tokens);
}

//判断是否为函数声明  F -> function ID ( SequenceExpression ) { ... }
function F(tokens) {
    //本例中不涉及,直接返回false
    return false;
}

//判断是否为变量声明  V -> 'var | let | const' ID [= Expression | Null] ?
function V(tokens) {
    //判断为1.单纯的声明 还是 2.带有初始值的声明
    if (tokens.length === 2) {
        return isVariableDeclarationKeywords(tokens[0]) && tokens[1][0] === TT.id;
    }
    return isVariableDeclarationKeywords(tokens[0]) && (A(tokens.slice(1))) || N(tokens.slice(1));
}

//....其他代码形式雷同,不再赘述

2.5 逐行解析

解析时默认每次遇到一个分号时表示一个statement的结束,前文已经提及过对于多行语句的处理思路。实现时只需要将tokens序列一点点读进buffer数组并从顶层的S方法启动分析,即可完成自顶向下的推理过程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 /**parser */
function parse(tokens) {
    let buffer = nextStatement(tokens);
    let flag = true;

    while (buffer && flag){

       if (!S(buffer)) {
           console.log('检测到不符合语法的tokens序列');
           flag = false;
       } 
       buffer = nextStatement(tokens);
    }   
    
    //如果没有出错则提示正确
    flag && console.log('检测结束,被检测tokens序列是合法的代码段');
}

//将下一个Statement全部读入缓冲区
function nextStatement(tokens) {
    let result = [];
    let token;

    while(tokens.length) {
        token = tokens.shift();
        result.push(token);
        //如果不是换行符则
        if (token[0] === CRLF) {
            break;
        }
    }

    return result.length ? result : null;
}

2.6 查看计算过程

单步执行查看计算过程可以帮助我们更好地理解递归下降法的执行过程:

在demo所在目录下打开命令行,输入:node --inspect-brk recursive-descent.js,然后单步执行就很容易看出代码在执行过程中如何实现递归和回溯:

三.小结

单纯地递归下降法最终的结果只找出了不满足任何语法规则的语句,或是最终所有语句都符合语法规则时给出提示,但并没有得到一个树结构的对象,也没有向下一个环节提供输出,如何在编译过程中与后续环节进行连接还有待探索。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-10-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
联发科向上,高通向下
新的战场上,过了而立之年的高通,面对血气方刚、才行冠礼的联发科,也不可能是永远的赢家。
镁客网
2018/12/26
6510
半导体老牌贵族做不好的移动处理器,为什么华为、高通可以无往不利
如今高通、海思、三星、联发科以及展锐,圈出了各自的一亩三分地,并且以5G为中心,展开新一轮的竞争。
镁客网
2019/06/20
9290
半导体老牌贵族做不好的移动处理器,为什么华为、高通可以无往不利
华为麒麟回归,高通和联发科将面临76亿美元的损失?
近期,随着搭载麒麟芯片的华为Mate 60系列及Mate X5系列的热销,不少分析机构纷纷看好华为手机今年以及后续的销量增长,不仅苹果iPhone 15系列将会受到直接冲击,三星及其他国产高端智能手机也将会面临压力。
芯智讯
2023/09/20
3230
华为麒麟回归,高通和联发科将面临76亿美元的损失?
惨烈车祸频发,无人车会消灭人类的头号杀手吗?
前几天看到央视新闻报道了一则北京发生的惨烈车祸:10月17日晚22:09在北京市通州区发生了一起交通事故,一辆出租车被拉石头的大卡车压扁,出租车司机当场死亡。这样的惨烈车祸不胜枚举,然而这次车祸发生的
罗超频道
2018/04/27
5720
惨烈车祸频发,无人车会消灭人类的头号杀手吗?
从高通骁龙855,看当前主流手机芯片之变迁
苹果iPhone XS系列手机自发布以来,“信号差”一直是备受诟病的问题。原因在于,苹果与高通“分手”了。而新一代的iPhone XS系列全部采用的是英特尔的信号基带。
VRPinea
2018/12/26
9970
美国政府要靠无人汽车降低交通事故死亡人数到0,这靠谱吗?
美国政府周三制定了一个野心勃勃的目标:在30年内把美国的交通事故死亡人数降为零。这一目标需要在很大程度上依赖无人驾驶汽车技术的发展。作为美国交通部下属的部门,美国国家高速公路交通安全管理局(以下简称“NHTSA”)将与其他机构共同推行这一计划。美国交通部将会在未来3年每年拨款100万美元推进该计划。 但对NHTSA而言,这项计划并不遥远。美国2015年的交通事故死亡人数增长7.2%,今年还有可能更高:NHTSA周三宣布,美国2016年上半年的交通事故死亡人数增长10.4%。 NHTSA局长马克·罗斯金德(M
机器人网
2018/04/16
6460
有人欢喜有人忧:海思跌出全球半导体营收Top 15,高通、联发科同比增长超50%
图 | 2021年,世界半导体大会将在南京开幕,期间镁客网将联合润展国际举办半导体行业论坛,欢迎扫码报名参会
镁客网
2021/06/08
6050
三星7nm制程代工毁了全部高通5G芯片?台积电、联发科的机会来了
昨天才把张朝阳请上台,高调地开了场Galaxy Note 10中国发布会的三星,今晨就被一条负面送上了热搜——#三星导致高通5G芯片全部报废#。
镁客网
2019/08/26
6460
三星7nm制程代工毁了全部高通5G芯片?台积电、联发科的机会来了
高通准备给骁龙芯片大换血,台积电或代替三星接手855处理器 | 热点
高通骁龙855不再由三星代工,台积电接手研制基于7纳米工艺的基带处理器和高通骁龙855平台。 高通骁龙845发布的热度尚未减淡,高通已经着手准备明年的855了。据外媒报道,高通正在和芯片大厂台积电展开合作,旨在研制基于7纳米工艺的基带处理器和高通骁龙855平台。 7纳米是指半导体线宽,线宽越小,同样面积整合的晶体管数量就会增加,对于手机来说就是芯片性能更强,能耗更低。7纳米技术使用的设备95%与10纳米相同,其工艺是10纳米工艺的进一步延伸,逻辑电路密度增加逾60%,功耗降低30%至40%。 鉴于骁龙835
镁客网
2018/05/30
4670
不再做“火龙”!高通「4nm旗舰芯」骁龙8 Gen 1正式亮相,小米12系列将全球首发
12月的第一天,高通终于端上了“年底大菜”——三星4nm工艺的骁龙8 Gen 1。
镁客网
2021/12/05
7920
不再做“火龙”!高通「4nm旗舰芯」骁龙8 Gen 1正式亮相,小米12系列将全球首发
业界 | 英特尔153亿美元收购Mobileye的背后,自动驾驶芯片之争愈演愈烈
机器之心原创 作者:李泽南、微胖、吴攀 (从左至右)英特尔 CEO Brian Krzanich、宝马 CEO Harald Krüger 和 Mobileye NV 主席 Amnon Shashua
机器之心
2018/05/07
6310
业界 | 英特尔153亿美元收购Mobileye的背后,自动驾驶芯片之争愈演愈烈
高通骁龙8 Gen2发布:CUP性能提升35%,GPU性能提升25%,AI性能提升4.35倍!
北京时间11月16日早间,高通在夏威夷举行了 2022 骁龙峰会,会上正式发布了其第二代骁龙8旗舰移动平台(骁龙8 Gen 2)。与第一代相比,骁龙8 Gen 2在CPU、GPU、AI、影像、音频、网络连接、游戏体验、可信安全等方面带来了全面的提升。
芯智讯
2022/11/22
1.1K0
高通骁龙8 Gen2发布:CUP性能提升35%,GPU性能提升25%,AI性能提升4.35倍!
搅局者联发科用“AI定义座舱”,能打破高通座舱垄断吗?
前言:在生成式端侧大模型上车、算力进化等技术背景下,智能座舱产业的市场机遇和竞争格局正在发生巨变。联发科正凭借“AI定义座舱”,加速赶超竞品,以实现智能座舱产业的重塑。
芯智讯
2024/04/30
1970
搅局者联发科用“AI定义座舱”,能打破高通座舱垄断吗?
三年后我们就可以坐无人车了?看完这些事实你应该会信
说到机器人,我们首先会联想到类似于Boston、Pepper这样的黑科技机器人。不过,从CCF-GAIR(人工智能与机器人峰会)大会上看到的趋势是,先行普及的机器人将会是“不那么像机器人的机器人,比如被称为空中机器人的无人机已走向普及,结合了大量黑科技的道路机器人无人车,正在各大巨头推动之下准备上路,百度给出的目标最为激进,百度高级副总裁、百度自动驾驶事业部总经理王劲在GAIR大会上再次明确,无人车要在三年后小规模商用,五年后大规模量产,这个时间表比最先探索无人车的互联网巨头更为激进,能行吗?看完这些事实有
罗超频道
2018/04/27
7000
三年后我们就可以坐无人车了?看完这些事实你应该会信
瞄准物联网的高通,能否收割全球IoT市场?丨科技云·视角
高通这家芯片制造商的身影,开始频频出现在物联网的产业链条中,将无线连接的技术优势发挥到极致。
科技云报道
2022/04/14
6430
瞄准物联网的高通,能否收割全球IoT市场?丨科技云·视角
硅谷VS底特律:谁会培育出下一个伟大的汽车企业?
我知道这次测试是经过精心安排的,而且会按照计划顺利实施,但是当我踩上福特Fusion小轿车的油门,直接向我前面停放的轿车加速的时候,我的身体开始紧张起来。 下意识的把身体往后靠不是一种自然而然的动作。这需要你注意力高度集中不要突然急刹车。右边停满了福特汽车,在下午的阳光下车漆反射出阳光,格外耀眼。远处,美国国旗在迎风飘扬。福特汽车公司的蓝色标志在在grassy hill的墙壁上熠熠生辉。 汽车主控板上传来警告的哔哔声。汽车显示板上红光闪烁。我突然觉得我就像是电影“疯狂马克斯:狂暴之路”里的一个角色,唱着电
点滴科技资讯
2018/04/28
3K0
硅谷VS底特律:谁会培育出下一个伟大的汽车企业?
【特斯拉事故反思】自动驾驶是智能汽车的终极目标吗?
【新智元导读】今天“特斯拉事故”的新闻刷爆了屏,舆论再次聚焦自动驾驶。一方面,宝马、英特尔和Mobileye等行业巨头不愿错过智能驾驶的市场先机,即将合作推出新合作框架以推进产品的快速迭代;另一方面,
新智元
2018/03/26
5550
【特斯拉事故反思】自动驾驶是智能汽车的终极目标吗?
快速行驶的自动驾驶,离现实还有多远?
随着人工智能的发展,萌芽于上个世纪70年代的自动驾驶技术,逐步成长为创业的风口。如今这个行业里已经盘踞着Google、百度、苹果、 Uber、乐视等科技公司,NVIDIA、Intel等芯片厂商,Tesla、丰田、宝马、沃尔沃、日产、福特、通用、奥迪、丰田等新老车厂。而这个领域的创业公司,诸如Otto、Lytx、 Comma.ai 、Itseez、图森互联、驭势科技、智行者等均已获得融资。自动驾驶已经开到了哪里?离我们还有多远?
曾响铃
2018/08/20
5130
快速行驶的自动驾驶,离现实还有多远?
CES 2018前瞻丨AI、自动驾驶将备受关注,VR/AR势头依然不弱
2018年国际消费类电子产品展览会(CES)将于美国时间1月9日至12日在拉斯维加斯举行,CES 作为世界上最大规模的科技展会之一,去年参观人数已经接近18万。无论是科技巨头还是小公司,都希望能够在C
VRPinea
2018/05/17
5680
四面楚歌的英特尔,能否夺下5G时代的宝座?丨科技云·视角
英特尔曾以PC和高性能处理器称霸市场的年代已经过去了,如今它面临四面八方的竞争,在5G和物联网的发展上英特尔可谓卯足了劲。
科技云报道
2022/04/14
2300
四面楚歌的英特尔,能否夺下5G时代的宝座?丨科技云·视角
推荐阅读
联发科向上,高通向下
6510
半导体老牌贵族做不好的移动处理器,为什么华为、高通可以无往不利
9290
华为麒麟回归,高通和联发科将面临76亿美元的损失?
3230
惨烈车祸频发,无人车会消灭人类的头号杀手吗?
5720
从高通骁龙855,看当前主流手机芯片之变迁
9970
美国政府要靠无人汽车降低交通事故死亡人数到0,这靠谱吗?
6460
有人欢喜有人忧:海思跌出全球半导体营收Top 15,高通、联发科同比增长超50%
6050
三星7nm制程代工毁了全部高通5G芯片?台积电、联发科的机会来了
6460
高通准备给骁龙芯片大换血,台积电或代替三星接手855处理器 | 热点
4670
不再做“火龙”!高通「4nm旗舰芯」骁龙8 Gen 1正式亮相,小米12系列将全球首发
7920
业界 | 英特尔153亿美元收购Mobileye的背后,自动驾驶芯片之争愈演愈烈
6310
高通骁龙8 Gen2发布:CUP性能提升35%,GPU性能提升25%,AI性能提升4.35倍!
1.1K0
搅局者联发科用“AI定义座舱”,能打破高通座舱垄断吗?
1970
三年后我们就可以坐无人车了?看完这些事实你应该会信
7000
瞄准物联网的高通,能否收割全球IoT市场?丨科技云·视角
6430
硅谷VS底特律:谁会培育出下一个伟大的汽车企业?
3K0
【特斯拉事故反思】自动驾驶是智能汽车的终极目标吗?
5550
快速行驶的自动驾驶,离现实还有多远?
5130
CES 2018前瞻丨AI、自动驾驶将备受关注,VR/AR势头依然不弱
5680
四面楚歌的英特尔,能否夺下5G时代的宝座?丨科技云·视角
2300
相关推荐
联发科向上,高通向下
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档