fix a lot of bugs + add test vm + funcs work
This commit is contained in:
parent
1509c3e1b2
commit
4fa6cd9ae0
154
src/compiler.rs
154
src/compiler.rs
|
|
@ -193,14 +193,15 @@ fn analyze(fs: &FuncStat, scope: &mut Scope, e: &mut Expr, gets_captured: bool)
|
|||
/* 1b is up? */
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum Stkval {
|
||||
/* 4b blank ; 8b offset */
|
||||
/* 12b offset */
|
||||
Local(u8),
|
||||
/* 4b up levels ; 8b offset */
|
||||
Shared(u8, u8),
|
||||
}
|
||||
/* 3b type tag */
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Val {
|
||||
/* where? ; pop after use? */
|
||||
Stack(Stkval, bool),
|
||||
/* u16 len, LEN data */
|
||||
String(String),
|
||||
|
|
@ -215,32 +216,32 @@ pub enum Val {
|
|||
/* ... */
|
||||
Nil,
|
||||
}
|
||||
/* 5b inst type */
|
||||
#[derive(Debug)]
|
||||
/* 3b inst type */
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Inst {
|
||||
/* ... */
|
||||
/* is shared? ; val to copy */
|
||||
Copy(bool, Val),
|
||||
/* pop a1? ; pop a2? */
|
||||
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),
|
||||
/* ... */
|
||||
/* where to write, val to write */
|
||||
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),
|
||||
/* 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.
|
||||
|
|
@ -322,6 +323,11 @@ impl<'a> FuncBuild<'a> {
|
|||
fn top(&self) -> Stkval {
|
||||
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.
|
||||
fn push_any(&mut self) -> Stkval {
|
||||
|
|
@ -329,19 +335,40 @@ impl<'a> FuncBuild<'a> {
|
|||
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 {
|
||||
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);
|
||||
|
||||
// Don't compute anything unnecessarily
|
||||
|
|
@ -349,19 +376,19 @@ impl<'a> FuncBuild<'a> {
|
|||
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)
|
||||
}
|
||||
fn gen_binop(
|
||||
&mut self,
|
||||
l: Expr,
|
||||
r: Expr,
|
||||
f: impl Fn(bool, bool, Val, Val) -> Inst,
|
||||
f: impl Fn(Val, Val) -> Inst,
|
||||
do_compute: bool,
|
||||
) -> Val {
|
||||
let (v1, v2) = (
|
||||
let (mut v1, mut v2) = (
|
||||
self.translate(l, do_compute, false),
|
||||
self.translate(r, do_compute, false),
|
||||
);
|
||||
|
|
@ -371,14 +398,14 @@ impl<'a> FuncBuild<'a> {
|
|||
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)
|
||||
}
|
||||
fn gen_copy(&mut self, v1: Val) -> Val {
|
||||
let a1 = self.check_drop(&v1);
|
||||
self.insts.push(Inst::Copy(a1, v1));
|
||||
self.check_drop(&v1);
|
||||
self.insts.push(Inst::Copy(false, v1));
|
||||
Val::Stack(self.push_any(), false)
|
||||
}
|
||||
|
||||
|
|
@ -420,18 +447,25 @@ impl<'a> FuncBuild<'a> {
|
|||
Val::Nil
|
||||
}
|
||||
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
|
||||
let n_args = args.len();
|
||||
for arg in args {
|
||||
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
|
||||
// if we are computing a value or yielding one, then yes
|
||||
let push = do_compute || do_yield;
|
||||
|
||||
// add call
|
||||
self.insts.push(Inst::Call(push, a1, v1));
|
||||
self.insts.push(Inst::Call(push, n_args as u8, v1));
|
||||
|
||||
// whatever we output
|
||||
if push {
|
||||
Val::Stack(self.push_any(), true)
|
||||
|
|
@ -455,7 +489,9 @@ impl<'a> FuncBuild<'a> {
|
|||
|
||||
/* vars */
|
||||
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::Assign(l, r) => {
|
||||
|
|
@ -469,8 +505,7 @@ impl<'a> FuncBuild<'a> {
|
|||
// if this isn't getting used for computation OR referenced,
|
||||
// just continue translation without adding to stack
|
||||
if !(do_compute || gets_referenced) {
|
||||
// do_compute doesn't matter for literals
|
||||
self.translate(*r, true, false)
|
||||
self.translate(*r, false, false)
|
||||
} else {
|
||||
// get val
|
||||
let val = match *r {
|
||||
|
|
@ -500,16 +535,29 @@ impl<'a> FuncBuild<'a> {
|
|||
if let Some(sv) = self.find(&id) {
|
||||
// yes, move it there
|
||||
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
|
||||
Val::Stack(sv, gets_referenced)
|
||||
} else {
|
||||
// no it doesn't
|
||||
if matches!(val, Val::Stack(_, _)) {
|
||||
// it is a stackval so keep track of it
|
||||
self.local.swap_top(FSValue::Var(id));
|
||||
return Val::Stack(sv, should_pop);
|
||||
} else if matches!(val, Val::Stack(_, _)) {
|
||||
// no, keep track of new stackval
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,13 @@ where
|
|||
fn swap_top(&mut self, new: Self::Value) {
|
||||
*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)> {
|
||||
let mut cur = Some(self);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ mod compiler;
|
|||
mod kind;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
mod vm;
|
||||
|
||||
fn main() {
|
||||
let script = std::fs::read_to_string("./start.lf").unwrap();
|
||||
|
|
@ -26,7 +27,12 @@ fn main() {
|
|||
let start = Instant::now();
|
||||
let insts = compiler::translation_demo(e);
|
||||
println!("Translation took {:?}", start.elapsed());
|
||||
for i in insts {
|
||||
for i in &insts {
|
||||
println!("=> {i:?}");
|
||||
}
|
||||
|
||||
println!("Starting VM!!!!!!!!!!!!!!!!");
|
||||
let start = Instant::now();
|
||||
let out = vm::run(&insts);
|
||||
println!("!! Got result (in {:?}): {out:?}", start.elapsed());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
Loading…
Reference in New Issue