clean / prepare for v1 ver
This commit is contained in:
parent
bfafb46f38
commit
3f18c5b02f
647
src/compiler.rs
647
src/compiler.rs
|
|
@ -1,648 +1 @@
|
||||||
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<RefMeta>)>,
|
|
||||||
parent: Option<&'a Scope<'a>>,
|
|
||||||
}
|
|
||||||
impl<'a> Stack<'a> for Scope<'a> {
|
|
||||||
type Value = (Ident, Rc<RefMeta>);
|
|
||||||
type Input = Ident;
|
|
||||||
type Output = Rc<RefMeta>;
|
|
||||||
|
|
||||||
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::Value> {
|
|
||||||
&self.values
|
|
||||||
}
|
|
||||||
fn values_mut(&mut self) -> &mut Vec<Self::Value> {
|
|
||||||
&mut self.values
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_map(_: usize, value: &Self::Value, input: &Self::Input) -> Option<Self::Output> {
|
|
||||||
(value.0 == *input).then_some(value.1.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Scope<'_> {
|
|
||||||
fn assigned(&mut self, id: Ident) -> Rc<RefMeta> {
|
|
||||||
let Some((rm, _)) = self.find(&id) else {
|
|
||||||
let rm: Rc<RefMeta> = Rc::default();
|
|
||||||
self.push((id, rm.clone()));
|
|
||||||
return rm;
|
|
||||||
};
|
|
||||||
rm
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct FuncMeta {
|
|
||||||
pub is_unreturnable: Cell<bool>,
|
|
||||||
}
|
|
||||||
pub type FuncStat = Rc<FuncMeta>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct RefStat {
|
|
||||||
pub now: u16,
|
|
||||||
pub meta: Rc<RefMeta>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct RefMeta {
|
|
||||||
pub total: Cell<u16>,
|
|
||||||
pub is_shared: Cell<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Inst>),
|
|
||||||
/* 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<FSValue>,
|
|
||||||
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::Value> {
|
|
||||||
&self.values
|
|
||||||
}
|
|
||||||
fn values_mut(&mut self) -> &mut Vec<Self::Value> {
|
|
||||||
&mut self.values
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_map(index: usize, value: &Self::Value, input: &Self::Input) -> Option<Self::Output> {
|
|
||||||
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<Inst>,
|
|
||||||
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<Stkval> {
|
|
||||||
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<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(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<Inst> {
|
|
||||||
// translation pass
|
|
||||||
let mut fb = FuncBuild::new_root();
|
|
||||||
fb.translate(e, false, false);
|
|
||||||
fb.insts
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
pub trait Stack<'a>
|
|
||||||
where
|
|
||||||
Self: 'a,
|
|
||||||
{
|
|
||||||
type Value;
|
|
||||||
type Input;
|
|
||||||
type Output;
|
|
||||||
|
|
||||||
fn with_parent(parent: Option<&'a Self>) -> Self;
|
|
||||||
|
|
||||||
fn parent(&self) -> Option<&'a Self>;
|
|
||||||
fn values(&self) -> &Vec<Self::Value>;
|
|
||||||
fn values_mut(&mut self) -> &mut Vec<Self::Value>;
|
|
||||||
|
|
||||||
fn find_map(index: usize, value: &Self::Value, input: &Self::Input) -> Option<Self::Output>;
|
|
||||||
|
|
||||||
fn push(&mut self, value: Self::Value) {
|
|
||||||
self.values_mut().push(value);
|
|
||||||
}
|
|
||||||
fn pop(&mut self, index: usize) {
|
|
||||||
self.values_mut().remove(index);
|
|
||||||
}
|
|
||||||
fn top_index(&self) -> usize {
|
|
||||||
self.values().len() - 1
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
let mut up_levels = 0;
|
|
||||||
|
|
||||||
while let Some(stack) = cur {
|
|
||||||
let Some(output) = stack
|
|
||||||
.values()
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.rev()
|
|
||||||
.find_map(|(index, value)| Self::find_map(index, value, input))
|
|
||||||
else {
|
|
||||||
cur = stack.parent();
|
|
||||||
up_levels += 1;
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
return Some((output, up_levels));
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
60
src/lexer.rs
60
src/lexer.rs
|
|
@ -4,7 +4,7 @@ use std::{
|
||||||
num::{ParseFloatError, ParseIntError},
|
num::{ParseFloatError, ParseIntError},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{compiler::RefStat, kinds};
|
use crate::kinds;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||||
pub struct Ident(String);
|
pub struct Ident(String);
|
||||||
|
|
@ -21,7 +21,7 @@ pub enum Literal {
|
||||||
Float(f64),
|
Float(f64),
|
||||||
Boolean(bool),
|
Boolean(bool),
|
||||||
Nil,
|
Nil,
|
||||||
Ident(Ident, Option<RefStat>),
|
Ident(Ident),
|
||||||
}
|
}
|
||||||
impl fmt::Display for Literal {
|
impl fmt::Display for Literal {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
|
@ -30,19 +30,7 @@ impl fmt::Display for Literal {
|
||||||
Literal::Integer(n) => write!(f, "{n}"),
|
Literal::Integer(n) => write!(f, "{n}"),
|
||||||
Literal::Float(n) => write!(f, "{n}"),
|
Literal::Float(n) => write!(f, "{n}"),
|
||||||
Literal::Boolean(b) => write!(f, "{b}"),
|
Literal::Boolean(b) => write!(f, "{b}"),
|
||||||
Literal::Ident(id, ref_stat) => write!(
|
Literal::Ident(id) => write!(f, "{id}"),
|
||||||
f,
|
|
||||||
"{id}{}",
|
|
||||||
ref_stat
|
|
||||||
.as_ref()
|
|
||||||
.map(|rs| format!(
|
|
||||||
"@{}{}/{}",
|
|
||||||
rs.meta.is_shared.get().then_some("sh+").unwrap_or(""),
|
|
||||||
rs.now,
|
|
||||||
rs.meta.total.get()
|
|
||||||
))
|
|
||||||
.unwrap_or_default()
|
|
||||||
),
|
|
||||||
Literal::Nil => write!(f, "nil"),
|
Literal::Nil => write!(f, "nil"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -86,6 +74,7 @@ kinds!(
|
||||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
|
||||||
pub enum Precedence {
|
pub enum Precedence {
|
||||||
Min,
|
Min,
|
||||||
|
Return,
|
||||||
Assign,
|
Assign,
|
||||||
WithAssign,
|
WithAssign,
|
||||||
Logical,
|
Logical,
|
||||||
|
|
@ -104,9 +93,8 @@ pub enum Associativity {
|
||||||
impl Token {
|
impl Token {
|
||||||
pub fn prefix_precedence(&self) -> Option<Precedence> {
|
pub fn prefix_precedence(&self) -> Option<Precedence> {
|
||||||
Some(match self {
|
Some(match self {
|
||||||
Token::Return | Token::If | Token::Func | Token::Minus | Token::Not => {
|
Token::Return => Precedence::Return,
|
||||||
Precedence::Prefix
|
Token::If | Token::Func | Token::Minus | Token::Not => Precedence::Prefix,
|
||||||
}
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -162,18 +150,14 @@ impl From<ParseFloatError> for LexError {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, LexError>;
|
type Result<T> = std::result::Result<T, LexError>;
|
||||||
|
|
||||||
pub struct Lexer<I>
|
pub struct Lexer<I>
|
||||||
where
|
where
|
||||||
I: Iterator<Item = char>,
|
I: Iterator<Item = char>,
|
||||||
{
|
{
|
||||||
chars: Peekable<I>,
|
chars: Peekable<I>,
|
||||||
}
|
offset: usize,
|
||||||
|
|
||||||
#[allow(clippy::unnecessary_wraps)]
|
|
||||||
fn t(tk: Token) -> Option<Result<Token>> {
|
|
||||||
Some(Ok(tk))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I> Lexer<I>
|
impl<I> Lexer<I>
|
||||||
|
|
@ -181,8 +165,10 @@ where
|
||||||
I: Iterator<Item = char>,
|
I: Iterator<Item = char>,
|
||||||
{
|
{
|
||||||
pub fn new(chars: I) -> Self {
|
pub fn new(chars: I) -> Self {
|
||||||
let chars = chars.peekable();
|
Self {
|
||||||
Self { chars }
|
chars: chars.peekable(),
|
||||||
|
offset: 0,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&mut self) -> Option<char> {
|
fn peek(&mut self) -> Option<char> {
|
||||||
|
|
@ -190,7 +176,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> Option<char> {
|
fn next(&mut self) -> Option<char> {
|
||||||
self.chars.next()
|
self.chars.next().inspect(|_| self.offset += 1)
|
||||||
}
|
}
|
||||||
fn next_unwrap(&mut self) -> char {
|
fn next_unwrap(&mut self) -> char {
|
||||||
match self.next() {
|
match self.next() {
|
||||||
|
|
@ -243,16 +229,17 @@ where
|
||||||
"true" => Token::Literal(Literal::Boolean(true)),
|
"true" => Token::Literal(Literal::Boolean(true)),
|
||||||
"false" => Token::Literal(Literal::Boolean(false)),
|
"false" => Token::Literal(Literal::Boolean(false)),
|
||||||
"nil" => Token::Literal(Literal::Nil),
|
"nil" => Token::Literal(Literal::Nil),
|
||||||
_ => Token::Literal(Literal::Ident(Ident(word), Option::default())),
|
_ => Token::Literal(Literal::Ident(Ident(word))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lex_number(&mut self) -> Result<Token> {
|
fn lex_number(&mut self, is_neg: bool) -> Result<Token> {
|
||||||
let mut n_str = String::new();
|
let mut n_str = String::new();
|
||||||
|
|
||||||
// we don't lex negatives. the impl for that is
|
if is_neg {
|
||||||
// a negation of a positive number at runtime.
|
n_str.push('-');
|
||||||
// maybe that's kind of stupid though, lol
|
}
|
||||||
|
|
||||||
let mut is_float = false;
|
let mut is_float = false;
|
||||||
while let Some('0'..='9' | '.') = self.peek() {
|
while let Some('0'..='9' | '.') = self.peek() {
|
||||||
if self.peek() == Some('.') {
|
if self.peek() == Some('.') {
|
||||||
|
|
@ -296,6 +283,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lex(&mut self) -> Option<Result<Token>> {
|
fn lex(&mut self) -> Option<Result<Token>> {
|
||||||
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
|
fn t(tk: Token) -> Option<Result<Token>> {
|
||||||
|
Some(Ok(tk))
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
break match self.lex_whitespace()? {
|
break match self.lex_whitespace()? {
|
||||||
// { and } start/end of code block
|
// { and } start/end of code block
|
||||||
|
|
@ -315,8 +307,10 @@ where
|
||||||
|
|
||||||
// - subtract
|
// - subtract
|
||||||
// or -= sub eq
|
// or -= sub eq
|
||||||
|
// or 0-9 number
|
||||||
'-' => match self.eat_peek() {
|
'-' => match self.eat_peek() {
|
||||||
Some('=') => self.eat_to(Token::MinusEquals),
|
Some('=') => self.eat_to(Token::MinusEquals),
|
||||||
|
Some('0'..='9' | '.') => Some(self.lex_number(true)),
|
||||||
_ => t(Token::Minus),
|
_ => t(Token::Minus),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -389,7 +383,7 @@ where
|
||||||
'a'..='z' | 'A'..='Z' | '_' => Some(Ok(self.lex_word())),
|
'a'..='z' | 'A'..='Z' | '_' => Some(Ok(self.lex_word())),
|
||||||
|
|
||||||
// 0-9 integer
|
// 0-9 integer
|
||||||
'0'..='9' | '.' => Some(self.lex_number()),
|
'0'..='9' | '.' => Some(self.lex_number(false)),
|
||||||
|
|
||||||
// " strings
|
// " strings
|
||||||
'"' => Some(self.lex_string()),
|
'"' => Some(self.lex_string()),
|
||||||
|
|
|
||||||
25
src/main.rs
25
src/main.rs
|
|
@ -9,7 +9,7 @@ mod parser;
|
||||||
mod vm;
|
mod vm;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// lexer (iterator)
|
// lexer
|
||||||
let script = std::fs::read_to_string("./start.lf").unwrap();
|
let script = std::fs::read_to_string("./start.lf").unwrap();
|
||||||
let lexer = Lexer::new(script.chars());
|
let lexer = Lexer::new(script.chars());
|
||||||
let mut parser = Parser::new(lexer.map(Result::unwrap));
|
let mut parser = Parser::new(lexer.map(Result::unwrap));
|
||||||
|
|
@ -18,26 +18,5 @@ fn main() {
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let block = parser.parse().unwrap();
|
let block = parser.parse().unwrap();
|
||||||
println!("Parse took {:?}", start.elapsed());
|
println!("Parse took {:?}", start.elapsed());
|
||||||
let mut e = parser::Expr::Block(block);
|
parser::util::display(&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());
|
|
||||||
for i in &insts {
|
|
||||||
println!("=> {i:?}");
|
|
||||||
}
|
|
||||||
|
|
||||||
// vm
|
|
||||||
println!("Starting VM!!!!!!!!!!!!!!!!");
|
|
||||||
let start = Instant::now();
|
|
||||||
let out = vm::run(&insts);
|
|
||||||
println!("!! Got result (in {:?}): {out:?}", start.elapsed());
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{fmt, iter::Peekable};
|
use std::{fmt, iter::Peekable};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
compiler::FuncStat,
|
|
||||||
kind::Kind,
|
kind::Kind,
|
||||||
lexer::{Associativity, LexError, Literal, Precedence, Token, TokenKind},
|
lexer::{Associativity, LexError, Literal, Precedence, Token, TokenKind},
|
||||||
};
|
};
|
||||||
|
|
@ -14,8 +13,8 @@ pub enum Expr {
|
||||||
Assign(Box<Expr>, Box<Expr>),
|
Assign(Box<Expr>, Box<Expr>),
|
||||||
Literal(Literal),
|
Literal(Literal),
|
||||||
// Non-literal datatypes
|
// Non-literal datatypes
|
||||||
Block(Block),
|
Block(Vec<Expr>),
|
||||||
Func(Vec<Expr>, Box<Expr>, Option<FuncStat>),
|
Func(Vec<Expr>, Box<Expr>),
|
||||||
// Control flow
|
// Control flow
|
||||||
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
|
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
|
||||||
Return(Box<Expr>),
|
Return(Box<Expr>),
|
||||||
|
|
@ -47,11 +46,6 @@ pub enum Expr {
|
||||||
DivideAssign(Box<Expr>, Box<Expr>),
|
DivideAssign(Box<Expr>, Box<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct Block {
|
|
||||||
pub exprs: Vec<Expr>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ParseError {
|
pub enum ParseError {
|
||||||
UnexpectedToken(Token),
|
UnexpectedToken(Token),
|
||||||
|
|
@ -101,6 +95,15 @@ where
|
||||||
self.tokens.next().ok_or(ParseError::UnexpectedEnd)
|
self.tokens.next().ok_or(ParseError::UnexpectedEnd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn expect_peek(&mut self, kind: TokenKind) -> Result<()> {
|
||||||
|
let t = self.try_peek()?;
|
||||||
|
|
||||||
|
if t.kind() != kind {
|
||||||
|
return Err(ParseError::UnexpectedToken(self.next_unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
fn expect_next(&mut self, kind: TokenKind) -> Result<()> {
|
fn expect_next(&mut self, kind: TokenKind) -> Result<()> {
|
||||||
let t = self.try_next()?;
|
let t = self.try_next()?;
|
||||||
|
|
||||||
|
|
@ -110,6 +113,7 @@ where
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_next(&mut self, kind: Option<TokenKind>) -> bool {
|
fn is_next(&mut self, kind: Option<TokenKind>) -> bool {
|
||||||
match self.try_peek() {
|
match self.try_peek() {
|
||||||
Ok(t) if Some(t.kind()) == kind => true,
|
Ok(t) if Some(t.kind()) == kind => true,
|
||||||
|
|
@ -138,7 +142,7 @@ where
|
||||||
let exprs = self.parse_until(Some(TokenKind::CurlyClose))?;
|
let exprs = self.parse_until(Some(TokenKind::CurlyClose))?;
|
||||||
// skip curly brace
|
// skip curly brace
|
||||||
self.eat();
|
self.eat();
|
||||||
Box::new(Expr::Block(Block { exprs }))
|
Box::new(Expr::Block(exprs))
|
||||||
}
|
}
|
||||||
|
|
||||||
// unary ops!! (prefix)
|
// unary ops!! (prefix)
|
||||||
|
|
@ -155,7 +159,7 @@ where
|
||||||
// parse body
|
// parse body
|
||||||
let body = self.parse_expr(prec, in_group)?;
|
let body = self.parse_expr(prec, in_group)?;
|
||||||
// pack
|
// pack
|
||||||
Box::new(Expr::Func(args, body, None))
|
Box::new(Expr::Func(args, body))
|
||||||
}
|
}
|
||||||
// parse if
|
// parse if
|
||||||
Token::If => {
|
Token::If => {
|
||||||
|
|
@ -193,6 +197,7 @@ where
|
||||||
};
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
// look for op, end, or special
|
||||||
let op = match self.try_peek() {
|
let op = match self.try_peek() {
|
||||||
// end (group)
|
// end (group)
|
||||||
Ok(Token::ParenClose) if in_group => break,
|
Ok(Token::ParenClose) if in_group => break,
|
||||||
|
|
@ -223,6 +228,7 @@ where
|
||||||
Ok(_) => break,
|
Ok(_) => break,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// get op precedence
|
||||||
let (prec, assoc) = op.infix_precedence().unwrap();
|
let (prec, assoc) = op.infix_precedence().unwrap();
|
||||||
|
|
||||||
// break if this op is meant for previous recursion
|
// break if this op is meant for previous recursion
|
||||||
|
|
@ -296,10 +302,9 @@ where
|
||||||
let mut exprs = Vec::new();
|
let mut exprs = Vec::new();
|
||||||
|
|
||||||
while !self.is_next(until) {
|
while !self.is_next(until) {
|
||||||
// skip delimiter
|
// skip delimiter (if it's not the first iteration)
|
||||||
if self.is_next(Some(delim)) {
|
if !exprs.is_empty() && self.is_next(Some(delim)) {
|
||||||
self.eat();
|
self.eat();
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to parse expr
|
// try to parse expr
|
||||||
|
|
@ -311,13 +316,12 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for delim
|
// check for delim
|
||||||
self.expect_next(delim)?;
|
self.expect_peek(delim)?;
|
||||||
}
|
}
|
||||||
Ok(exprs)
|
Ok(exprs)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(&mut self) -> Result<Block> {
|
pub fn parse(&mut self) -> Result<Expr> {
|
||||||
let exprs = self.parse_until(None)?;
|
Ok(Expr::Block(self.parse_until(None)?))
|
||||||
Ok(Block { exprs })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ fn fmt_expr(e: &Expr, depth: usize) -> String {
|
||||||
Expr::Return(l) => format!("return ({})", fmt_expr(l, depth)),
|
Expr::Return(l) => format!("return ({})", fmt_expr(l, depth)),
|
||||||
Expr::Block(b) => {
|
Expr::Block(b) => {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
let len = b.exprs.len();
|
let len = b.len();
|
||||||
for (i, expr) in b.exprs.iter().enumerate() {
|
for (i, expr) in b.iter().enumerate() {
|
||||||
result.push_str(&" ".repeat(depth));
|
result.push_str(&" ".repeat(depth));
|
||||||
result.push_str(&fmt_expr(expr, depth + 1));
|
result.push_str(&fmt_expr(expr, depth + 1));
|
||||||
if depth != 0 || i + 1 != len {
|
if depth != 0 || i + 1 != len {
|
||||||
|
|
@ -60,20 +60,12 @@ fn fmt_expr(e: &Expr, depth: usize) -> String {
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Expr::Func(a, e, func_stat) => format!(
|
Expr::Func(a, e) => format!(
|
||||||
"(func({}){} ({}))",
|
"(func({}) ({}))",
|
||||||
a.iter()
|
a.iter()
|
||||||
.map(|e| fmt_expr(e, depth))
|
.map(|e| fmt_expr(e, depth))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", "),
|
.join(", "),
|
||||||
func_stat
|
|
||||||
.as_ref()
|
|
||||||
.map(|fm| fm
|
|
||||||
.is_unreturnable
|
|
||||||
.get()
|
|
||||||
.then_some("@UNRET")
|
|
||||||
.unwrap_or("@OK"))
|
|
||||||
.unwrap_or_default(),
|
|
||||||
fmt_expr(e, depth)
|
fmt_expr(e, depth)
|
||||||
),
|
),
|
||||||
Expr::Negate(l) => format!("(-{})", fmt_expr(l, depth)),
|
Expr::Negate(l) => format!("(-{})", fmt_expr(l, depth)),
|
||||||
|
|
|
||||||
152
src/vm.rs
152
src/vm.rs
|
|
@ -1,153 +1 @@
|
||||||
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];
|
|
||||||
|
|
||||||
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