至此现已完成了所有功能, 最终在 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 根本逃不掉这个访问者形式

说在最终

个人感觉许多内容其实都讲的有遗失或者有不标准的当地, 可是整个编译领域, 光入门的理论知识就太多太多了, 况且笔者仅仅自己写了个玩具, 并且几乎是个纯编译前端的项目, 也不敢说入门了, 因而就权当是学习笔记的共享了

源码中有很多注释, 个人感觉结合着看会愈加明晰, 能够看一下, 在这里能够看