编译原理:LL(1)文法 语法分析器(预测分析表法)

设计要求:对于任意输入的一个LL(1)文法,构造其预测分析表,并对指定输入串分析其是否为该文法的句子。

思路:首先实现集合FIRST(X)构造算法和集合FOLLOW(A)构造算法,再根据FIRST和FOLLOW集合构造出预测分析表,并对指定的句子打印出分析栈的分析过程,判断是否为该文法的句子。


指定文法:

//文法
E->TK
K->+TK
K->$
T->FM
M->*FM
M->$
F->i
F->(E)


对于输入串i+i*i# ,这里我们先给出实验结果截图:



这个实验花了我一天的时间,主要难点在于first集和follow集中的递归判断部分,其他的只要按照下面的判断流程走就可以了。


(1)求FIRST集的算法思想

如果产生式右部第一个字符为终结符,则将其计入左部first集
 如果产生式右部第一个字符为非终结符
 ->求该非终结符的first集
 ->将该非终结符的非$first集计入左部的first集
->若存在$,则将指向产生式的指针右移
->若不存在$,则停止遍历该产生式,进入下一个产生式
->若已经到达产生式的最右部的非终结符,则将$加入左部的first集
 处理数组中重复的first集中的终结符


(2)求FOLLOW集的算法思想

 对于文法G中每个非终结符A构造FOLLOW(A)的办法是,连续使用下面的规则,直到每个FOLLOW不在增大为止.
(1) 对于文法的开始符号S,置#于FOLLOW(S)中;
(2) 若A->aBb是一个产生式,则把FIRST(b)\{ε}加至FOLLOW(B)中;
(3) 若A->aB是一个产生式,或A->aBb是一个产生式而b=>ε(即ε∈FIRST(b))则把FOLLOW(A)加至FOLLOW(B)中


(3)生成预测分析表的算法思想

构造分析表M的算法是: 
 (1) 对文法G的每个产生式A->a执行第二步和第三步;
 (2) 对每个终结符a∈FIRST(a),把A->a加至M[A,a]中;
 (3) 若ε∈FIRST(a),则把任何b∈FOLLOW(A)把A->a加至M[A,b]中;
 (4) 把所有无定义的M[A,a]标上出错标志.


(4)对符号串的分析过程

 预测分析程序的总控程序在任何时候都是按STACK栈顶符号X和当前的输入符号行事的,对于任何(X,a),总控程序
 每次都执行下述三种可能的动作之一;
(1) 若X=a=”#”,则宣布分析成功,停止分析过程.
(2) 若X=a≠”#”,则把X从STACK栈顶逐出,让a指向下一个输入符号.
(3) 若X是一个非终结符,则查看分析表M,若M[A,a]中存放着关于X的一个产生式,那么,首先把X逐出STACK栈顶,然后
把产生式的右部符号串按反序一一推进STACK栈(若右部符号为ε,则意味着不推什么东西进栈).在把产生式的右部
符号推进栈的同时应做这个产生式相应得语义动作,若M[A,a]中存放着”出错标志”,则调用出错诊察程序ERROR.
 

在我的程序中,Base类为基类,负责求出FIRST和FOLLOW集;TableStack继承Base类,求出预测分析表和显示符号串的分析过程。


Base.h

struct node
{
	char left;
	string right;
};

class Base
{
protected:
	int T;
	node analy_str[100]; //输入文法解析

	set<char> first_set[100];//first集
	set<char> follow_set[100];//follow集
	vector<char> ter_copy; //去$终结符
	vector<char> ter_colt;//终结符
	vector<char> non_colt;//非终结符

public:
	Base() :T(0){}
	bool isNotSymbol(char c);
	int get_index(char target);//获得在终结符集合中的下标
	int get_nindex(char target);//获得在非终结符集合中的下标
	void get_first(char target); //得到first集合
	void get_follow(char target);//得到follow集合
	void inputAndSolve(); //处理得到first和follow
	void display(); //显示

};

TableStack.h

class TableStack :public Base
{
protected:
	vector<char> to_any; //分析栈
	vector<char> left_any;//剩余输入串
	int tableMap[100][100];//预测表
public:
	TableStack(){ memset(tableMap, -1, sizeof(tableMap)); }

	void get_table(); //得到预测表
	void analyExp(string s); //分析栈的处理
	void print_out();//输出
	void getAns(); //结合处理
};

接下来只列出FIRST集中的核心递归过程,有详细注释:

void Base::get_first(char target)
{
	int tag = 0;
	int flag = 0;
	for (int i = 0; i<T; i++)
	{
		if (analy_str[i].left == target)//匹配产生式左部
		{
			if (!isNotSymbol(analy_str[i].right[0]))//对于终结符,直接加入first
			{
				first_set[get_index(target)].insert(analy_str[i].right[0]);
			}
			else
			{
				for (int j = 0; j<analy_str[i].right.length(); j++)
				{
					if (!isNotSymbol(analy_str[i].right[j]))//终结符结束
					{
						first_set[get_index(target)].insert(analy_str[i].right[j]);
						break;
					}
					get_first(analy_str[i].right[j]);//递归
					//	cout<<"curr :"<<analy_str[i].right[j];
					set<char>::iterator it; 
					for (it = first_set[get_index(analy_str[i].right[j])].begin(); it != first_set[get_index(analy_str[i].right[j])].end(); it++)
					{
						if (*it == '$')
							flag = 1;
						else
							first_set[get_index(target)].insert(*it);//将FIRST(Y)中的非$就加入FIRST(X)
					}
					if (flag == 0)
						break;
					else
					{
						tag += flag;
						flag = 0;
					}
				}
				if (tag == analy_str[i].right.length())//所有右部first(Y)都有$,将$加入FIRST(X)中
					first_set[get_index(target)].insert('$');
			}
		}
	}

}

这个语法分析的全部工程代码在我的 Github 上,欢迎大家star学习,希望可以给大家带来帮助。




  • 68
    点赞
  • 344
    收藏
    觉得还不错? 一键收藏
  • 34
    评论
### 回答1: 语法设计是指在编程语言中定义语法规则的过程。基于LL(1)文法预测分析表法是一种常用的语法设计方法,它可以通过构建预测分析表来判断输入的语句是否符合语法规则。 LL(1)文法是一种上下文无关文法,它具有以下特点:每个非终结符的每个产生式都具有不同的左部符号;每个非终结符的每个产生式的右部符号串的首符号都不相同;对于任意一个非终结符A和终结符a,最多只有一个产生式的右部符号串以a开头。 预测分析表是一个二维表格,其中行表示文法中的非终结符,列表示文法中的终结符。表格中的每个单元格都包含一个产生式,用于指示在分析输入时应该采取哪个产生式。 通过构建预测分析表,可以快速地判断输入的语句是否符合语法规则。如果输入的语句可以被预测分析表中的某个产生式所匹配,则说明该语句符合语法规则;否则,该语句就不符合语法规则。 总之,基于LL(1)文法预测分析表法是一种简单有效的语法设计方法,它可以帮助程序员快速地编写出符合语法规则的程序。 ### 回答2: 语法设计是编译原理中的一项重要内容,其主要目的是通过定义语法规则来描述程序代码的结构和语义。基于LL(1)文法预测分析表法是一种常用的语法设计方法,它可以帮助我们实现编译器中的语法分析器LL(1)文法是指左递归消除后的左公因子分解后形成的,而预测分析表法则是指根据分析词法单元和文法规则得到的一张表格。这张表格可以由预测分析算法自动生成,用于在语法分析的过程中实现自动推导。 预测分析表法的核心是构造预测分析表,该表包含了符号和文法规则之间的映射关系。在语法分析的过程中,将输入的符号(token)与预测分析表中的相应表项进行比较,然后根据表项中规定的文法规则进行推导。 预测分析表的构造过程分为两个步骤:首先,需要对每个非终结符号构造一个“select 集合”,该集合包含该非终结符号每个产生式的预测符号(可以是终结符或空符);其次,需要构造一个预测分析表,该表的行为非终结符号,列为终结符号或特殊符号(如$),表中填写的是该非终结符号对应的“select 集合”所包含的终结符号或空符。 预测分析表生成后,就可以利用它对文本进行语法分析。在分析过程中,用栈来存储分析栈,从左向右读入源程序符号串,根据栈顶符号和输入符号,使用预测分析表中的规则生成产生式,然后用分析栈替换符号串,直到规约结束或输入串错误。 总之,基于LL(1)文法预测分析表法可以减少编译器语法分析的复杂度,提高编译效率。但是,由于该方法对语法规则有一定的限制,因此在实际编程中需要根据编译器设计的实际需求选择合适的语法分析方法。 ### 回答3: 语法设计是编程语言领域非常重要的一个方向。基于LL(1)文法预测分析表法是一种常用的语法分析方法,它能够对源程序进行有效地分析,具有一定的实用性和普适性。 LL(1)文法是一种上下文无关文法,其中的每个产生式都可以归约为一个非终结符开始的左侧最长公共前缀与一个终结符构成的串,这个终结符可以通过预测分析表来确定。预测分析表中包含了产生式的各种组合情况及其对应的动作,包括移进、归约和出错等情况。因此,预测分析表法不需要借助语法分析器就可以进行语法分析,这是该方法的优点之一。 预测分析表法主要分为两个阶段:首先进行文法预处理,然后是分析过程。预处理的任务是将LL(1)文法转化为预测分析表,分为两个步骤:消除左递归和提取左公因子。这使得文法更符合LL(1)语法分析的要求并更容易生成预测分析表。 分析过程是在语法树的基础上进行的。语法树是由词法解析器生成的一个树形结构,在预测分析表的帮助下,从根节点开始匹配符号串,分析器根据预测分析表的内容进行相应的移进或归约操作,并按照规定的规则生成分析树,最终得到分析结果。由此可见,预测分析表法涵盖了从编译器到语言元素的底层,可以实现对编程语言的高效分析和处理。 总之,在编程语言设计中,语法设计是至关重要的一个方面。基于LL(1)文法预测分析表法是一种应用广泛的语法分析方法,具有一定的实用性和普适性。预测分析表法的实现需要进行文法预处理和分析过程,较为复杂。但是,通过预处理和分析过程的规则设计和优化,可以优化LL(1)文法语法分析,加快编译速度,提高编程效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 34
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值