Compiler Basic Notes
Basic Concepts
Definition of compilers
program_code
---compiler---> executable- data ---executable---> output
e.g Fortran(formula translation) 1 project
Structure of compilers
front-end to back-end:
- front-end: src ---lexical analysis---> tokens ---parsing/syntax analysis---> AST(Abstract Syntax Tree) ---semantic analysis---> intermediate
- back-end: intermediate ---...---> ... ---...---> ... ---code generation---> dist
details:
- lexical analysis(词法分析)
- parsing/syntax analysis(语法分析)
- semantic analysis(语义分析): type and scope
- optimization
- code generation: translate to other high level language/assembly code/machine code
文法与语言
符号与符号串
- 字母表/符号集: 元素的非空有穷集合
- 符号串: 字母表中的符号组成的任何有穷序列
- 固有头/尾: 非空首/尾子串
- 闭包:
Σ* = Σ0 U Σ1 U ... U Σn ...
. - 正闭包:
Σ* = Σ1 U ... U Σn ...
.
文法与语言的形式化表示
- 文法 (Grammar):
G = (Vn, Vt, P, S)
, 非终结符集, 终结符集, 规则/产生式集, 开始符号. - 语言 (Language):
L(G) = {x | S -> x, x <- Vt}
文法 G 一切句子的集合. - 句型: rhs of P, 句子: 不含非终结符的右部
- 直接推导:
v -> w
, 闭包推导:v -*> w
, 正闭包推导:v -+> w
.
乔姆斯基文法体系
- ((((3)2)1)0)
- 0 型文法: 任意文法
- 1 型文法: 上下文有关文法(context sensitive) αAβ -> αηβ
- 2 型文法(语法工具): 上下文无关文法 A -> α
- 3 型文法(词法工具): 正规文法
上下文无关文法
文法表示
G = (S, N, T, P):
- S: 开始符
- N: 非终结符集合
- T: 终结符集合
- P: 产生式规则集合 X
->
beta1, beta2, ..., betaN, X<-
N, beta<-
N+T
形式化表示
简易表示
Sentence -> Noun Verb Noun
Noun -> sheep
| tiger
| grass
| water
Verb -> eat
| drink
S: Sentence, N: Sentence/Verb/Noun, T: sheep/tiger/grass/water/eat/drink
E -> num
|id
|E + E
|E `*` E
S: E
, N: E
, T: num/id/+/*
.
巴科斯范式
Backus-Naur Form:
::=
:被定义为
.word
: 字符本身.- 双引号外的字: 语法部分.
- 尖括号(
< >
): 必选项(非终结符). - 方括号(
[ ]
) : 0/1. - 大括号(
{ }
) : 0/n. - 竖线(
|
) :OR
.
正规文法
正规文法可与正则语言相互转化:
- A -> aB|Ba
- A -> a
正则语言(Regular Expressions)
基本定义
对于给定的字符集 C:
- 空串
"\0"
是正则表达式. - 任意
char <- C
是正则表达式. - 若
M
,N
是正则表达式, 则M|N = {M, N}
,MN = {mn|m <- M, n <- N}
,M* = {"\0", M, MM, MMM, ...}
(选择/连接/闭包) 也是正则表达式.
形式表示
# 具有顺序性
e -> "\0" # basic definition
| c # basic definition
| e | e # recursive definition
| ee # recursive definition
| e* # recursive definition
正则语法糖(Syntax Sugar)
[a-z]
: a|...|z- c? : 0/1 个 c
- c+ : 1/n 个 c
c{i, j}
: i-j 个 c- "a*" : a* 自身(非 Kleene Closure)
- . : 除 ‘\n’ 外的任意字符
// 标识符
const identifier = /[a-z_]\w*/gi
// decimal integer
const integer = /(\+|-)?(0|[1-9]\d*)/g
// decimal float
const float = /(\+|-)?(0|[1-9]\d*)?\.\d+/g
分析树
进行文法推导时生成的树:
- 根 : 开始符
- 内部结点 : 非终结符
- 叶子结点 : 终结符
- 层 : 一步推导(优先级影响推导顺序)
- 叶子结点串: 最终表达式
- 后序遍历 : 最终结果
推导类型
- 最左推导(leftmost)
- 最右推导(rightmost)(规范推导)
句型分析
- 短语: 若
S -*> Aβ, A -+> α
, 则称 α 是句型 αβ 相对于非终结符 A 的短语 - 直接短语:
S -*> Aβ, A -> α
. - 一个右句型的直接短语称为该句型的句柄(用于自下而上的归约分析)
- 最左归约: 归约最左的句柄, 最右归约: 归约最右的句柄
meaning function(多对一)
L(syntax) = semantic: 多个语法对应一个语义(不同形式的表达式对应同一个意思)
二义性文法(一对多)
- 最左推导与最右推导得出的分析树不一致
- 若给定文法 G, 对于句子 s, 其有 2 种不同的分析树, 则称 G 是二义性文法
- 若一个上下文无关语言的所有文法都是二义性文法, 则称此语言是先天二义语言
文法重写
E -> E + T
|T
T -> T * F
|F
F -> num
|id
消除 + 与
*
的二义性, 如 3+4*
5
优先级与结合性
声明优先级与结合性可在一定程度上消除文法的二义性
文法规则
- 有害规则: 使文法产生二义性的规则
- 多余规则: 不可达/不可终止的规则
- 2 型文法的 ε 规则: 当语言中不含有 ε 符号串, 则一定存在终结符集不含有 ε 的等价文法(代入法消除 ε)
- 保证非终结符 A 的有效性:
S -*> αAβ, A -+> t
.
Lexical Analysis
Tokenizer - 词法分析器
- Maximal match
- Higher priority match
转移图算法
token nextToken(void) {
char c = getChar();
switch(c) {
case '<':
c = getChar();
switch (c) {
case '=':
return LE;
case '>':
return NE;
default:
rollback();
return LT;
}
case '=':
return EQ;
case '>':
c = getChar();
switch (c) {
case '=':
return GE;
case '<':
return NE;
default:
rollback();
return GT;
}
}
}
关键字(keyword)处理
- 根据 完美哈希算法(无冲突哈希函数) , 建立所有关键字对应的关键字完美哈希表
- 读入有效标识符(字符串型)后, 查询关键字哈希表, 检查当前标识符是否为关键字
#define KEYWORD_MAX_LEN 10
hash_one(char *str, int len) {
unsigned int keyValue = 0;
for (int i = 0; i < len; i++) {
keyValue += str[i] * ((int)pow(33, len - i));
}
keyValue = (keyValue * 3 + 7) % KEYWORD_MAX_LEN;
return keyValue;
}
#define KEYWORD_HASH_SEED 131
hash_two(char *str, int len) {
unsigned int keyValue = 0,
hash = 0;
for (int i = 0; i < len; i++) {
hash = hash * KEYWORD_HASH_SEED + str[i];
}
keyValue = hash & 0x7fffffff;
return keyValue;
}
有限状态自动机(Finite Automaton)
确定有限状态自动机(Deterministic Finite Automaton)
- Only a transition for a state with a input
- No epsilon moves
M = (AlphaSet/InputSet, StateSet, currentState, FiniteStateSet, transferFunction)
A = {a, b}, SS = {0, 1, 2}, cS = 0, FS = {2},
transferFunction = {
(cS0, a) -> cS1, (cS0, b) -> cS0,
(cS1, a) -> cS2, (cS1, b) -> cS1,
(cS2, a) -> cS2, (cS2, b) -> cS2,
}
状态转移表实现 DFA
状态\字符 | a | b |
---|---|---|
0 | 1 | 0 |
1 | 2 | 1 |
2 | 2 | 2 |
非确定有限状态自动机(Nondeterministic Finite Automaton)
transferFunction 中的次态不确定/不唯一(为一个开集合):
- Multiple transitions for a state with a input
- can epsilon moves
(cS0, a) ->
{cS1, cS2}
自动词法分析器
RegExp --Thompson 算法--> NFA --子集构造算法--> DFA --Hopcroft 最小化算法--> 词法分析器代码
Thompson 算法
RegExp --> NFA:
- 直接构造基本 RegExp
- 递归构造复合 RegExp
- epsilon : i --epsilon--> f
- RegExp : i --NFA(RegExp)--> f
- 选择 : i --NFA(RegExp1)--> f, i --NFA(RegExp2)--> f
- 连接 : i --NFA(RegExp1)--> m --NFA(RegExp2)--> f
- 闭包 : i --epsilon--> m --epsilon--> f, m --RegExp--> m
子集构造算法
NFA --> DFA:
由 Thompson 算法生成的 NFA, 当且仅当输入为 epsilon 时, 次态不唯一
- 将所有可达到次态作为一个集合 s, 视为单一次态 s
- delta(Sigma) + epsilon-closure(深度/广度优先遍历找寻可达到次态边界)
DFA subset_construction(NFA nfa) {
s0 = eps_closure(n0);
StateSet += s0;
enqueue(s0);
while (work_queue != []) {
dequeue(s);
foreach (ch in InputSet) {
next_state = eps_closure(delta(s, ch));
Fn[s, ch] = next_state; // DFA 中的转移函数
if (next_state not in StateSet) {
StateSet += next_state;
enqueue(next_state);
}
}
}
return DFA(StateSet, Fn);
}
Hopcroft 算法
最小化 DFA(数字逻辑中的最简状态表), 合并等价状态(等价类)
split(StateSet S) {
foreach (char ch) {
if (ch can split S) {
split S into S1, ..., Sk;
}
}
}
hopcroft(DFA) {
split all nodes into InitStateSet and FiniteStateSet (Two State Sets);
while (set is still changes) {
split(S);
}
}
实现
DFA
有向图
转移表
- 行: 现态
- 列: 输入
- 值: 次态/ERROR/-1
驱动代码: table 用于实现 switch/case, stack 用于实现最长匹配
next_token() {
state = 0;
stack = [];
while (state != ERROR) {
c = getChar();
if (state is ACCEPT/FINITE) {
clear(stack);
}
push(state);
state = table[state][c];
}
while (state is not ACCEPT/FINITE) {
state = pop();
rollback();
}
}
Syntax Analysis(语法分析)
Tokens + Grammar --Syntax Analysis--> AST(Abstract Syntax Tree)
自顶向下分析
- 从开始符号出发推导任意句子 t, 与给定句子 s 进行比较分析
- 利用分析树进行逐叶子匹配, 若匹配失败则进行回溯
bool top_down_parsing(tokens[]) {
i = 0;
stack = [S];
while (stack != []) {
if (stack[top] is a terminal t) {
t == tokens[i] ? pop(i++) : backtrack();
} else if (stack[top] is a non_terminal T) {
pop();
push(T next expansion); // 自右向左压栈, e.g pop(S), push(N_right), push(V), push(N_left)
} else {
throw new SyntaxError();
}
}
return i >= tokens.length && is_empty(stack) ? true : false;
}