lex_hello worked for a second just now

this is just a backup however
This commit is contained in:
2025-02-02 10:20:03 +01:00
parent 2b79e96dc9
commit 1556d54226
45 changed files with 646 additions and 371 deletions

198
Cargo.lock generated
View File

@@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "ahash"
version = "0.7.8"
@@ -77,7 +92,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -87,7 +102,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
dependencies = [
"anstyle",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -163,7 +178,7 @@ dependencies = [
"rustix",
"slab",
"tracing",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -217,7 +232,7 @@ dependencies = [
"rustix",
"signal-hook-registry",
"slab",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -286,6 +301,21 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "backtrace"
version = "0.3.74"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "bitflags"
version = "2.6.0"
@@ -583,7 +613,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [
"libc",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -760,6 +790,12 @@ dependencies = [
"wasi",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gloo-timers"
version = "0.3.0"
@@ -905,6 +941,16 @@ version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.22"
@@ -938,6 +984,26 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "miniz_oxide"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"wasi",
"windows-sys 0.52.0",
]
[[package]]
name = "never"
version = "0.1.0"
@@ -953,6 +1019,15 @@ dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.20.2"
@@ -1000,6 +1075,7 @@ version = "0.1.0"
dependencies = [
"async-once-cell",
"async-std",
"async-stream",
"derive_destructure",
"dyn-clone",
"futures",
@@ -1076,6 +1152,9 @@ dependencies = [
name = "orchid-std"
version = "0.1.0"
dependencies = [
"async-once-cell",
"async-std",
"futures",
"itertools",
"never",
"once_cell",
@@ -1085,17 +1164,22 @@ dependencies = [
"orchid-base",
"orchid-extension",
"ordered-float",
"tokio",
]
[[package]]
name = "orcx"
version = "0.1.0"
dependencies = [
"async-stream",
"camino",
"clap",
"futures",
"itertools",
"orchid-base",
"orchid-host",
"substack",
"tokio",
]
[[package]]
@@ -1113,6 +1197,29 @@ version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba"
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]]
name = "paste"
version = "1.0.15"
@@ -1154,7 +1261,7 @@ dependencies = [
"pin-project-lite",
"rustix",
"tracing",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -1273,6 +1380,15 @@ dependencies = [
"getrandom",
]
[[package]]
name = "redox_syscall"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834"
dependencies = [
"bitflags",
]
[[package]]
name = "regex"
version = "1.11.1"
@@ -1390,6 +1506,12 @@ dependencies = [
"serde_json",
]
[[package]]
name = "rustc-demangle"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
[[package]]
name = "rustix"
version = "0.38.43"
@@ -1400,7 +1522,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
@@ -1418,6 +1540,12 @@ dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "seahash"
version = "4.1.0"
@@ -1491,6 +1619,22 @@ dependencies = [
"autocfg",
]
[[package]]
name = "smallvec"
version = "1.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]]
name = "socket2"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8"
dependencies = [
"libc",
"windows-sys 0.52.0",
]
[[package]]
name = "some_executor"
version = "0.3.0"
@@ -1611,6 +1755,35 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.43.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e"
dependencies = [
"backtrace",
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.52.0",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2 1.0.92",
"quote 1.0.38",
"syn 2.0.95",
]
[[package]]
name = "toml_datetime"
version = "0.6.8"
@@ -1833,7 +2006,16 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [
"windows-sys",
"windows-sys 0.59.0",
]
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]

View File

@@ -12,7 +12,9 @@ pub fn derive(input: TokenStream) -> TokenStream {
let decode = decode_body(&input.data);
let expanded = quote! {
impl #impl_generics orchid_api_traits::Decode for #name #ty_generics #where_clause {
async fn decode<R: async_std::io::Read + ?Sized>(mut read: std::pin::Pin<&mut R>) -> Self {
async fn decode<R: orchid_api_traits::async_std::io::Read + ?Sized>(
mut read: std::pin::Pin<&mut R>
) -> Self {
#decode
}
}
@@ -27,7 +29,8 @@ fn decode_fields(fields: &syn::Fields) -> pm2::TokenStream {
let exprs = fields.iter().map(|f| {
let syn::Field { ty, ident, .. } = &f;
quote! {
#ident : < #ty as orchid_api_traits::Decode>::decode(read.as_mut()).await
#ident : (Box::pin(< #ty as orchid_api_traits::Decode>::decode(read.as_mut()))
as std::pin::Pin<Box<dyn std::future::Future<Output = _>>>).await
}
});
quote! { { #( #exprs, )* } }
@@ -36,7 +39,8 @@ fn decode_fields(fields: &syn::Fields) -> pm2::TokenStream {
let exprs = fields.iter().map(|field| {
let ty = &field.ty;
quote! {
< #ty as orchid_api_traits::Decode>::decode(read.as_mut()).await,
(Box::pin(< #ty as orchid_api_traits::Decode>::decode(read.as_mut()))
as std::pin::Pin<Box<dyn std::future::Future<Output = _>>>).await,
}
});
quote! { ( #( #exprs )* ) }

View File

@@ -14,7 +14,12 @@ pub fn derive(input: TokenStream) -> TokenStream {
let encode = encode_body(&input.data);
let expanded = quote! {
impl #e_impl_generics orchid_api_traits::Encode for #name #e_ty_generics #e_where_clause {
async fn encode<W: async_std::io::Write + ?Sized>(&self, mut write: std::pin::Pin<&mut W>) { #encode }
async fn encode<W: orchid_api_traits::async_std::io::Write + ?Sized>(
&self,
mut write: std::pin::Pin<&mut W>
) {
#encode
}
}
};
TokenStream::from(expanded)
@@ -37,7 +42,8 @@ fn encode_body(data: &syn::Data) -> Option<pm2::TokenStream> {
let body = encode_items(&v.fields);
quote! {
Self::#ident #dest => {
(#i as u8).encode(write.as_mut()).await;
(Box::pin((#i as u8).encode(write.as_mut()))
as std::pin::Pin<Box<dyn std::future::Future<Output = _>>>).await;
#body
}
}
@@ -53,7 +59,10 @@ fn encode_body(data: &syn::Data) -> Option<pm2::TokenStream> {
}
fn encode_names<T: ToTokens>(names: impl Iterator<Item = T>) -> pm2::TokenStream {
quote! { #( #names .encode(write.as_mut()).await; )* }
quote! { #(
(Box::pin(#names .encode(write.as_mut()))
as std::pin::Pin<Box<dyn std::future::Future<Output = _>>>).await;
)* }
}
fn encode_items(fields: &syn::Fields) -> Option<pm2::TokenStream> {

View File

@@ -3,6 +3,7 @@ mod helpers;
mod hierarchy;
mod relations;
pub use async_std;
pub use coding::*;
pub use helpers::*;
pub use hierarchy::*;

View File

@@ -1,10 +1,11 @@
use core::fmt;
use std::future::Future;
use super::coding::Coding;
use crate::helpers::enc_vec;
pub trait Request: Coding + Sized + Send + 'static {
type Response: Coding + Send + 'static;
pub trait Request: fmt::Debug + Coding + Sized + 'static {
type Response: fmt::Debug + Coding + 'static;
}
pub async fn respond<R: Request>(_: &R, rep: R::Response) -> Vec<u8> { enc_vec(&rep).await }
@@ -16,11 +17,11 @@ pub async fn respond_with<R: Request, F: Future<Output = R::Response>>(
}
pub trait Channel: 'static {
type Req: Coding + Sized + Send + 'static;
type Notif: Coding + Sized + Send + 'static;
type Req: Coding + Sized + 'static;
type Notif: Coding + Sized + 'static;
}
pub trait MsgSet: Send + Sync + 'static {
pub trait MsgSet: Sync + 'static {
type In: Channel;
type Out: Channel;
}

View File

@@ -6,6 +6,7 @@ use crate::ExtHostNotif;
pub enum LogStrategy {
StdErr,
File(String),
Discard,
}
#[derive(Clone, Debug, Coding, Hierarchy)]

View File

@@ -73,7 +73,7 @@ impl Request for Ping {
}
/// Requests running from the extension to the host
#[derive(Clone, Coding, Hierarchy)]
#[derive(Clone, Debug, Coding, Hierarchy)]
#[extendable]
pub enum ExtHostReq {
Ping(Ping),

View File

@@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
async-once-cell = "0.5.4"
async-std = "1.13.0"
async-stream = "0.3.6"
derive_destructure = "1.0.0"
dyn-clone = "1.0.17"
futures = "0.3.31"

View File

@@ -1,9 +1,13 @@
use std::ops::Deref;
use std::rc::Rc;
use futures::future::LocalBoxFuture;
use crate::api;
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
/// - send a message
/// - wait for a message to arrive
@@ -12,10 +16,7 @@ use crate::api;
/// There are no ordering guarantees about these
pub trait ExtPort {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()>;
fn recv<'a>(
&'a self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
) -> LocalBoxFuture<'a, ()>;
fn recv<'a>(&'a self, cb: RecvCB<'a>) -> LocalBoxFuture<'a, ()>;
}
pub struct ExtInit {
@@ -24,12 +25,7 @@ pub struct ExtInit {
}
impl ExtInit {
pub async fn send(&self, msg: &[u8]) { self.port.send(msg).await }
pub async fn recv<'a, 's: 'a>(
&'s self,
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
) {
self.port.recv(Box::new(cb)).await
}
pub async fn recv<'a, 's: 'a>(&'s self, cb: RecvCB<'a>) { self.port.recv(Box::new(cb)).await }
}
impl Deref for ExtInit {
type Target = api::ExtensionHeader;

View File

@@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::fmt;
use std::ops::Add;
use std::sync::Arc;
@@ -171,3 +172,17 @@ pub fn mk_errv(
pub trait Reporter {
fn report(&self, e: impl Into<OrcErrv>);
}
pub struct ReporterImpl {
errors: RefCell<Vec<OrcErr>>,
}
impl ReporterImpl {
pub fn new() -> Self { Self { errors: RefCell::new(vec![]) } }
pub fn errv(self) -> Option<OrcErrv> { OrcErrv::new(self.errors.into_inner()).ok() }
}
impl Reporter for ReporterImpl {
fn report(&self, e: impl Into<OrcErrv>) { self.errors.borrow_mut().extend(e.into()) }
}
impl Default for ReporterImpl {
fn default() -> Self { Self::new() }
}

View File

@@ -229,8 +229,6 @@ impl Interner {
/// Intern some data; query its identifier if not known locally
pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
let data = t.get_owned();
let job = format!("{t:?} in {}", if self.master.is_some() { "replica" } else { "master" });
eprintln!("Interning {job}");
let mut g = self.interners.lock().await;
let typed = T::bimap(&mut g);
if let Some(tok) = typed.by_value(&data) {
@@ -243,7 +241,6 @@ impl Interner {
};
let tok = Tok::new(data, marker);
T::bimap(&mut g).insert(tok.clone());
eprintln!("Interned {job}");
tok
}
/// Extern an identifier; query the data it represents if not known locally

View File

@@ -20,9 +20,11 @@ impl Logger {
}
pub fn write_fmt(&self, fmt: Arguments) {
match &self.0 {
api::LogStrategy::Discard => (),
api::LogStrategy::StdErr => stderr().write_fmt(fmt).expect("Could not write to stderr!"),
api::LogStrategy::File(f) => {
let mut file = File::open(f).expect("Could not open logfile");
let mut file = (File::options().write(true).create(true).truncate(true).open(f))
.expect("Could not open logfile");
file.write_fmt(fmt).expect("Could not write to logfile");
},
}

View File

@@ -2,8 +2,7 @@ use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::Arc;
use async_std::stream;
use async_std::sync::Mutex;
use async_stream::stream;
use futures::future::LocalBoxFuture;
use futures::{FutureExt, StreamExt};
use never::Never;
@@ -63,7 +62,7 @@ pub enum MTok<'a, A> {
Ref(Arc<MTok<'a, Never>>),
/// Used in the matcher to skip previous macro output which can only go in
/// vectorial placeholders
Done(Arc<MTok<'a, A>>),
Done(Rc<MTok<'a, A>>),
}
impl<'a, A> MTok<'a, A> {
pub(crate) async fn from_api(
@@ -99,15 +98,15 @@ impl<'a, A> MTok<'a, A> {
}
pub async fn mtreev_from_api<'a, 'b, A>(
api: impl IntoIterator<Item = &'b api::MacroTree>,
apiv: impl IntoIterator<Item = &'b api::MacroTree>,
i: &Interner,
do_atom: &mut impl MacroAtomFromApi<'a, A>,
do_atom: &'b mut (impl MacroAtomFromApi<'a, A> + 'b),
) -> Vec<MTree<'a, A>> {
let do_atom_lk = Mutex::new(do_atom);
stream::from_iter(api)
.then(|api| async {
MTree::from_api(api, &mut *do_atom_lk.lock().await, i).boxed_local().await
})
stream! {
for api in apiv {
yield MTree::from_api(api, do_atom, i).boxed_local().await
}
}
.collect()
.await
}

View File

@@ -192,13 +192,13 @@ pub struct Parsed<'a, 'b, T, A: AtomRepr, X: ExtraTok> {
pub type ParseRes<'a, 'b, T, A, X> = OrcRes<Parsed<'a, 'b, T, A, X>>;
pub async fn parse_multiname<'a, 'b, A: AtomRepr, X: ExtraTok>(
ctx: &impl Reporter,
ctx: &(impl Reporter + ?Sized),
tail: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, Vec<(Import, Pos)>, A, X> {
let ret = rec(ctx, tail).await;
#[allow(clippy::type_complexity)] // it's an internal function
pub async fn rec<'a, 'b, A: AtomRepr, X: ExtraTok>(
ctx: &impl Reporter,
ctx: &(impl Reporter + ?Sized),
tail: Snippet<'a, 'b, A, X>,
) -> ParseRes<'a, 'b, Vec<(Vec<Tok<String>>, Option<Tok<String>>, Pos)>, A, X> {
let comma = tail.i(",").await;

View File

@@ -18,6 +18,7 @@ use orchid_api_traits::{Channel, Coding, Decode, Encode, MsgSet, Request};
use trait_set::trait_set;
use crate::clone;
use crate::logging::Logger;
pub struct Receipt<'a>(PhantomData<&'a mut ()>);
@@ -110,16 +111,24 @@ impl Deref for RawReply {
fn deref(&self) -> &Self::Target { get_id(&self.0[..]).1 }
}
pub struct ReqNot<T: MsgSet>(Arc<Mutex<ReqNotData<T>>>);
pub struct ReqNot<T: MsgSet>(Arc<Mutex<ReqNotData<T>>>, Logger);
impl<T: MsgSet> ReqNot<T> {
pub fn new(send: impl SendFn<T>, notif: impl NotifFn<T>, req: impl ReqFn<T>) -> Self {
Self(Arc::new(Mutex::new(ReqNotData {
pub fn new(
logger: Logger,
send: impl SendFn<T>,
notif: impl NotifFn<T>,
req: impl ReqFn<T>,
) -> Self {
Self(
Arc::new(Mutex::new(ReqNotData {
id: 1,
send: Box::new(send),
notif: Box::new(notif),
req: Box::new(req),
responses: HashMap::new(),
})))
})),
logger,
)
}
/// Can be called from a polling thread or dispatched in any other way
@@ -133,7 +142,7 @@ impl<T: MsgSet> ReqNot<T> {
notif_cb(notif_val, self.clone()).await
} else if 0 < id.bitand(1 << 63) {
let sender = g.responses.remove(&!id).expect("Received response for invalid message");
sender.send(message.to_vec()).await.unwrap();
sender.send(message.to_vec()).await.unwrap()
} else {
let message = <T::In as Channel>::Req::decode(Pin::new(&mut &payload[..])).await;
let mut req_cb = clone_box(&*g.req);
@@ -154,28 +163,34 @@ impl<T: MsgSet> ReqNot<T> {
pub trait DynRequester {
type Transfer;
fn logger(&self) -> &Logger;
/// Encode and send a request, then receive the response buffer.
fn raw_request(&self, data: Self::Transfer) -> LocalBoxFuture<'_, RawReply>;
}
pub struct MappedRequester<'a, T: 'a>(Box<dyn Fn(T) -> LocalBoxFuture<'a, RawReply> + 'a>);
pub struct MappedRequester<'a, T: 'a>(Box<dyn Fn(T) -> LocalBoxFuture<'a, RawReply> + 'a>, Logger);
impl<'a, T> MappedRequester<'a, T> {
fn new<U: DynRequester + 'a>(req: U) -> Self
fn new<U: DynRequester + 'a>(req: U, logger: Logger) -> Self
where T: Into<U::Transfer> {
let req_arc = Arc::new(req);
MappedRequester(Box::new(move |t| {
MappedRequester(
Box::new(move |t| {
Box::pin(clone!(req_arc; async move { req_arc.raw_request(t.into()).await}))
}))
}),
logger,
)
}
}
impl<T> DynRequester for MappedRequester<'_, T> {
type Transfer = T;
fn logger(&self) -> &Logger { &self.1 }
fn raw_request(&self, data: Self::Transfer) -> LocalBoxFuture<'_, RawReply> { self.0(data) }
}
impl<T: MsgSet> DynRequester for ReqNot<T> {
type Transfer = <T::Out as Channel>::Req;
fn logger(&self) -> &Logger { &self.1 }
fn raw_request(&self, req: Self::Transfer) -> LocalBoxFuture<'_, RawReply> {
Box::pin(async move {
let mut g = self.0.lock().await;
@@ -203,17 +218,21 @@ pub trait Requester: DynRequester {
) -> impl Future<Output = R::Response>;
fn map<'a, U: Into<Self::Transfer>>(self) -> MappedRequester<'a, U>
where Self: Sized + 'a {
MappedRequester::new(self)
let logger = self.logger().clone();
MappedRequester::new(self, logger)
}
}
impl<This: DynRequester + ?Sized> Requester for This {
async fn request<R: Request + Into<Self::Transfer>>(&self, data: R) -> R::Response {
R::Response::decode(Pin::new(&mut &self.raw_request(data.into()).await[..])).await
let req = format!("{data:?}");
let rep = R::Response::decode(Pin::new(&mut &self.raw_request(data.into()).await[..])).await;
writeln!(self.logger(), "Request {req} got response {rep:?}");
rep
}
}
impl<T: MsgSet> Clone for ReqNot<T> {
fn clone(&self) -> Self { Self(self.0.clone()) }
fn clone(&self) -> Self { Self(self.0.clone(), self.1.clone()) }
}
#[cfg(test)]
@@ -223,12 +242,14 @@ mod test {
use async_std::sync::Mutex;
use futures::FutureExt;
use orchid_api::LogStrategy;
use orchid_api_derive::Coding;
use orchid_api_traits::{Channel, Request};
use test_executors::spin_on;
use super::{MsgSet, ReqNot};
use crate::clone;
use crate::logging::Logger;
use crate::reqnot::Requester as _;
#[derive(Clone, Debug, Coding, PartialEq)]
@@ -252,8 +273,10 @@ mod test {
#[test]
fn notification() {
spin_on(async {
let logger = Logger::new(LogStrategy::StdErr);
let received = Arc::new(Mutex::new(None));
let receiver = ReqNot::<TestMsgSet>::new(
logger.clone(),
|_, _| panic!("Should not send anything"),
clone!(received; move |notif, _| clone!(received; async move {
*received.lock().await = Some(notif);
@@ -261,6 +284,7 @@ mod test {
|_, _| panic!("Not receiving a request"),
);
let sender = ReqNot::<TestMsgSet>::new(
logger,
clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move {
receiver.receive(d).await
}))),
@@ -277,8 +301,10 @@ mod test {
#[test]
fn request() {
spin_on(async {
let logger = Logger::new(LogStrategy::StdErr);
let receiver = Rc::new(Mutex::<Option<ReqNot<TestMsgSet>>>::new(None));
let sender = Rc::new(ReqNot::<TestMsgSet>::new(
logger.clone(),
clone!(receiver; move |d, _| clone!(receiver; Box::pin(async move {
receiver.lock().await.as_ref().unwrap().receive(d).await
}))),
@@ -286,6 +312,7 @@ mod test {
|_, _| panic!("Should not receive request"),
));
*receiver.lock().await = Some(ReqNot::new(
logger,
clone!(sender; move |d, _| clone!(sender; Box::pin(async move {
sender.receive(d).await
}))),

View File

@@ -7,8 +7,7 @@ use std::ops::Range;
use std::sync::Arc;
pub use api::PhKind;
use async_std::stream;
use async_std::sync::Mutex;
use async_stream::stream;
use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, StreamExt};
use itertools::Itertools;
@@ -161,12 +160,11 @@ pub async fn ttv_from_api<A: AtomRepr, X: ExtraTok>(
ctx: &mut A::Ctx,
i: &Interner,
) -> Vec<TokTree<'static, A, X>> {
let ctx_lk = Mutex::new(ctx);
stream::from_iter(tokv.into_iter())
.then(|t| async {
let t = t;
TokTree::<A, X>::from_api(t.borrow(), *ctx_lk.lock().await, i).boxed_local().await
})
stream! {
for tok in tokv {
yield TokTree::<A, X>::from_api(tok.borrow(), ctx, i).boxed_local().await
}
}
.collect()
.await
}
@@ -186,11 +184,13 @@ pub async fn ttv_into_api<'a, A: AtomRepr, X: ExtraTok>(
tokv: impl IntoIterator<Item = TokTree<'a, A, X>>,
do_extra: &mut impl FnMut(X, Range<u32>) -> api::TokenTree,
) -> Vec<api::TokenTree> {
let mut new_tokv = Vec::new();
for item in tokv {
new_tokv.push(item.into_api(do_extra).await)
stream! {
for tok in tokv {
yield tok.into_api(do_extra).await
}
new_tokv
}
.collect()
.await
}
/// This takes a position and not a range because it assigns the range to

View File

@@ -148,7 +148,7 @@ pub trait AtomMethod: Request {
const NAME: &str;
}
pub trait Supports<M: AtomMethod>: AtomCard {
fn handle(&self, ctx: SysCtx, req: M) -> LocalBoxFuture<'_, <M as Request>::Response>;
fn handle(&self, ctx: SysCtx, req: M) -> impl Future<Output = <M as Request>::Response>;
}
trait_set! {

View File

@@ -144,8 +144,8 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
pub trait DeserializeCtx: Sized {
fn read<T: Decode>(&mut self) -> impl Future<Output = T>;
fn is_empty(&self) -> bool;
fn assert_empty(self) { assert!(self.is_empty(), "Bytes found after decoding") }
fn decode<T: Decode>(mut self) -> impl Future<Output = T> {
fn assert_empty(&self) { assert!(self.is_empty(), "Bytes found after decoding") }
fn decode<T: Decode>(&mut self) -> impl Future<Output = T> {
async {
let t = self.read().await;
self.assert_empty();

View File

@@ -18,7 +18,7 @@ use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::ApplyMacro;
use orchid_api_traits::{Decode, Encode, enc_vec};
use orchid_base::builtin::ExtPort;
use orchid_base::builtin::{ExtPort, Spawner};
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
use orchid_base::clone;
use orchid_base::interner::{Interner, Tok};
@@ -127,7 +127,7 @@ impl ExtPort for ExtensionOwner {
}
}
async fn extension_main_logic(data: ExtensionData, spawner: Rc<dyn LocalSpawn>) {
pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
let api::HostHeader { log_strategy } =
api::HostHeader::decode(Pin::new(&mut async_std::io::stdin())).await;
let mut buf = Vec::new();
@@ -142,7 +142,7 @@ async fn extension_main_logic(data: ExtensionData, spawner: Rc<dyn LocalSpawn>)
std::io::stdout().write_all(&buf).unwrap();
std::io::stdout().flush().unwrap();
let exiting = Arc::new(AtomicBool::new(false));
let logger = Arc::new(Logger::new(log_strategy));
let logger = Logger::new(log_strategy);
let interner_cell = Rc::new(RefCell::new(None::<Rc<Interner>>));
let interner_weak = Rc::downgrade(&interner_cell);
let obj_store = ObjStore::default();
@@ -159,10 +159,8 @@ async fn extension_main_logic(data: ExtensionData, spawner: Rc<dyn LocalSpawn>)
}.boxed_local())
});
let rn = ReqNot::<api::ExtMsgSet>::new(
clone!(logger; move |a, _| clone!(logger; async move {
logger.log_buf("Upsending", a);
send_parent_msg(a).await.unwrap()
}.boxed_local())),
logger.clone(),
move |a, _| async move { send_parent_msg(a).await.unwrap() }.boxed_local(),
clone!(systems, exiting, mk_ctx, obj_store; move |n, reqnot| {
clone!(systems, exiting, mk_ctx, obj_store; async move {
match n {
@@ -209,7 +207,7 @@ async fn extension_main_logic(data: ExtensionData, spawner: Rc<dyn LocalSpawn>)
.then(|(k, v)| {
let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules);
clone!(i, ctx; async move {
let name = i.i::<String>(&k).await.to_api();
let name = i.i(&k).await.to_api();
let value = v.into_api(&mut TIACtxImpl {
lazy_members: &mut *lazy_mems.lock().await,
rules: &mut *rules.lock().await,
@@ -434,6 +432,6 @@ async fn extension_main_logic(data: ExtensionData, spawner: Rc<dyn LocalSpawn>)
*interner_cell.borrow_mut() = Some(Rc::new(Interner::new_replica(rn.clone().map())));
while !exiting.load(Ordering::Relaxed) {
let rcvd = recv_parent_msg().await.unwrap();
rn.receive(&rcvd).await
spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd).await })))
}
}

View File

@@ -3,7 +3,6 @@ use std::rc::Rc;
use async_once_cell::OnceCell;
use derive_destructure::destructure;
use futures::task::LocalSpawnExt;
use orchid_base::error::OrcErrv;
use orchid_base::location::Pos;
use orchid_base::reqnot::Requester;
@@ -34,10 +33,8 @@ impl fmt::Debug for ExprHandle {
impl Drop for ExprHandle {
fn drop(&mut self) {
let notif = api::Release(self.ctx.id, self.tk);
let SysCtx { reqnot, spawner, logger, .. } = self.ctx.clone();
if let Err(e) = spawner.spawn_local(async move { reqnot.notify(notif).await }) {
writeln!(logger, "Failed to schedule notification about resource release: {e}");
}
let SysCtx { reqnot, spawner, .. } = self.ctx.clone();
spawner(Box::pin(async move { reqnot.notify(notif).await }))
}
}

View File

@@ -84,7 +84,7 @@ impl OwnedAtom for Fun {
self.path.to_api().encode(write).await;
self.args.clone()
}
async fn deserialize(ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
let sys = ctx.sys();
let path = Sym::from_api(ctx.decode().await, &sys.i).await;
let (arity, fun) = FUNS.with(|f| f.clone()).lock().await.get(&path).unwrap().clone();
@@ -128,6 +128,8 @@ impl OwnedAtom for Lambda {
}
mod expr_func_derives {
use std::future::Future;
use orchid_base::error::OrcRes;
use super::ExprFunc;
@@ -140,14 +142,14 @@ mod expr_func_derives {
paste::paste!{
impl<
$($t: TryFromExpr, )*
Out: ToExpr,
Func: Fn($($t,)*) -> Out + Clone + Send + Sync + 'static
> ExprFunc<($($t,)*), Out> for Func {
Fut: Future<Output: ToExpr>,
Func: Fn($($t,)*) -> Fut + Clone + Send + Sync + 'static
> ExprFunc<($($t,)*), Fut::Output> for Func {
const ARITY: u8 = $arity;
async fn apply(&self, v: Vec<Expr>) -> OrcRes<GExpr> {
assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch");
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).to_expr())
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_expr())
}
}
}

View File

@@ -1,3 +1,5 @@
use std::future::Future;
use futures::FutureExt;
use orchid_base::error::{OrcErr, OrcErrv};
use orchid_base::location::Pos;
@@ -98,9 +100,9 @@ pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}
pub fn with<I: TryFromExpr, O: ToExpr>(
pub fn with<I: TryFromExpr, Fut: Future<Output: ToExpr>>(
expr: GExpr,
cont: impl Fn(I) -> O + Clone + Send + Sync + 'static,
cont: impl Fn(I) -> Fut + Clone + Send + Sync + 'static,
) -> GExpr {
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
}

View File

@@ -1,9 +1,15 @@
use std::pin::pin;
use async_std::io;
use async_once_cell::OnceCell;
use async_std::io::{self, Stdout};
use async_std::sync::Mutex;
use orchid_base::msg::{recv_msg, send_msg};
static STDOUT: OnceCell<Mutex<Stdout>> = OnceCell::new();
pub async fn send_parent_msg(msg: &[u8]) -> io::Result<()> {
send_msg(pin!(io::stdout()), msg).await
let stdout_lk = STDOUT.get_or_init(async { Mutex::new(io::stdout()) }).await;
let mut stdout_g = stdout_lk.lock().await;
send_msg(pin!(&mut *stdout_g), msg).await
}
pub async fn recv_parent_msg() -> io::Result<Vec<u8>> { recv_msg(pin!(io::stdin())).await }

View File

@@ -4,13 +4,12 @@ use std::future::Future;
use std::num::NonZero;
use std::pin::Pin;
use std::rc::Rc;
use std::sync::Arc;
use futures::future::LocalBoxFuture;
use futures::task::LocalSpawn;
use hashbrown::HashMap;
use orchid_api_traits::{Coding, Decode};
use orchid_base::boxed_iter::BoxedIter;
use orchid_base::builtin::Spawner;
use orchid_base::interner::Interner;
use orchid_base::logging::Logger;
use orchid_base::reqnot::{Receipt, ReqNot};
@@ -133,10 +132,10 @@ where A: AtomicFeatures {
#[derive(Clone)]
pub struct SysCtx {
pub reqnot: ReqNot<api::ExtMsgSet>,
pub spawner: Rc<dyn LocalSpawn>,
pub spawner: Spawner,
pub id: api::SysId,
pub cted: CtedObj,
pub logger: Arc<Logger>,
pub logger: Logger,
pub obj_store: ObjStore,
pub i: Rc<Interner>,
}

View File

@@ -71,16 +71,16 @@ impl GenItem {
pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenItem> {
with_export(GenMember { name: name.to_string(), kind: MemKind::Const(value.to_expr()) }, public)
}
pub async fn module(
pub fn module(
public: bool,
name: &str,
imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = Vec<GenItem>>,
) -> Vec<GenItem> {
let (name, kind) = root_mod(name, imports, items).await;
let (name, kind) = root_mod(name, imports, items);
with_export(GenMember { name, kind }, public)
}
pub async fn root_mod(
pub fn root_mod(
name: &str,
imports: impl IntoIterator<Item = Sym>,
items: impl IntoIterator<Item = Vec<GenItem>>,
@@ -91,7 +91,7 @@ pub async fn root_mod(
};
(name.to_string(), kind)
}
pub async fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
let fac =
LazyMemberFactory::new(move |sym| async { MemKind::Const(Fun::new(sym, xf).await.to_expr()) });
with_export(GenMember { name: name.to_string(), kind: MemKind::Lazy(fac) }, exported)

View File

@@ -86,7 +86,7 @@ impl AtomHand {
impl AtomRepr for AtomHand {
type Ctx = Ctx;
async fn from_api(atom: &orchid_api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self {
Self::new(atom.clone(), &ctx).await
Self::new(atom.clone(), ctx).await
}
async fn to_api(&self) -> orchid_api::Atom { self.api_ref() }
async fn print(&self) -> String { self.to_string().await }

View File

@@ -1,12 +1,12 @@
use std::cell::RefCell;
use std::num::NonZeroU16;
use std::num::{NonZero, NonZeroU16};
use std::rc::Rc;
use std::{fmt, ops};
use async_std::sync::RwLock;
use futures::task::LocalSpawn;
use hashbrown::HashMap;
use orchid_api::SysId;
use orchid_base::builtin::Spawner;
use orchid_base::interner::Interner;
use crate::api;
@@ -15,7 +15,7 @@ use crate::system::{System, WeakSystem};
pub struct CtxData {
pub i: Rc<Interner>,
pub spawn: Rc<dyn LocalSpawn>,
pub spawn: Spawner,
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>,
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
@@ -24,9 +24,18 @@ pub struct CtxData {
pub struct Ctx(Rc<CtxData>);
impl ops::Deref for Ctx {
type Target = CtxData;
fn deref(&self) -> &Self::Target { &*self.0 }
fn deref(&self) -> &Self::Target { &self.0 }
}
impl Ctx {
pub fn new(spawn: Spawner) -> Self {
Self(Rc::new(CtxData {
spawn,
i: Rc::default(),
systems: RwLock::default(),
system_id: RefCell::new(NonZero::new(1).unwrap()),
owned_atoms: RwLock::default(),
}))
}
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
self.systems.read().await.get(&id).and_then(WeakSystem::upgrade)
}

View File

@@ -1,7 +1,6 @@
use std::collections::VecDeque;
use std::num::NonZeroU64;
use std::rc::{Rc, Weak};
use std::sync::atomic::AtomicBool;
use async_std::sync::RwLock;
use futures::FutureExt;
@@ -19,7 +18,6 @@ pub type ExprParseCtx = Extension;
#[derive(Debug)]
pub struct ExprData {
is_canonical: AtomicBool,
pos: Pos,
kind: RwLock<ExprKind>,
}
@@ -51,7 +49,7 @@ impl Expr {
}
let pos = Pos::from_api(&api.location, &ctx.ctx().i).await;
let kind = RwLock::new(ExprKind::from_api(&api.kind, pos.clone(), ctx).boxed_local().await);
Self(Rc::new(ExprData { is_canonical: AtomicBool::new(false), pos, kind }))
Self(Rc::new(ExprData { pos, kind }))
}
pub async fn to_api(&self) -> api::InspectedKind {
use api::InspectedKind as K;

View File

@@ -9,7 +9,6 @@ use async_std::sync::Mutex;
use derive_destructure::destructure;
use futures::FutureExt;
use futures::future::{join, join_all};
use futures::task::LocalSpawnExt;
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::HostMsgSet;
@@ -18,7 +17,6 @@ use orchid_base::builtin::ExtInit;
use orchid_base::clone;
use orchid_base::interner::Tok;
use orchid_base::logging::Logger;
use orchid_base::macros::mtreev_from_api;
use orchid_base::reqnot::{ReqNot, Requester as _};
use orchid_base::tree::AtomRepr;
@@ -26,7 +24,6 @@ use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::expr_store::ExprStore;
use crate::macros::{macro_recur, macro_treev_to_api};
use crate::system::SystemCtor;
pub struct ReqPair<R: Request>(R, Sender<R::Response>);
@@ -45,9 +42,13 @@ pub struct ExtensionData {
next_pars: RefCell<NonZeroU64>,
exprs: ExprStore,
lex_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::SubLex>>>>,
mac_recur: Mutex<HashMap<api::ParsId, channel::Sender<ReqPair<api::RunMacros>>>>,
}
impl Drop for ExtensionData {
fn drop(&mut self) { self.reqnot.notify(api::HostExtNotif::Exit); }
fn drop(&mut self) {
let reqnot = self.reqnot.clone();
(self.ctx.spawn)(Box::pin(async move { reqnot.notify(api::HostExtNotif::Exit).await }))
}
}
#[derive(Clone)]
@@ -60,17 +61,19 @@ impl Extension {
systems: (init.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
.collect(),
logger,
logger: logger.clone(),
init,
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(),
mac_recur: Mutex::default(),
reqnot: ReqNot::new(
logger,
clone!(weak; move |sfn, _| clone!(weak; async move {
let data = weak.upgrade().unwrap();
data.logger.log_buf("Downsending", sfn);
data.init.send(sfn).await
}.boxed_local())),
clone!(weak; move |notif, _| clone!(weak; async move {
clone!(weak; move |notif, _| {
clone!(weak; Box::pin(async move {
let this = Extension(weak.upgrade().unwrap());
match notif {
api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => {
@@ -90,12 +93,12 @@ impl Extension {
},
api::ExtHostNotif::Log(api::Log(str)) => this.logger().log(str),
}
}.boxed_local())),
}))}),
{
clone!(weak, ctx);
move |hand, req| {
clone!(weak, ctx);
async move {
Box::pin(async move {
let this = Self(weak.upgrade().unwrap());
let i = this.ctx().i.clone();
match req {
@@ -124,10 +127,12 @@ impl Extension {
hand.handle(fw, &sys.request(body.clone()).await).await
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, rep_out) = channel::bounded(0);
let (rep_in, rep_out) = channel::bounded(1);
{
let lex_g = this.0.lex_recur.lock().await;
let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap();
}
hand.handle(&sl, &rep_out.recv().await.unwrap()).await
},
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => {
@@ -140,19 +145,17 @@ impl Extension {
})
.await
},
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros { ref run_id, ref query }) => {
let mtreev =
mtreev_from_api(query, &i, &mut |_| panic!("Atom in macro recur")).await;
match macro_recur(*run_id, mtreev).await {
Some(x) => hand.handle(rm, &Some(macro_treev_to_api(*run_id, x).await)).await,
None => hand.handle(rm, &None).await,
}
api::ExtHostReq::RunMacros(rm) => {
let (rep_in, rep_out) = channel::bounded(1);
let lex_g = this.0.mac_recur.lock().await;
let req_in = lex_g.get(&rm.run_id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(rm.clone(), rep_in)).await.unwrap();
hand.handle(&rm, &rep_out.recv().await.unwrap()).await
},
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) =>
hand.handle(eap, &AtomHand::new(atom.clone(), &ctx).await.print().await).await,
}
}
.boxed_local()
})
}
},
),
@@ -185,7 +188,7 @@ impl Extension {
// get unique lex ID
let id = api::ParsId(self.next_pars());
// create and register channel
let (req_in, req_out) = channel::bounded(0);
let (req_in, req_out) = channel::bounded(1);
self.0.lex_recur.lock().await.insert(id, req_in); // lex_recur released
let (ret, ()) = join(
async {
@@ -207,19 +210,15 @@ impl Extension {
}
pub async fn recv_one(&self) {
let reqnot = self.0.reqnot.clone();
self
.0
.init
.recv(Box::new(move |msg| async move { reqnot.receive(msg).await }.boxed_local()))
(self.0.init.recv(Box::new(move |msg| async move { reqnot.receive(msg).await }.boxed_local())))
.await;
}
pub fn system_drop(&self, id: api::SysId) {
let rc = self.clone();
(self.ctx().spawn.spawn_local(async move {
(self.ctx().spawn)(Box::pin(async move {
rc.reqnot().notify(api::SystemDrop(id)).await;
rc.ctx().systems.write().await.remove(&id);
}))
.expect("Failed to drop system!");
}
pub fn downgrade(&self) -> WeakExtension { WeakExtension(Rc::downgrade(&self.0)) }
}

View File

@@ -34,7 +34,7 @@ impl<'a> LexCtx<'a> {
tail: &self.source[pos as usize..],
systems: self.systems,
sub_trees: &mut *self.sub_trees,
ctx: &self.ctx,
ctx: self.ctx,
}
}
pub fn get_pos(&self) -> u32 { self.end_pos() - self.tail.len() as u32 }
@@ -76,7 +76,7 @@ impl<'a> LexCtx<'a> {
}
}
pub async fn lex_once<'a>(ctx: &mut LexCtx<'a>) -> OrcRes<ParsTokTree> {
pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
let start = ctx.get_pos();
assert!(
!ctx.tail.is_empty() && !ctx.tail.starts_with(unrep_space),
@@ -140,7 +140,7 @@ pub async fn lex_once<'a>(ctx: &mut LexCtx<'a>) -> OrcRes<ParsTokTree> {
let numstr = ctx.get_start_matches(|x| x != ')').trim();
match parse_num(numstr) {
Ok(num) => ParsTok::Macro(Some(num.to_f64())),
Err(e) => return Err(num_to_err(e, pos, &*ctx.ctx.i).await.into()),
Err(e) => return Err(num_to_err(e, pos, &ctx.ctx.i).await.into()),
}
} else {
ParsTok::Macro(None)
@@ -154,8 +154,9 @@ pub async fn lex_once<'a>(ctx: &mut LexCtx<'a>) -> OrcRes<ParsTokTree> {
let errors_lck = &Mutex::new(&mut errors);
let lx = sys
.lex(source, pos, |pos| async move {
match lex_once(&mut ctx_lck.lock().await.push(pos)).boxed_local().await {
Ok(t) => Some(api::SubLexed { pos, ticket: ctx_lck.lock().await.add_subtree(t) }),
let mut ctx_g = ctx_lck.lock().await;
match lex_once(&mut ctx_g.push(pos)).boxed_local().await {
Ok(t) => Some(api::SubLexed { pos, ticket: ctx_g.add_subtree(t) }),
Err(e) => {
errors_lck.lock().await.push(e);
None
@@ -166,9 +167,12 @@ pub async fn lex_once<'a>(ctx: &mut LexCtx<'a>) -> OrcRes<ParsTokTree> {
match lx {
Err(e) =>
return Err(
errors.into_iter().fold(OrcErrv::from_api(&e, &*ctx.ctx.i).await, |a, b| a + b),
errors.into_iter().fold(OrcErrv::from_api(&e, &ctx.ctx.i).await, |a, b| a + b),
),
Ok(Some(lexed)) => return Ok(tt_to_owned(&lexed.expr, &mut ctx.push(lexed.pos)).await),
Ok(Some(lexed)) => {
ctx.set_pos(lexed.pos);
return Ok(tt_to_owned(&lexed.expr, ctx).await);
},
Ok(None) => match errors.into_iter().reduce(|a, b| a + b) {
Some(errors) => return Err(errors),
None => continue,
@@ -196,13 +200,13 @@ async fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree
Atom(atom =>
AtomHand::from_api(atom, Pos::Range(api.range.clone()), &mut ctx.ctx.clone()).await
),
Bottom(err => OrcErrv::from_api(err, &*ctx.ctx.i).await),
Bottom(err => OrcErrv::from_api(err, &ctx.ctx.i).await),
LambdaHead(arg => ttv_to_owned(arg, ctx).boxed_local().await),
Name(name => Tok::from_api(*name, &*ctx.ctx.i).await),
Name(name => Tok::from_api(*name, &ctx.ctx.i).await),
S(p.clone(), b => ttv_to_owned(b, ctx).boxed_local().await),
BR, NS,
Comment(c.clone()),
Ph(ph => Ph::from_api(ph, &*ctx.ctx.i).await),
Ph(ph => Ph::from_api(ph, &ctx.ctx.i).await),
Macro(*prio),
} {
api::Token::Slot(id) => return ctx.rm_subtree(*id),
@@ -216,7 +220,7 @@ async fn ttv_to_owned<'a>(
) -> Vec<ParsTokTree> {
let mut out = Vec::new();
for tt in api {
out.push(tt_to_owned(&tt, ctx).await)
out.push(tt_to_owned(tt, ctx).await)
}
out
}

View File

@@ -1,17 +1,16 @@
use std::rc::Rc;
use async_std::sync::RwLock;
use futures::FutureExt;
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use orchid_base::interner::Interner;
use orchid_base::clone;
use orchid_base::macros::{MTok, MTree, mtreev_from_api, mtreev_to_api};
use orchid_base::name::Sym;
use trait_set::trait_set;
use crate::api;
use crate::atom::AtomHand;
use crate::rule::matcher::{NamedMatcher, PriodMatcher};
use crate::ctx::Ctx;
use crate::rule::state::MatchState;
use crate::tree::Code;
@@ -22,36 +21,27 @@ trait_set! {
trait MacroCB = Fn(Vec<MacTree>) -> Option<Vec<MacTree>>;
}
thread_local! {
static RECURSION: RwLock<HashMap<api::ParsId, Box<dyn MacroCB>>> = RwLock::default();
static MACRO_SLOTS: RwLock<HashMap<api::ParsId, HashMap<api::MacroTreeId, Rc<MacTok>>>> =
RwLock::default();
}
type Slots = HashMap<api::MacroTreeId, Rc<MacTok>>;
pub async fn macro_recur(run_id: api::ParsId, input: Vec<MacTree>) -> Option<Vec<MacTree>> {
(RECURSION.read().unwrap()[&run_id])(input)
}
pub async fn macro_treev_to_api(run_id: api::ParsId, mtree: Vec<MacTree>) -> Vec<api::MacroTree> {
let mut g = MACRO_SLOTS.write().unwrap();
let run_cache = g.get_mut(&run_id).expect("Parser run not found");
pub async fn macro_treev_to_api(mtree: Vec<MacTree>, slots: &mut Slots) -> Vec<api::MacroTree> {
mtreev_to_api(&mtree, &mut |a: &AtomHand| {
let id = api::MacroTreeId((run_cache.len() as u64 + 1).try_into().unwrap());
run_cache.insert(id, Rc::new(MacTok::Atom(a.clone())));
api::MacroToken::Slot(id)
})
}
pub async fn macro_treev_from_api(api: Vec<api::MacroTree>, i: &Interner) -> Vec<MacTree> {
mtreev_from_api(&api, i, &mut |atom| {
async { MacTok::Atom(AtomHand::from_api(atom.clone())) }.boxed_local()
let id = api::MacroTreeId((slots.len() as u64 + 1).try_into().unwrap());
slots.insert(id, Rc::new(MacTok::Atom(a.clone())));
async move { api::MacroToken::Slot(id) }.boxed_local()
})
.await
}
pub fn deslot_macro(run_id: api::ParsId, tree: &[MacTree]) -> Option<Vec<MacTree>> {
let mut slots = (MACRO_SLOTS.write().unwrap()).remove(&run_id).expect("Run not found");
return work(&mut slots, tree);
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| {
clone!(ctx);
Box::pin(async move { MacTok::Atom(AtomHand::new(atom.clone(), &ctx).await) })
})
.await
}
pub fn deslot_macro(tree: &[MacTree], slots: &mut Slots) -> Option<Vec<MacTree>> {
return work(slots, tree);
fn work(
slots: &mut HashMap<api::MacroTreeId, Rc<MacTok>>,
tree: &[MacTree],
@@ -90,74 +80,6 @@ pub struct Macro<Matcher> {
cases: Vec<(Matcher, Code)>,
}
pub struct MacroRepo {
named: HashMap<Sym, Vec<Macro<NamedMatcher>>>,
prio: Vec<Macro<PriodMatcher>>,
}
impl MacroRepo {
/// TODO: the recursion inside this function needs to be moved into Orchid.
/// See the markdown note
pub fn process_exprv(&self, target: &[MacTree], i: &Interner) -> Option<Vec<MacTree>> {
let mut workcp = target.to_vec();
let mut lexicon;
'try_named: loop {
lexicon = HashSet::new();
target.iter().for_each(|tgt| fill_lexicon(tgt, &mut lexicon));
for (idx, tree) in workcp.iter().enumerate() {
let MacTok::Name(name) = &*tree.tok else { continue };
let matches = (self.named.get(name).into_iter().flatten())
.filter(|m| m.deps.is_subset(&lexicon))
.filter_map(|mac| {
(mac.cases.iter())
.find_map(|cas| cas.0.apply(&workcp[idx..], i, |_| false).map(|s| (cas, s)))
})
.collect_vec();
assert!(
matches.len() < 2,
"Multiple conflicting matches on {:?}: {:?}",
&workcp[idx..],
matches
);
let Some((case, (state, tail))) = matches.into_iter().next() else { continue };
let inj = (run_body(&case.1, state).into_iter())
.map(|MacTree { pos, tok }| MacTree { pos, tok: Rc::new(MacTok::Done(tok)) });
workcp.splice(idx..(workcp.len() - tail.len()), inj);
continue 'try_named;
}
break;
}
if let Some(((_, body), state)) = (self.prio.iter())
.filter(|mac| mac.deps.is_subset(&lexicon))
.flat_map(|mac| &mac.cases)
.find_map(|case| case.0.apply(&workcp, |_| false).map(|state| (case, state)))
{
return Some(run_body(body, state));
}
let results = (workcp.into_iter())
.map(|mt| match &*mt.tok {
MTok::S(p, body) => self.process_exprv(body, i).map(|body| MTok::S(*p, body).at(mt.pos)),
MTok::Lambda(arg, body) =>
match (self.process_exprv(arg, i), self.process_exprv(body, i)) {
(Some(arg), Some(body)) => Some(MTok::Lambda(arg, body).at(mt.pos)),
(Some(arg), None) => Some(MTok::Lambda(arg, body.to_vec()).at(mt.pos)),
(None, Some(body)) => Some(MTok::Lambda(arg.to_vec(), body).at(mt.pos)),
(None, None) => None,
},
_ => None,
})
.collect_vec();
results.iter().any(Option::is_some).then(|| {
(results.into_iter().zip(target))
.map(|(opt, fb)| opt.unwrap_or_else(|| fb.clone()))
.collect_vec()
})
}
}
fn fill_lexicon(tgt: &MacTree, lexicon: &mut HashSet<Sym>) {
match &*tgt.tok {
MTok::Name(n) => {

View File

@@ -4,7 +4,7 @@ use futures::FutureExt;
use futures::future::join_all;
use itertools::Itertools;
use never::Never;
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_err, mk_errv};
use orchid_base::error::{OrcErrv, OrcRes, Reporter, ReporterImpl, mk_err, mk_errv};
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
use orchid_base::macros::{MTok, MTree};
@@ -25,9 +25,19 @@ use crate::tree::{
type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>;
pub trait ParseCtx: Send + Sync {
pub struct ParseCtxImpl<'a> {
pub systems: &'a [System],
pub reporter: &'a ReporterImpl,
}
impl ParseCtx for ParseCtxImpl<'_> {
fn reporter(&self) -> &(impl Reporter + ?Sized) { self.reporter }
fn systems(&self) -> impl Iterator<Item = &System> { self.systems.iter() }
}
pub trait ParseCtx {
fn systems(&self) -> impl Iterator<Item = &System>;
fn reporter(&self) -> &impl Reporter;
fn reporter(&self) -> &(impl Reporter + ?Sized);
}
pub async fn parse_items(
@@ -239,8 +249,8 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
Token::LambdaHead(arg) => (
ttree.range.start..snip.pos().end,
MTok::Lambda(
parse_mtree(Snippet::new(ttree, arg, snip.interner())).await?,
parse_mtree(tail).await?,
parse_mtree(Snippet::new(ttree, arg, snip.interner())).boxed_local().await?,
parse_mtree(tail).boxed_local().await?,
),
Snippet::new(ttree, &[], snip.interner()),
),
@@ -272,7 +282,7 @@ pub async fn parse_macro(
let mut errors = Vec::new();
let mut rules = Vec::new();
for (i, item) in
line_items(Snippet::new(&prev, block, tail.interner())).await.into_iter().enumerate()
line_items(Snippet::new(prev, block, tail.interner())).await.into_iter().enumerate()
{
let Parsed { tail, output } = try_pop_no_fluff(item.tail).await?;
if !output.is_kw(tail.i("rule").await) {

View File

@@ -20,7 +20,7 @@ pub fn scal_match<'a>(
(ScalMatcher::Placeh { key }, _) =>
Some(MatchState::from_ph(key.clone(), StateEntry::Scalar(expr))),
(ScalMatcher::S(c1, b_mat), MacTok::S(c2, body)) if c1 == c2 =>
any_match(&b_mat, &body[..], save_loc),
any_match(b_mat, &body[..], save_loc),
(ScalMatcher::Lambda(arg_mat, b_mat), MacTok::Lambda(arg, body)) =>
Some(any_match(arg_mat, arg, save_loc)?.combine(any_match(b_mat, body, save_loc)?)),
_ => None,

View File

@@ -1,7 +1,6 @@
use std::cell::RefCell;
use std::path::PathBuf;
use std::pin::Pin;
use std::rc::Rc;
use std::thread;
use async_process::{self, Child, ChildStdin, ChildStdout};
@@ -9,7 +8,6 @@ use async_std::io::{self, BufReadExt, BufReader};
use async_std::sync::Mutex;
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use futures::task::LocalSpawnExt;
use orchid_api_traits::{Decode, Encode};
use orchid_base::builtin::{ExtInit, ExtPort};
use orchid_base::logging::Logger;
@@ -31,7 +29,7 @@ pub async fn ext_command(
.stderr(async_process::Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
api::HostHeader { log_strategy: logger.strat() }.encode(Pin::new(&mut stdin));
api::HostHeader { log_strategy: logger.strat() }.encode(Pin::new(&mut stdin)).await;
let mut stdout = child.stdout.take().unwrap();
let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await;
let child_stderr = child.stderr.take().unwrap();
@@ -50,7 +48,7 @@ pub async fn ext_command(
Ok(ExtInit {
header,
port: Box::new(Subprocess {
child: Rc::new(RefCell::new(child)),
child: RefCell::new(Some(child)),
stdin: Mutex::new(Box::pin(stdin)),
stdout: Mutex::new(Box::pin(stdout)),
ctx,
@@ -59,19 +57,18 @@ pub async fn ext_command(
}
pub struct Subprocess {
child: Rc<RefCell<Child>>,
child: RefCell<Option<Child>>,
stdin: Mutex<Pin<Box<ChildStdin>>>,
stdout: Mutex<Pin<Box<ChildStdout>>>,
ctx: Ctx,
}
impl Drop for Subprocess {
fn drop(&mut self) {
let child = self.child.clone();
(self.ctx.spawn.spawn_local(async move {
let status = child.borrow_mut().status().await.expect("Extension exited with error");
let mut child = self.child.borrow_mut().take().unwrap();
(self.ctx.spawn)(Box::pin(async move {
let status = child.status().await.expect("Extension exited with error");
assert!(status.success(), "Extension exited with error {status}");
}))
.expect("Could not spawn process terminating future")
}
}
impl ExtPort for Subprocess {

View File

@@ -7,7 +7,6 @@ use async_stream::stream;
use derive_destructure::destructure;
use futures::StreamExt;
use futures::future::join_all;
use futures::task::LocalSpawnExt;
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_base::async_once_cell::OnceCell;
@@ -57,7 +56,7 @@ impl System {
pub fn id(&self) -> api::SysId { self.0.id }
pub fn ext(&self) -> &Extension { &self.0.ext }
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.ext.reqnot() }
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { self.0.ext.reqnot() }
pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
self.reqnot().request(api::GetMember(self.0.id, id)).await
}
@@ -94,10 +93,9 @@ impl System {
}
pub(crate) fn drop_atom(&self, drop: api::AtomId) {
let this = self.0.clone();
(self.0.ctx.spawn.spawn_local(async move {
(self.0.ctx.spawn)(Box::pin(async move {
this.ctx.owned_atoms.write().await.remove(&drop);
}))
.expect("Failed to drop atom");
}
pub async fn print(&self) -> String {
let ctor = (self.0.ext.system_ctors().find(|c| c.id() == self.0.decl_id))

View File

@@ -4,6 +4,9 @@ version = "0.1.0"
edition = "2021"
[dependencies]
async-once-cell = "0.5.4"
async-std = "1.13.0"
futures = "0.3.31"
itertools = "0.14.0"
never = "0.1.0"
once_cell = "1.20.2"
@@ -13,3 +16,4 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" }
orchid-extension = { version = "0.1.0", path = "../orchid-extension" }
ordered-float = "4.6.0"
tokio = { version = "1.43.0", features = ["full"] }

View File

@@ -1,4 +1,16 @@
use orchid_extension::entrypoint::ExtensionData;
use orchid_std::StdSystem;
use std::mem;
use std::rc::Rc;
pub fn main() { ExtensionData::new("orchid-std::main", &[&StdSystem]).main() }
use orchid_extension::entrypoint::{ExtensionData, extension_main_logic};
use orchid_std::StdSystem;
use tokio::task::{LocalSet, spawn_local};
#[tokio::main(flavor = "current_thread")]
pub async fn main() {
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

@@ -1,6 +1,8 @@
use orchid_api_derive::Coding;
use orchid_base::error::OrcRes;
use orchid_extension::atom::{AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom};
use orchid_extension::atom::{
AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom,
};
use orchid_extension::atom_thin::{ThinAtom, ThinVariant};
use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::Expr;
@@ -15,8 +17,8 @@ impl Atomic for Int {
}
impl ThinAtom for Int {}
impl TryFromExpr for Int {
fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Int>::try_from_expr(expr).map(|t| t.value)
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Int>::try_from_expr(expr).await.map(|t| t.value)
}
}
@@ -29,8 +31,8 @@ impl Atomic for Float {
}
impl ThinAtom for Float {}
impl TryFromExpr for Float {
fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Float>::try_from_expr(expr).map(|t| t.value)
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Float>::try_from_expr(expr).await.map(|t| t.value)
}
}
@@ -39,10 +41,11 @@ pub enum Numeric {
Float(NotNan<f64>),
}
impl TryFromExpr for Numeric {
fn try_from_expr(expr: Expr) -> OrcRes<Self> {
Int::try_from_expr(expr.clone())
.map(|t| Numeric::Int(t.0))
.or_else(|e| Float::try_from_expr(expr).map(|t| Numeric::Float(t.0)).map_err(|e2| e + e2))
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match Int::try_from_expr(expr.clone()).await {
Ok(t) => Ok(Numeric::Int(t.0)),
Err(e) => Float::try_from_expr(expr).await.map(|t| Numeric::Float(t.0)).map_err(|e2| e + e2),
}
}
}
impl ToAtom for Numeric {

View File

@@ -13,14 +13,14 @@ use super::num_atom::{Float, Int};
pub struct NumLexer;
impl Lexer for NumLexer {
const CHAR_FILTER: &'static [RangeInclusive<char>] = &['0'..='9'];
fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c));
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
let fac = match parse_num(chars) {
Ok(Numeric::Float(f)) => Float(f).factory(),
Ok(Numeric::Uint(uint)) => Int(uint.try_into().unwrap()).factory(),
Ok(Numeric::Decimal(dec)) => Float(NotNan::new(dec.try_into().unwrap()).unwrap()).factory(),
Err(e) => return Err(num_to_err(e, ctx.pos(all)).into()),
Err(e) => return Err(num_to_err(e, ctx.pos(all), ctx.i).await.into()),
};
Ok((tail, GenTok::X(fac).at(ctx.pos(all)..ctx.pos(tail))))
}

View File

@@ -1,7 +1,7 @@
use std::sync::Arc;
use std::rc::Rc;
use never::Never;
use orchid_base::interner::Tok;
use orchid_base::reqnot::Receipt;
use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
use orchid_extension::entrypoint::ExtReq;
use orchid_extension::fs::DeclFs;
@@ -31,15 +31,15 @@ impl SystemCard for StdSystem {
}
}
impl System for StdSystem {
fn request(_: ExtReq, req: Self::Req) -> orchid_base::reqnot::Receipt { match req {} }
async fn request(_: ExtReq<'_>, req: Self::Req) -> Receipt<'_> { match req {} }
fn lexers() -> Vec<orchid_extension::lexer::LexerObj> { vec![&StringLexer] }
fn parsers() -> Vec<orchid_extension::parser::ParserObj> { vec![] }
fn vfs() -> DeclFs { DeclFs::Mod(&[]) }
fn env() -> Vec<(Tok<String>, MemKind)> {
fn env() -> Vec<(String, MemKind)> {
vec![root_mod("std", [], [module(true, "string", [], [comments(
["Concatenate two strings"],
fun(true, "concat", |left: OrcString, right: OrcString| {
StrAtom::new(Arc::new(left.get_string().to_string() + &right.get_string()))
fun(true, "concat", |left: OrcString<'static>, right: OrcString<'static>| async move {
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
}),
)])])]
}

View File

@@ -1,43 +1,42 @@
use std::borrow::Cow;
use std::io;
use std::ops::Deref;
use std::sync::Arc;
use std::pin::Pin;
use std::rc::Rc;
use async_std::io::Write;
use orchid_api_derive::Coding;
use orchid_api_traits::{Encode, Request};
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::intern;
use orchid_base::interner::{Tok, intern};
use orchid_base::interner::Tok;
use orchid_extension::atom::{AtomMethod, Atomic, MethodSetBuilder, Supports, TypAtom};
use orchid_extension::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::Expr;
use orchid_extension::system::SysCtx;
#[derive(Copy, Clone, Coding)]
#[derive(Copy, Clone, Debug, Coding)]
pub struct StringGetVal;
impl Request for StringGetVal {
type Response = Arc<String>;
type Response = Rc<String>;
}
impl AtomMethod for StringGetVal {
const NAME: &str = "std::string_get_val";
}
impl Supports<StringGetVal> for StrAtom {
fn handle(&self, _: SysCtx, _: StringGetVal) -> <StringGetVal as Request>::Response {
async fn handle(&self, _: SysCtx, _: StringGetVal) -> <StringGetVal as Request>::Response {
self.0.clone()
}
}
#[derive(Clone)]
pub struct StrAtom(Arc<String>);
pub struct StrAtom(Rc<String>);
impl Atomic for StrAtom {
type Variant = OwnedVariant;
type Data = ();
fn reg_reqs() -> MethodSetBuilder<Self> { MethodSetBuilder::new().handle::<StringGetVal>() }
}
impl StrAtom {
pub fn new(str: Arc<String>) -> Self { Self(str) }
pub fn value(&self) -> Arc<String> { self.0.clone() }
pub fn new(str: Rc<String>) -> Self { Self(str) }
}
impl Deref for StrAtom {
type Target = str;
@@ -45,12 +44,12 @@ impl Deref for StrAtom {
}
impl OwnedAtom for StrAtom {
type Refs = ();
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
fn serialize(&self, _: SysCtx, sink: &mut (impl io::Write + ?Sized)) -> Self::Refs {
self.deref().encode(sink)
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn serialize(&self, _: SysCtx, sink: Pin<&mut (impl Write + ?Sized)>) -> Self::Refs {
self.deref().encode(sink).await
}
fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
Self::new(Arc::new(ctx.read::<String>()))
async fn deserialize(mut ctx: impl DeserializeCtx, _: Self::Refs) -> Self {
Self::new(Rc::new(ctx.read::<String>().await))
}
}
@@ -66,34 +65,46 @@ impl From<Tok<String>> for IntStrAtom {
}
impl OwnedAtom for IntStrAtom {
type Refs = ();
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
fn print(&self, _ctx: SysCtx) -> String { format!("{:?}i", *self.0) }
fn serialize(&self, _: SysCtx, write: &mut (impl io::Write + ?Sized)) { self.0.encode(write) }
fn deserialize(ctx: impl DeserializeCtx, _: ()) -> Self { Self(intern(&ctx.decode::<String>())) }
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(self.0.to_api()) }
async fn print(&self, _ctx: SysCtx) -> String { format!("{:?}i", *self.0) }
async fn serialize(&self, _: SysCtx, write: Pin<&mut (impl Write + ?Sized)>) {
self.0.encode(write).await
}
async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self {
let s = ctx.decode::<String>().await;
Self(ctx.sys().i.i(&s).await)
}
}
#[derive(Clone)]
pub enum OrcString<'a> {
pub struct OrcString<'a> {
kind: OrcStringKind<'a>,
ctx: SysCtx,
}
#[derive(Clone)]
pub enum OrcStringKind<'a> {
Val(TypAtom<'a, StrAtom>),
Int(TypAtom<'a, IntStrAtom>),
}
impl OrcString<'_> {
pub fn get_string(&self) -> Arc<String> {
match &self {
Self::Int(tok) => Tok::from_api(tok.value).arc(),
Self::Val(atom) => atom.request(StringGetVal),
pub async fn get_string(&self) -> Rc<String> {
match &self.kind {
OrcStringKind::Int(tok) => self.ctx.i.ex(**tok).await.rc(),
OrcStringKind::Val(atom) => atom.request(StringGetVal).await,
}
}
}
impl TryFromExpr for OrcString<'static> {
fn try_from_expr(expr: Expr) -> OrcRes<OrcString<'static>> {
if let Ok(v) = TypAtom::<StrAtom>::try_from_expr(expr.clone()) {
return Ok(OrcString::Val(v));
async fn try_from_expr(expr: Expr) -> OrcRes<OrcString<'static>> {
if let Ok(v) = TypAtom::<StrAtom>::try_from_expr(expr.clone()).await {
return Ok(OrcString { ctx: expr.ctx(), kind: OrcStringKind::Val(v) });
}
match TypAtom::<IntStrAtom>::try_from_expr(expr) {
Ok(t) => Ok(OrcString::Int(t)),
Err(e) => Err(mk_errv(intern!(str: "A string was expected"), "", e.pos_iter())),
let ctx = expr.ctx();
match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }),
Err(e) => Err(mk_errv(ctx.i.i("A string was expected").await, "", e.pos_iter())),
}
}
}

View File

@@ -1,9 +1,9 @@
use itertools::Itertools;
use orchid_base::error::{OrcErr, OrcRes, mk_err, mk_errv};
use orchid_base::interner::intern;
use orchid_base::interner::Interner;
use orchid_base::location::Pos;
use orchid_base::tree::{vname_tv, wrap_tokv};
use orchid_base::{intern, vname};
use orchid_base::vname;
use orchid_extension::atom::AtomicFeatures;
use orchid_extension::lexer::{LexContext, Lexer, err_not_applicable};
use orchid_extension::tree::{GenTok, GenTokTree};
@@ -32,10 +32,10 @@ struct StringError {
impl StringError {
/// Convert into project error for reporting
pub fn into_proj(self, pos: u32) -> OrcErr {
pub async fn into_proj(self, pos: u32, i: &Interner) -> OrcErr {
let start = pos + self.pos;
mk_err(
intern!(str: "Failed to parse string"),
i.i("Failed to parse string").await,
match self.kind {
StringErrorKind::NotHex => "Expected a hex digit",
StringErrorKind::BadCodePoint => "The specified number is not a Unicode code point",
@@ -95,29 +95,40 @@ fn parse_string(str: &str) -> Result<String, StringError> {
pub struct StringLexer;
impl Lexer for StringLexer {
const CHAR_FILTER: &'static [std::ops::RangeInclusive<char>] = &['"'..='"'];
fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let mut tail = all.strip_prefix('"').ok_or_else(err_not_applicable)?;
let mut ret = GenTok::X(IntStrAtom::from(intern!(str: "")).factory()).at(ctx.tok_ran(0, all));
async fn lex<'a>(all: &'a str, ctx: &'a LexContext<'a>) -> OrcRes<(&'a str, GenTokTree<'a>)> {
let Some(mut tail) = all.strip_prefix('"') else {
return Err(err_not_applicable(ctx.i).await.into());
};
let mut ret = GenTok::X(IntStrAtom::from(ctx.i.i("").await).factory()).at(ctx.tok_ran(0, all));
let mut cur = String::new();
let mut errors = vec![];
let str_to_gen = |str: &mut String, tail: &str, err: &mut Vec<OrcErr>| {
let str_val = parse_string(&str.split_off(0))
.inspect_err(|e| err.push(e.clone().into_proj(ctx.pos(tail) - str.len() as u32)))
.unwrap_or_default();
GenTok::X(IntStrAtom::from(intern(&*str_val)).factory())
.at(ctx.tok_ran(str.len() as u32, tail))
};
let add_frag = |prev: GenTokTree<'a>, new: GenTokTree<'a>| {
wrap_tokv(vname_tv(&vname!(std::string::concat), new.range.end).chain([prev, new]))
async fn str_to_gen<'a>(
str: &mut String,
tail: &str,
err: &mut Vec<OrcErr>,
ctx: &'a LexContext<'a>,
) -> GenTokTree<'a> {
let str_val_res = parse_string(&str.split_off(0));
if let Err(e) = &str_val_res {
err.push(e.clone().into_proj(ctx.pos(tail) - str.len() as u32, ctx.i).await);
}
let str_val = str_val_res.unwrap_or_default();
GenTok::X(IntStrAtom::from(ctx.i.i(&*str_val).await).factory())
.at(ctx.tok_ran(str.len() as u32, tail)) as GenTokTree<'a>
}
let add_frag = |prev: GenTokTree<'a>, new: GenTokTree<'a>| async {
wrap_tokv(
vname_tv(&vname!(std::string::concat; ctx.i).await, new.range.end).chain([prev, new]),
)
};
loop {
if let Some(rest) = tail.strip_prefix('"') {
return Ok((rest, add_frag(ret, str_to_gen(&mut cur, tail, &mut errors))));
return Ok((rest, add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await));
} else if let Some(rest) = tail.strip_prefix('$') {
ret = add_frag(ret, str_to_gen(&mut cur, tail, &mut errors));
let (new_tail, tree) = ctx.recurse(rest)?;
ret = add_frag(ret, str_to_gen(&mut cur, tail, &mut errors, ctx).await).await;
let (new_tail, tree) = ctx.recurse(rest).await?;
tail = new_tail;
ret = add_frag(ret, tree);
ret = add_frag(ret, tree).await;
} else if tail.starts_with('\\') {
// parse_string will deal with it, we just have to skip the next char
tail = &tail[2..];
@@ -128,9 +139,11 @@ impl Lexer for StringLexer {
tail = ch.as_str();
} else {
let range = ctx.pos(all)..ctx.pos("");
return Err(mk_errv(intern!(str: "No string end"), "String never terminated with \"", [
Pos::Range(range.clone()).into(),
]));
return Err(mk_errv(
ctx.i.i("No string end").await,
"String never terminated with \"",
[Pos::Range(range.clone()).into()],
));
}
}
}

View File

@@ -6,8 +6,12 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-stream = "0.3.6"
camino = "1.1.9"
clap = { version = "4.5.24", features = ["derive"] }
futures = "0.3.31"
itertools = "0.14.0"
orchid-base = { version = "0.1.0", path = "../orchid-base" }
orchid-host = { version = "0.1.0", path = "../orchid-host" }
substack = "1.1.1"
tokio = { version = "1.43.0", features = ["full"] }

View File

@@ -1,17 +1,26 @@
use std::fs::File;
use std::io::Read;
use std::mem;
use std::process::Command;
use std::sync::Arc;
use std::rc::Rc;
use async_stream::try_stream;
use camino::Utf8PathBuf;
use clap::{Parser, Subcommand};
use itertools::Itertools;
use orchid_base::interner::intern;
use futures::{Stream, TryStreamExt, io};
use orchid_base::clone;
use orchid_base::error::ReporterImpl;
use orchid_base::logging::{LogStrategy, Logger};
use orchid_base::parse::Snippet;
use orchid_base::tree::ttv_fmt;
use orchid_host::extension::{Extension, init_systems};
use orchid_host::ctx::Ctx;
use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::subprocess::Subprocess;
use orchid_host::parse::{self, ParseCtx, ParseCtxImpl, parse_items};
use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems;
use substack::Substack;
use tokio::task::{LocalSet, spawn_local};
#[derive(Parser, Debug)]
#[command(version, about, long_about)]
@@ -30,23 +39,65 @@ pub enum Commands {
#[arg(short, long)]
file: Utf8PathBuf,
},
Parse {
#[arg(short, long)]
file: Utf8PathBuf,
},
}
fn main() {
fn get_all_extensions<'a>(
args: &'a Args,
logger: &'a Logger,
ctx: &'a Ctx,
) -> impl Stream<Item = io::Result<Extension>> + 'a {
try_stream! {
for exe in args.extension.iter() {
let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), ctx.clone()).await
.unwrap();
let ext = Extension::new(init, logger.clone(), ctx.clone())?;
spawn_local(clone!(ext; async move { loop { ext.recv_one().await }}));
yield ext
}
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() {
LocalSet::new()
.run_until(async {
let args = Args::parse();
let logger = Logger::new(LogStrategy::StdErr);
let ctx = &Ctx::new(Rc::new(|fut| mem::drop(spawn_local(fut))));
let logger = Logger::new(LogStrategy::Discard);
let extensions =
get_all_extensions(&args, &logger, ctx).try_collect::<Vec<Extension>>().await.unwrap();
match args.command {
Commands::Lex { file } => {
let extensions = (args.extension.iter())
.map(|f| Subprocess::new(Command::new(f.as_os_str()), logger.clone()).unwrap())
.map(|cmd| Extension::new(Arc::new(cmd), logger.clone()).unwrap())
.collect_vec();
let systems = init_systems(&args.system, &extensions).unwrap();
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(intern(&buf), &systems).unwrap();
println!("{}", ttv_fmt(&lexemes))
let lexemes = lex(ctx.i.i(&buf).await, &systems, ctx).await.unwrap();
println!("{}", ttv_fmt(&lexemes).await)
},
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(ctx.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, &ctx.i);
let ptree = parse_items(&pctx, Substack::Bottom, snip).await.unwrap();
for item in ptree {
println!("{item:?}")
}
},
}
})
.await
}

1
test-log.txt Normal file
View File

@@ -0,0 +1 @@
Upsending: [ff ff ff ff ff ff ff f7 00 00 00 00 00 00 00 08 22 75 73 65 72 21 22 69]