Phased out async-stream in pursuit of compile performance

This commit is contained in:
2025-09-04 15:01:53 +02:00
parent 088cb6a247
commit e339350505
26 changed files with 359 additions and 342 deletions

View File

@@ -6,9 +6,9 @@ orcxdb = "xtask orcxdb"
[env]
CARGO_WORKSPACE_DIR = { value = "", relative = true }
ORCHID_EXTENSIONS = "target/debug/orchid-std"
ORCHID_DEFAULT_SYSTEMS = "orchid::std"
ORCHID_DEFAULT_SYSTEMS = "orchid::std;orchid::macros"
ORCHID_LOG_BUFFERS = "true"
RUSTBACKTRACE = "1"
RUST_BACKTRACE = "1"
[build]
# rustflags = ["-Znext-solver"]

274
Cargo.lock generated
View File

@@ -23,24 +23,11 @@ version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
dependencies = [
"getrandom 0.2.15",
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
dependencies = [
"cfg-if",
"getrandom 0.3.3",
"once_cell",
"version_check",
"zerocopy 0.8.26",
]
[[package]]
name = "aho-corasick"
version = "1.1.3"
@@ -111,17 +98,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]]
name = "async-channel"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35"
dependencies = [
"concurrent-queue",
"event-listener 2.5.3",
"futures-core",
]
[[package]]
name = "async-channel"
version = "2.3.1"
@@ -135,31 +111,11 @@ dependencies = [
]
[[package]]
name = "async-executor"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec"
name = "async-fn-stream"
version = "0.1.0"
dependencies = [
"async-task",
"concurrent-queue",
"fastrand",
"futures-lite",
"slab",
]
[[package]]
name = "async-global-executor"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c"
dependencies = [
"async-channel 2.3.1",
"async-executor",
"async-io",
"async-lock",
"blocking",
"futures-lite",
"once_cell",
"futures",
"test_executors",
]
[[package]]
@@ -187,7 +143,7 @@ version = "3.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc"
dependencies = [
"event-listener 5.4.0",
"event-listener",
"event-listener-strategy",
"pin-project-lite",
]
@@ -204,14 +160,14 @@ version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00"
dependencies = [
"async-channel 2.3.1",
"async-channel",
"async-io",
"async-lock",
"async-signal",
"async-task",
"blocking",
"cfg-if",
"event-listener 5.4.0",
"event-listener",
"futures-lite",
"rustix 1.0.8",
]
@@ -234,54 +190,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "async-std"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c8e079a4ab67ae52b7403632e4618815d6db36d2a010cfe41b02c1b1578f93b"
dependencies = [
"async-channel 1.9.0",
"async-global-executor",
"async-io",
"async-lock",
"crossbeam-utils",
"futures-channel",
"futures-core",
"futures-io",
"futures-lite",
"gloo-timers",
"kv-log-macro",
"log",
"memchr",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
"wasm-bindgen-futures",
]
[[package]]
name = "async-stream"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476"
dependencies = [
"async-stream-impl",
"futures-core",
"pin-project-lite",
]
[[package]]
name = "async-stream-impl"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2 1.0.101",
"quote 1.0.40",
"syn 2.0.106",
]
[[package]]
name = "async-task"
version = "4.7.1"
@@ -348,7 +256,7 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea"
dependencies = [
"async-channel 2.3.1",
"async-channel",
"async-task",
"futures-io",
"futures-lite",
@@ -562,41 +470,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "darling"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4"
dependencies = [
"fnv",
"ident_case",
"proc-macro2 1.0.101",
"quote 1.0.40",
"strsim",
"syn 2.0.106",
]
[[package]]
name = "darling_macro"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
dependencies = [
"darling_core",
"quote 1.0.40",
"syn 2.0.106",
]
[[package]]
name = "derive_destructure"
version = "1.0.0"
@@ -646,12 +519,6 @@ dependencies = [
"windows-sys 0.59.0",
]
[[package]]
name = "event-listener"
version = "2.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
[[package]]
name = "event-listener"
version = "5.4.0"
@@ -669,7 +536,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2"
dependencies = [
"event-listener 5.4.0",
"event-listener",
"pin-project-lite",
]
@@ -679,12 +546,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "foldhash"
version = "0.2.0"
@@ -817,19 +678,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"wasi 0.14.3+wasi-0.2.4",
"wasi",
]
[[package]]
@@ -838,25 +687,13 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gloo-timers"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.8",
"ahash",
]
[[package]]
@@ -888,12 +725,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc"
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "include_dir"
version = "0.7.4"
@@ -982,15 +813,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bf92d396aa2df203577ebef8deaf1efc24d446366ca86be83ec8ac794b157d6"
[[package]]
name = "kv-log-macro"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f"
dependencies = [
"log",
]
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -1030,9 +852,6 @@ name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
dependencies = [
"value-bag",
]
[[package]]
name = "logwise"
@@ -1099,7 +918,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi",
"windows-sys 0.52.0",
]
@@ -1160,7 +979,6 @@ dependencies = [
name = "orchid-api-derive"
version = "0.1.0"
dependencies = [
"darling",
"itertools",
"orchid-api-traits",
"proc-macro2 1.0.101",
@@ -1172,7 +990,7 @@ dependencies = [
name = "orchid-api-traits"
version = "0.1.0"
dependencies = [
"async-stream",
"async-fn-stream",
"futures",
"itertools",
"never",
@@ -1183,8 +1001,8 @@ dependencies = [
name = "orchid-base"
version = "0.1.0"
dependencies = [
"async-fn-stream",
"async-once-cell",
"async-stream",
"derive_destructure",
"dyn-clone",
"futures",
@@ -1209,10 +1027,9 @@ dependencies = [
name = "orchid-extension"
version = "0.1.0"
dependencies = [
"ahash 0.8.12",
"async-fn-stream",
"async-lock",
"async-once-cell",
"async-stream",
"derive_destructure",
"dyn-clone",
"futures",
@@ -1241,10 +1058,10 @@ dependencies = [
name = "orchid-host"
version = "0.1.0"
dependencies = [
"async-fn-stream",
"async-lock",
"async-once-cell",
"async-process",
"async-stream",
"bound",
"derive_destructure",
"futures",
@@ -1269,7 +1086,6 @@ name = "orchid-std"
version = "0.1.0"
dependencies = [
"async-once-cell",
"async-stream",
"futures",
"hashbrown 0.16.0",
"itertools",
@@ -1291,8 +1107,7 @@ dependencies = [
name = "orcx"
version = "0.1.0"
dependencies = [
"async-std",
"async-stream",
"async-fn-stream",
"camino",
"clap",
"ctrlc",
@@ -1392,7 +1207,7 @@ version = "0.2.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
dependencies = [
"zerocopy 0.7.35",
"zerocopy",
]
[[package]]
@@ -1466,12 +1281,6 @@ dependencies = [
"proc-macro2 1.0.101",
]
[[package]]
name = "r-efi"
version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
[[package]]
name = "radium"
version = "0.7.0"
@@ -1505,7 +1314,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.15",
"getrandom",
]
[[package]]
@@ -2039,12 +1848,6 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a"
[[package]]
name = "value-bag"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ef4c4aa54d5d05a279399bfa921ec387b7aba77caf7a682ae8d86785b8fdad2"
[[package]]
name = "version_check"
version = "0.9.5"
@@ -2067,15 +1870,6 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasi"
version = "0.14.3+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95"
dependencies = [
"wit-bindgen",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
@@ -2279,12 +2073,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "wit-bindgen"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814"
[[package]]
name = "wyz"
version = "0.5.1"
@@ -2308,16 +2096,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
dependencies = [
"byteorder",
"zerocopy-derive 0.7.35",
]
[[package]]
name = "zerocopy"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f"
dependencies = [
"zerocopy-derive 0.8.26",
"zerocopy-derive",
]
[[package]]
@@ -2330,14 +2109,3 @@ dependencies = [
"quote 1.0.40",
"syn 2.0.106",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181"
dependencies = [
"proc-macro2 1.0.101",
"quote 1.0.40",
"syn 2.0.106",
]

View File

@@ -11,5 +11,5 @@ members = [
"orchid-api-derive",
"orchid-api-traits",
"stdio-perftest",
"xtask",
"xtask", "async-fn-stream",
]

View File

@@ -0,0 +1,10 @@
[package]
name = "async-fn-stream"
version = "0.1.0"
edition = "2024"
[dependencies]
futures = { version = "0.3.31", features = ["std"], default-features = false }
[dev-dependencies]
test_executors = "0.3.5"

207
async-fn-stream/src/lib.rs Normal file
View File

@@ -0,0 +1,207 @@
use std::cell::Cell;
use std::future::poll_fn;
use std::marker::PhantomData;
use std::pin::Pin;
use std::ptr;
use std::task::{Context, Poll};
use futures::future::LocalBoxFuture;
use futures::{FutureExt, Stream};
type YieldSlot<'a, T> = &'a Cell<Option<T>>;
/// Handle that allows you to emit values on a stream. If you drop
/// this, the stream will end and you will not be polled again.
pub struct StreamCtx<'a, T>(&'a Cell<Option<T>>, PhantomData<&'a ()>);
impl<T> StreamCtx<'_, T> {
pub fn emit(&mut self, value: T) -> impl Future<Output = ()> {
assert!(self.0.replace(Some(value)).is_none(), "Leftover value in stream");
let mut state = Poll::Pending;
poll_fn(move |_| std::mem::replace(&mut state, Poll::Ready(())))
}
}
enum FnOrFut<'a, T, O> {
Fn(Option<Box<dyn FnOnce(YieldSlot<'a, T>) -> LocalBoxFuture<'a, O> + 'a>>),
Fut(LocalBoxFuture<'a, O>),
}
struct AsyncFnStream<'a, T> {
driver: FnOrFut<'a, T, ()>,
output: Cell<Option<T>>,
}
impl<'a, T> Stream for AsyncFnStream<'a, T> {
type Item = T;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
unsafe {
let self_mut = self.get_unchecked_mut();
let fut = match &mut self_mut.driver {
FnOrFut::Fut(fut) => fut,
FnOrFut::Fn(f) => {
// safety: the cell is held inline in self, which is pinned.
let cell = ptr::from_ref(&self_mut.output).as_ref().unwrap();
let fut = f.take().unwrap()(cell);
self_mut.driver = FnOrFut::Fut(fut);
return Pin::new_unchecked(self_mut).poll_next(cx);
},
};
match fut.as_mut().poll(cx) {
Poll::Ready(()) => Poll::Ready(None),
Poll::Pending => match self_mut.output.replace(None) {
None => Poll::Pending,
Some(t) => Poll::Ready(Some(t)),
},
}
}
}
}
struct AsyncFnTryStream<'a, T, E> {
driver: FnOrFut<'a, T, Result<StreamCtx<'a, T>, E>>,
output: Cell<Option<T>>,
}
impl<'a, T, E> Stream for AsyncFnTryStream<'a, T, E> {
type Item = Result<T, E>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
unsafe {
let self_mut = self.get_unchecked_mut();
let fut = match &mut self_mut.driver {
FnOrFut::Fut(fut) => fut,
FnOrFut::Fn(f) => {
// safety: the cell is held inline in self, which is pinned.
let cell = ptr::from_ref(&self_mut.output).as_ref().unwrap();
let fut = f.take().unwrap()(cell);
self_mut.driver = FnOrFut::Fut(fut);
return Pin::new_unchecked(self_mut).poll_next(cx);
},
};
match fut.as_mut().poll(cx) {
Poll::Ready(Ok(_)) => Poll::Ready(None),
Poll::Ready(Err(ex)) => Poll::Ready(Some(Err(ex))),
Poll::Pending => match self_mut.output.replace(None) {
None => Poll::Pending,
Some(t) => Poll::Ready(Some(Ok(t))),
},
}
}
}
}
/// Create a stream from an async function acting as a coroutine
pub fn stream<'a, T: 'a>(
f: impl for<'b> AsyncFnOnce(StreamCtx<'b, T>) + 'a,
) -> impl Stream<Item = T> + 'a {
AsyncFnStream {
output: Cell::new(None),
driver: FnOrFut::Fn(Some(Box::new(|t| {
async { f(StreamCtx(t, PhantomData)).await }.boxed_local()
}))),
}
}
/// Create a stream of result from a fallible function.
pub fn try_stream<'a, T: 'a, E: 'a>(
f: impl for<'b> AsyncFnOnce(StreamCtx<'b, T>) -> Result<StreamCtx<'b, T>, E> + 'a,
) -> impl Stream<Item = Result<T, E>> + 'a {
AsyncFnTryStream {
output: Cell::new(None),
driver: FnOrFut::Fn(Some(Box::new(|t| {
async { f(StreamCtx(t, PhantomData)).await }.boxed_local()
}))),
}
}
#[cfg(test)]
mod test {
use std::task::Poll;
use std::{future, pin};
use futures::channel::mpsc::channel;
use futures::{Stream, StreamExt, TryStreamExt};
use test_executors::spin_on;
use crate::{stream, try_stream};
#[test]
fn sync() {
spin_on(async {
let v = stream(async |mut cx| {
for i in 0..5 {
cx.emit(i).await
}
})
.collect::<Vec<_>>()
.await;
assert_eq!(v, [0, 1, 2, 3, 4])
})
}
#[test]
/// The exact behaviour of the poll function under blocked use
fn with_delay() {
spin_on(async {
let (mut send, mut recv) = channel(0);
let mut s = pin::pin!(stream(async |mut cx| {
for i in 0..2 {
cx.emit(i).await
}
recv.next().await;
for i in 2..5 {
cx.emit(i).await
}
}));
let mut log = String::new();
let log = future::poll_fn(|cx| {
match s.as_mut().poll_next(cx) {
Poll::Ready(Some(r)) => log += &format!("Found {r}\n"),
Poll::Ready(None) => return Poll::Ready(format!("{log}Ended")),
Poll::Pending => match send.try_send(()) {
Ok(()) => log += "Unblocked\n",
Err(err) => return Poll::Ready(format!("{log}Unblock err: {err}")),
},
}
Poll::Pending
})
.await;
const EXPECTED: &str = "\
Found 0\n\
Found 1\n\
Unblocked\n\
Found 2\n\
Found 3\n\
Found 4\n\
Ended";
assert_eq!(log, EXPECTED)
})
}
#[test]
fn sync_try_all_ok() {
spin_on(async {
let v = try_stream::<_, ()>(async |mut cx| {
for i in 0..5 {
cx.emit(i).await
}
Ok(cx)
})
.try_collect::<Vec<_>>()
.await;
assert_eq!(v, Ok(vec![0, 1, 2, 3, 4]))
})
}
#[test]
fn sync_try_err() {
spin_on(async {
let v = try_stream::<_, ()>(async |mut cx| {
for i in 0..5 {
cx.emit(i).await
}
Err(())
})
.try_collect::<Vec<_>>()
.await;
assert_eq!(v, Err(()))
})
}
}

View File

@@ -13,5 +13,4 @@ quote = "1.0.40"
syn = { version = "2.0.106" }
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
proc-macro2 = "1.0.101"
darling = "0.21.3"
itertools = "0.14.0"

View File

@@ -6,8 +6,8 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-stream = "0.3.6"
futures = { version = "0.3.31", features = ["std"] }
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
futures = { version = "0.3.31", features = ["std"], default-features = false }
itertools = "0.14.0"
never = "0.1.0"
ordered-float = "5.0.0"

View File

@@ -7,7 +7,7 @@ use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use async_stream::stream;
use async_fn_stream::stream;
use futures::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, StreamExt};
use never::Never;
use ordered_float::NotNan;
@@ -133,7 +133,13 @@ impl Encode for str {
impl<T: Decode> Decode for Vec<T> {
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
let len = u64::decode(read.as_mut()).await.try_into().unwrap();
stream! { loop { yield T::decode(read.as_mut()).await } }.take(len).collect().await
stream(async |mut cx| {
for _ in 0..len {
cx.emit(T::decode(read.as_mut()).await).await
}
})
.collect()
.await
}
}
impl<T: Encode> Encode for Vec<T> {
@@ -186,7 +192,13 @@ impl<T: Encode, E: Encode> Encode for Result<T, E> {
impl<K: Decode + Eq + Hash, V: Decode> Decode for HashMap<K, V> {
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
let len = u64::decode(read.as_mut()).await.try_into().unwrap();
stream! { loop { yield <(K, V)>::decode(read.as_mut()).await } }.take(len).collect().await
stream(async |mut cx| {
for _ in 0..len {
cx.emit(<(K, V)>::decode(read.as_mut()).await).await
}
})
.collect()
.await
}
}
impl<K: Encode + Eq + Hash, V: Encode> Encode for HashMap<K, V> {
@@ -259,8 +271,13 @@ impl Encode for bool {
impl<T: Decode, const N: usize> Decode for [T; N] {
async fn decode<R: AsyncRead + ?Sized>(mut read: Pin<&mut R>) -> Self {
// TODO: figure out how to do this in safe rust on the stack
let v =
stream! { loop { yield T::decode(read.as_mut()).await } }.take(N).collect::<Vec<_>>().await;
let v = stream(async |mut cx| {
for _ in 0..N {
cx.emit(T::decode(read.as_mut()).await).await
}
})
.collect::<Vec<_>>()
.await;
v.try_into().unwrap_or_else(|_| unreachable!("The length of this stream is statically known"))
}
}

View File

@@ -9,7 +9,7 @@ edition = "2024"
ordered-float = "5.0.0"
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
futures = { version = "0.3.31", features = ["std"] }
futures = { version = "0.3.31", features = ["std"], default-features = false }
[dev-dependencies]
test_executors = "0.3.2"

View File

@@ -6,11 +6,11 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
async-once-cell = "0.5.4"
async-stream = "0.3.6"
derive_destructure = "1.0.0"
dyn-clone = "1.0.20"
futures = { version = "0.3.31", features = ["std"] }
futures = { version = "0.3.31", features = ["std"], default-features = false }
hashbrown = "0.16.0"
itertools = "0.14.0"
lazy_static = "1.5.0"

View File

@@ -4,7 +4,7 @@ use std::future::Future;
use std::marker::PhantomData;
use std::rc::Rc;
use async_stream::stream;
use async_fn_stream::stream;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
@@ -195,11 +195,11 @@ pub async fn ttv_from_api<H: ExprRepr, X: ExtraTok>(
src: &Sym,
i: &Interner,
) -> Vec<TokTree<H, X>> {
stream! {
stream(async |mut cx| {
for tok in tokv {
yield TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await
cx.emit(TokTree::<H, X>::from_api(tok.borrow(), hctx, xctx, src, i).boxed_local().await).await
}
}
})
.collect()
.await
}
@@ -209,11 +209,11 @@ pub async fn ttv_into_api<H: ExprRepr, X: ExtraTok>(
hctx: &mut H::ToApiCtx<'_>,
xctx: &mut X::ToApiCtx<'_>,
) -> Vec<api::TokenTree> {
stream! {
stream(async |mut cx| {
for tok in tokv {
yield tok.into_api(hctx, xctx).await
cx.emit(tok.into_api(hctx, xctx).await).await
}
}
})
.collect()
.await
}

View File

@@ -6,13 +6,15 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ahash = "0.8.12"
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
async-lock = "3.4.1"
async-once-cell = "0.5.4"
async-stream = "0.3.6"
derive_destructure = "1.0.0"
dyn-clone = "1.0.20"
futures = { version = "0.3.31", features = ["std"] }
futures = { version = "0.3.31", features = [
"std",
"async-await",
], default-features = false }
hashbrown = "0.16.0"
include_dir = { version = "0.7.4", optional = true }
itertools = "0.14.0"
@@ -29,10 +31,11 @@ ordered-float = "5.0.0"
pastey = "0.1.1"
some_executor = "0.6.1"
substack = "1.1.1"
tokio = { version = "1.47.1", optional = true }
tokio = { version = "1.47.1", optional = true, features = [] }
tokio-util = { version = "0.7.16", optional = true, features = ["compat"] }
trait-set = "0.3.0"
[features]
tokio = ["dep:tokio", "dep:tokio-util"]
default = ["tokio"]

View File

@@ -1,4 +1,5 @@
use std::any::{Any, TypeId, type_name};
use std::collections::HashMap;
use std::fmt;
use std::future::Future;
use std::num::NonZeroU32;
@@ -6,7 +7,6 @@ use std::ops::Deref;
use std::pin::Pin;
use std::rc::Rc;
use ahash::HashMap;
use dyn_clone::{DynClone, clone_box};
use futures::future::LocalBoxFuture;
use futures::{AsyncRead, AsyncWrite, FutureExt, StreamExt, stream};

View File

@@ -202,8 +202,12 @@ pub fn extension_init(
let record = SystemRecord { ctx, lazy_members: lazy_mems.into_inner() };
let systems = systems_weak.upgrade().expect("System constructed during shutdown");
systems.lock().await.insert(new_sys.id, record);
let response =
api::NewSystemResponse { lex_filter, const_root, line_types: vec![], prelude };
let line_types = join_all(
(cted.inst().dyn_parsers().iter())
.map(|p| async { i.i(p.line_head()).await.to_api() }),
)
.await;
let response = api::NewSystemResponse { lex_filter, const_root, line_types, prelude };
hand.handle(&new_sys, &response).await
},
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => {

View File

@@ -1,6 +1,6 @@
use std::marker::PhantomData;
use async_stream::stream;
use async_fn_stream::stream;
use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, Stream, StreamExt};
use itertools::Itertools;
@@ -218,14 +218,15 @@ impl ConstCtx {
sys: self.ctx.sys_id(),
names: names.into_iter().map(|n| n.to_api()).collect_vec(),
};
stream! {
stream(async |mut cx| {
for name_opt in self.ctx.reqnot().request(resolve_names).await {
yield match name_opt {
cx.emit(match name_opt {
Err(e) => Err(OrcErrv::from_api(&e, self.ctx.i()).await),
Ok(name) => Ok(Sym::from_api(name, self.ctx.i()).await)
}
Ok(name) => Ok(Sym::from_api(name, self.ctx.i()).await),
})
.await
}
}
})
}
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")

View File

@@ -2,7 +2,7 @@ use crate::entrypoint::ExtensionData;
#[cfg(feature = "tokio")]
pub async fn tokio_main(data: ExtensionData) {
use std::io::Write;
use std::io::{ErrorKind, Write};
use std::mem;
use std::pin::{Pin, pin};
use std::rc::Rc;
@@ -14,8 +14,7 @@ pub async fn tokio_main(data: ExtensionData) {
use futures::stream::FuturesUnordered;
use orchid_api_traits::{Decode, Encode};
use orchid_base::msg::{recv_msg, send_msg};
use tokio::io;
use tokio::io::Stdout;
use tokio::io::{Stdout, stdin, stdout};
use tokio::task::{LocalSet, spawn_local};
use tokio_util::compat::{Compat, TokioAsyncReadCompatExt, TokioAsyncWriteCompatExt};
@@ -24,7 +23,7 @@ pub async fn tokio_main(data: ExtensionData) {
let local_set = LocalSet::new();
local_set.spawn_local(async {
let host_header = api::HostHeader::decode(Pin::new(&mut tokio::io::stdin().compat())).await;
let host_header = api::HostHeader::decode(Pin::new(&mut stdin().compat())).await;
let init =
Rc::new(extension_init(data, host_header, Rc::new(|fut| mem::drop(spawn_local(fut)))));
let mut buf = Vec::new();
@@ -36,10 +35,10 @@ pub async fn tokio_main(data: ExtensionData) {
let mut io = FuturesUnordered::<LocalBoxFuture<()>>::new();
io.push(Box::pin(async {
loop {
match recv_msg(pin!(io::stdin().compat())).await {
match recv_msg(pin!(stdin().compat())).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) if e.kind() == ErrorKind::BrokenPipe => break,
Err(e) if e.kind() == ErrorKind::UnexpectedEof => break,
Err(e) => panic!("{e}"),
}
}
@@ -47,7 +46,7 @@ pub async fn tokio_main(data: ExtensionData) {
io.push(Box::pin(async {
while let Some(msg) = init.recv().await {
static STDOUT: OnceCell<Mutex<Compat<Stdout>>> = OnceCell::new();
let stdout_lk = STDOUT.get_or_init(async { Mutex::new(io::stdout().compat_write()) }).await;
let stdout_lk = STDOUT.get_or_init(async { Mutex::new(stdout().compat_write()) }).await;
let mut stdout_g = stdout_lk.lock().await;
send_msg(pin!(&mut *stdout_g), &msg[..]).await.expect("Parent pipe broken");
}

View File

@@ -1,7 +1,7 @@
use std::num::NonZero;
use std::rc::Rc;
use async_stream::stream;
use async_fn_stream::stream;
use dyn_clone::{DynClone, clone_box};
use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, StreamExt};
@@ -209,8 +209,14 @@ impl MemKind {
Self::Lazy(lazy) => api::MemberKind::Lazy(ctx.with_lazy(lazy)),
Self::Const(c) => api::MemberKind::Const(c.api_return(ctx.sys(), ctx.req()).await),
Self::Mod { members } => api::MemberKind::Module(api::Module {
members: Box::pin(stream! { for m in members { yield m.into_api(ctx).await } }.collect())
.await,
members: stream(async |mut cx| {
for m in members {
cx.emit(m.into_api(ctx).await).await
}
})
.collect()
.boxed_local()
.await,
}),
}
}

View File

@@ -6,13 +6,13 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
async-lock = "3.4.1"
async-once-cell = "0.5.4"
async-process = "2.4.0"
async-stream = "0.3.6"
bound = "0.6.0"
derive_destructure = "1.0.0"
futures = { version = "0.3.31", features = ["std"] }
futures = { version = "0.3.31", features = ["std"], default-features = false }
hashbrown = "0.16.0"
itertools = "0.14.0"
lazy_static = "1.5.0"

View File

@@ -5,7 +5,7 @@ use std::num::NonZeroU64;
use std::pin::pin;
use std::rc::{Rc, Weak};
use async_stream::stream;
use async_fn_stream::stream;
use derive_destructure::destructure;
use futures::channel::mpsc::{Sender, channel};
use futures::future::{join, join_all};
@@ -66,7 +66,7 @@ impl Extension {
let init = Rc::new(init);
let (exiting_snd, exiting_rcv) = channel::<()>(0);
(ctx.spawn)(clone!(init, weak, ctx; Box::pin(async move {
let rcv_stream = stream! { loop { yield init.recv().await } };
let rcv_stream = stream(async |mut cx| loop { cx.emit( init.recv().await).await });
let mut event_stream = pin!(stream::select(exiting_rcv.map(|()| None), rcv_stream));
while let Some(Some(msg)) = event_stream.next().await {
if let Some(reqnot) = weak.upgrade().map(|rc| rc.reqnot.clone()) {
@@ -207,14 +207,15 @@ impl Extension {
let sys = weak_sys.upgrade().expect("ResolveNames after sys drop");
sys.name_resolver(*constid).await
};
let responses = stream! {
let responses = stream(async |mut cx| {
for name in names {
yield match resolver(&ctx.i.ex(*name).await[..]).await {
cx.emit(match resolver(&ctx.i.ex(*name).await[..]).await {
Ok(abs) => Ok(abs.to_sym(&ctx.i).await.to_api()),
Err(e) => Err(e.to_api()),
}
})
.await
}
}
})
.collect()
.await;
hand.handle(rn, &responses).await

View File

@@ -120,7 +120,7 @@ pub async fn parse_exportable_item<'a>(
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv(
ctx.i().i("Unrecognized line type").await,
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
format!("Line types are: mod, {ext_lines}"),
[tail.prev().sr()],
));
};

View File

@@ -18,7 +18,7 @@ use orchid_base::location::SrcRange;
use orchid_base::name::{NameLike, Sym, VName, VPath};
use orchid_base::parse::Comment;
use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::tree::ttv_from_api;
use orchid_base::tree::{recur, ttv_from_api};
use ordered_float::NotNan;
use substack::{Stackframe, Substack};
@@ -28,7 +28,9 @@ use crate::dealias::walk;
use crate::expr::{ExprParseCtx, ExprWillPanic};
use crate::expr_store::ExprStore;
use crate::extension::{Extension, WeakExtension};
use crate::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule};
use crate::parsed::{
Item, ItemKind, ParsTok, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule,
};
use crate::tree::Root;
#[derive(destructure)]
@@ -112,7 +114,13 @@ impl System {
let src_path = line.first().expect("cannot be empty").sr.path();
let line = join_all(line.into_iter().map(|t| async {
let mut expr_store = self.0.ext.exprs().clone();
t.into_api(&mut expr_store, &mut ExprWillPanic).await
let without_new_expr = recur(t, &|t, r| {
if let ParsTok::NewExpr(expr) = t.tok {
return ParsTok::Handle(expr).at(t.sr);
}
r(t)
});
without_new_expr.into_api(&mut expr_store, &mut ExprWillPanic).await
}))
.await;
let comments = comments.iter().map(Comment::to_api).collect_vec();

View File

@@ -5,8 +5,7 @@ edition = "2024"
[dependencies]
async-once-cell = "0.5.4"
async-stream = "0.3.6"
futures = { version = "0.3.31", features = ["std"] }
futures = { version = "0.3.31", features = ["std"], default-features = false }
hashbrown = "0.16.0"
itertools = "0.14.0"
never = "0.1.0"

View File

@@ -24,7 +24,7 @@ pub struct MacroSystem;
impl SystemCtor for MacroSystem {
type Deps = StdSystem;
type Instance = Self;
const NAME: &'static str = "macros";
const NAME: &'static str = "orchid::macros";
const VERSION: f64 = 0.00_01;
fn inst(_: SystemHandle<StdSystem>) -> Self::Instance { Self }
}

View File

@@ -6,8 +6,7 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-std = "1.13.0"
async-stream = "0.3.6"
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
camino = "1.1.9"
clap = { version = "4.5.24", features = ["derive", "env"] }
ctrlc = "3.4.5"

View File

@@ -7,9 +7,7 @@ use std::mem;
use std::process::{Command, ExitCode};
use std::rc::Rc;
use async_std::io::stdin;
use async_std::path::PathBuf;
use async_stream::try_stream;
use async_fn_stream::try_stream;
use camino::Utf8PathBuf;
use clap::{Parser, Subcommand};
use futures::{Stream, TryStreamExt, io};
@@ -32,6 +30,7 @@ use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModul
use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems;
use substack::Substack;
use tokio::io::{AsyncBufReadExt, BufReader, stdin};
use tokio::task::{LocalSet, spawn_local};
use crate::parse_folder::parse_folder;
@@ -62,7 +61,7 @@ pub enum Commands {
file: Utf8PathBuf,
},
Repl,
Execute {
Exec {
#[arg(long)]
proj: Option<Utf8PathBuf>,
#[arg()]
@@ -76,19 +75,16 @@ fn get_all_extensions<'a>(
msg_logger: &'a Logger,
ctx: &'a Ctx,
) -> impl Stream<Item = io::Result<Extension>> + 'a {
try_stream! {
try_stream(async |mut cx| {
for ext_path in args.extension.iter() {
let exe = if cfg!(windows) {
ext_path.with_extension("exe")
} else {
ext_path.clone()
};
let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()).await
.unwrap();
let ext = Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?;
yield ext
let exe = if cfg!(windows) { ext_path.with_extension("exe") } else { ext_path.clone() };
let init =
ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone())
.await?;
cx.emit(Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?).await;
}
}
Ok(cx)
})
}
#[tokio::main]
@@ -153,13 +149,14 @@ async fn main() -> io::Result<ExitCode> {
let mut counter = 0;
let mut imports = Vec::new();
let usercode_path = sym!(usercode; i).await;
let mut stdin = BufReader::new(stdin());
loop {
counter += 1;
let (mut 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();
stdin.read_line(&mut prompt).await.unwrap();
let name = i.i(&format!("_{counter}")).await;
let path = usercode_path.suffix([name.clone()], i).await;
let mut lexemes =
@@ -229,13 +226,13 @@ async fn main() -> io::Result<ExitCode> {
}
}
},
Commands::Execute { proj, code } => {
Commands::Exec { proj, code } => {
let reporter = Reporter::new();
let path = sym!(usercode::entrypoint; i).await;
let prefix_sr = SrcRange::zw(path.clone(), 0);
let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap();
if let Some(proj_path) = proj {
let path = PathBuf::from(proj_path.into_std_path_buf());
let path = proj_path.into_std_path_buf();
match parse_folder(&root, path, sym!(src; i).await, &reporter, ctx.clone()).await {
Ok(r) => root = r,
Err(e) => {

View File

@@ -1,10 +1,6 @@
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use async_std::fs;
use async_std::fs::File;
use async_std::io::ReadExt;
use async_std::path::{Path, PathBuf};
use async_std::stream::StreamExt;
use futures::FutureExt;
use itertools::Itertools;
use orchid_base::error::{OrcRes, Reporter, async_io_err, mk_errv, os_str_to_string};
@@ -17,6 +13,8 @@ use orchid_host::parse::{HostParseCtxImpl, parse_items};
use orchid_host::parsed::ParsedModule;
use orchid_host::tree::Root;
use substack::Substack;
use tokio::fs::{self, File};
use tokio::io::AsyncReadExt;
pub async fn parse_folder(
root: &Root,
@@ -30,7 +28,7 @@ pub async fn parse_folder(
return Ok(root.add_parsed(&parsed_module, ns, rep).await);
async fn recur(path: &Path, ns: Sym, rep: &Reporter, ctx: Ctx) -> OrcRes<Option<ParsedModule>> {
let sr = SrcRange::new(0..0, &ns);
if path.is_dir().await {
if path.is_dir() {
let Some(name_os) = path.file_name() else {
return Err(mk_errv(
ctx.i.i("Could not read directory name").await,
@@ -46,9 +44,10 @@ pub async fn parse_folder(
Err(err) => return Err(async_io_err(err, &ctx.i, [sr]).await),
Ok(s) => s,
};
while let Some(entry_res) = stream.next().await {
let entry = match entry_res {
Ok(ent) => ent,
loop {
let entry = match stream.next_entry().await {
Ok(Some(ent)) => ent,
Ok(None) => break,
Err(err) => {
rep.report(async_io_err(err, &ctx.i, [sr.clone()]).await);
continue;