aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCody <cody@codyq.dev>2023-04-14 00:04:46 -0500
committerCody <cody@codyq.dev>2023-04-14 00:04:46 -0500
commitfa0da150a5a481be3d1de448edb6f23c170da9a9 (patch)
tree42771a909fca18fb0fe7d5645a7f40b4d69107ef
parenta8b2a413a455fb06e0b10cf13d132c812f92bd4b (diff)
downloadsloth-fa0da150a5a481be3d1de448edb6f23c170da9a9.tar.gz
Improved VM. Started work on std
-rw-r--r--Cargo.lock73
-rw-r--r--crates/sloth_vm/Cargo.toml2
-rw-r--r--crates/sloth_vm/src/lib.rs1
-rw-r--r--crates/sloth_vm/src/native.rs17
-rw-r--r--crates/sloth_vm/src/sloth_std/mod.rs24
-rw-r--r--crates/sloth_vm/src/sloth_std/rand.rs39
-rw-r--r--crates/sloth_vm/src/sloth_std/stdio.rs51
-rw-r--r--crates/sloth_vm/src/value.rs2
-rw-r--r--crates/sloth_vm/src/vm.rs104
9 files changed, 304 insertions, 9 deletions
diff --git a/Cargo.lock b/Cargo.lock
index fa3bcfa..b7ccbe0 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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(