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? */
|
/* 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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