Scripting Language
I wrote a scripting language in Rust that I use for all my game dev projects.
It’s statically typed but with type inference, has an incremental garbage collector, and compiles down to a bytecode I wrote so that it can be executed by a virtual machine I also wrote in Rust. This makes it super easy to hot swap out scripts in game without reloading the whole game, and also made it pretty easy to set up a read eval print loop. I’m not ready to release it publicly yet, but here’s a few snippets to show it off:
use dbg;
fn main() {
dbg::println("Hello, world!");
}
It’s expression based, if/else statements for example result in values that can be stored in variables or returned:
fn factorial(n: int) -> int {
if n > 1 {
n * factorial(n - 1)
} else {
n
}
}
It’s easy to interface with the language from Rust:
// Define a Rust marker type for a type that will be defined in a script
// so that we can pass it to Rust.
script_struct!(math::Point<T>);
fn main() {
// Create a new virtual machine to run the code in
let mut vm = Vm::new();
// Compile the geometry module from source
vm.load_string("geom", r"
pub struct Point::<T> {
pub x: T,
pub y: T,
}
").unwrap();
// Create an instance of `Point` in the script and pass it to Rust
let origin: Managed<Point<Ratio>> =
vm.repl("@Point::<ratio> { x: int, y: int }")
.unwrap().downcast(&vm).unwrap();
// ...now we're free to store the point, pass it back into other functions
// defined in the script, etc
}
And much more…
use dbg::println;
struct Vec2 {
x: ratio,
y: ratio,
}
// These vecs are passed in as immutable garbage collected references
fn dot(a: @Vec2, b: @Vec2) -> ratio {
a.x * b.x + a.y * b.y
}
// On the other hand, this vec is passed in as a mutable garbage collected
// reference
fn scale(v: @mut Vec2, s: ratio) {
a.x * b.x + a.y * b.y
}
fn main() {
let a = @mut Vec2 { x: 1.0, y: 2.0 };
let b = @mut Vec2 { x: 3.0, y: 4.0 };
let dot = dot(a, b); // If not specified, the types of locals are inferred
scale(a, dot);
// Support for sum types!
print_option_i32(Option::Some::<i32>(10));
}
enum Option::<T> {
Some(T),
None,
}
fn print_option_i32(n: Option::<i32>) {
match n {
Some(x) => println(f"The value is {x}!"),
None => println("no value :("),
}
}