General changes across the runtime crate in support of compile module

* Compile module is able to compile bytecode (or so it seems...)
* Runtime crate has had some new stuff added to it, mostly with objects
  and vtables. Still not 100% on the object method function call story,
  but I guess it'll be tackled when we get there.

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-09-14 14:09:29 -07:00
parent e2c43dc911
commit 8e2cbb10a4
17 changed files with 743 additions and 175 deletions

12
src/compile/error.rs Normal file
View File

@@ -0,0 +1,12 @@
use snafu::Snafu;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("invalid assignment target"))]
InvalidLhs,
#[snafu(display("invalid object attribute"))]
InvalidAttr,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;

74
src/compile/mod.rs Normal file
View File

@@ -0,0 +1,74 @@
pub mod thunk;
pub mod error;
use runtime::{obj::prelude::*, vm::consts::*};
use std::collections::HashMap;
pub struct Compile<'t> {
text: &'t str,
const_data: ConstData,
}
impl<'t> Compile<'t> {
pub fn new(text: &'t str) -> Self {
Compile { text, const_data: Default::default() }
}
pub fn text(&self) -> &'t str {
self.text
}
pub fn const_data(&self) -> &ConstData {
&self.const_data
}
pub fn const_data_mut(&mut self) -> &mut ConstData {
&mut self.const_data
}
/// Gets or inserts a static int reference.
pub fn const_int(&mut self, int: i64) -> (ConstHandle, IntRef) {
self.const_data_mut()
.const_int(int)
}
/// Gets or inserts a static string reference.
pub fn const_str(&mut self, s: impl AsRef<str>) -> (ConstHandle, StrRef) {
self.const_data_mut()
.const_str(s)
}
}
#[derive(Debug, Default)]
pub struct ConstData {
ints: HashMap<i64, (ConstHandle, IntRef)>,
strs: HashMap<String, (ConstHandle, StrRef)>,
const_pool: ConstPool,
}
impl ConstData {
/// Gets or inserts a static int reference.
pub fn const_int(&mut self, int: i64) -> (ConstHandle, IntRef) {
if let Some(pair) = self.ints.get(&int) {
pair.clone()
} else {
let obj = Int::new_obj(int);
let hdl = self.const_pool.push(obj.clone());
self.ints.insert(int, (hdl, obj.clone()));
(hdl, obj)
}
}
/// Gets or inserts a static string reference.
pub fn const_str(&mut self, s: impl AsRef<str>) -> (ConstHandle, StrRef) {
let s = s.as_ref();
if let Some(pair) = self.strs.get(s) {
pair.clone()
} else {
let obj = Str::new_obj(s.to_string());
let hdl = self.const_pool.push(obj.clone());
self.strs.insert(s.to_string(), (hdl, obj.clone()));
(hdl, obj)
}
}
}

228
src/compile/thunk.rs Normal file
View File

@@ -0,0 +1,228 @@
use crate::{
compile::{error::*, Compile},
syn::{ast::*, visit::*},
};
use runtime::{obj::prelude::*, vm::inst::*};
use std::mem;
pub enum Thunk {
Body(Vec<Inst>),
List(Vec<Thunk>),
Branch {
thunk_true: Box<Thunk>,
thunk_false: Box<Thunk>,
},
Nop,
}
impl Thunk {
pub fn push(&mut self, append: impl Into<Inst>) {
self.push_thunk(Thunk::Body(vec![append.into()]))
}
pub fn push_thunk(&mut self, append: impl Into<Thunk>) {
let append = append.into();
let inner = mem::replace(self, Thunk::Nop);
*self = match (inner, append) {
// X + Nop = X
(lhs, Thunk::Nop) => lhs,
(Thunk::Nop, rhs) => rhs,
// Body + Body = Body
(Thunk::Body(mut lhs), Thunk::Body(rhs)) => {
lhs.extend(rhs);
Thunk::Body(lhs)
}
// List + List = List
(Thunk::List(mut lhs), Thunk::List(rhs)) => {
lhs.extend(rhs);
Thunk::List(lhs)
}
// List + X = List
(Thunk::List(mut lhs), rhs) => {
lhs.push(rhs);
Thunk::List(lhs)
}
// X + List = List
(lhs, Thunk::List(mut rhs)) => {
rhs.insert(0, lhs);
Thunk::List(rhs)
}
// X + X = List
(lhs, rhs) => Thunk::List(vec![lhs, rhs]),
};
}
}
impl From<Inst> for Thunk {
fn from(other: Inst) -> Self {
Self::from(vec![other])
}
}
impl From<Vec<Inst>> for Thunk {
fn from(other: Vec<Inst>) -> Self {
Thunk::Body(other)
}
}
impl From<Vec<Thunk>> for Thunk {
fn from(other: Vec<Thunk>) -> Self {
Thunk::List(other)
}
}
pub struct CompileBody<'c, 't> {
compile: &'c mut Compile<'t>,
}
impl<'c, 't> CompileBody<'c, 't> {
pub fn new(compile: &'c mut Compile<'t>) -> Self {
CompileBody { compile }
}
pub fn compile(&mut self, body: &'c Body) -> Result<Thunk> {
self.visit_body(body)
}
}
impl Visit for CompileBody<'_, '_> {
type Out = Result<Thunk>;
fn visit_body(&mut self, body: &Body) -> Self::Out {
let mut thunk = Thunk::Nop;
for stmt in body.body.iter() {
thunk.push_thunk(stmt.accept(self)?);
}
Ok(thunk)
}
fn visit_stmt(&mut self, stmt: &Stmt) -> Self::Out {
DefaultAccept::default_accept(stmt, self)
}
fn visit_assign_stmt(&mut self, assign: &AssignStmt) -> Self::Out {
// - push rhs
// - push lhs (which handles the assignment)
let mut thunk = self.visit_expr(&assign.rhs)?;
thunk.push_thunk(self.visit_lhs_expr(&assign.lhs)?);
Ok(thunk)
}
fn visit_lhs_expr(&mut self, lhs_expr: &LhsExpr) -> Self::Out {
// Do different things depending on the LHS
let mut thunk;
match &lhs_expr {
LhsExpr::SetAttr(expr) => {
// - push lhs expression (without accessor)
// - setattr (access) NOTE : rhs should already be on stack
thunk = self.visit_expr(&expr.expr)?;
let attr = global_sym(expr.access.to_string());
thunk.push(Inst::SetAttr(attr));
}
LhsExpr::Local(local) => {
let local = global_sym(local.to_string());
thunk = Inst::Pop(Some(local)).into();
}
}
Ok(thunk)
}
fn visit_expr(&mut self, expr: &Expr) -> Self::Out {
DefaultAccept::default_accept(expr, self)
}
fn visit_bin_expr(&mut self, expr: &BinExpr) -> Self::Out {
// - push lhs
// - push rhs
// - call operator's function
let mut thunk = self.visit_expr(&expr.lhs)?;
thunk.push_thunk(self.visit_expr(&expr.rhs)?);
let inst = match expr.op {
BinOp::Plus => Inst::BinPlus,
BinOp::Minus => Inst::BinMinus,
BinOp::Times => Inst::BinMul,
BinOp::Div => Inst::BinDiv,
BinOp::Eq => Inst::BinEq,
BinOp::Neq => Inst::BinNeq,
BinOp::Lt => Inst::BinLt,
BinOp::Le => Inst::BinLe,
BinOp::Gt => Inst::BinGt,
BinOp::Ge => Inst::BinGe,
BinOp::And => Inst::BinAnd,
BinOp::Or => Inst::BinOr,
};
thunk.push(inst);
Ok(thunk)
}
fn visit_un_expr(&mut self, expr: &UnExpr) -> Self::Out {
// - push expr
// - call operator's function
let mut thunk = self.visit_expr(&expr.expr)?;
match expr.op {
UnOp::Plus => thunk.push(Inst::UnPos),
UnOp::Minus => thunk.push(Inst::UnNeg),
}
Ok(thunk)
}
fn visit_call_expr(&mut self, expr: &CallExpr) -> Self::Out {
// - push expr
// - push args in order
// - call function
let mut thunk = self.visit_expr(&expr.expr)?;
for arg in expr.args.iter() {
thunk.push_thunk(self.visit_expr(&arg)?);
}
Ok(thunk)
}
fn visit_index_expr(&mut self, expr: &IndexExpr) -> Self::Out {
// - eval expr
// - eval index
// - index
let mut thunk = self.visit_expr(&expr.expr)?;
thunk.push_thunk(self.visit_expr(&expr.index)?);
thunk.push(Inst::Index);
Ok(thunk)
}
fn visit_access_expr(&mut self, expr: &AccessExpr) -> Self::Out {
// - eval expr
// - getattr (expr.access)
let mut thunk = self.visit_expr(&expr.expr)?;
thunk.push_thunk(Thunk::Body(vec![
Inst::GetAttr(global_sym(expr.access.to_string())),
]));
Ok(thunk)
}
fn visit_atom(&mut self, atom: &Atom) -> Self::Out {
let thunk = match atom {
Atom::Ident(ident) => {
// get local
Inst::PushLocal(global_sym(ident.to_string())).into()
}
Atom::Sym(sym) => {
// push symbol
Inst::PushSym(global_sym(sym.clone())).into()
}
Atom::Num(num) => {
// push const
let (hdl, _) = self.compile.const_int(*num);
Inst::PushConst(hdl).into()
}
Atom::String(s) => {
// push const
let (hdl, _) = self.compile.const_str(s);
Inst::PushConst(hdl).into()
}
};
Ok(thunk)
}
}