partway through fixes, macro system needs resdesign
Some checks failed
Rust / build (push) Has been cancelled
Some checks failed
Rust / build (push) Has been cancelled
This commit is contained in:
@@ -9,6 +9,7 @@ edition = "2024"
|
||||
async-event = "0.2.1"
|
||||
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
|
||||
async-once-cell = "0.5.4"
|
||||
chrono = "0.4.44"
|
||||
derive_destructure = "1.0.0"
|
||||
dyn-clone = "1.0.20"
|
||||
futures = { version = "0.3.31", default-features = false, features = [
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use std::any::{Any, TypeId, type_name};
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::future::Future;
|
||||
@@ -19,7 +18,6 @@ use orchid_base::{
|
||||
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym,
|
||||
fmt, is, mk_errv, mk_errv_floating, take_first,
|
||||
};
|
||||
use task_local::task_local;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::gen_expr::GExpr;
|
||||
@@ -99,11 +97,11 @@ impl ForeignAtom {
|
||||
/// Call an IPC method. If the type does not support the given method type,
|
||||
/// this function returns [None]
|
||||
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> {
|
||||
let rep = (request(api::Fwd(
|
||||
self.atom.clone(),
|
||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&r.into_root()),
|
||||
)))
|
||||
let rep = (request(api::Fwd {
|
||||
target: self.atom.clone(),
|
||||
method: Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
body: enc_vec(&r.into_root()),
|
||||
}))
|
||||
.await?;
|
||||
Some(R::Response::decode_slice(&mut &rep[..]))
|
||||
}
|
||||
@@ -111,26 +109,22 @@ impl ForeignAtom {
|
||||
pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> {
|
||||
let mut data = &self.atom.data.0[..];
|
||||
let value = AtomTypeId::decode_slice(&mut data);
|
||||
if cfg!(debug_assertions) {
|
||||
let cted = dyn_cted();
|
||||
let own_inst = cted.inst();
|
||||
let owner_id = self.atom.owner;
|
||||
let typ = type_name::<A>();
|
||||
let owner = if sys_id() == owner_id {
|
||||
own_inst.card()
|
||||
} else {
|
||||
(cted.deps().find(|s| s.id() == self.atom.owner))
|
||||
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
|
||||
.get_card()
|
||||
};
|
||||
let Some(ops) = owner.ops_by_atid(value) else {
|
||||
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
|
||||
};
|
||||
if ops.tid() != TypeId::of::<A>() {
|
||||
panic!(
|
||||
"{value:?} of {owner_id:?} refers to a type other than {typ}. System version mismatch?"
|
||||
)
|
||||
}
|
||||
let cted = dyn_cted();
|
||||
let own_inst = cted.inst();
|
||||
let owner_id = self.atom.owner;
|
||||
let typ = type_name::<A>();
|
||||
let owner = if sys_id() == owner_id {
|
||||
own_inst.card()
|
||||
} else {
|
||||
(cted.deps().find(|s| s.id() == self.atom.owner))
|
||||
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
|
||||
.get_card()
|
||||
};
|
||||
let Some(ops) = owner.ops_by_atid(value) else {
|
||||
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
|
||||
};
|
||||
if ops.tid() != TypeId::of::<A>() {
|
||||
return Err(NotTypAtom { pos: self.pos.clone(), expr: self.ex(), typ });
|
||||
}
|
||||
let value = A::Data::decode_slice(&mut data);
|
||||
Ok(TAtom { value, untyped: self })
|
||||
@@ -187,10 +181,6 @@ pub trait AtomMethod: Coding + InHierarchy {
|
||||
const NAME: &str;
|
||||
}
|
||||
|
||||
task_local! {
|
||||
pub(crate) static ATOM_WITHOUT_HANDLE_FINAL_IMPL: Rc<RefCell<Option<Box<dyn Any>>>>;
|
||||
}
|
||||
|
||||
/// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be
|
||||
/// registered in [Atomic::reg_methods]
|
||||
pub trait Supports<M: AtomMethod>: Atomic {
|
||||
@@ -199,19 +189,6 @@ pub trait Supports<M: AtomMethod>: Atomic {
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: M,
|
||||
) -> impl Future<Output = io::Result<Receipt<'a>>>;
|
||||
fn handle_final<'a>(
|
||||
self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: M,
|
||||
) -> impl Future<Output = io::Result<Receipt<'a>>> {
|
||||
async move {
|
||||
let rcpt = self.handle(hand, req).await;
|
||||
let _ = ATOM_WITHOUT_HANDLE_FINAL_IMPL.try_with(|cell| cell.replace(Some(Box::new(self))));
|
||||
rcpt
|
||||
}
|
||||
}
|
||||
// TODO: default-implement the above somehow while calling OwnedAtom::free if
|
||||
// necessary
|
||||
}
|
||||
|
||||
trait HandleAtomMethod<A> {
|
||||
@@ -220,11 +197,6 @@ trait HandleAtomMethod<A> {
|
||||
atom: &'a A,
|
||||
reader: Box<dyn ReqReader<'b> + 'a>,
|
||||
) -> LocalBoxFuture<'a, ()>;
|
||||
fn handle_final<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
atom: A,
|
||||
reader: Box<dyn ReqReader<'b> + 'a>,
|
||||
) -> LocalBoxFuture<'a, ()>;
|
||||
}
|
||||
struct AtomMethodHandler<M, A>(PhantomData<M>, PhantomData<A>);
|
||||
impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M, A> {
|
||||
@@ -238,16 +210,6 @@ impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M,
|
||||
let _ = Supports::<M>::handle(atom, reader.finish().await, req).await.unwrap();
|
||||
})
|
||||
}
|
||||
fn handle_final<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
atom: A,
|
||||
mut reader: Box<dyn ReqReader<'b> + 'a>,
|
||||
) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
let req = reader.read_req::<M>().await.unwrap();
|
||||
let _ = Supports::<M>::handle_final(atom, reader.finish().await, req).await.unwrap();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of [Supports] impls for an [Atomic]. If a [Supports]
|
||||
@@ -282,20 +244,6 @@ pub(crate) struct MethodSet<A: Atomic> {
|
||||
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
|
||||
}
|
||||
impl<A: Atomic> MethodSet<A> {
|
||||
pub(crate) async fn final_dispatch<'a>(
|
||||
&self,
|
||||
atom: A,
|
||||
key: Sym,
|
||||
req: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> bool {
|
||||
match self.handlers.get(&key) {
|
||||
None => false,
|
||||
Some(handler) => {
|
||||
handler.handle_final(atom, req).await;
|
||||
true
|
||||
},
|
||||
}
|
||||
}
|
||||
pub(crate) async fn dispatch<'a>(
|
||||
&self,
|
||||
atom: &'_ A,
|
||||
@@ -353,11 +301,11 @@ impl<A: Atomic> TAtom<A> {
|
||||
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
|
||||
where A: Supports<<R as UnderRoot>::Root> {
|
||||
R::Response::decode_slice(
|
||||
&mut &(request(api::Fwd(
|
||||
self.untyped.atom.clone(),
|
||||
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
enc_vec(&req.into_root()),
|
||||
)))
|
||||
&mut &(request(api::Fwd {
|
||||
target: self.untyped.atom.clone(),
|
||||
method: Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
|
||||
body: enc_vec(&req.into_root()),
|
||||
}))
|
||||
.await
|
||||
.unwrap()[..],
|
||||
)
|
||||
@@ -389,12 +337,6 @@ pub trait AtomOps: 'static {
|
||||
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
||||
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
|
||||
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>;
|
||||
fn handle_req<'a>(
|
||||
&'a self,
|
||||
ctx: AtomCtx<'a>,
|
||||
key: Sym,
|
||||
req: Box<dyn ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool>;
|
||||
fn handle_req_ref<'a>(
|
||||
&'a self,
|
||||
ctx: AtomCtx<'a>,
|
||||
@@ -417,25 +359,25 @@ pub trait AtomOps: 'static {
|
||||
trait_set! {
|
||||
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone;
|
||||
}
|
||||
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>);
|
||||
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>, String);
|
||||
impl AtomFactory {
|
||||
pub fn new(f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
|
||||
Self(Box::new(|| f().boxed_local()))
|
||||
pub fn new(name: String, f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
|
||||
Self(Box::new(|| f().boxed_local()), name)
|
||||
}
|
||||
pub async fn build(self) -> api::LocalAtom { (self.0)().await }
|
||||
}
|
||||
impl Clone for AtomFactory {
|
||||
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
|
||||
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0), self.1.clone()) }
|
||||
}
|
||||
impl fmt::Debug for AtomFactory {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory<{}>", self.1) }
|
||||
}
|
||||
impl fmt::Display for AtomFactory {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") }
|
||||
}
|
||||
impl Format for AtomFactory {
|
||||
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
"AtomFactory".to_string().into()
|
||||
self.to_string().into()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,8 @@ use task_local::task_local;
|
||||
|
||||
use crate::gen_expr::{GExpr, bot};
|
||||
use crate::{
|
||||
ATOM_WITHOUT_HANDLE_FINAL_IMPL, AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl,
|
||||
AtomicVariant, DynSystemCardExt, Expr, MethodSet, MethodSetBuilder, ToExpr, api, dyn_cted,
|
||||
err_not_callable,
|
||||
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, DynSystemCardExt, Expr,
|
||||
MethodSet, MethodSetBuilder, ToExpr, api, dyn_cted, err_not_callable,
|
||||
};
|
||||
|
||||
/// Value of [Atomic::Variant] for a type that implements [OwnedAtom]
|
||||
@@ -32,7 +31,7 @@ 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 || {
|
||||
AtomFactory::new(type_name::<A>().to_string(), async move || {
|
||||
let obj_store = get_obj_store();
|
||||
let atom_id = {
|
||||
let mut id = obj_store.next_id.borrow_mut();
|
||||
@@ -73,7 +72,10 @@ 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 = get_obj_store().objects.write().await;
|
||||
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
|
||||
g.remove(&id).unwrap_or_else(|| {
|
||||
let name = dyn_cted().inst().card().name();
|
||||
panic!("{name} received invalid atom ID: {}", id.0)
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) struct OwnedAtomOps<T: OwnedAtom> {
|
||||
@@ -113,25 +115,6 @@ impl<A: OwnedAtom> AtomOps for OwnedAtomOps<A> {
|
||||
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
|
||||
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
|
||||
}
|
||||
fn handle_req<'a>(
|
||||
&'a self,
|
||||
AtomCtx(_, id): AtomCtx<'a>,
|
||||
key: Sym,
|
||||
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool> {
|
||||
Box::pin(async move {
|
||||
let a = take_atom(id.unwrap()).await;
|
||||
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
|
||||
let cell = Rc::new(RefCell::new(None));
|
||||
let matched = ATOM_WITHOUT_HANDLE_FINAL_IMPL
|
||||
.scope(cell.clone(), ms.final_dispatch(*a.as_any().downcast().unwrap(), key, req))
|
||||
.await;
|
||||
if let Some(val) = cell.take() {
|
||||
val.downcast::<A>().unwrap().free().await
|
||||
}
|
||||
matched
|
||||
})
|
||||
}
|
||||
fn handle_req_ref<'a>(
|
||||
&'a self,
|
||||
AtomCtx(_, id): AtomCtx<'a>,
|
||||
@@ -303,7 +286,6 @@ fn assert_serializable<T: OwnedAtom>() {
|
||||
|
||||
pub(crate) trait DynOwnedAtom: DynClone + 'static {
|
||||
fn as_any_ref(&self) -> &dyn Any;
|
||||
fn as_any(self: Box<Self>) -> Box<dyn Any>;
|
||||
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()>;
|
||||
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr>;
|
||||
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr>;
|
||||
@@ -316,7 +298,6 @@ pub(crate) trait DynOwnedAtom: DynClone + 'static {
|
||||
}
|
||||
impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
fn as_any_ref(&self) -> &dyn Any { self }
|
||||
fn as_any(self: Box<Self>) -> Box<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.unwrap() }.boxed_local()
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ 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 || {
|
||||
AtomFactory::new(type_name::<A>().to_string(), async move || {
|
||||
let (id, _) = dyn_cted().inst().card().ops::<A>();
|
||||
let mut buf = enc_vec(&id);
|
||||
self.encode_vec(&mut buf);
|
||||
@@ -49,7 +49,7 @@ impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
|
||||
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
|
||||
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
|
||||
}
|
||||
fn handle_req<'a>(
|
||||
fn handle_req_ref<'a>(
|
||||
&'a self,
|
||||
AtomCtx(buf, ..): AtomCtx<'a>,
|
||||
key: Sym,
|
||||
@@ -60,14 +60,6 @@ impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
|
||||
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await
|
||||
})
|
||||
}
|
||||
fn handle_req_ref<'a>(
|
||||
&'a self,
|
||||
ctx: AtomCtx<'a>,
|
||||
key: Sym,
|
||||
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
|
||||
) -> LocalBoxFuture<'a, bool> {
|
||||
self.handle_req(ctx, key, req)
|
||||
}
|
||||
fn serialize<'a, 'b: 'a>(
|
||||
&'a self,
|
||||
ctx: AtomCtx<'a>,
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
use std::borrow::Cow;
|
||||
use std::rc::Rc;
|
||||
|
||||
use dyn_clone::DynClone;
|
||||
use futures::future::LocalBoxFuture;
|
||||
use never::Never;
|
||||
use orchid_base::{Receipt, ReqHandle, ReqHandleExt};
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::gen_expr::{GExpr, new_atom};
|
||||
use crate::gen_expr::{GExpr, new_atom, serialize};
|
||||
use crate::std_reqs::RunCommand;
|
||||
use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr};
|
||||
|
||||
trait_set! {
|
||||
pub trait ClonableAsyncFnOnceDyn = FnOnce() -> LocalBoxFuture<'static, Option<GExpr>> + DynClone;
|
||||
pub trait AsyncFnDyn {
|
||||
fn call<'a>(&'a self) -> LocalBoxFuture<'a, Option<GExpr>>;
|
||||
}
|
||||
impl<T: AsyncFn() -> Option<GExpr>> AsyncFnDyn for T {
|
||||
fn call<'a>(&'a self) -> LocalBoxFuture<'a, Option<GExpr>> { Box::pin(async { (self)().await }) }
|
||||
}
|
||||
|
||||
pub struct CmdAtom(Box<dyn ClonableAsyncFnOnceDyn>);
|
||||
impl Clone for CmdAtom {
|
||||
fn clone(&self) -> Self { Self(dyn_clone::clone_box(&*self.0)) }
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct CmdAtom(Rc<dyn AsyncFnDyn>);
|
||||
impl Atomic for CmdAtom {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
@@ -29,17 +29,10 @@ impl Supports<RunCommand> for CmdAtom {
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: RunCommand,
|
||||
) -> std::io::Result<Receipt<'a>> {
|
||||
Self(dyn_clone::clone_box(&*self.0)).handle_final(hand, req).await
|
||||
}
|
||||
async fn handle_final<'a>(
|
||||
self,
|
||||
hand: Box<dyn ReqHandle<'a> + '_>,
|
||||
req: RunCommand,
|
||||
) -> std::io::Result<Receipt<'a>> {
|
||||
let reply = (self.0)().await;
|
||||
let reply = self.0.call().await;
|
||||
match reply {
|
||||
None => hand.reply(&req, &None).await,
|
||||
Some(next) => hand.reply(&req, &Some(next.serialize().await)).await,
|
||||
Some(next) => hand.reply(&req, &Some(serialize(next).await)).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,13 +41,9 @@ impl OwnedAtom for CmdAtom {
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
}
|
||||
|
||||
pub fn cmd<R: ToExpr>(f: impl AsyncFnOnce() -> Option<R> + Clone + 'static) -> GExpr {
|
||||
new_atom(CmdAtom(Box::new(|| {
|
||||
Box::pin(async {
|
||||
match f().await {
|
||||
None => None,
|
||||
Some(r) => Some(r.to_gen().await),
|
||||
}
|
||||
})
|
||||
pub fn cmd<R: ToExpr>(f: impl AsyncFn() -> Option<R> + Clone + 'static) -> GExpr {
|
||||
new_atom(CmdAtom(Rc::new(async move || match f().await {
|
||||
None => None,
|
||||
Some(r) => Some(r.to_gen().await),
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -71,6 +71,14 @@ pub trait ToExpr {
|
||||
where Self: Sized {
|
||||
async { self.to_gen().await.create().await }
|
||||
}
|
||||
fn boxed<'a>(self) -> Box<dyn ToExprDyn + 'a>
|
||||
where Self: Sized + 'a {
|
||||
Box::new(self)
|
||||
}
|
||||
fn clonable_boxed<'a>(self) -> Box<dyn ClonableToExprDyn + 'a>
|
||||
where Self: Clone + Sized + 'a {
|
||||
Box::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for a future that implements [ToExpr]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::any::type_name;
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
@@ -7,9 +8,9 @@ use futures::lock::Mutex;
|
||||
use futures::stream::{self, LocalBoxStream};
|
||||
use futures::{FutureExt, SinkExt, StreamExt};
|
||||
use never::Never;
|
||||
use orchid_base::OrcRes;
|
||||
use orchid_base::{FmtCtx, FmtUnit, OrcRes};
|
||||
|
||||
use crate::gen_expr::{GExpr, arg, call, lam, new_atom, seq};
|
||||
use crate::gen_expr::{GExpr, call, lam, new_atom, seq};
|
||||
use crate::{Atomic, Expr, OwnedAtom, OwnedVariant, ToExpr, TryFromExpr};
|
||||
|
||||
enum Command {
|
||||
@@ -18,6 +19,7 @@ enum Command {
|
||||
}
|
||||
|
||||
struct BuilderCoroutineData {
|
||||
name: &'static str,
|
||||
receiver: Mutex<LocalBoxStream<'static, Command>>,
|
||||
}
|
||||
|
||||
@@ -30,7 +32,7 @@ impl BuilderCoroutine {
|
||||
None => panic!("Exec handle dropped and coroutine blocked instead of returning"),
|
||||
Some(Command::Halt(expr)) => expr,
|
||||
Some(Command::Execute(expr, reply)) =>
|
||||
call(lam::<0>(seq(arg(0), call(new_atom(Replier { reply, builder: self }), arg(0)))), expr)
|
||||
call(lam(async |x| seq(x, call(new_atom(Replier { reply, builder: self }), x)).await), expr)
|
||||
.await,
|
||||
}
|
||||
}
|
||||
@@ -53,6 +55,9 @@ impl OwnedAtom for Replier {
|
||||
std::mem::drop(self.reply);
|
||||
self.builder.run().await
|
||||
}
|
||||
async fn print_atom<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
format!("Replier<{}>", self.builder.0.name).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// A long-lived async context that can yield to the executor. The expression
|
||||
@@ -62,6 +67,7 @@ pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R +
|
||||
let halt =
|
||||
async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream();
|
||||
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData {
|
||||
name: type_name::<R>(),
|
||||
receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()),
|
||||
}));
|
||||
coro.run().await
|
||||
|
||||
@@ -22,6 +22,7 @@ use orchid_base::{
|
||||
use substack::Substack;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::gen_expr::serialize;
|
||||
use crate::interner::new_interner;
|
||||
use crate::logger::LoggerImpl;
|
||||
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
|
||||
@@ -63,9 +64,11 @@ pub async fn mute_reply<F: Future>(f: F) -> F::Output { MUTE_REPLY.scope((), f).
|
||||
|
||||
/// Send a request through the global client's [ClientExt::request]
|
||||
pub async fn request<T: Request + UnderRoot<Root = api::ExtHostReq>>(t: T) -> T::Response {
|
||||
let req_str = if MUTE_REPLY.try_with(|b| *b).is_err() { format!("{t:?}") } else { String::new() };
|
||||
let response = get_client().request(t).await.unwrap();
|
||||
if MUTE_REPLY.try_with(|b| *b).is_err() {
|
||||
writeln!(log("msg"), "Got response {response:?}").await;
|
||||
let ext = dyn_cted().inst().card().name();
|
||||
writeln!(log("msg"), "{ext} {req_str} got response {response:?}").await;
|
||||
}
|
||||
response
|
||||
}
|
||||
@@ -342,7 +345,7 @@ impl ExtensionBuilder {
|
||||
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
|
||||
handle.reply(fpc, &serialize(cnst).await).await
|
||||
})
|
||||
.await,
|
||||
api::HostExtReq::AtomReq(atom_req) => {
|
||||
@@ -365,16 +368,8 @@ impl ExtensionBuilder {
|
||||
},
|
||||
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
|
||||
handle.reply(print, &nfo.print(actx).await.to_api()).await,
|
||||
api::AtomReq::FinalFwded(fwded) => {
|
||||
let api::FinalFwded(_, key, payload) = &fwded;
|
||||
let mut reply = Vec::new();
|
||||
let key = Sym::from_api(*key).await;
|
||||
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
||||
let some = nfo.handle_req(actx, key, Box::new(req)).await;
|
||||
handle.reply(fwded, &some.then_some(reply)).await
|
||||
},
|
||||
api::AtomReq::FwdedRef(fwded) => {
|
||||
let api::FinalFwded(_, key, payload) = &fwded;
|
||||
api::AtomReq::Fwded(fwded) => {
|
||||
let api::Fwded(_, key, payload) = &fwded;
|
||||
let mut reply = Vec::new();
|
||||
let key = Sym::from_api(*key).await;
|
||||
let req = TrivialReqCycle { req: payload, rep: &mut reply };
|
||||
@@ -385,7 +380,7 @@ impl ExtensionBuilder {
|
||||
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;
|
||||
let api_expr = serialize(ret).await;
|
||||
mem::drop(expr_handle);
|
||||
expr_store.dispose().await;
|
||||
handle.reply(call, &api_expr).await
|
||||
@@ -394,7 +389,7 @@ impl ExtensionBuilder {
|
||||
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;
|
||||
let api_expr = serialize(ret).await;
|
||||
mem::drop(expr_handle);
|
||||
expr_store.dispose().await;
|
||||
handle.reply(call, &api_expr).await
|
||||
|
||||
@@ -10,7 +10,7 @@ use futures::future::join_all;
|
||||
use hashbrown::HashSet;
|
||||
use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash};
|
||||
|
||||
use crate::gen_expr::{GExpr, GExprKind};
|
||||
use crate::gen_expr::{GExpr, slot};
|
||||
use crate::{ForeignAtom, api, notify, request, sys_id};
|
||||
|
||||
/// Handle for a lifetime associated with an [ExprHandle], such as a function
|
||||
@@ -158,9 +158,7 @@ impl Expr {
|
||||
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
|
||||
/// Wrap this expression in a [GExpr] synchronously as an escape hatch.
|
||||
/// Otherwise identical to this type's [crate::ToExpr] impl
|
||||
pub fn slot(&self) -> GExpr {
|
||||
GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) }
|
||||
}
|
||||
pub fn slot(&self) -> GExpr { slot(self.clone()) }
|
||||
/// Increments the refcount to ensure that the ticket remains valid even if
|
||||
/// the handle is freed. To avoid a leak, [Expr::deserialize] must eventually
|
||||
/// be called.
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::cell::RefCell;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::pin::{Pin, pin};
|
||||
use std::rc::Rc;
|
||||
@@ -6,22 +8,38 @@ use futures::{FutureExt, Stream, StreamExt, stream};
|
||||
use orchid_base::{
|
||||
FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache,
|
||||
};
|
||||
use substack::Substack;
|
||||
use task_local::task_local;
|
||||
|
||||
use crate::{AtomFactory, AtomicFeatures, Expr, ToExpr, ToExprFuture, api, request, sys_id};
|
||||
|
||||
/// Newly generated AST. Values of this type should not typically be constructed
|
||||
/// manually but through the helpers in this module
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct ExprSerializeCx<'a> {
|
||||
closures: Substack<'a, u64>,
|
||||
lambda_counter: &'a RefCell<u64>,
|
||||
}
|
||||
|
||||
/// Release notifications will not be sent for the slots. Use this with
|
||||
/// messages that imply ownership transfer
|
||||
pub async fn serialize(expr: GExpr) -> api::Expression {
|
||||
let cx = ExprSerializeCx { closures: Substack::Bottom, lambda_counter: &RefCell::new(0) };
|
||||
expr.serialize(cx).await
|
||||
}
|
||||
|
||||
/// Smart object representing AST not-yet-sent to the interpreter. This type can
|
||||
/// be cloned and persisted, and it must not have unbound arguments. The helper
|
||||
/// functions in this module let you build trees of [ToExpr] implementors which
|
||||
/// represent lambdas and their arguments separately, and then convert them into
|
||||
/// [GExpr] in one pass.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GExpr {
|
||||
/// AST node type
|
||||
pub kind: GExprKind,
|
||||
kind: GExprKind,
|
||||
/// Code location associated with the expression for debugging purposes
|
||||
pub pos: Pos,
|
||||
pos: Pos,
|
||||
}
|
||||
impl GExpr {
|
||||
/// Release notifications will not be sent for the slots. Use this with
|
||||
/// messages that imply ownership transfer
|
||||
pub async fn serialize(self) -> api::Expression {
|
||||
async fn serialize(self, cx: ExprSerializeCx<'_>) -> api::Expression {
|
||||
if let GExprKind::Slot(ex) = self.kind {
|
||||
let hand = ex.handle();
|
||||
mem::drop(ex);
|
||||
@@ -32,8 +50,8 @@ impl GExpr {
|
||||
}
|
||||
} else {
|
||||
api::Expression {
|
||||
location: api::Location::Inherit,
|
||||
kind: self.kind.serialize().boxed_local().await,
|
||||
location: self.pos.to_api(),
|
||||
kind: self.kind.serialize(cx).boxed_local().await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -42,7 +60,7 @@ impl GExpr {
|
||||
/// Send the expression to the interpreter to be compiled and to become
|
||||
/// shareable across extensions
|
||||
pub async fn create(self) -> Expr {
|
||||
Expr::deserialize(request(api::Create(sys_id(), self.serialize().await)).await).await
|
||||
Expr::deserialize(request(api::Create(sys_id(), serialize(self).await)).await).await
|
||||
}
|
||||
}
|
||||
impl Format for GExpr {
|
||||
@@ -56,8 +74,8 @@ impl Format for GExpr {
|
||||
pub enum GExprKind {
|
||||
/// Function call
|
||||
Call(Box<GExpr>, Box<GExpr>),
|
||||
/// Lambda expression. Argument numbers are matched when equal
|
||||
Lambda(u64, Box<GExpr>),
|
||||
/// Lambda expression. Argument must be the same for slot
|
||||
Lambda(Box<GExpr>),
|
||||
/// Slot for a lambda argument
|
||||
Arg(u64),
|
||||
/// The second expression is only valid after the first one had already been
|
||||
@@ -80,23 +98,40 @@ pub enum GExprKind {
|
||||
Bottom(OrcErrv),
|
||||
}
|
||||
impl GExprKind {
|
||||
async fn serialize(self) -> api::ExpressionKind {
|
||||
pub fn at(self, pos: Pos) -> GExpr { GExpr { kind: self, pos } }
|
||||
async fn serialize(self, cx: ExprSerializeCx<'_>) -> api::ExpressionKind {
|
||||
match_mapping!(self, Self => api::ExpressionKind {
|
||||
Call(
|
||||
f => Box::new(f.serialize().await),
|
||||
x => Box::new(x.serialize().await)
|
||||
f => Box::new(f.serialize(cx).await),
|
||||
x => Box::new(x.serialize(cx).await)
|
||||
),
|
||||
Seq(
|
||||
a => Box::new(a.serialize().await),
|
||||
b => Box::new(b.serialize().await)
|
||||
a => Box::new(a.serialize(cx).await),
|
||||
b => Box::new(b.serialize(cx).await)
|
||||
),
|
||||
Lambda(arg, body => Box::new(body.serialize().await)),
|
||||
Arg(arg),
|
||||
Const(name.to_api()),
|
||||
Bottom(err.to_api()),
|
||||
NewAtom(fac.clone().build().await),
|
||||
} {
|
||||
Self::Slot(_) => panic!("processed elsewhere")
|
||||
Self::Slot(_) => panic!("processed elsewhere"),
|
||||
Self::Lambda(body) => {
|
||||
let id: u64;
|
||||
{
|
||||
let mut g = cx.lambda_counter.borrow_mut();
|
||||
id = *g;
|
||||
*g += 1;
|
||||
};
|
||||
let cx = ExprSerializeCx {
|
||||
lambda_counter: cx.lambda_counter,
|
||||
closures: cx.closures.push(id)
|
||||
};
|
||||
api::ExpressionKind::Lambda(id,
|
||||
Box::new(body.serialize(cx).await)
|
||||
)
|
||||
},
|
||||
Self::Arg(arg) => {
|
||||
api::ExpressionKind::Arg(*cx.closures.iter().nth(arg as usize).expect("Unbound arg"))
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -106,9 +141,9 @@ impl Format for GExprKind {
|
||||
GExprKind::Call(f, x) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} ({1})")))
|
||||
.units([f.print(c).await, x.print(c).await]),
|
||||
GExprKind::Lambda(arg, body) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0}.{1}")))
|
||||
.units([arg.to_string().into(), body.print(c).await]),
|
||||
GExprKind::Lambda(body) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{1}")))
|
||||
.units([body.print(c).await]),
|
||||
GExprKind::Arg(arg) => arg.to_string().into(),
|
||||
GExprKind::Seq(a, b) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0}] {1}")))
|
||||
@@ -123,7 +158,11 @@ impl Format for GExprKind {
|
||||
}
|
||||
}
|
||||
|
||||
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
||||
pub fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
|
||||
|
||||
task_local! {
|
||||
pub static CLOSURE_DEPTH: u64;
|
||||
}
|
||||
|
||||
impl ToExpr for Sym {
|
||||
async fn to_expr(self) -> Expr
|
||||
@@ -135,6 +174,8 @@ impl ToExpr for Sym {
|
||||
/// Creates an expression from a new atom that we own.
|
||||
pub fn new_atom<A: AtomicFeatures>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.factory())) }
|
||||
|
||||
pub fn slot(expr: Expr) -> GExpr { GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(expr) } }
|
||||
|
||||
/// An expression which is only valid if a number of dependencies had already
|
||||
/// been normalized
|
||||
pub fn seq(
|
||||
@@ -155,17 +196,49 @@ pub fn seq(
|
||||
})
|
||||
}
|
||||
|
||||
/// Argument bound by an enclosing [lam] or [dyn_lambda]
|
||||
pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) }
|
||||
|
||||
/// A lambda expression. The difference from [dyn_lambda] is purely aesthetic
|
||||
pub fn lam<const N: u64>(b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||
dyn_lambda(N, b)
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum ArgState {
|
||||
Building,
|
||||
Serializing { depth: u64 },
|
||||
Ready,
|
||||
}
|
||||
|
||||
/// A lambda expression. The difference from [lam] is purely aesthetic
|
||||
pub fn dyn_lambda(n: u64, b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> {
|
||||
ToExprFuture(async move { inherit(GExprKind::Lambda(n, Box::new(b.to_gen().await))) })
|
||||
/// Argument bound by an enclosing [lam] or [dyn_lambda]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct GenArg<'a>(*const RefCell<ArgState>, PhantomData<&'a ()>);
|
||||
impl ToExpr for GenArg<'_> {
|
||||
async fn to_gen(self) -> GExpr {
|
||||
// SAFETY: Created from a Rc that lives as long as the lifetime arg, see [lam]
|
||||
let state = unsafe { self.0.as_ref().unwrap() };
|
||||
match (*state.borrow(), CLOSURE_DEPTH.try_with(|r| *r)) {
|
||||
(ArgState::Serializing { .. }, Err(_)) =>
|
||||
panic!("Lambda should have cleared up argstate alongside CLOSURE_DEPTH"),
|
||||
(ArgState::Serializing { depth }, Ok(total)) => inherit(GExprKind::Arg(total - depth)),
|
||||
(ArgState::Building, _) =>
|
||||
panic!("Argument serialized before lambda. Likely an over-eager ToExpr impl"),
|
||||
(ArgState::Ready, _) =>
|
||||
unreachable!("The arg should never be available this long, the GenArg is a convenience"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A lambda expression.
|
||||
pub fn lam<'a>(
|
||||
cb: impl for<'b> AsyncFnOnce(GenArg<'b>) -> GExpr + 'a,
|
||||
) -> ToExprFuture<impl Future<Output = GExpr> + 'a> {
|
||||
let state = Rc::new(RefCell::new(ArgState::Building));
|
||||
ToExprFuture(async move {
|
||||
let rank = CLOSURE_DEPTH.try_with(|r| *r + 1).unwrap_or(0);
|
||||
match *state.borrow_mut() {
|
||||
ref mut state @ ArgState::Building => *state = ArgState::Serializing { depth: rank },
|
||||
ArgState::Serializing { .. } => panic!("Lambda serialized twice, found interrupted"),
|
||||
ArgState::Ready => panic!("Lambda serialized twice"),
|
||||
}
|
||||
let gen_arg = GenArg(Rc::as_ptr(&state), PhantomData);
|
||||
let ret = CLOSURE_DEPTH.scope(rank, async { cb(gen_arg).await.to_gen().await }).await;
|
||||
mem::drop(state);
|
||||
inherit(GExprKind::Lambda(Box::new(ret)))
|
||||
})
|
||||
}
|
||||
|
||||
/// one or more items that are convertible to expressions. In practice, a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::num::NonZero;
|
||||
use std::time::Duration;
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use orchid_api_derive::{Coding, Hierarchy};
|
||||
use orchid_api_traits::Request;
|
||||
|
||||
@@ -24,11 +24,11 @@ impl AtomMethod for RunCommand {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
|
||||
pub struct AsDuration;
|
||||
impl Request for AsDuration {
|
||||
type Response = Duration;
|
||||
pub struct AsInstant;
|
||||
impl Request for AsInstant {
|
||||
type Response = DateTime<Utc>;
|
||||
}
|
||||
impl AtomMethod for AsDuration {
|
||||
impl AtomMethod for AsInstant {
|
||||
const NAME: &str = "orchid::time::as_duration";
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ use substack::Substack;
|
||||
use task_local::task_local;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::gen_expr::{GExpr, new_atom};
|
||||
use crate::gen_expr::{GExpr, new_atom, serialize};
|
||||
use crate::{BorrowedExprStore, Expr, ExprFunc, ExprHandle, Fun, ToExpr, api};
|
||||
|
||||
/// Tokens generated by lexers and parsers
|
||||
@@ -31,7 +31,7 @@ impl TokenVariant<api::Expression> for GExpr {
|
||||
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 }
|
||||
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { serialize(self).await }
|
||||
}
|
||||
|
||||
impl TokenVariant<api::ExprTicket> for Expr {
|
||||
@@ -193,7 +193,7 @@ impl LazyMemKind {
|
||||
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
|
||||
match self {
|
||||
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
|
||||
Self::Const(c) => api::MemberKind::Const(c.serialize().await),
|
||||
Self::Const(c) => api::MemberKind::Const(serialize(c).await),
|
||||
Self::Mod(members) => api::MemberKind::Module(api::Module {
|
||||
members: stream(async |mut cx| {
|
||||
for m in members {
|
||||
|
||||
Reference in New Issue
Block a user