partway through fixes, macro system needs resdesign

This commit is contained in:
2026-04-08 18:02:20 +02:00
parent 0909524dee
commit 9b4c7fa7d7
76 changed files with 1391 additions and 1065 deletions

View File

@@ -13,3 +13,12 @@ RUST_BACKTRACE = "1"
[build] [build]
# rustflags = ["-Znext-solver"] # rustflags = ["-Znext-solver"]
[profile.dev]
opt-level = 0
debug = 2
strip = 'none'
debug-assertions = true
overflow-checks = true
lto = false
panic = 'abort'

48
Cargo.lock generated
View File

@@ -92,6 +92,15 @@ version = "1.0.102"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
[[package]]
name = "ar_archive_writer"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b"
dependencies = [
"object",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.7.6" version = "0.7.6"
@@ -297,9 +306,9 @@ dependencies = [
[[package]] [[package]]
name = "chrono" name = "chrono"
version = "0.4.43" version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0"
dependencies = [ dependencies = [
"iana-time-zone", "iana-time-zone",
"js-sys", "js-sys",
@@ -994,6 +1003,15 @@ version = "4.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
[[package]]
name = "object"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"memchr",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.3" version = "1.21.3"
@@ -1087,6 +1105,7 @@ dependencies = [
"async-event", "async-event",
"async-fn-stream", "async-fn-stream",
"async-once-cell", "async-once-cell",
"chrono",
"derive_destructure", "derive_destructure",
"dyn-clone", "dyn-clone",
"futures", "futures",
@@ -1122,6 +1141,7 @@ dependencies = [
"async-fn-stream", "async-fn-stream",
"async-once-cell", "async-once-cell",
"bound", "bound",
"chrono",
"derive_destructure", "derive_destructure",
"futures", "futures",
"futures-locks", "futures-locks",
@@ -1187,6 +1207,7 @@ dependencies = [
"orchid-api", "orchid-api",
"orchid-base", "orchid-base",
"orchid-host", "orchid-host",
"stacker",
"substack", "substack",
"tokio", "tokio",
] ]
@@ -1293,6 +1314,16 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "psm"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8"
dependencies = [
"ar_archive_writer",
"cc",
]
[[package]] [[package]]
name = "ptr_meta" name = "ptr_meta"
version = "0.1.4" version = "0.1.4"
@@ -1731,6 +1762,19 @@ dependencies = [
"web-time", "web-time",
] ]
[[package]]
name = "stacker"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013"
dependencies = [
"cc",
"cfg-if",
"libc",
"psm",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "stdio-perftest" name = "stdio-perftest"
version = "0.1.0" version = "0.1.0"

View File

@@ -0,0 +1,2 @@
[profile.dev]
panic = 'unwind'

View File

@@ -376,3 +376,13 @@ impl Encode for chrono::TimeDelta {
self.subsec_nanos().encode(write).await self.subsec_nanos().encode(write).await
} }
} }
impl Decode for chrono::DateTime<chrono::Utc> {
async fn decode<R: AsyncRead + ?Sized>(read: Pin<&mut R>) -> io::Result<Self> {
Ok(Self::from_timestamp_micros(i64::decode(read).await?).unwrap())
}
}
impl Encode for chrono::DateTime<chrono::Utc> {
async fn encode<W: AsyncWrite + ?Sized>(&self, write: Pin<&mut W>) -> io::Result<()> {
self.timestamp_micros().encode(write).await
}
}

View File

@@ -95,14 +95,18 @@ impl Request for DeserAtom {
/// A request blindly routed to the system that provides an atom. /// A request blindly routed to the system that provides an atom.
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(AtomReq, HostExtReq)] #[extends(AtomReq, HostExtReq)]
pub struct FinalFwded(pub Atom, pub TStrv, pub Vec<u8>); pub struct Fwded(pub Atom, pub TStrv, pub Vec<u8>);
impl Request for FinalFwded { impl Request for Fwded {
type Response = Option<Vec<u8>>; type Response = Option<Vec<u8>>;
} }
#[derive(Clone, Debug, Coding, Hierarchy)] #[derive(Clone, Debug, Coding, Hierarchy)]
#[extends(ExtHostReq)] #[extends(ExtHostReq)]
pub struct Fwd(pub Atom, pub TStrv, pub Vec<u8>); pub struct Fwd {
pub target: Atom,
pub method: TStrv,
pub body: Vec<u8>,
}
impl Request for Fwd { impl Request for Fwd {
type Response = Option<Vec<u8>>; type Response = Option<Vec<u8>>;
} }
@@ -138,8 +142,7 @@ impl Request for ExtAtomPrint {
pub enum AtomReq { pub enum AtomReq {
CallRef(CallRef), CallRef(CallRef),
FinalCall(FinalCall), FinalCall(FinalCall),
FwdedRef(FinalFwded), Fwded(Fwded),
FinalFwded(FinalFwded),
AtomPrint(AtomPrint), AtomPrint(AtomPrint),
SerializeAtom(SerializeAtom), SerializeAtom(SerializeAtom),
} }
@@ -150,8 +153,7 @@ impl AtomReq {
match self { match self {
Self::CallRef(CallRef(a, ..)) Self::CallRef(CallRef(a, ..))
| Self::FinalCall(FinalCall(a, ..)) | Self::FinalCall(FinalCall(a, ..))
| Self::FwdedRef(FinalFwded(a, ..)) | Self::Fwded(Fwded(a, ..))
| Self::FinalFwded(FinalFwded(a, ..))
| Self::AtomPrint(AtomPrint(a)) | Self::AtomPrint(AtomPrint(a))
| Self::SerializeAtom(SerializeAtom(a)) => a, | Self::SerializeAtom(SerializeAtom(a)) => a,
} }

View File

@@ -6,7 +6,6 @@
//! the channel with the same protocol outlined in [crate::proto] //! the channel with the same protocol outlined in [crate::proto]
use unsync_pipe::{Reader, Writer}; use unsync_pipe::{Reader, Writer};
/// !Send !Sync owned waker /// !Send !Sync owned waker
/// ///
/// This object is [Clone] for convenience but it has `drop` and no `clone` so /// This object is [Clone] for convenience but it has `drop` and no `clone` so

View File

@@ -312,9 +312,9 @@ impl<'a> MsgWriter<'a> for IoNotifWriter {
fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() } fn writer(&mut self) -> Pin<&mut dyn AsyncWrite> { self.o.as_mut() }
fn finish(mut self: Box<Self>) -> LocalBoxFuture<'static, io::Result<()>> { fn finish(mut self: Box<Self>) -> LocalBoxFuture<'static, io::Result<()>> {
Box::pin(async move { Box::pin(async move {
self.o.flush().await?; let ret = self.o.flush().await;
self.drop_g.defuse(); self.drop_g.defuse();
Ok(()) ret
}) })
} }
} }

View File

@@ -142,7 +142,7 @@ impl OrcErrv {
/// If there is exactly one error, return it. Mostly used for simplified /// If there is exactly one error, return it. Mostly used for simplified
/// printing /// printing
#[must_use] #[must_use]
pub fn one(&self) -> Option<&OrcErr> { (self.0.len() == 1).then(|| &self.0[9]) } pub fn one(&self) -> Option<&OrcErr> { self.0.iter().exactly_one().ok() }
/// Iterate over all positions of all errors /// Iterate over all positions of all errors
pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ { pub fn pos_iter(&self) -> impl Iterator<Item = ErrPos> + '_ {
self.0.iter().flat_map(|e| e.positions.iter().cloned()) self.0.iter().flat_map(|e| e.positions.iter().cloned())

View File

@@ -332,5 +332,12 @@ pub async fn ttv_fmt<'a: 'b, 'b>(
FmtUnit::sequence("", " ", "", true, join_all(ttv.into_iter().map(|t| t.print(c))).await) FmtUnit::sequence("", " ", "", true, join_all(ttv.into_iter().map(|t| t.print(c))).await)
} }
pub struct FmtTTV<'a, H: ExprRepr, X: ExtraTok>(pub &'a [TokTree<H, X>]);
impl<'b, H: ExprRepr, X: ExtraTok> Format for FmtTTV<'b, H, X> {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
ttv_fmt(self.0, c).await
}
}
/// Indent a string by two spaces /// Indent a string by two spaces
pub fn indent(s: &str) -> String { s.replace("\n", "\n ") } pub fn indent(s: &str) -> String { s.replace("\n", "\n ") }

View File

@@ -9,6 +9,7 @@ edition = "2024"
async-event = "0.2.1" async-event = "0.2.1"
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
async-once-cell = "0.5.4" async-once-cell = "0.5.4"
chrono = "0.4.44"
derive_destructure = "1.0.0" derive_destructure = "1.0.0"
dyn-clone = "1.0.20" dyn-clone = "1.0.20"
futures = { version = "0.3.31", default-features = false, features = [ futures = { version = "0.3.31", default-features = false, features = [

View File

@@ -1,5 +1,4 @@
use std::any::{Any, TypeId, type_name}; use std::any::{Any, TypeId, type_name};
use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
use std::future::Future; use std::future::Future;
@@ -19,7 +18,6 @@ use orchid_base::{
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym, FmtCtx, FmtUnit, Format, IStr, OrcErrv, Pos, Receipt, ReqHandle, ReqReader, ReqReaderExt, Sym,
fmt, is, mk_errv, mk_errv_floating, take_first, fmt, is, mk_errv, mk_errv_floating, take_first,
}; };
use task_local::task_local;
use trait_set::trait_set; use trait_set::trait_set;
use crate::gen_expr::GExpr; use crate::gen_expr::GExpr;
@@ -99,11 +97,11 @@ impl ForeignAtom {
/// Call an IPC method. If the type does not support the given method type, /// Call an IPC method. If the type does not support the given method type,
/// this function returns [None] /// this function returns [None]
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> { pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, r: R) -> Option<R::Response> {
let rep = (request(api::Fwd( let rep = (request(api::Fwd {
self.atom.clone(), target: self.atom.clone(),
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(), method: Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
enc_vec(&r.into_root()), body: enc_vec(&r.into_root()),
))) }))
.await?; .await?;
Some(R::Response::decode_slice(&mut &rep[..])) Some(R::Response::decode_slice(&mut &rep[..]))
} }
@@ -111,26 +109,22 @@ impl ForeignAtom {
pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> { pub fn downcast<A: Atomic>(self) -> Result<TAtom<A>, NotTypAtom> {
let mut data = &self.atom.data.0[..]; let mut data = &self.atom.data.0[..];
let value = AtomTypeId::decode_slice(&mut data); let value = AtomTypeId::decode_slice(&mut data);
if cfg!(debug_assertions) { let cted = dyn_cted();
let cted = dyn_cted(); let own_inst = cted.inst();
let own_inst = cted.inst(); let owner_id = self.atom.owner;
let owner_id = self.atom.owner; let typ = type_name::<A>();
let typ = type_name::<A>(); let owner = if sys_id() == owner_id {
let owner = if sys_id() == owner_id { own_inst.card()
own_inst.card() } else {
} else { (cted.deps().find(|s| s.id() == self.atom.owner))
(cted.deps().find(|s| s.id() == self.atom.owner)) .ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })?
.ok_or_else(|| NotTypAtom { expr: self.clone().ex(), pos: self.pos(), typ })? .get_card()
.get_card() };
}; let Some(ops) = owner.ops_by_atid(value) else {
let Some(ops) = owner.ops_by_atid(value) else { panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}");
panic!("{value:?} does not refer to an atom in {owner_id:?} when downcasting {typ}"); };
}; if ops.tid() != TypeId::of::<A>() {
if ops.tid() != TypeId::of::<A>() { return Err(NotTypAtom { pos: self.pos.clone(), expr: self.ex(), typ });
panic!(
"{value:?} of {owner_id:?} refers to a type other than {typ}. System version mismatch?"
)
}
} }
let value = A::Data::decode_slice(&mut data); let value = A::Data::decode_slice(&mut data);
Ok(TAtom { value, untyped: self }) Ok(TAtom { value, untyped: self })
@@ -187,10 +181,6 @@ pub trait AtomMethod: Coding + InHierarchy {
const NAME: &str; const NAME: &str;
} }
task_local! {
pub(crate) static ATOM_WITHOUT_HANDLE_FINAL_IMPL: Rc<RefCell<Option<Box<dyn Any>>>>;
}
/// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be /// A handler for an [AtomMethod] on an [Atomic]. The [AtomMethod] must also be
/// registered in [Atomic::reg_methods] /// registered in [Atomic::reg_methods]
pub trait Supports<M: AtomMethod>: Atomic { pub trait Supports<M: AtomMethod>: Atomic {
@@ -199,19 +189,6 @@ pub trait Supports<M: AtomMethod>: Atomic {
hand: Box<dyn ReqHandle<'a> + '_>, hand: Box<dyn ReqHandle<'a> + '_>,
req: M, req: M,
) -> impl Future<Output = io::Result<Receipt<'a>>>; ) -> impl Future<Output = io::Result<Receipt<'a>>>;
fn handle_final<'a>(
self,
hand: Box<dyn ReqHandle<'a> + '_>,
req: M,
) -> impl Future<Output = io::Result<Receipt<'a>>> {
async move {
let rcpt = self.handle(hand, req).await;
let _ = ATOM_WITHOUT_HANDLE_FINAL_IMPL.try_with(|cell| cell.replace(Some(Box::new(self))));
rcpt
}
}
// TODO: default-implement the above somehow while calling OwnedAtom::free if
// necessary
} }
trait HandleAtomMethod<A> { trait HandleAtomMethod<A> {
@@ -220,11 +197,6 @@ trait HandleAtomMethod<A> {
atom: &'a A, atom: &'a A,
reader: Box<dyn ReqReader<'b> + 'a>, reader: Box<dyn ReqReader<'b> + 'a>,
) -> LocalBoxFuture<'a, ()>; ) -> LocalBoxFuture<'a, ()>;
fn handle_final<'a, 'b: 'a>(
&'a self,
atom: A,
reader: Box<dyn ReqReader<'b> + 'a>,
) -> LocalBoxFuture<'a, ()>;
} }
struct AtomMethodHandler<M, A>(PhantomData<M>, PhantomData<A>); struct AtomMethodHandler<M, A>(PhantomData<M>, PhantomData<A>);
impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M, A> { impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M, A> {
@@ -238,16 +210,6 @@ impl<M: AtomMethod, A: Supports<M>> HandleAtomMethod<A> for AtomMethodHandler<M,
let _ = Supports::<M>::handle(atom, reader.finish().await, req).await.unwrap(); let _ = Supports::<M>::handle(atom, reader.finish().await, req).await.unwrap();
}) })
} }
fn handle_final<'a, 'b: 'a>(
&'a self,
atom: A,
mut reader: Box<dyn ReqReader<'b> + 'a>,
) -> LocalBoxFuture<'a, ()> {
Box::pin(async {
let req = reader.read_req::<M>().await.unwrap();
let _ = Supports::<M>::handle_final(atom, reader.finish().await, req).await.unwrap();
})
}
} }
/// A collection of [Supports] impls for an [Atomic]. If a [Supports] /// A collection of [Supports] impls for an [Atomic]. If a [Supports]
@@ -282,20 +244,6 @@ pub(crate) struct MethodSet<A: Atomic> {
handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>, handlers: HashMap<Sym, Rc<dyn HandleAtomMethod<A>>>,
} }
impl<A: Atomic> MethodSet<A> { impl<A: Atomic> MethodSet<A> {
pub(crate) async fn final_dispatch<'a>(
&self,
atom: A,
key: Sym,
req: Box<dyn ReqReader<'a> + 'a>,
) -> bool {
match self.handlers.get(&key) {
None => false,
Some(handler) => {
handler.handle_final(atom, req).await;
true
},
}
}
pub(crate) async fn dispatch<'a>( pub(crate) async fn dispatch<'a>(
&self, &self,
atom: &'_ A, atom: &'_ A,
@@ -353,11 +301,11 @@ impl<A: Atomic> TAtom<A> {
pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response pub async fn call<R: Request + UnderRoot<Root: AtomMethod>>(&self, req: R) -> R::Response
where A: Supports<<R as UnderRoot>::Root> { where A: Supports<<R as UnderRoot>::Root> {
R::Response::decode_slice( R::Response::decode_slice(
&mut &(request(api::Fwd( &mut &(request(api::Fwd {
self.untyped.atom.clone(), target: self.untyped.atom.clone(),
Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(), method: Sym::parse(<R as UnderRoot>::Root::NAME).await.unwrap().tok().to_api(),
enc_vec(&req.into_root()), body: enc_vec(&req.into_root()),
))) }))
.await .await
.unwrap()[..], .unwrap()[..],
) )
@@ -389,12 +337,6 @@ pub trait AtomOps: 'static {
fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>; fn call<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>; fn call_ref<'a>(&'a self, ctx: AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr>;
fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>; fn print<'a>(&'a self, ctx: AtomCtx<'a>) -> LocalBoxFuture<'a, FmtUnit>;
fn handle_req<'a>(
&'a self,
ctx: AtomCtx<'a>,
key: Sym,
req: Box<dyn ReqReader<'a> + 'a>,
) -> LocalBoxFuture<'a, bool>;
fn handle_req_ref<'a>( fn handle_req_ref<'a>(
&'a self, &'a self,
ctx: AtomCtx<'a>, ctx: AtomCtx<'a>,
@@ -417,25 +359,25 @@ pub trait AtomOps: 'static {
trait_set! { trait_set! {
pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone; pub trait AtomFactoryFn = FnOnce() -> LocalBoxFuture<'static, api::LocalAtom> + DynClone;
} }
pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>); pub(crate) struct AtomFactory(Box<dyn AtomFactoryFn>, String);
impl AtomFactory { impl AtomFactory {
pub fn new(f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self { pub fn new(name: String, f: impl AsyncFnOnce() -> api::LocalAtom + Clone + 'static) -> Self {
Self(Box::new(|| f().boxed_local())) Self(Box::new(|| f().boxed_local()), name)
} }
pub async fn build(self) -> api::LocalAtom { (self.0)().await } pub async fn build(self) -> api::LocalAtom { (self.0)().await }
} }
impl Clone for AtomFactory { impl Clone for AtomFactory {
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) } fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0), self.1.clone()) }
} }
impl fmt::Debug for AtomFactory { impl fmt::Debug for AtomFactory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory<{}>", self.1) }
} }
impl fmt::Display for AtomFactory { impl fmt::Display for AtomFactory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "AtomFactory") } fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self:?}") }
} }
impl Format for AtomFactory { impl Format for AtomFactory {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
"AtomFactory".to_string().into() self.to_string().into()
} }
} }

View File

@@ -22,9 +22,8 @@ use task_local::task_local;
use crate::gen_expr::{GExpr, bot}; use crate::gen_expr::{GExpr, bot};
use crate::{ use crate::{
ATOM_WITHOUT_HANDLE_FINAL_IMPL, AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomCtx, AtomFactory, AtomOps, Atomic, AtomicFeaturesImpl, AtomicVariant, DynSystemCardExt, Expr,
AtomicVariant, DynSystemCardExt, Expr, MethodSet, MethodSetBuilder, ToExpr, api, dyn_cted, MethodSet, MethodSetBuilder, ToExpr, api, dyn_cted, err_not_callable,
err_not_callable,
}; };
/// Value of [Atomic::Variant] for a type that implements [OwnedAtom] /// Value of [Atomic::Variant] for a type that implements [OwnedAtom]
@@ -32,7 +31,7 @@ pub struct OwnedVariant;
impl AtomicVariant for OwnedVariant {} impl AtomicVariant for OwnedVariant {}
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A { impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
fn _factory(self) -> AtomFactory { fn _factory(self) -> AtomFactory {
AtomFactory::new(async move || { AtomFactory::new(type_name::<A>().to_string(), async move || {
let obj_store = get_obj_store(); let obj_store = get_obj_store();
let atom_id = { let atom_id = {
let mut id = obj_store.next_id.borrow_mut(); let mut id = obj_store.next_id.borrow_mut();
@@ -73,7 +72,10 @@ impl Deref for AtomReadGuard<'_> {
/// Remove an atom from the store /// Remove an atom from the store
pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> { pub(crate) async fn take_atom(id: api::AtomId) -> Box<dyn DynOwnedAtom> {
let mut g = get_obj_store().objects.write().await; let mut g = get_obj_store().objects.write().await;
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0)) g.remove(&id).unwrap_or_else(|| {
let name = dyn_cted().inst().card().name();
panic!("{name} received invalid atom ID: {}", id.0)
})
} }
pub(crate) struct OwnedAtomOps<T: OwnedAtom> { pub(crate) struct OwnedAtomOps<T: OwnedAtom> {
@@ -113,25 +115,6 @@ impl<A: OwnedAtom> AtomOps for OwnedAtomOps<A> {
fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> { fn print(&self, AtomCtx(_, id): AtomCtx<'_>) -> LocalBoxFuture<'_, FmtUnit> {
Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await }) Box::pin(async move { AtomReadGuard::new(id.unwrap()).await.dyn_print().await })
} }
fn handle_req<'a>(
&'a self,
AtomCtx(_, id): AtomCtx<'a>,
key: Sym,
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
) -> LocalBoxFuture<'a, bool> {
Box::pin(async move {
let a = take_atom(id.unwrap()).await;
let ms = self.ms.get_or_init(self.msbuild.pack()).await;
let cell = Rc::new(RefCell::new(None));
let matched = ATOM_WITHOUT_HANDLE_FINAL_IMPL
.scope(cell.clone(), ms.final_dispatch(*a.as_any().downcast().unwrap(), key, req))
.await;
if let Some(val) = cell.take() {
val.downcast::<A>().unwrap().free().await
}
matched
})
}
fn handle_req_ref<'a>( fn handle_req_ref<'a>(
&'a self, &'a self,
AtomCtx(_, id): AtomCtx<'a>, AtomCtx(_, id): AtomCtx<'a>,
@@ -303,7 +286,6 @@ fn assert_serializable<T: OwnedAtom>() {
pub(crate) trait DynOwnedAtom: DynClone + 'static { pub(crate) trait DynOwnedAtom: DynClone + 'static {
fn as_any_ref(&self) -> &dyn Any; fn as_any_ref(&self) -> &dyn Any;
fn as_any(self: Box<Self>) -> Box<dyn Any>;
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()>; fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()>;
fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr>; fn dyn_call_ref(&self, arg: Expr) -> LocalBoxFuture<'_, GExpr>;
fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr>; fn dyn_call(self: Box<Self>, arg: Expr) -> LocalBoxFuture<'static, GExpr>;
@@ -316,7 +298,6 @@ pub(crate) trait DynOwnedAtom: DynClone + 'static {
} }
impl<T: OwnedAtom> DynOwnedAtom for T { impl<T: OwnedAtom> DynOwnedAtom for T {
fn as_any_ref(&self) -> &dyn Any { self } fn as_any_ref(&self) -> &dyn Any { self }
fn as_any(self: Box<Self>) -> Box<dyn Any> { self }
fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> { fn encode<'a>(&'a self, buffer: Pin<&'a mut dyn AsyncWrite>) -> LocalBoxFuture<'a, ()> {
async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local() async { self.val().await.as_ref().encode(buffer).await.unwrap() }.boxed_local()
} }

View File

@@ -19,7 +19,7 @@ pub struct ThinVariant;
impl AtomicVariant for ThinVariant {} impl AtomicVariant for ThinVariant {}
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A { impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
fn _factory(self) -> AtomFactory { fn _factory(self) -> AtomFactory {
AtomFactory::new(async move || { AtomFactory::new(type_name::<A>().to_string(), async move || {
let (id, _) = dyn_cted().inst().card().ops::<A>(); let (id, _) = dyn_cted().inst().card().ops::<A>();
let mut buf = enc_vec(&id); let mut buf = enc_vec(&id);
self.encode_vec(&mut buf); self.encode_vec(&mut buf);
@@ -49,7 +49,7 @@ impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> { fn call_ref<'a>(&'a self, AtomCtx(buf, ..): AtomCtx<'a>, arg: Expr) -> LocalBoxFuture<'a, GExpr> {
Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await }) Box::pin(async move { T::decode_slice(&mut &buf[..]).call(arg).await })
} }
fn handle_req<'a>( fn handle_req_ref<'a>(
&'a self, &'a self,
AtomCtx(buf, ..): AtomCtx<'a>, AtomCtx(buf, ..): AtomCtx<'a>,
key: Sym, key: Sym,
@@ -60,14 +60,6 @@ impl<T: ThinAtom> AtomOps for ThinAtomOps<T> {
ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await ms.dispatch(&T::decode_slice(&mut &buf[..]), key, req).await
}) })
} }
fn handle_req_ref<'a>(
&'a self,
ctx: AtomCtx<'a>,
key: Sym,
req: Box<dyn orchid_base::ReqReader<'a> + 'a>,
) -> LocalBoxFuture<'a, bool> {
self.handle_req(ctx, key, req)
}
fn serialize<'a, 'b: 'a>( fn serialize<'a, 'b: 'a>(
&'a self, &'a self,
ctx: AtomCtx<'a>, ctx: AtomCtx<'a>,

View File

@@ -1,23 +1,23 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::rc::Rc;
use dyn_clone::DynClone;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use never::Never; use never::Never;
use orchid_base::{Receipt, ReqHandle, ReqHandleExt}; use orchid_base::{Receipt, ReqHandle, ReqHandleExt};
use trait_set::trait_set;
use crate::gen_expr::{GExpr, new_atom}; use crate::gen_expr::{GExpr, new_atom, serialize};
use crate::std_reqs::RunCommand; use crate::std_reqs::RunCommand;
use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr}; use crate::{Atomic, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, ToExpr};
trait_set! { pub trait AsyncFnDyn {
pub trait ClonableAsyncFnOnceDyn = FnOnce() -> LocalBoxFuture<'static, Option<GExpr>> + DynClone; fn call<'a>(&'a self) -> LocalBoxFuture<'a, Option<GExpr>>;
}
impl<T: AsyncFn() -> Option<GExpr>> AsyncFnDyn for T {
fn call<'a>(&'a self) -> LocalBoxFuture<'a, Option<GExpr>> { Box::pin(async { (self)().await }) }
} }
pub struct CmdAtom(Box<dyn ClonableAsyncFnOnceDyn>); #[derive(Clone)]
impl Clone for CmdAtom { pub struct CmdAtom(Rc<dyn AsyncFnDyn>);
fn clone(&self) -> Self { Self(dyn_clone::clone_box(&*self.0)) }
}
impl Atomic for CmdAtom { impl Atomic for CmdAtom {
type Data = (); type Data = ();
type Variant = OwnedVariant; type Variant = OwnedVariant;
@@ -29,17 +29,10 @@ impl Supports<RunCommand> for CmdAtom {
hand: Box<dyn ReqHandle<'a> + '_>, hand: Box<dyn ReqHandle<'a> + '_>,
req: RunCommand, req: RunCommand,
) -> std::io::Result<Receipt<'a>> { ) -> std::io::Result<Receipt<'a>> {
Self(dyn_clone::clone_box(&*self.0)).handle_final(hand, req).await let reply = self.0.call().await;
}
async fn handle_final<'a>(
self,
hand: Box<dyn ReqHandle<'a> + '_>,
req: RunCommand,
) -> std::io::Result<Receipt<'a>> {
let reply = (self.0)().await;
match reply { match reply {
None => hand.reply(&req, &None).await, None => hand.reply(&req, &None).await,
Some(next) => hand.reply(&req, &Some(next.serialize().await)).await, Some(next) => hand.reply(&req, &Some(serialize(next).await)).await,
} }
} }
} }
@@ -48,13 +41,9 @@ impl OwnedAtom for CmdAtom {
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
} }
pub fn cmd<R: ToExpr>(f: impl AsyncFnOnce() -> Option<R> + Clone + 'static) -> GExpr { pub fn cmd<R: ToExpr>(f: impl AsyncFn() -> Option<R> + Clone + 'static) -> GExpr {
new_atom(CmdAtom(Box::new(|| { new_atom(CmdAtom(Rc::new(async move || match f().await {
Box::pin(async { None => None,
match f().await { Some(r) => Some(r.to_gen().await),
None => None,
Some(r) => Some(r.to_gen().await),
}
})
}))) })))
} }

View File

@@ -71,6 +71,14 @@ pub trait ToExpr {
where Self: Sized { where Self: Sized {
async { self.to_gen().await.create().await } async { self.to_gen().await.create().await }
} }
fn boxed<'a>(self) -> Box<dyn ToExprDyn + 'a>
where Self: Sized + 'a {
Box::new(self)
}
fn clonable_boxed<'a>(self) -> Box<dyn ClonableToExprDyn + 'a>
where Self: Clone + Sized + 'a {
Box::new(self)
}
} }
/// A wrapper for a future that implements [ToExpr] /// A wrapper for a future that implements [ToExpr]

View File

@@ -1,3 +1,4 @@
use std::any::type_name;
use std::borrow::Cow; use std::borrow::Cow;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc; use std::rc::Rc;
@@ -7,9 +8,9 @@ use futures::lock::Mutex;
use futures::stream::{self, LocalBoxStream}; use futures::stream::{self, LocalBoxStream};
use futures::{FutureExt, SinkExt, StreamExt}; use futures::{FutureExt, SinkExt, StreamExt};
use never::Never; use never::Never;
use orchid_base::OrcRes; use orchid_base::{FmtCtx, FmtUnit, OrcRes};
use crate::gen_expr::{GExpr, arg, call, lam, new_atom, seq}; use crate::gen_expr::{GExpr, call, lam, new_atom, seq};
use crate::{Atomic, Expr, OwnedAtom, OwnedVariant, ToExpr, TryFromExpr}; use crate::{Atomic, Expr, OwnedAtom, OwnedVariant, ToExpr, TryFromExpr};
enum Command { enum Command {
@@ -18,6 +19,7 @@ enum Command {
} }
struct BuilderCoroutineData { struct BuilderCoroutineData {
name: &'static str,
receiver: Mutex<LocalBoxStream<'static, Command>>, receiver: Mutex<LocalBoxStream<'static, Command>>,
} }
@@ -30,7 +32,7 @@ impl BuilderCoroutine {
None => panic!("Exec handle dropped and coroutine blocked instead of returning"), None => panic!("Exec handle dropped and coroutine blocked instead of returning"),
Some(Command::Halt(expr)) => expr, Some(Command::Halt(expr)) => expr,
Some(Command::Execute(expr, reply)) => Some(Command::Execute(expr, reply)) =>
call(lam::<0>(seq(arg(0), call(new_atom(Replier { reply, builder: self }), arg(0)))), expr) call(lam(async |x| seq(x, call(new_atom(Replier { reply, builder: self }), x)).await), expr)
.await, .await,
} }
} }
@@ -53,6 +55,9 @@ impl OwnedAtom for Replier {
std::mem::drop(self.reply); std::mem::drop(self.reply);
self.builder.run().await self.builder.run().await
} }
async fn print_atom<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("Replier<{}>", self.builder.0.name).into()
}
} }
/// A long-lived async context that can yield to the executor. The expression /// A long-lived async context that can yield to the executor. The expression
@@ -62,6 +67,7 @@ pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R +
let halt = let halt =
async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream(); async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_gen().await) }.into_stream();
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData { let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData {
name: type_name::<R>(),
receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()), receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()),
})); }));
coro.run().await coro.run().await

View File

@@ -22,6 +22,7 @@ use orchid_base::{
use substack::Substack; use substack::Substack;
use task_local::task_local; use task_local::task_local;
use crate::gen_expr::serialize;
use crate::interner::new_interner; use crate::interner::new_interner;
use crate::logger::LoggerImpl; use crate::logger::LoggerImpl;
use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store}; use crate::tree::{TreeIntoApiCtxImpl, get_lazy, with_lazy_member_store};
@@ -63,9 +64,11 @@ pub async fn mute_reply<F: Future>(f: F) -> F::Output { MUTE_REPLY.scope((), f).
/// Send a request through the global client's [ClientExt::request] /// Send a request through the global client's [ClientExt::request]
pub async fn request<T: Request + UnderRoot<Root = api::ExtHostReq>>(t: T) -> T::Response { pub async fn request<T: Request + UnderRoot<Root = api::ExtHostReq>>(t: T) -> T::Response {
let req_str = if MUTE_REPLY.try_with(|b| *b).is_err() { format!("{t:?}") } else { String::new() };
let response = get_client().request(t).await.unwrap(); let response = get_client().request(t).await.unwrap();
if MUTE_REPLY.try_with(|b| *b).is_err() { if MUTE_REPLY.try_with(|b| *b).is_err() {
writeln!(log("msg"), "Got response {response:?}").await; let ext = dyn_cted().inst().card().name();
writeln!(log("msg"), "{ext} {req_str} got response {response:?}").await;
} }
response response
} }
@@ -342,7 +345,7 @@ impl ExtensionBuilder {
api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) => api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst(sys, id)) =>
with_sys_record(sys, async { with_sys_record(sys, async {
let cnst = get_const(id).await; let cnst = get_const(id).await;
handle.reply(fpc, &cnst.serialize().await).await handle.reply(fpc, &serialize(cnst).await).await
}) })
.await, .await,
api::HostExtReq::AtomReq(atom_req) => { api::HostExtReq::AtomReq(atom_req) => {
@@ -365,16 +368,8 @@ impl ExtensionBuilder {
}, },
api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) => api::AtomReq::AtomPrint(print @ api::AtomPrint(_)) =>
handle.reply(print, &nfo.print(actx).await.to_api()).await, handle.reply(print, &nfo.print(actx).await.to_api()).await,
api::AtomReq::FinalFwded(fwded) => { api::AtomReq::Fwded(fwded) => {
let api::FinalFwded(_, key, payload) = &fwded; let api::Fwded(_, key, payload) = &fwded;
let mut reply = Vec::new();
let key = Sym::from_api(*key).await;
let req = TrivialReqCycle { req: payload, rep: &mut reply };
let some = nfo.handle_req(actx, key, Box::new(req)).await;
handle.reply(fwded, &some.then_some(reply)).await
},
api::AtomReq::FwdedRef(fwded) => {
let api::FinalFwded(_, key, payload) = &fwded;
let mut reply = Vec::new(); let mut reply = Vec::new();
let key = Sym::from_api(*key).await; let key = Sym::from_api(*key).await;
let req = TrivialReqCycle { req: payload, rep: &mut reply }; let req = TrivialReqCycle { req: payload, rep: &mut reply };
@@ -385,7 +380,7 @@ impl ExtensionBuilder {
let expr_store = BorrowedExprStore::new(); let expr_store = BorrowedExprStore::new();
let expr_handle = ExprHandle::borrowed(*arg, &expr_store); let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await; let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await;
let api_expr = ret.serialize().await; let api_expr = serialize(ret).await;
mem::drop(expr_handle); mem::drop(expr_handle);
expr_store.dispose().await; expr_store.dispose().await;
handle.reply(call, &api_expr).await handle.reply(call, &api_expr).await
@@ -394,7 +389,7 @@ impl ExtensionBuilder {
let expr_store = BorrowedExprStore::new(); let expr_store = BorrowedExprStore::new();
let expr_handle = ExprHandle::borrowed(*arg, &expr_store); let expr_handle = ExprHandle::borrowed(*arg, &expr_store);
let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await; let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await;
let api_expr = ret.serialize().await; let api_expr = serialize(ret).await;
mem::drop(expr_handle); mem::drop(expr_handle);
expr_store.dispose().await; expr_store.dispose().await;
handle.reply(call, &api_expr).await handle.reply(call, &api_expr).await

View File

@@ -10,7 +10,7 @@ use futures::future::join_all;
use hashbrown::HashSet; use hashbrown::HashSet;
use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash}; use orchid_base::{FmtCtx, FmtUnit, Format, OrcErrv, Pos, stash};
use crate::gen_expr::{GExpr, GExprKind}; use crate::gen_expr::{GExpr, slot};
use crate::{ForeignAtom, api, notify, request, sys_id}; use crate::{ForeignAtom, api, notify, request, sys_id};
/// Handle for a lifetime associated with an [ExprHandle], such as a function /// Handle for a lifetime associated with an [ExprHandle], such as a function
@@ -158,9 +158,7 @@ impl Expr {
pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() } pub fn handle(&self) -> Rc<ExprHandle> { self.handle.clone() }
/// Wrap this expression in a [GExpr] synchronously as an escape hatch. /// Wrap this expression in a [GExpr] synchronously as an escape hatch.
/// Otherwise identical to this type's [crate::ToExpr] impl /// Otherwise identical to this type's [crate::ToExpr] impl
pub fn slot(&self) -> GExpr { pub fn slot(&self) -> GExpr { slot(self.clone()) }
GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(self.clone()) }
}
/// Increments the refcount to ensure that the ticket remains valid even if /// Increments the refcount to ensure that the ticket remains valid even if
/// the handle is freed. To avoid a leak, [Expr::deserialize] must eventually /// the handle is freed. To avoid a leak, [Expr::deserialize] must eventually
/// be called. /// be called.

View File

@@ -1,3 +1,5 @@
use std::cell::RefCell;
use std::marker::PhantomData;
use std::mem; use std::mem;
use std::pin::{Pin, pin}; use std::pin::{Pin, pin};
use std::rc::Rc; use std::rc::Rc;
@@ -6,22 +8,38 @@ use futures::{FutureExt, Stream, StreamExt, stream};
use orchid_base::{ use orchid_base::{
FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache, FmtCtx, FmtUnit, Format, OrcErr, OrcErrv, Pos, Sym, Variants, match_mapping, tl_cache,
}; };
use substack::Substack;
use task_local::task_local;
use crate::{AtomFactory, AtomicFeatures, Expr, ToExpr, ToExprFuture, api, request, sys_id}; use crate::{AtomFactory, AtomicFeatures, Expr, ToExpr, ToExprFuture, api, request, sys_id};
/// Newly generated AST. Values of this type should not typically be constructed #[derive(Clone, Copy, Debug)]
/// manually but through the helpers in this module struct ExprSerializeCx<'a> {
closures: Substack<'a, u64>,
lambda_counter: &'a RefCell<u64>,
}
/// Release notifications will not be sent for the slots. Use this with
/// messages that imply ownership transfer
pub async fn serialize(expr: GExpr) -> api::Expression {
let cx = ExprSerializeCx { closures: Substack::Bottom, lambda_counter: &RefCell::new(0) };
expr.serialize(cx).await
}
/// Smart object representing AST not-yet-sent to the interpreter. This type can
/// be cloned and persisted, and it must not have unbound arguments. The helper
/// functions in this module let you build trees of [ToExpr] implementors which
/// represent lambdas and their arguments separately, and then convert them into
/// [GExpr] in one pass.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct GExpr { pub struct GExpr {
/// AST node type /// AST node type
pub kind: GExprKind, kind: GExprKind,
/// Code location associated with the expression for debugging purposes /// Code location associated with the expression for debugging purposes
pub pos: Pos, pos: Pos,
} }
impl GExpr { impl GExpr {
/// Release notifications will not be sent for the slots. Use this with async fn serialize(self, cx: ExprSerializeCx<'_>) -> api::Expression {
/// messages that imply ownership transfer
pub async fn serialize(self) -> api::Expression {
if let GExprKind::Slot(ex) = self.kind { if let GExprKind::Slot(ex) = self.kind {
let hand = ex.handle(); let hand = ex.handle();
mem::drop(ex); mem::drop(ex);
@@ -32,8 +50,8 @@ impl GExpr {
} }
} else { } else {
api::Expression { api::Expression {
location: api::Location::Inherit, location: self.pos.to_api(),
kind: self.kind.serialize().boxed_local().await, kind: self.kind.serialize(cx).boxed_local().await,
} }
} }
} }
@@ -42,7 +60,7 @@ impl GExpr {
/// Send the expression to the interpreter to be compiled and to become /// Send the expression to the interpreter to be compiled and to become
/// shareable across extensions /// shareable across extensions
pub async fn create(self) -> Expr { pub async fn create(self) -> Expr {
Expr::deserialize(request(api::Create(sys_id(), self.serialize().await)).await).await Expr::deserialize(request(api::Create(sys_id(), serialize(self).await)).await).await
} }
} }
impl Format for GExpr { impl Format for GExpr {
@@ -56,8 +74,8 @@ impl Format for GExpr {
pub enum GExprKind { pub enum GExprKind {
/// Function call /// Function call
Call(Box<GExpr>, Box<GExpr>), Call(Box<GExpr>, Box<GExpr>),
/// Lambda expression. Argument numbers are matched when equal /// Lambda expression. Argument must be the same for slot
Lambda(u64, Box<GExpr>), Lambda(Box<GExpr>),
/// Slot for a lambda argument /// Slot for a lambda argument
Arg(u64), Arg(u64),
/// The second expression is only valid after the first one had already been /// The second expression is only valid after the first one had already been
@@ -80,23 +98,40 @@ pub enum GExprKind {
Bottom(OrcErrv), Bottom(OrcErrv),
} }
impl GExprKind { impl GExprKind {
async fn serialize(self) -> api::ExpressionKind { pub fn at(self, pos: Pos) -> GExpr { GExpr { kind: self, pos } }
async fn serialize(self, cx: ExprSerializeCx<'_>) -> api::ExpressionKind {
match_mapping!(self, Self => api::ExpressionKind { match_mapping!(self, Self => api::ExpressionKind {
Call( Call(
f => Box::new(f.serialize().await), f => Box::new(f.serialize(cx).await),
x => Box::new(x.serialize().await) x => Box::new(x.serialize(cx).await)
), ),
Seq( Seq(
a => Box::new(a.serialize().await), a => Box::new(a.serialize(cx).await),
b => Box::new(b.serialize().await) b => Box::new(b.serialize(cx).await)
), ),
Lambda(arg, body => Box::new(body.serialize().await)),
Arg(arg),
Const(name.to_api()), Const(name.to_api()),
Bottom(err.to_api()), Bottom(err.to_api()),
NewAtom(fac.clone().build().await), NewAtom(fac.clone().build().await),
} { } {
Self::Slot(_) => panic!("processed elsewhere") Self::Slot(_) => panic!("processed elsewhere"),
Self::Lambda(body) => {
let id: u64;
{
let mut g = cx.lambda_counter.borrow_mut();
id = *g;
*g += 1;
};
let cx = ExprSerializeCx {
lambda_counter: cx.lambda_counter,
closures: cx.closures.push(id)
};
api::ExpressionKind::Lambda(id,
Box::new(body.serialize(cx).await)
)
},
Self::Arg(arg) => {
api::ExpressionKind::Arg(*cx.closures.iter().nth(arg as usize).expect("Unbound arg"))
},
}) })
} }
} }
@@ -106,9 +141,9 @@ impl Format for GExprKind {
GExprKind::Call(f, x) => GExprKind::Call(f, x) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} ({1})"))) tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0} ({1})")))
.units([f.print(c).await, x.print(c).await]), .units([f.print(c).await, x.print(c).await]),
GExprKind::Lambda(arg, body) => GExprKind::Lambda(body) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{0}.{1}"))) tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("\\{1}")))
.units([arg.to_string().into(), body.print(c).await]), .units([body.print(c).await]),
GExprKind::Arg(arg) => arg.to_string().into(), GExprKind::Arg(arg) => arg.to_string().into(),
GExprKind::Seq(a, b) => GExprKind::Seq(a, b) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0}] {1}"))) tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0}] {1}")))
@@ -123,7 +158,11 @@ impl Format for GExprKind {
} }
} }
fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } } pub fn inherit(kind: GExprKind) -> GExpr { GExpr { pos: Pos::Inherit, kind } }
task_local! {
pub static CLOSURE_DEPTH: u64;
}
impl ToExpr for Sym { impl ToExpr for Sym {
async fn to_expr(self) -> Expr async fn to_expr(self) -> Expr
@@ -135,6 +174,8 @@ impl ToExpr for Sym {
/// Creates an expression from a new atom that we own. /// Creates an expression from a new atom that we own.
pub fn new_atom<A: AtomicFeatures>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.factory())) } pub fn new_atom<A: AtomicFeatures>(atom: A) -> GExpr { inherit(GExprKind::NewAtom(atom.factory())) }
pub fn slot(expr: Expr) -> GExpr { GExpr { pos: Pos::SlotTarget, kind: GExprKind::Slot(expr) } }
/// An expression which is only valid if a number of dependencies had already /// An expression which is only valid if a number of dependencies had already
/// been normalized /// been normalized
pub fn seq( pub fn seq(
@@ -155,17 +196,49 @@ pub fn seq(
}) })
} }
/// Argument bound by an enclosing [lam] or [dyn_lambda] #[derive(Debug, Clone, Copy)]
pub fn arg(n: u64) -> GExpr { inherit(GExprKind::Arg(n)) } pub enum ArgState {
Building,
/// A lambda expression. The difference from [dyn_lambda] is purely aesthetic Serializing { depth: u64 },
pub fn lam<const N: u64>(b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> { Ready,
dyn_lambda(N, b)
} }
/// A lambda expression. The difference from [lam] is purely aesthetic /// Argument bound by an enclosing [lam] or [dyn_lambda]
pub fn dyn_lambda(n: u64, b: impl ToExpr) -> ToExprFuture<impl Future<Output = GExpr>> { #[derive(Debug, Clone, Copy)]
ToExprFuture(async move { inherit(GExprKind::Lambda(n, Box::new(b.to_gen().await))) }) pub struct GenArg<'a>(*const RefCell<ArgState>, PhantomData<&'a ()>);
impl ToExpr for GenArg<'_> {
async fn to_gen(self) -> GExpr {
// SAFETY: Created from a Rc that lives as long as the lifetime arg, see [lam]
let state = unsafe { self.0.as_ref().unwrap() };
match (*state.borrow(), CLOSURE_DEPTH.try_with(|r| *r)) {
(ArgState::Serializing { .. }, Err(_)) =>
panic!("Lambda should have cleared up argstate alongside CLOSURE_DEPTH"),
(ArgState::Serializing { depth }, Ok(total)) => inherit(GExprKind::Arg(total - depth)),
(ArgState::Building, _) =>
panic!("Argument serialized before lambda. Likely an over-eager ToExpr impl"),
(ArgState::Ready, _) =>
unreachable!("The arg should never be available this long, the GenArg is a convenience"),
}
}
}
/// A lambda expression.
pub fn lam<'a>(
cb: impl for<'b> AsyncFnOnce(GenArg<'b>) -> GExpr + 'a,
) -> ToExprFuture<impl Future<Output = GExpr> + 'a> {
let state = Rc::new(RefCell::new(ArgState::Building));
ToExprFuture(async move {
let rank = CLOSURE_DEPTH.try_with(|r| *r + 1).unwrap_or(0);
match *state.borrow_mut() {
ref mut state @ ArgState::Building => *state = ArgState::Serializing { depth: rank },
ArgState::Serializing { .. } => panic!("Lambda serialized twice, found interrupted"),
ArgState::Ready => panic!("Lambda serialized twice"),
}
let gen_arg = GenArg(Rc::as_ptr(&state), PhantomData);
let ret = CLOSURE_DEPTH.scope(rank, async { cb(gen_arg).await.to_gen().await }).await;
mem::drop(state);
inherit(GExprKind::Lambda(Box::new(ret)))
})
} }
/// one or more items that are convertible to expressions. In practice, a /// one or more items that are convertible to expressions. In practice, a

View File

@@ -1,6 +1,6 @@
use std::num::NonZero; use std::num::NonZero;
use std::time::Duration;
use chrono::{DateTime, Utc};
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
@@ -24,11 +24,11 @@ impl AtomMethod for RunCommand {
} }
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding, Hierarchy)]
pub struct AsDuration; pub struct AsInstant;
impl Request for AsDuration { impl Request for AsInstant {
type Response = Duration; type Response = DateTime<Utc>;
} }
impl AtomMethod for AsDuration { impl AtomMethod for AsInstant {
const NAME: &str = "orchid::time::as_duration"; const NAME: &str = "orchid::time::as_duration";
} }

View File

@@ -13,7 +13,7 @@ use substack::Substack;
use task_local::task_local; use task_local::task_local;
use trait_set::trait_set; use trait_set::trait_set;
use crate::gen_expr::{GExpr, new_atom}; use crate::gen_expr::{GExpr, new_atom, serialize};
use crate::{BorrowedExprStore, Expr, ExprFunc, ExprHandle, Fun, ToExpr, api}; use crate::{BorrowedExprStore, Expr, ExprFunc, ExprHandle, Fun, ToExpr, api};
/// Tokens generated by lexers and parsers /// Tokens generated by lexers and parsers
@@ -31,7 +31,7 @@ impl TokenVariant<api::Expression> for GExpr {
async fn from_api(_: api::Expression, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self { async fn from_api(_: api::Expression, _: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
panic!("Received new expression from host") panic!("Received new expression from host")
} }
async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { self.serialize().await } async fn into_api(self, _: &mut Self::ToApiCtx<'_>) -> api::Expression { serialize(self).await }
} }
impl TokenVariant<api::ExprTicket> for Expr { impl TokenVariant<api::ExprTicket> for Expr {
@@ -193,7 +193,7 @@ impl LazyMemKind {
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind { pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MemberKind {
match self { match self {
Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)), Self::Lazy(lazy) => api::MemberKind::Lazy(add_lazy(ctx, lazy)),
Self::Const(c) => api::MemberKind::Const(c.serialize().await), Self::Const(c) => api::MemberKind::Const(serialize(c).await),
Self::Mod(members) => api::MemberKind::Module(api::Module { Self::Mod(members) => api::MemberKind::Module(api::Module {
members: stream(async |mut cx| { members: stream(async |mut cx| {
for m in members { for m in members {

View File

@@ -10,6 +10,7 @@ async-event = "0.2.1"
async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" } async-fn-stream = { version = "0.1.0", path = "../async-fn-stream" }
async-once-cell = "0.5.4" async-once-cell = "0.5.4"
bound = "0.6.0" bound = "0.6.0"
chrono = "0.4.44"
derive_destructure = "1.0.0" derive_destructure = "1.0.0"
futures = { version = "0.3.31", features = ["std"], default-features = false } futures = { version = "0.3.31", features = ["std"], default-features = false }
futures-locks = "0.7.1" futures-locks = "0.7.1"

View File

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

View File

@@ -7,40 +7,52 @@ use std::rc::Rc;
use async_event::Event; use async_event::Event;
use async_fn_stream::stream; use async_fn_stream::stream;
use chrono::{DateTime, Utc};
use futures::channel::mpsc; use futures::channel::mpsc;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use futures::stream::FuturesUnordered; use futures::stream::FuturesUnordered;
use futures::{SinkExt, StreamExt, select}; use futures::{SinkExt, StreamExt, select};
use never::Never; use never::Never;
use orchid_base::{OrcErrv, Receipt, ReqHandle, Sym}; use orchid_base::{OrcErrv, Receipt, ReqHandle, Sym, fmt, is, log, mk_errv};
use orchid_extension::{self as ox, AtomicFeatures as _}; use orchid_extension::{self as ox, AtomicFeatures as _, get_arg};
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::execute::{ExecCtx, ExecResult}; use crate::execute::{ExecCtx, ExecResult};
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder}; use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
use crate::extension::Extension;
use crate::inline::ext_inline;
use crate::system::System;
use crate::tree::Root; use crate::tree::Root;
/// Events internally recognized by this system sent through [CommandQueue]
enum Task {
RunCommand(Expr),
Sleep(DateTime<Utc>, Expr),
Exit,
}
struct CommandQueueState { struct CommandQueueState {
new: VecDeque<Expr>, new: VecDeque<Task>,
added: Rc<Event>, added: Rc<Event>,
wants_exit: bool,
ctx: Ctx, ctx: Ctx,
} }
/// Shared object serving as a communication point between the extension
/// [CmdSystemCtor] and the host toolkit [CmdRunner]
#[derive(Clone)] #[derive(Clone)]
struct CommandQueue(Rc<RefCell<CommandQueueState>>); struct CommandQueue(Rc<RefCell<CommandQueueState>>);
impl CommandQueue { impl CommandQueue {
fn new(ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self { fn new(ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
Self(Rc::new(RefCell::new(CommandQueueState { Self(Rc::new(RefCell::new(CommandQueueState {
new: init.into_iter().collect(), new: init.into_iter().map(Task::RunCommand).collect(),
added: Rc::default(), added: Rc::default(),
wants_exit: false,
ctx, ctx,
}))) })))
} }
pub fn push(&self, expr: Expr) { pub fn push(&self, task: Task) {
let was_empty = { let was_empty = {
let mut g = self.0.borrow_mut(); let mut g = self.0.borrow_mut();
g.new.push_back(expr); g.new.push_back(task);
g.new.len() == 1 g.new.len() == 1
}; };
if was_empty { if was_empty {
@@ -48,7 +60,7 @@ impl CommandQueue {
added.notify_one(); added.notify_one();
} }
} }
pub async fn get_new(&self) -> Expr { pub async fn get_new(&self) -> Task {
let added = { let added = {
let mut g = self.0.borrow_mut(); let mut g = self.0.borrow_mut();
if let Some(waiting) = g.new.pop_front() { if let Some(waiting) = g.new.pop_front() {
@@ -65,10 +77,13 @@ impl Debug for CommandQueue {
} }
} }
pub enum CmdResult { /// Events the embedder may want to be notified about
/// All command sequences settled pub enum CmdEvent {
/// All commands finished and there's nothing else to do
Settled, Settled,
/// Exit was requested explicitly by usercode /// Exit was requested explicitly by usercode. This request means that all
/// internal system state should be discarded, so if it is returned by the
/// [CmdRunner], no further commands are permitted
Exit, Exit,
/// Ran out of gas /// Ran out of gas
Gas, Gas,
@@ -89,35 +104,64 @@ pub struct CmdRunner {
queue: CommandQueue, queue: CommandQueue,
gas: Option<u64>, gas: Option<u64>,
interrupted: Option<ExecCtx>, interrupted: Option<ExecCtx>,
futures: FuturesUnordered<LocalBoxFuture<'static, Option<CmdResult>>>, system: System,
futures: FuturesUnordered<LocalBoxFuture<'static, Option<CmdEvent>>>,
} }
impl CmdRunner { impl CmdRunner {
pub async fn new(root: Root, ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self { pub async fn new(root: Root, ctx: Ctx, init: impl IntoIterator<Item = Expr>) -> Self {
Self { let queue = CommandQueue::new(ctx.clone(), init);
futures: FuturesUnordered::new(), let ext_builder = ox::ExtensionBuilder::new("orchid::cmd").system(CmdSystemCtor(queue.clone()));
gas: None, let extension = (Extension::new(ext_inline(ext_builder, ctx.clone()).await, ctx).await)
root, .expect("IO error on in-memory pipe");
interrupted: None, let system_ctor = (extension.system_ctors().find(|ctor| ctor.decl.name == "orchid::cmd"))
queue: CommandQueue::new(ctx, init), .expect("Missing command system ctor");
} let (cmd_root, system) = system_ctor.run(vec![]).await;
let root = root.merge(&cmd_root).await.expect("Could not merge command system into tree");
Self { futures: FuturesUnordered::new(), gas: None, root, interrupted: None, queue, system }
} }
#[must_use] #[must_use]
pub fn sys(&self) -> &System { &self.system }
#[must_use]
pub fn get_gas(&self) -> u64 { self.gas.expect("queried gas but no gas was set") } pub fn get_gas(&self) -> u64 { self.gas.expect("queried gas but no gas was set") }
pub fn set_gas(&mut self, gas: u64) { self.gas = Some(gas) } pub fn set_gas(&mut self, gas: u64) { self.gas = Some(gas) }
pub fn disable_gas(&mut self) { self.gas = None } pub fn disable_gas(&mut self) { self.gas = None }
pub async fn execute(&mut self) -> CmdResult { pub async fn execute(&mut self) -> CmdEvent {
let waiting_on_queue = RefCell::new(false); let waiting_on_queue = RefCell::new(false);
let (mut spawn, mut on_spawn) = mpsc::channel(1); let (mut spawn, mut on_spawn) = mpsc::channel::<LocalBoxFuture<Option<CmdEvent>>>(1);
let mut normalize_stream = pin!( let mut normalize_stream = pin!(
stream(async |mut h| { stream(async |mut h| {
loop { loop {
if self.queue.0.borrow().wants_exit {
h.emit(CmdResult::Exit).await;
break;
}
waiting_on_queue.replace(false); waiting_on_queue.replace(false);
let mut xctx = match self.interrupted.take() { let mut xctx = match self.interrupted.take() {
None => ExecCtx::new(self.root.clone(), self.queue.get_new().await).await, None => match self.queue.get_new().await {
Task::RunCommand(expr) => ExecCtx::new(self.root.clone(), expr).await,
Task::Sleep(until, expr) => {
let queue = self.queue.clone();
let ctx = queue.0.borrow_mut().ctx.clone();
spawn
.send(Box::pin(async move {
let delta = until - Utc::now();
match delta.to_std() {
Err(_) =>
writeln!(
log("debug"),
"Negative sleep found ({delta}), requeuing as instant"
)
.await,
Ok(delay) => ctx.sleep(delay).await,
};
queue.push(Task::RunCommand(expr));
None
}))
.await
.expect("Receiver stored in parent future");
continue;
},
Task::Exit => {
h.emit(CmdEvent::Exit).await;
break;
},
},
Some(xctx) => xctx, Some(xctx) => xctx,
}; };
waiting_on_queue.replace(true); waiting_on_queue.replace(true);
@@ -126,16 +170,16 @@ impl CmdRunner {
match res { match res {
ExecResult::Err(e, gas) => { ExecResult::Err(e, gas) => {
self.gas = gas; self.gas = gas;
h.emit(CmdResult::Err(e)).await; h.emit(CmdEvent::Err(e)).await;
}, },
ExecResult::Gas(exec) => { ExecResult::Gas(exec) => {
self.interrupted = Some(exec); self.interrupted = Some(exec);
h.emit(CmdResult::Gas).await; h.emit(CmdEvent::Gas).await;
}, },
ExecResult::Value(val, gas) => { ExecResult::Value(val, gas) => {
self.gas = gas; self.gas = gas;
let Some(atom) = val.as_atom().await else { let Some(atom) = val.as_atom().await else {
h.emit(CmdResult::NonCommand(val)).await; h.emit(CmdEvent::NonCommand(val)).await;
continue; continue;
}; };
let queue = self.queue.clone(); let queue = self.queue.clone();
@@ -143,11 +187,13 @@ impl CmdRunner {
spawn spawn
.send(Box::pin(async move { .send(Box::pin(async move {
match atom.ipc(ox::std_reqs::RunCommand).await { match atom.ipc(ox::std_reqs::RunCommand).await {
None => Some(CmdResult::NonCommand(val)), None => Some(CmdEvent::NonCommand(val)),
Some(None) => None, Some(None) => None,
Some(Some(expr)) => { Some(Some(expr)) => {
let from_api_cx = ExprFromApiCtx { ctx, sys: atom.api_ref().owner }; let from_api_cx = ExprFromApiCtx { ctx, sys: atom.api_ref().owner };
queue.push(Expr::from_api(expr, PathSetBuilder::new(), from_api_cx).await); queue.push(Task::RunCommand(
Expr::from_api(expr, PathSetBuilder::new(), from_api_cx).await,
));
None None
}, },
} }
@@ -161,17 +207,20 @@ impl CmdRunner {
.fuse() .fuse()
); );
loop { loop {
if self.queue.0.borrow().wants_exit {
break CmdResult::Exit;
}
let task = select!( let task = select!(
r_opt = self.futures.by_ref().next() => match r_opt { r_opt = self.futures.by_ref().next() => match r_opt {
Some(Some(r)) => break r, Some(Some(r)) => break r,
None if *waiting_on_queue.borrow() => break CmdResult::Settled, None if *waiting_on_queue.borrow() => break CmdEvent::Settled,
None | Some(None) => continue, None | Some(None) => continue,
}, },
r = normalize_stream.by_ref().next() => break r.expect("infinite stream"), r = normalize_stream.by_ref().next() => match r {
task = on_spawn.by_ref().next() => task.expect("sender moved into infinite stream"), None => break CmdEvent::Exit,
Some(r) => break r,
},
task = on_spawn.by_ref().next() => match task {
None => break CmdEvent::Exit,
Some(r) => r,
},
); );
self.futures.push(task) self.futures.push(task)
} }
@@ -189,9 +238,7 @@ impl ox::SystemCard for CmdSystemCard {
} }
#[derive(Debug)] #[derive(Debug)]
pub struct CmdSystemCtor { pub struct CmdSystemCtor(CommandQueue);
queue: CommandQueue,
}
impl ox::SystemCtor for CmdSystemCtor { impl ox::SystemCtor for CmdSystemCtor {
const NAME: &'static str = "orchid::cmd"; const NAME: &'static str = "orchid::cmd";
const VERSION: f64 = 0.1; const VERSION: f64 = 0.1;
@@ -199,7 +246,7 @@ impl ox::SystemCtor for CmdSystemCtor {
type Deps = (); type Deps = ();
type Instance = CmdSystemInst; type Instance = CmdSystemInst;
fn inst(&self, _: <Self::Deps as orchid_extension::DepDef>::Sat) -> Self::Instance { fn inst(&self, _: <Self::Deps as orchid_extension::DepDef>::Sat) -> Self::Instance {
CmdSystemInst { queue: self.queue.clone() } CmdSystemInst { queue: self.0.clone() }
} }
} }
@@ -217,12 +264,39 @@ impl ox::System for CmdSystemInst {
ox::tree::fun(true, "spawn", async |side: ox::Expr, cont: ox::Expr| { ox::tree::fun(true, "spawn", async |side: ox::Expr, cont: ox::Expr| {
ox::cmd(async move || { ox::cmd(async move || {
let queue = ox_get_queue(); let queue = ox_get_queue();
let side_xtk = side.serialize().await; let side_xtk = side.clone().serialize().await;
let mut g = queue.0.borrow_mut(); let mut g = queue.0.borrow_mut();
let host_ex = let host_ex =
g.ctx.exprs.take_expr(side_xtk).expect("Host could not locate leaked expr by ID "); g.ctx.exprs.take_expr(side_xtk).expect("Host could not locate leaked expr by ID ");
g.new.push_back(host_ex); g.new.push_back(Task::RunCommand(host_ex));
Some(cont) Some(cont.clone())
})
}),
ox::tree::cnst(true, "yield", ox::cmd(async || None::<Never>)),
ox::tree::cnst(
true,
"exit",
ox::cmd(async || {
ox_get_queue().push(Task::Exit);
None::<Never>
}),
),
ox::tree::fun(true, "sleep", async |until: ox::ForeignAtom, cont: ox::Expr| {
let Some(until) = until.call(ox::std_reqs::AsInstant).await else {
return ox::gen_expr::bot(mk_errv(
is("Not an instant").await,
format!("{} is not an instant", fmt(&until).await),
[get_arg(0).pos().await],
));
};
ox::cmd(async move || {
let queue = ox_get_queue();
let cont_xtk = cont.clone().serialize().await;
let mut g = queue.0.borrow_mut();
let host_ex =
g.ctx.exprs.take_expr(cont_xtk).expect("Host could not locate leaked expr by ID ");
g.new.push_back(Task::Sleep(until, host_ex));
None::<Never>
}) })
}), }),
]) ])

View File

@@ -18,7 +18,8 @@ pub trait JoinHandle {
/// It is guaranteed that the future will never be polled after this is called /// It is guaranteed that the future will never be polled after this is called
fn abort(&self); fn abort(&self);
/// take the future out of the task. If the return value /// take the future out of the task. If the return value
/// is dropped, the spawned future is also dropped /// is dropped, the spawned future is also dropped. The returned boxed future
/// will finish any sleep requested at spawn.
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>; fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
} }
@@ -30,6 +31,9 @@ pub trait Spawner {
/// exit while there are pending tasks to allow external communication /// exit while there are pending tasks to allow external communication
/// channels to cleanly shut down. /// channels to cleanly shut down.
fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>; fn spawn_obj(&self, delay: Duration, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
fn sleep(&self, delay: Duration) -> LocalBoxFuture<'static, ()> {
self.spawn_obj(delay, Box::pin(async move {})).join()
}
} }
pub struct CtxData { pub struct CtxData {
@@ -79,6 +83,9 @@ impl Ctx {
) -> Box<dyn JoinHandle> { ) -> Box<dyn JoinHandle> {
self.spawner.spawn_obj(delay, Box::pin(fut)) self.spawner.spawn_obj(delay, Box::pin(fut))
} }
/// Return after an amount of time has passed
#[must_use]
pub async fn sleep(&self, delay: Duration) { self.spawner.sleep(delay).await }
#[must_use] #[must_use]
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> { pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
self.systems.read().await.get(&id).and_then(WeakSystem::upgrade) self.systems.read().await.get(&id).and_then(WeakSystem::upgrade)

View File

@@ -27,7 +27,7 @@ use crate::ctx::{Ctx, JoinHandle};
use crate::dealias::{ChildError, ChildErrorKind, walk}; use crate::dealias::{ChildError, ChildErrorKind, walk};
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder}; use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
use crate::system::SystemCtor; use crate::system::SystemCtor;
use crate::tree::MemberKind; use crate::tree::MemKind;
pub struct ExtPort { pub struct ExtPort {
pub input: Pin<Box<dyn AsyncWrite>>, pub input: Pin<Box<dyn AsyncWrite>>,
@@ -54,17 +54,19 @@ pub struct ExtensionData {
strings: RefCell<HashSet<IStr>>, strings: RefCell<HashSet<IStr>>,
string_vecs: RefCell<HashSet<IStrv>>, string_vecs: RefCell<HashSet<IStrv>>,
/// Moved over from [ExtPort] to allow hooking to the extension's drop /// Moved over from [ExtPort] to allow hooking to the extension's drop
_drop_trigger: Box<dyn Any>, drop_trigger: Box<dyn Any>,
} }
impl Drop for ExtensionData { impl Drop for ExtensionData {
fn drop(&mut self) { fn drop(&mut self) {
let client = self.client.clone(); let client = self.client.clone();
let join_ext = self.join_ext.take().expect("Only called once in Drop"); let join_ext = self.join_ext.take().expect("Only called once in Drop");
let comm_cx = self.comm_cx.take().expect("Only used here"); let comm_cx = self.comm_cx.take().expect("Only used here");
let drop_trigger = std::mem::replace(&mut self.drop_trigger, Box::new(()));
stash(async move { stash(async move {
client.notify(api::HostExtNotif::Exit).await.unwrap(); client.notify(api::HostExtNotif::Exit).await.unwrap();
comm_cx.exit().await.unwrap(); comm_cx.exit().await.unwrap();
join_ext.join().await; join_ext.join().await;
std::mem::drop(drop_trigger);
}) })
} }
} }
@@ -158,12 +160,12 @@ impl Extension {
handle.reply(&vi, &markerv).await handle.reply(&vi, &markerv).await
}, },
}, },
api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => { api::ExtHostReq::Fwd(ref fw @ api::Fwd { ref target, ref method, ref body }) => {
let sys = let sys =
ctx.system_inst(atom.owner).await.expect("owner of live atom dropped"); ctx.system_inst(target.owner).await.expect("owner of live atom dropped");
let client = sys.client(); let client = sys.client();
let reply = client let reply = client
.request(api::FinalFwded(fw.0.clone(), *key, body.clone())) .request(api::Fwded(target.clone(), *method, body.clone()))
.await .await
.unwrap(); .unwrap();
handle.reply(fw, &reply).await handle.reply(fw, &reply).await
@@ -229,8 +231,8 @@ impl Extension {
let mut members = std::collections::HashMap::new(); let mut members = std::collections::HashMap::new();
for (k, v) in &module.members { for (k, v) in &module.members {
let kind = match v.kind(ctx.clone(), &root_data.consts).await { let kind = match v.kind(ctx.clone(), &root_data.consts).await {
MemberKind::Const => api::MemberInfoKind::Constant, MemKind::Const => api::MemberInfoKind::Constant,
MemberKind::Module(_) => api::MemberInfoKind::Module, MemKind::Module(_) => api::MemberInfoKind::Module,
}; };
members.insert(k.to_api(), api::MemberInfo { public: v.public, kind }); members.insert(k.to_api(), api::MemberInfo { public: v.public, kind });
} }
@@ -293,7 +295,7 @@ impl Extension {
client: Rc::new(client), client: Rc::new(client),
strings: RefCell::default(), strings: RefCell::default(),
string_vecs: RefCell::default(), string_vecs: RefCell::default(),
_drop_trigger: init.drop_trigger, drop_trigger: init.drop_trigger,
} }
}))) })))
} }

View File

@@ -262,8 +262,9 @@ pub async fn sys_lex(ctx: &mut LexCtx<'_>) -> Option<OrcRes<Vec<ParsTokTree>>> {
}) })
.await; .await;
match lx { match lx {
Err(e) => Err(e) => {
return Some(Err(errors.into_iter().fold(OrcErrv::from_api(e).await, |a, b| a + b))), return Some(Err(errors.into_iter().fold(OrcErrv::from_api(e).await, |a, b| a + b)));
},
Ok(Some(lexed)) => { Ok(Some(lexed)) => {
ctx.set_pos(lexed.pos); ctx.set_pos(lexed.pos);
let mut stable_trees = Vec::new(); let mut stable_trees = Vec::new();

View File

@@ -1,9 +1,9 @@
use orchid_api as api; use orchid_api as api;
pub mod atom; pub mod atom;
pub mod ctx;
#[cfg(feature = "orchid-extension")] #[cfg(feature = "orchid-extension")]
pub mod cmd_system; pub mod cmd_system;
pub mod ctx;
pub mod dealias; pub mod dealias;
#[cfg(feature = "tokio")] #[cfg(feature = "tokio")]
pub mod dylib; pub mod dylib;

View File

@@ -8,7 +8,7 @@ use substack::Substack;
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::expr::Expr; use crate::expr::Expr;
use crate::parsed::{Item, ItemKind, ParsedMember, ParsedMemberKind, ParsedModule}; use crate::parsed::{Item, ItemKind, ParsedMemKind, ParsedMember, ParsedModule};
use crate::system::System; use crate::system::System;
type ParsSnippet<'a> = Snippet<'a, Expr, Expr>; type ParsSnippet<'a> = Snippet<'a, Expr, Expr>;
@@ -101,7 +101,7 @@ pub async fn parse_exportable_item<'a>(
) -> OrcRes<Vec<Item>> { ) -> OrcRes<Vec<Item>> {
let kind = if discr == is("mod").await { let kind = if discr == is("mod").await {
let (name, body) = parse_module(ctx, path, tail).await?; let (name, body) = parse_module(ctx, path, tail).await?;
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::Mod(body) }) ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemKind::Mod(body) })
} else if let Some(parser) = ctx.systems().find_map(|s| s.get_parser(discr.clone())) { } else if let Some(parser) = ctx.systems().find_map(|s| s.get_parser(discr.clone())) {
return parser return parser
.parse(ctx, path, tail.to_vec(), exported, comments, &mut async |stack, lines| { .parse(ctx, path, tail.to_vec(), exported, comments, &mut async |stack, lines| {

View File

@@ -53,10 +53,10 @@ impl Format for Item {
let item_text = match &self.kind { let item_text = match &self.kind {
ItemKind::Import(i) => format!("import {i}").into(), ItemKind::Import(i) => format!("import {i}").into(),
ItemKind::Member(mem) => match &mem.kind { ItemKind::Member(mem) => match &mem.kind {
ParsedMemberKind::Const(_, sys) => ParsedMemKind::Const(_, sys) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}"))) tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}")))
.units([mem.name.to_string().into(), sys.print(c).await]), .units([mem.name.to_string().into(), sys.print(c).await]),
ParsedMemberKind::Mod(module) => ParsedMemKind::Mod(module) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}"))) tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
.units([mem.name.to_string().into(), module.print(c).boxed_local().await]), .units([mem.name.to_string().into(), module.print(c).boxed_local().await]),
}, },
@@ -69,12 +69,12 @@ impl Format for Item {
pub struct ParsedMember { pub struct ParsedMember {
pub name: IStr, pub name: IStr,
pub exported: bool, pub exported: bool,
pub kind: ParsedMemberKind, pub kind: ParsedMemKind,
} }
impl ParsedMember { impl ParsedMember {
#[must_use] #[must_use]
pub fn name(&self) -> IStr { self.name.clone() } pub fn name(&self) -> IStr { self.name.clone() }
pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemberKind>) -> Self { pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemKind>) -> Self {
Self { exported, name, kind: kind.into() } Self { exported, name, kind: kind.into() }
} }
} }
@@ -101,11 +101,11 @@ impl fmt::Debug for ParsedExpr {
} }
#[derive(Debug)] #[derive(Debug)]
pub enum ParsedMemberKind { pub enum ParsedMemKind {
Const(api::ParsedConstId, System), Const(api::ParsedConstId, System),
Mod(ParsedModule), Mod(ParsedModule),
} }
impl From<ParsedModule> for ParsedMemberKind { impl From<ParsedModule> for ParsedMemKind {
fn from(value: ParsedModule) -> Self { Self::Mod(value) } fn from(value: ParsedModule) -> Self { Self::Mod(value) }
} }
#[derive(Debug, Default)] #[derive(Debug, Default)]
@@ -137,7 +137,7 @@ impl ParsedModule {
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None }) .filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
} }
pub fn default_item(self, name: IStr, sr: SrcRange) -> Item { pub fn default_item(self, name: IStr, sr: SrcRange) -> Item {
let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) }; let mem = ParsedMember { exported: true, name, kind: ParsedMemKind::Mod(self) };
Item { comments: vec![], sr, kind: ItemKind::Member(mem) } Item { comments: vec![], sr, kind: ItemKind::Member(mem) }
} }
} }
@@ -157,8 +157,8 @@ impl Tree for ParsedModule {
.find(|m| m.name == key) .find(|m| m.name == key)
{ {
match &member.kind { match &member.kind {
ParsedMemberKind::Const(..) => return ChildResult::Err(ChildErrorKind::Constant), ParsedMemKind::Const(..) => return ChildResult::Err(ChildErrorKind::Constant),
ParsedMemberKind::Mod(m) => return ChildResult::Ok(m), ParsedMemKind::Mod(m) => return ChildResult::Ok(m),
} }
} }
ChildResult::Err(ChildErrorKind::Missing) ChildResult::Err(ChildErrorKind::Missing)

View File

@@ -9,7 +9,7 @@ use crate::expr::ExprFromApiCtx;
use crate::expr_store::ExprStore; use crate::expr_store::ExprStore;
use crate::parse::HostParseCtx; use crate::parse::HostParseCtx;
use crate::parsed::{ use crate::parsed::{
Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule, tt_to_api, Item, ItemKind, ParsTokTree, ParsedMemKind, ParsedMember, ParsedModule, tt_to_api,
}; };
use crate::system::System; use crate::system::System;
@@ -88,11 +88,11 @@ async fn conv(
let mkind = match kind { let mkind = match kind {
api::ParsedMemberKind::Module { lines, use_prelude } => { api::ParsedMemberKind::Module { lines, use_prelude } => {
let items = conv(lines, mem_path, callback, ctx).boxed_local().await?; let items = conv(lines, mem_path, callback, ctx).boxed_local().await?;
ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items)) ParsedMemKind::Mod(ParsedModule::new(use_prelude, items))
}, },
api::ParsedMemberKind::Constant(cid) => { api::ParsedMemberKind::Constant(cid) => {
ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await); ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await);
ParsedMemberKind::Const(cid, ctx.sys.clone()) ParsedMemKind::Const(cid, ctx.sys.clone())
}, },
}; };
items.push(Item { items.push(Item {

View File

@@ -21,11 +21,11 @@ use crate::api;
use crate::ctx::Ctx; use crate::ctx::Ctx;
use crate::dealias::{ChildErrorKind, Tree, absolute_path, resolv_glob, walk}; use crate::dealias::{ChildErrorKind, Tree, absolute_path, resolv_glob, walk};
use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder}; use crate::expr::{Expr, ExprFromApiCtx, PathSetBuilder};
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule}; use crate::parsed::{ItemKind, ParsedMemKind, ParsedModule};
use crate::system::System; use crate::system::System;
pub struct RootData { pub struct RootData {
pub root: Module, pub root: Mod,
pub consts: MemoMap<Sym, Expr>, pub consts: MemoMap<Sym, Expr>,
pub ctx: Ctx, pub ctx: Ctx,
} }
@@ -34,17 +34,13 @@ pub struct Root(pub Rc<RwLock<RootData>>);
impl Root { impl Root {
#[must_use] #[must_use]
pub fn new(ctx: Ctx) -> Self { pub fn new(ctx: Ctx) -> Self {
Root(Rc::new(RwLock::new(RootData { Root(Rc::new(RwLock::new(RootData { root: Mod::default(), consts: MemoMap::default(), ctx })))
root: Module::default(),
consts: MemoMap::default(),
ctx,
})))
} }
#[must_use] #[must_use]
pub async fn from_api(api: api::Module, sys: &System) -> Self { pub async fn from_api(api: api::Module, sys: &System) -> Self {
let consts = MemoMap::new(); let consts = MemoMap::new();
let mut tfac = TreeFromApiCtx { consts: &consts, path: iv(&[][..]).await, sys }; let mut tfac = TreeFromApiCtx { consts: &consts, path: iv(&[][..]).await, sys };
let root = Module::from_api(api, &mut tfac).await; let root = Mod::from_api(api, &mut tfac).await;
Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() }))) Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() })))
} }
pub async fn merge(&self, new: &Root) -> Result<Self, MergeErr> { pub async fn merge(&self, new: &Root) -> Result<Self, MergeErr> {
@@ -71,14 +67,14 @@ impl Root {
root: &this.root, root: &this.root,
ctx: &this.ctx, ctx: &this.ctx,
}; };
let mut module = Module::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await; let mut module = Mod::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await;
for step in pars_prefix.iter().rev() { for step in pars_prefix.iter().rev() {
let kind = OnceCell::from(MemberKind::Module(module)); let kind = OnceCell::from(MemKind::Module(module));
let members = HashMap::from([( let members = HashMap::from([(
step.clone(), step.clone(),
Rc::new(Member { public: true, lazy: RefCell::new(None), kind }), Rc::new(Member { public: true, lazy: RefCell::new(None), kind }),
)]); )]);
module = Module { imports: HashMap::new(), members } module = Mod { imports: HashMap::new(), members }
} }
let root = (this.root.merge(&module, this.ctx.clone(), &consts).await) let root = (this.root.merge(&module, this.ctx.clone(), &consts).await)
.expect("Merge conflict between parsed and existing module"); .expect("Merge conflict between parsed and existing module");
@@ -159,11 +155,11 @@ pub struct ResolvedImport {
} }
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct Module { pub struct Mod {
pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>, pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>,
pub members: HashMap<IStr, Rc<Member>>, pub members: HashMap<IStr, Rc<Member>>,
} }
impl Module { impl Mod {
#[must_use] #[must_use]
pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self { pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self {
let mut members = HashMap::new(); let mut members = HashMap::new();
@@ -178,11 +174,11 @@ impl Module {
let cx = ExprFromApiCtx { ctx: ctx.sys.ctx().clone(), sys: ctx.sys.id() }; let cx = ExprFromApiCtx { ctx: ctx.sys.ctx().clone(), sys: ctx.sys.id() };
let expr = Expr::from_api(val, PathSetBuilder::new(), cx).await; let expr = Expr::from_api(val, PathSetBuilder::new(), cx).await;
ctx.consts.insert(name.clone(), expr); ctx.consts.insert(name.clone(), expr);
(None, Some(MemberKind::Const)) (None, Some(MemKind::Const))
}, },
api::MemberKind::Module(m) => { api::MemberKind::Module(m) => {
let m = Self::from_api(m, &mut ctx.push(mem_name.clone()).await).boxed_local().await; let m = Self::from_api(m, &mut ctx.push(mem_name.clone()).await).boxed_local().await;
(None, Some(MemberKind::Module(m))) (None, Some(MemKind::Module(m)))
}, },
}; };
members.insert( members.insert(
@@ -305,7 +301,7 @@ impl Module {
match &item.kind { match &item.kind {
ItemKind::Member(mem) => { ItemKind::Member(mem) => {
let path = path.to_vname().suffix([mem.name.clone()]).to_sym().await; let path = path.to_vname().suffix([mem.name.clone()]).to_sym().await;
let kind = OnceCell::from(MemberKind::from_parsed(&mem.kind, path.clone(), ctx).await); let kind = OnceCell::from(MemKind::from_parsed(&mem.kind, path.clone(), ctx).await);
members.insert( members.insert(
mem.name.clone(), mem.name.clone(),
Rc::new(Member { kind, lazy: RefCell::default(), public: mem.exported }), Rc::new(Member { kind, lazy: RefCell::default(), public: mem.exported }),
@@ -314,14 +310,14 @@ impl Module {
ItemKind::Import(_) => (), ItemKind::Import(_) => (),
} }
} }
Module { imports, members } Mod { imports, members }
} }
pub async fn merge( pub async fn merge(
&self, &self,
other: &Module, other: &Mod,
ctx: Ctx, ctx: Ctx,
consts: &MemoMap<Sym, Expr>, consts: &MemoMap<Sym, Expr>,
) -> Result<Module, MergeErr> { ) -> Result<Mod, MergeErr> {
if !self.imports.is_empty() || !other.imports.is_empty() { if !self.imports.is_empty() || !other.imports.is_empty() {
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Imports }); return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Imports });
} }
@@ -335,7 +331,7 @@ impl Module {
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Visibility }); return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Visibility });
} }
match (own.kind(ctx.clone(), consts).await, mem.kind(ctx.clone(), consts).await) { match (own.kind(ctx.clone(), consts).await, mem.kind(ctx.clone(), consts).await) {
(MemberKind::Module(own_sub), MemberKind::Module(sub)) => { (MemKind::Module(own_sub), MemKind::Module(sub)) => {
match own_sub.merge(sub, ctx.clone(), consts).boxed_local().await { match own_sub.merge(sub, ctx.clone(), consts).boxed_local().await {
Ok(module) => { Ok(module) => {
members.insert( members.insert(
@@ -343,7 +339,7 @@ impl Module {
Rc::new(Member { Rc::new(Member {
lazy: RefCell::new(None), lazy: RefCell::new(None),
public: own.public, public: own.public,
kind: OnceCell::from(MemberKind::Module(module)), kind: OnceCell::from(MemKind::Module(module)),
}), }),
); );
}, },
@@ -361,7 +357,7 @@ impl Module {
slot.insert(mem.clone()); slot.insert(mem.clone());
} }
} }
Ok(Module { imports: HashMap::new(), members }) Ok(Mod { imports: HashMap::new(), members })
} }
} }
@@ -380,13 +376,13 @@ pub enum MergeErrKind {
pub struct FromParsedCtx<'a> { pub struct FromParsedCtx<'a> {
pars_prefix: Sym, pars_prefix: Sym,
pars_root: &'a ParsedModule, pars_root: &'a ParsedModule,
root: &'a Module, root: &'a Mod,
ctx: &'a Ctx, ctx: &'a Ctx,
consts: &'a MemoMap<Sym, Expr>, consts: &'a MemoMap<Sym, Expr>,
deferred_consts: &'a mut Vec<(Sym, api::SysId, api::ParsedConstId)>, deferred_consts: &'a mut Vec<(Sym, api::SysId, api::ParsedConstId)>,
} }
impl Tree for Module { impl Tree for Mod {
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>); type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
async fn child( async fn child(
&self, &self,
@@ -401,8 +397,8 @@ impl Tree for Module {
return Err(ChildErrorKind::Private); return Err(ChildErrorKind::Private);
} }
match &member.kind(ctx.clone(), consts).await { match &member.kind(ctx.clone(), consts).await {
MemberKind::Module(m) => Ok(m), MemKind::Module(m) => Ok(m),
MemberKind::Const => Err(ChildErrorKind::Constant), MemKind::Const => Err(ChildErrorKind::Constant),
} }
} }
fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> { fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> {
@@ -413,11 +409,11 @@ impl Tree for Module {
pub struct Member { pub struct Member {
pub public: bool, pub public: bool,
pub lazy: RefCell<Option<LazyMemberHandle>>, pub lazy: RefCell<Option<LazyMemberHandle>>,
pub kind: OnceCell<MemberKind>, pub kind: OnceCell<MemKind>,
} }
impl Member { impl Member {
#[must_use] #[must_use]
pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> &'a MemberKind { pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> &'a MemKind {
(self.kind.get_or_init(async { (self.kind.get_or_init(async {
let handle = self.lazy.borrow_mut().take().expect("If kind is uninit, lazy must be Some"); let handle = self.lazy.borrow_mut().take().expect("If kind is uninit, lazy must be Some");
handle.run(ctx, consts).await handle.run(ctx, consts).await
@@ -426,20 +422,20 @@ impl Member {
} }
} }
pub enum MemberKind { pub enum MemKind {
Const, Const,
Module(Module), Module(Mod),
} }
impl MemberKind { impl MemKind {
#[must_use] #[must_use]
async fn from_parsed(parsed: &ParsedMemberKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self { async fn from_parsed(parsed: &ParsedMemKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self {
match parsed { match parsed {
ParsedMemberKind::Const(id, sys) => { ParsedMemKind::Const(id, sys) => {
ctx.deferred_consts.push((path, sys.id(), *id)); ctx.deferred_consts.push((path, sys.id(), *id));
MemberKind::Const MemKind::Const
}, },
ParsedMemberKind::Mod(m) => ParsedMemKind::Mod(m) =>
MemberKind::Module(Module::from_parsed(m, path, ctx).boxed_local().await), MemKind::Module(Mod::from_parsed(m, path, ctx).boxed_local().await),
} }
} }
} }
@@ -452,7 +448,7 @@ pub struct LazyMemberHandle {
} }
impl LazyMemberHandle { impl LazyMemberHandle {
#[must_use] #[must_use]
pub async fn run(mut self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> MemberKind { pub async fn run(mut self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> MemKind {
let sys = ctx.system_inst(self.sys).await.expect("Missing system for lazy member"); let sys = ctx.system_inst(self.sys).await.expect("Missing system for lazy member");
match sys.get_tree(self.id).await { match sys.get_tree(self.id).await {
api::MemberKind::Const(c) => { api::MemberKind::Const(c) => {
@@ -460,12 +456,12 @@ impl LazyMemberHandle {
let expr = Expr::from_api(c, PathSetBuilder::new(), ctx).await; let expr = Expr::from_api(c, PathSetBuilder::new(), ctx).await;
let (.., path) = self.destructure(); let (.., path) = self.destructure();
consts.insert(path, expr); consts.insert(path, expr);
MemberKind::Const MemKind::Const
}, },
api::MemberKind::Module(m) => { api::MemberKind::Module(m) => {
let (.., path) = self.destructure(); let (.., path) = self.destructure();
MemberKind::Module( MemKind::Module(
Module::from_api(m, &mut TreeFromApiCtx { sys: &sys, consts, path: path.tok() }).await, Mod::from_api(m, &mut TreeFromApiCtx { sys: &sys, consts, path: path.tok() }).await,
) )
}, },
api::MemberKind::Lazy(id) => { api::MemberKind::Lazy(id) => {

View File

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

View File

@@ -4,16 +4,16 @@ use futures::{FutureExt, StreamExt, stream};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::{ use orchid_base::{
Comment, OrcRes, Paren, Parsed, Snippet, Sym, expect_tok, is, report, sym, token_errv, Comment, OrcRes, Paren, Parsed, Snippet, Sym, expect_tok, fmt, is, report, token_errv,
try_pop_no_fluff, with_reporter, try_pop_no_fluff, with_reporter,
}; };
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::{ use orchid_extension::{
ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr, ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser, TAtom, TryFromExpr,
}; };
use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq}; use crate::macros::mactree::{MacTok, MacTree, MacTreeSeq};
use crate::macros::ph_lexer::PhAtom; use crate::macros::ph_lexer::PhAtom;
use crate::macros::resolve::{ArgStack, resolve};
#[derive(Default)] #[derive(Default)]
pub struct LetLine; pub struct LetLine;
@@ -35,10 +35,16 @@ impl Parser for LetLine {
}; };
let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?; let Parsed { tail, .. } = expect_tok(tail, is("=").await).await?;
let aliased = parse_tokv(tail).await; let aliased = parse_tokv(tail).await;
eprintln!("Parsed tokv: {}", fmt(&aliased).await);
Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| { Ok(vec![ParsedLine::cnst(&line.sr(), &comments, exported, name, async move |ctx| {
let macro_input = let macro_input =
MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos()); MacTok::S(Paren::Round, with_reporter(dealias_mac_v(&aliased, &ctx)).await?).at(sr.pos());
Ok(call(sym!(macros::resolve), new_atom(macro_input))) eprintln!("Dealiased tokv: {}", fmt(&macro_input).await);
let gx = resolve(macro_input, ArgStack::end()).await;
eprintln!("resolves to: {}", fmt(&gx).await);
let x = gx.create().await;
eprintln!("created as: {}", fmt(&x).await);
Ok(x)
})]) })])
} }
} }
@@ -63,6 +69,7 @@ pub async fn dealias_mac_v(aliased: &MacTreeSeq, ctx: &ConstCtx) -> MacTreeSeq {
pub async fn parse_tokv(line: PSnippet<'_>) -> MacTreeSeq { pub async fn parse_tokv(line: PSnippet<'_>) -> MacTreeSeq {
if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) { if let Some((idx, arg)) = line.iter().enumerate().find_map(|(i, x)| Some((i, x.as_lambda()?))) {
eprintln!("Found a lambda while parsing tokv");
let (head, lambda) = line.split_at(idx as u32); let (head, lambda) = line.split_at(idx as u32);
let (_, body) = lambda.split_first().unwrap(); let (_, body) = lambda.split_first().unwrap();
let body = parse_tokv(body).boxed_local().await; let body = parse_tokv(body).boxed_local().await;

View File

@@ -1,83 +1,79 @@
use orchid_base::sym; use orchid_base::sym;
use orchid_extension::TAtom;
use orchid_extension::gen_expr::{call, new_atom}; use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{TAtom, exec};
use crate::macros::mactree::MacTree; use crate::macros::mactree::MacTree;
use crate::macros::resolve::resolve; use crate::macros::resolve::{ArgStack, resolve};
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::{HomoTpl, UntypedTuple}; use crate::{HomoTpl, UntypedTuple};
pub async fn gen_macro_lib() -> Vec<GenMember> { pub async fn gen_macro_lib() -> Vec<GenMember> {
prefix("macros", [ prefix("macros", [
fun(true, "resolve", async |tpl: TAtom<MacTree>| resolve(tpl.own().await).await), fun(true, "resolve", async |tpl: TAtom<MacTree>| {
resolve(tpl.own().await, ArgStack::end()).await
}),
prefix("common", [ prefix("common", [
build_macro(None, ["..", "_", "="]).finish(), build_macro(None, ["..", "_", "="]).finish(),
build_macro(Some(1), ["+"]) build_macro(Some(1), ["+"])
.rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::+ "...$" rhs 0), [async |cx, [lhs, rhs]| {
call(sym!(std::ops::add::resolve), (resolve(lhs).await, resolve(rhs).await)).await call(sym!(std::ops::add::resolve), (cx.recur(lhs), cx.recur(rhs))).await
}]) }])
.finish(), .finish(),
build_macro(Some(1), ["-"]) build_macro(Some(1), ["-"])
.rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::- "...$" rhs 0), [async |cx, [lhs, rhs]| {
call(sym!(std::ops::sub::resolve), (resolve(lhs).await, resolve(rhs).await)).await call(sym!(std::ops::sub::resolve), (cx.recur(lhs), cx.recur(rhs))).await
}]) }])
.finish(), .finish(),
build_macro(Some(2), ["*"]) build_macro(Some(2), ["*"])
.rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::* "...$" rhs 0), [async |cx, [lhs, rhs]| {
call(sym!(std::ops::mul::resolve), (resolve(lhs).await, resolve(rhs).await)).await call(sym!(std::ops::mul::resolve), (cx.recur(lhs), cx.recur(rhs))).await
}]) }])
.finish(), .finish(),
build_macro(Some(2), ["/"]) build_macro(Some(2), ["/"])
.rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::/ "...$" rhs 0), [async |cx, [lhs, rhs]| {
call(sym!(std::ops::div::resolve), (resolve(lhs).await, resolve(rhs).await)).await call(sym!(std::ops::div::resolve), (cx.recur(lhs), cx.recur(rhs))).await
}]) }])
.finish(), .finish(),
build_macro(Some(2), ["%"]) build_macro(Some(2), ["%"])
.rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::% "...$" rhs 0), [async |cx, [lhs, rhs]| {
call(sym!(std::ops::mod::resolve), (resolve(lhs).await, resolve(rhs).await)).await call(sym!(std::ops::mod::resolve), (cx.recur(lhs), cx.recur(rhs))).await
}]) }])
.finish(), .finish(),
build_macro(Some(3), ["."]) build_macro(Some(3), ["."])
.rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |[lhs, rhs]| { .rule(mactreev!("...$" lhs 1 macros::common::. "...$" rhs 0), [async |cx, [lhs, rhs]| {
call(sym!(std::ops::get::resolve), (resolve(lhs).await, resolve(rhs).await)).await call(sym!(std::ops::get::resolve), (cx.recur(lhs), cx.recur(rhs))).await
}]) }])
.finish(), .finish(),
build_macro(None, ["comma_list", ","]) build_macro(None, ["comma_list", ","])
.rule( .rule(
mactreev!(macros::common::comma_list ( "...$" head 0 macros::common::, "...$" tail 1)), mactreev!(macros::common::comma_list ( "...$" head 0 macros::common::, "...$" tail 1)),
[async |[head, tail]| { [async |mut cx, [head, tail]| {
exec(async |mut h| { let mut tail: HomoTpl<TAtom<MacTree>> =
let recur = resolve(mactree!(macros::common::comma_list "push" tail ;)).await; cx.exec(cx.recur(mactree!(macros::common::comma_list "push" tail ;))).await?;
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?; tail.0.insert(0, cx.exec(new_atom(head)).await?);
tail.0.insert(0, h.exec(new_atom(head)).await?); Ok(tail)
Ok(tail)
})
.await
}], }],
) )
.rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [async |[tail]| { .rule(mactreev!(macros::common::comma_list ( "...$" final_tail 0 )), [
HomoTpl(vec![new_atom(tail)]) async |_cx, [tail]| HomoTpl(vec![new_atom(tail)]),
}]) ])
.rule(mactreev!(macros::common::comma_list()), [async |[]| UntypedTuple(Vec::new())]) .rule(mactreev!(macros::common::comma_list()), [async |_cx, []| UntypedTuple(Vec::new())])
.finish(), .finish(),
build_macro(None, ["semi_list", ";"]) build_macro(None, ["semi_list", ";"])
.rule( .rule(
mactreev!(macros::common::semi_list ( "...$" head 0 macros::common::; "...$" tail 1)), mactreev!(macros::common::semi_list ( "...$" head 0 macros::common::; "...$" tail 1)),
[async |[head, tail]| { [async |mut cx, [head, tail]| {
exec(async |mut h| { let mut tail: HomoTpl<TAtom<MacTree>> =
let recur = resolve(mactree!(macros::common::semi_list "push" tail ;)).await; cx.exec(cx.recur(mactree!(macros::common::semi_list "push" tail ;))).await?;
let mut tail = h.exec::<HomoTpl<TAtom<MacTree>>>(recur).await?; tail.0.insert(0, cx.exec(new_atom(head)).await?);
tail.0.insert(0, h.exec(new_atom(head)).await?); Ok(tail)
Ok(tail)
})
.await
}], }],
) )
.rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [async |[tail]| { .rule(mactreev!(macros::common::semi_list ( "...$" final_tail 0 )), [
HomoTpl(vec![new_atom(tail)]) async |_cx, [tail]| HomoTpl(vec![new_atom(tail)]),
}]) ])
.rule(mactreev!(macros::common::semi_list()), [async |[]| UntypedTuple(Vec::new())]) .rule(mactreev!(macros::common::semi_list()), [async |_cx, []| UntypedTuple(Vec::new())])
.finish(), .finish(),
]), ]),
]) ])

View File

@@ -8,10 +8,8 @@ use orchid_base::{
Comment, OrcRes, Paren, Parsed, Snippet, Token, clone, expect_end, expect_tok, is, line_items, Comment, OrcRes, Paren, Parsed, Snippet, Token, clone, expect_end, expect_tok, is, line_items,
mk_errv, report, sym, token_errv, try_pop_no_fluff, with_reporter, mk_errv, report, sym, token_errv, try_pop_no_fluff, with_reporter,
}; };
use orchid_extension::TAtom;
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::gen_expr::{call, new_atom}; use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser}; use orchid_extension::{PSnippet, ParsCtx, ParsedLine, Parser, TAtom, ToExpr, TryFromExpr};
use crate::macros::let_line::{dealias_mac_v, parse_tokv}; use crate::macros::let_line::{dealias_mac_v, parse_tokv};
use crate::macros::macro_value::{Macro, MacroData, Rule}; use crate::macros::macro_value::{Macro, MacroData, Rule};

View File

@@ -13,6 +13,7 @@ use crate::macros::macro_value::Macro;
use crate::macros::mactree_lexer::MacTreeLexer; use crate::macros::mactree_lexer::MacTreeLexer;
use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib}; use crate::macros::match_macros::{MatcherAtom, gen_match_macro_lib};
use crate::macros::ph_lexer::{PhAtom, PhLexer}; use crate::macros::ph_lexer::{PhAtom, PhLexer};
use crate::macros::resolve::ArgStack;
use crate::macros::stdlib::gen_std_macro_lib; use crate::macros::stdlib::gen_std_macro_lib;
use crate::macros::utils::MacroBodyArgCollector; use crate::macros::utils::MacroBodyArgCollector;
use crate::{MacTree, StdSystem}; use crate::{MacTree, StdSystem};
@@ -38,6 +39,7 @@ impl SystemCard for MacroSystem {
Some(PhAtom::ops()), Some(PhAtom::ops()),
Some(MacroBodyArgCollector::ops()), Some(MacroBodyArgCollector::ops()),
Some(MatcherAtom::ops()), Some(MatcherAtom::ops()),
Some(ArgStack::ops()),
] ]
} }
} }

View File

@@ -2,10 +2,8 @@ use std::borrow::Cow;
use std::rc::Rc; use std::rc::Rc;
use never::Never; use never::Never;
use orchid_base::IStr; use orchid_base::{IStr, Sym};
use orchid_base::Sym; use orchid_extension::{Atomic, OwnedAtom, OwnedVariant};
use orchid_extension::Atomic;
use orchid_extension::{OwnedAtom, OwnedVariant};
use crate::macros::mactree::MacTreeSeq; use crate::macros::mactree::MacTreeSeq;
use crate::macros::rule::matcher::Matcher; use crate::macros::rule::matcher::Matcher;

View File

@@ -9,9 +9,7 @@ use orchid_api_derive::Coding;
use orchid_base::{ use orchid_base::{
FmtCtx, FmtUnit, Format, IStr, OrcErrv, Paren, Pos, Sym, Variants, indent, tl_cache, FmtCtx, FmtUnit, Format, IStr, OrcErrv, Paren, Pos, Sym, Variants, indent, tl_cache,
}; };
use orchid_extension::Atomic; use orchid_extension::{Atomic, Expr, OwnedAtom, OwnedVariant};
use orchid_extension::Expr;
use orchid_extension::{OwnedAtom, OwnedVariant};
fn union_rc_sets(seq: impl IntoIterator<Item = Rc<HashSet<Sym>>>) -> Rc<HashSet<Sym>> { fn union_rc_sets(seq: impl IntoIterator<Item = Rc<HashSet<Sym>>>) -> Rc<HashSet<Sym>> {
let mut acc = Rc::<HashSet<Sym>>::default(); let mut acc = Rc::<HashSet<Sym>>::default();
@@ -172,9 +170,10 @@ impl MacTok {
impl Format for MacTok { impl Format for MacTok {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
match self { match self {
Self::Value(v) => v.print(c).await, Self::Value(v) => tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("$({0b})")))
.units([v.print(c).await]),
Self::Lambda(arg, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default() Self::Lambda(arg, b) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
.unbounded("\\{0} {1l}") // .unbounded("\\{0} {1l}")
.bounded("(\\{0} {1b})"))) .bounded("(\\{0} {1b})")))
.units([arg.print(c).boxed_local().await, b.print(c).await]), .units([arg.print(c).boxed_local().await, b.print(c).await]),
Self::Name(n) => format!("{n}").into(), Self::Name(n) => format!("{n}").into(),

View File

@@ -2,12 +2,10 @@ use std::ops::RangeInclusive;
use futures::FutureExt; use futures::FutureExt;
use itertools::chain; use itertools::chain;
use orchid_base::Paren; use orchid_base::{OrcRes, PARENS, Paren, is, mk_errv};
use orchid_base::{OrcRes, PARENS, is, mk_errv};
use orchid_extension::gen_expr::new_atom; use orchid_extension::gen_expr::new_atom;
use orchid_extension::{LexContext, Lexer, err_not_applicable};
use orchid_extension::p_tree2gen;
use orchid_extension::tree::{GenTok, GenTokTree, x_tok}; use orchid_extension::tree::{GenTok, GenTokTree, x_tok};
use orchid_extension::{LexContext, Lexer, err_not_applicable, p_tree2gen};
use crate::macros::instantiate_tpl::InstantiateTplCall; use crate::macros::instantiate_tpl::InstantiateTplCall;
use crate::macros::let_line::parse_tok; use crate::macros::let_line::parse_tok;

View File

@@ -6,14 +6,12 @@ use futures::{Stream, StreamExt, stream};
use never::Never; use never::Never;
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_base::{OrcRes, Sym, fmt, is, mk_errv, sym}; use orchid_base::{OrcRes, Sym, fmt, is, mk_errv, sym};
use orchid_extension::ToExpr; use orchid_extension::gen_expr::{bot, call, call_v, lam, new_atom};
use orchid_extension::{ExecHandle, exec};
use orchid_extension::{Expr, ExprHandle};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, call_v, lam, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom}; use orchid_extension::{
Atomic, ExecHandle, Expr, ExprHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, exec,
};
use crate::macros::resolve::resolve;
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::std::reflection::sym_atom::SymAtom; use crate::std::reflection::sym_atom::SymAtom;
use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api}; use crate::{HomoTpl, MacTok, MacTree, OrcOpt, Tpl, UntypedTuple, api};
@@ -83,78 +81,63 @@ pub async fn gen_match_macro_lib() -> Vec<GenMember> {
}), }),
build_macro(None, ["match", "match_rule", "_row", "=>"]) build_macro(None, ["match", "match_rule", "_row", "=>"])
.rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [ .rule(mactreev!("pattern::match" "...$" value 0 { "..$" rules 0 }), [
async |[value, rules]| { async |mut cx, [value, rules]| {
exec(async move |mut h| { let rule_lines: HomoTpl<TAtom<MacTree>> =
let rule_lines = h cx.exec(cx.recur(mactree!(macros::common::semi_list "push" rules.clone();))).await?;
.exec::<HomoTpl<TAtom<MacTree>>>(call( let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new();
sym!(macros::resolve), for line_mac in rule_lines.0.iter() {
new_atom(mactree!(macros::common::semi_list "push" rules.clone();)), let Tpl((matcher, body)) =
)) cx.exec(cx.recur(mactree!(pattern::_row "push" line_mac.own().await ;))).await?;
.await?; rule_atoms.push((matcher, body));
let mut rule_atoms = Vec::<(TAtom<MatcherAtom>, Expr)>::new(); }
for line_mac in rule_lines.0.iter() { let base_case = lam(async |_| {
let Tpl((matcher, body)) = h bot(mk_errv(
.exec(call(
sym!(macros::resolve),
new_atom(mactree!(pattern::_row "push" line_mac.own().await ;)),
))
.await?;
rule_atoms.push((matcher, body));
}
let base_case = lam::<0>(bot(mk_errv(
is("No branches match").await, is("No branches match").await,
"None of the patterns matches this value", "None of the patterns matches this value",
[rules.pos()], [rules.pos()],
))) ))
.await;
let match_expr = stream::iter(rule_atoms.into_iter().rev())
.fold(base_case, async |tail, (mat, body)| {
lam::<0>(call(sym!(pattern::match_one), (mat, arg(0), body, call(tail, arg(0)))))
.await
})
.await;
Ok(call(match_expr, resolve(value).await))
}) })
.await .await;
let match_expr = stream::iter(rule_atoms.into_iter().rev())
.fold(base_case, |tail, (mat, body)| {
lam(async move |x| {
call(sym!(pattern::match_one), (mat, x, body, call(tail, x))).await
})
})
.await;
Ok(call(match_expr, cx.recur(value)))
}, },
]) ])
.rule(mactreev!(pattern::match_rule (( "...$" pattern 0 ))), [async |[pattern]| { .rule(mactreev!(pattern::match_rule (( "...$" pattern 0 ))), [async |cx, [pattern]| {
resolve(mactree!(pattern::match_rule "push" pattern; )).await cx.recur(mactree!(pattern::match_rule "push" pattern; )).to_gen().await
}]) }])
.rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |[]| { .rule(mactreev!(pattern::match_rule ( macros::common::_ )), [async |_cx, []| {
Ok(new_atom(MatcherAtom { Ok(new_atom(MatcherAtom {
keys: Vec::new(), keys: Vec::new(),
matcher: lam::<0>(OrcOpt(Some(Tpl(())))).await.create().await, matcher: lam(async |_| OrcOpt(Some(Tpl(()))).to_gen().await).await.create().await,
})) }))
}]) }])
.rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [ .rule(mactreev!(pattern::_row ( "...$" pattern 0 pattern::=> "...$" value 1 )), [
async |[pattern, mut value]| { async |mut cx, [pattern, mut value]| -> OrcRes<Tpl<(TAtom<MatcherAtom>, _)>> {
exec(async move |mut h| -> OrcRes<Tpl<(TAtom<MatcherAtom>, GExpr)>> { let Ok(pat): OrcRes<TAtom<MatcherAtom>> =
let Ok(pat) = h cx.exec(cx.recur(mactree!(pattern::match_rule "push" pattern.clone();))).await
.exec::<TAtom<MatcherAtom>>(call( else {
sym!(macros::resolve), return Err(mk_errv(
new_atom(mactree!(pattern::match_rule "push" pattern.clone();)), is("Invalid pattern").await,
)) format!("Could not parse {} as a match pattern", fmt(&pattern).await),
.await [pattern.pos()],
else { ));
return Err(mk_errv( };
is("Invalid pattern").await, value = (pat.keys())
format!("Could not parse {} as a match pattern", fmt(&pattern).await), .fold(value, async |value, name| mactree!("l_" name; ( "push" value ; )))
[pattern.pos()], .await;
)); Ok(Tpl((pat, cx.recur(value))))
};
value = (pat.keys())
.fold(value, async |value, name| mactree!("l_" name; ( "push" value ; )))
.await;
Ok(Tpl((pat, resolve(value).await)))
})
.await
}, },
]) ])
.finish(), .finish(),
fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))), fun(true, "ref_body", async |val| OrcOpt(Some(UntypedTuple(vec![val])))),
build_macro(None, ["ref"]) build_macro(None, ["ref"])
.rule(mactreev!(pattern::match_rule(pattern::ref "$" name)), [async |[name]| { .rule(mactreev!(pattern::match_rule(pattern::ref "$" name)), [async |_cx, [name]| {
let MacTok::Name(name) = name.tok() else { let MacTok::Name(name) = name.tok() else {
return Err(mk_errv( return Err(mk_errv(
is("pattern 'ref' requires a name to bind to").await, is("pattern 'ref' requires a name to bind to").await,

View File

@@ -8,6 +8,7 @@ pub mod mactree;
mod mactree_lexer; mod mactree_lexer;
pub mod match_macros; pub mod match_macros;
mod ph_lexer; mod ph_lexer;
pub mod postmac;
mod resolve; mod resolve;
mod rule; mod rule;
pub mod stdlib; pub mod stdlib;

View File

@@ -1,10 +1,8 @@
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_base::{FmtUnit, OrcRes, es, is, mk_errv, name_char, name_start}; use orchid_base::{FmtUnit, OrcRes, es, is, mk_errv, name_char, name_start};
use orchid_extension::Atomic;
use orchid_extension::gen_expr::new_atom; use orchid_extension::gen_expr::new_atom;
use orchid_extension::{LexContext, Lexer, err_not_applicable};
use orchid_extension::tree::{GenTokTree, x_tok}; use orchid_extension::tree::{GenTokTree, x_tok};
use orchid_extension::{ThinAtom, ThinVariant}; use orchid_extension::{Atomic, LexContext, Lexer, ThinAtom, ThinVariant, err_not_applicable};
use crate::macros::mactree::{Ph, PhKind}; use crate::macros::mactree::{Ph, PhKind};

View File

@@ -0,0 +1,53 @@
use std::any::TypeId;
use std::borrow::Cow;
use std::marker::PhantomData;
use never::Never;
use orchid_extension::gen_expr::GExpr;
use orchid_extension::{
Atomic, ClonableToExprDyn, MethodSetBuilder, OwnedAtom, OwnedVariant, ToExpr, ToExprFuture,
};
fn assert_not_gexpr<T: 'static>() {
assert_ne!(
TypeId::of::<T>(),
TypeId::of::<GExpr>(),
"Cannot output GExpr, use PostMac::boxed or PostMac::boxed_clone"
);
}
struct PostMacCx<'a>(PhantomData<&'a ()>);
impl PostMacCx<'_> {
pub fn ex<T: ToExpr>(&self, val: PostMac<T>) -> T { val.0 }
}
#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct PostMac<T: ToExpr + 'static>(T);
impl<T: ToExpr + 'static> PostMac<T> {
pub fn new(t: T) -> Self {
assert_not_gexpr::<T>();
Self(t)
}
}
impl<T: Future<Output: ToExpr + Clone + 'static> + Clone + 'static> PostMac<ToExprFuture<T>> {
pub fn with(f: impl for<'a> FnOnce(PostMacCx<'a>) -> T + Clone + 'static) -> Self {
assert_not_gexpr::<T>();
PostMac(ToExprFuture(f(PostMacCx(PhantomData))))
}
}
impl<T: ToExpr + Clone + 'static> PostMac<T> {
pub fn atom(self) -> PostMacAtom { PostMac(Box::new(self.0)) }
}
impl<T: ToExpr + 'static> From<T> for PostMac<T> {
fn from(value: T) -> Self { Self(value) }
}
pub type PostMacAtom = PostMac<Box<dyn ClonableToExprDyn>>;
impl Atomic for PostMacAtom {
type Data = ();
type Variant = OwnedVariant;
fn reg_methods() -> MethodSetBuilder<Self> { MethodSetBuilder::new() }
}
impl OwnedAtom for PostMacAtom {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
}

View File

@@ -1,71 +1,146 @@
use std::borrow::Cow;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ops::{Add, Range}; use std::ops::{Add, Range};
use std::rc::Rc;
use async_fn_stream::stream; use async_fn_stream::stream;
use futures::{FutureExt, StreamExt, stream}; use futures::{FutureExt, StreamExt, stream};
use hashbrown::{HashMap, HashSet}; use hashbrown::{HashMap, HashSet};
use itertools::Itertools; use itertools::Itertools;
use never::Never;
use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv}; use orchid_base::{NameLike, Paren, Pos, Sym, VPath, fmt, is, log, mk_errv};
use orchid_extension::gen_expr::{GExpr, arg, bot, call, call_v, dyn_lambda, new_atom}; use orchid_extension::gen_expr::{GExpr, GExprKind, bot, call, call_v, new_atom};
use orchid_extension::{ReflMemKind, TAtom, ToExpr, exec, refl}; use orchid_extension::{
Atomic, ExecHandle, OwnedAtom, OwnedVariant, ReflMemKind, TAtom, ToExpr, ToExprFuture, exec, refl,
};
use subslice_offset::SubsliceOffset; use subslice_offset::SubsliceOffset;
use substack::Substack;
use crate::macros::macro_value::{Macro, Rule}; use crate::macros::macro_value::{Macro, Rule};
use crate::macros::mactree::MacTreeSeq; use crate::macros::mactree::MacTreeSeq;
use crate::macros::postmac::{PostMac, PostMacAtom};
use crate::macros::rule::state::{MatchState, StateEntry}; use crate::macros::rule::state::{MatchState, StateEntry};
use crate::{MacTok, MacTree}; use crate::{MacTok, MacTree};
pub async fn resolve(val: MacTree) -> GExpr { pub enum ArgStackKind {
exec(async move |mut h| { End,
writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await; Cons(Sym, ArgStack),
let root = refl(); }
let mut macros = HashMap::new(); #[derive(Clone)]
for n in val.glossary() { pub struct ArgStack {
let (foot, body) = n.split_last_seg(); kind: Rc<ArgStackKind>,
let new_name = VPath::new(body.iter().cloned()) len: usize,
.name_with_suffix(is(&format!("__macro__{foot}")).await) }
.to_sym() impl ArgStack {
.await; pub fn end() -> Self { ArgStack { kind: Rc::new(ArgStackKind::End), len: 0 } }
if let Ok(ReflMemKind::Const) = root.get_by_path(&new_name).await.map(|m| m.kind()) { }
let Ok(mac) = h.exec::<TAtom<Macro>>(new_name).await else { continue }; impl Default for ArgStack {
let mac = mac.own().await; fn default() -> Self { Self::end() }
macros.entry(mac.0.canonical_name.clone()).or_insert(mac); }
impl Atomic for ArgStack {
type Data = ();
type Variant = OwnedVariant;
}
impl OwnedAtom for ArgStack {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
}
/// # TODO
///
/// convert macro system to return MacTree or otherwise bring it up to
/// speed with the new [ToExpr] / [GExpr] division
///
/// Idea: MacTree needs to be passed wherever the meaning of an expression can
/// change depending on where in the tree it is bound
///
/// Idea: lowering MacTree to ToExpr implementors is possible by just knowing
/// what names are bound, not their values, but lowering it to GExpr is not.
///
/// Problem: The required information is stackbound, so recursive macro matching
/// needs to be a single coroutine. Even when it forks out to Orchid, recursive
/// calls need to point back to this coroutine. Being a coroutine, this
/// recursion can overflow the Rust stack.
///
/// Limits:
///
/// - The concrete MacTree being generated sometimes depends on recursive macro
/// calls which need to complete before we return a MacTree
/// - Any placeholders representing expressions must be recursed over before
/// returning in a MacTree
/// - Exactly one of these things must be done on a subtree
///
/// Takeaways:
///
/// - Resolution should not lower to GExpr
/// - Consider separate types MacTree vs resolved tree
/// - MacTree can be built for the purpose of passing into recur
/// - Resolved tree can be built for the purpose of returning
/// - cannot contain [...], {...}, (), ( ... \. )
/// - is pretty much GExpr with sym / dynamic arg binding instead of
/// numbered. Can this be a wrapper type over ToExpr instead?
/// - In order to move recursive state off the stack, we need a loophole
/// for lambdas
/// - Ensures that resolution only happens exactly once which is important
/// because double resolve can produce bugs that are difficult to catch
/// - Macros may return ResolvedTree but they can also return a datastructure
/// containing MacTree
/// - Macros may never lower ResolvedTree to GExpr directly because it may
/// refer to bound arguments by name
/// - Macros returning datastructures can only ever be called as logic while
/// those returning ResolvedTree can only ever be inlined
/// - this is a type system concern so addressing it here is unnecessary
///
/// Problems:
/// - ToExpr are not usually copiable by default
/// - plain-orchid macros should be able to annotate data-to-return and
/// data-to-resolve with the same tick symbol to limit conceptual complexity,
/// - the case where a macro deliberately wants to bind a name explicitly within
/// a subexpression is tricky
///
/// The best option probably remains for resolve to process and return MacTree,
/// and for there to be a separate "lower" function. Nothing as yet suggests
/// however that macros can't be allowed to return different types
pub async fn resolve(h: &mut ExecHandle<'_>, val: MacTree, arg_stk: ArgStack) -> PostMacAtom {
writeln!(log("debug"), "Macro-resolving {}", fmt(&val).await).await;
let root = refl();
let mut macros = HashMap::new();
for n in val.glossary() {
let (foot, body) = n.split_last_seg();
let new_name = VPath::new(body.iter().cloned())
.name_with_suffix(is(&format!("__macro__{foot}")).await)
.to_sym()
.await;
if let Ok(ReflMemKind::Const) = root.get_by_path(&new_name).await.map(|m| m.kind()) {
let Ok(mac) = h.exec::<TAtom<Macro>>(new_name).await else { continue };
let mac = mac.own().await;
macros.entry(mac.0.canonical_name.clone()).or_insert(mac);
}
}
let mut exclusive = Vec::new();
let mut prios = Vec::<u64>::new();
let mut priod = Vec::<FilteredMacroRecord>::new();
for (_, mac) in macros.iter() {
let mut record = FilteredMacroRecord { mac, rules: Vec::new() };
for (rule_i, rule) in mac.0.rules.iter().enumerate() {
if rule.pattern.glossary.is_subset(val.glossary()) {
record.rules.push(rule_i);
} }
} }
let mut exclusive = Vec::new(); if !record.rules.is_empty() {
let mut prios = Vec::<u64>::new(); match mac.0.prio {
let mut priod = Vec::<FilteredMacroRecord>::new(); None => exclusive.push(record),
for (_, mac) in macros.iter() { Some(prio) => {
let mut record = FilteredMacroRecord { mac, rules: Vec::new() }; let i = prios.partition_point(|p| *p > prio);
for (rule_i, rule) in mac.0.rules.iter().enumerate() { prios.insert(i, prio);
if rule.pattern.glossary.is_subset(val.glossary()) { priod.insert(i, record);
record.rules.push(rule_i); },
}
}
if !record.rules.is_empty() {
match mac.0.prio {
None => exclusive.push(record),
Some(prio) => {
let i = prios.partition_point(|p| *p > prio);
prios.insert(i, prio);
priod.insert(i, record);
},
}
} }
} }
let mut rctx = ResolveCtx { exclusive, priod }; }
let gex = resolve_one(&mut rctx, Substack::Bottom, &val).await; let mut rctx = ResolveCtx { exclusive, priod };
writeln!( let gex = resolve_one(&mut rctx, arg_stk, &val).await;
log("debug"), writeln!(log("debug"), "Macro-resolution over {}", fmt(&val).await).await;
"Macro-resolution over {}\nreturned {}", gex
fmt(&val).await,
fmt(&gex).await
)
.await;
gex
})
.await
} }
/// Rules belonging to one macro that passed a particular filter /// Rules belonging to one macro that passed a particular filter
@@ -83,37 +158,54 @@ struct ResolveCtx<'a> {
pub priod: Vec<FilteredMacroRecord<'a>>, pub priod: Vec<FilteredMacroRecord<'a>>,
} }
async fn resolve_one( async fn resolve_one(ctx: &mut ResolveCtx<'_>, arg_stk: ArgStack, value: &MacTree) -> PostMacAtom {
ctx: &mut ResolveCtx<'_>, eprintln!("Resolving unit {}", fmt(value).await);
arg_stk: Substack<'_, Sym>,
value: &MacTree,
) -> GExpr {
match value.tok() { match value.tok() {
MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"), MacTok::Ph(_) | MacTok::Slot => panic!("Forbidden element in value mactree"),
MacTok::Bottom(err) => bot(err.clone()), MacTok::Bottom(err) => PostMac::new(bot(err.clone())).atom(),
MacTok::Value(v) => v.clone().to_gen().await, MacTok::Value(v) => {
MacTok::Name(n) => match arg_stk.iter().position(|arg| arg == n) { eprintln!("Found value {}", fmt(v).await);
Some(de_bruijn) => arg((arg_stk.len() - 1 - de_bruijn).try_into().unwrap()), PostMac::new(v.clone()).atom()
None => n.clone().to_gen().await, },
MacTok::Name(n) => {
eprintln!("Looking for {n} among [");
let mut cur = &arg_stk;
let mut counter = 0;
while let ArgStackKind::Cons(name, next) = &*cur.kind {
cur = next;
counter += 1;
eprintln!("{name}, ");
if name == n {
return PostMac::new(GExprKind::Arg(counter).at(value.pos())).atom();
}
}
PostMac::new(n.clone()).atom()
}, },
MacTok::Lambda(arg, body) => { MacTok::Lambda(arg, body) => {
eprintln!("Found lambda \\{} {}", fmt(arg).await, fmt(body).await);
let MacTok::Name(name) = &*arg.tok else { let MacTok::Name(name) = &*arg.tok else {
return bot(mk_errv( return PostMac::new(bot(mk_errv(
is("Syntax error after macros").await, is("Syntax error after macros").await,
"This token ends up as a binding, consider replacing it with a name", "This token ends up as a binding, consider replacing it with a name",
[arg.pos()], [arg.pos()],
)); )))
.atom();
}; };
let arg_pos = arg_stk.len() as u64; let arg_stk =
let arg_stk = arg_stk.push(name.clone()); ArgStack { len: arg_stk.len + 1, kind: Rc::new(ArgStackKind::Cons(name.clone(), arg_stk)) };
dyn_lambda(arg_pos, resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await).await let body = resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await;
let body2 = body.clone();
let pos = value.pos();
PostMac::with(async |cx| GExprKind::Lambda(Box::new(cx.ex(body).to_gen().await)).at(pos))
.atom()
}, },
MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await, MacTok::S(Paren::Round, body) => resolve_seq(ctx, arg_stk, body.clone(), value.pos()).await,
MacTok::S(..) => bot(mk_errv( MacTok::S(..) => PostMac::new(bot(mk_errv(
is("Leftover [] or {} not matched by macro").await, is("Leftover [] or {} not matched by macro").await,
format!("{} was not matched by any macro", fmt(value).await), format!("{} was not matched by any macro", fmt(value).await),
[value.pos()], [value.pos()],
)), )))
.atom(),
} }
} }
@@ -133,17 +225,18 @@ fn subsection<T>(
async fn resolve_seq( async fn resolve_seq(
ctx: &mut ResolveCtx<'_>, ctx: &mut ResolveCtx<'_>,
arg_stk: Substack<'_, Sym>, arg_stk: ArgStack,
val: MacTreeSeq, val: MacTreeSeq,
fallback_pos: Pos, fallback_pos: Pos,
) -> GExpr { ) -> PostMacAtom {
if val.items.is_empty() { if val.items.is_empty() {
return bot(mk_errv( return PostMac::new(bot(mk_errv(
is("Empty sequence").await, is("Empty sequence").await,
"() or (\\arg ) left after macro execution. \ "() or (\\arg ) left after macro execution. \
This is usually caused by an incomplete call to a macro with bad error detection", This is usually caused by an incomplete call to a macro with bad error detection",
[fallback_pos], [fallback_pos],
)); )))
.atom();
} }
// A sorted collection of overlapping but non-nested matches to exclusive // A sorted collection of overlapping but non-nested matches to exclusive
// macros // macros
@@ -226,14 +319,15 @@ async fn resolve_seq(
}) })
.reduce(|l, r| l + r); .reduce(|l, r| l + r);
if let Some(error) = error { if let Some(error) = error {
return bot(error); return PostMac::new(bot(error)).atom();
} }
// no conflicts, apply all exclusive matches // no conflicts, apply all exclusive matches
for (range, mac, rule, state) in x_matches.into_iter().rev() { for (range, mac, rule, state) in x_matches.into_iter().rev() {
// backwards so that the non-overlapping ranges remain valid // backwards so that the non-overlapping ranges remain valid
let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add)) let pos = (state.names().flat_map(|r| r.1).cloned().reduce(Pos::add))
.expect("All macro rules must contain at least one locally defined name"); .expect("All macro rules must contain at least one locally defined name");
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await; let subex =
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
new_val.splice(range, [MacTok::Value(subex).at(pos)]); new_val.splice(range, [MacTok::Value(subex).at(pos)]);
} }
}; };
@@ -251,7 +345,8 @@ async fn resolve_seq(
let range = pre.len()..new_val.len() - suf.len(); let range = pre.len()..new_val.len() - suf.len();
let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add)) let pos = (state.names().flat_map(|pair| pair.1).cloned().reduce(Pos::add))
.expect("All macro rules must contain at least one locally defined name"); .expect("All macro rules must contain at least one locally defined name");
let subex = mk_body_call(mac, rule, &state, pos.clone()).await.to_expr().await; let subex =
mk_body_call(mac, rule, &state, pos.clone(), arg_stk.clone()).await.to_expr().await;
std::mem::drop(state); std::mem::drop(state);
new_val.splice(range, [MacTok::Value(subex).at(pos)]); new_val.splice(range, [MacTok::Value(subex).at(pos)]);
} }
@@ -267,11 +362,21 @@ async fn resolve_seq(
let first = exprs.pop_front().expect( let first = exprs.pop_front().expect(
"We checked first that it isn't empty, and named macros get replaced with their results", "We checked first that it isn't empty, and named macros get replaced with their results",
); );
stream::iter(exprs).fold(first, async |f, x| call(f, x).await).await PostMac::with(async move |cx| {
stream::iter(exprs).fold(cx.ex(first), async |f, x| call(f, cx.ex(x)).await).await
})
.await
.atom()
} }
async fn mk_body_call(mac: &Macro, rule: &Rule, state: &MatchState<'_>, pos: Pos) -> GExpr { async fn mk_body_call(
let mut call_args = vec![]; mac: &Macro,
rule: &Rule,
state: &MatchState<'_>,
pos: Pos,
arg_stk: ArgStack,
) -> GExpr {
let mut call_args = vec![new_atom(arg_stk).at(Pos::None)];
for name in rule.ph_names.iter() { for name in rule.ph_names.iter() {
call_args.push(match state.get(name).expect("Missing state entry for placeholder") { call_args.push(match state.get(name).expect("Missing state entry for placeholder") {
StateEntry::Scalar(scal) => new_atom((**scal).clone()), StateEntry::Scalar(scal) => new_atom((**scal).clone()),

View File

@@ -1,8 +1,7 @@
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
use orchid_base::Sym; use orchid_base::{OrcRes, Sym, is};
use orchid_base::{OrcRes, is};
use super::any_match::any_match; use super::any_match::any_match;
use super::build::mk_any; use super::build::mk_any;

View File

@@ -3,8 +3,7 @@
use std::fmt; use std::fmt;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::{PARENS, Paren}; use orchid_base::{IStr, PARENS, Paren, Side, Sym};
use orchid_base::{IStr, Side, Sym};
pub enum ScalMatcher { pub enum ScalMatcher {
Name(Sym), Name(Sym),

View File

@@ -2,9 +2,7 @@
use std::any::Any; use std::any::Any;
use hashbrown::HashMap; use hashbrown::HashMap;
use orchid_base::Pos; use orchid_base::{IStr, Pos, Sym, join_maps, match_mapping};
use orchid_base::Sym;
use orchid_base::{IStr, join_maps, match_mapping};
use crate::macros::MacTree; use crate::macros::MacTree;

View File

@@ -1,12 +1,11 @@
use orchid_extension::tree::{GenMember, prefix}; use orchid_extension::tree::{GenMember, prefix};
use crate::macros::resolve::resolve;
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
pub async fn gen_functional_macro_lib() -> Vec<GenMember> { pub async fn gen_functional_macro_lib() -> Vec<GenMember> {
prefix("std::fn", [build_macro(Some(4), ["|>"]) prefix("std::fn", [build_macro(Some(4), ["|>"])
.rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [async |[lhs, fun, rhs]| { .rule(mactreev!("...$" lhs 0 "std::fn::|>" "$" fun "...$" rhs 0), [
resolve(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)).await async |cx, [lhs, fun, rhs]| cx.recur(mactree!(("push" fun ; "push" lhs ;) "pushv" rhs ;)),
}]) ])
.finish()]) .finish()])
} }

View File

@@ -5,7 +5,6 @@ use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Expr, TAtom, ToExpr, exec}; use orchid_extension::{Expr, TAtom, ToExpr, exec};
use crate::macros::match_macros::MatcherAtom; use crate::macros::match_macros::MatcherAtom;
use crate::macros::resolve::resolve;
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::{OrcOpt, Tpl}; use crate::{OrcOpt, Tpl};
@@ -26,19 +25,16 @@ pub async fn gen_option_macro_lib() -> Vec<GenMember> {
), ),
build_macro(None, ["some", "none"]) build_macro(None, ["some", "none"])
.rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [ .rule(mactreev!(pattern::match_rule ( std::option::some "...$" sub_pattern 0)), [
|[sub]: [_; _]| { async |mut cx, [sub]| {
exec(async move |mut h| { let sub: TAtom<MatcherAtom> =
let sub = h cx.exec(cx.recur(mactree!(pattern::match_rule "push" sub;))).await?;
.exec::<TAtom<MatcherAtom>>(resolve(mactree!(pattern::match_rule "push" sub;)).await) Ok(new_atom(MatcherAtom {
.await?; keys: sub.keys().collect().await,
Ok(new_atom(MatcherAtom { matcher: call(sym!(std::option::is_some_body), sub).await.create().await,
keys: sub.keys().collect().await, }))
matcher: call(sym!(std::option::is_some_body), sub).await.create().await,
}))
})
}, },
]) ])
.rule(mactreev!(pattern::match_rule(std::option::none)), [async |[]: [_; _]| { .rule(mactreev!(pattern::match_rule(std::option::none)), [async |_cx, []| {
new_atom(MatcherAtom { new_atom(MatcherAtom {
keys: vec![], keys: vec![],
matcher: sym!(std::option::is_none_body).to_expr().await, matcher: sym!(std::option::is_none_body).to_expr().await,

View File

@@ -1,41 +1,27 @@
use orchid_base::sym; use orchid_base::sym;
use orchid_extension::TAtom; use orchid_extension::gen_expr::call;
use orchid_extension::ToExpr;
use orchid_extension::exec;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{call, new_atom};
use orchid_extension::tree::{GenMember, prefix}; use orchid_extension::tree::{GenMember, prefix};
use orchid_extension::{Expr, TAtom, ToExpr};
use crate::macros::resolve::resolve;
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{build_macro, mactree, mactreev};
use crate::std::string::str_atom::IntStrAtom; use crate::std::string::str_atom::IntStrAtom;
use crate::{HomoTpl, MacTree, Tpl}; use crate::{HomoTpl, MacTree, Tpl};
pub async fn gen_record_macro_lib() -> Vec<GenMember> { pub async fn gen_record_macro_lib() -> Vec<GenMember> {
prefix("std::record", [build_macro(None, ["r", "_row"]) prefix("std::record", [build_macro(None, ["r", "_row"])
.rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |[elements]: [_; _]| { .rule(mactreev!(std::record::r[ "...$" elements 0 ]), [async |mut cx, [elements]| {
exec(async move |mut h| { let tup: HomoTpl<TAtom<MacTree>> =
let tup = h cx.exec(cx.recur(mactree!((macros::common::comma_list "push" elements ;)))).await?;
.exec::<HomoTpl<TAtom<MacTree>>>(call( let mut record = sym!(std::record::empty).to_gen().await;
sym!(macros::resolve), for item_exprh in tup.0 {
new_atom(mactree!((macros::common::comma_list "push" elements ;))), let Tpl((key, value)): Tpl<(TAtom<IntStrAtom>, Expr)> =
)) cx.exec(cx.recur(mactree!(std::record::_row "push" item_exprh.own().await ;))).await?;
.await?; record = call(sym!(std::record::set), (record, key, value)).await;
let mut record = sym!(std::record::empty).to_gen().await; }
for item_exprh in tup.0 { Ok(record)
let Tpl((key, value)) = h
.exec::<Tpl<(TAtom<IntStrAtom>, Expr)>>(
resolve(mactree!(std::record::_row "push" item_exprh.own().await ;)).await,
)
.await?;
record = call(sym!(std::record::set), (record, key, value)).await;
}
Ok(record)
})
.await
}]) }])
.rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |[name, value]| { .rule(mactreev!(std::record::_row ( "$" name "...$" value 1 )), [async |cx, [name, value]| {
Ok(Tpl((resolve(name).await, resolve(value).await))) Ok(Tpl((cx.recur(name), cx.recur(value))))
}]) }])
.finish()]) .finish()])
} }

View File

@@ -1,24 +1,20 @@
use futures::{StreamExt, stream}; use futures::{StreamExt, stream};
use orchid_base::{OrcRes, sym}; use orchid_base::{OrcRes, sym};
use orchid_extension::TAtom;
use orchid_extension::ToExpr;
use orchid_extension::exec;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{GExpr, call, new_atom}; use orchid_extension::gen_expr::{GExpr, call, new_atom};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{Expr, TAtom, ToExpr, exec};
use crate::macros::match_macros::MatcherAtom; use crate::macros::match_macros::MatcherAtom;
use crate::macros::utils::{build_macro, mactree, mactreev}; use crate::macros::utils::{RuleCtx, build_macro, mactree, mactreev};
use crate::{HomoTpl, MacTree, OrcOpt}; use crate::{HomoTpl, MacTree, OrcOpt};
pub async fn gen_tuple_macro_lib() -> Vec<GenMember> { pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
prefix("std::tuple", [ prefix("std::tuple", [
build_macro(None, ["t"]) build_macro(None, ["t"])
.rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [|[elements]: [_; _]| { .rule(mactreev!(std::tuple::t [ "...$" elements 0 ]), [async |mut cx, [elements]| {
exec(async move |mut h| { let tup: HomoTpl<TAtom<MacTree>> = cx
let tup = h .exec(cx.recur(
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym!(macros::resolve), mactree!((macros::common::comma_list "push" elements ;)),
new_atom(mactree!((macros::common::comma_list "push" elements ;))),
)) ))
.await?; .await?;
let val = stream::iter(&tup.0[..]) let val = stream::iter(&tup.0[..])
@@ -33,65 +29,52 @@ pub async fn gen_tuple_macro_lib() -> Vec<GenMember> {
}) })
.await; .await;
Ok(val) Ok(val)
})
}]) }])
.rule( .rule(
mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0 macros::common::, macros::common::..])), mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0 macros::common::, macros::common::..])),
[async |[elements]: [_; _]| parse_tpl(elements, Some(mactree!(macros::common::_))).await], [async |cx, [elements]| parse_tpl(cx, elements, Some(mactree!(macros::common::_))).await],
) )
.rule( .rule(
mactreev!(pattern::match_rule( mactreev!(pattern::match_rule(
std::tuple::t[ "...$" elements 1 macros::common::, macros::common::.. "...$" tail 0] std::tuple::t[ "...$" elements 1 macros::common::, macros::common::.. "...$" tail 0]
)), )),
[async |[elements, tail]: [_; _]| parse_tpl(elements, Some(tail)).await], [async |cx, [elements, tail]| parse_tpl(cx, elements, Some(tail)).await],
) )
.rule(mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0])), [ .rule(mactreev!(pattern::match_rule(std::tuple::t[ "...$" elements 0])), [
|[elements]: [_; _]| parse_tpl(elements, None), async |cx, [elements]| parse_tpl(cx, elements, None).await,
]) ])
.finish(), .finish(),
fun(false, "matcher_body", tuple_matcher_body), fun(false, "matcher_body", tuple_matcher_body),
]) ])
} }
fn parse_tpl(elements: MacTree, tail_matcher: Option<MacTree>) -> impl Future<Output = GExpr> { async fn parse_tpl(
exec(async move |mut h| -> OrcRes<GExpr> { mut cx: RuleCtx<'_>,
let tup = h elements: MacTree,
.exec::<HomoTpl<TAtom<MacTree>>>(call(sym!(macros::resolve), new_atom( tail_matcher: Option<MacTree>,
mactree!((macros::common::comma_list "push" elements ;)), ) -> OrcRes<GExpr> {
))) let tup: HomoTpl<TAtom<MacTree>> =
.await?; cx.exec(cx.recur(mactree!((macros::common::comma_list "push" elements ;)))).await?;
let mut subs = Vec::with_capacity(tup.0.len()); let mut subs = Vec::with_capacity(tup.0.len());
for mac_a in &tup.0[..] { for mac_a in &tup.0[..] {
let sub = h let sub: TAtom<MatcherAtom> =
.exec::<TAtom<MatcherAtom>>(call(sym!(macros::resolve), new_atom( cx.exec(cx.recur(mactree!(pattern::match_rule ("push" mac_a.own().await ;)))).await?;
mactree!(pattern::match_rule ("push" mac_a.own().await ;)), subs.push(sub);
))) }
.await?; let tail_matcher: Option<TAtom<MatcherAtom>> = match tail_matcher {
subs.push(sub); Some(mac) => Some(cx.exec(cx.recur(mactree!(pattern::match_rule "push" mac ;))).await?),
} None => None,
let tail_matcher = match tail_matcher { };
Some(mac) => Some( Ok(new_atom(MatcherAtom {
h.exec::<TAtom<MatcherAtom>>(call(sym!(macros::resolve), new_atom( keys: stream::iter(&subs[..])
mactree!(pattern::match_rule "push" mac ;), .flat_map(|t| t.keys())
))) .chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
.await?, .collect()
), .await,
None => None, matcher: call(sym!(std::tuple::matcher_body), (HomoTpl(subs), OrcOpt(tail_matcher)))
};
Ok(new_atom(MatcherAtom {
keys: stream::iter(&subs[..])
.flat_map(|t| t.keys())
.chain(stream::iter(&tail_matcher).flat_map(|mat| mat.keys()))
.collect()
.await,
matcher: call(sym!(std::tuple::matcher_body), (
HomoTpl(subs),
OrcOpt(tail_matcher),
))
.to_expr() .to_expr()
.await, .await,
})) }))
})
} }
fn tuple_matcher_body( fn tuple_matcher_body(

View File

@@ -6,14 +6,16 @@ use futures::StreamExt;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use itertools::{Itertools, chain}; use itertools::{Itertools, chain};
use never::Never; use never::Never;
use orchid_base::{NameLike, Sym, VPath, is}; use orchid_base::{NameLike, OrcRes, Sym, VPath, is};
use orchid_extension::ToExpr;
use orchid_extension::gen_expr::{GExpr, new_atom}; use orchid_extension::gen_expr::{GExpr, new_atom};
use orchid_extension::tree::{GenMember, MemKind, cnst, lazy}; use orchid_extension::tree::{GenMember, MemKind, cnst, lazy};
use orchid_extension::{Atomic, OwnedAtom, OwnedVariant, TAtom}; use orchid_extension::{
Atomic, ExecHandle, OwnedAtom, OwnedVariant, TAtom, ToExpr, ToExprFuture, TryFromExpr, exec,
};
use crate::macros::macro_value::{Macro, MacroData, Rule}; use crate::macros::macro_value::{Macro, MacroData, Rule};
use crate::macros::mactree::MacTreeSeq; use crate::macros::mactree::MacTreeSeq;
use crate::macros::resolve::{ArgStack, resolve};
use crate::macros::rule::matcher::Matcher; use crate::macros::rule::matcher::Matcher;
use crate::{MacTok, MacTree}; use crate::{MacTok, MacTree};
@@ -22,8 +24,9 @@ pub type Args = Vec<MacTree>;
#[derive(Clone)] #[derive(Clone)]
pub struct MacroBodyArgCollector { pub struct MacroBodyArgCollector {
argc: usize, argc: usize,
arg_stack: Option<ArgStack>,
args: Args, args: Args,
cb: Rc<dyn Fn(Args) -> LocalBoxFuture<'static, GExpr>>, cb: Rc<dyn for<'a> Fn(RuleCtx<'a>, Args) -> LocalBoxFuture<'a, GExpr>>,
} }
impl Atomic for MacroBodyArgCollector { impl Atomic for MacroBodyArgCollector {
type Data = (); type Data = ();
@@ -39,12 +42,23 @@ impl OwnedAtom for MacroBodyArgCollector {
self.clone().call(arg).await self.clone().call(arg).await
} }
async fn call(mut self, arg: orchid_extension::Expr) -> GExpr { async fn call(mut self, arg: orchid_extension::Expr) -> GExpr {
let atom = (TAtom::<MacTree>::downcast(arg.handle()).await).unwrap_or_else(|_| { if self.arg_stack.is_none() {
panic!("This is an intermediary value, the argument types are known in advance") let atom = (TAtom::<ArgStack>::downcast(arg.handle()).await).unwrap_or_else(|_| {
}); panic!("The argstack is an intermediary value, the argument types are known in advance")
self.args.push(atom.own().await); });
self.arg_stack = Some(atom.own().await);
} else {
let atom = (TAtom::<MacTree>::downcast(arg.handle()).await).unwrap_or_else(|_| {
panic!("This is an intermediary value, the argument types are known in advance")
});
self.args.push(atom.own().await);
}
if self.argc == self.args.len() { if self.argc == self.args.len() {
(self.cb)(self.args).await.to_gen().await exec(async move |handle| {
let rule_ctx = RuleCtx { arg_stk: self.arg_stack.unwrap(), handle };
(self.cb)(rule_ctx, self.args).await
})
.await
} else { } else {
new_atom(self) new_atom(self)
} }
@@ -53,6 +67,21 @@ impl OwnedAtom for MacroBodyArgCollector {
fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") } fn body_name(name: &str, counter: usize) -> String { format!("({name})::{counter}") }
pub struct RuleCtx<'a> {
arg_stk: ArgStack,
handle: ExecHandle<'a>,
}
impl RuleCtx<'_> {
pub fn recur(&self, mt: MacTree) -> impl ToExpr + use<> {
eprintln!("Recursion in macro");
let arg_stk = self.arg_stk.clone();
ToExprFuture(resolve(mt, arg_stk))
}
pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
self.handle.exec(val).await
}
}
pub(crate) fn build_macro( pub(crate) fn build_macro(
prio: Option<u64>, prio: Option<u64>,
own_kws: impl IntoIterator<Item = &'static str>, own_kws: impl IntoIterator<Item = &'static str>,
@@ -74,31 +103,25 @@ impl MacroBuilder {
pub(crate) fn rule<const N: usize, R: ToExpr>( pub(crate) fn rule<const N: usize, R: ToExpr>(
mut self, mut self,
pat: MacTreeSeq, pat: MacTreeSeq,
body: [impl AsyncFn([MacTree; N]) -> R + 'static; 1], body: [impl AsyncFn(RuleCtx, [MacTree; N]) -> R + 'static; 1],
) -> Self { ) -> Self {
let [body] = body; let [body] = body;
let body = Rc::new(body); let body = Rc::new(body);
let name = &body_name(self.own_kws[0], self.body_consts.len()); let name = &body_name(self.own_kws[0], self.body_consts.len());
self.body_consts.extend(match N { self.body_consts.extend(cnst(
0 => lazy(true, name, async move |_| { true,
let argv = [].into_iter().collect_array().expect("N is 0"); name,
MemKind::Const(body(argv).await.to_gen().await) new_atom(MacroBodyArgCollector {
}), argc: N,
1.. => cnst( arg_stack: None,
true, args: Vec::new(),
name, cb: Rc::new(move |rec, argv| {
new_atom(MacroBodyArgCollector { let arr = argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
argc: N, let body = body.clone();
args: Vec::new(), Box::pin(async move { body(rec, arr).await.to_gen().await })
cb: Rc::new(move |argv| {
let arr =
argv.into_iter().collect_array::<N>().expect("argc should enforce the length");
let body = body.clone();
Box::pin(async move { body(arr).await.to_gen().await })
}),
}), }),
), }),
}); ));
self.patterns.push(pat); self.patterns.push(pat);
self self
} }
@@ -282,4 +305,6 @@ macro_rules! mactreev {
}; };
} }
pub(crate) use {mactree, mactreev, mactreev_impl}; pub(crate) use mactree;
pub(crate) use mactreev;
pub(crate) use mactreev_impl;

View File

@@ -4,8 +4,7 @@ use std::rc::Rc;
use futures::AsyncWrite; use futures::AsyncWrite;
use orchid_api_traits::Encode; use orchid_api_traits::Encode;
use orchid_extension::Atomic; use orchid_extension::{Atomic, DeserializeCtx, OwnedAtom, OwnedVariant};
use orchid_extension::{DeserializeCtx, OwnedAtom, OwnedVariant};
#[derive(Clone)] #[derive(Clone)]
pub struct BlobAtom(pub(crate) Rc<Vec<u8>>); pub struct BlobAtom(pub(crate) Rc<Vec<u8>>);

View File

@@ -1,10 +1,8 @@
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_base::{FmtUnit, OrcRes, sym}; use orchid_base::{FmtUnit, OrcRes, sym};
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::Expr;
use orchid_extension::gen_expr::GExpr; use orchid_extension::gen_expr::GExpr;
use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix}; use orchid_extension::tree::{GenMember, cnst, comments, fun, prefix};
use orchid_extension::{Atomic, TAtom, ThinAtom, ThinVariant}; use orchid_extension::{Atomic, Expr, TAtom, ThinAtom, ThinVariant, ToExpr, TryFromExpr};
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)] #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, Coding)]
pub struct Bool(pub bool); pub struct Bool(pub bool);

View File

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

View File

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

View File

@@ -1,13 +1,9 @@
use itertools::{Itertools, chain}; use itertools::{Itertools, chain};
use orchid_base::Sym;
use orchid_base::{ use orchid_base::{
Import, Parsed, Snippet, expect_tok, line_items, parse_multiname, token_errv, IStr, Import, OrcRes, Paren, Parsed, Snippet, Sym, Token, expect_tok, is, line_items, mk_errv,
}; parse_multiname, token_errv,
use orchid_base::{Paren, Token};
use orchid_base::{IStr, OrcRes, is, mk_errv};
use orchid_extension::{
PTokTree, ParsCtx, ParsedLine, ParsedLineKind, p_tree2gen, p_v2gen,
}; };
use orchid_extension::{PTokTree, ParsCtx, ParsedLine, ParsedLineKind, p_tree2gen, p_v2gen};
pub async fn parse_impls( pub async fn parse_impls(
_: &ParsCtx<'_>, _: &ParsCtx<'_>,

View File

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

View File

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

View File

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

View File

@@ -3,10 +3,9 @@ use std::rc::Rc;
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::{is, mk_errv}; use orchid_base::{is, mk_errv};
use orchid_extension::TAtom; use orchid_extension::gen_expr::new_atom;
use orchid_extension::Expr;
use orchid_extension::gen_expr::{arg, new_atom};
use orchid_extension::tree::{GenMember, cnst, fun, prefix}; use orchid_extension::tree::{GenMember, cnst, fun, prefix};
use orchid_extension::{Expr, TAtom, get_arg_posv};
use crate::std::record::record_atom::RecordAtom; use crate::std::record::record_atom::RecordAtom;
use crate::std::string::str_atom::IntStrAtom; use crate::std::string::str_atom::IntStrAtom;
@@ -26,7 +25,7 @@ pub fn gen_record_lib() -> Vec<GenMember> {
None => Err(mk_errv( None => Err(mk_errv(
is("Key not found in record").await, is("Key not found in record").await,
format!("{} is not in this record, valid keys are {}", key.0, record.0.keys().join(", ")), format!("{} is not in this record, valid keys are {}", key.0, record.0.keys().join(", ")),
[arg(0).pos.clone(), arg(1).pos.clone()], get_arg_posv([0, 1]).await,
)), )),
} }
}), }),

View File

@@ -4,10 +4,10 @@ use futures::future::join_all;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_base::{Receipt, ReqHandle, ReqHandleExt, Sym, es, sym}; use orchid_base::{Receipt, ReqHandle, ReqHandleExt, Sym, es, sym};
use orchid_extension::gen_expr::new_atom; use orchid_extension::gen_expr::new_atom;
use orchid_extension::ParserObj;
use orchid_extension::tree::{GenMember, merge_trivial}; use orchid_extension::tree::{GenMember, merge_trivial};
use orchid_extension::{ use orchid_extension::{
AtomOps, AtomicFeatures, Expr, LexerObj, ReqForSystem, System, SystemCard, SystemCtor, ToExpr, AtomOps, AtomicFeatures, Expr, LexerObj, ParserObj, ReqForSystem, System, SystemCard, SystemCtor,
ToExpr,
}; };
use super::number::num_lib::gen_num_lib; use super::number::num_lib::gen_num_lib;

View File

@@ -4,7 +4,7 @@ use std::rc::Rc;
use never::Never; use never::Never;
use orchid_base::{ReqHandleExt, fmt, is, mk_errv}; use orchid_base::{ReqHandleExt, fmt, is, mk_errv};
use orchid_extension::gen_expr::{bot, call, new_atom}; use orchid_extension::gen_expr::{bot, call, new_atom, serialize};
use orchid_extension::std_reqs::{ReadLimit, ReadReq, RunCommand}; use orchid_extension::std_reqs::{ReadLimit, ReadReq, RunCommand};
use orchid_extension::{Atomic, Expr, ForeignAtom, OwnedAtom, OwnedVariant, Supports, ToExpr}; use orchid_extension::{Atomic, Expr, ForeignAtom, OwnedAtom, OwnedVariant, Supports, ToExpr};
@@ -50,6 +50,6 @@ impl Supports<RunCommand> for ReadStreamCmd {
), ),
Some(Ok(v)) => Ok(call(self.succ.clone(), new_atom(BlobAtom(Rc::new(v)))).await), Some(Ok(v)) => Ok(call(self.succ.clone(), new_atom(BlobAtom(Rc::new(v)))).await),
}; };
hand.reply(&req, &Some(ret.to_gen().await.serialize().await)).await hand.reply(&req, &Some(serialize(ret.to_gen().await).await)).await
} }
} }

View File

@@ -10,10 +10,9 @@ use orchid_api_traits::{Encode, Request};
use orchid_base::{ use orchid_base::{
FmtCtx, FmtUnit, IStr, OrcRes, Receipt, ReqHandle, ReqHandleExt, Sym, es, is, mk_errv, sym, FmtCtx, FmtUnit, IStr, OrcRes, Receipt, ReqHandle, ReqHandleExt, Sym, es, is, mk_errv, sym,
}; };
use orchid_extension::{ToExpr, TryFromExpr};
use orchid_extension::Expr;
use orchid_extension::{ use orchid_extension::{
AtomMethod, Atomic, DeserializeCtx, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom, AtomMethod, Atomic, DeserializeCtx, Expr, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports,
TAtom, ToExpr, TryFromExpr,
}; };
use crate::std::protocol::types::{GetImpl, ProtocolMethod}; use crate::std::protocol::types::{GetImpl, ProtocolMethod};

View File

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

View File

@@ -1,15 +1,14 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::io; use std::io;
use std::time::Instant;
use chrono::TimeDelta; use chrono::{DateTime, TimeDelta, Utc};
use never::Never; use never::Never;
use orchid_api::ExprTicket; use orchid_api::ExprTicket;
use orchid_api_derive::{Coding, Hierarchy}; use orchid_api_derive::{Coding, Hierarchy};
use orchid_api_traits::Request; use orchid_api_traits::Request;
use orchid_base::{Numeric, OrcRes, Receipt, ReqHandle, ReqHandleExt}; use orchid_base::{Numeric, OrcRes, Receipt, ReqHandle, ReqHandleExt};
use orchid_extension::gen_expr::{GExpr, call, new_atom}; use orchid_extension::gen_expr::{GExpr, call, new_atom, serialize};
use orchid_extension::std_reqs::{AsDuration, RunCommand}; use orchid_extension::std_reqs::{AsInstant, RunCommand};
use orchid_extension::tree::{GenMember, fun, prefix}; use orchid_extension::tree::{GenMember, fun, prefix};
use orchid_extension::{ use orchid_extension::{
Atomic, Expr, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom, ThinAtom, ThinVariant, Atomic, Expr, MethodSetBuilder, OwnedAtom, OwnedVariant, Supports, TAtom, ThinAtom, ThinVariant,
@@ -44,18 +43,9 @@ impl TryFromExpr for OrcDT {
Ok(TAtom::<OrcDT>::try_from_expr(expr).await?.value) Ok(TAtom::<OrcDT>::try_from_expr(expr).await?.value)
} }
} }
impl Supports<AsDuration> for OrcDT {
async fn handle<'a>(
&self,
hand: Box<dyn ReqHandle<'a> + '_>,
req: AsDuration,
) -> std::io::Result<Receipt<'a>> {
hand.reply(&req, &self.0.to_std().unwrap()).await
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct InstantAtom(Instant); pub struct InstantAtom(DateTime<Utc>);
impl Atomic for InstantAtom { impl Atomic for InstantAtom {
type Variant = OwnedVariant; type Variant = OwnedVariant;
type Data = (); type Data = ();
@@ -64,6 +54,15 @@ impl OwnedAtom for InstantAtom {
type Refs = Never; type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
} }
impl Supports<AsInstant> for InstantAtom {
async fn handle<'a>(
&self,
hand: Box<dyn ReqHandle<'a> + '_>,
req: AsInstant,
) -> std::io::Result<Receipt<'a>> {
hand.reply(&req, &self.0).await
}
}
#[derive(Clone)] #[derive(Clone)]
struct Now(Expr); struct Now(Expr);
@@ -82,7 +81,7 @@ impl Supports<RunCommand> for Now {
hand: Box<dyn ReqHandle<'a> + '_>, hand: Box<dyn ReqHandle<'a> + '_>,
req: RunCommand, req: RunCommand,
) -> io::Result<Receipt<'a>> { ) -> io::Result<Receipt<'a>> {
let cont = call(self.0.clone(), new_atom(InstantAtom(Instant::now()))).await.serialize().await; let cont = serialize(call(self.0.clone(), new_atom(InstantAtom(Utc::now()))).await).await;
hand.reply(&req, &Some(cont)).await hand.reply(&req, &Some(cont)).await
} }
} }

View File

@@ -4,6 +4,72 @@
"path": "." "path": "."
} }
], ],
"tasks": {
"version": "2.0.0",
"tasks": [
{
"label": "Build All",
"command": "cargo",
"options": {
"cwd": "${workspaceFolder}"
},
"type": "shell",
"args": ["build"],
"problemMatcher": [
"$rustc"
],
"presentation": {
"reveal": "always"
},
"group": "build"
}
]
},
"launch": {
"version": "0.2.0",
"configurations": [
{
"name": "(LLDB) Launch",
"type": "lldb",
"request": "launch",
"preLaunchTask": "Build All",
"env": {
"ORCHID_EXTENSIONS": "target/debug/orchid_std",
"ORCHID_DEFAULT_SYSTEMS": "orchid::std;orchid::macros",
},
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/target/debug/orcx.exe",
"args": [
"--logs=stderr",
"--logs=debug>stderr",
"--logs=msg>stderr",
"exec",
"1 + 1"
]
},
{
"name": "(Windows) Launch",
"type": "cppvsdbg",
"request": "launch",
"requireExactSource": true,
"preLaunchTask": "Build All",
"environment": [
{ "name": "ORCHID_EXTENSIONS", "value": "target/debug/orchid_std" },
{ "name": "ORCHID_DEFAULT_SYSTEMS", "value": "orchid::std;orchid::macros" },
],
"cwd": "${workspaceRoot}",
"program": "${workspaceRoot}/target/debug/orcx.exe",
"args": [
"--logs=stderr",
"--logs=debug>stderr",
"--logs=msg>stderr",
"exec",
"1 + 1"
]
}
],
"compounds": []
},
"settings": { "settings": {
"[markdown]": { "[markdown]": {
// markdown denotes line breaks with trailing space // markdown denotes line breaks with trailing space

View File

@@ -17,5 +17,6 @@ orchid-base = { version = "0.1.0", path = "../orchid-base" }
orchid-host = { version = "0.1.0", path = "../orchid-host", features = [ orchid-host = { version = "0.1.0", path = "../orchid-host", features = [
"tokio", "tokio",
] } ] }
stacker = "0.1.23"
substack = "1.1.1" substack = "1.1.1"
tokio = { version = "1.49.0", features = ["full"] } tokio = { version = "1.49.0", features = ["full"] }

View File

@@ -2,11 +2,13 @@ use orchid_base::Logger;
use orchid_host::dylib::ext_dylib; use orchid_host::dylib::ext_dylib;
use tokio::time::Instant; use tokio::time::Instant;
pub mod parse_folder; pub mod parse_folder;
mod print_mod;
mod repl;
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::Read;
use std::pin::pin; use std::pin::pin;
use std::process::{Command, ExitCode}; use std::process::{Command, ExitCode};
use std::rc::Rc; use std::rc::Rc;
@@ -20,8 +22,8 @@ use futures::{FutureExt, Stream, TryStreamExt, io};
use itertools::Itertools; use itertools::Itertools;
use orchid_base::local_interner::local_interner; use orchid_base::local_interner::local_interner;
use orchid_base::{ use orchid_base::{
FmtCtxImpl, Format, Import, NameLike, Snippet, SrcRange, Token, VPath, fmt, fmt_v, is, log, sym, FmtCtxImpl, Format, Snippet, SrcRange, Token, VPath, fmt, fmt_v, is, log, sym, take_first,
take_first, try_with_reporter, ttv_fmt, with_interner, with_logger, with_reporter, with_stash, try_with_reporter, ttv_fmt, with_interner, with_logger, with_reporter, with_stash,
}; };
use orchid_host::ctx::{Ctx, JoinHandle, Spawner}; use orchid_host::ctx::{Ctx, JoinHandle, Spawner};
use orchid_host::execute::{ExecCtx, ExecResult}; use orchid_host::execute::{ExecCtx, ExecResult};
@@ -30,15 +32,14 @@ use orchid_host::extension::Extension;
use orchid_host::lex::lex; use orchid_host::lex::lex;
use orchid_host::logger::LoggerImpl; use orchid_host::logger::LoggerImpl;
use orchid_host::parse::{HostParseCtxImpl, parse_item, parse_items}; use orchid_host::parse::{HostParseCtxImpl, parse_item, parse_items};
use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModule}; use orchid_host::parsed::{ParsTokTree, ParsedModule};
use orchid_host::subprocess::ext_command; use orchid_host::subprocess::ext_command;
use orchid_host::system::init_systems; use orchid_host::system::init_systems;
use orchid_host::tree::{MemberKind, Module, RootData};
use substack::Substack; use substack::Substack;
use tokio::io::{AsyncBufReadExt, BufReader, stdin};
use tokio::task::{LocalSet, spawn_local}; use tokio::task::{LocalSet, spawn_local};
use crate::parse_folder::parse_folder; use crate::parse_folder::parse_folder;
use crate::repl::repl;
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(version, about, long_about)] #[command(version, about, long_about)]
@@ -58,8 +59,10 @@ pub struct Args {
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
pub enum Commands { pub enum Commands {
Lex { Lex {
#[arg()] #[arg(long)]
file: Utf8PathBuf, file: Option<Utf8PathBuf>,
#[arg(long)]
line: Option<String>,
}, },
Parse { Parse {
#[arg(short, long)] #[arg(short, long)]
@@ -174,311 +177,168 @@ impl Spawner for SpawnerImpl {
} }
} }
#[tokio::main] fn main() -> io::Result<ExitCode> {
async fn main() -> io::Result<ExitCode> {
eprintln!("Orcx v0.1 is free software provided without warranty."); eprintln!("Orcx v0.1 is free software provided without warranty.");
let args = Args::parse(); // Use a 10MB stack for single-threaded, unoptimized operation
let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS)); stacker::grow(10 * 1024 * 1024, || {
let local_set = LocalSet::new(); tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap().block_on(async {
let exit_code1 = exit_code.clone(); let args = Args::parse();
let logger = get_logger(&args); let exit_code = Rc::new(RefCell::new(ExitCode::SUCCESS));
let logger2 = logger.clone(); let local_set = LocalSet::new();
unsafe { STARTUP = Some(Instant::now()) }; let exit_code1 = exit_code.clone();
let ctx = Ctx::new(SpawnerImpl, logger2); let logger = get_logger(&args);
let (signal_end_main, on_end_main) = futures::channel::oneshot::channel(); let logger2 = logger.clone();
let ctx1 = ctx.clone(); unsafe { STARTUP = Some(Instant::now()) };
local_set.spawn_local(async move { let ctx = Ctx::new(SpawnerImpl, logger2);
let ctx = &ctx1; let (signal_end_main, on_end_main) = futures::channel::oneshot::channel();
with_stash(async move { let ctx1 = ctx.clone();
let extensions = local_set.spawn_local(async move {
get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap(); let ctx = &ctx1;
time_print(&args, "Extensions loaded"); let res = with_stash(async move {
match args.command { let extensions =
Commands::Lex { file } => { get_all_extensions(&args, ctx).try_collect::<Vec<Extension>>().await.unwrap();
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); time_print(&args, "Extensions loaded");
let mut file = File::open(file.as_std_path()).unwrap(); match args.command {
let mut buf = String::new(); Commands::Lex { file, line } => {
file.read_to_string(&mut buf).unwrap(); let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
match lex(is(&buf).await, sym!(usercode), &systems, ctx).await { let mut buf = String::new();
Ok(lexemes) => match (&file, &line) {
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true)), (Some(file), None) => {
Err(e) => { let mut file = File::open(file.as_std_path()).unwrap();
eprintln!("{e}"); file.read_to_string(&mut buf).unwrap();
exit_code1.replace(ExitCode::FAILURE); },
(None, Some(line)) => buf = line.clone(),
(None, None) | (Some(_), Some(_)) =>
return Err("`lex` expected exactly one of --file and --line".to_string()),
};
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx)
.await
.map_err(|e| e.to_string())?;
println!("{}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true))
}, },
} Commands::Parse { file } => {
}, let (_, systems) = init_systems(&args.system, &extensions).await.unwrap();
Commands::Parse { file } => { let mut file = File::open(file.as_std_path()).unwrap();
let (_, systems) = init_systems(&args.system, &extensions).await.unwrap(); let mut buf = String::new();
let mut file = File::open(file.as_std_path()).unwrap(); file.read_to_string(&mut buf).unwrap();
let mut buf = String::new(); let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap();
file.read_to_string(&mut buf).unwrap(); let first = lexemes.first().ok_or("File empty!".to_string())?;
let lexemes = lex(is(&buf).await, sym!(usercode), &systems, ctx).await.unwrap(); let pctx =
let Some(first) = lexemes.first() else { HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) };
println!("File empty!"); let snip = Snippet::new(first, &lexemes);
return; let ptree = try_with_reporter(parse_items(&pctx, Substack::Bottom, snip))
}; .await
let pctx = HostParseCtxImpl { systems: &systems, ctx: ctx.clone(), src: sym!(usercode) }; .map_err(|e| e.to_string())?;
let snip = Snippet::new(first, &lexemes); if ptree.is_empty() {
match with_reporter(parse_items(&pctx, Substack::Bottom, snip)).await.unwrap() { return Err(
Err(errv) => { "File empty only after parsing, but no errors were reported".to_string(),
eprintln!("{errv}"); );
*exit_code1.borrow_mut() = ExitCode::FAILURE; }
},
Ok(ptree) if ptree.is_empty() => {
eprintln!("File empty only after parsing, but no errors were reported");
*exit_code1.borrow_mut() = ExitCode::FAILURE;
},
Ok(ptree) =>
for item in ptree { for item in ptree {
println!("{}", take_first(&item.print(&FmtCtxImpl::default()).await, true)) println!("{}", take_first(&item.print(&FmtCtxImpl::default()).await, true))
}, }
}; },
}, Commands::Repl => repl(&args, &extensions, ctx.clone()).await?,
Commands::Repl => { Commands::ModTree { proj, prefix } => {
let mut counter = 0; let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap();
let mut imports = Vec::new(); if let Some(proj_path) = proj {
let usercode_path = sym!(usercode); let path = proj_path.into_std_path_buf();
let mut stdin = BufReader::new(stdin()); root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
loop { .await
counter += 1; .map_err(|e| e.to_string())?
let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); }
print!("\\.> "); let prefix = match prefix {
std::io::stdout().flush().unwrap(); Some(pref) => VPath::parse(&pref).await,
let mut prompt = String::new(); None => VPath::new([]),
stdin.read_line(&mut prompt).await.unwrap(); };
let name = is(&format!("_{counter}")).await; let root_data = root.0.read().await;
let path = usercode_path.suffix([name.clone()]).await; print_mod::print_mod(&root_data.root, prefix, &root_data).await;
let mut lexemes = },
lex(is(prompt.trim()).await, path.clone(), &systems, ctx).await.unwrap(); Commands::Exec { proj, code } => {
let Some(discr) = lexemes.first() else { continue }; let path = sym!(usercode);
writeln!( let prefix_sr = SrcRange::zw(path.clone(), 0);
log("debug"), let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap();
"lexed: {}", if let Some(proj_path) = proj {
take_first(&ttv_fmt(&lexemes, &FmtCtxImpl::default()).await, true) let path = proj_path.into_std_path_buf();
) root = try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone()))
.await; .await
let prefix_sr = SrcRange::zw(path.clone(), 0); .map_err(|e| e.to_string())?;
let process_lexemes = async |lexemes: &[ParsTokTree]| { }
let snippet = Snippet::new(&lexemes[0], lexemes); let mut lexemes = lex(is(code.trim()).await, path.clone(), &systems, ctx)
let parse_ctx =
HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] };
match try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet))
.await .await
{ .map_err(|e| e.to_string())?;
Ok(items) => Some(items),
Err(e) => {
eprintln!("{e}");
None
},
}
};
let add_imports = |items: &mut Vec<Item>, imports: &[Import]| {
items
.extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone())));
};
if discr.is_kw(is("import").await) {
let Some(import_lines) = process_lexemes(&lexemes).await else { continue };
imports.extend(import_lines.into_iter().map(|it| match it.kind {
ItemKind::Import(imp) => imp,
_ => panic!("Expected imports from import line"),
}));
continue;
}
if !discr.is_kw(is("let").await) {
let prefix = [is("export").await, is("let").await, name.clone(), is("=").await];
lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone())));
}
let Some(mut new_lines) = process_lexemes(&lexemes).await else { continue };
let const_decl = new_lines.iter().exactly_one().expect("Multiple lines from let");
let input_sr = const_decl.sr.map_range(|_| 0..0);
let const_name = match &const_decl.kind {
ItemKind::Member(ParsedMember { name: const_name, .. }) => const_name.clone(),
_ => panic!("Expected exactly one constant declaration from let"),
};
add_imports(&mut new_lines, &imports);
imports.push(Import::new(
input_sr.clone(),
VPath::new(path.segs()),
const_name.clone(),
));
let new_module = ParsedModule::new(true, new_lines);
match with_reporter(root.add_parsed(&new_module, path.clone())).await {
Ok(new) => root = new,
Err(errv) => {
eprintln!("{errv}");
*exit_code1.borrow_mut() = ExitCode::FAILURE;
return;
},
}
eprintln!("parsed");
let entrypoint =
ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos());
let mut xctx = ExecCtx::new(root.clone(), entrypoint).await;
eprintln!("executed");
xctx.set_gas(Some(1000));
match xctx.execute().await {
ExecResult::Value(val, _) => {
println!(
"{const_name} = {}",
take_first(&val.print(&FmtCtxImpl::default()).await, false)
)
},
ExecResult::Err(e, _) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
}
},
Commands::ModTree { proj, prefix } => {
let (mut root, _systems) = init_systems(&args.system, &extensions).await.unwrap();
if let Some(proj_path) = proj {
let path = proj_path.into_std_path_buf();
match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await {
Ok(r) => root = r,
Err(e) => {
eprintln!("{e}");
*exit_code1.borrow_mut() = ExitCode::FAILURE;
return;
},
}
}
let prefix = match prefix {
Some(pref) => VPath::parse(&pref).await,
None => VPath::new([]),
};
let root_data = root.0.read().await;
print_mod(&root_data.root, prefix, &root_data).await;
async fn print_mod(module: &Module, path: VPath, root: &RootData) {
let indent = " ".repeat(path.len());
for (key, tgt) in &module.imports {
match tgt {
Ok(tgt) => println!("{indent}import {key} => {}", tgt.target),
Err(opts) => println!(
"{indent}import {key} conflicts between {}",
opts.iter().map(|i| &i.target).join(" ")
),
}
}
for (key, mem) in &module.members {
let new_path = path.clone().name_with_suffix(key.clone()).to_sym().await;
match mem.kind(root.ctx.clone(), &root.consts).await {
MemberKind::Module(module) => {
println!("{indent}module {key} {{");
print_mod(module, VPath::new(new_path.segs()), root).boxed_local().await;
println!("{indent}}}")
},
MemberKind::Const => {
let value = root.consts.get(&new_path).expect("Missing const!");
println!("{indent}const {key} = {}", fmt(value).await)
},
}
}
}
},
Commands::Exec { proj, code } => {
let path = sym!(usercode);
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 = proj_path.into_std_path_buf();
match try_with_reporter(parse_folder(&root, path, sym!(src), ctx.clone())).await {
Ok(r) => root = r,
Err(e) => {
eprintln!("{e}");
*exit_code1.borrow_mut() = ExitCode::FAILURE;
return;
},
}
}
let mut lexemes = match lex(is(code.trim()).await, path.clone(), &systems, ctx).await {
Ok(lexemes) => {
writeln!( writeln!(
log("debug"), log("debug"),
"lexed: {}", "lexed: {}",
fmt_v::<ParsTokTree>(lexemes.iter()).await.join(" ") fmt_v::<ParsTokTree>(lexemes.iter()).await.join(" ")
) )
.await; .await;
lexemes let parse_ctx =
}, HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] };
Err(e) => { let prefix =
eprintln!("{e}"); [is("export").await, is("let").await, is("entrypoint").await, is("=").await];
*exit_code1.borrow_mut() = ExitCode::FAILURE; lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone())));
return; let snippet = Snippet::new(&lexemes[0], &lexemes);
let items =
try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet))
.await
.map_err(|e| e.to_string())?;
let entrypoint = ParsedModule::new(true, items);
let root = with_reporter(root.add_parsed(&entrypoint, path.clone()))
.await
.map_err(|e| e.to_string())?;
let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos());
let mut xctx = ExecCtx::new(root.clone(), expr).await;
xctx.set_gas(Some(10_000));
match xctx.execute().await {
ExecResult::Value(val, _) => {
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
},
ExecResult::Err(e, _) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
}, },
}; };
let parse_ctx = Ok(())
HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] }; })
let prefix = [is("export").await, is("let").await, is("entrypoint").await, is("=").await]; .await;
lexemes.splice(0..0, prefix.map(|n| Token::Name(n).at(prefix_sr.clone()))); if let Err(s) = res {
let snippet = Snippet::new(&lexemes[0], &lexemes); eprintln!("{s}");
let entrypoint = match try_with_reporter(parse_item( *exit_code1.borrow_mut() = ExitCode::FAILURE;
&parse_ctx, }
Substack::Bottom, signal_end_main.send(()).expect("cleanup should still be waiting");
vec![], });
snippet, let cleanup = async {
)) if on_end_main.await.is_err() {
.await return;
{ }
Ok(items) => ParsedModule::new(true, items), tokio::time::sleep(Duration::from_secs(2)).await;
Err(e) => { let mut extensions = HashMap::new();
eprintln!("{e}"); let systems = ctx.systems.read().await.values().filter_map(|v| v.upgrade()).collect_vec();
*exit_code1.borrow_mut() = ExitCode::FAILURE; let exprs = ctx.exprs.iter().collect_vec();
return; for system in &systems {
}, extensions.insert(system.ext().name().clone(), system.ext().clone());
}; }
let root = match with_reporter(root.add_parsed(&entrypoint, path.clone())).await { if extensions.is_empty() && systems.is_empty() && exprs.is_empty() {
Err(e) => { return;
eprintln!("{e}"); }
*exit_code1.borrow_mut() = ExitCode::FAILURE; eprintln!("Shutdown is taking long. The following language constructs are still live:");
return; eprintln!("Extensions: {}", extensions.keys().join(", "));
}, for sys in &systems {
Ok(new_root) => new_root, eprintln!("System: {:?} = {}", sys.id(), sys.ctor().name())
}; }
let expr = ExprKind::Const(sym!(usercode::entrypoint)).at(prefix_sr.pos()); for (rc, expr) in &exprs {
let mut xctx = ExecCtx::new(root.clone(), expr).await; eprintln!("{rc}x {:?} = {}", expr.id(), fmt(expr).await)
xctx.set_gas(Some(10_000)); }
match xctx.execute().await { std::process::abort()
ExecResult::Value(val, _) => {
println!("{}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
},
ExecResult::Err(e, _) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
},
}; };
futures::future::select(
pin!(cleanup),
pin!(with_interner(local_interner(), with_logger(logger, local_set))),
)
.await;
let x = *exit_code.borrow();
Ok(x)
}) })
.await; })
signal_end_main.send(()).expect("cleanup should still be waiting");
});
let cleanup = async {
if on_end_main.await.is_err() {
return;
}
tokio::time::sleep(Duration::from_secs(2)).await;
let mut extensions = HashMap::new();
let systems = ctx.systems.read().await.values().filter_map(|v| v.upgrade()).collect_vec();
let exprs = ctx.exprs.iter().collect_vec();
for system in &systems {
extensions.insert(system.ext().name().clone(), system.ext().clone());
}
if extensions.is_empty() && systems.is_empty() && exprs.is_empty() {
return;
}
eprintln!("Shutdown is taking long. The following language constructs are still live:");
eprintln!("Extensions: {}", extensions.keys().join(", "));
for sys in &systems {
eprintln!("System: {:?} = {}", sys.id(), sys.ctor().name())
}
for (rc, expr) in &exprs {
eprintln!("{rc}x {:?} = {}", expr.id(), fmt(expr).await)
}
std::process::abort()
};
futures::future::select(
pin!(cleanup),
pin!(with_interner(local_interner(), with_logger(logger, local_set))),
)
.await;
let x = *exit_code.borrow();
Ok(x)
} }

View File

@@ -3,10 +3,7 @@ use std::path::{Path, PathBuf};
use futures::FutureExt; use futures::FutureExt;
use itertools::Itertools; use itertools::Itertools;
use orchid_base::SrcRange; use orchid_base::{OrcRes, Snippet, SrcRange, Sym, async_io_err, is, os_str_to_string, report};
use orchid_base::Sym;
use orchid_base::Snippet;
use orchid_base::{OrcRes, async_io_err, is, os_str_to_string, report};
use orchid_host::ctx::Ctx; use orchid_host::ctx::Ctx;
use orchid_host::lex::lex; use orchid_host::lex::lex;
use orchid_host::parse::{HostParseCtxImpl, parse_items}; use orchid_host::parse::{HostParseCtxImpl, parse_items};

31
orcx/src/print_mod.rs Normal file
View File

@@ -0,0 +1,31 @@
use futures::FutureExt;
use itertools::Itertools;
use orchid_base::{NameLike, VPath, fmt};
use orchid_host::tree::{MemKind, Mod, RootData};
pub async fn print_mod(module: &Mod, path: VPath, root: &RootData) {
let indent = " ".repeat(path.len());
for (key, tgt) in &module.imports {
match tgt {
Ok(tgt) => println!("{indent}import {key} => {}", tgt.target),
Err(opts) => println!(
"{indent}import {key} conflicts between {}",
opts.iter().map(|i| &i.target).join(" ")
),
}
}
for (key, mem) in &module.members {
let new_path = path.clone().name_with_suffix(key.clone()).to_sym().await;
match mem.kind(root.ctx.clone(), &root.consts).await {
MemKind::Module(module) => {
println!("{indent}module {key} {{");
print_mod(module, VPath::new(new_path.segs()), root).boxed_local().await;
println!("{indent}}}")
},
MemKind::Const => {
let value = root.consts.get(&new_path).expect("Missing const!");
println!("{indent}const {key} = {}", fmt(value).await)
},
}
}
}

102
orcx/src/repl.rs Normal file
View File

@@ -0,0 +1,102 @@
use std::io::Write;
use itertools::Itertools;
use orchid_base::{
FmtCtxImpl, FmtTTV, Format, Import, NameLike, Snippet, SrcRange, Token, VPath, fmt, is, log, sym,
take_first, try_with_reporter, with_reporter,
};
use orchid_host::ctx::Ctx;
use orchid_host::execute::{ExecCtx, ExecResult};
use orchid_host::expr::ExprKind;
use orchid_host::extension::Extension;
use orchid_host::lex::lex;
use orchid_host::parse::{HostParseCtxImpl, parse_item};
use orchid_host::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedModule};
use orchid_host::system::init_systems;
use substack::Substack;
use tokio::io::{AsyncBufReadExt, BufReader, stdin};
use crate::Args;
use crate::print_mod::print_mod;
pub async fn repl(args: &Args, extensions: &[Extension], ctx: Ctx) -> Result<(), String> {
let mut counter = 0;
let mut imports = Vec::new();
let usercode_path = sym!(usercode);
let mut stdin = BufReader::new(stdin());
let (mut root, systems) = init_systems(&args.system, extensions).await.unwrap();
loop {
counter += 1;
print!("\\.> ");
std::io::stdout().flush().unwrap();
let mut prompt = String::new();
stdin.read_line(&mut prompt).await.unwrap();
if let Some(cmdline) = prompt.trim().strip_prefix(":") {
if cmdline == "help" {
println!(
"Recognized commands are:\n\
- help print this message\n\
- modtree display the current module tree"
)
} else if cmdline == "modtree" {
let root_data = root.0.read().await;
print_mod(&root_data.root, VPath::new([]), &root_data).await
} else {
println!("Could not recognize command. Valid commands are: help")
}
continue;
}
let name = is(&format!("_{counter}")).await;
let path = usercode_path.suffix([name.clone()]).await;
let mut lexemes = lex(is(prompt.trim()).await, path.clone(), &systems, &ctx).await.unwrap();
let Some(discr) = lexemes.first() else { continue };
writeln!(log("debug"), "lexed: {}", fmt(&FmtTTV(&lexemes)).await).await;
let prefix_sr = SrcRange::zw(path.clone(), 0);
let process_lexemes = async |lexemes: &[ParsTokTree]| {
let snippet = Snippet::new(&lexemes[0], lexemes);
let parse_ctx =
HostParseCtxImpl { ctx: ctx.clone(), src: path.clone(), systems: &systems[..] };
try_with_reporter(parse_item(&parse_ctx, Substack::Bottom, vec![], snippet))
.await
.map_err(|e| e.to_string())
};
let add_imports = |items: &mut Vec<Item>, imports: &[Import]| {
items.extend(imports.iter().map(|import| Item::new(import.sr.clone(), import.clone())));
};
if discr.is_kw(is("import").await) {
let import_lines = process_lexemes(&lexemes).await?;
imports.extend(import_lines.into_iter().map(|it| match it.kind {
ItemKind::Import(imp) => imp,
_ => panic!("Expected imports from import line"),
}));
continue;
}
let mut prefix = vec![is("export").await];
if !discr.is_kw(is("let").await) {
prefix.extend([is("let").await, name.clone(), is("=").await]);
}
lexemes.splice(0..0, prefix.into_iter().map(|n| Token::Name(n).at(prefix_sr.clone())));
let mut new_lines = process_lexemes(&lexemes).await?;
let const_decl = new_lines.iter().exactly_one().expect("Multiple lines from let");
let input_sr = const_decl.sr.map_range(|_| 0..0);
let const_name = match &const_decl.kind {
ItemKind::Member(ParsedMember { name: const_name, .. }) => const_name.clone(),
_ => panic!("Expected exactly one constant declaration from let"),
};
add_imports(&mut new_lines, &imports);
imports.push(Import::new(input_sr.clone(), VPath::new(path.segs()), const_name.clone()));
let new_module = ParsedModule::new(true, new_lines);
root =
with_reporter(root.add_parsed(&new_module, path.clone())).await.map_err(|e| e.to_string())?;
let entrypoint = ExprKind::Const(path.suffix([const_name.clone()]).await).at(input_sr.pos());
let mut xctx = ExecCtx::new(root.clone(), entrypoint).await;
xctx.set_gas(Some(1000));
match xctx.execute().await {
ExecResult::Value(val, _) => {
println!("{const_name} = {}", take_first(&val.print(&FmtCtxImpl::default()).await, false))
},
ExecResult::Err(e, _) => println!("error: {e}"),
ExecResult::Gas(_) => println!("Ran out of gas!"),
}
}
}

View File

@@ -18,6 +18,7 @@ overflow_delimited_expr = true
use_small_heuristics = "Max" use_small_heuristics = "Max"
fn_single_line = true fn_single_line = true
where_single_line = true where_single_line = true
format_code_in_doc_comments = false
# literals # literals
hex_literal_case = "Lower" hex_literal_case = "Lower"

View File

@@ -16,3 +16,4 @@ test_executors = "0.4.1"
[dependencies] [dependencies]
futures-io = "0.3.31" futures-io = "0.3.31"
itertools = "0.14.0"

View File

@@ -12,6 +12,7 @@ use std::task::{Context, Poll, Waker};
use std::{io, mem}; use std::{io, mem};
use futures_io::{AsyncRead, AsyncWrite}; use futures_io::{AsyncRead, AsyncWrite};
use itertools::Itertools;
fn pipe_layout(bs: usize) -> Layout { Layout::from_size_align(bs, 1).expect("1-align is trivial") } fn pipe_layout(bs: usize) -> Layout { Layout::from_size_align(bs, 1).expect("1-align is trivial") }
@@ -130,7 +131,15 @@ impl AsyncRingbuffer {
} }
fn writer_wait<T>(&mut self, waker: &Waker) -> Poll<io::Result<T>> { fn writer_wait<T>(&mut self, waker: &Waker) -> Poll<io::Result<T>> {
if self.reader_dropped { if self.reader_dropped {
return Poll::Ready(Err(broken_pipe_error())); let mut buf = vec![0; self.size];
let count = self.wrapping_read(&mut buf);
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
format!(
"Pipe already closed from reader end with {count}b remaining in the buffer: [{}]",
buf[..count].chunks(4).map(|c| c.iter().map(|b| format!("{b:02x}")).join(" ")).join(" ")
),
)));
} }
self.write_waker.drop(); self.write_waker.drop();
self.write_waker = Trigger::new(waker.clone()); self.write_waker = Trigger::new(waker.clone());
@@ -138,7 +147,10 @@ impl AsyncRingbuffer {
} }
fn reader_wait(&mut self, waker: &Waker) -> Poll<io::Result<usize>> { fn reader_wait(&mut self, waker: &Waker) -> Poll<io::Result<usize>> {
if self.writer_dropped { if self.writer_dropped {
return Poll::Ready(Err(broken_pipe_error())); return Poll::Ready(Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"Pipe already closed from writer end.",
)));
} }
self.read_waker.drop(); self.read_waker.drop();
self.read_waker = Trigger::new(waker.clone()); self.read_waker = Trigger::new(waker.clone());
@@ -150,6 +162,14 @@ impl AsyncRingbuffer {
unsafe { &mut *slc }.copy_from_slice(buf); unsafe { &mut *slc }.copy_from_slice(buf);
self.write_idx = (self.write_idx + buf.len()) % self.size; self.write_idx = (self.write_idx + buf.len()) % self.size;
} }
/// Read a number of bytes from the reader head, then reset the head if
/// necessary.
///
/// # Safety
///
/// This function does not check for obstacles such as the writer head or the
/// end of the buffer. It is up to the caller to ensure that the requested
/// number of consecutive bytes is available.
unsafe fn non_wrapping_read_unchecked(&mut self, buf: &mut [u8]) { unsafe fn non_wrapping_read_unchecked(&mut self, buf: &mut [u8]) {
let read_ptr = unsafe { self.start.add(self.read_idx) }; let read_ptr = unsafe { self.start.add(self.read_idx) };
let slc = slice_from_raw_parts(read_ptr, buf.len()).cast_mut(); let slc = slice_from_raw_parts(read_ptr, buf.len()).cast_mut();
@@ -188,14 +208,33 @@ impl AsyncRingbuffer {
} }
} }
} }
/// Read as many bytes as possible into the buffer, and return how many bytes
/// were written
fn wrapping_read(&mut self, buf: &mut [u8]) -> usize {
let AsyncRingbuffer { read_idx, write_idx, size, .. } = *self;
if read_idx < write_idx {
// Frontside non-wrapping read
let count = buf.len().min(write_idx - read_idx);
unsafe { self.non_wrapping_read_unchecked(&mut buf[0..count]) };
count
} else if read_idx + buf.len() < size {
// Backside non-wrapping read
unsafe { self.non_wrapping_read_unchecked(buf) };
buf.len()
} else {
// Wrapping read
let (end, start) = buf.split_at_mut(size - read_idx);
unsafe { self.non_wrapping_read_unchecked(end) };
let start_count = start.len().min(write_idx);
unsafe { self.non_wrapping_read_unchecked(&mut start[0..start_count]) };
end.len() + start_count
}
}
} }
fn already_closed_error() -> io::Error { fn already_closed_error() -> io::Error {
io::Error::new(io::ErrorKind::BrokenPipe, "Pipe already closed from this end") io::Error::new(io::ErrorKind::BrokenPipe, "Pipe already closed from this end")
} }
fn broken_pipe_error() -> io::Error {
io::Error::new(io::ErrorKind::BrokenPipe, "Pipe already closed from other end")
}
#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum SyncWriteError { pub enum SyncWriteError {
@@ -286,37 +325,20 @@ impl AsyncRead for Reader {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
unsafe { let data = unsafe { self.0.as_mut().expect("Cannot be null") };
let data = self.0.as_mut().expect("Cannot be null"); if !buf.is_empty() && data.is_full() {
let AsyncRingbuffer { read_idx, write_idx, size, .. } = *data; data.wake_writer();
if !buf.is_empty() && data.is_full() {
data.wake_writer();
}
let poll = if !buf.is_empty() && data.is_empty() {
// Nothing to read, waiting...
data.reader_wait(cx.waker())
} else if read_idx < write_idx {
// Frontside non-wrapping read
let count = buf.len().min(write_idx - read_idx);
data.non_wrapping_read_unchecked(&mut buf[0..count]);
Poll::Ready(Ok(count))
} else if read_idx + buf.len() < size {
// Backside non-wrapping read
data.non_wrapping_read_unchecked(buf);
Poll::Ready(Ok(buf.len()))
} else {
// Wrapping read
let (end, start) = buf.split_at_mut(size - read_idx);
data.non_wrapping_read_unchecked(end);
let start_count = start.len().min(write_idx);
data.non_wrapping_read_unchecked(&mut start[0..start_count]);
Poll::Ready(Ok(end.len() + start_count))
};
if data.is_empty() {
data.wake_writer();
}
poll
} }
let poll = if !buf.is_empty() && data.is_empty() {
// Nothing to read, waiting...
data.reader_wait(cx.waker())
} else {
Poll::Ready(Ok(data.wrapping_read(buf)))
};
if data.is_empty() {
data.wake_writer();
}
poll
} }
} }
impl Drop for Reader { impl Drop for Reader {