先来实验指导书:
一、实验任务
设计、编制并调试一个中缀表达转换为后缀表达的实验程序,加深对词法分析、语法分析、语义分析及代码生成的理解。
二、 实验内容
1、词法
输入:扩展ASCII码字符集字符。除大小写26英文字母(letter)和数字0-9(digit)以及+ - * / ^ = ; , ( )以外,所有其他字符一律按等同于空格处理,一般用来分隔单词。
输出:识别单词,单词包括关键字、运算符、界符、标识符和整型常数。
(1)关键字:var
(2)运算符和界符:+ - * / ^ = ; , ( )
其中:乘除运算符(*, /)返回具有不同属性值的单词mulop,
加减运算符(+, -)返回具有不同属性值的单词addop。?
(3)标识符(id)和整型常数(num):
标识符(id)和整型常数(num)最大长度为8个字符,定义如下。
id = letter (letter | digit)* //以字母打头字母和数字组成的符号串
num = digit digit*//整形常数必须大于等于一位
2、语法(主要!!)
根据输入的单词序列,分析是否符合语法规则,如果不符合,应指明位置与理由;如果符合,则执行相应的语义子程序完成语义分析及中缀表达转换为后缀表达的过程。需注意的是,这里给出的是二义文法,从语义上考虑,表达式的计算按先幂次运算(^),再乘除运算(*, /)的 最后加减运算(+, - )的优先顺序; 括号((, ))用于调整运算先后顺序,既括号内部分先计算;赋值运算(=)最后进行。本实验系统的语法规则是:
program → compound
复合功能
compound → declaration assignstatement compound | ε
定义 赋值
declaration → var identifier_list ; | ε
定义 标识符
dentifier_list → id , dentifier_list | id
标识符中间可以有逗号相隔
assignstatement → id = expression ; | ε
词句 必须有赋值号
expression → expression addop expression |
加号两侧必须有单词
expression mulop expression |
乘号两侧必须有单词
expression ^ expression |
幂两侧必须有单词
( expression ) |
括号中间必须有单词
id | num
3、语义分析及代码生成
语义分析的主要任务是判断变量是否先定义后使用(只有这一个)。代码生成的的主要任务是将赋值语句从中缀表达转换为后缀表达。
三、 实验示例
示例1:
输入:
var a,b;
a = 10;
b = 5;
var x;
x = a + ( 25 * b ) ^ 2;
输出:
a 10 =
b 5 =
x a 25 b * 2 ^ + =
示例2:
输入:
var a;
a = 10;
b = 5;
var x;
x = a + ( 25 * b ^ 2;
输出:
第3行错误: 变量b未定义。
第5行错误: 缺右括号。
这坨代码全程都是压力比较大的情况下敲完的,高数复习进度比较慢,可是感觉又患了强迫症,代码不敲完不想停,哎挺简单的实验被我敲了350多行,还是能力不足,应该想出更好的方法。首先用逆波兰解决掉中缀转后缀,然后就是错误处理了。他这里不满足所有语法的都算错误吧,分为词法语法和语义,前前后后共找出17种错误,后来比较麻烦转化成signal数组检错,处理这些错误画了好多时间,一百多行的代码变成300多行。。
这里我模拟的是解释程序,老师后来让改翻译程序,改了的有点难看,这里就贴出解释程序:
附测试数据:
6
5
var a,b;
a = 10;
b = 5;
var x;
x = a + ( 25 * b ) ^ 2;
5
var a;
a = 10;
b = 5;
var x;
x = a + ( 25 * b ^ 2;
2
3 = 1 + 2;
3 = 1 & 2;
7
var apple666,banana,fffffffff,ff,0ccc,666p,1234;
banana = ( apple666 + 3 ) * 2;
ff = ( apple666 + 333333333 ) * 2;
banana = ( apple666 + 3 ) * 2
var a,b c d;
var banana;
9 + ( 3 - 1) * 3 + 10 / 2 = ;
13
var x;
x = 9 + ( 3 - 1) * 3 + 10 / 2;
x = 999999999 + 1;
x = 9 + ( 3 - 1) * + / 2;
x = 9 + ( 3 - 1) * - / 2;
x = 9 + ( 3 - 1) + * / 2;
x = 9 + ( 3 - 1) * 3 + / ;
x = 9 + ( 3 - 1) * 3 + ^ ;
x = 9 + ( - ) * 3 + 10 / 2;
x = 9 + ( ) * 3 + 10 / 2;
x = 9 + ( 3 - 1) * 3 + 10 2;
x = 9 + (((( 3 - 1 ) * 3 + 10 / 2;
x = 9 + ( 3 - 1 )))))) * 3 + 10 / 2;
3
var affffffffff,0xx;
affffffffff = 1+2;
0xx = 1+2;
#include <stdio.h>
#include <algorithm>
#include <string.h>
#include <stack>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1005;
const int INF = 0x3f3f3f3f;
struct node
{
string id;
}table[N];//标识符
string ans;
bool flag0;//true为执行定义语句
int flag1;//单词长度大于8的非法单词的数量
int flag2;//开头是数字后面有字母的非法单词的数量
bool flag3;//判断是否加封号
int flag4;//非第一个变量前不是是逗号而是空格的变量个数
bool flag5;//true代表有重定义的变量
bool flag51;//定义变量长度大于8
bool flag52;//首字母为数字的非法变量
bool flag6;//true代表没有赋值号
bool flag7;//加号两侧必须有单词或括号
bool flag8;//减号两侧必须有单词或括号
bool flag9;//乘号两侧必须有单词或括号
bool flag10;//除号两侧必须有单词或括号
bool flag11;//幂号两侧必须有单词或括号
bool flag12;//括号中间必须有单词
bool flag13;//两个数字挨在了一块,之间缺少运算符
int flag14;//不为0就是缺右括号,小于0缺左括号,大于0缺右括号
bool flag15;//错误类型,表示变量未定义
int Signal[N];//将中缀式转为信号标记
int signalnum;//信号标记的数量
int priority(char ch)
{
switch(ch)
{
case '+': return 1;
case '-': return 1;
case '*': return 2;
case '/': return 2;
case '^': return 3;
default: return 0;
}
}
void initcase()
{
for(int i = 0; i <= N; i++)
{
table[i].id.clear();
}
}
void initrow()
{
flag0 = false;
flag1 = 0;
flag2 = 0;
flag3 = false;
flag4 = 0;
flag5 = flag51 = flag52 = false;
flag6 = false;
flag7 = flag8 = flag9 = flag10 = flag11 = flag12 = flag13 = false;
flag14 = 0;
flag15 = false;
memset(Signal, 0, sizeof(Signal));
signalnum = 0;
}
void check()
{
for(int i = 0; i < signalnum; i++)
{
if(Signal[i]==5)//加
{
if((Signal[i-1]>3&&Signal[i+1]>3) || (Signal[i-1]==2&&Signal[i+1]==3)) flag7 = true;
}
else if(Signal[i]==6)//减
{
if((Signal[i-1]>3&&Signal[i+1]>3) || (Signal[i-1]==2&&Signal[i+1]==3)) flag8 = true;
}
else if(Signal[i]==7)//乘
{
if((Signal[i-1]>3&&Signal[i+1]>3) || (Signal[i-1]==2&&Signal[i+1]==3)) flag9 = true;
}
else if(Signal[i]==8)//除
{
if((Signal[i-1]>3&&Signal[i+1]>3) || (Signal[i-1]==2&&Signal[i+1]==3)) flag10 = true;
}
else if(Signal[i]==9)//幂
{
if((Signal[i-1]>3&&Signal[i+1]>3) || (Signal[i-1]==2&&Signal[i+1]==3)) flag11 = true;
}
else if(Signal[i]==2)//两个括号挨在了一起
{
if(Signal[i+1]==3) flag12 = true;
}
else if(Signal[i]==1)//两个单词挨在了一起
{
if(Signal[i+1]==1) flag13 = true;
}
}
}
int main()
{
freopen("in.txt", "r", stdin);
int t, allrow, Case = 0;
scanf("%d", &t);
while(t--)//用例数
{
scanf("%d", &allrow);//一个样例读入的指令数
getchar();
initcase();
printf("Case %d:\n", ++Case);
int cnt = 0;
for(int row = 1; row <= allrow; row++)
{
initrow();
string s;
string wa1;//错误参数
getline(cin,s);
int len = s.size();
if(s[len-1]==';' && flag3==false) flag3 = true;
if(s[0]=='v'&&s[1]=='a'&&s[2]=='r'&&s[3]==' ')//定义语句
{
flag0 = true;
// printf("当前有定义关键字: ");
int i = 4, varnum = 0;
string tmp1;
while(i<len)
{
varnum++;
tmp1.clear();
bool judge = false;
if(((s[i]>='a'&&s[i]<='z')||(s[i]>='A'&&s[i]<='Z')) && (s[i-1]==',' || s[i-1]==' '))//合法变量
{
judge = true;//说明是一个变量
if(varnum!=1 && s[i-1]==' ')
{
flag4++;
judge = false;//前面没逗号则变量定义错误
}
tmp1+=s[i];
i++;
while((s[i]>='0'&&s[i]<='9') || (s[i]>='a'&&s[i]<='z') || (s[i]>='A'&&s[i]<='Z'))
{
tmp1+=s[i];
i++;
}
}
else if((s[i]>='0'&&s[i]<='9') && (s[i-1]==',' || s[i-1]==' '))//开头是数字的都是非法变量
{
if(varnum!=1 && s[i-1]==' ') flag4++;
tmp1+=s[i];
i++;
flag52 = true;
while((s[i]>='0'&&s[i]<='9') || (s[i]>='a'&&s[i]<='z') || (s[i]>='A'&&s[i]<='Z'))
{
tmp1+=s[i];
i++;
}
if(tmp1.size() > 8) flag51 = true;
}
else i++;
//判断重定义变量
int m;
for(m = 0; m < cnt; m++)
{
if(!tmp1.compare(table[m].id))//如果在定义表中找到此字符变量
{
flag5 = true;
judge = false;//非法变量,不再定义
break;
}
}
if(judge && flag3)
{
table[cnt++].id = tmp1;
if(tmp1.size() > 8) flag51 = true;
}
}
/* for(int k = 0; k < cnt; k++)
{
cout << table[k].id;
printf(" ");
}
printf("\n");*/
}
else//赋值语句
{
stack<char>sta;
sta.push('#');
int i = 0, wordid = 0;//wordid代表第几个单词后面
ans.clear();
while(i < len)
{
if(wordid==1 && (s[i+1] != '=')) flag6 = true;
if(s[i]==' ')
{
i++;
}
else if(s[i]==';' || s[i]=='=')
{
Signal[signalnum++] = 4;
i++;
}
else if(s[i]=='(')
{
sta.push(s[i]);
Signal[signalnum++] = 2;
i++;
flag14++;
}
else if(s[i]==')')
{
flag14--;
Signal[signalnum++] = 3;
if(flag14 >= 0)
{
while(sta.top()!='(')
{
ans+=sta.top();
sta.pop();
ans+=' ';
}
sta.pop();
}
i++;
}
else if(s[i]=='+' || s[i]=='-' || s[i]=='*' || s[i]=='/' || s[i]=='^')
{
if(s[i]=='+') Signal[signalnum++] = 5;
else if(s[i]=='-') Signal[signalnum++] = 6;
else if(s[i]=='*') Signal[signalnum++] = 7;
else if(s[i]=='/') Signal[signalnum++] = 8;
else if(s[i]=='^') Signal[signalnum++] = 9;
while(priority(sta.top()) >= priority(s[i]))
{
ans+=sta.top();
ans+=' ';
sta.pop();
}
sta.push(s[i]);
i++;
}
else if(s[i]>='0'&&s[i]<='9')//是数字
{
Signal[signalnum++] = 1;
int num = 1;
ans+=s[i];
i++;
bool letter = false;;
while((s[i]>='0'&&s[i]<='9') || (s[i]>='a'&&s[i]<='z') || (s[i]>='A'&&s[i]<='Z'))
{
if((s[i]>='a'&&s[i]<='z') || (s[i]>='A'&&s[i]<='Z')) letter = true;
ans+=s[i];
i++;
num++;
}
ans+=' ';
if(num > 8) flag1++;
if(letter) flag2++;
}
else if((s[i]>='a'&&s[i]<='z') || (s[i]>='A'&&s[i]<='Z'))//是字母
{
Signal[signalnum++] = 1;
int num = 1;
string tmp2;
tmp2+=s[i];
i++;
while((s[i]>='a'&&s[i]<='z') || (s[i]>='A'&&s[i]<='Z') || (s[i]>='0'&&s[i]<='9'))
{
tmp2+=s[i];
i++;
num++;
}
if(num > 8) flag1++;
//在表中查看是否存在此变量
int k;
for(k = 0; k < cnt; k++)
{
if(!tmp2.compare(table[k].id))//如果在定义表中找到此字符变量
{
ans+=tmp2;
ans+=' ';
break;
}
}
if(k == cnt)
{
flag15 = 1;
wa1.clear();
wa1 = tmp2;
}
}
else i++;
wordid++;
}
while(sta.top()!='#')
{
ans+=sta.top();
sta.pop();
ans+=' ';
}
ans+='=';
/* printf("signal!............:\n");
for(int z = 0; z < signalnum; z++)
{
printf("%d ", Signal[z]);
}
printf("\n");*/
check();
}
if(flag0)//定义语句可能会出现的错误
{
if(!flag3) cout << "第" << row << "行错误: 未加封号" << endl;
if(flag4 > 0) cout << "第" << row << "行错误: 有" << flag4 << "个非第一个变量前不是是逗号而是空格" << endl;
if(flag5) cout << "第" << row << "行错误: 有重定义变量" << endl;
if(flag51) cout << "第" << row << "行错误: 定义变量长度大于8" << endl;
if(flag52) cout << "第" << row << "行错误: 出现首字母为数字的非法变量" << endl;
continue;
}
if(flag1 || flag2 || (!flag3) || flag6 || flag7 || flag8 || flag9 || flag10 || flag11 || flag12 || flag13 || flag14 || flag15)
{
// printf("[%d]\n", flag14);
if(flag1 > 0) cout << "第" << row << "行错误: 有" << flag1 << "个长度大于8的非法单词" << endl;
if(flag2 > 0) cout << "第" << row << "行错误: 有" << flag2 << "个开头数字后面有字母的非法单词" << endl;
if(!flag3) cout << "第" << row << "行错误: 未加封号" << endl;
if(flag6) cout << "第" << row << "行错误: 第一个单词后面不是赋值号" << endl;
if(flag7) cout << "第" << row << "行错误: 加号两侧只能有单词和括号,且不能同时为左右括号" << endl;
if(flag8) cout << "第" << row << "行错误: 减号两侧只能有单词和括号,且不能同时为左右括号" << endl;
if(flag9) cout << "第" << row << "行错误: 乘号两侧只能有单词和括号,且不能同时为左右括号" << endl;
if(flag10) cout << "第" << row << "行错误: 除号两侧只能有单词和括号,且不能同时为左右括号" << endl;
if(flag11) cout << "第" << row << "行错误: 幂号两侧只能有单词和括号,且不能同时为左右括号" << endl;
if(flag12) cout << "第" << row << "行错误: 左括号和右括号挨在了一起,中间不能为空" << endl;
if(flag13) cout << "第" << row << "行错误: 两个单词挨在了一起,中间应该有运算符" << endl;
if(flag14 > 0) cout << "第" << row << "行错误: 缺" << flag14 << "个右括号" << endl;
if(flag14 < 0) cout << "第" << row << "行错误: 缺" << flag14*(-1) << "个左括号" << endl;
if(flag15 == 1) cout << "第" << row << "行错误: 变量" << wa1 << "未定义" << endl;
}
else cout << ans << "\n";
}
printf("\n\n");
}
return 0;
}