要求:给出任意算术表达式,例如:-0.5-2*((-1.25+6)*2-(3-2)*-2),1要做到语法检测,2要算出其值。
思路:根据编译原理,在词法分析阶段通过自定义DFA来对表达式进行切词,按顺序分割出操作数和操作符。然后把词法分析的结果作为入参传入语法分析过程,检测语法是否正确。在词法分析和语法分析都未检测到错误之后执行表达式求值运算。表达式求值的过程中用到两个栈,分别是专门存放操作数的操作数栈和专门存放操作符的操作符栈。操作符栈开始有一项特殊处理就是先将字符“=”压栈,以便最后可以判断运算过程是否即将结束。由于在表达式中的操作符{“+”,“-”, “*”,“/”,“(", ")”, "="}之间是存在优先级关系的,所以在封装算术表达式运算的类中会定义一个算符优先表,运算的过程也就是基于算符优先表和两个栈的基础上进行的。
另外在词法分析过程中为了方便DFA的定义区分“-”是操作符还是操作数中的负数的一部分,所以先要对算术表达式进行一次规范化调整。例如原先的算术表达式:"-5-2.508",调整后变成"-5+ (-2.508)="。
在分词阶段的DFA的定义(如果需要扩展的话可以自己随意扩展):
有穷字母表:{'0..9', '+', '-', '*', '/', '(', ')', '='}
有限状态集:{0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, OPERAND=>(100), OPERATOR=>(101)}
初态:0
终态:{OPERAND=>(100), OPERATOR=>(101)}
单值映射关系:
(0, '1..9')-->1 (0, '-')-->4 (0, '0')-->5
(0, '(')-->6
(0, ')')-->7
(0, '+')-->8
(0, '*')-->10
(0, '/')-->11
(1, '0..9)-->1
(1, '.')-->2
(1, {'+','-','*','/',')','='})-->OPERAND
(2, '0..9')-->3
(3, '0..9')-->3
(3, {'+','-','*','/',')','='})-->OPERAND
(4, '1..9')-->1
(4, '(')-->OPERATOR
(4, '0')-->5
(5, '.')-->2
(5, {'+','-','*','/',')','='})-->OPERAND
(6, {'0..9', '-', '(', ')'})-->OPERATOR
(7, {'+','-','*','/',')','='})-->OPERATOR
(8, {'0..9', '('})-->OPERATOR
(10, {'0..9', '('})-->OPERATOR
(11, {'0..9', '('})-->OPERATOR
在表达式运算过程中的算符优先表的定义如下:
| "+", "-", "*", "/", "(", ")", "=" |
|-----------------------------------|
"+" | '>', '>', '<', '<', '<', '>', '>' |
"-" | '>', '>', '<', '<', '<', '>', '>' |
"*" | '>', '>', '>', '>', '<', '>', '>' |
"/" | '>', '>', '>', '>', '<', '>', '>' |
"(" | '<', '<', '<', '<', '<', '=', ' ' |
")" | '>', '>', '>', '>', ' ', '>', '>' |
"=" | '<', '<', '<', '<', '<', ' ', '=' |
|-----------------------------------|
语法分析阶段的文法定义:
原始的贴近人类思维的文法产生式:
E->E+F|E-F|E*F|E/F|F
F->(E)|i
很容易得出消除左递归后的文法产生式定义如下:
E->FE'
E'->+FE'|-FE'|*FE'|/FE'|空
F->(E)|i
其中非终结符E是表达式,非终结符F是表达式中的因子。
终结符:{'+', '-', '*', '/', '(', ')','i'},其中i是操作数。
开始符是E
目前本程序采用了两种语法分析方式,分别是有递归的预测分析和非递归的预测分析。具体的分析思路在代码中注解了详细分析步骤。
程序是用c++写的。以下从入口程序开始逐步贴出程序源码。源码在github上的地址:https://github.com/querenjie/ArithmeticExpression.git
ArithmeticExpression.cpp:
#include
#include "MathExpressService.h"
#include
using namespace std;
int main()
{
MathExpressService mathExpressService;
//算术表达式字符串
string expression = "-0.5-2*((-1.25+6)*2-(3-2)*-2)"; //这个可以正常运算
//string expression = "-0.5-2*(-1.25+6)*2-(3-2)*-2)"; //这会引起语法分析错误
//string expression = "-0.5//-2*(-1.25)"; //这个通不过词法分析
/**********************************************************************************************
//检查括号是否匹配。然而这个没必要调用,在后面的语法分析阶段也能检测出来,而且可扩展性更强。
bool isExpressOK = mathExpressService.checkParenthesesPair(expression);
if (!isExpressOK) {
cout << "---------------------------------------------------------" << endl;
cout << "表达式中括号不匹配。" << endl;
exit(1);
}
**********************************************************************************************/
//规范化算术表达式
string normalizedExpression = mathExpressService.getNormalizedExpression(expression);
cout << "---------------------------------------------------------" << endl;
cout << "原先的算术表达式:" << expression << endl;
cout << "规范化后的算术表达式:" << normalizedExpression << endl;
//获取词法分析之后的结果
list<LexicalAnalysisWordAndType> lexicalAnalysisWordAndTypeList = mathExpressService.getLexicalAnalysisResult(normalizedExpression);
cout << "---------------------------------------------------------" << endl;
cout << "词法分析的结果:" << endl;
if (lexicalAnalysisWordAndTypeList.size() == 0) {
cout << "因为有错无法所以词法分析无结果。" << endl;
} else {
for (list<LexicalAnalysisWordAndType>::iterator i = lexicalAnalysisWordAndTypeList.begin(); i != lexicalAnalysisWordAndTypeList.end(); i++) {
cout << i->getWord() << "\t" << i->getType() << endl;
}
}
//检查语法是否正常。(根据编译原理语法分析阶段的基于文法产生式分析语法)
cout << "-------------------开始语法分析--------------------------------------" << endl;
if (lexicalAnalysisWordAndTypeList.size() == 0) {
cout << "因为词法分析无结果,所以无需进行语法分析。" << endl;
}
else {
bool isSyntaxOK = mathExpressService.checkSyntax(lexicalAnalysisWordAndTypeList);
if (isSyntaxOK) {
cout << "语法检测正确" << endl;
}
else {
cout << "语法错误" << endl;
}
}
//表达式求值
string strResult = mathExpressService.calculateExpression(lexicalAnalysisWordAndTypeList);
cout << "---------------------------------------------------------" << endl;
cout << "表达式求值的结果:" << normalizedExpression << strResult << endl;
}
MathExpressService.h:
#pragma once
#include
#include "MathExpressExecutor.h"
#include "LexicalAnalysisPhase.h"
#include "LexicalAnalysisWordAndType.h"
#include
#include
#include "GlobalConstVars.h"
#include "SyntaxAnalysisPhase.h"
#include "SyntaxAnalysisPhase2.h"
using namespace std;
class MathExpressService {
public:
/**
* 获取规范化后的算术表达式
*/
string getNormalizedExpression(string mathExpression);
/**
* 获取词法分析的结果
* 入参是规范化之后的算术表达式字符串
*/
list<LexicalAnalysisWordAndType> getLexicalAnalysisResult(string normalizedExpression);
/**
* 这个方法只是简单检查表达式中的括号匹配情况。
* 这个还不能算真正意义上的语法检测。真正的语法检测在语法分析阶段的类中有定义。
* 入参为算术表达式字符串
*/
bool checkParenthesesPair(string expression);
/*
* 根据词法分析的输出结果计算表达式的值
*/
string calculateExpression(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList);
/**
* 检查语法是否有问题
*/
bool checkSyntax(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList);
};
string MathExpressService::getNormalizedExpression(string mathExpression) {
const char* ccMathExpression = mathExpression.c_str();
LexicalAnalysisPhase lexicalAnalysisPhase;
char* normalizedExpression;
int lenOfNormalizedExpression = lexicalAnalysisPhase.normalizeMathExpression(ccMathExpression, normalizedExpression);
/**************************************************************************
cout << "规范化后的算术表达式:" << endl;
for (int i = 0; i < lenOfNormalizedExpression; i++) {
cout << normalizedExpression[i];
}
cout << endl;
**************************************************************************/
string strNormalizedExpression(normalizedExpression, lenOfNormalizedExpression);
return strNormalizedExpression;
}
list<LexicalAnalysisWordAndType> MathExpressService::getLexicalAnalysisResult(string normalizedExpression) {
const char* ccNormalizedExpression = normalizedExpression.c_str();
list<LexicalAnalysisWordAndType> wordAndTypeResultList;
LexicalAnalysisPhase lexicalAnalysisPhase;
Status isLexicalAnalysisProcessOK = lexicalAnalysisPhase.generateLexicalAnalysisResultTableList(ccNormalizedExpression, wordAndTypeResultList);
if (isLexicalAnalysisProcessOK == ERROR) {
cout << "未能通过词法分析阶段" << endl;
list<LexicalAnalysisWordAndType> wordAndTypeResultListEmpty;
return wordAndTypeResultListEmpty;
}
return wordAndTypeResultList;
}
bool MathExpressService::checkParenthesesPair(string expression) {
LexicalAnalysisPhase lexicalAnalysisPhase;
const char* ccExpression = expression.c_str();
if (lexicalAnalysisPhase.checkParenthesesPair(ccExpression) == ERROR) {
cout << "检测到括号不匹配" << endl;
return false;
}
return true;
}
string MathExpressService::calculateExpression(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList) {
MathExpressExecutor mathExpressExecutor;
return mathExpressExecutor.evaluateExpression(lexicalAnalysisResultList);
}
bool MathExpressService::checkSyntax(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList) {
cout << "以下是执行有递归的预测分析:" << endl;
SyntaxAnalysisPhase syntaxAnalysisPhase(lexicalAnalysisResultList);
bool isSyntaxOK = syntaxAnalysisPhase.checkSyntax();
if (isSyntaxOK) {
cout << "通过有递归的预测分析的语法分析" << endl;
}
else {
cout << "未能通过有递归的预测分析的语法分析" << endl;
}
cout << "以下是执行非递归的预测分析:" << endl;
SyntaxAnalysisPhase2 syntaxAnalysisPhase2(lexicalAnalysisResultList);
bool isSyntaxOK2 = syntaxAnalysisPhase2.checkSyntax();
if (isSyntaxOK2) {
cout << "通过非递归的预测分析的语法分析" << endl;
}
else {
cout << "未能通过非递归的预测分析的语法分析" << endl;
}
return isSyntaxOK && isSyntaxOK2;
}
GlobalConstVars.h:
#pragma once
const int TRUE = 1;
const int FALSE = 0;
const int OK = 1;
const int ERROR = 0;
const int INFEASIBLE = -1;
typedef int Status;
LexicalAnalysisWordAndType.h:
#pragma once
#include
#include
using namespace std;
class LexicalAnalysisWordAndType {
private:
string word; //单词
string type; //类型
public:
void setWord(string word) {
this->word = word;
}
void setType(string type) {
this->type = type;
}
string getWord() {
return this->word;
}
string getType() {
return this->type;
}
};
MathExpressExecutor.h:
#pragma once
#include
#include
#include
#include "GlobalConstVars.h"
#include
#include "LexicalAnalysisWordAndType.h"
#include
using namespace std;
/**
* 这个类是定义了计算表达式值的时候的必要数据和操作。
* 所谓的计算表达式值的时候是指在词法分析和语法分析之后的时候,所以此类中不包括词法分析和语法分析的过程中的函数定义。
*
*/
class MathExpressExecutor {
private:
string operators[7] = { "+", "-", "*", "/", "(", ")", "=" }; //表达式中的操作符。这就是算符优先表中对应的行头和列头,算符优先表是个二维的表格。
/**
* 算符优先关系表,其行和列就是operators数组。至于这个关系表是如何产生的这要参考编译原理的相关制作算符优先表的内容,过程比较复杂,在此不作解释。
* 算符优先表的内容如下所示:
* | "+", "-", "*", "/", "(", ")", "=" |
* |-----------------------------------|
* "+" | '>', '>', '<', '<', '<', '>', '>' |
* "-" | '>', '>', '<', '<', '<', '>', '>' |
* "*" | '>', '>', '>', '>', '<', '>', '>' |
* "/" | '>', '>', '>', '>', '<', '>', '>' |
* "(" | '<', '<', '<', '<', '<', '=', ' ' |
* ")" | '>', '>', '>', '>', ' ', '>', '>' |
* "=" | '<', '<', '<', '<', '<', ' ', '=' |
* |-----------------------------------|
*/
const char precedences[7][7] = {
{ '>', '>', '<', '<', '<', '>', '>'},
{ '>', '>', '<', '<', '<', '>', '>'},
{ '>', '>', '>', '>', '<', '>', '>'},
{ '>', '>', '>', '>', '<', '>', '>'},
{ '<', '<', '<', '<', '<', '=', ' '},
{ '>', '>', '>', '>', ' ', '>', '>'},
{ '<', '<', '<', '<', '<', ' ', '='}
};
int locationOfCharInOperators(string operatorWord); //返回字符在operators中的索引位置
/*
* 从算符优先关系表中获得对应的优先值。操作符栈的栈顶元素和算术表达式中的当前字符比较得到的优先字符。
* 第一个参数:操作符栈的栈顶元素。对应表格左边的那一列中的某个操作符。这一列的每个操作符对应一行内容。
* 第二个参数:算术表达式中的当前字符。对应表格上面的那一行中的某个操作符。这一行的每个操作符对应一列内容。
* 获得的内容通过以下举例说明:
* 例子1:operatorWord1="(", operatorWord2=")", return '='
*/
char getPrecede(string operatorWord1, string operatorWord2); //从算符优先关系表中获得对应的优先值
/*
* 对2个数根据指定的操作符进行运算,得到新的值。
* a在表达式中是比b在前面的那个操作数。例如表达式为:3-4=,那么a就是3,b就是4
* 第四个参数hasFirstOperandInStack表示操作数栈中是否存在a,因为一个操作符并不一定会对应2个操作数,有时只能对应一个操作数。比如:-4
*/
double operate(double a, string operatorWord, double b, bool hasFirstOperandInStack);
/*
* string转数值类型
*/
template<class T>
T stringToNum(const string& str);
public:
/*
* 根据词法分析的结果来计算表达式的值
*
*/
string evaluateExpression(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList);
};
int MathExpressExecutor::locationOfCharInOperators(string operatorWord) {
for (int i = 0; i < sizeof(this->operators) / sizeof(char); i++) {
if (this->operators[i] == operatorWord) {
return i;
}
}
return -1;
}
char MathExpressExecutor::getPrecede(string operatorWord1, string operatorWord2) {
int indexOfOperatorWord1 = this->locationOfCharInOperators(operatorWord1);
int indexOfOperatorWord2 = this->locationOfCharInOperators(operatorWord2);
return this->precedences[indexOfOperatorWord1][indexOfOperatorWord2];
}
double MathExpressExecutor::operate(double a, string operatorWord, double b, bool hasFirstOperandInStack) {
double result = 0;
if (!hasFirstOperandInStack) {
//如果没有操作数a,进行如下处理
if (operatorWord == "+") {
result = b;
} else if (operatorWord == "-") {
result = 0 - b;
}
else {
cout << "表达式开头有错。" << endl;
}
return result;
}
if (operatorWord == "+") {
result = a + b;
}
if (operatorWord == "-") {
result = a - b;
}
if (operatorWord == "*") {
result = a * b;
}
if (operatorWord == "/") {
if (b == 0.0) {
cout << "除数不能等于0" << endl;
return NULL;
}
result = a / b;
}
return result;
}
template<class T>
T MathExpressExecutor::stringToNum(const string& str) {
istringstream iss(str);
T num;
iss >> num;
return num;
}
string MathExpressExecutor::evaluateExpression(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList) {
stack<string> operandStack; //初始化操作数栈
stack<string> operatorStack; //初始化操作符栈
string result;
operatorStack.push("=");
//cout << "operatorStack <== =" << endl;
for (list<LexicalAnalysisWordAndType>::iterator i = lexicalAnalysisResultList.begin(); i != lexicalAnalysisResultList.end(); i++) {
//cout << i->getWord() << "\t" << i->getType() << endl;
if (i->getWord() == "=" && operatorStack.top() == "=") {
//进入此条件语句表示表达式的计算已经到了尾声,把操作数栈中的结果弹出就是了
result = operandStack.top();
//cout << "result=" << result << endl;
return result;
}
if (i->getType() == "OPERAND") {
//如果词法分析结果表中当前元素是操作数,则操作数压入操作数栈。
operandStack.push(i->getWord());
//cout << "operandStack <== " << i->getWord() << endl;
}
else {
//如果词法分析结果表中当前元素是操作符,则根据操作符栈顶的操作符和当前的操作符比较优先级,再根据优先级作相应处理。
char precedence = this->getPrecede(operatorStack.top(), i->getWord());
if (precedence == '<') {
//操作符栈顶的操作符优先级<当前的操作符,把当前操作符压入操作符栈。
operatorStack.push(i->getWord());
//cout << "operatorStack <== " << i->getWord() << endl;
}
if (precedence == '=') {
//cout << operatorStack.top() << " pop from operatorStack" << endl;
//说明左右括号匹配,将操作符栈顶的括号去除。
operatorStack.pop();
}
if (precedence == '>') {
//操作符栈顶的操作符优先级比较高,提取栈顶操作符和操作数栈的2个数据进行运算,运算结果压入操作数栈。
string operatorWord = operatorStack.top();
//cout << operatorWord << " pop from operatorStack" << endl;
operatorStack.pop();
string data1 = "";
if (!operandStack.empty()) {
data1 = operandStack.top();
//cout << data1 << " pop from operandStack" << endl;
operandStack.pop();
}
string data2 = "";
if (!operandStack.empty()) {
data2 = operandStack.top();
//cout << data2 << " pop from operandStack" << endl;
operandStack.pop();
}
if (data1 == "") {
//说明操作数栈已经没数据了,这说明表达式有问题。
return "ERROR";
}
double dblData1 = this->stringToNum<double>(data1);
double dblData2 = 0;
bool hasData2 = false; //data2是否存在
if (data2 != "") {
dblData2 = this->stringToNum<double>(data2);
hasData2 = true;
}
//值得注意的是:由于栈是先进后出的,在表达式中data2在前面,data1在后面。在做减法或除法的时候一定要注意顺序。
double tempResult = this->operate(dblData2, operatorWord, dblData1, hasData2);
operandStack.push(to_string(tempResult));
//cout << "operandStack <== " << to_string(tempResult) << endl;
i--;
}
if (precedence == ' ') {
//从优先级表中得到的优先级为' '说明表达式有问题,应该返回错误而不应该计算值。
return "ERROR";
}
}
}
return "";
}
LexicalAnalysisPhase.h:
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
#pragma once
#include
#include "DoubleLinkListForExpression.h"
#include "LexicalAnalysisWordAndType.h"
#include
#include "GlobalConstVars.h"
#include "MathExpressDFA.h"
#include "SqStack.h"
/**
* 定义词法分析阶段要做的事情
*/
class LexicalAnalysisPhase {
public:
/*
* 规范化数学表达式,做3件事情:
* 1、消除所有空格字符
* 2、初步判定'-'属于操作数的开头部分还是操作符,会在适当的地方添加'+','(',')',目的是为了方便DFA状态机识别。
* 3、在算术表达式后面加上'=',为了方便DFA状态机识别。
* 例如:-2*(5.5 - 2/-1 ),规范化为:-2*(5.5+(-2)/(-1))=
* 第一个参数:传入的原始算术表达式
* 第二个参数:接手已规范化的算术表达式
* 返回:已规范化的算术表达式的长度。
*/
int normalizeMathExpression(const char* const expression, char*& resultExpression);
/**
* 生成词法分析的结果表
* 例如表达式:1.2*(1+3.5/0.5-2)*(-2.5)=,结果为一个类型二维表格的东西:
* 1.2 OPERAND
* * OPERATOR
* ( OPERATOR
* 1 OPERAND
* + OPERATOR
* 3.5 OPERAND
* / OPERATOR
* 0.5 OPERAND
* - OPERATOR
* 2 OPERAND
* ) OPERATOR
* * OPERATOR
* ( OPERATOR
* -2.5 OPERAND
* ) OPERATOR
* =
* 入参说明:
* 第一个参数: 传入已经规范化之后的算术表达式
* 第二个参数: 用于存放词法分析结果的列表。
*/
Status generateLexicalAnalysisResultTableList(const char* const expression, list<LexicalAnalysisWordAndType>& wordAndTypeResultList);
/**
* 这个方法只是简单检查表达式中的括号匹配情况。
* 这个还不能算真正意义上的语法检测。真正的语法检测在语法分析阶段的类中有定义。
* 入参为算术表达式字符串
*/
Status checkParenthesesPair(const char* expression);
};
int LexicalAnalysisPhase::normalizeMathExpression(const char* const expression, char*& resultExpression) {
char* tempChars = new char[strlen(expression)];
strcpy(tempChars, expression);
DoubleLinkListForExpression<char> doubleLinkListForExpressionObj(' ');
int len = strlen(expression);
if (ERROR == doubleLinkListForExpressionObj.createList(tempChars, len, doubleLinkListForExpressionObj)) {
cout << "创建双向循环链表失败" << endl;
return 0;
}
//cout << "循环双向链表原始内容:";
//doubleLinkListForExpressionObj.traverse();
doubleLinkListForExpressionObj.delAll(' ');
//cout << "删除所有空格后,内容:";
//doubleLinkListForExpressionObj.traverse();
//对算术公式进行规范化改造
doubleLinkListForExpressionObj.normalizeExpression(doubleLinkListForExpressionObj);
//cout << "规范化公式后,内容:";
//doubleLinkListForExpressionObj.traverse();
int resultSize = doubleLinkListForExpressionObj.getSize();
resultExpression = new char[resultSize];
for (int i = 0; i < resultSize; i++) {
resultExpression[i] = doubleLinkListForExpressionObj.getVal(i + 1);
}
return resultSize;
}
Status LexicalAnalysisPhase::generateLexicalAnalysisResultTableList(const char* const expression, list<LexicalAnalysisWordAndType>& wordAndTypeResultList) {
LexicalAnalysisWordAndType wordAndType;
int length = strlen(expression);
int i = 0;
int startStatus = 0;
string tempStr;
MathExpressDFA mathExpressDFA;
while (i < length) {
char readChar = expression[i];
int nextStatus = mathExpressDFA.statusMap[to_string(startStatus).append(1, ',').append(1, readChar)];
if (nextStatus == 0 && i < length - 1) {
//nextStatus==0表示在状态机中找不到符合此定义的状态映射,于是认为这是不合法的表达式。
//由于最后一个固定是'=','='的状态在状态机中也没定义,所以也会返回0,然而它是正常的情况而不应当作为错误。所以条件中要有‘i < length - 1’的限制。
//总之,此条件表示在最后的'='之前如果发现有不符合状态机定义的状态出现时就意味着表达式不能通过词法分析。下面将打印出在表达式的哪个位置上出现错误,然后词法分析过程中止。
cout << "词法分析检测出错误。错误位置在规范化后的算术表达式中的第 " << (i + 1) << " 个字符(" << readChar << ")上" << endl;
return ERROR;
}
if (nextStatus != mathExpressDFA.OPERAND && nextStatus != mathExpressDFA.OPERATOR) {
tempStr.append(1, readChar);
i++;
startStatus = nextStatus;
}
if (nextStatus == mathExpressDFA.OPERAND) {
wordAndType.setWord(tempStr);
wordAndType.setType("OPERAND");
wordAndTypeResultList.push_back(wordAndType);
tempStr = "";
startStatus = 0;
continue;
}
if (nextStatus == mathExpressDFA.OPERATOR) {
wordAndType.setWord(tempStr);
wordAndType.setType("OPERATOR");
wordAndTypeResultList.push_back(wordAndType);
tempStr = "";
startStatus = 0;
continue;
}
}
//最后再向wordAndTypeResultList中追加{"=",""}。这个在后面的运算过程中作为表达式的结束标志。
wordAndType.setWord("=");
wordAndType.setType("");
wordAndTypeResultList.push_back(wordAndType);
return OK;
}
Status LexicalAnalysisPhase::checkParenthesesPair(const char* expression) {
int len = strlen(expression);
SqStack<char> myStack;
char popedChar = NULL;
//从头到尾遍历算术表达式字符串中的每个字符,如果字符是'('则入栈,如果字符是')'时则取出栈顶元素看看是不是'(',如果是则正常,否则不正常。
for (int i = 0; i < len; i++) {
if ('(' == expression[i]) {
myStack.Push(expression[i]);
}
if (')' == expression[i]) {
if (myStack.StackEmpty()) {
cout << "检测第" << i + 1 << "个字符时,发现栈已空了,不能再pop出东西来了。" << endl;
return ERROR;
}
myStack.Pop(popedChar);
if (popedChar != '(') {
cout << "检测第" << i + 1 << "个字符时,发现输入的是'" << expression[i] << "', 而栈顶的元素是'" << popedChar << "',括号不匹配." << endl;
return ERROR;
}
}
}
return OK;
}
MathExpressDFA.h:
#pragma once
#include
#include
#include
using namespace std;
/**
* 数学表达式的确定的有向自动机
*/
class MathExpressDFA {
private:
const char charArray1[6] = { '+', '-', '*', '/', ')', '=' };
const char charArray2[13] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '(', ')' };
const char charArray3[11] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(' };
/*
* 判断指定的字符是否在指定的集合中存在
*/
bool isInCharArray(char c, const char* charArray, const int size) {
for (int i = 0; i < size; i++) {
if (c == charArray[i]) {
return true;
}
}
return false;
}
public:
MathExpressDFA(); //构造方法,初始化statusMap
~MathExpressDFA(); //析构方法
const int OPERAND = 100; //定义操作数的状态编号
const int OPERATOR = 101; //定义操作符的状态编号
map<string, int> statusMap; //存放状态机的各个状态映射。其中的key的格式是 ({状态编号}+','+{读入的字符}),例如:0,1。映射的值是新的状态编号。
};
/**
* 在构造函数中构造DFA
* DFA结构描述如下:
* 有穷字母表:{'0..9', '+', '-', '*', '/', '(', ')', '='}
* 有限状态集:{0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, OPERAND=>(100), OPERATOR=>(101)}
* 初态:0
* 终态:{OPERAND=>(100), OPERATOR=>(101)}
* 单值映射关系:
* (0, '1..9')-->1 (0, '-')-->4 (0, '0')-->5
* (0, '(')-->6
* (0, ')')-->7
* (0, '+')-->8
* (0, '*')-->10
* (0, '/')-->11
* (1, '0..9)-->1
* (1, '.')-->2
* (1, {'+','-','*','/',')','='})-->OPERAND
* (2, '0..9')-->3
* (3, '0..9')-->3
* (3, {'+','-','*','/',')','='})-->OPERAND
* (4, '1..9')-->1
* (4, '(')-->OPERATOR
* (4, '0')-->5
* (5, '.')-->2
* (5, {'+','-','*','/',')','='})-->OPERAND
* (6, {'0..9', '-', '(', ')'})-->OPERATOR
* (7, {'+','-','*','/',')','='})-->OPERATOR
* (8, {'0..9', '('})-->OPERATOR
* (10, {'0..9', '('})-->OPERATOR
* (11, {'0..9', '('})-->OPERATOR
*/
MathExpressDFA::MathExpressDFA() {
int sizeOfCharArray1 = sizeof(charArray1);
int sizeOfCharArray2 = sizeof(charArray2);
int sizeOfCharArray3 = sizeof(charArray3);
//初始化statusMap
//从状态0开始定义状态映射
for (int i = 1; i < 10; i++) {
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(to_string(i)), 1)); //从状态0开始,读取'1'..'9'中的任何一个字符,进入状态1
}
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, '-'), 4)); //从状态0开始,读取'-',进入状态4
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, '0'), 5)); //从状态0开始,读取'0',进入状态5
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, '('), 6)); //从状态0开始,读取'(',进入状态6
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, ')'), 7)); //从状态0开始,读取')',进入状态7
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, '+'), 8)); //从状态0开始,读取'+',进入状态8
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, '*'), 10)); //从状态0开始,读取'*',进入状态10
statusMap.insert(pair<string, int>(to_string(0).append(1, ',').append(1, '/'), 11)); //从状态0开始,读取'/',进入状态11
//从状态1开始定义状态映射
for (int i = 0; i < 10; i++) {
statusMap.insert(pair<string, int>(to_string(1).append(1, ',').append(to_string(i)), 1)); //从状态1开始,读取'0'..'9'中的任何一个字符,进入状态1
}
statusMap.insert(pair<string, int>(to_string(1).append(1, ',').append(1, '.'), 2)); //从状态1开始,读取'.',进入状态2
for (int i = 0; i < sizeOfCharArray1; i++) {
//从状态1开始,读取{ '+', '-', '*', '/', ')', '=' }中的任何一个字符,进入状态OPERAND
statusMap.insert(pair<string, int>(to_string(1).append(1, ',').append(1, charArray1[i]), OPERAND));
}
//从状态2开始定义状态映射
for (int i = 0; i < 10; i++) {
statusMap.insert(pair<string, int>(to_string(2).append(1, ',').append(to_string(i)), 3)); //从状态2开始,读取'0'..'9'中的任何一个字符,进入状态3
}
//从状态3开始定义状态映射
for (int i = 0; i < 10; i++) {
statusMap.insert(pair<string, int>(to_string(3).append(1, ',').append(to_string(i)), 3)); //从状态3开始,读取'0'..'9'中的任何一个字符,进入状态3
}
for (int i = 0; i < sizeOfCharArray1; i++) {
//从状态3开始,读取{ '+', '-', '*', '/', ')', '=' }中的任何一个字符,进入状态OPERAND
statusMap.insert(pair<string, int>(to_string(3).append(1, ',').append(1, charArray1[i]), OPERAND));
}
//从状态4开始定义状态映射
for (int i = 1; i < 10; i++) {
statusMap.insert(pair<string, int>(to_string(4).append(1, ',').append(to_string(i)), 1)); //从状态4开始,读取'1'..'9'中的任何一个字符,进入状态1
}
statusMap.insert(pair<string, int>(to_string(4).append(1, ',').append(1, '0'), 5)); //从状态4开始,读取'0',进入状态5
statusMap.insert(pair<string, int>(to_string(4).append(1, ',').append(1, '('), OPERATOR)); //从状态4开始,读取'(',进入状态OPERATOR
//从状态5开始定义状态映射
statusMap.insert(pair<string, int>(to_string(5).append(1, ',').append(1, '.'), 2)); //从状态5开始,读取'.',进入状态2
for (int i = 0; i < sizeOfCharArray1; i++) {
//从状态5开始,读取{ '+', '-', '*', '/', ')', '=' }中的任何一个字符,进入状态OPERAND
statusMap.insert(pair<string, int>(to_string(5).append(1, ',').append(1, charArray1[i]), OPERAND));
}
//从状态6开始定义状态映射
for (int i = 0; i < sizeOfCharArray2; i++) {
//从状态6开始,读取{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '(', ')' }中的任何一个字符,进入状态OPERATOR
statusMap.insert(pair<string, int>(to_string(6).append(1, ',').append(1, charArray2[i]), OPERATOR));
}
//从状态7开始定义状态映射
for (int i = 0; i < sizeOfCharArray1; i++) {
//从状态7开始,读取{ '+', '-', '*', '/', ')', '=' }中的任何一个字符,进入状态OPERATOR
statusMap.insert(pair<string, int>(to_string(7).append(1, ',').append(1, charArray1[i]), OPERATOR));
}
//从状态8开始定义状态映射
for (int i = 0; i < sizeOfCharArray3; i++) {
//从状态8开始,读取{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(' }中的任何一个字符,进入状态OPERATOR
statusMap.insert(pair<string, int>(to_string(8).append(1, ',').append(1, charArray3[i]), OPERATOR));
}
//从状态10开始定义状态映射
for (int i = 0; i < sizeOfCharArray3; i++) {
//从状态10开始,读取{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(' }中的任何一个字符,进入状态OPERATOR
statusMap.insert(pair<string, int>(to_string(10).append(1, ',').append(1, charArray3[i]), OPERATOR));
}
//从状态11开始定义状态映射
for (int i = 0; i < sizeOfCharArray3; i++) {
//从状态11开始,读取{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '(' }中的任何一个字符,进入状态OPERATOR
statusMap.insert(pair<string, int>(to_string(11).append(1, ',').append(1, charArray3[i]), OPERATOR));
}
/*
cout << "状态机定义:" << endl;
for (map
cout << i->first << "-->" << i->second << endl;
}
*/
}
MathExpressDFA::~MathExpressDFA() {
}
SqStack.h:
#pragma once
#include "iostream"
#include "GlobalConstVars.h"
using namespace std;
/********************************
顺序栈存储类
********************************/
const int STACK_INIT_SIZE = 1; //存储空间初始分配量
const int STACKINCREMENT = 10; //存储空间分配增量
template<class SElemType>
class SqStack {
private:
SElemType* base; //在栈构造之前和销毁之后,base的值为NULL
SElemType* top; //栈顶指针
int stacksize; //当前已分配的存储空间,以元素为单位
public:
SqStack();
~SqStack();
void DestoryStack(); //销毁栈
void ClearStack(); //置为空栈
bool StackEmpty(); //若为空栈返回true,否则返回false。
int StackLength(); //返回栈中元素个数,即栈的长度。
Status Pop(SElemType &e); //删除并返回栈顶元素
Status Push(SElemType e); //元素压栈
Status GetTop(SElemType &e); //若栈不空返回栈顶元素
};
template<class SElemType>
SqStack<SElemType>::SqStack() {
base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!base) {
cout << "初始化栈失败" << endl;
stacksize = -1;
top = NULL;
base = NULL;
return;
}
top = base;
stacksize = STACK_INIT_SIZE;
}
//销毁栈
template<class SElemType>
void SqStack<SElemType>::DestoryStack() {
if (base) {
delete base;
}
top = base = NULL;
cout << "栈被销毁。" << endl;
}
//置为空栈
template<class SElemType>
void SqStack<SElemType>::ClearStack() {
if (base) {
top = base;
}
}
//若为空栈返回true,否则返回false。
template<class SElemType>
bool SqStack<SElemType>::StackEmpty() {
if (base) {
return top - base == 0 ? true : false;
}
return true;
}
//返回栈中元素个数,即栈的长度。
template<class SElemType>
int SqStack<SElemType>::StackLength() {
if (base) {
return top - base;
}
return 0;
}
//删除并返回栈顶元素
template<class SElemType>
Status SqStack<SElemType>::Pop(SElemType &e) {
if (top && base && top - base > 0) {
e = *--top;
return OK;
}
cout << "栈不存在或空,无法返回栈顶元素" << endl;
return ERROR;
}
template<class SElemType>
Status SqStack<SElemType>::Push(SElemType e) {
if (!base) {
cout << "尚未初始化栈,无法将元素压栈" << endl;
return ERROR;
}
if (top - base >= stacksize) {
cout << "为栈扩展内存空间..." << endl;
SElemType* tempbase = (SElemType*)realloc(base, (stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!tempbase) {
cout << "补充存储空间失败" << endl;
exit(OVERFLOW);
}
base = tempbase;
top = base + stacksize;
stacksize += STACKINCREMENT;
cout << "目前栈空间大小:" << stacksize << endl;
}
*top++ = e;
return OK;
}
template<class SElemType>
SqStack<SElemType>::~SqStack() {
DestoryStack();
}
template<class SElemType>
Status SqStack<SElemType>::GetTop(SElemType &e) {
if (top && base && top - base > 0) {
e = *(top - 1);
return OK;
}
cout << "栈中无元素" << endl;
return ERROR;
}
DoubleLinkListForExpression.h:
#pragma once
#include
#include "DoubleLinkList.h"
#include "DoubleLinkNode.h"
#include "NumberUtil.h"
template<class T>
class DoubleLinkListForExpression : public DoubleLinkList<T> {
public:
DoubleLinkListForExpression(T initData);
~DoubleLinkListForExpression();
/*
* 用于规范负数的表达,因为‘-’可能属于负数的开头部分,也可能是操作符。
* 为了能让DFA能方便识别‘-’到底是属于操作数的开头部分还是操作符,于是采取以下方式规范表达式:
* 首先设置p1指针指向'-'之前的节点,p2指针指向负数之后的节点。
* 情况一:如果p1节点的值是'('并且p2节点的值是')',不对表达式做任何插入操作。如果p2节点的值不是')',则在p1之后插入'(',在p2之前插入')'。
* 情况二:如果p1节点的值是{数字,')'}中的一种,则在p1之后插入'+(',在p2之前插入')'。
* 情况三:如果p1节点的值是{'+','-','*','/'}中的一种,则在p1之后插入'(',在p2之前插入')'。
*
* 前面所述的负数又是如何判断的呢,规则是:开头为'-',之后紧跟的是数字,再之后是数字或小数点并且只能有一个小数点,最后一个一定是数字。
* 参数dblLinkListObj传入的是一个已经去除其中的空格的算术表达式。
*
* 如果表达式的最后一个元素值不是'=',则在表达式的最后添加元素'='
*/
void normalizeExpression(DoubleLinkList<T>& dblLinkListObj);
};
template<class T>
DoubleLinkListForExpression<T>::DoubleLinkListForExpression(T initData):DoubleLinkList<T>(initData) {
}
template<class T>
DoubleLinkListForExpression<T>::~DoubleLinkListForExpression() {
}
template<class T>
void DoubleLinkListForExpression<T>::normalizeExpression(DoubleLinkList<T>& dblLinkListObj) {
int size = dblLinkListObj.getSize();
DoubleLinkNode<T>* head = dblLinkListObj.getHead();
if (size > 0) {
DoubleLinkNode<T>* p = head->next;
DoubleLinkNode<T>* p1;
DoubleLinkNode<T>* p2 = NULL;
int pos_p = 1; //p指针指向的是第几个元素
int pos_p1 = 0; //p1指针指向的是第几个元素
int pos_p2 = 0; //p2指针指向的是第几个元素
NumberUtil numberUtil;
while (p && p != head) { //遍历所有节点
if (p->data == '-') {
//设置p1的位置
p1 = p->prior;
pos_p1 = pos_p - 1;
p = p->next;
pos_p++;
//此处不进行数值的正确性检验,正确性检验过程留到DFA切分词的时候执行。现在只要是个数字或'.'就算过了。
bool hasNumber = false; //'-'后面是否有数字
while (numberUtil.isIn0_9(p->data) || '.' == p->data) {
p = p->next;
pos_p++;
hasNumber = true;
}
if (hasNumber) { //如果前面是个负数,则设置p2的位置
p2 = p;
pos_p2 = pos_p;
}
if (pos_p1 >= 0 && pos_p2 > pos_p1) {
//情况一:如果p1节点的值是'('并且p2节点的值不是')',则在p1之后插入'(',在p2之前插入')'。
if ((p1->data == '(' && p2->data != ')') || p1->data == ')') {
dblLinkListObj.insert(pos_p1 + 1, '(');
pos_p2 = pos_p2 + 1; //由于前面添加了1个节点,所以pos_p2的位置就会增加1
dblLinkListObj.insert(pos_p2, ')');
p = p->next;
pos_p = pos_p + 3; //由于前面总共插入了2个节点,加上p的位置又后移了1位,所以pos_p要在原先的基础上加3
}
else if (numberUtil.isIn0_9(p1->data)) {
//情况二:如果p1节点的值是{数字,')'}中的一种,则在p1之后插入'+(',在p2之前插入')'。
dblLinkListObj.insert(pos_p1 + 1, '+');
dblLinkListObj.insert(pos_p1 + 2, '(');
pos_p2 = pos_p2 + 2; //由于前面添加了2个节点,所以pos_p2的位置就会增加2
dblLinkListObj.insert(pos_p2, ')');
p = p->next;
pos_p = pos_p + 4; //由于前面总共插入了3个节点,加上p的位置又后移了1位,所以pos_p要在原先的基础上加4
}
else if (p1->data == '+' || p1->data == '-' || p1->data == '*' || p1->data == '/') {
//情况三:如果p1节点的值是{'+','-','*','/'}中的一种,则在p1之后插入'(',在p2之前插入')'。
dblLinkListObj.insert(pos_p1 + 1, '(');
pos_p2 = pos_p2 + 1; //由于前面添加了1个节点,所以pos_p2的位置就会增加1
dblLinkListObj.insert(pos_p2, ')');
p = p->next;
pos_p = pos_p + 3; //由于前面总共插入了2个节点,加上p的位置又后移了1位,所以pos_p要在原先的基础上加3
}
}
}
else {
p = p->next;
pos_p++;
}
}
//如果表达式的最后一个元素值不是'=',则在表达式的最后添加元素'='
size = dblLinkListObj.getSize();
if (dblLinkListObj.getVal(size) != '=') {
dblLinkListObj.insert(size + 1, '=');
}
}
}
DoubleLinkList.h:
#pragma once
#include "iostream"
#include "GlobalConstVars.h"
#include
#include "DoubleLinkNode.h"
using namespace std;
/********************************
线性表的带头节点的循环双链表存储类
********************************/
template <class T>
class DoubleLinkList {
private:
DoubleLinkNode<T>* head; //头指针
int size; //存放链表长度(不包括头节点)
public:
DoubleLinkList(T initData); //构造函数,初始化头节点。
~DoubleLinkList(); //析构函数,彻底销毁链表实例。
int getSize(); //获取双向链表中元素个数
void destoryList(); //彻底销毁双向链表实例
void deleteAllDataNode(); //如果循环双向链表中有数据节点的话,循环删除每个尾巴节点,直到只剩下头节点。
Status insert(int i, T e); //在第i个位置插入值e。i是从1开始的整数。
void traverse(); //遍历循环双向链表
Status del(int i); //删除第i个位置的节点
/*
* 根据一串内容创建循环双向链表
* arrData: 内容
* size: 内容中元素个数
* doubleLinkListObj: 生成的目标循环双向链表
*/
Status createList(T* arrData, int size, DoubleLinkList<T>& doubleLinkListObj);
void delAll(T data); //删除循环双向链表中所有指定的内容。
DoubleLinkNode<T>* getHead();
void setSize(int size);
T getVal(int i); //获取第i个位置上的值
};
template<class T>
DoubleLinkList<T>::DoubleLinkList(T initData) {
this->head = new DoubleLinkNode<T>(initData);
if (!this->head) {
exit(OVERFLOW);
}
this->size = 0;
}
template<class T>
DoubleLinkList<T>::~DoubleLinkList() {
destoryList();
}
template<class T>
int DoubleLinkList<T>::getSize() {
return size;
}
template <class T>
void DoubleLinkList<T>::destoryList() {
if (this->head) {
if (this->size > 0) {
//如果循环双向链表中有数据节点的话,循环删除每个尾巴节点,直到只剩下头节点。
deleteAllDataNode();
}
//然后删除头节点
DoubleLinkNode<T>* p = this->head;
p->next = NULL;
p->prior = NULL;
cout << "销毁头节点 p(" << p->data << ")" << endl;
free(p);
}
}
template<class T>
void DoubleLinkList<T>::deleteAllDataNode() {
DoubleLinkNode<T>* p = this->head;
while (size > 0) {
p = this->head->prior; //p指向最后一个节点
DoubleLinkNode<T>* q = p; //新建q指针也指向尾节点
p = p->prior; //p指向前一个节点
q->next = NULL; //砍断最后一个节点的next指针
q->prior = NULL; //砍断最后一个节点的prior指针
if (p != NULL) {
p->next = this->head; //最后第二个节点的next指针指向头节点
}
this->head->prior = p; //头节点的prior指针指向最后第二个节点
cout << "销毁尾巴数据节点 q(" << q->data << ")" << endl;
free(q);
this->size--; //双向链表长度减1
}
}
template<class T>
Status DoubleLinkList<T>::insert(int i, T e) {
if (i < 1 || i > size + 1) {
cout << "插入位置超出范围。正确范围是1<=i<=" << (size + 1) << endl;
return ERROR;
}
int j = 1;
DoubleLinkNode<T>* p = this->head;
DoubleLinkNode<T>* q = new DoubleLinkNode<T>(e);
while (p && j < i) {
p = p->next;
j++;
}
if (p != NULL) {
q->next = p->next;
p->next->prior = q;
q->prior = p;
p->next = q;
this->size++;
}
return OK;
}
template<class T>
void DoubleLinkList<T>::traverse() {
if (size == 0) {
cout << "循环双向链表是空的" << endl;
return;
}
DoubleLinkNode<T>* p = this->head->next;
while (p) {
cout << p->data << ", ";
p = p->next;
if (p == this->head) {
break;
}
}
cout << endl;
}
template<class T>
Status DoubleLinkList<T>::del(int i) {
if (size == 0) {
cout << "循环双向链表是空的,无需删除任何节点。" << endl;
return ERROR;
}
if (i < 1 || i > size) {
cout << "位置i=" << i << "不在正常范围内。位置正常范围:[1," << size << "]" << endl;
return ERROR;
}
DoubleLinkNode<T>* p = this->head;
int j = 1;
while (p && j < i) {
p = p->next;
j++;
}
DoubleLinkNode<T>* q = p->next;
p->next = q->next;
q->next->prior = p;
q->next = NULL;
q->prior = NULL;
cout << "删除了第 " << i << " 个节点(" << q->data << ")" << endl;
free(q);
this->size--;
}
template<class T>
Status DoubleLinkList<T>::createList(T* arrData, int size, DoubleLinkList<T>& doubleLinkListObj) {
if (!arrData) {
cout << "传入数据不能为NULL" << endl;
return ERROR;
}
DoubleLinkNode<T>* p = doubleLinkListObj.head;
if (!p) {
return ERROR;
}
for (int i = 0; i < size; i++) {
DoubleLinkNode<T>* q = new DoubleLinkNode<T>(arrData[i]);
if (!q) {
return ERROR;
}
p->next = q;
q->next = doubleLinkListObj.head;
doubleLinkListObj.head->prior = q;
q->prior = p;
p = p->next;
doubleLinkListObj.size++;
}
return OK;
}
template <class T>
void DoubleLinkList<T>::delAll(T data) {
if (size == 0) {
cout << "循环双向链表是空的,没有内容可删除" << endl;
return;
}
DoubleLinkNode<T>* p = this->head;
DoubleLinkNode<T>* q = this->head->next;
while (q != this->head) {
if (q->data == data) {
//删除q节点
p->next = q->next;
q->next->prior = p;
q->next = NULL;
q->prior = NULL;
free(q);
q = p->next;
size--;
}
else {
q = q->next;
p = p->next;
}
}
}
template<class T>
DoubleLinkNode<T>* DoubleLinkList<T>::getHead() {
return this->head;
}
template<class T>
void DoubleLinkList<T>::setSize(int size) {
this->size = size;
}
template<class T>
T DoubleLinkList<T>::getVal(int i) {
if (size == 0) {
cout << "循环双向链表是空的" << endl;
}
if (i < 1 || i > size) {
cout << "位置i=" << i << "不在正常范围内。位置正常范围:[1," << size << "]" << endl;
return NULL;
}
DoubleLinkNode<T>* p = this->head->next;
int j = 1;
while (p && j < i) {
p = p->next;
j++;
}
return p->data;
}
DoubleLinkeNode.h:
#pragma once
#include "iostream"
/********************************
双向循环链表节点类
********************************/
template<class T>
class DoubleLinkNode {
template<class T>
friend class DoubleLinkList;
template<class T>
friend class DoubleLinkListForExpression;
private:
T data; //节点中的数据
DoubleLinkNode<T>* prior; //前驱指针
DoubleLinkNode<T>* next; //后驱指针
public:
DoubleLinkNode(const T data) {
this->data = data;
this->prior = this;
this->next = this;
}
};
NumberUtil.h:
#pragma once
#include
class NumberUtil {
private:
char numarrIn0_9[10] = { '0','1','2','3','4','5','6','7','8','9' };
char numarrIn1_9[9] = { '1','2','3','4','5','6','7','8','9' };
public:
bool isIn0_9(char c); //字符参数c的值是否属于'0'到'9'之间的值
bool isIn1_9(char c); //字符参数c的值是否属于'1'到'9'之间的值
};
bool NumberUtil::isIn0_9(char c) {
for (int i = 0; i < sizeof(numarrIn0_9) / sizeof(char); i++) {
if (c == numarrIn0_9[i]) {
return true;
}
}
return false;
}
bool NumberUtil::isIn1_9(char c) {
for (int i = 0; i < sizeof(numarrIn1_9) / sizeof(char); i++) {
if (c == numarrIn1_9[i]) {
return true;
}
}
return false;
}
SyntaxAnalysisPhase.h
#pragma once
#include
#include
#include "LexicalAnalysisWordAndType.h"
using namespace std;
/**
* 语法分析阶段要处理的事情。本语法分析过程采用的是有递归的预测分析
* 原始的贴近人类思维的文法产生式:
* E->E+F|E-F|E*F|E/F|F
* F->(E)|i
* 很容易得出消除左递归后的文法产生式定义如下:
* E->FE'
* E'->+FE'|-FE'|*FE'|/FE'|空
* F->(E)|i
* 其中非终结符E是表达式,非终结符F是表达式中的因子。
* 终结符:{'+', '-', '*', '/', '(', ')','i'},其中i是操作数。
* 开始符是E
*/
class SyntaxAnalysisPhase {
private:
void E();
void E_(); //E_就是E',因为方法名不能有''',所以用’_'表示'''。
void F();
string* wordPtr; //保存词法分析结果中的词到这个string数组中。其中去掉了"=",因为这个并未在语法中定义。
string* typePtr; //对应wordPtr中的每个词的类型。
int lengthOfWordAndTypePtr; //wordPtr或typePtr中的元素个数
int currentIndex = 0; //当前读取wordPtr的索引位置
bool passedSyntaxCheck = true; //是否通过语法检测
/**
* 判定表达式中的当前字符串是否匹配文法产生式的规范。
* 如果不匹配则会标志passedSyntaxCheck为false。
* 参数token是文法产生式中的某个终结符
*/
void match(string token);
public:
/**
* 构造函数。将词法分析的结果作为入参传进来。去除'=',词和属性分别保存到wordPtr和typePtr这两个数组中。
* wordPtr和typePtr这两个数组的相同位置的元素有对应关系,分别是某个单词和对应的词性。
*/
SyntaxAnalysisPhase(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList);
~SyntaxAnalysisPhase();
bool checkSyntax(); //检查语法
};
SyntaxAnalysisPhase::SyntaxAnalysisPhase(list<LexicalAnalysisWordAndType> lexicalAnalysisResultList) {
int length = lexicalAnalysisResultList.size();
string *wordPtr = new string[length];
string* typePtr = new string[length];
int i = 0;
for (list<LexicalAnalysisWordAndType>::iterator iter = lexicalAnalysisResultList.begin(); iter != lexicalAnalysisResultList.end(); iter++) {
if (iter->getWord() != "=") {
wordPtr[i] = iter->getWord();
typePtr[i] = iter->getType();
i++;
}
}
this->wordPtr = wordPtr;
this->typePtr = typePtr;
this->lengthOfWordAndTypePtr = i;
}
SyntaxAnalysisPhase::~SyntaxAnalysisPhase() {
}
/**
* E->FE'
*/
void SyntaxAnalysisPhase::E() {
F();
E_();
}
/**
* E'->+FE'|-FE'|*FE'|/FE'|空
*/
void SyntaxAnalysisPhase::E_() {
if (this->currentIndex >= this->lengthOfWordAndTypePtr) {
return;
}
string currentWordInWordPtr = this->wordPtr[this->currentIndex];
if (currentWordInWordPtr == "+") {
this->match("+");
F();
E_();
} else if (currentWordInWordPtr == "-") {
this->match("-");
F();
E_();
}
else if (currentWordInWordPtr == "*") {
this->match("*");
F();
E_();
}
else if (currentWordInWordPtr == "/") {
this->match("/");
F();
E_();
}
}
/**
* F->(E)|i
*/
void SyntaxAnalysisPhase::F() {
if (this->currentIndex >= this->lengthOfWordAndTypePtr) {
return;
}
if (this->typePtr[this->currentIndex] == "OPERAND") {
this->match("i");
}
else if (this->wordPtr[this->currentIndex] == "(") {
this->match("(");
E();
if (this->wordPtr[this->currentIndex] == ")") {
this->match(")");
}
else {
cout << "Syntax Error, at word offset of " << (this->currentIndex + 1) << ", lack of word \")\" " << endl;
this->passedSyntaxCheck = false;
}
}
else {
cout << "Syntax Error, at word offset of " << (this->currentIndex + 1) << " with content " << this->wordPtr[this->currentIndex] << endl;
this->passedSyntaxCheck = false;
}
}
void SyntaxAnalysisPhase::match(string token) {
string currentWord = this->wordPtr[currentIndex];
string currentType = this->typePtr[currentIndex];
if (token == "i") {
if (currentType == "OPERAND") {
cout << "matched:" << currentWord << endl;
this->currentIndex++;
}
else {
this->passedSyntaxCheck = false;
cout << "mismatched word at offset " << (this->currentIndex + 1) << " with content " << currentWord << endl;
}
}
else {
if (currentWord == token) {
cout << "matched:" << currentWord << endl;
this->currentIndex++;
}
else {
this->passedSyntaxCheck = false;
cout << "mismatched word at offset " << (this->currentIndex + 1) << " with content " << currentWord << endl;
}
}
}
bool SyntaxAnalysisPhase::checkSyntax() {
E();
if (this->currentIndex < this->lengthOfWordAndTypePtr) {
this->passedSyntaxCheck = false;
cout << "Syntax error occured at word offset of " << (this->currentIndex + 1) << " with word content " << this->wordPtr[this->currentIndex] << endl;
}
return this->passedSyntaxCheck;
}
GrammerProductionRuleUnit.h
#pragma once
#include
#include
#include
using namespace std;
/**
本类封装文法中的一个产生式,例如:E->FE'
调用的方式:
GrammerProductionRuleUnit gpru("E", "F E'");
*/
class GrammerProductionRuleUnit {
private:
string leftsideVn; //产生式左边的非终结符。必须注意的是左边只有一个非终结符,并且没有其他终结符或非终结符。
vector<string> rightsideVnVtVector; //产生式右边的终结符或非终结符的集合,存放是按照顺序存放的。
public:
/*
构造函数
第一个参数: 产生式的左边的唯一的非终结符。必须只有这么一个非终结符,不能有其他的元素存在。
第二个参数: 产生式右边的表达式,其中的各符号之间用一个空格隔开,以便识别终结符。整个表达式是一个字符串。
作用: 将第一个参数直接赋值给私有属性leftsideVn,第二个参数要根据空格切分字符串,取出其中的各个符号,依次放入私有属性rightsideVnVts数组中,同时计数元素个数保存到私有属性lengthOfRightsideVnVtsArray中。
*/
GrammerProductionRuleUnit(string leftsideVn, string rightsideVnVtStatement);
~GrammerProductionRuleUnit();
string getLeftsideVn(); //获取产生式左边的非终结符。
vector<string> getRightsideVnVtVector(); //获取产生式右边的终结符或非终结符的集合
};
/*
string leftsideVn: 产生式的左边的唯一的非终结符。必须只有这么一个非终结符,不能有其他的元素存在。
string rightsideVnVtStatement: 产生式右边的表达式,其中的各符号之间用一个空格隔开,以便识别终结符。整个表达式是一个字符串。例如:F E’
*/
GrammerProductionRuleUnit::GrammerProductionRuleUnit(string leftsideVn, string rightsideVnVtStatement) {
this->leftsideVn = leftsideVn;
//以下代码是对右边的表达式进行切分符号,依次把符号写入rightsideVnVtVector
int lengthOfRightsideVnVtStatement = rightsideVnVtStatement.size();
string currentSymbol = "";
for (int i = 0; i < lengthOfRightsideVnVtStatement; i++) {
char c = rightsideVnVtStatement.at(i);
if (c != ' ') {
currentSymbol = currentSymbol.append(1, c);
}
else {
if (currentSymbol != "") {
this->rightsideVnVtVector.push_back(currentSymbol);
currentSymbol = "";
}
}
}
if (currentSymbol != "") {
this->rightsideVnVtVector.push_back(currentSymbol);
currentSymbol = "";
}
}
GrammerProductionRuleUnit::~GrammerProductionRuleUnit() {
}
string GrammerProductionRuleUnit::getLeftsideVn() {
return this->leftsideVn;
}
vector<string> GrammerProductionRuleUnit::getRightsideVnVtVector() {
return this->rightsideVnVtVector;
}
SyntaxAnalysisPhase2.h
#pragma once
#include
#include
#include
#include "LexicalAnalysisWordAndType.h"
#include
#include "GrammerProductionRuleUnit.h"
#include
#include
using namespace std;
/**
语法分析阶段要处理的事情。本语法分析过程采用的是非递归的预测分析,用的是预测分析表。
原始的贴近人类思维的文法产生式:
E->E+F|E-F|E*F|E/F|F
F->(E)|i
很容易得出消除左递归后的文法产生式定义如下:
E->FE'
E'->+FE'|-FE'|*FE'|/FE'|空
F->(E)|i
其中非终结符E是表达式,非终结符F是表达式中的因子。
终结符:{'+', '-', '*', '/', '(', ')','i'},其中i是操作数。
开始符是E
非递归的预测分析方法需要创建预测分析表,预测分析表的创建需要先得出文法中各非终结符的First集和Follow集。
先分析First集:
First(F)={'(', 'i'}
First(E')={'+', '-', '*', '/', 空}
First(E)=First(F)={'(', 'i'}
然后分析Follow集:(所有非终结符的Follow集都包含'=')
Follow(E)={')', '='}
First(E')-{空}属于Follow(F),故Follow(F)={'+', '-', '*', '/'}
由于E'经过0步或若干步能推导出空并且有E->FE',所有Follow(E)都属于Follow(E'),故Follow(E')={')', '='}
最后整理一下结果如下:
First(E)=First(F)={'(', 'i'}
First(E')={'+', '-', '*', '/', 空}
Follow(E)=Follow(E')={')', '='}
Follow(F)={'+', '-', '*', '/'}
在构造预测分析表之前先检查一下文法是否符合LL(1)文法
由F->(E)|i,得到First((E))交First(i)=空集
由E'->+FE',得到First(+FE')交Follow(E')={'+'}交{')', '='}=空集
由E'->-FE',得到First(-FE')交Follow(E')={'-'}交{')', '='}=空集
由E'->-FE',得到First(*FE')交Follow(E')={'*'}交{')', '='}=空集
由E'->-FE',得到First(/FE')交Follow(E')={'/'}交{')', '='}=空集
所以文法是LL(1)文法
构造预测分析表
对E->FE',因First(FE')={'(', 'i'},故E->FE'记入M[E,(]、M[E,i]
对E'->+FE',因First(+FE')={'+'},故E'->+FE'记入M[E',+]
对E'->-FE',因First(-FE')={'-'},故E'->-FE'记入M[E',-]
对E'->*FE',因First(*FE')={'*'},故E'->*FE'记入M[E',*]
对E'->/FE',因First(/FE')={'/'},故E'->/FE'记入M[E',/]
对E'->空,因Follow(E')={')', '='},故E'->空记录M[E',)]、M[E',=]
对F->(E),显然F->(E)应记入M[F,(]
对F->i,显然F->i应记入M[F,i]
凡是没记入M的都是要进行错误处理的。
预测分析表:
_________________________________________________________________________________________________________________________________________
| | i | + | - | * | / | ( | ) | = |
-----------------------------------------------------------------------------------------------------------------------------------------
| E | E->FE' | | | | | E->FE' | | |
-----------------------------------------------------------------------------------------------------------------------------------------
| E' | | E'->+FE' | E'->-FE' | E'->*FE' | E'->/FE' | | E'->空 | E'->空 |
-----------------------------------------------------------------------------------------------------------------------------------------
| F | F->i | | | | | F->(E) | | |
-----------------------------------------------------------------------------------------------------------------------------------------
*/
class SyntaxAnalysisPhase2 {
private:
string* wordPtr; //保存词法分析结果中的词到这个string数组中。其中最后的"="不要去掉。
string* typePtr; //对应wordPtr中的每个词的类型。
int lengthOfWordAndTypePtr; //wordPtr或typePtr中的元素个数
vector
map
vector
vector
/**
* 重新将词法分析结果取出放到string数组中,目的是数组操作比较方便。
* 会更改属性wordPtr、typePtr、lengthOfWordAndTypePtr的值
*/
void reencapsulateWordAndType(list
/**
构造文法产生式的存储表,存放在grammerProductionRuleVector中
构造预测分析表,存放在predictAnalyticalMatrix中
*/
void initGrammerProductionRuleVectorAndPredictAnalyticalMatrix();
void initVtVector(); //初始化所有终结符,存放在vtVector中。
void initVnVector(); //初始化所有非终结符,存放在vnVector中。
bool isVt(string s); //判断s是否为终结符
bool isVn(string s); //判断s是否为非终结符
public:
/**
* 构造函数。将词法分析的结果作为入参传进来。
功能:
1、词法分析结果中去除'=',词和属性分别保存到wordPtr和typePtr这两个数组中。wordPtr和typePtr这两个数组的相同位置的元素有对应关系,分别是某个单词和对应的词性。
2、构造文法产生式的存储表,存放在grammerProductionRulesMap中
*/
SyntaxAnalysisPhase2(list
~SyntaxAnalysisPhase2();
bool checkSyntax(); //检查语法
};
void SyntaxAnalysisPhase2::reencapsulateWordAndType(list
int length = lexicalAnalysisResultList.size();
string* wordPtr = new string[length];
string* typePtr = new string[length];
int i = 0;
for (list
wordPtr[i] = iter->getWord();
typePtr[i] = iter->getType();
i++;
}
this->wordPtr = wordPtr;
this->typePtr = typePtr;
this->lengthOfWordAndTypePtr = i;
}
void SyntaxAnalysisPhase2::initGrammerProductionRuleVectorAndPredictAnalyticalMatrix() {
/*
E->FE'
E'->+FE'|-FE'|*FE'|/FE'|空
F->(E)|i
*/
GrammerProductionRuleUnit grammerProductionRuleUnit01("E", "F E'");
GrammerProductionRuleUnit grammerProductionRuleUnit02("E'", "+ F E'");
GrammerProductionRuleUnit grammerProductionRuleUnit03("E'", "- F E'");
GrammerProductionRuleUnit grammerProductionRuleUnit04("E'", "* F E'");
GrammerProductionRuleUnit grammerProductionRuleUnit05("E'", "/ F E'");
GrammerProductionRuleUnit grammerProductionRuleUnit06("E'", "nothing");
GrammerProductionRuleUnit grammerProductionRuleUnit07("F", "( E )");
GrammerProductionRuleUnit grammerProductionRuleUnit08("F", "i");
//构造文法产生式的存储表
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit01);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit02);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit03);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit04);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit05);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit06);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit07);
this->grammerProductionRuleVector.push_back(grammerProductionRuleUnit08);
//遍历文法产生式并打印出来
cout << "文法产生式列表:" << endl;
for (vector
GrammerProductionRuleUnit currentGrammerProductionRuleUnit = *i;
cout << currentGrammerProductionRuleUnit.getLeftsideVn() << "->";
vector
for (vector
cout << (*j);
}
cout << endl;
}
/*********************************构造预测分析表************************************************
预测分析表(若格式乱了可以看github中的源码里的注释):
_________________________________________________________________________________________________________________________________________
| | i | + | - | * | / | ( | ) | = |
-----------------------------------------------------------------------------------------------------------------------------------------
| E | E->FE' | | | | | E->FE' | | |
-----------------------------------------------------------------------------------------------------------------------------------------
| E' | | E'->+FE' | E'->-FE' | E'->*FE' | E'->/FE' | | E'->空 | E'->空 |
-----------------------------------------------------------------------------------------------------------------------------------------
| F | F->i | | | | | F->(E) | | |
-----------------------------------------------------------------------------------------------------------------------------------------
***************************************************************************************************/
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
this->predictAnalyticalMatrix.insert(pair
//遍历预测分析表并打印出来
cout << "遍历预测分析表:" << endl;
for (map
string key = i->first;
GrammerProductionRuleUnit currentGrammerProductionRuleUnit = i->second;
string leftsideVn = currentGrammerProductionRuleUnit.getLeftsideVn();
vector
cout << "M[" << key << "]=" << leftsideVn << "->";
for (vector
cout << (*j);
}
cout << endl;
}
}
SyntaxAnalysisPhase2::~SyntaxAnalysisPhase2() {
}
SyntaxAnalysisPhase2::SyntaxAnalysisPhase2(list
this->reencapsulateWordAndType(lexicalAnalysisResultList);
this->initGrammerProductionRuleVectorAndPredictAnalyticalMatrix();
this->initVtVector();
this->initVnVector();
}
/**
语法检测大致过程:
初始化一个分析栈,先把"="压栈,再把文法开始符压栈。
从词法分析结果中读取一个word(从wordPtr中读取)。如果读取的是操作数,则word="i"
while (分析栈不空) {
读取栈顶符号并赋值给x
if ("=" == word == x) {
return true;
}
if (x属于Vt) {
if (x=="nothing") {
pop出x
continue;
}
if (x==word) {
将x从栈中弹出并从wordPtr中读取下一个word
} else {
error()
}
} else {
//分析栈顶符号如果是非终结符
从预测分析表predictAnalyticalMatrix中读取key为 非终结符,word 对应的值并赋值给matrixVal
if (matrixVal is not null) {
pop出x
读取出产生式右边的各符号,按照倒序将各符号压入分析栈
} else {
error()
}
}
}
*/
bool SyntaxAnalysisPhase2::checkSyntax() {
stack
analyticalStack.push("=");
analyticalStack.push("E");
//首先把wordPtr中的第一个word读出来
string word = "";
string type = "";
if (lengthOfWordAndTypePtr > 0) {
word = wordPtr[0];
type = typePtr[0];
}
if (word != "" && type == "OPERAND") {
word = "i";
}
int i = 1;
while (!analyticalStack.empty()) {
string x = analyticalStack.top();
if (x == "=" && word == x) {
return true;
}
if (this->isVt(x)) {
if (x == "nothing") {
analyticalStack.pop(); //将x从栈中弹出
continue;
}
//如果栈顶是终结符的情况进行以下处理
if (x == word) {
analyticalStack.pop(); //将x从栈中弹出
//从wordPtr中读取下一个word
if (i < lengthOfWordAndTypePtr) {
word = wordPtr[i];
type = typePtr[i];
}
if (word != "" && type == "OPERAND") {
word = "i";
}
i++;
}
else {
cout << "Syntax error: " << wordPtr[i - 1] << " at index of " << i << " in lexical analysis result." << endl;
return false;
}
}
else {
//如果栈顶是非终结符的话则得到其产生式右边的符号串,按倒序入栈
analyticalStack.pop(); //将x从栈中弹出
//从预测分析表中获取表达式
string key = x + "," + word;
map
if (iter != this->predictAnalyticalMatrix.end()) {
//表示已经找到了
GrammerProductionRuleUnit currentGrammerProductionRuleUnit = iter->second;
string leftsideVn = currentGrammerProductionRuleUnit.getLeftsideVn();
vector
//倒序遍历产生式右边的元素,然后元素压入分析栈
for (vector
analyticalStack.push((*j));
}
}
else {
//如果在预测分析表中找不到对应key的内容,则报错
cout << "Syntax error: " << wordPtr[i - 1] << " at index of " << i << " in lexical analysis result." << endl;
return false;
}
}
}
return false;
}
void SyntaxAnalysisPhase2::initVtVector() {
this->vtVector.push_back("i");
this->vtVector.push_back("+");
this->vtVector.push_back("-");
this->vtVector.push_back("*");
this->vtVector.push_back("/");
this->vtVector.push_back("(");
this->vtVector.push_back(")");
this->vtVector.push_back("nothing");
this->vtVector.push_back("=");
}
void SyntaxAnalysisPhase2::initVnVector() {
this->vnVector.push_back("E");
this->vnVector.push_back("E'");
this->vnVector.push_back("F");
}
bool SyntaxAnalysisPhase2::isVt(string s) {
for (vector
if ((*i) == s) {
return true;
}
}
return false;
}
bool SyntaxAnalysisPhase2::isVn(string s) {
for (vector
if ((*i) == s) {
return true;
}
}
return false;
}
----------------------------------------------------------------------------------------------------------------------------------------
运行结果如下所示:
销毁尾巴数据节点 q(=)
销毁尾巴数据节点 q())
销毁尾巴数据节点 q())
销毁尾巴数据节点 q(2)
销毁尾巴数据节点 q(-)
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(*)
销毁尾巴数据节点 q())
销毁尾巴数据节点 q())
销毁尾巴数据节点 q(2)
销毁尾巴数据节点 q(-)
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(+)
销毁尾巴数据节点 q(3)
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(-)
销毁尾巴数据节点 q(2)
销毁尾巴数据节点 q(*)
销毁尾巴数据节点 q())
销毁尾巴数据节点 q(6)
销毁尾巴数据节点 q(+)
销毁尾巴数据节点 q())
销毁尾巴数据节点 q(5)
销毁尾巴数据节点 q(2)
销毁尾巴数据节点 q(.)
销毁尾巴数据节点 q(1)
销毁尾巴数据节点 q(-)
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(*)
销毁尾巴数据节点 q())
销毁尾巴数据节点 q(2)
销毁尾巴数据节点 q(-)
销毁尾巴数据节点 q(()
销毁尾巴数据节点 q(+)
销毁尾巴数据节点 q(5)
销毁尾巴数据节点 q(.)
销毁尾巴数据节点 q(0)
销毁尾巴数据节点 q(-)
销毁头节点 p( )
---------------------------------------------------------
原先的算术表达式:-0.5-2*((-1.25+6)*2-(3-2)*-2)
规范化后的算术表达式:-0.5+(-2)*(((-1.25)+6)*2-(3+(-2))*(-2))=
---------------------------------------------------------
词法分析的结果:
-0.5 OPERAND
+ OPERATOR
( OPERATOR
-2 OPERAND
) OPERATOR
* OPERATOR
( OPERATOR
( OPERATOR
( OPERATOR
-1.25 OPERAND
) OPERATOR
+ OPERATOR
6 OPERAND
) OPERATOR
* OPERATOR
2 OPERAND
- OPERATOR
( OPERATOR
3 OPERAND
+ OPERATOR
( OPERATOR
-2 OPERAND
) OPERATOR
) OPERATOR
* OPERATOR
( OPERATOR
-2 OPERAND
) OPERATOR
) OPERATOR
=
-------------------开始语法分析--------------------------------------
以下是执行有递归的预测分析:
matched:-0.5
matched:+
matched:(
matched:-2
matched:)
matched:*
matched:(
matched:(
matched:(
matched:-1.25
matched:)
matched:+
matched:6
matched:)
matched:*
matched:2
matched:-
matched:(
matched:3
matched:+
matched:(
matched:-2
matched:)
matched:)
matched:*
matched:(
matched:-2
matched:)
matched:)
通过有递归的预测分析的语法分析
以下是执行非递归的预测分析:
文法产生式列表:
E->FE'
E'->+FE'
E'->-FE'
E'->*FE'
E'->/FE'
E'->nothing
F->(E)
F->i
遍历预测分析表:
M[E',)]=E'->nothing
M[E',*]=E'->*FE'
M[E',+]=E'->+FE'
M[E',-]=E'->-FE'
M[E',/]=E'->/FE'
M[E',=]=E'->nothing
M[E,(]=E->FE'
M[E,i]=E->FE'
M[F,(]=F->(E)
M[F,i]=F->i
通过非递归的预测分析的语法分析
语法检测正确
---------------------------------------------------------
表达式求值的结果:-0.5+(-2)*(((-1.25)+6)*2-(3+(-2))*(-2))=-23.500000
完毕!