1 unstable release

new 0.2.0 May 21, 2025

#839 in Math


Used in 2 crates

MIT and LGPL-3.0+

680KB
14K SLoC

Virtual machine for cas-rs.

This crate provides [Vm], a virtual machine that executes bytecode instructions generated by the CalcScript Compiler. It maintains its own state and allows you to inspect and manipulate its variables over the course of program execution.

Usage

To run a program, you can easily create a [Vm] by compiling a program with Vm::compile_program, then calling Vm::run to execute it. It returns a Value that contains the return value of the program.

use cas_compute::numerical::value::Value;
use cas_parser::parser::Parser;
use cas_vm::Vm;

let source = "5sin(pi/2) * 6!";
let mut parser = Parser::new(source);
let stmts = parser.try_parse_full_many().unwrap();

let mut vm = Vm::compile_program(stmts).unwrap();

use cas_compute::primitive::float;
// 5sin(pi/2) * 6!
// = 5 * 1 * 720 = 3600
assert_eq!(vm.run().unwrap(), 3600.into());

It is also possible to programatically manipulate the [Vm]'s variables, although this will take some work. You'll have to manually declare the variable in the compilation phase to obtain an index for it, and then use that index to set the variable in the [Vm]. Here's an example of how to do that:

use cas_compiler::Compiler;
use cas_compiler::expr::compile_stmts;
use cas_parser::parser::ast::LitSym;
use cas_parser::parser::Parser;
use cas_vm::Vm;

let source = "x^2 + 5x + 6";
let mut parser = Parser::new(source);
let stmts = parser.try_parse_full_many().unwrap();

let mut compiler = Compiler::new();

// declare existence of `x` in the compiler (or a compilation error will occur)
// while also obtaining an index for `x` in the compiler
let x_id = compiler.add_symbol(&LitSym {
    name: String::from("x"),
    span: 0..0
}).unwrap();

// `x` must be initialized before we execute the vm (or a runtime error will occur)!

// helper function to compile the statements in place of `Compiler::compile_program`
compile_stmts(&stmts, &mut compiler).unwrap();

// create the virtual machine from the compiler and run test cases
let mut vm = Vm::from(compiler);

let cases = [
    // (x, expected)
    (2, 20),
    (5, 56),
    (9, 132),
    (14, 272),
    (35, 1406),
    (256, 66822),
];

for (x, expected) in cases.into_iter() {
    // set the value of `x` in the vm
    vm.variables.insert(x_id, x.into());

    // ensure `x^2 + 5x + 6` is equal to `expected`
    let result = vm.run().unwrap();
    assert_eq!(result, expected.into());
}

This process will likely be simplified in the future.

Dependencies

~7MB
~97K SLoC