编译原理之词法分析器随笔和简单实现

news2025/7/8 6:16:54

借鉴:   编译原理之美. 极客时间上

什么是词法分析

编译原理:词法分析简单的来说就是在字符串中提取一系列的word单词. 编译器的眼里, 我们的一切输入都是什么? 都是一个一个的字符串. 所以编译器干的就是字符串解析工作. 

词法分析:自然就是解析出来一个一个的单词的. word单词

如何做词法分析

首先:明确的第一点, 单词(词法记号)是存在类型的. 解析出来的单词是不同的类型, 我们后序将其标记为Token结构体:{text + type(代码中用的status)}两个字段.   一个是单词内容, 另外一个就是单词类型. 类型赋予不同的分类. 不同的状态。

思考一下: Parse("ab2  345  34.56  87.  3ab 45.0 098 4  6."); 这样一个字符串解析操作

 先看词法分析规矩:

 ab2  345  34.56  87.  3ab 45.0 098 4  6.

ab2 合法, 是标识符类型. identifier.

345  合法,  是整数类型.

34.56 合法, 是实数类型

3ab 这种, 不合法,  是错误类型 (错误状态, 标记)

 针对上述的这些不同的类型, 状态,一般都是用枚举类型来枚举一下不同的状态的

然后接下来:就是把握状态迁移的时机了. 状态是如何迁移的, 以及什么时候应该进行状态迁移, 这些都是我们需要关心的问题.

思考状态迁移的时机:当一个单词分析完成的时候。需要解析下一个单词了, 状态可能需要迁移.  相同状态就一直循环提取,直到这个单词提取完成,再从初始状态继续提取下一个单词.

INIT初始状态,就是为了做一个缓冲状态, 中间状态. 标记的新的单词提取的开始. 新的词法记号提取的开始. 

画出来一个状态迁移的图:

其实上述这个规则,那是相当的简单, 随便一说,大家都能懂是啥意思,哎呀,就是说,按照一定的不同类型的数据的限制规则来提取出合法不合法的单词, 提取完全之后,单词只是一个字符串,不具备一个特殊的含义, 所以我们需要为其添加类型,让其区别含义. 于是我们就加上一个status状态字段, 或者说是Type类型字段也OK

接下来看代码

前置数据声明:  tokens用来保存我们解析字符串过程中提取出来的诸多的token词法标记. STATUS标记的是当前单词的提取状态, 也可以说是类型。

#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <string>

using namespace std;

//枚举状态
enum STATUS {
    INIT = 1,   //初始状态
    IDENTIFIER, //标识符状态, 小写字母开头 + 后序小写字母||下划线
    INTEGER,    //整数状态
    FLOAT,      //实数状态
    ERROR       //错误状态, 词法错误
};

//Token:表示的是词法记号
struct Token {
    string text;
    STATUS status;
};

vector<std::shared_ptr<Token>> tokens;//保存所有的词法记号

InitToken: 创建Token, 按照单词的首字母,确定单词的Type类型(STATUS状态)

//根据词法记号首个字符来初始化Token
Token* InitToken(char ch) {
    Token* token = new Token;
    assert(token != NULL);
    if ((isalpha(ch) && islower(ch)) || ch == '_') {//是小写字母开头||下划线
        token->status = IDENTIFIER;
    } else if (isdigit(ch) && ch != '0') {
        token->status = INTEGER;
    } else if (ch == '.') {
        token->status = FLOAT;
    } else {
        token->status = ERROR;
    }
    token->text = "";//初始化为空字符串
    token->text += ch;//加上初始字符串
}

然后就是Parse函数了, 用于解析字符串,初始化以及提取Token操作,时间紧张,没有做内存管理, 有心之人可以对内存资源加上智能指针, 培养习惯

首先: 提取每一个单词过程中,遇到空格, 表示这个单词提取完成,应该接着提取下一个单词, 所以修改一下当前状态status为INIT状态. 让其自动转换为INIT来完成新的Token的创建. 进入下一个新的单词的解析. 自动进入,核心关键在于自动进入。我们只是需要修改当前的status状态就能自动进入新的下一个单词状态的处理,所以这个玩也也叫做自动机。

在计算机网络的协议封装中,也存在这样类似的状态机。自动的进行状态转移. 连接建立状态, 数据交互状态,连接断开状态. 所以对于这个自动机(状态机的深入理解)是有一定必要的,所以我才写了这篇博客

void Parse(const string& s) {
    int i = 0;
    char ch = s[i];
    STATUS status = INIT;//枚举当前状态
    Token* token = nullptr;//记录token
    while (ch) {
        switch(status) {
            case INIT: {//初始化状态, 创建ch即可
                while (isspace(ch)) {
                    ch = s[++i];//跳过多余的空格
                }
                token = InitToken(ch);
                status = token->status;//更新status
                ch = s[++i];//更新ch
            } break;
            case IDENTIFIER: {
                if ((isalpha(ch)&&islower(ch)) || (ch == '_') || isdigit(ch)) {
                    token->text += ch;
                    ch = s[++i];
                } else {
                    if (ch == ' ') {
                        status = INIT;//是新的单词
                        tokens.push_back(shared_ptr<Token>(token));    
                    } else {//词法错误
                        token->status = ERROR;
                        token->text += ch;
                        ch = s[++i];
                    }
                }
            } break;
            case INTEGER: {
                 if (isdigit(ch)) {
                    token->text += ch;
                    ch = s[++i];//更新ch
                } else {//结束或者跳跃到实数
                    if (ch == '.') {
                        token->status = FLOAT;
                        token->text += ch;
                        ch = s[++i];//更新ch
                    } else if (ch == ' ') {//是新的单词
                        status = INIT;
                        tokens.push_back(shared_ptr<Token>(token));    
                    } else {//词法错误
                        token->status = ERROR;
                        token->text += ch;
                        ch = s[++i];
                    }
                }
            } break;
            case FLOAT: {
                if (isdigit(ch) && ch != '0') {
                    token->text += ch;
                    ch = s[++i];
                } else if (ch == ' ') {//是新的单词
                    status = INIT;
                    tokens.push_back(shared_ptr<Token>(token));    
                } else {//词法错误
                    token->status = ERROR;
                    token->text += ch;
                    ch = s[++i];
                }
            } break;
            case ERROR: {
                if (ch == ' ') {//是新的单词
                    status = INIT;
                    tokens.push_back(shared_ptr<Token>(token));
                } else {
                    token->text += ch;
                    ch = s[++i];
                }
            } break;
        }
    }
    if (token != nullptr)
    tokens.push_back(shared_ptr<Token>(token));
}
  • 感谢兄弟们的阅读,要是对某些开设这门课的兄弟有所帮助。  希望来电收藏点赞,以资鼓励小杰创作,蟹蟹

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/37079.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue中el和data的写法与 MVVM模型

目录 el和data的两种写法 el的两种写法 data的两种写法 data与el的写法小结 MVVM模型 MVVM模型小结 el和data的两种写法 el的两种写法 <body> <div id"root"> hello,{{name}}</div><script type"text/javascript">const vm ne…

iwebsec靶场 SQL注入漏洞通关笔记3- bool注入(布尔型盲注)

系列文章目录 iwebsec靶场 SQL注入漏洞通关笔记1- 数字型注入_mooyuan的博客-CSDN博客 iwebsec靶场 SQL注入漏洞通关笔记2- 字符型注入&#xff08;宽字节注入&#xff09;_mooyuan的博客-CSDN博客 目录 文章目录 前言 一、源码分析 二、sqlmap渗透 1.注入命令 2.完整交互过程…

小技巧1:如何给pip install进行换源

大家好&#xff0c;我是Kamen Black君&#xff0c;今天给大家介绍一个小技巧&#xff1a;如何给pip安装进行换源。 print("祝大家每天快乐&#xff0c;love and peace&#xff01;") 曾几何时&#xff0c;当我准备好了一段很有趣的代码准备进行运行时&#xff0c;发…

亚马逊云科技re:Invent:这些成功的数据驱动新企业背后的秘诀是什么?

北京时间11月29号&#xff0c;亚马逊云科技年度峰会re:Invent 2022将在拉斯维加斯开幕。这场年度最重磅的云计算技术大会不仅是科技盛宴&#xff0c;也是亚马逊云科技与诸多客户交流互鉴的绝佳平台&#xff0c;今天带大家认识一下几位资深云计算用户&#xff0c;以及他们和re:I…

ENVI5.6超详细教程大全-从安装包到安装流程详解

一、获取资源&#xff1a; https://www.xsoftnet.com/share/a000HzsWPCJXi.html二、安装教程&#xff1a;安装前请关闭杀毒软件&#xff0c;系统防火墙&#xff0c;断开网络连接 1.解压ENVI5.6安装包&#xff1a;ENVI5.6.zip 2.找到envi56-win.exe文件&#xff0c;双击进行安装…

Centos7 安装部署Kubernetes(k8s)集群实现过程

一.系统环境 服务器版本docker软件版本CPU架构CentOS Linux release 7.4.1708 (Core)Docker version 20.10.12x86_64二.前言 下图描述了软件部署方式的变迁&#xff1a;传统部署时代&#xff0c;虚拟化部署时代&#xff0c;容器部署时代。 传统部署时代&#xff1a; 早期&am…

制作一个简单HTML游戏网页(HTML+CSS)仿龙之谷网络游戏官网

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

vite+ts-3-后端准备(慢慢更新)

random recording 随心记录 What seems to us as bitter trials are often blessings in disguise. 看起来对我们痛苦的试炼&#xff0c;常常是伪装起来的好运。 应用tskoamysql构建高可维护性和扩展性架构 数据库基础 定义和特点 数据库就是按照特定格式存储数据的文件集合&…

二本Java渣渣9面字节遭虐,苦修数月深造这份宝典,终进阿里

见过真正头铁的程序员吗&#xff1f;如题所示&#xff0c;真人真事&#xff0c;这位二本的兄弟在这短短几个月内海投了638份简历&#xff0c;全挑的大厂岗位投的&#xff0c;仅字节跳动就前前后后面试了九次&#xff0c;他说&#xff1a;没有撤退可言。 九次面试经历也是奇了—…

西门子精彩触摸屏SMART V3组态用户管理的具体方法和步骤

西门子精彩触摸屏SMART V3组态用户管理的具体方法和步骤 如下图所示,新建一个项目,在连接中添加连接,触摸屏的IP地址要和 PLC的IP地址保持在同一网段, 如下图所示,在变量中添加一个变量, 如下图所示,在画面1中添加一个IO域,并连接上图中的变量, 如下图所示,设置…

python数据分析绘图

ROC-AUC曲线&#xff08;分类模型&#xff09; 混淆矩阵 混淆矩阵中所包含的信息 True negative(TN)&#xff0c;称为真阴率&#xff0c;表明实际是负样本预测成负样本的样本数&#xff08;预测是负样本&#xff0c;预测对了&#xff09;False positive(FP)&#xff0c;称为假…

HTML5期末大作业:游戏网站设计与实现——基于bootstrap响应式游戏资讯网站制作HTML+CSS+JavaScript

&#x1f389;精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

力扣每日一题:882. 细分图中的可到达节点【最短路径dijkstra】

给你一个无向图&#xff08;原始图&#xff09;&#xff0c;图中有 n 个节点&#xff0c;编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链&#xff0c;每条边之间的新节点数各不相同。 图用由边组成的二维数组 edges 表示&#xff0c;其中 edges[i] [ui, vi, cn…

搭建集群常用脚本

rsync同步工具&#xff0c;编写xsync脚本 yum install -y rsynchostname处理&#xff1a; vim /etc/hosts 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 ::1 localhost localhost.localdomain localhost6 localhost6.localdomai…

花房集团通过上市聆讯:5个月营收20亿 周鸿祎是大股东

雷递网 雷建平 11月25日花椒母公司花房集团今日正式通过聆讯&#xff0c;准备在港交所上市。这之前&#xff0c;360集团孵化的360数科、鲁大师也分别在美股和港股上市。360数科还将于下周在香港上市。这意味着&#xff0c;360董事长周鸿祎将在香港收获两个IPO。5个月营收20亿 经…

音视频开发之音频基础知识!

前言&#xff1a; 在音视频开发中&#xff0c;音频也是一个重要功能之一。在音频开发中有两个重要的概念&#xff0c;一个是PCM数据格式&#xff0c;另外一个是AAC数据格式。下面我们会重点介绍&#xff01;一、PCM重要音频参数&#xff1a; PCM数据也被称之为脉冲编码调制数据…

什么是coms coms设置图解

对与经常玩电脑的朋友来说&#xff0c;对与BIOS、COMS可能有点陌生&#xff0c;对于经常修电脑的朋友来说COMS可能会经常接触&#xff0c;今天我们来简单的说下coms设置图解&#xff0c;什么是COMS呢?coms主要是用于存储BIOS设置程序的设置参数和数据&#xff0c;而BIOS程序是…

计算机网络——如何构造HTTP请求

构造HTTP请求的常见方式有&#xff0c;通过form表单标签来实现&#xff0c;通过ajax来实现&#xff0c;这两种是通过HTML/JS来实现的&#xff0c;还可通过java使用Socket来进行构造&#xff08;本质就是根据不同的格式来解析字符串&#xff0c;处理请求&#xff09;&#xff0c…

【调优】大数据常见 Join 的使用场景

【调优】大数据常见 Join 的使用场景 上次写了大表和大表 join 的调优方法&#xff0c;今天总结一下大数据常见的 Join 方法。 1.Shuffle Join 大数据采用的是分布式存储&#xff0c;一个表的数据会分散在各个节点。为了进行 join&#xff0c;通常都会进行 shuffle 操作&…

【无标题】好消息 突破:IM开源项目OpenIM采用wasm技术实现jssdk

OpenIM 客户端sdk用golang实现&#xff0c;同时采用sqlite存储本地聊天记录&#xff0c;通过gomobile生成sdk&#xff0c;供iOS Android 调用&#xff0c;达到了了一套代码多端复用的效果。最近融合wasm技术&#xff0c;让浏览器具备存储能力&#xff0c;本地聊天记录存储在浏览…