⑴ 编译原理如何判断id+id*id没有语法错误
构造LL(1)语法分析程序,任意输入一个文法符号串,并判断它是否为文法的一个句子.程序要求为该文法构造预测分析表,并按照预测分析算法对输入串进行语法分析,判别程序是否符合已知的语法规则,如果不符合(编译出错),则输出错误信息.
以你说的SQL语句为例,词法分析是将语句中的单词流识别出来,比如create table Student 词法分析是分析出 这句的单词流是 “create” “table” “identifier”(前提是你给它们编号 比如用宏或者枚举),然后语法分析 是通过单词流 判断 非逻辑错误 比如 有不能识别的符号 create table后面不是标示符等等 语义分析是分析语句的逻辑关系 比如字段长度越界什么的如 vchar(2) 你赋值为“啊啊啊啊啊啊”这种错误的识别是语义分析阶段完成的 希望能帮到你
⑵ 编译原理词法语法语义错误题!!!!!求大神啊!!!!
错误如下
在main函数中,调用了max函数取三个值中的最大值,而在max函数的声明和定义中都只有两个参数,属于语义错误,应该把参数中加一个“int z”(在max函数的定义中出现了名为c的变量,不能重名,故用z)
在max函数中,第二行的“c==a>b?a:b”从上下文来看没有任何意义,应该把条件表达式改为赋值表达式(“==”改为“=”),这个部分在语法分析中不会出问题,故属于语义错误(编译器不会报错)
在max函数的第三行中,额。。我也不知道这个想表达什么,而且点号一般用于获取结构体中的成员,属于后缀表达式,其yacc语法为
postfix_expression '.' IDENTIFIER
而在语法和词法分析中都不会出错,属于语义错误,应改为“c=c > z ? c : z;”
这样改动后,max中的局部变量d就没有了什么作用,建议删去
⑶ 编译原理词法语法问题
词法分析是识别一个一个的单词,3a是数字字母组合,只能看成一个单词来识别,3a以数字开头,以字母结束,既不是标识符,也不是数字,所以是词法错误。+-是两个词,一个是+,一个是-,不会当做一个词来识别。并不是词法错误。
a+-不符合语法规则,即不能由产生式推出。所以是语法错误。
⑷ 编译的时候能发现哪些错误
词法分析阶段能够检测出输入中不能形成源语言任何记号的错误字符串。语法分析阶段可以确定记号流中违反源语言结构(语法)规则的错误。语义分析阶段试图检测出具有正确语法结构但对操作无意义的部分。例如,我们试图将两个标识符相加,其中一个标识符是数组名,而另一个标识符却是过程名。(编译原理-龙书原话)。其他错误例如算法错误编译程序检测不出。
⑸ 编译原理-LL1文法详细讲解
我们知道2型文法( CFG ),它的每个产生式类型都是 α→β ,其中 α ∈ VN , β ∈ (VN∪VT)*。
例如, 一个表达式的文法:
最终推导出 id + (id + id) 的句子,那么它的推导过程就会构成一颗树,即 CFG 分析树:
从分析树可以看出,我们从文法开始符号起,不断地利用产生式的右部替换产生式左部的非终结符,最终推导出我们想要的句子。这种方式我们称为自顶向下分析法。
从文法开始符号起,不断用非终结符的候选式(即产生式)替换当前句型中的非终结符,最终得到相应的句子。
在每一步推导过程中,我们需要做两个选择:
因为一个句型中,可能存在多个非终结符,我们就不确定选择那一个非终结符进行替换。
对于这种情况,我们就需要做强制规定,每次都选择句型中第一个非终结符进行替换(或者每次都选择句型中最后一个非终结符进行替换)。
自顶向下的语法分析采用最左推导方式,即总是选择每个句型的最左非终结符进行替换。
最终的结果是要推导出一个特定句子(例如 id + (id + id) )。
我们将特定句子看成一个输入字符串,而每一个非终结符对应一个处理方法,这个处理方法用来匹配输入字符串的部分,算法如下:
方法解析:
这种方式称为递归下降分析( Recursive-Descent Parsing ):
当选择的候选式不正确,就需要回溯( backtracking ),重新选择候选式,进行下一次尝试匹配。因为要不断的回溯,导致分析效率比较低。
这种方式叫做预测分析( Predictive Parsing ):
要实现预测分析,我们必须保证从文法开始符号起,每一个推导过程中,当前句型最左非终结符 A 对于当前输入字符 a ,只能得到唯一的 A 候选式。
根据上面的解决方法,我们首先想到,如果非终结符 A 的候选式只有一个以终结符 a 开头候选式不就行了么。
进而我们可以得出,如果一个非终结符 A ,它的候选式都是以终结符开头,并且这些终结符都各不相同,那么本身就符合预测分析了。
这就是S_文法,满足下面两个条件:
例子:
这就是一个典型的S_文法,它的每一个非终结符遇到任一终结符得到候选式是确定的。如 S -> aA | bAB , 只有遇到终结符 a 和 b 的时候,才能返回 S 的候选式,遇到其他终结符时,直接报错,匹配不成功。
虽然S_文法可以实现预测分析,但是从它的定义上看,S_文法不支持空产生式(ε产生式),极大地限制了它的应用。
什么是空产生式(ε产生式)?
例子
这里 A 有了空产生式,那么 S 的产生式组 S -> aA | bAB ,就可以是 a | bB ,这样 a , bb , bc 就变成这个文法 G 的新句子了。
根据预测分析的定义,非终结符对于任一终结符得到的产生式是确定的,要么能获取唯一的产生式,要么不匹配直接报错。
那么空产生式何时被选择呢?
由此可以引入非终结符 A 的后继符号集的概念:
定义: 由文法 G 推导出来的所有句型,可以出现在非终结符 A 后边的终结符 a 的集合,就是这个非终结符 A 的后继符号集,记为 FOLLOW(A) 。
因此对于 A -> ε 空产生式,只要遇到非终结符 A 的后继符号集中的字符,可以选择这个空产生式。
那么对于 A -> a 这样的产生式,只要遇到终结符 a 就可以选择了。
由此我们引入的产生式可选集概念:
定义: 在进行推导时,选用非终结符 A 一个产生式 A→β 对应的输入符号的集合,记为 SELECT(A→β)
因为预测分析要求非终结符 A 对于输入字符 a ,只能得到唯一的 A 候选式。
那么对于一个文法 G 的所有产生式组,要求有相同左部的产生式,它们的可选集不相交。
在 S_文法基础上,我们允许有空产生式,但是要做限制:
将上面例子中的文法改造:
但是q_文法的产生式不能是非终结符打头,这就限制了其应用,因此引入LL(1)文法。
LL(1)文法允许产生式的右部首字符是非终结符,那么怎么得到这个产生式可选集。
我们知道对于产生式:
定义: 给定一个文法符号串 α , α 的 串首终结符集 FIRST(α) 被定义为可以从 α 推导出的所有串首终结符构成的集合。
定义已经了解清楚了,那么该如何求呢?
例如一个文法符号串 BCDe , 其中 B C D 都是非终结符, e 是终结符。
因此对于一个文法符号串 X1X2 … Xn ,求解 串首终结符集 FIRST(X1X2 … Xn) 算法:
但是这里有一个关键点,如何求非终结符的串首终结符集?
因此对于一个非终结符 A , 求解 串首终结符集 FIRST(A) 算法:
这里大家可能有个疑惑,怎么能将 FIRST(Bβ) 添加到 FIRST(A) 中,如果问文法符号串 Bβ 中包含非终结符 A ,就产生了循环调用的情况,该怎么办?
对于 串首终结符集 ,我想大家疑惑的点就是,串首终结符集到底是针对 文法符号串 的,还是针对 非终结符 的,这个容易弄混。
其实我们应该知道, 非终结符 本身就属于一个特殊的 文法符号串 。
而求解 文法符号串 的串首终结符集,其实就是要知道文法符号串中每个字符的串首终结符集:
上面章节我们知道了,对于非终结符 A 的 后继符号集 :
就是由文法 G 推导出来的所有句型,可以出现在非终结符 A 后边的终结符的集合,记为 FOLLOW(A) 。
仔细想一下,什么样的终结符可以出现在非终结符 A 后面,应该是在产生式中就位于 A 后面的终结符。例如 S -> Aa ,那么终结符 a 肯定属于 FOLLOW(A) 。
因此求非终结符 A 的 后继符号集 算法:
如果非终结符 A 是产生式结尾,那么说明这个产生式左部非终结符后面能出现的终结符,也都可以出现在非终结符 A 后面。
我们可以求出 LL(1) 文法中每个产生式可选集:
根据产生式可选集,我们可以构建一个预测分析表,表中的每一行都是一个非终结符,表中的每一列都是一个终结符,包括结束符号 $ ,而表中的值就是产生式。
这样进行语法推导的时候,非终结符遇到当前输入字符,就可以从预测分析表中获取对应的产生式了。
有了预测分析表,我们就可以进行预测分析了,具体流程:
可以这么理解:
我们知道要实现预测分析,要求相同左部的产生式,它们的可选集是不相交。
但是有的文法结构不符合这个要求,要进行改造。
如果相同左部的多个产生式有共同前缀,那么它们的可选集必然相交。
例如:
那么如何进行改造呢?
其实很简单,进行如下转换:
如此文法的相同左部的产生式,它们的可选集是不相交,符合现预测分析。
这种改造方法称为 提取公因子算法 。
当我们自顶向下的语法分析时,就需要采用最左推导方式。
而这个时候,如果产生式左部和产生式右部首字符一样(即A→Aα),那么推导就可能陷入无限循环。
例如:
因此对于:
文法中不能包含这两种形式,不然最左推导就没办法进行。
例如:
它能够推导出如下:
你会惊奇的发现,它能推导出 b 和 (a)* (即由 0 个 a 或者无数个 a 生成的文法符号串)。其实就可以改造成:
因此消除 直接左递归 算法的一般形式:
例如:
消除间接左递归的方法就是直接带入消除,即
消除间接左递归算法:
这个算法看起来描述很多,其实理解起来很简单:
思考 : 我们通过 Ai -> Ajβ 来判断是不是间接左递归,那如果有产生式 Ai -> BAjβ 且 B -> ε ,那么它是不是间接左递归呢?
间接地我们可以推出如果一个产生式 Ai -> αAjβ 且 FIRST(α) 包括空串ε,那么这个产生式是不是间接左递归。
⑹ 【编译原理】第四章:语法分析
从分析树的根节点到叶节点方向构造分析树。
即从开始符号S推导出词串w的过程。
例:
总是选择每个句型的 最左非终结符 进行替换。
总是选择每个句型的 最右非终结符 进行替换。
在自底向上的分析中,总是采用 最左规约 的方式,因此把 最左规约 称为 规范规约 ,对应的 最右推导 称为 规范推导 。
最左推导、最右推导具有唯一性。
自顶向下的语法分析采用最左推导方试,总是选择每个句型的 最左非终结符 进行替换。
由一组 过程 组成,每一个过程对应一个 非终结符 。
从文法开始符号S开始,递归调用文法中的其他非终结符,最终扫描整个输入串,完成分析。
如果其间有不唯一的产生式,就可能需要退回上一步重新尝试的情况,称为 回溯 。
预测分析 是 递归下降分析 技术的一个特例,通过输入中向前看固定个数的符号选择正确的产生式。
如果一个文法可以构造出向前看k个符号的预测分析器,称为LL(k)文法 。
预测分析不需要回溯,具有确定性。
含有 形式产生式的文法称为是 直接左递归 的。
如果一个文法中有一个非终结符A使得对某个串存在推导 ,那么这个文法是 左递归 的。其中,经过两步或以上推导产生的左递归,称为 间接左递归 的。
左递归会使递归下降分析器陷入无限循环。
文法
即
该文法是直接左递归的,会陷入无限循环。
将以上文法转换为:
即可消除左递归。事实上,这个过程把左递归转换成了右递归。
消除直接左递归的一般形式
使用代入法。
对于一个文法,通过改写产生式来 推迟决定 ,等获得足够多的输入信息再做正确的决定。
例:文法:
可以改写为:
从文法的开始符号S开始,每一步推导根据当前句型的最左非终结符A和当前输入符号α,选择正确的A-产生式。为保证分析的确定性,选出的候选式必须是唯一的。
S_文法(简单的确定型文法)
可能在某个举行中紧跟在A后面的终结符a的集合,记为 FOLLOW(A) 。
如果A是某个句型的最右符号,则将结束符“ $ ”添加到FOLLOW(A)中。
例:文法:
中,FOLLOW(B) = {a, c}
产生式 的可选集是指可以选用该产生式进行推导时对应的输入符号的集合,记为 SELECT(A->β) 。
例如
SELECT(A -> aβ)={a}
SELECT(A -> aβ | bγ)={a, b}
SELECT(A -> ε)=FOLLOW(A)
q_文法
文法符号串α串首终结符的集合,记作 FIRST(A) 。
⑺ 编译原理,小问题提问!!
无符号常数的识别工作通常在编译的词法分析阶段完成
编译程序把一个源程序翻译成目标程序的工作过程分为五个阶段:词法分析;语法分析;语义检查&[font style="BACKGROUND-COLOR: #ffff00"]中间代码[/font]生成;代码优化;目标代码生成。主要是进行词法分析和语法分析,又称为源程序分析,分析过程中发现有语法错误,给出提示信息。
词法分析器的功能和输出形式
词法分析器的功能是输入源程序,输出单词符号。单词符号是一个程序语言的基本语法符号。程序语言的单词符号一般可分为下列五种。
(1)关键字
是由程序语言定义的具有固定意义的标志符。有时称这些标志符为保留字或基本字。例如,Pascal中的begin,end,if,while都是保留字。这些字通常不用作一般标志符。
(2)标识符
用来表示各种名字,如变量名、数组名、过程名等等。
(3)常数
常数的类型一般有整型、实型、布尔型、文字型等等。例如,100,3.14159,TRUE,‘Sample’。
(4)运算符
如+、-、*、/等等
(5)界符
如逗号、分号、括号、/*,*/等等。
⑻ 编译原理笔记17:自下而上语法分析(4)LR(0)、SLR(1) 分析表的构造
(移进项目就是纳凯态指圆点右边是终结符的项目,规约项目指的就是圆点在右部最右端的项目)
LR(0) 文法可以直接通过识别活前缀的 DFA 来构造 LR 分析表
假定 C = {I 0 , I 1 , ... , I n } (aka. LR(0) 项目规范族、DFA 状态集)
首先为文法产生式进行编号,拓广文法的产生式要标记为 0(这里就是后面分析表中 rj 的产生式编号 j 的由来)
然后令每个项目集 I k 的下标 k 作为分析器洞源的状态(行首),包含 S' → .S 的集合下标为分析器的初态(也就是 DFA 的初态孙型,一般都是 0 )。
下面用一个例子来说明 ACTION、GOTO 子表的构造:
SLR(1) 为解决冲突提出了一个简单的方法:通过识别活前缀的 DFA 和【简单向前看一个终结符】构造 SLR(1) 分析表。
如果我们的识别活前缀的 DFA 中存在移进-规约冲突、规约-规约冲突,都可以尝试使用这个方法来解决冲突。(这里说【尝试】,当然是因为 SLR 也只能解决一部分问题,并不是万能的灵丹妙药。。)
这里,我们拿前面那个 LR(0) 解决不了的文法来举例
该文法不是 LR(0) 文法,但是是 SLR(1) 文法。
观察上图 DFA 中的状态2,想象当我们的自动机正处于这个状态:次栈顶已经规约为 T 了,栈顶也是当前的状态 2 ,而当前剩余输入为 *。
如果这个自动机不会【往前多看一步】的话,那么对处于这个状态的自动机来说,看起来状态 2 中的移进项目和规约项目都是可选的。这就是移进-规约冲突。
想要解决这个冲突,就轮到【往前多看一步】上场了——把当前剩余输入考虑进来,辅助进行项目的选择:
对其他的冲突也使用同样的方法进行判断。
这种冲突性动作的解决办法叫做 SLR(1) 解决办法
准备工作部分,与 LR(0) 分析表的构造差不多:同样使用每个项目集的状态编号作为分析器的状态编号,也就同样用作行下标;同样使用拓广文法产生式作为 0 号产生式。
填表也和 LR(0) 类似,唯一的不同体现在对规约项的处理方法上:如果当前状态有项目 A → α.aβ 和 A → α. ,而次栈顶此时是 α 且读写头读到的是 a,那么当且仅当 a∈FOLLOW(A) 时,我们才会用 A → α 对 α 进行规约。
如果构造出来的表的每个入口都不含多重定义(也就是如上图中表格那样的,每个格子里面最多只有一个动作),那么该表就是该文法的 SLR(1) 表,这个文法就是 SLR(1) 文法。使用 SLR(1) 表的分析器叫做一个 SLR(1) 分析器。
任意的二义文法都不能构造出 SLR(1) 分析表
例:悬空 else
例:
这里的 L 可以理解为左值,R 可以理解为右值
经过计算可以确定其 DFA 如下图所示。
在 状态4 中,由于 "=" 同时存在于 FOLLOW(L) 与 FOLLOW(R) 中,因此该状态内存在移进-规约冲突,故该文法不是 SLR(1) 文法。
这样的非二义文法可以通过增加向前看终结符的个数来解决冲突(比如LL(2)、LR(2))但这会让问题更加复杂,故一般不采用。而二义文法无论向前看多少个终结符都无法解决二义性。
⑼ 编译原理
编译原理是计算机专业的一门重要专业课,旨在介绍编译程序构造的一般原理和基本方法。内容包括语言和文法、词法分析、语法分析、语法制导翻译、中间代码生成、存储管理、代码优化和目标代码生成。 编译原理是计算机专业设置的一门重要的专业课程。编译原理课程是计算机相关专业学生的必修课程和高等学校培养计算机专业人才的基础及核心课程,同时也是计算机专业课程中最难及最挑战学习能力的课程之一。编译原理课程内容主要是原理性质,高度抽象[1]。
中文名
编译原理[1]
外文名
Compilers: Principles, Techniques, and Tools[1]
领域
计算机专业的一门重要专业课[1]
快速
导航
编译器
编译原理课程
编译技术的发展
编译的基本流程
编译过程概述
基本概念
编译原理即是对高级程序语言进行翻译的一门科学技术, 我们都知道计算机程序由程序语言编写而成, 在早期计算机程序语言发展较为缓慢, 因为计算机存储的数据和执行的程序都是由0、1代码组合而成的, 那么在早期程序员编写计算机程序时必须十分了解计算机的底层指令代码通过将这些微程序指令组合排列从而完成一个特定功能的程序, 这就对程序员的要求非常高了。人们一直在研究如何如何高效的开发计算机程序, 使编程的门槛降低。[2]
编译器
C语言编译器是一种现代化的设备, 其需要借助计算机编译程序, C语言编译器的设计是一项专业性比较强的工作, 设计人员需要考虑计算机程序繁琐的设计流程, 还要考虑计算机用户的需求。计算机的种类在不断增加, 所以, 在对C语言编译器进行设计时, 一定要增加其适用性。C语言具有较强的处理能力, 其属于结构化语言, 而且在计算机系统维护中应用比较多, C语言具有高效率的优点, 在其不同类型的计算机中应用比较多。[3]
C语言编译器前端设计
编译过程一般是在计算机系统中实现的, 是将源代码转化为计算机通用语言的过程。编译器中包含入口点的地址、名称以及机器代码。编译器是计算机程序中应用比较多的工具, 在对编译器进行前端设计时, 一定要充分考虑影响因素, 还要对词法、语法、语义进行分析。[3]
1 词法分析[3]
词法分析是编译器前端设计的基础阶段, 在这一阶段, 编译器会根据设定的语法规则, 对源程序进行标记, 在标记的过程中, 每一处记号都代表着一类单词, 在做记号的过程中, 主要有标识符、关键字、特殊符号等类型, 编译器中包含词法分析器、输入源程序、输出识别记号符, 利用这些功能可以将字号转化为熟悉的单词。[3]
2 语法分析[3]
语法分析是指利用设定的语法规则, 对记号中的结构进行标识, 这包括句子、短语等方式, 在标识的过程中, 可以形成特殊的结构语法树。语法分析对编译器功能的发挥有着重要影响, 在设计的过程中, 一定要保证标识的准确性。[3]
3 语义分析[3]
语义分析也需要借助语法规则, 在对语法单元的静态语义进行检查时, 要保证语法规则设定的准确性。在对词法或者语法进行转化时, 一定要保证语法结构设置的合法性。在对语法、词法进行检查时, 语法结构设定不合理, 则会出现编译错误的问题。前端设计对精确性要求比较好, 设计人员能够要做好校对工作, 这会影响到编译的准确性, 如果前端设计存在失误, 则会影响C语言编译的效果。[3]
⑽ 编译原理语法分析实验问题
错误1:在3.txt中,第二个表达式x:=2*3,在编译器里面没有对*符号进行解释,这个应补充,或者改掉*为+。
错误2:代码中出现3次类似syn==15||16的代码,我理解应该是(syn==15)||(syn==16)
改掉这两点后代码可以正常运行。
建议:写代码是一项工作,更是一个创作过程,建议你按照代码写作规范来写,这样的代码清晰易读,易于交流和纠错。