Change up how function calls are handled

This is a big one.

For a while, builtin functions were a bit cumbersome and not easily
re-entrant. If you needed to call a function from within a builtin
function, the only method of doing so was to take a `FunctionState`
parameter, which would either be "Begin", meaning the function was being
called for the first time, or "Resume", meaning the function was being
re-entered. This meant that if we wanted to call another function within
this function, we'd have to set up a whole `match` statement to figure
out whether we were re-entering the function or starting out. It was a
mess and not very ergonomic, and most importantly, made it very
difficult to implement hashmaps.

Now, builtin functions are handled a little more elegantly. A native
function is pushed to the stack, where it is detected in the
`Vm::dispatch()` function. It is then called, like normal. If the
builtin function then needs to call *another* function, it will push
that function to the stack and call it, and then call `Vm::resume()` to
resume VM execution. `Vm::dispatch()` is then called again, this time
with the current function on top of the stack. If it's another builtin
function, the above is repeated. If it's a user-defined function, then
bytecode is executed in the main `loop` inside of resume. Ultimately, we
are able to compose builtin functions like we would any other internal
function to the program. Overall this should speed things up a little,
make them a whole lot easier to read, and make them a million times
easier to compose with other builtin parts of Rust.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2024-10-11 16:22:19 -07:00
parent 3fd0ba3a0b
commit c6e242c39d
12 changed files with 226 additions and 405 deletions

View File

@@ -91,6 +91,7 @@ impl Object for Ty {
.vtable
.get("__call__")
.expect("Why does a type not have a __call__ member?");
function.borrow().call(vm, argc + 1);
}