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)]
#![feature(new_uninit)]
#![feature(unsize, coerce_unsized, new_uninit)]
mod compile;
mod syn;

View File

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

View File

@@ -2,14 +2,13 @@ use crate::{
obj::prelude::*,
mem::{
ptr::ObjCell,
intern::Intern,
}
};
pub trait Gc: Intern {
pub trait Gc {
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]);

View File

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

View File

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

View File

@@ -1,9 +1,10 @@
use crate::obj::Obj;
use std::{
any::{self, Any},
any,
cell::RefCell,
fmt::{self, Debug, Formatter},
ops::Deref,
marker::Unsize,
ops::{CoerceUnsized, Deref},
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
//

View File

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

View File

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

View File

@@ -10,19 +10,12 @@ pub struct Dict {
}
impl Dict {
// TODO : base types need to be interned. This should probably be stored in a root namespace.
// * 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!()
/*
pub fn new(attrs: AttrsRef) -> Self {
Dict {
dict: Default::default(),
attrs: Attrs::new(ctx),
attrs,
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 struct NativeFun {
attrs: ObjRef<Attrs>,
attrs: AttrsRef,
fn_ptr: NativeFunPtr,
}
impl NativeFun {
pub fn new(ctx: &mut ObjCtx, name: StrRef, fn_ptr: NativeFunPtr) -> Self {
let attrs = Attrs::new(ctx, Default::default());
pub fn new(attrs: AttrsRef, name: StrRef, fn_ptr: NativeFunPtr) -> Self {
// TODO : clone attrs, add name
NativeFun {
attrs,
fn_ptr,

View File

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

View File

@@ -8,7 +8,7 @@ pub type StrRef = ObjRef<Str>;
#[derivative(PartialEq, PartialOrd, Ord, Eq)]
pub struct Str {
#[derivative(PartialEq = "ignore", PartialOrd = "ignore", Ord = "ignore")]
attrs: ObjRef<Attrs>,
attrs: AttrsRef,
contents: String,
@@ -17,20 +17,14 @@ pub struct Str {
}
impl Str {
pub fn new(ctx: &mut ObjCtx, contents: String) -> Self {
pub fn new<G: Gc>(attrs: AttrsRef, contents: String) -> Self {
Str {
attrs: Self::type_attrs(ctx),
attrs,
contents,
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 {
&self.contents
}

View File

@@ -1,17 +1,26 @@
use crate::obj::prelude::*;
use std::collections::BTreeMap;
use crate::{
obj::prelude::*,
vm::op::Op,
};
use std::rc::Rc;
#[derive(Default)]
pub struct Frame {
stack: Vec<DynRef>,
ip: usize,
return_value: Option<DynRef>,
locals: BTreeMap<Sym, DynRef>,
locals: Ns,
ops: Rc<Vec<Op>>,
}
impl Frame {
pub fn new() -> Self {
Default::default()
pub fn new(ops: Rc<Vec<Op>>) -> Self {
Frame {
stack: Default::default(),
ip: 0,
return_value: None,
locals: Default::default(),
ops,
}
}
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> {
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::{
obj::prelude::*,
mem::gc::Gc,
vm::{
op::Op,
frame::Frame,
@@ -10,61 +11,95 @@ use crate::{
};
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>,
}
impl State {
pub fn new() -> Self {
State { frames: Default::default(), }
impl<I: Intern, G: Gc> State<I, G> {
pub fn new(mut intern: I, mut gc: G) -> Self {
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
.last()
.unwrap()
}
fn frame_mut(&mut self) -> &mut Frame {
pub fn frame_mut(&mut self) -> &mut Frame {
self.frames
.last_mut()
.unwrap()
}
fn push_stack(&mut self, value: DynRef) {
pub fn push_stack(&mut self, value: DynRef) {
self.frame_mut()
.push(value);
}
fn pop_stack(&mut self) -> Option<DynRef> {
pub fn pop_stack(&mut self) -> Option<DynRef> {
self.frame_mut()
.pop()
}
fn ip(&self) -> usize {
pub fn ip(&self) -> usize {
self.frame().ip()
}
fn set_ip(&mut self, ip: usize) {
pub fn set_ip(&mut self, ip: usize) {
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)
}
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)
}
pub fn run(&mut self, ops: Rc<Vec<Op>>) {
self.frames.push(Default::default());
while self.ip() < ops.len() {
pub fn ops(&self) -> &Rc<Vec<Op>> {
self.frame().ops()
}
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;
match &ops[self.ip()] {
match self.ops()[self.ip()] {
Op::Push(sym) => {
// TODO - local not found
let obj_ref = self.get_local(*sym)
let obj_ref = self.get_local(sym)
.expect("TODO - local not found");
self.push_stack(obj_ref);
}
@@ -72,7 +107,7 @@ impl State {
// stack should not underflow
let obj_ref = self.pop_stack()
.expect("misaligned stack for pop");
if let Some(sym) = *sym {
if let Some(sym) = sym {
self.set_local(sym, obj_ref);
}
}
@@ -84,7 +119,7 @@ impl State {
let attrs = top_ref.attrs();
let attrs_ref = attrs.borrow();
// TODO - local not found
attrs_ref.get(*sym)
attrs_ref.get(sym)
.expect("TODO - local not found")
};
self.push_stack(obj_ref);
@@ -92,8 +127,8 @@ impl State {
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 mut argv = Vec::with_capacity(argc);
for _ in 0 .. argc {
let arg = self.pop_stack()
.expect("misaligned stack for argv");
argv.push(arg);
@@ -101,31 +136,20 @@ impl State {
// 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::<Fun>() {
} else if let Some(fun) = fun.borrow().as_any().downcast_ref::<NativeFun>() {
// move ops pointer to the stack frame
if let Some(_fun) = fun.borrow().as_any().downcast_ref::<Fun>() {
self.set_ip(next_ip);
next_ip = 0;
todo!("New stack frame");
} else if let Some(_fun) = fun.borrow().as_any().downcast_ref::<NativeFun>() {
todo!();
} else {
todo!("TODO - not a function")
todo!("TODO - not a function");
}
todo!()
}
}
self.set_ip(next_ip);
}
self.frames.pop();
}
}

View File

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