编译原理LL(1)文法

从左向右扫描输入,然后产生最左推导(就是每次都把最左边的非终结字符用产生式代替)。

(一)First集合

比如有产生式 A-> + T | - P , 当我们读到串为 +开头的时候,我们可以很直接地判断选择 A-> + T 这个生成式;串为- 开头的时候,选择 A-> - P 这个生成式。但如果文法是类似于A →T | P 这样的都以非终结字符开头的呢?一眼就很难判断的,我们就需要知道,T 是怎么展开的,如果 T -> a |b ,P->c|d , 那当串以a或b开头的时候,我们显然需要选择A →T ,而当串以c或d开头的时候,就应该选择A->P 这个生成式了。也就是说,我们需要知道T这个分支和P这个分支,都可以用什么终结字符开头。因此我们需要计算每个生成式的开始记号的集合,也就是First集合。

下面给出First集合的算法:

?直接收取:如果存在 T→a … (a为终止符), 把a直接放到First(T)中,很显然A →T |  P →a…|P,遇到a的时候走T这条分支

?反复推送: T→E … ,把First(E)的元素加入到First(T)中

这里举个简单的例子:四则运算

exp→exp addop term | term
addop→ +| -
term →term mulop factor | factor
mulop→?|/
factor →(exp)|<number>

把它分解后变成

(1) exp→exp addop term
(2) exp→term
(3) addop→+
(4) addop→-
(5) term→term mulop factor
(6) term→factor
(7) mulop→?
(8) mulop→/
(9) factor→(exp)
(10) factor→<number>

最容易算的是First(addop)和First(mulop),因为他们的分支都直接以终结字符开始。

First(mulop)={*,/}
First(addop)={+,-}
First(factor)={(,<number>}

term的分支一边是term自己(就是说把term的First内容加到本身,不对自己产生变化),一边是factor(把factor的first集合内容加到term的first集合中),因此First(term)=First(factor) 同理First(exp)=First(term)

当然这个例子只是为了说明First集合的计算,本身存在左递归,是不能做LL1运算的。

(二)Follow集合

其实Follow集合是专门为了空操作而存在的 

我们可以来看这个例子:

A→Tb | P
T→ε | a
P→c

我们知道First(T) = {ε , a} Frist(P) = {c},当遇到a开头的串的时候,选择A→Tb,遇到c开头的串,选择 A-> P

但其实,由于ε在First(T) 里,我们可以得到

A→Tb | P         →(ε│a)b | P      → (b| ab) | c

也就是说,当串以b开头的时候,其实选择的也是A→Tb

所以,为了特殊处理当一个非终结字符可以推出空时候的情况,我们需要知道它后面紧跟的是什么终结符合,这个终结符号也是能被这个串所直接接收的。

?定义:FOLLOW(A)是可能在某些句型中紧跟在A右边的终结符号的集合

Follow集合的算法

S→…U…算Follow(U)

1.如果存在一个产生式 A→…UP,那么First(P)中除了ε 之外都应该放入Follow(U)中

2.如果存在一个产生式 A→…U,或者存在产生式 A→…UP,且First(P)中包含ε,则Follow(A)中的所有元素都在Follow(U)中

3.将$放入Follow(S)中,其中S是开始符号,$是输入右端的结束标识

2的推导: S->EAT, A可以用…U代替,S->E…UT,所以A后面出现的终止符和U后面出现的终止符一样

2和3可得出: S->…U,那么Follow(S)的元素就在Follow(U)中,所以$在Follow(U)中

还是刚才的例子:

(1) exp→exp addop term
(2) exp→term
(3) addop→+
(4) addop→-
(5) term→term mulop factor
(6) term→factor
(7) mulop→?
(8) mulop→/
(9)factor→(exp)
(10) factor→<number>

Exp为开始符号,所以把$放入Follow(exp)中,由式(1)知First(addop)要加入Follow(exp)中,此时Follow(exp)={+,-,$},同理,Follow(addop)={(,number},由式(2),把Follow(exp)加入到Follow(term)中……

(三) 从First,Follow到预测分析表

由First集合和Follow集合就可以得出我们需要的预测分析表了,先来感官性地认识一下:

我们可以看到表格的Y方向是所有的非终结符(也就是所有生成式的左边部分的集合),X方向是所有终结符。

表格的每一项表示,当我目前在N这个非终结符,遇到T这个终结符之后,应该选择的生成式。 比如在stmt时候,如果遇到s,则选择stmt->s这个生成式。

从开始符号出发,每遇到一个输入,就判断往哪边走,最后走完为止,如果中间没有路可以走了,就说明语法有错。

下面来看预测分析表是怎么生成的。其实跟刚才First集合和Follow集合的思路一致。

M[N,T] 其中N为非终止符,T为终止符

算法:为每个非终结符A和产生式 A→ α重复以下两个步骤:

1)对于First(α)中的每个记号a,都将 A→ α 添加到项目 M[ A, a ]中(即,当输入中遇到a,选择A→ α 这一产生式)

2)如果ε在First(α)中,对于Follow(A)中的每个元素a,(记号或者$), 都将A→ α 添加到项目 M[ A, a ]中。

就是正常的话直接看终结符,在哪个分支就往哪个分支走。但如果这个分支的First集合里有ε,那么需要看它后面的终止字符集合。

例子:

E→nE′
E′→ +nE′ | ε

  

First(E) = { n }
First(E’)= { + , ε}
Follow(E) = Follow(E’) = {$}
M[N,T] n + $
E E→nE′    
E‘   E′→ +nE′  E′→ε

根据它来做的出栈入栈如下,比如分析 3+4=5(栈中的#只是为了计算结果,可以不理)

首先把$和开始字符E入栈,然后读取输入串。第一个字符是3,也就是n,M[E,n]是E→nE′,所以我们把E出栈,把n和E’入栈。从右到左入栈,即先入E’,再入n。这个时候,输入n和栈顶n匹配,把n出栈,读取字符串的下一个字符,即+,栈顶的E‘遇到+根据表格知道应该选择E′→ +nE′ ,把E’出栈,E’,n,+入栈,+和输入的+匹配,出栈,顶端为E’.......依次下去,直到匹配结束。

时间: 2024-10-11 00:14:09

编译原理LL(1)文法的相关文章

编译原理之形式语言文法分类

高级程序设计语言的三个基本因素: 语法:描述语言成分的构成规则(包括词法规则和语法规则) 语义:描述语法成分的含义 语用:描述语法成分的使用方法 形式语言理论(formal language theory)是用数学方法研究自然语言(如英语)和人工语言(如程序设计语言)的产生方式.一般性质和规则的理论.形式语言是模拟这些语言的一类数学语言,它采用数学符号,按照严格的语法规则构成.从广义上说,形式语言是符号取自某个字母表的字符串的集合.如同自然语言具有语法规则一样,形式语言也是由形式文法生成的.一个

北航编译原理总结 C文法

定位:传说中北航计算机学院最头疼课程其实也没有辣么难,一点点的完成,并不会出现传说中的刷夜~ 0.pascal-s和PL/0编译器源码有必要结合编译器基础知识认真读一下,当然不必细枝末节,重点是看一下人家的编译器中所谓的"词法分析""语法分析"等阶段以及符号表的建立需要什么量,每个量分别代表什么,以及运行栈出现在什么时候,如何设计(可以和后期优化结合起来~)等. 1.我的文法为 扩充c0文法-高,具体文法如下: <加法运算符> ::= +|- <乘

编译原理之理解文法和语言

简介 一个程序设计语言是一个记号系统,如同自然语言一样,它的完整定义应包括语法和语义两个方面.所谓一个语言的语法是指一组规则,用它可以形成和产生一个合适的程序.目前广泛使用的手段是上下文无关文法,即用上下文无关文法作为程序设计语言语法的描述工具.语法只是定义什么样的符号序列是合法的,与这些符号的含义毫无关系,比如对于一个Pascal程序来说,一个上下文无关文法可以定义符号串A:=B+C是一个合乎语法的赋值语句,而A:=B+就不是.但是,如果B是实型的,而C是布尔型的,或者B.C中任何一个变量没有

编译原理——算符优先分析文法(附源代码)

算符优先分析文法 一.写在前面 算符优先分析文法是一种工具,在编译的过程中,隶属于语法分析环节,却又与中间代码的生成息息相关,编译可以分为五个阶段:词法分析.语法分析.语义分析(中间代码的生成).代码优化.目标代码生成.语法分析是指:在词法分析基础上,将单词符号串转化为语法单位(语法范畴)(短语.子句.句子.程序段.程序),并确定整个输入串是否构成语法上正确的程序.也就是说语法分析是检验输入串的语法是否正确,注意这里的语法正确,只是简单地符合自己定义的规范,而不能检测出运行时错误,比如"X/0&

编译原理123

1.编译原理就是什么? 个人理解,编译就是像翻译一样,将一种语言翻译成另一种语言,编译就是将高级语言或汇编语言翻译成电脑能识别的二进制机器语言,编译原理就是讲解这 个编译的过程.内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成. 编译原理是计算机专业设置的一门重要的专业课程.虽然只有少数人从事编译方面的工作,但是这门课在理论.技术.方法上都对学生提供了系统而有效的训练,有 利于提高软件人员的素质和能力. 2.学习编译原理有什么好处? 学习编译原

编译原理的理解

编译原理内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成.大一点的应用可以做到一定的技术先进性,从而让你在本行业站稳脚跟:分析和分解用户输入的SQL语句,理解是否有害和是否有SQL注入等.如果不学,对于不是不是本行业的人来说就没损失,如果是本行业的专业人士,不学只会令自己的编译理解更慢.

编译原理基础概念介绍

关于编译原理 语法树 句柄 简单短语 短语 的区分,通过两个例子来理解概念以及方法: 例子1——语法树 S -> a|b|(T)  T -> TdS|S Vt={a,b,d,(,)}.Vn={S,T},S是开始符 句型(Sd(T)db)是S的一个推导,其中___是句柄;____是最左素短语:____是该句型的直接短语,_____是短语.     素短语的概念:它是一个递归的定义,至少含有一个终结符,并且除它自身之外不再含任何更小的素短语,所谓最左素短语就是处于句型最左边的素短语的短语.而一个算

python实现算术表达式的词法语法语义分析(编译原理应用)

本学期编译原理的一个大作业,我的选题是算术表达式的词法语法语义分析,当时由于学得比较渣,只用了递归下降的方法进行了分析. 首先,用户输入算术表达式,其中算术表达式可以包含基本运算符,括号,数字,以及用户自定义变量. 词法分析,检查单词变量是否正确:语法分析,检查算术表达式语法是否正确并输出生成语法树:语义分析,输出四元表达式. 最终效果图: 例如输入: 词法分析结果: 语法分析结果: 语义分析结果: 算术表达式的组成语法如下: 无符号整数 = 〈数字〉{〈数字〉} 〈标识符〉= 〈字母〉{〈字母

编译原理随笔

最近初步接触到了编译原理这门课程,通过老师提供的一些链接,以及课上的知识,对这门课有了一些认识与了解. 编译原理旨在介绍编译程序构造的一般原理和基本方法.内容包括语言和文法.词法分析.语法分析.语法制导翻译.中间代码生成.存储管理.代码优化和目标代码生成.从源语言提取需要的信息:把源语言翻译成目标语言:自动生成满足一定规范的文本... 学习编译原理可以更加容易的理解在一个语言种哪些写法是等价的,哪些是有差异的可以更加客观的比较不同语言的差异,更不容易被某个特定语言的宣扬者忽悠,学习新的语言是效率