编译原理(5) 语义分析
2023-08-09 14:53:19 # NJU # 编译原理

语义分析

本节课代码见https://github.com/courses-at-nju-by-hfwei/2022-compilers-coding

一、类型系统与类型检查

1. 类型系统

image-20221207151819743

1.1 JS 糟糕的类型系统

image-20221207151842025

image-20221207151856769

2. 类型检查

2.1 类型检查

image-20221207152018144

2.2 类型转换

image-20221207152042825

不要写这样的代码!!!

image-20221207152141192

2.3 类型综合

根据子表达式的类型确定表达式的类型

image-20221207152444442

2.4 类型推导

根据某语言结构的使用方式确定表达式的类型

image-20221207152504821

3. 数组类型文法

image-20221207152709599

3.1 语法分析树

image-20221207152729596

3.2 Listener

image-20221212160736411

3.3 ParseTreeProperty

  • 通过 ANTLR4 提供的 ParseTreeProperty<> 来存储 basicTypearrayType
private final ParseTreeProperty<Type> arrayTypeProperty = new ParseTreeProperty<>();
private final ParseTreeProperty<Type> basicTypeProperty = new ParseTreeProperty<>();

image-20221212160750687

  • 赋值语句类型检查,详见代码

image-20221212162801953

4. 类型声明文法

image-20221212160914001

  • 与数组类似,需要保存最左侧的类型

5. 类型系统

https://youtu.be/SWTWkYbcWU0

二、属性文法

1. 属性文法

image-20221212165143201

$\textcolor{red}{\textbf{属性文法}\text{(Attribute Grammar)}:} 为上下文无关文法赋予\textcolor{blue}{\textbf{语义}}$

终结符或非终结符上的属性就可以看作语义

属性分为两类:

  • 综合属性
  • 继承属性

1.1 关键问题

如何基于上下文无关文法做上下文相关分析?

上下文相关文法分析复杂度太高

关键: 语法分析树上的有序信息流动

  • 通过 DFS 来遍历

2. 计算属性值

2.1 Offline 方式

image-20221212170016385

但因为要先建立语法分析树,实际上遍历了两遍,效率并不高

2.2 语法分析过程中

语法分析过程中实现属性文法

基本思想: 一个动作在它左边的所有文法符号都处理过之后立刻执行

image-20221212170232773

时机(Timing)

语义动作嵌入在什么地方? 这决定了何时执行语义动作。

2.3 实现迷你计算器

image-20221212172137328

expr 的 value 只依赖于子节点的 value

@header {
package ag;
import java.util.*;
}

@parser::members {
Map<String, Integer> memory = new HashMap<>();

int eval(int left, int right, int op) {
switch (op) {
case ADD : return left + right;
case SUB : return left - right;
case MUL : return left * right;
case DIV : return left / right;
default : return 0;
}
}
}

prog : stat* EOF ;

stat : expr { System.out.println($expr.val); }
| ID '=' expr { memory.put($ID.text, $expr.val); } // $ID.text -> ID.getText()
;

expr returns [int val]
: left = expr op = ('*' | '/') right = expr { $val = eval($left.val, $right.val, $op.type); }
| left = expr op = ('+' | '-') right = expr { $val = eval($left.val, $right.val, $op.type); }
| '(' expr ')' { $val = $expr.val; }
| ID { String id = $ID.text; $val = memory.getOrDefault(id, 0); }
| INT { $val = $INT.int; }
;

2.4 输出表格文件

image-20221212174143821

3. SDD

3.1 SDD

Definition (语法制导定义 (Syntax-Directed Definition; SDD))

SDD 是一个上下文无关文法和属性规则的结合

image-20221214141005939

要点:

  • SDD 唯一确定了语法分析树上每个非终结符节点的属性值
  • SDD 没有规定以什么方式、什么顺序计算这些属性值

3.2 注释语法分析树

注释(annotated)语法分析树: 显示了各个属性值的语法分析树

ANTLR4 中使用 ParseTreeProperty 存放属性值

  • ParseTreeProperty<Integer> put(ctx, ...) get(ctx, ...)

image-20221219144009716

3.3 综合属性

Definition (综合属性 (Synthesized Attribute))

image-20221219144328213

Definition ($S$ 属性定义 ($S$-Attributed Definition))

如果一个 SDD 的每个属性都是综合属性, 则它是 $S$ 属性定义

image-20221219144737275

  • 此类属性值的计算可以在 自顶向下 的 $LL$ 语法分析过程中实现
  • 在 $LL$ 语法分析器中, 递归下降函数 A 返回 时, 计算相应节点 A 的综合属性值

3.4 继承属性

Definition (继承属性 (Inherited Attribute))

节点 $N$ 上的继承属性只能通过 $N$ 的父节点、$N$ 本身和 $N$ 的兄弟节点上的属性来定义。

举例

$T$′ 有一个综合属性 $syn$ 与一个继承属性 $inh$

  • 继承属性 $T$′.$inh$ 用于在表达式中从左向右传递中间计算结果

image-20221219145400168

image-20221219145643816

3.5 $L$ 属性定义

Definition ($L$ 属性定义 ($\textcolor{red}{L}$-Attributed Definition))

image-20221219145937914

例1:非 $L$ 属性定义举例

image-20221219150215536

  • $B.i$ 依赖了右兄弟节点的 $C.c$ 属性
例2:数组类型文法举例

image-20221219150436131

  • 继承属性 $C.b$ 将一个基本类型沿着树向下传播

  • 综合属性 $C.t$ 收集最终得到的类型表达式

    image-20221219150516491

例3:后缀表示

image-20221219151655273

  • (9 − 5) + 2 $\Rightarrow$ 95 − 2 +
  • 9 − (5 + 2) $\Rightarrow$ 952 + −

image-20221219151759248

image-20221219151812012

例4:有符号二进制数文法

image-20221219152055063

image-20221219152127179

image-20221219152316587

4. SDT

4.1 语法制导的翻译方案(SDT)

Definition (语法制导的翻译方案 (Syntax-Directed Translation Scheme; SDT))

SDT 是在其产生式体中嵌入语义动作的上下文无关文法。

image-20221219152659177

$Q$ : 如何将带有语义规则的 SDD 转换为带有语义动作的 SDT

4.2 后缀翻译方案

image-20221219152852152

4.3 $L$ 属性定义 与 $LL$ 语法分析

image-20221219153015846

原则:

  1. 从左到右处理各个 $X_i$ 符号
  2. 对每个 $X_i$ , 先计算继承属性, 后计算综合属性

具体如下图所示

image-20221219153132204

举例

image-20221219153410001

4.4 左递归问题

许多工具是不支持左递归的,会转化为右递归,这会带来一些问题

  • 因为 ANTLR4 支持直接左递归,暂不考虑

image-20221219153929953

  • 继承属性 $\textcolor{blue}{R.i}$ 用于计算并传递中间结果

    image-20221219154145315

  • 右递归翻译方案

    • 原则: 继承属性在处理文法符号之前, 综合属性在处理文法符号之后

      image-20221219154523447

    • image-20221219154606283

    • image-20221219154556836

What is the difference between ANTLR 3 and 4?

image-20221219155751002