1
0
Fork 0
forked from fun/fun

Some more Rusty Fun... :) (0.38.3)

This commit is contained in:
Johannes Findeisen 2026-01-29 03:29:14 +01:00
commit d39ef1a58e
10 changed files with 157 additions and 113 deletions

View file

@ -1,114 +1,3 @@
# Writing Rust-backed opcodes for Fun VM
!!! CAUTION !!! I am not very experienced in using Rust. This should show that it is possible to implement opcodes in Rust and integrate them with the Fun VM.
This guide explains how to implement VM opcodes in Rust, wire them into the C VM, and use them from Fun scripts.
It assumes you are comfortable with basic Rust and C and have a working Fun checkout.
## Overview
Funs VM is written in C, but you can implement opcode handlers in Rust and call them via FFI. The typical flow is:
1) Write a Rust function with a stable C ABI (extern "C", #[no_mangle]) that takes a pointer to the VM and returns an int status code.
2) Use VM stack helpers (exposed to Rust via FFI) to pop arguments and push results.
3) Expose that Rust function to the C VM by calling it from the opcode dispatch (a case in the VMs opcode switch or a small shim under src/vm/rust/).
4) Add or reuse a Fun builtin that maps to your opcode, then call it from Fun code.
## Project layout (relevant parts)
- src/rust/src/lib.rs — Rust library with exported opcode functions and FFI helpers.
- src/vm/rust/ — C-side wiring examples and small opcode cases calling into Rust.
- examples/rust_hello.fun — Example Fun script using a Rust-backed opcode.
- docs/opcodes.md — General overview of many built-in opcodes (mostly C-based).
## Enabling Rust in the build
Rust integration is optional and gated by a CMake flag. Default builds usually have it OFF.
Enable it for a configured profile (Debug or Release):
- Debug example:
cmake -S . -B build_debug -DFUN_WITH_RUST=ON
cmake --build build_debug --target fun
- Release example:
cmake -S . -B build_release -DFUN_WITH_RUST=ON
cmake --build build_release --target fun
Useful targets in this repository include:
- fun — the main executable
- rust_ops_build — helps build/link Rust ops when enabled
- test_opcodes — test executable (if you want to extend tests)
Note: In CLion, prefer building with one of the provided CMake profiles (Debug/Release) and avoid creating custom build directories.
## Writing an opcode in Rust
The Rust side is a no_std static library exposing C ABI functions that the VM can call. See src/rust/src/lib.rs for examples already in the tree.
Key points:
- Use extern "C" and #[no_mangle] to fix the symbol name.
- Take a raw pointer to the VM as *mut Vm; return i32 status (0 for success).
- Interact with the VM stack via helper FFI functions declared as externs.
- Provide a minimal panic handler (no_std) as shown in lib.rs.
Example: integer addition opcode implemented in Rust.
In src/rust/src/lib.rs:
#![no_std]
#[repr(C)]
pub struct Vm;
extern "C" {
fn vm_pop_i64(vm: *mut Vm) -> i64;
fn vm_push_i64(vm: *mut Vm, v: i64);
}
#[no_mangle]
pub extern "C" fn fun_op_radd(vm: *mut Vm) -> i32 {
unsafe {
let b = vm_pop_i64(vm);
let a = vm_pop_i64(vm);
vm_push_i64(vm, a + b);
}
0
}
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! { loop {} }
What this does:
- Pops two 64-bit integers from the VM stack.
- Pushes back their sum.
- Returns 0 to indicate success to the VM.
You can add more extern helpers (e.g., for strings, arrays, maps) once they are exposed by the C VM. The repository already includes a simple string example returning a const char* from Rust, see fun_rust_get_string() usage below.
## Wiring the opcode in C
To make the VM call your Rust opcode, add a small C-side case that invokes the exported Rust symbol. A minimal pattern lives under src/vm/rust/.
String demo wiring (already present): src/vm/rust/hello.c
case OP_RUST_HELLO: {
#ifdef FUN_WITH_RUST
const char *s = fun_rust_get_string();
if (!s) s = "";
push_value(vm, make_string(s));
#else
vm_raise_error(vm, "RUST_HELLO requires FUN_WITH_RUST=ON at build time");
push_value(vm, make_nil());
#endif
break;
}
For a stack-based math opcode (like fun_op_radd), you would declare and call the Rust function similarly:
#ifdef FUN_WITH_RUST
extern int fun_op_radd(void* vm); // or use the proper VM type if available
#endif
case OP_RADD: {
@ -197,3 +86,19 @@ Authoritative and practical resources on exposing Rust to C (FFI) and maintainin
https://cbindgen.github.io/cbindgen/
- bindgen (generate Rust bindings to existing C headers; useful when mixing C and Rust)
https://github.com/rust-lang/rust-bindgen
## Return-only Rust string helper
Two variants are available for passing a string to Rust and getting output:
- rust_hello_args(msg)
- Rust side prints the message to stdout; Fun receives Nil. Use when you only want side-effect printing.
- rust_hello_args_return(msg)
- Rust side does not print; it returns the provided string to Fun (useful for assignment or chaining).
Example:
msg = rust_hello_args_return("Hello back from Rust (no print)!")
print(msg)
See examples/rust_hello_args_return.fun for a complete script.