Compiles again after command subsystem
Some checks failed
Rust / build (push) Failing after 3m52s

terrified to start testing
This commit is contained in:
2026-03-27 23:50:58 +01:00
parent 09cfcb1839
commit 0909524dee
75 changed files with 1165 additions and 609 deletions

1
Cargo.lock generated
View File

@@ -1139,6 +1139,7 @@ dependencies = [
"ordered-float",
"pastey",
"substack",
"task-local",
"tokio",
"tokio-util",
"trait-set",

View File

@@ -23,7 +23,7 @@ pub fn stream<'a, T: 'a>(
let (send, recv) = mpsc::channel::<T>(1);
let fut = async { f(StreamCtx(send, PhantomData)).await };
// use options to ensure that the stream is driven to exhaustion
select_with_strategy(fut.into_stream().map(|()| None), recv.map(|t| Some(t)), left_strat)
select_with_strategy(fut.into_stream().map(|()| None), recv.map(Some), left_strat)
.filter_map(async |opt| opt)
}

View File

@@ -18,4 +18,4 @@ The orchid embedder and extension API mean something different by command than a
## Continuation
Since commands are expected to be composed into arbitrarily deep TC structures,to avoid a memory leak, commands should not remain passively present in the system; they must be able to express certain outcomes as plain data and return. The most obvious of such outcomes is the single continuation wherein a subexpression evaluating to another command from the same set will eventually run, but other ones may potentially exist. Are there any that cannot be emulated by continuing with a call to an environment constant which expresses the outcome in terms of its parameters?
Since commands are expected to be composed into arbitrarily deep TC structures,to avoid a memory leak, commands should not remain passively present in the system; they must be able to express certain outcomes as plain data and return. The most obvious of such outcomes is the single continuation wherein a subexpression evaluating to another command from the same set will eventually run, and it can emulate more complex patterns by continuing with a call to an environment constant which expresses the more complex outcome in terms of its parameters

View File

@@ -5,9 +5,7 @@ use itertools::Itertools;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use crate::{
ExprTicket, Expression, ExtHostReq, FormattingUnit, HostExtReq, OrcResult, SysId, TStrv,
};
use crate::{ExprTicket, Expression, ExtHostReq, FormattingUnit, HostExtReq, SysId, TStrv};
#[derive(Clone, Coding)]
pub struct AtomData(pub Vec<u8>);
@@ -97,8 +95,8 @@ impl Request for DeserAtom {
/// A request blindly routed to the system that provides an atom.
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(AtomReq, HostExtReq)]
pub struct Fwded(pub Atom, pub TStrv, pub Vec<u8>);
impl Request for Fwded {
pub struct FinalFwded(pub Atom, pub TStrv, pub Vec<u8>);
impl Request for FinalFwded {
type Response = Option<Vec<u8>>;
}
@@ -109,32 +107,6 @@ impl Request for Fwd {
type Response = Option<Vec<u8>>;
}
/// What to do after a command has finished executing
#[derive(Clone, Debug, Coding)]
pub enum NextStep {
/// Add more work. Parallel work is fairly executed in parallel, so different
/// command chains can block on each other. When the command queue is empty,
/// the interpreter may exit without waiting for timers.
Continue {
/// Run these commands immediately. Since timers don't keep the interpreter
/// alive, this should usually be non-empty, but this is not required.
immediate: Vec<Expression>,
/// Schedule these commands after the specified number of milliseconds, if
/// the interpreter had not exited by then.
delayed: Vec<(NonZeroU64, Expression)>,
},
/// Discard the rest of the queue and exit. It is possible to fail the program
/// without raising an error because the convention on most OSes is to
/// separate error reporting from a failure exit.
Exit { success: bool },
}
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(AtomReq, HostExtReq)]
pub struct Command(pub Atom);
impl Request for Command {
type Response = OrcResult<NextStep>;
}
/// Notification that an atom is being dropped because its associated expression
/// isn't referenced anywhere. This should have no effect if the atom's `drop`
/// flag is false.
@@ -166,8 +138,8 @@ impl Request for ExtAtomPrint {
pub enum AtomReq {
CallRef(CallRef),
FinalCall(FinalCall),
Fwded(Fwded),
Command(Command),
FwdedRef(FinalFwded),
FinalFwded(FinalFwded),
AtomPrint(AtomPrint),
SerializeAtom(SerializeAtom),
}
@@ -177,9 +149,9 @@ impl AtomReq {
pub fn get_atom(&self) -> &Atom {
match self {
Self::CallRef(CallRef(a, ..))
| Self::Command(Command(a))
| Self::FinalCall(FinalCall(a, ..))
| Self::Fwded(Fwded(a, ..))
| Self::FwdedRef(FinalFwded(a, ..))
| Self::FinalFwded(FinalFwded(a, ..))
| Self::AtomPrint(AtomPrint(a))
| Self::SerializeAtom(SerializeAtom(a)) => a,
}

View File

@@ -1,18 +1,17 @@
use std::any::{Any, TypeId, type_name};
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::{self, Debug};
use std::future::Future;
use std::io;
use std::marker::PhantomData;
use std::num::{NonZero, NonZeroU32, NonZeroU64};
use std::num::NonZeroU32;
use std::ops::Deref;
use std::pin::Pin;
use std::rc::Rc;
use std::time::Duration;
use dyn_clone::{DynClone, clone_box};
use futures::future::LocalBoxFuture;
use futures::stream::LocalBoxStream;
use futures::{AsyncWrite, FutureExt, StreamExt, stream};
use orchid_api_derive::Coding;
use orchid_api_traits::{Coding, Decode, InHierarchy, Request, UnderRoot, enc_vec};
@@ -20,15 +19,14 @@ 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::api;
use crate::atom_owned::{OwnedAtom, get_obj_store};
use crate::conv::ToExpr;
use crate::entrypoint::request;
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
use crate::gen_expr::{GExpr, IntoGExprStream};
use crate::system::{DynSystemCardExt, cted, sys_id};
use crate::gen_expr::GExpr;
use crate::{
DynSystemCardExt, Expr, ExprData, ExprHandle, ExprKind, OwnedAtom, ToExpr, api, dyn_cted,
get_obj_store, request, sys_id,
};
/// Every atom managed via this system starts with an ID into the type table
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Coding)]
@@ -114,7 +112,7 @@ impl ForeignAtom {
let mut data = &self.atom.data.0[..];
let value = AtomTypeId::decode_slice(&mut data);
if cfg!(debug_assertions) {
let cted = cted();
let cted = dyn_cted();
let own_inst = cted.inst();
let owner_id = self.atom.owner;
let typ = type_name::<A>();
@@ -189,6 +187,10 @@ 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 {
@@ -197,25 +199,53 @@ 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> {
fn handle<'a, 'b: 'a>(
&'a self,
atom: &'a A,
req: Box<dyn ReqReader<'b> + '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> {
fn handle<'a, 'b: 'a>(
&'a self,
a: &'a A,
atom: &'a 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(a, reader.finish().await, req).await.unwrap();
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();
})
}
}
@@ -252,6 +282,20 @@ 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,
@@ -334,75 +378,6 @@ impl<A: AtomicFeatures> Format for TAtom<A> {
pub(crate) struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>);
pub enum Next {
ExitSuccess,
Continue(Continuation),
}
#[derive(Default)]
pub struct Continuation {
immediate: Vec<LocalBoxStream<'static, GExpr>>,
delayed: Vec<(NonZeroU64, LocalBoxStream<'static, GExpr>)>,
}
impl Continuation {
pub fn immediate<T: IntoGExprStream + 'static>(mut self, expr: T) -> Self {
self.immediate.push(Box::pin(expr.into_gexpr_stream()));
self
}
pub fn schedule<T: IntoGExprStream + 'static>(mut self, delay: Duration, expr: T) -> Self {
let delay = delay.as_millis().try_into().unwrap();
let Some(nzdelay) = NonZero::new(delay) else { return self.immediate(expr) };
self.delayed.push((nzdelay, Box::pin(expr.into_gexpr_stream())));
self
}
}
impl From<Continuation> for Next {
fn from(value: Continuation) -> Self { Self::Continue(value) }
}
impl From<Continuation> for CmdResult {
fn from(value: Continuation) -> Self { Ok(Next::Continue(value)) }
}
pub enum CmdError {
Orc(OrcErrv),
FatalError,
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct FatalError;
impl From<FatalError> for CmdError {
fn from(FatalError: FatalError) -> Self { Self::FatalError }
}
impl From<OrcErrv> for CmdError {
fn from(value: OrcErrv) -> Self { Self::Orc(value) }
}
pub(crate) async fn encode_command_result(
result: Result<Next, CmdError>,
) -> api::OrcResult<api::NextStep> {
match result {
Ok(Next::ExitSuccess) => Ok(api::NextStep::Exit { success: true }),
Err(CmdError::FatalError) => Ok(api::NextStep::Exit { success: false }),
Err(CmdError::Orc(errv)) => Err(errv.to_api()),
Ok(Next::Continue(Continuation { immediate, delayed })) => Ok(api::NextStep::Continue {
immediate: (stream::iter(immediate).flatten())
.then(async |x| x.serialize().await)
.collect()
.await,
delayed: stream::iter(delayed.into_iter().map(|(delay, expr)| {
expr.then(move |expr| async move { (delay, expr.serialize().await) })
}))
.flatten()
.collect()
.await,
}),
}
}
pub type CmdResult = Result<Next, CmdError>;
/// A vtable-like type that collects operations defined by an [Atomic] without
/// associating with an instance of that type. This must be registered in
/// [crate::SystemCard]
@@ -420,7 +395,12 @@ pub trait AtomOps: 'static {
key: Sym,
req: Box<dyn ReqReader<'a> + 'a>,
) -> LocalBoxFuture<'a, bool>;
fn command<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult>;
fn handle_req_ref<'a>(
&'a self,
ctx: AtomCtx<'a>,
key: Sym,
req: Box<dyn ReqReader<'a> + 'a>,
) -> LocalBoxFuture<'a, bool>;
fn serialize<'a, 'b: 'a>(
&'a self,
ctx: AtomCtx<'a>,
@@ -504,6 +484,6 @@ pub async fn err_exit_failure() -> OrcErrv {
pub(crate) fn resolve_atom_type(atom: &api::Atom) -> (Box<dyn AtomOps>, AtomTypeId, &[u8]) {
let mut data = &atom.data.0[..];
let atid = AtomTypeId::decode_slice(&mut data);
let atom_record = cted().inst().card().ops_by_atid(atid).expect("Unrecognized atom type ID");
let atom_record = dyn_cted().inst().card().ops_by_atid(atid).expect("Unrecognized atom type ID");
(atom_record, atid, data)
}

View File

@@ -20,15 +20,12 @@ use orchid_api_traits::{Decode, Encode, enc_vec};
use orchid_base::{FmtCtx, FmtCtxImpl, FmtUnit, Format, Sym, log, take_first};
use task_local::task_local;
use crate::atom::{
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
MethodSetBuilder, err_not_callable, err_not_command,
};
use crate::conv::ToExpr;
use crate::expr::Expr;
use crate::gen_expr::{GExpr, bot};
use crate::system::{DynSystemCardExt, cted};
use crate::{CmdError, CmdResult, api};
use crate::{
ATOM_WITHOUT_HANDLE_FINAL_IMPL, 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]
pub struct OwnedVariant;
@@ -42,7 +39,7 @@ impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVari
*id += 1;
api::AtomId(NonZero::new(*id + 1).unwrap())
};
let (typ_id, _) = cted().inst().card().ops::<A>();
let (typ_id, _) = dyn_cted().inst().card().ops::<A>();
let mut data = enc_vec(&typ_id);
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
obj_store.objects.read().await.insert(atom_id, Box::new(self));
@@ -121,6 +118,25 @@ impl<A: OwnedAtom> AtomOps for OwnedAtomOps<A> {
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>,
key: Sym,
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
) -> LocalBoxFuture<'a, bool> {
Box::pin(async move {
let a = AtomReadGuard::new(id.unwrap()).await;
@@ -128,9 +144,6 @@ impl<A: OwnedAtom> AtomOps for OwnedAtomOps<A> {
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), key, req).await
})
}
fn command<'a>(&'a self, AtomCtx(_, id): AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult> {
Box::pin(async move { take_atom(id.unwrap()).await.dyn_command().await })
}
fn drop(&self, AtomCtx(_, id): AtomCtx) -> LocalBoxFuture<'_, ()> {
Box::pin(async move { take_atom(id.unwrap()).await.dyn_free().await })
}
@@ -252,10 +265,6 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
gcl
}
}
/// Called when this is the final value of the program
fn command(self) -> impl Future<Output = CmdResult> {
async move { Err(CmdError::Orc(err_not_command(&self.dyn_print().await).await)) }
}
/// Drop and perform any cleanup. Unlike Rust's [Drop::drop], this is
/// guaranteed to be called
fn free(self) -> impl Future<Output = ()> { async {} }
@@ -294,10 +303,10 @@ 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>;
fn dyn_command(self: Box<Self>) -> LocalBoxFuture<'static, CmdResult>;
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit>;
fn dyn_serialize<'a>(
@@ -307,6 +316,7 @@ 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()
}
@@ -316,7 +326,6 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr> {
async { self.call(arg).await.to_gen().await }.boxed_local()
}
fn dyn_command(self: Box<Self>) -> LocalBoxFuture<'static, CmdResult> { Box::pin(self.command()) }
fn dyn_free(self: Box<Self>) -> LocalBoxFuture<'static, ()> { self.free().boxed_local() }
fn dyn_print(&self) -> LocalBoxFuture<'_, FmtUnit> {
async move { self.print_atom(&FmtCtxImpl::default()).await }.boxed_local()

View File

@@ -3,19 +3,16 @@ use std::future::Future;
use std::pin::Pin;
use async_once_cell::OnceCell;
use futures::AsyncWrite;
use futures::future::LocalBoxFuture;
use futures::{AsyncWrite, FutureExt};
use orchid_api_traits::{Coding, enc_vec};
use orchid_base::{FmtUnit, Sym, log};
use crate::atom::{
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
MethodSetBuilder, err_not_callable, err_not_command,
};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, bot};
use crate::system::{DynSystemCardExt, cted};
use crate::{CmdResult, api};
use crate::{
AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, DynSystemCardExt, Expr,
MethodSet, MethodSetBuilder, api, dyn_cted, err_not_callable,
};
/// Value of [Atomic::Variant] for a type that implements [ThinAtom]
pub struct ThinVariant;
@@ -23,7 +20,7 @@ impl AtomicVariant for ThinVariant {}
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
fn _factory(self) -> AtomFactory {
AtomFactory::new(async move || {
let (id, _) = cted().inst().card().ops::<A>();
let (id, _) = dyn_cted().inst().card().ops::<A>();
let mut buf = enc_vec(&id);
self.encode_vec(&mut buf);
api::LocalAtom { drop: None, data: api::AtomData(buf) }
@@ -63,8 +60,13 @@ impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await
})
}
fn command<'a>(&'a self, AtomCtx(buf, _): AtomCtx<'a>) -> LocalBoxFuture<'a, CmdResult> {
async move { T::decode_slice(&mut &buf[..]).command().await }.boxed_local()
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,
@@ -99,10 +101,6 @@ pub trait ThinAtom: Atomic<Data = Self> + Atomic<Variant = ThinVariant> + Coding
async move { bot(err_not_callable(&self.print().await).await) }
}
#[allow(unused_variables)]
fn command(&self) -> impl Future<Output = CmdResult> {
async move { Err(err_not_command(&self.print().await).await.into()) }
}
#[allow(unused_variables)]
fn print(&self) -> impl Future<Output = FmtUnit> {
async { format!("ThinAtom({})", type_name::<Self>()).into() }
}

View File

@@ -4,9 +4,7 @@ use std::time::Duration;
use futures::future::LocalBoxFuture;
use orchid_base::future_to_vt;
use crate::api;
use crate::entrypoint::ExtensionBuilder;
use crate::ext_port::ExtPort;
use crate::{ExtPort, ExtensionBuilder, api};
pub type ExtCx = api::binary::ExtensionContext;

View File

@@ -0,0 +1,60 @@
use std::borrow::Cow;
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::std_reqs::RunCommand;
use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr};
trait_set! {
pub trait ClonableAsyncFnOnceDyn = FnOnce() -> LocalBoxFuture<'static, Option<GExpr>> + DynClone;
}
pub struct CmdAtom(Box<dyn ClonableAsyncFnOnceDyn>);
impl Clone for CmdAtom {
fn clone(&self) -> Self { Self(dyn_clone::clone_box(&*self.0)) }
}
impl Atomic for CmdAtom {
type Data = ();
type Variant = OwnedVariant;
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<RunCommand>() }
}
impl Supports<RunCommand> for CmdAtom {
async fn handle<'a>(
&self,
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;
match reply {
None => hand.reply(&req, &None).await,
Some(next) => hand.reply(&req, &Some(next.serialize().await)).await,
}
}
}
impl OwnedAtom for CmdAtom {
type Refs = Never;
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),
}
})
})))
}

View File

@@ -2,19 +2,20 @@ use std::future::Future;
use std::pin::Pin;
use dyn_clone::DynClone;
use futures::future::FusedFuture;
use never::Never;
use orchid_base::{Format, OrcErrv, OrcRes, Pos, fmt, is, mk_errv};
use trait_set::trait_set;
use crate::atom::{AtomicFeatures, ForeignAtom, TAtom};
use crate::expr::{Expr, ExprKind};
use crate::gen_expr::{GExpr, bot};
use crate::{AtomicFeatures, Expr, ExprKind, ForeignAtom, TAtom};
/// Attempt to cast a generic Orchid expression reference to a concrete value.
/// Note that this cannot evaluate the expression, and if it is not already
/// evaluated, it will simply fail. Use [crate::ExecHandle::exec] inside
/// [crate::exec] to wait for an expression to be evaluated
/// Values that may be converted from certain specific Orchid expressions
pub trait TryFromExpr: Sized {
/// Attempt to cast a generic Orchid expression reference to a concrete value.
/// Note that this cannot evaluate the expression, and if it is not already
/// evaluated, it will simply fail. Use [crate::ExecHandle::exec] inside
/// [crate::exec] to wait for an expression to be evaluated
fn try_from_expr(expr: Expr) -> impl Future<Output = OrcRes<Self>>;
}
@@ -58,6 +59,9 @@ impl<A: AtomicFeatures> TryFromExpr for TAtom<A> {
/// Values that are convertible to an Orchid expression. This could mean that
/// the value owns an [Expr] or it may involve more complex operations
///
/// [ToExpr] is also implemented for [orchid_base::Sym] where it converts to a
/// reference to the constant by that name
pub trait ToExpr {
/// Inline the value in an expression returned from a function or included in
/// the const tree returned by [crate::System::env]
@@ -69,6 +73,8 @@ pub trait ToExpr {
}
}
/// A wrapper for a future that implements [ToExpr]
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ToExprFuture<F>(pub F);
impl<F: Future<Output: ToExpr>> ToExpr for ToExprFuture<F> {
@@ -78,6 +84,9 @@ impl<F: Future<Output: ToExpr>> ToExpr for ToExprFuture<F> {
self.0.await.to_expr().await
}
}
impl<F: FusedFuture> FusedFuture for ToExprFuture<F> {
fn is_terminated(&self) -> bool { self.0.is_terminated() }
}
impl<F: Future> Future for ToExprFuture<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {

View File

@@ -9,11 +9,8 @@ use futures::{FutureExt, SinkExt, StreamExt};
use never::Never;
use orchid_base::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, lam, new_atom, seq};
use crate::{Atomic, Expr, OwnedAtom, OwnedVariant, ToExpr, TryFromExpr};
enum Command {
Execute(GExpr, Sender<Expr>),
@@ -30,7 +27,7 @@ impl BuilderCoroutine {
pub async fn run(self) -> GExpr {
let cmd = self.0.receiver.lock().await.next().await;
match cmd {
None => panic!("Before the stream ends, we should have gotten a Halt"),
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)
@@ -40,7 +37,7 @@ impl BuilderCoroutine {
}
#[derive(Clone)]
pub struct Replier {
pub(crate) struct Replier {
reply: Sender<Expr>,
builder: BuilderCoroutine,
}
@@ -52,12 +49,14 @@ impl OwnedAtom for Replier {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call(mut self, arg: Expr) -> impl ToExpr {
self.reply.send(arg).await.expect("What the heck");
self.reply.send(arg).await.expect("Resolution request dropped after sending");
std::mem::drop(self.reply);
self.builder.run().await
}
}
/// A long-lived async context that can yield to the executor. The expression
/// representing an in-progress exec block is not serializable.
pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
let (cmd_snd, cmd_recv) = channel(0);
let halt =
@@ -70,8 +69,11 @@ pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R +
static WEIRD_DROP_ERR: &str = "Coroutine dropped while we are being polled somehow";
/// The handle an [exec] callback uses to yield to the executor
pub struct ExecHandle<'a>(Sender<Command>, PhantomData<&'a ()>);
impl ExecHandle<'_> {
/// Yield to the executor by resolving to an expression that normalizes the
/// value and then calls the continuation of the body with the result.
pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
let (reply_snd, mut reply_recv) = channel(1);
self.0.send(Command::Execute(val.to_gen().await, reply_snd)).await.expect(WEIRD_DROP_ERR);

View File

@@ -22,21 +22,16 @@ use orchid_base::{
use substack::Substack;
use task_local::task_local;
use crate::atom::{AtomCtx, AtomTypeId, resolve_atom_type};
use crate::atom_owned::{take_atom, with_obj_store};
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
use crate::ext_port::ExtPort;
use crate::func_atom::with_funs_ctx;
use crate::interner::new_interner;
use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable};
use crate::logger::LoggerImpl;
use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api, with_parsed_const_ctx};
use crate::reflection::with_refl_roots;
use crate::system::{DynSystemCardExt, SysCtx, cted, with_sys};
use crate::system_ctor::{CtedObj, DynSystemCtor, SystemCtor};
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
use crate::trivial_req::TrivialReqCycle;
use crate::{api, encode_command_result};
use crate::{
AtomCtx, AtomTypeId, BorrowedExprStore, CtedObj, DynSystemCardExt, DynSystemCtor, Expr,
ExprHandle, ExtPort, LexContext, PTokTree, ParsCtx, SysCtx, SystemCtor, api, dyn_cted,
ekey_cascade, ekey_not_applicable, get_const, linev_into_api, resolve_atom_type, take_atom,
with_funs_ctx, with_obj_store, with_parsed_const_ctx, with_refl_roots, with_sys,
};
task_local::task_local! {
static CLIENT: Rc<dyn Client>;
@@ -44,21 +39,28 @@ task_local::task_local! {
}
fn get_client() -> Rc<dyn Client> { CLIENT.get() }
/// Do not expect any more requests or notifications, exit once all pending
/// requests settle
pub async fn exit() {
let cx = CTX.get().borrow_mut().take();
cx.unwrap().exit().await.unwrap()
}
/// Sent the client used for global [request] and [notify] functions within the
/// Set the client used for global [request] and [notify] functions within the
/// runtime of this future
pub async fn with_comm<F: Future>(c: Rc<dyn Client>, ctx: CommCtx, fut: F) -> F::Output {
CLIENT.scope(c, CTX.scope(Rc::new(RefCell::new(Some(ctx))), fut)).await
}
task_local! {
pub static MUTE_REPLY: ();
static MUTE_REPLY: ();
}
/// Silence replies within this block even if the `msg` log channel is active to
/// prevent excessive log noise.
pub async fn mute_reply<F: Future>(f: F) -> F::Output { MUTE_REPLY.scope((), f).await }
/// 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 response = get_client().request(t).await.unwrap();
@@ -73,7 +75,7 @@ pub async fn notify<T: UnderRoot<Root = api::ExtHostNotif>>(t: T) {
get_client().notify(t).await.unwrap()
}
pub struct SystemRecord {
struct SystemRecord {
cted: CtedObj,
}
@@ -88,6 +90,7 @@ async fn with_sys_record<F: Future>(id: api::SysId, fut: F) -> F::Output {
with_sys(SysCtx(id, cted), fut).await
}
/// Context that can be attached to a [Future] using [task_local]
pub trait ContextModifier: 'static {
fn apply<'a>(self: Box<Self>, fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()>;
}
@@ -108,12 +111,22 @@ task_local! {
Rc<dyn Fn(Duration, LocalBoxFuture<'static, Box<dyn Any>>) -> Box<dyn DynTaskHandle> + 'static>
}
/// Handle for a task that is not associated with a particular pending request
/// or past notification
pub struct TaskHandle<T>(Box<dyn DynTaskHandle>, PhantomData<T>);
impl<T: 'static> TaskHandle<T> {
/// Immediately stop working on the task. Unlike in Tokio's abort, this is a
/// guarantee
pub fn abort(self) { self.0.abort(); }
/// Stop working on the task and return the nested future. The distinction
/// between this and waiting until the task is complete without reparenting it
/// is significant for the purpose of [task_local] context
pub async fn join(self) -> T { *self.0.join().await.downcast().unwrap() }
}
/// Spawn a future that is not associated with a pending request or a past
/// notification. Pending tasks are cancelled and dropped when the extension
/// exits
pub fn spawn<F: Future<Output: 'static> + 'static>(delay: Duration, f: F) -> TaskHandle<F::Output> {
SPAWN.with(|spawn| {
TaskHandle(spawn(delay, Box::pin(async { Box::new(f.await) as Box<dyn Any> })), PhantomData)
@@ -125,24 +138,37 @@ impl DynTaskHandle for Handle<Box<dyn Any>> {
fn join(self: Box<Self>) -> LocalBoxFuture<'static, Box<dyn Any>> { Box::pin(Self::join(*self)) }
}
/// A new Orchid extension as specified in loaders. An extension is a unit of
/// distribution and its name serves for debugging purposes primarily. In
/// contrast, [SystemCtor] is a unit of features of which [ExtensionBuilder] may
/// contain multiple
pub struct ExtensionBuilder {
pub name: &'static str,
pub systems: Vec<Box<dyn DynSystemCtor>>,
pub context: Vec<Box<dyn ContextModifier>>,
}
impl ExtensionBuilder {
/// Create a new extension
pub fn new(name: &'static str) -> Self { Self { name, systems: Vec::new(), context: Vec::new() } }
/// Add a system to the extension
pub fn system(mut self, ctor: impl SystemCtor) -> Self {
self.systems.push(Box::new(ctor) as Box<_>);
self
}
/// Add some [task_local] state to the extension. Bear in mind that distinct
/// [crate::System] instances should not visibly affect each other
pub fn add_context(&mut self, fun: impl ContextModifier) {
self.context.push(Box::new(fun) as Box<_>);
}
/// Builder form of [Self::add_context]
pub fn context(mut self, fun: impl ContextModifier) -> Self {
self.add_context(fun);
self
}
/// Start the extension on a message channel, blocking the task until the peer
/// on the other side drops the extension. Extension authors would typically
/// pass the prepared builder into some function that is responsible for
/// managing the [ExtPort]
pub async fn run(mut self, mut ctx: ExtPort) {
self.add_context(with_funs_ctx);
self.add_context(with_parsed_const_ctx);
@@ -246,7 +272,7 @@ impl ExtensionBuilder {
with_sys_record(sys_id, async {
let mut reply = Vec::new();
let req = TrivialReqCycle { req: &payload, rep: &mut reply };
let _ = cted().inst().dyn_request(Box::new(req)).await;
let _ = dyn_cted().inst().dyn_request(Box::new(req)).await;
handle.reply(fwd_tok, &reply).await
})
.await
@@ -260,7 +286,7 @@ impl ExtensionBuilder {
let trigger_char = tail.chars().next().unwrap();
let ekey_na = ekey_not_applicable().await;
let ekey_cascade = ekey_cascade().await;
let lexers = cted().inst().dyn_lexers();
let lexers = dyn_cted().inst().dyn_lexers();
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char))
{
let ctx = LexContext::new(&expr_store, &text, id, pos, src.clone());
@@ -292,7 +318,7 @@ impl ExtensionBuilder {
let req = Witness::of(&pline);
let api::ParseLine { module, src, exported, comments, sys, line, idx } = pline;
with_sys_record(sys, async {
let parsers = cted().inst().dyn_parsers();
let parsers = dyn_cted().inst().dyn_parsers();
let src = Sym::from_api(src).await;
let comments =
join_all(comments.iter().map(|c| Comment::from_api(c, src.clone()))).await;
@@ -339,14 +365,22 @@ impl ExtensionBuilder {
},
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
handle.reply(print, &nfo.print(actx).await.to_api()).await,
api::AtomReq::Fwded(fwded) => {
let api::Fwded(_, key, payload) = &fwded;
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;
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_ref(actx, key, Box::new(req)).await;
handle.reply(fwded, &some.then_some(reply)).await
},
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
let expr_store = BorrowedExprStore::new();
let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
@@ -365,8 +399,6 @@ impl ExtensionBuilder {
expr_store.dispose().await;
handle.reply(call, &api_expr).await
},
api::AtomReq::Command(cmd @ api::Command(_)) =>
handle.reply(cmd, &encode_command_result(nfo.command(actx).await).await).await,
}
})
.await
@@ -380,7 +412,7 @@ impl ExtensionBuilder {
.map(|tk| Expr::from_handle(ExprHandle::deserialize(*tk)))
.collect_vec();
let id = AtomTypeId::decode_slice(read);
let nfo = (cted().inst().card().ops_by_atid(id))
let nfo = (dyn_cted().inst().card().ops_by_atid(id))
.expect("Deserializing atom with invalid ID");
handle.reply(&deser, &nfo.deserialize(read, &refs).await).await
})

View File

@@ -6,23 +6,28 @@ use std::thread::panicking;
use async_once_cell::OnceCell;
use derive_destructure::destructure;
use futures::future::join_all;
use hashbrown::HashSet;
use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash};
use crate::api;
use crate::atom::ForeignAtom;
use crate::entrypoint::{notify, request};
use crate::gen_expr::{GExpr, GExprKind};
use crate::system::sys_id;
use crate::{ForeignAtom, api, notify, request, sys_id};
/// Handle for a lifetime associated with an [ExprHandle], such as a function
/// call. Can be passed into [ExprHandle::borrowed] as an optimization over
/// [ExprHandle::from_ticket]
///
/// # Panics
///
/// The [Drop] of this type panics by default unless the stack is already
/// unwinding. You need to make sure you dispose of it by calling
/// [Self::dispose].
pub struct BorrowedExprStore(RefCell<Option<HashSet<Rc<ExprHandle>>>>);
impl BorrowedExprStore {
pub(crate) fn new() -> Self { Self(RefCell::new(Some(HashSet::new()))) }
pub async fn dispose(self) {
let elements = self.0.borrow_mut().take().unwrap();
for handle in elements {
handle.on_borrow_expire().await
}
let set = self.0.borrow_mut().take().expect("Double dispose of BorrowedExprStore!");
join_all(set.into_iter().map(ExprHandle::on_borrow_expire)).await;
}
}
impl Drop for BorrowedExprStore {
@@ -33,6 +38,10 @@ impl Drop for BorrowedExprStore {
}
}
/// A RAII wrapper over an [api::ExprTicket]. Extension authors usually use
/// [Expr] for all purposes as this type does not deal with the details of the
/// expression associated with the ticket, it merely ensures that [api::Acquire]
/// and [api::Release] are sent at appropriate times.
#[derive(destructure, PartialEq, Eq, Hash)]
pub struct ExprHandle(api::ExprTicket);
impl ExprHandle {
@@ -72,7 +81,7 @@ impl ExprHandle {
/// [ExprHandle::borrowed] or [ExprHandle::from_ticket]
pub fn ticket(&self) -> api::ExprTicket { self.0 }
async fn send_acq(&self) { notify(api::Acquire(sys_id(), self.0)).await }
/// If this is the last one reference, do nothing, otherwise send an Acquire
/// If this is the last reference, do nothing, otherwise send an Acquire
pub async fn on_borrow_expire(self: Rc<Self>) { self.serialize().await; }
/// Drop the handle and get the ticket without a release notification.
/// Use this with messages that imply ownership transfer. This function is
@@ -97,13 +106,20 @@ impl Drop for ExprHandle {
}
}
/// A smart object that keeps an expression alive in the interpreter until all
/// references are dropped and provides information about that expression. These
/// can be stored in any pattern, but care must be taken as adding new ones into
/// a structure that is already visible to the interpreter can easily cause a
/// memory leak.
#[derive(Clone, Debug, destructure)]
pub struct Expr {
handle: Rc<ExprHandle>,
data: Rc<OnceCell<ExprData>>,
}
impl Expr {
/// Wrap a handle in order to retrieve details about it
pub fn from_handle(handle: Rc<ExprHandle>) -> Self { Self { handle, data: Rc::default() } }
/// Wrap a handle the details of which are already known
pub fn from_data(handle: Rc<ExprHandle>, d: ExprData) -> Self {
Self { handle, data: Rc::new(OnceCell::from(d)) }
}
@@ -113,6 +129,8 @@ impl Expr {
pub async fn deserialize(tk: api::ExprTicket) -> Self {
Self::from_handle(ExprHandle::deserialize(tk))
}
/// Fetch the details of this expression via [api::Inspect] if not already
/// known, and return them
pub async fn data(&self) -> &ExprData {
(self.data.get_or_init(async {
let details = request(api::Inspect { target: self.handle.ticket() }).await;
@@ -127,15 +145,19 @@ impl Expr {
}))
.await
}
/// Attempt to downcast this to a [ForeignAtom]
pub async fn atom(self) -> Result<ForeignAtom, Self> {
match self.data().await {
ExprData { kind: ExprKind::Atom(atom), .. } => Ok(atom.clone()),
_ => Err(self),
}
}
/// Obtain code location info associated with this expression for logging.
pub async fn pos(&self) -> Pos { self.data().await.pos.clone() }
/// Clone out the handle for this expression
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()) }
}
@@ -161,15 +183,23 @@ impl Hash for Expr {
fn hash<H: Hasher>(&self, state: &mut H) { self.handle.hash(state); }
}
/// Information about an expression
#[derive(Clone, Debug)]
pub struct ExprData {
/// Source code location data associated with the expression for debug logging
pub pos: Pos,
/// Limited information on the value available to extensions
pub kind: ExprKind,
}
/// All that is visible about a runtime value to extensions. This
/// information is limited for the sake of extensibility
#[derive(Clone, Debug)]
pub enum ExprKind {
/// An atom, local or foreign
Atom(ForeignAtom),
/// A runtime error
Bottom(OrcErrv),
/// Some other value, possibly normalizes to one of the above
Opaque,
}

View File

@@ -15,14 +15,10 @@ use orchid_base::{FmtCtx, FmtUnit, OrcRes, Pos, Sym, clone};
use task_local::task_local;
use trait_set::trait_set;
use crate::api;
use crate::atom::Atomic;
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr;
use crate::coroutine_exec::{ExecHandle, exec};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, new_atom};
use crate::system::sys_id;
use crate::{
Atomic, DeserializeCtx, ExecHandle, Expr, OwnedAtom, OwnedVariant, ToExpr, api, exec, sys_id,
};
trait_set! {
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
@@ -32,6 +28,7 @@ task_local! {
static ARGV: Vec<Expr>;
}
/// Wihtin an [ExprFunc]'s body, access a raw argument by index
pub fn get_arg(idx: usize) -> Expr {
ARGV
.try_with(|argv| {
@@ -41,16 +38,26 @@ pub fn get_arg(idx: usize) -> Expr {
.expect("get_arg called outside ExprFunc")
}
/// Find the number of arguments accepted by this [ExprFunc]
pub fn get_argc() -> usize {
ARGV.try_with(|argv| argv.len()).expect("get_arg called outside ExprFunc")
}
/// Retrieve the code locations associated with specific arguments by index.
/// This is intended to be the last argument to [orchid_base::mk_errv]
pub async fn get_arg_posv(idxes: impl IntoIterator<Item = usize>) -> impl Iterator<Item = Pos> {
let args = (ARGV.try_with(|argv| idxes.into_iter().map(|i| &argv[i]).cloned().collect_vec()))
.expect("get_arg_posv called outside ExprFunc");
join_all(args.iter().map(|expr| expr.pos())).await.into_iter()
}
/// A Rust lambda that can be placed into an Orchid atom. This requires that
///
/// - the lambda is [Clone] and `'static`
/// - All of its arguments are [crate::TryFromExpr]
/// - Its return value is [crate::ToExpr]
/// - For the sake of compilation time, at present the trait is only implemented
/// for up to 6 arguments
pub trait ExprFunc<I, O>: Clone + 'static {
fn argtyps() -> &'static [TypeId];
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
@@ -152,13 +159,14 @@ impl OwnedAtom for Fun {
/// An Atom representing a partially applied native lambda. These are not
/// serializable.
///
/// See [Fun] for the serializable variant
/// See [crate::fun] for the serializable variant
#[derive(Clone)]
pub struct Lambda {
args: Vec<Expr>,
record: FunRecord,
}
impl Lambda {
/// Embed a lambda in an Orchid expression
pub fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
Self { args: vec![], record: process_args(f) }
}

View File

@@ -7,16 +7,15 @@ use orchid_base::{
FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache,
};
use crate::api;
use crate::atom::{AtomFactory, AtomicFeatures};
use crate::conv::{ToExpr, ToExprFuture};
use crate::entrypoint::request;
use crate::expr::Expr;
use crate::system::sys_id;
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, Debug)]
pub struct GExpr {
/// AST node type
pub kind: GExprKind,
/// Code location associated with the expression for debugging purposes
pub pos: Pos,
}
impl GExpr {
@@ -38,7 +37,10 @@ impl GExpr {
}
}
}
/// Reassign location information. The typical default is [Pos::Inherit]
pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } }
/// 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
}
@@ -49,20 +51,36 @@ impl Format for GExpr {
}
}
/// AST nodes recognized by the interpreter
#[derive(Clone, Debug)]
pub enum GExprKind {
/// Function call
Call(Box<GExpr>, Box<GExpr>),
/// Lambda expression. Argument numbers are matched when equal
Lambda(u64, Box<GExpr>),
/// Slot for a lambda argument
Arg(u64),
/// The second expression is only valid after the first one had already been
/// fully normalized. The main use case is the pattern `Lambda(0, Seq(0,
/// Call(foo, 0)))` where foo is an atom that attempts to downcast its
/// argument.
Seq(Box<GExpr>, Box<GExpr>),
/// A reference to a constant from the shared constant tree. It is best to
/// mark the system that provides named constants as a dependency, but this is
/// not required
Const(Sym),
/// A newly created atom. Since at this point the atom needs to be registered
/// inside the extension but doesn't yet have an [api::ExprTicket], atoms need
/// their own [api::Atom::drop] if they have an identity
#[allow(private_interfaces)]
NewAtom(AtomFactory),
/// An expression previously registered or coming from outside the extension
Slot(Expr),
/// A runtime error
Bottom(OrcErrv),
}
impl GExprKind {
pub async fn serialize(self) -> api::ExpressionKind {
async fn serialize(self) -> api::ExpressionKind {
match_mapping!(self, Self => api::ExpressionKind {
Call(
f => Box::new(f.serialize().await),
@@ -117,6 +135,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())) }
/// An expression which is only valid if a number of dependencies had already
/// been normalized
pub fn seq(
deps: impl IntoGExprStream,
val: impl ToExpr,
@@ -135,17 +155,24 @@ 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)
}
/// 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))) })
}
/// one or more items that are convertible to expressions. In practice, a
/// [ToExpr], [Vec<GExpr>], or a tuple of types that all implement [ToExpr]. For
/// compilation performance, the tuple's arity may not be more than 6
pub trait IntoGExprStream {
/// Convert each item to an expression and return them
fn into_gexpr_stream(self) -> impl Stream<Item = GExpr>;
}
impl<T: ToExpr> IntoGExprStream for T {
@@ -184,6 +211,7 @@ mod tuple_impls {
tuple_impl!(A B C D E F);
}
/// Call a (curried) function
pub fn call(
f: impl ToExpr,
argv: impl IntoGExprStream,
@@ -195,6 +223,7 @@ pub fn call(
})
}
/// Call a function on a dynamic number of arguments
pub fn call_v(
f: impl ToExpr,
argv: impl IntoIterator<Item: ToExpr>,
@@ -208,6 +237,7 @@ pub fn call_v(
})
}
/// A runtime error
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}

View File

@@ -5,8 +5,7 @@ use itertools::Itertools;
use orchid_base::local_interner::{Int, StrBranch, StrvBranch};
use orchid_base::{IStr, IStrv, InternerSrv};
use crate::api;
use crate::entrypoint::{MUTE_REPLY, request};
use crate::{api, mute_reply, request};
#[derive(Default)]
struct ExtInterner {
@@ -17,9 +16,8 @@ impl InternerSrv for ExtInterner {
fn is<'a>(&'a self, v: &'a str) -> LocalBoxFuture<'a, IStr> {
match self.str.i(v) {
Ok(i) => Box::pin(ready(i)),
Err(e) => Box::pin(async {
e.set_if_empty(MUTE_REPLY.scope((), request(api::InternStr(v.to_owned()))).await)
}),
Err(e) =>
Box::pin(async { e.set_if_empty(mute_reply(request(api::InternStr(v.to_owned()))).await) }),
}
}
fn es(&self, t: api::TStr) -> LocalBoxFuture<'_, IStr> {
@@ -47,4 +45,4 @@ impl InternerSrv for ExtInterner {
}
}
pub fn new_interner() -> Rc<dyn InternerSrv> { Rc::<ExtInterner>::default() }
pub(crate) fn new_interner() -> Rc<dyn InternerSrv> { Rc::<ExtInterner>::default() }

View File

@@ -7,27 +7,30 @@ use futures::FutureExt;
use futures::future::LocalBoxFuture;
use orchid_base::{IStr, OrcErrv, OrcRes, Pos, SrcRange, Sym, is, mk_errv};
use crate::api;
use crate::entrypoint::request;
use crate::expr::BorrowedExprStore;
use crate::parser::PTokTree;
use crate::tree::GenTokTree;
use crate::{BorrowedExprStore, PTokTree, api, request};
pub async fn ekey_cascade() -> IStr { is("An error cascading from a recursive call").await }
pub async fn ekey_not_applicable() -> IStr {
pub(crate) async fn ekey_cascade() -> IStr { is("An error cascading from a recursive call").await }
pub(crate) async fn ekey_not_applicable() -> IStr {
is("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
}
const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\
it should not be emitted by the extension.";
pub async fn err_cascade() -> OrcErrv {
pub(crate) async fn err_cascade() -> OrcErrv {
mk_errv(ekey_cascade().await, MSG_INTERNAL_ERROR, [Pos::None])
}
/// Return this error if your lexer can determine that it is not applicable to
/// this piece of syntax. This error will not be raised if another lexer
/// matches, or if the piece of matched syntax is found to be valid until
/// runtime
pub async fn err_not_applicable() -> OrcErrv {
mk_errv(ekey_not_applicable().await, MSG_INTERNAL_ERROR, [Pos::None])
}
/// Object passed to lexers for recursion and position-related convenience
/// methods
pub struct LexContext<'a> {
pub(crate) exprs: &'a BorrowedExprStore,
pub text: &'a IStr,
@@ -36,7 +39,7 @@ pub struct LexContext<'a> {
pub(crate) src: Sym,
}
impl<'a> LexContext<'a> {
pub fn new(
pub(crate) fn new(
exprs: &'a BorrowedExprStore,
text: &'a IStr,
id: api::ParsId,
@@ -45,7 +48,11 @@ impl<'a> LexContext<'a> {
) -> Self {
Self { exprs, id, pos, src, text }
}
/// The logical path of the source file, also the path of the file's root
/// module
pub fn src(&self) -> &Sym { &self.src }
/// Lex an interpolated expression of some kind
///
/// This function returns [PTokTree] because it can never return
/// [orchid_base::Token::NewExpr]. You can use
/// [crate::parser::p_tree2gen] to convert this to [crate::tree::GenTokTree]
@@ -58,17 +65,23 @@ impl<'a> LexContext<'a> {
let tree = PTokTree::from_api(lx.tree, &mut { self.exprs }, &mut (), &self.src).await;
Ok((&self.text[lx.pos as usize..], tree))
}
/// Find the index of a cursor given the remaining, not-yet-consumed text
pub fn pos(&self, tail: &'a str) -> u32 { (self.text.len() - tail.len()) as u32 }
/// Convenience method to find the source position of a token given the text
/// it was found in and the text after it was parsed.
pub fn pos_tt(&self, tail_with: &'a str, tail_without: &'a str) -> SrcRange {
SrcRange::new(self.pos(tail_with)..self.pos(tail_without), &self.src)
}
/// Convenience method to find the source position of a token given its length
/// and the remaining text afterwards. The length can be any number type but
/// must convert to a u32 without errors
pub fn pos_lt(&self, len: impl TryInto<u32, Error: fmt::Debug>, tail: &'a str) -> SrcRange {
SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src)
}
}
/// One or more tokens returned by the parser. In practice, [GenTokTree],
/// `Vec<GenTokTree>`, or `[GenTokTree; usize]`
pub trait LexedData {
fn into_vec(self) -> Vec<GenTokTree>;
}
@@ -82,16 +95,26 @@ impl<const N: usize> LexedData for [GenTokTree; N] {
fn into_vec(self) -> Vec<GenTokTree> { self.to_vec() }
}
/// A lexer plugin to extend the syntax of Orchid
pub trait Lexer: Debug + Send + Sync + Sized + Default + 'static {
/// As an optimization, your lexer will only receive snippets that start with
/// a character included in one of these ranges. If you have a multi-character
/// discriminator, include all possible starting chars in this and return
/// [err_not_applicable] if the entire discriminator was not found
const CHAR_FILTER: &'static [RangeInclusive<char>];
/// Attempt to lex some custom syntax from the start of the tail string.
/// Return the remaining text and the lexed tokens.
fn lex<'a>(
tail: &'a str,
lctx: &'a LexContext<'a>,
) -> impl Future<Output = OrcRes<(&'a str, impl LexedData)>>;
}
/// Type-erased [Lexer]
pub trait DynLexer: Debug + Send + Sync + 'static {
/// Type-erased [Lexer::CHAR_FILTER]
fn char_filter(&self) -> &'static [RangeInclusive<char>];
/// Type-erased [Lexer::lex]
fn lex<'a>(
&self,
tail: &'a str,
@@ -110,4 +133,6 @@ impl<T: Lexer> DynLexer for T {
}
}
/// Type-erased instance of a lexer that is returned by
/// [crate::System::lexers]
pub type LexerObj = &'static dyn DynLexer;

View File

@@ -3,27 +3,44 @@ use orchid_api as api;
mod atom;
pub use atom::*;
mod cmd_atom;
pub use cmd_atom::*;
mod atom_owned;
pub use atom_owned::*;
mod atom_thin;
pub use atom_thin::*;
pub mod binary;
pub mod conv;
pub mod coroutine_exec;
pub mod entrypoint;
pub mod expr;
pub mod ext_port;
pub mod func_atom;
mod conv;
pub use conv::*;
mod coroutine_exec;
pub use coroutine_exec::*;
mod entrypoint;
pub use entrypoint::*;
mod expr;
pub use expr::*;
mod ext_port;
pub use ext_port::*;
mod func_atom;
pub use func_atom::*;
pub mod gen_expr;
pub mod interner;
pub mod lexer;
pub mod logger;
pub mod other_system;
pub mod parser;
pub mod reflection;
pub mod stream_reqs;
pub mod system;
pub mod system_ctor;
pub mod tokio;
mod interner;
mod lexer;
pub use lexer::*;
mod logger;
mod other_system;
pub use other_system::*;
mod parser;
pub use parser::*;
mod reflection;
pub use reflection::*;
pub mod std_reqs;
mod system;
pub use system::*;
mod system_ctor;
pub use system_ctor::*;
mod system_card;
pub use system_card::*;
mod tokio;
pub use tokio::*;
pub mod tree;
mod trivial_req;

View File

@@ -7,10 +7,9 @@ use futures::future::LocalBoxFuture;
use hashbrown::HashMap;
use orchid_base::{LogWriter, Logger, is};
use crate::api;
use crate::entrypoint::notify;
use crate::{api, notify};
pub struct LogWriterImpl {
pub(crate) struct LogWriterImpl {
category: String,
strat: api::LogStrategy,
}
@@ -33,7 +32,7 @@ impl LogWriter for LogWriterImpl {
}
#[derive(Clone)]
pub struct LoggerImpl {
pub(crate) struct LoggerImpl {
default: Option<api::LogStrategy>,
routing: HashMap<String, api::LogStrategy>,
}

View File

@@ -1,25 +1,52 @@
use crate::api;
use crate::system::{DynSystemCard, SystemCard};
use std::any::TypeId;
use orchid_api_traits::{Decode, Encode, Request};
use crate::system::{dyn_cted, sys_id};
use crate::{DynSystemCard, SystemCard, api, request};
/// A reference to an instance of an alternate system, passed to
/// [crate::SystemCtor::inst] for dependencies. It can be accepted by functions
/// that call [sys_req] to prove a dependency relationship.
#[derive(Debug)]
pub struct SystemHandle<C: SystemCard> {
pub(crate) card: C,
pub(crate) _card: C,
pub(crate) id: api::SysId,
}
impl<C: SystemCard> SystemHandle<C> {
pub(crate) fn new(id: api::SysId) -> Self { Self { card: C::default(), id } }
pub(crate) fn new(id: api::SysId) -> Self { Self { _card: C::default(), id } }
pub fn id(&self) -> api::SysId { self.id }
}
impl<C: SystemCard> Clone for SystemHandle<C> {
fn clone(&self) -> Self { Self::new(self.id) }
}
/// Type-erased system handle, not used for anything at the moment
pub trait DynSystemHandle {
fn id(&self) -> api::SysId;
fn get_card(&self) -> &dyn DynSystemCard;
fn get_card(&self) -> Box<dyn DynSystemCard>;
}
impl<C: SystemCard> DynSystemHandle for SystemHandle<C> {
fn id(&self) -> api::SysId { self.id }
fn get_card(&self) -> &dyn DynSystemCard { &self.card }
fn get_card(&self) -> Box<dyn DynSystemCard> { Box::new(C::default()) }
}
/// Make a global request to a system that supports this request type. The
/// target system must either be the system in which this function is called, or
/// one of its direct dependencies.
pub async fn sys_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
let mut msg = Vec::new();
req.into().encode_vec(&mut msg);
let cted = dyn_cted();
let own_inst = cted.inst();
let owner = if own_inst.card().type_id() == TypeId::of::<Sys>() {
sys_id()
} else {
(cted.deps().find(|s| s.get_card().type_id() == TypeId::of::<Sys>()))
.expect("System not in dependency array yet we have a handle somehow")
.id()
};
let reply = request(api::SysFwd(owner, msg)).await;
Req::Response::decode(std::pin::pin!(&reply[..])).await.unwrap()
}

View File

@@ -14,18 +14,23 @@ use orchid_base::{
};
use task_local::task_local;
use crate::api;
use crate::conv::ToExpr;
use crate::entrypoint::request;
use crate::expr::Expr;
use crate::gen_expr::GExpr;
use crate::system::sys_id;
use crate::tree::{GenTok, GenTokTree};
use crate::{Expr, ToExpr, api, request, sys_id};
/// [PTokTree] without [orchid_base::Pos] metadata
pub type PTok = Token<Expr, Never>;
/// [TokTree] received by [Parser], or returned from recursive [crate::Lexer]
/// call. It is different from [GenTok] because it cannot be [Token::NewExpr].
/// Convert with [p_tok2gen], [p_tree2gen], [p_v2gen]
pub type PTokTree = TokTree<Expr, Never>;
/// Wrapper [Snippet] around a slice of [PTokTree] with a fallback item for
/// [orchid_base::Pos] metadata
pub type PSnippet<'a> = Snippet<'a, Expr, Never>;
/// Convert ingress syntax tokens into egress for interpolation into the output
///
/// See also [p_tree2gen], [p_v2gen]
pub fn p_tok2gen(tok: PTok) -> GenTok {
match_mapping!(tok, PTok => GenTok {
Comment(s), Name(n), BR, Handle(ex), Bottom(err),
@@ -36,15 +41,33 @@ pub fn p_tok2gen(tok: PTok) -> GenTok {
PTok::NewExpr(never) => match never {}
})
}
/// Convert ingress syntax tokens into egress for interpolation into the output
///
/// See also [p_tok2gen], [p_v2gen]
pub fn p_tree2gen(tree: PTokTree) -> GenTokTree {
TokTree { tok: p_tok2gen(tree.tok), sr: tree.sr }
}
/// Convert ingress syntax tokens into egress for interpolation into the output
///
/// See also [p_tok2gen], [p_tree2gen]
pub fn p_v2gen(v: impl IntoIterator<Item = PTokTree>) -> Vec<GenTokTree> {
v.into_iter().map(p_tree2gen).collect_vec()
}
/// A parser extension for the stage of parsing that converts source lines into
/// a module tree. These are invoked by usercode intentionally through a custom
/// keyword at the start of the line
///
/// Unlike [crate::Lexer]'s [crate::err_not_applicable], these don't have a
/// mechanism to reject work. Each keyword must refer to one and exactly one
/// parser
pub trait Parser: Send + Sync + Sized + Default + 'static {
/// The keyword at the head of the line that invokes this parser
const LINE_HEAD: &'static str;
/// Parsing work. If the line head is preceded by the keyword "export", the
/// 2nd parameter is true. Both the line head and the optional export keyword
/// are trimmed off the 4th parameter. The parser should forward all comments
/// to the generated lines
fn parse<'a>(
ctx: ParsCtx<'a>,
exported: bool,
@@ -53,8 +76,11 @@ pub trait Parser: Send + Sync + Sized + Default + 'static {
) -> impl Future<Output = OrcRes<Vec<ParsedLine>>> + 'a;
}
/// Type-erased [Parser]
pub trait DynParser: Send + Sync + 'static {
/// Type-erased [Parser::LINE_HEAD]
fn line_head(&self) -> &'static str;
/// Type-erased [Parser::parse]
fn parse<'a>(
&self,
ctx: ParsCtx<'a>,
@@ -77,14 +103,18 @@ impl<T: Parser> DynParser for T {
}
}
/// Trait-object of [Parser]
pub type ParserObj = &'static dyn DynParser;
/// Information about the parsing context
pub struct ParsCtx<'a> {
_parse: PhantomData<&'a mut ()>,
module: Sym,
}
impl<'a> ParsCtx<'a> {
pub(crate) fn new(module: Sym) -> Self { Self { _parse: PhantomData, module } }
/// The full path of the module that contains the line the parser was invoked
/// with
pub fn module(&self) -> Sym { self.module.clone() }
}
@@ -100,12 +130,19 @@ pub(crate) fn with_parsed_const_ctx<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBox
Box::pin(CONST_TBL.scope(RefCell::default(), NEXT_CONST_ID.scope(id_cell, fut)))
}
/// A logical subtree returned from a parser
pub struct ParsedLine {
/// Source location associated with this item
pub sr: SrcRange,
/// Comments placed above this line
pub comments: Vec<Comment>,
/// Possible nodes in the logical tree
pub kind: ParsedLineKind,
}
impl ParsedLine {
/// Define a constant. The callback is only called later if the constant is
/// referenced, and it can call [crate::refl] to base its value on the module
/// tree or use its argument for context-specific queries
pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>(
sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>,
@@ -118,6 +155,12 @@ impl ParsedLine {
let comments = comments.into_iter().cloned().collect();
ParsedLine { comments, sr: sr.clone(), kind }
}
/// Define a module containing additional items. `use_prelude` determines
/// whether the globally visible imports specified in the preludes of
/// extensions will be added to this module. In practice, it should typically
/// be enabled if you are interpolating any user-expressions or user-lines
/// into the module and disabled if your custom line parses all input and does
/// not interact with other plugins and macros
pub fn module<'a>(
sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>,
@@ -131,7 +174,7 @@ impl ParsedLine {
let comments = comments.into_iter().cloned().collect();
ParsedLine { comments, sr: sr.clone(), kind: line_kind }
}
pub async fn into_api(self) -> api::ParsedLine {
pub(crate) async fn into_api(self) -> api::ParsedLine {
api::ParsedLine {
comments: self.comments.into_iter().map(|c| c.to_api()).collect(),
source_range: self.sr.to_api(),
@@ -162,27 +205,50 @@ pub(crate) async fn linev_into_api(v: Vec<ParsedLine>) -> Vec<api::ParsedLine> {
join_all(v.into_iter().map(|l| l.into_api())).await
}
/// Lines in the logical tree returnable from parsers
pub enum ParsedLineKind {
/// A single subtree in the logical tree. This kind can more conveniently be
/// generated with [ParsedLine::cnst] or [ParsedLine::module]
Mem(ParsedMem),
/// Additional syntax that will be passed to another line parser and may
/// evaluate to zero or more lines
Rec(Vec<GenTokTree>),
}
/// A single subtree in the logical tree, also visible via [crate::refl]
pub struct ParsedMem {
/// Name must be unique
pub name: IStr,
/// Whether the subtree is visible outside its enclosing module. If not, only
/// siblings and their descendants will be able to import it
pub exported: bool,
/// Variants
pub kind: ParsedMemKind,
}
/// Kind of [ParsedMem]
pub enum ParsedMemKind {
/// A constant
Const(BoxConstCallback),
Mod { lines: Vec<ParsedLine>, use_prelude: bool },
/// A module with additional descendants
Mod {
/// Logical lines inside the module
lines: Vec<ParsedLine>,
/// Whether the module will include prelude imports provided by all
/// extensions
use_prelude: bool,
},
}
/// Enable a generated constant to query about its environment
#[derive(Clone)]
pub struct ConstCtx {
constid: api::ParsedConstId,
}
impl ConstCtx {
/// Resolve a set of local names into the full names they would point to if
/// they were globally bound. Errors produced at this stage are soft, as the
/// names may still be found to be locally bound within the expression
pub fn names<'b>(
&'b self,
names: impl IntoIterator<Item = &'b Sym> + 'b,
@@ -202,6 +268,7 @@ impl ConstCtx {
}
})
}
/// Static-length version of [Self::names]
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
}

View File

@@ -9,39 +9,45 @@ use memo_map::MemoMap;
use orchid_base::{IStr, NameLike, VPath, es, iv};
use task_local::task_local;
use crate::api;
use crate::entrypoint::request;
use crate::system::sys_id;
use crate::{api, request, sys_id};
#[derive(Debug)]
pub struct ReflMemData {
// None for inferred steps
struct ReflMemData {
/// None for inferred steps
public: OnceCell<bool>,
kind: ReflMemKind,
}
/// Potentially partial reflected information about a member inside a module
#[derive(Clone, Debug)]
pub struct ReflMem(Rc<ReflMemData>);
impl ReflMem {
pub fn kind(&self) -> ReflMemKind { self.0.kind.clone() }
}
/// The kind of [ReflMem]
#[derive(Clone, Debug)]
pub enum ReflMemKind {
/// A plain constant. Information about constants can be obtained by passing a
/// [crate::gen_expr::GExprKind::Const] to the interpreter
Const,
/// A module that can be reflected further
Mod(ReflMod),
}
#[derive(Debug)]
pub struct ReflModData {
struct ReflModData {
inferred: Mutex<bool>,
path: VPath,
members: MemoMap<IStr, ReflMem>,
}
/// A module whose members can be listed and inspected
#[derive(Clone, Debug)]
pub struct ReflMod(Rc<ReflModData>);
impl ReflMod {
/// Retrieve the path of the module
pub fn path(&self) -> &[IStr] { &self.0.path[..] }
/// Whether this path is the root or if it has a parent
pub fn is_root(&self) -> bool { self.0.path.is_empty() }
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
let path_tok = iv(&self.0.path[..]).await;
@@ -70,6 +76,7 @@ impl ReflMod {
}
Ok(())
}
/// Find a child by name within the module
pub async fn get_child(&self, key: &IStr) -> Option<ReflMem> {
let inferred_g = self.0.inferred.lock().await;
if let Some(mem) = self.0.members.get(key) {
@@ -90,6 +97,9 @@ impl ReflMod {
}
self.0.members.get(key).cloned()
}
/// Find a descendant by sub-path within the module. Note that this is not the
/// same as the paths accepted by import statements, as the `self` and `super`
/// keywords don't work
pub async fn get_by_path(&self, path: &[IStr]) -> Result<ReflMem, InvalidPathError> {
let (next, tail) = path.split_first().expect("Attempted to walk by empty path");
let inferred_g = self.0.inferred.lock().await;
@@ -138,8 +148,11 @@ task_local! {
static REFL_ROOTS: RefCell<HashMap<api::SysId, ReflMod>>
}
/// Indicates that the caller provided a path that does not exist
#[derive(Clone, Debug)]
pub struct InvalidPathError {
/// When unwinding a recursive path lookup, decides whether the temporary
/// member created for the parent object should be kept or deleted
keep_ancestry: bool,
}
@@ -154,12 +167,18 @@ fn default_member(is_root: bool, kind: ReflMemKind) -> ReflMem {
}))
}
/// Obtains the root module for reflection. This function may only be called in
/// callbacks provided by [crate::OwnedAtom] except for
/// [crate::OwnedAtom::free], callbacks provided by [crate::ThinAtom], the
/// callback for [crate::ParsedLine::cnst], [crate::gen_expr] callbacks, and
/// other places where we know that the interpreter is running and holding a
/// reference to the module tree
pub fn refl() -> ReflMod {
REFL_ROOTS.with(|tbl| {
tbl.borrow_mut().entry(sys_id()).or_insert_with(|| default_module(VPath::new([]))).clone()
})
}
pub fn with_refl_roots<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
pub(crate) fn with_refl_roots<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
Box::pin(REFL_ROOTS.scope(RefCell::default(), fut))
}

View File

@@ -1,26 +1,52 @@
use std::num::NonZero;
use std::time::Duration;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use crate::api;
use crate::atom::AtomMethod;
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
pub struct Spawn(pub api::ExprTicket);
impl Request for Spawn {
type Response = api::ExprTicket;
}
/// Execute the atom as a command.
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
pub struct RunCommand;
impl Request for RunCommand {
type Response = Option<api::Expression>;
}
impl AtomMethod for RunCommand {
const NAME: &str = "orchid::cmd::run";
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
pub struct AsDuration;
impl Request for AsDuration {
type Response = Duration;
}
impl AtomMethod for AsDuration {
const NAME: &str = "orchid::time::as_duration";
}
/// Represents [std::io::ErrorKind] values that are produced while operating on
/// already-opened files
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
pub enum IoErrorKind {
BrokenPipe,
UnexpectedEof,
ConnectionAborted,
/// Produced when a stream ends prematurely due to an identifiably unintended
/// reason, such as a TCP socket timeout, the file becoming inaccessible or
/// disappearing, or
Interrupted,
Other,
}
impl IoErrorKind {
pub fn message(self) -> &'static str {
match self {
IoErrorKind::Other => "Failed to read from stream",
IoErrorKind::BrokenPipe => "Broken pipe",
IoErrorKind::UnexpectedEof => "Unexpected EOF",
IoErrorKind::ConnectionAborted => "Connection aborted",
IoErrorKind::Interrupted => "Stream interrupted",
}
}
}

View File

@@ -1,78 +1,21 @@
use std::any::{Any, TypeId, type_name};
use std::fmt::Debug;
use std::future::Future;
use std::num::NonZero;
use std::rc::Rc;
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use orchid_api_traits::{Coding, Decode, Encode, Request};
use orchid_base::{BoxedIter, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym};
use orchid_base::{Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym};
use task_local::task_local;
use crate::api;
use crate::atom::{AtomOps, AtomTypeId, Atomic, AtomicFeatures};
use crate::coroutine_exec::Replier;
use crate::entrypoint::request;
use crate::func_atom::{Fun, Lambda};
use crate::lexer::LexerObj;
use crate::parser::ParserObj;
use crate::system_ctor::{CtedObj, SystemCtor};
use crate::tree::GenMember;
use crate::{Cted, CtedObj, DynSystemCard, LexerObj, ParserObj, SystemCard, SystemCtor, api};
/// Description of a system. This is a distinct object because [SystemCtor]
/// isn't [Default]
pub trait SystemCard: Debug + Default + 'static {
type Ctor: SystemCtor;
type Req: Coding;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomOps>>>;
}
/// Type-erased [SystemCard]
pub trait DynSystemCard: Any + 'static {
fn name(&self) -> &'static str;
/// Atoms explicitly defined by the system card. Do not rely on this for
/// querying atoms as it doesn't include the general atom types
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>>;
}
impl<T: DynSystemCard + ?Sized> DynSystemCardExt for T {}
pub(crate) trait DynSystemCardExt: DynSystemCard {
fn ops_by_tid(&self, tid: TypeId) -> Option<(AtomTypeId, Box<dyn AtomOps>)> {
(self.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u32 + 1).unwrap(), o)))
.chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u32)).unwrap(), o)))
.filter_map(|(i, o)| o.map(|a| (AtomTypeId(i), a)))
.find(|ent| ent.1.tid() == tid)
}
fn ops_by_atid(&self, tid: AtomTypeId) -> Option<Box<dyn AtomOps>> {
if (u32::from(tid.0) >> (u32::BITS - 1)) & 1 == 1 {
general_atoms().nth(!u32::from(tid.0) as usize).unwrap()
} else {
self.atoms().nth(u32::from(tid.0) as usize - 1).unwrap()
}
}
fn ops<A: Atomic>(&self) -> (AtomTypeId, Box<dyn AtomOps>) {
self
.ops_by_tid(TypeId::of::<A>())
.unwrap_or_else(|| panic!("{} is not an atom in {}", type_name::<A>(), self.name()))
}
}
/// Atoms supported by this package which may appear in all extensions.
/// The indices of these are bitwise negated, such that the MSB of an atom index
/// marks whether it belongs to this package (0) or the importer (1)
fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>> {
[Some(Fun::ops()), Some(Lambda::ops()), Some(Replier::ops())].into_iter()
}
impl<T: SystemCard> DynSystemCard for T {
fn name(&self) -> &'static str { T::Ctor::NAME }
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>> {
Box::new(Self::atoms().into_iter())
}
}
pub type CardForSystem<T> = <<T as System>::Ctor as SystemCtor>::Card;
pub type ReqForSystem<T> = <CardForSystem<T> as SystemCard>::Req;
/// System as defined by author
pub trait System: SystemCard + 'static {
pub trait System: Debug + 'static {
type Ctor: SystemCtor<Instance = Self>;
fn prelude(&self) -> impl Future<Output = Vec<Sym>>;
fn env(&self) -> impl Future<Output = Vec<GenMember>>;
fn lexers(&self) -> Vec<LexerObj>;
@@ -80,11 +23,11 @@ pub trait System: SystemCard + 'static {
fn request<'a>(
&self,
hand: Box<dyn ReqHandle<'a> + 'a>,
req: Self::Req,
req: ReqForSystem<Self>,
) -> impl Future<Output = Receipt<'a>>;
}
pub trait DynSystem: DynSystemCard + 'static {
pub trait DynSystem: Debug + 'static {
fn dyn_prelude(&self) -> LocalBoxFuture<'_, Vec<Sym>>;
fn dyn_env(&self) -> LocalBoxFuture<'_, Vec<GenMember>>;
fn dyn_lexers(&self) -> Vec<LexerObj>;
@@ -93,7 +36,7 @@ pub trait DynSystem: DynSystemCard + 'static {
&'a self,
hand: Box<dyn ReqReader<'b> + 'b>,
) -> LocalBoxFuture<'a, Receipt<'b>>;
fn card(&self) -> &dyn DynSystemCard;
fn card(&self) -> Box<dyn DynSystemCard>;
}
impl<T: System> DynSystem for T {
@@ -106,11 +49,11 @@ impl<T: System> DynSystem for T {
mut hand: Box<dyn ReqReader<'b> + 'b>,
) -> LocalBoxFuture<'a, Receipt<'b>> {
Box::pin(async move {
let value = hand.read_req::<<Self as SystemCard>::Req>().await.unwrap();
let value = hand.read_req().await.unwrap();
self.request(hand.finish().await, value).await
})
}
fn card(&self) -> &dyn DynSystemCard { self }
fn card(&self) -> Box<dyn DynSystemCard> { Box::new(CardForSystem::<Self>::default()) }
}
#[derive(Clone)]
@@ -125,23 +68,7 @@ pub(crate) async fn with_sys<F: Future>(sys: SysCtx, fut: F) -> F::Output {
}
pub fn sys_id() -> api::SysId { SYS_CTX.with(|cx| cx.0) }
pub fn cted() -> CtedObj { SYS_CTX.with(|cx| cx.1.clone()) }
/// Make a global request to a system that supports this request type. The
/// target system must either be the system in which this function is called, or
/// one of its direct dependencies.
pub async fn sys_req<Sys: SystemCard, Req: Request + Into<Sys::Req>>(req: Req) -> Req::Response {
let mut msg = Vec::new();
req.into().encode_vec(&mut msg);
let cted = cted();
let own_inst = cted.inst();
let owner = if own_inst.card().type_id() == TypeId::of::<Sys>() {
sys_id()
} else {
(cted.deps().find(|s| s.get_card().type_id() == TypeId::of::<Sys>()))
.expect("System not in dependency array")
.id()
};
let reply = request(api::SysFwd(owner, msg)).await;
Req::Response::decode(std::pin::pin!(&reply[..])).await.unwrap()
pub fn dyn_cted() -> CtedObj { SYS_CTX.with(|cx| cx.1.clone()) }
pub fn cted<C: SystemCtor>() -> Cted<C> {
Rc::downcast::<Cted<C>>(dyn_cted().as_any()).unwrap().as_ref().clone()
}

View File

@@ -0,0 +1,60 @@
use std::any::{Any, TypeId, type_name};
use std::fmt::Debug;
use std::num::NonZero;
use orchid_api_traits::Coding;
use orchid_base::BoxedIter;
use crate::{AtomOps, AtomTypeId, Atomic, AtomicFeatures, Fun, Lambda, Replier, SystemCtor};
/// Description of a system. This is intended to be a ZST storing the static
/// properties of a [SystemCtor] which should be known to foreign systems
pub trait SystemCard: Debug + Default + 'static {
type Ctor: SystemCtor;
type Req: Coding;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn AtomOps>>>;
}
/// Type-erased [SystemCard]
pub trait DynSystemCard: Any + 'static {
fn name(&self) -> &'static str;
/// Atoms explicitly defined by the system card. Do not rely on this for
/// querying atoms as it doesn't include the general atom types
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>>;
}
impl<T: SystemCard> DynSystemCard for T {
fn name(&self) -> &'static str { T::Ctor::NAME }
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomOps>>> {
Box::new(Self::atoms().into_iter())
}
}
impl<T: DynSystemCard + ?Sized> DynSystemCardExt for T {}
pub(crate) trait DynSystemCardExt: DynSystemCard {
fn ops_by_tid(&self, tid: TypeId) -> Option<(AtomTypeId, Box<dyn AtomOps>)> {
(self.atoms().enumerate().map(|(i, o)| (NonZero::new(i as u32 + 1).unwrap(), o)))
.chain(general_atoms().enumerate().map(|(i, o)| (NonZero::new(!(i as u32)).unwrap(), o)))
.filter_map(|(i, o)| o.map(|a| (AtomTypeId(i), a)))
.find(|ent| ent.1.tid() == tid)
}
fn ops_by_atid(&self, tid: AtomTypeId) -> Option<Box<dyn AtomOps>> {
if (u32::from(tid.0) >> (u32::BITS - 1)) & 1 == 1 {
general_atoms().nth(!u32::from(tid.0) as usize).unwrap()
} else {
self.atoms().nth(u32::from(tid.0) as usize - 1).unwrap()
}
}
fn ops<A: Atomic>(&self) -> (AtomTypeId, Box<dyn AtomOps>) {
self
.ops_by_tid(TypeId::of::<A>())
.unwrap_or_else(|| panic!("{} is not an atom in {}", type_name::<A>(), self.name()))
}
}
/// Atoms supported by this package which may appear in all extensions.
/// The indices of these are bitwise negated, such that the MSB of an atom index
/// marks whether it belongs to this package (0) or the importer (1)
pub(crate) fn general_atoms() -> impl Iterator<Item = Option<Box<dyn AtomOps>>> {
[Some(Fun::ops()), Some(Lambda::ops()), Some(Replier::ops())].into_iter()
}

View File

@@ -1,33 +1,31 @@
use std::any::Any;
use std::fmt::Debug;
use std::sync::Arc;
use std::rc::Rc;
use orchid_base::{BoxedIter, box_empty, box_once};
use ordered_float::NotNan;
use crate::api;
use crate::other_system::{DynSystemHandle, SystemHandle};
use crate::system::{DynSystem, System, SystemCard};
use crate::{DynSystem, DynSystemHandle, System, SystemCard, SystemHandle, api};
#[derive(Debug)]
pub struct Cted<Ctor: SystemCtor + ?Sized> {
pub deps: <Ctor::Deps as DepDef>::Sat,
pub inst: Arc<Ctor::Instance>,
pub inst: Rc<Ctor::Instance>,
}
impl<C: SystemCtor + ?Sized> Clone for Cted<C> {
fn clone(&self) -> Self { Self { deps: self.deps.clone(), inst: self.inst.clone() } }
}
pub trait DynCted: Debug + 'static {
fn as_any(&self) -> &dyn Any;
fn as_any(self: Rc<Self>) -> Rc<dyn Any>;
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
fn inst(&self) -> Arc<dyn DynSystem>;
fn inst(&self) -> Rc<dyn DynSystem>;
}
impl<C: SystemCtor + ?Sized> DynCted for Cted<C> {
fn as_any(&self) -> &dyn Any { self }
fn as_any(self: Rc<Self>) -> Rc<dyn Any> { self }
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)> { self.deps.iter() }
fn inst(&self) -> Arc<dyn DynSystem> { self.inst.clone() }
fn inst(&self) -> Rc<dyn DynSystem> { self.inst.clone() }
}
pub type CtedObj = Arc<dyn DynCted>;
pub type CtedObj = Rc<dyn DynCted>;
pub trait DepSat: Debug + Clone + 'static {
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
@@ -61,7 +59,8 @@ impl DepDef for () {
pub trait SystemCtor: Debug + 'static {
type Deps: DepDef;
type Instance: System;
type Instance: System<Ctor = Self>;
type Card: SystemCard<Ctor = Self>;
const NAME: &'static str;
const VERSION: f64;
/// Create a system instance.
@@ -85,8 +84,8 @@ impl<T: SystemCtor> DynSystemCtor for T {
fn new_system(&self, api::NewSystem { system: _, id: _, depends }: &api::NewSystem) -> CtedObj {
let mut ids = depends.iter().copied();
let deps = T::Deps::create(&mut || ids.next().unwrap());
let inst = Arc::new(self.inst(deps.clone()));
Arc::new(Cted::<T> { deps, inst })
let inst = Rc::new(self.inst(deps.clone()));
Rc::new(Cted::<T> { deps, inst })
}
}

View File

@@ -1,7 +1,6 @@
use std::rc::Rc;
use crate::entrypoint::ExtensionBuilder;
use crate::ext_port::ExtPort;
use crate::{ExtPort, ExtensionBuilder};
/// Run an extension inside a Tokio localset. Since the extension API does not
/// provide a forking mechanism, it can safely abort once the localset is
/// exhausted. If an extension absolutely needs a parallel thread, it can import
@@ -10,7 +9,7 @@ use crate::ext_port::ExtPort;
/// value returned by [crate::system_ctor::SystemCtor::inst] to initiate
/// shutdown.
#[cfg(feature = "tokio")]
pub async fn tokio_entrypoint(builder: ExtensionBuilder) {
pub async fn __tokio_entrypoint(builder: ExtensionBuilder) {
use std::cell::RefCell;
use async_event::Event;
@@ -61,6 +60,6 @@ pub async fn tokio_entrypoint(builder: ExtensionBuilder) {
macro_rules! tokio_main {
($builder:expr) => {
#[tokio::main]
pub async fn main() { $crate::tokio::tokio_entrypoint($builder).await }
pub async fn main() { $crate::__tokio_entrypoint($builder).await }
};
}

View File

@@ -13,13 +13,16 @@ use substack::Substack;
use task_local::task_local;
use trait_set::trait_set;
use crate::api;
use crate::conv::ToExpr;
use crate::expr::{BorrowedExprStore, Expr, ExprHandle};
use crate::func_atom::{ExprFunc, Fun};
use crate::gen_expr::{GExpr, new_atom};
use crate::{BorrowedExprStore, Expr, ExprFunc, ExprHandle, Fun, ToExpr, api};
/// Tokens generated by lexers and parsers
///
/// See: [GenTok], [Token], [crate::Lexer], [crate::Parser]
pub type GenTokTree = TokTree<Expr, GExpr>;
/// Tokens generated by lexers and parsers - without location data
///
/// See: [GenTokTree], [Token], [crate::Lexer], [crate::Parser]
pub type GenTok = Token<Expr, GExpr>;
impl TokenVariant<api::Expression> for GExpr {
@@ -41,9 +44,15 @@ impl TokenVariant<api::ExprTicket> for Expr {
async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::ExprTicket { self.handle().ticket() }
}
/// Embed a literal value in generated syntax.
pub async fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_gen().await) }
/// Embed a reference to a constant in generated syntax. The constant doesn't
/// need to be visible per export rules, and if it doesn't exist, the expression
/// will raise a runtime error
pub async fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(path.to_gen().await) }
/// Create a new subtree that is evaluated as-needed, asynchronously, and can
/// use its own path to determine its value
pub fn lazy(
public: bool,
name: &str,
@@ -51,30 +60,32 @@ pub fn lazy(
) -> Vec<GenMember> {
vec![GenMember {
name: name.to_string(),
kind: MemKind::Lazy(LazyMemberFactory::new(cb)),
kind: LazyMemKind::Lazy(LazyMemberFactory::new(cb)),
comments: vec![],
public,
}]
}
/// A constant node in the module tree
pub fn cnst(public: bool, name: &str, value: impl ToExpr + Clone + 'static) -> Vec<GenMember> {
lazy(public, name, async |_| MemKind::Const(value.to_gen().await))
}
/// A module in the tree. These can be merged by [merge_trivial]
pub fn module(
public: bool,
name: &str,
mems: impl IntoIterator<Item = Vec<GenMember>>,
) -> Vec<GenMember> {
let (name, kind) = root_mod(name, mems);
let (name, kind) = (name.to_string(), LazyMemKind::Mod(mems.into_iter().flatten().collect()));
vec![GenMember { name, kind, public, comments: vec![] }]
}
pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (String, MemKind) {
(name.to_string(), MemKind::module(mems))
}
/// A Rust function that is passed to Orchid via [Fun]
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
let fac =
LazyMemberFactory::new(async move |sym| MemKind::Const(new_atom(Fun::new(sym, xf).await)));
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
vec![GenMember { name: name.to_string(), kind: LazyMemKind::Lazy(fac), public, comments: vec![] }]
}
/// A chain of nested modules with names taken from the duble::colon::delimited
/// path ultimately containing the elements
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
let mut items = items.into_iter().flatten().collect_vec();
for step in path.split("::").collect_vec().into_iter().rev() {
@@ -82,7 +93,7 @@ pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Ve
}
items
}
/// Add comments to a set of members
pub fn comments<'a>(
cmts: impl IntoIterator<Item = &'a str>,
mut val: Vec<GenMember>,
@@ -101,20 +112,20 @@ pub fn comments<'a>(
/// - Duplicate constants result in an error
/// - A combination of lazy and anything results in an error
pub fn merge_trivial(trees: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {
let mut all_members = HashMap::<String, (MemKind, Vec<String>)>::new();
let mut all_members = HashMap::<String, (LazyMemKind, Vec<String>)>::new();
for mem in trees.into_iter().flatten() {
assert!(mem.public, "Non-trivial merge in {}", mem.name);
match mem.kind {
unit @ (MemKind::Const(_) | MemKind::Lazy(_)) => {
unit @ (LazyMemKind::Const(_) | LazyMemKind::Lazy(_)) => {
let prev = all_members.insert(mem.name.clone(), (unit, mem.comments.into_iter().collect()));
assert!(prev.is_none(), "Conflict in trivial tree merge on {}", mem.name);
},
MemKind::Mod(members) => match all_members.entry(mem.name.clone()) {
LazyMemKind::Mod(members) => match all_members.entry(mem.name.clone()) {
hashbrown::hash_map::Entry::Vacant(slot) => {
slot.insert((MemKind::Mod(members), mem.comments.into_iter().collect()));
slot.insert((LazyMemKind::Mod(members), mem.comments.into_iter().collect()));
},
hashbrown::hash_map::Entry::Occupied(mut old) => match old.get_mut() {
(MemKind::Mod(old_items), old_cmts) => {
(LazyMemKind::Mod(old_items), old_cmts) => {
let mut swap = vec![];
std::mem::swap(&mut swap, old_items);
*old_items = merge_trivial([swap, members]);
@@ -135,7 +146,7 @@ trait_set! {
trait LazyMemberCallback =
FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + DynClone
}
pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>);
pub(crate) struct LazyMemberFactory(Box<dyn LazyMemberCallback>);
impl LazyMemberFactory {
pub fn new(cb: impl AsyncFnOnce(Sym) -> MemKind + Clone + 'static) -> Self {
Self(Box::new(|s| cb(s).boxed_local()))
@@ -147,10 +158,10 @@ impl Clone for LazyMemberFactory {
}
pub struct GenMember {
pub name: String,
pub kind: MemKind,
pub public: bool,
pub comments: Vec<String>,
pub(crate) name: String,
pub(crate) kind: LazyMemKind,
pub(crate) public: bool,
pub(crate) comments: Vec<String>,
}
impl GenMember {
pub(crate) async fn into_api(self, tia_cx: &mut impl TreeIntoApiCtx) -> api::Member {
@@ -161,16 +172,24 @@ impl GenMember {
}
}
/// Items in the tree after deferrals have been resolved
pub enum MemKind {
Const(GExpr),
Mod(Vec<GenMember>),
Lazy(LazyMemberFactory),
}
impl MemKind {
pub async fn cnst(val: impl ToExpr) -> Self { Self::Const(val.to_gen().await) }
pub fn module(mems: impl IntoIterator<Item = Vec<GenMember>>) -> Self {
Self::Mod(mems.into_iter().flatten().collect())
}
}
pub(crate) enum LazyMemKind {
Const(GExpr),
Mod(Vec<GenMember>),
Lazy(LazyMemberFactory),
}
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)),
@@ -189,7 +208,7 @@ impl MemKind {
}
}
pub enum MemberRecord {
pub(crate) enum MemberRecord {
Gen(Vec<IStr>, LazyMemberFactory),
Res,
}
@@ -201,7 +220,7 @@ task_local! {
static LAZY_MEMBERS: LazyMemberStore;
}
pub fn with_lazy_member_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
pub(crate) fn with_lazy_member_store<'a>(fut: LocalBoxFuture<'a, ()>) -> LocalBoxFuture<'a, ()> {
Box::pin(LAZY_MEMBERS.scope(LazyMemberStore::default(), fut))
}
@@ -215,7 +234,7 @@ fn add_lazy(cx: &impl TreeIntoApiCtx, fac: LazyMemberFactory) -> api::TreeId {
})
}
pub async fn get_lazy(id: api::TreeId) -> (Sym, MemKind) {
pub(crate) async fn get_lazy(id: api::TreeId) -> (Sym, LazyMemKind) {
let (path, cb) =
LAZY_MEMBERS.with(|tbl| match tbl.0.borrow_mut().insert(id, MemberRecord::Res) {
None => panic!("Tree for ID not found"),
@@ -223,7 +242,10 @@ pub async fn get_lazy(id: api::TreeId) -> (Sym, MemKind) {
Some(MemberRecord::Gen(path, cb)) => (path, cb),
});
let path = Sym::new(path).await.unwrap();
(path.clone(), cb.build(path).await)
(path.clone(), match cb.build(path).await {
MemKind::Const(c) => LazyMemKind::Const(c),
MemKind::Mod(m) => LazyMemKind::Mod(m),
})
}
pub(crate) trait TreeIntoApiCtx {
@@ -231,7 +253,7 @@ pub(crate) trait TreeIntoApiCtx {
fn path(&self) -> impl Iterator<Item = IStr>;
}
pub struct TreeIntoApiCtxImpl<'a> {
pub(crate) struct TreeIntoApiCtxImpl<'a> {
pub basepath: &'a [IStr],
pub path: Substack<'a, IStr>,
}

View File

@@ -27,6 +27,7 @@ orchid-extension = { version = "0.1.0", path = "../orchid-extension", optional =
ordered-float = "5.1.0"
pastey = "0.2.1"
substack = "1.1.1"
task-local = "0.1.0"
tokio = { version = "1.49.0", features = ["process"], optional = true }
tokio-util = { version = "0.7.18", features = ["compat"], optional = true }
trait-set = "0.3.0"

View File

@@ -94,7 +94,7 @@ impl AtomHand {
#[must_use]
pub fn ext(&self) -> &Extension { self.sys().ext() }
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
self.0.owner.client().request(api::Fwded(self.0.api_ref(), key, req)).await.unwrap()
self.0.owner.client().request(api::FinalFwded(self.0.api_ref(), key, req)).await.unwrap()
}
#[must_use]
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }

View File

@@ -0,0 +1,249 @@
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::fmt::Debug;
use std::pin::pin;
use std::rc::Rc;
use async_event::Event;
use async_fn_stream::stream;
use futures::channel::mpsc;
use futures::future::LocalBoxFuture;
use futures::stream::FuturesUnordered;
use futures::{SinkExt, StreamExt, select};
use never::Never;
use orchid_base::{OrcErrv, Receipt, ReqHandle, Sym};
use orchid_extension::{self as ox, AtomicFeatures as _};
use crate::ctx::Ctx;
use crate::execute::{ExecCtx, ExecResult};
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
use crate::tree::Root;
struct CommandQueueState {
new: VecDeque<Expr>,
added: Rc<Event>,
wants_exit: bool,
ctx: Ctx,
}
#[derive(Clone)]
struct CommandQueue(Rc<RefCell<CommandQueueState>>);
impl CommandQueue {
fn new(ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
Self(Rc::new(RefCell::new(CommandQueueState {
new: init.into_iter().collect(),
added: Rc::default(),
wants_exit: false,
ctx,
})))
}
pub fn push(&self, expr: Expr) {
let was_empty = {
let mut g = self.0.borrow_mut();
g.new.push_back(expr);
g.new.len() == 1
};
if was_empty {
let added = self.0.borrow_mut().added.clone();
added.notify_one();
}
}
pub async fn get_new(&self) -> Expr {
let added = {
let mut g = self.0.borrow_mut();
if let Some(waiting) = g.new.pop_front() {
return waiting;
}
g.added.clone()
};
added.wait_until(|| self.0.borrow_mut().new.pop_front()).await
}
}
impl Debug for CommandQueue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CommandQueue").finish_non_exhaustive()
}
}
pub enum CmdResult {
/// All command sequences settled
Settled,
/// Exit was requested explicitly by usercode
Exit,
/// Ran out of gas
Gas,
/// Received a value that wasn't a command
///
/// This is potentially user error, but implementors may choose to well-define
/// certain responses
NonCommand(Expr),
/// Received an Orchid error
///
/// This is definitely user error, but implementors may choose to continue
/// running other chains of execution after handling it
Err(OrcErrv),
}
pub struct CmdRunner {
root: Root,
queue: CommandQueue,
gas: Option<u64>,
interrupted: Option<ExecCtx>,
futures: FuturesUnordered<LocalBoxFuture<'static, Option<CmdResult>>>,
}
impl CmdRunner {
pub async fn new(root: Root, ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
Self {
futures: FuturesUnordered::new(),
gas: None,
root,
interrupted: None,
queue: CommandQueue::new(ctx, init),
}
}
#[must_use]
pub fn get_gas(&self) -> u64 { self.gas.expect("queried gas but no gas was set") }
pub fn set_gas(&mut self, gas: u64) { self.gas = Some(gas) }
pub fn disable_gas(&mut self) { self.gas = None }
pub async fn execute(&mut self) -> CmdResult {
let waiting_on_queue = RefCell::new(false);
let (mut spawn, mut on_spawn) = mpsc::channel(1);
let mut normalize_stream = pin!(
stream(async |mut h| {
loop {
if self.queue.0.borrow().wants_exit {
h.emit(CmdResult::Exit).await;
break;
}
waiting_on_queue.replace(false);
let mut xctx = match self.interrupted.take() {
None => ExecCtx::new(self.root.clone(), self.queue.get_new().await).await,
Some(xctx) => xctx,
};
waiting_on_queue.replace(true);
xctx.set_gas(self.gas);
let res = xctx.execute().await;
match res {
ExecResult::Err(e, gas) => {
self.gas = gas;
h.emit(CmdResult::Err(e)).await;
},
ExecResult::Gas(exec) => {
self.interrupted = Some(exec);
h.emit(CmdResult::Gas).await;
},
ExecResult::Value(val, gas) => {
self.gas = gas;
let Some(atom) = val.as_atom().await else {
h.emit(CmdResult::NonCommand(val)).await;
continue;
};
let queue = self.queue.clone();
let ctx = queue.0.borrow_mut().ctx.clone();
spawn
.send(Box::pin(async move {
match atom.ipc(ox::std_reqs::RunCommand).await {
None => Some(CmdResult::NonCommand(val)),
Some(None) => None,
Some(Some(expr)) => {
let from_api_cx = ExprFromApiCtx { ctx, sys: atom.api_ref().owner };
queue.push(Expr::from_api(expr, PathSetBuilder::new(), from_api_cx).await);
None
},
}
}))
.await
.expect("Receiver is owned by the layer that polls this");
},
}
}
})
.fuse()
);
loop {
if self.queue.0.borrow().wants_exit {
break CmdResult::Exit;
}
let task = select!(
r_opt = self.futures.by_ref().next() => match r_opt {
Some(Some(r)) => break r,
None if *waiting_on_queue.borrow() => break CmdResult::Settled,
None | Some(None) => continue,
},
r = normalize_stream.by_ref().next() => break r.expect("infinite stream"),
task = on_spawn.by_ref().next() => task.expect("sender moved into infinite stream"),
);
self.futures.push(task)
}
}
}
#[derive(Default, Debug)]
pub struct CmdSystemCard;
impl ox::SystemCard for CmdSystemCard {
type Ctor = CmdSystemCtor;
type Req = Never;
fn atoms() -> impl IntoIterator<Item = Option<Box<dyn ox::AtomOps>>> {
[Some(CommandQueue::ops())]
}
}
#[derive(Debug)]
pub struct CmdSystemCtor {
queue: CommandQueue,
}
impl ox::SystemCtor for CmdSystemCtor {
const NAME: &'static str = "orchid::cmd";
const VERSION: f64 = 0.1;
type Card = CmdSystemCard;
type Deps = ();
type Instance = CmdSystemInst;
fn inst(&self, _: <Self::Deps as orchid_extension::DepDef>::Sat) -> Self::Instance {
CmdSystemInst { queue: self.queue.clone() }
}
}
fn ox_get_queue() -> CommandQueue { ox::cted::<CmdSystemCtor>().inst.queue.clone() }
#[derive(Debug)]
pub struct CmdSystemInst {
queue: CommandQueue,
}
impl ox::System for CmdSystemInst {
type Ctor = CmdSystemCtor;
async fn env(&self) -> Vec<ox::tree::GenMember> {
ox::tree::prefix("orchid::cmd", [
ox::tree::cnst(false, "queue", ox::gen_expr::new_atom(ox_get_queue())),
ox::tree::fun(true, "spawn", async |side: ox::Expr, cont: ox::Expr| {
ox::cmd(async move || {
let queue = ox_get_queue();
let side_xtk = side.serialize().await;
let mut g = queue.0.borrow_mut();
let host_ex =
g.ctx.exprs.take_expr(side_xtk).expect("Host could not locate leaked expr by ID ");
g.new.push_back(host_ex);
Some(cont)
})
}),
])
}
async fn prelude(&self) -> Vec<Sym> { vec![] }
fn lexers(&self) -> Vec<ox::LexerObj> { vec![] }
fn parsers(&self) -> Vec<ox::ParserObj> { vec![] }
async fn request<'a>(
&self,
_hand: Box<dyn ReqHandle<'a> + 'a>,
req: ox::ReqForSystem<Self>,
) -> Receipt<'a> {
match req {}
}
}
impl ox::Atomic for CommandQueue {
type Data = ();
type Variant = ox::OwnedVariant;
}
impl ox::OwnedAtom for CommandQueue {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
}

View File

@@ -20,9 +20,9 @@ enum StackOp {
}
pub enum ExecResult {
Value(Expr),
Value(Expr, Option<u64>),
Gas(ExecCtx),
Err(OrcErrv),
Err(OrcErrv, Option<u64>),
}
pub struct ExecCtx {
@@ -46,17 +46,6 @@ impl ExecCtx {
#[must_use]
pub fn idle(&self) -> bool { self.did_pop }
#[must_use]
pub fn result(self) -> ExecResult {
if self.idle() {
match &*self.cur {
ExprKind::Bottom(errv) => ExecResult::Err(errv.clone()),
_ => ExecResult::Value(*self.cur.unbind()),
}
} else {
ExecResult::Gas(self)
}
}
#[must_use]
pub fn use_gas(&mut self, amount: u64) -> bool {
if let Some(gas) = &mut self.gas {
*gas -= amount;
@@ -79,7 +68,7 @@ impl ExecCtx {
Err(TryLockError) => panic!("Cycle encountered!"),
}
}
pub async fn execute(&mut self) {
pub async fn execute(mut self) -> ExecResult {
while self.use_gas(1) {
let mut kind_swap = ExprKind::Missing;
mem::swap(&mut kind_swap, &mut self.cur);
@@ -135,7 +124,7 @@ impl ExecCtx {
StackOp::Nop => (),
StackOp::Pop => match self.stack.pop() {
Some(top) => self.cur = top,
None => return,
None => return ExecResult::Value(*self.cur.unbind(), self.gas),
},
StackOp::Push(sub) => {
self.cur_pos = sub.pos();
@@ -150,10 +139,11 @@ impl ExecCtx {
}
*self.cur = ExprKind::Bottom(err.clone());
self.stack = vec![];
return;
return ExecResult::Err(err.clone(), self.gas);
},
}
}
ExecResult::Gas(self)
}
}

View File

@@ -162,8 +162,10 @@ impl Extension {
let sys =
ctx.system_inst(atom.owner).await.expect("owner of live atom dropped");
let client = sys.client();
let reply =
client.request(api::Fwded(fw.0.clone(), *key, body.clone())).await.unwrap();
let reply = client
.request(api::FinalFwded(fw.0.clone(), *key, body.clone()))
.await
.unwrap();
handle.reply(fw, &reply).await
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {

View File

@@ -1,28 +1,18 @@
#[cfg(feature = "orchid-extension")]
use std::io;
use std::rc::Rc;
use std::time::Duration;
#[cfg(feature = "orchid-extension")]
use orchid_base::on_drop;
#[cfg(feature = "orchid-extension")]
use futures::io::BufReader;
use futures::{AsyncBufReadExt, StreamExt};
use orchid_base::{log, on_drop};
use orchid_extension as ox;
use unsync_pipe::pipe;
#[cfg(feature = "orchid-extension")]
use crate::ctx::Ctx;
#[cfg(feature = "orchid-extension")]
use crate::extension::ExtPort;
#[cfg(feature = "orchid-extension")]
use crate::task_set::TaskSet;
#[cfg(feature = "orchid-extension")]
pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) -> ExtPort {
use std::io;
use std::rc::Rc;
use futures::io::BufReader;
use futures::{AsyncBufReadExt, StreamExt};
use orchid_base::log;
use unsync_pipe::pipe;
pub async fn ext_inline(builder: ox::ExtensionBuilder, ctx: Ctx) -> ExtPort {
let (in_stdin, out_stdin) = pipe(1024);
let (in_stdout, out_stdout) = pipe(1024);
let (in_stderr, out_stderr) = pipe(1024);
@@ -48,7 +38,7 @@ pub async fn ext_inline(builder: ox::entrypoint::ExtensionBuilder, ctx: Ctx) ->
std::mem::drop(ctx.clone().spawn(Duration::ZERO, async move {
let task_set2 = task_set1.clone();
builder
.run(ox::ext_port::ExtPort {
.run(ox::ExtPort {
input: Box::pin(out_stdin),
output: Box::pin(in_stdout),
log: Box::pin(in_stderr),

View File

@@ -2,6 +2,8 @@ use orchid_api as api;
pub mod atom;
pub mod ctx;
#[cfg(feature = "orchid-extension")]
pub mod cmd_system;
pub mod dealias;
#[cfg(feature = "tokio")]
pub mod dylib;
@@ -9,6 +11,7 @@ pub mod execute;
pub mod expr;
pub mod expr_store;
pub mod extension;
#[cfg(feature = "orchid-extension")]
pub mod inline;
pub mod lex;
pub mod logger;

View File

@@ -13,11 +13,10 @@ pub use std::tuple::{HomoTpl, Tpl, Tuple, UntypedTuple};
pub use macros::macro_system::MacroSystem;
pub use macros::mactree::{MacTok, MacTree};
use orchid_api as api;
use orchid_extension::dylib_main;
use orchid_extension::entrypoint::ExtensionBuilder;
use orchid_extension::{ExtensionBuilder, dylib_main};
pub fn builder() -> ExtensionBuilder {
ExtensionBuilder::new("orchid-std::main").system(StdSystem::default()).system(MacroSystem)
ExtensionBuilder::new("orchid-std::main").system(StdSystem).system(MacroSystem)
}
dylib_main! { builder() }

View File

@@ -2,11 +2,9 @@ use std::borrow::Cow;
use never::Never;
use orchid_base::fmt;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::Expr;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom};
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec};
use crate::macros::mactree::{MacTok, MacTree};

View File

@@ -7,10 +7,10 @@ use orchid_base::{
Comment, OrcRes, Paren, Parsed, Snippet, Sym, expect_tok, is, report, sym, token_errv,
try_pop_no_fluff, with_reporter,
};
use orchid_extension::TAtom;
use orchid_extension::conv::TryFromExpr;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser};
use orchid_extension::{
ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr,
};
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
use crate::macros::ph_lexer::PhAtom;

View File

@@ -1,8 +1,7 @@
use orchid_base::sym;
use orchid_extension::TAtom;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{TAtom, exec};
use crate::macros::mactree::MacTree;
use crate::macros::resolve::resolve;

View File

@@ -9,9 +9,9 @@ use orchid_base::{
mk_errv, report, sym, token_errv, try_pop_no_fluff, with_reporter,
};
use orchid_extension::TAtom;
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser};
use crate::macros::let_line::{dealias_mac_v, parse_tokv};
use crate::macros::macro_value::{Macro, MacroData, Rule};

View File

@@ -1,12 +1,9 @@
use never::Never;
use orchid_base::{Receipt, ReqHandle, Sym, sym};
use orchid_extension::{AtomOps, AtomicFeatures};
use orchid_extension::lexer::LexerObj;
use orchid_extension::other_system::SystemHandle;
use orchid_extension::parser::ParserObj;
use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::tree::{GenMember, merge_trivial};
use orchid_extension::{
AtomOps, AtomicFeatures, LexerObj, ParserObj, System, SystemCard, SystemCtor, SystemHandle,
};
use crate::macros::instantiate_tpl::InstantiateTplCall;
use crate::macros::let_line::LetLine;
@@ -24,10 +21,11 @@ use crate::{MacTree, StdSystem};
pub struct MacroSystem;
impl SystemCtor for MacroSystem {
type Deps = StdSystem;
type Instance = Self;
type Instance = MacroSystemInst;
type Card = Self;
const NAME: &'static str = "orchid::macros";
const VERSION: f64 = 0.00_01;
fn inst(&self, _: SystemHandle<StdSystem>) -> Self::Instance { Self }
fn inst(&self, std: SystemHandle<StdSystem>) -> Self::Instance { MacroSystemInst { _std: std } }
}
impl SystemCard for MacroSystem {
type Ctor = Self;
@@ -43,7 +41,13 @@ impl SystemCard for MacroSystem {
]
}
}
impl System for MacroSystem {
#[derive(Debug)]
pub struct MacroSystemInst {
_std: SystemHandle<StdSystem>,
}
impl System for MacroSystemInst {
type Ctor = MacroSystem;
async fn request<'a>(&self, _: Box<dyn ReqHandle<'a> + 'a>, req: Never) -> Receipt<'a> {
match req {}
}

View File

@@ -10,7 +10,7 @@ use orchid_base::{
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Paren, Pos, Sym, Variants, indent, tl_cache,
};
use orchid_extension::Atomic;
use orchid_extension::expr::Expr;
use orchid_extension::Expr;
use orchid_extension::{OwnedAtom, OwnedVariant};
fn union_rc_sets(seq: impl IntoIterator<Item = Rc<HashSet<Sym>>>) -> Rc<HashSet<Sym>> {

View File

@@ -5,8 +5,8 @@ use itertools::chain;
use orchid_base::Paren;
use orchid_base::{OrcRes, PARENS, is, mk_errv};
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::parser::p_tree2gen;
use orchid_extension::{LexContext, Lexer, err_not_applicable};
use orchid_extension::p_tree2gen;
use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
use crate::macros::instantiate_tpl::InstantiateTplCall;

View File

@@ -6,9 +6,9 @@ use futures::{Stream, StreamExt, stream};
use never::Never;
use orchid_api_derive::Coding;
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv, sym};
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::{ExecHandle, exec};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::ToExpr;
use orchid_extension::{ExecHandle, exec};
use orchid_extension::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, call_v, lam, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom};

View File

@@ -2,7 +2,7 @@ use orchid_api_derive::Coding;
use orchid_base::{FmtUnit, OrcRes, es, is, mk_errv, name_char, name_start};
use orchid_extension::Atomic;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::{LexContext, Lexer, err_not_applicable};
use orchid_extension::tree::{GenTokTree, x_tok};
use orchid_extension::{ThinAtom, ThinVariant};

View File

@@ -6,11 +6,8 @@ use futures::{FutureExt, StreamExt, stream};
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv};
use orchid_extension::TAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::gen_expr::{GExpr, arg, bot, call, call_v, dyn_lambda, new_atom};
use orchid_extension::reflection::{ReflMemKind, refl};
use orchid_extension::{ReflMemKind, TAtom, ToExpr, exec, refl};
use subslice_offset::SubsliceOffset;
use substack::Substack;

View File

@@ -1,11 +1,8 @@
use futures::StreamExt;
use orchid_base::sym;
use orchid_extension::TAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Expr, TAtom, ToExpr, exec};
use crate::macros::match_macros::MatcherAtom;
use crate::macros::resolve::resolve;

View File

@@ -1,8 +1,8 @@
use orchid_base::sym;
use orchid_extension::TAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::ToExpr;
use orchid_extension::exec;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, prefix};

View File

@@ -1,9 +1,9 @@
use futures::{StreamExt, stream};
use orchid_base::{OrcRes, sym};
use orchid_extension::TAtom;
use orchid_extension::conv::ToExpr;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::ToExpr;
use orchid_extension::exec;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{GExpr, call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix};

View File

@@ -7,7 +7,7 @@ use futures::future::LocalBoxFuture;
use itertools::{Itertools, chain};
use never::Never;
use orchid_base::{NameLike, Sym, VPath, is};
use orchid_extension::conv::ToExpr;
use orchid_extension::ToExpr;
use orchid_extension::gen_expr::{GExpr, new_atom};
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom};
@@ -32,13 +32,13 @@ impl Atomic for MacroBodyArgCollector {
impl OwnedAtom for MacroBodyArgCollector {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: orchid_extension::expr::Expr) -> GExpr {
async fn call_ref(&self, arg: orchid_extension::Expr) -> GExpr {
if !self.args.is_empty() {
eprintln!("This is an intermediary value. It should never be copied");
}
self.clone().call(arg).await
}
async fn call(mut self, arg: orchid_extension::expr::Expr) -> GExpr {
async fn call(mut self, arg: orchid_extension::Expr) -> GExpr {
let atom = (TAtom::<MacTree>::downcast(arg.handle()).await).unwrap_or_else(|_| {
panic!("This is an intermediary value, the argument types are known in advance")
});

View File

@@ -1,10 +1,9 @@
use std::rc::Rc;
use orchid_base::{OrcErrv, is, mk_errv};
use orchid_extension::TAtom;
use orchid_extension::func_atom::get_arg_posv;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::tree::{GenMember, comments, fun, prefix};
use orchid_extension::{TAtom, get_arg_posv};
use crate::std::binary::binary_atom::BlobAtom;
use crate::std::boolean::Bool;

View File

@@ -1,7 +1,7 @@
use orchid_api_derive::Coding;
use orchid_base::{FmtUnit, OrcRes, sym};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::Expr;
use orchid_extension::gen_expr::GExpr;
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
use orchid_extension::{Atomic, TAtom, ThinAtom, ThinVariant};

View File

@@ -19,15 +19,14 @@ use never::Never;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::{FmtCtxImpl, OrcRes};
use orchid_extension::conv::ToExpr;
use orchid_extension::ToExpr;
use orchid_extension::entrypoint::spawn;
use orchid_extension::expr::Expr;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{GExpr, IntoGExprStream, call, new_atom};
use orchid_extension::system::cted;
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
use orchid_extension::{
Atomic, CmdResult, Continuation, ForeignAtom, Next, OwnedAtom, OwnedVariant, err_not_callable,
err_not_command,
Atomic, ForeignAtom, OwnedAtom, OwnedVariant, err_not_callable, err_not_command,
};
use rust_decimal::prelude::Zero;
use tokio::task::{JoinHandle, spawn_local};

View File

@@ -1 +1 @@
pub mod future_lib;
// pub mod future_lib;

View File

@@ -3,10 +3,10 @@ use std::io;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::{FmtUnit, Numeric, OrcRes, Receipt, ReqHandle, ReqHandleExt, Sym, sym};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_extension::system::sys_req;
use orchid_extension::{Atomic, MethodSetBuilder, Supports, TAtom, ThinAtom, ThinVariant};
use orchid_extension::{
Atomic, Expr, MethodSetBuilder, Supports, TAtom, ThinAtom, ThinVariant, ToExpr, TryFromExpr,
sys_req,
};
use ordered_float::NotNan;
use rust_decimal::prelude::Zero;

View File

@@ -1,9 +1,8 @@
use std::ops::RangeInclusive;
use orchid_base::{OrcRes, num_to_errv, parse_num};
use orchid_extension::conv::ToExpr;
use orchid_extension::lexer::{LexContext, Lexer};
use orchid_extension::tree::{GenTokTree, x_tok};
use orchid_extension::{LexContext, Lexer};
use super::num_atom::Num;

View File

@@ -1,5 +1,5 @@
use orchid_base::{Numeric, is, mk_errv};
use orchid_extension::func_atom::get_arg;
use orchid_extension::get_arg;
use orchid_extension::tree::{GenMember, fun, prefix};
use ordered_float::NotNan;
use rust_decimal::prelude::ToPrimitive;

View File

@@ -1,7 +1,7 @@
use orchid_base::{name_char, name_start};
use orchid_base::{OrcRes, is};
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, LexedData, Lexer, err_not_applicable};
use orchid_extension::{LexContext, LexedData, Lexer, err_not_applicable};
use orchid_extension::tree::GenTok;
use crate::std::string::str_atom::IntStrAtom;

View File

@@ -4,8 +4,8 @@ use std::pin::Pin;
use futures::AsyncWrite;
use orchid_api_traits::Encode;
use orchid_base::{is, mk_errv, sym};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::{Expr, ExprHandle};
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
use orchid_extension::{Atomic, DeserializeCtx, ForeignAtom, OwnedAtom, OwnedVariant, TAtom};

View File

@@ -5,7 +5,7 @@ use orchid_base::{
};
use orchid_base::{Paren, Token};
use orchid_base::{IStr, OrcRes, is, mk_errv};
use orchid_extension::parser::{
use orchid_extension::{
PTokTree, ParsCtx, ParsedLine, ParsedLineKind, p_tree2gen, p_v2gen,
};

View File

@@ -2,9 +2,9 @@ use std::rc::Rc;
use hashbrown::HashMap;
use orchid_base::{Comment, OrcRes, Parsed, Token, expect_end, is, mk_errv, sym, try_pop_no_fluff};
use orchid_extension::conv::ToExpr;
use orchid_extension::ToExpr;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser};
use crate::std::protocol::parse_impls::parse_impls;
use crate::std::protocol::types::Tag;

View File

@@ -2,9 +2,9 @@ use std::rc::Rc;
use hashbrown::HashMap;
use orchid_base::{Comment, OrcRes, Parsed, Token, expect_end, is, mk_errv, sym, try_pop_no_fluff};
use orchid_extension::conv::ToExpr;
use orchid_extension::ToExpr;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::parser::{PSnippet, ParsCtx, ParsedLine, Parser};
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser};
use crate::std::protocol::parse_impls::parse_impls;
use crate::std::protocol::types::Tag;

View File

@@ -11,14 +11,11 @@ use never::Never;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::{NameLike, OrcRes, ReqHandleExt, Sym, VName, ev, fmt, is, mk_errv};
use orchid_extension::conv::{ClonableToExprDyn, ToExpr};
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, call, new_atom};
use orchid_extension::system::sys_req;
use orchid_extension::tree::{GenMember, MemKind, cnst, fun, lazy, prefix};
use orchid_extension::{
AtomMethod, Atomic, ForeignAtom, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom,
AtomMethod, Atomic, ClonableToExprDyn, Expr, ExprHandle, ForeignAtom, MethodSetBuilder,
OwnedAtom, OwnedVariant, Supports, TAtom, ToExpr, exec, sys_req,
};
use crate::std::std_system::StdReq;

View File

@@ -9,8 +9,8 @@ use hashbrown::HashMap;
use orchid_api_derive::Coding;
use orchid_api_traits::{Encode, Request};
use orchid_base::{IStr, Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
use orchid_extension::conv::ToExpr;
use orchid_extension::expr::Expr;
use orchid_extension::ToExpr;
use orchid_extension::Expr;
use orchid_extension::{
Atomic, DeserializeCtx, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports,
};

View File

@@ -4,7 +4,7 @@ use hashbrown::HashMap;
use itertools::Itertools;
use orchid_base::{is, mk_errv};
use orchid_extension::TAtom;
use orchid_extension::expr::Expr;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{arg, new_atom};
use orchid_extension::tree::{GenMember, cnst, fun, prefix};

View File

@@ -3,12 +3,11 @@ use std::borrow::Cow;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::{NameLike, ReqHandleExt, Sym, es, is, mk_errv};
use orchid_extension::{Atomic, Supports, TAtom};
use orchid_extension::{OwnedAtom, OwnedVariant};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::new_atom;
use orchid_extension::system::sys_req;
use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{
Atomic, Expr, ExprHandle, OwnedAtom, OwnedVariant, Supports, TAtom, sys_req,
};
use crate::std::std_system::StdReq;
use crate::std::string::str_atom::IntStrAtom;

View File

@@ -1,18 +1,14 @@
use std::cell::OnceCell;
use std::rc::Rc;
use futures::future::join_all;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_base::{Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
use orchid_extension::{AtomOps, AtomicFeatures};
use orchid_extension::conv::ToExpr;
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::LexerObj;
use orchid_extension::parser::ParserObj;
use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::ParserObj;
use orchid_extension::tree::{GenMember, merge_trivial};
use orchid_extension::{
AtomOps, AtomicFeatures, Expr, LexerObj, ReqForSystem, System, SystemCard, SystemCtor, ToExpr,
};
use super::number::num_lib::gen_num_lib;
use super::string::str_atom::{IntStrAtom, StrAtom};
@@ -20,7 +16,6 @@ use super::string::str_lib::gen_str_lib;
use crate::std::binary::binary_atom::BlobAtom;
use crate::std::binary::binary_lib::gen_binary_lib;
use crate::std::boolean::gen_bool_lib;
use crate::std::future::future_lib::{FutureReq, Scheduler, gen_future_lib};
use crate::std::number::num_atom::{CreateFloat, CreateInt};
use crate::std::number::num_lexer::NumLexer;
use crate::std::ops::gen_ops_lib;
@@ -49,19 +44,17 @@ pub enum StdReq {
CreateTuple(CreateTuple),
CreateRecord(CreateRecord),
CreateSymAtom(CreateSymAtom),
FutureReq(FutureReq),
}
#[derive(Debug, Default)]
pub struct StdSystem {
pub(crate) sched: OnceCell<Scheduler>,
}
pub struct StdSystem;
impl SystemCtor for StdSystem {
type Deps = ();
type Card = Self;
type Instance = Self;
const NAME: &'static str = "orchid::std";
const VERSION: f64 = 0.00_01;
fn inst(&self, _: ()) -> Self::Instance { Self::default() }
fn inst(&self, _: ()) -> Self::Instance { Self }
}
impl SystemCard for StdSystem {
type Ctor = Self;
@@ -83,16 +76,13 @@ impl SystemCard for StdSystem {
}
}
impl System for StdSystem {
async fn request<'a>(&self, xreq: Box<dyn ReqHandle<'a> + 'a>, req: Self::Req) -> Receipt<'a> {
type Ctor = Self;
async fn request<'a>(
&self,
xreq: Box<dyn ReqHandle<'a> + 'a>,
req: ReqForSystem<Self>,
) -> Receipt<'a> {
match req {
StdReq::FutureReq(req) => {
let sched = self.sched.get_or_init(Scheduler::default);
match req {
FutureReq::AddAsyncWork(req) => xreq.reply(&req, &sched.add(&req).await).await.unwrap(),
FutureReq::FinishAsyncWork(req) =>
xreq.reply(&req, &sched.finish(&req).await).await.unwrap(),
}
},
StdReq::CreateInt(ref req @ CreateInt(int)) =>
xreq.reply(req, &new_atom(int).to_expr().await.serialize().await).await.unwrap(),
StdReq::CreateFloat(ref req @ CreateFloat(float)) =>
@@ -151,7 +141,6 @@ impl System for StdSystem {
gen_binary_lib(),
gen_stream_lib(),
gen_time_lib(),
gen_future_lib(),
])
}
async fn prelude(&self) -> Vec<Sym> {

View File

@@ -1,12 +1,12 @@
use std::borrow::Cow;
use std::io;
use std::rc::Rc;
use never::Never;
use orchid_base::{OrcRes, fmt, is, mk_errv};
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::{GExpr, bot, call, new_atom};
use orchid_extension::stream_reqs::{ReadLimit, ReadReq};
use orchid_extension::{Atomic, ForeignAtom, OwnedAtom, OwnedVariant};
use orchid_base::{ReqHandleExt, fmt, is, mk_errv};
use orchid_extension::gen_expr::{bot, call, new_atom};
use orchid_extension::std_reqs::{ReadLimit, ReadReq, RunCommand};
use orchid_extension::{Atomic, Expr, ForeignAtom, OwnedAtom, OwnedVariant, Supports, ToExpr};
use crate::std::binary::binary_atom::BlobAtom;
@@ -24,16 +24,22 @@ impl Atomic for ReadStreamCmd {
impl OwnedAtom for ReadStreamCmd {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn command(self) -> OrcRes<Option<GExpr>> {
match self.hand.call(ReadReq(self.limit)).await {
}
impl Supports<RunCommand> for ReadStreamCmd {
async fn handle<'a>(
&self,
hand: Box<dyn orchid_base::ReqHandle<'a> + '_>,
req: RunCommand,
) -> io::Result<orchid_base::Receipt<'a>> {
let ret = match self.hand.call(ReadReq(self.limit.clone())).await {
None => Err(mk_errv(
is("Atom is not readable").await,
format!("Expected a readable stream handle, found {}", fmt(&self.hand).await),
[self.hand.pos()],
)),
Some(Err(e)) => Ok(Some(
Some(Err(e)) => Ok(
call(
self.fail,
self.fail.clone(),
bot(mk_errv(
is(e.kind.message()).await,
format!("An error occurred while reading: {}", e.message),
@@ -41,8 +47,9 @@ impl OwnedAtom for ReadStreamCmd {
)),
)
.await,
)),
Some(Ok(v)) => Ok(Some(call(self.succ, new_atom(BlobAtom(Rc::new(v)))).await)),
}
),
Some(Ok(v)) => Ok(call(self.succ.clone(), new_atom(BlobAtom(Rc::new(v)))).await),
};
hand.reply(&req, &Some(ret.to_gen().await.serialize().await)).await
}
}

View File

@@ -1,12 +1,14 @@
use std::num::NonZero;
use std::rc::Rc;
use orchid_base::{is, mk_errv};
use orchid_extension::ForeignAtom;
use orchid_extension::expr::Expr;
use orchid_extension::func_atom::get_arg;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::stream_reqs::ReadLimit;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::std_reqs::ReadLimit;
use orchid_extension::tree::{GenMember, comments, fun, prefix};
use orchid_extension::{Expr, ForeignAtom, get_arg};
use crate::Int;
use crate::std::binary::binary_atom::BlobAtom;
use crate::std::stream::stream_cmds::ReadStreamCmd;
pub fn gen_stream_lib() -> Vec<GenMember> {
@@ -30,7 +32,16 @@ pub fn gen_stream_lib() -> Vec<GenMember> {
Ok(new_atom(ReadStreamCmd { hand, succ, fail, limit: ReadLimit::Delimiter(end) }))
}),
fun(true, "read_bytes", async |hand: ForeignAtom, count: Int, succ: Expr, fail: Expr| {
Int(todo!())
match count.0.try_into().map(NonZero::new) {
Ok(Some(nzlen)) =>
Ok(new_atom(ReadStreamCmd { hand, succ, fail, limit: ReadLimit::Length(nzlen) })),
Ok(None) => Ok(call(succ, new_atom(BlobAtom(Rc::default()))).await),
Err(_) => Err(mk_errv(
is("Length cannot be negative").await,
format!("{} is negative and cannot be used as a length", count.0),
[get_arg(1).pos().await],
)),
}
}),
]),
)])

View File

@@ -10,8 +10,8 @@ use orchid_api_traits::{Encode, Request};
use orchid_base::{
FmtCtx, FmtUnit, IStr, OrcRes, Receipt, ReqHandle, ReqHandleExt, Sym, es, is, mk_errv, sym,
};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::Expr;
use orchid_extension::{
AtomMethod, Atomic, DeserializeCtx, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom,
};

View File

@@ -1,9 +1,9 @@
use itertools::Itertools;
use orchid_base::{OrcErr, OrcErrv, OrcRes, Paren, SrcRange, Sym, is, mk_errv, sym, wrap_tokv};
use orchid_extension::conv::ToExpr;
use orchid_extension::ToExpr;
use orchid_extension::gen_expr::new_atom;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::parser::p_tree2gen;
use orchid_extension::{LexContext, Lexer, err_not_applicable};
use orchid_extension::p_tree2gen;
use orchid_extension::tree::{GenTok, GenTokTree, ref_tok, x_tok};
use super::str_atom::IntStrAtom;

View File

@@ -1,12 +1,9 @@
use std::rc::Rc;
use orchid_base::{fmt, is, mk_errv, sym};
use orchid_extension::ForeignAtom;
use orchid_extension::coroutine_exec::exec;
use orchid_extension::expr::Expr;
use orchid_extension::func_atom::get_arg;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, comments, fun, prefix};
use orchid_extension::{Expr, ForeignAtom, exec, get_arg};
use unicode_segmentation::UnicodeSegmentation;
use super::str_atom::StrAtom;

View File

@@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::io;
use std::time::Instant;
use chrono::TimeDelta;
@@ -6,13 +7,14 @@ use never::Never;
use orchid_api::ExprTicket;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::{Numeric, OrcRes};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_base::{Numeric, OrcRes, Receipt, ReqHandle, ReqHandleExt};
use orchid_extension::gen_expr::{GExpr, call, new_atom};
use orchid_extension::system::sys_req;
use orchid_extension::std_reqs::{AsDuration, RunCommand};
use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom, ThinAtom, ThinVariant};
use orchid_extension::{
Atomic, Expr, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom, ThinAtom, ThinVariant,
ToExpr, TryFromExpr, sys_req,
};
use ordered_float::NotNan;
use crate::std::std_system::StdReq;
@@ -42,6 +44,15 @@ impl TryFromExpr for OrcDT {
Ok(TAtom::<OrcDT>::try_from_expr(expr).await?.value)
}
}
impl Supports<AsDuration> for OrcDT {
async fn handle<'a>(
&self,
hand: Box<dyn ReqHandle<'a> + '_>,
req: AsDuration,
) -> std::io::Result<Receipt<'a>> {
hand.reply(&req, &self.0.to_std().unwrap()).await
}
}
#[derive(Clone)]
pub struct InstantAtom(Instant);
@@ -59,12 +70,20 @@ struct Now(Expr);
impl Atomic for Now {
type Variant = OwnedVariant;
type Data = ();
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<RunCommand>() }
}
impl OwnedAtom for Now {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn command(self) -> OrcRes<Option<GExpr>> {
Ok(Some(call(self.0, new_atom(InstantAtom(Instant::now())))))
}
impl Supports<RunCommand> for Now {
async fn handle<'a>(
&self,
hand: Box<dyn ReqHandle<'a> + '_>,
req: RunCommand,
) -> io::Result<Receipt<'a>> {
let cont = call(self.0.clone(), new_atom(InstantAtom(Instant::now()))).await.serialize().await;
hand.reply(&req, &Some(cont)).await
}
}

View File

@@ -9,13 +9,11 @@ use never::Never;
use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request;
use orchid_base::{FmtCtx, FmtUnit, Format, OrcRes, ReqHandleExt, Sym, Variants, is, mk_errv, sym};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, new_atom};
use orchid_extension::system::sys_req;
use orchid_extension::tree::{GenMember, cnst, fun, prefix};
use orchid_extension::{
Atomic, DeserializeCtx, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom,
Atomic, DeserializeCtx, Expr, ExprHandle, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports,
TAtom, ToExpr, TryFromExpr, sys_req,
};
use crate::std::protocol::types::{GetImpl, ProtocolMethod};
@@ -171,9 +169,8 @@ pub struct Tpl<T>(pub T);
mod tpl_impls {
use itertools::Itertools;
use orchid_base::{OrcRes, is, mk_errv};
use orchid_extension::conv::{ToExpr, TryFromExpr};
use orchid_extension::expr::Expr;
use orchid_extension::gen_expr::GExpr;
use orchid_extension::{Expr, ToExpr, TryFromExpr};
use super::{Tpl, UntypedTuple};

View File

@@ -169,8 +169,8 @@ impl JoinHandle for JoinHandleImpl {
struct SpawnerImpl;
impl Spawner for SpawnerImpl {
fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle> {
Box::new(JoinHandleImpl(spawn_local(fut)))
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle> {
Box::new(JoinHandleImpl(spawn_local(tokio::time::sleep(delay).then(|()| fut))))
}
}
@@ -317,13 +317,14 @@ async fn main() -> io::Result<ExitCode> {
let mut xctx = ExecCtx::new(root.clone(), entrypoint).await;
eprintln!("executed");
xctx.set_gas(Some(1000));
xctx.execute().await;
match xctx.result() {
ExecResult::Value(val) => println!(
"{const_name} = {}",
take_first(&val.print(&FmtCtxImpl::default()).await, false)
),
ExecResult::Err(e) => println!("error: {e}"),
match xctx.execute().await {
ExecResult::Value(val, _) => {
println!(
"{const_name} = {}",
take_first(&val.print(&FmtCtxImpl::default()).await, false)
)
},
ExecResult::Err(e, _) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
}
@@ -436,12 +437,11 @@ async fn main() -> io::Result<ExitCode> {
let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos());
let mut xctx = ExecCtx::new(root.clone(), expr).await;
xctx.set_gas(Some(10_000));
xctx.execute().await;
match xctx.result() {
ExecResult::Value(val) => {
match xctx.execute().await {
ExecResult::Value(val, _) => {
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
},
ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Err(e, _) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
},