编译原理 实验3 语法分析

语法分析

一、 实验目的

算术表达式的文法可以是(你可以根据需要适当改变):

E→E+E|E-E|E*E|E/E|(E)|i

根据算符优先分析法,将表达式进行语法分析,判断一个表达式是否正确。

二、 实验环境

操作系统:window xp

编写环境:visual c++

编写语言:c语言

三、 实验内容

程序输入/输出示例:

如参考C语言的运算符。输入如下表达式(以分号为结束)和输出结果:

(1)10;

输出:正确

(2)1+2;

输出:正确

(3)(1+2)/3+4-(5+6/7);

输出:正确

(4)((1-2)/3+4

输出:错误

(5)1+2-3+(*4/5)

输出:错误

实验步骤:

1.简述你的程序实现的功能是什么?

判断一个表达式是否遵循给的文法

E→E+E|E-E|E*E|E/E|(E)|i

2.程序的模块描述。

字符串ch是读取txt文件的一个表达式 每个表达式以分号分割

字符串ch2是将读取的表达式转化 变成以#结尾的符号串 如10*(2+3)变成i*(i*i)#

St是符号栈栈 以#开始

Table是算符优先表

四、 实验结果

五、 实验小结

#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 1000;
FILE * in;
FILE * out;
char st[maxn];//符号栈
char ch[maxn], ch2[maxn];//符号串
int top, id;
int len, n;//符号串长度
char map[6][4] = {"E+E","E-E","E*E","E/E","(E)","i"};	//文法产生式右部
char msg[1000];//错误信息
char table[8][8] = {
	{'>','>','<','<','<','>','<','>'},
	{'>','>','<','<','<','>','<','>'},
	{'>','>','>','>','<','>','<','>'},
	{'>','>','>','>','<','>','<','>'},
	{'<','<','<','<','<','=','<','-'},
	{'>','>','>','>','-','>','-','>'},
	{'>','>','>','>','-','>','-','>'},
	{'<','<','<','<','<','-','<','='}
};//优先关系表:八个字符分别是+-*/()i#,其中'-'表示出错

void init()
{
	in = fopen("f:\\f3.txt", "r");
	out = fopen("f:\\fd.txt", "w");
}

void end()
{
	fclose(in);
	fclose(out);
}
bool get_one()//得到一个符号串
{
	char c;
	len = 0;
	int flag = 0;
	while(1)
	{
		if((c = fgetc(in)) != EOF)
		{
			if(c == ';')
			{
				//c[len++] = '#';
				break;
			}
			ch[len++] = c;
		}
		else//文件结束
		{
			flag = 1;
			break;
		}
	}
	if(len)
	{
		ch[len] = 0;
		//puts(ch);
		return true;
	}
	if(!flag)
		return true;
	return false;
}

bool get()
{
	n = 0;
	for(int i = 0; i < len; i++)
	{
		if(ch[i] == '+' || ch[i] == '-' || ch[i] == '*' || ch[i] == '/')
			ch2[n++] = ch[i];
		else if(ch[i] == '(' || ch[i] == ')')
			ch2[n++] = ch[i];
		else if(ch[i] >= '0' && ch[i] <= '9')
		{
			while(i < len && ch[i] >= '0' && ch[i] <= '9')
				i++;
			i--;
			ch2[n++] = 'i';
		}
		else if(ch[i] == 10 || ch[i] == 9 || ch[i] == 32)
			continue;
		else
			return false;
	}
	ch2[n++] = '#';
	ch2[n] = 0;
	puts(ch2);
	return true;
}
int ID(char c)
{
	if(c == '+')
		return 0;
	if(c == '-')
		return 1;
	if(c == '*')
		return 2;
	if(c == '/')
		return 3;
	if(c == '(')
		return 4;
	if(c == ')')
		return 5;
	if(c == 'i')
		return 6;
	if(c == '#')
		return 7;
}

int get_pos(int p)
{
	while(st[p] == 'E')
		p--;
	return p;
}

bool gy(int p)
{
	//printf("----%d\n", p);
	for(int i = 0; i < 6; i++)
	{
		int flag = 0, j, k;
		for(j = p, k = 0; j < top && map[i][k]; j++)
		{
			if(map[i][k++] != st[j])
			{
				flag = 1;
				break;
			}
		}
		if(!flag && map[i][k] == 0)
		{
			top = p;
			st[top++] = 'E';
			return true;
		}
	}
	return false;
}
int guiyue()
{
	int p = get_pos(top-1);
	int q = p;
	while(1)
	{
		q = p;
		p = get_pos(p-1);

		char c1 = st[p], c2 = st[q];
		char r = table[ID(c1)][ID(c2)];
		//printf("%d %d %c\n", p, q, r);
		if(r == '<')
			break;
		else if(r == '=')
			continue;
		else
			return -1;
	}
	int tmp = gy(p+1);
	//printf("+++++++%d\n", tmp);
	if(tmp == 0)
		return tmp;
	else
	{
		//for(int i = 0; i < top; i++)
			//printf("%c", st[i]);
		//puts("");
		if(top == 2 && st[0] == '#' && st[1] == 'E' && ch2[id] == '#')
			return 1;
		return 2;
	}
}
int movein()
{
	char p = st[get_pos(top-1)];
	char q = ch2[id];
	char r = table[ID(p)][ID(q)];
	//printf("****%c******\n", r);
	if(q == '#')
		return 1;
	if(r == '<' || r == '=')//需要移进
	{
		st[top++] = q;
		id++;
		return 2;
	}
	else if(r == '>')
		return 1;
	else
		return 0;
}

bool ok()
{
	top = 0;//栈指针
	id = 0;//符号串指针
	st[top++] = '#';
	while(true)
	{
		int tmp = movein();
		//printf("******%d\n", tmp);
		if(tmp == 1)//需要规约
		{
			int ans = guiyue();
			if(ans == 1)//规约完成
				return true;
			else if(ans == 2)
				continue;
			else
			{
				strcpy(msg, "规约错误");
				return false;
			}
		}
		else if(tmp == 2)// 继续
			continue;
		else
		{
			strcpy(msg, "移进错误");
			return false;
		}
	}
}
int main()
{
	init();
	while(get_one())
	{
		get();
		if(n == 1)
			continue;

		if(ok())
			strcpy(msg, "正确");
		fprintf(out, "%s\n%s\n", ch, msg);
	}
	end();
	return 0;
}

参考代码

//《编译原理》实验示例
//
//程序功能:
//根据算符优先分析法,将表达式进行语法分析,判断一个表达式是否正确。
//文法:E→E+E|E-E|E*E|E/E|(E)|i
//		其中i为无符号整数
//
//例:
//输入:10;
//输出:正确
//输入:1+2;
//输出:正确
//输入:(1+2)/3+4-(5+6/7);
//输出:正确
//输入:((1-2)/3+4;
//输出:错误
//
//输入测试数据保存在同目录的文本文件testin.txt中,保存格式:
//		表达式行;
//		表达式行;
//		.....
//预期的输出保存在同目录的文本文件testout.txt中,保存格式:
//		表达式行;
//      正确/错误
//		表达式行;
//      正确/错误
//		.....
/////////////////////////////////////////////////////////////////

#include "stdio.h"
#include "stdlib.h"
#define TRUE 1
#define FALSE 0

//文件信息:
#define TESTIN_FILENAME "testin.txt"
#define TESTOUT_FILENAME "testout.txt"
FILE * fTestIn;
FILE * fTestOut;	//打开文件后的柄

//运算符定义:
#define O_NUMBER 8		//运算符个数,+-*/()i#
#define O_PLUS 0	// 加+
#define O_MINUS 1	// 减-
#define O_TIMES 2	// 乘*
#define O_SLASH 3	// 除/
#define O_L_PAREN 4	//左括号(parenthesis)
#define O_R_PAREN 5	//右括号
#define O_IDENT 6	//标识符
#define O_NUL 7		//语法界符#

//表达式缓冲区:由专门函数操作(ReadFormula(),GetChar())
#define BUFFER_SIZE 1000	//表达式缓冲区大小
char Buffer[BUFFER_SIZE];	//表达式缓冲区,以'\0'表示结束
int ipBuffer = 0;		//表达式缓冲区当前位置序号

//算符优先关系表:
char O_Table[O_NUMBER][O_NUMBER] = {
	{'>','>','<','<','<','>','<','>'},
	{'>','>','<','<','<','>','<','>'},
	{'>','>','>','>','<','>','<','>'},
	{'>','>','>','>','<','>','<','>'},
	{'<','<','<','<','<','=','<','-'},
	{'>','>','>','>','-','>','-','>'},
	{'>','>','>','>','-','>','-','>'},
	{'<','<','<','<','<','-','<','='}
	};	//优先关系表:八个字符分别是+-*/()i#,其中'-'表示出错

//文法:
#define OG_NUMBER 6	//文法产生式个数
char OG[OG_NUMBER][4] = {"E+E","E-E","E*E","E/E","(E)","i"};	//文法产生式右部

//单词序列存放格式定义:
#define TOKEN_MAX_LENTH 100		//最大的单词长度+1
typedef struct
	{
		char ch;	//存放字符:+-*/()i#E
		int No;	//存放算符优先关系表中的序号
		//double Value;	//当ch==i时,且为数值时,存放值的大小
	} SToken;
#define MAX_TOKEN_NUMBER 1000	//在一个表达式中允许最大的单词个数
SToken Token[MAX_TOKEN_NUMBER];	//单词序列,最后一个以“#”结束
int TokenNumber = 0;	//单词序列中包含的单词个数
int ipToken = 0;	//进行“移进-规约”时的位置指示

//堆栈:由专门的函数操作(PopUp(),Push(),…)
#define STACK_MAX_SIZE 1000	//堆栈最大存储量
SToken Stack[STACK_MAX_SIZE];	//堆栈
int ipStack = 0;		//堆栈指针,指向栈顶(下一个空位置)

//词法分析专用全局变量:
char ch;	//存放取得的一个字符
//char AToken[TOKEN_MAX_LENTH];	//存放组成的单词,存放时以\0为结束
//int ipAToken;	//用于读字符时,指向下一个AToken[]的位置,便于组成单词

//错误信息:
char * ErrMsg;	//出错信息

//函数声明:
bool Judge(); //利用算符优先关系表判断单词序列是否正确
int GuiYue(); //规约,并判断是否完成
bool IsOK(); //判断规约是否全部完成
bool GuiYueN(int n); //将堆栈中0~n单词规约
int FindPriorOp(int Begin); //在堆栈中,从Begin开始,查找前一个终结符位置
int MoveIn(); //移进,并判断是否需要规约
void JudgeInit(); //(利用算符优先关系表判断单词序列是否正确)判断前的初始化
SToken Peek(int n); //窥视堆栈
bool PopUp(int n); //弹出堆栈
void PushToken(char ch, int O_No); //压栈(以字符形式)
void Push(SToken Token); //压栈
bool Init(); //全局初始化
void End(); //程序退出前作善后处理
void OutPut(char * Formula, char * Result); //将结果输出到文件
bool ReadFormula(); //从文件中读出一个表达式存于表达式缓冲区Buffer[]中,以'\0'结束,并置ipBuffer=0;
bool ChangeToTokens(); //将表达式分割成单词序列
char GetFirstChar(); //从表达式缓冲区中取到下面第一个非空字符
char GetChar(); //从表达式缓冲区取一个字符,返回该字符的同时将它存于全局变量ch中
bool MakeErr(char * ErrMassage); //生成错误信息,错误信息存于全局变量ErrMsg中
///////////////////////////////////////

void main()
{
	if(! Init()) //初始化
	{
		printf("初始化失败!程序不能继续。错误信息如下:\n%s\n",ErrMsg);
		exit(0);
	}

	while(ReadFormula())	//从文件中读表达式成功
	{
		if(ChangeToTokens())	//将表达式分割成单词序列
		{
        	if(Judge())	//利用算符优先关系表判断表达式(单词序列)是否正确
				OutPut(Buffer,"正确!");
			else
				OutPut(Buffer,ErrMsg); //输出错误信息
		}
		else //出错
		{
			OutPut(Buffer,ErrMsg); //输出错误信息
		}
	}
	End(); //程序退出前作善后处理
}

//利用算符优先关系表判断单词序列是否正确
//返回:TRUE正确;FALSE错误,且错误信息存于ErrMsg
//本函数的实现思路:
//    将单词序列进行“移进-规约”操作,最后判断是否能全部完成
//    使用到:堆栈(SToken Stack[])、文法(char OG[][])、算符优先关系表(char O_Table[][])等
bool Judge()
{
	JudgeInit();
	PushToken('#',O_NUL);	//将“#”号置栈底
	while(TRUE)	//进行“移进-规约”操作
	{
		switch(MoveIn())
		{
		case 1: //需要规约
			switch(GuiYue())//规约
			{
			case 1: //这一步规约成功
				break;
			case 2: //规约全部完成
				return TRUE;
			default: //出错
				ErrMsg = "规约错误。";
				return FALSE;
			}
			break;
		case 2: //需要继续移进
			break;
		default: //出错
			return FALSE;
		}
	}
}

//规约,并判断是否完成
//返回:-1出错,1这一步规约成功,2规约全部完成
int GuiYue()
{
	int n0,n;
	char r; //存优先关系

	n = FindPriorOp(-1); //取得堆栈中第一个终结符
	if(Peek(n).ch == '#') //出错或全部结束
	{
		if(IsOK())
			return 2;
		else
			return -1;
	}
	while(TRUE)
	{
		n0 = n;
		n = FindPriorOp(n0); //前一个终结符的堆栈位置
		if(n - n0 > 2) //出错(多个非终结符相邻)
			return -1;
		r = O_Table[Peek(n).No][Peek(n0).No];
		if(r == '<') //寻找结束
		{
			if(! GuiYueN(n - 1)) //规约(从前一个后的字符开始)规约失败
				return -1;
			else //规约成功,还要判断是否全部完成
			{
				if(IsOK())
					return 2; //规约全部完成
				else
					return 1; //这一步规约成功
			}
		}
		else if(r == '=') //继续向前找
		{
			continue;
		}
		else //出错(r为>或没有关系)
			return -1;
	}
}

//判断规约是否全部完成
//返回:TRUE全部完成;FALSE没有完成
bool IsOK()
{
	//if(Peek(1) == NULL) return FALSE;
	if(Peek(0).ch == 'E'&& Peek(1).ch == '#' && Token[ipToken].ch == '#')
		return TRUE;
	else
		return FALSE;
}

//返回:TRUE成功,FALSE失败
bool GuiYueN(int n) //将堆栈中0~n单词规约
{
	int i,j;
	bool k;
	for(i=0;i<OG_NUMBER;i++) //将规约串和文法右部OG[][]每一个进行比较
	{
		for(j=n,k=FALSE;j>=0;j--)
		{
			if(OG[i][n-j] != Peek(j).ch)
			{
				k = TRUE; //TRUE表示规约串和文法右部不符,
				break;
			}
		}
		if(k) continue;
		//k==FALSE表示规约串判断完成
		if(OG[i][n+1]=='\0') //文法也判断完成,匹配成功
		{
			PopUp(n + 1); //弹出规约串
			PushToken('E',O_IDENT);//压入左部“E”
			return TRUE;
		}
	}
	return FALSE;
}

//在堆栈中,从Begin开始,查找前一个终结符位置
//如果从开始找,让 Begin = -1
int FindPriorOp(int Begin)
{
	int n;
	n = Begin + 1;
	while(Peek(n).ch == 'E')
	{
		n ++;
	}
	return n;
}

//移进,并判断是否需要规约
//返回:-1出错,1需要规约,2可继续移进
//   1.单词结束(遇到“#”号),无法移进,需要规约,返回:1
//   2.单词没有结束,需判断是否可以移进
//     2-1.堆栈单词<=单词:移进后返回:2
//     2-2.堆栈单词>单词:不能移进,需要规约,返回:1
//     2-3.两单词没有优先关系:出错,返回:-1
int MoveIn()
{
	SToken s,t; //分别存堆栈顶单词和单词序列的第一个单词
	char r; //存放优先关系
	s = Peek(FindPriorOp(-1)); //取得堆栈中第一个终结符位置
	t = Token[ipToken];
	r = O_Table[s.No][t.No];
	if(t.ch == '#') //单词结束,无法移进,需要规约
		return 1;
	else //单词没有结束,需判断是否可以移进
	{
		if(r == '<' || r == '=') //需要移进
		{
			Push(t);
			ipToken ++;
			return 2;
		}
		else if(r == '>') //不能移进,需要规约
			return 1;
		else //没有优先关系,出错
		{
			MakeErr("移进时出现两个没有优先关系的相邻单词。");
			return -1;
		}
	}
}

//(利用算符优先关系表判断单词序列是否正确)判断前的初始化
//由于多个表达式需要依次判断,因此对每个表达式判断前都需要初始化
void JudgeInit()
{
	ipStack = 0; //堆栈初始化(如果有专门的StackClear()函数则更好)

	ipToken = 0; //指向首个单词
}

//窥视堆栈
//参数:n相对栈顶的位置(0开始)
//成功返回:返回单词
//不成功返回:NULL
SToken Peek(int n)
{
	SToken Token;
	if(n > 0 || n < ipStack)
		Token = Stack[ipStack - n - 1];
	else if(n < 0)
		Token = Stack[ipStack - 1];
	else
		Token = Stack[0];
	return Token;
}

//弹出堆栈
//参数:n弹出单词个数(不能全部弹空,即保留#号)
//不成功返回:FALSE
//成功返回:TRUE
bool PopUp(int n)
{
	if(ipStack < 2) return FALSE; //只剩0个或1个
	if(n > ipStack - 1) n = ipStack - 1;
	ipStack -= n;
	return TRUE;
}

//压栈(以字符形式)
//参数:ch是要压栈的字符(+-*/()i#E 之一),O_No运算符序号
//调用:Push()
void PushToken(char ch, int O_No)
{
	SToken Token;
	Token.ch = ch;
	Token.No = O_No;
	Push(Token);
}

//压栈
//参数:Token是要压栈的SToken结构体类型的单词
//缺点:没有判断堆栈是否满
void Push(SToken Token)
{
	Stack[ipStack ++] = Token;
}

//全局初始化
//成功:返回TRUE;失败:返回FALSE
bool Init()
{
	//if((fTestIn = fopen(TESTIN_FILENAME, "r")) = NULL) return ! MakeErr("不能打开测试文件!");
	//if((fTestOut = fopen(TESTOUT_FILENAME, "w")) = NULL) return ! MakeErr("不能打开结果输出文件!");
fTestIn = fopen("d:\\fd3.txt", "r");
fTestOut = fopen("d:\\fd.txt", "w");
	return TRUE;
}

//程序退出前作善后处理
//主要是关闭文件等
void End()
{
	fclose(fTestIn);
	fclose(fTestOut);
}

//将结果输出到文件
//要求文件事先以追加方式打开,文件指针为fTestOut
//参数:Formula表达式内容,Result判断结果
void OutPut(char * Formula, char * Result)
{
	fprintf(fTestOut,"%s\n%s\n",Formula,Result);
}

//从文件中读出一个表达式存于表达式缓冲区Buffer[]中,以'\0'结束,并置ipBuffer=0;
//需要先打开文件,文件指针存于fTestIn
//读出非空表达式:返回 TRUE;文件结束:返回 FALSE
bool ReadFormula()
{
	int n = 0;
	bool k = FALSE;	//当 k==TRUE 时表示文件结束,否则文件没有结束
	while(TRUE)
	{
		if((Buffer[n] = fgetc(fTestIn)) != EOF) //读出一个字符成功
		{
			if(Buffer[n] == ';') break;
			n ++;
		}
		else //文件结束
		{
			k = TRUE;
			break;
		}
	}
	Buffer[n] = '\0';	//最后一个字符用结束标记'\0'代替
	ipBuffer = 0;	//初始化缓冲区指针
	if(n > 0) //读出的数据非空,返回成功
		return TRUE;
	else //读出的数据为空,需要判断文件结束,还是只有';'的空表达式
	{
		if(k) //文件结束
			return FALSE;
		else //空表达式,文件没有结束,让它继续读下一个表达式
			return ReadFormula();
	}
}

//将表达式分割成单词序列
//结果:单词序列存于SToken Token[]中,单词个数存于TokenNumber中
//这是一个大模块,其中要调用一些子函数
//本函数只识别:运算符+-*/、括号()、无符号整数i,并在末尾添加#号
//				遇到其它任何字符都返回错误信息
//返回:TRUE表示成功;FALSE表示失败,同时将错误信息存于全局变量ErrMsg中
//使用到的其他全局变量:ch(取一个字符)、AToken[](取到的单词)
bool ChangeToTokens()
{
	TokenNumber = 0;
	if(GetFirstChar() == '\0') return ! MakeErr("表达式为空。");
	while(TRUE) //对缓冲区进行循环读
	{
		if(ch <= 32 && ch > 0) GetFirstChar(); //滤去空格
		switch(ch) //对单词的第一个进行判断,在下面一次处理整个单词
		{
		case '\0':
			Token[TokenNumber].ch = '#';
			Token[TokenNumber].No = O_NUL;
			return TRUE; //处理结束
		case '+':
			Token[TokenNumber].ch = '+';
			Token[TokenNumber].No = O_PLUS;
			GetChar();
			break;
		case '-':
			Token[TokenNumber].ch = '-';
			Token[TokenNumber].No = O_MINUS;
			GetChar();
			break;
		case '*':
			Token[TokenNumber].ch = '*';
			Token[TokenNumber].No = O_TIMES;
			GetChar();
			break;
		case '/':
			Token[TokenNumber].ch = '/';
			Token[TokenNumber].No = O_SLASH;
			GetChar();
			break;
		case '(':
			Token[TokenNumber].ch = '(';
			Token[TokenNumber].No = O_L_PAREN;
			GetChar();
			break;
		case ')':
			Token[TokenNumber].ch = ')';
			Token[TokenNumber].No = O_R_PAREN;
			GetChar();
			break;
		default:
			if(ch >= '0' && ch <= '9') //整数
			{
				while(GetChar()>0)
				{
					if(ch < '0' || ch > '9') break;
				}
				Token[TokenNumber].ch = 'i';
				Token[TokenNumber].No = O_IDENT;
			}
			else
			{
				return ! MakeErr("表达式中含有非法字符。");
			}
			break;
		}
		TokenNumber ++;
	}
}

//从表达式缓冲区中取到下面第一个非空字符
//成功:返回字符;不成功:返回'\0'
char GetFirstChar()
{
	while(GetChar() != '\0')
	{
		if(ch>32) return ch;
	}
	return '\0';
}

//从表达式缓冲区取一个字符,返回该字符的同时将它存于全局变量ch中
//成功:返回字符;不成功:返回'\0'
char GetChar()
{
	if((ch = Buffer[ipBuffer]) != '\0')
		ipBuffer ++;
	return ch;
}

//生成错误信息
//错误信息存于全局变量ErrMsg中
//返回:TRUE
bool MakeErr(char * ErrMassage)
{
	ErrMsg = ErrMassage;
	return TRUE;
}
时间: 2024-10-11 01:01:50

编译原理 实验3 语法分析的相关文章

编译原理实验代码(词法分析,语法分析,中间代码生成)

花了一天写出的程序没有顾及很多层面,但对于理解基本的实验道理和交上实验还是有点帮助的.代码实现了基于有限自动机的词法分析,采用递归下降分析法和EBNF文法实现语法分析并生成中间代码. lexAnalysis.h /* * lexAnalysis.h * * Created on: 2014-12-2 * Author: liuqiushan */ #ifndef LEXANALYSIS_H_ #define LEXANALYSIS_H_ #include <stdio.h> #include

哈工大软件学院编译原理实验1——词法分析

这次实验被"过来人"们定位非常easy,实验内容例如以下: ----------------------------------------------------------------------------------- 对例如以下工作进行展开描写叙述 (1) 给出语言的词法规则描写叙述 · 标识符.keyword.整常数.字符常数.浮点常数 · 单界符:+,-,×,;,- · 双界符:/*,:=,>=,<=,!=,- · 凝视 (2) 针对这样的单词的状态转换图和程

吉首大学_编译原理实验题_基于预測方法的语法分析程序的设计【通过代码】

一.实验要求 实验二 基于预測方法的语法分析程序的设计 一.实验目的 了解预測分析器的基本构成及用自顶向下的预測法对表达式进行语法分析的方法,掌握预測语法分析程序的手工构造方法. 二.实验内容 1.了解编译程序的基于预測方法的语法分析过程. 2.依据预測分析原理设计一个基于预測方法的语法分析程序. 三.实验要求 对给定文法G[S]: S->AT       A->BU     T->+AT|$      U->*BU|$    B->(S)|m 当中,$表示空串. 1.推断上

编译原理实验二:LL(1)语法分析器

一.实验要求 不得不想吐槽一下编译原理的实验代码量实在是太大了,是编译原理撑起了我大学四年的代码量... 这次实验比上次要复杂得多,涵盖的功能也更多了,我觉得这次实验主要的难点有两个(其实都是难点...): 1. 提取左公因子或消除左递归(实现了消除左递归) 2. 递归求First集和Follow集 其它的只要按照课本上的步骤顺序写下来就好(但是代码量超多...),下面我贴出实验的一些关键代码和算法思想. 二.基于预测分析表法的语法分析 2.1 代码结构 2.1.1  Grammar类    功

编译原理 - 实验三 - 递归下降语法分析器的调试及扩展

一. 语法分析介绍 语法分析是编译过程的核心部分,它的主要任务是按照程序语言的语法规则,从由词法分析输出的源程序符号串中识别出各类语法成分,同时进行语法检查,为语义分析和代码生成做准备.执行语法分析任务的程序叫语法分析程序或语法分析器. 二. 所实现的语义分析和代码生成程序能处理什么语句 (1)简单变量的声明语句 (2)表达式语句 (3)if语句. (4)while语句 (5)for语句 (6)write语句 (7)read语句 (8)do语句. (9)处理过程调用和返回 三.实验过程 ①用VC

编译原理 实验1 PL/0语言词法分析

PL/0语言词法分析 一. 实验目的 通过完成词法分析程序,了解词法分析的过程.编制一个读单词程序,对PL/0语言进行词法分析,把输入的字符串形式的源程序分割成一个个单词符号,即基本保留字.标识符.常数.运算符.界符五大类. 二. 实验环境 操作系统:window xp 编写环境:visual c++ .c-free.turbo c 编写语言:c语言 分析语言:PL/0 三. 实验内容 对PL/0语言进行词法分析,把输入的字符串形式的源程序分割成一个个单词符号,其词法描述如下: (1) 关键字:

编译原理 实验2 DFA的运行

DFA的运行 一. 实验目的 1.理解有穷自动机的作用: 2.利用状态图和状态表表示有穷自动机: 3.以程序实现有穷自动机的运行过程; 4.利用状态表和有穷自动机的运行原理编制程序,使得程序能够识别一个输入串是否为一个有效的符号串.具体可以选择下面之一:无符号定点实数,无符号正偶数,自然数,整数,十六进制数,或其他自己定义的符号串. 二. 实验环境 操作系统:window xp 编写环境:visual c++ 编写语言:c语言 三. 实验内容 1.简单介绍你所设计的有穷自动机.(要求必须完成无符

编译原理 - 实验四 - yacc基本使用(bison)

一.介绍 只要在Unix环境中写过程序,必定会邂逅神秘的Lex&YACC,就如GNU/Linux用户所熟知的Flex&Bison,这里的Flex就是由Vern Paxon实现的一个Lex,Bison则是GNU版本的YACC,在此我们将统一称呼这些程序为Lex和YACC,所以我们将用Flex和Bison来尝试下我们的实例. 二.准备工作(win7) ①  下载flex和bison并安装到D:\GnuWin32(尽量是根目录) ②    由于我们使用的flex和bison都是GNU的工具,所以

编译原理 - 实验二 - FLEX词法分析器

FLEX词法分析器 一.Lex和Yacc介绍 Lex 是一种生成扫描器的工具.扫描器是一种识别文本中的词汇模式的程序. 一种匹配的常规表达式可能会包含相关的动作.这一动作可能还包括返回一个标记. 当 Lex 接收到文件或文本形式的输入时,它试图将文本与常规表达式进行匹配. 它一次读入一个输入字符,直到找到一个匹配的模式. 如果能够找到一个匹配的模式,Lex 就执行相关的动作(可能包括返回一个标记). 另一方面,如果没有可以匹配的常规表达式,将会停止进一步的处理,Lex 将显示一个错误消息. Ya