【编译原理】自顶向下语法分析方法的实现

合集下载

编译原理-四章自顶向下语法分析法

编译原理-四章自顶向下语法分析法

第四章自顶向下语法分析方法语法分析是编译过程的核心部分。

语法分析的任务是:按照文法,从源程序符号串中识别出各类语法成份,同时进行语法检查,为语义分析和代码生成作准备。

执行语法分析任务的程序称为分析程序。

也称为语法分析器,它是编译程序的主要子程序之一。

在第二章中我们已经介绍过。

通过语法分析可建立起相应的语法树。

按语法树的建立方法,我们将语法分析方法分成两大类,即自顶向下分析和自底向上分析。

下面,我们先介绍自顶向下分析。

本章重点:自顶向下分析、LL(1)分析第一节自顶向下分析方法一、带回溯的自顶向下分析算法这是自顶向下分析的一般方法,即对任一输入符号串,试图用一切可能的方法,从识别符号出发,根据文法自上而下地为输入串建立一棵语法树。

下面用一个简单例子来说明这种过程:假定有文法G[S]:S→cAdA→ab|a 以及输入串w=cad为了自上而下地构造w的语法树,我们首先按文法的识别符号产生根结点S,并让指示器IP指c的规则(此处左部为S的规则仅有一条)( a)(b)(c)图3-1-1图3-1-1a。

我们希望用S的子结从左至右匹配整个输入串w。

首先,此树的最左子结是终结符c为标志的子结,它和输入串的第一个符号相匹配。

于是,我们就把IP调整为指向下一输入符号a,并让第二个子结A去进行匹配,非终结符A有二个选择,我们试着用它的第一个选择去匹配输入串,于是把语法树发展为图3-1-1b。

子树A的最左子结和IP所指的符号相符,然后我们再把IP调为指向下一符号d并让A的第二个子结进入工作。

但A 的第二个子结为终结符号b,与IP当前指的符号d不一致。

因此,A宣告失败。

这意味着A的第一个选择此刻不适用于构造w的语法树。

这时,我们应该回头(回溯)看A是否还有别的选择。

为了实现回溯,我们一方面应把A的第一个选择所生长的子树注销掉;另一方面,应把IP恢复为进入A时的原值,也就是让它重新指向第二输入符号a。

现在我们试探用A的第二个选择,即考虑生成图3-1-1c的语法树。

自顶向下的语法分析-编译原理-04-(三)

自顶向下的语法分析-编译原理-04-(三)
给定文法S→cAd
A→ab|a
S
?句子cad是该文法定义语言的句子
S c a A b d c
A a
d
候选式的确定与回溯(Backtracking)

当要进行某个语法变量的推导时,希望能 够根据当前符号确定候选式。如果有几个 候选式(右部)左端第一个符号相同,则 分析程序无法根据当前输入符号选择产生 式,只能试探 希望:寻找一类文法,我们可以方便地根 据当前输入符合确定正确的候选式
三、重要问题——虚假匹配
S xAy
x*y 输入串x**y S→xAy A→**|* S xAy x**y
匹配成功 发现不匹配,需 要回退
S x A
*
y
S x
*
A
*
y
三、重要问题——回溯
x * * y
xAy x*y
发现不匹配,需要回退
S
S x A
*
y
x * * y
S xAy x**y
A′→αA′|ε
例:表达式文法直接左递归的消除
E → E + T|T T → T * F|F F → ( E )|id E→ T E’ E’→ + T E’|ε T→ F T’ T’→* F T’|ε F→ ( E )|id
例: 间接左递归的消除
S→Ac|c
A→Bb|b B→Sa|a 将B的定义代入A产生式得:A→Sab|ab|b 将A的定义代入S产生式得: S→Sabc|abc|bc|c 消除直接左递归: S→abcS’|bcS’|cS’ S’→abcS’|ε 删除“多余的”产生式:A→Sab|ab|b和B→Sa|a 结果: S→abcS’|bcS’|cS’ S’→abcS’|ε

编译原理第6节课第二章

编译原理第6节课第二章
n n m m n m m n
为一先天二义性语言。 为一先天二义性语言。 • CFL的先天二义性也是不可判定的。 的先天二义性也是不可判定的。 的先天二义性也是不可判定的
2.3.3 短语和句柄
• 问题:在自底向上 问题: 的语法分析中, 的语法分析中,对 于每一步直接归约, 于每一步直接归约, 应如何正确地确定 当前句型中应被归 约的最左子串 约的最左子串? 最左子串 F i E T + T F i T * F i E
E(2) + T(2)
• 但是 对一句型而言,其直接短语可能不唯一。 但是,对一句型而言,其直接短语可能不唯一。 对一句型而言 为了让分析能够机械地进行,我们只考虑最左 为了让分析能够机械地进行,我们只考虑最左 归约。 归约。 E E T F i + T F i T * F i E + T F i
* +
归约时被替换子串的选择
• 从句型 η=E+T*F+i 的语法树可知 E+T 绝不是 的语法树可知, 它的一个直接短语 因为虽然 它的一个直接短语,因为虽然 E→E+T 是 G2[E] 直接短语 的一个产生式,但不存在从 的推导。 的一个产生式 但不存在从 E 到 E*F+i 的推导。 E E(1) E(2) + T(3) T(2) * F(3) + T(1) F(1) i
E + T F T * F i
i • 对一语法树而言,其构造过程不同对应了不 对一语法树而言, 同的推导(归约)过程。 同的推导(归约)过程。 推导
文法的二义性
• 存在这样的文法 ,其某个句子 w ∈ L(G) , 存在这样的文法G, 可对应结构不同的语法树, 可对应结构不同的语法树,即 w 对应了多个 不同的最左(右)推导,这类文法称为二义 不同的最左* +

编译原理-自顶向下的语法分析

编译原理-自顶向下的语法分析

编译原理-⾃顶向下的语法分析1.代替部分由公因⼦就会出现回溯。

2.LL(1)⽂法(1)FIREST集这样说从⽂法的左部找右部相关的⾮终结符号。

若 X->BC..D,则将First(B)所有元素(除了ε)加⼊First(A),然后检测First(B),若First(B)中不存在ε,则停⽌,若存在则向B的后⾯查看,将First(C)中所有元素(除了ε)加⼊First(A),然后再检测First(C)中是否有ε...直到最后,若D之前的所有⾮终结符的First集中都含有ε,则检测到D时,将First(D)也加⼊First(A),若First(D)中含有ε,则将ε加⼊First(A)。

(2)FOLLOW集:记住是从⽂法的右部去找的。

 1)S是开始符号,将#放到follow(S)中. 2)存在A→αBβ(且β!=ε),那么first(β)中除ε之外的所有符号都在follow(B)中。

3)存在A→αB或A→αBβ(且first(β)包含ε),那么follow(A)中的所有符号都在follow(B)中。

(注意都没有说α⼀定存在。

)(3)SELECT集对于产⽣式A—>α。

集合select(A—>α)定义如下:1. 若α不能推出ε,则select(A—>α) = first(α)。

2. 若α能推出ε,则select(A—>α)= first(α)∪ follow(A)。

(4)如何判断⼀个⽂法是不是LL(1)⽂法:第⼀消除左递归,先消除去直接左递归,然后看是否有相同最左公因⼦的,提取出来。

第⼆分别求出每个推导的SELECT集,同⼀左部的求SELECT集的交集,所有的这种交集为空的话就是LL(1)⽂法,否则不是。

3.递归下降分析法:只针对LL(1)⽂法的,所以没有左递归。

简单的来说就是为每个⾮终结符号编写⼀个处理程序,⽽处理程序的代码结构是由相应的⾮终结符号的规则右部所决定。

p68.4.预测分析表的构造:p74。

编译原理语法分析-自顶向下

编译原理语法分析-自顶向下

实例分析
1
子规则匹配
根据语法规则,递归地匹配输入的源代码,构建语法树。
2
构建语法树
通过逐步匹配子规则,将语法树逐渐构建起来,形象地表示复杂的程序结构。
3
解释分析结果
对语法树进行解释,执行语义分析和生成中间分析方法,通过递归嵌套和预测分析,将复杂的源代码转换成易于处理的 语法树。
自顶向下分析算法
1 概述
自顶向下分析算法从目标语言的最高级别规则开始,逐步向下查找并匹配规则,构建语 法树。
2 递归下降分析
递归下降分析是自顶向下分析的一种常见方法,它通过递归调用子规则来分析输入的源 代码。
3 LL(1)分析
LL(1)分析是一种基于预测的自顶向下分析方法,它使用一个预测分析表来确定下一步要 采取的动作。
编译原理语法分析-自顶 向下
语法分析是编译器的重要组成部分,它负责将输入的源代码转换成语法树以 进行后续分析和解释。本节将介绍自顶向下的语法分析算法及其挑战。
语法分析概述
1 什么是语法分析
语法分析是编译器的第二个阶段,负责验证 输入的源代码是否符合语言的规范语法。
2 为什么需要语法分析
语法分析可以检查和纠正源代码中的语法错 误,以确保程序的正确性和可读性。
问题和挑战
1 二义性文法
当文法存在多个解释时,会导致语法分析的 困扰和歧义。需要通过合适的方法解决二义 性。
2 左递归文法
左递归文法会导致递归下降分析算法进入无 限循环,需要通过消除左递归来解决。
改进方法
1 消除二义性文法
通过重写或修改文法规则,消除存在二义性 的产生式。
2 消除左递归文法
通过改写产生式,消除文法中的左递归问题, 使得递归下降分析算法不会陷入无限循环。

软件工程 编译原理 第五章 自顶向下的语法分析方法

软件工程 编译原理 第五章 自顶向下的语法分析方法

P→1P | 2P |… | mP |
(2)消除间接左递归
对于间接左递归的消除需先将间接左递归变为直接左 递归,然后再按a)消除直接左递归。
例:文法G为例: (1) A→aB (2) A→Bb (3) B→Ac (4) B→d 用产生式(1)、(2)的右部 代替产生式(3)中的非终 结符A得到左部为B的产 生式为: (1) B→aBc (2) BG的产生式为: (1) S→aSb (2) S→aS (3) S→ε 请提取文法中的左公因子
对产生式(1)、(2)提取左公因子后得: S→ aS(b|ε) S→ε 进一步变换为文法G′: S→aSA A→b A→ε S→ε
例2:若文法G的产生式为: (1) A→ad (2) A→Bc (3) B→aA (4) B→bB 请提取文法中的隐式左公因子。 对文法G2分别用(3)、(4)的右 部替换(2)中的B,可得: 提取产生式(1)、(2)的左 (1) A→ad 公共因子得: (2) A→aAc A→a(d|Ac) (3) A→bBc A→bBc (4) B→aA B→aA (5) B→bB B→bB
由上面所举例子可以说明以下问题:
① 不一定每个文法的左公共因子都能在有限的步骤内 替换成无左公共因子的文法,上面文法G4就是如此。 ② 一个文法提取了左公共因子后,只解决了相同左部 产生式右部的FIRST集不相交问题,当改写后的文法 不含空产生式,且无左递归时,则改写后的文法是 LL(1)文法,否则还需用LL(1)文法的判别方式进行判 断才能确定是否为LL(1)文法。
例:文法G(E):
E→TE E→+TE | T→FT T→*FT | F→(E) | i
每个非终结符有对应的子程序的定义, 首先在分析过程中,当需要从某个非终 结符出发进行展开(推导)时,就调用这 个非终结符对应的子程序。

第5部分自顶向下语法分析方法-

第5部分自顶向下语法分析方法-
2. 如果两个产生式有相同的 左部,那么它们的右部由 不同的终结符开始。
• 文法 G2[S]: S→Ap S→Bq A→a A→cA B→b B→dB
编译原理
W=ccap自顶向下的推导过程: S Ap cAp ccAp ccap
语法树:
S
S
Ap Ap cA
S Ap cA cA
S Ap cA cA
编译原理
A→b
3.化为: S→aSA' S→bc A'→d A'→c A→aS A→b
结果中A是不可达到的符号。
• 例:文法G4[S] 为:
编译原理
3.化为:
S→Ap|Bq
S→aS'
A→aAp|d B→aBq|e
1.化为: S→aApp|aBqq|dq|eq A→aAp|d B→aBq|e
S→dq|eq S'→App|Bqq A→aAp|d B→aBq|e 4.化为: S→aS' S→dq|eq
FOLLOW(A),加至FOLLOW(B)中.
S→AB S→bC A→ε A→b B→ε B→aD C→AD C→b D→aS D→c
编译原理
FOLLOW(S)={#}∪FOLLOW(D) FOLLOW(A)={a}∪{a,c}∪FOLLOW(S) FOLLOW(B)=FOLLOW(S) FOLLOW(C)=FOLLOW(S) FOLLOW(D)=FOLLOW(B)∪FOLLOW(C)
SELECT(A→ε)
={a,d,#,ε}
SELECT(S→aA) ∩ SELECT(S→d)={a}∩{d}=Φ
SELECT(A→bAS)∩SELECT(A→ε)={b}∩{a,d,#, ε}=Φ

编译原理 第四章自顶向下语法分析法

编译原理 第四章自顶向下语法分析法
若b不∈FIRST(αi),其中i=1~n,则语法错,转出错处理。这样就避免了分析过程的回溯。
若文法的任一非终结符号,其规则右部的各个选择所能推出的终结符号串的头符号集合不满足两两相交的条件时,那么,要构造一个不带回溯的自顶向下的语法分析程序,需要采取什么措施呢?一般可采取改写文法的办法来解决。
(三)改写文法当文法不满足,可改写文法
对每一文法符号X∈V计算FIRST(X)。
(a)若X∈VT,则FIRST(X)={x}
(b)若X∈VN,且有产生式X→a…,a∈FIRST(X)。
(c)若X∈VN,X→ε,则ε∈FIRST(X)。
(d)若X∈VN,Y1,Y2,…,Yi都∈VN,而有产生式X→Y1Y2…Yn。当Y1,Y2,…,Yi-1都 ε时,(其中1≤i≤n),则FIRST(Y1)-{ε},FIRST(Y2)-{ε},…,FIRST(Yi-1)-{ε},FIRST(Yi)都包含在FIRST(X)中。
二、存在问题及解决办法
(一)左递归问题
自顶向下分析法只有规则排列得合适时,才能正确工作。该法的一个基本缺点是不能处理具有左递归的文法。如下所示。
如:直接左递归和间接左递归
无法确定语法树的终止,
清除直接左递归的较好方法是改
为右递归
如:S→Sa|b改为
S→bS′
S′→aS′|ε
一般情况下,直接左递归的形式可为:
为了自上而下地构造w的语法树,我们首先按文法的识别符号产生根结点S,并让指示器IP指
向输入串的第一符号c。然后,用S的规则(此处左部为S的规则仅有一条)把这棵树发展为
(a)
(b)(c)
图3-1-1
图3-1-1a。我们希望用S的子结从左至右匹配整个输入串w。首先,此树的最左子结是终结符c为标志的子结,它和输入串的第一个符号相匹配。于是,我们就把IP调整为指向下一输入符号a,并让第二个子结A去进行匹配,非终结符A有二个选择,我们试着用它的第一个选择去匹配输入串,于是把语法树发展为图3-1-1b。子树A的最左子结和IP所指的符号相符,然后我们再把IP调为指向下一符号d并让A的第二个子结进入工作。但A的第二个子结为终结符号b,与IP当前指的符号d不一致。因此,A宣告失败。这意味着A的第一个选择此刻不适用于构造w的语法树。这时,我们应该回头(回溯)看A是否还有别的选择。
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

实验报告实验项目列表一、实验名称自顶向下语法分析方法的实现二、实验目的1.掌握自顶向下语法分析的方法;2.运用编程的手段实现自顶向下语法分析。

三、实验内容和要求四、实验环境1.硬件环境:PC机2.软件环境:Windows操作系统,VC++集成开发环境五、算法设计思想六、主要问题与解决方法七、实验结果以下是程序的用户运行界面截图:八、体会、质疑、建议九、源代码#include<fstream>#include<sstream>#include<iostream>#include<vector>#include<string>#include<windows.h>#include<iterator>#include<algorithm>#include<stdlib.h>#define M 20using namespace std;void gotoxy(int x,int y){COORD coord;coord.X=x;coord.Y=y;SetConsoleCursorPosition( GetStdHandle( STD_OUTPUT_HANDLE ), coord );}class table{public:void read();friend class an_process;string find_str(char row,char col);private:string matrix[M][M];char start;vector<string> vec_row;vector<string> vec_col;};class sentence{public:void read_in();friend class an_process;private:string str_input;};class an_process{public:void analyse(const table LL1_table,const sentence input);void write_result();void print2(int x,int y);private:vector<int> step;vector<vector<char> > stack_note;vector<string> remain;vector<string> production;};void table::read(){string a,str;char x,y;int linenum = 0;char line[1024]={0};ifstream infile("table1.txt");if(!infile){cerr<<"错误!无法存储用户数据!\n";}while(infile.getline(line,sizeof(line))){stringstream word(line);if(linenum>1){if(word>>x&&word>>y&&word>>str){matrix[x-48][y-48]=str;/*cout<<x<<" ";cout<<y<<" ";cout<<matrix[x-48][y-48]<<" ";*/}}while(word>>a&&linenum<2){if(linenum==0){vec_col.push_back(a);/*cout<<a<<" ";*/}else if(linenum==1){vec_row.push_back(a);/* cout<<a<<" ";*/}}/*cout<<endl;*/linenum++;}start=vec_col[0][0];/* for(int icnt=0;icnt<vec_col.size();icnt++){cout<<vec_col[icnt]<<" ";}*/}string table::find_str(char row,char col){for(int x=0;x<vec_col.size();x++)for(int y=0;y<vec_row.size();y++){if(row==vec_col[x][0]&&col==vec_row[y][0]){return matrix[x+1][y+1];}}return"";}void an_process::analyse(table LL1_table,sentence input) {int icnt=0,jcnt=0,kcnt;string ac,prod="",re="";vector<char> stack;char Now_word=input.str_input[icnt];char NOW_state=LL1_table.start;string::const_iterator Eiter;string::const_iterator Biter;stack.push_back('#');stack.push_back(NOW_state);for(;icnt<input.str_input.size();){step.push_back(++jcnt);stack_note.push_back(stack);for(kcnt=icnt;kcnt<input.str_input.size();kcnt++){re=re+input.str_input[kcnt];}re=re+"#";remain.push_back(re);re.erase();Now_word=input.str_input[icnt];NOW_state=stack.back();if(NOW_state==Now_word){stack.erase(stack.end()-1);icnt++;production.push_back("(匹配)");}else{ac=LL1_table.find_str(NOW_state,Now_word);Biter=ac.begin();Biter--;Eiter=ac.end();Eiter--;if(ac=="ε"){prod=prod+NOW_state;prod=prod+"→";prod=prod+"ε";stack.erase(stack.end()-1);production.push_back(prod);prod.erase();}else if(ac!=""){stack.erase(stack.end()-1);prod.empty();prod=prod+NOW_state;prod=prod+"→"+ac;production.push_back(prod);prod.erase();for(;Eiter!=Biter;Eiter--){stack.push_back(*Eiter);}}else{cout<<"分析失败\n";exit(0);}}}if(stack.size()!=1||stack.back()!='#'){cout<<"分析失败\n";exit(0);}}void an_process::write_result(){step.push_back(step.back()+1);vector<char> a;a.push_back('#');stack_note.push_back(a);remain.push_back("#");production.push_back("接受");gotoxy(27,1);cout<<"输入串"<<'"'<<remain[0];cout<<"\b"<<'"'<<"的分析过程"<<endl<<endl;print2(step.size()+1,4);vector<int>::const_iterator iter1=step.begin();vector<vector<char> >::const_iterator iter2=stack_note.begin(); vector<string>::const_iterator iter3=remain.begin();vector<string>::const_iterator iter4=production.begin(); gotoxy(8,4);cout<<"步骤";gotoxy(27,4);cout<<"分析栈";gotoxy(44,4);cout<<"剩余输入串";gotoxy(63,4);cout<<"所用产生式";for(int xy=0;iter1!=step.end();iter1++){gotoxy(9,xy+6);cout<<(*iter1);xy=xy+2;}for(xy=0;iter2!=stack_note.end();iter2++) {gotoxy(27,xy+6);for(int icnt=0;icnt<(*iter2).size();icnt++) {cout<<(*iter2)[icnt];}xy=xy+2;}for(xy=0;iter3!=remain.end();iter3++){gotoxy(44,xy+6);cout<<(*iter3);xy=xy+2;}for(xy=0;iter4!=production.end();iter4++){gotoxy(63,xy+6);cout<<(*iter4);xy=xy+2;}cout<<endl<<endl;}void an_process::print2(int x,int y)//简易画线代码,呵呵,画行直线{int i,j;for(int icnt=0;icnt<y-1;icnt++){for(i=0;i<37/y;i++)printf("─");printf("┬");}for(i=0;i<37/y;i++)printf("─");putchar(10);for(icnt=0;icnt<y-1;icnt++) {for(i=0;i<37/y;i++)printf(" ");printf("│");}putchar(10);for(j=0;j<x-1;j++)//画5行直线{for(icnt=0;icnt<y-1;icnt++){for(i=0;i<37/y;i++)printf("─");printf("┼");}for(i=0;i<37/y;i++)printf("─");putchar(10);for(icnt=0;icnt<y-1;icnt++){for(i=0;i<37/y;i++)printf(" ");printf("│");}putchar(10);}for(icnt=0;icnt<y-1;icnt++) {for(i=0;i<37/y;i++)printf("─");printf("┴");}for(i=0;i<37/y;i++)printf("─");putchar(10);}void sentence::read_in(){cout<<"请输入需要分析的句子:\n";cin>>str_input;system("cls");}void main(){table LL1_table;sentence input;an_process A;input.read_in();LL1_table.read();A.analyse(LL1_table,input);A.write_result();}。

相关文档
最新文档