Add support for &optional arguments

This commit is contained in:
2026-05-21 09:52:11 +03:00
parent 43ff442d89
commit bf537cbeda
7 changed files with 63 additions and 12 deletions
+3
View File
@@ -28,6 +28,9 @@
;; vectors
#[1 2 3]
; alternate syntax for lists
[] () nil NIL
;; progn
(progn
1
+13 -4
View File
@@ -27,13 +27,13 @@ pub struct FunctionBlock {
parent: Option<usize>,
identifier: Option<IdentifierValue>,
docstring: Option<StringValue>,
signature: FunctionSignature,
// Data
pub(crate) constants: Vec<Value>,
locals: Vec<Local>,
upvalues: Vec<UpvalueDef>,
scope_depth: usize,
arity: usize,
// Code
pub(crate) instructions: Vec<Emitted>,
@@ -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,
}))
}
+1 -1
View File
@@ -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)?;
}
+9 -1
View File
@@ -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
}
}
}
+3
View File
@@ -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![];
+32 -5
View File
@@ -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::<Result<Vec<_>, _>>()?;
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::<Result<Vec<_>, _>>()?;
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<Value, MachineErrorAt> {
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)?;
+2 -1
View File
@@ -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 {