写点什么

语法分析 之表达式一

作者:Miracle
  • 2025-09-23
    四川
  • 本文字数:1679 字

    阅读完需:约 6 分钟

语法分析 之表达式一
let name = "zhuchuanjing";
复制代码


现在我们开始做点实际的工作了,上面 这段代码是简单的 但是这段代码又是不简单的

如果我们简单的做,比如说把后面的字符串赋值给前面的这个 id 的话,是很简单。但是在实际的开发中,或者是真实的语言。后面一定不仅是字符串,还包括了无限多可能性。他一定会是他的表达是 包括了比如说 a + b a +c a 加 b 加 d 括号。包括等等等等 系列的东西。所以我们现在需要的是一个所有语法。最核心的的元素。也就是表达式的算子。

听到表达式我记得很多年以前在学那个计算机基础的时候有个什么?什么逆波兰后缀还是前缀表示法。现在已经完全忘了。反正记得呃是把什么运算符放在前面,然后放运算对象好像是这种。

嗯,但是我们如果用组合器的方来做这个表达式。比想象的要简单。因为组合器加上递归这个神器之后我们可以实现非常简单。算子组合,然后可以实现非常厉害的解析效果。

所有表达式的基础。一定是运算符。那么我们把 运算符分为两大类,呃,不考虑那个坑爹的那个问号运算符,所谓的 3 元操作符那个东西现在在新的。开发语言基本上都被抛弃了,因为那个会极大的增加复杂的为了支持一个方便的 ? 选择 增加太多复杂性。所以我们只考虑 一元和二元运算符。

#[derive(Debug, Clone, PartialEq)]pub enum UnaryOp {    Neg, // -x    Not, // !x}
复制代码

这个一眼可见,我就不想我就不详细解释了。

2 元操作在下面。我也不详细解释了,应该一眼就能看明白

#[derive(Debug, Clone, PartialEq)]pub enum BinaryOp {    Add,       // x + y    AddAssign, //x += y    Sub,       // x - y    SubAssign, //x -= y    Mul,       // x * y    MulAssign, //x *= y    Div,       // x / y    DivAssign, //x /= y    Mod,       // x % y    ModAssign, //x %= y    Shr,    Shl,    BitAnd,    BitOr,    BitXor,    Eq,  // x == y    Ne,  // x != y    Lt,  // x < y    Gt,  // x > y    Le,  // x <= y    Ge,  // x >= y    And, // x && y    Or,  // x || y    Idx, // x[y] or x.y}
复制代码

如果把函数调用也看成一种后缀操作符的话,那么我们整个的表达式我们可以用下面的枚举来表示。

#[derive(Debug, Clone, PartialEq)]pub enum Expr {    Unary { op: UnaryOp, value: Box<Expr> },    Binary { left: Box<Expr>, op: BinaryOp, right: Box<Expr> },    Call { obj: Box<Expr>, args: Vec<Box<Expr>> },}
复制代码

这里面需要注意啊,大家可以看。我在那个在操作数的时候,left,right 那个 value call 的时候定义操作数的时候,大家注意到。前面加了一个 box。这个是 Rust 需要注意的一个点。因为在 Rust 中那个你如果说不加 box 它就会形成递归引用。但是加了个 box 是你就会认为它把它放到盒子里,用 C 的概念理解,就是说它是加了一个指针,引用了一个指针。所以这样的话就不会产生递归,所以如果在一个类型的引用了自己这个类型的话一定要加上 box。


我们先定一个 list,解析器。可以把方括号里面用逗号分割的各个表达式。组合成一个 Vec

pub fn list_parser<'a>(expr: impl Parser<'a, &'a str, Expr, Extra<'a>> + Clone,) -> impl Parser<'a, &'a str, Vec<Expr>, Extra<'a>> + Clone {    let expr_ws = expr.padded_by(rust_ws());    expr_ws.clone().separated_by(just(',').padded_by(rust_ws())).allow_trailing().collect::<Vec<_>>()        .delimited_by(just('[').padded_by(rust_ws()), just(']').padded_by(rust_ws()))}
复制代码

回到一开始的

let name = "zhuchuanjing";
复制代码

显然 刚才的 Exp 无法表示 右边这个表达式,所以我们至少应该有 Value(Dynamic) 这个枚举项 表示简单的文字量

这种简单的

let x = x + 1;
复制代码

也没办法表示 所以我们至少还有一个 Ident 这个枚举项表示 表达式中的标识符

use smol_str::SmolStr;#[derive(Debug, Clone, PartialEq)]pub enum Expr {    Value(Dynamic),    Ident(SmolStr),    Unary { op: UnaryOp, value: Box<Expr> },    Binary { left: Box<Expr>, op: BinaryOp, right: Box<Expr> },    Call { obj: Box<Expr>, args: Vec<Box<Expr>> },}
复制代码


用户头像

Miracle

关注

三十年资深码农 2019-10-25 加入

还未添加个人简介

评论

发布
暂无评论
语法分析 之表达式一_Miracle_InfoQ写作社区