读一段程序和读一篇文章的处理是有相似之处的
首先需要能够认出一个个字符(字,单词或者标点)
然后理解文章的结构(段落,句子,句子内部主谓宾等结构)
最后再结合一些前置的知识和上下文,推导、理解每一句的含义,最后理解整个文章的含义
以上就对应了编译的前三个阶段 词法分析 语法分析 语义分析
但是编译的目的是让计算机或者运行环境(而不是人脑)理解程序的含义,所以语义分析的阶段就特别困难,因为人脑可以自然的学习和理解语言,但我们却不知道人脑是怎么做到的。我们只能保证语言的形式可以以语法树的结构来表示和验证,而语义的部分由形式本身来确定。这种由完全由形式确定语义的语言称作形式语言(formal language),它可以使让机器无歧义地理解程序这一目的变得容易实现。理解形式语言与自然语言的不同能够将编译的研究领域与自然语言识别的领域区分开来。
所以我们最终会由一棵语法树来生成机器能够理解的机器代码,这个过程中会涉及到许多的优化技术,以及底层相关的知识。同时为了保证语言本身的跨平台特性,或者是出于优化的需要和编译器实现的模块化要求,通常会先由语法树生成中间代码,这个过程中进行与平台无关的优化。再由中间代码生成机器相关的代码,中间掺杂进行机器相关的优化。
总结来说,编译器的实现大概以中间代码生成为界分为了前端和后端
整体步骤是:词法分析->语法分析->语义分析(类型检查,中间代码生成和优化)->目标代码生成和优化
因为有像LLVM这样的项目,我们设计一门语言可以只完成编译器的前端,生成某种形式的中间代码,然后和LLVM的后端对接,LLVM可以针对中间代码进行优化,并生成指定机器格式的优化后的机器代码。
当然,这些优化技术本身也有相当的学习价值,基本上可以说是基础库开发的idea来源,因为他们本身就是如何改变程序的结构来达到目的(优化性能,优化表现形式等)的典范。
为什么讲词法分析会提到正则语言,自动机,形式语言和形式系统这些东西?
这是我上编译原理课的时候最为困惑不解的地方。
事实上形式语言的语法syntax本身就由词法Lexical和语法grammar共同决定,要解析出语法树(syntax tree),两者就不可分割
词法分析的前置是形式语言的字母表,要得出一个token流或符号表
语法分析的前置是推导规则构建的自动机,要得出一棵语法树
前者的输出是后者的输入
一些形式语言的词法本身也是由另一种形式语言定义的,比如C语言的词法中标识符和数字常量的定义可以由正则表达式定义。正则表达式是正则语言的表述工具,它内部实现了自动机。
而用来描述grammar的语言CFG又是另一种形式语言。
所以词法分析和语法分析的本质都是形式语言的推导,或规约。他们的实现就是构建这么一个以字母表和自动机为基础的形式系统。
由一个字母表按一定规律推导出的一个语言就是形式语言,它的文法就是形式语言的文法
比如由ASCII字母表可以推导出C语言,C是一种形式语言
而形式语言的推导类似于初中所做的等式推导题
由一个已知的条件集合和一组规则,经过多步的推导,推出了希望证明的结论
每一步推导都有想要证明的结论,使用的已知条件(比如各种代数规则,交换律结合律之类的),和使用的规则
推导成功就能把结论放入已知条件集合中,作为下一步推导的依据
一个已知条件(形式语言的程序),结论(程序符合语法)加上推导和转化规则(词法/语法规则)构成了形式系统,比如一个编译器的语法解析器(Syntax Parser)
正则语言是最简单的形式语言
它的字母表有空字符串,普通字符集,代表特殊操作的有并/或(’|’),拼接/且(’’), 重复/闭包(*).
其他的常用扩展有(0或1次(?),1或更多次(+),指定次数{m,n},自定义字符集如[^a-z] 等)
推导规则就是匹配运算的自动机(连接,并,闭包这些运算也是匹配)
结论就是匹配成功,或者不成功
一个推导中,产生式左边(也就是结论)只有一个非终结符的形式语言文法syntax称为上下文无关文法
只有一个非终结符,就意味着推导到某一步不需要根据前后文(也就是上下文)来确定该用什么规则继续推导。无论这个符号出现在哪里,它的推导方式都是相同的。
终结符就是不可再分的原子token,代表了不可再推导的状态
非终结符是由非终结符和终结符递归构成的,代表了可以继续推导的状态
注意一点 正则文法和上下文无关文法的区别是正则文法无法记忆状态,只能知道当前状态是什么。
正则文法的推导不允许递归,也就是用结论推结论(数学归纳法),而上下文无关文法的非终结符的推导支持递归
不支持递归推导意味着什么?
比如要匹配一些递归嵌套的结构
例如(((i+1) * 2) -7)/2)使用正则文法无法将其识别为一个表达式,还有将类似if …else…的嵌套识别为一个if块都不能用正则文法推导
但是正则语言可以判断操作的数量是否能够被状态数K整数。如果总状态数为2,就是判断操作出现的奇偶性。像是成对出现的标签的匹配是可以用正则表达式去做的,这其中没有用到递归推导,只是识别一个开始和一个对应的结束 ,如果输入本身不满足成对出现,最后一定会匹配不成功。
递归本身和记忆自身状态等价,因此也可以说正则文法没有记忆之前状态的功能。一些正则表达式中有像是可以指定出现次数,还有回溯这样的功能,这其实已经超出了正则语言的范畴。