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、各种单词符号对应的种别码

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

二、项目实现

实践出真知,只有在实践过程中才能发现不足,现在让我们打开命令行,键入命令新建一个 .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;
}

四、编译运行

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

g++ testword.cpp -o testword

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

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

时间: 2024-08-02 23:14:46

c++ 词法分析器的相关文章

词法分析器

相关定义: 识别器:可以在字符流中识别特定单词的程序. 有限自动机(FA):识别器的一种形式化方法,包含一个有限状态集,一个字母表,一个转移函数,一个起始状态和一个或多个接受状态. 正则表达式:有限自动机所接受的单词的集合,形成的语言.eg:n(ew|ot) RE--NFA--DFA--最小DFA--词法分析器 RE-NFA:Thompson构造法,re的连接,选择,闭包等有一个模板,各部分的连接也有一个模板,按照模板构造 NFA--DFA:子集构造法(不动点计算,即即便计算过程中不断添加要计算

词法分析器实验报告

一.        实验目的 编制一个词法分析器,通过该词法分析程序的设计实例,进一步了解词法分析程序构造的一些细节. 二.        实验内容和要求 实验内容: 对字符串表示的源程序,从左到右进行扫描和分解.根据词法规则,识别出一个一个具有独立意义的单词符号,以供语法分析之用,若发现词法错误,则返回出错信息. 实验要求: 输入:源程序字符串 输出:二元组(种别,单词本身) 待分析语言的词法规则 三.        实验方法.步骤及结果测试  1.      源程序名:cffx.cpp 可执

编译原理简单词法分析器(first,follow,分析表)源码下载

编译原理(简单词法分析器下载) http://files.cnblogs.com/files/hujunzheng/%E7%AE%80%E5%8D%95%E8%AF%AD%E6%B3%95%E5%88%86%E6%9E%90%E5%99%A8.zip

【词法分析器 】 编译原理作业

自己写的版本: 问题: 1:考虑到由于是嵌套接收,浮点型感觉少麻烦,因为还要判断是否两个小数点等,古没有实现 2:对于一些特殊符号,如三元运算符,格式控制符%d等分析不到位 3:类别应该分的更细,否则用唯一的symbol(sym)标识的话无法进行后续的语法分析 4:没有用文件指针,数据输入要在控制台,不利于交互 #include <iostream> #include <string.h> #include <cstdio> using namespace std; i

词法分析器 /c++实现

#include<iostream> #include<string> #include<vector> #include<map> #include<cstdio> #include<cstring> #include<cstdlib> using namespace std; int line=1,row=1; char c; map<char,int>ma; struct kind { string na

词法分析器:代码注释

前沿:词法分析器是将一段程序的代码按照类别分开.一般来说是将关键字, 变量名  , 常数 运算符( + _ * / )和界符分类词法分析算是编译的基础把今天上编译原理的实验课, 看了看  老师给的代码 添加了一些注释大致的流程是这样的:规定关键字的符号是10数字的符号是数字本身+ - *  = 这些符号代码中的case里面有(分别是13 14 ...),可以看懂的首先, 把程序存到制定的内存区域, 这里是划出了一个连续的空间(放到字符数组);然后再按字节读取里面的内容 , 当读到空格(" &qu

简易词法分析器

最近开始学习编译器,希望能坚持下去,通过做编译器也把c++练好点,今天花了一天做了一个简单的词法分析器,过几天编译原理书到了后,希望能通过看书得到更好的方法. //learn.cpp #include <iostream> #include "learn.h" #include <fstream> using namespace std; static char *st_line; static int st_line_pos; int length; type

词法分析器总结

总结一些编译原理词法分析的内容. 1 简单介绍 对于编译语言而言,程序都需要通过编译器编译成二进制的代码,这样才可以在计算机上运行.对于现在的程序而言,并不能直接将其翻译成二进制可执行文件.中间需要若干中间环节,其中通常包含词法分析器,语法分析器,语意分析器.这里主要总结一下词法分析器. 词法分析器的主要功能是将程序切分为一个个单词,并将单词进行相关的分类. 1 int x; 2 x = 10; 比如说以上最简单的一个赋值语句.经过词法分析后,就会被切分成,其中的一些空格等等都会被处理过滤掉 1

编译器实践 二 之 小型的词法分析器

下面是一个简单的词法分析器 #include <stdio.h> #include <string.h> using namespace std ; int main() { char str[150] ; // freopen("C:\\Users\\Lionel\\Desktop\\1.txt","r",stdin) ; int row = 1 , cow = 0 ; while(gets(str)) { char temp[10] ;

编译原理(简单自动词法分析器LEX)

编译原理(简单自动词法分析器LEX)源程序下载地址:  http://files.cnblogs.com/files/hujunzheng/%E6%B1%87%E7%BC%96%E5%8E%9F%E7%90%86%E7%AE%80%E5%8D%95LEX%EF%BC%88%E8%AF%8D%E6%B3%95%E8%87%AA%E5%8A%A8%E5%88%86%E6%9E%90%E5%99%A8%EF%BC%89.zip