课程内容链接:https://space.bilibili.com/479141149/channel/collectiondetail?sid=837891

课程实验代码:https://github.com/Ling-Yuchen/compiler-hw

00 - 编译原理概述

image-20221128205347262 image-20221128205501109 image-20221128205542408 image-20221128205745813 image-20221128205833346 image-20221128205905002 image-20221128205957585 image-20221128210026532 image-20221128210102832 image-20221128210144056 image-20221128210257332

01 - 词法分析器生成器

image-20221128210809272

使用 ANTLR 工具编写词法分析器生成器:

  • 输入:词法单元的归约 SysYLexer.g4
  • 输出:词法分析器 SysYLexer.tokensSysYLexer.interpSysYLexer.java

样例:(来自 lab1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
lexer grammar SysYLexer;

CONST : 'const';
INT : 'int';
VOID : 'void';
IF : 'if';
ELSE : 'else';
WHILE : 'while';
BREAK : 'break';
CONTINUE : 'continue';
RETURN : 'return';
MUL : '*';
DIV : '/';
MOD : '%';
PLUS : '+';
MINUS : '-';
ASSIGN : '=';
EQ : '==';
NEQ : '!=';
LT : '<';
GT : '>';
LE : '<=';
GE : '>=';
NOT : '!';
AND : '&&';
OR : '||';
L_PAREN : '(';
R_PAREN : ')';
L_BRACE : '{';
R_BRACE : '}';
L_BRACKT : '[';
R_BRACKT : ']';
COMMA : ',';
SEMICOLON : ';';
IDENT : ([_a-zA-Z])([_a-zA-Z0-9])*;
INTEGR_CONST : [1-9][0-9]*
| '0'
| '0'([0-7])+
| '0'('x'|'X')[0-9a-fA-F]+;
WS : [ \r\n\t]+ -> skip;
LINE_COMMENT : '//' .*? '\n' -> skip;
MULTILINE_COMMENT : '/*' .*? '*/' -> skip;

一些定义:

image-20221128212525749 image-20221128212605291 image-20221128212645712 image-20221128212712972 image-20221128212748099

能够在语言(集合)上进行的操作:

image-20221128212950178

正则表达式:

image-20221128213723288 image-20221128213934333

02 - 手写词法分析器

03 - 自动机理论与词法分析器生成器

image-20221128214349235 image-20221128214448586

目标:RE to Lexer

image-20221128214604688

RE:正则表达式

NFA:不确定的有穷自动机(简洁易于理解,便于描述语言 L(A))

image-20221128220050425 image-20221128220127256 image-20221129144808425

其不确定性在于:

  • 在同一状态下,看到相同的字符,可以转移到不同的状态
  • 没有看到任何字符(看到 ε),也有可以发生状态的转移
image-20221129144244929

DFA:确定的有穷自动机(易于判断 x ∈ L(A),适合产生词法分析器)

image-20221129145044628 image-20221129145111066 image-20221129145709299

约定:所有没有对应出边的字符默认指向一个“死状态”(不可能再被接收)

其确定性在于:

  • 只对字符表中的字符做状态转移
  • 每个状态下对每个字符都有唯一的状态转移
image-20221129150132187

Thompson 构造法(RE to NFA)

image-20221129150222036

依据正则表达式的定义,分步处理:

image-20221129150515701 image-20221129150548348 image-20221129150700360 image-20221129150811023 image-20221129150848024

分析:

image-20221129151046116

例子:

image-20221129151138451

子集构造法(NFA to DFA)

image-20221129153848792 image-20221129154444220 image-20221129155102446 image-20221129155217511

DFA 最小化(DFA to 最简等价DFA)

DFA 最小化的基本思想:等价的状态可以合并

image-20221129155713732

出发点:

image-20221129160003787 image-20221129160054643 image-20221129160129715

DFA 最小化算法总结:

image-20221129160220576

算法要在严格定义的 DFA 上执行,注意要先检查是否需要补全“死状态”

Kleene 构造法(DFA to RE)

image-20221228112928764 image-20221228113052063 image-20221228113839478 image-20221228114004685 image-20221228114257896

词法分析器生成

遵循规则:最前优先匹配,最长优先匹配

  • 根据正则表达式构造相应的 NFA

    image-20230220092340899
  • 合并 NFA,构造完整词法规则的 NFA

  • 使用子集构造法将 NFA 转化为等价 DFA(并消除“死状态”)

    image-20230220092904184 image-20230220093007486
  • 特定于词法分析器的 DFA 最小化(可选)

    初始划分不能把所有终止状态笼统地分成一类,需要考虑不同的词法单元,按照识别不同词法单元规约分成不同的类,并补全“死状态”(然后再正常根据 DFA 最小化算法进行处理)

    image-20230220094124812
  • 模拟运行该 DFA,直到无法继续运行为止(输入结束或状态无法转移)

    image-20230220093337028

04 - 语法分析器

ANTLR v4 生成语法分析器

(已通过SysYLexer.g4生成对应的词法分析器)

  • 输入:语法归约 SysYParser.g4
  • 输出:语法分析器及访问生成的语法树的辅助类 SysYParser.tokensSysYParser.interpSysYParser.javaSysYParserBaseListener.javaSysYParserBaseVisitor.javaSysYParserListener.javaSysYParserVisitor.java
    • ANTLR 提供了 Visitor 和 Listener 两种访问语法树节点的方式,即访问者模式和观察者模式

样例:(来自 lab2)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
parser grammar SysYParser;

options {
tokenVocab = SysYLexer;
}

program : compUnit EOF ;

compUnit : (decl | funcDef)+ ;

decl : constDecl | varDecl ;

constDecl : CONST bType constDef (COMMA constDef)* SEMICOLON ;

bType : INT ;

constDef : IDENT (L_BRACKT constExp R_BRACKT)* ASSIGN constInitVal ;

constInitVal : constExp | L_BRACE (constInitVal (COMMA constInitVal)*)? R_BRACE ;

varDecl : bType varDef (COMMA varDef)* SEMICOLON ;

varDef : IDENT (L_BRACKT constExp R_BRACKT)* (ASSIGN initVal)? ;

initVal : exp | L_BRACE (initVal (COMMA initVal)*)? R_BRACE ;

funcDef : funcType IDENT L_PAREN funcFParams? R_PAREN block ;

funcType : VOID | INT ;

funcFParams : funcFParam (COMMA funcFParam)* ;

funcFParam : bType IDENT (L_BRACKT R_BRACKT (L_BRACKT exp R_BRACKT)*)? ;

block : L_BRACE blockItem* R_BRACE ;

blockItem : decl | stmt ;

stmt : lVal ASSIGN exp SEMICOLON
| exp? SEMICOLON
| block
| IF L_PAREN cond R_PAREN stmt (ELSE stmt)?
| WHILE L_PAREN cond R_PAREN stmt
| BREAK SEMICOLON
| CONTINUE SEMICOLON
| RETURN exp? SEMICOLON
;

exp : L_PAREN exp R_PAREN
| lVal
| number
| IDENT L_PAREN funcRParams? R_PAREN
| unaryOp exp
| exp (MUL | DIV | MOD) exp
| exp (PLUS | MINUS) exp
;

cond : exp
| cond (LT | GT | LE | GE) cond
| cond (EQ | NEQ) cond
| cond AND cond
| cond OR cond
;

lVal : IDENT (L_BRACKT exp R_BRACKT)* ;

number : INTEGR_CONST ;

unaryOp : PLUS | MINUS | NOT ;

funcRParams : param (COMMA param)* ;

constExp : exp ;

param: exp ;

二义性文法

例如:stmt:”if” expr “then” stmt “else” stmt ; 是一个二义性文法(if-else 的匹配问题)

可以通过改写来消除二义性(将 else 与最近的未匹配的 if 相匹配):

1
2
3
4
5
stmt : matched_stmt | open_stmt ;
matched_stmt : "if" expr "then" matched_stmt "else" matched_stmt
| expr ;
open_stmt : "if" expr "then" stmt
| "if" expr "then" matched_stmt "else" open_stmt ;

例如:expr:expr “*” expr | expr “+” expr | DIGIT ; 是一个二义性文法(运算符的结合性和优先级)

常识告诉我们乘法的优先级比加法高,并且两者都是左结合的

1
2
3
4
/* 左递归文法 */
expr : expr "+" term | term ;
term : term "*" factor | factor ;
factor : DIGIT ;

ANTLR 可以通过算法直接处理二义性文法和具有左公因子的文法,无需人为改写(优先级高的写在前)

ANTLR 中二元运算符默认为左结合,一元运算符默认为右结合,对于右结合的二元运算符,使用关键字 assoc 加以限定,expr:<assoc = right> expr “^” expr

上下文无关文法

image-20221228103735005 image-20221228104150107

一些复杂的例子

image-20221228111512276 image-20221228105110789 image-20221228105232697

上下文无关文法的表达能力强于正则表达式

image-20221228105339687 image-20221228105605841 image-20221228105824901 image-20221228110306581

证明一个上下文无关文法没有二义性

对于 A → X | Y | Z,要证明:

  • L(X) ∩ L(Y) = Ø
  • L(Y) ∩ L(Z) = Ø
  • L(Z) ∩ L(X) = Ø

其中,L(X) 表示 X 所定义的语言

故,更一般的,对于 A → X1 | X2 | X3 | … | Xn,需要证明,∀ i, j ∈ [1, n] (i != j),有 L(Xi) ∩ L(Xj) = Ø

05 - 语法分析算法

image-20230220094542795

LL(1) 语法分析算法

自顶向下的,递归下降的,基于分析预测表的,适用于 LL(1) 文法的 LL(1) 语法分析器

自顶向下

LL(1) 从左往右读入词法单元(left-to-right),并且在推导的每一步,总是选择最左边的非终结符进行展开(left-most description)

递归下降

image-20230220095224395

分析预测表

image-20230220100730617

LL(1) 文法

image-20230220100923810

实现样例

image-20230220101059108

计算给定文法 G 的预测分析表

First 集合

image-20230220102228084 image-20230220102303259

Follow 集合

image-20230220102435072 image-20230220102625682

计算 First 集合

对于每个符号串 α 的产生式:α = X1 X2 X3 … Xm

  • 先计算每个 X 的 First(X) 集合
image-20230220104053883
  • 再计算每个符号串 α 的 First(α) 集合
image-20230220104558617

计算 Follow 集合

  • 为每个非终结符 X 计算 Follow(X) 集合
image-20230220105107878

生成预测分析表

image-20230220105629679

非递归的预测分析算法

image-20230220110342405 image-20230220110424089

改造非 LL(1) 文法

  • 消除左递归(使用右递归)
  • 提取左公因子

Adaptive LL(*) 语法分析算法(AllStar 算法)

关于 ANTLR v4:

image-20230220111538654

处理左递归+优先级

image-20230220121644591

优先级上升算法

变为非左递归,然后用迭代的方式进行处理

展开递归表达式时,需要一个优先级参数

image-20230220121957805

更多例子:

image-20230220122923959 image-20230220123004166
image-20230220123158588 image-20230220122854917
image-20230220131823952 image-20230220131926622
image-20230220132352223 image-20230220132407304

进行错误报告与恢复

恐慌/应急模式(Panic Mode):假装成功,调整状态,继续进行

image-20230220132724391 image-20230220132926556 image-20230220133643768

AllStar 算法

image-20230220133946836 image-20230220134014892 image-20230220134205766 image-20230220134749427

06 - 符号表

每个符号表代表了一个作用域,不同的作用域需要通过树结构来维护:

image-20230220140121490 image-20230220140342843

每个作用域需要提供的接口:

image-20230220140512430

符号表相关的类层次结构设计

image-20230220140908716

07 - 语义分析

类型系统与类型检查

  • 类型检查

  • 类型转换

image-20230220144425670
  • 类型综合
image-20230220144547713
  • 类型推导
image-20230220144638313

生成数组类型的类型表达式

image-20230220153819939 image-20230220153754567

属性文法

属性文法:为上下文无关文法赋予语义

关键问题:如何基于上下文无关文法做上下文相关分析?(语法分析树上的有序信息流动,DFS 遍历)

image-20230220161257010

在 ANTLR v4 中,使用参数的形式来表示继承属性,使用返回值来表示综合属性

(代码演示部分略)

语法制导定义

image-20230220192334350 image-20230220192552257

S 属性定义

综合属性

image-20230220192854928 image-20230220192953775 image-20230220193042113 image-20230220193122241

L 属性定义

继承属性

image-20230220193309453 image-20230220193659176 image-20230220200519082
  • 例子:属性文法计算后缀表达式
image-20230220201141864 image-20230220201312671
  • 例子:属性文法计算有符号二进制数
image-20230220201545335 image-20230220201740985 image-20230220201849477

语法制导的翻译方案

image-20230220202655013

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

  • S 属性的翻译方案
image-20230220202928233
  • L 属性定义与 LL 语法分析
image-20230220203423540 image-20230220203525251
  • 比较:在左递归与右递归上的属性定义
image-20230220210233365 image-20230220210256289
  • L 属性翻译方案
image-20230220210540575 image-20230220210633745
  • L 属性翻译方案样例
image-20230220211418305 image-20230220211445122

08 - LLVM IR

LLVM(Low-Level Virtual Machine)

The LLVM Compiler Infrastructure

The LLVM Project is a collection of modular and reusable compiler and tool-chain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name “LLVM” itself is not an acronym; it is the full name of the project.

image-20230220212550131

LLVM IR

image-20230220213427834

自动生成 LLVM IR

使用命令 clang -S -emit-llvm xxx.c -o xxx.ll 生成 LLVM IR code 样例:

image-20230220220126419

演示代码 1:https://github.com/courses-at-nju-by-hfwei/compilers-antlr/tree/main/src/main/java/llvm/factorial && https://github.com/courses-at-nju-by-hfwei/learning-llvm/tree/main/10-llvm

更多关于 LLVM 的使用:https://pictures-1312865652.cos.ap-nanjing.myqcloud.com/LLVM.pdf

使控制流满足 SSA:Φ 函数根据控制流决定选择 y1 还是 y2

image-20230220221003332

不同优化等级下对控制流的实现方式

image-20230220222526053 image-20230220222623949 image-20230220223642412 image-20230220224220970

编程方式生成 LLVM IR

详见实验代码

09 - 中间代码生成

表达式翻译与控制流翻译

image-20230220235003531

注意:下图中的文法对布尔表达式和非布尔表达式做出了区分

image-20230221001735737

非布尔表达式的中间代码翻译

image-20230221090506622

数组引用表达式的中间代码翻译

image-20230221081957482 image-20230221082244527 image-20230221082449049

LLVM IR 的生成结果:

image-20230221083640233

控制流语句与布尔表达式的中间代码翻译

image-20230221084113845

if 条件语句:

image-20230221084512124 image-20230221084547737 image-20230221084615354

最终生成的 IR code:

1
2
3
4
5
6
	goto L1
L1:
goto L0
L3:
assign
L0:

if else 条件语句:

image-20230221085547052 image-20230221085737101

最终生成的 IR code:

1
2
3
4
5
6
7
8
9
10
11
12
	goto L1
L1:
goto L3
L3:
assign
goto L0
L4:
assign
goto L0
L2:
assign
L0:

while 循环语句:

image-20230221092013672 image-20230221092534286

最终生成的 IR code:

1
2
3
4
5
6
7
8
9
10
11
begin:
goto L1
L1:
goto L4
L3:
assign
goto begin
L4:
assign
goto begin
L0:

并列顺序语句:

image-20230221093336145

布尔表达式:

  • image-20230221093742808
  • image-20230221093703309
  • image-20230221093628479
  • image-20230221093924755
  • image-20230221094042026

一个复杂的例子:

image-20230221094123089

最终生成的 IR code:

1
2
3
4
5
6
7
8
9
10
11
	if x < 100 goto L2
goto L3
L3:
if x > 200 goto L4
goto L1
L4:
if x != y goto L2
goto L1
L2:
x = 0
L1:

生成 LLVM IR code 并可视化展示控制流图

image-20230221100702370

使用 ANTLR v4 生成控制流

参考样例代码:

https://github.com/courses-at-nju-by-hfwei/compilers-antlr/blob/main/src/main/antlr/codegen/Control.g4

https://github.com/courses-at-nju-by-hfwei/compilers-antlr/blob/main/src/main/java/codegen/CodeGenListener.java

关于布尔表达式的补充

image-20230221101915982 image-20230221101938668