diff --git a/src/compiler.rs b/src/compiler.rs index 43243c7..9445078 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -1,649 +1,648 @@ -use std::{cell::Cell, rc::Rc}; - -use crate::{ - lexer::{Ident, Literal}, - parser::Expr, -}; - -use stack::Stack; - -mod stack; - -struct Scope<'a> { - values: Vec<(Ident, Rc)>, - parent: Option<&'a Scope<'a>>, -} -impl<'a> Stack<'a> for Scope<'a> { - type Value = (Ident, Rc); - type Input = Ident; - type Output = Rc; - - fn with_parent(parent: Option<&'a Self>) -> Self { - Self { - values: Vec::new(), - parent, - } - } - fn parent(&self) -> Option<&'a Self> { - self.parent - } - fn values(&self) -> &Vec { - &self.values - } - fn values_mut(&mut self) -> &mut Vec { - &mut self.values - } - - fn find_map(_: usize, value: &Self::Value, input: &Self::Input) -> Option { - (value.0 == *input).then_some(value.1.clone()) - } -} -impl Scope<'_> { - fn assigned(&mut self, id: Ident) -> Rc { - let Some((rm, _)) = self.find(&id) else { - let rm: Rc = Rc::default(); - self.push((id, rm.clone())); - return rm; - }; - rm - } -} - -#[derive(Debug, Default, Clone)] -pub struct FuncMeta { - pub is_unreturnable: Cell, -} -pub type FuncStat = Rc; - -#[derive(Debug, Clone)] -pub struct RefStat { - pub now: u16, - pub meta: Rc, -} - -#[derive(Debug, Default)] -pub struct RefMeta { - pub total: Cell, - pub is_shared: Cell, -} - -fn analyze(fs: &FuncStat, scope: &mut Scope, e: &mut Expr, gets_captured: bool) { - match e { - Expr::Assign(a, b) => { - let Expr::Literal(Literal::Ident(id, ref_stat)) = &mut **a else { - panic!("invalid assignment"); - }; - - // add to scope - let rm = scope.assigned(id.clone()); - - // add ref stat - *ref_stat = Some(RefStat { - now: rm.total.get(), - meta: rm, - }); - - // analyse the value - analyze(fs, scope, b, true); - } - Expr::Literal(Literal::Ident(id, ref_stat)) => { - // lookup ident - let Some((rm, up_levels)) = scope.find(id) else { - panic!("unfound variable") - }; - - // the var got used, so the compiler will gen code for it - // it is okay to count - if gets_captured { - // increment # of uses - rm.total.update(|c| c + 1); - - // if we used something external to this scope, note it - if up_levels != 0 { - fs.is_unreturnable.set(true); - rm.is_shared.set(true); - } - } - - // set ref meta - *ref_stat = Some(RefStat { - now: rm.total.get(), - meta: rm.clone(), - }); - } - // ignore - Expr::Literal(_) => {} - // for recursion.. - Expr::Block(a) => { - // blocks have their own scope - let mut scope = Scope::with_parent(Some(scope)); - // last is treated differently - let last = a.exprs.pop(); - // analyze the contents in the new scope - for e in &mut a.exprs { - analyze(fs, &mut scope, e, false); - } - // analyze last - if let Some(mut last) = last { - analyze(fs, &mut scope, &mut last, gets_captured); - a.exprs.push(last); - } - } - Expr::Func(a, b, func_stat) => { - // new function new context - let fs = FuncStat::default(); - *func_stat = Some(fs.clone()); - // functions have their own scope, because they have args - let mut scope = Scope::with_parent(Some(scope)); - - // init args - for e in a { - let Expr::Literal(Literal::Ident(id, _)) = e else { - panic!("invalid arg def"); - }; - scope.assigned(id.clone()); - } - - // now analyze the body in the new scope - analyze(&fs, &mut scope, b, true); - } - Expr::If(a, b, c) => { - analyze(fs, scope, a, true); - analyze(fs, scope, b, gets_captured); - if let Some(c) = c { - analyze(fs, scope, c, gets_captured); - } - } - Expr::Call(a, b) => { - analyze(fs, scope, a, true); - for e in b { - analyze(fs, scope, e, true); - } - } - Expr::Return(a) | Expr::Negate(a) | Expr::Not(a) => analyze(fs, scope, a, true), - Expr::EqualTo(a, b) - | Expr::NotEqualTo(a, b) - | Expr::And(a, b) - | Expr::Or(a, b) - | Expr::LessThan(a, b) - | Expr::LessThanOrEqualTo(a, b) - | Expr::GreaterThan(a, b) - | Expr::GreaterThanOrEqualTo(a, b) - | Expr::Add(a, b) - | Expr::Subtract(a, b) - | Expr::Multiply(a, b) - | Expr::Divide(a, b) - | Expr::Exponent(a, b) - | Expr::Modulo(a, b) => { - analyze(fs, scope, a, gets_captured); - analyze(fs, scope, b, gets_captured); - } - Expr::AddAssign(a, b) - | Expr::SubtractAssign(a, b) - | Expr::MultiplyAssign(a, b) - | Expr::DivideAssign(a, b) => { - analyze(fs, scope, a, true); - analyze(fs, scope, b, true); - } - } -} - -// --- translate pass --- // - -/* 1b is up? */ -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum Stkval { - /* 12b offset */ - Local(u8), - /* 4b up levels ; 8b offset */ - Shared(u8, u8), -} -/* 3b type tag */ -#[derive(Debug, Clone, PartialEq)] -pub enum Val { - /* where? ; pop after use? */ - Stack(Stkval, bool), - /* u16 len, LEN data */ - String(String), - /* 1b returnability, 4b arity, insts */ - Func(bool, u8, Vec), - /* 1b value */ - Bool(bool), - /* i64 data */ - Int64(i64), - /* f64 data */ - Float64(f64), - /* ... */ - Nil, -} -/* 3b inst type */ -#[derive(Debug, Clone, PartialEq)] -pub enum Inst { - /* is shared? ; val to copy */ - Copy(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. -#[derive(Debug)] -enum FSValue { - Var(Ident), - Any, -} - -/// A stack that keeps track of values during translation. -/// (Local or shared) -struct FakeStack<'a> { - values: Vec, - parent: Option<&'a FakeStack<'a>>, -} -impl<'a> Stack<'a> for FakeStack<'a> { - type Value = FSValue; - type Input = Ident; - type Output = usize; - - fn with_parent(parent: Option<&'a Self>) -> Self { - Self { - values: Vec::new(), - parent, - } - } - fn parent(&self) -> Option<&'a Self> { - self.parent - } - fn values(&self) -> &Vec { - &self.values - } - fn values_mut(&mut self) -> &mut Vec { - &mut self.values - } - - fn find_map(index: usize, value: &Self::Value, input: &Self::Input) -> Option { - matches!(value, FSValue::Var(x) if x == input).then_some(index) - } -} - -/// Build scope of a function. -/// -/// * Starts with a base block scope that includes any arguments passed -/// * Contains the compiled instructions of all blocks inside it -/// * Keeps track of its own shared stack (contains vars that higher functions access) -/// * Keeps track of its own local stack (vars only used locally) -struct FuncBuild<'a> { - insts: Vec, - shared: FakeStack<'a>, - local: FakeStack<'a>, -} - -impl<'a> FuncBuild<'a> { - fn new_root() -> Self { - FuncBuild { - insts: Vec::new(), - shared: FakeStack::with_parent(None), - local: FakeStack::with_parent(None), - } - } - fn with_parent(parent: &'a FuncBuild<'a>) -> Self { - FuncBuild { - insts: Vec::new(), - shared: FakeStack::with_parent(Some(&parent.shared)), - local: FakeStack::with_parent(None), - } - } - - fn find(&mut self, id: &Ident) -> Option { - self.shared - .find(id) - .map(|(count, up_levels)| Stkval::Shared(up_levels as u8, count as u8)) - .or_else(|| self.local.find(id).map(|(c, _)| Stkval::Local(c as u8))) - } - - /// Returns stackval for top item of stack. - /// (Panics if empty) - 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 { - self.local.push(FSValue::Any); - self.top() - } - - fn check_drop(&mut self, v: &Val) { - if let Val::Stack(Stkval::Local(i), true) = v { - self.local.pop(*i as usize); - } - } - - fn check_drops(&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(Val) -> Inst, do_compute: bool) -> Val { - let v1 = self.translate(r, do_compute, false); - - // Don't compute anything unnecessarily - if !do_compute { - return Val::Nil; - } - - self.check_drop(&v1); - - self.insts.push(f(v1)); - Val::Stack(self.push_any(), true) - } - fn gen_binop( - &mut self, - l: Expr, - r: Expr, - f: impl Fn(Val, Val) -> Inst, - do_compute: bool, - ) -> Val { - let (mut v1, mut v2) = ( - self.translate(l, do_compute, false), - self.translate(r, do_compute, false), - ); - - // If this is unused, do not generate code - if !do_compute { - return Val::Nil; - } - - self.check_drops([&mut v1, &mut v2]); - - self.insts.push(f(v1, v2)); - Val::Stack(self.push_any(), true) - } - fn gen_copy(&mut self, v1: Val) -> Val { - self.check_drop(&v1); - self.insts.push(Inst::Copy(false, v1)); - Val::Stack(self.push_any(), false) - } - - fn translate(&mut self, e: Expr, do_compute: bool, do_yield: bool) -> Val { - match e { - /* organisational */ - Expr::Block(mut b) => { - let last = b.exprs.pop(); - for e in b.exprs { - self.translate(e, false, false); - } - // compute/yield last expr if requested - last.map_or(Val::Nil, |e| self.translate(e, do_compute, do_yield)) - } - Expr::Func(args, expr, fs) => { - // neww function!!!! - let mut fb = FuncBuild::with_parent(self); - // push args to stack - let arity = args.len() as u8; - for arg in args { - let Expr::Literal(Literal::Ident(id, _)) = arg else { - unreachable!() - }; - fb.local.push(FSValue::Var(id)); - } - // translate expr - fb.translate(*expr, true, false); - // pack - let returnability = fs.unwrap().is_unreturnable.get(); - Val::Func(returnability, arity, fb.insts) - } - - /* control flow */ - Expr::Return(r) => { - // calculate return value - let v1 = self.translate(*r, true, false); - // add return inst, eval to nil - self.insts.push(Inst::Return(v1)); - Val::Nil - } - Expr::Call(func, args) => { - // 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, n_args as u8, v1)); - - // whatever we output - if push { - Val::Stack(self.push_any(), true) - } else { - Val::Nil - } - } - - /* captured literal */ - Expr::Literal(lit) if do_yield => { - let v1 = self.translate(Expr::Literal(lit), true, false); - self.gen_copy(v1) - } - - /* 1 to 1 literals */ - Expr::Literal(Literal::Boolean(b)) => Val::Bool(b), - Expr::Literal(Literal::Float(f)) => Val::Float64(f), - Expr::Literal(Literal::Integer(i)) => Val::Int64(i), - Expr::Literal(Literal::Nil) => Val::Nil, - Expr::Literal(Literal::String(s)) => Val::String(s), - - /* vars */ - Expr::Literal(Literal::Ident(id, Some(rs))) if do_compute => { - 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) => { - let Expr::Literal(Literal::Ident(id, Some(ref_stat))) = *l else { - unreachable!() - }; - - // will the var ever get referenced? - let gets_referenced = ref_stat.now != ref_stat.meta.total.get(); - - // if this isn't getting used for computation OR referenced, - // just continue translation without adding to stack - if !(do_compute || gets_referenced) { - self.translate(*r, false, false) - } else { - // get val - let val = match *r { - // the var's value is a literal - // if the var gets used as a var, yield to stack - // otherwise just return the literal - Expr::Literal(lit) => { - self.translate(Expr::Literal(lit), true, gets_referenced) - } - - // value is an expr - // compute it and yield to stack if it gets used - e => self.translate(e, true, gets_referenced), - }; - - // handle value - let val = match val { - // apply appropriate drop rule - Val::Stack(sv, _) => Val::Stack(sv, gets_referenced), - // non-literal type - val if matches!(val, Val::Func(_, _, _)) => self.gen_copy(val), - // okay as-is - val => val, - }; - - // check if var already exists - 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 - 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 - } - } - - /* math */ - Expr::Add(l, r) => self.gen_binop(*l, *r, Inst::Add, do_compute), - Expr::Multiply(l, r) => self.gen_binop(*l, *r, Inst::Mul, do_compute), - Expr::Divide(l, r) => self.gen_binop(*l, *r, Inst::Div, do_compute), - Expr::Modulo(l, r) => self.gen_binop(*l, *r, Inst::Mod, do_compute), - Expr::Exponent(l, r) => self.gen_binop(*l, *r, Inst::Pow, do_compute), - - /* math assignments */ - Expr::AddAssign(l, r) => self.translate( - Expr::Assign(l.clone(), Box::new(Expr::Add(l, r))), - do_compute, - do_yield, - ), - Expr::SubtractAssign(l, r) => self.translate( - Expr::Assign(l.clone(), Box::new(Expr::Subtract(l, r))), - do_compute, - do_yield, - ), - Expr::MultiplyAssign(l, r) => self.translate( - Expr::Assign(l.clone(), Box::new(Expr::Multiply(l, r))), - do_compute, - do_yield, - ), - Expr::DivideAssign(l, r) => self.translate( - Expr::Assign(l.clone(), Box::new(Expr::Divide(l, r))), - do_compute, - do_yield, - ), - - /* math substitutions */ - Expr::Negate(r) if do_compute => { - // negate - match *r { - // statically - Expr::Literal(Literal::Integer(i)) => Val::Int64(-i), - Expr::Literal(Literal::Float(f)) => Val::Float64(-f), - - // at runtime - e => { - let e = Box::new(e); - let minus_one = Box::new(Expr::Literal(Literal::Integer(-1))); - self.translate(Expr::Multiply(e, minus_one), do_compute, do_yield) - } - } - } - Expr::Negate(_) => Val::Nil, - Expr::Subtract(l, r) => self.translate( - Expr::Add(l, Box::new(Expr::Negate(r))), - do_compute, - do_yield, - ), - - /* logic */ - Expr::And(l, r) => self.gen_binop(*l, *r, Inst::And, do_compute), - Expr::Or(l, r) => self.gen_binop(*l, *r, Inst::Or, do_compute), - Expr::EqualTo(l, r) => self.gen_binop(*l, *r, Inst::Eq, do_compute), - Expr::GreaterThan(l, r) => self.gen_binop(*l, *r, Inst::Gt, do_compute), - Expr::GreaterThanOrEqualTo(l, r) => self.gen_binop(*l, *r, Inst::GtEq, do_compute), - Expr::Not(r) => self.gen_unop(*r, Inst::Not, do_compute), - - /* logic substitutions */ - Expr::NotEqualTo(l, r) => { - self.translate(Expr::Not(Box::new(Expr::EqualTo(l, r))), do_compute, false) - } - Expr::LessThan(l, r) => self.translate(Expr::GreaterThan(r, l), do_compute, false), - Expr::LessThanOrEqualTo(l, r) => { - self.translate(Expr::GreaterThanOrEqualTo(r, l), do_compute, false) - } - - e => unimplemented!("{e:?}"), - } - } -} - -pub fn analysis_demo(e: &mut Expr) { - // analysis pass - let fs = FuncStat::default(); - let mut scope = Scope::with_parent(None); - analyze(&fs, &mut scope, e, false); -} -pub fn translation_demo(e: Expr) -> Vec { - // translation pass - let mut fb = FuncBuild::new_root(); - fb.translate(e, false, false); - fb.insts -} +use std::{cell::Cell, rc::Rc}; + +use crate::{ + lexer::{Ident, Literal}, + parser::Expr, +}; + +use stack::Stack; + +mod stack; + +struct Scope<'a> { + values: Vec<(Ident, Rc)>, + parent: Option<&'a Scope<'a>>, +} +impl<'a> Stack<'a> for Scope<'a> { + type Value = (Ident, Rc); + type Input = Ident; + type Output = Rc; + + fn with_parent(parent: Option<&'a Self>) -> Self { + Self { + values: Vec::new(), + parent, + } + } + fn parent(&self) -> Option<&'a Self> { + self.parent + } + fn values(&self) -> &Vec { + &self.values + } + fn values_mut(&mut self) -> &mut Vec { + &mut self.values + } + + fn find_map(_: usize, value: &Self::Value, input: &Self::Input) -> Option { + (value.0 == *input).then_some(value.1.clone()) + } +} +impl Scope<'_> { + fn assigned(&mut self, id: Ident) -> Rc { + let Some((rm, _)) = self.find(&id) else { + let rm: Rc = Rc::default(); + self.push((id, rm.clone())); + return rm; + }; + rm + } +} + +#[derive(Debug, Default, Clone)] +pub struct FuncMeta { + pub is_unreturnable: Cell, +} +pub type FuncStat = Rc; + +#[derive(Debug, Clone)] +pub struct RefStat { + pub now: u16, + pub meta: Rc, +} + +#[derive(Debug, Default)] +pub struct RefMeta { + pub total: Cell, + pub is_shared: Cell, +} + +fn analyze(fs: &FuncStat, scope: &mut Scope, e: &mut Expr, gets_captured: bool) { + match e { + Expr::Assign(a, b) => { + let Expr::Literal(Literal::Ident(id, ref_stat)) = &mut **a else { + panic!("invalid assignment"); + }; + + // add to scope + let rm = scope.assigned(id.clone()); + + // add ref stat + *ref_stat = Some(RefStat { + now: rm.total.get(), + meta: rm, + }); + + // analyse the value + analyze(fs, scope, b, true); + } + Expr::Literal(Literal::Ident(id, ref_stat)) => { + // lookup ident + let Some((rm, up_levels)) = scope.find(id) else { + panic!("unfound variable") + }; + + // the var got used, so the compiler will gen code for it + // it is okay to count + if gets_captured { + // increment # of uses + rm.total.update(|c| c + 1); + + // if we used something external to this scope, note it + if up_levels != 0 { + fs.is_unreturnable.set(true); + rm.is_shared.set(true); + } + } + + // set ref meta + *ref_stat = Some(RefStat { + now: rm.total.get(), + meta: rm.clone(), + }); + } + // ignore + Expr::Literal(_) => {} + // for recursion.. + Expr::Block(a) => { + // blocks have their own scope + let mut scope = Scope::with_parent(Some(scope)); + // last is treated differently + let last = a.exprs.pop(); + // analyze the contents in the new scope + for e in &mut a.exprs { + analyze(fs, &mut scope, e, false); + } + // analyze last + if let Some(mut last) = last { + analyze(fs, &mut scope, &mut last, gets_captured); + a.exprs.push(last); + } + } + Expr::Func(a, b, func_stat) => { + // new function new context + let fs = FuncStat::default(); + *func_stat = Some(fs.clone()); + // functions have their own scope, because they have args + let mut scope = Scope::with_parent(Some(scope)); + + // init args + for e in a { + let Expr::Literal(Literal::Ident(id, _)) = e else { + panic!("invalid arg def"); + }; + scope.assigned(id.clone()); + } + + // now analyze the body in the new scope + analyze(&fs, &mut scope, b, true); + } + Expr::If(a, b, c) => { + analyze(fs, scope, a, true); + analyze(fs, scope, b, gets_captured); + if let Some(c) = c { + analyze(fs, scope, c, gets_captured); + } + } + Expr::Call(a, b) => { + analyze(fs, scope, a, true); + for e in b { + analyze(fs, scope, e, true); + } + } + Expr::Return(a) | Expr::Negate(a) | Expr::Not(a) => analyze(fs, scope, a, true), + Expr::EqualTo(a, b) + | Expr::NotEqualTo(a, b) + | Expr::And(a, b) + | Expr::Or(a, b) + | Expr::LessThan(a, b) + | Expr::LessThanOrEqualTo(a, b) + | Expr::GreaterThan(a, b) + | Expr::GreaterThanOrEqualTo(a, b) + | Expr::Add(a, b) + | Expr::Subtract(a, b) + | Expr::Multiply(a, b) + | Expr::Divide(a, b) + | Expr::Exponent(a, b) + | Expr::Modulo(a, b) => { + analyze(fs, scope, a, gets_captured); + analyze(fs, scope, b, gets_captured); + } + Expr::AddAssign(a, b) + | Expr::SubtractAssign(a, b) + | Expr::MultiplyAssign(a, b) + | Expr::DivideAssign(a, b) => { + analyze(fs, scope, a, true); + analyze(fs, scope, b, true); + } + } +} + +// --- translate pass --- // + +/* 1b is up? */ +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum Stkval { + /* 12b offset */ + Local(u8), + /* 4b up levels ; 8b offset */ + Shared(u8, u8), +} +/* 3b type tag */ +#[derive(Debug, Clone, PartialEq)] +pub enum Val { + /* where? ; pop after use? */ + Stack(Stkval, bool), + /* u16 len, LEN data */ + String(String), + /* 1b returnability, 4b arity, insts */ + Func(bool, u8, Vec), + /* 1b value */ + Bool(bool), + /* i64 data */ + Int64(i64), + /* f64 data */ + Float64(f64), + /* ... */ + Nil, +} +/* 3b inst type */ +#[derive(Debug, Clone, PartialEq)] +pub enum Inst { + /* is shared? ; val to copy */ + Copy(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. +#[derive(Debug)] +enum FSValue { + Var(Ident), + Any, +} + +/// A stack that keeps track of values during translation. +/// (Local or shared) +struct FakeStack<'a> { + values: Vec, + parent: Option<&'a FakeStack<'a>>, +} +impl<'a> Stack<'a> for FakeStack<'a> { + type Value = FSValue; + type Input = Ident; + type Output = usize; + + fn with_parent(parent: Option<&'a Self>) -> Self { + Self { + values: Vec::new(), + parent, + } + } + fn parent(&self) -> Option<&'a Self> { + self.parent + } + fn values(&self) -> &Vec { + &self.values + } + fn values_mut(&mut self) -> &mut Vec { + &mut self.values + } + + fn find_map(index: usize, value: &Self::Value, input: &Self::Input) -> Option { + matches!(value, FSValue::Var(x) if x == input).then_some(index) + } +} + +/// Build scope of a function. +/// +/// * Starts with a base block scope that includes any arguments passed +/// * Contains the compiled instructions of all blocks inside it +/// * Keeps track of its own shared stack (contains vars that higher functions access) +/// * Keeps track of its own local stack (vars only used locally) +struct FuncBuild<'a> { + insts: Vec, + shared: FakeStack<'a>, + local: FakeStack<'a>, +} + +impl<'a> FuncBuild<'a> { + fn new_root() -> Self { + FuncBuild { + insts: Vec::new(), + shared: FakeStack::with_parent(None), + local: FakeStack::with_parent(None), + } + } + fn with_parent(parent: &'a FuncBuild<'a>) -> Self { + FuncBuild { + insts: Vec::new(), + shared: FakeStack::with_parent(Some(&parent.shared)), + local: FakeStack::with_parent(None), + } + } + + fn find(&mut self, id: &Ident) -> Option { + self.shared + .find(id) + .map(|(count, up_levels)| Stkval::Shared(up_levels as u8, count as u8)) + .or_else(|| self.local.find(id).map(|(c, _)| Stkval::Local(c as u8))) + } + + /// Returns stackval for top item of stack. + /// (Panics if empty) + 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 { + self.local.push(FSValue::Any); + self.top() + } + + fn check_drop(&mut self, v: &Val) { + if let Val::Stack(Stkval::Local(i), true) = v { + self.local.pop(*i as usize); + } + } + + fn check_drops(&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(Val) -> Inst, do_compute: bool) -> Val { + let v1 = self.translate(r, do_compute, false); + + // Don't compute anything unnecessarily + if !do_compute { + return Val::Nil; + } + + self.check_drop(&v1); + + self.insts.push(f(v1)); + Val::Stack(self.push_any(), true) + } + fn gen_binop( + &mut self, + l: Expr, + r: Expr, + f: impl Fn(Val, Val) -> Inst, + do_compute: bool, + ) -> Val { + let (mut v1, mut v2) = ( + self.translate(l, do_compute, false), + self.translate(r, do_compute, false), + ); + + // If this is unused, do not generate code + if !do_compute { + return Val::Nil; + } + + self.check_drops([&mut v1, &mut v2]); + + self.insts.push(f(v1, v2)); + Val::Stack(self.push_any(), true) + } + fn gen_copy(&mut self, v1: Val) -> Val { + self.check_drop(&v1); + self.insts.push(Inst::Copy(false, v1)); + Val::Stack(self.push_any(), false) + } + + fn translate(&mut self, e: Expr, do_compute: bool, do_yield: bool) -> Val { + match e { + /* organisational */ + Expr::Block(mut b) => { + let last = b.exprs.pop(); + for e in b.exprs { + self.translate(e, false, false); + } + // compute/yield last expr if requested + last.map_or(Val::Nil, |e| self.translate(e, do_compute, do_yield)) + } + Expr::Func(args, expr, fs) => { + // neww function!!!! + let mut fb = FuncBuild::with_parent(self); + // push args to stack + let arity = args.len() as u8; + for arg in args { + let Expr::Literal(Literal::Ident(id, _)) = arg else { + unreachable!() + }; + fb.local.push(FSValue::Var(id)); + } + // translate expr + fb.translate(*expr, true, false); + // pack + let returnability = fs.unwrap().is_unreturnable.get(); + Val::Func(returnability, arity, fb.insts) + } + + /* control flow */ + Expr::Return(r) => { + // calculate return value + let v1 = self.translate(*r, true, false); + // add return inst, eval to nil + self.insts.push(Inst::Return(v1)); + Val::Nil + } + Expr::Call(func, args) => { + // 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, n_args as u8, v1)); + + // whatever we output + if push { + Val::Stack(self.push_any(), true) + } else { + Val::Nil + } + } + Expr::If(cond, true_case, false_case) => todo!(), + + /* captured literal */ + Expr::Literal(lit) if do_yield => { + let v1 = self.translate(Expr::Literal(lit), true, false); + self.gen_copy(v1) + } + + /* 1 to 1 literals */ + Expr::Literal(Literal::Boolean(b)) => Val::Bool(b), + Expr::Literal(Literal::Float(f)) => Val::Float64(f), + Expr::Literal(Literal::Integer(i)) => Val::Int64(i), + Expr::Literal(Literal::Nil) => Val::Nil, + Expr::Literal(Literal::String(s)) => Val::String(s), + + /* vars */ + Expr::Literal(Literal::Ident(id, Some(rs))) if do_compute => { + 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) => { + let Expr::Literal(Literal::Ident(id, Some(ref_stat))) = *l else { + unreachable!() + }; + + // will the var ever get referenced? + let gets_referenced = ref_stat.now != ref_stat.meta.total.get(); + + // if this isn't getting used for computation OR referenced, + // just continue translation without adding to stack + if !(do_compute || gets_referenced) { + self.translate(*r, false, false) + } else { + // get val + let val = match *r { + // the var's value is a literal + // if the var gets used as a var, yield to stack + // otherwise just return the literal + Expr::Literal(lit) => { + self.translate(Expr::Literal(lit), true, gets_referenced) + } + + // value is an expr + // compute it and yield to stack if it gets used + e => self.translate(e, true, gets_referenced), + }; + + // handle value + let val = match val { + // apply appropriate drop rule + Val::Stack(sv, _) => Val::Stack(sv, gets_referenced), + // non-literal type + val if matches!(val, Val::Func(_, _, _)) => self.gen_copy(val), + // okay as-is + val => val, + }; + + // check if var already exists + 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 + 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 + } + } + + /* math */ + Expr::Add(l, r) => self.gen_binop(*l, *r, Inst::Add, do_compute), + Expr::Multiply(l, r) => self.gen_binop(*l, *r, Inst::Mul, do_compute), + Expr::Divide(l, r) => self.gen_binop(*l, *r, Inst::Div, do_compute), + Expr::Modulo(l, r) => self.gen_binop(*l, *r, Inst::Mod, do_compute), + Expr::Exponent(l, r) => self.gen_binop(*l, *r, Inst::Pow, do_compute), + + /* math assignments */ + Expr::AddAssign(l, r) => self.translate( + Expr::Assign(l.clone(), Box::new(Expr::Add(l, r))), + do_compute, + do_yield, + ), + Expr::SubtractAssign(l, r) => self.translate( + Expr::Assign(l.clone(), Box::new(Expr::Subtract(l, r))), + do_compute, + do_yield, + ), + Expr::MultiplyAssign(l, r) => self.translate( + Expr::Assign(l.clone(), Box::new(Expr::Multiply(l, r))), + do_compute, + do_yield, + ), + Expr::DivideAssign(l, r) => self.translate( + Expr::Assign(l.clone(), Box::new(Expr::Divide(l, r))), + do_compute, + do_yield, + ), + + /* math substitutions */ + Expr::Negate(r) if do_compute => { + // negate + match *r { + // statically + Expr::Literal(Literal::Integer(i)) => Val::Int64(-i), + Expr::Literal(Literal::Float(f)) => Val::Float64(-f), + + // at runtime + e => { + let e = Box::new(e); + let minus_one = Box::new(Expr::Literal(Literal::Integer(-1))); + self.translate(Expr::Multiply(e, minus_one), do_compute, do_yield) + } + } + } + Expr::Negate(_) => Val::Nil, + Expr::Subtract(l, r) => self.translate( + Expr::Add(l, Box::new(Expr::Negate(r))), + do_compute, + do_yield, + ), + + /* logic */ + Expr::And(l, r) => self.gen_binop(*l, *r, Inst::And, do_compute), + Expr::Or(l, r) => self.gen_binop(*l, *r, Inst::Or, do_compute), + Expr::EqualTo(l, r) => self.gen_binop(*l, *r, Inst::Eq, do_compute), + Expr::GreaterThan(l, r) => self.gen_binop(*l, *r, Inst::Gt, do_compute), + Expr::GreaterThanOrEqualTo(l, r) => self.gen_binop(*l, *r, Inst::GtEq, do_compute), + Expr::Not(r) => self.gen_unop(*r, Inst::Not, do_compute), + + /* logic substitutions */ + Expr::NotEqualTo(l, r) => { + self.translate(Expr::Not(Box::new(Expr::EqualTo(l, r))), do_compute, false) + } + Expr::LessThan(l, r) => self.translate(Expr::GreaterThan(r, l), do_compute, false), + Expr::LessThanOrEqualTo(l, r) => { + self.translate(Expr::GreaterThanOrEqualTo(r, l), do_compute, false) + } + } + } +} + +pub fn analysis_demo(e: &mut Expr) { + // analysis pass + let fs = FuncStat::default(); + let mut scope = Scope::with_parent(None); + analyze(&fs, &mut scope, e, false); +} +pub fn translation_demo(e: Expr) -> Vec { + // translation pass + let mut fb = FuncBuild::new_root(); + fb.translate(e, false, false); + fb.insts +} diff --git a/src/main.rs b/src/main.rs index c5e9065..6009c3d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,21 +9,25 @@ mod parser; mod vm; fn main() { + // lexer (iterator) let script = std::fs::read_to_string("./start.lf").unwrap(); let lexer = Lexer::new(script.chars()); let mut parser = Parser::new(lexer.map(Result::unwrap)); + // parser let start = Instant::now(); let block = parser.parse().unwrap(); println!("Parse took {:?}", start.elapsed()); let mut e = parser::Expr::Block(block); parser::util::display(&e); + // compiler - analysis let start = Instant::now(); compiler::analysis_demo(&mut e); println!("Analysis took {:?}", start.elapsed()); parser::util::display(&e); + // compiler - translation let start = Instant::now(); let insts = compiler::translation_demo(e); println!("Translation took {:?}", start.elapsed()); @@ -31,6 +35,7 @@ fn main() { println!("=> {i:?}"); } + // vm println!("Starting VM!!!!!!!!!!!!!!!!"); let start = Instant::now(); let out = vm::run(&insts); diff --git a/src/vm.rs b/src/vm.rs index d3ffe05..172c7c7 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -1,155 +1,153 @@ -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>, - locals: Vec, -} -impl<'a> FuncVm<'a> { - fn with(parent_vm: &'a FuncVm<'a>, locals: Vec) -> 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) -} +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>, + locals: Vec, +} +impl<'a> FuncVm<'a> { + fn with(parent_vm: &'a FuncVm<'a>, locals: Vec) -> 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]; + + 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) +}