reference cycles resolved

This commit is contained in:
2025-02-22 19:01:05 +01:00
parent cfa8b6ee52
commit 5e474069e0
32 changed files with 433 additions and 388 deletions

6
Cargo.lock generated
View File

@@ -1137,6 +1137,7 @@ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"async-once-cell", "async-once-cell",
"async-std", "async-std",
"async-stream",
"derive_destructure", "derive_destructure",
"dyn-clone", "dyn-clone",
"futures", "futures",
@@ -1155,6 +1156,7 @@ dependencies = [
"paste", "paste",
"some_executor 0.4.0", "some_executor 0.4.0",
"substack", "substack",
"tokio",
"trait-set", "trait-set",
] ]
@@ -1223,9 +1225,9 @@ dependencies = [
[[package]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "4.6.0" version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01"
dependencies = [ dependencies = [
"num-traits", "num-traits",
] ]

View File

@@ -11,4 +11,4 @@ async-stream = "0.3.6"
futures = "0.3.31" futures = "0.3.31"
itertools = "0.14.0" itertools = "0.14.0"
never = "0.1.0" never = "0.1.0"
ordered-float = "4.6.0" ordered-float = "5.0.0"

View File

@@ -9,8 +9,7 @@ use std::sync::Arc;
use async_std::io::{Read, ReadExt, Write, WriteExt}; use async_std::io::{Read, ReadExt, Write, WriteExt};
use async_stream::stream; use async_stream::stream;
use futures::future::LocalBoxFuture; use futures::StreamExt;
use futures::{FutureExt, StreamExt};
use never::Never; use never::Never;
use ordered_float::NotNan; use ordered_float::NotNan;
@@ -28,11 +27,8 @@ pub trait Encode {
pub trait Coding: Encode + Decode + Clone { pub trait Coding: Encode + Decode + Clone {
fn get_decoder<T: 'static, F: Future<Output = T> + 'static>( fn get_decoder<T: 'static, F: Future<Output = T> + 'static>(
map: impl Fn(Self) -> F + Clone + 'static, map: impl Fn(Self) -> F + Clone + 'static,
) -> impl for<'a> Fn(Pin<&'a mut dyn Read>) -> LocalBoxFuture<'a, T> { ) -> impl AsyncFn(Pin<&mut dyn Read>) -> T {
move |r| { async move |r| map(Self::decode(r).await).await
let map = map.clone();
async move { map(Self::decode(r).await).await }.boxed_local()
}
} }
} }
impl<T: Encode + Decode + Clone> Coding for T {} impl<T: Encode + Decode + Clone> Coding for T {}

View File

@@ -6,7 +6,7 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ordered-float = "4.6.0" ordered-float = "5.0.0"
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
async-std = "1.13.0" async-std = "1.13.0"

View File

@@ -20,7 +20,7 @@ num-traits = "0.2.19"
orchid-api = { version = "0.1.0", path = "../orchid-api" } orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
ordered-float = "4.6.0" ordered-float = "5.0.0"
regex = "1.11.1" regex = "1.11.1"
rust-embed = "8.5.0" rust-embed = "8.5.0"
some_executor = "0.4.0" some_executor = "0.4.0"

View File

@@ -6,7 +6,6 @@ use futures::future::LocalBoxFuture;
use crate::api; use crate::api;
pub type Spawner = Rc<dyn Fn(LocalBoxFuture<'static, ()>)>; pub type Spawner = Rc<dyn Fn(LocalBoxFuture<'static, ()>)>;
pub type RecvCB<'a> = Box<dyn for<'b> FnOnce(&'b [u8]) -> LocalBoxFuture<'b, ()> + 'a>;
/// The 3 primary contact points with an extension are /// The 3 primary contact points with an extension are
/// - send a message /// - send a message
@@ -16,7 +15,7 @@ pub type RecvCB<'a> = Box<dyn for<'b> FnOnce(&'b [u8]) -> LocalBoxFuture<'b, ()>
/// There are no ordering guarantees about these /// There are no ordering guarantees about these
pub trait ExtPort { pub trait ExtPort {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()>; fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()>;
fn recv<'a>(&'a self, cb: RecvCB<'a>) -> LocalBoxFuture<'a, ()>; fn recv(&self) -> LocalBoxFuture<'_, Option<Vec<u8>>>;
} }
pub struct ExtInit { pub struct ExtInit {
@@ -25,7 +24,7 @@ pub struct ExtInit {
} }
impl ExtInit { impl ExtInit {
pub async fn send(&self, msg: &[u8]) { self.port.send(msg).await } pub async fn send(&self, msg: &[u8]) { self.port.send(msg).await }
pub async fn recv<'a, 's: 'a>(&'s self, cb: RecvCB<'a>) { self.port.recv(Box::new(cb)).await } pub async fn recv(&self) -> Option<Vec<u8>> { self.port.recv().await }
} }
impl Deref for ExtInit { impl Deref for ExtInit {
type Target = api::ExtensionHeader; type Target = api::ExtensionHeader;

View File

@@ -3,7 +3,7 @@ use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use async_stream::stream; use async_stream::stream;
use futures::future::{LocalBoxFuture, join_all}; use futures::future::join_all;
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use never::Never; use never::Never;
use trait_set::trait_set; use trait_set::trait_set;
@@ -22,9 +22,8 @@ impl MacroSlot<'_> {
} }
trait_set! { trait_set! {
pub trait MacroAtomToApi<A> = for<'a> FnMut(&'a A) -> LocalBoxFuture<'a, api::MacroToken>; pub trait MacroAtomToApi<A> = AsyncFnMut(&A) -> api::MacroToken;
pub trait MacroAtomFromApi<'a, A> = pub trait MacroAtomFromApi<'a, A> = AsyncFnMut(&api::Atom) -> MTok<'a, A>;
for<'b> FnMut(&'b api::Atom) -> LocalBoxFuture<'b, MTok<'a, A>>;
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@@ -87,7 +86,7 @@ impl<'a, A> MTok<'a, A> {
}) })
} }
pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroToken { pub(crate) async fn to_api(&self, do_atom: &mut impl MacroAtomToApi<A>) -> api::MacroToken {
fn sink<T>(n: &Never) -> LocalBoxFuture<'_, T> { match *n {} } async fn sink<T>(n: &Never) -> T { match *n {} }
match_mapping!(&self, MTok => api::MacroToken { match_mapping!(&self, MTok => api::MacroToken {
Lambda(x => mtreev_to_api(x, do_atom).await, b => mtreev_to_api(b, do_atom).await), Lambda(x => mtreev_to_api(x, do_atom).await, b => mtreev_to_api(b, do_atom).await),
Name(t.tok().to_api()), Name(t.tok().to_api()),

View File

@@ -17,8 +17,8 @@ use hashbrown::HashMap;
use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request}; use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request};
use trait_set::trait_set; use trait_set::trait_set;
use crate::clone;
use crate::logging::Logger; use crate::logging::Logger;
use crate::{api, clone};
pub struct Receipt<'a>(PhantomData<&'a mut ()>); pub struct Receipt<'a>(PhantomData<&'a mut ()>);

View File

@@ -8,7 +8,7 @@ use std::sync::Arc;
pub use api::PhKind; pub use api::PhKind;
use async_stream::stream; use async_stream::stream;
use futures::future::{LocalBoxFuture, join_all}; use futures::future::join_all;
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
@@ -26,8 +26,7 @@ use crate::{api, match_mapping, tl_cache};
trait_set! { trait_set! {
pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>; pub trait RecurCB<'a, A: AtomRepr, X: ExtraTok> = Fn(TokTree<'a, A, X>) -> TokTree<'a, A, X>;
pub trait ExtraTok = Format + Clone + fmt::Debug; pub trait ExtraTok = Format + Clone + fmt::Debug;
pub trait RefDoExtra<X> = pub trait RefDoExtra<X> = AsyncFnMut(&X, Range<u32>) -> api::TokenTree;
for<'b> FnMut(&'b X, Range<u32>) -> LocalBoxFuture<'b, api::TokenTree>;
} }
pub fn recur<'a, A: AtomRepr, X: ExtraTok>( pub fn recur<'a, A: AtomRepr, X: ExtraTok>(

View File

@@ -9,6 +9,7 @@ edition = "2021"
ahash = "0.8.11" ahash = "0.8.11"
async-once-cell = "0.5.4" async-once-cell = "0.5.4"
async-std = "1.13.0" async-std = "1.13.0"
async-stream = "0.3.6"
derive_destructure = "1.0.0" derive_destructure = "1.0.0"
dyn-clone = "1.0.17" dyn-clone = "1.0.17"
futures = "0.3.31" futures = "0.3.31"
@@ -23,8 +24,9 @@ orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" } orchid-base = { version = "0.1.0", path = "../orchid-base" }
ordered-float = "4.6.0" ordered-float = "5.0.0"
paste = "1.0.15" paste = "1.0.15"
some_executor = "0.4.0" some_executor = "0.4.0"
substack = "1.1.1" substack = "1.1.1"
tokio = { version = "1.43.0", optional = true }
trait-set = "0.3.0" trait-set = "0.3.0"

View File

@@ -319,8 +319,8 @@ trait_set! {
} }
pub struct AtomFactory(Box<dyn AtomFactoryFn>); pub struct AtomFactory(Box<dyn AtomFactoryFn>);
impl AtomFactory { impl AtomFactory {
pub fn new<F: Future<Output = api::Atom> + 'static>( pub fn new(
f: impl FnOnce(SysCtx) -> F + Clone + 'static, f: impl AsyncFnOnce(SysCtx) -> api::Atom + Clone + 'static,
) -> Self { ) -> Self {
Self(Box::new(|ctx| f(ctx).boxed_local())) Self(Box::new(|ctx| f(ctx).boxed_local()))
} }

View File

@@ -35,7 +35,7 @@ pub struct OwnedVariant;
impl AtomicVariant for OwnedVariant {} impl AtomicVariant for OwnedVariant {}
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A { impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
fn _factory(self) -> AtomFactory { fn _factory(self) -> AtomFactory {
AtomFactory::new(move |ctx| async move { AtomFactory::new(async move |ctx| {
let serial = let serial =
ctx.get_or_default::<ObjStore>().next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); ctx.get_or_default::<ObjStore>().next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap()); let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap());
@@ -82,25 +82,26 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
fn tid(&self) -> TypeId { TypeId::of::<T>() } fn tid(&self) -> TypeId { TypeId::of::<T>() }
fn name(&self) -> &'static str { type_name::<T>() } fn name(&self) -> &'static str { type_name::<T>() }
fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> { fn decode<'a>(&'a self, AtomCtx(data, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
async { Box::pin(async {
Box::new(<T as AtomCard>::Data::decode(Pin::new(&mut &data[..])).await) as Box<dyn Any> Box::new(<T as AtomCard>::Data::decode(Pin::new(&mut &data[..])).await) as Box<dyn Any>
} })
.boxed_local()
} }
fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> { fn call(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> LocalBoxFuture<'_, GExpr> {
async move { take_atom(id.unwrap(), &ctx).await.dyn_call(ctx.clone(), arg).await }.boxed_local() Box::pin(async move { take_atom(id.unwrap(), &ctx).await.dyn_call(ctx.clone(), arg).await })
} }
fn call_ref<'a>( fn call_ref<'a>(
&'a self, &'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>, AtomCtx(_, id, ctx): AtomCtx<'a>,
arg: api::ExprTicket, arg: api::ExprTicket,
) -> LocalBoxFuture<'a, GExpr> { ) -> LocalBoxFuture<'a, GExpr> {
async move { AtomReadGuard::new(id.unwrap(), &ctx).await.dyn_call_ref(ctx.clone(), arg).await } Box::pin(async move {
.boxed_local() AtomReadGuard::new(id.unwrap(), &ctx).await.dyn_call_ref(ctx.clone(), arg).await
})
} }
fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> { fn print(&self, AtomCtx(_, id, ctx): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
async move { AtomReadGuard::new(id.unwrap(), &ctx).await.dyn_print(ctx.clone()).await } Box::pin(
.boxed_local() async move { AtomReadGuard::new(id.unwrap(), &ctx).await.dyn_print(ctx.clone()).await },
)
} }
fn handle_req<'a, 'b: 'a, 'c: 'a>( fn handle_req<'a, 'b: 'a, 'c: 'a>(
&'a self, &'a self,
@@ -109,34 +110,32 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
req: Pin<&'b mut dyn Read>, req: Pin<&'b mut dyn Read>,
rep: Pin<&'c mut dyn Write>, rep: Pin<&'c mut dyn Write>,
) -> LocalBoxFuture<'a, bool> { ) -> LocalBoxFuture<'a, bool> {
async move { Box::pin(async move {
let a = AtomReadGuard::new(id.unwrap(), &ctx).await; let a = AtomReadGuard::new(id.unwrap(), &ctx).await;
let ms = self.ms.get_or_init(self.msbuild.pack(ctx.clone())).await; let ms = self.ms.get_or_init(self.msbuild.pack(ctx.clone())).await;
ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx.clone(), key, req, rep).await ms.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx.clone(), key, req, rep).await
} })
.boxed_local()
} }
fn command<'a>( fn command<'a>(
&'a self, &'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>, AtomCtx(_, id, ctx): AtomCtx<'a>,
) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> { ) -> LocalBoxFuture<'a, OrcRes<Option<GExpr>>> {
async move { take_atom(id.unwrap(), &ctx).await.dyn_command(ctx.clone()).await }.boxed_local() Box::pin(async move { take_atom(id.unwrap(), &ctx).await.dyn_command(ctx.clone()).await })
} }
fn drop(&self, AtomCtx(_, id, ctx): AtomCtx) -> LocalBoxFuture<'_, ()> { fn drop(&self, AtomCtx(_, id, ctx): AtomCtx) -> LocalBoxFuture<'_, ()> {
async move { take_atom(id.unwrap(), &ctx).await.dyn_free(ctx.clone()).await }.boxed_local() Box::pin(async move { take_atom(id.unwrap(), &ctx).await.dyn_free(ctx.clone()).await })
} }
fn serialize<'a, 'b: 'a>( fn serialize<'a, 'b: 'a>(
&'a self, &'a self,
AtomCtx(_, id, ctx): AtomCtx<'a>, AtomCtx(_, id, ctx): AtomCtx<'a>,
mut write: Pin<&'b mut dyn Write>, mut write: Pin<&'b mut dyn Write>,
) -> LocalBoxFuture<'a, Option<Vec<api::ExprTicket>>> { ) -> LocalBoxFuture<'a, Option<Vec<api::ExprTicket>>> {
async move { Box::pin(async move {
let id = id.unwrap(); let id = id.unwrap();
id.encode(write.as_mut()).await; id.encode(write.as_mut()).await;
let refs = AtomReadGuard::new(id, &ctx).await.dyn_serialize(ctx.clone(), write).await; let refs = AtomReadGuard::new(id, &ctx).await.dyn_serialize(ctx.clone(), write).await;
refs.map(|v| v.into_iter().map(|t| t.handle().tk).collect_vec()) refs.map(|v| v.into_iter().map(|t| t.handle().tk).collect_vec())
} })
.boxed_local()
} }
fn deserialize<'a>( fn deserialize<'a>(
&'a self, &'a self,
@@ -144,13 +143,12 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
data: &'a [u8], data: &'a [u8],
refs: &'a [api::ExprTicket], refs: &'a [api::ExprTicket],
) -> LocalBoxFuture<'a, api::Atom> { ) -> LocalBoxFuture<'a, api::Atom> {
async move { Box::pin(async move {
let refs = let refs =
refs.iter().map(|tk| Expr::from_handle(Rc::new(ExprHandle::from_args(ctx.clone(), *tk)))); refs.iter().map(|tk| Expr::from_handle(Rc::new(ExprHandle::from_args(ctx.clone(), *tk))));
let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs)).await; let obj = T::deserialize(DeserCtxImpl(data, &ctx), T::Refs::from_iter(refs)).await;
obj._factory().build(ctx).await obj._factory().build(ctx).await
} })
.boxed_local()
} }
} }

View File

@@ -25,7 +25,7 @@ pub struct ThinVariant;
impl AtomicVariant for ThinVariant {} impl AtomicVariant for ThinVariant {}
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A { impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
fn _factory(self) -> AtomFactory { fn _factory(self) -> AtomFactory {
AtomFactory::new(move |ctx| async move { AtomFactory::new(async move |ctx| {
let (id, _) = get_info::<A>(ctx.get::<CtedObj>().inst().card()); let (id, _) = get_info::<A>(ctx.get::<CtedObj>().inst().card());
let mut buf = enc_vec(&id).await; let mut buf = enc_vec(&id).await;
self.encode(Pin::new(&mut buf)).await; self.encode(Pin::new(&mut buf)).await;
@@ -42,12 +42,12 @@ pub struct ThinAtomDynfo<T: ThinAtom> {
} }
impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> { impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn print<'a>(&self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> { fn print<'a>(&self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit> {
async move { T::decode(Pin::new(&mut &buf[..])).await.print(ctx).await }.boxed_local() Box::pin(async move { T::decode(Pin::new(&mut &buf[..])).await.print(ctx).await })
} }
fn tid(&self) -> TypeId { TypeId::of::<T>() } fn tid(&self) -> TypeId { TypeId::of::<T>() }
fn name(&self) -> &'static str { type_name::<T>() } fn name(&self) -> &'static str { type_name::<T>() }
fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> { fn decode<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>) -> LocalBoxFuture<'a, Box<dyn Any>> {
async { Box::new(T::decode(Pin::new(&mut &buf[..])).await) as Box<dyn Any> }.boxed_local() Box::pin(async { Box::new(T::decode(Pin::new(&mut &buf[..])).await) as Box<dyn Any> })
} }
fn call<'a>( fn call<'a>(
&'a self, &'a self,
@@ -102,14 +102,13 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
refs: &'a [api::ExprTicket], refs: &'a [api::ExprTicket],
) -> LocalBoxFuture<'a, api::Atom> { ) -> LocalBoxFuture<'a, api::Atom> {
assert!(refs.is_empty(), "Refs found when deserializing thin atom"); assert!(refs.is_empty(), "Refs found when deserializing thin atom");
async { T::decode(Pin::new(&mut &data[..])).await._factory().build(ctx).await }.boxed_local() Box::pin(async { T::decode(Pin::new(&mut &data[..])).await._factory().build(ctx).await })
} }
fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> { fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> {
async move { Box::pin(async move {
let string_self = T::decode(Pin::new(&mut &buf[..])).await.print(ctx.clone()).await; let string_self = T::decode(Pin::new(&mut &buf[..])).await.print(ctx.clone()).await;
writeln!(ctx.logger(), "Received drop signal for non-drop atom {string_self:?}"); writeln!(ctx.logger(), "Received drop signal for non-drop atom {string_self:?}");
} })
.boxed_local()
} }
} }

View File

@@ -1,23 +1,20 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::future::Future; use std::future::Future;
use std::io::Write;
use std::mem; use std::mem;
use std::num::NonZero; use std::num::NonZero;
use std::pin::Pin; use std::pin::Pin;
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use async_std::channel::{Receiver, Sender}; use async_std::channel::{self, Receiver, RecvError, Sender};
use async_std::stream; use async_std::stream;
use async_std::sync::Mutex; use async_std::sync::Mutex;
use futures::future::{LocalBoxFuture, join_all}; use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt, stream_select};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_api::{ApplyMacro, ExtMsgSet}; use orchid_api::{ApplyMacro, ExtMsgSet};
use orchid_api_traits::{Decode, Encode, enc_vec}; use orchid_api_traits::{Decode, enc_vec};
use orchid_base::builtin::{ExtPort, Spawner}; use orchid_base::builtin::{ExtInit, ExtPort, Spawner};
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter}; use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
use orchid_base::clone; use orchid_base::clone;
use orchid_base::interner::{Interner, Tok}; use orchid_base::interner::{Interner, Tok};
@@ -36,7 +33,6 @@ use crate::atom_owned::take_atom;
use crate::fs::VirtFS; use crate::fs::VirtFS;
use crate::lexer::{LexContext, err_cascade, err_not_applicable}; use crate::lexer::{LexContext, err_cascade, err_not_applicable};
use crate::macros::{Rule, RuleCtx}; use crate::macros::{Rule, RuleCtx};
use crate::msg::{recv_parent_msg, send_parent_msg};
use crate::system::{SysCtx, atom_by_idx}; use crate::system::{SysCtx, atom_by_idx};
use crate::system_ctor::{CtedObj, DynSystemCtor}; use crate::system_ctor::{CtedObj, DynSystemCtor};
use crate::tree::{GenTok, GenTokTree, LazyMemberFactory, TreeIntoApiCtxImpl, do_extra}; use crate::tree::{GenTok, GenTokTree, LazyMemberFactory, TreeIntoApiCtxImpl, do_extra};
@@ -69,18 +65,18 @@ pub struct SystemRecord {
} }
trait_set! { trait_set! {
pub trait WARCallback<'a, T> = FnOnce( pub trait WithAtomRecordCallback<'a, T> = AsyncFnOnce(
Box<dyn AtomDynfo>, Box<dyn AtomDynfo>,
SysCtx, SysCtx,
AtomTypeId, AtomTypeId,
&'a [u8] &'a [u8]
) -> LocalBoxFuture<'a, T> ) -> T
} }
pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>( pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>(
get_sys_ctx: &impl Fn(api::SysId) -> F, get_sys_ctx: &impl Fn(api::SysId) -> F,
atom: &'a api::Atom, atom: &'a api::Atom,
cb: impl WARCallback<'a, T>, cb: impl WithAtomRecordCallback<'a, T>,
) -> T { ) -> T {
let mut data = &atom.data[..]; let mut data = &atom.data[..];
let ctx = get_sys_ctx(atom.owner).await; let ctx = get_sys_ctx(atom.owner).await;
@@ -104,49 +100,41 @@ pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>(
// } // }
pub struct ExtensionOwner { pub struct ExtensionOwner {
rn: ReqNot<api::ExtMsgSet>, _interner_cell: Rc<RefCell<Option<Interner>>>,
_systems_lock: Rc<Mutex<HashMap<api::SysId, SystemRecord>>>,
out_recv: Receiver<Vec<u8>>, out_recv: Receiver<Vec<u8>>,
out_send: Sender<Vec<u8>>, out_send: Sender<Vec<u8>>,
ext_header: api::ExtensionHeader,
} }
impl ExtPort for ExtensionOwner { impl ExtPort for ExtensionOwner {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> { fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> {
self.rn.receive(msg).boxed_local() Box::pin(async { self.out_send.send(msg.to_vec()).boxed_local().await.unwrap() })
} }
fn recv<'a>( fn recv(&self) -> LocalBoxFuture<'_, Option<Vec<u8>>> {
&'a self, Box::pin(async {
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>, match self.out_recv.recv().await {
) -> LocalBoxFuture<'a, ()> { Ok(v) => Some(v),
async { Err(RecvError) => None,
let msg = self.out_recv.recv().await.unwrap(); }
cb(&msg[..]).await })
}
.boxed_local()
} }
} }
impl ExtensionOwner {
pub fn ext_header(&self) -> &api::ExtensionHeader { &self.ext_header }
// pub async fn new(data: ExtensionData, spawner: Spawner, header:
// api::HostHeader) -> Self { let decls =
// }
}
pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) { pub fn extension_init(
let api::HostHeader { log_strategy, msg_logs } = data: ExtensionData,
api::HostHeader::decode(Pin::new(&mut async_std::io::stdin())).await; host_header: api::HostHeader,
let mut buf = Vec::new(); spawner: Spawner,
) -> ExtInit {
let api::HostHeader { log_strategy, msg_logs } = host_header;
let decls = (data.systems.iter().enumerate()) let decls = (data.systems.iter().enumerate())
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys)) .map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
.map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap()))) .map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap())))
.collect_vec(); .collect_vec();
let systems_lock = Rc::new(Mutex::new(HashMap::<api::SysId, SystemRecord>::new())); let systems_lock = Rc::new(Mutex::new(HashMap::<api::SysId, SystemRecord>::new()));
api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() } let ext_header = api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() };
.encode(Pin::new(&mut buf)) let (out_send, in_recv) = channel::bounded::<Vec<u8>>(1);
.await; let (in_send, out_recv) = channel::bounded::<Vec<u8>>(1);
std::io::stdout().write_all(&buf).unwrap(); let (exit_send, exit_recv) = channel::bounded(1);
std::io::stdout().flush().unwrap();
let exiting = Arc::new(AtomicBool::new(false));
let logger = Logger::new(log_strategy); let logger = Logger::new(log_strategy);
let msg_logger = Logger::new(msg_logs); let msg_logger = Logger::new(msg_logs);
let interner_cell = Rc::new(RefCell::new(None::<Interner>)); let interner_cell = Rc::new(RefCell::new(None::<Interner>));
@@ -159,9 +147,9 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
x x
})); }));
let init_ctx = { let init_ctx = {
clone!(systems_weak, interner_weak, spawner, logger); clone!(interner_weak, spawner, logger);
move |id: api::SysId, cted: CtedObj, reqnot: ReqNot<ExtMsgSet>| { move |id: api::SysId, cted: CtedObj, reqnot: ReqNot<ExtMsgSet>| {
clone!(systems_weak, interner_weak, spawner, logger; async move { clone!(interner_weak, spawner, logger; async move {
let interner_rc = let interner_rc =
interner_weak.upgrade().expect("System construction order while shutting down"); interner_weak.upgrade().expect("System construction order while shutting down");
let i = interner_rc.borrow().clone().expect("mk_ctx called very early, no interner!"); let i = interner_rc.borrow().clone().expect("mk_ctx called very early, no interner!");
@@ -171,11 +159,11 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
}; };
let rn = ReqNot::<api::ExtMsgSet>::new( let rn = ReqNot::<api::ExtMsgSet>::new(
msg_logger.clone(), msg_logger.clone(),
move |a, _| async move { send_parent_msg(a).await.unwrap() }.boxed_local(), move |a, _| clone!(in_send; Box::pin(async move { in_send.send(a.to_vec()).await.unwrap() })),
clone!(systems_weak, exiting, get_ctx; move |n, reqnot| { clone!(systems_weak, exit_send, get_ctx; move |n, _| {
clone!(systems_weak, exiting, get_ctx; async move { clone!(systems_weak, exit_send, get_ctx; async move {
match n { match n {
api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), api::HostExtNotif::Exit => exit_send.send(()).await.unwrap(),
api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) => api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) =>
if let Some(rc) = systems_weak.upgrade() { if let Some(rc) = systems_weak.upgrade() {
mem::drop(rc.lock().await.remove(&sys_id)) mem::drop(rc.lock().await.remove(&sys_id))
@@ -188,9 +176,9 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
}.boxed_local()) }.boxed_local())
}), }),
{ {
clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, spawner, decls, msg_logger); clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, decls, msg_logger);
move |hand, req| { move |hand, req| {
clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, spawner, decls, msg_logger); clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, decls, msg_logger);
async move { async move {
let interner_cell = interner_weak.upgrade().expect("Interner dropped before request"); let interner_cell = interner_weak.upgrade().expect("Interner dropped before request");
let i = interner_cell.borrow().clone().expect("Request arrived before interner set"); let i = interner_cell.borrow().clone().expect("Request arrived before interner set");
@@ -302,11 +290,8 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
return hand.handle(&lex, &eopt).await; return hand.handle(&lex, &eopt).await;
}, },
Ok((s, expr)) => { Ok((s, expr)) => {
let expr = expr let expr =
.to_api(&mut |f, r| { expr.to_api(&mut async |f, r| do_extra(f, r, sys_ctx.clone()).await).await;
clone!(sys_ctx; async move { do_extra(f, r, sys_ctx).await }).boxed_local()
})
.await;
let pos = (text.len() - s.len()) as u32; let pos = (text.len() - s.len()) as u32;
return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await; return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await;
}, },
@@ -329,12 +314,9 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
let o_line = match parser.parse(*exported, comments, tail) { let o_line = match parser.parse(*exported, comments, tail) {
Err(e) => Err(e.to_api()), Err(e) => Err(e.to_api()),
Ok(t) => Ok( Ok(t) => Ok(
ttv_to_api(t, &mut |f, range| { ttv_to_api(t, &mut async move |f, range| api::TokenTree {
clone!(ctx); range,
async move { token: api::Token::Atom(f.clone().build(ctx.clone()).await),
api::TokenTree { range, token: api::Token::Atom(f.clone().build(ctx).await) }
}
.boxed_local()
}) })
.await, .await,
), ),
@@ -344,52 +326,49 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
api::HostExtReq::AtomReq(atom_req) => { api::HostExtReq::AtomReq(atom_req) => {
let atom = atom_req.get_atom(); let atom = atom_req.get_atom();
let atom_req = atom_req.clone(); let atom_req = atom_req.clone();
with_atom_record(&get_ctx, atom, move |nfo, ctx, id, buf| { with_atom_record(&get_ctx, atom, async move |nfo, ctx, id, buf| {
async move { let actx = AtomCtx(buf, atom.drop, ctx.clone());
let actx = AtomCtx(buf, atom.drop, ctx.clone()); match &atom_req {
match &atom_req { api::AtomReq::SerializeAtom(ser) => {
api::AtomReq::SerializeAtom(ser) => { let mut buf = enc_vec(&id).await;
let mut buf = enc_vec(&id).await; let refs_opt = nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await;
let refs_opt = nfo.serialize(actx, Pin::<&mut Vec<_>>::new(&mut buf)).await; hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await
hand.handle(ser, &refs_opt.map(|refs| (buf, refs))).await },
}, api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) => hand.handle(print, &nfo.print(actx).await.to_api()).await,
hand.handle(print, &nfo.print(actx).await.to_api()).await, api::AtomReq::Fwded(fwded) => {
api::AtomReq::Fwded(fwded) => { let api::Fwded(_, key, payload) = &fwded;
let api::Fwded(_, key, payload) = &fwded; let mut reply = Vec::new();
let mut reply = Vec::new(); let key = Sym::from_api(*key, &i).await;
let key = Sym::from_api(*key, &i).await; let some = nfo
let some = nfo .handle_req(
.handle_req( actx,
actx, key,
key, Pin::<&mut &[u8]>::new(&mut &payload[..]),
Pin::<&mut &[u8]>::new(&mut &payload[..]), Pin::<&mut Vec<_>>::new(&mut reply),
Pin::<&mut Vec<_>>::new(&mut reply), )
) .await;
.await; hand.handle(fwded, &some.then_some(reply)).await
hand.handle(fwded, &some.then_some(reply)).await },
}, api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => {
api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => { let ret = nfo.call_ref(actx, *arg).await;
let ret = nfo.call_ref(actx, *arg).await; hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await
hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await },
}, api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => {
api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => { let ret = nfo.call(actx, *arg).await;
let ret = nfo.call(actx, *arg).await; hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await
hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await },
}, api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await {
api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await { Err(e) => hand.handle(cmd, &Err(e.to_api())).await,
Err(e) => hand.handle(cmd, &Err(e.to_api())).await, Ok(opt) => match opt {
Ok(opt) => match opt { None => hand.handle(cmd, &Ok(api::NextStep::Halt)).await,
None => hand.handle(cmd, &Ok(api::NextStep::Halt)).await, Some(cont) => {
Some(cont) => { let cont = cont.api_return(ctx.clone(), &hand).await;
let cont = cont.api_return(ctx.clone(), &hand).await; hand.handle(cmd, &Ok(api::NextStep::Continue(cont))).await
hand.handle(cmd, &Ok(api::NextStep::Continue(cont))).await
},
}, },
}, },
} },
} }
.boxed_local()
}) })
.await .await
}, },
@@ -411,7 +390,7 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
for (k, v) in params { for (k, v) in params {
ctx.args.insert( ctx.args.insert(
Tok::from_api(k, &i).await, Tok::from_api(k, &i).await,
mtreev_from_api(&v, &i, &mut |_| panic!("No atom in macro prompt!")).await, mtreev_from_api(&v, &i, &mut async |_| panic!("No atom in macro prompt!")).await,
); );
} }
let err_cascade = err_cascade(&i).await; let err_cascade = err_cascade(&i).await;
@@ -424,10 +403,9 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
hand.handle_as(tok, &new_errors.map(|e| Err(e.to_api()))).await hand.handle_as(tok, &new_errors.map(|e| Err(e.to_api()))).await
}, },
Ok(t) => { Ok(t) => {
let result = mtreev_to_api(&t, &mut |a| { clone!(sys_ctx);
clone!(sys_ctx; async move { let result = mtreev_to_api(&t, &mut async |a| {
api::MacroToken::Atom(a.clone().build(sys_ctx.clone()).await) api::MacroToken::Atom(a.clone().build(sys_ctx.clone()).await)
}.boxed_local())
}) })
.await; .await;
hand.handle_as(tok, &Some(Ok(result))).await hand.handle_as(tok, &Some(Ok(result))).await
@@ -441,8 +419,22 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
}, },
); );
*interner_cell.borrow_mut() = Some(Interner::new_replica(rn.clone().map())); *interner_cell.borrow_mut() = Some(Interner::new_replica(rn.clone().map()));
while !exiting.load(Ordering::Relaxed) { spawner(Box::pin(clone!(spawner; async move {
let rcvd = recv_parent_msg().await.unwrap(); let mut streams = stream_select! { in_recv.map(Some), exit_recv.map(|_| None) };
spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd).await }))) while let Some(item) = streams.next().await {
match item {
Some(rcvd) => spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd[..]).await }))),
None => break,
}
}
})));
ExtInit {
header: ext_header,
port: Box::new(ExtensionOwner {
out_recv,
out_send,
_interner_cell: interner_cell,
_systems_lock: systems_lock,
}),
} }
} }

View File

@@ -133,8 +133,6 @@ impl OwnedAtom for Lambda {
} }
mod expr_func_derives { mod expr_func_derives {
use std::future::Future;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use super::ExprFunc; use super::ExprFunc;
@@ -147,9 +145,9 @@ mod expr_func_derives {
paste::paste!{ paste::paste!{
impl< impl<
$($t: TryFromExpr, )* $($t: TryFromExpr, )*
Fut: Future<Output: ToExpr>, Out: ToExpr,
Func: Fn($($t,)*) -> Fut + Clone + Send + Sync + 'static Func: AsyncFn($($t,)*) -> Out + Clone + Send + Sync + 'static
> ExprFunc<($($t,)*), Fut::Output> for Func { > ExprFunc<($($t,)*), Out> for Func {
const ARITY: u8 = $arity; const ARITY: u8 = $arity;
async fn apply(&self, v: Vec<Expr>) -> OrcRes<GExpr> { async fn apply(&self, v: Vec<Expr>) -> OrcRes<GExpr> {
assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch"); assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch");

View File

@@ -100,9 +100,9 @@ pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap())) inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
} }
pub fn with<I: TryFromExpr, Fut: Future<Output: ToExpr>>( pub fn with<I: TryFromExpr, O: ToExpr>(
expr: GExpr, expr: GExpr,
cont: impl Fn(I) -> Fut + Clone + Send + Sync + 'static, cont: impl AsyncFn(I) -> O + Clone + Send + Sync + 'static,
) -> GExpr { ) -> GExpr {
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr]) call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
} }

View File

@@ -16,4 +16,5 @@ pub mod other_system;
pub mod parser; pub mod parser;
pub mod system; pub mod system;
pub mod system_ctor; pub mod system_ctor;
pub mod tokio;
pub mod tree; pub mod tree;

View File

@@ -43,13 +43,13 @@ impl<'a> RuleCtx<'a> {
pub async fn recurse(&mut self, tree: &[MTree<'a, Never>]) -> OrcRes<Vec<MTree<'a, Never>>> { pub async fn recurse(&mut self, tree: &[MTree<'a, Never>]) -> OrcRes<Vec<MTree<'a, Never>>> {
let req = api::RunMacros { let req = api::RunMacros {
run_id: self.run_id, run_id: self.run_id,
query: mtreev_to_api(tree, &mut |b| match *b {}).await, query: mtreev_to_api(tree, &mut async |b| match *b {}).await,
}; };
let Some(treev) = self.sys.get::<ReqNot<ExtMsgSet>>().request(req).await else { let Some(treev) = self.sys.get::<ReqNot<ExtMsgSet>>().request(req).await else {
return Err(err_cascade(self.sys.i()).await.into()); return Err(err_cascade(self.sys.i()).await.into());
}; };
static ATOM_MSG: &str = "Returned atom from Rule recursion"; static ATOM_MSG: &str = "Returned atom from Rule recursion";
Ok(mtreev_from_api(&treev, self.sys.i(), &mut |_| panic!("{ATOM_MSG}")).await) Ok(mtreev_from_api(&treev, self.sys.i(), &mut async |_| panic!("{ATOM_MSG}")).await)
} }
pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a, Never>> { pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a, Never>> {
self.args.remove(key).expect("Key not found") self.args.remove(key).expect("Key not found")
@@ -83,7 +83,7 @@ impl Rule {
})) }))
.await, .await,
location: api::Location::Inherit, location: api::Location::Inherit,
pattern: mtreev_to_api(&self.pattern, &mut |b| match *b {}).await, pattern: mtreev_to_api(&self.pattern, &mut async |b| match *b {}).await,
id: ctx.with_rule(Rc::new(self)), id: ctx.with_rule(Rc::new(self)),
} }
} }

View File

@@ -0,0 +1,54 @@
use crate::entrypoint::ExtensionData;
#[cfg(feature = "tokio")]
pub async fn tokio_main(data: ExtensionData) {
use std::future::Future;
use std::io::Write;
use std::mem;
use std::pin::{Pin, pin};
use std::rc::Rc;
use async_std::io;
use async_stream::stream;
use futures::future::LocalBoxFuture;
use futures::stream::FuturesUnordered;
use futures::{StreamExt, stream, stream_select};
use orchid_api_traits::{Decode, Encode};
use orchid_base::clone;
use tokio::task::{LocalSet, spawn_local};
use crate::api;
use crate::entrypoint::extension_init;
use crate::msg::{recv_parent_msg, send_parent_msg};
let local_set = LocalSet::new();
local_set.spawn_local(async {
let host_header = api::HostHeader::decode(Pin::new(&mut async_std::io::stdin())).await;
let init =
Rc::new(extension_init(data, host_header, Rc::new(|fut| mem::drop(spawn_local(fut)))));
let mut buf = Vec::new();
init.header.encode(Pin::new(&mut buf)).await;
std::io::stdout().write_all(&buf).unwrap();
std::io::stdout().flush().unwrap();
// These are concurrent processes that never exit, so if the FuturesUnordered
// produces any result the extension should exit
let mut io = FuturesUnordered::<LocalBoxFuture<()>>::new();
io.push(Box::pin(async {
loop {
match recv_parent_msg().await {
Ok(msg) => init.send(&msg[..]).await,
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => break,
Err(e) => panic!("{e}"),
}
}
}));
io.push(Box::pin(async {
while let Some(msg) = init.recv().await {
send_parent_msg(&msg[..]).await.unwrap();
}
}));
io.next().await;
});
local_set.await;
}

View File

@@ -1,4 +1,3 @@
use std::future::Future;
use std::num::NonZero; use std::num::NonZero;
use std::ops::Range; use std::ops::Range;
use std::rc::Rc; use std::rc::Rc;
@@ -133,9 +132,7 @@ trait_set! {
} }
pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>); pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>);
impl LazyMemberFactory { impl LazyMemberFactory {
pub fn new<F: Future<Output = MemKind> + 'static>( pub fn new(cb: impl AsyncFnOnce(Sym, SysCtx) -> MemKind + Clone + 'static) -> Self {
cb: impl FnOnce(Sym, SysCtx) -> F + Clone + 'static,
) -> Self {
Self(Box::new(|s, ctx| cb(s, ctx).boxed_local())) Self(Box::new(|s, ctx| cb(s, ctx).boxed_local()))
} }
pub async fn build(self, path: Sym, ctx: SysCtx) -> MemKind { (self.0)(path, ctx).await } pub async fn build(self, path: Sym, ctx: SysCtx) -> MemKind { (self.0)(path, ctx).await }

View File

@@ -21,7 +21,7 @@ num-traits = "0.2.19"
orchid-api = { version = "0.1.0", path = "../orchid-api" } orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" } orchid-base = { version = "0.1.0", path = "../orchid-base" }
ordered-float = "4.6.0" ordered-float = "5.0.0"
paste = "1.0.15" paste = "1.0.15"
substack = "1.1.1" substack = "1.1.1"
test_executors = "0.3.2" test_executors = "0.3.2"

View File

@@ -20,7 +20,7 @@ pub struct CtxData {
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>, pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>, pub system_id: RefCell<NonZeroU16>,
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>, pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub root: RwLock<Module>, // pub root: RwLock<Module>,
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Ctx(Rc<CtxData>); pub struct Ctx(Rc<CtxData>);
@@ -36,7 +36,7 @@ impl Ctx {
systems: RwLock::default(), systems: RwLock::default(),
system_id: RefCell::new(NonZero::new(1).unwrap()), system_id: RefCell::new(NonZero::new(1).unwrap()),
owned_atoms: RwLock::default(), owned_atoms: RwLock::default(),
root: RwLock::new(Module::default()), // root: RwLock::new(Module::default()),
})) }))
} }
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> { pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {

View File

@@ -11,7 +11,7 @@ use orchid_base::name::NameLike;
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, PathSet, Step}; use crate::expr::{Expr, ExprKind, PathSet, Step};
use crate::tree::{ItemKind, MemberKind}; use crate::tree::{ItemKind, MemberKind, Module, Root};
type ExprGuard = Bound<RwLockWriteGuard<'static, ExprKind>, Expr>; type ExprGuard = Bound<RwLockWriteGuard<'static, ExprKind>, Expr>;
@@ -38,12 +38,13 @@ pub struct ExecCtx {
cur_pos: Pos, cur_pos: Pos,
did_pop: bool, did_pop: bool,
logger: Logger, logger: Logger,
root: Root,
} }
impl ExecCtx { impl ExecCtx {
pub async fn new(ctx: Ctx, logger: Logger, init: Expr) -> Self { pub async fn new(ctx: Ctx, logger: Logger, root: Root, init: Expr) -> Self {
let cur_pos = init.pos(); let cur_pos = init.pos();
let cur = Bound::async_new(init, |init| init.kind().write()).await; let cur = Bound::async_new(init, |init| init.kind().write()).await;
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger } Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, root, logger }
} }
pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") } pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") }
pub fn set_gas(&mut self, gas: Option<u64>) { self.gas = gas } pub fn set_gas(&mut self, gas: Option<u64>) { self.gas = gas }
@@ -91,38 +92,11 @@ impl ExecCtx {
}, },
ExprKind::Seq(a, b) if !self.did_pop => (ExprKind::Seq(a.clone(), b), StackOp::Push(a)), ExprKind::Seq(a, b) if !self.did_pop => (ExprKind::Seq(a.clone(), b), StackOp::Push(a)),
ExprKind::Seq(_, b) => (ExprKind::Identity(b), StackOp::Nop), ExprKind::Seq(_, b) => (ExprKind::Identity(b), StackOp::Nop),
ExprKind::Const(name) => { ExprKind::Const(name) =>
let (cn, mp) = name.split_last(); match self.root.get_const_value(name, self.cur_pos.clone(), self.ctx.clone()).await {
let root_lock = self.ctx.root.read().await; Err(e) => (ExprKind::Bottom(e), StackOp::Pop),
let module = root_lock.walk(true, mp.iter().cloned()).await.unwrap(); Ok(v) => (ExprKind::Identity(v), StackOp::Nop),
let member = (module.items.iter()) },
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => (
ExprKind::Bottom(mk_errv(
self.ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[self.cur_pos.clone().into()],
)),
StackOp::Pop,
),
Some(mem) => match mem.kind().await {
MemberKind::Mod(_) => (
ExprKind::Bottom(mk_errv(
self.ctx.i.i("module used as constant").await,
format!("{name} is a module"),
[self.cur_pos.clone().into()],
)),
StackOp::Pop,
),
MemberKind::Const(c) => {
let value = c.get_bytecode(&self.ctx).await;
(ExprKind::Identity(value.clone()), StackOp::Nop)
},
},
}
},
ExprKind::Arg => panic!("This should not appear outside function bodies"), ExprKind::Arg => panic!("This should not appear outside function bodies"),
ek @ ExprKind::Atom(_) => (ek, StackOp::Pop), ek @ ExprKind::Atom(_) => (ek, StackOp::Pop),
ExprKind::Bottom(bot) => (ExprKind::Bottom(bot.clone()), StackOp::Unwind(bot)), ExprKind::Bottom(bot) => (ExprKind::Bottom(bot.clone()), StackOp::Unwind(bot)),

View File

@@ -2,13 +2,15 @@ use std::cell::RefCell;
use std::future::Future; use std::future::Future;
use std::io; use std::io;
use std::num::NonZeroU64; use std::num::NonZeroU64;
use std::pin::pin;
use std::rc::{Rc, Weak}; use std::rc::{Rc, Weak};
use async_std::channel::{self, Sender}; use async_std::channel::{self, Sender};
use async_std::sync::Mutex; use async_std::sync::Mutex;
use async_stream::stream;
use derive_destructure::destructure; use derive_destructure::destructure;
use futures::FutureExt;
use futures::future::{join, join_all}; use futures::future::{join, join_all};
use futures::{FutureExt, StreamExt, stream, stream_select};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_api::HostMsgSet; use orchid_api::HostMsgSet;
@@ -41,13 +43,18 @@ pub struct ExtensionData {
logger: Logger, logger: Logger,
next_pars: RefCell<NonZeroU64>, next_pars: RefCell<NonZeroU64>,
exprs: ExprStore, exprs: ExprStore,
exiting_snd: Sender<()>,
lex_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::SubLex>>>>, lex_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::SubLex>>>>,
mac_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::RunMacros>>>>, mac_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::RunMacros>>>>,
} }
impl Drop for ExtensionData { impl Drop for ExtensionData {
fn drop(&mut self) { fn drop(&mut self) {
let reqnot = self.reqnot.clone(); let reqnot = self.reqnot.clone();
(self.ctx.spawn)(Box::pin(async move { reqnot.notify(api::HostExtNotif::Exit).await })) let exiting_snd = self.exiting_snd.clone();
(self.ctx.spawn)(Box::pin(async move {
reqnot.notify(api::HostExtNotif::Exit).await;
exiting_snd.send(()).await.unwrap()
}))
} }
} }
@@ -57,43 +64,34 @@ impl Extension {
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> { pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| { Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| {
let init = Rc::new(init); let init = Rc::new(init);
let (exiting_snd, exiting_rcv) = channel::bounded::<()>(1);
(ctx.spawn)(clone!(init, weak, ctx; Box::pin(async move { (ctx.spawn)(clone!(init, weak, ctx; Box::pin(async move {
let reqnot_opt = weak.upgrade().map(|rc| rc.reqnot.clone()); let rcv_stream = stream! { loop { yield init.recv().await } };
if let Some(reqnot) = reqnot_opt { let mut event_stream = pin!(stream::select(exiting_rcv.map(|()| None), rcv_stream));
let mut repeat = true; while let Some(Some(msg)) = event_stream.next().await {
while repeat { if let Some(reqnot) = weak.upgrade().map(|rc| rc.reqnot.clone()) {
repeat = false; let reqnot = reqnot.clone();
(init.recv(Box::new(|msg| { (ctx.spawn)(Box::pin(async move {
repeat = true; reqnot.receive(&msg).await;
Box::pin(clone!(reqnot, ctx; async move { }))
let msg = msg.to_vec();
let reqnot = reqnot.clone();
(ctx.spawn)(Box::pin(async move {
reqnot.receive(&msg).await;
}))
}))
})))
.await;
} }
} }
}))); })));
ExtensionData { ExtensionData {
exiting_snd,
exprs: ExprStore::default(), exprs: ExprStore::default(),
ctx: ctx.clone(), ctx: ctx.clone(),
systems: (init.systems.iter().cloned()) systems: (init.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) }) .map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
.collect(), .collect(),
logger: logger.clone(), logger: logger.clone(),
init, init: init.clone(),
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()), next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(), lex_recur: Mutex::default(),
mac_recur: Mutex::default(), mac_recur: Mutex::default(),
reqnot: ReqNot::new( reqnot: ReqNot::new(
msg_logger, msg_logger,
clone!(weak; move |sfn, _| clone!(weak; async move { move |sfn, _| clone!(init; Box::pin(async move { init.send(sfn).await })),
let data = weak.upgrade().unwrap();
data.init.send(sfn).await
}.boxed_local())),
clone!(weak; move |notif, _| { clone!(weak; move |notif, _| {
clone!(weak; Box::pin(async move { clone!(weak; Box::pin(async move {
let this = Extension(weak.upgrade().unwrap()); let this = Extension(weak.upgrade().unwrap());

View File

@@ -33,9 +33,8 @@ pub async fn macro_treev_to_api(mtree: Vec<MacTree>, slots: &mut Slots) -> Vec<a
} }
pub async fn macro_treev_from_api(api: Vec<api::MacroTree>, ctx: Ctx) -> Vec<MacTree> { pub async fn macro_treev_from_api(api: Vec<api::MacroTree>, ctx: Ctx) -> Vec<MacTree> {
mtreev_from_api(&api, &ctx.clone().i, &mut move |atom| { mtreev_from_api(&api, &ctx.clone().i, &mut async move |atom| {
clone!(ctx); MacTok::Atom(AtomHand::new(atom.clone(), &ctx).await)
Box::pin(async move { MacTok::Atom(AtomHand::new(atom.clone(), &ctx).await) })
}) })
.await .await
} }

View File

@@ -4,7 +4,7 @@ use std::pin::Pin;
use async_process::{self, Child, ChildStdin, ChildStdout}; use async_process::{self, Child, ChildStdin, ChildStdout};
use async_std::io::{self, BufReadExt, BufReader}; use async_std::io::{self, BufReadExt, BufReader};
use async_std::sync::Mutex; use async_std::sync::Mutex;
use futures::FutureExt; use futures::AsyncWriteExt;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use orchid_api_traits::{Decode, Encode}; use orchid_api_traits::{Decode, Encode};
use orchid_base::builtin::{ExtInit, ExtPort}; use orchid_base::builtin::{ExtInit, ExtPort};
@@ -46,7 +46,7 @@ pub async fn ext_command(
header, header,
port: Box::new(Subprocess { port: Box::new(Subprocess {
child: RefCell::new(Some(child)), child: RefCell::new(Some(child)),
stdin: Mutex::new(Box::pin(stdin)), stdin: Some(Mutex::new(Box::pin(stdin))),
stdout: Mutex::new(Box::pin(stdout)), stdout: Mutex::new(Box::pin(stdout)),
ctx, ctx,
}), }),
@@ -55,14 +55,16 @@ pub async fn ext_command(
pub struct Subprocess { pub struct Subprocess {
child: RefCell<Option<Child>>, child: RefCell<Option<Child>>,
stdin: Mutex<Pin<Box<ChildStdin>>>, stdin: Option<Mutex<Pin<Box<ChildStdin>>>>,
stdout: Mutex<Pin<Box<ChildStdout>>>, stdout: Mutex<Pin<Box<ChildStdout>>>,
ctx: Ctx, ctx: Ctx,
} }
impl Drop for Subprocess { impl Drop for Subprocess {
fn drop(&mut self) { fn drop(&mut self) {
let mut child = self.child.borrow_mut().take().unwrap(); let mut child = self.child.borrow_mut().take().unwrap();
let stdin = self.stdin.take().unwrap();
(self.ctx.spawn)(Box::pin(async move { (self.ctx.spawn)(Box::pin(async move {
stdin.lock().await.close().await.unwrap();
let status = child.status().await.expect("Extension exited with error"); let status = child.status().await.expect("Extension exited with error");
assert!(status.success(), "Extension exited with error {status}"); assert!(status.success(), "Extension exited with error {status}");
})) }))
@@ -70,18 +72,17 @@ impl Drop for Subprocess {
} }
impl ExtPort for Subprocess { impl ExtPort for Subprocess {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> { fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> {
async { send_msg(Pin::new(&mut *self.stdin.lock().await), msg).await.unwrap() }.boxed_local() Box::pin(async {
send_msg(Pin::new(&mut *self.stdin.as_ref().unwrap().lock().await), msg).await.unwrap()
})
} }
fn recv<'a>( fn recv(&self) -> LocalBoxFuture<'_, Option<Vec<u8>>> {
&'a self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
) -> LocalBoxFuture<'a, ()> {
Box::pin(async { Box::pin(async {
std::io::Write::flush(&mut std::io::stderr()).unwrap(); std::io::Write::flush(&mut std::io::stderr()).unwrap();
match recv_msg(self.stdout.lock().await.as_mut()).await { match recv_msg(self.stdout.lock().await.as_mut()).await {
Ok(msg) => cb(&msg).await, Ok(msg) => Some(msg),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => (), Err(e) if e.kind() == io::ErrorKind::BrokenPipe => None,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => (), Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => None,
Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()), Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()),
} }
}) })

View File

@@ -24,7 +24,7 @@ use substack::{Stackframe, Substack};
use crate::api; use crate::api;
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::extension::{Extension, WeakExtension}; use crate::extension::{Extension, WeakExtension};
use crate::tree::{ItemKind, Member, Module, ParsTokTree}; use crate::tree::{ItemKind, Member, Module, ParsTokTree, Root};
#[derive(destructure)] #[derive(destructure)]
struct SystemInstData { struct SystemInstData {
@@ -80,7 +80,7 @@ impl System {
comments: Vec<Comment>, comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> { ) -> OrcRes<Vec<ParsTokTree>> {
let line = let line =
join_all(line.iter().map(|t| async { t.to_api(&mut |n, _| match *n {}).await })).await; join_all(line.iter().map(|t| async { t.to_api(&mut async |n, _| match *n {}).await })).await;
let comments = comments.iter().map(Comment::to_api).collect_vec(); let comments = comments.iter().map(Comment::to_api).collect_vec();
match self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }).await { match self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }).await {
Ok(parsed) => Ok(ttv_from_api(parsed, &mut self.ctx().clone(), &self.ctx().i).await), Ok(parsed) => Ok(ttv_from_api(parsed, &mut self.ctx().clone(), &self.ctx().i).await),
@@ -122,7 +122,7 @@ impl SystemCtor {
self.decl.depends.iter().map(|s| &**s) self.decl.depends.iter().map(|s| &**s)
} }
pub fn id(&self) -> api::SysDeclId { self.decl.id } pub fn id(&self) -> api::SysDeclId { self.decl.id }
pub async fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> System { pub async fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> (Module, System) {
let depends = depends.into_iter().map(|si| si.id()).collect_vec(); let depends = depends.into_iter().map(|si| si.id()).collect_vec();
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided"); debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension"); let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
@@ -150,10 +150,8 @@ impl SystemCtor {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.await; .await;
ext.ctx().systems.write().await.insert(id, data.downgrade()); ext.ctx().systems.write().await.insert(id, data.downgrade());
let mut swap = Module::default(); let root = Module::new(const_root);
mem::swap(&mut swap, &mut *ext.ctx().root.write().await); (root, data)
*ext.ctx().root.write().await = Module::new(swap.items.into_iter().chain(const_root));
data
} }
} }
@@ -166,7 +164,7 @@ pub enum SysResolvErr {
pub async fn init_systems( pub async fn init_systems(
tgts: &[String], tgts: &[String],
exts: &[Extension], exts: &[Extension],
) -> Result<Vec<System>, SysResolvErr> { ) -> Result<(Root, Vec<System>), SysResolvErr> {
let mut to_load = HashMap::<&str, &SystemCtor>::new(); let mut to_load = HashMap::<&str, &SystemCtor>::new();
let mut to_find = tgts.iter().map(|s| s.as_str()).collect::<VecDeque<&str>>(); let mut to_find = tgts.iter().map(|s| s.as_str()).collect::<VecDeque<&str>>();
while let Some(target) = to_find.pop_front() { while let Some(target) = to_find.pop_front() {
@@ -205,9 +203,11 @@ pub async fn init_systems(
walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?; walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?;
} }
let mut systems = HashMap::<&str, System>::new(); let mut systems = HashMap::<&str, System>::new();
let mut root = Module::default();
for ctor in to_load_ordered.iter() { for ctor in to_load_ordered.iter() {
let sys = ctor.run(ctor.depends().map(|n| &systems[n])).await; let (sys_root, sys) = ctor.run(ctor.depends().map(|n| &systems[n])).await;
systems.insert(ctor.name(), sys); systems.insert(ctor.name(), sys);
root.merge(sys_root);
} }
Ok(systems.into_values().collect_vec()) Ok((Root::new(root), systems.into_values().collect_vec()))
} }

View File

@@ -2,17 +2,18 @@ use std::fmt::Debug;
use std::rc::Rc; use std::rc::Rc;
use async_once_cell::OnceCell; use async_once_cell::OnceCell;
use async_std::sync::Mutex; use async_std::sync::{Mutex, RwLock};
use async_stream::stream; use async_stream::stream;
use futures::future::join_all; use futures::future::join_all;
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::location::Pos; use orchid_base::location::Pos;
use orchid_base::macros::{mtreev_fmt, mtreev_from_api}; use orchid_base::macros::{mtreev_fmt, mtreev_from_api};
use orchid_base::name::Sym; use orchid_base::name::{NameLike, Sym};
use orchid_base::parse::{Comment, Import}; use orchid_base::parse::{Comment, Import};
use orchid_base::tree::{AtomRepr, TokTree, Token}; use orchid_base::tree::{AtomRepr, TokTree, Token};
use orchid_base::{clone, tl_cache}; use orchid_base::{clone, tl_cache};
@@ -52,7 +53,7 @@ impl Item {
let kind = match tree.kind { let kind = match tree.kind {
api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys).await), api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys).await),
api::ItemKind::Import(name) => ItemKind::Import(Import { api::ItemKind::Import(name) => ItemKind::Import(Import {
path: Sym::from_api(name, &sys.ctx().i).await.iter().cloned().collect(), path: Sym::from_api(name, &sys.ctx().i).await.iter().collect(),
name: None, name: None,
}), }),
api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e, &sys.ctx().i).await), api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e, &sys.ctx().i).await),
@@ -66,11 +67,8 @@ impl Item {
let pos = Pos::from_api(&rule.location, &sys.ctx().i).await; let pos = Pos::from_api(&rule.location, &sys.ctx().i).await;
let pattern = mtreev_from_api(&rule.pattern, &sys.ctx().i, &mut { let pattern = mtreev_from_api(&rule.pattern, &sys.ctx().i, &mut {
clone!(pos, sys); clone!(pos, sys);
move |a| { async move |a| {
clone!(pos, sys); MacTok::Atom(AtomHand::from_api(a, pos.clone(), &mut sys.ctx().clone()).await)
Box::pin(async move {
MacTok::Atom(AtomHand::from_api(a, pos.clone(), &mut sys.ctx().clone()).await)
})
} }
}) })
.await; .await;
@@ -182,6 +180,11 @@ impl Module {
.collect_vec(); .collect_vec();
Self { imports: vec![], exports, items } Self { imports: vec![], exports, items }
} }
pub fn merge(&mut self, other: Module) {
let mut swap = Module::default();
std::mem::swap(self, &mut swap);
*self = Module::new(swap.items.into_iter().chain(other.items))
}
pub async fn from_api(m: api::Module, path: &mut Vec<Tok<String>>, sys: &System) -> Self { pub async fn from_api(m: api::Module, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
Self::new( Self::new(
stream! { for item in m.items { yield Item::from_api(item, path, sys).boxed_local().await } } stream! { for item in m.items { yield Item::from_api(item, path, sys).boxed_local().await } }
@@ -340,3 +343,32 @@ impl CodeLocator {
Self { steps, rule_loc: Some((macro_i, rule_i)) } Self { steps, rule_loc: Some((macro_i, rule_i)) }
} }
} }
#[derive(Clone)]
pub struct Root(Rc<RwLock<Module>>);
impl Root {
pub fn new(module: Module) -> Self { Self(Rc::new(RwLock::new(module))) }
pub async fn get_const_value(&self, name: impl NameLike, pos: Pos, ctx: Ctx) -> OrcRes<Expr> {
let (cn, mp) = name.split_last();
let root_lock = self.0.read().await;
let module = root_lock.walk(true, mp.iter().cloned()).await.unwrap();
let member = (module.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos.clone().into()],
)),
Some(mem) => match mem.kind().await {
MemberKind::Mod(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos.clone().into()],
)),
MemberKind::Const(c) => Ok((c.get_bytecode(&ctx).await).clone()),
},
}
}
}

View File

@@ -14,7 +14,9 @@ orchid-api = { version = "0.1.0", path = "../orchid-api" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" } orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" } orchid-base = { version = "0.1.0", path = "../orchid-base" }
orchid-extension = { version = "0.1.0", path = "../orchid-extension" } orchid-extension = { version = "0.1.0", path = "../orchid-extension", features = [
ordered-float = "4.6.0" "tokio",
] }
ordered-float = "5.0.0"
rust_decimal = "1.36.0" rust_decimal = "1.36.0"
tokio = { version = "1.43.0", features = ["full"] } tokio = { version = "1.43.0", features = ["full"] }

View File

@@ -1,16 +1,10 @@
use std::mem; use std::mem;
use std::rc::Rc; use std::rc::Rc;
use orchid_extension::entrypoint::{ExtensionData, extension_main_logic}; use orchid_extension::entrypoint::ExtensionData;
use orchid_extension::tokio::tokio_main;
use orchid_std::StdSystem; use orchid_std::StdSystem;
use tokio::task::{LocalSet, spawn_local}; use tokio::task::{LocalSet, spawn_local};
#[tokio::main(flavor = "current_thread")] #[tokio::main(flavor = "current_thread")]
pub async fn main() { pub async fn main() { tokio_main(ExtensionData::new("orchid-std::main", &[&StdSystem])).await }
LocalSet::new()
.run_until(async {
let data = ExtensionData::new("orchid-std::main", &[&StdSystem]);
extension_main_logic(data, Rc::new(|fut| mem::drop(spawn_local(fut)))).await;
})
.await
}

View File

@@ -32,6 +32,8 @@
"rust-analyzer.rustfmt.extraArgs": [ "rust-analyzer.rustfmt.extraArgs": [
"+nightly" "+nightly"
], ],
"rust-analyzer.cargo.features": "all",
"rust-analyzer.check.features": "all",
"files.associations": { "files.associations": {
"*.mjsd": "markdown" "*.mjsd": "markdown"
}, },

View File

@@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::mem; use std::mem;
@@ -35,7 +36,9 @@ pub struct Args {
#[arg(short, long, env = "ORCHID_DEFAULT_SYSTEMS", value_delimiter = ';')] #[arg(short, long, env = "ORCHID_DEFAULT_SYSTEMS", value_delimiter = ';')]
system: Vec<String>, system: Vec<String>,
#[arg(short, long)] #[arg(short, long)]
verbose: bool, logs: bool,
#[arg(short, long)]
msg_logs: bool,
#[command(subcommand)] #[command(subcommand)]
command: Commands, command: Commands,
} }
@@ -78,113 +81,117 @@ fn get_all_extensions<'a>(
} }
} }
#[tokio::main(flavor = "current_thread")] #[tokio::main]
async fn main() -> io::Result<ExitCode> { async fn main() -> io::Result<ExitCode> {
let mut code = ExitCode::SUCCESS; let code = Rc::new(RefCell::new(ExitCode::SUCCESS));
LocalSet::new() let local_set = LocalSet::new();
.run_until(async { let code1 = code.clone();
let args = Args::parse(); local_set.spawn_local(async move {
let ctx = &Ctx::new(Rc::new(|fut| mem::drop(spawn_local(fut)))); let args = Args::parse();
let i = &ctx.i; let ctx = &Ctx::new(Rc::new(|fut| mem::drop(spawn_local(fut))));
let logger = let i = &ctx.i;
Logger::new(if args.verbose { LogStrategy::StdErr } else { LogStrategy::Discard }); let logger = Logger::new(if args.logs { LogStrategy::StdErr } else { LogStrategy::Discard });
let extensions = get_all_extensions(&args, &logger, &Logger::new(LogStrategy::Discard), ctx) let msg_logger =
.try_collect::<Vec<Extension>>() Logger::new(if args.msg_logs { LogStrategy::StdErr } else { LogStrategy::Discard });
let extensions = get_all_extensions(&args, &logger, &msg_logger, ctx)
.try_collect::<Vec<Extension>>()
.await
.unwrap();
match args.command {
Commands::Lex { file } => {
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
let mut file = File::open(file.as_std_path()).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
let lexemes = lex(i.i(&buf).await, &systems, ctx).await.unwrap();
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true))
},
Commands::Parse { file } => {
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
let mut file = File::open(file.as_std_path()).unwrap();
let mut buf = String::new();
file.read_to_string(&mut buf).unwrap();
let lexemes = lex(i.i(&buf).await, &systems, ctx).await.unwrap();
let Some(first) = lexemes.first() else {
println!("File empty!");
return;
};
let reporter = ReporterImpl::new();
let pctx = ParseCtxImpl { reporter: &reporter, systems: &systems };
let snip = Snippet::new(first, &lexemes, i);
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap();
if let Some(errv) = reporter.errv() {
eprintln!("{errv}");
*code1.borrow_mut() = ExitCode::FAILURE;
return;
}
if ptree.is_empty() {
eprintln!("File empty only after parsing, but no errors were reported");
*code1.borrow_mut() = ExitCode::FAILURE;
return;
}
for item in ptree {
println!("{}", take_first(&item.print(&FmtCtxImpl { i }).await, true))
}
},
Commands::Repl => loop {
let (root, systems) = init_systems(&args.system, &extensions).await.unwrap();
print!("\\.> ");
std::io::stdout().flush().unwrap();
let mut prompt = String::new();
stdin().read_line(&mut prompt).await.unwrap();
let lexemes = lex(i.i(prompt.trim()).await, &systems, ctx).await.unwrap();
if args.logs {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let mtreev = parse_mtree(
Snippet::new(&lexemes[0], &lexemes, i),
Substack::Bottom.push(i.i("orcx").await).push(i.i("input").await),
)
.await .await
.unwrap(); .unwrap();
match args.command { if args.logs {
Commands::Lex { file } => { let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
let systems = init_systems(&args.system, &extensions).await.unwrap(); println!("parsed: {}", take_first(&fmt, true));
let mut file = File::open(file.as_std_path()).unwrap(); }
let mut buf = String::new(); let expr = mtreev_to_expr(&mtreev, Substack::Bottom, ctx).await;
file.read_to_string(&mut buf).unwrap(); let mut xctx =
let lexemes = lex(i.i(&buf).await, &systems, ctx).await.unwrap(); ExecCtx::new(ctx.clone(), logger.clone(), root.clone(), expr.at(Pos::None)).await;
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)) xctx.set_gas(Some(1000));
}, xctx.execute().await;
Commands::Parse { file } => { match xctx.result() {
let systems = init_systems(&args.system, &extensions).await.unwrap(); ExecResult::Value(val) =>
let mut file = File::open(file.as_std_path()).unwrap(); println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)),
let mut buf = String::new(); ExecResult::Err(e) => println!("error: {e}"),
file.read_to_string(&mut buf).unwrap(); ExecResult::Gas(_) => println!("Ran out of gas!"),
let lexemes = lex(i.i(&buf).await, &systems, ctx).await.unwrap(); }
let Some(first) = lexemes.first() else { },
println!("File empty!"); Commands::Execute { code } => {
return; let (root, systems) = init_systems(&args.system, &extensions).await.unwrap();
}; let lexemes = lex(i.i(code.trim()).await, &systems, ctx).await.unwrap();
let reporter = ReporterImpl::new(); if args.logs {
let pctx = ParseCtxImpl { reporter: &reporter, systems: &systems }; println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
let snip = Snippet::new(first, &lexemes, i); }
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap(); let mtreev =
if let Some(errv) = reporter.errv() { parse_mtree(Snippet::new(&lexemes[0], &lexemes, i), Substack::Bottom).await.unwrap();
eprintln!("{errv}"); if args.logs {
code = ExitCode::FAILURE; let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
return; println!("parsed: {}", take_first(&fmt, true));
} }
if ptree.is_empty() { let expr = mtreev_to_expr(&mtreev, Substack::Bottom, ctx).await;
eprintln!("File empty only after parsing, but no errors were reported"); let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), root, expr.at(Pos::None)).await;
code = ExitCode::FAILURE; xctx.set_gas(Some(1000));
return; xctx.execute().await;
} match xctx.result() {
for item in ptree { ExecResult::Value(val) =>
println!("{}", take_first(&item.print(&FmtCtxImpl { i }).await, true)) println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)),
} ExecResult::Err(e) => println!("error: {e}"),
}, ExecResult::Gas(_) => println!("Ran out of gas!"),
Commands::Repl => loop { }
let systems = init_systems(&args.system, &extensions).await.unwrap(); },
print!("\\.> "); }
std::io::stdout().flush().unwrap(); });
let mut prompt = String::new(); local_set.await;
stdin().read_line(&mut prompt).await.unwrap(); let x = *code.borrow();
let lexemes = lex(i.i(prompt.trim()).await, &systems, ctx).await.unwrap(); Ok(x)
if args.verbose {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let mtreev = parse_mtree(
Snippet::new(&lexemes[0], &lexemes, i),
Substack::Bottom.push(i.i("orcx").await).push(i.i("input").await),
)
.await
.unwrap();
if args.verbose {
let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
println!("parsed: {}", take_first(&fmt, true));
}
let expr = mtreev_to_expr(&mtreev, Substack::Bottom, ctx).await;
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), expr.at(Pos::None)).await;
xctx.set_gas(Some(1000));
xctx.execute().await;
match xctx.result() {
ExecResult::Value(val) =>
println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)),
ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
},
Commands::Execute { code } => {
let systems = init_systems(&args.system, &extensions).await.unwrap();
let lexemes = lex(i.i(code.trim()).await, &systems, ctx).await.unwrap();
if args.verbose {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
}
let mtreev =
parse_mtree(Snippet::new(&lexemes[0], &lexemes, i), Substack::Bottom).await.unwrap();
if args.verbose {
let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
println!("parsed: {}", take_first(&fmt, true));
}
let expr = mtreev_to_expr(&mtreev, Substack::Bottom, ctx).await;
let mut xctx = ExecCtx::new(ctx.clone(), logger.clone(), expr.at(Pos::None)).await;
xctx.set_gas(Some(1000));
xctx.execute().await;
match xctx.result() {
ExecResult::Value(val) =>
println!("{}", take_first(&val.print(&FmtCtxImpl { i }).await, false)),
ExecResult::Err(e) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
},
}
})
.await;
Ok(code)
} }