Changes all around - objects and GC

Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
This commit is contained in:
2020-05-16 18:46:09 -04:00
parent a15dde0fc2
commit d5cf39108b
15 changed files with 223 additions and 203 deletions

View File

@@ -1,5 +1,5 @@
#![allow(dead_code)] #![allow(dead_code)]
#![feature(new_uninit)] #![feature(unsize, coerce_unsized, new_uninit)]
mod compile; mod compile;
mod syn; mod syn;

View File

@@ -2,17 +2,15 @@ use crate::{
mem::{ mem::{
gc::Gc, gc::Gc,
intern::Intern, intern::Intern,
ptr::{DynRef, ObjCell, ObjRef}, ptr::ObjCell,
}, },
obj::{ctx::ObjCtx, Obj, Str, Sym}, obj::prelude::*,
}; };
use std::{collections::HashMap, ptr::NonNull}; use std::{collections::HashMap, ptr::NonNull};
#[derive(Default)] #[derive(Default)]
pub struct BasicGc { pub struct BasicGc {
objects: Vec<Box<ObjCell<dyn Obj>>>, objects: Vec<Box<ObjCell<dyn Obj>>>,
strs: HashMap<String, ObjRef<Str>>,
syms: HashMap<String, Sym>,
} }
impl Gc for BasicGc { impl Gc for BasicGc {
@@ -23,7 +21,7 @@ impl Gc for BasicGc {
ObjRef::new(NonNull::new(obj_ptr).unwrap()) ObjRef::new(NonNull::new(obj_ptr).unwrap())
} }
fn add_obj<O: Obj + 'static>(&mut self, owned: Box<ObjCell<O>>) { fn add_obj(&mut self, owned: Box<ObjCell<dyn Obj>>) {
self.objects.push(owned); self.objects.push(owned);
} }
@@ -47,24 +45,23 @@ impl Gc for BasicGc {
} }
} }
impl Intern for BasicGc { pub struct BasicIntern {
fn get_str(&self, s: &str) -> Option<ObjRef<Str>> { strs: HashMap<String, ObjRef<Str>>,
self.strs.get(s).copied() syms: HashMap<String, Sym>,
} }
fn add_str(&mut self, ctx: &mut ObjCtx, s: &str) -> ObjRef<Str> { impl Intern for BasicIntern {
*self.strs.entry(s.to_string()) fn intern_str<G: Gc>(&mut self, gc: &mut G, s: &str) -> StrRef {
.or_insert_with(|| { if let Some(obj_ref) = self.strs.get(s) {
let s = Str::new(ctx, s.to_string()); *obj_ref
ctx.gc_mut().alloc(s) } else {
}) todo!("TODO(obj) intern strings - need attrs somehow")
//let s = Str::new(&mut ctx, s.to_string());
//ctx.gc_mut().alloc(s)
}
} }
fn get_sym(&self, s: &str) -> Option<Sym> { fn intern_sym(&mut self, s: &str) -> Sym {
self.syms.get(s).copied()
}
fn add_sym(&mut self, s: &str) -> Sym {
let len = self.syms.len(); let len = self.syms.len();
*self.syms.entry(s.to_string()) *self.syms.entry(s.to_string())
.or_insert_with(|| len) .or_insert_with(|| len)

View File

@@ -2,14 +2,13 @@ use crate::{
obj::prelude::*, obj::prelude::*,
mem::{ mem::{
ptr::ObjCell, ptr::ObjCell,
intern::Intern,
} }
}; };
pub trait Gc: Intern { pub trait Gc {
fn alloc<O: Obj + 'static>(&mut self, obj: O) -> ObjRef<O>; fn alloc<O: Obj + 'static>(&mut self, obj: O) -> ObjRef<O>;
fn add_obj<O: Obj + 'static>(&mut self, owned: Box<ObjCell<O>>); fn add_obj(&mut self, owned: Box<ObjCell<dyn Obj>>);
fn mark(&mut self, alive: &[DynRef]); fn mark(&mut self, alive: &[DynRef]);

View File

@@ -1,15 +1,7 @@
use crate::{ use crate::obj::prelude::*;
mem::ptr::ObjRef,
obj::{
ctx::ObjCtx,
Str, Sym,
},
};
pub trait Intern { pub trait Intern {
fn get_str(&self, s: &str) -> Option<ObjRef<Str>>; fn intern_str<G: Gc>(&mut self, gc: &mut G, s: &str) -> StrRef;
fn add_str(&mut self, ctx: &mut ObjCtx, s: &str) -> ObjRef<Str>;
fn get_sym(&self, s: &str) -> Option<Sym>; fn intern_sym(&mut self, s: &str) -> Sym;
fn add_sym(&mut self, s: &str) -> Sym;
} }

View File

@@ -1,16 +1,16 @@
mod basic; pub mod basic;
pub mod intern;
pub mod gc; pub mod gc;
pub mod intern;
pub mod ptr; pub mod ptr;
pub use self::basic::BasicGc; pub use basic::BasicGc;
/*
pub mod prelude { pub mod prelude {
pub use crate::mem::{ pub use crate::mem::{
alloc::Alloc, BasicGc,
gc::Gc, gc::Gc,
ptr::* intern::Intern,
ptr::{DynRef, ObjRef},
};
} }
}
*/

View File

@@ -1,9 +1,10 @@
use crate::obj::Obj; use crate::obj::Obj;
use std::{ use std::{
any::{self, Any}, any,
cell::RefCell, cell::RefCell,
fmt::{self, Debug, Formatter}, fmt::{self, Debug, Formatter},
ops::Deref, marker::Unsize,
ops::{CoerceUnsized, Deref},
ptr::NonNull, ptr::NonNull,
}; };
@@ -124,6 +125,16 @@ where
} }
} }
impl<T, U> CoerceUnsized<ObjRef<U>> for ObjRef<T>
where T: Obj + Unsize<U> + ?Sized,
U: Obj + ?Sized
{}
impl<T, U> CoerceUnsized<ObjCell<U>> for ObjCell<T>
where T: CoerceUnsized<U> + Obj,
U: Obj
{}
// //
// ObjCell // ObjCell
// //

View File

@@ -4,28 +4,27 @@ use crate::{
}; };
use std::{ use std::{
cell::Cell, cell::Cell,
collections::BTreeMap,
mem::MaybeUninit, mem::MaybeUninit,
ptr::{self, NonNull}, ptr::{self, NonNull},
}; };
pub type AttrsMap = BTreeMap<Sym, DynRef>; pub type AttrsRef = ObjRef<Attrs>;
pub struct Attrs { pub struct Attrs {
attrs: AttrsMap, attrs: Ns,
marked: Cell<bool>, marked: Cell<bool>,
this: ObjRef<Attrs>, this: ObjRef<Attrs>,
} }
impl Attrs { impl Attrs {
pub fn new(ctx: &mut ObjCtx, attrs: BTreeMap<Sym, DynRef>) -> ObjRef<Self> { pub fn new<G: Gc>(gc: &mut G, attrs: Ns) -> ObjRef<Self> {
let obj: Box<ObjCell<Attrs>> = unsafe { let obj: Box<ObjCell<Attrs>> = unsafe {
let mut obj: Box<MaybeUninit<ObjCell<Attrs>>> = Box::new_uninit(); let mut obj: Box<MaybeUninit<ObjCell<Attrs>>> = Box::new_uninit();
// maybe UB? taking away the uninit right before it's initialized may be bad // maybe UB? taking away the uninit right before it's initialized may be bad
let this = &*obj as *const _ as *const ObjCell<Attrs>; let this = &*obj as *const _ as *const ObjCell<Attrs>;
obj.as_mut_ptr().write(ObjCell::new(Attrs { obj.as_mut_ptr().write(ObjCell::new(Attrs {
attrs: Default::default(), attrs,
marked: Cell::new(false), marked: Cell::new(false),
this: ObjRef::new(NonNull::new(this as *mut _).unwrap()), this: ObjRef::new(NonNull::new(this as *mut _).unwrap()),
})); }));
@@ -43,7 +42,7 @@ impl Attrs {
}; };
// Add the object to the allocator pool // Add the object to the allocator pool
ctx.gc_mut().add_obj(obj); gc.add_obj(obj);
obj_ref obj_ref
} }
@@ -87,25 +86,25 @@ impl Obj for Attrs {
} }
} }
pub struct AttrsBuilder<'ctx> { pub struct AttrsBuilder<'i, I: Intern> {
ctx: &'ctx mut ObjCtx, intern: &'i mut I,
attrs: AttrsMap, attrs: Ns,
} }
impl<'ctx> AttrsBuilder<'ctx> { impl<'i, I: Intern> AttrsBuilder<'i, I> {
pub fn new(ctx: &'ctx mut ObjCtx) -> Self { pub fn new(intern: &'i mut I) -> Self {
Self::with_base(ctx, Default::default()) Self::with_base(intern, Default::default())
} }
pub fn with_base(ctx: &'ctx mut ObjCtx, attrs: AttrsMap) -> Self { pub fn with_base(intern: &'i mut I, attrs: Ns) -> Self {
AttrsBuilder { AttrsBuilder {
attrs, attrs,
ctx, intern,
} }
} }
pub fn attr(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); let sym = self.intern.intern_sym(symbol_name);
self.attr_sym(sym, value) self.attr_sym(sym, value)
} }
@@ -114,9 +113,9 @@ impl<'ctx> AttrsBuilder<'ctx> {
self self
} }
pub fn finish(self) -> ObjRef<Attrs> { pub fn finish<G: Gc>(self, gc: &mut G) -> ObjRef<Attrs> {
let Self { ctx, attrs } = self; let Self { intern: _, attrs } = self;
Attrs::new(ctx, attrs) Attrs::new(gc, attrs)
} }
} }
@@ -124,13 +123,13 @@ impl<'ctx> AttrsBuilder<'ctx> {
fn test_attrs_new() { fn test_attrs_new() {
use crate::mem::BasicGc; use crate::mem::BasicGc;
let mut ctx = ObjCtx::new(BasicGc::default()); let mut gc = BasicGc::default();
let attrs_ref = Attrs::new(&mut ctx, Default::default()); let attrs_ref = Attrs::new(&mut gc, Default::default());
{ {
let mut attrs = attrs_ref.borrow_mut(); let mut attrs = attrs_ref.borrow_mut();
let sym = ctx.gc_mut().add_sym("symbol"); let sym = gc.add_sym("symbol");
attrs.insert(sym, attrs_ref.as_dyn()); attrs.insert(sym, attrs_ref.as_dyn());
assert!( assert!(

View File

@@ -1,40 +1,31 @@
use crate::{ use crate::{
mem::{gc::Gc, ptr::DynRef, BasicGc}, mem::{gc::Gc, BasicGc},
obj::Sym, obj::prelude::*,
}; };
use std::collections::BTreeMap;
pub type DefaultGc = BasicGc; pub type DefaultGc = BasicGc;
pub struct ObjCtx<G = DefaultGc> pub struct ObjCtx<'g, 'n, G>
where where
G: Gc, G: Gc + 'g,
{ {
gc: G, gc: &'g mut G,
globals: BTreeMap<Sym, DynRef>, ns: &'n mut Ns,
} }
impl<G> ObjCtx<G> impl<'g, 'n, G> ObjCtx<'g, 'n, G>
where where
G: Gc, G: Gc + 'g,
{ {
pub fn new(gc: G) -> Self { pub fn new(gc: &'g mut G, ns: &'n mut Ns) -> Self {
ObjCtx { gc, globals: Default::default() } ObjCtx { gc, ns }
} }
pub fn gc(&self) -> &G { pub fn gc_mut(&'g mut self) -> &'g mut G {
&self.gc self.gc
} }
pub fn gc_mut(&mut self) -> &mut G { pub fn ns_mut(&'n mut self) -> &'n mut Ns {
&mut self.gc self.ns
}
pub fn globals(&self) -> &BTreeMap<Sym, DynRef> {
&self.globals
}
pub fn globals_mut(&mut self) -> &mut BTreeMap<Sym, DynRef> {
&mut self.globals
} }
} }

View File

@@ -10,19 +10,12 @@ pub struct Dict {
} }
impl Dict { impl Dict {
// TODO : base types need to be interned. This should probably be stored in a root namespace. pub fn new(attrs: AttrsRef) -> Self {
// * How to get the root namespace?
// * ObjCtx object that holds allocator, gc, root namespace, etc..?
// * How to create initial types?
pub fn new(ctx: &mut ObjCtx) -> Self {
todo!()
/*
Dict { Dict {
dict: Default::default(), dict: Default::default(),
attrs: Attrs::new(ctx), attrs,
marked: Cell::new(false), marked: Cell::new(false),
} }
*/
} }
} }

View File

@@ -4,13 +4,13 @@ use std::{any::Any, cell::Cell, rc::Rc};
pub type NativeFunPtr = fn(argv: Vec<DynRef>); pub type NativeFunPtr = fn(argv: Vec<DynRef>);
pub struct NativeFun { pub struct NativeFun {
attrs: ObjRef<Attrs>, attrs: AttrsRef,
fn_ptr: NativeFunPtr, fn_ptr: NativeFunPtr,
} }
impl NativeFun { impl NativeFun {
pub fn new(ctx: &mut ObjCtx, name: StrRef, fn_ptr: NativeFunPtr) -> Self { pub fn new(attrs: AttrsRef, name: StrRef, fn_ptr: NativeFunPtr) -> Self {
let attrs = Attrs::new(ctx, Default::default()); // TODO : clone attrs, add name
NativeFun { NativeFun {
attrs, attrs,
fn_ptr, fn_ptr,

View File

@@ -4,6 +4,13 @@ pub mod dict;
pub mod error; pub mod error;
pub mod fun; pub mod fun;
//pub mod num; //pub mod num;
pub mod ns {
use crate::{mem::ptr::DynRef, obj::Sym};
use std::collections::BTreeMap;
/// A namespace
pub type Ns = BTreeMap<Sym, DynRef>;
}
pub mod str; pub mod str;
pub mod sym { pub mod sym {
pub type Sym = usize; pub type Sym = usize;
@@ -12,22 +19,21 @@ pub mod sym {
pub mod prelude { pub mod prelude {
pub use crate::{ pub use crate::{
obj::{
Attrs, Dict, DictRef, Fun, NativeFun, Str, StrRef, Sym, ObjCtx, Obj,
},
mem::{ mem::{
ptr::{ObjRef, DynRef},
intern::Intern,
gc::Gc, gc::Gc,
} intern::Intern,
ptr::{DynRef, ObjRef},
},
obj::{Attrs, AttrsRef, Dict, DictRef, Fun, NativeFun, Ns, Obj, ObjCtx, Str, StrRef, Sym},
}; };
} }
pub use self::{ pub use self::{
attrs::Attrs, attrs::{Attrs, AttrsBuilder, AttrsRef},
ctx::ObjCtx, ctx::ObjCtx,
dict::{Dict, DictRef}, dict::{Dict, DictRef},
fun::{NativeFun, Fun}, fun::{Fun, NativeFun},
ns::Ns,
str::{Str, StrRef}, str::{Str, StrRef},
sym::Sym, sym::Sym,
}; };

View File

@@ -8,7 +8,7 @@ pub type StrRef = ObjRef<Str>;
#[derivative(PartialEq, PartialOrd, Ord, Eq)] #[derivative(PartialEq, PartialOrd, Ord, Eq)]
pub struct Str { pub struct Str {
#[derivative(PartialEq = "ignore", PartialOrd = "ignore", Ord = "ignore")] #[derivative(PartialEq = "ignore", PartialOrd = "ignore", Ord = "ignore")]
attrs: ObjRef<Attrs>, attrs: AttrsRef,
contents: String, contents: String,
@@ -17,20 +17,14 @@ pub struct Str {
} }
impl Str { impl Str {
pub fn new(ctx: &mut ObjCtx, contents: String) -> Self { pub fn new<G: Gc>(attrs: AttrsRef, contents: String) -> Self {
Str { Str {
attrs: Self::type_attrs(ctx), attrs,
contents, contents,
marked: Cell::new(false), marked: Cell::new(false),
} }
} }
fn type_attrs(ctx: &mut ObjCtx) -> ObjRef<Attrs> {
// TODO need global namespace
// TODO need upcast from dyn to static
todo!()
}
pub fn contents(&self) -> &String { pub fn contents(&self) -> &String {
&self.contents &self.contents
} }

View File

@@ -1,17 +1,26 @@
use crate::obj::prelude::*; use crate::{
use std::collections::BTreeMap; obj::prelude::*,
vm::op::Op,
};
use std::rc::Rc;
#[derive(Default)]
pub struct Frame { pub struct Frame {
stack: Vec<DynRef>, stack: Vec<DynRef>,
ip: usize, ip: usize,
return_value: Option<DynRef>, return_value: Option<DynRef>,
locals: BTreeMap<Sym, DynRef>, locals: Ns,
ops: Rc<Vec<Op>>,
} }
impl Frame { impl Frame {
pub fn new() -> Self { pub fn new(ops: Rc<Vec<Op>>) -> Self {
Default::default() Frame {
stack: Default::default(),
ip: 0,
return_value: None,
locals: Default::default(),
ops,
}
} }
pub fn push(&mut self, obj_ref: DynRef) { pub fn push(&mut self, obj_ref: DynRef) {
@@ -45,5 +54,9 @@ impl Frame {
pub fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option<DynRef> { pub fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option<DynRef> {
self.locals.insert(sym, obj_ref) self.locals.insert(sym, obj_ref)
} }
pub fn ops(&self) -> &Rc<Vec<Op>> {
&self.ops
}
} }

View File

@@ -3,6 +3,7 @@ pub mod frame;
use crate::{ use crate::{
obj::prelude::*, obj::prelude::*,
mem::gc::Gc,
vm::{ vm::{
op::Op, op::Op,
frame::Frame, frame::Frame,
@@ -10,61 +11,95 @@ use crate::{
}; };
use std::rc::Rc; use std::rc::Rc;
pub struct State { fn root_ns<I: Intern, G: Gc>(intern: &mut I, gc: &mut G) -> Ns {
let mut ns: Ns = Default::default();
ns.insert(intern.intern_sym("Unit"), Attrs::new(gc, Default::default()));
ns
}
pub struct State<I: Intern, G: Gc> {
intern: I,
gc: G,
root_ns: Ns,
frames: Vec<Frame>, frames: Vec<Frame>,
} }
impl State { impl<I: Intern, G: Gc> State<I, G> {
pub fn new() -> Self { pub fn new(mut intern: I, mut gc: G) -> Self {
State { frames: Default::default(), } let root_ns = root_ns(&mut intern, &mut gc);
State { intern, gc, root_ns, frames: Default::default(), }
} }
fn frame(&self) -> &Frame { pub fn frames(&self) -> &Vec<Frame> {
&self.frames
}
pub fn frames_mut(&mut self) -> &mut Vec<Frame> {
&mut self.frames
}
pub fn frame(&self) -> &Frame {
self.frames self.frames
.last() .last()
.unwrap() .unwrap()
} }
fn frame_mut(&mut self) -> &mut Frame { pub fn frame_mut(&mut self) -> &mut Frame {
self.frames self.frames
.last_mut() .last_mut()
.unwrap() .unwrap()
} }
fn push_stack(&mut self, value: DynRef) { pub fn push_stack(&mut self, value: DynRef) {
self.frame_mut() self.frame_mut()
.push(value); .push(value);
} }
fn pop_stack(&mut self) -> Option<DynRef> { pub fn pop_stack(&mut self) -> Option<DynRef> {
self.frame_mut() self.frame_mut()
.pop() .pop()
} }
fn ip(&self) -> usize { pub fn ip(&self) -> usize {
self.frame().ip() self.frame().ip()
} }
fn set_ip(&mut self, ip: usize) { pub fn set_ip(&mut self, ip: usize) {
self.frame_mut().set_ip(ip); self.frame_mut().set_ip(ip);
} }
fn get_local(&self, sym: Sym) -> Option<DynRef> { pub fn get_local(&self, sym: Sym) -> Option<DynRef> {
self.frame().get_local(sym) self.frame().get_local(sym)
} }
fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option<DynRef> { pub fn set_local(&mut self, sym: Sym, obj_ref: DynRef) -> Option<DynRef> {
self.frame_mut().set_local(sym, obj_ref) self.frame_mut().set_local(sym, obj_ref)
} }
pub fn run(&mut self, ops: Rc<Vec<Op>>) { pub fn ops(&self) -> &Rc<Vec<Op>> {
self.frames.push(Default::default()); self.frame().ops()
while self.ip() < ops.len() { }
pub fn resume(&mut self) {
while !self.frames().is_empty() {
self.resume_fun();
let _frame = self.frames_mut().pop().unwrap();
// Push return value?
}
}
pub fn resume_fun(&mut self) {
while self.ip() < self.ops().len() {
self.step();
}
}
pub fn step(&mut self) {
let mut next_ip = self.ip() + 1; let mut next_ip = self.ip() + 1;
match &ops[self.ip()] { match self.ops()[self.ip()] {
Op::Push(sym) => { Op::Push(sym) => {
// TODO - local not found // TODO - local not found
let obj_ref = self.get_local(*sym) let obj_ref = self.get_local(sym)
.expect("TODO - local not found"); .expect("TODO - local not found");
self.push_stack(obj_ref); self.push_stack(obj_ref);
} }
@@ -72,7 +107,7 @@ impl State {
// stack should not underflow // stack should not underflow
let obj_ref = self.pop_stack() let obj_ref = self.pop_stack()
.expect("misaligned stack for pop"); .expect("misaligned stack for pop");
if let Some(sym) = *sym { if let Some(sym) = sym {
self.set_local(sym, obj_ref); self.set_local(sym, obj_ref);
} }
} }
@@ -84,7 +119,7 @@ impl State {
let attrs = top_ref.attrs(); let attrs = top_ref.attrs();
let attrs_ref = attrs.borrow(); let attrs_ref = attrs.borrow();
// TODO - local not found // TODO - local not found
attrs_ref.get(*sym) attrs_ref.get(sym)
.expect("TODO - local not found") .expect("TODO - local not found")
}; };
self.push_stack(obj_ref); self.push_stack(obj_ref);
@@ -92,8 +127,8 @@ impl State {
Op::Call(argc) => { Op::Call(argc) => {
let fun = self.pop_stack() let fun = self.pop_stack()
.expect("misaligned stack for function call"); .expect("misaligned stack for function call");
let mut argv = Vec::with_capacity(*argc); let mut argv = Vec::with_capacity(argc);
for i in 0 .. *argc { for _ in 0 .. argc {
let arg = self.pop_stack() let arg = self.pop_stack()
.expect("misaligned stack for argv"); .expect("misaligned stack for argv");
argv.push(arg); argv.push(arg);
@@ -101,31 +136,20 @@ impl State {
// reverse since arguments are pushed in order of being passed // reverse since arguments are pushed in order of being passed
argv.reverse(); 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 // TODO
// Figure out a way to either call a native function with NativeFun::call, or // move ops pointer to the stack frame
// call a user function with Fun::code() if let Some(_fun) = fun.borrow().as_any().downcast_ref::<Fun>() {
if let Some(fun) = fun.borrow().as_any().downcast_ref::<Fun>() { self.set_ip(next_ip);
} else if let Some(fun) = fun.borrow().as_any().downcast_ref::<NativeFun>() { next_ip = 0;
todo!("New stack frame");
} else if let Some(_fun) = fun.borrow().as_any().downcast_ref::<NativeFun>() {
todo!();
} else { } else {
todo!("TODO - not a function") todo!("TODO - not a function");
} }
todo!()
} }
} }
self.set_ip(next_ip); self.set_ip(next_ip);
} }
self.frames.pop();
}
} }

View File

@@ -6,6 +6,7 @@ use crate::obj::Sym;
// * ops deal with symbols directly // * ops deal with symbols directly
// * stack values are either obj references or symbols // * stack values are either obj references or symbols
#[derive(Clone, Copy)]
pub enum Op { pub enum Op {
/// Push a value from a symbol /// Push a value from a symbol
Push(Sym), Push(Sym),