fix a lot of bugs + add test vm + funcs work

This commit is contained in:
minish 2025-10-12 18:05:52 -04:00
parent 1509c3e1b2
commit 4fa6cd9ae0
Signed by: min
SSH Key Fingerprint: SHA256:mf+pUTmK92Y57BuCjlkBdd82LqztTfDCQIUp0fCKABc
4 changed files with 270 additions and 54 deletions

View File

@ -193,14 +193,15 @@ fn analyze(fs: &FuncStat, scope: &mut Scope, e: &mut Expr, gets_captured: bool)
/* 1b is up? */ /* 1b is up? */
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum Stkval { pub enum Stkval {
/* 4b blank ; 8b offset */ /* 12b offset */
Local(u8), Local(u8),
/* 4b up levels ; 8b offset */ /* 4b up levels ; 8b offset */
Shared(u8, u8), Shared(u8, u8),
} }
/* 3b type tag */ /* 3b type tag */
#[derive(Debug)] #[derive(Debug, Clone, PartialEq)]
pub enum Val { pub enum Val {
/* where? ; pop after use? */
Stack(Stkval, bool), Stack(Stkval, bool),
/* u16 len, LEN data */ /* u16 len, LEN data */
String(String), String(String),
@ -215,32 +216,32 @@ pub enum Val {
/* ... */ /* ... */
Nil, Nil,
} }
/* 5b inst type */ /* 3b inst type */
#[derive(Debug)] #[derive(Debug, Clone, PartialEq)]
pub enum Inst { pub enum Inst {
/* ... */ /* is shared? ; val to copy */
Copy(bool, Val), Copy(bool, Val),
/* pop a1? ; pop a2? */ /* where to write, val to write */
Eq(bool, bool, Val, Val),
Gt(bool, bool, Val, Val),
GtEq(bool, bool, Val, Val),
/* is conditional? ; what condition? */
Skip(bool, bool, i16),
/* push result? ; pop a1? */
Call(bool, bool, Val),
/* pop a1? ; pop a2 */
Add(bool, bool, Val, Val),
Mul(bool, bool, Val, Val),
Div(bool, bool, Val, Val),
Mod(bool, bool, Val, Val),
Pow(bool, bool, Val, Val),
And(bool, bool, Val, Val),
Or(bool, bool, Val, Val),
/* pop a1? */
Not(bool, Val),
/* ... */
Move(Stkval, Val), Move(Stkval, Val),
/* how much to increment PC */
CJump(usize),
/* push result? ; # args provided ; val to call */
Call(bool, u8, Val),
/* value to return */
Return(Val), Return(Val),
/* lhs, rhs */
Eq(Val, Val),
Gt(Val, Val),
GtEq(Val, Val),
Add(Val, Val),
Mul(Val, Val),
Div(Val, Val),
Mod(Val, Val),
Pow(Val, Val),
And(Val, Val),
Or(Val, Val),
/* rhs */
Not(Val),
} }
/// Value on fake stack. /// Value on fake stack.
@ -322,6 +323,11 @@ impl<'a> FuncBuild<'a> {
fn top(&self) -> Stkval { fn top(&self) -> Stkval {
Stkval::Local(self.local.top_index() as u8) Stkval::Local(self.local.top_index() as u8)
} }
/// Returns stackval for top item of shared stack.
/// (Panics if empty)
fn top_shared(&self) -> Stkval {
Stkval::Shared(0, self.shared.top_index() as u8)
}
/// Pushes a value to stack and returns its stackval. /// Pushes a value to stack and returns its stackval.
fn push_any(&mut self) -> Stkval { fn push_any(&mut self) -> Stkval {
@ -329,19 +335,40 @@ impl<'a> FuncBuild<'a> {
self.top() self.top()
} }
fn check_drop(&mut self, v: &Val) -> bool { fn check_drop(&mut self, v: &Val) {
if let Val::Stack(Stkval::Local(i), true) = v { if let Val::Stack(Stkval::Local(i), true) = v {
self.local.pop(*i as usize); self.local.pop(*i as usize);
true
} else {
false
} }
} }
fn check_drop2(&mut self, v1: &Val, v2: &Val) -> (bool, bool) {
(self.check_drop(v1), self.check_drop(v2)) fn check_drops<const N: usize>(&mut self, mut vl: [&mut Val; N]) {
use {Stkval::*, Val::*};
// Sort low->high
vl.sort_by_key(|v| match v {
Val::Stack(Stkval::Local(o), _) => *o as i16,
// It doesn't matter
_ => 0,
});
// Fix indices
let mut to_pop = Vec::new();
for v in vl {
if let Stack(Local(o), p) = v {
*o -= to_pop.len() as u8;
if *p {
to_pop.push(*o as usize);
}
}
}
// Pop
for o in to_pop {
self.local.pop(o);
}
} }
fn gen_unop(&mut self, r: Expr, f: impl Fn(bool, Val) -> Inst, do_compute: bool) -> Val { fn gen_unop(&mut self, r: Expr, f: impl Fn(Val) -> Inst, do_compute: bool) -> Val {
let v1 = self.translate(r, do_compute, false); let v1 = self.translate(r, do_compute, false);
// Don't compute anything unnecessarily // Don't compute anything unnecessarily
@ -349,19 +376,19 @@ impl<'a> FuncBuild<'a> {
return Val::Nil; return Val::Nil;
} }
let a1 = self.check_drop(&v1); self.check_drop(&v1);
self.insts.push(f(a1, v1)); self.insts.push(f(v1));
Val::Stack(self.push_any(), true) Val::Stack(self.push_any(), true)
} }
fn gen_binop( fn gen_binop(
&mut self, &mut self,
l: Expr, l: Expr,
r: Expr, r: Expr,
f: impl Fn(bool, bool, Val, Val) -> Inst, f: impl Fn(Val, Val) -> Inst,
do_compute: bool, do_compute: bool,
) -> Val { ) -> Val {
let (v1, v2) = ( let (mut v1, mut v2) = (
self.translate(l, do_compute, false), self.translate(l, do_compute, false),
self.translate(r, do_compute, false), self.translate(r, do_compute, false),
); );
@ -371,14 +398,14 @@ impl<'a> FuncBuild<'a> {
return Val::Nil; return Val::Nil;
} }
let (a1, a2) = self.check_drop2(&v1, &v2); self.check_drops([&mut v1, &mut v2]);
self.insts.push(f(a1, a2, v1, v2)); self.insts.push(f(v1, v2));
Val::Stack(self.push_any(), true) Val::Stack(self.push_any(), true)
} }
fn gen_copy(&mut self, v1: Val) -> Val { fn gen_copy(&mut self, v1: Val) -> Val {
let a1 = self.check_drop(&v1); self.check_drop(&v1);
self.insts.push(Inst::Copy(a1, v1)); self.insts.push(Inst::Copy(false, v1));
Val::Stack(self.push_any(), false) Val::Stack(self.push_any(), false)
} }
@ -420,18 +447,25 @@ impl<'a> FuncBuild<'a> {
Val::Nil Val::Nil
} }
Expr::Call(func, args) => { Expr::Call(func, args) => {
// get the function
let v1 = self.translate(*func, true, false);
let a1 = self.check_drop(&v1);
// yield all args to stack // yield all args to stack
let n_args = args.len();
for arg in args { for arg in args {
self.translate(arg, true, true); self.translate(arg, true, true);
} }
// pop all of them
self.local.pop_top_n(n_args);
// get the function
let v1 = self.translate(*func, true, false);
self.check_drop(&v1);
// decide if we push result to stack // decide if we push result to stack
// if we are computing a value or yielding one, then yes // if we are computing a value or yielding one, then yes
let push = do_compute || do_yield; let push = do_compute || do_yield;
// add call // add call
self.insts.push(Inst::Call(push, a1, v1)); self.insts.push(Inst::Call(push, n_args as u8, v1));
// whatever we output // whatever we output
if push { if push {
Val::Stack(self.push_any(), true) Val::Stack(self.push_any(), true)
@ -455,7 +489,9 @@ impl<'a> FuncBuild<'a> {
/* vars */ /* vars */
Expr::Literal(Literal::Ident(id, Some(rs))) if do_compute => { Expr::Literal(Literal::Ident(id, Some(rs))) if do_compute => {
Val::Stack(self.find(&id).unwrap(), rs.now == rs.meta.total.get()) let is_last_use = rs.now == rs.meta.total.get();
let is_shared = rs.meta.is_shared.get();
Val::Stack(self.find(&id).unwrap(), is_last_use && !is_shared)
} }
Expr::Literal(Literal::Ident(_, _)) => Val::Nil, Expr::Literal(Literal::Ident(_, _)) => Val::Nil,
Expr::Assign(l, r) => { Expr::Assign(l, r) => {
@ -469,8 +505,7 @@ impl<'a> FuncBuild<'a> {
// if this isn't getting used for computation OR referenced, // if this isn't getting used for computation OR referenced,
// just continue translation without adding to stack // just continue translation without adding to stack
if !(do_compute || gets_referenced) { if !(do_compute || gets_referenced) {
// do_compute doesn't matter for literals self.translate(*r, false, false)
self.translate(*r, true, false)
} else { } else {
// get val // get val
let val = match *r { let val = match *r {
@ -500,16 +535,29 @@ impl<'a> FuncBuild<'a> {
if let Some(sv) = self.find(&id) { if let Some(sv) = self.find(&id) {
// yes, move it there // yes, move it there
self.insts.push(Inst::Move(sv.clone(), val)); self.insts.push(Inst::Move(sv.clone(), val));
self.local.pop_top();
// find out if it should be popped
let is_shared = matches!(sv, Stkval::Shared(_, _));
let should_pop = gets_referenced && !is_shared;
// return new stackval // return new stackval
Val::Stack(sv, gets_referenced) return Val::Stack(sv, should_pop);
} else { } else if matches!(val, Val::Stack(_, _)) {
// no it doesn't // no, keep track of new stackval
if matches!(val, Val::Stack(_, _)) { self.local.swap_top(FSValue::Var(id));
// it is a stackval so keep track of it
self.local.swap_top(FSValue::Var(id)); // also move to shared if we're supposed to do that :)
if ref_stat.meta.is_shared.get()
&& let Val::Stack(sv, _) = val
{
// move fs value
let v = self.local.pop_top();
self.shared.push(v);
// copy to shared w pop
self.insts.push(Inst::Copy(true, Val::Stack(sv, true)));
return Val::Stack(self.top_shared(), false);
} }
val
} }
val
} }
} }

View File

@ -26,6 +26,13 @@ where
fn swap_top(&mut self, new: Self::Value) { fn swap_top(&mut self, new: Self::Value) {
*self.values_mut().last_mut().unwrap() = new; *self.values_mut().last_mut().unwrap() = new;
} }
fn pop_top(&mut self) -> Self::Value {
self.values_mut().pop().unwrap()
}
fn pop_top_n(&mut self, n: usize) -> Vec<Self::Value> {
let start = self.values().len() - n;
self.values_mut().split_off(start)
}
fn find(&self, input: &Self::Input) -> Option<(Self::Output, u16)> { fn find(&self, input: &Self::Input) -> Option<(Self::Output, u16)> {
let mut cur = Some(self); let mut cur = Some(self);

View File

@ -6,6 +6,7 @@ mod compiler;
mod kind; mod kind;
mod lexer; mod lexer;
mod parser; mod parser;
mod vm;
fn main() { fn main() {
let script = std::fs::read_to_string("./start.lf").unwrap(); let script = std::fs::read_to_string("./start.lf").unwrap();
@ -26,7 +27,12 @@ fn main() {
let start = Instant::now(); let start = Instant::now();
let insts = compiler::translation_demo(e); let insts = compiler::translation_demo(e);
println!("Translation took {:?}", start.elapsed()); println!("Translation took {:?}", start.elapsed());
for i in insts { for i in &insts {
println!("=> {i:?}"); println!("=> {i:?}");
} }
println!("Starting VM!!!!!!!!!!!!!!!!");
let start = Instant::now();
let out = vm::run(&insts);
println!("!! Got result (in {:?}): {out:?}", start.elapsed());
} }

155
src/vm.rs Normal file
View File

@ -0,0 +1,155 @@
use std::cell::RefCell;
use crate::compiler::{Inst, Stkval, Val};
#[derive(Default)]
struct FuncVm<'a> {
depth: usize,
parent_vm: Option<&'a FuncVm<'a>>,
shared: RefCell<Vec<Val>>,
locals: Vec<Val>,
}
impl<'a> FuncVm<'a> {
fn with(parent_vm: &'a FuncVm<'a>, locals: Vec<Val>) -> Self {
Self {
parent_vm: Some(parent_vm),
depth: parent_vm.depth + 1,
shared: RefCell::default(),
locals,
}
}
fn get(&mut self, v: &Val) -> Val {
use {Stkval::*, Val::*};
match v {
Stack(Local(o), true) => self.locals.remove(*o as usize),
Stack(Local(o), false) => self.locals[*o as usize].clone(),
Stack(Shared(l, o), false) => {
let mut vm = &*self;
for _ in 0..*l {
vm = vm.parent_vm.as_ref().unwrap();
}
vm.shared.borrow_mut()[*o as usize].clone()
}
Stack(Shared(_, _), true) => panic!("not allowed"),
v => v.clone(),
}
}
fn eval_all(&mut self, insts: &[Inst]) -> Val {
use {Inst::*, Val::*};
let mut pc = 0;
while pc < insts.len() {
let inst = &insts[pc];
// println!("{}> {:?} {inst:?}", "=".repeat(self.depth), self.locals);
match inst {
/* rhs */
Not(a) | Return(a) => {
let r = match (inst, self.get(a)) {
(Not(_), Bool(a)) => Bool(!a),
(Return(_), Func(true, _, _)) => panic!("func is unreturnable"),
(Return(_), a) => return a,
_ => unimplemented!(),
};
self.locals.push(r);
}
/* s rhs */
Copy(s, a) => {
let a = self.get(a);
if !*s {
self.locals.push(a);
} else {
self.shared.borrow_mut().push(a);
}
}
/* k rhs */
Call(k, n, a) => {
// check validity
let a = self.get(a);
let Func(_, arity, insts) = a else {
panic!("called non-function {a:?}")
};
// collect args from stack :)
let args_start = self.locals.len() - *n as usize;
let args = self.locals.split_off(args_start);
// make sure its the right amount
if *n != arity {
panic!("wrong # args")
}
// exec
let mut vm = FuncVm::with(self, args);
let r = vm.eval_all(&insts);
// push value if were supposed to
if *k {
self.locals.push(r);
}
}
/* sv rhs */
Move(sv, a) => {
let a = self.get(a);
match sv {
Stkval::Local(o) => {
self.locals[*o as usize] = a;
}
Stkval::Shared(l, o) => {
let mut vm = &*self;
for _ in 0..*l {
vm = vm.parent_vm.unwrap();
}
vm.shared.borrow_mut()[*o as usize] = a;
}
}
}
/* lhs rhs */
Eq(a, b)
| Gt(a, b)
| GtEq(a, b)
| Add(a, b)
| Mul(a, b)
| Div(a, b)
| Mod(a, b)
| Pow(a, b)
| And(a, b)
| Or(a, b) => {
let r = match (inst, self.get(a), self.get(b)) {
(Add(_, _), Int64(a), Int64(b)) => Int64(a + b),
(Mul(_, _), Int64(a), Int64(b)) => Int64(a * b),
(Div(_, _), Int64(a), Int64(b)) => Int64(a / b),
(Mod(_, _), Int64(a), Int64(b)) => Int64(a % b),
(Pow(_, _), Int64(a), Int64(b)) => Int64(a.pow(b.try_into().unwrap())),
(And(_, _), Bool(a), Bool(b)) => Bool(a && b),
(Or(_, _), Bool(a), Bool(b)) => Bool(a || b),
(Eq(_, _), a, b) => Bool(a == b),
(Gt(_, _), Int64(a), Int64(b)) => Bool(a > b),
(GtEq(_, _), Int64(a), Int64(b)) => Bool(a >= b),
x => unimplemented!("{x:?}"),
};
self.locals.push(r);
}
_ => unimplemented!(),
}
pc += 1;
}
Nil
}
}
pub fn run(insts: &[Inst]) -> Val {
let mut vm = FuncVm::default();
vm.eval_all(insts)
}