rhai icon indicating copy to clipboard operation
rhai copied to clipboard

Is it possible to get an iterator over unbounded variables in the AST? Like iter_literal_variables() for literal variables.

Open PhilVince96 opened this issue 1 year ago • 4 comments

Imagine something like this:

    let expression = String::from("src_port == 8080 && dst_port == 6868");

    let engine = Engine::new();

    // Build the abstract syntax tree
    let ast = engine.compile_expression(expression).unwrap();

   // HERE: before I build the Scope, I would like to know which unbounded variables are even relevant.

    let mut scope = Scope::new();
    scope.push("src_port", 8080_i64)
       .push("dst_port", 6868_i64);
    let result = engine
        .eval_ast_with_scope::<bool>(&mut scope, &ast)
        .unwrap();

I would like to get something like ast.iter_unbounded_variables() which yields something like ["src_port", "dst_port"] in the end.

Thank you all in advance for any helpful input. :)

PhilVince96 avatar Feb 06 '25 16:02 PhilVince96

There is a similar function to iterate all imported modules. Maybe you can base on that function and write something similar. Shouldn't be difficult....

But you need to take care of variables declared in local blocks.

Alternatively you can look at the parser implementation methods. They actually collect lists of unbounded variables as part of parsing closures that capture external variables. That's how the strict variables mode can work.

schungx avatar Feb 07 '25 00:02 schungx

Thanks a bunch for your quick reply, @schungx! I did search for the function you mentioned and read a lot of the code, but I’m still not sure which one you’re talking about. Could you please give me the exact name or location of the function that iterates over all imported modules? I’ve also looked into the parser implementation, but I hope I can avoid getting too deep into the logic.

By the way, in the ast.statements() output, you can clearly see the variables. But maybe thinking about getting them out easily and confidently is a bit too naive of me, since the current expression is still quite simple.

Output: Stmt: [Expr(And { lhs: FnCallExpr { hash: 13027229112934639772 (native only), name: "==", args: [Variable(src_port) @ 1:1, 8080 @ 1:13], op_token: Some(EqualsTo) } @ 1:10, rhs: FnCallExpr { hash: 13027229112934639772 (native only), name: "==", args: [Variable(dst_port)@ 1:21, 6868 @ 1:33], op_token: Some(EqualsTo) } @ 1:30 } @ 1:18)]

PhilVince96 avatar Feb 07 '25 09:02 PhilVince96

If you're only parsing expressions then it gets easier because you don't declare local variables. Therefore any Variable node in the AST is a reference to an external variable.

It only gets hairy when you can declare local variables in blocks.

You can use the walk API to recursively scan through the AST to pick out the variables. This is simplest and quite easy to use. Just pass in a closure capturing a Vec or something and catch Variable nodes.

schungx avatar Feb 07 '25 09:02 schungx

Just to check up on how it is going for you. Did you eventually resolve your issue?

schungx avatar Mar 19 '25 03:03 schungx

Closing this for now. Feel free to reopen if there are any more issues.

schungx avatar Aug 06 '25 04:08 schungx