使用Logos实现简易计算器:从词法分析到表达式求值
logos Create ridiculously fast Lexers 项目地址: https://gitcode.com/gh_mirrors/log/logos
本文将介绍如何利用Logos库实现一个简易算术表达式计算器。我们将完整实现从词法分析到语法解析再到表达式求值的全过程,帮助读者理解编译器前端的基本工作原理。
计算器功能概述
我们的计算器将支持以下功能:
- 整数常量(如123)
- 基本算术运算符(+、-、*、/)
- 括号表达式(改变运算优先级)
- 一元负号运算符(如-5)
最终实现效果示例:
输入:1 + 2 * (3 - 4)
输出:
[AST结构]
Add(
Int(1),
Mul(
Int(2),
Sub(
Int(3),
Int(4)
)
)
)
[计算结果]
-1
技术实现架构
整个计算器的实现分为三个核心模块:
- 词法分析器(Lexer):将输入字符串转换为标记流
- 语法解析器(Parser):将标记流转换为抽象语法树(AST)
- 表达式求值器(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),
}
}
功能扩展建议
完成基础实现后,可以考虑以下扩展:
-
错误处理增强:
- 除零检查
- 表达式语法错误提示
- 溢出检测
-
运算符扩展:
- 模运算(%)
- 指数运算(^)
-
函数支持:
- 内置数学函数(abs, pow等)
- 用户自定义函数
-
变量支持:
- 变量存储与引用
- 赋值表达式
总结
通过这个项目,我们完整实现了一个简易算术表达式计算器,涵盖了从词法分析到语法解析再到表达式求值的完整流程。这个实现展示了如何使用Logos和Chumsky这两个强大的Rust库来构建语言处理工具的核心组件。
读者可以基于这个基础框架,进一步扩展功能,实现更复杂的领域特定语言(DSL)或编程语言。理解这些基础概念对于深入学习编译原理和语言实现技术至关重要。
logos Create ridiculously fast Lexers 项目地址: https://gitcode.com/gh_mirrors/log/logos
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考