文法推导

题目描述

识别一个合法的表达式,这个表达式只包含加法,乘法和括号。如果能够成功识别,给出其推导过程。这个文法描述如下:

\(E\rightarrow TE'\)
\(E'\rightarrow +TE' \;|\; \epsilon\)
\(T\rightarrow FT'\)
\(T'\rightarrow *FT' \;|\; \epsilon\)
\(F\rightarrow (E)\; |\; \epsilon\)

输入数据

有多组测试数据,每组数据一行,包含一个字符串,字符串只包含有文法中的终端符号。字符串长度不超过100。

输入以#号结束。

输出数据

对于每组数据,如果输入字符串符合文法的定义,输出其最左推导的过程,每步推导占一行;否则,输出“Syntax Error”。每组数据输出结束后再输出一个空行

输入样例

3+5*0
1*2*
#

输出样例

E
TE'
FT'E'
3T'E'
3E'
3+TE'
3+FT'E'
3+5T'E'
3+5*FT'E'
3+5*0T'E'
3+5*0E'
3+5*0

Syntax Error

题目解析

\(E\rightarrow TE'\)
\(E'\rightarrow +TE' \;|\; \epsilon\)
\(T\rightarrow FT'\)
\(T'\rightarrow *FT' \;|\; \epsilon\)
\(F\rightarrow (E)\; |\; id\)

这个文法代表什么意思?E代表expression(表达式)F代表factor(因子)T代表term(项)。这个文法告诉你:

  • 一个表达式是一个多项式,可以表示为很多个项的加和
  • 一个项是一个乘积,可以表示为多个因子相乘
  • 一个因子是一个括起来的表达式或者一个数字

明白了上述文法的含义,我们再来模拟课本上给我们提供的方法(以识别 3+5*0 这一表达式为例):

  1. 初始的语法分析树只包含一个标号为E的节点,输入指针指向3
  2. 首先看到3无法与E匹配,并且E只有一个产生式,所以对E进行展开,展开后的表达式变为TE'
  3. 再看3无法与T进行匹配 (这是最左推导的要求),并且'T'只有一个产生式,对T再进行展开,展开后变为FT'E'
  4. 这时看到3可以和F进行匹配,我们进行匹配,同时输入指针指向3的下一个字符+,匹配完后结果为3T'E'
  5. 这时看到+无法与T'进行匹配,那么此时应该选择展开,还是和空串匹配吗?我们注意到,如果展开T',那么这个+*号是匹配不上的,所以这里我们选择和空产生式进行匹配,匹配后结果就是3E'。(这也就是我们常说的LL(1)文法)

重复上述步骤,直到匹配完整个式子即可,如果出现无法匹配或展开的情况,或者整个字符串已经全部遍历完,但是你的文法中仍然存在无法展开或者匹配的非终结符,那么就是报错。

总的来说,由于题目已经限制是最左推导,所以咬准这一点,每一次都选择最左非终结符进行展开或者匹配,这道题就能顺利完成。

源代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;

//推导函数,输入是待扩展的非终结符和当前处理到的字符
string deduction(string nonterminal, char ch) {
	//printf("当前要匹配的字符是:%c\n\n", ch);

	if (nonterminal == "E") {	//E直接扩展
		return "TE'";
	}

	if (nonterminal == "E'") {	//逐个判断
		if (ch == '+') {		//如果能和加号匹配,则扩展
			return "+TE'";
		}
		else {					//否则认为和空串匹配
			string tmp = "";
			return tmp;
		}
	}

	if (nonterminal == "T") {
		return "FT'";
	}

	if (nonterminal == "T'") {
		if (ch == '*') {
			return "*FT'";
		}
		else {
			string tmp = "";
			return tmp;
		}
	}
	
	if (nonterminal == "F") {
		if (ch == '(') {
			return "(E)";
		}
		else if (ch >= '0' && ch <= '9'){
			string tmp = "";
			return tmp + ch;
		}
	}

	return "error";
}

//获取最左待扩展非终结符的位置(因为是最左推导所以可以这么干)
int get_pos_of_nonterminal(const string &exp_str) {
	int len = exp_str.length();

	for (int i = 0; i < len; i++) {
		if (exp_str[i] == 'E' || exp_str[i] == 'F' || exp_str[i] == 'T') {	//这里不怕有',单独判断即可
			return i;
		}
	}

	return -1;
}

int main() {
	string str;			//输入的要判断的字符串
	string exp_str; //待扩展的字符串
	int str_index;		//课本上例4.29的输入指针,指目前处理到要判断的字符串的第几位
	int str_len;			//输入字符串的长度
	int exp_str_len;		//待扩展字符串的长度
	int exp_str_index;		//当前处理到待扩展字符串的第几位
	vector<string> output_buffer;
	bool flag;

	while (1) {

		cin >> str;		//输入这个字符串

		if (str[0] == '#') {
			break;
		}

		flag = true;
		str_len = str.length();
		output_buffer.clear();
		exp_str = "E";
		output_buffer.push_back(exp_str);
		str_index = 0;
		exp_str_index = 0;

		while (1) {
			int pos = get_pos_of_nonterminal(exp_str), process_len;			//表示现在在处理待扩展字符串的第几位和处理多长的字符串

			if (pos + 1 < exp_str.length() && exp_str[pos + 1] == '\'') {	//判断这个字符后面有没有'
				process_len = 2;
			}
			else {
				process_len = 1;
			}

			//例如3+T'E'
			string exp_result = deduction(exp_str.substr(pos, process_len), str[str_index]);	//扩展这个非终结符
			//扩展后变为3+FT'E'

			if (exp_result == "error") {
				flag = false;
			}

			string left_str = exp_str.substr(0, pos), right_str = exp_str.substr(pos + process_len, exp_str.length() - (pos + process_len));

			exp_str = left_str + exp_result + right_str;

			//cout << exp_str << endl;

			output_buffer.push_back(exp_str);

			//cout << exp_str << endl;

			int next_pos = get_pos_of_nonterminal(exp_str);

			while (exp_str_index < next_pos && str_index < str_len) {	//匹配字符
				if (exp_str[exp_str_index] != str[str_index]) {
					flag = false;
				}
				exp_str_index++;
				str_index++;
			}

			if (exp_str_index == exp_str.length()) {

				if (str_index == str_len) {
					for (auto itor = output_buffer.begin(); itor != output_buffer.end(); itor++) {
						cout << (*itor) << endl;
					}
					break;
				}
				else {
					flag = false;
				}
			}

			if (!flag) {
				puts("Syntax Error");
				break;
			}


		}

		puts("");
	}
	return 0;
}

posted @ 2021-01-02 16:49  随心所欲丶蜻蜓卡兹克  阅读(604)  评论(1)    收藏  举报