编译原理实验一:词法分析

2023-11-14

实验一:词法分析程序
一、实验目的 
    通过设计编制调试一个具体的词法分析程序,加深对词法分析原理的理解。并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法。
编制一个读单词过程,从输入的源程序中,识别出各个具有独立意义的单词,即基本保留字、标识符、常数、运算符、分隔符五大类。并依次输出各个单词的类型码及单词符号的自身值。(遇到错误时可显示“Error”,然后跳过错误部分继续显示) 
二、实验要求
用C或C++写一个简单的词法分析程序,程序可以满足下列要求:
1、能分析如下几种简单的语言词法
(1) 标识符: ID=letter(letter|digit)*
(2) 关键字(全部小写)
main int float double char if  then  else switch case break continue while do for
(3)整型常量:NUM=digit digit*
(4)运算符
   = + - * / < <= == != > >= ; ( )? : 
(5)空格由空白、制表符和换行符组成,用以分隔ID、NUM、运算符等,字符分析时被忽略。
2、单词符号和相应的类别码
假定单词符号和相应的类别码如下:
单词符号 种别码 
int  1
=  17
float  2 
<  20
if  3 
<=  21
switch 4 
==  22
while  5 
!=  23
Do  6 
>  24
标识符 10 
>=  25
整型常量 11 
;  26
+  13 
(  27
-  14 
)  28
*  15 
?  29
/  16 
:  30
3、词法分析程序实现的功能
输入:单词序列(以文件形式提供),输出识别的单词的二元组序列到文件和屏幕
输出:二元组构成: (syn,token或sum)
其中: syn 为单词的种别码
token 为存放的单词自身符号串
sum 为整型常数
例:
源程序: int ab; float ef=20;
ab=10+ef;
输出:
(保留字--1,int)
(标识符--10,ab)
(分号--26,;)
(保留字--2,float)
(标识符--10,ef)
(等号--17,=)
(整数--11,20)
(分号--26,;)
(标识符--10,ab) 
(等号--17,=) 
(整数--11,10)
(加号--13,+)
(标识符--10,ef) 
(分号--26,;)
4、自己准备测试数据存放于TestData.txt文件中,测试数据中应覆盖有以上5种数据,测试结果要求以原数据与结果对照的形式输出并保存在Result.txt中,同时要把结果输出到屏幕。
5、提前准备 
① 实验前,先编制好程序,上机时输入并调试程序。
②   准备好多组测试数据(存放于文件TestData.txt中)。

本以为很快就能做出来的,没想到前前后后共总共大概花了3个小时,惭愧,大体流程就是读取文件中的内容,先一行一行的读,读取一行后然后利用istringstream流读取单词,需要注意,比如一行输入是”int a=1,bcd=4;”,这里单词就是”int”和”a=1,bcd=4”,因为读取时遇到空格,制表符等就读入一个单词,所以还要对单词进行分割,将标识符,运算符(+,-,<=,>=,!=,==,逗号等等),整型数字(为了简单起见,只用整形数字)分开,难点就在于怎么分,分开后对每个分开的单词然后再根据是标识符还是运算符等等,对应输出就行了
代码:

# include<iostream>
# include<string>
# include<fstream>
# include<sstream>//流对象
# include<vector>
# include<map>
using namespace std;

bool isIdentifier(string s);//标识符
bool isKeywords(string s);//关键字
bool isDigit(string s);//整型数字
bool isOperator(string s);//运算符
bool isOperator(char c);//运算符
string result(string s);//根据传入的参数s产生对应的输出
int main()
{
    //=====================测试函数是否正确=============================
    /*if(isIdentifier("_s2ias"))
        cout << "是标识符" << endl;
    if (isKeywords("for"))
        cout << "是关键字" << endl;
    if (isDigit("12a42"))
        cout << "是数字" << endl;
    if (isOperator(">"))
        cout << "是运算符" << endl;
    result("?");*/
    //===================================================================

    string file = ("TestData1.txt");
    ifstream input(file);
    //输入文件流,注意编码,文本文件编码格式需和项目一直,否则乱码

    ofstream output("Result.txt",ofstream::app);
    //先将TtestData.txt内容拷贝到Result.txt中
    string copy;

    getline(input, copy, '\0');
    cout<< copy << endl;//测试是否正确

    //此时input已经指到了文件尾,为了后面的读取,需要关闭再打开
    input.close();
    input.open(file);

    /*测试结果要求以原数据与结果对照的形式输出并保存在Result.txt中,
    同时要把结果输出到屏幕。
    */
    output << "原数据:\n";
    output << copy;
    output << "处理后结果:\n";

    string str;
    string words;

    cout << "处理后结果:\n";
    while (getline(input, str)) //读取文件每一次读取一行,遇到EOF结束
    {
        //从输入流中获取单词,需要用到输入流对象,即istringstream
        istringstream strCin(str);
        string s;
        while (strCin >> words)
        {
            //注意处理逗号,比如int a,b;这里有一个单词"a,b;”,所以要处理一个字符串里面
            //的各种运算符,但是这样会很麻烦,发现没有,用ide写代码写完一句输入分号时,ide
            //会自动加入空格,这样就方便处理多了


            //1.首先可以确定的是关键字肯定是单独作为一个单词的
            if (isKeywords(words))
            {
                s = result(words);
                cout << s << endl;
                output << s << endl;
                continue;
            }

            //2,对单词进行扫描,肯定是标识符,运算符,逗号分号,数字等等混合在一起的单词
            vector<int> index = {0};
            vector<string> mulWords;//将words分解为多个单词
            for (int i = 0; i < words.length(); i++)
            {

                //运算符有两位的,比如"<=",">=","==","!="
                if ((i < words.length() - 1) && isOperator(words[i]) && isOperator(words[i + 1]))
                {
                    //但是要注意只有以上四种两位运算符,比如+-,))就不是,但是))还是要输出),)
                    if (string(words.begin() + i, words.begin() + i + 2) == "<=" ||
                        string(words.begin() + i, words.begin() + i + 2) == ">=" ||
                        string(words.begin() + i, words.begin() + i + 2) == "==" ||
                        string(words.begin() + i, words.begin() + i + 2) == "!=")
                    {
                        index.push_back(i);
                        index.push_back(i + 2);
                        ++i;
                    }
                    else if (isOperator(words[i]))
                    {
                        if (find(index.begin(), index.end(), i) == index.end())
                            index.push_back(i);
                        if (find(index.begin(), index.end(), i + 1) == index.end())
                            index.push_back(i + 1);

                    }
                }
                //逗号,运算符作为分隔
                 else if (isOperator(words[i]))
                {
                    if (find(index.begin(), index.end(), i) == index.end())
                    //比如遇到"a,b"这里下标0和1将a分开,1到2将逗号分开,2到3将b分开
                        index.push_back(i);
                    if (find(index.begin(), index.end(), i+1) == index.end())
                        index.push_back(i + 1);

                    //如果是a<=b这样的呢?一样,先0和1将a分开,1和2将<分开,2和3将=分开
                    //3和4将b分开,然后后面分隔单词时,注意如果相邻都是运算符,则忽略,比如
                    //后面判断到1和2,2和3都是运算符,则忽略2

                }

            }
            for (int i = 0; i < index.size()-1; i++)
            {
                string rel;
                //比如遇到"<=",需要提取”<=“
                /*if (isOperator(words[index[i]]) && isOperator(words[index[i + 1]]))
                {
                    rel = result(string(words.begin() + index[i], words.begin() + index[i + 2]));
                    ++i;
                }
                else*/
                rel=result(string(words.begin() + index[i], words.begin() + index[i + 1]));

                output << rel << endl;
                cout << rel<<endl;
            }

        }
    }
    output << endl;
    output.close();
    input.close();
    system("pause");
    return 0;
}

bool isIdentifier(string s)//标识符,试验要求:ID=letter(letter|digit)*
{
    if (!isKeywords(s))//标识符不能是关键字
    {
        if ((s[0] >= 'a'&&s[0] <= 'z') || (s[0] >= 'A'&&s[0] <= 'Z'))//是字母
        {
            for (int i = 1; i < s.length(); i++)
            {
                if ((s[i] >= 'a'&&s[i] <= 'z') || (s[i] >= 'A'&&s[i] <= 'Z')
                    || (s[i] >= '0'&&s[i] <= '9'))
                    continue;
                else return false;
            }
            return true;
        }
        return false;
    }
    return false;
}
bool isKeywords(string s)//关键字
{
    static vector<string> keyVec = { "main", "int", "float", "double", "char",
        "if", "then","else", "switch", "case", "break", "continue", "while",
        "do", "for" };
    vector<string>::iterator result = find(keyVec.begin(), keyVec.end(),s);
    if (result != keyVec.end())
        return true;
    else return false;
}
bool isDigit(string s)//整型数字,NUM=digit digit*
{
    if (s[0] >= '0'&&s[0] <= '9')
    {
        for (int i = 1; i < s.length(); ++i)
            if (s[i] >= '0'&&s[i] <= '9')
                continue;
            else return false;
        return true;
    }
    return false;
}

bool isOperator(string s)//运算符
{
    static vector<string> opeVec = { "=","+","-","*","/","<","<=","==","!=",
        ">",">=",";","(",")","?",":","," };
    vector<string>::iterator result = find(opeVec.begin(), opeVec.end(), s);
    if (result != opeVec.end())
        return true;
    else return false;
}

bool isOperator(char c)//运算符
{
    static vector<char> opeVec = { '=','+','-','*','/','<',
        //"<=","==","!=",
        '>',
        //">=",
        ';','(',')','?',':',',' };
    vector<char>::iterator result = find(opeVec.begin(), opeVec.end(), c);
    if (result != opeVec.end())
        return true;
    else return false;
}

string result(string s)//根据传入的参数s产生对应的输出
{
    //种别码
    //1.标识符
    if (isIdentifier(s))
        return "(标识符--10,"+s+")";

    //2.关键字
    static map<string, string> keyMap;
    keyMap["int"] = "1";
    keyMap["float"] = "2";
    keyMap["if"] = "3";
    keyMap["switch"] = "4";
    keyMap["while"] = "5";//只写5个吧,没必要全写
    if (isKeywords(s))
        return "(关键字--"+keyMap[s]+"," +s+")";

    //3.整型常量
    if (isDigit(s))
        return "(整型常量--11,"+s+")";

    //4.运算符
    static map<string, string> opeMap;
    opeMap["="] = "(等号--17,=)";
    opeMap["<"] = "(小于号--20,<)";
    opeMap["<="] = "(小于等于号--21,<=)";
    opeMap["=="] = "(赋值运算符--22,==)";
    opeMap["!="] = "(不等于号--23,!=)";
    opeMap[">"] = "(大于号--24,>)";
    opeMap[">="] = "(大于等于号--25,>=)";
    opeMap[";"] = "(分号--26,;)";
    opeMap["+"] = "(加号--13,+)";
    opeMap["("] = "( 左括号--27,( )";
    opeMap["-"] = "(减号--14,-)";
    opeMap[")"] = "(右括号--28,) )";
    opeMap[">"] = "(大于号--24,>)";
    opeMap["*"] = "(星号--15,*)";
    opeMap["?"] = "(问号--29,?)";
    opeMap["/"] = "(除号--16,/)";
    opeMap[":"] = "(冒号--30,:)";
    opeMap[","] = "(逗号--31,,)";
    if (isOperator(s))
        return opeMap[s];
    return "Error";
}

测试结果
先来简单点的
这里写图片描述

然后复杂一点的
这里写图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

编译原理实验一:词法分析 的相关文章

  • 编译原理三大经典书籍(龙书 虎书 鲸书)

    1 龙书 Dragon book 英文名 Compilers Principles Techniques and Tools 作者 Alfred V Aho Ravi Sethi Jeffrey D Ullman 中文名 编译原理技术和工具
  • flex&bison编写语法分析器

    使用flex和bison 对c语言代码块进行词法分析 识别词法错误 按照c 语法规则进行文法分析 并形成c语言代码块的语法树 syntax tree 并将语法树按照特定的格式打印出来 如何编译 两种方法 1 使用make命令 先将要执行的所
  • 【编译原理】【C语言】实验三:递归下降分析法

    C语言 实验环境 Visual Studio 2019 author zoxiii 递归下降分析法 1 实验内容 2 前期准备 2 1 递归下降分析法原理 2 2 要实现的文法 2 3 需要的函数 3 分析过程 3 1 递归下降分析法设计思
  • FLEX & BISON 联合使用

    flex是词法分析器 bison是语法分析器 基本原理就是flex解析出token后 让bison来使用 实际上 一般是先编写bison脚本 里面的token就是一个定义 没有实现 里面的yylex也是没有实现 只有定义 为什么先做biso
  • 编译原理------词法分析器C/C++代码实现

    一 实验目的 设计 编制并调试一个词法分析程序 加深对词法分析原理的理解 二 实验内容 2 1 待分析的简单的词法 1 关键字 begin if then while do end 所有的关键字都是小写 2 运算符和界符 lt lt lt
  • gcc常用选项及常见的文件格式,扩展名

    gcc常用选项 编译过程 预处理 编译 汇编 链接 gcc的选项 必须分开给出 x 语言名 指出后面文件的语言 c 编译 汇编源文件 生成目标文件 S 编译不汇编 生成汇编文件 E 预处理 输出送到标准输出 o 指定输出的文件名 pipe
  • 移入——归约技术

    归约 定义 我们可以将自底向上语法分析过程看成是建一个串w 归约 慰问发开始符号的过程 在归约中 一个与某产生式体相匹配的特定子串被替换为该产生式的头部的非终结符号 定义理解起来比较晦涩 我们来看个例子就知道了 已知有文法 E gt E T
  • LLVM IR / LLVM指令集入门

    本文基于LLVM 12官方文档的LLVM Language Reference Manual 以学习笔记为主 所以本文会摘录一些常见 常用的指令 对于一些更加深层次的指令属性 特性 待我对LLVM有更深的理解再单独写文章记录 1 LLVM
  • 简单的递归下降语法分析程序

    简单递归分析程序 其代码如下 include
  • 编译原理基础知识+笔记(1)

    一 编译原理概述 1 翻译程序 把某一种语言程序 称为源语言程序 等价地转换成另一种语言程序 称为目标语言程序 的程序 2 编译程序 把某一种高级语言程序等价地转换成另一种低级语言程序 如汇编语言或机器语言程序 的程序 又分为 诊断编译程序
  • 程序语言翻译器的设计与实现----算术表达式转换四元式(编译原理)

    此篇博客是将前面的内容进行整合并进一步提升 真正实现一个简单表达式语法的编译器 如果有不了解的地方请查看下面链接 词法分析 LR 1 分析 一 LR 1 分析 二 这里说的程序语言编译器是指将算术表达式部分进行翻译 暂时不包括优化以及目标语
  • 【编译原理】LR(0)分析方法(c++实现)

    基本流程 Created with Rapha l 2 2 0 输入文法 拓广文法 求项目集规范族 GO I a 转移函数 构造DF
  • arm-linux-gcc char与signed char和unsigned char

    1 三者关系 1 ANSI C 提供了3种字符类型 分别是char signed char unsigned char 而不是像short int一样只有两种 int默认就是unsigned int 2 三者都占1个字节 3 signed
  • 编译原理-总概

    语言执行过程 代码 解释器编译器 机器代码 cpu执行 编译型语言 在程序在执行之前需要一个专门的编译过程 通过编译器把程序编译成为可执行文件 再由机器运行这个文件 运行时不需要重新翻译 直接使用编译的结果就行了 解释型语言 是一边执行一边
  • 递归下降算法语法分析c语言

    递归下降算法 自上而下文法的递归下降的预测分析 从根节点出发逐步构造分析树 所以被称作自上而下文法 在文法中有递归的函数 例如 E gt TE 所以叫做递归下降算法 使用的文法如下 E gt TE E gt TE e T gt FT T g
  • 语义分析- 符号表

    符号表 1 用来存储程序中的变量相关信息 类型 作用域 访问控制信息 2 必须非常高效 程序中的变量规模会很大 符号表的接口 ifndef TABLE H define TABLE H typedef Table t 数据结构 新建一个符号
  • 编译原理(4)LR(0)语法分析程序设计(Python实现)

    1 实验要求 1 已知文法G S 0 S E 1 E aA 2 E bB 3 A cA 4 A d 5 B cB 6 B d 手工建立文法G S 的LR 0 的项目集规范族DFA和LR 0 分析表 2 根据清华大学版 编译原理 第3版 教材
  • LL(1)文法的预测分析表以及对某输入串的分析过程

    举例说明LL 1 文法的预测分析 以及对 a a 的分析过程 文法G S S gt a S gt S gt T T gt SN N gt SN N gt 是否 gt First集 Follow集 S 否 a T 否 a N 是 Select
  • Compiler- volatile关键字

    为了直观的感受编译器为程序所做的编译优化 我们通过以下的C 程序来进行演示 只能体现编译优化的一小部分hh 请大家预测一下下面代码的输出结果 include
  • Go 程序编译过程(基于 Go1.21)

    版本说明 Go 1 21 官方文档 Go 语言官方文档详细阐述了 Go 语言编译器的具体执行过程 Go1 21 版本可以看这个 https github com golang go tree release branch go1 21 sr

随机推荐

  • 两个二维数组合并

  • 重磅发布

    导语 后疫情时代 随着各行业线下业务与线上业务的深度结合转型 流量思维的增量导向逐渐转向降本增效 虚假流量已经成为互联网时代信息化数字资产最大的威胁之一 据极验最新行业数据统计 各个行业都有较高比例的虚假流量存在 机器流量最为泛滥的区块链行
  • Flutter实现类似Android中的PopupWindow控件

    最近在网上看到一段话 产品有三宝 弹窗 浮层加引导 设计有三宝 透明 阴影加圆角 运营有三宝 短信 push加红包 在日常开发中经常会遇到弹窗 浮层之类的效果 这些在Android中实现很简单 可以用PopupWindow完成 但是在flu
  • 静态映射和动态映射

    1 为什么需要映射 在内核启动过程中会开启MMU 建立虚拟映射表 以后内核使用的都是虚拟地址 但是我们查询数据手册得到I O寄存器地址都是物理地址 于是需要将物理地址转换到虚拟地址 这样才能在内核空间去访问I O寄存器 物理地址转换到虚拟地
  • Linux下配置pptp协议之拨号上网

    首先安装pptp sodo apt get install pptp linux y 创建连接 sudo pptpsetup create nodeName server yourServerAddr username xxx passwo
  • zookeeper的安装部署

    1安装zookeeper集群 上传安装包 移动到指定文件夹 mv zookeeper 3 4 6 tar gz opt apps 3 解压 tar zxvf zookeeper 3 4 6 tar gz 4 修改配置文件 1 进入到conf
  • Git介绍及常用命令

    Git介绍及常用命令 在软件开发过程中 团队协作基本上都会使用到git git可以使得团队开发效率变高 因此 我们接下来介绍git的使用方法 国内一般使用gitee 当然 也可以使用github github是国外的 所以加载慢 甚至加载不
  • SQL知识整理三:变量、全局变量、视图、事务、异常

    SQL知识整理三 变量 全局变量 视图 事务 异常 参考文章 1 SQL知识整理三 变量 全局变量 视图 事务 异常 2 https www cnblogs com chengxingliang p 3333277 html 备忘一下
  • 【马普所2008】机器学习中的核方法(上)

    Hofmann T Sch Lkopf B Smola A J Kernel methods in machine learning J Annals of Stats 2008 36 3 1 Integrating structured
  • Spring的事务隔离级别

    Spring的事务隔离级别是用于控制事务并发访问数据库时的行为 Spring框架提供了五个事务隔离级别 分别是 1 DEFAULT 默认 使用数据库默认的事务隔离级别 在大多数情况下 这等同于使用READ COMMITTED级别 2 REA
  • 使用纯java ssh方式连接linux服务器,并用此方式部署war到linux的tomcat下

    b 纯java代码使用ssh方式登录linux服务 实际应用中 可以使用这种方式上传部署web工程war包 并且部署启动tomcat 一个自动化完成所有工作 起到节省时间作用 1 去 url http www jcraft com jsch
  • QIIME2-DADA2&Deblur

    Deblur使用序列错误配置文件将错误的序列与从其来源的真实生物序列相关联 从而得到高质量的序列变异数据 主要为两个步骤 DADA2 质控 汇总版 qiime dada2 denoise single i demultiplexed seq
  • notepad 自动换行 分屏 快捷键

    一 自动换行 视图 gt 自动换行 二 分屏 Tab标签 上方文件名 右键 gt 移动到另一视图 三 快捷键 快速复制 Ctrl D 区块注释 Ctrl Shift Q 保存所有打开文件 Ctrl Shift S 行注释 Ctrl K 取消
  • 内卷化时代,一名普通测试员的铁饭碗究竟是什么?

    内卷 是现在热度非常高的一个词汇 随着热度不断攀升 隐隐有了 万物皆可卷 的程度 究其来源 内卷这个词的出现 是伴随着996开始讨论的 很不幸 996 福报等等这些词的重灾区和源头就是计算机 互联网行业 那么作为行业中一个非常重要的分支 测
  • web前端模块化框架,一句代码让html可直接引入别的html文件

    web前端模块化框架 介绍 一个web前端模块化框架 可以引入模板html文件 利于前后端分离的网站重复代码以及模块的复用 软件架构 本框架是利用mloader js文件加载带有mloader template的类的标签从而进行的文档的动态
  • 基于Keil创建汇编语言的STM32工程

    本文是在Keil嵌入式开发环境下完成一个基于STM32汇编程序的编写 学习在没有硬件条件下进行仿真调试 观察ARM寄存器的变化状况 记录过程生成的 hex文件各段的大小 了解Hex文件格式及其前8个字节内容含义 文章目录 一 新建工程 二
  • 建立双机调试

    1 首先你得有一个VMware 我这里是VM10 主机是64windows操作系统 2 我在虚拟机中装了32 位win7 3 下载 VirtualKD 我预先放在百度云盘的资源 https pan baidu com s 1eRD4AR4
  • C# 系统应用之无标题窗体移动的两种方法

    在做项目界面设计中 常常为了美观需要设置窗体属性 FormBorderStyle 窗体边框和标题栏外观 为None无标题窗口 此时隐藏标题的窗口怎样实现移动呢 我根据自己的项目从自己完成的两种方法进行讲解 一 MouseDown Mouse
  • TypeScript的类型推导

    TypeScript 简称ts 是一种静态类型的编程语言 在类型检查和类型推导方面具有一定的优势 类型推导是TypeScript在代码编写的过程中自动识别并设置变量类型 从而提高代码的可读性和健壮性 减少了代码中潜在的错误 在 TypeSc
  • 编译原理实验一:词法分析

    实验一 词法分析程序 一 实验目的 通过设计编制调试一个具体的词法分析程序 加深对词法分析原理的理解 并掌握在对程序设计语言源程序进行扫描过程中将其分解为各类单词的词法分析方法 编制一个读单词过程 从输入的源程序中 识别出各个具有独立意义的