terrified to start testing
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1139,6 +1139,7 @@ dependencies = [
|
||||
"ordered-float",
|
||||
"pastey",
|
||||
"substack",
|
||||
"task-local",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"trait-set",
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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() }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
60
orchid-extension/src/cmd_atom.rs
Normal file
60
orchid-extension/src/cmd_atom.rs
Normal 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),
|
||||
}
|
||||
})
|
||||
})))
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
60
orchid-extension/src/system_card.rs
Normal file
60
orchid-extension/src/system_card.rs
Normal 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()
|
||||
}
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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() }
|
||||
|
||||
249
orchid-host/src/cmd_system.rs
Normal file
249
orchid-host/src/cmd_system.rs
Normal 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(()) }
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)) => {
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() }
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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 {}
|
||||
}
|
||||
|
||||
@@ -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>> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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")
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -1 +1 @@
|
||||
pub mod future_lib;
|
||||
// pub mod future_lib;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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],
|
||||
)),
|
||||
}
|
||||
}),
|
||||
]),
|
||||
)])
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
|
||||
@@ -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!"),
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user