1

How do I take a vector of function argument AST variants, extract the values, and use them to instantiate a function call?

I am writing an interpreter that evaluates certain expressions. Some of the expressions are function calls. I am having a hard time figuring out how to translate the function calls AST to the actual call. The AST gives me the function name and a vector of arguments. I can lookup the function pointer to call from the name using a map, but passing the arguments to the function pointer is problem.

Rust does not have a splat operator (argument expansion). I could pass them as a tuple and use destructuring of the arguments, but I can't figure out how to convert the vector of AST argument enum variants to a tuple of the concrete types.

I can't simply map or loop over the AST arguments to extract the values and produce a tuple.

I can use nested tuples to build a heterogenous list incrementally:

fn prepend<I,T>(i: I, t: T) -> (I,T) { (i, t) }

fn foo() {
    let x = ();
    let x = prepend(1, x);
    let x = prepend(2.0, x);
    let x = prepend(true, x);
}

But that only works because x gets shadowed and the new binding has a different type. This won't work:

fn foo() {
    let mut x = ();
    x = prepend(1, x);
    x = prepend(2.0, x);
    x = prepend(true, x);
}

Any ideas?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • user ExpHP in the rust lang forum answered your question properly: https://users.rust-lang.org/t/how-to-dynamically-build-function-calls-in-rust/29880 – n0099 Dec 28 '22 at 08:22

1 Answers1

2

You don't. Rust is a statically typed language and you are attempting to do non-statically-determinable actions.

Instead, all of your functions need to take in a collection of arguments, verify that there is the right number of arguments (and type, if appropriate to your interpreter), then call the appropriate Rust function with a fixed number of arguments:

// All of the panicking can be replaced by proper error handling.

enum Arg {
    Bool(bool),
    Int(i32),
}

impl Arg {
    fn into_bool(self) -> bool {
        match self {
            Arg::Bool(b) => b,
            _ => panic!("Not a bool"),
        }
    }

    fn into_int(self) -> i32 {
        match self {
            Arg::Int(i) => i,
            _ => panic!("Not an int"),
        }
    }
}

fn some_fn_wrapper(mut args: Vec<Arg>) {
    assert_eq!(args.len(), 3);

    let c = args.pop().unwrap();
    let b = args.pop().unwrap();
    let a = args.pop().unwrap();

    some_fn(a.into_bool(), b.into_int(), c.into_bool())
}

fn some_fn(_a: bool, _b: i32, _c: bool) {}

All of this will happen at runtime, as you want to create a highly dynamic language.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Thanks for the reply. Indeed, I can do all that you propose, except for the step that converts a vector of AST enum variants (e.g. `vec![Arg::Bool(true), Arg::Float(2.0), Arg::Int(1)])` to a heterogenous collection of values (e.g. `(true, 2.0, 1)` or `(true, (2.0, (1,)))` to pass the function. I can look up the function signature in a map. I can lookup the function pointer in different maps based on the signature. And I can call the functions, up to some number of supported arguments. But that crucial step is where I am stuck. – Dumbfounded Jul 02 '19 at 18:06
  • Why not? [Just write the code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=87c2725d8bccb78322dab9429f410111) – Shepmaster Jul 02 '19 at 18:11
  • Yes, you can take a `Vec` and convert it to a tuple. The issue is how to do so generically, for potentially any combination of types in the tuple. E.g., there are a number of built-in functions with different signature types, some take (i32), some (i32, f64), some (&str, bool, i32), etc. Given a `Function(String,Vec)`, how to generically take that `Vec` and produce a tuple or `HList` that conforms to the function signature. Implement something like `do_it` for all possible combination of argument types and argument numbers up to some limit, and then select the right one? – Dumbfounded Jul 02 '19 at 21:45