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 imbl::{HashMap, Vector}; use num_traits::FromPrimitive; use std::cell::RefCell; use std::fmt; use std::mem::swap; use std::rc::Rc; #[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, pub caller: Spanned, 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 } #[derive(Debug, Clone, PartialEq)] pub struct Creature { pub stack: Vec, pub call_stack: Vec, pub frame: CallFrame, pub ip: usize, pub return_register: [Value; 8], pub matches: bool, pub match_depth: u8, pub result: Option>, debug: bool, last_code: usize, pub id: &'static str, } impl Creature { pub fn new(chunk: Chunk, 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)); let base_frame = CallFrame { function: base_fn.clone(), stack_base: 0, ip: 0, arity: 0, }; Creature { stack: vec![], call_stack: Vec::with_capacity(64), frame: base_frame, ip: 0, return_register: [const { Value::Nothing }; 8], matches: false, match_depth: 0, result: None, debug, last_code: 0, id: "", } } 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 .return_register .iter() .map(|val| val.to_string()) .collect::>() .join(","); println!("{:04}: [{inner}] ({register})", self.last_code); } 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 { 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() } pub fn interpret(&mut self) { loop { if self.at_end() { self.result = Some(Ok(self.stack.pop().unwrap())); 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.return_register[0] = self.pop(); } StoreN => { let n = self.read() as usize; for i in (0..n).rev() { self.return_register[i] = self.pop(); } } Stash => { self.return_register[0] = self.peek().clone(); } Load => { let mut value = Value::Nothing; swap(&mut self.return_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.return_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 => { 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::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.return_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.return_register[i] != Value::Nothing { let mut value = Value::Nothing; swap(&mut self.return_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 => { let arity = self.read(); let called = self.pop(); if self.debug { println!("=== calling into {called}/{arity} ==="); } match called { 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()) } self.frame = self.call_stack.pop().unwrap(); self.ip = self.frame.ip; let mut value = Value::Nothing; swap(&mut self.return_register[0], &mut value); self.push(value); } 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!(); } } } } } }