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) 这个枚举项 表示简单的文字量
这种简单的
也没办法表示 所以我们至少还有一个 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>> },
}
复制代码
评论