talc/talc-lang/src/vm.rs

400 lines
12 KiB
Rust
Raw Normal View History

2024-02-23 14:54:34 -05:00
use std::{cmp::Ordering, collections::HashMap, rc::Rc, sync::{atomic::AtomicBool, Arc}};
2024-02-21 11:04:18 -05:00
2024-02-23 14:54:34 -05:00
use crate::{ast::{BinaryOp, UnaryOp}, chunk::Instruction, symbol::{Symbol, SYM_CALL_STACK_OVERFLOW, SYM_INTERRUPTED, SYM_NAME_ERROR, SYM_TYPE_ERROR}, value::{exception::{throw, Exception, Result}, function::Function, Value}};
2024-02-21 11:04:18 -05:00
2024-02-22 14:27:35 -05:00
struct TryFrame { idx: usize, stack_len: usize }
2024-02-21 11:04:18 -05:00
2024-02-22 14:27:35 -05:00
#[derive(Default)]
2024-02-21 11:04:18 -05:00
struct CallFrame {
func: Rc<Function>,
2024-02-22 14:27:35 -05:00
locals: Vec<Value>,
try_frames: Vec<TryFrame>,
2024-02-21 11:04:18 -05:00
ip: usize,
2024-02-22 14:27:35 -05:00
root: bool,
}
impl CallFrame {
fn new(func: Rc<Function>, locals: Vec<Value>) -> Self {
Self {
func,
locals,
..Self::default()
}
}
2024-02-21 11:04:18 -05:00
}
pub struct Vm {
stack: Vec<Value>,
call_stack: Vec<CallFrame>,
stack_max: usize,
globals: HashMap<Symbol, Value>,
2024-02-23 14:54:34 -05:00
interrupt: Arc<AtomicBool>,
2024-02-21 11:04:18 -05:00
}
pub fn binary_op(o: BinaryOp, a: Value, b: Value) -> Result<Value> {
match o {
BinaryOp::Add => a + b,
BinaryOp::Sub => a - b,
BinaryOp::Mul => a * b,
BinaryOp::Div => a / b,
BinaryOp::Mod => a.modulo(b),
BinaryOp::IntDiv => a.int_div(b),
BinaryOp::Pow => a.pow(b),
BinaryOp::Eq => Ok(Value::Bool(a == b)),
BinaryOp::Ne => Ok(Value::Bool(a != b)),
BinaryOp::Gt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Greater)),
BinaryOp::Ge => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Less)),
BinaryOp::Lt => a.val_cmp(&b).map(|o| Value::Bool(o == Ordering::Less)),
BinaryOp::Le => a.val_cmp(&b).map(|o| Value::Bool(o != Ordering::Greater)),
BinaryOp::Range => a.range(&b, false),
BinaryOp::RangeIncl => a.range(&b, true),
BinaryOp::Concat => a.concat(&b),
2024-02-23 14:54:34 -05:00
BinaryOp::Append => a.append(b),
2024-02-21 11:04:18 -05:00
}
}
pub fn unary_op(o: UnaryOp, a: Value) -> Result<Value> {
match o {
UnaryOp::Neg => -a,
UnaryOp::Not => Ok(Value::Bool(!a.truthy())),
UnaryOp::RangeEndless => a.range_endless(),
}
}
impl Vm {
pub fn new(stack_max: usize) -> Self {
Self {
stack: Vec::with_capacity(16),
call_stack: Vec::with_capacity(16),
globals: HashMap::with_capacity(16),
stack_max,
2024-02-23 14:54:34 -05:00
interrupt: Arc::new(AtomicBool::new(false)),
2024-02-21 11:04:18 -05:00
}
}
2024-02-23 14:54:34 -05:00
pub fn get_interrupt(&self) -> Arc<AtomicBool> {
self.interrupt.clone()
}
2024-02-21 11:04:18 -05:00
pub fn set_global(&mut self, name: Symbol, val: Value) {
self.globals.insert(name, val);
}
2024-02-23 14:54:34 -05:00
pub fn set_global_name(&mut self, name: &str, val: Value) {
self.globals.insert(Symbol::get(name), val);
}
2024-02-21 11:04:18 -05:00
pub fn get_global(&self, name: Symbol) -> Option<&Value> {
self.globals.get(&name)
}
#[inline]
fn push(&mut self, v: Value) {
self.stack.push(v);
}
#[inline]
fn pop(&mut self) -> Value {
self.stack.pop().expect("temporary stack underflow")
}
#[inline]
fn pop_n(&mut self, n: usize) -> Vec<Value> {
let res = self.stack.split_off(self.stack.len() - n);
assert!(res.len() == n, "temporary stack underflow");
res
}
2024-02-23 14:54:34 -05:00
fn check_interrupt(&mut self) -> Result<()> {
if self.interrupt.fetch_and(false, std::sync::atomic::Ordering::Relaxed) {
throw!(*SYM_INTERRUPTED)
}
Ok(())
}
2024-02-21 11:04:18 -05:00
2024-02-22 14:27:35 -05:00
fn run_instr(&mut self, frame: &mut CallFrame, instr: Instruction) -> Result<Option<Value>> {
2024-02-21 11:04:18 -05:00
use Instruction as I;
2024-02-23 14:54:34 -05:00
2024-02-22 14:27:35 -05:00
match instr {
2024-02-23 14:54:34 -05:00
// do nothing
2024-02-22 14:27:35 -05:00
I::Nop => (),
2024-02-23 14:54:34 -05:00
// [] -> [locals[n]]
2024-02-22 14:27:35 -05:00
I::LoadLocal(n)
=> self.push(frame.locals[usize::from(n)].clone()),
2024-02-23 14:54:34 -05:00
// [x] -> [], locals[n] = x
2024-02-22 14:27:35 -05:00
I::StoreLocal(n)
=> frame.locals[usize::from(n)] = self.pop(),
2024-02-23 14:54:34 -05:00
// [x] -> [], locals.push(x)
2024-02-22 14:27:35 -05:00
I::NewLocal
=> frame.locals.push(self.pop()),
2024-02-23 14:54:34 -05:00
// locals.pop_n(n)
2024-02-22 14:27:35 -05:00
I::DropLocal(n)
=> frame.locals.truncate(frame.locals.len() - usize::from(n)),
2024-02-23 14:54:34 -05:00
// [] -> [globals[s]]
2024-02-22 14:27:35 -05:00
I::LoadGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() };
let v = match self.globals.get(&sym) {
Some(v) => v.clone(),
2024-02-23 14:54:34 -05:00
None => throw!(*SYM_NAME_ERROR, "undefined global {}", sym.name()),
2024-02-22 14:27:35 -05:00
};
self.push(v);
},
2024-02-23 14:54:34 -05:00
// [x] -> [], globals[s] = x
2024-02-22 14:27:35 -05:00
I::StoreGlobal(s) => {
let sym = unsafe { s.to_symbol_unchecked() };
let v = self.pop();
self.globals.insert(sym, v);
},
2024-02-23 14:54:34 -05:00
// [] -> [consts[n]]
2024-02-22 14:27:35 -05:00
I::Const(n)
=> self.push(frame.func.chunk.consts[usize::from(n)].clone()),
2024-02-23 14:54:34 -05:00
// [] -> [nil]
2024-02-22 14:27:35 -05:00
I::Nil => self.push(Value::Nil),
2024-02-23 14:54:34 -05:00
// [] -> [b]
2024-02-22 14:27:35 -05:00
I::Bool(b) => self.push(Value::Bool(b)),
2024-02-23 14:54:34 -05:00
// [] -> [s]
I::Symbol(s) => {
let sym = unsafe { Symbol::from_id_unchecked(u32::from(s)) };
2024-02-22 14:27:35 -05:00
self.push(Value::Symbol(sym));
},
2024-02-23 14:54:34 -05:00
// [] -> [n]
2024-02-22 14:27:35 -05:00
I::Int(n) => self.push(Value::Int(i64::from(n))),
2024-02-23 14:54:34 -05:00
// [x] -> [x,x]
2024-02-22 14:27:35 -05:00
I::Dup => self.push(self.stack[self.stack.len() - 1].clone()),
2024-02-23 14:54:34 -05:00
// [x,y] -> [x,y,x,y]
2024-02-22 14:27:35 -05:00
I::DupTwo => {
self.push(self.stack[self.stack.len() - 2].clone());
self.push(self.stack[self.stack.len() - 2].clone());
},
2024-02-23 14:54:34 -05:00
// [a0,a1...an] -> []
2024-02-22 14:27:35 -05:00
I::Drop(n) => for _ in 0..u32::from(n) { self.pop(); },
2024-02-23 14:54:34 -05:00
// [x,y] -> [y,x]
2024-02-22 14:27:35 -05:00
I::Swap => {
let len = self.stack.len();
self.stack.swap(len - 1, len - 2);
},
2024-02-23 14:54:34 -05:00
// [x,y] -> [y op x]
2024-02-22 14:27:35 -05:00
I::BinaryOp(op) => {
let b = self.pop();
let a = self.pop();
self.push(binary_op(op, a, b)?);
},
2024-02-23 14:54:34 -05:00
// [x] -> [op x]
2024-02-22 14:27:35 -05:00
I::UnaryOp(op) => {
let a = self.pop();
self.push(unary_op(op, a)?);
},
2024-02-23 14:54:34 -05:00
// [a0,a1...an] -.> [[a0,a1...an]]
2024-02-22 14:27:35 -05:00
I::NewList(n) => {
let list = self.pop_n(n as usize);
2024-02-23 14:54:34 -05:00
self.push(list.into())
2024-02-22 14:27:35 -05:00
},
2024-02-23 14:54:34 -05:00
// [l,a0,a1...an] -.> [l ++ [a0,a1...an]]
2024-02-22 14:27:35 -05:00
I::GrowList(n) => {
let ext = self.pop_n(n as usize);
let list = self.pop();
let Value::List(list) = list else { panic!("not a list") };
list.borrow_mut().extend(ext);
self.push(Value::List(list));
},
2024-02-23 14:54:34 -05:00
// [k0,v0...kn,vn] -.> [{k0=v0...kn=vn}]
2024-02-22 14:27:35 -05:00
I::NewTable(n) => {
let mut table = HashMap::new();
for _ in 0..n {
let v = self.pop();
let k = self.pop();
table.insert(k.try_into()?, v);
}
2024-02-23 14:54:34 -05:00
self.push(table.into())
2024-02-22 14:27:35 -05:00
},
2024-02-23 14:54:34 -05:00
// [t,k0,v0...kn,vn] -> [t ++ {k0=v0...kn=vn}]
2024-02-22 14:27:35 -05:00
I::GrowTable(n) => {
let mut ext = self.pop_n(2 * n as usize);
let table = self.pop();
let Value::Table(table) = table else { panic!("not a table") };
let mut table_ref = table.borrow_mut();
for _ in 0..n {
// can't panic: pop_n checked that ext would have len 2*n
let v = ext.pop().unwrap();
let k = ext.pop().unwrap();
table_ref.insert(k.try_into()?, v);
}
drop(table_ref);
self.push(Value::Table(table));
},
2024-02-23 14:54:34 -05:00
// [ct, idx] -> [ct!idx]
2024-02-22 14:27:35 -05:00
I::Index => {
let idx = self.pop();
let ct = self.pop();
self.push(ct.index(idx)?);
},
2024-02-23 14:54:34 -05:00
// [ct, idx, v] -> [v], ct!idx = v
2024-02-22 14:27:35 -05:00
I::StoreIndex => {
let v = self.pop();
let idx = self.pop();
let ct = self.pop();
ct.store_index(idx, v.clone())?;
self.push(v);
},
2024-02-23 14:54:34 -05:00
// ip = n
I::Jump(n) => {
self.check_interrupt()?;
frame.ip = usize::from(n)
},
// [v] ->, [], if v then ip = n
I::JumpTrue(n) => if self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n)
},
// [v] ->, [], if not v then ip = n
I::JumpFalse(n) => if !self.pop().truthy() {
self.check_interrupt()?;
frame.ip = usize::from(n)
},
// [v] -> [iter(v)]
2024-02-22 14:27:35 -05:00
I::IterBegin => {
let iter = self.pop().to_iter_function()?;
self.push(iter);
},
2024-02-23 14:54:34 -05:00
// [i] -> if i() succeeds then [i, i()], otherwise [] and ip = n
I::IterNext(n) => {
2024-02-22 14:27:35 -05:00
let v = &self.stack[self.stack.len() - 1];
2024-02-23 14:54:34 -05:00
self.call_stack.push(std::mem::take(frame));
let res = self.call_value(v.clone(), vec![v.clone()]);
*frame = self.call_stack.pop().expect("no frame to pop");
match res {
Ok(res) => self.push(res),
Err(e) => if e.ty == Symbol::get("stop_iterator") {
self.pop();
frame.ip = usize::from(n)
} else {
return Err(e)
}
}
2024-02-22 14:27:35 -05:00
},
2024-02-23 14:54:34 -05:00
// try_frames.push(t, stack.len())
2024-02-22 14:27:35 -05:00
I::BeginTry(t) => {
let tryframe = TryFrame {
idx: usize::from(t),
stack_len: self.stack.len()
};
frame.try_frames.push(tryframe);
},
2024-02-23 14:54:34 -05:00
// try_frames.pop()
2024-02-22 14:27:35 -05:00
I::EndTry => {
frame.try_frames.pop().expect("no try to pop");
},
2024-02-23 14:54:34 -05:00
// [f,a0,a1...an] -> [f(a0,a1...an)]
2024-02-22 14:27:35 -05:00
I::Call(n) => {
2024-02-23 14:54:34 -05:00
self.check_interrupt()?;
2024-02-22 14:27:35 -05:00
let n = usize::from(n);
2024-02-23 14:54:34 -05:00
let args = self.pop_n(n + 1);
let func = &args[0];
2024-02-22 14:27:35 -05:00
if let Value::NativeFunc(nf) = func {
let nf = nf.clone();
if nf.arity != n {
2024-02-23 14:54:34 -05:00
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
2024-02-22 14:27:35 -05:00
}
2024-02-21 11:04:18 -05:00
2024-02-22 14:27:35 -05:00
self.call_stack.push(std::mem::take(frame));
2024-02-23 14:54:34 -05:00
let res = (nf.f)(self, args)?;
*frame = self.call_stack.pop().expect("no frame to pop");
2024-02-22 14:27:35 -05:00
self.stack.push(res);
} else if let Value::Function(func) = func {
if func.arity != n {
2024-02-23 14:54:34 -05:00
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
2024-02-22 14:27:35 -05:00
}
if self.call_stack.len() + 1 >= self.stack_max {
2024-02-23 14:54:34 -05:00
throw!(*SYM_CALL_STACK_OVERFLOW, "call stack overflow")
2024-02-22 14:27:35 -05:00
}
self.call_stack.push(std::mem::take(frame));
let func = func.clone();
*frame = CallFrame::new(func, args);
} else {
2024-02-23 14:54:34 -05:00
throw!(*SYM_TYPE_ERROR, "attempt to call non-function {func}");
2024-02-22 14:27:35 -05:00
}
},
2024-02-23 14:54:34 -05:00
// [v] -> [], return v
2024-02-22 14:27:35 -05:00
I::Return if frame.root => {
2024-02-23 14:54:34 -05:00
return Ok(Some(self.pop()));
2024-02-22 14:27:35 -05:00
},
2024-02-23 14:54:34 -05:00
// [v] -> [], return v
2024-02-22 14:27:35 -05:00
I::Return => {
*frame = self.call_stack.pop().expect("no root frame");
},
}
Ok(None)
}
fn handle_exception(&mut self, frame: &mut CallFrame, exc: Exception) -> Result<()> {
loop {
while let Some(try_frame) = frame.try_frames.pop() {
let table = &frame.func.chunk.try_tables[try_frame.idx];
for catch in &table.catches {
if catch.types.is_none() || catch.types.as_ref().unwrap().contains(&exc.ty) {
frame.ip = catch.addr;
frame.locals.truncate(table.local_count);
self.stack.truncate(try_frame.stack_len);
self.stack.push(Value::Table(exc.to_table()));
return Ok(())
}
}
}
if frame.root {
return Err(exc)
}
*frame = self.call_stack.pop().expect("no root frame");
}
}
2024-02-23 14:54:34 -05:00
pub fn call_value(&mut self, value: Value, args: Vec<Value>) -> Result<Value> {
match value {
Value::Function(f) => self.call(f, args),
Value::NativeFunc(f) => {
if f.arity + 1 != args.len() {
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
}
(f.f)(self, args)
},
_ => throw!(*SYM_TYPE_ERROR, "not a function"),
}
}
pub fn call_no_args(&mut self, func: Rc<Function>) -> Result<Value> {
self.call(func.clone(), vec![func.into()])
}
pub fn call(&mut self, func: Rc<Function>, args: Vec<Value>) -> Result<Value> {
if func.arity + 1 != args.len() {
throw!(*SYM_TYPE_ERROR, "function call with wrong argument count");
}
2024-02-22 14:27:35 -05:00
let init_stack_len = self.stack.len();
2024-02-23 14:54:34 -05:00
let mut frame = CallFrame::new(func, args);
frame.root = true;
2024-02-21 11:04:18 -05:00
loop {
let instr = frame.func.chunk.instrs[frame.ip];
frame.ip += 1;
2024-02-22 14:27:35 -05:00
match self.run_instr(&mut frame, instr) {
Ok(None) => (),
Ok(Some(v)) => {
self.stack.truncate(init_stack_len);
return Ok(v)
}
Err(e) => {
if let Err(e) = self.handle_exception(&mut frame, e) {
self.stack.truncate(init_stack_len);
return Err(e)
2024-02-21 11:04:18 -05:00
}
2024-02-22 14:27:35 -05:00
}
2024-02-21 11:04:18 -05:00
}
}
}
}