语法分析-算术表达式-C++实现
1.1 算术表达式文法规则:
E->TE’
E’->+TE’|e
T->FT’
T’->*FT’|e
F->i|(E)
1.2 符号集:
² First集:
\1. First(F)={ ( , id }
\2. First(T) { ( , id }
\3. First(T’)={ * , ε }
\4. First(E’)={ + , ε }
\5. First(E)=First(T)={ ( , id }
² Follow集
\1. FOLLOW(E’) = { $ , ) }
\2. FOLLOW(T)= { $ , ) , + }
\3. FOLLOW(T’)= { $ , ) , + }
\4. FOLLOW(F)= { $ , + , ) , * }
\5. FOLLOW(E)={ $ , ) }
² Select集
\1. Select(E --> TE’) = { (, i}
\2. Select(E’ --> à +TE) = { + }
\3. Select(E’ --> ε) = { ) , # }
\4. select(T --> FT’) = { (, i}
\5. select(T’ --> *FT’) = { * }
\6. select(T’ --> ε) = { + , ), #}
\7. select(F --> (E)) = { ( }
\8. select(F --> i) = {i}
1.3 预测分析表

1.4 程序设计
我用的是递归下降法,当我得到这个预测分析表后,我为每个非终结符设计一个函数,在内部通过判断读入的非终结符判断该用哪一个产生式进行替换,而主程序中就是最左推导,通过调用最左边的非终结符的递归子程序,让整个过程链式执行下去,最后能得到输入的算术表达式,则识别成功
C++代码:****
#include <iostream>
#include <cstring>
#define INPUTMAX 30
#define RESLUTMAX 100
using namespace std;
char InputString[INPUTMAX]; //保存输入的字符串,用数组更方便,直接更改下标就可以往右扫描下一个
string ResultString[RESLUTMAX]; //保存每一次的中间推导过程
int ptr = 0; //当前输入符号的下标
int Result_Length = 0; //ResultString的长度
/*
递归子程序法我觉得也可以称之为递归的预测分析法,如何选择产生式是看预测分析表的
也就是为每个非终结符设计一个函数,让它自己选择相应的产生式
从开始符号推导的过程中,遇到相应的非终结符就调用其函数
*/
void E();
void T();
void E1();
void F();
void T1();
void UpdateResult(string non_terminal, string replace_str); //用于添加每一次的中间推导式
void ShowResult(); //显示所有中间推导式
void Deal_Production(char production[]); //接受产生式,该函数把它分解成左边和右边,并调用UpdateResult(),防止代码冗余
void Trim(char s[]);
int main() {
cout << "请输入以#结尾的字符串,无空格" << endl;
cin.getline(InputString, INPUTMAX); //如果直接cin>>会遇到第一个空格就返回
Trim(InputString); //去除空格,并看他有没有#结尾
E(); //start
//string在C++中可以直接两个等号判断相等
if (ResultString[Result_Length - 1] == InputString) //如果最后一个推导式就是输入串
cout << "\n接受成功" << endl;
else
cout << "\n不接受";
ShowResult(); //显示推导式
return 0;
}
void E() {
//用if主要是防止无限推导,因为找不到某个字符
if (InputString[ptr] == 'i' || InputString[ptr] == '(') //在预测分析表中只有 i,(, E才有相应产生式
{
Deal_Production("E --> TE'");
T(); //调用所有非终结符的相应的函数
E1();
}
}
void T() {
if (InputString[ptr] == 'i' || InputString[ptr] == '(') {
Deal_Production("T --> FT'");
F();
T1();
}
}
void E1() {
if (InputString[ptr] == '+') {
Deal_Production("E' --> +TE'");
ptr++; //匹配到一个+,扫描下一个输入符号
T(); //一定要记得调用产生式右边所有非终结符的函数,并且按顺序
E1();
} else if (InputString[ptr] == ')' || InputString[ptr] == '#')
Deal_Production("E' --> ε");
}
void T1() {
if (InputString[ptr] == '*') {
Deal_Production("T' --> *FT'");
ptr++;
F();
T1();
} else if (InputString[ptr] == ')' || InputString[ptr] == '#' || InputString[ptr] == '+')
Deal_Production("T' --> ε");
}
void F() {
if (InputString[ptr] == 'i') {
Deal_Production("F --> i");
ptr++;
} else if (InputString[ptr] == '(') {
Deal_Production("F --> (E)");
ptr++;
E();
}
}
void UpdateResult(string non_terminal, string replace_str) {
if (Result_Length == 0) //开始的时候要初始化,加个开始符号
{
string str = non_terminal;
ResultString[Result_Length++] = str;
UpdateResult(non_terminal, replace_str);
} else {
string str = ResultString[Result_Length - 1]; //先获取上次的推导式
int index = str.find(non_terminal); //找到需要替换的终结符的下标
string str1 = str.substr(0, index); //获取终结符前的子串
string str2 = str.substr(index + non_terminal.length()); //获取终结符后的子串
if (replace_str != "ε")
str1 += replace_str; //如果替换为ε,该符号是不用加进推导式中
str1 += str2; //拼接起来
if (str1 + "#" == InputString) //如果推导式后跟#就等于输入串,最好还是不要直接==InputString
str1 += "#"; //那么就在其后面加一个#,方便直接判断相等
ResultString[Result_Length++] = str1; //推导式加进数组中
}
}
void ShowResult() {
cout << "----------------" << endl;
cout << "推导过程:" << endl;
for (int i = 0; i < Result_Length; i++) {
cout << ResultString[i] << endl;
}
}
void Deal_Production(char production[]) {
string str = production; //把数组转化成string可以用很多函数
int front_index = str.find(' '); //第一个空格出现的位置
int back_index = str.find('>');
string str1 = str.substr(0, front_index); //截取左边
string str2 = str.substr(back_index + 2, str2.length() - back_index - 1); //截取后面
cout << production << endl;
UpdateResult(str1, str2);
}
void Trim(char s[]) {
string str = s;
string str1, str2;
while (true) {
int flag = str.find(' ');
if (flag == str.npos) //即没找到空格
break;
str1 = str.substr(0, flag);
str2 = str.substr(flag + 1, str.length() - flag - 1);
str1 += str2;
str = str1;
}
for (int i = 0; i < INPUTMAX; i++)
s[i] = '\0'; //清空
//但是这种复制有缺陷,比如当str长度不如s时,实际上s后面的字符保留着,是直接加了个\0隔离开
//例如 把{1,2,3}复制给{1,2,3,4}就会变成{1,2,3,'\0',4}; 而strlen是\0前面的长度
strcpy(s, str.c_str()); //把string复制给数组是这么写的
if (s[strlen(s) - 1] != '#')
s[strlen(s)] = '#';
}
运行结果:

本文介绍了一种使用C++实现的算术表达式语法分析方法,采用递归下降法结合预测分析表来识别算术表达式的正确性,并提供了详细的程序代码及运行结果。

1047





