Initial extension asynchronization efforts.

This commit is contained in:
2025-01-22 13:02:13 +01:00
parent 1974c69019
commit 7be8716b19
29 changed files with 1077 additions and 882 deletions

View File

@@ -4,14 +4,17 @@ use std::future::Future;
use std::io::{Read, Write};
use std::marker::PhantomData;
use std::ops::Deref;
use std::sync::Arc;
use std::rc::Rc;
use ahash::HashMap;
use async_std::stream;
use dyn_clone::{DynClone, clone_box};
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use futures::{FutureExt, StreamExt};
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
use orchid_base::clone;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::intern;
use orchid_base::interner::Interner;
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::reqnot::Requester;
@@ -21,6 +24,7 @@ use trait_set::trait_set;
use crate::api;
// use crate::error::{ProjectError, ProjectResult};
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
use crate::gen_expr::GExpr;
use crate::system::{DynSystemCard, SysCtx, atom_info_for, downcast_atom};
pub trait AtomCard: 'static + Sized {
@@ -31,7 +35,7 @@ pub trait AtomicVariant {}
pub trait Atomic: 'static + Sized {
type Variant: AtomicVariant;
type Data: Clone + Coding + Sized;
fn reg_reqs() -> MethodSet<Self>;
fn reg_reqs() -> MethodSetBuilder<Self>;
}
impl<A: Atomic> AtomCard for A {
type Data = <Self as Atomic>::Data;
@@ -74,11 +78,11 @@ pub fn get_info<A: AtomCard>(
#[derive(Clone)]
pub struct ForeignAtom<'a> {
pub expr: Option<Arc<ExprHandle>>,
pub _life: PhantomData<&'a ()>,
pub ctx: SysCtx,
pub atom: api::Atom,
pub pos: Pos,
pub(crate) expr: Option<Rc<ExprHandle>>,
pub(crate) _life: PhantomData<&'a ()>,
pub(crate) ctx: SysCtx,
pub(crate) atom: api::Atom,
pub(crate) pos: Pos,
}
impl ForeignAtom<'_> {
pub fn ex_opt(self) -> Option<Expr> {
@@ -86,16 +90,18 @@ impl ForeignAtom<'_> {
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { _life: PhantomData, ..self }) };
Some(Expr::new(handle, data))
}
pub fn pos(&self) -> Pos { self.pos.clone() }
pub fn ctx(&self) -> SysCtx { self.ctx.clone() }
}
impl ForeignAtom<'static> {
pub fn ex(self) -> Expr { self.ex_opt().unwrap() }
pub(crate) fn new(handle: Arc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
pub(crate) fn new(handle: Rc<ExprHandle>, atom: api::Atom, pos: Pos) -> Self {
ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos }
}
pub async fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
let rep = (self.ctx.reqnot.request(api::Fwd(
self.atom.clone(),
Sym::parse(M::NAME).await.unwrap().tok().to_api(),
Sym::parse(M::NAME, &self.ctx.i).await.unwrap().tok().to_api(),
enc_vec(&m),
)))
.await?;
@@ -118,13 +124,18 @@ impl AtomRepr for ForeignAtom<'_> {
fn to_api(&self) -> orchid_api::Atom { self.atom.clone() }
}
pub struct NotTypAtom(pub Pos, pub Expr, pub Box<dyn AtomDynfo>);
pub struct NotTypAtom {
pub pos: Pos,
pub expr: Expr,
pub typ: Box<dyn AtomDynfo>,
pub ctx: SysCtx,
}
impl NotTypAtom {
pub async fn mk_err(&self) -> OrcErr {
mk_err(
intern!(str: "Not the expected type").await,
format!("This expression is not a {}", self.2.name()),
[self.0.clone().into()],
self.ctx.i.i("Not the expected type").await,
format!("This expression is not a {}", self.typ.name()),
[self.pos.clone().into()],
)
}
}
@@ -142,52 +153,64 @@ trait_set! {
SysCtx,
&'a mut dyn Read,
&'a mut dyn Write
) -> LocalBoxFuture<'a, ()> + Send + Sync
) -> LocalBoxFuture<'a, ()>
}
pub struct AtomReqHandler<A: AtomCard> {
key: Sym,
cb: Box<dyn AtomReqCb<A>>,
pub struct MethodSetBuilder<A: AtomCard> {
handlers: Vec<(&'static str, Rc<dyn AtomReqCb<A>>)>,
}
pub struct MethodSet<A: AtomCard> {
handlers: Vec<AtomReqHandler<A>>,
}
impl<A: AtomCard> MethodSet<A> {
impl<A: AtomCard> MethodSetBuilder<A> {
pub fn new() -> Self { Self { handlers: vec![] } }
pub async fn handle<M: AtomMethod>(mut self) -> Self
pub fn handle<M: AtomMethod>(mut self) -> Self
where A: Supports<M> {
self.handlers.push(AtomReqHandler {
key: Sym::parse(M::NAME).await.expect("AtomMethod::NAME cannoot be empty"),
cb: Box::new(move |a: &A, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write| {
assert!(!M::NAME.is_empty(), "AtomMethod::NAME cannoot be empty");
self.handlers.push((
M::NAME,
Rc::new(move |a: &A, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write| {
async { Supports::<M>::handle(a, ctx, M::decode(req)).await.encode(rep) }.boxed_local()
}),
});
));
self
}
pub(crate) fn dispatch<'a>(
pub async fn pack(&self, ctx: SysCtx) -> MethodSet<A> {
MethodSet {
handlers: stream::from_iter(self.handlers.iter())
.then(|(k, v)| {
clone!(ctx; async move {
(Sym::parse(k, &ctx.i).await.unwrap(), v.clone())
})
})
.collect()
.await,
}
}
}
pub struct MethodSet<A: AtomCard> {
handlers: HashMap<Sym, Rc<dyn AtomReqCb<A>>>,
}
impl<A: AtomCard> MethodSet<A> {
pub(crate) async fn dispatch<'a>(
&'a self,
atom: &'a A,
ctx: SysCtx,
key: Sym,
req: &'a mut dyn Read,
rep: &'a mut dyn Write,
) -> impl Future<Output = bool> + 'a {
async move {
match self.handlers.iter().find(|h| h.key == key) {
None => false,
Some(handler) => {
(handler.cb)(atom, ctx, req, rep).await;
true
},
}
) -> bool {
match self.handlers.get(&key) {
None => false,
Some(handler) => {
handler(atom, ctx, req, rep).await;
true
},
}
}
}
impl<A: AtomCard> Default for MethodSet<A> {
impl<A: AtomCard> Default for MethodSetBuilder<A> {
fn default() -> Self { Self::new() }
}
@@ -197,11 +220,21 @@ pub struct TypAtom<'a, A: AtomicFeatures> {
pub value: A::Data,
}
impl<A: AtomicFeatures> TypAtom<'static, A> {
pub async fn downcast(expr: Arc<ExprHandle>) -> Result<Self, NotTypAtom> {
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
match Expr::from_handle(expr).atom().await {
Err(oe) => Err(NotTypAtom(oe.data().await.pos.clone(), oe, Box::new(A::info()))),
Err(expr) => Err(NotTypAtom {
ctx: expr.handle().get_ctx(),
pos: expr.data().await.pos.clone(),
expr,
typ: Box::new(A::info()),
}),
Ok(atm) => match downcast_atom::<A>(atm) {
Err(fa) => Err(NotTypAtom(fa.pos.clone(), fa.ex(), Box::new(A::info()))),
Err(fa) => Err(NotTypAtom {
pos: fa.pos.clone(),
ctx: fa.ctx.clone(),
expr: fa.ex(),
typ: Box::new(A::info()),
}),
Ok(tatom) => Ok(tatom),
},
}
@@ -213,7 +246,7 @@ impl<A: AtomicFeatures> TypAtom<'_, A> {
M::Response::decode(
&mut &(self.data.ctx.reqnot.request(api::Fwd(
self.data.atom.clone(),
Sym::parse(M::NAME).await.unwrap().tok().to_api(),
Sym::parse(M::NAME, &self.data.ctx.i).await.unwrap().tok().to_api(),
enc_vec(&req),
)))
.await
@@ -228,12 +261,12 @@ impl<A: AtomicFeatures> Deref for TypAtom<'_, A> {
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx);
pub trait AtomDynfo: Send + Sync + 'static {
pub trait AtomDynfo: 'static {
fn tid(&self) -> TypeId;
fn name(&self) -> &'static str;
fn decode(&self, ctx: AtomCtx<'_>) -> Box<dyn Any>;
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, Expr>;
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, Expr>;
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: api::ExprTicket) -> LocalBoxFuture<'a, GExpr>;
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, String>;
fn handle_req<'a, 'b: 'a, 'c: 'a>(
&'a self,
@@ -242,7 +275,7 @@ pub trait AtomDynfo: Send + Sync + 'static {
req: &'b mut dyn Read,
rep: &'c mut dyn Write,
) -> LocalBoxFuture<'a, bool>;
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Option<Expr>>>;
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>>;
fn serialize<'a, 'b: 'a>(
&'a self,
ctx: AtomCtx<'a>,
@@ -258,12 +291,16 @@ pub trait AtomDynfo: Send + Sync + 'static {
}
trait_set! {
pub trait AtomFactoryFn = FnOnce(SysCtx) -> api::Atom + DynClone;
pub trait AtomFactoryFn = FnOnce(SysCtx) -> LocalBoxFuture<'static, api::Atom> + DynClone;
}
pub struct AtomFactory(Box<dyn AtomFactoryFn>);
impl AtomFactory {
pub fn new(f: impl FnOnce(SysCtx) -> api::Atom + Clone + 'static) -> Self { Self(Box::new(f)) }
pub fn build(self, ctx: SysCtx) -> api::Atom { (self.0)(ctx) }
pub fn new<F: Future<Output = api::Atom> + 'static>(
f: impl FnOnce(SysCtx) -> F + Clone + 'static,
) -> Self {
Self(Box::new(|ctx| f(ctx).boxed_local()))
}
pub async fn build(self, ctx: SysCtx) -> api::Atom { (self.0)(ctx).await }
}
impl Clone for AtomFactory {
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
@@ -275,11 +312,10 @@ impl fmt::Display for AtomFactory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") }
}
pub async fn err_not_callable() -> OrcErr {
mk_err(intern!(str: "This atom is not callable").await, "Attempted to apply value as function", [
])
pub async fn err_not_callable(i: &Interner) -> OrcErr {
mk_err(i.i("This atom is not callable").await, "Attempted to apply value as function", [])
}
pub async fn err_not_command() -> OrcErr {
mk_err(intern!(str: "This atom is not a command").await, "Settled on an inactionable value", [])
pub async fn err_not_command(i: &Interner) -> OrcErr {
mk_err(i.i("This atom is not a command").await, "Settled on an inactionable value", [])
}

View File

@@ -2,8 +2,9 @@ use std::any::{Any, TypeId, type_name};
use std::borrow::Cow;
use std::future::Future;
use std::io::{Read, Write};
use std::sync::Arc;
use std::rc::Rc;
use async_once_cell::OnceCell;
use futures::FutureExt;
use futures::future::{LocalBoxFuture, ready};
use itertools::Itertools;
@@ -17,24 +18,25 @@ use orchid_base::name::Sym;
use crate::api;
use crate::atom::{
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
err_not_callable, err_not_command, get_info,
MethodSetBuilder, err_not_callable, err_not_command, get_info,
};
use crate::expr::{Expr, ExprHandle, bot};
use crate::expr::{Expr, ExprHandle};
use crate::gen_expr::{GExpr, bot};
use crate::system::SysCtx;
pub struct OwnedVariant;
impl AtomicVariant for OwnedVariant {}
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
fn _factory(self) -> AtomFactory {
AtomFactory::new(move |ctx| {
AtomFactory::new(move |ctx| async move {
let rec = ctx.obj_store.add(Box::new(self));
let (id, _) = get_info::<A>(ctx.cted.inst().card());
let mut data = enc_vec(&id);
rec.encode(&mut data);
rec.encode(&mut data).await;
api::Atom { drop: Some(api::AtomId(rec.id())), data, owner: ctx.id }
})
}
fn _info() -> Self::_Info { OwnedAtomDynfo(A::reg_reqs()) }
fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } }
type _Info = OwnedAtomDynfo<A>;
}
@@ -46,21 +48,24 @@ fn with_atom<'a, U>(
f(ctx.obj_store.get(id.0).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0)))
}
pub struct OwnedAtomDynfo<T: OwnedAtom>(MethodSet<T>);
pub struct OwnedAtomDynfo<T: OwnedAtom> {
msbuild: MethodSetBuilder<T>,
ms: OnceCell<MethodSet<T>>,
}
impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
fn tid(&self) -> TypeId { TypeId::of::<T>() }
fn name(&self) -> &'static str { type_name::<T>() }
fn decode(&self, AtomCtx(data, ..): AtomCtx) -> Box<dyn Any> {
Box::new(<T as AtomCard>::Data::decode(&mut &data[..]))
}
fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, Expr> {
fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> {
with_atom(id.unwrap(), &ctx, |a| a.remove()).dyn_call(ctx.clone(), arg)
}
fn call_ref<'a>(
&'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>,
arg: api::ExprTicket,
) -> LocalBoxFuture<'a, Expr> {
) -> LocalBoxFuture<'a, GExpr> {
async move {
with_atom(id.unwrap(), &ctx, |a| clone!(ctx; async move { a.dyn_call_ref(ctx, arg).await }))
.await
@@ -83,7 +88,8 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
async move {
with_atom(id.unwrap(), &ctx, |a| {
clone!(ctx; async move {
self.0.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx, key, req, rep).await
let ms = self.ms.get_or_init(self.msbuild.pack(ctx.clone())).await;
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx, key, req, rep).await
})
})
.await
@@ -93,7 +99,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
fn command<'a>(
&'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>,
) -> LocalBoxFuture<'a, OrcRes<Option<Expr>>> {
) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> {
async move { with_atom(id.unwrap(), &ctx, |a| a.remove().dyn_command(ctx.clone())).await }
.boxed_local()
}
@@ -111,7 +117,7 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
id.encode(write);
with_atom(id, &ctx, |a| clone!(ctx; async move { a.dyn_serialize(ctx, write).await }))
.await
.map(|v| v.into_iter().map(|t| t.handle().unwrap().tk).collect_vec())
.map(|v| v.into_iter().map(|t| t.handle().tk).collect_vec())
}
.boxed_local()
}
@@ -123,9 +129,9 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
) -> LocalBoxFuture<'a, api::Atom> {
async move {
let refs =
refs.iter().map(|tk| Expr::from_handle(Arc::new(ExprHandle::from_args(ctx.clone(), *tk))));
refs.iter().map(|tk| Expr::from_handle(Rc::new(ExprHandle::from_args(ctx.clone(), *tk))));
let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs)).await;
obj._factory().build(ctx)
obj._factory().build(ctx).await
}
.boxed_local()
}
@@ -197,20 +203,20 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
type Refs: RefSet;
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
#[allow(unused_variables)]
fn call_ref(&self, arg: ExprHandle) -> impl Future<Output = Expr> {
async { bot([err_not_callable().await]) }
fn call_ref(&self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(&arg.ctx.i).await]) }
}
fn call(self, arg: ExprHandle) -> impl Future<Output = Expr> {
fn call(self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async {
let ctx = arg.get_ctx();
let gcl = self.call_ref(arg).await;
self.free(ctx);
self.free(ctx).await;
gcl
}
}
#[allow(unused_variables)]
fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<Expr>>> {
async { Err(err_not_command().await.into()) }
fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
async move { Err(err_not_command(&ctx.i).await.into()) }
}
#[allow(unused_variables)]
fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} }
@@ -245,9 +251,10 @@ pub trait DynOwnedAtom: 'static {
fn atom_tid(&self) -> TypeId;
fn as_any_ref(&self) -> &dyn Any;
fn encode<'a>(&'a self, buffer: &'a mut dyn Write) -> LocalBoxFuture<'a, ()>;
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, Expr>;
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'static, Expr>;
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<Expr>>>;
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr>;
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket)
-> LocalBoxFuture<'static, GExpr>;
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>>;
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()>;
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, String>;
fn dyn_serialize<'a>(
@@ -262,13 +269,17 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
fn encode<'a>(&'a self, buffer: &'a mut dyn Write) -> LocalBoxFuture<'a, ()> {
async { self.val().await.as_ref().encode(buffer) }.boxed_local()
}
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, Expr> {
fn dyn_call_ref(&self, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> {
self.call_ref(ExprHandle::from_args(ctx, arg)).boxed_local()
}
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: api::ExprTicket) -> LocalBoxFuture<'static, Expr> {
fn dyn_call(
self: Box<Self>,
ctx: SysCtx,
arg: api::ExprTicket,
) -> LocalBoxFuture<'static, GExpr> {
self.call(ExprHandle::from_args(ctx, arg)).boxed_local()
}
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<Expr>>> {
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, OrcRes<Option<GExpr>>> {
self.command(ctx).boxed_local()
}
fn dyn_free(self: Box<Self>, ctx: SysCtx) -> LocalBoxFuture<'static, ()> {
@@ -287,4 +298,4 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
}
}
pub type ObjStore = Arc<IdStore<Box<dyn DynOwnedAtom>>>;
pub type ObjStore = Rc<IdStore<Box<dyn DynOwnedAtom>>>;

View File

@@ -2,6 +2,7 @@ use std::any::{Any, TypeId, type_name};
use std::future::Future;
use std::io::Write;
use async_once_cell::OnceCell;
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use orchid_api_traits::{Coding, enc_vec};
@@ -11,27 +12,31 @@ use orchid_base::name::Sym;
use crate::api;
use crate::atom::{
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
err_not_callable, err_not_command, get_info,
MethodSetBuilder, err_not_callable, err_not_command, get_info,
};
use crate::expr::{Expr, ExprHandle, bot};
use crate::expr::ExprHandle;
use crate::gen_expr::{GExpr, bot};
use crate::system::SysCtx;
pub struct ThinVariant;
impl AtomicVariant for ThinVariant {}
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
fn _factory(self) -> AtomFactory {
AtomFactory::new(move |ctx| {
AtomFactory::new(move |ctx| async move {
let (id, _) = get_info::<A>(ctx.cted.inst().card());
let mut buf = enc_vec(&id);
self.encode(&mut buf);
api::Atom { drop: None, data: buf, owner: ctx.id }
})
}
fn _info() -> Self::_Info { ThinAtomDynfo(Self::reg_reqs()) }
fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } }
type _Info = ThinAtomDynfo<Self>;
}
pub struct ThinAtomDynfo<T: ThinAtom>(MethodSet<T>);
pub struct ThinAtomDynfo<T: ThinAtom> {
msbuild: MethodSetBuilder<T>,
ms: OnceCell<MethodSet<T>>,
}
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn print<'a>(&self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, String> {
async move { T::decode(&mut &buf[..]).print(ctx).await }.boxed_local()
@@ -43,7 +48,7 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
&'a self,
AtomCtx(buf, _, ctx): AtomCtx<'a>,
arg: api::ExprTicket,
) -> LocalBoxFuture<'a, Expr> {
) -> LocalBoxFuture<'a, GExpr> {
async move { T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg)).await }
.boxed_local()
}
@@ -51,7 +56,7 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
&'a self,
AtomCtx(buf, _, ctx): AtomCtx<'a>,
arg: api::ExprTicket,
) -> LocalBoxFuture<'a, Expr> {
) -> LocalBoxFuture<'a, GExpr> {
async move { T::decode(&mut &buf[..]).call(ExprHandle::from_args(ctx, arg)).await }
.boxed_local()
}
@@ -62,13 +67,16 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
req: &'m1 mut dyn std::io::Read,
rep: &'m2 mut dyn Write,
) -> LocalBoxFuture<'a, bool> {
async move { self.0.dispatch(&T::decode(&mut &buf[..]), sys, key, req, rep).await }
.boxed_local()
async move {
let ms = self.ms.get_or_init(self.msbuild.pack(sys.clone())).await;
ms.dispatch(&T::decode(&mut &buf[..]), sys, key, req, rep).await
}
.boxed_local()
}
fn command<'a>(
&'a self,
AtomCtx(buf, _, ctx): AtomCtx<'a>,
) -> LocalBoxFuture<'a, OrcRes<Option<Expr>>> {
) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> {
async move { T::decode(&mut &buf[..]).command(ctx).await }.boxed_local()
}
fn serialize<'a, 'b: 'a>(
@@ -86,7 +94,7 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
refs: &'a [api::ExprTicket],
) -> LocalBoxFuture<'a, api::Atom> {
assert!(refs.is_empty(), "Refs found when deserializing thin atom");
async { T::decode(&mut &data[..])._factory().build(ctx) }.boxed_local()
async { T::decode(&mut &data[..])._factory().build(ctx).await }.boxed_local()
}
fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> {
async move {
@@ -101,12 +109,12 @@ pub trait ThinAtom:
AtomCard<Data = Self> + Atomic<Variant = ThinVariant> + Coding + Send + Sync + 'static
{
#[allow(unused_variables)]
fn call(&self, arg: ExprHandle) -> impl Future<Output = Expr> {
async { bot([err_not_callable().await]) }
fn call(&self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(&arg.ctx.i).await]) }
}
#[allow(unused_variables)]
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<Expr>>> {
async { Err(err_not_command().await.into()) }
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
async move { Err(err_not_command(&ctx.i).await.into()) }
}
#[allow(unused_variables)]
fn print(&self, ctx: SysCtx) -> impl Future<Output = String> {

View File

@@ -1,11 +1,12 @@
use std::future::Future;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::intern;
use orchid_base::interner::Interner;
use orchid_base::location::Pos;
use crate::atom::{AtomicFeatures, ToAtom, TypAtom};
use crate::expr::{Expr, atom, bot};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, atom, bot};
use crate::system::downcast_atom;
pub trait TryFromExpr: Sized {
@@ -22,38 +23,39 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
}
}
async fn err_not_atom(pos: Pos) -> OrcErr {
mk_err(intern!(str: "Expected an atom").await, "This expression is not an atom", [pos.into()])
async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErr {
mk_err(i.i("Expected an atom").await, "This expression is not an atom", [pos.into()])
}
async fn err_type(pos: Pos) -> OrcErr {
mk_err(intern!(str: "Type error").await, "The atom is a different type than expected", [
pos.into()
])
async fn err_type(pos: Pos, i: &Interner) -> OrcErr {
mk_err(i.i("Type error").await, "The atom is a different type than expected", [pos.into()])
}
impl<A: AtomicFeatures> TryFromExpr for TypAtom<'_, A> {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match expr.atom().await {
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone()).await.into()),
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), &ex.ctx().i).await.into()),
Ok(f) => match downcast_atom(f) {
Ok(a) => Ok(a),
Err(f) => Err(err_type(f.pos).await.into()),
Err(f) => Err(err_type(f.pos(), &f.ctx().i).await.into()),
},
}
}
}
pub trait ToExpr {
fn to_expr(self) -> Expr;
fn to_expr(self) -> GExpr;
}
impl ToExpr for GExpr {
fn to_expr(self) -> GExpr { self }
}
impl ToExpr for Expr {
fn to_expr(self) -> Expr { self }
fn to_expr(self) -> GExpr { self.gen() }
}
impl<T: ToExpr> ToExpr for OrcRes<T> {
fn to_expr(self) -> Expr {
fn to_expr(self) -> GExpr {
match self {
Err(e) => bot(e),
Ok(t) => t.to_expr(),
@@ -62,5 +64,5 @@ impl<T: ToExpr> ToExpr for OrcRes<T> {
}
impl<A: ToAtom> ToExpr for A {
fn to_expr(self) -> Expr { atom(self) }
fn to_expr(self) -> GExpr { atom(self) }
}

View File

@@ -1,35 +1,41 @@
use std::cell::RefCell;
use std::future::Future;
use std::io::Write;
use std::mem;
use std::num::NonZero;
use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::{mem, process, thread};
use async_std::channel::{Receiver, Sender};
use async_std::stream;
use async_std::sync::Mutex;
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use futures::future::{LocalBoxFuture, join_all};
use futures::task::LocalSpawn;
use futures::{FutureExt, StreamExt};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::ApplyMacro;
use orchid_api_traits::{Decode, Encode, enc_vec};
use orchid_base::builtin::ExtPort;
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
use orchid_base::clone;
use orchid_base::interner::{Tok, init_replica, sweep_replica};
use orchid_base::interner::{Interner, Tok};
use orchid_base::logging::Logger;
use orchid_base::macros::{mtreev_from_api, mtreev_to_api};
use orchid_base::name::{PathSlice, Sym};
use orchid_base::parse::{Comment, Snippet};
use orchid_base::reqnot::{Receipt, ReqHandlish, ReqNot, RequestHandle, Requester};
use orchid_base::reqnot::{ReqNot, RequestHandle, Requester};
use orchid_base::tree::{ttv_from_api, ttv_to_api};
use substack::Substack;
use trait_set::trait_set;
use crate::api;
use crate::atom::{AtomCtx, AtomDynfo};
use crate::atom_owned::ObjStore;
use crate::fs::VirtFS;
use crate::lexer::{LexContext, err_cascade, err_not_applicable};
use crate::macros::{RuleCtx, apply_rule};
use crate::macros::{Rule, RuleCtx};
use crate::msg::{recv_parent_msg, send_parent_msg};
use crate::system::{SysCtx, atom_by_idx};
use crate::system_ctor::{CtedObj, DynSystemCtor};
@@ -59,16 +65,26 @@ pub struct SystemRecord {
vfses: HashMap<api::VfsId, &'static dyn VirtFS>,
declfs: api::EagerVfs,
lazy_members: HashMap<api::TreeId, MemberRecord>,
rules: HashMap<api::MacroId, Rc<Rule>>,
}
pub async fn with_atom_record<T>(
get_sys_ctx: &impl Fn(api::SysId, ReqNot<api::ExtMsgSet>) -> SysCtx,
trait_set! {
pub trait WARCallback<'a, T> = FnOnce(
Box<dyn AtomDynfo>,
SysCtx,
api::AtomId,
&'a [u8]
) -> LocalBoxFuture<'a, T>
}
pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>(
get_sys_ctx: &impl Fn(api::SysId, ReqNot<api::ExtMsgSet>) -> F,
reqnot: ReqNot<api::ExtMsgSet>,
atom: &api::Atom,
cb: impl for<'c> FnOnce(Box<dyn AtomDynfo>, SysCtx, api::AtomId, &'c [u8]) -> LocalBoxFuture<'c, T>,
atom: &'a api::Atom,
cb: impl WARCallback<'a, T>,
) -> T {
let mut data = &atom.data[..];
let ctx = get_sys_ctx(atom.owner, reqnot);
let ctx = get_sys_ctx(atom.owner, reqnot).await;
let inst = ctx.cted.inst();
let id = api::AtomId::decode(&mut data);
let atom_record = atom_by_idx(inst.card(), id).expect("Atom ID reserved");
@@ -106,239 +122,301 @@ impl ExtPort for ExtensionOwner {
}
}
async fn extension_main_logic(data: ExtensionData, spawner: Arc<dyn LocalSpawn>) {
async fn extension_main_logic(data: ExtensionData, spawner: Rc<dyn LocalSpawn>) {
let api::HostHeader { log_strategy } = api::HostHeader::decode(&mut std::io::stdin().lock());
let mut buf = Vec::new();
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 = Arc::new(Mutex::new(HashMap::<api::SysId, SystemRecord>::new()));
let systems = Rc::new(Mutex::new(HashMap::<api::SysId, SystemRecord>::new()));
api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() }.encode(&mut buf);
std::io::stdout().write_all(&buf).unwrap();
std::io::stdout().flush().unwrap();
let exiting = Arc::new(AtomicBool::new(false));
let logger = Arc::new(Logger::new(log_strategy));
let interner_cell = Rc::new(RefCell::new(None::<Rc<Interner>>));
let interner_weak = Rc::downgrade(&interner_cell);
let obj_store = ObjStore::default();
let mk_ctx = clone!(
logger, systems;
move |id: api::SysId, reqnot: ReqNot<api::ExtMsgSet>| async {
let cted = systems.lock().await[&id].cted.clone();
SysCtx { id, cted, logger, reqnot, spawner, obj_store }
}.boxed_local()
);
logger, systems, spawner, obj_store, interner_weak;
move |id: api::SysId, reqnot: ReqNot<api::ExtMsgSet>| {
clone!(logger, systems, spawner, obj_store, interner_weak; async move {
let cted = systems.lock().await[&id].cted.clone();
let interner_cell = (interner_weak.upgrade())
.expect("mk_ctx called after Interner rc dropped");
let i = (interner_cell.borrow().clone())
.expect("mk_ctx called before interner initialized");
SysCtx { id, cted, logger, reqnot, spawner, obj_store, i: i.clone() }
}.boxed_local())
});
let rn = ReqNot::<api::ExtMsgSet>::new(
clone!(logger; move |a, _| async {
clone!(logger; move |a, _| clone!(logger; async move {
logger.log_buf("Upsending", a);
send_parent_msg(a).await.unwrap()
}.boxed_local()),
clone!(systems, exiting, mk_ctx; move |n, reqnot| async {
match n {
api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) =>
mem::drop(systems.lock().await.remove(&sys_id)),
api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) =>
obj_store.get(atom.0).unwrap().remove().dyn_free(mk_ctx(sys_id, reqnot).await).await,
}
}.boxed_local()),
}.boxed_local())),
clone!(systems, exiting, mk_ctx, obj_store; move |n, reqnot| {
clone!(systems, exiting, mk_ctx, obj_store; async move {
match n {
api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) =>
mem::drop(systems.lock().await.remove(&sys_id)),
api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) =>
obj_store.get(atom.0).unwrap().remove().dyn_free(mk_ctx(sys_id, reqnot).await).await,
}
}.boxed_local())
}),
{
let systems = systems.clone();
let logger = logger.clone();
(move |hand, req| {
async {
let receipt: Receipt = match req {
clone!(systems, logger, mk_ctx, interner_weak, obj_store, spawner, decls);
move |hand, req| {
clone!(systems, logger, mk_ctx, interner_weak, obj_store, spawner, decls);
async move {
let interner_cell = interner_weak.upgrade().expect("Interner dropped before request");
let i = interner_cell.borrow().clone().expect("Request arrived before interner set");
match req {
api::HostExtReq::Ping(ping @ api::Ping) => hand.handle(&ping, &()).await,
_ => panic!(),
// api::HostExtReq::Sweep(sweep@api::Sweep) => hand.handle(&sweep,
// &sweep_replica()).await,
// api::HostExtReq::SysReq(api::SysReq::NewSystem(new_sys)) => {
// let i = decls.iter().enumerate().find(|(_,s)|s.id==new_sys.system).unwrap().0;
// let cted = data.systems[i].new_system(&new_sys);
// let mut vfses = HashMap::new();
// let lex_filter = cted.inst().dyn_lexers().iter().fold(api::CharFilter(vec![]),
// |cf,lx|{ let lxcf = mk_char_filter(lx.char_filter().iter().cloned());
// char_filter_union(&cf, &lxcf)
// });
// let mut lazy_mems = HashMap::new();
// let ctx = SysCtx {
// cted:cted.clone(),id:new_sys.id,logger:logger.clone(),reqnot:hand.reqnot()
// };
// let mut tia_ctx = TIACtxImpl {
// lazy: &mut lazy_mems,sys:ctx.clone(),basepath: &[],path:Substack::Bottom,
// };
// let const_root =
// (cted.inst().dyn_env().into_iter()).map(|(k,v)|(k.to_api(),v.into_api(&mut
// tia_ctx))).collect(); systems.lock().unwrap().insert(new_sys.id,
// SystemRecord { declfs:cted.inst().dyn_vfs().to_api_rec(&mut
// vfses),vfses,cted,lazy_members:lazy_mems });
// hand.handle(&new_sys, &api::SystemInst {
// lex_filter,const_root,line_types:vec![]
// }).await
// }
// api::HostExtReq::GetMember(get_tree@api::GetMember(sys_id,tree_id)) => {
// let mut systems_g = systems.lock().unwrap();
// let sys = systems_g.get_mut(&sys_id).expect("System not found");
// let lazy = &mut sys.lazy_members;
// let(path,cb) = match lazy.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(path.clone());
// hand.handle(&get_tree, &tree.into_api(&mut TIACtxImpl {
// sys:SysCtx::new(sys_id, &sys.cted,
// &logger,hand.reqnot()),path:Substack::Bottom,basepath: &path,lazy,
// })).await
// }
// api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs@api::GetVfs(sys_id))) => {
// let systems_g = systems.lock().unwrap();
// hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await
// }
// api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
// let api::SysFwded(sys_id,payload) = fwd;
// let ctx = mk_ctx(sys_id,hand.reqnot());
// let sys = ctx.cted.inst();
// sys.dyn_request(hand,payload)
// }
// api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => {
// let api::VfsRead(sys_id,vfs_id,path) = &vfs_read;
// let systems_g = systems.lock().unwrap();
// let path = path.iter().map(|t|Tok::from_api(*t)).collect_vec();
// hand.handle(&vfs_read,
// &systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path))).await }
// api::HostExtReq::LexExpr(lex@api::LexExpr {
// sys,text,pos,id
// }) => {
// let systems_g = systems.lock().unwrap();
// let lexers = systems_g[&sys].cted.inst().dyn_lexers();
// mem::drop(systems_g);
// let text = Tok::from_api(text);
// let ctx = LexContext {
// sys,id,pos,reqnot:hand.reqnot(),text: &text
// };
// let trigger_char = text.await.chars().nth(pos as usize).unwrap();
// for lx in
// lexers.iter().filter(|l|char_filter_match(l.char_filter(),trigger_char)){
// match lx.lex(&text[pos as usize..], &ctx){
// Err(e)if e.any(|e| *e==err_not_applicable()) => continue,
// Err(e) => {
// let eopt = e.keep_only(|e|
// *e!=err_cascade()).map(|e|Err(e.to_api())); return
// hand.handle(&lex, &eopt) },
// Ok((s,expr)) => {
// let ctx = mk_ctx(sys,hand.reqnot());
// let expr = expr.to_api(&mut |f,r|do_extra(f,r,ctx.clone()));
// let pos = (text.len()-s.len())as u32;
// return hand.handle(&lex, &Some(Ok(api::LexedExpr {
// pos,expr
// })))
// }
// }
// }writeln!(logger,"Got notified about n/a character '{trigger_char}'");
// hand.handle(&lex, &None).await
// },
// api::HostExtReq::ParseLine(pline) => {
// let api::ParseLine {
// exported,comments,sys,line
// } = &pline;
// let mut ctx = mk_ctx(*sys,hand.reqnot());
// let parsers = ctx.cted.inst().dyn_parsers();
// let comments = comments.iter().map(Comment::from_api).collect();
// let line:Vec<GenTokTree> = ttv_from_api(line, &mut ctx);
// let snip = Snippet::new(line.first().expect("Empty line"), &line);
// let(head,tail) = snip.pop_front().unwrap();
// let name = if let GenTok::Name(n) = &head.tok {
// n
// }else {
// panic!("No line head")
// };
// let parser = parsers.iter().find(|p|p.line_head()== **name).expect("No parser
// candidate"); let o_line = match parser.parse(*exported,comments,tail){
// Err(e) => Err(e.to_api()),
// Ok(t) => Ok(ttv_to_api(t, &mut |f,range|{
// api::TokenTree {
// range,token:api::Token::Atom(f.clone().build(ctx.clone()))
// }
// })),
// };
// hand.handle(&pline, &o_line).await
// }
// api::HostExtReq::AtomReq(atom_req) => {
// let atom = atom_req.get_atom();
// with_atom_record(&mk_ctx,hand.reqnot(),atom, |nfo,ctx,id,buf|async {
// let actx = AtomCtx(buf,atom.drop,ctx.clone());
// match&atom_req {
// api::AtomReq::SerializeAtom(ser) => {
// let mut buf = enc_vec(&id);
// let refs_opt = nfo.serialize(actx, &mut buf);
// hand.handle(ser, &refs_opt.map(|refs|(buf,refs))),await
// }
// api::AtomReq::AtomPrint(print@api::AtomPrint(_)) => hand.handle(print,
// &nfo.print(actx)).await, api::AtomReq::Fwded(fwded) => {
// let api::Fwded(_,key,payload) = &fwded;
// let mut reply = Vec::new();
// let some = nfo.handle_req(actx,Sym::from_api(*key), &mut
// &payload[..], &mut reply); hand.handle(fwded,
// &some.then_some(reply)).await }
// api::AtomReq::CallRef(call@api::CallRef(_,arg)) => {
// let ret = nfo.call_ref(actx, *arg);
// hand.handle(call, &ret.api_return(ctx.clone(), &mut
// |h|hand.defer_drop(h))).await },
// api::AtomReq::FinalCall(call@api::FinalCall(_,arg)) => {
// let ret = nfo.call(actx, *arg);
// hand.handle(call, &ret.api_return(ctx.clone(), &mut
// |h|hand.defer_drop(h))).await }
// api::AtomReq::Command(cmd@api::Command(_)) => {
// hand.handle(cmd, &match nfo.command(actx){
// Err(e) => Err(e.to_api()),
// Ok(opt) => Ok(match opt {
// None => api::NextStep::Halt,
// Some(cont) =>
// api::NextStep::Continue(cont.api_return(ctx.clone(), &mut |h|hand.defer_drop(h))),
// })
// }).await
// }
// }
// }).await
// },
// api::HostExtReq::DeserAtom(deser) => {
// let api::DeserAtom(sys,buf,refs) = &deser;
// let mut read = &mut &buf[..];
// let ctx = mk_ctx(*sys,hand.reqnot());
// let id = api::AtomId::decode(&mut read);
// let inst = ctx.cted.inst();
// let nfo = atom_by_idx(inst.card(),id).expect("Deserializing atom with invalid
// ID"); hand.handle(&deser, &nfo.deserialize(ctx.clone(),read,refs)).await
// },
// orchid_api::HostExtReq::ApplyMacro(am) => {
// let tok = hand.will_handle_as(&am);
// let sys_ctx = mk_ctx(am.sys,hand.reqnot());
// let ctx = RuleCtx {
// args:(am.params.into_iter()).map(|(k,v)|(Tok::from_api(k),mtreev_from_api(&v,
// &mut |_|panic!("No atom in macro
// prompt!")))).collect(),run_id:am.run_id,sys:sys_ctx.clone(), };
// hand.handle_as(tok, &match apply_rule(am.id,ctx){
// Err(e) => e.keep_only(|e| *e!=err_cascade()).map(|e|Err(e.to_api())),
// Ok(t) => Some(Ok(mtreev_to_api(&t, &mut |a|{
// api::MacroToken::Atom(a.clone().build(sys_ctx.clone()))
// }))),
// }).await
// }
};
receipt
api::HostExtReq::Sweep(sweep @ api::Sweep) =>
hand.handle(&sweep, &i.sweep_replica().await).await,
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);
let mut vfses = HashMap::new();
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_mems = Mutex::new(HashMap::new());
let rules = Mutex::new(HashMap::new());
let ctx = SysCtx {
cted: cted.clone(),
id: new_sys.id,
logger: logger.clone(),
reqnot: hand.reqnot(),
i: i.clone(),
obj_store: obj_store.clone(),
spawner: spawner.clone(),
};
let const_root = stream::from_iter(cted.inst().dyn_env())
.then(|(k, v)| {
let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules);
clone!(i, ctx; async move {
let name = i.i::<String>(&k).await.to_api();
let value = v.into_api(&mut TIACtxImpl {
lazy_members: &mut *lazy_mems.lock().await,
rules: &mut *rules.lock().await,
sys: ctx,
basepath: &[],
path: Substack::Bottom,
req
})
.await;
(name, value)
})
})
.collect()
.await;
let declfs = cted.inst().dyn_vfs().to_api_rec(&mut vfses, &i).await;
let record = SystemRecord {
declfs,
vfses,
cted,
lazy_members: lazy_mems.into_inner(),
rules: rules.into_inner(),
};
systems.lock().await.insert(new_sys.id, record);
hand
.handle(&new_sys, &api::SystemInst { lex_filter, const_root, line_types: vec![] })
.await
},
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => {
let sys_ctx = mk_ctx(sys_id, hand.reqnot()).await;
let mut systems_g = systems.lock().await;
let SystemRecord { lazy_members, rules, .. } =
systems_g.get_mut(&sys_id).expect("System not found");
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(), &i).await.unwrap()).await;
let mut tia_ctx = TIACtxImpl {
sys: sys_ctx,
path: Substack::Bottom,
basepath: &path,
lazy_members,
rules,
req: &hand,
};
hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await
},
api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs @ api::GetVfs(sys_id))) => {
let systems_g = systems.lock().await;
hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await
},
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
let api::SysFwded(sys_id, payload) = fwd;
let ctx = mk_ctx(sys_id, hand.reqnot()).await;
let sys = ctx.cted.inst();
sys.dyn_request(hand, payload).await
},
api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => {
let api::VfsRead(sys_id, vfs_id, path) = &vfs_read;
let ctx = mk_ctx(*sys_id, hand.reqnot()).await;
let systems_g = systems.lock().await;
let path = join_all(path.iter().map(|t| Tok::from_api(*t, &i))).await;
let vfs = systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path), ctx).await;
hand.handle(&vfs_read, &vfs).await
},
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, text, pos, id }) => {
let systems_g = systems.lock().await;
let lexers = systems_g[&sys].cted.inst().dyn_lexers();
mem::drop(systems_g);
let text = Tok::from_api(text, &i).await;
let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text, i: &i };
let trigger_char = text.chars().nth(pos as usize).unwrap();
let err_na = err_not_applicable(&i).await;
let err_cascade = err_cascade(&i).await;
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) {
match lx.lex(&text[pos as usize..], &ctx).await {
Err(e) if e.any(|e| *e == err_na) => continue,
Err(e) => {
let eopt = e.keep_only(|e| *e != err_cascade).map(|e| Err(e.to_api()));
return hand.handle(&lex, &eopt).await;
},
Ok((s, expr)) => {
let ctx = mk_ctx(sys, hand.reqnot()).await;
let expr = expr
.to_api(&mut |f, r| {
clone!(ctx; async move { do_extra(f, r, ctx).await }).boxed_local()
})
.await;
let pos = (text.len() - s.len()) as u32;
return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
},
}
}
writeln!(logger, "Got notified about n/a character '{trigger_char}'");
hand.handle(&lex, &None).await
},
api::HostExtReq::ParseLine(pline) => {
let api::ParseLine { exported, comments, sys, line } = &pline;
let mut ctx = mk_ctx(*sys, hand.reqnot()).await;
let parsers = ctx.cted.inst().dyn_parsers();
let comments = join_all(comments.iter().map(|c| Comment::from_api(c, &i))).await;
let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx, &i).await;
let snip = Snippet::new(line.first().expect("Empty line"), &line, &i);
let (head, tail) = snip.pop_front().unwrap();
let name = if let GenTok::Name(n) = &head.tok { n } else { panic!("No line head") };
let parser =
parsers.iter().find(|p| p.line_head() == **name).expect("No parser candidate");
let o_line = match parser.parse(*exported, comments, tail) {
Err(e) => Err(e.to_api()),
Ok(t) => Ok(
ttv_to_api(t, &mut |f, range| {
clone!(ctx);
async move {
api::TokenTree { range, token: api::Token::Atom(f.clone().build(ctx).await) }
}
.boxed_local()
})
.await,
),
};
hand.handle(&pline, &o_line).await
},
api::HostExtReq::AtomReq(atom_req) => {
let atom = atom_req.get_atom();
let atom_req = atom_req.clone();
with_atom_record(&mk_ctx, hand.reqnot(), atom, move |nfo, ctx, id, buf| {
async move {
let actx = AtomCtx(buf, atom.drop, ctx.clone());
match &atom_req {
api::AtomReq::SerializeAtom(ser) => {
let mut buf = enc_vec(&id);
let refs_opt = nfo.serialize(actx, &mut buf).await;
hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await
},
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
hand.handle(print, &nfo.print(actx).await).await,
api::AtomReq::Fwded(fwded) => {
let api::Fwded(_, key, payload) = &fwded;
let mut reply = Vec::new();
let key = Sym::from_api(*key, &i).await;
let some = nfo.handle_req(actx, key, &mut &payload[..], &mut reply).await;
hand.handle(fwded, &some.then_some(reply)).await
},
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
let ret = nfo.call_ref(actx, *arg).await;
hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await
},
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
let ret = nfo.call(actx, *arg).await;
hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).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.api_return(ctx.clone(), &hand).await;
hand.handle(cmd, &Ok(api::NextStep::Continue(cont))).await
},
},
},
}
}
.boxed_local()
})
.await
},
api::HostExtReq::DeserAtom(deser) => {
let api::DeserAtom(sys, buf, refs) = &deser;
let mut read = &mut &buf[..];
let ctx = mk_ctx(*sys, hand.reqnot()).await;
let id = api::AtomId::decode(&mut read);
let inst = ctx.cted.inst();
let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID");
hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, refs).await).await
},
orchid_api::HostExtReq::ApplyMacro(am) => {
let tok = hand.will_handle_as(&am);
let ApplyMacro { id, params, run_id, sys } = am;
let sys_ctx = mk_ctx(sys, hand.reqnot()).await;
let mut ctx =
RuleCtx { args: ahash::HashMap::default(), run_id, sys: sys_ctx.clone() };
for (k, v) in params {
ctx.args.insert(
Tok::from_api(k, &i).await,
mtreev_from_api(&v, &mut |_| panic!("No atom in macro prompt!"), &i).await,
);
}
let err_cascade = err_cascade(&i).await;
let systems_g = systems.lock().await;
let rule = &systems_g[&sys].rules[&id];
match (rule.apply)(ctx).await {
Err(e) => {
let new_errors = e.keep_only(|e| *e != err_cascade);
hand.handle_as(tok, &new_errors.map(|e| Err(e.to_api()))).await
},
Ok(t) => {
let result = mtreev_to_api(&t, &mut |a| {
clone!(sys_ctx; async move {
api::MacroToken::Atom(a.clone().build(sys_ctx.clone()).await)
}.boxed_local())
})
.await;
hand.handle_as(tok, &Some(Ok(result))).await
},
}
},
}
}
.boxed_local()
})
}
},
);
init_replica(rn.clone().map());
*interner_cell.borrow_mut() = Some(Rc::new(Interner::new_replica(rn.clone().map())));
while !exiting.load(Ordering::Relaxed) {
let rcvd = recv_parent_msg().await.unwrap();
rn.receive(&rcvd).await

View File

@@ -1,18 +1,16 @@
use std::fmt;
use std::sync::Arc;
use std::rc::Rc;
use async_once_cell::OnceCell;
use derive_destructure::destructure;
use futures::task::LocalSpawnExt;
use orchid_base::error::{OrcErr, OrcErrv};
use orchid_base::interner::Tok;
use orchid_base::error::OrcErrv;
use orchid_base::location::Pos;
use orchid_base::reqnot::Requester;
use crate::api;
use crate::atom::{AtomFactory, ForeignAtom, ToAtom};
use crate::conv::{ToExpr, TryFromExpr};
use crate::func_atom::Lambda;
use crate::atom::ForeignAtom;
use crate::gen_expr::{GExpr, GExprKind};
use crate::system::SysCtx;
#[derive(destructure)]
@@ -31,7 +29,11 @@ impl fmt::Debug for ExprHandle {
}
impl Clone for ExprHandle {
fn clone(&self) -> Self {
self.ctx.reqnot.notify(api::Acquire(self.ctx.id, self.tk));
let SysCtx { reqnot, spawner, .. } = self.ctx.clone();
let notif = api::Acquire(self.ctx.id, self.tk);
if let Err(e) = spawner.spawn_local(async move { reqnot.notify(notif).await }) {
panic!("Failed to schedule cloning notification, resource may not exist: {e}");
}
Self { ctx: self.ctx.clone(), tk: self.tk }
}
}
@@ -47,25 +49,24 @@ impl Drop for ExprHandle {
#[derive(Clone, Debug, destructure)]
pub struct Expr {
handle: Option<Arc<ExprHandle>>,
data: Arc<OnceCell<ExprData>>,
handle: Rc<ExprHandle>,
data: Rc<OnceCell<ExprData>>,
}
impl Expr {
pub fn new(h: Arc<ExprHandle>, d: ExprData) -> Self {
Self { handle: Some(h), data: Arc::new(OnceCell::from(d)) }
pub fn from_handle(handle: Rc<ExprHandle>) -> Self { Self { handle, data: Rc::default() } }
pub fn new(handle: Rc<ExprHandle>, d: ExprData) -> Self {
Self { handle, data: Rc::new(OnceCell::from(d)) }
}
pub fn from_handle(h: Arc<ExprHandle>) -> Self { Self { handle: Some(h), data: Arc::default() } }
pub fn from_data(d: ExprData) -> Self { Self { handle: None, data: Arc::new(OnceCell::from(d)) } }
pub async fn data(&self) -> &ExprData {
(self.data.get_or_init(async {
let handle = self.handle.as_ref().expect("Either the value or the handle must be set");
let details = handle.ctx.reqnot.request(api::Inspect { target: handle.tk }).await;
let pos = Pos::from_api(&details.location).await;
let details = self.handle.ctx.reqnot.request(api::Inspect { target: self.handle.tk }).await;
let pos = Pos::from_api(&details.location, &self.handle.ctx.i).await;
let kind = match details.kind {
api::InspectedKind::Atom(a) =>
ExprKind::Atom(ForeignAtom::new(handle.clone(), a, pos.clone())),
api::InspectedKind::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b).await),
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
api::InspectedKind::Bottom(b) =>
ExprKind::Bottom(OrcErrv::from_api(&b, &self.handle.ctx.i).await),
api::InspectedKind::Opaque => ExprKind::Opaque,
};
ExprData { pos, kind }
@@ -73,25 +74,15 @@ impl Expr {
.await
}
pub async fn atom(self) -> Result<ForeignAtom<'static>, Self> {
match (self.data().await, &self.handle) {
(ExprData { kind: ExprKind::Atom(atom), .. }, Some(_)) => Ok(atom.clone()),
match self.data().await {
ExprData { kind: ExprKind::Atom(atom), .. } => Ok(atom.clone()),
_ => Err(self),
}
}
pub fn api_return(
self,
ctx: SysCtx,
do_slot: &mut impl FnMut(Arc<ExprHandle>),
) -> api::Expression {
if let Some(h) = self.handle {
do_slot(h.clone());
api::Expression { location: api::Location::SlotTarget, kind: api::ExpressionKind::Slot(h.tk) }
} else {
let data = self.data.get().expect("Either value or handle must be set");
data.clone().api_return(ctx, do_slot)
}
}
pub fn handle(&self) -> Option<Arc<ExprHandle>> { self.handle.clone() }
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
pub fn ctx(&self) -> SysCtx { self.handle.ctx.clone() }
pub fn gen(&self) -> GExpr { GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) } }
}
#[derive(Clone, Debug)]
@@ -99,84 +90,10 @@ pub struct ExprData {
pub pos: Pos,
pub kind: ExprKind,
}
impl ExprData {
pub fn api_return(
self,
ctx: SysCtx,
do_slot: &mut impl FnMut(Arc<ExprHandle>),
) -> api::Expression {
api::Expression { location: self.pos.to_api(), kind: self.kind.api_return(ctx, do_slot) }
}
}
#[derive(Clone, Debug)]
pub enum ExprKind {
Call(Box<Expr>, Box<Expr>),
Lambda(u64, Box<Expr>),
Arg(u64),
Seq(Box<Expr>, Box<Expr>),
Const(Tok<Vec<Tok<String>>>),
NewAtom(AtomFactory),
Atom(ForeignAtom<'static>),
Bottom(OrcErrv),
Opaque,
}
impl ExprKind {
pub fn api_return(
self,
ctx: SysCtx,
do_slot: &mut impl FnMut(Arc<ExprHandle>),
) -> api::ExpressionKind {
use api::ExpressionKind as K;
match self {
Self::Call(f, x) =>
K::Call(Box::new(f.api_return(ctx.clone(), do_slot)), Box::new(x.api_return(ctx, do_slot))),
Self::Seq(a, b) =>
K::Seq(Box::new(a.api_return(ctx.clone(), do_slot)), Box::new(b.api_return(ctx, do_slot))),
Self::Lambda(arg, body) => K::Lambda(arg, Box::new(body.api_return(ctx, do_slot))),
Self::Arg(arg) => K::Arg(arg),
Self::Const(name) => K::Const(name.to_api()),
Self::Bottom(err) => K::Bottom(err.to_api()),
Self::NewAtom(fac) => K::NewAtom(fac.clone().build(ctx)),
kind @ (Self::Atom(_) | Self::Opaque) => panic!("{kind:?} should have a token"),
}
}
}
fn inherit(kind: ExprKind) -> Expr { Expr::from_data(ExprData { pos: Pos::Inherit, kind }) }
pub fn sym_ref(path: Tok<Vec<Tok<String>>>) -> Expr { inherit(ExprKind::Const(path)) }
pub fn atom<A: ToAtom>(atom: A) -> Expr { inherit(ExprKind::NewAtom(atom.to_atom_factory())) }
pub fn seq(ops: impl IntoIterator<Item = Expr>) -> Expr {
fn recur(mut ops: impl Iterator<Item = Expr>) -> Option<Expr> {
let op = ops.next()?;
Some(match recur(ops) {
None => op,
Some(rec) => inherit(ExprKind::Seq(Box::new(op), Box::new(rec))),
})
}
recur(ops.into_iter()).expect("Empty list provided to seq!")
}
pub fn arg(n: u64) -> Expr { inherit(ExprKind::Arg(n)) }
pub fn lambda(n: u64, b: impl IntoIterator<Item = Expr>) -> Expr {
inherit(ExprKind::Lambda(n, Box::new(call(b))))
}
pub fn call(v: impl IntoIterator<Item = Expr>) -> Expr {
v.into_iter()
.reduce(|f, x| inherit(ExprKind::Call(Box::new(f), Box::new(x))))
.expect("Empty call expression")
}
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> Expr {
inherit(ExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}
pub fn with<I: TryFromExpr, O: ToExpr>(
expr: Expr,
cont: impl Fn(I) -> O + Clone + Send + Sync + 'static,
) -> Expr {
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
}

View File

@@ -1,16 +1,20 @@
use std::future::{Future, ready};
use std::num::NonZero;
use futures::FutureExt;
use futures::future::{join, join_all};
use futures::future::LocalBoxFuture;
use hashbrown::HashMap;
use orchid_base::interner::intern;
use orchid_base::interner::Interner;
use orchid_base::name::PathSlice;
use crate::api;
use crate::system::SysCtx;
pub trait VirtFS: Send + Sync + 'static {
fn load(&self, path: &PathSlice) -> api::OrcResult<api::Loaded>;
fn load<'a>(
&'a self,
path: &'a PathSlice,
ctx: SysCtx,
) -> LocalBoxFuture<'a, api::OrcResult<api::Loaded>>;
}
pub enum DeclFs {
@@ -18,26 +22,25 @@ pub enum DeclFs {
Mod(&'static [(&'static str, DeclFs)]),
}
impl DeclFs {
pub fn to_api_rec(
pub async fn to_api_rec(
&self,
vfses: &mut HashMap<api::VfsId, &'static dyn VirtFS>,
) -> impl Future<Output = api::EagerVfs> + '_ {
i: &Interner,
) -> api::EagerVfs {
match self {
DeclFs::Lazy(fs) => {
let vfsc: u16 = vfses.len().try_into().expect("too many vfses (more than u16::MAX)");
let id = api::VfsId(NonZero::new(vfsc + 1).unwrap());
vfses.insert(id, *fs);
ready(api::EagerVfs::Lazy(id)).boxed_local()
api::EagerVfs::Lazy(id)
},
DeclFs::Mod(children) => {
let promises: Vec<_> =
children.iter().map(|(k, v)| join(intern(*k), v.to_api_rec(vfses))).collect();
async {
api::EagerVfs::Eager(
join_all(promises).await.into_iter().map(|(k, v)| (k.to_api(), v)).collect(),
)
let mut output = std::collections::HashMap::new();
for (k, v) in children.iter() {
output
.insert(i.i::<String>(*k).await.to_api(), v.to_api_rec(vfses, i).boxed_local().await);
}
.boxed_local()
api::EagerVfs::Eager(output)
},
}
}

View File

@@ -2,12 +2,12 @@ use std::borrow::Cow;
use std::collections::HashMap;
use std::future::Future;
use std::io;
use std::sync::{Arc, Mutex};
use std::rc::Rc;
use async_std::sync::Mutex;
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use itertools::Itertools;
use lazy_static::lazy_static;
use never::Never;
use orchid_api_traits::Encode;
use orchid_base::clone;
@@ -15,23 +15,24 @@ use orchid_base::error::OrcRes;
use orchid_base::name::Sym;
use trait_set::trait_set;
use crate::atom::{Atomic, MethodSet};
use crate::atom::{Atomic, MethodSetBuilder};
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr;
use crate::expr::{Expr, ExprHandle};
use crate::gen_expr::GExpr;
use crate::system::SysCtx;
trait_set! {
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<Expr>> + Send + Sync + 'static;
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
}
pub trait ExprFunc<I, O>: Clone + Send + Sync + 'static {
pub trait ExprFunc<I, O>: Clone + 'static {
const ARITY: u8;
fn apply(&self, v: Vec<Expr>) -> impl Future<Output = OrcRes<Expr>>;
fn apply(&self, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
}
lazy_static! {
static ref FUNS: Mutex<HashMap<Sym, (u8, Arc<dyn FunCB>)>> = Mutex::default();
thread_local! {
static FUNS: Rc<Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>> = Rc::default();
}
/// An Atom representing a partially applied named native function. These
@@ -44,15 +45,16 @@ pub(crate) struct Fun {
path: Sym,
args: Vec<Expr>,
arity: u8,
fun: Arc<dyn FunCB>,
fun: Rc<dyn FunCB>,
}
impl Fun {
pub fn new<I, O, F: ExprFunc<I, O>>(path: Sym, f: F) -> Self {
let mut fung = FUNS.lock().unwrap();
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, f: F) -> Self {
let funs = FUNS.with(|funs| funs.clone());
let mut fung = funs.lock().await;
let fun = if let Some(x) = fung.get(&path) {
x.1.clone()
} else {
let fun = Arc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
fung.insert(path.clone(), (F::ARITY, fun.clone()));
fun
};
@@ -62,14 +64,13 @@ impl Fun {
impl Atomic for Fun {
type Data = ();
type Variant = OwnedVariant;
fn reg_reqs() -> MethodSet<Self> { MethodSet::new() }
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl OwnedAtom for Fun {
type Refs = Vec<Expr>;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: ExprHandle) -> Expr {
let new_args =
self.args.iter().cloned().chain([Expr::from_handle(Arc::new(arg))]).collect_vec();
async fn call_ref(&self, arg: ExprHandle) -> GExpr {
let new_args = self.args.iter().cloned().chain([Expr::from_handle(Rc::new(arg))]).collect_vec();
if new_args.len() == self.arity.into() {
(self.fun)(new_args).await.to_expr()
} else {
@@ -77,14 +78,15 @@ impl OwnedAtom for Fun {
.to_expr()
}
}
async fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg).await }
async fn call(self, arg: ExprHandle) -> GExpr { self.call_ref(arg).await }
async fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs {
self.path.to_api().encode(sink);
self.args.clone()
}
async fn deserialize(ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
let path = Sym::from_api(ctx.decode()).await;
let (arity, fun) = FUNS.lock().unwrap().get(&path).unwrap().clone();
let sys = ctx.sys();
let path = Sym::from_api(ctx.decode(), &sys.i).await;
let (arity, fun) = FUNS.with(|f| f.clone()).lock().await.get(&path).unwrap().clone();
Self { args, arity, path, fun }
}
}
@@ -97,32 +99,31 @@ impl OwnedAtom for Fun {
pub struct Lambda {
args: Vec<Expr>,
arity: u8,
fun: Arc<dyn FunCB>,
fun: Rc<dyn FunCB>,
}
impl Lambda {
pub fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
let fun = Arc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
Self { args: vec![], arity: F::ARITY, fun }
}
}
impl Atomic for Lambda {
type Data = ();
type Variant = OwnedVariant;
fn reg_reqs() -> MethodSet<Self> { MethodSet::new() }
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl OwnedAtom for Lambda {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: ExprHandle) -> Expr {
let new_args =
self.args.iter().cloned().chain([Expr::from_handle(Arc::new(arg))]).collect_vec();
async fn call_ref(&self, arg: ExprHandle) -> GExpr {
let new_args = self.args.iter().cloned().chain([Expr::from_handle(Rc::new(arg))]).collect_vec();
if new_args.len() == self.arity.into() {
(self.fun)(new_args).await.to_expr()
} else {
Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr()
}
}
async fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg).await }
async fn call(self, arg: ExprHandle) -> GExpr { self.call_ref(arg).await }
}
mod expr_func_derives {
@@ -131,6 +132,7 @@ mod expr_func_derives {
use super::ExprFunc;
use crate::conv::{ToExpr, TryFromExpr};
use crate::func_atom::Expr;
use crate::gen_expr::GExpr;
macro_rules! expr_func_derive {
($arity: tt, $($t:ident),*) => {
@@ -141,7 +143,7 @@ mod expr_func_derives {
Func: Fn($($t,)*) -> Out + Clone + Send + Sync + 'static
> ExprFunc<($($t,)*), Out> for Func {
const ARITY: u8 = $arity;
async fn apply(&self, v: Vec<Expr>) -> OrcRes<Expr> {
async fn apply(&self, v: Vec<Expr>) -> OrcRes<GExpr> {
assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch");
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).to_expr())

View File

@@ -0,0 +1,106 @@
use futures::FutureExt;
use orchid_base::error::{OrcErr, OrcErrv};
use orchid_base::location::Pos;
use orchid_base::match_mapping;
use orchid_base::name::Sym;
use orchid_base::reqnot::ReqHandlish;
use crate::api;
use crate::atom::{AtomFactory, ToAtom};
use crate::conv::{ToExpr, TryFromExpr};
use crate::expr::Expr;
use crate::func_atom::Lambda;
use crate::system::SysCtx;
pub struct GExpr {
pub kind: GExprKind,
pub pos: Pos,
}
impl GExpr {
pub async fn api_return(self, ctx: SysCtx, hand: &impl ReqHandlish) -> api::Expression {
if let GExprKind::Slot(ex) = self.kind {
hand.defer_drop(ex.handle());
api::Expression {
location: api::Location::SlotTarget,
kind: api::ExpressionKind::Slot(ex.handle().tk),
}
} else {
api::Expression {
location: api::Location::Inherit,
kind: self.kind.api_return(ctx, hand).boxed_local().await,
}
}
}
}
pub enum GExprKind {
Call(Box<GExpr>, Box<GExpr>),
Lambda(u64, Box<GExpr>),
Arg(u64),
Seq(Box<GExpr>, Box<GExpr>),
Const(Sym),
NewAtom(AtomFactory),
Slot(Expr),
Bottom(OrcErrv),
}
impl GExprKind {
pub async fn api_return(self, ctx: SysCtx, hand: &impl ReqHandlish) -> api::ExpressionKind {
match_mapping!(self, Self => api::ExpressionKind {
Call(
f => Box::new(f.api_return(ctx.clone(), hand).await),
x => Box::new(x.api_return(ctx, hand).await)
),
Seq(
a => Box::new(a.api_return(ctx.clone(), hand).await),
b => Box::new(b.api_return(ctx, hand).await)
),
Lambda(arg, body => Box::new(body.api_return(ctx, hand).await)),
Arg(arg),
Const(name.to_api()),
Const(name.to_api()),
Bottom(err.to_api()),
NewAtom(fac.clone().build(ctx).await),
} {
Self::Slot(_) => panic!("processed elsewhere")
})
}
}
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
pub fn sym_ref(path: Sym) -> GExpr { inherit(GExprKind::Const(path)) }
pub fn atom<A: ToAtom>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.to_atom_factory())) }
pub fn seq(ops: impl IntoIterator<Item = GExpr>) -> GExpr {
fn recur(mut ops: impl Iterator<Item = GExpr>) -> Option<GExpr> {
let op = ops.next()?;
Some(match recur(ops) {
None => op,
Some(rec) => inherit(GExprKind::Seq(Box::new(op), Box::new(rec))),
})
}
recur(ops.into_iter()).expect("Empty list provided to seq!")
}
pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) }
pub fn lambda(n: u64, b: impl IntoIterator<Item = GExpr>) -> GExpr {
inherit(GExprKind::Lambda(n, Box::new(call(b))))
}
pub fn call(v: impl IntoIterator<Item = GExpr>) -> GExpr {
v.into_iter()
.reduce(|f, x| inherit(GExprKind::Call(Box::new(f), Box::new(x))))
.expect("Empty call expression")
}
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}
pub fn with<I: TryFromExpr, O: ToExpr>(
expr: GExpr,
cont: impl Fn(I) -> O + Clone + Send + Sync + 'static,
) -> GExpr {
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
}

View File

@@ -1,8 +1,10 @@
use std::future::Future;
use std::ops::{Range, RangeInclusive};
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::intern;
use orchid_base::interner::Tok;
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::tree::TokHandle;
@@ -10,20 +12,19 @@ use orchid_base::tree::TokHandle;
use crate::api;
use crate::tree::{GenTok, GenTokTree};
pub async fn err_cascade() -> OrcErr {
pub async fn err_cascade(i: &Interner) -> OrcErr {
mk_err(
intern!(str: "An error cascading from a recursive call").await,
i.i("An error cascading from a recursive call").await,
"This error is a sentinel for the extension library.\
it should not be emitted by the extension.",
[Pos::None.into()],
)
}
pub async fn err_not_applicable() -> OrcErr {
pub async fn err_not_applicable(i: &Interner) -> OrcErr {
mk_err(
intern!(str: "Pseudo-error to communicate that the current branch in a dispatch doesn't apply")
.await,
&*err_cascade().await.message,
i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await,
&*err_cascade(i).await.message,
[Pos::None.into()],
)
}
@@ -34,12 +35,13 @@ pub struct LexContext<'a> {
pub id: api::ParsId,
pub pos: u32,
pub reqnot: ReqNot<api::ExtMsgSet>,
pub i: &'a Interner,
}
impl<'a> LexContext<'a> {
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let start = self.pos(tail);
let Some(lx) = self.reqnot.request(api::SubLex { pos: start, id: self.id }).await else {
return Err(err_cascade().await.into());
return Err(err_cascade(self.i).await.into());
};
Ok((&self.text[lx.pos as usize..], GenTok::Slot(TokHandle::new(lx.ticket)).at(start..lx.pos)))
}
@@ -53,18 +55,29 @@ impl<'a> LexContext<'a> {
pub trait Lexer: Send + Sync + Sized + Default + 'static {
const CHAR_FILTER: &'static [RangeInclusive<char>];
fn lex<'a>(tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)>;
fn lex<'a>(
tail: &'a str,
ctx: &'a LexContext<'a>,
) -> impl Future<Output = OrcRes<(&'a str, GenTokTree<'a>)>>;
}
pub trait DynLexer: Send + Sync + 'static {
fn char_filter(&self) -> &'static [RangeInclusive<char>];
fn lex<'a>(&self, tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)>;
fn lex<'a>(
&self,
tail: &'a str,
ctx: &'a LexContext<'a>,
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree<'a>)>>;
}
impl<T: Lexer> DynLexer for T {
fn char_filter(&self) -> &'static [RangeInclusive<char>] { T::CHAR_FILTER }
fn lex<'a>(&self, tail: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
T::lex(tail, ctx)
fn lex<'a>(
&self,
tail: &'a str,
ctx: &'a LexContext<'a>,
) -> LocalBoxFuture<'a, OrcRes<(&'a str, GenTokTree<'a>)>> {
T::lex(tail, ctx).boxed_local()
}
}

View File

@@ -8,6 +8,7 @@ pub mod entrypoint;
pub mod expr;
pub mod fs;
pub mod func_atom;
pub mod gen_expr;
pub mod lexer;
pub mod macros;
pub mod msg;

View File

@@ -1,15 +1,12 @@
use std::num::NonZero;
use std::sync::RwLock;
use std::rc::Rc;
use ahash::HashMap;
use futures::future::join_all;
use lazy_static::lazy_static;
use futures::future::{LocalBoxFuture, join_all};
use itertools::Itertools;
use never::Never;
use orchid_base::error::OrcRes;
use orchid_base::interner::{Tok, intern};
use orchid_base::location::Pos;
use orchid_base::interner::Tok;
use orchid_base::macros::{MTree, mtreev_from_api, mtreev_to_api};
use orchid_base::parse::Comment;
use orchid_base::reqnot::Requester;
use trait_set::trait_set;
@@ -17,6 +14,7 @@ use crate::api;
use crate::atom::AtomFactory;
use crate::lexer::err_cascade;
use crate::system::SysCtx;
use crate::tree::TreeIntoApiCtx;
pub trait Macro {
fn pattern() -> MTree<'static, Never>;
@@ -42,12 +40,15 @@ pub struct RuleCtx<'a> {
}
impl<'a> RuleCtx<'a> {
pub async fn recurse(&mut self, tree: &[MTree<'a, Never>]) -> OrcRes<Vec<MTree<'a, Never>>> {
let req =
api::RunMacros { run_id: self.run_id, query: mtreev_to_api(tree, &mut |b| match *b {}) };
let Some(treev) = self.sys.reqnot.request(req).await else {
return Err(err_cascade().await.into());
let req = api::RunMacros {
run_id: self.run_id,
query: mtreev_to_api(tree, &mut |b| match *b {}).await,
};
Ok(mtreev_from_api(&treev, &mut |_| panic!("Returned atom from Rule recursion")).await)
let Some(treev) = self.sys.reqnot.request(req).await else {
return Err(err_cascade(&self.sys.i).await.into());
};
static ATOM_MSG: &str = "Returned atom from Rule recursion";
Ok(mtreev_from_api(&treev, &mut |_| panic!("{ATOM_MSG}"), &self.sys.i).await)
}
pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a, Never>> {
self.args.remove(key).expect("Key not found")
@@ -65,52 +66,37 @@ impl<'a> RuleCtx<'a> {
}
trait_set! {
pub trait RuleCB = for<'a> Fn(RuleCtx<'a>) -> OrcRes<Vec<MTree<'a, AtomFactory>>> + Send + Sync;
}
lazy_static! {
static ref RULES: RwLock<HashMap<api::MacroId, Box<dyn RuleCB>>> = RwLock::default();
pub trait RuleCB = for<'a> Fn(RuleCtx<'a>) -> LocalBoxFuture<'a, OrcRes<Vec<MTree<'a, AtomFactory>>>>;
}
pub struct Rule {
pub(crate) comments: Vec<Comment>,
pub(crate) comments: Vec<String>,
pub(crate) pattern: Vec<MTree<'static, Never>>,
pub(crate) id: api::MacroId,
pub(crate) apply: Rc<dyn RuleCB>,
}
impl Rule {
pub(crate) fn to_api(&self) -> api::MacroRule {
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MacroRule {
api::MacroRule {
comments: self.comments.iter().map(|c| c.to_api()).collect(),
comments: join_all(self.comments.iter().map(|c| async {
api::Comment { text: ctx.sys().i.i(c).await.to_api(), location: api::Location::Inherit }
}))
.await,
location: api::Location::Inherit,
pattern: mtreev_to_api(&self.pattern, &mut |b| match *b {}),
id: self.id,
pattern: mtreev_to_api(&self.pattern, &mut |b| match *b {}).await,
id: ctx.with_rule(Rc::new(self)),
}
}
}
pub async fn rule_cmt<'a>(
pub fn rule_cmt<'a>(
cmt: impl IntoIterator<Item = &'a str>,
pattern: Vec<MTree<'static, Never>>,
apply: impl RuleCB + 'static,
) -> Rule {
let mut rules = RULES.write().unwrap();
let id = api::MacroId(NonZero::new(rules.len() as u64 + 1).unwrap());
rules.insert(id, Box::new(apply));
let comments = join_all(
cmt.into_iter().map(|s| async { Comment { pos: Pos::Inherit, text: intern(s).await } }),
)
.await;
Rule { comments, pattern, id }
let comments = cmt.into_iter().map(|s| s.to_string()).collect_vec();
Rule { comments, pattern, apply: Rc::new(apply) }
}
pub async fn rule(pattern: Vec<MTree<'static, Never>>, apply: impl RuleCB + 'static) -> Rule {
rule_cmt([], pattern, apply).await
}
pub(crate) fn apply_rule(
id: api::MacroId,
ctx: RuleCtx<'static>,
) -> OrcRes<Vec<MTree<'static, AtomFactory>>> {
let rules = RULES.read().unwrap();
rules[&id](ctx)
pub fn rule(pattern: Vec<MTree<'static, Never>>, apply: impl RuleCB + 'static) -> Rule {
rule_cmt([], pattern, apply)
}

View File

@@ -1,6 +1,8 @@
use core::fmt;
use std::any::TypeId;
use std::future::Future;
use std::num::NonZero;
use std::rc::Rc;
use std::sync::Arc;
use futures::FutureExt;
@@ -9,14 +11,13 @@ use futures::task::LocalSpawn;
use hashbrown::HashMap;
use orchid_api_traits::{Coding, Decode};
use orchid_base::boxed_iter::BoxedIter;
use orchid_base::id_store::IdStore;
use orchid_base::interner::Tok;
use orchid_base::interner::Interner;
use orchid_base::logging::Logger;
use orchid_base::reqnot::{Receipt, ReqNot};
use crate::api;
use crate::atom::{AtomCtx, AtomDynfo, AtomicFeatures, ForeignAtom, TypAtom, get_info};
use crate::atom_owned::{DynOwnedAtom, ObjStore};
use crate::atom_owned::ObjStore;
use crate::entrypoint::ExtReq;
use crate::fs::DeclFs;
use crate::func_atom::Fun;
@@ -79,28 +80,28 @@ impl<T: SystemCard> DynSystemCard for T {
/// System as defined by author
pub trait System: Send + Sync + SystemCard + 'static {
fn env() -> Vec<(Tok<String>, MemKind)>;
fn env() -> Vec<(String, MemKind)>;
fn vfs() -> DeclFs;
fn lexers() -> Vec<LexerObj>;
fn parsers() -> Vec<ParserObj>;
fn request<'a>(hand: ExtReq<'a>, req: Self::Req) -> impl Future<Output = Receipt<'a>>;
fn request(hand: ExtReq<'_>, req: Self::Req) -> impl Future<Output = Receipt<'_>>;
}
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
fn dyn_env(&self) -> HashMap<Tok<String>, MemKind>;
fn dyn_env(&self) -> HashMap<String, MemKind>;
fn dyn_vfs(&self) -> DeclFs;
fn dyn_lexers(&self) -> Vec<LexerObj>;
fn dyn_parsers(&self) -> Vec<ParserObj>;
fn dyn_request<'a>(&'a self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
fn card(&self) -> &dyn DynSystemCard;
}
impl<T: System> DynSystem for T {
fn dyn_env(&self) -> HashMap<Tok<String>, MemKind> { Self::env().into_iter().collect() }
fn dyn_env(&self) -> HashMap<String, MemKind> { Self::env().into_iter().collect() }
fn dyn_vfs(&self) -> DeclFs { Self::vfs() }
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
fn dyn_request<'a>(&'a self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
Self::request(hand, <Self as SystemCard>::Req::decode(&mut &req[..])).boxed_local()
}
fn card(&self) -> &dyn DynSystemCard { self }
@@ -125,9 +126,15 @@ pub fn downcast_atom<A: AtomicFeatures>(foreign: ForeignAtom) -> Result<TypAtom<
#[derive(Clone)]
pub struct SysCtx {
pub reqnot: ReqNot<api::ExtMsgSet>,
pub spawner: Arc<dyn LocalSpawn>,
pub spawner: Rc<dyn LocalSpawn>,
pub id: api::SysId,
pub cted: CtedObj,
pub logger: Arc<Logger>,
pub obj_store: ObjStore,
pub i: Rc<Interner>,
}
impl fmt::Debug for SysCtx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SysCtx({:?})", self.id)
}
}

View File

@@ -1,16 +1,16 @@
use std::future::Future;
use std::num::NonZero;
use std::ops::Range;
use std::rc::Rc;
use dyn_clone::{DynClone, clone_box};
use futures::FutureExt;
use futures::future::{LocalBoxFuture, join_all};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_base::interner::{Tok, intern};
use orchid_base::location::Pos;
use orchid_base::interner::Tok;
use orchid_base::name::Sym;
use orchid_base::parse::Comment;
use orchid_base::reqnot::ReqHandlish;
use orchid_base::tree::{TokTree, Token};
use ordered_float::NotNan;
use substack::Substack;
@@ -20,47 +20,56 @@ use crate::api;
use crate::atom::{AtomFactory, ForeignAtom};
use crate::conv::ToExpr;
use crate::entrypoint::MemberRecord;
use crate::expr::Expr;
use crate::func_atom::{ExprFunc, Fun};
use crate::gen_expr::GExpr;
use crate::macros::Rule;
use crate::system::SysCtx;
pub type GenTokTree<'a> = TokTree<'a, ForeignAtom<'a>, AtomFactory>;
pub type GenTok<'a> = Token<'a, ForeignAtom<'a>, AtomFactory>;
pub fn do_extra(f: &AtomFactory, r: Range<u32>, ctx: SysCtx) -> api::TokenTree {
api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx)) }
pub async fn do_extra(f: &AtomFactory, r: Range<u32>, ctx: SysCtx) -> api::TokenTree {
api::TokenTree { range: r, token: api::Token::Atom(f.clone().build(ctx).await) }
}
fn with_export(mem: GenMember, public: bool) -> Vec<GenItem> {
(public.then(|| GenItemKind::Export(mem.name.clone()).at(Pos::Inherit)).into_iter())
.chain([GenItemKind::Member(mem).at(Pos::Inherit)])
(public.then(|| GenItemKind::Export(mem.name.clone())).into_iter())
.chain([GenItemKind::Member(mem)])
.map(|kind| GenItem { comments: vec![], kind })
.collect()
}
pub struct GenItem {
pub kind: GenItemKind,
pub comments: Vec<Comment>,
pub pos: Pos,
pub comments: Vec<String>,
}
impl GenItem {
pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item {
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item {
let kind = match self.kind {
GenItemKind::Export(n) => api::ItemKind::Export(n.to_api()),
GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx)),
GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i.i::<String>(&n).await.to_api()),
GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx).await),
GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()),
GenItemKind::Macro(prio, rules) => api::ItemKind::Macro(api::MacroBlock {
priority: prio,
rules: rules.into_iter().map(|r| r.to_api()).collect_vec(),
}),
GenItemKind::Macro(priority, gen_rules) => {
let mut rules = Vec::with_capacity(gen_rules.len());
for rule in gen_rules {
rules.push(rule.into_api(ctx).await)
}
api::ItemKind::Macro(api::MacroBlock { priority, rules })
},
};
let comments = self.comments.into_iter().map(|c| c.to_api()).collect_vec();
api::Item { location: self.pos.to_api(), comments, kind }
let comments = join_all(self.comments.iter().map(|c| async {
api::Comment {
location: api::Location::Inherit,
text: ctx.sys().i.i::<String>(c).await.to_api(),
}
}))
.await;
api::Item { location: api::Location::Inherit, comments, kind }
}
}
pub async fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenItem> {
with_export(GenMember { name: intern(name).await, kind: MemKind::Const(value.to_expr()) }, public)
pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenItem> {
with_export(GenMember { name: name.to_string(), kind: MemKind::Const(value.to_expr()) }, public)
}
pub async fn module(
public: bool,
@@ -75,31 +84,31 @@ pub async fn root_mod(
name: &str,
imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = Vec<GenItem>>,
) -> (Tok<String>, MemKind) {
) -> (String, MemKind) {
let kind = MemKind::Mod {
imports: imports.into_iter().collect(),
items: items.into_iter().flatten().collect(),
};
(intern(name).await, kind)
(name.to_string(), kind)
}
pub async fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
let fac =
LazyMemberFactory::new(move |sym| async { MemKind::Const(Fun::new(sym, xf).to_expr()) });
with_export(GenMember { name: intern(name).await, kind: MemKind::Lazy(fac) }, exported)
LazyMemberFactory::new(move |sym| async { MemKind::Const(Fun::new(sym, xf).await.to_expr()) });
with_export(GenMember { name: name.to_string(), kind: MemKind::Lazy(fac) }, exported)
}
pub fn macro_block(prio: Option<f64>, rules: impl IntoIterator<Item = Rule>) -> Vec<GenItem> {
let prio = prio.map(|p| NotNan::new(p).unwrap());
vec![GenItemKind::Macro(prio, rules.into_iter().collect_vec()).gen()]
vec![GenItem {
kind: GenItemKind::Macro(prio, rules.into_iter().collect_vec()),
comments: vec![],
}]
}
pub async fn comments<'a>(
pub fn comments<'a>(
cmts: impl IntoIterator<Item = &'a str>,
mut val: Vec<GenItem>,
) -> Vec<GenItem> {
let cmts = join_all(
cmts.into_iter().map(|c| async { Comment { text: intern(c).await, pos: Pos::Inherit } }),
)
.await;
let cmts = cmts.into_iter().map(|c| c.to_string()).collect_vec();
for v in val.iter_mut() {
v.comments.extend(cmts.iter().cloned());
}
@@ -108,12 +117,12 @@ pub async fn comments<'a>(
trait_set! {
trait LazyMemberCallback =
FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + Send + Sync + DynClone
FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + DynClone
}
pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>);
impl LazyMemberFactory {
pub fn new<F: Future<Output = MemKind> + 'static>(
cb: impl FnOnce(Sym) -> F + Send + Sync + Clone + 'static,
cb: impl FnOnce(Sym) -> F + Clone + 'static,
) -> Self {
Self(Box::new(|s| cb(s).boxed_local()))
}
@@ -125,49 +134,45 @@ impl Clone for LazyMemberFactory {
pub enum GenItemKind {
Member(GenMember),
Export(Tok<String>),
Export(String),
Import(Sym),
Macro(Option<NotNan<f64>>, Vec<Rule>),
}
impl GenItemKind {
pub fn at(self, pos: Pos) -> GenItem { GenItem { kind: self, comments: vec![], pos } }
pub fn gen(self) -> GenItem { GenItem { kind: self, comments: vec![], pos: Pos::Inherit } }
pub fn gen_equiv(self, comments: Vec<Comment>) -> GenItem {
GenItem { kind: self, comments, pos: Pos::Inherit }
}
}
pub struct GenMember {
name: Tok<String>,
name: String,
kind: MemKind,
}
impl GenMember {
pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member {
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member {
let name = ctx.sys().i.i::<String>(&self.name).await;
api::Member {
name: self.name.to_api(),
kind: self.kind.into_api(&mut ctx.push_path(self.name)),
kind: self.kind.into_api(&mut ctx.push_path(name.clone())).await,
name: name.to_api(),
}
}
}
pub enum MemKind {
Const(Expr),
Const(GExpr),
Mod { imports: Vec<Sym>, items: Vec<GenItem> },
Lazy(LazyMemberFactory),
}
impl MemKind {
pub fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
match self {
Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)),
Self::Const(c) =>
api::MemberKind::Const(c.api_return(ctx.sys(), &mut |_| panic!("Slot in const tree"))),
Self::Mod { imports, items } => api::MemberKind::Module(api::Module {
items: (imports.into_iter())
.map(|t| GenItemKind::Import(t).gen())
.chain(items)
.map(|i| i.into_api(ctx))
.collect_vec(),
}),
Self::Const(c) => api::MemberKind::Const(c.api_return(ctx.sys(), ctx.req()).await),
Self::Mod { imports, items } => {
let all_items = (imports.into_iter())
.map(|t| GenItem { comments: vec![], kind: GenItemKind::Import(t) })
.chain(items);
let mut items = Vec::new();
for i in all_items {
items.push(i.into_api(ctx).boxed_local().await)
}
api::MemberKind::Module(api::Module { items })
},
}
}
}
@@ -175,30 +180,42 @@ impl MemKind {
pub trait TreeIntoApiCtx {
fn sys(&self) -> SysCtx;
fn with_lazy(&mut self, fac: LazyMemberFactory) -> api::TreeId;
fn with_rule(&mut self, rule: Rc<Rule>) -> api::MacroId;
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx;
fn req(&self) -> &impl ReqHandlish;
}
pub struct TIACtxImpl<'a, 'b> {
pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> {
pub sys: SysCtx,
pub basepath: &'a [Tok<String>],
pub path: Substack<'a, Tok<String>>,
pub lazy: &'b mut HashMap<api::TreeId, MemberRecord>,
pub lazy_members: &'b mut HashMap<api::TreeId, MemberRecord>,
pub rules: &'b mut HashMap<api::MacroId, Rc<Rule>>,
pub req: &'a RH,
}
impl<'a, 'b> TreeIntoApiCtx for TIACtxImpl<'a, 'b> {
impl<RH: ReqHandlish> TreeIntoApiCtx for TIACtxImpl<'_, '_, RH> {
fn sys(&self) -> SysCtx { self.sys.clone() }
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx {
TIACtxImpl {
req: self.req,
lazy_members: self.lazy_members,
rules: self.rules,
sys: self.sys.clone(),
lazy: self.lazy,
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.len() + 2) as u64).unwrap());
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.insert(id, MemberRecord::Gen(path, fac));
self.lazy_members.insert(id, MemberRecord::Gen(path, fac));
id
}
fn with_rule(&mut self, rule: Rc<Rule>) -> orchid_api::MacroId {
let id = api::MacroId(NonZero::new((self.lazy_members.len() + 1) as u64).unwrap());
self.rules.insert(id, rule);
id
}
fn req(&self) -> &impl ReqHandlish { self.req }
}