Macro system done in theory
too afraid to begin debugging, resting for a moment
This commit is contained in:
96
orchid-extension/src/coroutine_exec.rs
Normal file
96
orchid-extension/src/coroutine_exec.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use std::borrow::Cow;
|
||||
use std::marker::PhantomData;
|
||||
use std::pin::Pin;
|
||||
use std::rc::Rc;
|
||||
|
||||
use async_std::channel::{Receiver, RecvError, Sender, bounded};
|
||||
use async_std::sync::Mutex;
|
||||
use futures::future::Fuse;
|
||||
use futures::{FutureExt, select};
|
||||
use never::Never;
|
||||
use orchid_base::error::OrcRes;
|
||||
|
||||
use crate::atom::Atomic;
|
||||
use crate::atom_owned::{OwnedAtom, OwnedVariant};
|
||||
use crate::conv::{ToExpr, TryFromExpr};
|
||||
use crate::expr::Expr;
|
||||
use crate::gen_expr::{GExpr, arg, call, lambda, seq};
|
||||
|
||||
enum Command {
|
||||
Execute(GExpr, Sender<Expr>),
|
||||
Register(GExpr, Sender<Expr>),
|
||||
}
|
||||
|
||||
struct BuilderCoroutineData {
|
||||
work: Mutex<Fuse<Pin<Box<dyn Future<Output = GExpr>>>>>,
|
||||
cmd_recv: Receiver<Command>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
struct BuilderCoroutine(Rc<BuilderCoroutineData>);
|
||||
impl BuilderCoroutine {
|
||||
pub async fn run(self) -> GExpr {
|
||||
let cmd = {
|
||||
let mut work = self.0.work.lock().await;
|
||||
select! {
|
||||
ret_val = &mut *work => { return ret_val },
|
||||
cmd_res = self.0.cmd_recv.recv().fuse() => match cmd_res {
|
||||
Ok(cmd) => cmd,
|
||||
Err(RecvError) => return (&mut *work).await
|
||||
},
|
||||
}
|
||||
};
|
||||
match cmd {
|
||||
Command::Execute(expr, reply) => call([
|
||||
lambda(0, [seq([
|
||||
arg(0),
|
||||
call([Replier { reply, builder: self }.to_expr().await, arg(0)]),
|
||||
])]),
|
||||
expr,
|
||||
]),
|
||||
Command::Register(expr, reply) =>
|
||||
call([Replier { reply, builder: self }.to_expr().await, expr]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Replier {
|
||||
reply: Sender<Expr>,
|
||||
builder: BuilderCoroutine,
|
||||
}
|
||||
impl Atomic for Replier {
|
||||
type Data = ();
|
||||
type Variant = OwnedVariant;
|
||||
}
|
||||
impl OwnedAtom for Replier {
|
||||
type Refs = Never;
|
||||
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
async fn call(self, arg: Expr) -> GExpr {
|
||||
let _ = self.reply.send(arg).await;
|
||||
self.builder.run().await
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
|
||||
let (cmd_snd, cmd_recv) = bounded(1);
|
||||
let work =
|
||||
async { f(ExecHandle(cmd_snd, PhantomData)).await.to_expr().await }.boxed_local().fuse();
|
||||
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData { cmd_recv, work: Mutex::new(work) }));
|
||||
coro.run().await
|
||||
}
|
||||
|
||||
static WEIRD_DROP_ERR: &str = "Coroutine dropped while we are being polled somehow";
|
||||
|
||||
pub struct ExecHandle<'a>(Sender<Command>, PhantomData<&'a ()>);
|
||||
impl ExecHandle<'_> {
|
||||
pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
|
||||
let (reply_snd, reply_recv) = bounded(1);
|
||||
self.0.send(Command::Execute(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
||||
T::try_from_expr(reply_recv.recv().await.expect(WEIRD_DROP_ERR)).await
|
||||
}
|
||||
pub async fn register(&mut self, val: impl ToExpr) -> Expr {
|
||||
let (reply_snd, reply_recv) = bounded(1);
|
||||
self.0.send(Command::Register(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
|
||||
reply_recv.recv().await.expect(WEIRD_DROP_ERR)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user