From d21f927a2418a66003dca0d2d98bd5032bc7c567 Mon Sep 17 00:00:00 2001 From: Mark Poliakov Date: Thu, 21 May 2026 14:55:03 +0300 Subject: [PATCH] Add eval in env + env/create, env/load-prelude --- Cargo.lock | 59 ++--------------------------------- Cargo.toml | 1 - examples/env.lysp | 16 ++++++++++ examples/repl.lysp | 2 +- src/main.rs | 9 +++--- src/read.rs | 6 ++-- src/vm/env.rs | 54 +++++++++++++++++++++++++++----- src/vm/machine.rs | 46 +++++++++++++++------------ src/vm/macros.rs | 4 +-- src/vm/prelude/collections.rs | 2 +- src/vm/prelude/convert.rs | 4 ++- src/vm/prelude/debug.rs | 4 ++- src/vm/prelude/eval.rs | 42 +++++++++++++++++++------ src/vm/prelude/functional.rs | 4 ++- src/vm/prelude/io.rs | 2 +- src/vm/prelude/math.rs | 41 ++++++++++++------------ src/vm/prelude/mod.rs | 4 ++- src/vm/value/convert.rs | 2 +- src/vm/value/identifier.rs | 8 ++++- src/vm/value/mod.rs | 10 ++++++ src/vm/value/native.rs | 10 ++++-- tests/integration.rs | 19 ++++++----- 22 files changed, 206 insertions(+), 143 deletions(-) create mode 100644 examples/env.lysp diff --git a/Cargo.lock b/Cargo.lock index 1ca0baa..8c11dad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,28 +52,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "bitmatch" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a53e105d41966c9b4594b8e3b7cf8e81ae63cc83664880b049af8a11381a3ad" -dependencies = [ - "boolean_expression", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "boolean_expression" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c33ef624481a2d2252fd352266c050e83203343d0884622f7ba09782abbfa83" -dependencies = [ - "itertools", - "smallvec", -] - [[package]] name = "clap" version = "4.6.1" @@ -105,7 +83,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -120,12 +98,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - [[package]] name = "heck" version = "0.5.0" @@ -138,20 +110,10 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" -[[package]] -name = "itertools" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" -dependencies = [ - "either", -] - [[package]] name = "lysp" version = "0.1.0" dependencies = [ - "bitmatch", "clap", "nom", "thiserror", @@ -196,29 +158,12 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "smallvec" -version = "1.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" - [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.117" @@ -247,7 +192,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e18d2c3..f5b0361 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,3 @@ edition = "2024" thiserror = "2.0.18" clap = { version = "4.6.1", features = ["derive"] } nom = "8.0.0" -bitmatch = "0.1.1" diff --git a/examples/env.lysp b/examples/env.lysp new file mode 100644 index 0000000..e01e0e5 --- /dev/null +++ b/examples/env.lysp @@ -0,0 +1,16 @@ +; empty env +(setq custom-env (env/create nil)) +(setq + custom-env-expr + '(progn + (defun my-function () 1234) + ) + ) + +(defun my-function () 4321) +(assert (= 'ok (car (eval custom-env custom-env-expr)))) +(assert (= 4321 (my-function))) +(assert (= '(ok 1234) (eval custom-env '(my-function)))) +(assert (= 'err (car (eval custom-env '(print 1234))))) ;; print is not defined in custom-env +(env/load-prelude custom-env) +(assert (= '(ok nil) (eval custom-env '(print 1234)))) diff --git a/examples/repl.lysp b/examples/repl.lysp index 6faadbb..adb8515 100644 --- a/examples/repl.lysp +++ b/examples/repl.lysp @@ -13,7 +13,7 @@ (print "==>" value) ) (defun repl-eval-print (expression) - (map-ok-err repl-print repl-eval-error (unquote (eval expression))) + (map-ok-err repl-print repl-eval-error (eval expression)) ) (defun repl-eval-error (error) diff --git a/src/main.rs b/src/main.rs index 552fc21..336d486 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use std::{ io::{self, BufReader}, path::{Path, PathBuf}, process::ExitCode, + rc::Rc, str::FromStr, }; @@ -124,7 +125,7 @@ fn handle_module_error(input: Either>) -> Error fn eval( options: &CompileOptions, vm: &mut Machine, - env: &Environment, + env: &Rc, value: Value, ) -> Option { let result = vm.evaluate_value( @@ -145,7 +146,7 @@ fn eval( fn run_interactive( compile_options: &CompileOptions, vm: &mut Machine, - env: &Environment, + env: &Rc, ) -> Result<(), Error> { let mut reader = InteractiveReader::new("> ", ">> "); @@ -171,7 +172,7 @@ fn run_interactive( fn run_module>( compile_options: &CompileOptions, vm: &mut Machine, - env: &Environment, + env: &Rc, path: P, ) -> Result<(), Error> { let path = path.as_ref(); @@ -202,7 +203,7 @@ fn main() -> ExitCode { vm.trace_calls = args.trace.contains(&Trace::Call); vm.trace_returns = args.trace.contains(&Trace::Return); vm.trace_stack = args.trace.contains(&Trace::Stack); - let env = Environment::default(); + let env = Rc::new(Environment::default()); prelude::load(&env); let mut arguments = vec![]; if let Some(script) = args.module.as_ref() { diff --git a/src/read.rs b/src/read.rs index f30ff13..fd337ec 100644 --- a/src/read.rs +++ b/src/read.rs @@ -104,7 +104,7 @@ impl ModuleReader { pub fn read_expression( &mut self, options: &CompileOptions, - env: &Environment, + env: &Rc, ) -> Result>, Either>> { loop { let value = @@ -127,7 +127,7 @@ impl ModuleReader { mut self, module_name: Option, options: &CompileOptions, - env: &Environment, + env: &Rc, ) -> Result, Either>> { let mut cx = CompileContext::new(options.clone(), module_name); let mut body = FunctionBody { @@ -249,7 +249,7 @@ fn read_inner( pub fn read( reader: &mut R, vm: &mut Machine, - env: &Environment, + env: &Rc, ) -> Result, MachineErrorAt> { let raw_value = reader.read().map_err(Into::into)?; let Some(raw_value) = raw_value else { diff --git a/src/vm/env.rs b/src/vm/env.rs index 25f9078..ed20746 100644 --- a/src/vm/env.rs +++ b/src/vm/env.rs @@ -1,20 +1,23 @@ -use std::{borrow::Borrow, cell::RefCell, collections::HashMap, hash::Hash, rc::Rc}; +use std::{borrow::Borrow, cell::RefCell, collections::HashMap, fmt, hash::Hash, rc::Rc}; use crate::{ error::MachineError, vm::{ machine::Machine, - value::{BytecodeFunction, IdentifierValue, NativeFunction, StringValue, Value}, + value::{ + BytecodeFunction, IdentifierValue, NativeFunction, NativeObject, StringValue, Value, + convert::{AnyFunction, TryFromValue}, + }, }, }; -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum Macro { Native(NativeFunction), Bytecode(Rc), } -#[derive(Default)] +#[derive(Default, Debug)] pub struct Environment { globals: RefCell>, macros: RefCell>, @@ -22,11 +25,18 @@ pub struct Environment { } impl Environment { + pub fn new(parent: Option>) -> Self { + Self { + parent, + ..Default::default() + } + } + pub fn defun_native(&self, identifier: S, docstring: D, function: F) -> Value where S: Into, D: Into, - F: Fn(&mut Machine, &Environment, &[Value]) -> Result + 'static, + F: Fn(&mut Machine, &Rc, &[Value]) -> Result + 'static, { let identifier = identifier.into(); let native = NativeFunction::new(identifier.clone(), docstring, function); @@ -39,7 +49,7 @@ impl Environment { where S: Into, D: Into, - F: Fn(&mut Machine, &Environment, &[Value]) -> Result + 'static, + F: Fn(&mut Machine, &Rc, &[Value]) -> Result + 'static, { let identifier = identifier.into(); let native = NativeFunction::new(identifier.clone(), docstring, function); @@ -63,7 +73,7 @@ impl Environment { pub fn global_value(&self, identifier: &Q) -> Option where IdentifierValue: Borrow, - Q: Hash + Eq, + Q: Hash + Eq + ?Sized, { self.globals.borrow().get(identifier).cloned().or_else(|| { self.parent @@ -93,4 +103,34 @@ impl Environment { .and_then(|parent| parent.global_macro(identifier)) }) } + + pub fn evaluate_global_function( + self: &Rc, + vm: &mut Machine, + identifier: &Q, + arguments: &[Value], + ) -> Result + where + IdentifierValue: Borrow, + Q: Hash + Eq + ?Sized, + for<'a> &'a Q: Into, + { + let value = self + .global_value(identifier) + .ok_or_else(|| MachineError::UnboundIdentifier(identifier.into()))?; + let function = AnyFunction::try_from_value(&value)?; + function.invoke(vm, self, arguments) + } +} + +impl fmt::Display for Environment { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "environment") + } +} + +impl NativeObject for Environment { + fn as_any(&self) -> &dyn std::any::Any { + self + } } diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 93638ff..f3120ea 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::{ compile::{CompileContext, CompileOptions}, error::{ @@ -150,7 +152,7 @@ impl Machine { Ok(()) } - fn execute_get_global(&mut self, env: &Environment) -> Result<(), MachineError> { + fn execute_get_global(&mut self, env: &Rc) -> Result<(), MachineError> { let identifier = self.pop()?; let Value::Identifier(identifier) = identifier else { return Err(MachineError::InvalidInstructionArgument( @@ -168,7 +170,7 @@ impl Machine { Ok(()) } - fn execute_set_global(&mut self, env: &Environment) -> Result<(), MachineError> { + fn execute_set_global(&mut self, env: &Rc) -> Result<(), MachineError> { let identifier = self.pop()?; let Value::Identifier(identifier) = identifier else { return Err(MachineError::InvalidInstructionArgument( @@ -186,7 +188,7 @@ impl Machine { fn execute_call( &mut self, - env: &Environment, + env: &Rc, argument_count: usize, ) -> Result<(), MachineError> { let base_pointer = self @@ -380,7 +382,7 @@ impl Machine { Ok(()) } - fn execute_declare_macro(&mut self, env: &Environment) -> Result<(), MachineError> { + fn execute_declare_macro(&mut self, env: &Rc) -> Result<(), MachineError> { let identifier = self.pop()?; let function = self.pop()?; let Value::Identifier(identifier) = identifier else { @@ -448,7 +450,7 @@ impl Machine { frame.closure.function.disassemble(frame.ip, 0, 0, false); } - fn execute_next(&mut self, env: &Environment) -> Result<(), MachineError> { + fn execute_next(&mut self, env: &Rc) -> Result<(), MachineError> { if self.trace_instructions { if self.trace_stack { self.trace_stack(); @@ -571,7 +573,7 @@ impl Machine { pub fn evaluate_closure( &mut self, - env: &Environment, + env: &Rc, closure: ClosureValue, argument_count: usize, ) -> Result { @@ -593,7 +595,7 @@ impl Machine { pub fn evaluate_closure_args( &mut self, - env: &Environment, + env: &Rc, closure: ClosureValue, args: &[Value], ) -> Result { @@ -635,7 +637,7 @@ impl Machine { &mut self, compile_options: CompileOptions, chunk_name: Option, - env: &Environment, + env: &Rc, value: Value, ) -> Result { let value = value.macro_expand(self, env, false)?; @@ -669,7 +671,7 @@ mod tests { }; fn try_eval( - env: &Environment, + env: &Rc, mut instructions: Vec, constants: Vec, ) -> (Machine, Result) { @@ -691,7 +693,11 @@ mod tests { (machine, result) } - fn eval0(env: &Environment, instructions: Vec, constants: Vec) -> (Machine, Value) { + fn eval0( + env: &Rc, + instructions: Vec, + constants: Vec, + ) -> (Machine, Value) { let (machine, value) = try_eval(env, instructions, constants); let value = value.expect("evaluation failed"); (machine, value) @@ -699,16 +705,16 @@ mod tests { #[test] fn test_stack_execution() { - let mut env = Environment::default(); - let (m, v) = eval0(&mut env, vec![Instruction::PushNil.into()], vec![]); + let env = Rc::new(Environment::default()); + let (m, v) = eval0(&env, vec![Instruction::PushNil.into()], vec![]); assert_eq!(v, Value::Nil); assert!(m.data_stack.is_empty()); assert!(m.call_stack.is_empty()); - let (m, v) = eval0(&mut env, vec![Instruction::PushTrue.into()], vec![]); + let (m, v) = eval0(&env, vec![Instruction::PushTrue.into()], vec![]); assert_eq!(v, true.into()); assert!(m.data_stack.is_empty()); assert!(m.call_stack.is_empty()); - let (m, v) = eval0(&mut env, vec![Instruction::PushFalse.into()], vec![]); + let (m, v) = eval0(&env, vec![Instruction::PushFalse.into()], vec![]); assert_eq!(v, false.into()); assert!(m.data_stack.is_empty()); assert!(m.call_stack.is_empty()); @@ -716,9 +722,9 @@ mod tests { #[test] fn test_unwind() { - let mut env = Environment::default(); + let env = Rc::new(Environment::default()); // Cause data stack underflow for unwind - let (m, r) = try_eval(&mut env, vec![Instruction::Drop.into()], vec![]); + let (m, r) = try_eval(&env, vec![Instruction::Drop.into()], vec![]); let e = r.unwrap_err(); assert_eq!(e.location.map(|a| a.offset), Some(1)); assert_eq!(e.error, MachineError::DataStackUnderflow); @@ -728,7 +734,7 @@ mod tests { #[test] fn test_closure_call_no_upvalues_yes_locals() { - let mut env = Environment::default(); + let env = Rc::new(Environment::default()); // (lambda (y) (let (x 123) (+ x y))) let lambda_function = Rc::new(BytecodeFunction { identifier: None, @@ -756,7 +762,7 @@ mod tests { max_arity: 1, }); let (m, r) = eval0( - &mut env, + &env, vec![ Instruction::PushConstant.into(), 0, @@ -776,7 +782,7 @@ mod tests { #[test] fn test_closure_call_no_upvalues_no_locals() { - let mut env = Environment::default(); + let env = Rc::new(Environment::default()); // (lambda (x y) (+ x y)) let lambda_function = Rc::new(BytecodeFunction { identifier: None, @@ -797,7 +803,7 @@ mod tests { max_arity: 2, }); let (m, r) = eval0( - &mut env, + &env, vec![ Instruction::PushConstant.into(), 0, diff --git a/src/vm/macros.rs b/src/vm/macros.rs index 9bdecb7..959c353 100644 --- a/src/vm/macros.rs +++ b/src/vm/macros.rs @@ -13,7 +13,7 @@ pub trait MacroExpand: Sized { fn macro_expand( &self, vm: &mut Machine, - env: &Environment, + env: &Rc, tail: bool, ) -> Result; } @@ -22,7 +22,7 @@ impl MacroExpand for Value { fn macro_expand( &self, vm: &mut Machine, - env: &Environment, + env: &Rc, tail: bool, ) -> Result { match self { diff --git a/src/vm/prelude/collections.rs b/src/vm/prelude/collections.rs index 27752b2..58b4498 100644 --- a/src/vm/prelude/collections.rs +++ b/src/vm/prelude/collections.rs @@ -9,7 +9,7 @@ use crate::{ }, }; -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { // // vectors // env.defun_native("getv", |vm, _, args| { // let [vec, index] = args else { diff --git a/src/vm/prelude/convert.rs b/src/vm/prelude/convert.rs index 8ed729d..b682a88 100644 --- a/src/vm/prelude/convert.rs +++ b/src/vm/prelude/convert.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::{ error::MachineError, vm::{ @@ -7,7 +9,7 @@ use crate::{ }, }; -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { env.defun_native( "->string", "Converts a value to string representation", diff --git a/src/vm/prelude/debug.rs b/src/vm/prelude/debug.rs index da9024f..105bb90 100644 --- a/src/vm/prelude/debug.rs +++ b/src/vm/prelude/debug.rs @@ -1,9 +1,11 @@ +use std::rc::Rc; + use crate::{ error::MachineError, vm::{Value, env::Environment, value::Keyword}, }; -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { env.defun_native( "explain", "Provides an explanation for a given value", diff --git a/src/vm/prelude/eval.rs b/src/vm/prelude/eval.rs index 6404472..880ffef 100644 --- a/src/vm/prelude/eval.rs +++ b/src/vm/prelude/eval.rs @@ -1,26 +1,50 @@ +use std::rc::Rc; + use crate::{ error::MachineError, read::{self, InteractiveReader}, - vm::{Value, env::Environment}, + vm::{Value, env::Environment, value::NativeObject}, }; -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { env.defun_native("eval", "Evaluates the given expression", |vm, env, args| { - // TODO eval in env - let [value] = args else { - return Err(MachineError::InvalidArgumentCount); + let (env, value) = match args { + [value] => (env.clone(), value), + [env, value] => (env.as_native()?, value), + _ => return Err(MachineError::InvalidArgumentCount), }; let outcome = - match vm.evaluate_value(Default::default(), Some("eval".into()), env, value.clone()) { - Ok(result) => Value::list_or_nil([Value::Identifier("ok".into()), result]).quote(), + match vm.evaluate_value(Default::default(), Some("eval".into()), &env, value.clone()) { + Ok(result) => Value::list_or_nil([Value::Identifier("ok".into()), result]), Err(error) => Value::list_or_nil([ Value::Identifier("err".into()), Value::String(format!("{error}").into()), - ]) - .quote(), + ]), }; Ok(outcome) }); + env.defun_native("env/create", "Create a new environment", |_, env, args| { + let parent = match args { + [] => Some(env.clone()), + [env] if env.is_nil() => None, + [env] => Some(env.as_native()?), + _ => return Err(MachineError::InvalidArgumentCount), + }; + let environment: Rc = Rc::new(Environment::new(parent)); + Ok(Value::NativeValue(environment.into())) + }); + env.defun_native( + "env/load-prelude", + "Adds prelude definitions to the environment", + |_, _, args| { + let [env] = args else { + return Err(MachineError::InvalidArgumentCount); + }; + let env = env.as_native::()?; + super::load(&env); + Ok(Value::Nil) + }, + ); env.defun_native( "read", "Reads a form from standard input", diff --git a/src/vm/prelude/functional.rs b/src/vm/prelude/functional.rs index 1ae1311..0a6122a 100644 --- a/src/vm/prelude/functional.rs +++ b/src/vm/prelude/functional.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::{ error::{MachineError, ValueConversionError}, vm::{ @@ -6,7 +8,7 @@ use crate::{ }, }; -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { // env.defun_native("map", |vm, env, args| { // let [f, xs] = args else { // return Err(vm.error_at_ip(MachineErrorKind::InvalidArgument)); diff --git a/src/vm/prelude/io.rs b/src/vm/prelude/io.rs index a09e673..12d19a2 100644 --- a/src/vm/prelude/io.rs +++ b/src/vm/prelude/io.rs @@ -76,7 +76,7 @@ impl Stream { } } -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { let stdin: Rc = Rc::new(Stream::Stdin); let stdout: Rc = Rc::new(Stream::Stdout); diff --git a/src/vm/prelude/math.rs b/src/vm/prelude/math.rs index 3e123d0..41c12f0 100644 --- a/src/vm/prelude/math.rs +++ b/src/vm/prelude/math.rs @@ -1,4 +1,4 @@ -use std::{cmp::Ordering, ops::Mul}; +use std::{cmp::Ordering, ops::Mul, rc::Rc}; use crate::{ error::MachineError, @@ -10,9 +10,10 @@ use crate::{ }, }; +#[allow(clippy::type_complexity)] pub(crate) fn dispatch_arithmetic( instruction: Instruction, -) -> fn(&mut Machine, &Environment, &[Value]) -> Result { +) -> fn(&mut Machine, &Rc, &[Value]) -> Result { match instruction { Instruction::Add => builtin_add, Instruction::Sub => builtin_sub, @@ -30,7 +31,7 @@ pub(crate) fn dispatch_arithmetic( } } -pub(super) fn load(env: &Environment) { +pub(super) fn load(env: &Rc) { env.defun_native("+", "Adds values", builtin_add); env.defun_native("-", "Subtracts values", builtin_sub); let mul = env.defun_native("*", "Multiplies values", builtin_mul); @@ -137,14 +138,14 @@ where pub(crate) fn builtin_add( _vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_fold(value_add, Value::Number(0.into()), args) } pub(crate) fn builtin_sub( _vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { match args { @@ -165,14 +166,14 @@ pub(crate) fn builtin_sub( } pub(crate) fn builtin_mul( _vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_fold_t(Mul::mul, NumberValue::from(1), args) } pub(crate) fn builtin_mod( _vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { let [x, y] = args else { @@ -184,7 +185,7 @@ pub(crate) fn builtin_mod( } pub(crate) fn builtin_div( _vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { match args { @@ -206,35 +207,35 @@ pub(crate) fn builtin_div( // pub(crate) fn builtin_and( // vm: &mut Machine, -// _env: &Environment, +// _env: &Rc, // args: &[Value], // ) -> Result { // builtin_fold_t(BitAnd::bitand, true, args) // } // pub(crate) fn builtin_or( // vm: &mut Machine, -// _env: &Environment, +// _env: &Rc, // args: &[Value], // ) -> Result { // builtin_fold_t(BitOr::bitor, false, args) // } // pub(crate) fn builtin_bitwise_and( // vm: &mut Machine, -// _env: &Environment, +// _env: &Rc, // args: &[Value], // ) -> Result { // builtin_fold_t(BitAnd::bitand, 1, args) // } // pub(crate) fn builtin_bitwise_or( // vm: &mut Machine, -// _env: &Environment, +// _env: &Rc, // args: &[Value], // ) -> Result { // builtin_fold_t(BitOr::bitor, 0, args) // } // pub(crate) fn builtin_bitwise_xor( // vm: &mut Machine, -// _env: &Environment, +// _env: &Rc, // args: &[Value], // ) -> Result { // builtin_fold_t(BitXor::bitxor, 0, args) @@ -296,42 +297,42 @@ pub(crate) fn builtin_cmp( } pub(crate) fn builtin_cmp_eq( vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_cmp(vm, args, CompareOperation::Eq) } pub(crate) fn builtin_cmp_ne( vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_cmp(vm, args, CompareOperation::Ne) } pub(crate) fn builtin_cmp_gt( vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_cmp(vm, args, CompareOperation::Gt) } pub(crate) fn builtin_cmp_lt( vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_cmp(vm, args, CompareOperation::Lt) } pub(crate) fn builtin_cmp_ge( vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_cmp(vm, args, CompareOperation::Ge) } pub(crate) fn builtin_cmp_le( vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { builtin_cmp(vm, args, CompareOperation::Le) @@ -339,7 +340,7 @@ pub(crate) fn builtin_cmp_le( pub(crate) fn builtin_not( _vm: &mut Machine, - _env: &Environment, + _env: &Rc, args: &[Value], ) -> Result { let [x] = args else { diff --git a/src/vm/prelude/mod.rs b/src/vm/prelude/mod.rs index af9264f..1180279 100644 --- a/src/vm/prelude/mod.rs +++ b/src/vm/prelude/mod.rs @@ -1,3 +1,5 @@ +use std::rc::Rc; + use crate::vm::env::Environment; mod collections; @@ -10,7 +12,7 @@ mod math; pub(crate) use math::*; -pub fn load(env: &Environment) { +pub fn load(env: &Rc) { math::load(env); eval::load(env); functional::load(env); diff --git a/src/vm/value/convert.rs b/src/vm/value/convert.rs index 651d2ba..6e98f4a 100644 --- a/src/vm/value/convert.rs +++ b/src/vm/value/convert.rs @@ -144,7 +144,7 @@ impl AnyFunction { pub fn invoke( &self, vm: &mut Machine, - env: &Environment, + env: &Rc, args: &[Value], ) -> Result { match self { diff --git a/src/vm/value/identifier.rs b/src/vm/value/identifier.rs index af7442a..177663d 100644 --- a/src/vm/value/identifier.rs +++ b/src/vm/value/identifier.rs @@ -1,4 +1,4 @@ -use std::{fmt, rc::Rc}; +use std::{borrow::Borrow, fmt, rc::Rc}; #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct IdentifierValue(Rc); @@ -21,6 +21,12 @@ impl AsRef for IdentifierValue { } } +impl Borrow for IdentifierValue { + fn borrow(&self) -> &str { + self.as_ref() + } +} + impl fmt::Display for IdentifierValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) diff --git a/src/vm/value/mod.rs b/src/vm/value/mod.rs index 8f739df..820174f 100644 --- a/src/vm/value/mod.rs +++ b/src/vm/value/mod.rs @@ -188,6 +188,16 @@ impl Value { }), } } + + pub fn as_native_ref(&self) -> Result<&T, ValueConversionError> { + match self { + Self::NativeValue(value) if let Some(value) = value.cast_ref() => Ok(value), + _ => Err(ValueConversionError { + expected: "native value".into(), + got: self.clone(), + }), + } + } } impl fmt::Display for Value { diff --git a/src/vm/value/native.rs b/src/vm/value/native.rs index c88d527..f441850 100644 --- a/src/vm/value/native.rs +++ b/src/vm/value/native.rs @@ -22,7 +22,7 @@ pub trait NativeObject: Any + fmt::Debug + fmt::Display { pub struct NativeValue(Rc); pub type NativeFunctionImpl = - Rc Result + 'static>; + Rc, &[Value]) -> Result + 'static>; #[derive(Clone)] pub struct NativeFunction { @@ -36,7 +36,7 @@ impl NativeFunction { where S: Into, D: Into, - F: Fn(&mut Machine, &Environment, &[Value]) -> Result + 'static, + F: Fn(&mut Machine, &Rc, &[Value]) -> Result + 'static, { Self { name: name.into(), @@ -56,7 +56,7 @@ impl NativeFunction { pub fn invoke( &self, machine: &mut Machine, - environment: &Environment, + environment: &Rc, arguments: &[Value], ) -> Result { (self.inner)(machine, environment, arguments) @@ -102,6 +102,10 @@ impl NativeValue { pub fn cast_rc(&self) -> Option> { Rc::downcast(self.0.clone()).ok() } + + pub fn cast_ref(&self) -> Option<&T> { + self.0.as_any().downcast_ref() + } } impl From> for NativeValue { diff --git a/tests/integration.rs b/tests/integration.rs index 0c39706..2dc5e86 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -1,4 +1,7 @@ -use std::io::{self, BufReader, Read}; +use std::{ + io::{self, BufReader, Read}, + rc::Rc, +}; use lysp::{ error::MachineErrorAt, @@ -18,7 +21,7 @@ impl Read for SliceReader<'_> { } #[track_caller] -fn eval_str_in(code: &str, env: &Environment) -> Result { +fn eval_str_in(code: &str, env: &Rc) -> Result { let mut machine = Machine::default(); let reader = BufReader::new(SliceReader(code.as_bytes())); let mut reader = FileReader::new(reader); @@ -39,16 +42,16 @@ fn eval_str_in(code: &str, env: &Environment) -> Result { #[track_caller] fn eval_str(code: &str) -> Value { - let mut env = Environment::default(); - prelude::load(&mut env); - eval_str_in(code, &mut env).expect("expression evaluation failed") + let env = Rc::new(Environment::default()); + prelude::load(&env); + eval_str_in(code, &env).expect("expression evaluation failed") } #[track_caller] fn eval_str_err(code: &str) -> MachineErrorAt { - let mut env = Environment::default(); - prelude::load(&mut env); - eval_str_in(code, &mut env).expect_err("expression was expected to fail") + let env = Rc::new(Environment::default()); + prelude::load(&env); + eval_str_in(code, &env).expect_err("expression was expected to fail") } #[test]