【编译原理】词法分析(编译器、转移图、正则表达式)

news2024/9/23 21:41:14

#词法分析

目录

    • 编译器的阶段
      • 前端
    • 转移图
    • 标识符的转移图
    • 关键字表算法
    • 正则表达式
      • 自动生成
      • 什么是正则表达式
      • 语法糖
      • 有限状态自动机(FA)
      • 小结

编译器的阶段

在这里插入图片描述
源程序→前端→中间表示→后端→目标程序
在这里插入图片描述

前端

在这里插入图片描述

其中词法分析器的作用:在这里插入图片描述

在这里插入图片描述
字符流到单词流的切分或转换
词法分析是编译器前端的一个重要步骤,它将源代码文本分解成一系列标记(tokens),这些标记代表了源代码中的各种语法元素,如关键字、标识符、常量、运算符等。

if (x > 5)
    y = "hello";
else
    z = 1;

词法分析器将这段代码分解成以下词法单元:

  1. IF - 关键词,表示条件语句的开始。
  2. GT - 运算符,表示“大于”。
  3. INT(5) - 整数常量,值为5。
  4. RPAREN - 右括号,表示条件表达式的结束。
  5. LPAREN - 左括号,表示条件表达式的开始。
  6. IDENT(x) - 标识符,代表变量x。
  7. SEMICOLON - 分号,表示语句的结束。
  8. IDENT(y) - 标识符,代表变量y。
  9. ASSIGN - 赋值运算符,用于将值赋给变量。
  10. STRING("hello") - 字符串常量,值为"hello"。
  11. ELSE - 关键词,表示条件语句的“否则”部分。
  12. INT(1) - 整数常量,值为1。
  13. IDENT(z) - 标识符,代表变量z。
  14. ASSIGN - 赋值运算符,用于将值赋给变量。
  15. SEMICOLON - 分号,表示语句的结束。
  16. EOF - 文件结束标记,表示源代码的结束。

词法分析器通常会生成一个词法单元流,每个词法单元包含类型(如关键字、标识符、常量等)和值(如变量名、数值、字符串等)。这些词法单元随后会被语法分析器使用,以构建抽象语法树(AST),这是源代码的树状结构表示,用于后续的编译或解释执行。

在这里插入图片描述
在这里插入图片描述

##手工构造法
在这里插入图片描述
手工编码实现

转移图

在这里插入图片描述
星号代表向前回退
在这里插入图片描述
这张转移图描述的是一个用于识别和解析关系运算符(如 <, >, <=, >=, ==, !=)的有限状态机(FSM)。这个FSM通过读取字符序列来识别这些运算符,并根据读取的字符返回相应的关系运算符。

  1. 初始状态(start)

    • 从初始状态开始,FSM读取第一个字符。
  2. 状态转移和动作

    • 当读取到 < 时,FSM进入一个新的状态,并再次读取下一个字符。
      • 如果下一个字符是 =,则返回 LE(小于等于)。
      • 如果下一个字符是 >,则返回 NE(不等于)。
      • 如果读取到其他字符,则回退到上一个字符,并返回 LT(小于)。
    • 当读取到 = 时,FSM再次读取下一个字符。
      • 如果下一个字符是 =,则返回 EQ(等于)。
      • 如果读取到其他字符,则回退到上一个字符,并返回 EQ(等于)。
    • 当读取到 > 时,FSM进入一个新的状态,并再次读取下一个字符。
      • 如果下一个字符是 =,则返回 GE(大于等于)。
      • 如果读取到其他字符,则回退到上一个字符,并返回 GT(大于)。
  3. 状态转移图

    • 转移图通过状态和转移边来描述FSM的行为。
    • 每个状态都有一个或多个转移边,这些转移边由输入字符触发。
    • 每个状态也可能有一个或多个动作,这些动作在转移时执行。
  4. 回退机制

    • 当FSM在某个状态读取到一个不符合预期的字符时,它会回退到上一个字符,并返回相应的关系运算符。
  5. 动作

    • 每个动作都返回一个关系运算符,如 LE, NE, LT, EQ, GE, GT

伪代码解释

token nextToken() {
    char c = getChar(); // 读取第一个字符
    switch(c) {
        case '<':
            c = getChar(); // 读取下一个字符
            switch(c) {
                case '=':
                    return LE; // 返回小于等于
                case '>':
                    return NE; // 返回不等于
                default:
                    rollback(); // 回退到上一个字符
                    return LT; // 返回小于
            }
        case '=':return EQ; // 返回等于
        case '>':
            c = getChar(); // 读取下一个字符
            if (c == '=') {
                return GE; // 返回大于等于
            } else {
                rollback(); // 回退到上一个字符
                return GT; // 返回大于
            }
        default:
            return other; // 处理其他字符
    }
}

标识符的转移图

在这里插入图片描述
这个FSM用于解析程序代码中的标识符,例如变量名、函数名等。标识符通常由字母、数字、下划线组成,且不能以数字开头。

  1. 初始状态

    • 从初始状态开始,FSM读取第一个字符。
  2. 状态转移和动作

    • 当读取到一个字母(a-zA-Z)或下划线(_)时,FSM进入一个新的状态,并继续读取下一个字符。
    • 如果读取到其他字符,则返回标识符(ID)并结束解析。
  3. 循环读取

    • 在新状态中,FSM继续读取字符,直到读取到的字符不再是字母、数字或下划线。
    • 这个过程是一个循环,直到遇到一个不符合条件的字符。
  4. 动作

    • 当FSM读取到一个符合条件的字符序列后,它会返回一个标识符(ID)。

伪代码解释

token nextToken() {
    char c = getChar(); // 读取第一个字符
    switch(c) {
        case 'a', 'b', ..., 'z', 'A', ..., 'Z', '_':
            c = getChar(); // 读取下一个字符
            while(c == 'a' || c == 'b' || ... || c == 'z' || c == 'A' || ... || c == 'Z' || c == '0' || ... || c == '9' || c == '_') {
                c = getChar(); // 继续读取下一个字符
            }
            return ID; // 返回标识符
        default:
            return other; // 处理其他字符
    }
}

这个FSM通过逐步读取字符并根据读取的字符序列返回相应的标识符,从而实现对标识符的识别和解析。这个过程确保了标识符的正确性,例如不允许以数字开头,且只能包含字母、数字和下划线。

在这里插入图片描述
这个FSM用于解析程序代码中的关键字,关键字是编程语言中预定义的、具有特殊意义的标识符,如 “if”、“else”、“while” 等。

  1. 初始状态

    • 从初始状态开始,FSM读取第一个字符。
  2. 状态转移和动作

    • 当读取到一个字母(a-zA-Z)或下划线(_)时,FSM进入状态 1
    • 在状态 1,如果读取到的字符是 i,则转移到状态 2
    • 在状态 2,如果读取到的字符是 f,则转移到状态 3
    • 在状态 3,如果读取到的字符是字母、数字或下划线,则继续读取下一个字符,直到读取到一个非这些字符的字符。
    • 如果在任何状态中读取到的字符不是预期的字符,FSM将返回到状态 other 并返回 ID(标识符)。
  3. 结束状态

    • 在状态 3,如果读取到的字符不是字母、数字或下划线,FSM将确认已经读取到完整的关键字 “if” 并返回关键字。
  4. 动作

    • 当FSM读取到一个符合条件的字符序列后,它会返回相应的关键字或标识符。

伪代码解释

token nextToken() {
    char c = getChar(); // 读取第一个字符
    if (isLetter(c) || c == '_') {
        c = getChar(); // 读取下一个字符
        if (c == 'i') {
            c = getChar(); // 读取下一个字符
            if (c == 'f') {
                c = getChar(); // 继续读取下一个字符
                while (isLetter(c) || isDigit(c) || c == '_') {
                    c = getChar(); // 继续读取下一个字符
                }
                if (!isLetter(c) && !isDigit(c) && c != '_') {
                    return "if"; // 返回关键字 "if"
                }
            }
        }
    }
    return ID; // 返回标识符
}

这个FSM通过逐步读取字符并根据读取的字符序列返回相应的关键字或标识符,从而实现对关键字 “if” 的识别和解析。这个过程确保了关键字的正确性,例如不允许关键字后面直接跟字母、数字或下划线,除非它们是关键字的一部分。

关键字表算法

在这里插入图片描述

正则表达式

自动生成

如何去写声明式的规范

什么是正则表达式

指出规则 产生出词法分析器
Kleene闭包

在正则表达式中,闭包(也称为星号运算符或 Kleene 星号)表示一个模式可以出现零次或多次。这个运算符用星号(*)表示。当应用到某个正则表达式上时,它意味着该表达式可以匹配零个或多个该模式。

举例解释

  1. 简单字符闭包

    • 正则表达式 a* 表示字符 ‘a’ 可以出现零次或多次。
    • 例如,对于字符串 “aaabaaa”,a* 可以匹配整个字符串,也可以只匹配 “a”, “aa”, “aaa” 等。
  2. 字符组合闭包

    • 正则表达式 ab* 表示字符 ‘a’ 后面跟着零个或多个 ‘b’。
    • 例如,它可以匹配 “a”, “ab”, “abb”, “abbb” 等。
  3. 复杂模式闭包

    • 正则表达式 (ab|cd)* 表示 ‘ab’ 或 ‘cd’ 可以出现零次或多次。
    • 例如,它可以匹配空字符串,“ab”, “cd”, “abab”, “cdcd”, “ababab”, “cdcdcd” 等。
  4. 数字模式闭包

    • 正则表达式 \d* 表示数字(0-9)可以出现零次或多次。
    • 例如,它可以匹配任何不包含数字的字符串,也可以匹配 “123”, “456”, “7890” 等。
  5. 实际应用示例

    • 假设我们需要匹配一个可能包含多个空格的字符串。正则表达式 \s* 可以匹配零个或多个空白字符(包括空格、制表符等)。
    • 例如,它可以匹配 “hello”, “hello world”, " hello world " 等。

给定字符集 Σ = { a , b } \Sigma = \{a, b\} Σ={a,b},我们可以构造多种正则表达式。正则表达式可以是单个字符、空串、或者通过选择(或)、连接和闭包操作符组合字符形成的更复杂的模式。以下是一些基于给定字符集的正则表达式示例:

  1. 单个字符

    • a
    • b
  2. 空串

    • ε \varepsilon ε(表示空字符串)
  3. 选择(或)

    • a|b(匹配 ‘a’ 或 ‘b’)
  4. 连接

    • ab(匹配 ‘a’ 后跟 ‘b’)
    • ba(匹配 ‘b’ 后跟 ‘a’)
  5. 闭包

    • a*(匹配 ‘a’ 出现零次或多次)
    • b*(匹配 ‘b’ 出现零次或多次)
  6. 组合使用

    • a|a*(匹配 ‘a’ 或者 ‘a’ 出现零次或多次)
    • b|b*(匹配 ‘b’ 或者 ‘b’ 出现零次或多次)
    • (a|b)*(匹配 ‘a’ 或 ‘b’ 出现零次或多次的任意序列)
  7. 更复杂的组合

    • a(b|a)*(匹配 ‘a’ 后跟 ‘b’ 或 ‘a’ 的任意序列)
    • b(a|b)*(匹配 ‘b’ 后跟 ‘a’ 或 ‘b’ 的任意序列)
    • (a|b)+(匹配 ‘a’ 或 ‘b’ 出现一次或多次的序列)
  8. 嵌套闭包

    • (a*|b*)*(匹配 ‘a’ 或 ‘b’ 出现零次或多次的序列,且该序列可以重复零次或多次)

在这里插入图片描述
0|(1-9)(0-9)*

语法糖

在这里插入图片描述

有限状态自动机(FA)

在这里插入图片描述
(DFA)
在这里插入图片描述
转移函数:

  • ( (q_0, a) \rightarrow q_1 ):从状态 ( q_0 ) 读取 ‘a’ 转移到状态 ( q_1 )。
  • ( (q_0, b) \rightarrow q_0 ):从状态 ( q_0 ) 读取 ‘b’ 仍然留在状态 ( q_0 )。
  • ( (q_1, a) \rightarrow q_2 ):从状态 ( q_1 ) 读取 ‘a’ 转移到状态 ( q_2 ),这是一个接受状态。
  • ( (q_1, b) \rightarrow q_1 ):从状态 ( q_1 ) 读取 ‘b’ 仍然留在状态 ( q_1 )。
  • ( (q_2, a) \rightarrow q_2 ):从状态 ( q_2 ) 读取 ‘a’ 仍然留在状态 ( q_2 )。
  • ( (q_2, b) \rightarrow q_2 ):从状态 ( q_2 ) 读取 ‘b’ 仍然留在状态 ( q_2 )。

要使字符串被接受,FSM 必须最终停在接受状态 ( q_2 )。根据转移函数,只有从状态 ( q_1 ) 读取 ‘a’ 才能转移到接受状态 ( q_2 )。因此,任何被接受的字符串必须至少包含一个 ‘a’ 来从 ( q_0 ) 转移到 ( q_1 ),并且至少包含另一个 ‘a’ 来从 ( q_1 ) 转移到 ( q_2 )。

被接受的字符串模式包括:

  • 至少包含两个 ‘a’。
  • 第一个 ‘a’ 使得 FSM 从 ( q_0 ) 转移到 ( q_1 )。
  • 第二个 ‘a’ 使得 FSM 从 ( q_1 ) 转移到 ( q_2 ),这是一个接受状态。
  • 除了这两个 ‘a’ 之外,字符串中可以包含任意数量的 ‘b’,并且这些 ‘b’ 可以在第一个 ‘a’ 之前、两个 ‘a’ 之间,或者在第二个 ‘a’ 之后。

因此,被接受的字符串包括:

  • “aa”(最少情况下,直接满足条件)
  • “ababa”
  • “baa”
  • “baab”
  • “bbaab”
  • “aab”
  • “bbbaabb”
  • 等等。

只要字符串中至少有两个 ‘a’,并且这两个 ‘a’ 之间的字符可以是任意数量的 ‘b’,那么这个字符串就会被接受。
什么叫接受?
最后到达双圈的接受状态

非确定的:(NFA)
在这里插入图片描述
将NFA转化为与之等价的DFA

小结

在这里插入图片描述

幂集 就是一个集合所有子集构成的集合
在这里插入图片描述

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

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

相关文章

【CanMV K230 AI视觉】 人体关键点检测

【CanMV K230 AI视觉】 人体关键点检测 人体关键点检测 动态测试效果可以去下面网站自己看。 B站视频链接&#xff1a;已做成合集 抖音链接&#xff1a;已做成合集 人体关键点检测 人体关键点检测是指标注出人体关节等关键信息&#xff0c;分析人体姿态、运动轨迹、动作角度等…

Vue常见面试题目

computed与watch区别 computed&#xff08;计算属性&#xff09;watch&#xff08;侦听器&#xff09;定义与用途计算属性&#xff08;computed&#xff09;用于声明式地描述一些依赖响应式属性的计算值。当依赖的响应式属性值发生变化时&#xff0c;计算属性会重新求值。侦听…

【深度学习】Pytorch基础

目录 梯度下降算法&#xff08;Gradient Descent&#xff09;代码实现 梯度下降算法&#xff08;Gradient Descent&#xff09; 梯度下降算法在机器学习中应用十分的广泛&#xff0c;不论是在线性回归还是Logistic回归中&#xff0c;它的主要目的是通过迭代找到目标函数的最小…

[网络]TCP/IP协议 之 网络层IP协议(3)

文章目录 网络层IP协议NAT机制 网络层 网络层主要做的事情: 1.路径规划(路由器选择) 2.地址管理 IP协议 1)4位版本 指定IP协议的版本, 4 > ipv4 , 6 > ipv6 2)4位首部长度 4位bit能表示0-15, 单位也是4字节, 所以IP报头最长60字节, 最短20字节 3)8位服务类型(TOS) ty…

【Qt】Qt界面美化 | 绘画

文章目录 绘画概述绘制API1. 图形(1). 线段(2). 矩形(3). 圆形(4). 绘制文本 2. 工具(1). 画笔(2). 画刷 3. 绘制图片QPainter图片操作 绘图设备(1). QPixmap(2). QImage(3). QPicture 结束语 绘画概述 虽然 Qt 已经内置了很多的控件&#xff0c;但还是有很多时候需要“自定义…

源于AI绘画的爆火,尝试做了一个工具网站

这两天做了一个 网页 Demo&#xff0c;实现了一些 AI 图像处理功能&#xff0c;前端用的是 React&#xff0c;后端用的是 Django 现在 &#xff0c;Demo 中已经实现的功能有两个&#xff1a; 1&#xff0c;人像分割&#xff1a;从图像中把人物分割出来&#xff0c;用一个透明…

Vue与React的Diff算法

虚拟DOM 定义 虚拟DOM是一种用于在前端开发中模拟真实DOM的技术。它是一种抽象的数据结构&#xff08;简单来说就是一个Javascript对象&#xff09;&#xff0c;用于描述HTML或XML文档的结构和内容。通过将页面的状态和结构保存在内存中&#xff0c;而不是直接操作真实的DOM&am…

C++·io流

本节主要是了解为主&#xff0c;需要具体使用时可以查看文档。 io流操作指的是对各种流的操作&#xff0c;我们经常使用的cin和cout是对标准流的操作&#xff0c;实际上它还可以对文件流操作。 官网资料&#xff1a;Input/Output - C Reference 流输入和流提取功能的实现中有着…

鸿蒙交互事件开发04——手势事件

1 概 述 手势事件是移动应用开发中最常见的事件之一&#xff0c;鸿蒙提供了一些方法来绑定手势事件。通过给各个组件绑定不同的手势事件&#xff0c;并设计事件的响应方式&#xff0c;当手势识别成功时&#xff0c;ArkUI框架将通过事件回调通知组件手势识别的结果。 …

记录一款人气领先的开源国产 ERP 系统

推荐一款人气领先的国产ERP系统&#xff0c;目前在Gitee上有12.3k star&#xff0c;在github上面也有1.2k 管伊佳ERP&#xff08;原名华夏ERP&#xff09;基于SpringBoot框架和SaaS模式&#xff0c;立志为中小企业提供开源好用的ERP软件&#xff0c;目前仅支持进销存财务生产功…

2025年第八届计算机图形和虚拟国际会议(ICCGV 2025)即将召开!

2025年第八届计算机图形和虚拟国际会议&#xff08;ICCGV 2025&#xff09;将于2025年2月21-23日在中国成都举行。随着信息技术的飞速发展&#xff0c;计算机图形学与虚拟现实技术正以前所未有的速度重塑着我们的认知世界与交互体验。从沉浸式游戏到精准医疗模拟&#xff0c;从…

气压测试实验(用IIC)

I2C: 如果没有I2c这类总线&#xff0c;连接方法可能会如下图&#xff1a; 单片机所有的通讯协议&#xff0c;无非是建立在引脚&#xff08;高低电平的变换高低电平持续的时间&#xff09;这二者的组合上&#xff0c;i2c 多了一个clock线&#xff0c;负责为数据传输打节拍。 (i2…

Unity3d中制作触发区域为圆形的按钮

一、常规矩形的Button制作 设置Highlighed Color为绿色。 此时当鼠标在button上方时会显示绿色&#xff1a; 二、圆形Button制作 在Button的Image中选择Source Image为Knob。 程序运行时&#xff0c;鼠标在矩形和圆形之间的区域&#xff0c;button还是会变成绿色。 所以&…

【时时三省】(C语言基础)指针进阶 例题5

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 abcdef是个常量字符串 把首地址a放在了p里面 第一个p 算的是指针变量的大小 它打印就是4或者8 第二个p1 p本来是a的地址 1就是b的地址 是地址就是打印四或者八 …

chapter14-集合——(List-LikedHashSet)——day18

目录 528-LikedHashSet介绍 529-LikedHashSet源码介绍 528-LikedHashSet课堂练习 528-LikedHashSet介绍 529-LikedHashSet源码介绍 528-LikedHashSet课堂练习

【计网】从零开始认识网络socket

优于别人&#xff0c;并不高贵&#xff0c; 真正的高贵应该是优于过去的自己。 --- 海明威 --- 从零开始认识网络socket 1 网络通信流程2 IP地址 VS MAC地址3 网络socket3.1 理解源 IP 地址和目的 IP 地址3.2 传输层的典型代表3.3 网络字节序3.4 socket编程基础 1 网络通信流…

高压直流接触器及使用注意事项

高压直流接触器及使用注意事项 1.概述2.接触器和继电器有什么区别3.节能线圈4.安装位置5.直流继电器线圈抑制6.高压继电器和接触器有哪些应用?7.线圈效率的决定因素是什么?对线圈驱动器有什么要求?8.负载极性对接触器的影响9.触点参数注意事项10.线圈供电要求1.概述 航空接…

基于单片机实现的的多点分布室内环境监测系统

基于STM32单片机为基础设计的一款室内环境监测系统程序源码 程序分为接收端和采集端两部分 接收端与采集端均以STM32单片机作为控制核心&#xff0c;两者通过ZigBee无线通信技术进行连接。接收端主要用于接收数据并将其实时显示以及报警。 采集端主要用于监测室内环境的各类数…

【Python工具】Python 装饰器实现统计函数执行时长

文章目录 1、背景2、轮子 1、背景 业务基本代码堆积完毕了&#xff0c;部分流程需要调优&#xff0c;想着在几个关键执行路径上进行时间记录&#xff0c;类似 SQL 慢日志一样的方法&#xff0c;方便进行性能问题发现。 了解了下&#xff0c;应该是采用装饰器实现是代码侵入最…

林草湿地址、导出echart为word

2.导出 // 导出exportDoc () {this.loading truelet arrRefs [this.$refs.endChart, this.$refs.processChart, this.$refs.officeEndChart]setTimeout(() > {Promise.all(arrRefs.map((canvasDom) > {return this.html2canvasHandle(canvasDom)})).then(res > {let…