编译原理学习笔记

概述

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

  • 编译型 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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
【问题描述】 请根据给定的文法设计并实现词法分析程序,从源程序中识别出单词,记录其单词类别和单词值,输入输出及处理要求如下: (1)数据结构和与语法分析程序的接口请自行定义;类别码需按下表格式统一定义; (2)为了方便进行自动评测,输入的被编译源文件统一命名为testfile.txt(注意不要写错文件名);输出的结果文件统一命名为output.txt(注意不要写错文件名),结果文件中每行按如下方式组织: 单词类别码 单词的字符/字符串形式(中间仅用一个空格间隔) 单词的类别码请统一按如下形式定义: 单词名称 类别码 单词名称 类别码 单词名称 类别码 单词名称 类别码 标识符 IDENFR else ELSETK - MINU = ASSIGN 整形常量 INTCON switch SWITCHTK * MULT ; SEMICN 字符常量 CHARCON case CASETK / DIV , COMMA 字符串 STRCON default DEFAULTTK < LSS ( LPARENT const CONSTTK while WHILETK GRE [ LBRACK char CHARTK scanf SCANFTK >= GEQ ] RBRACK void VOIDTK printf PRINTFTK == EQL { LBRACE main MAINTK return RETURNTK != NEQ } RBRACE if IFTK + PLUS : COLON 【输入形式】testfile.txt中的符合文法要求的测试程序。 【输出形式】要求将词法分析结果输出至output.txt中。 【特别提醒】(1)读取的字符串要原样保留着便于输出,特别是数字,这里输出的并不是真正的单词值,其实是读入的字符串,单词值需另行记录。 (2)本次作业只考核对正确程序的处理,但需要为今后可能出现的错误情况预留接口。 (3)在今后的错误处理作业中,需要输出错误的行号,在词法分析的时候注意记录该信息。 (4)单词的类别和单词值以及其他关注的信息,在词法分析阶段获取后,后续的分析阶段会使用,请注意记录;当前要求的输出只是为了便于评测,完成编译器中无需出现这些信息,请设计为方便打开/关闭这些输出的方案。 【文法定义】请到“2020年文法定义(用于查看文法,不计入成绩)”作业中查看文法 【样例输入】 coNst int cONst1 = 001, const2 = -100; const char const3 = '_'; int change1; char change3; int gets1(int var1,int var2){ change1 = var1 + var2; return (change1); } void main(){ printf("Hello World"); printf(gets1(10, 20)); } 【样例输出】 CONSTTK coNst INTTK int IDENFR cONst1 ASSIGN = INTCON 001 COMMA , IDENFR const2 ASSIGN = MINU - INTCON 100 SEMICN ; CONSTTK const CHARTK char IDENFR const3 ASSIGN = CHARCON _ SEMICN ; INTTK int IDENFR change1 SEMICN ; CHARTK char IDENFR change3 SEMICN ; INTTK int IDENFR gets1 LPARENT ( INTTK int IDENFR var1 COMMA , INTTK int IDENFR var2 RPARENT ) LBRACE { IDENFR change1 ASSIGN = IDENFR var1 PLUS + IDENFR var2 SEMICN ; RETURNTK return LPARENT ( IDENFR change1 RPARENT ) SEMICN ; RBRACE } VOIDTK void MAINTK main LPARENT ( RPARENT ) LBRACE { PRINTFTK printf LPARENT ( STRCON Hello World RPARENT ) SEMICN ; PRINT
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LutingWang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值