Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
PhilVince96 opened this issue Feb 6, 2025 · 3 comments

Comments

@PhilVince96
Copy link

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. :)

@schungx
Copy link
Collaborator

schungx commented Feb 7, 2025

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.

@PhilVince96
Copy link
Author

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

@schungx
Copy link
Collaborator

schungx commented Feb 7, 2025

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants