编译原理学习笔记

概述

编译程序源程序翻译为目标程序。根据源程序的语言种类,翻译程序可以分为汇编程序编译程序。与之相对,解释程序是对源程序进行解释执行的程序。相应的可以将高级语言分为

  • 编译型 C/C++, Swift, etc.
  • 解释型 Python, javascript, etc.
  • 混合型 Java, etc.

本文重点放在编译程序的设计上。典型的编译程序具有 7 7 7 个逻辑部分

符号表管理
词法分析程序
语法分析程序
语义分析程序
代码优化程序
目标代码生成程序
错误处理

对源程序扫描一次被称为一遍 (pass)。典型的一遍扫描编译程序有如下形式

backend
frontend
取词
语法成分
分析结果
代码优化程序
目标代码生成程序
O.P.
S.P.
词法分析
语法分析
语义分析
中间代码

通常将中间代码生成前的分析部分称为编译器的前端,其后的综合部分则被称为后端。这样就把一个编译程序分为了与源语言相关和与目标机有关的两个独立的部分,降低了程序的耦合。假设 llvm 编译器 支持 M M M 种源语言到 N N N 种目标语言的编译

源语言1
LLVM IR
源语言2
...
源语言M
目标机1
目标机2
...
目标机N

传统的编译器如 gcc 可能需要开发 M × N M \times N M×N 个不同的子模块。而 llvm 使用统一的中间语言 llvm Intermediate Representation 只需要 M M M 个前端与 N N N 个后端,大大降低了开发成本。

文法

非空有穷集合 Σ \Sigma Σ 为一字母表,则其上的符号串 ∀ s ∈ Σ ∗ \forall s \in \Sigma^* sΣ ,其中 ∗ * 表示集合的闭包。特别的记 Σ 0 = ε \Sigma^0 = {\varepsilon} Σ0=ε 为空串组成的集合。规则通常写作

U : : = x  or  U → x , ∣ U ∣ = 1 , ∣ x ∣ ≥ 0 U ::= x\text{ or }U\rightarrow x,\quad |U| = 1, |x| \ge 0 U::=x or Ux,U=1,x0

其中左部 U U U 是符号,右部 x x x 是有穷符号串。规则的集合 P P P 即可确定一个文法 G G G

<程序>				::= <常量说明><变量说明><函数说明>
	<常量说明>		::= {const<常量定义>;}
		<常量定义> 	::= int<标识符>=<整数>{,<标识符>=<整数>}|char<标识符>=<字符>{,<标识符>=<字符>}
	<变量说明>		::= {<类型标识符><变量定义>;}
		<变量定义> 	::= <标识符>[<下标>]{,<标识符>[<下标>]}
			<下标>	::= '['<无符号整数>']' // <无符号整数>表示数组元素的个数,其值需大于0
	<函数说明>		::= {(<类型标识符>|void)<函数定义>}void<主函数>
		<函数定义>	::= <标识符>'('<参数表>')'<复合语句>
			<参数表>	::= [<类型标识符><标识符>{,<类型标识符><标识符>}]
		<主函数>		::= main'('')'<复合语句>

<复合语句>			::= '{'<常量说明><变量说明>{<语句>}'}'
<语句>				::= <条件语句>|'{'{<语句>}'}'|<函数调用语句>;|<赋值语句>;|<读语句>;|<写语句>;|<返回语句>;|;
	<条件语句>	::= <if语句>|<while语句>|<do语句>|<for语句>
		<if语句>		::= if'('<条件>')'<语句>[else<语句>]
		<while语句>	::= while'('<条件>')'<语句>
		<do语句>		::= do<语句>while'('<条件>')'
		<for语句>	::= for'('<标识符>=<表达式>;<条件>;<标识符>=<标识符><加法运算符><无符号整数>')'<语句>
			<条件>	::= <表达式>[<关系运算符><表达式>] // 表达式为0条件为假,否则为真
	<函数调用语句>	::= <标识符>'('[<表达式>{,<表达式>}]')'
	<赋值语句>		::= <标识符>['['<表达式>']']=<表达式>
	<读语句>			::= scanf'('<标识符>{,<标识符>}')'
	<写语句>			::= printf'('<字符串>[,<表达式>]')'|printf'('<表达式>')' 
	<返回语句>		::= return['('<表达式>')']

<表达式>				::= [<加法运算符>]<>{<加法运算符><>} // [+|-]只作用于第一个<项>
<>				::= <因子>{<乘法运算符><因子>}
<因子>				::= <标识符>['['<表达式>']']|'('<表达式>')'|<整数>|<字符>|<函数调用语句>
<整数>				::= [<加法运算符>]<无符号整数>

<标识符>		::= <字母>{<字母>|<数字>}
<无符号整数> 	::= <非零数字>{<数字>}|0
	<数字>		::= 0|<非零数字>
	<非零数字>	::= 1|...|<字符>		::= '<加法运算符>'|'<乘法运算符>'|'<字母>'|'<数字>'
<字符串>		::= "{十进制编码为32,33,35-126的ASCII字符}"
<类型标识符> 	::= int|char
<加法运算符>	::= +|-
<乘法运算符> 	::= *|/
<关系运算符> 	::= <|<=|>|>=|!=|==
<字母>		::= _|a|...|z|A|...|Z

上述文法使用扩充的 BNF 表示法进行描述

符号定义说明
∣ \vert 作用域由括号限定
{ t } n m \{t\}^m_n {t}nm t t t 重复连接 n ∼ m n \sim m nm缺省时 m = ∞ ,   n = 0 m = \infin,\ n = 0 m=, n=0
[ t ] [t] [t]符号串 t t t 可有可无等价于 { t } 1 \{t\}^1 {t}1
( t ) (t) (t)局部作用域主要用于限定 ∣ \vert 范围

相关概念有

概念符号定义示例
识别符号 Z Z Z文法中第一条规则的左部符号<程序>
字汇表 V V V文法中出现的全部符号{ <程序>, <常量说明>, …, 0, 1, … }
非终结符号集 V n V_n Vn全部规则的左部组成的集合{ <程序>, <常量说明>, <变量说明>, … }
终结符号集 V t V_t Vt V − V n V - V_n VVn{ 0, 1, …, _, a, b, … }

U : : = u ∈ P U ::= u \in P U::=uP 则对于 ∀ x , y ∈ V ∗ \forall x, y \in V^* x,yV直接推导 x U y ⇒ x u y xUy \Rightarrow xuy xUyxuy 。如果 y ∈ V t ∗ y \in V_t^* yVt x U y   ⤃   x u y xUy\ ⤃\ xuy xUy  xuy 称为规范推导。直接推导序列 u 0 ⇒ u 1 ⇒ ⋯ ⇒ u n u_0 \Rightarrow u_1 \Rightarrow \cdots \Rightarrow u_n u0u1un 可简记为

{ u 0 ⇒ + u n n > 0 u 0 ⇒ ∗ u n n ≥ 0 \begin{cases} u_0 \mathop\Rightarrow\limits^+ u_n & n > 0\\ u_0 \mathop\Rightarrow\limits^* u_n & n \ge 0\\ \end{cases} {u0+unu0unn>0n0

进一步定义

  • 句型 V ∗ ∋ x ⇐ ∗ Z V^* \ni x \mathop\Leftarrow\limits^* Z VxZ
  • 句子 V t ∗ ∋ x ⇐ + Z V_t^* \ni x \mathop\Leftarrow\limits^+ Z Vtx+Z
  • 语言 L ( G ) = { x ∣ x  is sentence } L(G) = \{ x| x\text{ is sentence} \} L(G)={xx is sentence}

如果文法 G G G G ′ G' G L ( G ) = L ( G ′ ) L(G) = L(G') L(G)=L(G) ,则称这两个文法等价。设 w = x u y w=xuy w=xuy 为一句型,称 u u u 为一个相对于 U ∈ V n U \in V_n UVn

  • w w w 的短语 如果 Z ⇒ ∗ x U y ∧ U ⇒ + u Z \mathop\Rightarrow\limits^* xUy \land U \mathop\Rightarrow\limits^+ u ZxUyU+u
  • w w w 的简单短语 如果 u u u 是短语且 U ⇒ u U \mathop\Rightarrow\limits u Uu

句型的最左简单短语称为句柄

二义性

文法 G G G 是二义性的,如果 ∃ x ∈ L ( G ) \exist x \in L(G) xL(G) 使下列条件之一成立

  1. x x x 可以对应两颗不同的语法树
  2. x x x 有两个不同的规范推导

词法(线性)分析

扫描源程序字符,按词法规则识别单词,同时进行词法检查

单词是语言的基本语法单位

种类属性类型属性值
IDENFRstring标志符名称
INTCONint无符号整数值
CHARCONchar字符常量
STRCONstring字符串常量
RESERVEDReservedCONSTTK, INTTK, CHARTK, VOIDTK, MAINTK, IFTK, ELSETK, DOTK, WHILETK, FORTK, SCANFTK, PRINTFTK, RETURNTK
DELIMDelimASSIGN, SEMICN, COMMA, LPARENT, RPARENT, LBRACK, RBRACK, LBRACE, RBRACE
OPEROperPLUS, MINU, MULT, DIV
COMPCompLSS, LEQ, GRE, GEQ, EQL, NEQ

语法(层次)分析

根据文法分析并识别出各种语法成分,并进行正确性检查

语义分析

对语法树进行语义分析,产生相应的中间代码

中间代码是一种介于源语言和目标语言之间的语言形式,常用的有四元式逆波兰表示等。

代码优化

生成高质量的目标程序

生成目标程序

由中间代码生成目标程序

符号表管理

把源程序中的信息和编译过程中所产生的信息登记在表格中,便于在随后的编译过程中进行查找

错误处理

诊断出源代码的错误,并报告用户错误的性质和位置

参考

深入浅出让你理解什么是LLVM

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LutingWang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值