目录
一、实验题目
编写识别由下列文法所定义的表达式的预测分析程序。
E E+T | E-T | T
T à T*F | T/F | F
F à (E) | i
输入:从键盘输入表达式,或每行含有一个表达式的文本文件。其中,表达式中含有任意的十进制数或十六进制数,并以#结束,如:80-5H+(6+1)+4h/2#。
输出:分析成功或不成功信息。
二、分析与设计
(1) 分析
a) 判断文法是否为OPG文法
答:因为在该文法中,任意产生式都不包含两个相邻的非终结符,且任意两个算符之间只存在一种优先关系,因此该文法是OPG文法。
b) 求FIRSTVT与LASTVT集
c) 构造算符优先关系表
+ | - | * | / | i | ( | ) | # | |
+ | > | > | < | < | < | < | > | > |
- | > | > | < | < | < | < | > | > |
* | > | > | > | > | < | < | > | > |
/ | > | > | > | > | < | < | > | > |
i | > | > | > | > | > | > | ||
( | < | < | < | < | < | < | = | |
) | > | > | > | > | > | > | ||
# | < | < | < | < | < | < | = |
(2) 设计
a) 程序总体结构框图
b) 主要数据结构及实现
进行算符优先分析程序的一个重要的数据结构是分析栈,主要实现存取的数据先进后出的功能,分析栈使用双向链表来实现,并定义了相应的出栈和入栈操作。利用二维数组table[8][8]来实现算符优先关系表,行和列分别对应于各个算符,表中数据表示在行和列上的两个元素的关系:大于关系为1,等于关系为0,小于关系为-1,出错为9。
c) 主要程序模块流程图
(3) 编码实现
(4) 上机调试
三、源代码
#include <iostream>
#include<cstring>
#include<string>
#include "conio.h"
#include <vector>
using namespace std;
/*词法分析
输入:包含十进制和十六进制的表达式
输出:标准形式的表达式
*/
enum type { digit, Hh, AF, letter, oparetors };
char* q;//指向输入符号串中当前的字符
char word[20];//存储当前识别的单词
int state = 0;//表示所处的状态,初始状态为0
int i;//单词的下标
int j = 0;//输出数组out数组的下标
int wordErro = 1;//词法分析是否正确的标志,1:正确;0:错误
char line[30]; //保存读入的一行表达式
char out[30]; //保存词法分析后带i的表达式
int isDigitOrChar(char ch) {
if (ch >= 48 && ch <= 57) //数字
return digit;
else if (ch == 72 || ch == 104)
return Hh; //Hh
else if ((ch >= 65 && ch <= 70) || (ch >= 97 && ch <= 102))//ABCDEF或者abcdef
return AF;
else if ((ch >= 65 && ch <= 90) || (ch >= 97 && ch <= 122))//除A-F外的其他字母
return letter;
else if (ch == 40 || ch == 41 || ch == 42 || ch == 43 || ch == 45 || ch == 47 || ch == 35)//(、)、*、+、-、/ 、# 这六个运算符
return oparetors;
}
//词法分析
int toExpress(char* words, char* out) {
wordErro = 1;
state = 0;
i = 0;
j = 0;
printf("词法分析的结果为:");
q = words;
while (*q) {
switch (state) {
case 0: //当前为0状态
switch (isDigitOrChar(*q)) {
case digit://数字
word[i++] = *q;
state = 2;
break;
case Hh: //Hh
case AF: //ABCDEF或者abcdef
case letter: //除A-F外的其他字母
word[i++] = *q;
state = 1;
break;
case oparetors: //运算符
word[i++] = *q;
printf("%c", word[0]);
out[j++] = word[0];
state = 0;
memset(word, 0, sizeof(word));
i = 0;
break;
default: //其他字符(非法)
word[i++] = *q;
state = 5; //转到出错状态
break;
}
break;
case 1: //当前为1状态
switch (isDigitOrChar(*q)) {
case digit://数字
word[i++] = *q;
state = 1;
break;
case Hh: //Hh
case AF: //ABCDEF或者abcdef
case letter: //除A-F外的其他字母
word[i++] = *q;
state = 1;
break;
case oparetors: //运算符,1状态可以为标识符的结束状态
word[i++] = *q;
state = 0;
printf("i");
out[j++] = 'i';
for (int m = 0;m < sizeof(word);m++) {
if (isDigitOrChar(word[m]) == oparetors) {
printf("%c", word[m]);
out[j++] = word[m];
}
}
memset(word, 0, sizeof(word));
i = 0;
break;
default: //其他字符(非法)
word[i++] = *q;
state = 5; //转到出错状态
break;
}
break;
case 2: //当前为2状态
switch (isDigitOrChar(*q)) {
case digit://数字
word[i++] = *q;
state = 2;
break;
case Hh: //Hh
word[i++] = *q;
state = 3;
break;
case AF: //ABCDEF或者abcdef
word[i++] = *q;
state = 4;
break;
case oparetors: //运算符,2状态可以为十进制整数的结束状态
word[i++] = *q;
state = 0;
printf("i");
out[j++] = 'i';
for (int m = 0;m < sizeof(word);m++) {
if (isDigitOrChar(word[m]) == oparetors) {
printf("%c", word[m]);
out[j++] = word[m];
}
}
memset(word, 0, sizeof(word));
i = 0;
break;
default: //其他字符(非法)
word[i++] = *q;
state = 5; //转到出错状态
break;
}
break;
case 3: //当前为3状态
switch (isDigitOrChar(*q)) {
case oparetors: //运算符,3状态可以为十六进制整数的结束状态
word[i++] = *q;
state = 0;
printf("i");
out[j++] = 'i';
for (int m = 0;m < sizeof(word);m++) {
if (isDigitOrChar(word[m]) == oparetors) {
printf("%c", word[m]);
out[j++] = word[m];
}
}
memset(word, 0, sizeof(word));
i = 0;
break;
default: //其他字符(非法)
word[i++] = *q;
state = 5;
break;
}
break;
case 4: //当前为4状态
switch (isDigitOrChar(*q)) {
case digit://数字
word[i++] = *q;
state = 4;
break;
case Hh: //Hh
word[i++] = *q;
state = 3;
break;
case AF: //ABCDEF或者abcdef
word[i++] = *q;
state = 4;
break;
default: //其他字符(非法)
word[i++] = *q;
state = 5; //转到出错状态
break;
}
break;
case 5: //出错状态
switch (isDigitOrChar(*q)) {
case oparetors: //运算符
word[i++] = *q;
state = 0;
memset(word, 0, sizeof(word));
i = 0;
wordErro = 0;
break;
default: //其他字符(非法)
word[i++] = *q;
state = 5; //转到出错状态
break;
}
break;
}
q++; //指针下移(指向输入符号串的下一个字符)
}
if (wordErro == 1) {
printf("\n》》》词法正确!《《《\n下面进行语法分析......\n");
}
else {
printf("\n》》》词法错误!《《《\n");
}
return 0;
}
/*
读入由i和运算符组成的式子,
判断语法是否合法
*/
char sym; //保存输入的字符
int cur; //表达式字符串的当前下标
char cmpchar;//出栈的字符
char current;//输入串中当前分析的字符
int error; //语法分析的错误标志 0:正确 -1:错误
//定义分析栈的数据结构,双向链表
typedef struct node {
char data;
struct node* before;
struct node* next;
}*SNode;
SNode temp, top;
//定义入栈操作
void push(char c) {
temp = (SNode)malloc(sizeof(node));//申请内存空间
temp->data = c;
temp->before = top;
temp->next = NULL;
top->next = temp;
top = temp;
}
//定义出栈操作
char pop() {
cmpchar = top->data;
if (top->before != NULL) {
temp = top;
top = top->before;
top->next = NULL;
free(temp);
}
return cmpchar;
}
// 分析表结构如下:
// 存储算符优先关系表
// 大于关系为1,等于关系为0,小于关系为-1,出错为9
//+, -, *, /, (, ), i, #
int table[8][8] ={ /* + */{ 1, 1, -1, -1, -1, 1, -1, 1},
/* - */{ 1, 1, -1, -1, -1, 1, -1, 1},
/* * */{ 1, 1, 1, 1, -1, 1, -1, 1},
/* / */{ 1, 1, 1, 1, -1, 1, -1, 1},
/* ( */{-1,-1, -1, -1, -1, 0, -1, 9},
/* ) */{ 1, 1, 1, 1, 9, 1, 9, 1},
/* i */{ 1, 1, 1, 1, 9, 1, 9, 1},
/* # */{-1,-1, -1, -1, -1, 9, -1, 0}
};
//将字符数字化
int Vt2d(char current) {
switch (current) {
case '+':j = 0;break;
case '-':j = 1;break;
case '*':j = 2;break;
case '/':j = 3;break;
case '(':j = 4;break;
case ')':j = 5;break;
case 'i':j = 6;break;
case '#':j = 7;
}
return j;
}
//判断是否是终结符
bool isVt(char ch) {
if (isDigitOrChar(ch) == oparetors || ch == 'i') return true;
else return false;
}
//读入当前字符
char read(string line, int k) {
return line[k];
}
void showMess(int l, string line) {
node* t = top;
vector<char> v;
while (t) {
v.push_back(t->data);
t = t->before;
}
string str;
for (int i = v.size() - 1; i >= 0; i--) {
str += v[i];
}
cout << str;
cout << "\t\t";
for (int i = l; i < line.size(); i++) {
cout << line[i];
}
cout << "\t\t";
}
//语法分析
//输入:符号串line,以‘#’结束
//输出:error=0:语法正确 error=-1:语法错误
void gramAnalysis(char line[]) {
int i, j;
push('#');
int k = 1;
error = 0;//初始化为正确
for (int l = 0; ; l++) {
showMess(l, line);
// 退出条件
if (top->data == 'N' && top->before->data == '#' && line[l] == '#') break;
node* sktemp = top;
char sk = top->data;
char a = line[l];
// 任何两终结符之间最多只有一非终结符,若非终结符往前寻找一位即可
if (!isVt(sk)) {
sk = top->before->data;
sktemp = top->before;
}
i = Vt2d(sk); // 获取栈顶终结符
j = Vt2d(a); // 获取当前输入符号
switch (table[i][j]) {
case 9: // 语法错误
error = -1;
break;
case 0:
cout << "=" << "\t\t" << "移进" << endl;
push(a);
break;
case -1: // 小于,移进
cout << "<" << "\t\t" << "移进" << endl;
push(a);
break;
case 1: // 大于,归约
cout << ">" << "\t\t" << "归约" << endl;
node* Q = sktemp;
node* sj = sktemp;
do {
sj = Q;
if (isVt(Q->before->data)) Q = Q->before;
else Q = Q->before->before;
} while (table[Vt2d(Q->data)][Vt2d(sj->data)] == 0);
int idx = 0;
while (top != Q) {
char c = pop();
idx++;
}
if (idx % 2 == 0) {
error = -1;
break;
}
push('N');
l--;
break;
}
if (error == -1) break;
}
}
/*
主分析程序,先词法分析,
若正确则进行语法分析
*/
int main() {
FILE* fp;
//打开文本文件
fopen_s(&fp, "C:/Users/wzy/Desktop/编译原理实验4 -测试用例-test.txt", "r");
if (fp == NULL) {
printf("open file error!\n");
getchar();
return -1;
}
//逐行读入表达式,并分析是否符合语法规则
while (!feof(fp)) {
printf("------------------------------------------\n");
strcpy_s(line, "\0");
fscanf_s(fp, "%s", &line, 30); //逐行读
cur = 0;
error = 0;
printf("输入字符串为:%s \n", line);
char inputString[30] = { 0 };//中间变量,存储输入的字符串
for (int i = 0;i < sizeof(line);i++) {
inputString[i] = line[i];
}
//进行词法分析
toExpress(line, out);
memset(line, 0, sizeof(line));
for (int i = 0;i < sizeof(line);i++) {
line[i] = out[i];
}
if (wordErro == 1) {//若词法正确,则进行语法分析
sym = read(line, cur);//读入当前字符
//初始化指针
top = NULL;
top = (node*)malloc(sizeof(node));
top->before = NULL;
top->next = NULL;
top->data = ' ';
gramAnalysis(line);//开始语法分析
//如果表达式语法分析出错
if (error == -1) {
printf("\n》》》语法错误!《《《\n分析结果:%s为非法字符串\n", inputString);
printf("------------------------------------------\n");
}
//如果表达式合法
else if (error == 0) {
printf("\n》》》语法正确!《《《\n分析结果:%s为合法字符串\n", inputString);
printf("------------------------------------------\n");
}
memset(line, 0, sizeof(line));
memset(out, 0, sizeof(out));
wordErro = 1;
}
else {
printf("》》》不进行语法分析!!《《《\n分析结果:%s为非法字符串\n", inputString);
printf("------------------------------------------\n");
continue;
}
}
fclose(fp);
getchar();
return 0;
}
测试用例
80+5eH+(6+1h)*2+4h#
6+(5+2))*5+80bh#
95eah+3*(5+10)+35h#
9*6+(5+2)*5+80bh#
59h+((3+9ah)*3+4#
7+9*2#