pd-vm 0.22.4

RustScript bytecode compiler and VM
Documentation
#![cfg(feature = "runtime")]
use std::path::Path;

use vm::{
    CallOutcome, FunctionDecl, HostFunction, SourceFlavor, Value, Vm, VmStatus,
    compile_source_file, compile_source_with_flavor,
};

struct PrintFunction;
struct AddOneFunction;

impl HostFunction for PrintFunction {
    fn call(&mut self, _vm: &mut Vm, args: &[Value]) -> Result<CallOutcome, vm::VmError> {
        Ok(CallOutcome::Return(args.to_vec().into()))
    }
}

impl HostFunction for AddOneFunction {
    fn call(&mut self, _vm: &mut Vm, args: &[Value]) -> Result<CallOutcome, vm::VmError> {
        let value = match args.first() {
            Some(Value::Int(value)) => *value,
            _ => return Err(vm::VmError::TypeMismatch("int")),
        };
        Ok(CallOutcome::Return(vec![Value::Int(value + 1)].into()))
    }
}

fn register_functions(vm: &mut Vm, functions: &[FunctionDecl]) {
    for decl in functions {
        match decl.name.as_str() {
            "print" => {
                vm.bind_function("print", Box::new(PrintFunction));
            }
            "add_one" => {
                vm.bind_function("add_one", Box::new(AddOneFunction));
            }
            "runtime::sleep" | "runtime::exit" => {}
            other => panic!("unknown function '{other}'"),
        }
    }
}

fn run_vm_until_halted(vm: &mut Vm) {
    loop {
        match vm.run().expect("vm should run") {
            VmStatus::Halted => break,
            VmStatus::Yielded => continue,
            VmStatus::Waiting(_op_id) => vm
                .wait_for_host_op_blocking()
                .expect("vm should complete host operation"),
        }
    }
}

fn run_compiled_file(path: &Path) -> Vec<Value> {
    let compiled = compile_source_file(path).expect("compile should succeed");
    let mut vm = Vm::new(compiled.program);
    let mut jit_config = *vm.jit_config();
    jit_config.enabled = false;
    vm.set_jit_config(jit_config);
    register_functions(&mut vm, &compiled.functions);
    run_vm_until_halted(&mut vm);
    vm.stack().to_vec()
}

fn run_compiled_source(flavor: SourceFlavor, source: &str) -> Vec<Value> {
    let compiled = compile_source_with_flavor(source, flavor).expect("compile should succeed");
    let mut vm = Vm::new(compiled.program);
    let mut jit_config = *vm.jit_config();
    jit_config.enabled = false;
    vm.set_jit_config(jit_config);
    register_functions(&mut vm, &compiled.functions);
    run_vm_until_halted(&mut vm);
    vm.stack().to_vec()
}

#[test]
fn ifft_math_example_runs() {
    let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("examples");
    let stack = run_compiled_file(&root.join("ifft_math.rss"));
    assert_eq!(
        stack,
        vec![
            Value::Float(1.0),
            Value::Float(2.0),
            Value::Float(3.0),
            Value::Float(4.0),
        ]
    );
}

#[test]
fn rustscript_optional_chain_uses_declared_schema_and_handling_runs() {
    let rss_source = r#"
struct Inner { c: int }
struct Outer { b: Inner }

let present: Outer = { b: { c: 7 } };
let missing: Outer? = null;

present?.b?.c.unwrap_or(0);
missing?.b?.c.unwrap_or(0);
"#;
    assert_eq!(
        run_compiled_source(SourceFlavor::RustScript, rss_source),
        vec![Value::Int(7), Value::Int(0)]
    );
}

#[test]
fn rustscript_optional_chain_handles_declared_array_and_string_indexes() {
    let rss_source = r#"
struct Data {
    arr: [int],
    text: string,
}

let data: Data = { arr: [10, 20], text: "abc" };
data?.arr?.[1].unwrap_or(0);
data?.arr?.[2].unwrap_or(0);
data?.text?.[1].unwrap_or("");
data?.text?.[5].unwrap_or("");
"#;
    assert_eq!(
        run_compiled_source(SourceFlavor::RustScript, rss_source),
        vec![
            Value::Int(20),
            Value::Int(0),
            Value::string("b"),
            Value::string(""),
        ]
    );
}