rudus/src/vm.rs
2025-06-26 20:30:40 -04:00

1149 lines
44 KiB
Rust

use crate::base::BaseFn;
use crate::chunk::Chunk;
use crate::op::Op;
use crate::parser::Ast;
use crate::spans::Spanned;
use crate::value::{LFn, Value};
use crate::world::Zoo;
use imbl::{HashMap, Vector};
use num_traits::FromPrimitive;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt;
use std::mem::swap;
use std::rc::Rc;
const MAX_REDUCTIONS: usize = 100;
#[derive(Debug, Clone, PartialEq)]
pub enum Panic {
Str(&'static str),
String(String),
}
impl fmt::Display for Panic {
fn fmt(self: &Panic, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Panic::Str(msg) => write!(f, "{msg}"),
Panic::String(msg) => write!(f, "{msg}"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Trace {
pub callee: Spanned<Ast>,
pub caller: Spanned<Ast>,
pub function: Value,
pub arguments: Value,
pub input: &'static str,
pub src: &'static str,
}
#[derive(Debug, Clone, PartialEq)]
pub struct CallFrame {
pub function: Value,
pub arity: u8,
pub stack_base: usize,
pub ip: usize,
}
impl CallFrame {
pub fn chunk(&self) -> &Chunk {
let Value::Fn(ref function) = self.function else {
unreachable!()
};
function.chunk(self.arity)
}
}
impl fmt::Display for CallFrame {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let Value::Fn(ref function) = self.function else {
unreachable!()
};
write!(
f,
"CallFrame: {}/{} @ {}",
function.name(),
self.arity,
self.ip
)
}
}
fn combine_bytes(high: u8, low: u8) -> usize {
let out = ((high as u16) << 8) + low as u16;
out as usize
}
const REGISTER_SIZE: usize = 8;
#[derive(Debug, Clone, PartialEq)]
pub struct Creature {
pub stack: Vec<Value>,
pub call_stack: Vec<CallFrame>,
pub frame: CallFrame,
pub ip: usize,
pub register: [Value; REGISTER_SIZE],
pub matches: bool,
pub match_depth: u8,
pub result: Option<Result<Value, Panic>>,
debug: bool,
last_code: usize,
pub id: &'static str,
pub mbx: VecDeque<Value>,
pub reductions: usize,
pub zoo: Rc<RefCell<Zoo>>,
pub r#yield: bool,
}
impl std::fmt::Display for Creature {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "Creature. {} @{}", self.id, self.ip)
}
}
impl Creature {
pub fn new(chunk: Chunk, zoo: Rc<RefCell<Zoo>>, debug: bool) -> Creature {
let lfn = LFn::Defined {
name: "user script",
doc: None,
chunks: vec![chunk],
arities: vec![0],
splat: 0,
closed: RefCell::new(vec![]),
};
let base_fn = Value::Fn(Rc::new(lfn));
Creature::spawn(base_fn, zoo, debug)
}
pub fn spawn(function: Value, zoo: Rc<RefCell<Zoo>>, debug: bool) -> Creature {
let base_frame = CallFrame {
function,
stack_base: 0,
ip: 0,
arity: 0,
};
Creature {
stack: vec![],
call_stack: Vec::with_capacity(64),
frame: base_frame,
ip: 0,
register: [const { Value::Nothing }; REGISTER_SIZE],
matches: false,
match_depth: 0,
result: None,
debug,
last_code: 0,
id: "",
zoo,
mbx: VecDeque::new(),
reductions: 0,
r#yield: false,
}
}
pub fn reduce(&mut self) {
self.reductions += 1;
}
pub fn reset_reductions(&mut self) {
self.reductions = 0;
self.r#yield = false;
}
pub fn receive(&mut self, value: Value) {
self.mbx.push_back(value);
}
pub fn chunk(&self) -> &Chunk {
self.frame.chunk()
}
pub fn push(&mut self, value: Value) {
self.stack.push(value);
}
pub fn pop(&mut self) -> Value {
self.stack.pop().unwrap()
}
pub fn peek(&self) -> &Value {
self.stack.last().unwrap()
}
pub fn print_stack(&self) {
let mut inner = vec![];
for (i, value) in self.stack.iter().enumerate() {
if i == self.frame.stack_base {
inner.push(format!("->{}<-", value.show()))
} else {
inner.push(value.show())
}
}
let inner = inner.join("|");
let register = self
.register
.iter()
.map(|val| val.to_string())
.collect::<Vec<_>>()
.join(",");
let mbx = self
.mbx
.iter()
.map(|val| val.show())
.collect::<Vec<_>>()
.join("/");
println!(
"{:04}: [{inner}] ({register}) {} {{{mbx}}}",
self.last_code, self.id
);
}
fn print_debug(&self) {
self.print_stack();
let mut ip = self.last_code;
self.chunk().dissasemble_instr(&mut ip);
}
// pub fn run(&mut self) -> &Result<Value, Panic> {
// while self.result.is_none() {
// self.interpret();
// }
// self.result.as_ref().unwrap()
// }
pub fn call_stack(&mut self) -> String {
let mut stack = format!(" calling {}", self.frame.function.show());
for frame in self.call_stack.iter().rev() {
let mut name = frame.function.show();
name = if name == "fn user script" {
"user script".to_string()
} else {
name
};
stack = format!("{stack}\n from {name}");
}
stack
}
pub fn panic(&mut self, msg: &'static str) {
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
self.result = Some(Err(Panic::String(msg)));
}
pub fn panic_with(&mut self, msg: String) {
let msg = format!("{msg}\nPanic traceback:\n{}", self.call_stack());
self.result = Some(Err(Panic::String(msg)));
}
fn get_value_at(&mut self, idx: u8) -> Value {
let idx = idx as usize;
let idx = idx + self.frame.stack_base;
self.stack[idx].clone()
}
fn get_scrutinee(&mut self) -> Value {
let idx = self.stack.len() - self.match_depth as usize - 1;
self.stack[idx].clone()
}
fn read(&mut self) -> u8 {
let code = self.chunk().bytecode[self.ip];
self.ip += 1;
code
}
fn read2(&mut self) -> usize {
let high = self.read();
let low = self.read();
combine_bytes(high, low)
}
fn at_end(&mut self) -> bool {
self.ip >= self.chunk().bytecode.len()
}
fn handle_msg(&mut self, args: Vec<Value>) {
println!("message received! {}", args[0]);
let Value::Keyword(msg) = args.first().unwrap() else {
return self.panic("malformed message to Process");
};
match *msg {
"self" => self.push(Value::Keyword(self.id)),
"msgs" => {
let msgs = self.mbx.iter().cloned().collect::<Vec<_>>();
let msgs = Vector::from(msgs);
self.push(Value::List(Box::new(msgs)));
}
"send" => {
let Value::Keyword(pid) = args[1] else {
return self.panic("malformed pid");
};
if self.id == pid {
self.mbx.push_back(args[2].clone());
} else {
self.zoo
.as_ref()
.borrow_mut()
.send_msg(pid, args[2].clone());
}
self.push(Value::Nil);
}
"spawn" => {
println!("spawning new process!");
let f = args[1].clone();
let proc = Creature::spawn(f, self.zoo.clone(), self.debug);
let id = self.zoo.as_ref().borrow_mut().put(proc);
self.push(Value::Keyword(id));
}
"yield" => {
self.r#yield = true;
self.push(Value::Nil);
}
msg => panic!("Process does not understand message: {msg}"),
}
}
pub fn interpret(&mut self) {
loop {
if self.at_end() {
self.result = Some(Ok(self.stack.pop().unwrap()));
return;
}
if self.reductions >= MAX_REDUCTIONS || self.r#yield {
return;
}
let code = self.read();
if self.debug {
self.last_code = self.ip - 1;
self.print_debug();
}
let op = Op::from_u8(code).unwrap();
use Op::*;
match op {
Noop => (),
Nil => self.push(Value::Nil),
Nothing => self.push(Value::Nothing),
True => self.push(Value::True),
False => self.push(Value::False),
Msg => {
let _ = self.read();
}
Constant => {
let const_idx = self.read2();
let value = self.chunk().constants[const_idx].clone();
self.push(value);
}
Jump => {
let jump_len = self.read2();
self.ip += jump_len;
}
JumpBack => {
let jump_len = self.read2();
self.ip -= jump_len + 3;
}
JumpIfFalse => {
let jump_len = self.read2();
let cond = self.pop();
match cond {
Value::Nil | Value::False => self.ip += jump_len,
_ => (),
}
}
JumpIfTrue => {
let jump_len = self.read2();
let cond = self.pop();
match cond {
Value::Nil | Value::False => (),
_ => self.ip += jump_len,
}
}
JumpIfZero => {
let jump_len = self.read2();
let cond = self.pop();
match cond {
Value::Number(x) if x <= 0.0 => self.ip += jump_len,
Value::Number(..) => (),
_ => return self.panic("repeat requires a number"),
}
}
Pop => {
self.pop();
}
PopN => {
let n = self.read() as usize;
self.stack.truncate(self.stack.len() - n);
}
PushBinding => {
let idx = self.read();
let value = self.get_value_at(idx);
self.push(value);
}
PushGlobal => {
let key = self.pop();
let Value::Keyword(name) = key else {
unreachable!("internal Ludus error: expected key for global resolution")
};
let value = self.chunk().env.get(name).unwrap();
self.push(value.clone());
}
Store => {
self.register[0] = self.pop();
}
StoreN => {
let n = self.read() as usize;
for i in (0..n).rev() {
self.register[i] = self.pop();
}
}
Stash => {
self.register[0] = self.peek().clone();
}
Load => {
let mut value = Value::Nothing;
swap(&mut self.register[0], &mut value);
self.push(value);
}
LoadN => {
let n = self.read() as usize;
for i in 0..n {
let mut value = Value::Nothing;
swap(&mut self.register[i], &mut value);
self.push(value);
}
}
ResetMatch => {
self.matches = false;
self.match_depth = 0;
}
UnconditionalMatch => {
self.matches = true;
}
MatchType => {
let as_type = self.pop();
let Value::Keyword(as_type) = as_type else {
unreachable!()
};
let value = self.get_scrutinee();
let val_type = value.type_of();
self.matches = val_type == as_type;
}
MatchNil => {
let value = self.get_scrutinee();
self.matches = value == Value::Nil;
}
MatchTrue => {
let value = self.get_scrutinee();
self.matches = value == Value::True;
}
MatchFalse => {
let value = self.get_scrutinee();
self.matches = value == Value::False;
}
PanicIfNoMatch => {
if !self.matches {
return self.panic("no match");
}
}
MatchConstant => {
let const_idx = self.read2();
let scrutinee = self.get_scrutinee();
// let idx = self.stack.len() - self.match_depth as usize - 1;
self.matches = scrutinee == self.chunk().constants[const_idx];
}
MatchString => {
let pattern_idx = self.read();
let scrutinee = self.get_scrutinee();
self.matches = match scrutinee {
Value::String(str) => self.chunk().string_patterns[pattern_idx as usize]
.re
.is_match(str.as_str()),
Value::Interned(str) => self.chunk().string_patterns[pattern_idx as usize]
.re
.is_match(str),
_ => false,
};
}
PushStringMatches => {
let pattern_idx = self.read();
let pattern_len = self.chunk().string_patterns[pattern_idx as usize]
.words
.len();
// let scrutinee_idx = self.stack.len() - self.match_depth as usize - 1;
let scrutinee = self.get_scrutinee();
let scrutinee = match scrutinee {
Value::String(str) => str.as_ref().clone(),
Value::Interned(str) => str.to_string(),
_ => unreachable!(),
};
let captures = self.chunk().string_patterns[pattern_idx as usize]
.re
.captures(scrutinee.as_str())
.unwrap();
for cap in 0..pattern_len {
self.push(Value::String(Rc::new(captures[cap + 1].to_string())))
}
self.match_depth += pattern_len as u8;
}
MatchTuple => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
let tuple_len = self.read() as usize;
let scrutinee = self.get_scrutinee();
match scrutinee {
Value::Tuple(members) => self.matches = members.len() == tuple_len,
_ => self.matches = false,
};
}
MatchSplattedTuple => {
let patt_len = self.read() as usize;
let scrutinee = self.get_scrutinee();
match scrutinee {
Value::Tuple(members) => self.matches = members.len() >= patt_len,
_ => self.matches = false,
}
}
PushTuple => {
let tuple_len = self.read() as usize;
let tuple_members = self.stack.split_off(self.stack.len() - tuple_len);
let tuple = Value::Tuple(Rc::new(tuple_members));
self.push(tuple);
}
LoadTuple => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
// let tuple = self.stack[idx].clone();
let tuple = self.get_scrutinee();
match tuple {
Value::Tuple(members) => {
for member in members.iter() {
self.push(member.clone());
}
}
_ => return self.panic("internal error: expected tuple"),
};
}
LoadSplattedTuple => {
let load_len = self.read() as usize;
let tuple = self.get_scrutinee();
let Value::Tuple(members) = tuple else {
return self.panic("internal error: expected tuple");
};
for i in 0..load_len - 1 {
self.push(members[i].clone());
}
let mut splatted = Vector::new();
for i in load_len - 1..members.len() {
splatted.push_back(members[i].clone());
}
self.push(Value::List(Box::new(splatted)));
}
PushList => {
self.push(Value::List(Box::new(Vector::new())));
}
AppendList => {
let value = self.pop();
let list = self.pop();
let Value::List(mut list) = list else {
return self.panic("only lists may be splatted into lists");
};
list.push_back(value);
self.push(Value::List(list));
}
ConcatList => {
let splatted = self.pop();
let target = self.pop();
let Value::List(mut target) = target else {
unreachable!()
};
let Value::List(splatted) = splatted else {
return self.panic("only lists may be splatted into lists");
};
target.append(*splatted);
self.push(Value::List(target));
}
MatchList => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
let list_len = self.read() as usize;
let scrutinee = self.get_scrutinee();
match scrutinee {
Value::List(members) => self.matches = members.len() == list_len,
_ => self.matches = false,
};
}
MatchSplattedList => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
let patt_len = self.read() as usize;
let scrutinee = self.get_scrutinee();
match scrutinee {
Value::List(members) => self.matches = members.len() >= patt_len,
_ => self.matches = false,
}
}
LoadList => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
let list = self.get_scrutinee();
match list {
Value::List(members) => {
for member in members.iter() {
self.push(member.clone());
}
}
_ => return self.panic("internal error: expected list"),
};
}
LoadSplattedList => {
let loaded_len = self.read() as usize;
let list = self.get_scrutinee();
let Value::List(members) = list else {
return self.panic("internal error: expected list");
};
for i in 0..loaded_len - 1 {
self.push(members[i].clone());
}
let splatted = Value::List(Box::new(members.skip(loaded_len - 1)));
self.push(splatted);
}
PushDict => {
self.push(Value::Dict(Box::new(HashMap::new())));
}
AppendDict => {
let value = self.pop();
let Value::Keyword(key) = self.pop() else {
unreachable!()
};
let Value::Dict(mut dict) = self.pop() else {
unreachable!()
};
dict.insert(key, value);
self.push(Value::Dict(dict));
}
ConcatDict => {
let Value::Dict(splatted) = self.pop() else {
return self.panic("only dicts may be splatted into dicts");
};
let Value::Dict(target) = self.pop() else {
unreachable!()
};
let union = splatted.union(*target);
self.push(Value::Dict(Box::new(union)));
}
LoadDictValue => {
let dict_idx = self.read();
let dict = match self.get_value_at(dict_idx) {
Value::Dict(dict) => dict,
value => {
println!(
"internal Ludus error in function {}",
self.frame.function.as_fn().name()
);
unreachable!("expected dict, got {value}")
}
};
let Value::Keyword(key) = self.pop() else {
unreachable!("expected keyword, got something else")
};
let value = dict.get(&key).unwrap_or(&Value::Nil);
self.push(value.clone());
}
MatchDict => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
let dict_len = self.read();
let scrutinee = self.get_scrutinee();
match scrutinee {
Value::Dict(members) => self.matches = members.len() == dict_len as usize,
_ => self.matches = false,
};
}
MatchSplattedDict => {
// let idx = self.stack.len() - self.match_depth as usize - 1;
let patt_len = self.read() as usize;
let scrutinee = self.get_scrutinee();
match scrutinee {
Value::Dict(members) => self.matches = members.len() >= patt_len,
_ => self.matches = false,
}
}
DropDictEntry => {
let Value::Keyword(key_to_drop) = self.pop() else {
unreachable!()
};
let Value::Dict(mut dict) = self.pop() else {
unreachable!()
};
dict.remove(key_to_drop);
self.push(Value::Dict(dict));
}
PushBox => {
let val = self.pop();
self.push(Value::Box(Rc::new(RefCell::new(val))));
}
GetKey => {
let key = self.pop();
let Value::Keyword(idx) = key else {
unreachable!()
};
let dict = self.pop();
let value = match dict {
Value::Dict(d) => d.as_ref().get(&idx).unwrap_or(&Value::Nil).clone(),
_ => Value::Nil,
};
self.push(value);
}
JumpIfNoMatch => {
let jump_len = self.read2();
// let jump_len = self.chunk().bytecode[self.ip + 1] as usize;
if !self.matches {
self.ip += jump_len
}
}
JumpIfMatch => {
let jump_len = self.read2();
if self.matches {
self.ip += jump_len;
}
}
TypeOf => {
let val = self.pop();
let type_of = Value::Keyword(val.type_of());
self.push(type_of);
}
ToInt => {
let val = self.pop();
if let Value::Number(x) = val {
self.push(Value::Number(x as usize as f64));
} else {
return self.panic("repeat requires a number");
}
}
Decrement => {
let val = self.pop();
if let Value::Number(x) = val {
self.push(Value::Number(x - 1.0));
} else {
return self.panic("you may only decrement a number");
}
}
Duplicate => {
self.push(self.peek().clone());
}
MatchDepth => {
self.match_depth = self.read();
}
PanicNoWhen | PanicNoMatch => {
return self.panic("no match");
}
Eq => {
let first = self.pop();
let second = self.pop();
if first == second {
self.push(Value::True)
} else {
self.push(Value::False)
}
}
Add => {
let first = self.pop();
let second = self.pop();
if let (Value::Number(x), Value::Number(y)) = (first, second) {
self.push(Value::Number(x + y))
} else {
return self.panic("`add` requires two numbers");
}
}
Sub => {
let first = self.pop();
let second = self.pop();
if let (Value::Number(x), Value::Number(y)) = (first, second) {
self.push(Value::Number(y - x))
} else {
return self.panic("`sub` requires two numbers");
}
}
Mult => {
let first = self.pop();
let second = self.pop();
if let (Value::Number(x), Value::Number(y)) = (first, second) {
self.push(Value::Number(x * y))
} else {
return self.panic("`mult` requires two numbers");
}
}
Div => {
let first = self.pop();
let second = self.pop();
if let (Value::Number(x), Value::Number(y)) = (first, second) {
if x == 0.0 {
return self.panic("division by 0");
}
self.push(Value::Number(y / x))
} else {
return self.panic("`div` requires two numbers");
}
}
Unbox => {
let the_box = self.pop();
let inner = if let Value::Box(b) = the_box {
b.borrow().clone()
} else {
return self.panic("`unbox` requires a box");
};
self.push(inner);
}
BoxStore => {
let new_value = self.pop();
let the_box = self.pop();
if let Value::Box(b) = the_box {
b.replace(new_value.clone());
} else {
return self.panic("`store` requires a box");
}
self.push(new_value);
}
Assert => {
let value = self.stack.last().unwrap();
if let Value::Nil | Value::False = value {
return self.panic("asserted falsy value");
}
}
Get => {
let key = self.pop();
let dict = self.pop();
let value = match (key, dict) {
(Value::Keyword(k), Value::Dict(d)) => {
d.as_ref().get(&k).unwrap_or(&Value::Nil).clone()
}
(Value::Keyword(_), _) => Value::Nil,
_ => return self.panic("keys must be keywords"),
};
self.push(value);
}
At => {
let idx = self.pop();
let ordered = self.pop();
let value = match (ordered, idx) {
(Value::List(l), Value::Number(i)) => {
l.get(i as usize).unwrap_or(&Value::Nil).clone()
}
(Value::Tuple(t), Value::Number(i)) => {
t.get(i as usize).unwrap_or(&Value::Nil).clone()
}
(_, Value::Number(_)) => Value::Nil,
_ => return self.panic("indexes must be numbers"),
};
self.push(value);
}
Not => {
let value = self.pop();
let negated = match value {
Value::Nil | Value::False => Value::True,
_ => Value::False,
};
self.push(negated);
}
Panic => {
let msg = self.pop().show();
return self.panic_with(msg);
}
EmptyString => {
self.push(Value::String(Rc::new("".to_string())));
}
//TODO: don't use the schlemiel's algo here
ConcatStrings => {
let second = self.pop();
let first = self.pop();
let combined = match (first, second) {
(Value::String(first), Value::String(second)) => {
let mut new = first.as_ref().clone();
new.push_str(second.as_str());
Value::String(Rc::new(new))
}
_ => unreachable!(),
};
self.push(combined);
}
Stringify => {
let to_stringify = self.pop();
let the_string = to_stringify.stringify();
let stringified = Value::String(Rc::new(the_string));
self.push(stringified);
}
Partial => {
let arity = self.read();
let the_fn = self.pop();
let Value::Fn(ref inner) = the_fn else {
return self.panic("only functions may be partially applied");
};
let args = self.stack.split_off(self.stack.len() - arity as usize);
let partial = crate::value::Partial {
args,
name: inner.name(),
function: the_fn,
};
self.push(Value::Partial(Rc::new(partial)));
}
TailCall => {
self.reduce();
let arity = self.read();
let called = self.pop();
if self.debug {
println!(
"=== tail call into {called}/{arity} from {} ===",
self.frame.function.as_fn().name()
);
}
match called {
Value::Process => {
let args = self.stack.split_off(self.stack.len() - arity as usize);
self.handle_msg(args);
}
Value::Fn(_) => {
if !called.as_fn().accepts(arity) {
return self.panic_with(format!(
"wrong number of arguments to {} passing {arity} args",
called.show()
));
}
// first put the arguments in the register
for i in 0..arity as usize {
self.register[arity as usize - i - 1] = self.pop();
}
// self.print_stack();
// then pop everything back to the current stack frame
self.stack.truncate(self.frame.stack_base);
// then push the arguments back on the stack
let mut i = 0;
while i < 8 && self.register[i] != Value::Nothing {
let mut value = Value::Nothing;
swap(&mut self.register[i], &mut value);
self.push(value);
i += 1;
}
let splat_arity = called.as_fn().splat_arity();
if splat_arity > 0 && arity >= splat_arity {
let splatted_args = self.stack.split_off(
self.stack.len() - (arity - splat_arity) as usize - 1,
);
let gathered_args = Vector::from(splatted_args);
self.push(Value::List(Box::new(gathered_args)));
}
let arity = if splat_arity > 0 {
splat_arity.min(arity)
} else {
arity
};
let mut frame = CallFrame {
function: called,
arity,
stack_base: self.stack.len() - arity as usize,
ip: 0,
};
swap(&mut self.frame, &mut frame);
frame.ip = self.ip;
self.ip = 0;
}
Value::BaseFn(base_fn) => {
let value = match (arity, base_fn) {
(0, BaseFn::Nullary(_, f)) => f(),
(1, BaseFn::Unary(_, f)) => f(&self.pop()),
(2, BaseFn::Binary(_, f)) => {
let y = &self.pop();
let x = &self.pop();
f(x, y)
}
(3, BaseFn::Ternary(_, f)) => {
let z = &self.pop();
let y = &self.pop();
let x = &self.pop();
f(x, y, z)
}
_ => return self.panic("internal ludus error"),
};
// // algo:
// // clear the stack
// self.stack.truncate(self.frame.stack_base);
// // then pop back out to the enclosing stack frame
// self.frame = self.call_stack.pop().unwrap();
// self.ip = self.frame.ip;
// // finally, throw the value on the stack
self.push(value);
// println!(
// "=== returning to {} ===",
// self.frame.function.as_fn().name()
// );
}
Value::Partial(partial) => {
let last_arg = self.pop();
let args = &partial.args;
for arg in args {
if *arg == Value::Nothing {
self.push(last_arg.clone());
} else {
self.push(arg.clone());
}
}
let the_fn = partial.function.clone();
let mut frame = CallFrame {
function: the_fn,
arity: args.len() as u8,
stack_base: self.stack.len() - args.len(),
ip: 0,
};
swap(&mut self.frame, &mut frame);
frame.ip = self.ip;
self.call_stack.push(frame);
self.ip = 0;
}
_ => {
return self.panic_with(format!("{} is not a function", called.show()))
}
}
}
Call => {
self.reduce();
let arity = self.read();
let called = self.pop();
if self.debug {
println!("=== calling into {called}/{arity} ===");
}
match called {
Value::Process => {
let args = self.stack.split_off(self.stack.len() - arity as usize);
self.handle_msg(args);
}
Value::Fn(_) => {
if !called.as_fn().accepts(arity) {
return self.panic_with(format!(
"wrong number of arguments to {} passing {arity} args",
called.show()
));
}
let splat_arity = called.as_fn().splat_arity();
if splat_arity > 0 && arity >= splat_arity {
let splatted_args = self.stack.split_off(
self.stack.len() - (arity - splat_arity) as usize - 1,
);
let gathered_args = Vector::from(splatted_args);
self.push(Value::List(Box::new(gathered_args)));
}
let arity = if splat_arity > 0 {
splat_arity.min(arity)
} else {
arity
};
let mut frame = CallFrame {
function: called,
arity,
stack_base: self.stack.len() - arity as usize,
ip: 0,
};
swap(&mut self.frame, &mut frame);
frame.ip = self.ip;
self.call_stack.push(frame);
self.ip = 0;
}
Value::BaseFn(base_fn) => {
let value = match (arity, base_fn) {
(0, BaseFn::Nullary(_, f)) => f(),
(1, BaseFn::Unary(_, f)) => f(&self.pop()),
(2, BaseFn::Binary(_, f)) => {
let y = &self.pop();
let x = &self.pop();
f(x, y)
}
(3, BaseFn::Ternary(_, f)) => {
let z = &self.pop();
let y = &self.pop();
let x = &self.pop();
f(x, y, z)
}
_ => return self.panic("internal ludus error"),
};
self.push(value);
}
Value::Partial(partial) => {
let last_arg = self.pop();
let args = &partial.args;
for arg in args {
if *arg == Value::Nothing {
self.push(last_arg.clone());
} else {
self.push(arg.clone());
}
}
let the_fn = partial.function.clone();
let mut frame = CallFrame {
function: the_fn,
arity: args.len() as u8,
stack_base: self.stack.len() - args.len(),
ip: 0,
};
swap(&mut self.frame, &mut frame);
frame.ip = self.ip;
self.call_stack.push(frame);
self.ip = 0;
}
_ => {
return self.panic_with(format!("{} is not a function", called.show()))
}
}
}
Return => {
if self.debug {
println!("== returning from {} ==", self.frame.function.show())
}
let mut value = Value::Nothing;
swap(&mut self.register[0], &mut value);
match self.call_stack.pop() {
Some(frame) => {
self.ip = frame.ip;
self.frame = frame;
self.push(value);
}
None => {
self.result = Some(Ok(value));
return;
}
}
}
Print => {
println!("{}", self.pop().show());
self.push(Value::Keyword("ok"));
}
SetUpvalue => {
let value = self.pop();
let Value::Fn(lfn) = self.peek() else {
panic!("expected function closing over value, got {}", self.peek());
};
lfn.close(value);
}
GetUpvalue => {
let idx = self.read();
if let Value::Fn(ref inner) = self.frame.function {
self.push(inner.as_ref().upvalue(idx));
} else {
unreachable!();
}
}
}
}
}
}