diff --git a/src/compile/mod.rs b/src/compile/mod.rs new file mode 100644 index 0000000..e73a458 --- /dev/null +++ b/src/compile/mod.rs @@ -0,0 +1,42 @@ +pub mod sym; + +use crate::{ + syn::ast::Stmt, + compile::sym::SymStack, +}; + +pub trait Pass { + type Out; + fn pass(&self, ctx: &mut Ctx) -> Self::Out; +} + +// * Desugar +// * Collect names as symbols +// * Create basic blocks + +pub struct Ctx { + sym_stack: SymStack, +} + +impl Ctx { + pub fn sym_stack(&self) -> &SymStack { + &self.sym_stack + } + + pub fn sym_stack_mut(&mut self) -> &mut SymStack { + &mut self.sym_stack + } +} + +// no sugar in the syntax quite yet +/* +pub struct Desugar; + +impl Pass> for Desugar { + type Out = Vec; + + fn pass(&mut self, input: Vec) -> Vec { + input + } +} +*/ diff --git a/src/compile/sym.rs b/src/compile/sym.rs new file mode 100644 index 0000000..f30e01c --- /dev/null +++ b/src/compile/sym.rs @@ -0,0 +1,16 @@ +use crate::obj::Sym; +use std::collections::HashMap; + +pub struct SymStack { + syms: HashMap, + stack: Vec>, +} + +impl SymStack { + pub fn new() -> Self { + SymStack { + syms: Default::default(), + stack: vec![Default::default()], + } + } +} diff --git a/src/main.rs b/src/main.rs index 690b804..e1bb2ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,11 @@ #![allow(dead_code)] #![feature(new_uninit)] +mod compile; mod syn; mod obj; mod mem; +mod vm; use std::{ convert::TryFrom, diff --git a/src/mem/gc.rs b/src/mem/gc.rs index 84c64a8..71534ac 100644 --- a/src/mem/gc.rs +++ b/src/mem/gc.rs @@ -5,7 +5,6 @@ use crate::{ intern::Intern, } }; -use std::ptr::NonNull; pub trait Gc: Intern { fn alloc(&mut self, obj: O) -> ObjRef; diff --git a/src/mem/ptr.rs b/src/mem/ptr.rs index c38784b..32daaf9 100644 --- a/src/mem/ptr.rs +++ b/src/mem/ptr.rs @@ -1,6 +1,6 @@ use crate::obj::Obj; use std::{ - any, + any::{self, Any}, cell::RefCell, fmt::{self, Debug, Formatter}, ops::Deref, @@ -10,13 +10,14 @@ use std::{ pub type DynRef = ObjRef; pub struct ObjRef - where O: Obj + ?Sized +where O: Obj + ?Sized, { ptr: NonNull>, } impl ObjRef - where O: Obj + ?Sized +where + O: Obj + ?Sized, { pub fn new(ptr: NonNull>) -> Self { ObjRef { ptr } @@ -27,8 +28,16 @@ impl ObjRef } } +impl AsRef> for ObjRef { + fn as_ref(&self) -> &ObjCell { + // safe because pointer should point at live data assuming GC is working appropriately + unsafe { &*self.as_ptr() } + } +} + impl ObjRef - where O: Obj +where + O: Obj, { pub fn as_dyn(&self) -> ObjRef { let ptr = self.as_ptr() as *const ObjCell; @@ -37,7 +46,8 @@ impl ObjRef } impl Deref for ObjRef - where O: Obj + ?Sized +where + O: Obj + ?Sized, { type Target = RefCell; @@ -48,28 +58,37 @@ impl Deref for ObjRef } impl Clone for ObjRef - where O: Obj + ?Sized +where + O: Obj + ?Sized, { fn clone(&self) -> Self { ObjRef { ptr: self.ptr } } } -impl Copy for ObjRef - where O: Obj + ?Sized -{} +impl Copy for ObjRef where O: Obj + ?Sized {} impl Debug for ObjRef - where O: Obj + Debug + ?Sized +where + O: Obj + Debug + ?Sized, { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - write!(fmt, "<{} object at {:#x}>", any::type_name::(), self.as_ptr() as *const () as usize) + write!( + fmt, + "<{} object at {:#x}>", + any::type_name::(), + self.as_ptr() as *const () as usize + ) } } impl Debug for ObjRef { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { - write!(fmt, "", self.as_ptr() as *const () as usize) + write!( + fmt, + "", + self.as_ptr() as *const () as usize + ) } } diff --git a/src/obj/attrs.rs b/src/obj/attrs.rs index 505bcbf..a0c7d38 100644 --- a/src/obj/attrs.rs +++ b/src/obj/attrs.rs @@ -104,7 +104,7 @@ impl<'ctx> AttrsBuilder<'ctx> { } } - pub fn attr(mut self, symbol_name: &str, value: DynRef) -> Self { + pub fn attr(self, symbol_name: &str, value: DynRef) -> Self { let sym = self.ctx.gc_mut().add_sym(symbol_name); self.attr_sym(sym, value) } diff --git a/src/obj/fun.rs b/src/obj/fun.rs new file mode 100644 index 0000000..8fd86a1 --- /dev/null +++ b/src/obj/fun.rs @@ -0,0 +1,92 @@ +use crate::{obj::prelude::*, vm::op::Op}; +use std::{any::Any, cell::Cell, rc::Rc}; + +pub type NativeFunPtr = fn(argv: Vec); + +pub struct NativeFun { + attrs: ObjRef, + fn_ptr: NativeFunPtr, +} + +impl NativeFun { + pub fn new(ctx: &mut ObjCtx, name: StrRef, fn_ptr: NativeFunPtr) -> Self { + let attrs = Attrs::new(ctx, Default::default()); + NativeFun { + attrs, + fn_ptr, + } + } + + pub fn call(&self, argv: Vec) { + (self.fn_ptr)(argv) + } +} + +impl Obj for NativeFun { + fn attrs(&self) -> ObjRef { + self.attrs + } + + fn is_marked(&self) -> bool { + // native functions are always available + true + } + + fn mark(&self) {} + + fn unmark(&self) {} + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +pub struct Fun { + attrs: ObjRef, + code: Rc>, + marked: Cell, +} + +impl Fun { + pub fn new(attrs: ObjRef, code: Rc>) -> Self { + Fun { + attrs, + code, + marked: Cell::new(false), + } + } + + pub fn code(&self) -> Rc> { + Rc::clone(&self.code) + } +} + +impl Obj for Fun { + fn attrs(&self) -> ObjRef { + self.attrs + } + + fn is_marked(&self) -> bool { + self.marked.get() + } + + fn mark(&self) { + if self.is_marked() { + return; + } + self.marked.set(true); + self.attrs.borrow().mark(); + } + + fn unmark(&self) { + if !self.is_marked() { + return; + } + self.marked.set(false); + self.attrs.borrow().unmark(); + } + + fn as_any(&self) -> &dyn Any { + self + } +} diff --git a/src/obj/mod.rs b/src/obj/mod.rs index 98b6c81..56c4b0b 100644 --- a/src/obj/mod.rs +++ b/src/obj/mod.rs @@ -2,7 +2,7 @@ pub mod attrs; pub mod ctx; pub mod dict; pub mod error; -//pub mod native_fun; +pub mod fun; //pub mod num; pub mod str; pub mod sym { @@ -13,7 +13,7 @@ pub mod sym { pub mod prelude { pub use crate::{ obj::{ - Attrs, Dict, DictRef, Str, StrRef, Sym, ObjCtx, Obj, + Attrs, Dict, DictRef, Fun, NativeFun, Str, StrRef, Sym, ObjCtx, Obj, }, mem::{ ptr::{ObjRef, DynRef}, @@ -25,8 +25,9 @@ pub mod prelude { pub use self::{ attrs::Attrs, - dict::{Dict, DictRef}, ctx::ObjCtx, + dict::{Dict, DictRef}, + fun::{NativeFun, Fun}, str::{Str, StrRef}, sym::Sym, }; diff --git a/src/obj/native_fun.rs b/src/obj/native_fun.rs deleted file mode 100644 index 413bdab..0000000 --- a/src/obj/native_fun.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::obj::prelude::*; - -pub type NativeFunPtr = *const fn(); - -pub struct NativeFun { - attrs: Attrs, - fn_ptr: NativeFunPtr, -} - -impl NativeFun { - pub fn new(ctx: &mut ObjCtx, name: StrRef, fn_ptr: NativeFunPtr) -> Self { - todo!() - /* - let attrs = Attrs::default(); - NativeFun { - attrs, - fn_ptr, - } - */ - } -} -/* - -impl Obj for NativeFun { - fn get_attr(&self, key: &DynRef) -> Option -} -*/ diff --git a/src/obj/str.rs b/src/obj/str.rs index 986b8a8..a187166 100644 --- a/src/obj/str.rs +++ b/src/obj/str.rs @@ -1,6 +1,6 @@ -use crate::obj::{attrs::AttrsBuilder, prelude::*}; +use crate::obj::prelude::*; use derivative::Derivative; -use std::{any::Any, cell::Cell, collections::BTreeMap}; +use std::{any::Any, cell::Cell}; pub type StrRef = ObjRef; diff --git a/src/vm/frame.rs b/src/vm/frame.rs new file mode 100644 index 0000000..043e51f --- /dev/null +++ b/src/vm/frame.rs @@ -0,0 +1,49 @@ +use crate::obj::prelude::*; +use std::collections::BTreeMap; + +#[derive(Default)] +pub struct Frame { + stack: Vec, + ip: usize, + return_value: Option, + locals: BTreeMap, +} + +impl Frame { + pub fn new() -> Self { + Default::default() + } + + pub fn push(&mut self, obj_ref: DynRef) { + self.stack.push(obj_ref) + } + + pub fn pop(&mut self) -> Option { + self.stack.pop() + } + + pub fn ip(&self) -> usize { + self.ip + } + + pub fn set_ip(&mut self, ip: usize) { + self.ip = ip; + } + + pub fn return_value(&self) -> Option { + self.return_value + } + + pub fn set_return_value(&mut self, obj_ref: DynRef) { + self.return_value = Some(obj_ref); + } + + pub fn get_local(&self, sym: Sym) -> Option { + self.locals.get(&sym).copied() + } + + pub fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option { + self.locals.insert(sym, obj_ref) + } +} + diff --git a/src/vm/mod.rs b/src/vm/mod.rs new file mode 100644 index 0000000..53dedb2 --- /dev/null +++ b/src/vm/mod.rs @@ -0,0 +1,131 @@ +pub mod op; +pub mod frame; + +use crate::{ + obj::prelude::*, + vm::{ + op::Op, + frame::Frame, + } +}; +use std::rc::Rc; + +pub struct State { + frames: Vec, +} + +impl State { + pub fn new() -> Self { + State { frames: Default::default(), } + } + + fn frame(&self) -> &Frame { + self.frames + .last() + .unwrap() + } + + fn frame_mut(&mut self) -> &mut Frame { + self.frames + .last_mut() + .unwrap() + } + + fn push_stack(&mut self, value: DynRef) { + self.frame_mut() + .push(value); + } + + fn pop_stack(&mut self) -> Option { + self.frame_mut() + .pop() + } + + fn ip(&self) -> usize { + self.frame().ip() + } + + fn set_ip(&mut self, ip: usize) { + self.frame_mut().set_ip(ip); + } + + fn get_local(&self, sym: Sym) -> Option { + self.frame().get_local(sym) + } + + fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option { + self.frame_mut().set_local(sym, obj_ref) + } + + pub fn run(&mut self, ops: Rc>) { + self.frames.push(Default::default()); + while self.ip() < ops.len() { + let mut next_ip = self.ip() + 1; + match &ops[self.ip()] { + Op::Push(sym) => { + // TODO - local not found + let obj_ref = self.get_local(*sym) + .expect("TODO - local not found"); + self.push_stack(obj_ref); + } + Op::Pop(sym) => { + // stack should not underflow + let obj_ref = self.pop_stack() + .expect("misaligned stack for pop"); + if let Some(sym) = *sym { + self.set_local(sym, obj_ref); + } + } + Op::GetAttr(sym) => { + let top = self.pop_stack() + .expect("misaligned stack for getattrs"); + let obj_ref = { + let top_ref = top.borrow(); + let attrs = top_ref.attrs(); + let attrs_ref = attrs.borrow(); + // TODO - local not found + attrs_ref.get(*sym) + .expect("TODO - local not found") + }; + self.push_stack(obj_ref); + } + Op::Call(argc) => { + let fun = self.pop_stack() + .expect("misaligned stack for function call"); + let mut argv = Vec::with_capacity(*argc); + for i in 0 .. *argc { + let arg = self.pop_stack() + .expect("misaligned stack for argv"); + argv.push(arg); + } + // reverse since arguments are pushed in order of being passed + argv.reverse(); + + // call function + // TODO - call the function indirectly? + // + // problem: downcast_ref returns a reference bound by the lifetime of the + // function (because of the ref cell). This keeps the fun object borrowed, + // which means any attempts to modify the function while it is running will + // cause the VM to panic. + // + // possible solution: + // pop the function object, and then determine which function to call on the + // object (possibly after downcasting?) + // + // TODO + // Figure out a way to either call a native function with NativeFun::call, or + // call a user function with Fun::code() + if let Some(fun) = fun.borrow().as_any().downcast_ref::() { + } else if let Some(fun) = fun.borrow().as_any().downcast_ref::() { + } else { + todo!("TODO - not a function") + } + todo!() + } + } + self.set_ip(next_ip); + } + self.frames.pop(); + } +} diff --git a/src/vm/op.rs b/src/vm/op.rs new file mode 100644 index 0000000..5f047d5 --- /dev/null +++ b/src/vm/op.rs @@ -0,0 +1,22 @@ +use crate::obj::Sym; + +// VM execution model: +// * every function call has its own stack frame +// * stack frames keep track of locals +// * ops deal with symbols directly +// * stack values are either obj references or symbols + +pub enum Op { + /// Push a value from a symbol + Push(Sym), + + /// Pop the top stack value into a local (if supplied) + Pop(Option), + + /// Pop the top stack value, getting an attribute and replacing pushing that value to the + /// stack. + GetAttr(Sym), + + /// Pop the top stack value and the number of arguments, and attempt to call the function. + Call(usize), +}