【编译原理】实验一 词法分析器的构造(预处理去注释空格Tab换行)

一、实验目的

设计、编制、调试一个词法分析程序,对单词进行识别和编码,加深对词法分析原理的理解。

二、设计内容
设计并实现一个词法分析器,实现对指定位置的类C 语言源程序文本文件的读取,并能够对该源程

序中的所有单词进行分类,指出其所属类型,实现简单的词法分析操作。

三、实验要求
1、允许用户自己输入程序并保存为文件
2、系统能够输出经过预处理后的源程序(去掉注释、换行、空格等)
3、能够将该源程序中所有的单词根据其所属类型(整数、保留字、运算符、标识符等。定义的类
C 语言中的标识符只能以字母或下划线开头)进行归类显示,例如:识别保留字:if、int、for、while、
do、return、break、continue 等,其他的都识别为标识符;常数为无符号整形数;运算符包括:+、-、
*、/、=、>、<、>=、<=、!=等;分隔符包括:,、;、{、}、(、)等。

4、实现文件的读取操作,而不是将文本以字符串形式预存于程序中。文本内容为待分析的类C 语
言程序。

解决方案:

关于实验要求1:主要涉及的是文件的写入和保存,写入和保存的内容是用户输入的程序。怎么处理的?我的方法是在这部分写两个函数,一个函数用来输入程序:,另一个函数用来保存文件。

关于实验要求2:一个函数,用来清除文本信息中的空格 换行 Tab,同时调用该函数后,将经过预处理的内容保存下来,我是在本地又新建了一个文本文件。

关于实验要求3:这部分是整个实验的关键部分,涉及到对预处理文本的处理。关于这部分的算法思路,在编译原理书上有提到,但是使用Pascal语言写的,但是思想是基本不变的,理解下就比较好些了。我想提的一点是,关于RETRACT函数,即实现“将字符数组指针向前移动一个位置”的功能,请查找fseek()函数的使用。

关于实验要求4:文件的读取函数,readFile()。

自己写的代码实验要求基本都实现了,测了几组也没bug,不过感觉还需要改进,过些日子再贴上来,暂时留这。


--------------------------------------------------------------------2015.10.23更新--------------------------------------------------------------------

之前写的代码不具备处理注释的功能,后来加进去了,需要对上述解决方案做下改动,主要是函数和结构,改动如下:

函数说明:

对读文件函数和写文件函数、预处理文件函数进行了改动,增加注释预处理部分dealNote()。

程序运行过程:先由用户在键盘上写程序,保存文件至G:\contextfile.txt,对注释部分进行处理,保存处理后的文件至G:\nonotecontextfile.txt,再对该文件进行在空格、Tab键和空格上的预处理,最后进行judge判断。

测试数据如下:

/*

main()
{
int a,b;
a = 10;
b = a + 20;   /*dsdsdsdsd*/

}

*/

代码:

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <fstream>
#include <string.h>
#include <math.h>

using namespace std;

const int LEN = 0Xfff;

char fsm[8][128];  //清除注释,做一个状态机

FILE *fp;     //文件指针

char CHAR;    //字符变量CHAR,它存放着新读入的源程序字符

string TOKEN;  //字符串TOKEN,它存放着构成单词符号的字符串

string afterPreDeal;  //存放着经过预处理后的程序内容

string TABLE[LEN] = {"if", "int", "for", "while", "do", "return", "break", "continue", "else"};  //保留字

void inputFile();  //输入程序内容(允许换行、空格和Tab键)

void dealNote();

void saveToFile(char *p, char * filename); //将输入的程序内容保存到文件里

void judge(char * filename);  //对单词进行归类显示

string& trim(string &str, string::size_type pos = 0); //去除文本文件中的空格、Tab和换行

void readFile(char * filename);  //读取文件内容并输出

void preDeal(char * filename);   //对程序文件做预处理,并将预处理后的内容保存在新文件G:\newcontextfile.txt中

void GETCHAR();   //用于读取下一个原程序字符至CHAR中,并把字符指针向后移一位

void GETNBC();   //先检查CHAR是否为空白字符,若是则反复调用GETCHAR直到CHAR读入的是一个非空白字符

void CONCAT();   //将CHAR字符连接到TOKEN后面

bool LETTER(char CHAR);  //布尔函数,若CHAR为字母则返回TRUE,反之返回FALSE;

bool DIGIT(char CHAR);   //布尔函数,若CHAR为数字则返回TRUE,反之返回FALSE;

bool UNDERLINE(char CHAR);  //布尔函数,若CHAR为下划线则返回TRUE,反之返回FALSE;

int RESERVE();   //由TOKEN字符串查保留字表,若TOKEN中字符串为保留字则返回其种别编码,否则返回值为0

void RETRACT();  //将字符指针向前移动一个位置,CHAR置为空白字符

void initfsm();

int main()
{
    //保存内容
    cout << "*********你好,请输入程序内容(以Ctrl+Z结束)*********" << endl;

    inputFile();

    dealNote(); //清除注释

    preDeal("G:\nonotecontextfile.txt");  //对清除注释后的文件进行其他预处理,比如去空格,去tab,去换行

    cout << "*********输出判断结果*********" << endl;

    judge("G:\newcontextfile.txt");

    cout << endl;

    return 0;
}

void inputFile()
{
    char s[10] = {''};
    fstream file("G:\contextfile.txt", ios::out);
    while((scanf("%c",&s))!=EOF)
    {
        file.write(s, strlen(s));
    }
    file.close();
}

void dealNote()
{
    int state=0;
    char c;
    std::string s;
    FILE *fin=fopen("G:\contextfile.txt","r");
    FILE *fout=fopen("G:\nonotecontextfile.txt","w");
    initfsm();
    while(fscanf(fin,"%c",&c)!=EOF)
    {
        state=fsm[state][c];
        s+=c;
        switch(state)
        {
            case 0:
                fprintf(fout,"%s",s.c_str());
                s="";
                break;
            case 7:
                s="";
                if(c=='
')
                {
                    fputc(c,fout);
                }
                break;
        }
    }
    fclose(fin);
    fclose(fout);
}

void saveToFile(char *p, char * filename)
{
    if ((fp = fopen(filename, "wb")) == NULL )
    {
        return;
    }
    else
    {
        fwrite(p, strlen(p), 1, fp);
    }
    fclose(fp);
}

void judge(char *filename)
{
    fp = fopen(filename, "r+");
    while(!feof(fp))
    {
        TOKEN.clear();
        GETCHAR();
        if(LETTER(CHAR))
        {
            int c;
            int flag = 0;
            while(LETTER(CHAR) || DIGIT(CHAR) || UNDERLINE(CHAR))
            {
                CONCAT();
                GETCHAR();
                {
                    c = RESERVE();
                    if(c == 2)
                    {
                        cout << "(1," << """ << TOKEN << """ << ")" << endl;
                        flag = 1;
                        break;
                    }
                }
            }
            RETRACT();
            if(flag == 0)
            {
                c = RESERVE();
                if(c == 0)
                    cout << "(2," << """ << TOKEN  << """ << ")" << endl;
                else
                      cout << "(1," << """ << TOKEN  << """ << ")" << endl;
            }
            continue;
        }
        else if(UNDERLINE(CHAR))
        {
           int c;
            int flag = 0;
            while(LETTER(CHAR) || DIGIT(CHAR) || UNDERLINE(CHAR))
            {
                CONCAT();
                GETCHAR();
                {
                    c = RESERVE();
                    if(c == 2)
                    {
                          cout << "(1," << """ << TOKEN << """ << ")" << endl;
                        flag = 1;
                        break;
                    }
                }
            }
            RETRACT();
            if(flag == 0)
            {
                c = RESERVE();
                if(c == 0)
                      cout << "(2," << """ << TOKEN << """ << ")" << endl;
                else
                    cout << "(1," << """ << TOKEN << """ << ")" << endl;
            }
            continue;
        }
        else if(DIGIT(CHAR))
        {
             while(DIGIT(CHAR))
             {
                 CONCAT();
                 GETCHAR();
             }
             RETRACT();

             cout << "(3," << """ << TOKEN << """ << ")" << endl;

             continue;
        }
        else if(CHAR == ',' || CHAR == ';' || CHAR == '{' || CHAR == '}' || CHAR == '(' || CHAR == ')')
        {
              cout << "(5," << """ << CHAR << """ << ")" << endl;
              continue;
        }
        else if( CHAR == '+' || CHAR == '-' || CHAR == '*' || CHAR == '\' || CHAR == '=' )
        {
              char mmd = CHAR;
              GETCHAR();
              if(CHAR == '=')
              {
                  cout << "(4," << """ << mmd << """ << "=)" << endl;
              }
              else
              {
                 RETRACT();
                 cout << "(4," << """ << mmd << """ << ")" << endl;
              }
              continue;
        }
        else if(CHAR == ' '|| CHAR == '
' || CHAR == '	')
        {
            continue;
        }
        else
        {
              if((int)CHAR == -1)
                return;
          //    cout << "ERROR Message" << endl;
        }

    }
    fclose(fp);/*关闭文件*/
}

string& trim(string &str, string::size_type pos)
{
    static const string delim = " 	
"; //删除空格或者tab字符
    pos = str.find_first_of(delim, pos);
    if (pos == string::npos)
       return str;
    return trim(str.erase(pos, 1));
}

void readFile(char * filename)
{
    FILE *pFile=fopen(filename, "r"); //获取文件的指针
    char *pBuf;  //定义文件指针
    fseek(pFile, 0, SEEK_END); //把指针移动到文件的结尾 ,获取文件长度
    int len = ftell(pFile); //获取文件长度
    pBuf = new char[len+1]; //定义数组长度
    rewind(pFile); //把指针移动到文件开头 因为我们一开始把指针移动到结尾,如果不移动回来 会出错
    fread(pBuf,1,len,pFile); //读文件
    pBuf[len]=0; //把读到的文件最后一位 写为0 要不然系统会一直寻找到0后才结束
    printf("%s", pBuf);  //显示读到的数据
    fclose(pFile); // 关闭文件
}

void preDeal(char * filename)   //对程序文件做预处理,保存在新文件G:\newcontextfile.txt中
{
  //  dealNote();
    FILE *pFile=fopen(filename, "r"); //获取文件的指针
    char *pBuf;  //定义文件指针
    fseek(pFile, 0, SEEK_END); //把指针移动到文件的结尾 ,获取文件长度
    int len = ftell(pFile); //获取文件长度
    pBuf = new char[len+1]; //定义数组长度

    string predeal;

    int n = len;

    if ((fp = fopen(filename, "rb")) == NULL )
    {
        return;
    }
    else
    {
        char p[100];
        fread(p, n, 1, fp);
        p[n]='';
        predeal = p;
    }
    predeal = trim(predeal);
    char *con = new char[predeal.length() + 10];
    strcpy(con, predeal.c_str());

    saveToFile(con, "G:\newcontextfile.txt");  //将新文件内容保存为G:\newcontextfile.txt
    cout << endl;

    afterPreDeal = predeal;

    fclose(fp);/*关闭文件*/
}

void GETCHAR()
{
    CHAR = fgetc(fp);
   // cout << "*" << CHAR << "*" << endl;
}

void GETNBC()
{
    while(CHAR == ' ' || CHAR == '
' || CHAR == '	')
    {
        GETCHAR();
    }
}

void CONCAT()
{
    TOKEN += CHAR;
}

bool LETTER(char CHAR)
{
    bool isLetter = isalpha(CHAR);
    return isLetter;
}

bool DIGIT(char CHAR)
{
    bool isDigit = isdigit(CHAR);
    return isDigit;
}

bool UNDERLINE(char CHAR)
{
    if(CHAR == '_')
        return true;
    else
        return false;
}

int RESERVE()
{
    int i = 0;
    int N = 9;
    int val = 0;
    for(i = 0; i < N; i++)
    {
        if(TOKEN == TABLE[i])
        {
            val = 2;
            break;
        }
    }
    return val;
}

void RETRACT()
{
    CHAR = ' ';
    fseek(fp,-1,SEEK_CUR);
}

void initfsm()
{
	const int line_len = sizeof(char)*128;
	memset(fsm[0],0,line_len);
	memset(fsm[1],0,line_len);
	memset(fsm[2],2,line_len);
	memset(fsm[3],3,line_len);
	memset(fsm[4],3,line_len);
	memset(fsm[5],5,line_len);
	memset(fsm[6],5,line_len);
	memset(fsm[7],0,line_len);
	fsm[0]['/'] = 1;
	fsm[0]['"'] = 5;
	fsm[1]['/'] = 2;
	fsm[1]['*'] = 3;
	fsm[1]['"'] = 5;
	fsm[2]['
'] = 7;
	fsm[3]['*'] = 4;
	fsm[4]['/'] = 7;
	fsm[4]['*'] = 4;
	fsm[5]['"'] = 0;
	fsm[5]['\'] = 6;
	fsm[7]['/'] = 1;
	fsm[7]['"'] = 5;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

原文地址:https://www.cnblogs.com/Tobyuyu/p/4965341.html