reference cycles resolved

This commit is contained in:
2025-02-22 19:01:05 +01:00
parent cfa8b6ee52
commit 5e474069e0
32 changed files with 433 additions and 388 deletions

View File

@@ -21,7 +21,7 @@ num-traits = "0.2.19"
orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" }
ordered-float = "4.6.0"
ordered-float = "5.0.0"
paste = "1.0.15"
substack = "1.1.1"
test_executors = "0.3.2"

View File

@@ -20,7 +20,7 @@ pub struct CtxData {
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>,
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub root: RwLock<Module>,
// pub root: RwLock<Module>,
}
#[derive(Clone)]
pub struct Ctx(Rc<CtxData>);
@@ -36,7 +36,7 @@ impl Ctx {
systems: RwLock::default(),
system_id: RefCell::new(NonZero::new(1).unwrap()),
owned_atoms: RwLock::default(),
root: RwLock::new(Module::default()),
// root: RwLock::new(Module::default()),
}))
}
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {

View File

@@ -11,7 +11,7 @@ use orchid_base::name::NameLike;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, PathSet, Step};
use crate::tree::{ItemKind, MemberKind};
use crate::tree::{ItemKind, MemberKind, Module, Root};
type ExprGuard = Bound<RwLockWriteGuard<'static, ExprKind>, Expr>;
@@ -38,12 +38,13 @@ pub struct ExecCtx {
cur_pos: Pos,
did_pop: bool,
logger: Logger,
root: Root,
}
impl ExecCtx {
pub async fn new(ctx: Ctx, logger: Logger, init: Expr) -> Self {
pub async fn new(ctx: Ctx, logger: Logger, root: Root, init: Expr) -> Self {
let cur_pos = init.pos();
let cur = Bound::async_new(init, |init| init.kind().write()).await;
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger }
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, root, logger }
}
pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") }
pub fn set_gas(&mut self, gas: Option<u64>) { self.gas = gas }
@@ -91,38 +92,11 @@ impl ExecCtx {
},
ExprKind::Seq(a, b) if !self.did_pop => (ExprKind::Seq(a.clone(), b), StackOp::Push(a)),
ExprKind::Seq(_, b) => (ExprKind::Identity(b), StackOp::Nop),
ExprKind::Const(name) => {
let (cn, mp) = name.split_last();
let root_lock = self.ctx.root.read().await;
let module = root_lock.walk(true, mp.iter().cloned()).await.unwrap();
let member = (module.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => (
ExprKind::Bottom(mk_errv(
self.ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[self.cur_pos.clone().into()],
)),
StackOp::Pop,
),
Some(mem) => match mem.kind().await {
MemberKind::Mod(_) => (
ExprKind::Bottom(mk_errv(
self.ctx.i.i("module used as constant").await,
format!("{name} is a module"),
[self.cur_pos.clone().into()],
)),
StackOp::Pop,
),
MemberKind::Const(c) => {
let value = c.get_bytecode(&self.ctx).await;
(ExprKind::Identity(value.clone()), StackOp::Nop)
},
},
}
},
ExprKind::Const(name) =>
match self.root.get_const_value(name, self.cur_pos.clone(), self.ctx.clone()).await {
Err(e) => (ExprKind::Bottom(e), StackOp::Pop),
Ok(v) => (ExprKind::Identity(v), StackOp::Nop),
},
ExprKind::Arg => panic!("This should not appear outside function bodies"),
ek @ ExprKind::Atom(_) => (ek, StackOp::Pop),
ExprKind::Bottom(bot) => (ExprKind::Bottom(bot.clone()), StackOp::Unwind(bot)),

View File

@@ -2,13 +2,15 @@ use std::cell::RefCell;
use std::future::Future;
use std::io;
use std::num::NonZeroU64;
use std::pin::pin;
use std::rc::{Rc, Weak};
use async_std::channel::{self, Sender};
use async_std::sync::Mutex;
use async_stream::stream;
use derive_destructure::destructure;
use futures::FutureExt;
use futures::future::{join, join_all};
use futures::{FutureExt, StreamExt, stream, stream_select};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::HostMsgSet;
@@ -41,13 +43,18 @@ pub struct ExtensionData {
logger: Logger,
next_pars: RefCell<NonZeroU64>,
exprs: ExprStore,
exiting_snd: Sender<()>,
lex_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::SubLex>>>>,
mac_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::RunMacros>>>>,
}
impl Drop for ExtensionData {
fn drop(&mut self) {
let reqnot = self.reqnot.clone();
(self.ctx.spawn)(Box::pin(async move { reqnot.notify(api::HostExtNotif::Exit).await }))
let exiting_snd = self.exiting_snd.clone();
(self.ctx.spawn)(Box::pin(async move {
reqnot.notify(api::HostExtNotif::Exit).await;
exiting_snd.send(()).await.unwrap()
}))
}
}
@@ -57,43 +64,34 @@ impl Extension {
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| {
let init = Rc::new(init);
let (exiting_snd, exiting_rcv) = channel::bounded::<()>(1);
(ctx.spawn)(clone!(init, weak, ctx; Box::pin(async move {
let reqnot_opt = weak.upgrade().map(|rc| rc.reqnot.clone());
if let Some(reqnot) = reqnot_opt {
let mut repeat = true;
while repeat {
repeat = false;
(init.recv(Box::new(|msg| {
repeat = true;
Box::pin(clone!(reqnot, ctx; async move {
let msg = msg.to_vec();
let reqnot = reqnot.clone();
(ctx.spawn)(Box::pin(async move {
reqnot.receive(&msg).await;
}))
}))
})))
.await;
let rcv_stream = stream! { loop { yield init.recv().await } };
let mut event_stream = pin!(stream::select(exiting_rcv.map(|()| None), rcv_stream));
while let Some(Some(msg)) = event_stream.next().await {
if let Some(reqnot) = weak.upgrade().map(|rc| rc.reqnot.clone()) {
let reqnot = reqnot.clone();
(ctx.spawn)(Box::pin(async move {
reqnot.receive(&msg).await;
}))
}
}
})));
ExtensionData {
exiting_snd,
exprs: ExprStore::default(),
ctx: ctx.clone(),
systems: (init.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
.collect(),
logger: logger.clone(),
init,
init: init.clone(),
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(),
mac_recur: Mutex::default(),
reqnot: ReqNot::new(
msg_logger,
clone!(weak; move |sfn, _| clone!(weak; async move {
let data = weak.upgrade().unwrap();
data.init.send(sfn).await
}.boxed_local())),
move |sfn, _| clone!(init; Box::pin(async move { init.send(sfn).await })),
clone!(weak; move |notif, _| {
clone!(weak; Box::pin(async move {
let this = Extension(weak.upgrade().unwrap());

View File

@@ -33,9 +33,8 @@ pub async fn macro_treev_to_api(mtree: Vec<MacTree>, slots: &mut Slots) -> Vec<a
}
pub async fn macro_treev_from_api(api: Vec<api::MacroTree>, ctx: Ctx) -> Vec<MacTree> {
mtreev_from_api(&api, &ctx.clone().i, &mut move |atom| {
clone!(ctx);
Box::pin(async move { MacTok::Atom(AtomHand::new(atom.clone(), &ctx).await) })
mtreev_from_api(&api, &ctx.clone().i, &mut async move |atom| {
MacTok::Atom(AtomHand::new(atom.clone(), &ctx).await)
})
.await
}

View File

@@ -4,7 +4,7 @@ use std::pin::Pin;
use async_process::{self, Child, ChildStdin, ChildStdout};
use async_std::io::{self, BufReadExt, BufReader};
use async_std::sync::Mutex;
use futures::FutureExt;
use futures::AsyncWriteExt;
use futures::future::LocalBoxFuture;
use orchid_api_traits::{Decode, Encode};
use orchid_base::builtin::{ExtInit, ExtPort};
@@ -46,7 +46,7 @@ pub async fn ext_command(
header,
port: Box::new(Subprocess {
child: RefCell::new(Some(child)),
stdin: Mutex::new(Box::pin(stdin)),
stdin: Some(Mutex::new(Box::pin(stdin))),
stdout: Mutex::new(Box::pin(stdout)),
ctx,
}),
@@ -55,14 +55,16 @@ pub async fn ext_command(
pub struct Subprocess {
child: RefCell<Option<Child>>,
stdin: Mutex<Pin<Box<ChildStdin>>>,
stdin: Option<Mutex<Pin<Box<ChildStdin>>>>,
stdout: Mutex<Pin<Box<ChildStdout>>>,
ctx: Ctx,
}
impl Drop for Subprocess {
fn drop(&mut self) {
let mut child = self.child.borrow_mut().take().unwrap();
let stdin = self.stdin.take().unwrap();
(self.ctx.spawn)(Box::pin(async move {
stdin.lock().await.close().await.unwrap();
let status = child.status().await.expect("Extension exited with error");
assert!(status.success(), "Extension exited with error {status}");
}))
@@ -70,18 +72,17 @@ impl Drop for Subprocess {
}
impl ExtPort for Subprocess {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> {
async { send_msg(Pin::new(&mut *self.stdin.lock().await), msg).await.unwrap() }.boxed_local()
Box::pin(async {
send_msg(Pin::new(&mut *self.stdin.as_ref().unwrap().lock().await), msg).await.unwrap()
})
}
fn recv<'a>(
&'a self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
) -> LocalBoxFuture<'a, ()> {
fn recv(&self) -> LocalBoxFuture<'_, Option<Vec<u8>>> {
Box::pin(async {
std::io::Write::flush(&mut std::io::stderr()).unwrap();
match recv_msg(self.stdout.lock().await.as_mut()).await {
Ok(msg) => cb(&msg).await,
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => (),
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => (),
Ok(msg) => Some(msg),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => None,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => None,
Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()),
}
})

View File

@@ -24,7 +24,7 @@ use substack::{Stackframe, Substack};
use crate::api;
use crate::ctx::Ctx;
use crate::extension::{Extension, WeakExtension};
use crate::tree::{ItemKind, Member, Module, ParsTokTree};
use crate::tree::{ItemKind, Member, Module, ParsTokTree, Root};
#[derive(destructure)]
struct SystemInstData {
@@ -80,7 +80,7 @@ impl System {
comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> {
let line =
join_all(line.iter().map(|t| async { t.to_api(&mut |n, _| match *n {}).await })).await;
join_all(line.iter().map(|t| async { t.to_api(&mut async |n, _| match *n {}).await })).await;
let comments = comments.iter().map(Comment::to_api).collect_vec();
match self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }).await {
Ok(parsed) => Ok(ttv_from_api(parsed, &mut self.ctx().clone(), &self.ctx().i).await),
@@ -122,7 +122,7 @@ impl SystemCtor {
self.decl.depends.iter().map(|s| &**s)
}
pub fn id(&self) -> api::SysDeclId { self.decl.id }
pub async fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> System {
pub async fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> (Module, System) {
let depends = depends.into_iter().map(|si| si.id()).collect_vec();
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
@@ -150,10 +150,8 @@ impl SystemCtor {
.collect::<Vec<_>>()
.await;
ext.ctx().systems.write().await.insert(id, data.downgrade());
let mut swap = Module::default();
mem::swap(&mut swap, &mut *ext.ctx().root.write().await);
*ext.ctx().root.write().await = Module::new(swap.items.into_iter().chain(const_root));
data
let root = Module::new(const_root);
(root, data)
}
}
@@ -166,7 +164,7 @@ pub enum SysResolvErr {
pub async fn init_systems(
tgts: &[String],
exts: &[Extension],
) -> Result<Vec<System>, SysResolvErr> {
) -> Result<(Root, Vec<System>), SysResolvErr> {
let mut to_load = HashMap::<&str, &SystemCtor>::new();
let mut to_find = tgts.iter().map(|s| s.as_str()).collect::<VecDeque<&str>>();
while let Some(target) = to_find.pop_front() {
@@ -205,9 +203,11 @@ pub async fn init_systems(
walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?;
}
let mut systems = HashMap::<&str, System>::new();
let mut root = Module::default();
for ctor in to_load_ordered.iter() {
let sys = ctor.run(ctor.depends().map(|n| &systems[n])).await;
let (sys_root, sys) = ctor.run(ctor.depends().map(|n| &systems[n])).await;
systems.insert(ctor.name(), sys);
root.merge(sys_root);
}
Ok(systems.into_values().collect_vec())
Ok((Root::new(root), systems.into_values().collect_vec()))
}

View File

@@ -2,17 +2,18 @@ use std::fmt::Debug;
use std::rc::Rc;
use async_once_cell::OnceCell;
use async_std::sync::Mutex;
use async_std::sync::{Mutex, RwLock};
use async_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
use never::Never;
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
use orchid_base::macros::{mtreev_fmt, mtreev_from_api};
use orchid_base::name::Sym;
use orchid_base::name::{NameLike, Sym};
use orchid_base::parse::{Comment, Import};
use orchid_base::tree::{AtomRepr, TokTree, Token};
use orchid_base::{clone, tl_cache};
@@ -52,7 +53,7 @@ impl Item {
let kind = match tree.kind {
api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys).await),
api::ItemKind::Import(name) => ItemKind::Import(Import {
path: Sym::from_api(name, &sys.ctx().i).await.iter().cloned().collect(),
path: Sym::from_api(name, &sys.ctx().i).await.iter().collect(),
name: None,
}),
api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e, &sys.ctx().i).await),
@@ -66,11 +67,8 @@ impl Item {
let pos = Pos::from_api(&rule.location, &sys.ctx().i).await;
let pattern = mtreev_from_api(&rule.pattern, &sys.ctx().i, &mut {
clone!(pos, sys);
move |a| {
clone!(pos, sys);
Box::pin(async move {
MacTok::Atom(AtomHand::from_api(a, pos.clone(), &mut sys.ctx().clone()).await)
})
async move |a| {
MacTok::Atom(AtomHand::from_api(a, pos.clone(), &mut sys.ctx().clone()).await)
}
})
.await;
@@ -182,6 +180,11 @@ impl Module {
.collect_vec();
Self { imports: vec![], exports, items }
}
pub fn merge(&mut self, other: Module) {
let mut swap = Module::default();
std::mem::swap(self, &mut swap);
*self = Module::new(swap.items.into_iter().chain(other.items))
}
pub async fn from_api(m: api::Module, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
Self::new(
stream! { for item in m.items { yield Item::from_api(item, path, sys).boxed_local().await } }
@@ -340,3 +343,32 @@ impl CodeLocator {
Self { steps, rule_loc: Some((macro_i, rule_i)) }
}
}
#[derive(Clone)]
pub struct Root(Rc<RwLock<Module>>);
impl Root {
pub fn new(module: Module) -> Self { Self(Rc::new(RwLock::new(module))) }
pub async fn get_const_value(&self, name: impl NameLike, pos: Pos, ctx: Ctx) -> OrcRes<Expr> {
let (cn, mp) = name.split_last();
let root_lock = self.0.read().await;
let module = root_lock.walk(true, mp.iter().cloned()).await.unwrap();
let member = (module.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos.clone().into()],
)),
Some(mem) => match mem.kind().await {
MemberKind::Mod(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos.clone().into()],
)),
MemberKind::Const(c) => Ok((c.get_bytecode(&ctx).await).clone()),
},
}
}
}