代码编织梦想

1.1 实验目的

1)理解有穷自动机及其应用。

2)掌握 NFA DFA 的等价变换方法、DFA 最小化的方法。

3)掌握设计、编码、调试词法分析程序的技术和方法。

1.2 实验任务

选择任务二:

编写一个程序对输入的源代码进行词法分析,并打印分析结果。

自己编写一个 PL/0 词法分析程序,本次实验使用C++语言进行编写。

1.3 实验内容

实验要求:

你的程序要能够查出源代码中可能包含的词法错误:

输入格式:

程序输入是一个包含 PL/0 源代码的文本文件,程序需要能够接收一个输入文件名作为参数,以获得相应的输出结果。

输出格式: 

要求输出的信息包括错误类型、出错的行号以及说明文字。

其格式为:

Error type [错误类型] at Line [行号]: [说明文字].

算法描述

首先把程序中合法的单词放在对应类型的map<string,int>变量里,然后从输入文件中读入程序,第一遍遍历:逐个判断输入的一个字符串是否合法,如果不合法则输出错误接着判断下一个单词,如果合法则判断类型。如果有错误则直接结束程序,如果没有错误则进行第二遍遍历,输出词法判断序列。

程序结构

主要变量说明

cc:当前读取的单个字符

ww:判断当前输入程序是否存在词法错误

ss: 存放当前判断的标识符

h:  存储当前行号

gjz:存储当前判断语言的关键字/保留字

ysf:存储当前判断语言的运算符

jf: 存储当前判断语言的界符

程序清单

函数isl()用来判断当前输入字符是否为字母,通过与‘a’、‘z’、‘A’、‘Z’比较大小来判断并返回结果。

bool isl(char c)//判断字母

{

    if(c>='a'&&c<='z')

    return 1;

    if(c>='A'&&c<='Z')

    return 1;

    return 0;

}

函数ism()用来判断当前输入字符是否为数字,通过与‘0’、‘9’比较大小来判断并返回结果。

bool ism(char c)//判断数字

{

    if(c>='0'&&c<='9')

    return 1;

    return 0;

}

函数isf()用来判断当前输入字符是否为运算符或者界符,通过与搜索map中当前搜索的字符出现次数,未出现则返回0,以此来判断并返回结果。

int isf(string c)//判断字符

{

    //运算符

    if(ysf.count(c))

    {

        return 1;

    }

    //界符

    if(jf.count(c))

    {

        return 2;

    }

    return 0;

}

函数analysis()用来对输入文件进行词法分析,依次读入当前程序的字符,

首先判断当前程序字符是否读完,以及将空格换行等格式符号跳过;

再判断当前输入符号是否为字母,如果当前字符为字母则接收整个字符串直到空格出现或者运算符和界符出现,在此过程中对每个字符进行检查,如果出现程序中不应该出现的符号则报错提示不合法输入。

接收完字符串后判断当前字符串是否为保留字,如果不是保留字则为标识符。

如果当前字符为数字,则继续接收整个字符串,当空格出现或者运算符和界符出现停止接收,在此过程中对每个字符进行检查,如果出现程序中不应该出现的符号则报错提示不合法输入,如果接收的字符串全为数字,则当前字符串为常数。

如果当前字符为运算符或者界符,则继续判断下一个字符是否为运算符或界符,因为有的运算符由两个字符组成,在此过程中对每个字符进行检查,如果出现程序中不应该出现的符号则报错提示不合法输入,如果接收的字符串能在map类型的jf中找到,则当前字符串为运算符或界符。

当前字符指向下一个字符,重复上述判断步骤。

int analysis(FILE* fl)

{

    do

    {

      cc = fgetc(fl);

      if( feof(fl) ) // 读完

      {

          break ;

      }

      if(cc==' '|| cc=='\t')//格式符号,不处理

      {

           continue;        

      }

      if(cc=='\n')  //换行加一

      {

            h++;

      }

      else

      {

           if(isl(cc))        

 //字母开头    保留字   标识符    非法字符

           {  

                string s="";

                s+=cc;

               //接收整个字符

               while(1){

                char ls=' ';

                ls=fgetc(fl);

                if(ls==' '|| ls=='\t'||ls=='\n'||feof(fl))

                {

                    if(ls=='\n')

                    h++;

                    break;

                }

                string lss="";

                lss+=ls;

                if(isf(lss))  

//符号有可能紧挨着标识符等出现

                {

                    //回退一个指针

                    fseek(fl, -1L, SEEK_CUR);

                    break;

                }

                if(ism(ls)==0&&isl(ls)==0&&isf(lss)==0)  

//不合法输入

                {

                    cc=ls;

                    ww=1;

                        cout<<"Error type A at Line ";

                        cout<<h;

                        cout<<": Mysterious character ";

                        cout<<cc;

                        cout<<"."<<endl;

                }

                s+=ls;

               }

               //判断是否为关键字  

                if(gjz.count(s)&&ww==2)

                {

                    cout<<"保留字:"<<s<<endl;

                }

                else if(ww==2){

                    cout<<"标识符:"<<s<<endl;

                }

           }

           else if (ism(cc))  

 //数字开头   只能是纯数字   其他不合法

           {

                bool zm=0;

                string s="";

                s+=cc;

               //接收整个字符

               while(1){

                char ls=' ';

                ls=fgetc(fl);

                if(ls==' '|| ls=='\t'||ls=='\n'||feof(fl))

                {

                    if(ls=='\n')

                    h++;

                     break;

                }

                string lss="";

                lss+=ls;

                if(isf(lss))  

//符号有可能紧挨着标识符等出现

                {

                    if(lss!=".")

                    {

                        //回退一个指针

                        fseek(fl, -1L, SEEK_CUR);

                        break;

                    }

                }

                if(ism(ls)==0&&isl(ls)==0&&isf(lss)==0)  

//不合法输入

                {

                    cc=ls;

                    ww=1;

               

                        cout<<"Error type A at Line ";

                        cout<<h;

                        cout<<": Mysterious character ";

                        cout<<cc;

                        cout<<"."<<endl;

                }

                if(isl(ls))

                {

                    zm=1;

                }

                s+=ls;

               }

                if(zm)

                {

                    ss=s;

                    ww=1;

                      //标识符不合法

                        cout<<"Error type A at Line ";

                        cout<<h;

                        cout<<": Mysterious character ";

                        cout<<ss;

                        cout<<"."<<endl;

                }

                else if(ww==2){

                    cout<<"常数:"<<s<<endl;

                }

            /* code */

           }

           else{

            string lss="";

                  lss+=cc;

            if (isf(lss))     //字符处理

           {

                if(lss==":")

                {

                char ls=' ';

                ls=fgetc(fl);

                if(ls==' '|| ls=='\t'||ls=='\n'||feof(fl))

                {

                    if(ls=='\n')

                    h++;

                     break;

                }

                if(ism(ls)==0&&isl(ls)==0&&isf(lss)==0)  

//不合法输入

                {

                    cc=ls;

                    ww=1;

                   

                        cout<<"Error type A at Line ";

                        cout<<h;

                        cout<<": Mysterious character ";

                        cout<<cc;

                        cout<<"."<<endl;

                }

                if(ls=='=')

                {

                    if(ww==2)

                    {

                        cout<<"运算符:"<<":="<<endl;

                    }

                   

                }

                else{

                    cc=ls;

                    //回退一个指针

                    fseek(fl, -1L, SEEK_CUR);

                   

                        cout<<"Error type A at Line ";

                        cout<<h;

                        cout<<": Mysterious character ";

                        cout<<cc;

                        cout<<"."<<endl;

                }

                }

                else if(lss==">")

                {

                char ls=' ';

                ls=fgetc(fl);

                if(ls==' '|| ls=='\t'||ls=='\n'||feof(fl))

                {

                    if(ls=='\n')

                    h++;

                     break;

                }

                if(ls=='='&&ww==2)

                {

                    cout<<"运算符:"<<">="<<endl;

                }

                else if(ww==2){

                    cout<<"运算符:"<<">"<<endl;

                }

                }

                else if(lss=="<")

                {

                char ls=' ';

                ls=fgetc(fl);

                if(ls==' '|| ls=='\t'||ls=='\n'||feof(fl))

                {

                    if(ls=='\n')

                    h++;

                     break;

                }

                if(ls=='='&&ww==2)

                {

                    cout<<"运算符:"<<"<="<<endl;

                }

                else if(ww==2){

                    cout<<"运算符:"<<"<"<<endl;

                }

                }

                else{

                    if(isf(lss)==1&&ww==2)

                    {

                        cout<<"运算符:"<<lss<<endl;

                    }

                    else if(ww==2){

                        cout<<"界符:"<<lss<<endl;

                    }

                }

           }

           else{

                    ww=1;

                       

                        cout<<"Error type A at Line ";

                        cout<<h;

                        cout<<": Mysterious character ";

                        cout<<cc;

                        cout<<"."<<endl;

           }

           }

      }

    }while(1);

   return 2;

};

主函数main()实现以下功能:

将相应保留字,运算符,界符存入相应的map中。

提示用户输入文件,并接收文件,判断文件是否可以正常打开。

调用词法分析函数进行判断,此次判断主要目的是检查输入程序中是否有错误,有则输出错误信息。

如果没有错误信息则进行第二次遍历,输出词法分析序列。

最后,关闭打开文件,结束程序。

int main()

{  

    //PL/0 保留字

    gjz["const"]=1;gjz["var"]=1;gjz["procedur"]=1;gjz["begin"]=1;gjz["end"]=1;

    gjz["odd"]=1;gjz["if"]=1;gjz["then"]=1;gjz["call"]=1;gjz["while"]=1;

    gjz["do"]=1;gjz["read"]=1;gjz["write"]=1;

    //PL/0 运算符

    ysf["+"]=1;ysf["-"]=1;ysf["*"]=1;ysf["/"]=1;ysf["<"]=1;

    ysf["<="]=1;ysf[">"]=1;ysf[">="]=1;ysf["#"]=1;ysf["="]=1;ysf[":"]=1;

    //PL/0 界符

    jf["("]=1;jf[")"]=1;jf[","]=1;jf[";"]=1;jf["."]=1;

    char input[30];

    FILE* flin;

    cout<<"请输入源文件名:"<<endl;

    for(;;)

    {

        cin>>input;

        if ((flin = fopen(input, "r")) != NULL) //打开文件

             break;

        else

            cout << "路径输入错误" << endl;

    }

    cout<<"词法分析结果:"<<endl;

   

    int jg=analysis(flin);

    flin = fopen(input, "r");

    if(!ww)

    {

        ww=2;

        jg=analysis(flin);

    }

    fclose(flin);//关闭文件

    return 0;

}

调试情况及各种情况运行结果截图

结果展示:

  1. 输入程序没有错误的案例:

  1. 输入程序有一个错误的案例

  1. 输入程序有多个错误的案例:

1.4 心得体会

1.5 参考资料

完整版资源中有

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_51534768/article/details/127832877

编译原理 实验一 词法分析器_erin_yu的博客-爱代码爱编程_词法分析器

编写一个词法分析程序 实验目的:理解词法分析在编译程序中的作用;                  加深对有穷自动机模型的理解;                  掌握词法分析程序的实现方法和技术。 实验内容:选择部分C语言的语法成分,设计其词法分析程序,要求能够识别关键字、运算符、分界符、标识符、常量(至少是整型常量,可以自己扩充识别其他常量)等,并

编译原理 实验1 词法分析-爱代码爱编程

文章目录 1 运行结果2 项目代码2.1 代码结构2.2 具体代码3 实验内容3.1 实验要求3.2 算法描述3.3 程序结构3.4 主要变量说明3.5 主要常量及类型说明3.6 扩展功能测试 1 运行结果 t1.txt: const a = 10; var b,c; procedure p; begin c:=b+a end;

编译原理--实验1 词法分析-爱代码爱编程

文章目录 前言1.1 实验目的1.2 实验任务1.3 实验内容1.3.1 实验要求1.3.2 输入格式1.3.3 输出格式1.3.4 样例1.3.5 C--语言文法1.4 程序代码1.4.1 程序流程图1.4.2 程序源码1.5 总结 前言 编译原理课程实验的实验课内容—构造词法分析程序。通过本次实验,希望对于编译原理中词法分析这个功能有更深

基于openssl的aes加密(c/c++)_无聊到发博客的菜鸟的博客-爱代码爱编程

环境 操作系统:WSL2-Ubuntu22.04 加密库:OpenSSL,Base64 在线AES计算网站:SSLeye 代码中需要用到OpenSSL和Base64,可以根据上述链接获取 简介 高级加密标准(

【c++】模板template_是星星鸭的博客-爱代码爱编程

前言:本教程使用到的工具是vs2010; 目录 为什么要使用模板?  template模板         函数模板         类的模板 template模板的本质  总结 为什么要使用模板?          我们先来大概了解一下模板的概念,下面是菜鸟教程对于模板给出的解释:         模板是泛型编程的