当前位置: 首页 > news >正文

编译原理第一次实验报告

  • 源代码及附件:编译原理实验一源程序及附件资源-CSDN文库
  • 实验题目

  • 实验要求

  • 实验设计

前两部分指出了实验的宏观把控,为了具体实施实验,我们需要预先为实验做出如下设计:

本次实验我选取了C语言的一个子集进行设计词法分析器,其中单词种类如下:(也可参考附件中的单词种类表)

根据题目要求,我们用正则文法来定义我们的子集:

  1. S→关键字|运算符|分界符|整型常量|浮点型常量|标识符
  2. 关键字

→ void|main|int|float|for|while|switch|case|if|else|return|break

(3)运算符 → +|-|*|/|=|==|<|<=|>|>=

(4)分界符 → (|)|[|]|{|}|,|;|:

(5)整型常量 → digit(digit)*

(6)浮点型常量 → digit(digit).digit(digit)

(7)digit→ 0|1|2|3|4|5|6|7|8|9

(8)letter→ a|b|…|z|A|B|…|Z

(9)标识符 → letter(letter|digit)*

  • 实验分析

基本算法思想是:读取文件,逐个字符分析,若是空白符则跳过,为字母时将连续的字母使用超前搜索组合成为变量或关键字;若是数字,则要判断是否为浮点数,即利用超前搜索,判断扫描到的字符是否为小数点;若是分隔符或者操作符,利用switch语句判断并输出,若是其他字符,输出为未定义的字符。

相关代码段:

void lexicalAnalysis(FILE* fp)

{

    char ch;        

    while ((ch = fgetc(fp)) != EOF)    /

    {

        token = ch;                                   

        if (ch == ' ' || ch == '\t' || ch == '\n'//忽略空格、Tab和回车

        {

             if (ch == '\n')                            //遇到换行符,记录行数的row加1

                 row++;

             continue;                                

        }

        else if (isLetter(ch))        //以字母开头,关键字或标识符

        {

             token = "";                   //token初始化

             while (isLetter(ch) || isDigit(ch)) //非字母或数字时退出,将单词存储在token中

             {

                 token.push_back(ch);  //将读取的字符ch存入token中

                 ch = fgetc(fp);           //获取下一个字符

             }

             //文件指针后退一个字节,即重新读取上述单词后的第一个字符

             fseek(fp, -1L, SEEK_CUR);

             if (isKey(token)) //关键字

                 code = TokenCode(getKeyID(token));

             else //标识符

                 code = TK_IDENT; //单词为标识符

        }

        else if (isDigit(ch)) //无符号常数以数字开头

        {

             int isdouble = 0; //标记是否为浮点数

             token = "";          //token初始化

             while (isDigit(ch))   //当前获取到的字符为数字

             {

                 token.push_back(ch);      //读取数字,将其存入token中

                 ch = fgetc(fp);               //从文件中获取下一个字符

                 //该单词中第一次出现小数点

                 if (ch == '.' && isdouble == 0)

                 {

                     //小数点下一位是数字

                     if (isDigit(fgetc(fp)))

                     {

                         isdouble = 1;    //标记该常数中已经出现过小数点

                         fseek(fp, -1L, SEEK_CUR);     //将超前读取的小数点后一位重新读取

                         token.push_back(ch);          //将小数点入token中

                         ch = fgetc(fp);               //读取小数点后的下一位数字

                     }

                 }

             }

             if (isdouble == 1)

                 code = TK_DOUBLE; //单词为浮点型

             else

                 code = TK_INT;                //单词为整型

             //文件指针后退一个字节,即重新读取常数后的第一个字符

             fseek(fp, -1L, SEEK_CUR);

        }

        else switch (ch)

        {

             /*运算符*/

        case '+': code = TK_PLUS;     //+加号         

             break;

        case '-': code = TK_MINUS;    //-减号

             break;

        case '*': code = TK_STAR;     //*乘号     

             break;

        case '/': code = TK_DIVIDE;        //除号

             break;

        case '=':

        {

             ch = fgetc(fp);               //超前读取'='后面的字符

             if (ch == '=')                //==等于号

             {

                 token.push_back(ch);  //将'='后面的'='存入token中

                 code = TK_EQ;        //单词为"=="

             }

             else {                        //=赋值运算符

                 code = TK_ASSIGN;     //单词为"="

                 fseek(fp, -1L, SEEK_CUR); //将超前读取的字符重新读取

             }

        }

        break;

        case '<':

        {

             ch = fgetc(fp);               //超前读取'<'后面的字符

             if (ch == '=')                //<=小于等于号

             {

                 token.push_back(ch);  //将'<'后面的'='存入token中

                 code = TK_LEQ;            //单词为"<="

             }

             else {                        //<小于号

                 code = TK_LT;        //单词为"<"

                 fseek(fp, -1L, SEEK_CUR); //将超前读取的字符重新读取

             }

        }

        break;

        case '>':

        {

             ch = fgetc(fp);               //超前读取'>'后面的字符

             if (ch == '=')                //>=大于等于号

             {

                 token.push_back(ch);  //将'>'后面的'='存入token中

                 code = TK_GEQ;            //单词为">="

             }

             else {                        //>大于号

                 code = TK_GT;        //单词为">"

                 fseek(fp, -1L, SEEK_CUR); //将超前读取的字符重新读取

             }

        }

        break;

        /*分界符*/

        case '(': code = TK_OPENPA;        //(左圆括号

             break;

        case ')': code = TK_CLOSEPA;   //)右圆括号

             break;

        case '[': code = TK_OPENBR;        //[左中括号

             break;

        case ']': code = TK_CLOSEBR;   //]右中括号

             break;

        case '{': code = TK_BEGIN;    //{左大括号

             break;

        case '}': code = TK_END;      //}右大括号

             break;

        case ',': code = TK_COMMA;    //,逗号

             break;

        case ';': code = TK_SEMOCOLOM; //;分号

             break;

        case':':code = TK_MAO;//:冒号

             break;

             //未识别符号

        default: code = TK_UNDEF;

        }

        print(code);              //打印词法分析结果

    }

}

定义了如下数据结构作为状态终态:

    /* 关键字 */

    KW_VOID, //void关键字

    KW_MAIN, //main关键字

    KW_INT,      //int关键字

    KW_DOUBLE,   //double关键字

    KW_FOR,      //for关键字

    KW_WHILE,    //while关键字

    KW_SWITCH,   //switch关键字

    KW_CASE, //case关键字

    KW_IF,       //if关键字

    KW_ELSE, //else关键字

    KW_RETURN,   //return关键字

    KW_BREAK,//break关键字

    /* 运算符 */

    TK_PLUS, //+加号

    TK_MINUS,    //-减号

    TK_STAR, //*乘号

    TK_DIVIDE,   ///除号

    TK_ASSIGN,   //=赋值运算符

    TK_EQ,       //==等于号

    TK_LT,       //<小于号

    TK_LEQ,      //<=小于等于号

    TK_GT,       //>大于号

    TK_GEQ,      //>=大于等于号

    TK_MAO,    //:冒号

    /* 分隔符 */

    TK_OPENPA,   //(左圆括号

    TK_CLOSEPA//)右圆括号

    TK_OPENBR,   //[左中括号

    TK_CLOSEBR//]右中括号

    TK_BEGIN,    //{左大括号

    TK_END,      //}右大括号

    TK_COMMA,    //,逗号

    TK_SEMOCOLOM, //;分号

    /* 常量 */

    TK_INT,      //整型常量

    TK_DOUBLE,   //浮点型常量

    /* 标识符 */

TK_IDENT

  为了使得输出有所取分,使用不同颜色输出:

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); //未识别的符号为红色

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);    //关键字为蓝色

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);   //运算符和分隔符为绿色

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);   //常量为黄色

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY); //关键字为灰色

利用文件作为输入。

输入文档1.txt:

double add(double x, double y)

{

    double a = 3.456;

    return x + y;

}

$

int main()

{

            int i;

            switch(i)

           {

               case 1:   break;

            }

            if(double(i,i)>i)

                  break;

            int a[10];

}         

输出结果为:

其中注意红色标记:

说明我们实现了错误识别的功能。

  • 源代码

#include <iostream>

#include <string>

#include <Windows.h>

using namespace std;

/* 单词编码 */

enum TokenCode

{

    /*未定义*/

    TK_UNDEF = 0,

    /* 关键字 */

    KW_VOID, //void关键字

    KW_MAIN, //main关键字

    KW_INT,      //int关键字

    KW_DOUBLE,   //double关键字

    KW_FOR,      //for关键字

    KW_WHILE,    //while关键字

    KW_SWITCH,   //switch关键字

    KW_CASE, //case关键字

    KW_IF,       //if关键字

    KW_ELSE, //else关键字

    KW_RETURN,   //return关键字

    KW_BREAK,//break关键字

    /* 运算符 */

    TK_PLUS, //+加号

    TK_MINUS,    //-减号

    TK_STAR, //*乘号

    TK_DIVIDE,   ///除号

    TK_ASSIGN,   //=赋值运算符

    TK_EQ,       //==等于号

    TK_LT,       //<小于号

    TK_LEQ,      //<=小于等于号

    TK_GT,       //>大于号

    TK_GEQ,      //>=大于等于号

    TK_MAO,    //:冒号

    /* 分隔符 */

    TK_OPENPA,   //(左圆括号

    TK_CLOSEPA//)右圆括号

    TK_OPENBR,   //[左中括号

    TK_CLOSEBR//]右中括号

    TK_BEGIN,    //{左大括号

    TK_END,      //}右大括号

    TK_COMMA,    //,逗号

    TK_SEMOCOLOM, //;分号

    /* 常量 */

    TK_INT,      //整型常量

    TK_DOUBLE,   //浮点型常量

    /* 标识符 */

    TK_IDENT

};

TokenCode code = TK_UNDEF;    //记录单词的种别码

const int MAX = 12;               //关键字数量

int row = 1;                  //记录字符所在的行数

string token = "";                //用于存储单词

char  keyWord[][10] = { "void","main","int","double","for","while","switch","case","if","else","return","break"};    //存储关键词

void print(TokenCode code)

{

    switch (code)

    {

        /*未识别的符号*/

    case TK_UNDEF:

        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); //未识别的符号为红色

        cout << '(' << code << ',' << token << ")" << "未识别的符号在第" << row << "行。" << endl;

        return;

        break;

        /*关键字*/

    case KW_VOID:

    case KW_MAIN:

    case KW_INT:    

    case KW_DOUBLE

    case KW_FOR:    

    case KW_WHILE:  

    case KW_SWITCH

    case KW_CASE:

    case KW_IF:     

    case KW_ELSE:

    case KW_RETURN

    case KW_BREAK:

        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_BLUE);    //关键字为蓝色

        break;

        /* 运算符 */

    case TK_PLUS:

    case TK_MINUS:  

    case TK_STAR:

    case TK_DIVIDE

    case TK_ASSIGN

    case TK_EQ:     

    case TK_LT:     

    case TK_LEQ:

    case TK_GT:     

    case TK_GEQ:        

    /* 分隔符 */

    case TK_OPENPA

    case TK_CLOSEPA:

    case TK_OPENBR

    case TK_CLOSEBR:

    case TK_BEGIN:  

    case TK_END:

    case TK_COMMA:  

    case TK_SEMOCOLOM:   

    case TK_MAO:   

        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);   //运算符和分隔符为绿色

        break;

        /* 常量 */

    case TK_INT:

    case TK_DOUBLE

        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);   //常量为黄色

        if (token.find('.') == token.npos)

             cout << '(' << code << ',' << atoi(token.c_str()) << ")" << endl;                     

        else

             cout << '(' << code << ',' << atof(token.c_str()) << ")" << endl;                         

        return;

        break;

        /* 标识符 */

    case TK_IDENT:

        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY); //关键字为灰色

        break;

    default:

        break;

    }

    cout << '(' << code << ',' << token << ")" << endl;

}

bool isKey(string token)

{

    for (int i = 0; i < MAX; i++)

    {

        if (token.compare(keyWord[i]) == 0)

             return true;

    }

    return false;

}

int  getKeyID(string token)

{

    for (int i = 0; i < MAX; i++)

    {   //关键字的内码值为keyWord数组中对应的下标加1

        if (token.compare(keyWord[i]) == 0)

             return i + 1;

    }

    return -1;

}

bool isLetter(char letter)

{

    if ((letter >= 'a' && letter <= 'z') || (letter >= 'A' && letter <= 'Z'))

        return true;

    return false;

}

bool isDigit(char digit)

{

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

        return true;

    return false;

}

void lexicalAnalysis(FILE* fp1)

{

    char ch;        

    while ((ch = fgetc(fp1)) != EOF)  

    {

        token = ch;                                   

        if (ch == ' ' || ch == '\t' || ch == '\n'

        {

             if (ch == '\n')                           

                 row++;

             continue;                                

        }

        else if (isLetter(ch))       

        {

             token = "";                  

             while (isLetter(ch) || isDigit(ch))

             {

                 token.push_back(ch); 

                 ch = fgetc(fp1);     

             }

             fseek(fp1, -1L, SEEK_CUR);

             if (isKey(token))

                 code = TokenCode(getKeyID(token));

             else

                 code = TK_IDENT;

        }

        else if (isDigit(ch))

        {

             int isdouble = 0;

             token = "";         

             while (isDigit(ch))  

             {

                 token.push_back(ch);     

                 ch = fgetc(fp1);             

                

                 if (ch == '.' && isdouble == 0)

                 {

                     if (isDigit(fgetc(fp1)))

                     {

                         isdouble = 1;   

                         fseek(fp1, -1L, SEEK_CUR);   

                         token.push_back(ch);         

                         ch = fgetc(fp1);             

                     }

                 }

             }

             if (isdouble == 1)

                 code = TK_DOUBLE;

             else

                 code = TK_INT;               

             fseek(fp1, -1L, SEEK_CUR);

        }

        else switch (ch)

        {

        case '+': code = TK_PLUS;                 

             break;

        case '-': code = TK_MINUS;   

             break;

        case '*': code = TK_STAR;             

             break;

        case '/': code = TK_DIVIDE;       

             break;

        case '=':

        {

            ch = fgetc(fp1);             

             if (ch == '=')               

             {

                 token.push_back(ch); 

                 code = TK_EQ;       

             }

             else {                       

                 code = TK_ASSIGN;    

                 fseek(fp1, -1L, SEEK_CUR);

             }

        }

        break;

        case '<':

        {

             ch = fgetc(fp1);             

             if (ch == '=')               

            {

                 token.push_back(ch); 

                 code = TK_LEQ;           

             }

             else {                       

                 code = TK_LT;       

                 fseek(fp1, -1L, SEEK_CUR);

             }

        }

        break;

        case '>':

        {

             ch = fgetc(fp1);             

             if (ch == '=')               

             {

                 token.push_back(ch); 

                 code = TK_GEQ;           

             }

             else {                       

                 code = TK_GT;       

                 fseek(fp1, -1L, SEEK_CUR);

             }

        }

        break;

       

        case '(': code = TK_OPENPA;       

             break;

        case ')': code = TK_CLOSEPA;  

             break;

        case '[': code = TK_OPENBR;       

             break;

        case ']': code = TK_CLOSEBR;  

             break;

        case '{': code = TK_BEGIN;   

             break;

        case '}': code = TK_END;     

             break;

        case ',': code = TK_COMMA;   

             break;

        case ';': code = TK_SEMOCOLOM;

             break;

        case':':code = TK_MAO;

             break;

            

        default: code = TK_UNDEF;

        }

        print(code);              //打印词法分析结果

    }

}

int main()

{

    string filename;     

    FILE* fp1;               

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

    while (true) {

        cin >> filename;     

        if ((fopen_s(&fp1, filename.c_str(), "r")) == 0)    

             break;

        else

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

    }

    cout << "/=***************************词法分析结果***************************=/" << endl;

    lexicalAnalysis(fp1);     //词法分析

    fclose(fp1);

    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); //字体恢复原来的颜色

    return 0;

}

  • 实验总结

这是编译原理的第一次实验,之前因为理解题意不明,也与老师探讨了多次,上网查阅各种资料等等,最终终于完成了实验。

在实验过程中发生过种种问题,一开始为图方便,想到过使用硬编码来实现实验,但最终还是使用了FA的方式。

通过本次实验,我对NFA、DFA的知识有了更深入的了解,编程能力也进一步增强,可以说这次实验是我对编译原理与实际应用的第一次初步实现,对我的影响深远!

http://www.lryc.cn/news/475338.html

相关文章:

  • uniapp的video视频属性打包app后层级过高
  • 问:Redis为什么这么快?
  • 环信鸿蒙IM SDK实现附件消息发送与下载
  • 探索NetCat:网络流量监测与数据传输的利器
  • 【运动的&足球】足球运动员球守门员裁判检测系统源码&数据集全套:改进yolo11-DBBNCSPELAN
  • 求最大公约数,最小公倍数
  • Android——横屏竖屏
  • scala---10.30
  • Pinctrl子需要中client端使用pinctrl过程的驱动分析
  • 【网络】传输层协议TCP
  • 00-开发环境 MPLAB IDE 配置
  • <meta property=“og:type“ content=“website“>
  • C++ 实现俄罗斯方块游戏
  • QT打包Macosx应用发布App Store简易流程
  • untiy mlagents 飞机大战 ai训练
  • 从0开始学统计-什么是中心极限定理
  • 工具方法 - 个人活动的分类
  • 11.1组会汇报-基于区块链的安全多方计算研究现状与展望
  • ubuntu【桌面】 配置NAT模式固定IP
  • 评估 机器学习 回归模型 的性能和准确度
  • 如何下载安装TestLink?
  • 基于SSM+微信小程序的订餐管理系统(点餐2)
  • 【C++排序 双指针】1996. 游戏中弱角色的数量|1996
  • GESP4级考试语法知识(捕捉异常)
  • HTML 基础标签——元数据标签 <meta>
  • 栈虚拟机和寄存器虚拟机,有什么不同?
  • Windows下基于fping进行批量IP测试
  • 一款实用的Word文档图片转换与水印保护工具
  • 优化用于传感应用的衬底集成波导技术
  • Java多态特性的向上转型