0

After trying some tree structures in Rust, I finally decided to built up a linearized tree, e.g. something like

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)
   }
}

Hence, when parsing an expression (e.g. "+ + 3 4 1") the parsers needs to mutate AST, by pushing new expressions into it and using the ExprRef for further expressions.

So, I've thought of something like

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))
   }
}

This works, as long as I don't use branching combinators, since, you might have guessed it already, there would be the need for multiple mutable borrows of ast! E.g.

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)
   }
}

I try to achieve parsing into this sort of AST with nom, so I'm open for any suggestions in the right direction, even if this means I have to go a different route with the parsers. But it seems that some state is involved here anyway.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
aphorisme
  • 115
  • 4
  • Is it possible to do something like `let lit = literal_int(ast); let parens = parens(ast, term);`, then in the capture `alt((lit, parents))(input)`? – Alexey S. Larionov Jan 12 '22 at 17:52
  • I've tested it in my current implementation. Unfortunately, this leads to the same error, since the closure captures the mutable reference. : / – aphorisme Jan 12 '22 at 18:14

1 Answers1

2

Such side effect are an anti pattern in a parser conbinator. You may use the wrong tool for your code style. it's the parser that own the ast that should mutate it, by sharing ast with your sub parser you ask for bug specially using alt.

Nom linked issue, note that if you really want to do that as Geal said you may use Rc<RefCell<AST>>.

You could also just do alt() manually it's just a bunch of if.

Stargateur
  • 24,473
  • 8
  • 65
  • 91