算符优先分析
一、功能描述:
根据算符优先分析法,将表达式进行语法分析,判断一个表达式是否正确并计算结果。
二、程序结构描述:
全局变量:
#define N 8 ——算符个数
string ops = "+-*/()i#"; ——算符
map<char, int> ophash; ——算符哈希表
int opv[N][N]; ——算符优先表: 0-无 1-大于 2-等于 3-小于
string str; ——存放输入串
int top; ——输入串位置
stack<char> st; ——堆栈
stack<int> stnum; ——存储结果堆栈
bool divzero; ——除零标志
程序分成如下几个函数:
void init() —— 初始化函数:构造算符优先表。
bool isNumber(string s, int v = 0) ——判断一个串是否是无符号整数
int toInt(string s) —— 转换一个数字串为无符号整数
string next() —— 取得下一个单词
bool out(string s) —— 归约过程,不能归约则返回false
char first() —— 获得距离栈顶最近的VT元素
bool check() —— 分析过程,不匹配则返回false
main —— 主函数
处理输入串的部分,对输入的文件test2.txt内容进行算符优先语法分析。
* 输入串必须以“;”结束。当输入串合法时将计算该串的值,结果将显示在控制台。
程序总体执行流程详见代码。
三、实验总结:
完成总使用八课时:
其中纸上时间四课时,编程两课时,调试两课时。
收获:
了解到一些算符优先语法分析的知识。
四、程式源码:
-
#include<iostream>
-
#include<string>
-
#include<stack>
-
#include<map>
-
using namespace std;
-
-
#define N 8 // 算符个数
-
string ops = "+-*/()i#"; // 算符
-
-
map<char, int> ophash; // 算符哈希表
-
int opv[N][N]; // 算符优先表: 0-无 1-大于 2-等于 3-小于
-
-
string str; // 存放输入串
-
int top; // 输入串位置
-
stack<char> st; // 堆栈
-
stack<int> stnum; // 存储结果堆栈
-
bool divzero; // 除零标志
-
-
/* 判断一个串是否是无符号整数 */
-
bool isNumber(string s, int v = 0){
-
if(s[v]>='0'&&s[v]<='9')return true;
-
return false;
-
}
-
-
/* 转换一个数字串为无符号整数 */
-
int toInt(string s){
-
int x = 0;
-
for(int i=0; s[i]!='\0'; i++){
-
x = x*10 + s[i]-'0';
-
}
-
return x;
-
}
-
-
/* 取得下一个单词 */
-
string next(){
-
string tmp = "";
-
-
if(str.size()-1==top){
-
top ++;
-
return "#";
-
}
-
-
if(str.size()==top)return "";
-
-
// 如果不是数字 说明是运算符 直接返回该字符的串
-
if(!isNumber(str, top)){
-
tmp.append(1, str[top++]);
-
return tmp;
-
}
-
-
// 如果是数字 读完整个数字
-
while(isNumber(str, top))tmp.append(1, str[top++]);
-
return tmp;
-
}
-
-
/* 初始化 */
-
void init(){
-
int i,j;
-
-
// 构造算符哈希表
-
for(i=0; i<N; i++){
-
ophash[ops[i]] = i;
-
}
-
-
// 构造算符优先表
-
{
-
{
-
opv[ophash['+']][ophash['+']] = 1;
-
opv[ophash['+']][ophash['-']] = 1;
-
opv[ophash['+']][ophash['*']] = 3;
-
opv[ophash['+']][ophash['/']] = 3;
-
opv[ophash['+']][ophash['(']] = 3;
-
opv[ophash['+']][ophash[')']] = 1;
-
opv[ophash['+']][ophash['i']] = 3;
-
opv[ophash['+']][ophash['#']] = 1;
-
}
-
{
-
opv[ophash['-']][ophash['+']] = 1;
-
opv[ophash['-']][ophash['-']] = 1;
-
opv[ophash['-']][ophash['*']] = 3;
-
opv[ophash['-']][ophash['/']] = 3;
-
opv[ophash['-']][ophash['(']] = 3;
-
opv[ophash['-']][ophash[')']] = 1;
-
opv[ophash['-']][ophash['i']] = 3;
-
opv[ophash['-']][ophash['#']] = 1;
-
}
-
{
-
opv[ophash['*']][ophash['+']] = 1;
-
opv[ophash['*']][ophash['-']] = 1;
-
opv[ophash['*']][ophash['*']] = 1;
-
opv[ophash['*']][ophash['/']] = 1;
-
opv[ophash['*']][ophash['(']] = 3;
-
opv[ophash['*']][ophash[')']] = 1;
-
opv[ophash['*']][ophash['i']] = 3;
-
opv[ophash['*']][ophash['#']] = 1;
-
}
-
{
-
opv[ophash['/']][ophash['+']] = 1;
-
opv[ophash['/']][ophash['-']] = 1;
-
opv[ophash['/']][ophash['*']] = 1;
-
opv[ophash['/']][ophash['/']] = 1;
-
opv[ophash['/']][ophash['(']] = 3;
-
opv[ophash['/']][ophash[')']] = 1;
-
opv[ophash['/']][ophash['i']] = 3;
-
opv[ophash['/']][ophash['#']] = 1;
-
}
-
{
-
opv[ophash['(']][ophash['+']] = 3;
-
opv[ophash['(']][ophash['-']] = 3;
-
opv[ophash['(']][ophash['*']] = 3;
-
opv[ophash['(']][ophash['/']] = 3;
-
opv[ophash['(']][ophash['(']] = 3;
-
opv[ophash['(']][ophash[')']] = 2;
-
opv[ophash['(']][ophash['i']] = 3;
-
opv[ophash['(']][ophash['#']] = 0;
-
}
-
{
-
opv[ophash[')']][ophash['+']] = 1;
-
opv[ophash[')']][ophash['-']] = 1;
-
opv[ophash[')']][ophash['*']] = 1;
-
opv[ophash[')']][ophash['/']] = 1;
-
opv[ophash[')']][ophash['(']] = 0;
-
opv[ophash[')']][ophash[')']] = 1;
-
opv[ophash[')']][ophash['i']] = 0;
-
opv[ophash[')']][ophash['#']] = 1;
-
}
-
{
-
opv[ophash['i']][ophash['+']] = 1;
-
opv[ophash['i']][ophash['-']] = 1;
-
opv[ophash['i']][ophash['*']] = 1;
-
opv[ophash['i']][ophash['/']] = 1;
-
opv[ophash['i']][ophash['(']] = 0;
-
opv[ophash['i']][ophash[')']] = 1;
-
opv[ophash['i']][ophash['i']] = 0;
-
opv[ophash['i']][ophash['#']] = 1;
-
}
-
{
-
opv[ophash['#']][ophash['+']] = 3;
-
opv[ophash['#']][ophash['-']] = 3;
-
opv[ophash['#']][ophash['*']] = 3;
-
opv[ophash['#']][ophash['/']] = 3;
-
opv[ophash['#']][ophash['(']] = 3;
-
opv[ophash['#']][ophash[')']] = 0;
-
opv[ophash['#']][ophash['i']] = 3;
-
opv[ophash['#']][ophash['#']] = 2;
-
}
-
}
-
}
-
-
/* 归约 */
-
bool out(string s){
-
if(s=="i"){
-
st.push('E');
-
return true;
-
}
-
if(s==")E("||s=="E/E"||s=="E*E"||s=="E-E"||s=="E+E"){
-
st.push('E');
-
if(s[0] == 'E'){
-
int b = stnum.top();
-
stnum.pop();
-
int a = stnum.top();
-
stnum.pop();
-
switch(s[1]){
-
case '/':
-
if(b==0){
-
divzero = true;
-
b = 1;
-
}
-
stnum.push(a/b);
-
break;
-
case '*':
-
stnum.push(a*b);
-
break;
-
case '-':
-
stnum.push(a-b);
-
break;
-
case '+':
-
stnum.push(a+b);
-
break;
-
}
-
}
-
return true;
-
}
-
return false;
-
}
-
-
/* 获得距离栈顶最近的VT元素 */
-
char first(){
-
stack<char> tmpst;
-
while(!st.empty()&&ops.find(st.top(),0)==-1){
-
tmpst.push(st.top());
-
st.pop();
-
}
-
char x = st.top();
-
while(!tmpst.empty()){
-
st.push(tmpst.top());
-
tmpst.pop();
-
}
-
return x;
-
}
-
-
/* 进行算符优先分析 */
-
bool check(){
-
string s;
-
char t;
-
bool flag = false;
-
-
while(!st.empty())st.pop();
-
st.push('#');
-
while((s=next())!="\0"){
-
if(isNumber(s)){ // 如果串是无符号整数 则把它看成i
-
t = 'i';
-
stnum.push(toInt(s));
-
}else{ // 否则当成操作符
-
t = s[0];
-
}
-
char h = st.top();
-
char b = first();
-
bool f;
-
switch(opv[ophash[first()]][ophash[t]]){
-
case 1: // 大于则归约
-
s = ""; f = false;
-
do{
-
s.append(1, h);
-
st.pop();
-
if(out(s)){
-
s = "";
-
break;
-
}
-
if(st.empty())break;
-
h = st.top();
-
f = first()!=h;
-
}while(f||opv[ophash[first()]][ophash[b]]!=3); // 直到第一个<关系出现
-
if(s==""){
-
top--;
-
flag = true;
-
break;
-
}
-
// 不能归约则报错
-
if(!out(s)){
-
if(t=='#'&&st.size()==1&&s=="E")return true;
-
if(!flag){
-
return false;
-
}else{
-
for(int i = s.size()-1; i>=0; i--) st.push(s[i]);
-
st.push(t);
-
flag = false;
-
}
-
}else{
-
top--;
-
flag = true;
-
}
-
break;
-
case 2: // 等于
-
if(t=='#'){ // 输入是# 则检测是否正常结束
-
if(top<str.size()-1)return false;
-
return true;
-
}
-
// 否则移进
-
case 3: // 小于则移进
-
st.push(t);
-
flag = false;
-
break;
-
case 0: // 无关则报错
-
return false;
-
}
-
}
-
-
return true;
-
}
-
-
int main(){
-
init();
-
-
freopen("test2.txt", "r", stdin);
-
-
while(cin>>str){
-
if(str == "exit")break;
-
divzero = false;
-
-
cout<<str<<endl;
-
-
top = 0;
-
-
if(check()){ // 若合法则输出计算结果
-
if(!divzero){
-
cout<<"正确"<<endl
-
<<"结果是"<<stnum.top()<<endl;
-
}else{
-
cout<<"除零异常!"<<endl;
-
}
-
}else{ // 非法提示
-
cout<<"错误"<<endl;
-
}
-
-
cout<<"===================================="<<endl;
-
}
-
-
return 0;
-
}