编译原理

来源:互联网 发布:在线js代码调试工具 编辑:程序博客网 时间:2024/04/23 23:29

一、scanner 词法分析器,将代码转换成tokens,核心方法是,用有限自动机的形式将一个句子分成有意义的词组。如 identifier = letter(letter | digit)*,如果识别到了letter,那么继续识别,它后面如果是letter或者数字或者null,我们就可以把它看做是标识符(变量名或者函数名等)。

  sanner的核心是有限自动机FA(Finite Automata),FA又可以分为确定的有限自动机(DFA)或者非确定的有限自动机(NFA))。DFA就是从一个状态接收一个字符a之后,转向的下一个状态是唯一的。

DFA与NFA的区别:1,有无ε,二,转移函数不同。

scanner用的是DFA,NFA是因为绘制的时候限制少,很容易绘制出来,之后NFA都要转成DFA,而且根据状态等价定理还需要化简DFA。

编写一个scanner比较简单,首先是把所有的保留字都用一个数组存起来,把所有运算符都用另一个数组保留起来,然后是弄清楚怎么样才算是标识符(identifier),如上面的式子,什么才算是数字(digit),于是就开始判断,如果是保留字,那么接下来一般是怎样的(如if后面一般会跟着一个“(”),用一个容器把你所判断完毕的东西(tokens)存好以方便以后语法分析器的调用。

二、parser 语法分析器,将tokens转换成语法树。核心的方法是Top-dwon parsing,Bottom-up parsing。自顶而下和自底而上的两种分析方法。前一种最有名的是递归下降法,LL(K)就是支持递归下降法的一种文法。常用的是LL(1),第一个L表示从左往右扫描字符串;而第二个L表示最左推导。

这个其实就是给你一堆的文法(语法规则),然后让你通过first set 和follow set来构造一个分析表,然后就可以根据输入的tokens来对号入座,发现它们原来是属于哪一个文法。而这个文法又可以构建成一棵特定的树。

semantic analyzer 语义分析器,将语法树转换成注释树。这个方法我贴一段文字(http://www.cnblogs.com/ninputer/archive/2011/07/22/2112030.html):

谓编程语言语义,就是这段代码实际的含义。编程语言的代码必须有绝对明确的含义,这样人们才能让程序做自己想做的事情。比如最简单的一行代码:a = 1; 它的语义是“将32位整型常量存储到变量a中”。首先我们对“1”有明确的定义,它是32位有符号整型字面量,这里“32位有符号整型”就是表达式“1”的类型。其次,这句话成为合法的编程语言,32位整型常量必须能够隐式转换为a的类型。假设a就是int型变量,那么这条语句就直接将1存储到a所在内存里。如果a是浮点数类型的,那么这句话就隐含着将整型常量1转换为浮点类型的步骤。在语义分析中,类型检查是贯穿始终的一个步骤。像miniSharp这样的静态类型语言,类型检查通常要做到:

  1. 判定每一个表达式的声明类型
  2. 判定每一个字段、形式参数、变量声明的类型
  3. 判断每一次赋值、传参数时,是否存在合法的隐式类型转换
  4. 判断一元和二元运算符左右两侧的类型是否合法(比如+不就不能在bool和int之间进行)
  5. 将所有要发生的隐式类型转换明确化

要进行以上操作,需要一个表存储所有已知的类型。

这个表俗称符号表。

三、source code optimizer 源代码优化,注释树变成了中间代码,如三地址代码。核心思想是注释树的线性化。“中间代码是抽象语法树的线性表示”。

四、code generator 代码生成器,将中间代码转换成了目标代码。

五、target code optimizer 目标代码优化,优化目标代码 

六、运行环境,分为完全静态和基于栈式以及完全动态(堆式)三种。贴文字如下:(http://www.blogjava.net/renyangok/archive/2006/12/12/87160.html)

静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.

栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的.和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存.和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。
静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例.堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放.

原创粉丝点击