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:
12
src/compile/error.rs
Normal file
12
src/compile/error.rs
Normal 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
74
src/compile/mod.rs
Normal 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
228
src/compile/thunk.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user