task_local context over context objects
- interner impls logically separate from API in orchid-base (default host interner still in base for testing) - error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options - no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited - still deadlocks nondeterministically for some ungodly reason
This commit is contained in:
@@ -8,6 +8,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
async-once-cell = "0.5.4"
|
||||
bound = "0.6.0"
|
||||
derive_destructure = "1.0.0"
|
||||
dyn-clone = "1.0.20"
|
||||
futures = { version = "0.3.31", features = [
|
||||
@@ -29,7 +30,6 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
ordered-float = "5.0.0"
|
||||
pastey = "0.1.1"
|
||||
some_executor = "0.6.1"
|
||||
substack = "1.1.1"
|
||||
task-local = "0.1.0"
|
||||
tokio = { version = "1.47.1", optional = true, features = [] }
|
||||
|
||||
@@ -14,20 +14,20 @@ use orchid_api_derive::Coding;
|
||||
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, fmt};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::Requester;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::context::{ctx, i};
|
||||
use crate::conv::ToExpr;
|
||||
use crate::entrypoint::request;
|
||||
// use crate::error::{ProjectError, ProjectResult};
|
||||
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
|
||||
use crate::gen_expr::GExpr;
|
||||
use crate::system::{DynSystemCard, atom_info_for, downcast_atom};
|
||||
use crate::system::{DynSystemCard, atom_by_idx, atom_info_for, cted, downcast_atom};
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
|
||||
pub struct AtomTypeId(pub NonZeroU32);
|
||||
|
||||
pub trait AtomCard: 'static + Sized {
|
||||
@@ -99,13 +99,13 @@ impl ForeignAtom {
|
||||
ForeignAtom { atom, expr: handle, pos }
|
||||
}
|
||||
pub async fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
|
||||
let rep = (ctx().reqnot().request(api::Fwd(
|
||||
let rep = (request(api::Fwd(
|
||||
self.atom.clone(),
|
||||
Sym::parse(M::NAME, &i()).await.unwrap().tok().to_api(),
|
||||
enc_vec(&m).await,
|
||||
Sym::parse(M::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&m),
|
||||
)))
|
||||
.await?;
|
||||
Some(M::Response::decode(Pin::new(&mut &rep[..])).await)
|
||||
Some(M::Response::decode_slice(&mut &rep[..]))
|
||||
}
|
||||
pub async fn downcast<T: AtomicFeatures>(self) -> Result<TAtom<T>, NotTypAtom> {
|
||||
TAtom::downcast(self.ex().handle()).await
|
||||
@@ -119,7 +119,7 @@ impl fmt::Debug for ForeignAtom {
|
||||
}
|
||||
impl Format for ForeignAtom {
|
||||
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
FmtUnit::from_api(&ctx().reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
|
||||
FmtUnit::from_api(&request(api::ExtAtomPrint(self.atom.clone())).await)
|
||||
}
|
||||
}
|
||||
impl ToExpr for ForeignAtom {
|
||||
@@ -138,8 +138,8 @@ pub struct NotTypAtom {
|
||||
impl NotTypAtom {
|
||||
pub async fn mk_err(&self) -> OrcErrv {
|
||||
mk_errv(
|
||||
i().i("Not the expected type").await,
|
||||
format!("The expression {} is not a {}", fmt(&self.expr, &i()).await, self.typ.name()),
|
||||
is("Not the expected type").await,
|
||||
format!("The expression {} is not a {}", fmt(&self.expr).await, self.typ.name()),
|
||||
[self.pos.clone()],
|
||||
)
|
||||
}
|
||||
@@ -172,8 +172,10 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
self.handlers.push((
|
||||
M::NAME,
|
||||
Rc::new(move |a: &A, req: Pin<&mut dyn AsyncRead>, rep: Pin<&mut dyn AsyncWrite>| {
|
||||
async { Supports::<M>::handle(a, M::decode(req).await).await.encode(rep).await }
|
||||
.boxed_local()
|
||||
async {
|
||||
Supports::<M>::handle(a, M::decode(req).await.unwrap()).await.encode(rep).await.unwrap()
|
||||
}
|
||||
.boxed_local()
|
||||
}),
|
||||
));
|
||||
self
|
||||
@@ -182,7 +184,7 @@ impl<A: AtomCard> MethodSetBuilder<A> {
|
||||
pub async fn pack(&self) -> MethodSet<A> {
|
||||
MethodSet {
|
||||
handlers: stream::iter(self.handlers.iter())
|
||||
.then(async |(k, v)| (Sym::parse(k, &i()).await.unwrap(), v.clone()))
|
||||
.then(async |(k, v)| (Sym::parse(k).await.unwrap(), v.clone()))
|
||||
.collect()
|
||||
.await,
|
||||
}
|
||||
@@ -234,16 +236,15 @@ impl<A: AtomicFeatures> TAtom<A> {
|
||||
}
|
||||
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
|
||||
where A: Supports<M> {
|
||||
M::Response::decode(Pin::new(
|
||||
&mut &(ctx().reqnot().request(api::Fwd(
|
||||
M::Response::decode_slice(
|
||||
&mut &(request(api::Fwd(
|
||||
self.untyped.atom.clone(),
|
||||
Sym::parse(M::NAME, &i()).await.unwrap().tok().to_api(),
|
||||
enc_vec(&req).await,
|
||||
Sym::parse(M::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&req),
|
||||
)))
|
||||
.await
|
||||
.unwrap()[..],
|
||||
))
|
||||
.await
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<A: AtomicFeatures> Deref for TAtom<A> {
|
||||
@@ -311,9 +312,18 @@ impl Format for AtomFactory {
|
||||
}
|
||||
|
||||
pub async fn err_not_callable() -> OrcErrv {
|
||||
mk_errv_floating(i().i("This atom is not callable").await, "Attempted to apply value as function")
|
||||
mk_errv_floating(is("This atom is not callable").await, "Attempted to apply value as function")
|
||||
}
|
||||
|
||||
pub async fn err_not_command() -> OrcErrv {
|
||||
mk_errv_floating(i().i("This atom is not a command").await, "Settled on an inactionable value")
|
||||
mk_errv_floating(is("This atom is not a command").await, "Settled on an inactionable value")
|
||||
}
|
||||
|
||||
/// Read the type ID prefix from an atom, return type information and the rest
|
||||
/// of the data
|
||||
pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box<dyn AtomDynfo>, AtomTypeId, &[u8]) {
|
||||
let mut data = &atom.data.0[..];
|
||||
let tid = AtomTypeId::decode_slice(&mut data);
|
||||
let atom_record = atom_by_idx(cted().inst().card(), tid).expect("Unrecognized atom type ID");
|
||||
(atom_record, tid, data)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use std::any::{Any, TypeId, type_name};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::marker::PhantomData;
|
||||
use std::num::NonZero;
|
||||
use std::ops::Deref;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
@@ -19,32 +20,33 @@ use orchid_api_traits::{Decode, Encode, enc_vec};
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, take_first};
|
||||
use orchid_base::name::Sym;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::{
|
||||
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
||||
MethodSetBuilder, TAtom, err_not_callable, err_not_command, get_info,
|
||||
};
|
||||
use crate::context::{SysCtxEntry, ctx, i};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, bot};
|
||||
use crate::system_ctor::CtedObj;
|
||||
use crate::system::{cted, sys_id};
|
||||
|
||||
pub struct OwnedVariant;
|
||||
impl AtomicVariant for OwnedVariant {}
|
||||
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
|
||||
fn _factory(self) -> AtomFactory {
|
||||
AtomFactory::new(async move || {
|
||||
let serial = ctx()
|
||||
.get_or_default::<ObjStore>()
|
||||
.next_id
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap());
|
||||
let (typ_id, _) = get_info::<A>(ctx().get::<CtedObj>().inst().card());
|
||||
let mut data = enc_vec(&typ_id).await;
|
||||
let obj_store = get_obj_store();
|
||||
let atom_id = {
|
||||
let mut id = obj_store.next_id.borrow_mut();
|
||||
*id += 1;
|
||||
api::AtomId(NonZero::new(*id + 1).unwrap())
|
||||
};
|
||||
let (typ_id, _) = get_info::<A>(cted().inst().card());
|
||||
let mut data = enc_vec(&typ_id);
|
||||
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
|
||||
ctx().get_or_default::<ObjStore>().objects.read().await.insert(atom_id, Box::new(self));
|
||||
api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: ctx().sys_id() }
|
||||
obj_store.objects.read().await.insert(atom_id, Box::new(self));
|
||||
api::Atom { drop: Some(atom_id), data: api::AtomData(data), owner: sys_id() }
|
||||
})
|
||||
}
|
||||
fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } }
|
||||
@@ -59,7 +61,7 @@ pub(crate) struct AtomReadGuard<'a> {
|
||||
}
|
||||
impl<'a> AtomReadGuard<'a> {
|
||||
async fn new(id: api::AtomId) -> Self {
|
||||
let guard = ctx().get_or_default::<ObjStore>().objects.read().await;
|
||||
let guard = get_obj_store().objects.read().await;
|
||||
if guard.get(&id).is_none() {
|
||||
panic!("Received invalid atom ID: {id:?}");
|
||||
}
|
||||
@@ -73,7 +75,7 @@ impl Deref for AtomReadGuard<'_> {
|
||||
|
||||
/// Remove an atom from the store
|
||||
pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> {
|
||||
let mut g = ctx().get_or_default::<ObjStore>().objects.write().await;
|
||||
let mut g = get_obj_store().objects.write().await;
|
||||
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
|
||||
}
|
||||
|
||||
@@ -86,7 +88,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
fn name(&self) -> &'static str { type_name::<T>() }
|
||||
fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
||||
Box::pin(async {
|
||||
Box::new(<T as AtomCard>::Data::decode(Pin::new(&mut &data[..])).await) as Box<dyn Any>
|
||||
Box::new(<T as AtomCard>::Data::decode_slice(&mut &data[..])) as Box<dyn Any>
|
||||
})
|
||||
}
|
||||
fn call(&self, AtomCtx(_, id): AtomCtx, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||
@@ -127,7 +129,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
|
||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
|
||||
Box::pin(async move {
|
||||
let id = id.unwrap();
|
||||
id.encode(write.as_mut()).await;
|
||||
id.encode(write.as_mut()).await.unwrap();
|
||||
AtomReadGuard::new(id).await.dyn_serialize(write).await
|
||||
})
|
||||
}
|
||||
@@ -155,7 +157,7 @@ pub trait DeserializeCtx: Sized {
|
||||
|
||||
struct DeserCtxImpl<'a>(&'a [u8]);
|
||||
impl DeserializeCtx for DeserCtxImpl<'_> {
|
||||
async fn read<T: Decode>(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await }
|
||||
async fn read<T: Decode>(&mut self) -> T { T::decode(Pin::new(&mut self.0)).await.unwrap() }
|
||||
fn is_empty(&self) -> bool { self.0.is_empty() }
|
||||
}
|
||||
|
||||
@@ -266,7 +268,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
fn atom_tid(&self) -> TypeId { TypeId::of::<T>() }
|
||||
fn as_any_ref(&self) -> &dyn Any { self }
|
||||
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> {
|
||||
async { self.val().await.as_ref().encode(buffer).await }.boxed_local()
|
||||
async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local()
|
||||
}
|
||||
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr> {
|
||||
self.call_ref(arg).boxed_local()
|
||||
@@ -279,7 +281,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
}
|
||||
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()> { self.free().boxed_local() }
|
||||
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit> {
|
||||
async move { self.print_atom(&FmtCtxImpl { i: &i() }).await }.boxed_local()
|
||||
async move { self.print_atom(&FmtCtxImpl::default()).await }.boxed_local()
|
||||
}
|
||||
fn dyn_serialize<'a>(
|
||||
&'a self,
|
||||
@@ -294,13 +296,24 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ObjStore {
|
||||
pub(crate) next_id: AtomicU64,
|
||||
pub(crate) next_id: RefCell<u64>,
|
||||
pub(crate) objects: RwLock<MemoMap<api::AtomId, Box<dyn DynOwnedAtom>>>,
|
||||
}
|
||||
impl SysCtxEntry for ObjStore {}
|
||||
|
||||
task_local! {
|
||||
static OBJ_STORE: Rc<ObjStore>;
|
||||
}
|
||||
|
||||
pub(crate) fn with_obj_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(OBJ_STORE.scope(Rc::new(ObjStore::default()), fut))
|
||||
}
|
||||
|
||||
pub(crate) fn get_obj_store() -> Rc<ObjStore> {
|
||||
OBJ_STORE.try_with(|store| store.clone()).expect("Owned atom store not initialized")
|
||||
}
|
||||
|
||||
pub async fn own<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
||||
let g = ctx().get_or_default::<ObjStore>().objects.read().await;
|
||||
let g = get_obj_store().objects.read().await;
|
||||
let atom_id = typ.untyped.atom.drop.expect("Owned atoms always have a drop ID");
|
||||
let dyn_atom =
|
||||
g.get(&atom_id).expect("Atom ID invalid; atom type probably not owned by this crate");
|
||||
@@ -308,8 +321,7 @@ pub async fn own<A: OwnedAtom>(typ: &TAtom<A>) -> A {
|
||||
}
|
||||
|
||||
pub async fn debug_print_obj_store(show_atoms: bool) {
|
||||
let ctx = ctx();
|
||||
let store = ctx.get_or_default::<ObjStore>();
|
||||
let store = get_obj_store();
|
||||
let keys = store.objects.read().await.keys().cloned().collect_vec();
|
||||
let mut message = "Atoms in store:".to_string();
|
||||
if !show_atoms {
|
||||
|
||||
@@ -8,6 +8,7 @@ use futures::{AsyncRead, AsyncWrite, FutureExt};
|
||||
use orchid_api_traits::{Coding, enc_vec};
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::FmtUnit;
|
||||
use orchid_base::logging::logger;
|
||||
use orchid_base::name::Sym;
|
||||
|
||||
use crate::api;
|
||||
@@ -15,20 +16,19 @@ use crate::atom::{
|
||||
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
|
||||
MethodSetBuilder, err_not_callable, err_not_command, get_info,
|
||||
};
|
||||
use crate::context::ctx;
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, bot};
|
||||
use crate::system_ctor::CtedObj;
|
||||
use crate::system::{cted, sys_id};
|
||||
|
||||
pub struct ThinVariant;
|
||||
impl AtomicVariant for ThinVariant {}
|
||||
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
|
||||
fn _factory(self) -> AtomFactory {
|
||||
AtomFactory::new(async move || {
|
||||
let (id, _) = get_info::<A>(ctx().get::<CtedObj>().inst().card());
|
||||
let mut buf = enc_vec(&id).await;
|
||||
self.encode(Pin::new(&mut buf)).await;
|
||||
api::Atom { drop: None, data: api::AtomData(buf), owner: ctx().sys_id() }
|
||||
let (id, _) = get_info::<A>(cted().inst().card());
|
||||
let mut buf = enc_vec(&id);
|
||||
self.encode_vec(&mut buf);
|
||||
api::Atom { drop: None, data: api::AtomData(buf), owner: sys_id() }
|
||||
})
|
||||
}
|
||||
fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } }
|
||||
@@ -41,18 +41,18 @@ pub struct ThinAtomDynfo<T: ThinAtom> {
|
||||
}
|
||||
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
fn print<'a>(&self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> {
|
||||
Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.print().await })
|
||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).print().await })
|
||||
}
|
||||
fn tid(&self) -> TypeId { TypeId::of::<T>() }
|
||||
fn name(&self) -> &'static str { type_name::<T>() }
|
||||
fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
|
||||
Box::pin(async { Box::new(T::decode(Pin::new(&mut &buf[..])).await) as Box<dyn Any> })
|
||||
Box::pin(async { Box::new(T::decode_slice(&mut &buf[..])) as Box<dyn Any> })
|
||||
}
|
||||
fn call<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||
Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.call(arg).await })
|
||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
|
||||
}
|
||||
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||
Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.call(arg).await })
|
||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
|
||||
}
|
||||
fn handle_req<'a, 'm1: 'a, 'm2: 'a>(
|
||||
&'a self,
|
||||
@@ -63,14 +63,14 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
) -> LocalBoxFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
||||
ms.dispatch(&T::decode(Pin::new(&mut &buf[..])).await, key, req, rep).await
|
||||
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req, rep).await
|
||||
})
|
||||
}
|
||||
fn command<'a>(
|
||||
&'a self,
|
||||
AtomCtx(buf, _): AtomCtx<'a>,
|
||||
) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> {
|
||||
async move { T::decode(Pin::new(&mut &buf[..])).await.command().await }.boxed_local()
|
||||
async move { T::decode_slice(&mut &buf[..]).command().await }.boxed_local()
|
||||
}
|
||||
fn serialize<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
@@ -78,18 +78,18 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
|
||||
write: Pin<&'b mut dyn AsyncWrite>,
|
||||
) -> LocalBoxFuture<'a, Option<Vec<Expr>>> {
|
||||
Box::pin(async {
|
||||
T::decode(Pin::new(&mut &ctx.0[..])).await.encode(write).await;
|
||||
T::decode_slice(&mut &ctx.0[..]).encode(write).await.unwrap();
|
||||
Some(Vec::new())
|
||||
})
|
||||
}
|
||||
fn deserialize<'a>(&'a self, data: &'a [u8], refs: &'a [Expr]) -> LocalBoxFuture<'a, api::Atom> {
|
||||
assert!(refs.is_empty(), "Refs found when deserializing thin atom");
|
||||
Box::pin(async { T::decode(Pin::new(&mut &data[..])).await._factory().build().await })
|
||||
Box::pin(async { T::decode_slice(&mut &data[..])._factory().build().await })
|
||||
}
|
||||
fn drop<'a>(&'a self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
let string_self = T::decode(Pin::new(&mut &buf[..])).await.print().await;
|
||||
writeln!(ctx().logger(), "Received drop signal for non-drop atom {string_self:?}");
|
||||
let string_self = T::decode_slice(&mut &buf[..]).print().await;
|
||||
writeln!(logger(), "Received drop signal for non-drop atom {string_self:?}");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
use std::any::{Any, TypeId, type_name};
|
||||
use std::fmt;
|
||||
use std::num::NonZero;
|
||||
use std::rc::Rc;
|
||||
|
||||
use memo_map::MemoMap;
|
||||
use orchid_base::builtin::Spawner;
|
||||
use orchid_base::interner::Interner;
|
||||
use orchid_base::logging::Logger;
|
||||
use orchid_base::reqnot::ReqNot;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::system_ctor::CtedObj;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SysCtx(Rc<MemoMap<TypeId, Box<dyn Any>>>);
|
||||
impl SysCtx {
|
||||
pub fn new(
|
||||
id: api::SysId,
|
||||
i: Interner,
|
||||
reqnot: ReqNot<api::ExtMsgSet>,
|
||||
spawner: Spawner,
|
||||
logger: Logger,
|
||||
cted: CtedObj,
|
||||
) -> Self {
|
||||
let this = Self(Rc::new(MemoMap::new()));
|
||||
this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted);
|
||||
this
|
||||
}
|
||||
pub fn add<T: SysCtxEntry>(&self, t: T) -> &Self {
|
||||
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
|
||||
self
|
||||
}
|
||||
pub fn get_or_insert<T: SysCtxEntry>(&self, f: impl FnOnce() -> T) -> &T {
|
||||
(self.0.get_or_insert_owned(TypeId::of::<T>(), || Box::new(f())).downcast_ref())
|
||||
.expect("Keyed by TypeId")
|
||||
}
|
||||
pub fn get_or_default<T: SysCtxEntry + Default>(&self) -> &T { self.get_or_insert(T::default) }
|
||||
pub fn try_get<T: SysCtxEntry>(&self) -> Option<&T> {
|
||||
Some(self.0.get(&TypeId::of::<T>())?.downcast_ref().expect("Keyed by TypeId"))
|
||||
}
|
||||
pub fn get<T: SysCtxEntry>(&self) -> &T {
|
||||
self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::<T>()))
|
||||
}
|
||||
/// Shorthand to get the messaging link
|
||||
pub fn reqnot(&self) -> &ReqNot<api::ExtMsgSet> { self.get::<ReqNot<api::ExtMsgSet>>() }
|
||||
/// Shorthand to get the system ID
|
||||
pub fn sys_id(&self) -> api::SysId { *self.get::<api::SysId>() }
|
||||
/// Spawn a task that will eventually be executed asynchronously
|
||||
pub fn spawn(&self, f: impl Future<Output = ()> + 'static) {
|
||||
(self.get::<Spawner>())(Box::pin(CTX.scope(self.clone(), f)))
|
||||
}
|
||||
/// Shorthand to get the logger
|
||||
pub fn logger(&self) -> &Logger { self.get::<Logger>() }
|
||||
/// Shorthand to get the constructed system object
|
||||
pub fn cted(&self) -> &CtedObj { self.get::<CtedObj>() }
|
||||
}
|
||||
impl fmt::Debug for SysCtx {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "SysCtx({:?})", self.sys_id())
|
||||
}
|
||||
}
|
||||
pub trait SysCtxEntry: 'static + Sized {}
|
||||
impl SysCtxEntry for api::SysId {}
|
||||
impl SysCtxEntry for ReqNot<api::ExtMsgSet> {}
|
||||
impl SysCtxEntry for Spawner {}
|
||||
impl SysCtxEntry for CtedObj {}
|
||||
impl SysCtxEntry for Logger {}
|
||||
impl SysCtxEntry for Interner {}
|
||||
|
||||
task_local! {
|
||||
static CTX: SysCtx;
|
||||
}
|
||||
|
||||
pub async fn with_ctx<F: Future>(ctx: SysCtx, f: F) -> F::Output { CTX.scope(ctx, f).await }
|
||||
pub fn ctx() -> SysCtx { CTX.get() }
|
||||
|
||||
/// Shorthand to get the [Interner] instance
|
||||
pub fn i() -> Interner { ctx().get::<Interner>().clone() }
|
||||
|
||||
pub fn mock_ctx() -> SysCtx {
|
||||
let ctx = SysCtx(Rc::default());
|
||||
ctx
|
||||
.add(Logger::new(api::LogStrategy::StdErr))
|
||||
.add(Interner::new_master())
|
||||
.add::<Spawner>(Rc::new(|_| panic!("Cannot fork in test environment")))
|
||||
.add(api::SysId(NonZero::<u16>::MIN));
|
||||
ctx
|
||||
}
|
||||
@@ -4,11 +4,11 @@ use std::pin::Pin;
|
||||
use dyn_clone::DynClone;
|
||||
use never::Never;
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
||||
use orchid_base::interner::is;
|
||||
use orchid_base::location::Pos;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom, ToAtom};
|
||||
use crate::context::i;
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, atom, bot};
|
||||
|
||||
@@ -27,7 +27,7 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
|
||||
}
|
||||
|
||||
async fn err_not_atom(pos: Pos) -> OrcErrv {
|
||||
mk_errv(i().i("Expected an atom").await, "This expression is not an atom", [pos])
|
||||
mk_errv(is("Expected an atom").await, "This expression is not an atom", [pos])
|
||||
}
|
||||
|
||||
impl TryFromExpr for ForeignAtom {
|
||||
|
||||
@@ -1,453 +1,438 @@
|
||||
use std::cell::RefCell;
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
use std::num::NonZero;
|
||||
use std::pin::Pin;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::rc::Rc;
|
||||
use std::{io, mem};
|
||||
|
||||
use futures::channel::mpsc::{Receiver, Sender, channel};
|
||||
use futures::future::{LocalBoxFuture, join_all};
|
||||
use futures::lock::Mutex;
|
||||
use futures::{FutureExt, SinkExt, StreamExt, stream, stream_select};
|
||||
use futures_locks::RwLock;
|
||||
use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, StreamExt, stream};
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_api_traits::{Decode, UnderRoot, enc_vec};
|
||||
use orchid_base::builtin::{ExtInit, ExtPort, Spawner};
|
||||
use orchid_api::{ExtHostNotif, ExtHostReq};
|
||||
use orchid_api_traits::{Decode, Encode, Request, UnderRoot, enc_vec};
|
||||
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
|
||||
use orchid_base::clone;
|
||||
use orchid_base::error::Reporter;
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::logging::Logger;
|
||||
use orchid_base::error::try_with_reporter;
|
||||
use orchid_base::interner::{es, is, with_interner};
|
||||
use orchid_base::logging::{Logger, with_logger};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{Comment, Snippet};
|
||||
use orchid_base::reqnot::{ReqNot, ReqReader, Requester};
|
||||
use orchid_base::reqnot::{
|
||||
Client, ClientExt, CommCtx, MsgReader, MsgReaderExt, Receipt, RepWriter, ReqHandle, ReqHandleExt,
|
||||
ReqReader, ReqReaderExt, Witness, io_comm,
|
||||
};
|
||||
use orchid_base::stash::with_stash;
|
||||
use orchid_base::tree::{TokenVariant, ttv_from_api};
|
||||
use substack::Substack;
|
||||
use trait_set::trait_set;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId};
|
||||
use crate::atom_owned::take_atom;
|
||||
use crate::context::{SysCtx, ctx, i, with_ctx};
|
||||
use crate::atom::{AtomCtx, AtomTypeId, resolve_atom_type};
|
||||
use crate::atom_owned::{take_atom, with_obj_store};
|
||||
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
||||
use crate::ext_port::ExtPort;
|
||||
use crate::func_atom::with_funs_ctx;
|
||||
use crate::interner::new_interner;
|
||||
use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable};
|
||||
use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api};
|
||||
use crate::system::atom_by_idx;
|
||||
use crate::system_ctor::{CtedObj, DynSystemCtor};
|
||||
use crate::tree::{LazyMemberFactory, TreeIntoApiCtxImpl};
|
||||
use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api, with_parsed_const_ctx};
|
||||
use crate::reflection::with_refl_roots;
|
||||
use crate::system::{SysCtx, atom_by_idx, cted, with_sys};
|
||||
use crate::system_ctor::{CtedObj, DynSystemCtor, SystemCtor};
|
||||
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
||||
|
||||
task_local::task_local! {
|
||||
static CLIENT: Rc<dyn Client>;
|
||||
static CTX: Rc<RefCell<Option<CommCtx>>>;
|
||||
}
|
||||
|
||||
fn get_client() -> Rc<dyn Client> {
|
||||
CLIENT.with(|c| c.expect("Client not set, not running inside a duplex reqnot channel!").clone())
|
||||
fn get_client() -> Rc<dyn Client> { CLIENT.get() }
|
||||
pub async fn exit() {
|
||||
let cx = CTX.get().borrow_mut().take();
|
||||
cx.unwrap().exit().await
|
||||
}
|
||||
|
||||
/// Sent the client used for global [request] and [notify] functions within the
|
||||
/// runtime of this future
|
||||
pub async fn with_client<F: Future>(c: Rc<dyn Client>, fut: F) -> F::Output {
|
||||
CLIENT.scope(c, fut).await
|
||||
pub async fn with_comm<F: Future>(c: Rc<dyn Client>, ctx: CommCtx, fut: F) -> F::Output {
|
||||
CLIENT.scope(c, CTX.scope(Rc::new(RefCell::new(Some(ctx))), fut)).await
|
||||
}
|
||||
|
||||
/// Send a request through the global client's [ClientExt::request]
|
||||
pub async fn request<T: Request + UnderRoot<Root: Encode>>(t: T) -> T::Response {
|
||||
get_client().request(t).await
|
||||
pub async fn request<T: Request + UnderRoot<Root = ExtHostReq>>(t: T) -> T::Response {
|
||||
get_client().request(t).await.unwrap()
|
||||
}
|
||||
|
||||
/// Send a notification through the global client's [ClientExt::notify]
|
||||
pub async fn notify<T: UnderRoot<Root: Encode> + 'static>(t: T) { get_client().notify(t).await }
|
||||
|
||||
pub type ExtReq<'a> = ReqReader<'a, api::ExtMsgSet>;
|
||||
pub type ExtReqNot = ReqNot<api::ExtMsgSet>;
|
||||
|
||||
pub struct ExtensionData {
|
||||
pub name: &'static str,
|
||||
pub systems: &'static [&'static dyn DynSystemCtor],
|
||||
}
|
||||
impl ExtensionData {
|
||||
pub fn new(name: &'static str, systems: &'static [&'static dyn DynSystemCtor]) -> Self {
|
||||
Self { name, systems }
|
||||
}
|
||||
}
|
||||
|
||||
pub enum MemberRecord {
|
||||
Gen(Vec<Tok<String>>, LazyMemberFactory),
|
||||
Res,
|
||||
pub async fn notify<T: UnderRoot<Root = ExtHostNotif>>(t: T) {
|
||||
get_client().notify(t).await.unwrap()
|
||||
}
|
||||
|
||||
pub struct SystemRecord {
|
||||
lazy_members: Mutex<HashMap<api::TreeId, MemberRecord>>,
|
||||
ctx: SysCtx,
|
||||
cted: CtedObj,
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait WithAtomRecordCallback<'a, T> = AsyncFnOnce(
|
||||
Box<dyn AtomDynfo>,
|
||||
AtomTypeId,
|
||||
&'a [u8]
|
||||
) -> T
|
||||
type SystemTable = RefCell<HashMap<api::SysId, Rc<SystemRecord>>>;
|
||||
|
||||
task_local! {
|
||||
static SYSTEM_TABLE: SystemTable;
|
||||
}
|
||||
|
||||
pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>(
|
||||
get_sys_ctx: &impl Fn(api::SysId) -> F,
|
||||
atom: &'a api::Atom,
|
||||
cb: impl WithAtomRecordCallback<'a, T>,
|
||||
) -> T {
|
||||
let mut data = &atom.data.0[..];
|
||||
let ctx = get_sys_ctx(atom.owner).await;
|
||||
let inst = ctx.get::<CtedObj>().inst();
|
||||
let id = AtomTypeId::decode(Pin::new(&mut data)).await;
|
||||
let atom_record = atom_by_idx(inst.card(), id.clone()).expect("Atom ID reserved");
|
||||
with_ctx(ctx, async move { cb(atom_record, id, data).await }).await
|
||||
async fn with_sys_record<F: Future>(id: api::SysId, fut: F) -> F::Output {
|
||||
let cted = SYSTEM_TABLE.with(|tbl| {
|
||||
eprintln!(
|
||||
"Existing systems are {}",
|
||||
tbl.borrow().iter().map(|(k, v)| format!("{k:?}={:?}", v.cted)).join(";")
|
||||
);
|
||||
let sys = tbl.borrow().get(&id).expect("Invalid sys ID").cted.clone();
|
||||
eprintln!("Selected {:?}", sys);
|
||||
sys
|
||||
});
|
||||
with_sys(SysCtx(id, cted), fut).await
|
||||
}
|
||||
|
||||
pub struct ExtensionOwner {
|
||||
_interner_cell: Rc<RefCell<Option<Interner>>>,
|
||||
_systems_lock: Rc<RwLock<HashMap<api::SysId, SystemRecord>>>,
|
||||
out_recv: Mutex<Receiver<Vec<u8>>>,
|
||||
out_send: Sender<Vec<u8>>,
|
||||
pub trait ContextModifier: 'static {
|
||||
fn apply<'a>(self: Box<Self>, fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()>;
|
||||
}
|
||||
|
||||
impl ExtPort for ExtensionOwner {
|
||||
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(async { self.out_send.clone().send(msg.to_vec()).boxed_local().await.unwrap() })
|
||||
}
|
||||
fn recv(&self) -> LocalBoxFuture<'_, Option<Vec<u8>>> {
|
||||
Box::pin(async { self.out_recv.lock().await.next().await })
|
||||
impl<F: AsyncFnOnce(LocalBoxFuture<'_, ()>) + 'static> ContextModifier for F {
|
||||
fn apply<'a>(self: Box<Self>, fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin((self)(fut))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extension_init(
|
||||
data: ExtensionData,
|
||||
host_header: api::HostHeader,
|
||||
spawner: Spawner,
|
||||
) -> ExtInit {
|
||||
let api::HostHeader { log_strategy, msg_logs } = host_header;
|
||||
let decls = (data.systems.iter().enumerate())
|
||||
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
|
||||
.map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap())))
|
||||
.collect_vec();
|
||||
let systems_lock = Rc::new(RwLock::new(HashMap::<api::SysId, SystemRecord>::new()));
|
||||
let ext_header = api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() };
|
||||
let (out_send, in_recv) = channel::<Vec<u8>>(1);
|
||||
let (in_send, out_recv) = channel::<Vec<u8>>(1);
|
||||
let (exit_send, exit_recv) = channel(1);
|
||||
let logger = Logger::new(log_strategy);
|
||||
let msg_logger = Logger::new(msg_logs);
|
||||
let interner_cell = Rc::new(RefCell::new(None::<Interner>));
|
||||
let interner_weak = Rc::downgrade(&interner_cell);
|
||||
let systems_weak = Rc::downgrade(&systems_lock);
|
||||
let get_ctx = clone!(systems_weak; move |id: api::SysId| clone!(systems_weak; async move {
|
||||
let systems =
|
||||
systems_weak.upgrade().expect("System table dropped before request processing done");
|
||||
systems.read().await.get(&id).expect("System not found").ctx.clone()
|
||||
}));
|
||||
let init_ctx = {
|
||||
clone!(interner_weak, spawner, logger);
|
||||
move |id: api::SysId, cted: CtedObj, reqnot: ReqNot<api::ExtMsgSet>| {
|
||||
clone!(interner_weak, spawner, logger; async move {
|
||||
let interner_rc =
|
||||
interner_weak.upgrade().expect("System construction order while shutting down");
|
||||
let i = interner_rc.borrow().clone().expect("mk_ctx called very early, no interner!");
|
||||
SysCtx::new(id, i, reqnot, spawner, logger, cted)
|
||||
})
|
||||
}
|
||||
};
|
||||
let rn = ReqNot::<api::ExtMsgSet>::new(
|
||||
msg_logger.clone(),
|
||||
move |a, _| {
|
||||
clone!(in_send mut);
|
||||
Box::pin(async move { in_send.send(a.to_vec()).await.unwrap() })
|
||||
},
|
||||
{
|
||||
clone!(exit_send);
|
||||
move |n, _| {
|
||||
clone!(exit_send mut);
|
||||
async move {
|
||||
match n {
|
||||
api::HostExtNotif::Exit => exit_send.send(()).await.unwrap(),
|
||||
pub struct ExtensionBuilder {
|
||||
pub name: &'static str,
|
||||
pub systems: Vec<Box<dyn DynSystemCtor>>,
|
||||
pub context: Vec<Box<dyn ContextModifier>>,
|
||||
}
|
||||
impl ExtensionBuilder {
|
||||
pub fn new(name: &'static str) -> Self { Self { name, systems: Vec::new(), context: Vec::new() } }
|
||||
pub fn system(mut self, ctor: impl SystemCtor) -> Self {
|
||||
self.systems.push(Box::new(ctor) as Box<_>);
|
||||
self
|
||||
}
|
||||
pub fn add_context(&mut self, fun: impl ContextModifier) {
|
||||
self.context.push(Box::new(fun) as Box<_>);
|
||||
}
|
||||
pub fn context(mut self, fun: impl ContextModifier) -> Self {
|
||||
self.add_context(fun);
|
||||
self
|
||||
}
|
||||
pub fn build(mut self, mut ctx: ExtPort) {
|
||||
self.add_context(with_funs_ctx);
|
||||
self.add_context(with_parsed_const_ctx);
|
||||
self.add_context(with_obj_store);
|
||||
self.add_context(with_lazy_member_store);
|
||||
self.add_context(with_refl_roots);
|
||||
(ctx.spawn)(Box::pin(async move {
|
||||
let api::HostHeader { log_strategy, msg_logs } =
|
||||
api::HostHeader::decode(ctx.input.as_mut()).await.unwrap();
|
||||
let decls = (self.systems.iter().enumerate())
|
||||
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
|
||||
.map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap())))
|
||||
.collect_vec();
|
||||
api::ExtensionHeader { name: self.name.to_string(), systems: decls.clone() }
|
||||
.encode(ctx.output.as_mut())
|
||||
.await
|
||||
.unwrap();
|
||||
ctx.output.as_mut().flush().await.unwrap();
|
||||
let logger = Logger::new(log_strategy);
|
||||
let logger2 = logger.clone();
|
||||
let msg_logger = Logger::new(msg_logs);
|
||||
let (client, ctx, run_extension) = io_comm(
|
||||
Rc::new(Mutex::new(ctx.output)),
|
||||
Mutex::new(ctx.input),
|
||||
async move |n: Box<dyn MsgReader<'_>>| {
|
||||
match n.read().await.unwrap() {
|
||||
api::HostExtNotif::Exit => exit().await,
|
||||
}
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
},
|
||||
{
|
||||
clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, decls, msg_logger);
|
||||
move |hand, req| {
|
||||
clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, decls, msg_logger);
|
||||
async move {
|
||||
let interner_cell = interner_weak.upgrade().expect("Interner dropped before request");
|
||||
let interner =
|
||||
interner_cell.borrow().clone().expect("Request arrived before interner set");
|
||||
if !matches!(req, api::HostExtReq::AtomReq(api::AtomReq::AtomPrint(_))) {
|
||||
writeln!(msg_logger, "{} extension received request {req:?}", data.name);
|
||||
}
|
||||
|
||||
match req {
|
||||
api::HostExtReq::SystemDrop(sys_drop) => {
|
||||
if let Some(rc) = systems_weak.upgrade() {
|
||||
mem::drop(rc.write().await.remove(&sys_drop.0))
|
||||
}
|
||||
hand.handle(&sys_drop, &()).await
|
||||
},
|
||||
api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) =>
|
||||
with_ctx(get_ctx(sys_id).await, async move {
|
||||
take_atom(atom).await.dyn_free().await;
|
||||
hand.handle(&atom_drop, &()).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::Ping(ping @ api::Ping) => hand.handle(&ping, &()).await,
|
||||
api::HostExtReq::Sweep(api::Sweep) => todo!(),
|
||||
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
|
||||
let (sys_id, _) = (decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system))
|
||||
.expect("NewSystem call received for invalid system");
|
||||
let cted = data.systems[sys_id].new_system(&new_sys);
|
||||
with_ctx(init_ctx(new_sys.id, cted.clone(), hand.reqnot()).await, async move {
|
||||
let lex_filter =
|
||||
cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| {
|
||||
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
|
||||
});
|
||||
let lazy_members = Mutex::new(HashMap::new());
|
||||
let const_root = stream::iter(cted.inst().dyn_env().await)
|
||||
.then(|mem| {
|
||||
let lazy_mems = &lazy_members;
|
||||
async move {
|
||||
let name = i().i(&mem.name).await;
|
||||
Ok(())
|
||||
},
|
||||
async move |mut reader| {
|
||||
with_stash(async {
|
||||
let req = reader.read_req().await.unwrap();
|
||||
let handle = reader.finish().await;
|
||||
if !matches!(req, api::HostExtReq::AtomReq(api::AtomReq::AtomPrint(_))) {
|
||||
writeln!(msg_logger, "{} extension received request {req:?}", self.name);
|
||||
}
|
||||
match req {
|
||||
api::HostExtReq::SystemDrop(sys_drop) => {
|
||||
SYSTEM_TABLE.with(|l| l.borrow_mut().remove(&sys_drop.0));
|
||||
handle.reply(&sys_drop, &()).await
|
||||
},
|
||||
api::HostExtReq::AtomDrop(atom_drop @ api::AtomDrop(sys_id, atom)) =>
|
||||
with_sys_record(sys_id, async {
|
||||
take_atom(atom).await.dyn_free().await;
|
||||
handle.reply(&atom_drop, &()).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::Ping(ping @ api::Ping) => handle.reply(&ping, &()).await,
|
||||
api::HostExtReq::Sweep(api::Sweep) => todo!(),
|
||||
api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
|
||||
let (ctor_idx, _) =
|
||||
(decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system))
|
||||
.expect("NewSystem call received for invalid system");
|
||||
let cted = self.systems[ctor_idx].new_system(&new_sys);
|
||||
let record = Rc::new(SystemRecord { cted: cted.clone() });
|
||||
SYSTEM_TABLE.with(|tbl| {
|
||||
let mut g = tbl.borrow_mut();
|
||||
g.insert(new_sys.id, record);
|
||||
});
|
||||
with_sys_record(new_sys.id, async {
|
||||
let lex_filter =
|
||||
cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]), |cf, lx| {
|
||||
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
|
||||
});
|
||||
let const_root = stream::iter(cted.inst().dyn_env().await)
|
||||
.then(async |mem| {
|
||||
let name = is(&mem.name).await;
|
||||
let mut tia_ctx = TreeIntoApiCtxImpl {
|
||||
lazy_members: &mut *lazy_mems.lock().await,
|
||||
basepath: &[],
|
||||
path: Substack::Bottom.push(name.clone()),
|
||||
};
|
||||
(name.to_api(), mem.kind.into_api(&mut tia_ctx).await)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
.await;
|
||||
let prelude =
|
||||
cted.inst().dyn_prelude().await.iter().map(|sym| sym.to_api()).collect();
|
||||
let line_types = join_all(
|
||||
(cted.inst().dyn_parsers().iter())
|
||||
.map(async |p| is(p.line_head()).await.to_api()),
|
||||
)
|
||||
.await;
|
||||
let prelude =
|
||||
cted.inst().dyn_prelude().await.iter().map(|sym| sym.to_api()).collect();
|
||||
let record = SystemRecord { ctx: ctx(), lazy_members };
|
||||
let systems = systems_weak.upgrade().expect("System constructed during shutdown");
|
||||
systems.write().await.insert(new_sys.id, record);
|
||||
let line_types = join_all(
|
||||
(cted.inst().dyn_parsers().iter())
|
||||
.map(|p| async { interner.i(p.line_head()).await.to_api() }),
|
||||
)
|
||||
.await;
|
||||
let response =
|
||||
api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
|
||||
hand.handle(&new_sys, &response).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) =>
|
||||
with_ctx(get_ctx(sys_id).await, async move {
|
||||
let systems = systems_weak.upgrade().expect("Member queried during shutdown");
|
||||
let systems_g = systems.read().await;
|
||||
let mut lazy_members =
|
||||
systems_g.get(&sys_id).expect("System not found").lazy_members.lock().await;
|
||||
let (path, cb) = match lazy_members.insert(tree_id, MemberRecord::Res) {
|
||||
None => panic!("Tree for ID not found"),
|
||||
Some(MemberRecord::Res) => panic!("This tree has already been transmitted"),
|
||||
Some(MemberRecord::Gen(path, cb)) => (path, cb),
|
||||
};
|
||||
let tree = cb.build(Sym::new(path.clone(), &interner).await.unwrap()).await;
|
||||
let mut tia_ctx = TreeIntoApiCtxImpl {
|
||||
path: Substack::Bottom,
|
||||
basepath: &path,
|
||||
lazy_members: &mut lazy_members,
|
||||
};
|
||||
hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
|
||||
let fwd_tok = hand.will_handle_as(&fwd);
|
||||
let api::SysFwded(sys_id, payload) = fwd;
|
||||
let ctx = get_ctx(sys_id).await;
|
||||
with_ctx(ctx.clone(), async move {
|
||||
let sys = ctx.cted().inst();
|
||||
let reply = Rc::new(RefCell::new(None));
|
||||
let reply2 = reply.clone();
|
||||
let sub_hand = ExtReq::new(hand.reqnot(), async move |v| {
|
||||
reply2.borrow_mut().replace(v);
|
||||
});
|
||||
sys.dyn_request(sub_hand, payload).await;
|
||||
let reply_buf =
|
||||
reply.borrow_mut().take().expect("Request discarded but did not throw");
|
||||
hand.handle_as(fwd_tok, &reply_buf).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) =>
|
||||
with_ctx(get_ctx(sys).await, async move {
|
||||
let text = Tok::from_api(text, &i()).await;
|
||||
let src = Sym::from_api(src, &i()).await;
|
||||
let rep = Reporter::new();
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let trigger_char = text.chars().nth(pos as usize).unwrap();
|
||||
let ekey_na = ekey_not_applicable().await;
|
||||
let ekey_cascade = ekey_cascade().await;
|
||||
let lexers = ctx().cted().inst().dyn_lexers();
|
||||
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
|
||||
{
|
||||
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone(), &rep);
|
||||
match lx.lex(&text[pos as usize..], &ctx).await {
|
||||
Err(e) if e.any(|e| *e == ekey_na) => continue,
|
||||
Err(e) => {
|
||||
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
||||
expr_store.dispose().await;
|
||||
return hand.handle(&lex, &eopt).await;
|
||||
let response =
|
||||
api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
|
||||
handle.reply(&new_sys, &response).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) =>
|
||||
with_sys_record(sys_id, async {
|
||||
let (path, tree) = get_lazy(tree_id).await;
|
||||
let mut tia_ctx =
|
||||
TreeIntoApiCtxImpl { path: Substack::Bottom, basepath: &path[..] };
|
||||
handle.reply(&get_tree, &tree.into_api(&mut tia_ctx).await).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
|
||||
let fwd_tok = Witness::of(&fwd);
|
||||
let api::SysFwded(sys_id, payload) = fwd;
|
||||
with_sys_record(sys_id, async {
|
||||
struct TrivialReqCycle<'a> {
|
||||
req: &'a [u8],
|
||||
rep: &'a mut Vec<u8>,
|
||||
}
|
||||
impl<'a> ReqReader<'a> for TrivialReqCycle<'a> {
|
||||
fn reader(&mut self) -> Pin<&mut dyn AsyncRead> {
|
||||
Pin::new(&mut self.req) as Pin<&mut _>
|
||||
}
|
||||
fn finish(self: Box<Self>) -> LocalBoxFuture<'a, Box<dyn ReqHandle<'a> + 'a>> {
|
||||
Box::pin(async { self as Box<_> })
|
||||
}
|
||||
}
|
||||
impl<'a> ReqHandle<'a> for TrivialReqCycle<'a> {
|
||||
fn start_reply(
|
||||
self: Box<Self>,
|
||||
) -> LocalBoxFuture<'a, io::Result<Box<dyn RepWriter<'a> + 'a>>> {
|
||||
Box::pin(async { Ok(self as Box<_>) })
|
||||
}
|
||||
}
|
||||
impl<'a> RepWriter<'a> for TrivialReqCycle<'a> {
|
||||
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> {
|
||||
Pin::new(&mut self.rep) as Pin<&mut _>
|
||||
}
|
||||
fn finish(
|
||||
self: Box<Self>,
|
||||
) -> LocalBoxFuture<'a, io::Result<orchid_base::reqnot::Receipt<'a>>>
|
||||
{
|
||||
Box::pin(async { Ok(Receipt::_new()) })
|
||||
}
|
||||
}
|
||||
let mut reply = Vec::new();
|
||||
let req = TrivialReqCycle { req: &payload, rep: &mut reply };
|
||||
let _ = cted().inst().dyn_request(Box::new(req)).await;
|
||||
handle.reply(fwd_tok, &reply).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, src, text, pos, id }) =>
|
||||
with_sys_record(sys, async {
|
||||
let text = es(text).await;
|
||||
let src = Sym::from_api(src).await;
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let trigger_char = text.chars().nth(pos as usize).unwrap();
|
||||
let ekey_na = ekey_not_applicable().await;
|
||||
let ekey_cascade = ekey_cascade().await;
|
||||
let lexers = cted().inst().dyn_lexers();
|
||||
writeln!(
|
||||
logger,
|
||||
"sys={sys:?}, tc={trigger_char}, lexers={}",
|
||||
lexers.iter().map(|l| format!("{l:?}")).join(",")
|
||||
);
|
||||
for lx in
|
||||
lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
|
||||
{
|
||||
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone());
|
||||
match try_with_reporter(lx.lex(&text[pos as usize..], &ctx)).await {
|
||||
Err(e) if e.any(|e| *e == ekey_na) => continue,
|
||||
Err(e) => {
|
||||
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
|
||||
expr_store.dispose().await;
|
||||
return handle.reply(&lex, &eopt).await;
|
||||
},
|
||||
Ok((s, expr)) => {
|
||||
let expr = expr.into_api(&mut (), &mut ()).await;
|
||||
let pos = (text.len() - s.len()) as u32;
|
||||
expr_store.dispose().await;
|
||||
return handle.reply(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
writeln!(logger, "Got notified about n/a character '{trigger_char}'");
|
||||
expr_store.dispose().await;
|
||||
handle.reply(&lex, &None).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::ParseLine(pline) => {
|
||||
let api::ParseLine { module, src, exported, comments, sys, line, idx } = &pline;
|
||||
with_sys_record(*sys, async {
|
||||
let parsers = cted().inst().dyn_parsers();
|
||||
let src = Sym::from_api(*src).await;
|
||||
let comments =
|
||||
join_all(comments.iter().map(|c| Comment::from_api(c, src.clone()))).await;
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let line: Vec<PTokTree> =
|
||||
ttv_from_api(line, &mut &expr_store, &mut (), &src).await;
|
||||
let snip = Snippet::new(line.first().expect("Empty line"), &line);
|
||||
let parser = parsers[*idx as usize];
|
||||
let module = Sym::from_api(*module).await;
|
||||
let pctx = ParsCtx::new(module);
|
||||
let o_line =
|
||||
match try_with_reporter(parser.parse(pctx, *exported, comments, snip)).await {
|
||||
Err(e) => Err(e.to_api()),
|
||||
Ok(t) => Ok(linev_into_api(t).await),
|
||||
};
|
||||
mem::drop(line);
|
||||
expr_store.dispose().await;
|
||||
handle.reply(&pline, &o_line).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
|
||||
with_sys_record(sys, async {
|
||||
let cnst = get_const(id).await;
|
||||
handle.reply(fpc, &cnst.serialize().await).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::AtomReq(atom_req) => {
|
||||
let atom = atom_req.get_atom();
|
||||
with_sys_record(atom.owner, async {
|
||||
let (nfo, id, buf) = resolve_atom_type(atom);
|
||||
let actx = AtomCtx(buf, atom.drop);
|
||||
match &atom_req {
|
||||
api::AtomReq::SerializeAtom(ser) => {
|
||||
let mut buf = enc_vec(&id);
|
||||
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
|
||||
None => handle.reply(ser, &None).await,
|
||||
Some(refs) => {
|
||||
let refs =
|
||||
join_all(refs.into_iter().map(async |ex| ex.into_api(&mut ()).await))
|
||||
.await;
|
||||
handle.reply(ser, &Some((buf, refs))).await
|
||||
},
|
||||
}
|
||||
},
|
||||
Ok((s, expr)) => {
|
||||
let expr = expr.into_api(&mut (), &mut ()).await;
|
||||
let pos = (text.len() - s.len()) as u32;
|
||||
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
||||
handle.reply(print, &nfo.print(actx).await.to_api()).await,
|
||||
api::AtomReq::Fwded(fwded) => {
|
||||
let api::Fwded(_, key, payload) = &fwded;
|
||||
let mut reply = Vec::new();
|
||||
let key = Sym::from_api(*key).await;
|
||||
let some = nfo
|
||||
.handle_req(
|
||||
actx,
|
||||
key,
|
||||
Pin::<&mut &[u8]>::new(&mut &payload[..]),
|
||||
Pin::<&mut Vec<_>>::new(&mut reply),
|
||||
)
|
||||
.await;
|
||||
handle.reply(fwded, &some.then_some(reply)).await
|
||||
},
|
||||
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||
let api_expr = ret.serialize().await;
|
||||
mem::drop(expr_handle);
|
||||
expr_store.dispose().await;
|
||||
return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
|
||||
handle.reply(call, &api_expr).await
|
||||
},
|
||||
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||
let api_expr = ret.serialize().await;
|
||||
mem::drop(expr_handle);
|
||||
expr_store.dispose().await;
|
||||
handle.reply(call, &api_expr).await
|
||||
},
|
||||
api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await {
|
||||
Err(e) => handle.reply(cmd, &Err(e.to_api())).await,
|
||||
Ok(opt) => match opt {
|
||||
None => handle.reply(cmd, &Ok(api::NextStep::Halt)).await,
|
||||
Some(cont) => {
|
||||
let cont = cont.serialize().await;
|
||||
handle.reply(cmd, &Ok(api::NextStep::Continue(cont))).await
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
writeln!(logger, "Got notified about n/a character '{trigger_char}'");
|
||||
expr_store.dispose().await;
|
||||
hand.handle(&lex, &None).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::ParseLine(pline) => {
|
||||
let api::ParseLine { module, src, exported, comments, sys, line, idx } = &pline;
|
||||
with_ctx(get_ctx(*sys).await, async {
|
||||
let parsers = ctx().cted().inst().dyn_parsers();
|
||||
let src = Sym::from_api(*src, &i()).await;
|
||||
let comments =
|
||||
join_all(comments.iter().map(|c| Comment::from_api(c, src.clone(), &interner)))
|
||||
.await;
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let line: Vec<PTokTree> =
|
||||
ttv_from_api(line, &mut &expr_store, &mut (), &src, &i()).await;
|
||||
let snip = Snippet::new(line.first().expect("Empty line"), &line);
|
||||
let parser = parsers[*idx as usize];
|
||||
let module = Sym::from_api(*module, &i()).await;
|
||||
let reporter = Reporter::new();
|
||||
let pctx = ParsCtx::new(module, &reporter);
|
||||
let parse_res = parser.parse(pctx, *exported, comments, snip).await;
|
||||
let o_line = match reporter.merge(parse_res) {
|
||||
Err(e) => Err(e.to_api()),
|
||||
Ok(t) => Ok(linev_into_api(t).await),
|
||||
};
|
||||
mem::drop(line);
|
||||
expr_store.dispose().await;
|
||||
hand.handle(&pline, &o_line).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
|
||||
with_ctx(get_ctx(sys).await, async move {
|
||||
let cnst = get_const(id).await;
|
||||
hand.handle(fpc, &cnst.serialize().await).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::AtomReq(atom_req) => {
|
||||
let atom = atom_req.get_atom();
|
||||
let atom_req = atom_req.clone();
|
||||
with_atom_record(&get_ctx, atom, async move |nfo, id, buf| {
|
||||
let actx = AtomCtx(buf, atom.drop);
|
||||
match &atom_req {
|
||||
api::AtomReq::SerializeAtom(ser) => {
|
||||
let mut buf = enc_vec(&id).await;
|
||||
match nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await {
|
||||
None => hand.handle(ser, &None).await,
|
||||
Some(refs) => {
|
||||
let refs =
|
||||
join_all(refs.into_iter().map(|ex| async { ex.into_api(&mut ()).await }))
|
||||
.await;
|
||||
hand.handle(ser, &Some((buf, refs))).await
|
||||
},
|
||||
}
|
||||
},
|
||||
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
||||
hand.handle(print, &nfo.print(actx).await.to_api()).await,
|
||||
api::AtomReq::Fwded(fwded) => {
|
||||
let api::Fwded(_, key, payload) = &fwded;
|
||||
let mut reply = Vec::new();
|
||||
let key = Sym::from_api(*key, &interner).await;
|
||||
let some = nfo
|
||||
.handle_req(
|
||||
actx,
|
||||
key,
|
||||
Pin::<&mut &[u8]>::new(&mut &payload[..]),
|
||||
Pin::<&mut Vec<_>>::new(&mut reply),
|
||||
)
|
||||
.await;
|
||||
hand.handle(fwded, &some.then_some(reply)).await
|
||||
},
|
||||
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||
let api_expr = ret.serialize().await;
|
||||
mem::drop(expr_handle);
|
||||
expr_store.dispose().await;
|
||||
hand.handle(call, &api_expr).await
|
||||
},
|
||||
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
|
||||
let expr_store = BorrowedExprStore::new();
|
||||
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
|
||||
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
|
||||
let api_expr = ret.serialize().await;
|
||||
mem::drop(expr_handle);
|
||||
expr_store.dispose().await;
|
||||
hand.handle(call, &api_expr).await
|
||||
},
|
||||
api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await {
|
||||
Err(e) => hand.handle(cmd, &Err(e.to_api())).await,
|
||||
Ok(opt) => match opt {
|
||||
None => hand.handle(cmd, &Ok(api::NextStep::Halt)).await,
|
||||
Some(cont) => {
|
||||
let cont = cont.serialize().await;
|
||||
hand.handle(cmd, &Ok(api::NextStep::Continue(cont))).await
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::DeserAtom(deser) => {
|
||||
let api::DeserAtom(sys, buf, refs) = &deser;
|
||||
let mut read = &mut &buf[..];
|
||||
let ctx = get_ctx(*sys).await;
|
||||
// SAFETY: deserialization implicitly grants ownership to previously owned exprs
|
||||
let refs = (refs.iter())
|
||||
.map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk)))
|
||||
.collect_vec();
|
||||
let id = AtomTypeId::decode(Pin::new(&mut read)).await;
|
||||
let inst = ctx.cted().inst();
|
||||
let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID");
|
||||
hand.handle(&deser, &nfo.deserialize(read, &refs).await).await
|
||||
},
|
||||
}
|
||||
}
|
||||
.boxed_local()
|
||||
}
|
||||
},
|
||||
);
|
||||
*interner_cell.borrow_mut() =
|
||||
Some(Interner::new_replica(rn.clone().map(|ir: api::IntReq| ir.into_root())));
|
||||
spawner(Box::pin(clone!(spawner; async move {
|
||||
let mut streams = stream_select! { in_recv.map(Some), exit_recv.map(|_| None) };
|
||||
while let Some(item) = streams.next().await {
|
||||
match item {
|
||||
Some(rcvd) => spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd[..]).await }))),
|
||||
None => break,
|
||||
}
|
||||
}
|
||||
})));
|
||||
ExtInit {
|
||||
header: ext_header,
|
||||
port: Box::new(ExtensionOwner {
|
||||
out_recv: Mutex::new(out_recv),
|
||||
out_send,
|
||||
_interner_cell: interner_cell,
|
||||
_systems_lock: systems_lock,
|
||||
}),
|
||||
})
|
||||
.await
|
||||
},
|
||||
api::HostExtReq::DeserAtom(deser) => {
|
||||
let api::DeserAtom(sys, buf, refs) = &deser;
|
||||
let read = &mut &buf[..];
|
||||
with_sys_record(*sys, async {
|
||||
// SAFETY: deserialization implicitly grants ownership to previously owned exprs
|
||||
let refs = (refs.iter())
|
||||
.map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk)))
|
||||
.collect_vec();
|
||||
let id = AtomTypeId::decode_slice(read);
|
||||
let nfo = atom_by_idx(cted().inst().card(), id)
|
||||
.expect("Deserializing atom with invalid ID");
|
||||
handle.reply(&deser, &nfo.deserialize(read, &refs).await).await
|
||||
})
|
||||
.await
|
||||
},
|
||||
}
|
||||
})
|
||||
.await
|
||||
},
|
||||
);
|
||||
// add essential services to the very tail, then fold all context into the run
|
||||
// future
|
||||
SYSTEM_TABLE
|
||||
.scope(
|
||||
RefCell::default(),
|
||||
with_interner(
|
||||
new_interner(),
|
||||
with_logger(
|
||||
logger2,
|
||||
with_comm(
|
||||
Rc::new(client),
|
||||
ctx,
|
||||
(self.context.into_iter()).fold(
|
||||
Box::pin(async move { run_extension.await.unwrap() }) as LocalBoxFuture<()>,
|
||||
|fut, cx| cx.apply(fut),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.await
|
||||
}) as Pin<Box<_>>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,12 +10,13 @@ use hashbrown::HashSet;
|
||||
use orchid_base::error::OrcErrv;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::reqnot::Requester;
|
||||
use orchid_base::stash::stash;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::ForeignAtom;
|
||||
use crate::context::{ctx, i};
|
||||
use crate::entrypoint::{notify, request};
|
||||
use crate::gen_expr::{GExpr, GExprKind};
|
||||
use crate::system::sys_id;
|
||||
|
||||
pub struct BorrowedExprStore(RefCell<Option<HashSet<Rc<ExprHandle>>>>);
|
||||
impl BorrowedExprStore {
|
||||
@@ -73,7 +74,7 @@ impl ExprHandle {
|
||||
/// to lend the expr, and you expect the receiver to use
|
||||
/// [ExprHandle::borrowed] or [ExprHandle::from_ticket]
|
||||
pub fn ticket(&self) -> api::ExprTicket { self.0 }
|
||||
async fn send_acq(&self) { ctx().reqnot().notify(api::Acquire(ctx().sys_id(), self.0)).await }
|
||||
async fn send_acq(&self) { notify(api::Acquire(sys_id(), self.0)).await }
|
||||
/// If this is the last one reference, do nothing, otherwise send an Acquire
|
||||
pub async fn on_borrow_expire(self: Rc<Self>) { self.serialize().await; }
|
||||
/// Drop the handle and get the ticket without a release notification.
|
||||
@@ -94,8 +95,8 @@ impl fmt::Debug for ExprHandle {
|
||||
}
|
||||
impl Drop for ExprHandle {
|
||||
fn drop(&mut self) {
|
||||
let notif = api::Release(ctx().sys_id(), self.0);
|
||||
ctx().spawn(async move { ctx().reqnot().clone().notify(notif).await })
|
||||
let notif = api::Release(sys_id(), self.0);
|
||||
stash(async move { notify(notif).await })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,12 +118,12 @@ impl Expr {
|
||||
}
|
||||
pub async fn data(&self) -> &ExprData {
|
||||
(self.data.get_or_init(async {
|
||||
let details = ctx().reqnot().request(api::Inspect { target: self.handle.ticket() }).await;
|
||||
let pos = Pos::from_api(&details.location, &i()).await;
|
||||
let details = request(api::Inspect { target: self.handle.ticket() }).await;
|
||||
let pos = Pos::from_api(&details.location).await;
|
||||
let kind = match details.kind {
|
||||
api::InspectedKind::Atom(a) =>
|
||||
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
|
||||
api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b, &i()).await),
|
||||
api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b).await),
|
||||
api::InspectedKind::Opaque => ExprKind::Opaque,
|
||||
};
|
||||
ExprData { pos, kind }
|
||||
@@ -150,8 +151,7 @@ impl Format for Expr {
|
||||
match &self.data().await.kind {
|
||||
ExprKind::Opaque => "OPAQUE".to_string().into(),
|
||||
ExprKind::Bottom(b) => format!("Bottom({b})").into(),
|
||||
ExprKind::Atom(a) =>
|
||||
FmtUnit::from_api(&ctx().reqnot().request(api::ExtAtomPrint(a.atom.clone())).await),
|
||||
ExprKind::Atom(a) => FmtUnit::from_api(&request(api::ExtAtomPrint(a.atom.clone())).await),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
orchid-extension/src/ext_port.rs
Normal file
12
orchid-extension/src/ext_port.rs
Normal file
@@ -0,0 +1,12 @@
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::{AsyncRead, AsyncWrite};
|
||||
|
||||
pub struct ExtPort {
|
||||
pub input: Pin<Box<dyn AsyncRead>>,
|
||||
pub output: Pin<Box<dyn AsyncWrite>>,
|
||||
pub log: Pin<Box<dyn AsyncWrite>>,
|
||||
pub spawn: Rc<dyn Fn(LocalBoxFuture<'static, ()>)>,
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
use std::any::TypeId;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::Mutex;
|
||||
use futures::{AsyncWrite, FutureExt};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
@@ -15,15 +15,17 @@ use orchid_base::clone;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit};
|
||||
use orchid_base::name::Sym;
|
||||
use task_local::task_local;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::Atomic;
|
||||
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
|
||||
use crate::context::{SysCtxEntry, ctx, i};
|
||||
use crate::conv::ToExpr;
|
||||
use crate::coroutine_exec::{ExecHandle, exec};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::GExpr;
|
||||
use crate::system::sys_id;
|
||||
|
||||
trait_set! {
|
||||
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
|
||||
@@ -34,9 +36,14 @@ pub trait ExprFunc<I, O>: Clone + 'static {
|
||||
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FunsCtx(Mutex<HashMap<Sym, FunRecord>>);
|
||||
impl SysCtxEntry for FunsCtx {}
|
||||
task_local! {
|
||||
static FUNS_CTX: RefCell<HashMap<(api::SysId, Sym), FunRecord>>;
|
||||
}
|
||||
|
||||
pub(crate) fn with_funs_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(FUNS_CTX.scope(RefCell::default(), fut))
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FunRecord {
|
||||
argtyps: &'static [TypeId],
|
||||
@@ -77,17 +84,17 @@ pub(crate) struct Fun {
|
||||
}
|
||||
impl Fun {
|
||||
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, f: F) -> Self {
|
||||
let ctx = ctx();
|
||||
let funs: &FunsCtx = ctx.get_or_default();
|
||||
let mut fung = funs.0.lock().await;
|
||||
let record = if let Some(record) = fung.get(&path) {
|
||||
record.clone()
|
||||
} else {
|
||||
let record = process_args(f);
|
||||
fung.insert(path.clone(), record.clone());
|
||||
record
|
||||
};
|
||||
Self { args: vec![], path, record }
|
||||
FUNS_CTX.with(|cx| {
|
||||
let mut fung = cx.borrow_mut();
|
||||
let record = if let Some(record) = fung.get(&(sys_id(), path.clone())) {
|
||||
record.clone()
|
||||
} else {
|
||||
let record = process_args(f);
|
||||
fung.insert((sys_id(), path.clone()), record.clone());
|
||||
record
|
||||
};
|
||||
Self { args: vec![], path, record }
|
||||
})
|
||||
}
|
||||
pub fn arity(&self) -> u8 { self.record.argtyps.len() as u8 }
|
||||
}
|
||||
@@ -108,12 +115,12 @@ impl OwnedAtom for Fun {
|
||||
}
|
||||
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
|
||||
async fn serialize(&self, write: Pin<&mut (impl AsyncWrite + ?Sized)>) -> Self::Refs {
|
||||
self.path.to_api().encode(write).await;
|
||||
self.path.to_api().encode(write).await.unwrap();
|
||||
self.args.clone()
|
||||
}
|
||||
async fn deserialize(mut ds_cx: impl DeserializeCtx, args: Self::Refs) -> Self {
|
||||
let path = Sym::from_api(ds_cx.decode().await, &i()).await;
|
||||
let record = (ctx().get::<FunsCtx>().0.lock().await.get(&path))
|
||||
let path = Sym::from_api(ds_cx.decode().await).await;
|
||||
let record = (FUNS_CTX.with(|funs| funs.borrow().get(&(sys_id(), path.clone())).cloned()))
|
||||
.expect("Function missing during deserialization")
|
||||
.clone();
|
||||
Self { args, path, record }
|
||||
|
||||
@@ -6,12 +6,11 @@ use orchid_base::error::{OrcErr, OrcErrv};
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::Requester;
|
||||
use orchid_base::{match_mapping, tl_cache};
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::{AtomFactory, ToAtom};
|
||||
use crate::context::ctx;
|
||||
use crate::entrypoint::request;
|
||||
use crate::expr::Expr;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@@ -40,7 +39,7 @@ impl GExpr {
|
||||
}
|
||||
pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } }
|
||||
pub async fn create(self) -> Expr {
|
||||
Expr::deserialize(ctx().reqnot().request(api::Create(self.serialize().await)).await).await
|
||||
Expr::deserialize(request(api::Create(self.serialize().await)).await).await
|
||||
}
|
||||
}
|
||||
impl Format for GExpr {
|
||||
|
||||
@@ -1,73 +1,48 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::rc::Rc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api_traits::Coding;
|
||||
use orchid_base::interner::{IStr, IStrHandle, IStrv, IStrvHandle};
|
||||
use futures::future::{LocalBoxFuture, join_all, ready};
|
||||
use itertools::Itertools;
|
||||
use orchid_base::interner::local_interner::{Int, StrBranch, StrvBranch};
|
||||
use orchid_base::interner::{IStr, IStrv, InternerSrv};
|
||||
|
||||
use crate::api;
|
||||
use crate::entrypoint::request;
|
||||
|
||||
trait Branch: 'static {
|
||||
type Token: Clone + Copy + Debug + Hash + PartialEq + Eq + PartialOrd + Ord + Coding + 'static;
|
||||
type Data: 'static + Borrow<Self::Borrow>;
|
||||
type Borrow: ToOwned<Owned = Self::Data> + ?Sized;
|
||||
type Handle: AsRef<Self::Borrow>;
|
||||
type Interned: Clone;
|
||||
fn mk_interned(t: Self::Token, h: Rc<Self::Handle>) -> Self::Interned;
|
||||
#[derive(Default)]
|
||||
struct ExtInterner {
|
||||
str: Int<StrBranch>,
|
||||
strv: Int<StrvBranch>,
|
||||
}
|
||||
impl InternerSrv for ExtInterner {
|
||||
fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr> {
|
||||
match self.str.i(v) {
|
||||
Ok(i) => Box::pin(ready(i)),
|
||||
Err(e) => Box::pin(async { e.set_if_empty(request(api::InternStr(v.to_owned())).await) }),
|
||||
}
|
||||
}
|
||||
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr> {
|
||||
match self.str.e(t) {
|
||||
Ok(i) => Box::pin(ready(i)),
|
||||
Err(e) => Box::pin(async move { e.set_if_empty(Rc::new(request(api::ExternStr(t)).await)) }),
|
||||
}
|
||||
}
|
||||
fn iv<'a>(&'a self, v: &'a [IStr]) -> LocalBoxFuture<'a, IStrv> {
|
||||
match self.strv.i(v) {
|
||||
Ok(i) => Box::pin(ready(i)),
|
||||
Err(e) => Box::pin(async {
|
||||
e.set_if_empty(request(api::InternStrv(v.iter().map(|is| is.to_api()).collect_vec())).await)
|
||||
}),
|
||||
}
|
||||
}
|
||||
fn ev(&self, t: orchid_api::TStrv) -> LocalBoxFuture<'_, IStrv> {
|
||||
match self.strv.e(t) {
|
||||
Ok(i) => Box::pin(ready(i)),
|
||||
Err(e) => Box::pin(async move {
|
||||
let tstr_v = request(api::ExternStrv(t)).await;
|
||||
e.set_if_empty(Rc::new(join_all(tstr_v.into_iter().map(|t| self.es(t))).await))
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StrBranch;
|
||||
impl Branch for StrBranch {
|
||||
type Data = String;
|
||||
type Token = api::TStr;
|
||||
type Borrow = str;
|
||||
type Handle = Handle<Self>;
|
||||
type Interned = IStr;
|
||||
fn mk_interned(t: Self::Token, h: Rc<Self::Handle>) -> Self::Interned { IStr(t, h) }
|
||||
}
|
||||
struct StrvBranch;
|
||||
impl Branch for StrvBranch {
|
||||
type Data = Vec<IStr>;
|
||||
type Token = api::TStrv;
|
||||
type Borrow = [IStr];
|
||||
type Handle = Handle<Self>;
|
||||
type Interned = IStrv;
|
||||
fn mk_interned(t: Self::Token, h: Rc<Self::Handle>) -> Self::Interned { IStrv(t, h) }
|
||||
}
|
||||
|
||||
struct Data<B: Branch> {
|
||||
token: B::Token,
|
||||
data: Rc<B::Data>,
|
||||
}
|
||||
|
||||
struct Handle<B: Branch> {
|
||||
data: Rc<Data<B>>,
|
||||
parent: Weak<RefCell<IntData<B>>>,
|
||||
}
|
||||
impl IStrHandle for Handle<StrBranch> {}
|
||||
impl AsRef<str> for Handle<StrBranch> {
|
||||
fn as_ref(&self) -> &str { self.data.data.as_ref().as_ref() }
|
||||
}
|
||||
impl IStrvHandle for Handle<StrvBranch> {}
|
||||
impl AsRef<[IStr]> for Handle<StrvBranch> {
|
||||
fn as_ref(&self) -> &[IStr] { self.data.data.as_ref().as_ref() }
|
||||
}
|
||||
|
||||
struct Rec<B: Branch> {
|
||||
handle: Weak<B::Handle>,
|
||||
data: Rc<Data<B>>,
|
||||
}
|
||||
|
||||
struct IntData<B: Branch> {
|
||||
by_tok: HashMap<B::Token, Rec<B>>,
|
||||
by_data: HashMap<Rc<B::Data>, Rec<B>>,
|
||||
}
|
||||
impl<B: Branch> IntData<B> {
|
||||
async fn i(&mut self, q: &B::Borrow) -> B::Interned { todo!() }
|
||||
async fn e(&mut self, q: &B::Token) -> B::Interned { todo!() }
|
||||
}
|
||||
|
||||
struct Int<B: Branch>(Rc<RefCell<IntData<B>>>);
|
||||
pub fn new_interner() -> Rc<dyn InternerSrv> { Rc::<ExtInterner>::default() }
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv};
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
||||
use orchid_base::interner::{IStr, is};
|
||||
use orchid_base::location::{Pos, SrcRange};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::ParseCtx;
|
||||
use orchid_base::reqnot::Requester;
|
||||
|
||||
use crate::api;
|
||||
use crate::context::{ctx, i};
|
||||
use crate::entrypoint::request;
|
||||
use crate::expr::BorrowedExprStore;
|
||||
use crate::parser::PTokTree;
|
||||
use crate::tree::GenTokTree;
|
||||
|
||||
pub async fn ekey_cascade() -> Tok<String> {
|
||||
i().i("An error cascading from a recursive call").await
|
||||
}
|
||||
pub async fn ekey_not_applicable() -> Tok<String> {
|
||||
i().i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
|
||||
pub async fn ekey_cascade() -> IStr { is("An error cascading from a recursive call").await }
|
||||
pub async fn ekey_not_applicable() -> IStr {
|
||||
is("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
|
||||
}
|
||||
const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\
|
||||
it should not be emitted by the extension.";
|
||||
@@ -36,23 +33,20 @@ pub async fn err_not_applicable() -> OrcErrv {
|
||||
|
||||
pub struct LexContext<'a> {
|
||||
pub(crate) exprs: &'a BorrowedExprStore,
|
||||
pub text: &'a Tok<String>,
|
||||
pub text: &'a IStr,
|
||||
pub id: api::ParsId,
|
||||
pub pos: u32,
|
||||
i: Interner,
|
||||
pub(crate) src: Sym,
|
||||
pub(crate) rep: &'a Reporter,
|
||||
}
|
||||
impl<'a> LexContext<'a> {
|
||||
pub fn new(
|
||||
exprs: &'a BorrowedExprStore,
|
||||
text: &'a Tok<String>,
|
||||
text: &'a IStr,
|
||||
id: api::ParsId,
|
||||
pos: u32,
|
||||
src: Sym,
|
||||
rep: &'a Reporter,
|
||||
) -> Self {
|
||||
Self { exprs, i: i(), id, pos, rep, src, text }
|
||||
Self { exprs, id, pos, src, text }
|
||||
}
|
||||
pub fn src(&self) -> &Sym { &self.src }
|
||||
/// This function returns [PTokTree] because it can never return
|
||||
@@ -61,10 +55,10 @@ impl<'a> LexContext<'a> {
|
||||
/// for embedding in the return value.
|
||||
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> {
|
||||
let start = self.pos(tail);
|
||||
let Some(lx) = ctx().reqnot().request(api::SubLex { pos: start, id: self.id }).await else {
|
||||
let Some(lx) = request(api::SubLex { pos: start, id: self.id }).await else {
|
||||
return Err(err_cascade().await);
|
||||
};
|
||||
let tree = PTokTree::from_api(&lx.tree, &mut { self.exprs }, &mut (), &self.src, &i()).await;
|
||||
let tree = PTokTree::from_api(&lx.tree, &mut { self.exprs }, &mut (), &self.src).await;
|
||||
Ok((&self.text[lx.pos as usize..], tree))
|
||||
}
|
||||
|
||||
@@ -77,12 +71,8 @@ impl<'a> LexContext<'a> {
|
||||
SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src)
|
||||
}
|
||||
}
|
||||
impl ParseCtx for LexContext<'_> {
|
||||
fn i(&self) -> &Interner { &self.i }
|
||||
fn rep(&self) -> &Reporter { self.rep }
|
||||
}
|
||||
|
||||
pub trait Lexer: Send + Sync + Sized + Default + 'static {
|
||||
pub trait Lexer: Debug + Send + Sync + Sized + Default + 'static {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>];
|
||||
fn lex<'a>(
|
||||
tail: &'a str,
|
||||
@@ -90,7 +80,7 @@ pub trait Lexer: Send + Sync + Sized + Default + 'static {
|
||||
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree)>>;
|
||||
}
|
||||
|
||||
pub trait DynLexer: Send + Sync + 'static {
|
||||
pub trait DynLexer: Debug + Send + Sync + 'static {
|
||||
fn char_filter(&self) -> &'static [RangeInclusive<char>];
|
||||
fn lex<'a>(
|
||||
&self,
|
||||
|
||||
@@ -7,12 +7,11 @@ pub mod conv;
|
||||
pub mod coroutine_exec;
|
||||
pub mod entrypoint;
|
||||
pub mod expr;
|
||||
pub mod ext_port;
|
||||
pub mod func_atom;
|
||||
pub mod gen_expr;
|
||||
pub mod lexer;
|
||||
// pub mod msg;
|
||||
pub mod context;
|
||||
pub mod interner;
|
||||
pub mod lexer;
|
||||
pub mod other_system;
|
||||
pub mod parser;
|
||||
pub mod reflection;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::api;
|
||||
use crate::system::{DynSystemCard, SystemCard};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SystemHandle<C: SystemCard> {
|
||||
pub(crate) card: C,
|
||||
pub(crate) id: api::SysId,
|
||||
|
||||
@@ -5,21 +5,22 @@ use futures::future::{LocalBoxFuture, join_all};
|
||||
use futures::{FutureExt, Stream, StreamExt};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::error::{OrcErrv, OrcRes, Reporter};
|
||||
use orchid_base::error::{OrcErrv, OrcRes};
|
||||
use orchid_base::id_store::IdStore;
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::interner::IStr;
|
||||
use orchid_base::location::SrcRange;
|
||||
use orchid_base::match_mapping;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{Comment, ParseCtx, Snippet};
|
||||
use orchid_base::reqnot::Requester;
|
||||
use orchid_base::parse::{Comment, Snippet};
|
||||
use orchid_base::tree::{TokTree, Token, ttv_into_api};
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::context::{SysCtxEntry, ctx, i};
|
||||
use crate::conv::ToExpr;
|
||||
use crate::entrypoint::request;
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::GExpr;
|
||||
use crate::system::sys_id;
|
||||
use crate::tree::{GenTok, GenTokTree};
|
||||
|
||||
pub type PTok = Token<Expr, Never>;
|
||||
@@ -82,27 +83,21 @@ pub type ParserObj = &'static dyn DynParser;
|
||||
pub struct ParsCtx<'a> {
|
||||
_parse: PhantomData<&'a mut ()>,
|
||||
module: Sym,
|
||||
reporter: &'a Reporter,
|
||||
i: Interner,
|
||||
}
|
||||
impl<'a> ParsCtx<'a> {
|
||||
pub(crate) fn new(module: Sym, reporter: &'a Reporter) -> Self {
|
||||
Self { _parse: PhantomData, module, reporter, i: i() }
|
||||
}
|
||||
pub(crate) fn new(module: Sym) -> Self { Self { _parse: PhantomData, module } }
|
||||
pub fn module(&self) -> Sym { self.module.clone() }
|
||||
}
|
||||
impl ParseCtx for ParsCtx<'_> {
|
||||
fn i(&self) -> &Interner { &self.i }
|
||||
fn rep(&self) -> &Reporter { self.reporter }
|
||||
}
|
||||
|
||||
type BoxConstCallback = Box<dyn FnOnce(ConstCtx) -> LocalBoxFuture<'static, GExpr>>;
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ParsedConstCtxEntry {
|
||||
pub(crate) consts: IdStore<BoxConstCallback>,
|
||||
task_local! {
|
||||
static PARSED_CONST_CTX: IdStore<BoxConstCallback>
|
||||
}
|
||||
|
||||
pub(crate) fn with_parsed_const_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(PARSED_CONST_CTX.scope(IdStore::default(), fut))
|
||||
}
|
||||
impl SysCtxEntry for ParsedConstCtxEntry {}
|
||||
|
||||
pub struct ParsedLine {
|
||||
pub sr: SrcRange,
|
||||
@@ -114,7 +109,7 @@ impl ParsedLine {
|
||||
sr: &SrcRange,
|
||||
comments: impl IntoIterator<Item = &'a Comment>,
|
||||
exported: bool,
|
||||
name: Tok<String>,
|
||||
name: IStr,
|
||||
f: F,
|
||||
) -> Self {
|
||||
let cb = Box::new(|ctx| async move { f(ctx).await.to_gen().await }.boxed_local());
|
||||
@@ -126,7 +121,7 @@ impl ParsedLine {
|
||||
sr: &SrcRange,
|
||||
comments: impl IntoIterator<Item = &'a Comment>,
|
||||
exported: bool,
|
||||
name: &Tok<String>,
|
||||
name: &IStr,
|
||||
use_prelude: bool,
|
||||
lines: impl IntoIterator<Item = ParsedLine>,
|
||||
) -> Self {
|
||||
@@ -145,7 +140,7 @@ impl ParsedLine {
|
||||
exported: mem.exported,
|
||||
kind: match mem.kind {
|
||||
ParsedMemKind::Const(cb) => api::ParsedMemberKind::Constant(api::ParsedConstId(
|
||||
ctx().get_or_default::<ParsedConstCtxEntry>().consts.add(cb).id(),
|
||||
PARSED_CONST_CTX.with(|consts| consts.add(cb).id()),
|
||||
)),
|
||||
ParsedMemKind::Mod { lines, use_prelude } => api::ParsedMemberKind::Module {
|
||||
lines: linev_into_api(lines).boxed_local().await,
|
||||
@@ -170,7 +165,7 @@ pub enum ParsedLineKind {
|
||||
}
|
||||
|
||||
pub struct ParsedMem {
|
||||
pub name: Tok<String>,
|
||||
pub name: IStr,
|
||||
pub exported: bool,
|
||||
pub kind: ParsedMemKind,
|
||||
}
|
||||
@@ -191,14 +186,14 @@ impl ConstCtx {
|
||||
) -> impl Stream<Item = OrcRes<Sym>> + 'b {
|
||||
let resolve_names = api::ResolveNames {
|
||||
constid: self.constid,
|
||||
sys: ctx().sys_id(),
|
||||
sys: sys_id(),
|
||||
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
|
||||
};
|
||||
stream(async |mut cx| {
|
||||
for name_opt in ctx().reqnot().request(resolve_names).await {
|
||||
for name_opt in request(resolve_names).await {
|
||||
cx.emit(match name_opt {
|
||||
Err(e) => Err(OrcErrv::from_api(&e, &i()).await),
|
||||
Ok(name) => Ok(Sym::from_api(name, &i()).await),
|
||||
Err(e) => Err(OrcErrv::from_api(&e).await),
|
||||
Ok(name) => Ok(Sym::from_api(name).await),
|
||||
})
|
||||
.await
|
||||
}
|
||||
@@ -210,8 +205,7 @@ impl ConstCtx {
|
||||
}
|
||||
|
||||
pub(crate) async fn get_const(id: api::ParsedConstId) -> GExpr {
|
||||
let cb = (ctx().get_or_default::<ParsedConstCtxEntry>().consts.get(id.0))
|
||||
.expect("Bad ID or double read of parsed const")
|
||||
.remove();
|
||||
let cb = PARSED_CONST_CTX
|
||||
.with(|ent| ent.get(id.0).expect("Bad ID or double read of parsed const").remove());
|
||||
cb(ConstCtx { constid: id }).await
|
||||
}
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use std::cell::OnceCell;
|
||||
use std::cell::{OnceCell, RefCell};
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::FutureExt;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::Mutex;
|
||||
use hashbrown::HashMap;
|
||||
use memo_map::MemoMap;
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::interner::{IStr, es, iv};
|
||||
use orchid_base::name::{NameLike, VPath};
|
||||
use orchid_base::reqnot::Requester;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::context::{SysCtxEntry, ctx, i};
|
||||
use crate::entrypoint::request;
|
||||
use crate::system::sys_id;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReflMemData {
|
||||
@@ -33,29 +36,28 @@ pub enum ReflMemKind {
|
||||
pub struct ReflModData {
|
||||
inferred: Mutex<bool>,
|
||||
path: VPath,
|
||||
members: MemoMap<Tok<String>, ReflMem>,
|
||||
members: MemoMap<IStr, ReflMem>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ReflMod(Rc<ReflModData>);
|
||||
impl ReflMod {
|
||||
pub fn path(&self) -> &[Tok<String>] { &self.0.path[..] }
|
||||
pub fn path(&self) -> &[IStr] { &self.0.path[..] }
|
||||
pub fn is_root(&self) -> bool { self.0.path.is_empty() }
|
||||
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
|
||||
let path_tok = i().i(&self.0.path[..]).await;
|
||||
let reply = match ctx().reqnot().request(api::LsModule(ctx().sys_id(), path_tok.to_api())).await
|
||||
{
|
||||
let path_tok = iv(&self.0.path[..]).await;
|
||||
let reply = match request(api::LsModule(sys_id(), path_tok.to_api())).await {
|
||||
Err(api::LsModuleError::TreeUnavailable) =>
|
||||
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."),
|
||||
Err(err) => return Err(err),
|
||||
Ok(details) => details,
|
||||
};
|
||||
for (k, v) in reply.members {
|
||||
let k = i().ex(k).await;
|
||||
let k = es(k).await;
|
||||
let mem = match self.0.members.get(&k) {
|
||||
Some(mem) => mem,
|
||||
None => {
|
||||
let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym(&i()).await;
|
||||
let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym().await;
|
||||
let kind = match v.kind {
|
||||
api::MemberInfoKind::Constant => ReflMemKind::Const,
|
||||
api::MemberInfoKind::Module =>
|
||||
@@ -68,7 +70,7 @@ impl ReflMod {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
pub async fn get_child(&self, key: &Tok<String>) -> Option<ReflMem> {
|
||||
pub async fn get_child(&self, key: &IStr) -> Option<ReflMem> {
|
||||
let inferred_g = self.0.inferred.lock().await;
|
||||
if let Some(mem) = self.0.members.get(key) {
|
||||
return Some(mem.clone());
|
||||
@@ -86,7 +88,7 @@ impl ReflMod {
|
||||
}
|
||||
self.0.members.get(key).cloned()
|
||||
}
|
||||
pub async fn get_by_path(&self, path: &[Tok<String>]) -> Result<ReflMem, InvalidPathError> {
|
||||
pub async fn get_by_path(&self, path: &[IStr]) -> Result<ReflMem, InvalidPathError> {
|
||||
let (next, tail) = path.split_first().expect("Attempted to walk by empty path");
|
||||
let inferred_g = self.0.inferred.lock().await;
|
||||
if let Some(next) = self.0.members.get(next) {
|
||||
@@ -130,9 +132,9 @@ impl ReflMod {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ReflRoot(ReflMod);
|
||||
impl SysCtxEntry for ReflRoot {}
|
||||
task_local! {
|
||||
static REFL_ROOTS: RefCell<HashMap<api::SysId, ReflMod>>
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InvalidPathError {
|
||||
@@ -150,8 +152,12 @@ fn default_member(is_root: bool, kind: ReflMemKind) -> ReflMem {
|
||||
}))
|
||||
}
|
||||
|
||||
fn get_root() -> ReflRoot {
|
||||
ctx().get_or_insert(|| ReflRoot(default_module(VPath::new([])))).clone()
|
||||
pub fn refl() -> ReflMod {
|
||||
REFL_ROOTS.with(|tbl| {
|
||||
tbl.borrow_mut().entry(sys_id()).or_insert_with(|| default_module(VPath::new([]))).clone()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn refl() -> ReflMod { get_root().0.clone() }
|
||||
pub fn with_refl_roots<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(REFL_ROOTS.scope(RefCell::default(), fut))
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::fmt::Debug;
|
||||
use std::future::Future;
|
||||
use std::num::NonZero;
|
||||
use std::pin::Pin;
|
||||
@@ -8,13 +9,13 @@ use futures::future::LocalBoxFuture;
|
||||
use orchid_api_traits::{Coding, Decode, Encode, Request};
|
||||
use orchid_base::boxed_iter::BoxedIter;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::reqnot::{Receipt, Requester};
|
||||
use orchid_base::reqnot::{Receipt, ReqHandle, ReqReader, ReqReaderExt};
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TAtom, get_info};
|
||||
use crate::context::ctx;
|
||||
use crate::coroutine_exec::Replier;
|
||||
use crate::entrypoint::ExtReq;
|
||||
use crate::entrypoint::request;
|
||||
use crate::func_atom::{Fun, Lambda};
|
||||
use crate::lexer::LexerObj;
|
||||
use crate::parser::ParserObj;
|
||||
@@ -22,7 +23,7 @@ use crate::system_ctor::{CtedObj, SystemCtor};
|
||||
use crate::tree::GenMember;
|
||||
|
||||
/// System as consumed by foreign code
|
||||
pub trait SystemCard: Default + Send + Sync + 'static {
|
||||
pub trait SystemCard: Debug + Default + Send + Sync + 'static {
|
||||
type Ctor: SystemCtor;
|
||||
type Req: Coding;
|
||||
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomDynfo>>>;
|
||||
@@ -67,7 +68,7 @@ pub async fn resolv_atom(
|
||||
sys: &(impl DynSystemCard + ?Sized),
|
||||
atom: &api::Atom,
|
||||
) -> Box<dyn AtomDynfo> {
|
||||
let tid = AtomTypeId::decode(Pin::new(&mut &atom.data.0[..])).await;
|
||||
let tid = AtomTypeId::decode(Pin::new(&mut &atom.data.0[..])).await.unwrap();
|
||||
atom_by_idx(sys, tid).expect("Value of nonexistent type found")
|
||||
}
|
||||
|
||||
@@ -84,7 +85,10 @@ pub trait System: Send + Sync + SystemCard + 'static {
|
||||
fn env() -> impl Future<Output = Vec<GenMember>>;
|
||||
fn lexers() -> Vec<LexerObj>;
|
||||
fn parsers() -> Vec<ParserObj>;
|
||||
fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future<Output = Receipt<'_>>;
|
||||
fn request<'a>(
|
||||
hand: Box<dyn ReqHandle<'a> + 'a>,
|
||||
req: Self::Req,
|
||||
) -> impl Future<Output = Receipt<'a>>;
|
||||
}
|
||||
|
||||
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
||||
@@ -92,7 +96,7 @@ pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
|
||||
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>>;
|
||||
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
||||
fn dyn_parsers(&self) -> Vec<ParserObj>;
|
||||
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
||||
fn dyn_request<'a>(&self, hand: Box<dyn ReqReader<'a> + 'a>) -> LocalBoxFuture<'a, Receipt<'a>>;
|
||||
fn card(&self) -> &dyn DynSystemCard;
|
||||
}
|
||||
|
||||
@@ -101,26 +105,41 @@ impl<T: System> DynSystem for T {
|
||||
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>> { Self::env().boxed_local() }
|
||||
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
|
||||
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
|
||||
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
|
||||
fn dyn_request<'a>(
|
||||
&self,
|
||||
mut hand: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, Receipt<'a>> {
|
||||
Box::pin(async move {
|
||||
Self::request(hand, <Self as SystemCard>::Req::decode(Pin::new(&mut &req[..])).await).await
|
||||
let value = hand.read_req::<<Self as SystemCard>::Req>().await.unwrap();
|
||||
Self::request(hand.finish().await, value).await
|
||||
})
|
||||
}
|
||||
fn card(&self) -> &dyn DynSystemCard { self }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct SysCtx(pub api::SysId, pub CtedObj);
|
||||
|
||||
task_local! {
|
||||
static SYS_CTX: SysCtx;
|
||||
}
|
||||
|
||||
pub(crate) async fn with_sys<F: Future>(sys: SysCtx, fut: F) -> F::Output {
|
||||
SYS_CTX.scope(sys, fut).await
|
||||
}
|
||||
|
||||
pub fn sys_id() -> api::SysId { SYS_CTX.with(|cx| cx.0) }
|
||||
pub fn cted() -> CtedObj { SYS_CTX.with(|cx| cx.1.clone()) }
|
||||
pub async fn downcast_atom<A>(foreign: ForeignAtom) -> Result<TAtom<A>, ForeignAtom>
|
||||
where A: AtomicFeatures {
|
||||
let mut data = &foreign.atom.data.0[..];
|
||||
let ctx = ctx();
|
||||
let value = AtomTypeId::decode(Pin::new(&mut data)).await;
|
||||
let own_inst = ctx.get::<CtedObj>().inst();
|
||||
let owner = if *ctx.get::<api::SysId>() == foreign.atom.owner {
|
||||
let value = AtomTypeId::decode_slice(&mut data);
|
||||
let cted = cted();
|
||||
let own_inst = cted.inst();
|
||||
let owner = if sys_id() == foreign.atom.owner {
|
||||
own_inst.card()
|
||||
} else {
|
||||
(ctx.get::<CtedObj>().deps().find(|s| s.id() == foreign.atom.owner))
|
||||
.ok_or_else(|| foreign.clone())?
|
||||
.get_card()
|
||||
cted.deps().find(|s| s.id() == foreign.atom.owner).ok_or_else(|| foreign.clone())?.get_card()
|
||||
};
|
||||
if owner.atoms().flatten().all(|dynfo| dynfo.tid() != TypeId::of::<A>()) {
|
||||
return Err(foreign);
|
||||
@@ -130,22 +149,24 @@ where A: AtomicFeatures {
|
||||
return Err(foreign);
|
||||
}
|
||||
let val = dynfo.decode(AtomCtx(data, foreign.atom.drop)).await;
|
||||
let value = *val.downcast::<A::Data>().expect("atom decode returned wrong type");
|
||||
Ok(TAtom { value, untyped: foreign })
|
||||
let Ok(value) = val.downcast::<A::Data>() else {
|
||||
panic!("decode of {} returned wrong type.", dynfo.name());
|
||||
};
|
||||
Ok(TAtom { value: *value, untyped: foreign })
|
||||
}
|
||||
|
||||
pub async fn dep_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
|
||||
let ctx = ctx();
|
||||
let mut msg = Vec::new();
|
||||
req.into().encode(std::pin::pin!(&mut msg)).await;
|
||||
let own_inst = ctx.get::<CtedObj>().inst();
|
||||
req.into().encode_vec(&mut msg);
|
||||
let cted = cted();
|
||||
let own_inst = cted.inst();
|
||||
let owner = if own_inst.card().type_id() == TypeId::of::<Sys>() {
|
||||
ctx.sys_id()
|
||||
sys_id()
|
||||
} else {
|
||||
(ctx.get::<CtedObj>().deps().find(|s| s.get_card().type_id() == TypeId::of::<Sys>()))
|
||||
(cted.deps().find(|s| s.get_card().type_id() == TypeId::of::<Sys>()))
|
||||
.expect("System not in dependency array")
|
||||
.id()
|
||||
};
|
||||
let reply = ctx.reqnot().request(api::SysFwd(owner, msg)).await;
|
||||
Req::Response::decode(std::pin::pin!(&reply[..])).await
|
||||
let reply = request(api::SysFwd(owner, msg)).await;
|
||||
Req::Response::decode(std::pin::pin!(&reply[..])).await.unwrap()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
use orchid_base::boxed_iter::{BoxedIter, box_empty, box_once};
|
||||
@@ -8,6 +9,7 @@ use crate::api;
|
||||
use crate::other_system::{DynSystemHandle, SystemHandle};
|
||||
use crate::system::{DynSystem, System, SystemCard};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Cted<Ctor: SystemCtor + ?Sized> {
|
||||
pub deps: <Ctor::Deps as DepDef>::Sat,
|
||||
pub inst: Arc<Ctor::Instance>,
|
||||
@@ -15,7 +17,7 @@ pub struct Cted<Ctor: SystemCtor + ?Sized> {
|
||||
impl<C: SystemCtor + ?Sized> Clone for Cted<C> {
|
||||
fn clone(&self) -> Self { Self { deps: self.deps.clone(), inst: self.inst.clone() } }
|
||||
}
|
||||
pub trait DynCted: Send + Sync + 'static {
|
||||
pub trait DynCted: Debug + Send + Sync + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
||||
fn inst(&self) -> Arc<dyn DynSystem>;
|
||||
@@ -27,11 +29,11 @@ impl<C: SystemCtor + ?Sized> DynCted for Cted<C> {
|
||||
}
|
||||
pub type CtedObj = Arc<dyn DynCted>;
|
||||
|
||||
pub trait DepSat: Clone + Send + Sync + 'static {
|
||||
pub trait DepSat: Debug + Clone + Send + Sync + 'static {
|
||||
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
||||
}
|
||||
|
||||
pub trait DepDef {
|
||||
pub trait DepDef: Debug {
|
||||
type Sat: DepSat;
|
||||
fn report(names: &mut impl FnMut(&'static str));
|
||||
fn create(take: &mut impl FnMut() -> api::SysId) -> Self::Sat;
|
||||
@@ -57,17 +59,17 @@ impl DepDef for () {
|
||||
fn report(_: &mut impl FnMut(&'static str)) {}
|
||||
}
|
||||
|
||||
pub trait SystemCtor: Send + Sync + 'static {
|
||||
pub trait SystemCtor: Debug + Send + Sync + 'static {
|
||||
type Deps: DepDef;
|
||||
type Instance: System;
|
||||
const NAME: &'static str;
|
||||
const VERSION: f64;
|
||||
/// Create a system instance. When this function is called, a context object
|
||||
/// isn't yet available
|
||||
fn inst(deps: <Self::Deps as DepDef>::Sat) -> Self::Instance;
|
||||
fn inst(&self, deps: <Self::Deps as DepDef>::Sat) -> Self::Instance;
|
||||
}
|
||||
|
||||
pub trait DynSystemCtor: Send + Sync + 'static {
|
||||
pub trait DynSystemCtor: Debug + Send + Sync + 'static {
|
||||
fn decl(&self, id: api::SysDeclId) -> api::SystemDecl;
|
||||
fn new_system(&self, new: &api::NewSystem) -> CtedObj;
|
||||
}
|
||||
@@ -84,7 +86,7 @@ impl<T: SystemCtor> DynSystemCtor for T {
|
||||
fn new_system(&self, api::NewSystem { system: _, id: _, depends }: &api::NewSystem) -> CtedObj {
|
||||
let mut ids = depends.iter().copied();
|
||||
let deps = T::Deps::create(&mut || ids.next().unwrap());
|
||||
let inst = Arc::new(T::inst(deps.clone()));
|
||||
let inst = Arc::new(self.inst(deps.clone()));
|
||||
Arc::new(Cted::<T> { deps, inst })
|
||||
}
|
||||
}
|
||||
@@ -151,8 +153,4 @@ mod dep_set_tuple_impls {
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J);
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K);
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L); // 12
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M);
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
|
||||
dep_set_tuple_impl!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); // 16
|
||||
}
|
||||
|
||||
@@ -1,57 +1,24 @@
|
||||
use crate::entrypoint::ExtensionData;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::entrypoint::ExtensionBuilder;
|
||||
use crate::ext_port::ExtPort;
|
||||
|
||||
#[cfg(feature = "tokio")]
|
||||
pub async fn tokio_main(data: ExtensionData) {
|
||||
use std::io::{ErrorKind, Write};
|
||||
use std::mem;
|
||||
use std::pin::{Pin, pin};
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use futures::StreamExt;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use futures::lock::Mutex;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use orchid_api_traits::{Decode, Encode};
|
||||
use orchid_base::msg::{recv_msg, send_msg};
|
||||
use tokio::io::{Stdout, stdin, stdout};
|
||||
pub async fn tokio_main(builder: ExtensionBuilder) {
|
||||
use tokio::io::{stderr, stdin, stdout};
|
||||
use tokio::task::{LocalSet, spawn_local};
|
||||
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
use crate::api;
|
||||
use crate::entrypoint::extension_init;
|
||||
use tokio_util::compat::{TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
|
||||
|
||||
let local_set = LocalSet::new();
|
||||
local_set.spawn_local(async {
|
||||
let host_header = api::HostHeader::decode(Pin::new(&mut stdin().compat())).await;
|
||||
let init =
|
||||
Rc::new(extension_init(data, host_header, Rc::new(|fut| mem::drop(spawn_local(fut)))));
|
||||
let mut buf = Vec::new();
|
||||
init.header.encode(Pin::new(&mut buf)).await;
|
||||
std::io::stdout().write_all(&buf).unwrap();
|
||||
std::io::stdout().flush().unwrap();
|
||||
// These are concurrent processes that never exit, so if the FuturesUnordered
|
||||
// produces any result the extension should exit
|
||||
let mut io = FuturesUnordered::<LocalBoxFuture<()>>::new();
|
||||
io.push(Box::pin(async {
|
||||
loop {
|
||||
match recv_msg(pin!(stdin().compat())).await {
|
||||
Ok(msg) => init.send(&msg[..]).await,
|
||||
Err(e) if e.kind() == ErrorKind::BrokenPipe => break,
|
||||
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
|
||||
Err(e) => panic!("{e}"),
|
||||
}
|
||||
}
|
||||
}));
|
||||
io.push(Box::pin(async {
|
||||
while let Some(msg) = init.recv().await {
|
||||
static STDOUT: OnceCell<Mutex<Compat<Stdout>>> = OnceCell::new();
|
||||
let stdout_lk = STDOUT.get_or_init(async { Mutex::new(stdout().compat_write()) }).await;
|
||||
let mut stdout_g = stdout_lk.lock().await;
|
||||
send_msg(pin!(&mut *stdout_g), &msg[..]).await.expect("Parent pipe broken");
|
||||
}
|
||||
}));
|
||||
io.next().await;
|
||||
builder.build(ExtPort {
|
||||
input: Box::pin(stdin().compat()),
|
||||
output: Box::pin(stdout().compat_write()),
|
||||
log: Box::pin(stderr().compat_write()),
|
||||
spawn: Rc::new(|fut| {
|
||||
spawn_local(fut);
|
||||
}),
|
||||
});
|
||||
});
|
||||
local_set.await;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
use std::cell::RefCell;
|
||||
use std::num::NonZero;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_fn_stream::stream;
|
||||
use dyn_clone::{DynClone, clone_box};
|
||||
@@ -6,17 +8,16 @@ use futures::future::{LocalBoxFuture, join_all};
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::interner::{IStr, is};
|
||||
use orchid_base::location::SrcRange;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::tree::{TokTree, Token, TokenVariant};
|
||||
use substack::Substack;
|
||||
use task_local::task_local;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::api;
|
||||
use crate::context::i;
|
||||
use crate::conv::ToExpr;
|
||||
use crate::entrypoint::MemberRecord;
|
||||
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
|
||||
use crate::func_atom::{ExprFunc, Fun};
|
||||
use crate::gen_expr::{GExpr, sym_ref};
|
||||
@@ -27,12 +28,7 @@ pub type GenTok = Token<Expr, GExpr>;
|
||||
impl TokenVariant<api::Expression> for GExpr {
|
||||
type FromApiCtx<'a> = ();
|
||||
type ToApiCtx<'a> = ();
|
||||
async fn from_api(
|
||||
_: &api::Expression,
|
||||
_: &mut Self::FromApiCtx<'_>,
|
||||
_: SrcRange,
|
||||
_: &Interner,
|
||||
) -> Self {
|
||||
async fn from_api(_: &api::Expression, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||
panic!("Received new expression from host")
|
||||
}
|
||||
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { self.serialize().await }
|
||||
@@ -40,12 +36,7 @@ impl TokenVariant<api::Expression> for GExpr {
|
||||
|
||||
impl TokenVariant<api::ExprTicket> for Expr {
|
||||
type FromApiCtx<'a> = &'a BorrowedExprStore;
|
||||
async fn from_api(
|
||||
api: &api::ExprTicket,
|
||||
exprs: &mut Self::FromApiCtx<'_>,
|
||||
_: SrcRange,
|
||||
_: &Interner,
|
||||
) -> Self {
|
||||
async fn from_api(api: &api::ExprTicket, exprs: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
|
||||
// SAFETY: receiving trees from sublexers implies borrowing
|
||||
Expr::from_handle(ExprHandle::borrowed(*api, exprs))
|
||||
}
|
||||
@@ -84,9 +75,8 @@ pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (
|
||||
(name.to_string(), kind)
|
||||
}
|
||||
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
|
||||
let fac = LazyMemberFactory::new(async move |sym| {
|
||||
MemKind::Const(Fun::new(sym, xf).await.to_gen().await)
|
||||
});
|
||||
let fac =
|
||||
LazyMemberFactory::new(async move |sym| MemKind::Const(Fun::new(sym, xf).await.to_gen().await));
|
||||
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
|
||||
}
|
||||
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
|
||||
@@ -167,10 +157,10 @@ pub struct GenMember {
|
||||
pub comments: Vec<String>,
|
||||
}
|
||||
impl GenMember {
|
||||
pub async fn into_api(self, tia_cx: &mut impl TreeIntoApiCtx) -> api::Member {
|
||||
let name = i().i::<String>(&self.name).await;
|
||||
pub(crate) async fn into_api(self, tia_cx: &mut impl TreeIntoApiCtx) -> api::Member {
|
||||
let name = is(&self.name).await;
|
||||
let kind = self.kind.into_api(&mut tia_cx.push_path(name.clone())).await;
|
||||
let comments = join_all(self.comments.iter().map(async |cmt| i().i(cmt).await.to_api())).await;
|
||||
let comments = join_all(self.comments.iter().map(async |cmt| is(cmt).await.to_api())).await;
|
||||
api::Member { kind, name: name.to_api(), comments, exported: self.public }
|
||||
}
|
||||
}
|
||||
@@ -181,9 +171,9 @@ pub enum MemKind {
|
||||
Lazy(LazyMemberFactory),
|
||||
}
|
||||
impl MemKind {
|
||||
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
||||
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
||||
match self {
|
||||
Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)),
|
||||
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
|
||||
Self::Const(c) => api::MemberKind::Const(c.serialize().await),
|
||||
Self::Mod { members } => api::MemberKind::Module(api::Module {
|
||||
members: stream(async |mut cx| {
|
||||
@@ -199,29 +189,58 @@ impl MemKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TreeIntoApiCtx {
|
||||
fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId;
|
||||
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx;
|
||||
pub enum MemberRecord {
|
||||
Gen(Vec<IStr>, LazyMemberFactory),
|
||||
Res,
|
||||
}
|
||||
|
||||
pub struct TreeIntoApiCtxImpl<'a, 'b> {
|
||||
pub basepath: &'a [Tok<String>],
|
||||
pub path: Substack<'a, Tok<String>>,
|
||||
pub lazy_members: &'b mut HashMap<api::TreeId, MemberRecord>,
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct LazyMemberStore(Rc<RefCell<HashMap<api::TreeId, MemberRecord>>>);
|
||||
|
||||
task_local! {
|
||||
static LAZY_MEMBERS: LazyMemberStore;
|
||||
}
|
||||
|
||||
impl TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_> {
|
||||
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx {
|
||||
TreeIntoApiCtxImpl {
|
||||
lazy_members: self.lazy_members,
|
||||
basepath: self.basepath,
|
||||
path: self.path.push(seg),
|
||||
}
|
||||
}
|
||||
fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId {
|
||||
let id = api::TreeId(NonZero::new((self.lazy_members.len() + 2) as u64).unwrap());
|
||||
let path = self.basepath.iter().cloned().chain(self.path.unreverse()).collect_vec();
|
||||
self.lazy_members.insert(id, MemberRecord::Gen(path, fac));
|
||||
pub fn with_lazy_member_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(LAZY_MEMBERS.scope(LazyMemberStore::default(), fut))
|
||||
}
|
||||
|
||||
fn add_lazy(cx: &impl TreeIntoApiCtx, fac: LazyMemberFactory) -> api::TreeId {
|
||||
LAZY_MEMBERS.with(|lazy_members| {
|
||||
let mut g = lazy_members.0.borrow_mut();
|
||||
let id = api::TreeId(NonZero::new((g.len() + 2) as u64).unwrap());
|
||||
let path = cx.path().collect_vec();
|
||||
g.insert(id, MemberRecord::Gen(path, fac));
|
||||
id
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_lazy(id: api::TreeId) -> (Sym, MemKind) {
|
||||
let (path, cb) =
|
||||
LAZY_MEMBERS.with(|tbl| match tbl.0.borrow_mut().insert(id, MemberRecord::Res) {
|
||||
None => panic!("Tree for ID not found"),
|
||||
Some(MemberRecord::Res) => panic!("This tree has already been transmitted"),
|
||||
Some(MemberRecord::Gen(path, cb)) => (path, cb),
|
||||
});
|
||||
let path = Sym::new(path).await.unwrap();
|
||||
(path.clone(), cb.build(path).await)
|
||||
}
|
||||
|
||||
pub(crate) trait TreeIntoApiCtx {
|
||||
fn push_path(&mut self, seg: IStr) -> impl TreeIntoApiCtx;
|
||||
fn path(&self) -> impl Iterator<Item = IStr>;
|
||||
}
|
||||
|
||||
pub struct TreeIntoApiCtxImpl<'a> {
|
||||
pub basepath: &'a [IStr],
|
||||
pub path: Substack<'a, IStr>,
|
||||
}
|
||||
|
||||
impl TreeIntoApiCtx for TreeIntoApiCtxImpl<'_> {
|
||||
fn push_path(&mut self, seg: IStr) -> impl TreeIntoApiCtx {
|
||||
TreeIntoApiCtxImpl { basepath: self.basepath, path: self.path.push(seg) }
|
||||
}
|
||||
fn path(&self) -> impl Iterator<Item = IStr> {
|
||||
self.basepath.iter().cloned().chain(self.path.unreverse())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user