diff options
| author | Cody <cody@codyq.dev> | 2023-04-14 00:04:46 -0500 |
|---|---|---|
| committer | Cody <cody@codyq.dev> | 2023-04-14 00:04:46 -0500 |
| commit | fa0da150a5a481be3d1de448edb6f23c170da9a9 (patch) | |
| tree | 42771a909fca18fb0fe7d5645a7f40b4d69107ef | |
| parent | a8b2a413a455fb06e0b10cf13d132c812f92bd4b (diff) | |
| download | sloth-fa0da150a5a481be3d1de448edb6f23c170da9a9.tar.gz | |
Improved VM. Started work on std
| -rw-r--r-- | Cargo.lock | 73 | ||||
| -rw-r--r-- | crates/sloth_vm/Cargo.toml | 2 | ||||
| -rw-r--r-- | crates/sloth_vm/src/lib.rs | 1 | ||||
| -rw-r--r-- | crates/sloth_vm/src/native.rs | 17 | ||||
| -rw-r--r-- | crates/sloth_vm/src/sloth_std/mod.rs | 24 | ||||
| -rw-r--r-- | crates/sloth_vm/src/sloth_std/rand.rs | 39 | ||||
| -rw-r--r-- | crates/sloth_vm/src/sloth_std/stdio.rs | 51 | ||||
| -rw-r--r-- | crates/sloth_vm/src/value.rs | 2 | ||||
| -rw-r--r-- | crates/sloth_vm/src/vm.rs | 104 |
9 files changed, 304 insertions, 9 deletions
@@ -9,12 +9,29 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] name = "either" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] +name = "getrandom" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -24,6 +41,24 @@ dependencies = [ ] [[package]] +name = "libc" +version = "0.2.141" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] name = "proc-macro2" version = "1.0.54" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -42,6 +77,36 @@ dependencies = [ ] [[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] name = "sloth" version = "0.1.0" dependencies = [ @@ -60,6 +125,8 @@ dependencies = [ name = "sloth_vm" version = "0.1.0" dependencies = [ + "once_cell", + "rand", "sloth_bytecode", ] @@ -99,3 +166,9 @@ name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/crates/sloth_vm/Cargo.toml b/crates/sloth_vm/Cargo.toml index 1b1b6fe..f484402 100644 --- a/crates/sloth_vm/Cargo.toml +++ b/crates/sloth_vm/Cargo.toml @@ -8,3 +8,5 @@ edition.workspace = true [dependencies] sloth_bytecode = { path = "../sloth_bytecode" } +once_cell = "1.17.1" +rand = "0.8.5" diff --git a/crates/sloth_vm/src/lib.rs b/crates/sloth_vm/src/lib.rs index 121ea6a..295827d 100644 --- a/crates/sloth_vm/src/lib.rs +++ b/crates/sloth_vm/src/lib.rs @@ -8,6 +8,7 @@ )] pub mod native; +pub mod sloth_std; pub mod value; pub mod vm; diff --git a/crates/sloth_vm/src/native.rs b/crates/sloth_vm/src/native.rs index 8b13789..8790cbd 100644 --- a/crates/sloth_vm/src/native.rs +++ b/crates/sloth_vm/src/native.rs @@ -1 +1,18 @@ +use crate::value::Primitive; +use crate::VM; +pub type NativeFunctionResult = Result<Primitive, Error>; +pub type NativeFunctionInput = fn(&mut VM, &[Primitive]) -> NativeFunctionResult; + +pub enum Error { + InvalidArgument, + Unknown(String), +} + +#[allow(clippy::type_complexity)] +pub struct NativeFunction { + pub name: &'static str, + pub function: NativeFunctionInput, + pub arity: u8, + pub returns_value: bool, +} diff --git a/crates/sloth_vm/src/sloth_std/mod.rs b/crates/sloth_vm/src/sloth_std/mod.rs new file mode 100644 index 0000000..86611d7 --- /dev/null +++ b/crates/sloth_vm/src/sloth_std/mod.rs @@ -0,0 +1,24 @@ +use std::collections::HashMap; + +use once_cell::sync::Lazy; + +use crate::native::NativeFunction; + +pub mod rand; +pub mod stdio; + +pub static NATIVE_LIBRARY: Lazy<HashMap<&'static str, NativeFunction>> = Lazy::new(|| { + let mut map = HashMap::new(); + + // rand + map.insert("rand$gen", rand::GEN_FUNCTION); + map.insert("rand$gen_range", rand::GEN_RANGE_FUNCTION); + + // stdio + map.insert("write", stdio::WRITE_FUNCTION); + map.insert("read", stdio::READ_FUNCTION); + + // filesystem + + map +}); diff --git a/crates/sloth_vm/src/sloth_std/rand.rs b/crates/sloth_vm/src/sloth_std/rand.rs new file mode 100644 index 0000000..bae0606 --- /dev/null +++ b/crates/sloth_vm/src/sloth_std/rand.rs @@ -0,0 +1,39 @@ +use rand::Rng; + +use crate::native::{self, NativeFunction, NativeFunctionResult}; +use crate::value::Primitive; +use crate::value::Primitive::{Float, Integer}; +use crate::VM; + +fn gen(_vm: &mut VM, _args: &[Primitive]) -> NativeFunctionResult { + let value = rand::thread_rng().gen_range(0.0..1.0); + + Ok(Float(value)) +} + +pub const GEN_FUNCTION: NativeFunction = NativeFunction { + name: "rand$gen", + function: gen, + arity: 0, + returns_value: true, +}; + +fn gen_range(_vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult { + let min = args.get(0).cloned(); + let max = args.get(1).cloned(); + + let (Some(Integer(min)), Some(Integer(max))) = (min, max) else { + return Err(native::Error::InvalidArgument); + }; + + let value = rand::thread_rng().gen_range(min..max); + + Ok(Integer(value)) +} + +pub const GEN_RANGE_FUNCTION: NativeFunction = NativeFunction { + name: "rand$gen_range", + function: gen_range, + arity: 2, + returns_value: true, +}; diff --git a/crates/sloth_vm/src/sloth_std/stdio.rs b/crates/sloth_vm/src/sloth_std/stdio.rs new file mode 100644 index 0000000..a743ad1 --- /dev/null +++ b/crates/sloth_vm/src/sloth_std/stdio.rs @@ -0,0 +1,51 @@ +use std::io::{stdin, BufRead}; + +use crate::native::{self, NativeFunction, NativeFunctionResult}; +use crate::value::{Object, ObjectType, Primitive}; +use crate::VM; + +fn write(vm: &mut VM, args: &[Primitive]) -> NativeFunctionResult { + let Some(Primitive::Object(ptr)) = args.get(0).cloned() else { + return Err(native::Error::InvalidArgument); + }; + + let object = vm + .objects() + .get(ptr as usize) + .ok_or(native::Error::InvalidArgument)?; + + let ObjectType::String(str) = &object.typ else { + return Err(native::Error::InvalidArgument); + }; + + println!("{str}"); + + Ok(Primitive::Empty) +} + +pub const WRITE_FUNCTION: NativeFunction = NativeFunction { + name: "write", + function: write, + arity: 1, + returns_value: false, +}; + +fn read(vm: &mut VM, _args: &[Primitive]) -> NativeFunctionResult { + let mut line = String::new(); + stdin() + .lock() + .read_line(&mut line) + .map_err(|it| native::Error::Unknown(it.to_string()))?; + + let object = Object::new(ObjectType::String(line)); + let ptr = vm.objects_mut().allocate(object); + + Ok(Primitive::Object(ptr as u32)) +} + +pub const READ_FUNCTION: NativeFunction = NativeFunction { + name: "read", + function: read, + arity: 0, + returns_value: true, +}; diff --git a/crates/sloth_vm/src/value.rs b/crates/sloth_vm/src/value.rs index 9862326..f149c0e 100644 --- a/crates/sloth_vm/src/value.rs +++ b/crates/sloth_vm/src/value.rs @@ -1,3 +1,4 @@ +use crate::native::NativeFunction; use crate::Chunk; #[derive(Debug, Clone, Copy, PartialEq)] @@ -28,6 +29,7 @@ pub enum ObjectType { List(Vec<Primitive>), Function(Function), + NativeFunction(NativeFunction), Free { next: usize }, } diff --git a/crates/sloth_vm/src/vm.rs b/crates/sloth_vm/src/vm.rs index 300e22e..2d58c3d 100644 --- a/crates/sloth_vm/src/vm.rs +++ b/crates/sloth_vm/src/vm.rs @@ -3,7 +3,7 @@ use std::mem::MaybeUninit; use sloth_bytecode::Opcode; use crate::value::{Function, Object, ObjectType, Primitive}; -use crate::{ObjectMap, Stack}; +use crate::{native, ObjectMap, Stack}; #[derive(Clone, Copy)] pub struct CallFrame { @@ -238,18 +238,47 @@ impl VM { while self.step() {} } - fn call(&mut self, ptr: usize) { + pub fn call(&mut self, ptr: usize) { let Some(obj) = self.objects.get(ptr) else { panic!("Pointer referenced nothing"); }; - let ObjectType::Function(function) = &obj.typ else { - panic!("Object was not a function"); - }; + match &obj.typ { + ObjectType::Function(function) => { + // Add a callstack entry for the function + let offset = self.stack.top - (function.arity as usize); + self.call_stack.push(CallFrame::new(offset, function)); + } + ObjectType::NativeFunction(function) => { + let mut args = Vec::with_capacity(function.arity as usize); + for _ in 0..function.arity { + args.push(self.stack.pop()); + } - // Add a callstack entry for the function - let offset = self.stack.top - (function.arity as usize); - self.call_stack.push(CallFrame::new(offset, function)); + let name = function.name; + let returns_value = function.returns_value; + + let internal = function.function; + let result = internal(self, &args); + + match result { + Ok(value) => { + if returns_value { + self.stack.push(value); + } + } + Err(error) => match error { + native::Error::InvalidArgument => { + panic!("Invalid argument provided to '{name}'"); + } + native::Error::Unknown(msg) => { + panic!("Native function '{name}' failed due to '{msg}'"); + } + }, + } + } + _ => panic!("Object was not a function"), + } } fn call_return(&mut self) { @@ -295,12 +324,22 @@ impl VM { ((bytes.0 as u16) << 8) + (bytes.1 as u16) } + + #[inline(always)] + pub fn objects(&self) -> &ObjectMap { + &self.objects + } + + #[inline(always)] + pub fn objects_mut(&mut self) -> &mut ObjectMap { + &mut self.objects + } } #[cfg(test)] mod tests { use crate::value::{Function, Object, ObjectType, Primitive}; - use crate::{Chunk, ObjectMap, VM}; + use crate::{sloth_std, Chunk, ObjectMap, VM}; #[test] fn arithmetic_ops() { @@ -355,6 +394,8 @@ mod tests { Primitive::Integer(6), Primitive::Integer(3), Primitive::Object(0), + Primitive::Object(1), + Primitive::Object(2), ], code: vec![ 0x00, 0, 0, // Load first function parameter from 0 @@ -371,6 +412,51 @@ mod tests { } #[test] + fn native_function() { + let mut vm = VM::new( + ObjectMap::from(vec![ + Object::new(ObjectType::NativeFunction(sloth_std::rand::GEN_FUNCTION)), + Object::new(ObjectType::NativeFunction( + sloth_std::rand::GEN_RANGE_FUNCTION, + )), + ]), + Function::root(Chunk { + constants: vec![ + Primitive::Object(0), + Primitive::Object(1), + Primitive::Integer(5), + Primitive::Integer(10), + ], + code: vec![ + // First part + 0x00, 0, 0, // + 0x50, // + 0xE0, // + // Second part + 0x00, 0, 3, // + 0x00, 0, 2, // + 0x00, 0, 1, // + 0x50, // + ], + }), + ); + + vm.run(); + + assert!({ + let Primitive::Float(i) = vm.stack.peek() else { panic!(); }; + (0.0..=1.0).contains(&i) + }); + + vm.run(); + + assert!({ + let Primitive::Integer(i) = vm.stack.peek() else { panic!(); }; + (5..10).contains(&i) + }); + } + + #[test] fn fibonacci() { #[rustfmt::skip] let mut vm = VM::new( |
