c++ 词法分析器

 

一、项目说明

1. 项目简介

项目目的:设计并实现一个包含预处理功能的词法分析程序,加深对编译中词法分析过程的理解。

项目要求:

1、实现预处理功能 源程序中可能包含有对程序执行无意义的符号,要求将其剔除。 首先编制一个源程序的输入过程,从键盘、文件或文本框输入若干行语句,依次存入输入缓冲区(字符型数据);然后编制一个预处理子程序,去掉输入串中的回车符、换行符和跳格符等编辑性文字;把多个空白符合并为一个;去掉注释。

2、实现词法分析功能 输入:所给文法的源程序字符串。 输出:二元组构成的序列。 具体实现时,可以将单词的二元组用结构进行处理。

3、待分析的C语言子集的词法 1)关键字 main if then while do static int double struct break else long switch case typedef char return const float short continue for void default sizeof do
所有的关键字都是小写。

2)运算符和界符 “ + - * / : := < <> <= > >= = ; ( ) #”

3)其他标记ID和NUM 通过以下正规式定义其他标记: ID→letter(letter|digit)NUM→digit digit letter→a|…|z|A|…|Z digit→0|…|9…

4)空格由空白、制表符和换行符组成 空格一般用来分隔ID、NUM、专用符号和关键字,词法分析阶段通常被忽略。

4、各种单词符号对应的种别码

c++ 词法分析器_第1张图片

功能流程图(代码实现思路基本根据流程图来的):

c++ 词法分析器_第2张图片

二、项目实现

实践出真知,只有在实践过程中才能发现不足,现在让我们打开命令行,键入命令新建一个 .cpp 的文本:

图片描述信息

从流程图可以知道,我们需要判断字符是否为数字、字母、定界符、关键字 我们通过 IsDigit()、IsLetter()、IsSymbol()、IsKeyword() 去实现这个四个功能

第一个函数 IsDigit()

//判断是否为数字 
bool IsDigit(char ch)
{
    if(ch>='0'&&ch<='9')
        return true;
    return false;
}

 

第二个函数 IsLetter()

//判断是否为字母 
bool IsLetter(char ch)
{
    if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
        return true;
    return false;
}

 

第三个函数 IsSymbol()

//判断是否为定界符等
int IsSymbol(char ch)
{
    for(int i=0;    i<9;    i++)
    {
        if(ch==symbol[i])
            return i; 
    }
    return -1;
}

 

第四个函数 IsKeyword()

//判断是否为关键字 
int IsKeyword(string str)
{
    for(int i=0;    i<26;    i++)
    {
        if(str==keyword[i])
        {
            return i;
        }
    }
    return 25;
}

 

读者仔细阅读上面项目要求会发现,还有一个预处理的要求,需要合并空格,去掉注释的功能,下面我们就来完成合并空格的功能。

//空格处理
void HandleSpace(char a[])
{
        int j=0;
        memset(word,0,255);
        temp=false;
        for(int i=0;    i<strlen(a);    i++)
        {

            if(a[i]!=' ' && a[i]!='\t')  //'\t'是table键
                {     
                    word[j++]=a[i];
                    temp=false;
                }
            else
            {

                if(!temp&&a[i]!='\t')
                {
                    word[j++]=a[i];
                    temp=true;
                }

            }
        } 
}

 

然后是处理注释,这里我是将 // 注释进行了预处理,/* */ 注释是在主程序中处理的>

//处理"//"注释
void prePro()
{
    int j=0;
    memset(tempstr,0,255);
    for(int i=0;     i<strlen(word);    i++)
    {
        if(word[i]=='/'&&word[i+1]=='/')
        {
            while(i<strlen(word))
            {
                i++;
            }
        }

        else {
                tempstr[j++]=word[i];

        }
    }

}

 

这样整个程序的核心大部分就完成了,思路就是判断读入的第一个单词是否为字母,若为字母,则为关键字或者标识符,若为数字则为 NUM。

三、完整源码

整个程序的源代码如下:

/*
*author:leetao
*contact:[email protected]
*/
#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;

//存放处理后的字符串 
char tempstr[255]={};
//空格标志 
bool temp=false;
//临时数组
char word[255]={}; 
//keyword关键字 
string keyword[26]={
"main","if","then","while","do","static","defualt","do","int","double","struct","break","else","long","swtich","case","typedf","char","return","const","float","short","continue","for","void","sizeof"};

int keyword_num[26]={1,2,3,4,5,6,39,40,7,8,9,10,11,
            12,13,14,15,16,17,18,19,20,21,22,23,24};
//部分运算符,定界符等 
char symbol[9]={'+','-','*','/','=',';','(',')','#'};
//对应的种码值
int symbol_num[9]={27,28,29,30,38,41,42,43,0};

//判断是否为字母 
bool IsLetter(char ch)
{
    if((ch>='a'&&ch<='z')||(ch>='A'&&ch<='Z'))
        return true;
    return false;
}

//判断是否为数字 
bool IsDigit(char ch)
{
    if(ch>='0'&&ch<='9')
        return true;
    return false;
}

//判断是否为定界符等
int IsSymbol(char ch)
{
    for(int i=0;    i<9;    i++)
    {
        if(ch==symbol[i])
            return i; 
    }
    return -1;
} 

//判断是否为关键字 
int IsKeyword(string str)
{
    for(int i=0;    i<26;    i++)
    {
        if(str==keyword[i])
        {
            return i;
        }
    }
    //不是关键字即为ID
    return 25;
}

//空格处理
void HandleSpace(char a[])
{
        int j=0;
        memset(word,0,255);//需要清空,不然可能残留上次的字符串
        temp=false;
        for(int i=0;    i<strlen(a);    i++)
        {

            if(a[i]!=' ' && a[i]!='\t') 
                {     
                    word[j++]=a[i];
                    temp=false;
                }
            else
            {

                if(!temp&&a[i]!='\t')
                {
                    word[j++]=a[i];
                    temp=true;
                }

            }
        } 
} 

//处理"//"注释
void prePro()
{
    int j=0;
    memset(tempstr,0,255);
    for(int i=0;     i<strlen(word);    i++)
    {
        if(word[i]=='/'&&word[i+1]=='/')
        {
            while(i<strlen(word))
            {
                i++;
            }
        }

        else {
                tempstr[j++]=word[i];
             }
    }
}

int main()
{    
    char instr[255]={}; //接收输入字符串 
    bool flag=false; //多行注释标志,false为未处于注释区域 
    string Token;//存放字符串 
    char *str=NULL;//存放每行的字符串 
    char delims[]=" ";//分割标志 
    freopen("test.cpp","r",stdin);
    freopen("result.txt","w",stdout); //此行注释后,控制台输出,
    //否则文本输出
    while((gets(instr))!=NULL)
    {
        HandleSpace(instr);    
         prePro();

         str=strtok(tempstr,delims);//分割字符串 

         while(str!=NULL) 
            {
                    //头文件,宏定义
                if(*(str)=='#') 
                {
                    printf("#\n"); 
                    break;
                }

                for(int i=0;    i<strlen(str);i++)
                {
                    if(*(str+i)=='/')
                        {
                            if(*(str+i+1)=='*')
                            {
                                flag=true;
                                break;
                            }
                        } 
                        //注释处理: */,注释区域结束 
                        if(*(str+i)=='*'&&flag)
                        {
                            if(*(str+i+1)=='/')
                            {
                                flag=false;
                                i++;
                                break;
                            }
                        }
                        //标识符,关键词 
                      if(IsLetter(*(str+i))&&(!flag))
                        {
//                    printf("进入标识符判断\n");
        while(IsLetter(*(str+i))||IsDigit(*(str+i))
        ||*(str+i)=='_')
                            {
                                Token+=*(str+i);
                                i++;
                            }

        if(IsKeyword(Token)!=25) 
                        {
                printf("%s---->%d\n",Token.c_str(),
                        keyword_num[IsKeyword(Token)]);
                            }
                else printf("%s---->25\n",Token.c_str());

                        Token="";
//                printf("退出标识符判断\n");
                        }        
                    if(IsDigit(*(str+i))&&(!flag))
                        {
//                        printf("进入数字判断\n");
                            while(IsDigit(*(str+i)))
                                {
                                    Token+=*(str+i);
                                    i++;
                                }
                printf("%s------>26\n",Token.c_str());
                            Token="";
                        }

                    //<,<=,<>
                if(*(str+i)=='<'&&(!flag))
                        {
                            if(*(str+i)=='=')    {printf("<=------>35\n");i++;} 
                            if(*(str+i)=='>')    {printf("<>------>34\n");i++;}
                            else printf("<------>33\n");
                        }
                    //>,>=
                         else if(*(str+i)=='>'&&(!flag))
                        {
                            if(*(str+i+1)=='=') {printf(">------>37\n");}
                            else printf(">-------36\n");
                        }
                    //:,:=
                        else  if(*(str+i)==':'&&(!flag))
                        {
                            if(*(str+i+1)=='=') {printf(":=------->32\n");}
                            else printf(":-------->31\n");
                        }
                    //余下定界符等
                        else if(IsSymbol(*(str+i))!=-1&&(!flag))
                        {
                            printf("%c------->%d\n",*(str+i),
                                    symbol_num[IsSymbol(*(str+i))]);
                        } 
                    } 
                str=strtok(NULL,delims);
                }
        }

    return 0;
}

 

这个代码完成了我们还需要一个程序,在当前目录下使用命令行:gvim test.cpp新建一个测试程序test.cpp文件代码如下(读者也可以自行发挥):

#include<stdio.h>
int main()
{
    //test

    /* test */
      for(int i=1;i<0;i++)
        printf("%d",i);

      return 0;
}

 

c++ 词法分析器_第3张图片

四、编译运行

自此准备工作都完成了,现在开始编译了:

g++ testword.cpp -o testword

 

c++ 词法分析器_第4张图片

注意是使用 g++ 而不是 gcc 编译,会出现 waring,不用管,gets() 函数在输入时没有限定字符串的长度,而 linux 是很严谨的,所以这里给出一warning。 这个时候输入 ls,发现目录下已经出现编译成功的 testword 可运行程序,然后运行,成功运行结果如图:

图片描述信息

有个 result.txt 的文件,打开它,内容如下:

c++ 词法分析器_第5张图片

 

 

你可能感兴趣的:(C++)