大家好,下面介绍的是我当时上编译原理所做的实验,主要内容就是根据算符优先分析法,对表达式进行语法分析,使其能够判断一个表达式是否正确。用的是最基本的C语言写的,如有不足,欢迎大家批评指正!
一、 实验目的
根据算符优先分析法,对表达式进行语法分析,使其能够判断一个表达式是否正确。通过算符优先分析方法的实现,加深对自下而上语法分析方法的理解。
二、 实验内容
1、输入文法。可以是如下算术表达式的文法(你可以根据需要适当改变):
E→E+T|E-T|T
T→T*F|T/F|F
F→(E)|i
2、对给定表达式进行分析,输出表达式正确与否的判断。
程序输入/输出示例:
输入:1+2;
输出:正确
输入:(1+2)/3+4-(5+6/7);
输出:正确
输入:((1-2)/3+4
输出:错误
输入:1+2-3+(*4/5)
输出:错误
三、实验设计
1.实验步骤
1)根据课本上的知识构造文法的firstvt集和lastvt集
2)根据firstvt集和lastvt集构造算符优先表
3)根据课本上提供的算法,利用堆栈和算符优先表来判断输入串是否是该文法的一个合适的语法范畴
四、代码实现及运行结果
运行效果,由于结果太长,我就把结果截取成了若干图片以便展示
代码由三个文件构成,如下图所示,其中,Hong.h中主要是对一下常用的变量进行宏定义,fun.h中是对各种方法的实现,main.c则是对fun.h中实现的方法的简单的调用,一些需要注意的地方都在代码中以注释的形式展现,话不多说,上代码!
Hong.h
#include <stdio.h>
#include <stdlib.h>
#define STACK_INIT_SIZE 100 //栈的初始大小
#define STACKINCREMENT 10 //能扩增的栈的大小
typedef struct Hong
{
char vn; //非终结符
char vt; //终结符
} SElemType;
typedef struct //定义栈的结构体
{
SElemType *base;
SElemType *top;
int stacksize;
} SqStack;
int len_gram = 0; //产生式的句数
char gramOldSet[200][200]; //保存产生式的数组
int len_ter = 0; //终结符的个数
int len_non_ter = 0; //非终结符的个数
char terSymbol[200]; //终结符数组
char non_ter[200]; //非终结符数组
int firstvt_table[100][100]; //求firstvt集所用的表
int lastvt_table[100][100]; //求lastvt集所用的表
int len_firstvt = 0; //存每个非终结符的firstvt集的数组的个数
int len_lastvt = 0; //求每个终结符的lastvt集的数组的个数
char firstvt[100][100]; //firstvt集数组
char lastvt[100][100]; //lastvt集数组
char priority_table[200][200]; //最终的算符优先表
fun.h
#include "Hong.h"
//构造一个空栈
int InitStack(SqStack *S)
{
S->base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType));
if (!S->base)
{
printf("内存分配失败!\n");
exit(0);
}
S->top = S->base;
S->stacksize = STACKINCREMENT;
return 1;
}
//若栈不为空,则用e返回S的栈顶元素
SElemType GetTop(SqStack *S)
{
return *(S->top - 1);
}
//插入元素e为新的栈顶元素
int Push(SqStack *S, SElemType e)
{
if (S->top - S->base >= STACK_INIT_SIZE) //栈满, 追加存储空间
{
S->base = (SElemType *)realloc(S->base, (S->stacksize + STACKINCREMENT) * sizeof(SElemType));
if (!S->base)
{
printf("内存分配失败!\n");
exit(0);
}
S->top = S->base + S->stacksize;
S->stacksize += STACKINCREMENT;
}
*S->top++ = e;
return 1;
}
//若栈不为空,则删除S的栈顶元素
SElemType Pop(SqStack *S)
{
return *--S->top;
}
//判断栈是否为空
int IsEmpty(SqStack *S)
{
if (S->top == S->base)
return 1;
return 0;
}
//判断数组中是否有字符c
int have_char(char str[], char c, int len)
{
for (int i = 0; i < len; i++)
{
if (c == str[i])
return 1;
}
return 0;
}
//区分是终结符还是非终结符
int check(char c)
{
if (c >= 'A' && c <= 'Z')
return 1; //表示非终结符
else if (c == '@' || c == '|' || c == ' ' || c == '\n' || c == '\r')
return 0; //表示一些无用的符号以及空字
else
return 2; //表示是终结符
}
//预处理,将产生式从文件中读出,并进行拆分、识别终结符和非终结符
int Yu(char s[])
{
FILE *fp;
fp = fopen(s, "r");
char temp_gram[200];
int index_temp_gram = 3;
int index_gram = 0;
//将表达式读进数组中
if (fp != NULL)
{
printf("原始产生式为:\n");
while (fgets(temp_gram, 200, fp) != NULL)
{
printf("%s", temp_gram); //回车换行算作一个字符,在\0前面
char gram_right = temp_gram[0];
// printf("%c\n",gram_right);
/*将产生式中的或都给拆开,方便下面的操作*/
index_temp_gram = 3;
index_gram = 0;
gramOldSet[len_gram][0] = gram_right;
gramOldSet[len_gram][1] = '-';
gramOldSet[len_gram][2] = '>';
index_gram = 3;
while (temp_gram[index_temp_gram] != '\0')
{
if (temp_gram[index_temp_gram] == '\n' || temp_gram[index_temp_gram] == '\r')
{
gramOldSet[len_gram][index_gram] = '\0';
len_gram++;
break;
}
else if (temp_gram[index_temp_gram] == '|')
{
index_temp_gram++;
gramOldSet[len_gram][index_gram] = '\0';
len_gram++;
gramOldSet[len_gram][0] = gram_right;
gramOldSet[len_gram][1] = '-';
gramOldSet[len_gram][2] = '>';
index_gram = 3;
}
gramOldSet[len_gram][index_gram] = temp_gram[index_temp_gram];
index_gram++;
index_temp_gram++;
}
}
gramOldSet[len_gram][index_gram] = '\0';
len_gram++;
//printf("%c\n", temp_gram[0]);
}
else
{
printf("无法打开文件");
}
//查看结果
printf("\n将产生式中的或拆开之后的结果为\n");
for (int i = 0; i < len_gram; i++)
{
printf("%s\n", gramOldSet[i]);
}
//区分终结符和非终结符
for (int i = 0; i < len_gram; i++)
{
for (int j = 3; gramOldSet[i][j] != '\0'; j++)
{
char temp = gramOldSet[i][j];
int value_return = check(temp);
if (value_return == 1)
{
if (!have_char(non_ter, temp, len_non_ter))
{
non_ter[len_non_ter] = temp;
len_non_ter++;
}
}
else if (value_return == 2)
{
if (!have_char(terSymbol, temp, len_ter))
{
terSymbol[len_ter] = temp;
len_ter++;
}
}
else
{
continue;
}
}
}
//区分终结符和非终结符
printf("终结符为:\n");
for (int i = 0; i < len_ter; i++)
{
printf("%c ", terSymbol[i]);
}
printf("\n");
printf("非终结符为:\n");
for (int i = 0; i < len_non_ter; i++)
{
printf("%c ", non_ter[i]);
}
printf("\n");
return 0;
}
//初始化firstvt_table
void init_firstvt_table()
{
for (int i = 0; i <= len_non_ter; i++)
{
for (int j = 0; j <= len_ter; j++)
{
firstvt_table[i][j] = -1;
}
}
}
//初始化lastvt_table
void init_lastvt_table()
{
for (int i = 0; i <= len_non_ter; i++)
{
for (int j = 0; j <= len_ter; j++)
{
lastvt_table[i][j] = -1;
}
}
}
//显示firstvt_table
void show_firstvt_table()
{
printf("\n\n求firstvt集所用的布尔数组为:\n\n");
for (int i = 0; i <= len_non_ter; i++)
{
for (int j = 0; j <= len_ter; j++)
{
if (i == 0)
{
if (j == 0)
{
printf("0\t");
}
else
printf("%c\t", terSymbol[j - 1]);
}
else if (j == 0)
{
if (i == 0)
{
printf("0\t");
}
else
printf("%c\t", non_ter[i - 1]);
}
else
{
printf("%d\t", firstvt_table[i][j]);
}
}
printf("\n");
}
printf("\n");
}
//显示lastvt_table
void show_lastvt_table()
{
printf("\n\n求lastvt集所用的布尔数组为:\n\n");
for (int i = 0; i <= len_non_ter; i++)
{
for (int j = 0; j <= len_ter; j++)
{
if (i == 0)
{
if (j == 0)
{
printf("0\t");
}
else
printf("%c\t", terSymbol[j - 1]);
}
else if (j == 0)
{
if (i == 0)
{
printf("0\t");
}
else
printf("%c\t", non_ter[i - 1]);
}
else
{
printf("%d\t", lastvt_table[i][j]);
}
}
printf("\n");
}
printf("\n");
}
//求每个产生式的长度
int length_gram(char str[])
{
int i = 0;
while (str[i] != '\0')
{
i++;
}
return i;
}
//求终结符或者非终结符在对应数组中的位置
int find_position_char(char c, char str[], int len)
{
for (int i = 0; i < len; i++)
{
if (c == str[i])
{
return i;
}
}
return 0;
}
//初始化vt集
void init_firstvt()
{
int temp = 0;
while (temp < len_non_ter)
{
firstvt[len_firstvt][0] = non_ter[temp];
firstvt[len_firstvt][1] = '\0';
temp++;
len_firstvt++;
}
}
//初始化lastvt集
void init_lastvt()
{
int temp = 0;
while (temp < len_non_ter)
{
lastvt[len_lastvt][0] = non_ter[temp];
lastvt[len_lastvt][1] = '\0';
temp++;
len_lastvt++;
}
}
//显示vt集
void show_vt()
{
printf("first集有:\n");
for (int i = 0; i < len_firstvt; i++)
{
printf("firstvt(%c): ", firstvt[i][0]);
int t = 1;
while (firstvt[i][t] != '\0')
{
printf("%c\t", firstvt[i][t]);
t++;
}
printf("\n");
}
printf("last集有:\n");
for (int i = 0; i < len_lastvt; i++)
{
printf("lastvt(%c): ", lastvt[i][0]);
int t = 1;
while (lastvt[i][t] != '\0')
{
printf("%c\t", lastvt[i][t]);
t++;
}
printf("\n");
}
}
//往集合中加入元素
void add_element(char str[], char c)
{
int i = 0;
while (str[i] != '\0')
{
if (str[i] == c)
return;
i++;
}
str[i] = c;
i++;
str[i] = '\0';
}
//生成firstvt_table,并产生firstvt集
void creat_firstvt()
{
init_firstvt_table();
init_firstvt();
//1、遍历每一个产生式,先获取产生式左部的非终结符A,找到A在非终结符数组中的位置,index_non,index_non++
//如果它的产生式右部第一个元素为终结符a,找到a在终结符数组中的位置,index_ter,index_ter++
//如果它的长度大于4,获取它的产生式右部的第二个元素,如果是终结符,和上述一样
SqStack stack;
InitStack(&stack); //初始化一个栈
SElemType elem;
for (int i = 0; i < len_gram; i++)
{
char left_gram = gramOldSet[i][0];
int index_non = find_position_char(left_gram, non_ter, len_non_ter);
index_non++;
char right_temp = gramOldSet[i][3];
if (check(right_temp) == 2)
{
int index_ter = find_position_char(right_temp, terSymbol, len_ter);
index_ter++;
firstvt_table[index_non][index_ter] = 1;
elem.vn = left_gram;
elem.vt = right_temp;
Push(&stack, elem);
}
if (length_gram(gramOldSet[i]) > 4)
{
right_temp = gramOldSet[i][4];
if ((check(right_temp) == 2) && (check(gramOldSet[i][3] == 1)))
{ //第一个是非终结符,第二个是终结符
int index_ter = find_position_char(right_temp, terSymbol, len_ter);
index_ter++;
firstvt_table[index_non][index_ter] = 1;
elem.vn = left_gram;
elem.vt = right_temp;
Push(&stack, elem);
}
}
}
//2、直到栈不为空,先将栈顶元素弹出,vn,vt,
//遍历每一个产生式,先判断产生式右部第一个元素是不是vn
//如果是,找到产生式左部left_gram,index_non++
//还有index_ter++,判断表中是否为-1,
//如果是-1,置1,压栈
while (!IsEmpty(&stack))
{
elem = Pop(&stack);
for (int i = 0; i < len_gram; i++)
{
if (gramOldSet[i][3] == elem.vn)
{
int index_non = find_position_char(gramOldSet[i][0], non_ter, len_non_ter);
index_non++;
int index_ter = find_position_char(elem.vt, terSymbol, len_ter);
index_ter++;
if (firstvt_table[index_non][index_ter] == -1)
{
firstvt_table[index_non][index_ter] = 1;
elem.vn = gramOldSet[i][0];
Push(&stack, elem);
}
}
}
}
for (int i = 1; i <= len_non_ter; i++)
{
for (int j = 1; j <= len_ter; j++)
{
if (firstvt_table[i][j] == 1)
{
add_element(firstvt[i - 1], terSymbol[j - 1]);
}
}
}
show_firstvt_table();
}
//生成lastvt集,并产生lastvt集
void creat_lastvt()
{
init_lastvt_table();
init_lastvt();
//初始化堆栈
SqStack stack;
InitStack(&stack);
SElemType elem;
//1、遍历每一个产生式,找到产生式左部非终结符index_non,index_non++
//判断产生式右部的最后一个字符是否为终结符,如果是,index_ter,index_ter++,压栈
//再看倒数第二个是不是终结符,如果是,index_ter,index_ter++,压栈
for (int i = 0; i < len_gram; i++)
{
char left_gram = gramOldSet[i][0];
int index_non = find_position_char(left_gram, non_ter, len_non_ter);
index_non++;
int len = length_gram(gramOldSet[i]);
len--;
if (have_char(terSymbol, gramOldSet[i][len], len_ter))
{
int index_ter = find_position_char(gramOldSet[i][len], terSymbol, len_ter);
index_ter++;
lastvt_table[index_non][index_ter] = 1;
elem.vn = left_gram;
elem.vt = gramOldSet[i][len];
Push(&stack, elem);
}
else
{
if (have_char(terSymbol, gramOldSet[i][len - 1], len_ter))
{
int index_ter = find_position_char(gramOldSet[i][len - 1], terSymbol, len_ter);
index_ter++;
lastvt_table[index_non][index_ter] = 1;
elem.vn = left_gram;
elem.vt = gramOldSet[i][len - 1];
Push(&stack, elem);
}
}
}
//2、先将栈顶元素弹出,vn,vt
//遍历每个产生式,判断产生式右部最后一个符号是不是==vn,如果是
//产生式左部,index_non++,找到vt的index_ter++
//如果表中位置==-1,置1,压栈
while (!IsEmpty(&stack))
{
elem = Pop(&stack);
for (int i = 0; i < len_gram; i++)
{
int len = length_gram(gramOldSet[i]);
len--;
if (elem.vn == gramOldSet[i][len])
{
int index_non = find_position_char(gramOldSet[i][0], non_ter, len_non_ter);
index_non++;
int index_ter = find_position_char(elem.vt, terSymbol, len_ter);
index_ter++;
if (lastvt_table[index_non][index_ter] == -1)
{
lastvt_table[index_non][index_ter] = 1;
elem.vn = gramOldSet[i][0];
Push(&stack, elem);
}
}
}
}
for (int i = 1; i <= len_non_ter; i++)
{
for (int j = 1; j <= len_ter; j++)
{
if (lastvt_table[i][j] == 1)
{
add_element(lastvt[i - 1], terSymbol[j - 1]);
}
}
}
show_lastvt_table();
show_vt();
}
//初始化算符优先表
void init_priority_table()
{
//先把'#'加进去
terSymbol[len_ter] = '#';
len_ter++;
for (int i = 0; i <= len_ter; i++)
{
for (int j = 0; j <= len_ter; j++)
{
priority_table[i][j] = ' ';
}
}
for (int i = 1; i <= len_ter; i++)
{
priority_table[0][i] = terSymbol[i - 1];
priority_table[i][0] = terSymbol[i - 1];
}
}
//显示算符优先表
void show_priority_table()
{
printf("\n算符优先表为:\n\n");
for (int i = 0; i <= len_ter; i++)
{
for (int j = 0; j <= len_ter; j++)
{
printf("%c\t", priority_table[i][j]);
}
printf("\n");
}
}
//定位firstvt集和lastvt集的位置
int find_position(char c, char str[][100], int len)
{
for (int i = 0; i < len; i++)
{
if (c == str[i][0])
return i;
}
}
//生成算符优先表
void creat_priority_table()
{
init_priority_table();
//先把#开始符号#加到产生式数组中
gramOldSet[len_gram][0] = ' ';
gramOldSet[len_gram][1] = ' ';
gramOldSet[len_gram][2] = ' ';
gramOldSet[len_gram][3] = '#';
gramOldSet[len_gram][4] = gramOldSet[0][0];
gramOldSet[len_gram][5] = '#';
gramOldSet[len_gram][6] = '\0';
len_gram++;
//遍历每条产生式
for (int i = 0; i < len_gram; i++)
{
int len = length_gram(gramOldSet[i]);
len--;
for (int j = 3; j <= len - 1; j++)
{
if (have_char(terSymbol, gramOldSet[i][j], len_ter) && have_char(terSymbol, gramOldSet[i][j + 1], len_ter))
{
int x = find_position_char(gramOldSet[i][j], terSymbol, len_ter);
x++;
int y = find_position_char(gramOldSet[i][j + 1], terSymbol, len_ter);
y++;
priority_table[x][y] = '=';
}
if (j <= len - 2 && have_char(terSymbol, gramOldSet[i][j], len_ter) && have_char(terSymbol, gramOldSet[i][j + 2], len_ter) && have_char(non_ter, gramOldSet[i][j + 1], len_non_ter))
{
int x = find_position_char(gramOldSet[i][j], terSymbol, len_ter);
x++;
int y = find_position_char(gramOldSet[i][j + 2], terSymbol, len_ter);
y++;
priority_table[x][y] = '=';
}
if (have_char(terSymbol, gramOldSet[i][j], len_ter) && have_char(non_ter, gramOldSet[i][j + 1], len_non_ter))
{
int x = find_position_char(gramOldSet[i][j], terSymbol, len_ter);
x++;
int index_firstvt = find_position(gramOldSet[i][j + 1], firstvt, len_firstvt);
int temp = 1;
while (firstvt[index_firstvt][temp] != '\0')
{
int y = find_position_char(firstvt[index_firstvt][temp], terSymbol, len_ter);
y++;
priority_table[x][y] = '<';
temp++;
}
}
if (have_char(non_ter, gramOldSet[i][j], len_non_ter) && have_char(terSymbol, gramOldSet[i][j + 1], len_ter))
{
int y = find_position_char(gramOldSet[i][j + 1], terSymbol, len_ter);
y++;
int index_lastvt = find_position(gramOldSet[i][j], lastvt, len_lastvt);
int temp = 1;
while (lastvt[index_lastvt][temp] != '\0')
{
int x = find_position_char(lastvt[index_lastvt][temp], terSymbol, len_ter);
x++;
priority_table[x][y] = '>';
temp++;
}
}
}
}
show_priority_table();
}
//返回优先表中相应位置的字符
char judgement(char x, char y)
{
int index_x = find_position_char(x, terSymbol, len_ter);
index_x++;
int index_y = find_position_char(y, terSymbol, len_ter);
index_y++;
return priority_table[index_x][index_y];
}
//总控程序
void control(char str[])
{
//用一个一维数组模拟堆栈
char stack[100];
//将str数组中的数字都用i替换,数字都统一用i表示
char new_str[100];
int i=0;
while (str[i]!='#')
{
if(str[i]>='0'&&str[i]<='9'){
new_str[i]='i';
}
else{
new_str[i]=str[i];
}
i++;
}
new_str[i]='#';
int k = 1;
stack[k] = '#';
int pointer = 0;
int j = 0;
char Q;
char a;
do
{
a =new_str[pointer];
if (have_char(terSymbol, stack[k], len_ter))
{ //判断栈顶元素是否是终结符
//如果是
j = k;
}
else
{
//如果不是
j = k - 1;
}
printf("\n栈里面的元素为");
for (int x = 1; x <= k; x++)
{
printf("%c", stack[x]);
}
printf("\t\t面对的栈外的元素为%c\n", str[pointer]);
while (judgement(stack[j], a) == '>') //如果最上面的终结符a小于外面的终结符,就往下找小于a的终结符
{
do
{
Q = stack[j];
if (have_char(terSymbol, stack[j - 1], len_ter))
{
j = j - 1;
}
else
{
j = j - 2;
}
} while (judgement(stack[j], Q) != '<');
printf("从%c开始规约\n", stack[j + 1]);
int len_aim = k - j;
for (int i = 0; i < len_gram; i++)
{
int len = length_gram(gramOldSet[i]);
len -= 3;
if (len == len_aim)
{
int t = 0;
for (t = 0; t < len; t++)
{
if (have_char(non_ter, gramOldSet[i][3 + t], len_non_ter) && have_char(non_ter, stack[j + 1 + t], len_non_ter))
{
continue;
}
else if (have_char(terSymbol, gramOldSet[i][3 + t], len_ter) && have_char(terSymbol, stack[j + 1 + t], len_ter) && gramOldSet[i][3 + t] == stack[j + 1 + t])
{
continue;
}
else
{
break;
}
}
if (t == len)
{ //说明满足条件
k = j + 1;
printf("将%c替换为%c\t使用的产生式为%s\n", stack[k], gramOldSet[i][0], gramOldSet[i]);
stack[k] = gramOldSet[i][0];
printf("\n栈里面的元素为");
for (int x = 1; x <= k; x++)
{
printf("%c", stack[x]);
}
printf("\t\t面对的栈外的元素为%c\n", str[pointer]);
break; //跳出for循环,结束查找
}
}
}
}
if (judgement(stack[j], a) == '<' || judgement(stack[j], a) == '=')
{
printf("将%c入栈\n", str[pointer]);
k = k + 1;
stack[k] = a;
}
else
{
printf("%s不是一个合适的语法范畴\n", str);
break;
}
pointer++;
} while (a != '#');
if (stack[2] == gramOldSet[0][0])
{
printf("%s是一个合适的语法范畴\n", str);
}
printf("栈里面有:\n");
for (int x = 1; x <= k; x++)
{
printf("%c ", stack[x]);
}
}
Main.c
大家可以自行新建文本文件,只需要把对应的文件地址改一下即可,具体说明在注释中
#include "fun.h"
int main()
{
Yu("E:\\test\\shiyan2\\source2.txt"); //从文件中读取产生式,并进行预处理
creat_firstvt(); //构造firstvt
creat_lastvt(); //构造lastvt
creat_priority_table(); //构造算符优先表
printf("请输入表达式,并以#号结尾\n");
int i=0;
char str_arry[100]={0};
char ch;
while((ch=getchar())!='#' && ch!=EOF)
{
str_arry[i++]=ch;
}
str_arry[i]='#';
control(str_arry);
return 0;
}
source2.txt中的内容为
E->E+T|E-T|T
T->T*F|T/F|F
F->(E)|i
写在最后,如果大家觉得这篇文章对你有帮助的话,还请大家赞一下下啦 : )