diff --git a/examples/syntax.lysp b/examples/syntax.lysp index 26e4e92..d33f574 100644 --- a/examples/syntax.lysp +++ b/examples/syntax.lysp @@ -28,6 +28,9 @@ ;; vectors #[1 2 3] +; alternate syntax for lists +[] () nil NIL + ;; progn (progn 1 diff --git a/src/compile/block.rs b/src/compile/block.rs index 9b08f42..6b68a7e 100644 --- a/src/compile/block.rs +++ b/src/compile/block.rs @@ -27,13 +27,13 @@ pub struct FunctionBlock { parent: Option, identifier: Option, docstring: Option, + signature: FunctionSignature, // Data pub(crate) constants: Vec, locals: Vec, upvalues: Vec, scope_depth: usize, - arity: usize, // Code pub(crate) instructions: Vec, @@ -363,20 +363,25 @@ impl FunctionBlock { parent, identifier, docstring, + signature: signature.clone(), constants: vec![], locals: vec![], upvalues: vec![], scope_depth: 0, instructions: vec![], labels: vec![], - arity: signature.required_arguments.len(), loop_stack: vec![], }; - for required in signature.required_arguments.iter().rev() { + for required in signature.required_arguments.iter() { block .add_local(required.clone(), Some(-100)) .expect("couldn't add an argument"); } + for optional in signature.optional_arguments.iter() { + block + .add_local(optional.clone(), Some(-100)) + .expect("couldn't add an argument"); + } block } @@ -484,13 +489,17 @@ impl FunctionBlock { instructions[position] = branch_offset as u8; } + let min_arity = self.signature.min_arity(); + let max_arity = self.signature.max_arity(); + Ok(Rc::new(BytecodeFunction { identifier: self.identifier.clone(), instructions: instructions.into(), docstring: self.docstring.clone(), constants: self.constants.iter().cloned().collect(), upvalues: self.upvalues.iter().copied().collect(), - arity: self.arity, + min_arity, + max_arity, })) } diff --git a/src/compile/codegen/function.rs b/src/compile/codegen/function.rs index 02f3365..849b963 100644 --- a/src/compile/codegen/function.rs +++ b/src/compile/codegen/function.rs @@ -54,7 +54,7 @@ impl Compile for CallExpression { let callee = self.callee.compile(cx)?; cx.push(callee)?; } - for expression in self.arguments.iter().rev() { + for expression in self.arguments.iter() { let value = expression.compile(cx)?; cx.push(value)?; } diff --git a/src/compile/function.rs b/src/compile/function.rs index 1a33e89..ed0a3a5 100644 --- a/src/compile/function.rs +++ b/src/compile/function.rs @@ -38,7 +38,15 @@ impl FunctionSignature { } } - pub fn arity(&self) -> usize { + pub fn min_arity(&self) -> usize { self.required_arguments.len() } + + pub fn max_arity(&self) -> usize { + if self.rest_argument.is_none() { + self.required_arguments.len() + self.optional_arguments.len() + } else { + usize::MAX + } + } } diff --git a/src/main.rs b/src/main.rs index 0b9b408..9276e78 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,7 @@ pub enum Trace { Execute, Call, Return, + Stack, } impl FromStr for Trace { @@ -46,6 +47,7 @@ impl FromStr for Trace { "execute" => Ok(Self::Execute), "call" => Ok(Self::Call), "return" => Ok(Self::Return), + "stack" => Ok(Self::Stack), _ => Err(format!("Unknown trace flag: {s:?}")), } } @@ -199,6 +201,7 @@ fn main() -> ExitCode { vm.trace_instructions = args.trace.contains(&Trace::Execute); 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 mut env = Environment::default(); prelude::load(&mut env); let mut arguments = vec![]; diff --git a/src/vm/machine.rs b/src/vm/machine.rs index 6b47794..0ec9da4 100644 --- a/src/vm/machine.rs +++ b/src/vm/machine.rs @@ -203,9 +203,10 @@ impl Machine { Value::NativeFunction(function) => { let function = function.clone(); // TODO remove argument cloning - let arguments = (0..argument_count) + let mut arguments = (0..argument_count) .map(|_| self.pop()) .collect::, _>>()?; + arguments.reverse(); if self.trace_calls { eprintln!("TRACE: Call native"); @@ -240,9 +241,21 @@ impl Machine { } }; - if argument_count != closure.function.arity { - todo!("TODO error here") + if argument_count < closure.function.min_arity { + todo!("TODO function called with less arguments than expected") } + if argument_count > closure.function.max_arity { + todo!("TODO function called with more arguments than expected") + } + if closure.function.max_arity == usize::MAX { + todo!("VM support for &rest argument") + } + for _ in argument_count..closure.function.max_arity { + self.push(Value::Nil)?; + } + // if argument_count != closure.function.arity { + // todo!("TODO error here") + // } if self.trace_calls { eprintln!("TRACE: Call closure"); @@ -500,9 +513,10 @@ impl Machine { | Instruction::Not | Instruction::Negate => { let argument_count = usize::from(ArgumentCount::read_encoded(self)?); - let arguments = (0..argument_count) + let mut arguments = (0..argument_count) .map(|_| self.pop()) .collect::, _>>()?; + arguments.reverse(); let function = prelude::dispatch_arithmetic(opcode); let value = (function)(self, env, &arguments[..])?; self.push(value)?; @@ -579,11 +593,24 @@ impl Machine { closure: ClosureValue, args: &[Value], ) -> Result { + let max_arity = closure.function.max_arity; + if args.len() < closure.function.min_arity { + todo!() + } + if args.len() > closure.function.max_arity { + todo!() + } self.push(Value::Closure(closure)) .map_err(MachineErrorAt::at_unknown)?; - for arg in args.iter().rev() { + if max_arity == usize::MAX { + todo!("VM support for &rest argument") + } + for arg in args.iter() { self.push(arg.clone()).map_err(MachineErrorAt::at_unknown)?; } + for _ in args.len()..max_arity { + self.push(Value::Nil).map_err(MachineErrorAt::at_unknown)?; + } let unwind_target = self.call_stack.pointer(); self.execute_call(env, args.len()) .map_err(MachineErrorAt::at_unknown)?; diff --git a/src/vm/value/function.rs b/src/vm/value/function.rs index 833ada4..1aeef6b 100644 --- a/src/vm/value/function.rs +++ b/src/vm/value/function.rs @@ -16,7 +16,8 @@ pub struct BytecodeFunction { pub instructions: Box<[u8]>, pub constants: Box<[Value]>, pub upvalues: Box<[UpvalueDef]>, - pub arity: usize, + pub min_arity: usize, + pub max_arity: usize, } enum TraceArgument {