实验1《词法分析程序设计与实现》
一、实验目的

  加深对词法分析器的工作过程的理解;加强对词法分析方法的掌握;能够采用一种编程语言实现简单的词法分析程序;能够使用自己编写的分析程序对简单的程序段进行词法分析。

二、实验内容

  自定义一种程序设计语言,或者选择已有的一种高级语言,编制它的词法分析程序。词法分析程序的实现可以采用任何一种编程语言和编程工具。
  从输入的源程序中,识别出各个具有独立意义的单词,即关键字、标识符、常数、运算符、界符。并依次输出各个单词的内部编码及单词符号自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示)

三、实验方法
  1. 实验采用C++程序语言进行设计,利用txt文本对源程序进行存储;
  2. 实验先自定义词法规则,更具定义的词法规则绘制对应的DFA图,根据DFA图进行源程序编写;
  3. 源程序编写先定义部分函数,比如判断字符串是否为关键字、是否为运算符等,再进行源程序编写;
四、实验步骤
  1. 定义目标语言的可用符号表;
    1.1 关键字:int、char、string、bool、float、double、true、false、return、if、else、while、for、default、do、public、static、switch。关键字的内部编码为1;
    1.2 标识符:①标识符由字母、数字、下划线(_)组成;②不能把C++关键字作为标识符;③标识符对大小写敏感;④首字符只能是字母或下划线,不能是数字。标识符的内部编码为2。
    1.3 常数:无符号整数值。常量的内部编码为3。
    1.4 运算符:+、-、*、/、^、=、<=、>=、!=。运算符的内部编码为4。
    1.5 界符:,、;、{、}、(、)。界符的内部编码为5。

  2. 定义程序输入输出;
    2.1 程序输入:依照C++程序规则输入,以txt文本形式存储;
    2.2 程序输出:程序依照<单词内部编码,值>的形式每行一个进行输出,例如(5,”(”)。

  3. 依据读入的规则,构造本词法分析器的DFA图;

  4. 根据程序要求,绘制流程图;
    在这里插入图片描述

  5. 编写源程序,依次读入源程序符号,对源程序进行逐个字符的读入,然后判别再处理,直到源程序结束;

  6. 对正确的单词,按照<单词内部编码,值>的形式进行输出;

  7. 对不正确的单词,做出错误处理,并进行记录输出。

五、实验结果
  1. 实验的测试源代码为:
      实验测试源代码中包含关键字(int、if)、标识符(main、_a2、b)、常数(10、20、3-5)、运算符(=、+、<=)、分隔符(;、(、)、{、})以及错误输入(==、%、3ab),对应自定义的目标可用符号表内容均包含在内,能够完整的展示程序功能。
    test1.txt:
main(){
	int  _a2,b;
	_a2 == 10;
	b = a + 20;
%
	int 3ab;
	if(a<=3){
		a=5;
	}
}
  1. 实验测试结果:
      测试结果符合预期结果,根据自定义的词法结构,程序对源程序进行识别的单词类别与值均能一一对应,一些错误输入或者未定义的字符也能以Error形式进行标识,比如(Error,”%”)。实验截图如下所示:
    在这里插入图片描述
六、实验结论
  1. 实验利用自定义的源程序进行测试,结果正确,符合预期结果,测试源码及结果截图和说明如上所示。
  2. 实验源代码如下所示:
    test1.cpp
/**************************
Compiler Principle
test1 lexical analyzer
author:zz
codeblocks
2019.03.29
***************************/

#include <iostream>
#include <fstream>
#include <cassert>
#include <string>

using namespace std;

//判断当前字符串是否为关键字
bool isKey(string s){
    //关键字数组
    string keyArray[] = {"int","char","string","bool","float","double","float","true","false","return",
                        "if","else","while","for","default","do","public","static","switch"};
    //与当前字符串一一对比
    for(int i=0;i<sizeof(keyArray);i++){
        if(keyArray[i] == s){
            return true;
        }
        else{
            return false;
        }
    }
}

//判断当前字符是否是运算符
bool isOperator(char ch){
    if('+'==ch || '-'==ch || '*'==ch || '/'==ch || '^'==ch || '='==ch || '<'==ch || '>'==ch || '!'==ch)
        return true;
    else
        return false;
}

//判断当前字符是否是分隔符
bool isSeparator(char ch){
    if(','==ch || ';'==ch || '{'==ch || '}'==ch || '('==ch || ')'==ch)
        return true;
    else
        return false;
}


int main( )
{
    //定义字符变量,保存从源程序中读取的单个字符
    char ch;
    //定义字符串,保存从源程序中连续读取的字符串
    string result;
    //存放每个获取的单词的值
    string resultArray[999];
    //记录获取单词的个数
    int resultNum=0;

    //代码存放的文件名
    string file = "test1.txt";

    ifstream infile;
    //将文件流对象与文件连接起来
    infile.open(file.data());
    //若失败,则输出错误消息,并终止程序运行
    assert(infile.is_open());

    //txt文本中读取空格符与换行符
    //infile >> noskipws;
    //读取文本中的一个字符
    infile>>ch;

    while (!infile.eof())
    {
        //ch是英文字母或者下划线
        if(isalpha(ch) || '_'==ch){
            result.append(1,ch);
            infile>>ch;
            //判断是否为关键字
            if(isKey(result)){
                resultArray[resultNum++]="(1,\""+result+"\")";
                result="";
            }
            //读入首字符为字母,继续读入字母、数字、下划线,组成标识符或者关键字
            while(isalpha(ch) || isdigit(ch) || '_'==ch){
                result.append(1,ch);
                infile>>ch;
                if(isKey(result)){
                    resultArray[resultNum++]="(1,\""+result+"\")";
                    result="";
                }
            }
            //读入操作符或者运算符,正确保存标识符或者关键字
            if(isSeparator(ch) || isOperator(ch)){
                if(isKey(result)){
                    resultArray[resultNum++]="(1,\""+result+"\")";
                    result="";
                    continue;
                }
                else{
                    if(isdigit(result.at(0))){
                        resultArray[resultNum++]="(Error,\""+result+"\")";
                    }else{
                        resultArray[resultNum++]="(2,\""+result+"\")";
                    }
                    result="";
                    continue;
                }

            }
            //读入不是字母、数字、运算符、标识符,继续读入直到遇到运算符或者分隔符
            else{
                result.append(1,ch);
                infile>>ch;
                while(!isSeparator(ch) && !isOperator(ch)){
                    result.append(1,ch);
                    infile>>ch;
                }
                resultArray[resultNum++]="(Error,\""+result+"\")";
                result="";
                continue;
            }
        }
        //读入数字
        else if(isdigit(ch)){
            result.append(1,ch);
            infile>>ch;
            //继续读入数字,组成常数
            while(isdigit(ch)){
                result.append(1,ch);
                infile>>ch;
            }
            //遇到操作符或者运算符,正常终止
            if(isOperator(ch) || isSeparator(ch)){
                resultArray[resultNum++]="(3,\""+result+"\")";
                result="";
                continue;
            }
            //读入其他错误字符
            else{
                result.append(1,ch);
                infile>>ch;
                while(!isSeparator(ch) && !isOperator(ch)){
                    result.append(1,ch);
                    infile>>ch;
                }
                resultArray[resultNum++]="(Error,\""+result+"\")";
                result="";
                continue;
            }
        }
        //遇到运算符
        else if(isOperator(ch)){
            result.append(1,ch);
            infile>>ch;
            //判断是否存在<=、>=、!=
            if("<"==result || ">"==result || "!"==result){
                if('='==ch){
                    result.append(1,ch);
                    infile>>ch;
                }
            }
            //下一个读入符为字母、数字、分隔符,即正确
            if(isalpha(ch) || isdigit(ch) || isSeparator(ch)){
                resultArray[resultNum++]="(4,\""+result+"\")";
                result="";
                continue;
            }
            else{
                //将错误输入符一起读入,直到正确
                while(!isSeparator(ch) && !isalpha(ch) && !isdigit(ch)){
                    result.append(1,ch);
                    infile>>ch;
                }
                resultArray[resultNum++]="(Error,\""+result+"\")";
                result="";
                continue;
            }
        }
        //读取到分隔符
        else if(isSeparator(ch)){
            result.append(1,ch);
            resultArray[resultNum++]="(5,\""+result+"\")";
            result="";
            infile>>ch;
        }
        //读取到未定义输入
        else{
            //出错处理
            result.append(1,ch);
            resultArray[resultNum++]="(Error,\""+result+"\")";
            result="";
            infile>>ch;

        }
    }
    //关闭文件输入流
    infile.close();

    //以 (单词类编码,值) 输出结果
    for(int i=0;i<resultNum;i++){
        cout<<resultArray[i]<<endl;
    }

   return 0;
}
七、实验小结
  1. 本次实验最主要的是通过对源代码进行分析得到一个个的单词,通过编程实现后对词法分析这一过程更加的熟悉了。
  2. 实验过程中第一个问题是如何利用C++进行txt文档读取,并逐个获取到单个字符。
    解决方式:通过谷歌搜索,得知C++的读取txt文本的方式,同时通过设计可以只读取字符,直接忽略空格与换行符。
  3. 实验过程读取关键字后读取下划线开头的标识符与以数字开头的标识符不能正确区分。
    解决方式:在读取关键字后,加入continue重新进行字符读取,直接进入下一次循环即可正确识别。
  4. 对于实验部分控制输出的代码重复率比较高,可以进一步优化,但是如果写成函数则读取字符不方便操作,所以暂未改进。
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐