Rust实现一个表达式Parser小结

目录
  • 正文
  • lexer
  • parser
  • traversal
  • 说在最后

正文

src/lib.rs 补上一个函数和一个 smoke test, 如下

pub use traversal::{eval, format};

pub fn build_ast(expr: &str) -> Result<Node, String> {
    let root = syntax(lex(expr)?)?;
    Ok(root)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn smoke() {
        let expr = "1*2+(3/(4+(-5)))";
        let ast = build_ast(expr).unwrap();

        assert_eq!(-1, eval(&ast));
        assert_eq!("1 * 2 + 3 / (4 + (-5))", format(&ast));
    }
}
复制代码

lexer

Lexer 模块主要围绕 DFA 展开讲了很多, 具体实现的时候其实并没有那么复杂, 主要是需要设计好存储结构, 并理解清楚状态转移表的含义

parser

Parser 模块大部分篇幅都在讲文法和 Parser Combinator, 最后真正实现的时候反而非常简单, 这里有两点需要注意

  • 文法的处理

    • 处理优先级
    • 消除左递归
  • Parser Combinator 的封装和设计理念

Parser Combinator 只是一种工具而已, 同类型的还有 Parser Generator, 由于笔者接触不多, 就不好展开讲了

不过这里个人认为, 比起 Parser Combinator 本身, 他的设计理念更值得关注, 尤其是这种相对比较小众的函数式编程思维, 个人觉得很有意思

traversal

Traversal 模块最主要的就是访问者模式了, 根据我查到的资料来看, 普通的 AST 基本只有这一种遍历方式

借助访问者模式, 将所有的 visitor 单独抽离进行实现, 代码可读性和耦合度得到了很大的优化, 笔者最开始其实是希望实现一个最最最简化的表达式解析器, 因此第一版并没有引入访问者模式, 而在实现最后的两个需求时发现代码完全耦合在一起实在很恶心, 就干脆加进来了, 反正遍历 AST 基本逃不掉这个访问者模式

说在最后

个人感觉很多内容其实都讲的有遗漏或者有不规范的地方, 但是整个编译领域, 光入门的理论知识就太多太多了, 况且笔者只是自己写了个玩具, 而且几乎是个纯编译前端的项目, 也不敢说入门了, 因此就权当是学习笔记的分享了

源码中有大量注释, 个人感觉结合着看会更加清晰, 可以看一下, 在这里可以看

以上就是Rust实现一个表达式Parser小结的详细内容,更多关于Rust Parser表达式小结的资料请关注我们其它相关文章!

(0)

相关推荐

  • Rust for循环语法糖背后的API场景分析

    目录 1.拿走所有权的for循环 2.只读for循环 3.读写for循环 Rust中for循环实质上是一个语法糖,in后面的对象要求是一个迭代器,for循环就是对这个迭代器循环调用next,而in前面的名称就是每一次迭代后返回的结果,如果next返回Option::None则退出循环.了解这一点后我们可以自己编写自己的迭代器类型,然后使用for循环进行迭代. rust有三种for循环,分别用于不同的场景. 1.拿走所有权的for循环 形式如:for item in collection(集合或容

  • Rust语言中的String和HashMap使用示例详解

    目录 String 新建字符串 更新字符串 使用 + 运算符或 format! 宏拼接字符串 索引字符串 字符串 slice 遍历字符串 HashMap 新建 HashMap HashMap 和 ownership 访问 HashMap 中的值 更新 HashMap 直接覆盖 新插入 更新旧值 总结 String 字符串是比很多开发者所理解的更为复杂的数据结构.加上 UTF-8 的不定长编码等原因,Rust 中的字符串并不如其它语言中那么好理解. Rust 的核心语言中只有一种字符串类型:str

  • Rust 模式匹配示例详解

    目录 函数 斐波那契数列函数 小结 函数 今天学习的内容是 Rust 中的函数. 函数,和 if 条件控制,while.loop.for 循环控制,在底层都是使用跳转实现的,所以函数也属于流程控制的一种.可以这样理解,当函数执行时,程序的执行主体就进入了函数内部,当函数执行完毕,则又返还回去,这就是函数的流程控制. Rust 中使用函数和 Typescript 比较像,需要关注参数的类型和返回值的类型, 再就是有一点 Rust 语言的特性,返回值可以是一个表达式. 参数的类型注解和变量是一样的,

  • Rust Aya 框架编写 eBPF 程序

    目录 1. 前言 2. Rust 开发环境搭建 2.1 创建 VM 虚拟机 2.2 安装 Rust 开发环境 2.3 安装 bpf-linker 依赖 和 bpftool 工具 3. Aya 向导创建 eBPF 程序 3.1 使用向导创建项目 3.2 编译 eBPF 程序 3.3 运行用户空间程序 4. 总结 参考 1. 前言 Linux 内核 6.1 版本中有一个非常引人注意的变化:引入了对 Rust 编程语言的支持.Rust 是一种系统编程语言,Rust 通过提供非常强大的编译时保证和对内存

  • Rust实现一个表达式Parser小结

    目录 正文 lexer parser traversal 说在最后 正文 在 src/lib.rs 补上一个函数和一个 smoke test, 如下 pub use traversal::{eval, format}; pub fn build_ast(expr: &str) -> Result<Node, String> { let root = syntax(lex(expr)?)?; Ok(root) } #[cfg(test)] mod tests { use super

  • 利用Rust实现一个简单的Ping应用

    目录 目标 命令行解析 实现Ping 周期性发送 其他 验证 总结 这两年Rust火的一塌糊涂,甚至都烧到了前端,再不学习怕是要落伍了.最近翻了翻文档,写了个简单的Ping应用练练手,被所有权折腾的够呛,相比起Golang上手难度大很多,现将开发中的一些问题总结如下,所有源码见ring. 目标 实现一个Ping,功能包含: 命令行解析 实现ICMP协议,pnet包中已经包含了ICMP包定义,可以使用socket2库发送 周期性发送Ping,通过多线程发送,再汇总结果 监听退出信号 命令行解析 系

  • 尝试自己动手用react来写一个分页组件(小结)

    本文介绍了尝试自己动手用react来写一个分页组件(小结),分享给大家,具体如下: 分页效果 在线预览 github地址 效果截图(样式可自行修改): 构建项目 create-react-app react-paging-component 分页组件 1.子组件 创建 Pagecomponent.js 文件 核心代码: 初始化值 constructor(props) { super(props) this.state = { currentPage: 1, //当前页码 groupCount:

  • Rust 搭建一个小程序运行环境的方法详解

    目录 从零到一:构建一个能运行小程序的App FinClip 安全沙箱的初始化 获得 SDK Key 以及 SDK Secret 的两种方式 方式一:采用 FinClip.com 托管服务 方式二:自行部署 FinClip 社区版 FinClip SDK 在 App 中的初始化 Rust 开发环境的准备 Rust 代码编译成 iOS 静态库的验证 搭建一个FinClip社区版docker运行环境,安装设置Rust开发编译iOS代码的环境,设置xcode的项目配合,集成FinClip SDK,准备

  • 利用Rust编写一个简单的字符串时钟

    目录 1.简介 2.用到的知识点 2.1 取utc时间 2.2 图片变换为像素图案 2.3 字符方式显示当前时间 2.4 时间刷新 1.简介 用rust写的一个简单的练手的demo,一个字符串时钟,在终端用字符串方式显示当前时间.本质是对图片取灰度,然后每个像素按灰度门限用星号代替灰度值,就把图片变为由星号组成的字符型图案.把时间字符串的每个字符按照字母和数字图片的样式转换为字符,然后拼接字符图案就实现了字符时钟的效果. 主要用到的知识有:rust操作时间.字符串.vector,字符串和vect

  • C++98/11/17表达式类别(小结)

    目标 以下代码能否编译通过,能否按照期望运行? #include <utility> #include <type_traits> namespace cpp98 { struct A { }; A func() { return A(); } int main() { int i = 1; i = 2; // 3 = 4; const int j = 5; // j = 6; i = j; func() = A(); return 0; } } namespace cpp11 {

  • 基于Java实现一个复杂关系表达式过滤器

    目录 背景 分析准备 实现方式 写在最后 背景 最近,有一个新需求,需要后台设置一个复杂的关系表达式,根据用户指定ID,解析该用用户是否满足该条件,后台设置类似于禅道的搜索条件 但是不同的是禅道有且仅有两个组,每个组最多三个条件 而我们这边组与关系可能是更复杂的,组中有组,每个条件都是有且或关系的.由于保密原因,原型就不发出来了. 看到这个需求,作为一个后端,第一时间想到的是类似QLEpress这类的表达式框架,只要构建一个表达式,通过解析表达式即可快速对目标用户进行筛选,但是可惜的是前端同学不

  • JavaScript学习小结之被嫌弃的eval函数和with语句实例详解

    前面的话 eval和with经常被嫌弃,好像它们的存在就是错误.在CSS中,表格被嫌弃,在网页中只是用表格来展示数据,而不是做布局,都可能被斥为不规范,矫枉过正.那关于eval和with到底是什么情况呢?本文将详细介绍eval()函数和with语句 eval 定义 eval()是一个全局函数,javascript通过eval()来解释运行由javascript源代码组成的字符串 var result = eval('3+2'); console.log(result,typeof result)

  • 一篇教会你写90%的shell脚本(入门小结)

    shell脚本? 在说什么是shell脚本之前,先说说什么是shell. shell是外壳的意思,就是操作系统的外壳.我们可以通过shell命令来操作和控制操作系统,比如Linux中的Shell命令就包括ls.cd.pwd等等.总结来说,Shell是一个命令解释器,它通过接受用户输入的Shell命令来启动.暂停.停止程序的运行或对计算机进行控制. shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效.安全.低成本地使用 Linux 内核,这就是 Shell 的本质.

随机推荐