导航:首页 > 源码编译 > 编译原理句型分析任务

编译原理句型分析任务

发布时间:2022-09-12 15:14:41

1. 编译原理实现判断是不是一个文法的句子

构造LL(1)语法分析程序,任意输入一个文法符号串,并判断它是否为文法的一个句子。程序要求为该文法构造预测分析表,并按照预测分析算法对输入串进行语法分析,判别程序是否符合已知的语法规则,如果不符合(编译出错),则输出错误信息。

2. 提问 编译原理问题(高分)

词法分析 的作用是把输入的源语句转化成单词形式
第五个最右推导没给要推出的句子 如果是 cbb 那过程也不对
E->CB

C->c

B->b

最右推导的分析为

1 CB

2 Cb

3 cb
你给的文法有问题吧,最右推导通俗的说 就是只按照最右边的非终结符推导

你这些都是要干什么的题,如果要考试,后面那几道的类型几乎必考!!!

3. 编译原理中词法分析和语法分析的任务分别是什么

在编译原理中,语法规则和词法规则不同之处在于:规则主要识别单词,而语法主要识别多个单词组成的句子。
词法分析和词法分析程序:
词法分析阶段是编译过程的第一个阶段。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。词法分析程序实现这个任务。词法分析程序可以使用lex等工具自动生成。
语法分析(Syntax analysis或Parsing)和语法分析程序(Parser)
语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确.源程序的结构由上下文无关文法描述.
语义分析(Syntax analysis)
语义分析是编译过程的一个逻辑阶段. 语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查, 进行类型审查.语义分析将审查类型并报告错误:不能在表达式中使用一个数组变量,赋值语句的右端和左端的类型不匹配.

4. 编译原理实验求助

1)定义
所有token或者叫单词的有限自动机。
2)将有限自动机用代码实现。
3)写分析程序,利用你定义的有限自动机来识别所有的“单词”。并将识别出来的单词的相关信息,如名称,位置,类别等记录在相关的数据结构中。

5. 编译原理LR分析法中的SLR(1)分析表和LR分析过程、语法树怎么求

第二题和第三题拿去,刚做的:


由B->cAa|c就可知该文法不是LR(0)文法了


6. 编译原理-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(α) 包括空串ε,那么这个产生式是不是间接左递归。

7. 编译原理试题·

Lex和Yacc应用方法(一).初识Lex
草木瓜 20070301
Lex(Lexical Analyzar 词法分析生成器),Yacc(Yet Another Compiler Compiler
编译器代码生成器)是Unix下十分重要的词法分析,语法分析的工具。经常用于语言分
析,公式编译等广泛领域。遗憾的是网上中文资料介绍不是过于简单,就是跳跃太大,
入门参考意义并不大。本文通过循序渐进的例子,从0开始了解掌握Lex和Yacc的用法。

一.Lex(Lexical Analyzar) 初步示例
先看简单的例子(注:本文所有实例皆在RetHat linux下完成):
一个简单的Lex文件 exfirst.l 内容:
%{
#include "stdio.h"
%}
%%
[\n] ;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
命令行下执行命令flex解析,会自动生成lex.yy.c文件:
[root@localhost liweitest]flex exfirst.l
进行编译生成parser可执行程序:
[root@localhost liweitest]cc -o parser lex.yy.c -ll
[注意:如果不加-ll链结选项,cc编译时会出现以下错误,后面会进一步说明。]
/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/../../../crt1.o(.text+0x18): In function `_start':
../sysdeps/i386/elf/start.S:77: undefined reference to `main'
/tmp/cciACkbX.o(.text+0x37b): In function `yylex':
: undefined reference to `yywrap'
/tmp/cciACkbX.o(.text+0xabd): In function `input':
: undefined reference to `yywrap'
collect2: ld returned 1 exit status

创建待解析的文件 file.txt:
title
i=1+3.9;
a3=909/6
bcd=4%9-333
通过已生成的可执行程序,进行文件解析。
[root@localhost liweitest]# ./parser < file.txt
Var : title
Var : i
Unknown : =
Int : 1
Op : +
Float : 3.9
Unknown : ;
Var : a3
Unknown : =
Int : 909
Op : /
Int : 6
Var : bcd
Unknown : =
Int : 4
Op : %
Int : 9
Op : -
Int : 333
到此Lex用法会有个直观的了解:
1.定义Lex描述文件
2.通过lex,flex工具解析成lex.yy.c文件
3.使用cc编译lex.yy.c生成可执行程序

再来看一个比较完整的Lex描述文件 exsec.l :

%{
#include "stdio.h"
int linenum;
%}
%%
title showtitle();
[\n] linenum++;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
showtitle()
{
printf("----- Lex Example -----\n");
}
int main()
{
linenum=0;
yylex(); /* 进行分析 */
printf("\nLine Count: %d\n",linenum);
return 0;
}
int yywrap()
{
return 1;
}
进行解析编译:
[root@localhost liweitest]flex exsec.l
[root@localhost liweitest]cc -o parser lex.yy.c
[root@localhost liweitest]./parser < file.txt
----- Lex Example -----
Var : i
Unknown : =
Int : 1
Op : +
Float : 3.9
Unknown : ;
Var : a3
Unknown : =
Int : 909
Op : /
Int : 6
Var : bcd
Unknown : =
Int : 4
Op : %
Int : 9
Op : -
Int : 333
Line Count: 4
这里就没有加-ll选项,但是可以编译通过。下面开始着重整理下Lex描述文件.l。

二.Lex(Lexical Analyzar) 描述文件的结构介绍
Lex工具是一种词法分析程序生成器,它可以根据词法规则说明书的要求来生成单词识
别程序,由该程序识别出输入文本中的各个单词。一般可以分为<定义部分><规则部
分><用户子程序部分>。其中规则部分是必须的,定义和用户子程序部分是任选的。

(1)定义部分
定义部分起始于 %{ 符号,终止于 %} 符号,其间可以是包括include语句、声明语句
在内的C语句。这部分跟普通C程序开头没什么区别。
%{
#include "stdio.h"
int linenum;
%}
(2) 规则部分
规则部分起始于"%%"符号,终止于"%%"符号,其间则是词法规则。词法规则由模式和
动作两部分组成。模式部分可以由任意的正则表达式组成,动作部分是由C语言语句组
成,这些语句用来对所匹配的模式进行相应处理。需要注意的是,lex将识别出来的单
词存放在yytext[]字符数据中,因此该数组的内容就代表了所识别出来的单词的内容。
类似yytext这些预定义的变量函数会随着后面内容展开一一介绍。动作部分如果有多
行执行语句,也可以用{}括起来。
%%
title showtitle();
[\n] linenum++;
[0-9]+ printf("Int : %s\n",yytext);
[0-9]*\.[0-9]+ printf("Float : %s\n",yytext);
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
[\+\-\*\/\%] printf("Op : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
A.规则部分的正则表达式
规则部分是Lex描述文件中最为复杂的一部分,下面列出一些模式部分的正则表达式字
符含义:
A-Z, 0-9, a-z 构成模式部分的字符和数字。
- 指定范围。例如:a-z 指从 a 到 z 之间的所有字符。
\ 转义元字符。用来覆盖字符在此表达式中定义的特殊意义,
只取字符的本身。

[] 表示一个字符集合。匹配括号内的任意字符。如果第一个字
符是^那么它表示否定模式。例如: [abC] 匹配 a, b, 和C
的任何一个。

^ 表示否定。
* 匹配0个或者多个上述模式。
+ 匹配1个或者多个上述模式。
? 匹配0个或1个上述模式。
$ 作为模式的最后一个字符时匹配一行的结尾。
{ } 表示一个模式可能出现的次数。 例如: A{1,3} 表示 A 可
能出现1次或3次。[a-z]{5} 表示长度为5的,由a-z组成的
字符。此外,还可以表示预定义的变量。

. 匹配任意字符,除了 \n。
( ) 将一系列常规表达式分组。如:{Letter}({Letter}|{Digit})*
| 表达式间的逻辑或。
"一些符号" 字符的字面含义。元字符具有。如:"*" 相当于 [\*]。
/ 向前匹配。如果在匹配的模式中的"/"后跟有后续表达式,
只匹配模版中"/"前面的部分。如:模式为 ABC/D 输入 ABCD,
时ABC会匹配ABC/D,而D会匹配相应的模式。输入ABCE的话,
ABCE就不会去匹配ABC/D。

B.规则部分的优先级

规则部分具有优先级的概念,先举个简单的例子:

%{
#include "stdio.h"
%}
%%
[\n] ;
A {printf("ONE\n");};
AA {printf("TWO\n");};
AAAA {printf("THREE\n");};
%%
此时,如果输入内容:
[root@localhost liweitest]# cat file1.txt
AAAAAAA
[root@localhost liweitest]# ./parser < file1.txt
THREE
TWO
ONE
Lex分析词法时,是逐个字符进行读取,自上而下进行规则匹配的,读取到第一个A字符
时,遍历后发现三个规则皆匹配成功,Lex会继续分析下去,读至第五个字符时,发现
"AAAA"只有一个规则可用,即按行为进行处理,以此类推。可见Lex会选择最长的字符
匹配规则。
如果将规则
AAAA {printf("THREE\n");};
改为
AAAAA {printf("THREE\n");};
./parser < file1.txt 输出结果为:
THREE
TWO

再来一个特殊的例子:
%%
title showtitle();
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
%%
并输入title,Lex解析完后发现,仍然存在两个规则,这时Lex只会选择第一个规则,下面
的则被忽略的。这里就体现了Lex的顺序优先级。把这个例子稍微改一下:
%%
[a-zA-Z][a-zA-Z0-9]* printf("Var : %s\n",yytext);
title showtitle();
%%
Lex编译时会提示:warning, rule cannot be matched.这时处理title字符时,匹配
到第一个规则后,第二个规则就无效了。
再把刚才第一个例子修改下,加深下印象!
%{
#include "stdio.h"
%}
%%
[\n] ;
A {printf("ONE\n");};
AA {printf("TWO\n");};
AAAA {printf("THREE\n");};
AAAA {printf("Cannot be executed!");};
./parser < file1.txt 显示效果是一样的,最后一项规则肯定是会忽略掉的。

C.规则部分的使用变量
且看下面示例:
%{
#include "stdio.h"
int linenum;
%}
int [0-9]+
float [0-9]*\.[0-9]+
%%
{int} printf("Int : %s\n",yytext);
{float} printf("Float : %s\n",yytext);
. printf("Unknown : %c\n",yytext[0]);
%%
在%}和%%之间,加入了一些类似变量的东西,注意是没有;的,这表示int,float分
别代指特定的含义,在两个%%之间,可以通过{int}{float}进行直接引用,简化模
式定义。

(3) 用户子程序部分
最后一个%%后面的内容是用户子程序部分,可以包含用C语言编写的子程序,而这些子
程序可以用在前面的动作中,这样就可以达到简化编程的目的。这里需要注意的是,
当编译时不带-ll选项时,是必须加入main函数和yywrap(yywrap将下后面说明)。如:
...
%%
showtitle()
{
printf("----- Lex Example -----\n");
}
int main()
{
linenum=0;
yylex(); /* 进行Lex分析 */
printf("\nLine Count: %d\n",linenum);
return 0;
}
int yywrap()
{
return 1;
}

三.Lex(Lexical Analyzar) 一些的内部变量和函数
内部预定义变量:
yytext char * 当前匹配的字符串
yyleng int 当前匹配的字符串长度
yyin FILE * lex当前的解析文件,默认为标准输出
yyout FILE * lex解析后的输出文件,默认为标准输入
yylineno int 当前的行数信息
内部预定义宏:
ECHO #define ECHO fwrite(yytext, yyleng, 1, yyout) 也是未匹配字符的
默认动作

内部预定义的函数:
int yylex(void) 调用Lex进行词法分析
int yywrap(void) 在文件(或输入)的末尾调用。如果函数的返回值是1,就停止解
析。 因此它可以用来解析多个文件。代码可以写在第三段,这
样可以解析多个文件。 方法是使用 yyin 文件指针指向不同的
文件,直到所有的文件都被解析。最后,yywrap() 可以返回1
来表示解析的结束。

lex和flex都是解析Lex文件的工具,用法相近,flex意为fast lexical analyzer generator。
可以看成lex的升级版本。

相关更多内容就需要参考flex的man手册了,十分详尽。

四.关于Lex的一些综述
Lex其实就是词法分析器,通过配置文件*.l,依据正则表达式逐字符去顺序解析文件,
并动态更新内存的数据解析状态。不过Lex只有状态和状态转换能力。因为它没有堆栈,
它不适合用于剖析外壳结构。而yacc增加了一个堆栈,并且能够轻易处理像括号这样的
结构。Lex善长于模式匹配,如果有更多的运算要求就需要yacc了。

8. 一个编译原理问题

首先写出指定句型的规范推导:

S→(L)→(L,S)→(L,(L))→(L,(S))→(L,(a))→(S,(a))

然后画出分析树如下图

根据分析树的叶子结点可以找出该句型的所有短语:

aS(a)S,(a)(S,(a))

直接短语,就是经过一次非终结符替换得到的短语:

aS没了

句柄就是最左直接短语,要进行规约的部分,根据分析树我们找到最左直接短语为:

S

9. 编译原理这科里词法分析器的主要任务是什么单词常分为哪几类识别出的单词在编译程序中如何表示

1、识别出源程序中的各个单词符号,并转换成内部编码形式
2、删除无用的空白字符回车字符以及其他非实质性字符
3、删除注释
4、进行词法检查,报告所发现的错误。

阅读全文

与编译原理句型分析任务相关的资料

热点内容
卡尔曼滤波算法书籍 浏览:766
安卓手机怎么用爱思助手传文件进苹果手机上 浏览:841
安卓怎么下载60秒生存 浏览:800
外向式文件夹 浏览:233
dospdf 浏览:428
怎么修改腾讯云服务器ip 浏览:385
pdftoeps 浏览:490
为什么鸿蒙那么像安卓 浏览:733
安卓手机怎么拍自媒体视频 浏览:183
单片机各个中断的初始化 浏览:721
python怎么集合元素 浏览:478
python逐条解读 浏览:829
基于单片机的湿度控制 浏览:496
ios如何使用安卓的帐号 浏览:880
程序员公园采访 浏览:809
程序员实战教程要多长时间 浏览:972
企业数据加密技巧 浏览:132
租云服务器开发 浏览:811
程序员告白妈妈不同意 浏览:333
攻城掠地怎么查看服务器 浏览:600