自己动手写编译器:自顶向下的自动状态机

news2025/1/4 18:57:34

本节我们介绍编译原理中一种新的数据结构叫自顶向下的自动状态机。前面我们在做词法解析时接触了大量自动状态机,他们存在一个缺陷那就是无法对要识别的字符串进行计数,因此当我们要判断括号对是否匹配时,使用在词法解析的状态机就处理不了,例如给定字符串"((())()))",我们判断其中左右括号是否都能匹配上,以前的状态机就无法处理。

但如果我们匹配上一个数据结构,也就是“栈”,那么问题就能得到解决。我们把状态机跟一个栈组合在一起的情况就叫自顶向下的状态机(push-down automaton)也叫 PDA。这个结构很重要,后续我们的语法解析算法就得依赖它。

我们看看其运行的基本流程。在词法解析中,状态机的当前所处状态由上一个状态和输入字符共同决定,但是在 PDA 中,状态机的状态由堆栈顶部的元素决定,堆栈中存储的是状态机各个状态的状态值,同时状态机在接收到字符输入后,它输出的不再是下一个状态节点,而是对应要采取的行动,下一个状态节点要从堆栈的顶部获取。在状态机中有四种行动可以采取,分别为:
1,接收,当状态机采取该行动时表示当前接收字符所形成的字符串。
2,错误,当状态机采取该行动时表示当前接收字符形成的字符串不符合状态机的规则。
3,push N, 把状态机节点 n压入堆栈顶部。
4,pop, 从堆栈中取出顶部元素,该元素的取值对应状态机所在状态。

我们看看如何使用 PDA 来识别括号字符串是否满足括号匹配。首先状态表如:
请添加图片描述
我们使用 state_table来表示上表,在状态 0 就是起始状态,我们使用如下算法或流程来表示括号识别流程:

将初始状态节点压入堆栈
while(action=state_table[当前堆栈顶部节点数值][输入字符] != accept) {
    if(action == error) {
        print("括号字符串不匹配")
        return 1;
    }
    else {
        执行 action 对应操作
    }
}

我们看看如何使用代码实现上面算法,还是基于前面的dragon-compiler 项目来进行,在项目目录下新建一个 pda 文件夹,然后执行:

init pda

进行初始化模块,然后在给定目录创建代码文件 pda.go,并下输入代码如下:

package pda

import (
	"fmt"
)

const (
	ERROR = iota
	ACCEPT
	PUSH1
	POP
	EOF
)

type StateTable struct{}

func (s *StateTable) get(state int8, symbol int) int8 {
	if state == 0 {
		switch symbol {
		case '(':
			return PUSH1
		case ')':
			return ERROR
		case EOF:
			return ACCEPT
		}
	}

	if state == 1 {
		switch symbol {
		case '(':
			return PUSH1
		case ')':
			return POP
		case EOF:
			return ERROR
		}
	}

	panic("state can only 0 or 1")
}

type BracketPDA struct {
	stateTable *StateTable
	stack      []int8
}

func NewBracketPDA() *BracketPDA {
	pda := &BracketPDA{
		stateTable: &StateTable{},
		stack:      make([]int8, 0),
	}

	pda.stack = append(pda.stack, 0)

	return pda
}

func (b *BracketPDA) Parse(str string) {
	pos := 0
	for true {
		symbol := EOF
		if pos < len(str) {
			symbol = int(str[pos])
		}
		state := b.stack[len(b.stack)-1]
		action := b.stateTable.get(state, symbol)
		switch action {
		case ERROR:
			fmt.Printf("str: %s, is rejected\n", str)
			return
		case ACCEPT:
			fmt.Printf("str: %s, is accept\n", str)
			return
		case PUSH1:
			b.stack = append(b.stack, 1)
		case POP:
			b.stack = b.stack[:len(b.stack)-1]
		}

		pos += 1
		if symbol == EOF {
			return
		}
	}
}

上面代码中StateTable用来模拟状态表,它只有一个方法那就是 get,输入当前状态和读入的字符,它给出要采取的行动,如果返回 PUSH1,那么我们需要将状态值 1 压入堆栈,其他的依次类推。BracketPDA用来模拟整个 PDA,它包含一个堆栈 stack 用来存放状态节点,对应的 Parse 函数在输入括号字符串后启动匹配过程,Parse 函数遍历输入字符串的每个字符,然后获取堆栈顶部的状态节点值,通过 StateTable 的 get 函数获取要采取的动作,如果 get 返回 accept,那么进入接收状态,如果返回 ERROR,那么进入错误状态,返回 PUSH1 则将节点 1 压入堆栈,如果返回 POP,则将堆栈顶部的元素弹开。

在 main.go 中使用调用 PDA 的代码如下:

package main

import (
	"pda"
)

func main() {
	pdaParser := pda.NewBracketPDA()
	pdaParser.Parse("(())")
}

上面代码运行后所得结果如下:

str: (()), is accept

也就是输入的括号字符串"(())“能够匹配,我们可以去掉其中一个括号试试,例如 pdaParser.Parse(”(()"),所得结果如下:

str: ((), is rejected

由此可见我们实现的 PDA 能有效的识别输入的括号字符串是否匹配。更多调试演示和讲解视频请在 b 站搜索"coding 迪斯尼“,代码下载为:
https://github.com/wycl16514/compiler-push-down-automata.git

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

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

相关文章

C++基础算法之枚举

星光不问赶路人 岁月不负有心人 &#x1f3a5;烟雨长虹&#xff0c;孤鹜齐飞的个人主页 &#x1f525;个人专栏 期待小伙伴们的支持与关注&#xff01;&#xff01;&#xff01; 目录 枚举算法的简介 枚举算法的运用 #特别数的和 题目描述# 输入描述# 输入输出样例# #找到最多…

机器学习+大数据项目

一、特征工程 特征清洗 特征监控 特征选择 计算每一个特征和响应变量的相关性 通过L1正则项来选择特征 训练能对特征打分的预选模型 通过特征组合后再来选择特征 通过深度学习来进行特征选择

Python笔记08-面向对象

文章目录 类和对象构造方法内置方法封装继承类型注解多态 类只是一种程序内的“设计图纸”&#xff0c;需要基于图纸生产实体&#xff08;对象&#xff09;&#xff0c;才能正常工作 这种套路&#xff0c;称之为&#xff1a;面向对象编程 类和对象 定义类的语法如下&#xff…

Qt 调试体统输出报警声

文章目录 前言一、方法1 使用 Qsound1.添加都文件 直接报错2.解决这个错误 添加 QT multimedia3. 加入代码又遇到新的错误小结 二、第二种方法1.引入库 总结 前言 遇到一个需求&#xff0c;使用Qt输出报警声&#xff0c;于是试一试能调用的方法。 一、方法1 使用 Qsound 1.…

Hive 数据同步

一、需求 同步集团的数据到断直连环境。 二、思路 三、同步数据&#xff08;方案&#xff09; 1、环境&#xff1a;断直连模拟环境 2、操作机器&#xff1a;ETL 机器 XX.14.36.216 3、工作路径&#xff1a;cd /usr/local/fqlhadoop/hadoop/bin 4、执行命令&#xff1a; 命令…

IPv6路由协议----BGP4+

BGP基本概念 边界网关协议BGP(Border Gateway Protocol)是一种实现自治系统AS(Autonomous System)之间的路由可达,并选择最佳路由的距离矢量路由协议。 MP-BGP是对BGP4进行了扩展达到在不同网络中应用的目的,BGP4原有的消息机制和路由机制并没有改变。MP-BGP在IPv6单播网…

如何在“Microsoft Visual Studio”中使用OpenCV构建应用程序

我在这里描述的所有内容都将应用于 OpenCV 的界面。我首先假设您已经阅读并成功完成了 Windows 中的安装教程。因此&#xff0c;在进一步操作之前&#xff0c;请确保您有一个包含 OpenCV 头文件和二进制文件的 OpenCV 目录&#xff0c;并且您已按照此处所述设置环境变量 设置 O…

每日一题——LeetCode1103.分糖果 ||

方法一 个人方法&#xff1a; 有多少人就创建多大的数组并把数组的所有元素初始化为0&#xff0c;只要还有糖果&#xff0c;就循环给数组从头到尾添加糖果&#xff0c;每次分的糖果数递增1&#xff0c;最后可能刚好分完也可能不够&#xff0c;不够就还剩多少给多少。 var dis…

力扣刷题记录(28)LeetCode:797、200、463

797. 所有可能的路径 解题思路&#xff1a;回溯算法&#xff0c;当收集到的路径的最后一个值等于n-1时&#xff0c;收集答案。 参数&#xff1a;图、当前结点 class Solution { public:vector<int> path;vector<vector<int>> ans;void dfs(vector<vector…

Java获取IP地址及对应的归属地

目录 前言 一、获取访问的IP地址 二、通过IP地址获取对应的归属地 2.1 Ip2region 2.1.1 高达 99.9 % 的查询准确率 2.1.2 Ip2region V2.0 特性 2.1.3 多语言以及查询客户端的支持 2.2 Ip2region xdb Java 查询客户端实现 2.2.1 引入 Maven 仓库 2.2.2 ip2region.xdb …

从Scroll怒喷社区用户事件,看L2龙头ZKFair的做事格局

这两天&#xff0c;随着美国SEC正式批准所有11只比特币现货ETF的消息公布&#xff0c;吸引了传统主流增量资金的入场&#xff0c;比特币多头一举将比特币干到了48000刀的位置&#xff0c;并随时向着前高发起了冲击。比特币的强势带动了其他加密资产的保障&#xff0c;整个加密市…

Android开发基础(四)

Android开发基础&#xff08;四&#xff09; 本篇将从Android数据存储方式去理解Android开发。 Android数据存储方式 Android提供了多种数据存储方式。 一、SharedPreferences存储 主要用于存储一些简单的配置信息&#xff0c;如登录账号密码等&#xff1b; 这种存储方式采…

类和对象---C++

类和对象目录 类和对象1.封装1.1 封装的意义1.2 struct和class区别1.3 成员属性设置为私有1.3.1 联系---判断圆和点的位置关系 2.对象的初始化和清理2.1 构造函数和析构函数2.2 构造函数的分类及调用2.2.1无参构造函数调用2.2.2有参构造函数调用2.2.2.1括号法2.2.2.2显式法2.2.…

C++学习笔记(二十八):c++ 静态库及动态库的使用

静态库的使用 库的使用会很大程度减少我们的工作&#xff0c;本节对c中静态库和动态库的使用进行简单的介绍。静态链接库意味着这个库会被放到可执行文件中&#xff0c;在生成的exe中。动态链接库是在程序运行时链接的&#xff0c;可以在程序运行时调用加载库函数的方法来实现&…

蚂蚁爱购--靠谱的SpringBoot项目

简介 这是一个靠谱的SpringBoot项目实战&#xff0c;名字叫蚂蚁爱购。从零开发项目&#xff0c;视频加文档&#xff0c;十天就能学会开发JavaWeb项目。 教程路线是&#xff1a;搭建环境> 安装软件> 创建项目> 添加依赖和配置> 通过表生成代码> 编写Java代码&g…

测试八年|对业务测试人员的一些思考

自从事测试工作八年多以来&#xff0c;经历过三个部门多条业务线&#xff0c;也经历过测试转型再回到测试&#xff0c;在此过程中对测试工作和角色的认知也逐步有些思考&#xff0c;想把这些思考分享给大家&#xff0c;希望为业务测试同学提供一些有价值的思路。 一、质量保障…

【Leetcode】2085. 统计出现过一次的公共字符串

文章目录 题目思路代码 题目 2085. 统计出现过一次的公共字符串 思路 使用两个哈希表 words1Count 和 words2Count 分别统计两个数组中每个单词的出现次数。然后遍历 words1Count 中的每个单词&#xff0c;如果该单词在 words1 中出现了一次&#xff0c;且在 words2 中也出…

如何运用自养号测评策略在Lazada、Shopee上轻松提升销售和排名

卖家们常常会为Lazada、Shopee店铺销量不佳而感到困惑。然而&#xff0c;仅仅感叹并不能解决问题。作为卖家&#xff0c;我们需要深入分析问题&#xff0c;并采取有效的措施来解决它们。基本功是提升销量的基石&#xff0c;但仅仅依靠基本功是不够的。我们需要将运营和测评结合…

Java面试题(java高级面试题)

线程池的核心线程数设置为多大比较合理&#xff1f; Worker线程在执行的过程中&#xff0c;有一部计算时间需要占用CPU&#xff0c;另一部分等待时间不需要占用CPU&#xff0c;通过量化分析&#xff0c;例如打日志进行统计&#xff0c;可以统计出整个Worker线程执行过程中这两…