zoukankan      html  css  js  c++  java
  • 编译原理笔记 2

    语法分析 (Syntactic Analysis / Parsing)

    识别程序语法结构,构造抽象语法树(Abstract Syntax Tree)

    1. 自顶向下

    • 递归下降算法(Recursive Descent Parsing)
      • 下降:语法分析的过程中,上级文法嵌套下级文法。
      • 递归:上下文无关文法。

    算术表达式在简单情况下,使用类似于词法分析用到的正则文法可以解析(如 "int a = 1")。
    但在复杂情况下("1+2*3""1*2+3" 等),难以穷尽各种组合,需要抽象出一种通用的规则:乘除法 看作 加减法 的子规则,生成 AST 时,乘除法节点就一定是加减法的子节点,从而达到排列优先级的效果。

    function add = multiply | (add + multiply)
    function multiply = numericLiteral | (multiply * numericLiteral)
    

    1.1 巴科斯范式

    实际应用中,上面的语法规则使用 巴科斯范式(BNF) 的形式,工具 Antlr 和 Yacc 也是用这种写法:

    add ::= mul | add + mul
    mul ::= pri | mul * pri
    pri ::= Id | num | (add)
    

    上面规则的优先级从低到高依次为:add, mul, pri。其中带括号的 (add) 实现了语法中常见的括号改变优先级

    1.2 解决左递归

    上面的定义方式转换为算法时,会出现 左递归 的情况(add 无限递归调用 add)
    将 add 交换到加号右侧:

    function add = multiply | (multiply + add)
    function multiply = numericLiteral | (numericLiteral * multiply)
    

    这样就能解决无限递归的问题了,但此时又会出现 "1+2+3" 优先计算了 2+3 的问题(从右向左计算,改变了加法的结合性)

    1.3 解决结合性问题

    还是把递归项放到左边:

    add -> mul | add + mul
    

    通过类似于 尾递归 的方式,将算法改写为循环语句可以解决左递归的问题,同时又保持结合性的正确:

    add -> mul (+ mul)*
    

    2. 自底向上

    递归下降算法在尝试一个规则(消耗 Token)不成功后,需要恢复到尝试之前的状态(不消耗 Token),即 回溯
    而自底向上的算法,回溯的次数较少。

  • 相关阅读:
    理解RabbitMQ中的AMQP-0-9-1模型
    深入分析Java反射(八)-优化反射调用性能
    一张图帮你记忆,Spring Boot 应用在启动阶段执行代码的几种方式
    Java equals 和 hashCode 的这几个问题可以说明白吗?
    如何妙用Spring 数据绑定机制?
    Lombok 使用详解,简化Java编程
    Java升级那么快,多个版本如何灵活切换和管理?
    手把手教你定制标准Spring Boot starter,真的很清晰
    Java12 Collectors.teeing 你需要了解一下
    Maven optional关键字透彻图解
  • 原文地址:https://www.cnblogs.com/cdyang/p/compiler-theory-note-2.html
Copyright © 2011-2022 走看看