使用 nom (>5) 和 `alt` 的状态

State with nom (>5) and `alt`

在尝试了一些 Rust 中的树结构之后,我最终决定建立一个线性化的树,例如像

struct AST {
  exprs: Vec<Expr>,
}

enum Expr {
    LiteralInt(i32),
    OpAdd(ExprRef, ExprRef),
}

struct ExprRef(usize);

impl AST {
   // ...
   fn add_expr(&mut self, expr: Expr) -> ExprRef {
      self.exprs.push(expr);
      ExprRef(self.exprs.len() - 1)
   }
}

因此,当解析一个表达式(例如 "+ + 3 4 1")时,解析器需要改变 AST,方法是将新的表达式推入其中并使用 ExprRef 来进一步表达。

所以,我想到了

fn literal_int(ast: &mut AST) -> impl FnMut(&str) -> IResult<&str, ExprRef> {
   move |input: &str| {
      // ...
      let expr_ref = ast.add_expr(/* ... */);
      // ...
      Ok((input, expr_ref))
   }
}

这行得通,只要我不使用分支组合器,因为,您可能已经猜到了,需要对 ast 进行多次可变借用!例如

fn factor(ast: &mut AST) -> impl FnMut(&str) -> IResult<&str, ExprRef> {
   move |input: &str| {
      alt((
         literal_int(ast),
         parens(ast, term) // cannot borrow `ast` as mutable again
         ))(input)
   }
}

我尝试用 nom 实现对这种 AST 的解析,所以我愿意接受任何正确方向的建议,即使这意味着我必须走另一条路与解析器。不过这里好像还是有状态参与的。

这种副作用是解析器组合器中的反模式。您可能会针对您的代码风格使用错误的工具。它是拥有 ast 的解析器应该改变它,通过与你的子解析器共享 ast 你专门使用 alt.

请求错误

Nom linked issue,请注意,如果你真的想像 Geal 所说的那样做,你可以使用 Rc<RefCell<AST>>

你也可以手动 alt() 它只是一堆 if.