湖南大学编译原理实验二:词法分析程序

实验目的

学习和掌握词法分析程序手工构造状态图及其代码实现方法。

 

实验任务

  1. 阅读已有编译器的经典词法分析源程序;
  2. 用C或C++语言编写一门语言的词法分析器。

 

实验内容

  1. 阅读已有编译器的经典词法分析源程序。

选择一个编译器,如:TINY,其它编译器也可(需自备源代码)。阅读词法分析源程序,理解词法分析程序的手工构造方法——状态图代码化。尤其要求对相关函数与重要变量的作用与功能进行稍微详细的描述。若能加上学习心得则更好。TINY语言请参考《编译原理及实践》第2.5节(见压缩包里附带的文档)。

  1. 确定今后其他实验中要设计编译器的语言,如TINY语言,又如更复杂的C-语言(其定义在《编译原理及实践》附录A中)。也可选择其它语言,不过要有该语言的详细定义(可仿照C-语言)。一旦选定,不能更改,因为要在以后继续实现编译器的其它部分。鼓励自己定义一门语言。
  2. 根据该语言的关键词和识别的词法单元以及注释等,确定关键字表,画出所有词法单元和注释对应的DFA图。
  3. 仿照前面学习的词法分析器,编写选定语言的词法分析器。
  4. 准备2~3个测试用例,要求包含正例和反例,测试编译结果。

 

提示

在充分理解状态转换图代码化思想的基础上,思考不同的程序设计语言从词法角度有什么区别,可利用增量编程的思想提高编程效率。实验通过测试后,按规定时间上交源代码、测试样例、输出文件(如有输出文件)和电子版实验报告。

 

状态转换图

 

代码实现

#include<iostream>
#include<fstream>
#include<string.h>
#include<assert.h>
using namespace std;
int LINE=1;   //用于记录代码的行数 
bool flag=false;   //用于表示当前注释是否结束 

typedef enum 
{
    ENDFILE,ERROR,   //文件读取完毕和错误 
    IF,ELSE,INT,RETURN,VOID,WHILE,   //六关键字 
    ID,NUM,   //数字和字母 
    ADD,SUB,MUL,DIV,L,LE,G,GE,ISE,NT,NTE,FZ,FH,DH,LXK,RXK,LZK,RZK,LDK,RDK,LZS,RZS   // + - * / < <= > >= == != = ; , ( ) [ ] { } /* */
}TokenType;

typedef enum
{
    START,INNUM,INID,INLT,INGT,INEQ,INNOT,INCOMMENT,INDELETE,READDELETE,DONE
	//开始、数字、字符、小于等于、大于等于、赋值或等于、注释开始,注释内容开始,注释内容结束,完成和单个字符直接完成 
}TokenState;

bool isnum(char a)  //检测当前字符是否为数字 
{
	if(a>='0'&&a<='9')
	return true;
	return false;
}

bool isalpha(char a)   //检测当前字符是否为字母 
{
	if((a>='a'&&a<='z')||(a>='A'&&a<='Z'))
	return true;
	return false;
}

void PrintToken(TokenType token,const string TokenString)   //对于每一种状态输出每一样的词法分类结果 
{
	if(TokenString!="\0")   //如果是空行,那么跳过,保持格式 
	cout<<"\t"<<LINE<<":";
	switch(token)   //根据当前状态输出应当匹配的东西 
    {
	    case IF:
        case INT:
        case ELSE:
        case RETURN:
        case VOID:
        case WHILE:
        cout<<"reserved word:"<<TokenString<<endl;   //六个预留词都是单独的状态 
		break;
        case ADD: printf("+\n");  break;
        case SUB: printf("-\n");  break;
        case MUL: printf("*\n");  break;
        case DIV: printf("/\n");  break;
        case L:   printf("<\n");  break;
        case LE:  printf("<=\n"); break;
        case G:   printf(">\n");  break;
        case GE:  printf(">=\n"); break;
        case ISE: printf("==\n"); break;
        case NT:  printf("!\n");  break;
        case NTE: printf("!=\n"); break;
        case FZ:  printf("=\n");  break;
        case FH:  printf(";\n");  break;
        case DH:  printf(",\n");  break;
        case LXK: printf("(\n");  break;
        case RXK: printf(")\n");  break;
        case LZK: printf("[\n");  break;
        case RZK: printf("]\n");  break;
        case LDK: printf("{\n");  break;
        case RDK: printf("}\n");  break;
        case ENDFILE: if(TokenString!="\0") cout<<"COMMENT:"<<TokenString<<endl;break;   //ENDFILE作为注释结束的输出,直接输出整个字符串 
		case NUM:
	        cout<<"NUM, val="<<TokenString<<endl; break;
        case ID:
	        cout<<"ID, name="<<TokenString<<endl; break;
        case ERROR:   //如果输入符合文法,那么不会出现这种状态 
	        cout<<"ERROR:"<<TokenString<<endl; break;
	}
}

TokenType Find(string a)   //预留词也是特殊的ID,因此当匹配到ID时,应当检测它有没有可能是预留词 
{
	if(a=="if")     return IF;
	else if(a=="else")   return ELSE;
	else if(a=="int")    return INT;
	else if(a=="return") return RETURN;
	else if(a=="void")   return VOID;
	else if(a=="while")  return WHILE;
	else return ID;
}

void getToken(string tmp)   //状态转换函数 
{
	bool save=true;   //是否将当前字符存入匹配单词字符 
	TokenType currentToken;   //当前的字符 
	TokenState state;   //当前的状态 
	string tokenString="";   //用于存放待匹配的单词 
	if(flag==false)   //如果当前还处在注释内部 
	state=START;
	else
	state=INDELETE;   //那么继续返回注释状态 
	for(int i=0;i<=tmp.length();i++)   //按照行为单位读入 
	{
		save=true;
		switch(state)   //判断当前状态 
		{
			case START:   //开始状态下,之后状态参考DFA图片! 
				if(isnum(tmp[i]))
				state=INNUM;
				else if(isalpha(tmp[i]))
				state=INID;
				else if(tmp[i]=='<')
				state=INLT;
				else if(tmp[i]=='>')
				state=INGT;
				else if(tmp[i]=='=')
				state=INEQ;
				else if(tmp[i]=='!')
				state=INNOT;
				else if(tmp[i]==' '||tmp[i]=='\t'||tmp[i]=='\n')
				save=false;
				else if(tmp[i]=='/')
				state=INCOMMENT;
				else
                {
                	state=DONE;
                    switch(tmp[i])
                    {
					    case '\0':   //读取注释到了最后,仍然没有结束,此时返回特殊的状态 
                            save=false;
                            currentToken=ENDFILE;
                            break;
                        case '+':
                            currentToken=ADD;
                            break;
                        case '-':
                            currentToken=SUB;
                            break;
                        case '*':
                            currentToken=MUL;
                            break;
                        case '(':
                            currentToken=LXK;
                            break;
                        case ')':
                            currentToken=RXK;
                            break;
                        case '[':
                            currentToken=LZK;
                            break;
                        case ']':
                            currentToken=RZK;
                            break;
                        case '{':
                            currentToken=LDK;
                            break;
                        case '}':
                            currentToken=RDK;
                            break;
                        case ';':
                            currentToken=FH;
                            break;
                        case ',':
                            currentToken=DH;
                            break;
                        default:
						    currentToken=ERROR;
							break; 
                    }
                }
            break;
			case INCOMMENT:   //将进入注释状态的时候 
				if(tmp[i]!='*')
				{
                    save=true;   //如果是连续的/*,那么进入注释,否则只有一个/,返回除法 
                    state=DONE;
                    i--;
                    currentToken=DIV;
		        }
                else
                {
				    state=INDELETE;
				    flag=true;
				}
				break;
			case INDELETE:
				save=true;
			    if(tmp[i]=='*')
				state=READDELETE;   //正式进入注释,如果有一个*,进入到将要退出注释的状态 
       	        if(tmp[i]=='\0')
                {
				    state=DONE;
                    currentToken=ENDFILE;   //如果当前行所有都已经结束,那么直接把所有的字符给到注释 
                }
       	        break;
       	    case READDELETE:   //将要退出注释的状态,此时如果再来一个/,退出注释,否则返回正式注释状态 
       	        if(tmp[i]=='/')
       	        {
				    state=DONE;
				    flag=false;
				    currentToken=ENDFILE;
				}
       	        else
       	        {
				    i--;
				    state=INDELETE;
				    save=false;
				}
       	        break;
			case INLT:  //<和<=第一个字符都是<,=和==第一个字符都是=,>和>=第一个字符都是>,!和!=第一个字符都是!,下面4种状态,如果没有匹配到第二个“=”,需要退回一个字符 
				state=DONE;
				if(tmp[i]=='=')
				currentToken=LE;
				else
				{
					save=false;
					i--;
					currentToken=L;
				}
				break;
			case INGT:  
				state=DONE;
				if(tmp[i]=='=')
				currentToken=GE;
				else
				{
					save=false;
					i--;
					currentToken=G;
				}
				break;
			case INEQ:
			    state=DONE;
				if(tmp[i]=='=')
				currentToken=ISE;
				else
				{
					save=false;
					i--;
				    currentToken=FZ;
				}
				break;
			case INNOT:
				state=DONE;
				if(tmp[i]=='=')
				currentToken=NTE;
				else
				{
					save=false;
					i--;
					currentToken=NT;
				}
				break;
			case INNUM:   //数字和字母状态,只要接下来不能连续匹配到数字和字母,那么就会停止匹配 
				if(!isdigit(tmp[i]))
                {
                	i--;
                    save=false;
                    state=DONE;
                    currentToken=NUM;
                }
                break;
			case INID:
				if(!isalpha(tmp[i]))
                {
                	i--;
                    save=false;
                    state=DONE;
                    currentToken=ID;
                }
                break;
			case DONE:break;
			default:
				state=DONE;
				currentToken=ERROR;
				break;
		}
		if(save && tmp[i]!='\0')
		tokenString+=tmp[i];   //保存好当前的字符,加入匹配字符串 
		if(currentToken==ID)   //如果是ID类型,那么找一找有没有可能会是预留类 
		currentToken=Find(tokenString);
		if(state==DONE)   //如果匹配完毕 
		{
			PrintToken(currentToken,tokenString);   //输出! 
			tokenString="";   //匹配字符串归零,开始下一轮 
			if(flag==false)   //flag表示是否在注释内,如果当前注释还没结束,需要退回到注释内部状态 
			state=START;
			else
			state=INDELETE;
		}
	}
	LINE++;
}

void read()   //从lab2.txt读取数据 
{
	fstream ff("lab2.txt",ios::in);
	assert(ff.is_open());
    string tmp;
    cout<<"C MINUS COMPILATION:lab2.txt"<<endl;
	while(!ff.eof())
	{
		getline(ff,tmp);
		{
			cout<<endl;
		    cout<<"LINE"<<LINE<<":"<<tmp;
		    if(!ff.eof())
		    cout<<endl;
		}
		if(ff.eof())
		cout<<"EOF"<<endl;
		getToken(tmp);
	}
}

int main(int argc,char* argv[])
{
	read();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/yyd19981117/article/details/86695534