使用Logos实现简易计算器:从词法分析到表达式求值

使用Logos实现简易计算器:从词法分析到表达式求值

本文将介绍如何利用Logos库实现一个简易算术表达式计算器。我们将完整实现从词法分析到语法解析再到表达式求值的全过程,帮助读者理解编译器前端的基本工作原理。

计算器功能概述

我们的计算器将支持以下功能:

  • 整数常量(如123)
  • 基本算术运算符(+、-、*、/)
  • 括号表达式(改变运算优先级)
  • 一元负号运算符(如-5)

最终实现效果示例:

输入:1 + 2 * (3 - 4)
输出:
[AST结构]
Add(
    Int(1),
    Mul(
        Int(2),
        Sub(
            Int(3),
            Int(4)
        )
    )
)

[计算结果]
-1

技术实现架构

整个计算器的实现分为三个核心模块:

  1. 词法分析器(Lexer):将输入字符串转换为标记流
  2. 语法解析器(Parser):将标记流转换为抽象语法树(AST)
  3. 表达式求值器(Evaluator):递归计算AST的值

词法分析实现

使用Logos库定义词法标记:

#[derive(Logos, Debug, PartialEq)]
enum Token {
    #[regex(r"[0-9]+", |lex| lex.slice().parse().ok())]
    Int(isize),
    
    #[token("+")]
    Add,
    #[token("-")]
    Sub,
    #[token("*")]
    Mul,
    #[token("/")]
    Div,
    
    #[token("(")]
    LParen,
    #[token(")")]
    RParen,
    
    #[error]
    #[regex(r"[ \t\n]+", logos::skip)]
    Error,
}

关键点说明:

  • 使用正则表达式匹配整数和空白字符
  • 每个运算符都有对应的标记类型
  • 错误标记用于处理无法识别的输入

语法解析与AST设计

我们使用Chumsky库构建解析器,首先定义AST结构:

#[derive(Debug)]
enum Expr {
    Int(isize),
    Neg(Box<Expr>),
    Add(Box<Expr>, Box<Expr>),
    Sub(Box<Expr>, Box<Expr>),
    Mul(Box<Expr>, Box<Expr>),
    Div(Box<Expr>, Box<Expr>),
}

解析器实现的关键在于正确处理运算符优先级和括号:

fn parser() -> impl Parser<Token, Expr, Error = Simple<Token>> {
    let int = select! { Token::Int(x) => Expr::Int(x) };
    
    let expr = recursive(|expr| {
        let unary = just(Token::Sub)
            .repeated()
            .then(expr.clone())
            .foldr(|_op, rhs| Expr::Neg(Box::new(rhs)));
            
        let atom = int.or(unary)
            .or(expr.delimited_by(just(Token::LParen), just(Token::RParen)));
            
        let product = atom.clone()
            .then(
                just(Token::Mul).to(Expr::Mul as fn(_, _) -> _)
                    .or(just(Token::Div).to(Expr::Div))
                    .then(atom)
                    .repeated(),
            )
            .foldl(|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)));
            
        product.clone()
            .then(
                just(Token::Add).to(Expr::Add as fn(_, _) -> _)
                    .or(just(Token::Sub).to(Expr::Sub))
                    .then(product)
                    .repeated(),
            )
            .foldl(|lhs, (op, rhs)| op(Box::new(lhs), Box::new(rhs)))
    });
    
    expr.then_ignore(end())
}

表达式求值实现

求值器采用递归下降的方式遍历AST:

fn eval(expr: &Expr) -> isize {
    match expr {
        Expr::Int(x) => *x,
        Expr::Neg(x) => -eval(x),
        Expr::Add(l, r) => eval(l) + eval(r),
        Expr::Sub(l, r) => eval(l) - eval(r),
        Expr::Mul(l, r) => eval(l) * eval(r),
        Expr::Div(l, r) => eval(l) / eval(r),
    }
}

功能扩展建议

完成基础实现后,可以考虑以下扩展:

  1. 错误处理增强

    • 除零检查
    • 表达式语法错误提示
    • 溢出检测
  2. 运算符扩展

    • 模运算(%)
    • 指数运算(^)
  3. 函数支持

    • 内置数学函数(abs, pow等)
    • 用户自定义函数
  4. 变量支持

    • 变量存储与引用
    • 赋值表达式

总结

通过这个项目,我们完整实现了一个简易算术表达式计算器,涵盖了从词法分析到语法解析再到表达式求值的完整流程。这个实现展示了如何使用Logos和Chumsky这两个强大的Rust库来构建语言处理工具的核心组件。

读者可以基于这个基础框架,进一步扩展功能,实现更复杂的领域特定语言(DSL)或编程语言。理解这些基础概念对于深入学习编译原理和语言实现技术至关重要。

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

强妲佳Darlene

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值