编译器设计-有限自动机
编译器设计-有限自动机
Compiler Design - Finite Automata
有限自动机是一种状态机,它以一串符号作为输入,并相应地改变其状态。有限自动机是正则表达式的识别器。当正则表达式字符串被输入到有限自动机中时,它会为每个文本更改其状态。如果输入字符串成功处理并且自动机达到其最终状态,则接受它,即刚刚输入的字符串被认为是当前语言的有效标记。
有限自动机的数学模型包括:
Finite
set of states (Q)
Finite
set of input symbols (Σ)
One
Start state (q0)
Set
of final states (qf)
Transition
function (δ)
设L(r)是有限自动机(FA)识别的正则语言。
状态States:FA的状态用圆圈表示。状态名写在圆圈里。
开始状态Start state:自动机开始的状态,称为开始状态。开始状态有一个箭头指向它。
中间状态Intermediate states :所有中间状态至少有两个箭头;一个指向外,另一个指向内(自己)。
最终状态Final state:如果成功解析输入字符串,则自动机应处于此状态。最终状态用双圆表示。它可能有任意奇数个指向它的箭头和偶数个指向它的箭头。奇数箭头的数目比偶数大一个,即奇数=偶数+1。
转换Transition:当在输入中找到所需的符号时,从一个状态转换到另一个状态。在转换时,自动机可以移动到下一个状态,也可以保持在相同的状态。从一个状态到另一个状态的移动显示为定向箭头,箭头指向目标状态。如果自动机保持在相同的状态,则会绘制一个从状态指向自身的箭头。
Example : We assume FA accepts any three digit binary value
ending in digit 1. FA = {Q(q0, qf), Σ(0,1), q0, qf, δ}
语法分析或分析是编译器的第二个阶段。在本章中,我们将学习构造解析器时使用的基本概念。
我们已经看到,词法分析器可以借助正则表达式和模式规则来识别标记。但是由于正则表达式的限制,词法分析器无法检查给定句子的语法。正则表达式无法检查平衡标记,如括号。因此,这个阶段使用上下文无关语法(CFG),这是由下推自动机识别的。
另一方面,CFG是正则语法的超集,如下所示:
这意味着每一种规则语法都是上下文无关的,但也存在一些超出规则语法范围的问题。CFG是描述编程语言语法的有用工具。
上下文无关语法Context-Free Grammar
在本节中,我们将首先看到上下文无关语法的定义,并介绍用于解析技术的术语。
上下文无关语法有四个组成部分:
一组非终端(V)。非终结符是表示字符串集的语法变量。非终端定义字符串集,帮助定义由语法生成的语言。
一组标记,称为终端符号(∑)。终端是构成字符串的基本符号。
一组产品(P)。语法的产生指定了终端和非终端可以组合成字符串的方式。每个产品都由一个称为产品左侧的非终端、一个箭头和一系列令牌和/或终端(称为产品右侧)组成。
其中一个非终端被指定为开始符号;从那里开始生产。
字符串是从开始符号派生的,方法是重复地将非终端(最初是开始符号)替换为生产的右侧(对于该非终端)。
例子
我们讨论回文语言的问题,它不能用正则表达式来描述。也就是说,L={w | w=wR}不是常规语言。但可以通过CFG来描述,如下所示:
G = ( V, Σ, P, S )
其中
V = { Q, Z, N }Σ = { 0, 1 }P = { Q → Z | Q → N | Q → ℇ | Z → 0Q0 | N → 1Q1 }S = { Q }
此语法描述回文语言,如:1001、11100111、00100、1010101、11111等。
语法分析器syntax analyzer
语法分析器或解析器以令牌流的形式接受词法分析器的输入。解析器根据生产规则分析源代码(令牌流),以检测代码中的任何错误。这个阶段的输出是一个解析树。
这样,解析器完成两个任务,即解析代码、查找错误和生成解析树作为阶段的输出。
即使程序中存在错误,解析器也需要解析整个代码。解析器使用错误恢复策略,我们将在本章后面学习。
派生Derivation
派生基本上是一系列产生式规则,以便获取输入字符串。在解析过程中,我们对某些句子形式的输入做出两个决定:
决定要更换的非终端。
决定生产规则,用它来代替非终端。
要决定用生产规则替换哪个非终端,我们可以有两个选择。
最左端派生 Left-most Derivation
如果从左到右扫描并替换输入的句子形式,则称之为最左派生。由最左边派生出来的句子式叫做左边的句子形式。
最右派生
如果我们Right-most Derivation
从右到左扫描并用产生式规则替换输入,则称为最右派生。从最右边派生出来的句子形式叫做右边的句子形式。
例子
生产规则
E → E + EE → E * EE → id
Input string: id + id * id
The left-most derivation is:
E → E * EE → E + E * EE → id + E * EE → id + id * EE → id + id * id
请注意,最左边的非终端总是先处理。
最正确的推导是:
E → E + EE → E + E * EE → E + E * idE → E + id * idE → id + id * id
Parse Tree
请注意,最左边的非终端总是先处理。
解析树
解析树是派生的图形描述。可以方便地看到字符串是如何从开始符号派生的。派生的开始符号将成为解析树的根。让我们从上一个主题的一个例子来看这个问题。
我们取a+b*c的最左边的导出
最左边的推导是:
E → E * EE → E + E * EE → id + E * EE → id + id * EE → id + id * id
在解析树中
All
leaf nodes are terminals.
All
interior nodes are non-terminals.
In-order
traversal gives original input string.
所有叶节点都是终端。
所有内部节点都不是终端。
按顺序遍历给出原始输入字符串。
按顺序遍历给出原始输入字符串。解析树描述运算符的关联性和优先级。最深的子树首先遍历,因此该子树中的运算符优先于父节点中的运算符。
模棱两可
如果语法G对于至少一个字符串有多个解析树(左派生或右派生),则称它是不明确的。
Example
E → E + EE → E – EE → id
对于字符串id+id–id,上面的语法生成两个解析树:
由歧义语法生成的语言被称为天生的歧义。语法中的歧义不利于编译器构造。没有一种方法能够自动检测和消除歧义,但是可以通过重新编写整个语法而不产生歧义,或者通过设置和遵循关联性和优先约束来消除歧义。
关联性Associativity
如果一个操作数的两边都有运算符,则运算符接受此操作数的那一边由这些运算符的关联性决定。如果操作是左关联的,则操作数将由左运算符执行;如果操作是右关联的,则右运算符将执行操作数。
例子
加法、乘法、减法和除法等运算是左关联的。如果表达式包含:
id op id op id
it will be evaluated as:
(id op id) op id
例如,(id+id)+id
指数运算之类的运算是右关联的,即同一表达式中的求值顺序为:
id op (id op id)
For example, id ^ (id ^ id)
优先Presedence
如果两个不同的运算符共享同一个操作数,则由运算符的优先级决定哪个将成为该操作数。也就是说,2+34可以有两个不同的解析树,一个对应于(2+3)4,另一个对应于2+(34)。通过设置运算符之间的优先级,可以很容易地解决此问题。如上例所示,数学上(乘法)优先于+(加法),因此表达式2+3*4将始终解释为:
2 + (3 * 4)
这些方法减少了语言或语法中的歧义。
左递归 Left Recursion
如果语法有任何非终端“A”,其派生包含“A”本身作为最左边的符号,则语法将变为左递归。对于自顶向下的解析器来说,左递归语法被认为是一个有问题的情况。自上而下的解析器从开始符号开始解析,开始符号本身是非终端的。因此,当解析器在派生过程中遇到同一个非终端时,就很难判断何时停止解析左边的非终端,从而进入无限循环。
Example:
(1) A => Aα | β (2) S => Aα | β A => Sd
(1) 是立即左递归的一个例子,其中A是任何非终端符号,α表示一个非终端字符串。
(2) 是间接左递归的一个例子。
自上而下的解析器将首先解析A,然后A将产生一个由A本身组成的字符串,解析器可能永远进入一个循环。
删除左递归
删除左递归的一种方法是使用以下技术:
生产
A => Aα | β
is converted into following productions
A => βA’A’=> αA’ | ε这不会影响从语法派生的字符串,但会删除立即左递归。
第二种方法是使用下面的算法,它应该消除所有直接和间接的左递归。START Arrange non-terminals in some order like A1, A2, A3,…, An for each i from 1 to n
{ for each j from 1 to i-1
{
replace each production of form Ai ⟹Aj
编译器设计-有限自动机相关推荐
- 编译器设计-代码优化
编译器设计-代码优化 Compiler Design - Code Optimization 优化是一种程序转换技术,它试图通过使代码消耗更少的资源(如CPU.内存)来改进代码,并提供高速. 在优化中 ...
- 编译器设计-代码生成
编译器设计-代码生成 Compiler Design - Code Generation 代码生成可以看作是编译的最后阶段.通过后代码生成,优化过程可以应用到代码上,但这可以看作是代码生成阶段本身的一 ...
- 编译器设计-符号表-中间代码生成
编译器设计-符号表-中间代码生成 Compiler Design - Symbol Table Compiler - Intermediate Code Generation 一.Compiler D ...
- 编译器设计-RunTime运行时环境
编译器设计-RunTime运行时环境 Compiler Design - Run-Time Environment 作为源代码的程序仅仅是文本(代码.语句等)的集合,要使其活动,它需要在目标计算机上执 ...
- 编译器设计-自下而上分析器-误差恢复-语义分析
编译器设计-自下而上分析器-误差恢复-语义分析 Compiler Design - Bottom-Up Parser Compiler Design - Error Recovery Compiler ...
- 编译器设计-解析类型
编译器设计-解析类型 Compiler Design - Types of Parsing 语法分析器遵循由上下文无关语法定义的产生式规则.生成规则的实现方式(派生)将解析分为两种类型:自上而下解析和 ...
- 毕业设计 c语言编译器的设计开发-字节代码格式设计与实现 开题报告,C语言编译器设计与实现...
C语言编译器设计与实现(任务书,外文翻译,毕业论文20000字,答辩PPT) 摘 要 随着计算机的广泛应用,计算机程序设计语言也从初期的机器语言发展为汇编语言,以及现在的各种高级程序设计语言.而编译技 ...
- 最严谨的计算机语言p,用于PLC的华P语言编译器设计及实现.pdf
2009年10月 沈阳理工大学学报 V01.28No.5 OFSHENYANGUGONGUNIVERSITYOct.2 第28卷第5期 TRANSACTIONS 0 09 文章编号:1003-1251 ...
- 设计一个处理两种类型地址的地址簿程序_编译器设计-符号表-中间代码生成
编译器设计-符号表-中间代码生成 Compiler Design - Symbol Table Compiler - Intermediate Code Generation 一.Compiler D ...
最新文章
- 设计模式-扩展-父类对子类的要求
- C语言指针-从底层原理到花式技巧,用图文和代码帮你讲解透彻
- Apache优化——日志管理
- QPW 行政区划字典表(td_area)
- 联想340c笔记本cpu能升级吗_联想扬天威6 2021款酷睿版怎么样?评测值得买吗?从价格和配置看就知道了...
- Apache Log4j2远程JNDI代码执行漏洞修复
- TARS 微服务开源生态
- 拓端tecdat|Stata 中Mata的st_view函数 - 小心使用!
- call及apply的用法理解
- linux mongo 添加用户名和密码,mongodb3.4 安装及用户名密码设置
- linux I2C读写应用程序
- ImageAI (一) 使用Python快速简单实现图像内容预测 Image Prediction
- gbk与gb2312并不一样
- win10记得pin码 重置密码登录
- 这次,多地消防控制室双人值守可变单人值守
- “新元宇宙”奇科幻小说原创作品系列连载《地球人奇游天球记》第三回零点惊魂
- 【经典】对static关键字深入理解
- BPR: Bayesian Personalized Ranking from Implicit Feedback 论文笔记
- 花了5000元改了一个带触摸板的小红点键盘
- Motorola S19(S-record)格式解析