for a moment, everything works

This commit is contained in:
2025-02-21 23:53:31 +01:00
parent 9e7648bc72
commit cfa8b6ee52
23 changed files with 491 additions and 371 deletions

2
Cargo.lock generated
View File

@@ -1124,7 +1124,6 @@ dependencies = [
"ordered-float", "ordered-float",
"regex", "regex",
"rust-embed", "rust-embed",
"rust_decimal",
"some_executor 0.4.0", "some_executor 0.4.0",
"substack", "substack",
"test_executors", "test_executors",
@@ -1201,6 +1200,7 @@ dependencies = [
"orchid-base", "orchid-base",
"orchid-extension", "orchid-extension",
"ordered-float", "ordered-float",
"rust_decimal",
"tokio", "tokio",
] ]

View File

@@ -1,9 +1,5 @@
## Async conversion ## Async conversion
convert host to async non-send
demonstrate operation with existing lex-hello example
consider converting extension's SysCtx to a typed context bag consider converting extension's SysCtx to a typed context bag
align fn atom and macros on both sides with new design. No global state. align fn atom and macros on both sides with new design. No global state.

View File

@@ -23,7 +23,6 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
ordered-float = "4.6.0" ordered-float = "4.6.0"
regex = "1.11.1" regex = "1.11.1"
rust-embed = "8.5.0" rust-embed = "8.5.0"
rust_decimal = "1.36.0"
some_executor = "0.4.0" some_executor = "0.4.0"
substack = "1.1.1" substack = "1.1.1"
test_executors = "0.3.2" test_executors = "0.3.2"

View File

@@ -98,7 +98,7 @@ impl Interned for String {
impl InternMarker for api::TStr { impl InternMarker for api::TStr {
type Interned = String; type Interned = String;
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> { async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
Tok::new(Rc::new(i.master.as_ref().unwrap().request(api::ExternStr(self)).await), self) Tok::new(Rc::new(i.0.master.as_ref().unwrap().request(api::ExternStr(self)).await), self)
} }
fn get_id(self) -> NonZeroU64 { self.0 } fn get_id(self) -> NonZeroU64 { self.0 }
fn from_id(id: NonZeroU64) -> Self { Self(id) } fn from_id(id: NonZeroU64) -> Self { Self(id) }
@@ -125,7 +125,7 @@ impl Interned for Vec<Tok<String>> {
impl InternMarker for api::TStrv { impl InternMarker for api::TStrv {
type Interned = Vec<Tok<String>>; type Interned = Vec<Tok<String>>;
async fn resolve(self, i: &Interner) -> Tok<Self::Interned> { async fn resolve(self, i: &Interner) -> Tok<Self::Interned> {
let rep = i.master.as_ref().unwrap().request(api::ExternStrv(self)).await; let rep = i.0.master.as_ref().unwrap().request(api::ExternStrv(self)).await;
let data = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await; let data = futures::future::join_all(rep.into_iter().map(|m| i.ex(m))).await;
Tok::new(Rc::new(data), self) Tok::new(Rc::new(data), self)
} }
@@ -217,24 +217,26 @@ pub struct TypedInterners {
} }
#[derive(Default)] #[derive(Default)]
pub struct Interner { pub struct InternerData {
interners: Mutex<TypedInterners>, interners: Mutex<TypedInterners>,
master: Option<Box<dyn DynRequester<Transfer = api::IntReq>>>, master: Option<Box<dyn DynRequester<Transfer = api::IntReq>>>,
} }
#[derive(Clone, Default)]
pub struct Interner(Rc<InternerData>);
impl Interner { impl Interner {
pub fn new_master() -> Self { Self::default() } pub fn new_master() -> Self { Self::default() }
pub fn new_replica(req: impl DynRequester<Transfer = api::IntReq> + 'static) -> Self { pub fn new_replica(req: impl DynRequester<Transfer = api::IntReq> + 'static) -> Self {
Self { master: Some(Box::new(req)), interners: Mutex::default() } Self(Rc::new(InternerData { master: Some(Box::new(req)), interners: Mutex::default() }))
} }
/// Intern some data; query its identifier if not known locally /// Intern some data; query its identifier if not known locally
pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> { pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
let data = t.get_owned(); let data = t.get_owned();
let mut g = self.interners.lock().await; let mut g = self.0.interners.lock().await;
let typed = T::bimap(&mut g); let typed = T::bimap(&mut g);
if let Some(tok) = typed.by_value(&data) { if let Some(tok) = typed.by_value(&data) {
return tok; return tok;
} }
let marker = match &self.master { let marker = match &self.0.master {
Some(c) => data.clone().intern(&**c).await, Some(c) => data.clone().intern(&**c).await,
None => None =>
T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()), T::Marker::from_id(NonZeroU64::new(ID.fetch_add(1, atomic::Ordering::Relaxed)).unwrap()),
@@ -245,29 +247,29 @@ impl Interner {
} }
/// Extern an identifier; query the data it represents if not known locally /// Extern an identifier; query the data it represents if not known locally
pub async fn ex<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> { pub async fn ex<M: InternMarker>(&self, marker: M) -> Tok<M::Interned> {
if let Some(tok) = M::Interned::bimap(&mut *self.interners.lock().await).by_marker(marker) { if let Some(tok) = M::Interned::bimap(&mut *self.0.interners.lock().await).by_marker(marker) {
return tok; return tok;
} }
assert!(self.master.is_some(), "ID not in local interner and this is master"); assert!(self.0.master.is_some(), "ID not in local interner and this is master");
let token = marker.resolve(self).await; let token = marker.resolve(self).await;
M::Interned::bimap(&mut *self.interners.lock().await).insert(token.clone()); M::Interned::bimap(&mut *self.0.interners.lock().await).insert(token.clone());
token token
} }
pub async fn sweep_replica(&self) -> api::Retained { pub async fn sweep_replica(&self) -> api::Retained {
assert!(self.master.is_some(), "Not a replica"); assert!(self.0.master.is_some(), "Not a replica");
let mut g = self.interners.lock().await; let mut g = self.0.interners.lock().await;
api::Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() } api::Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() }
} }
pub async fn sweep_master(&self, retained: api::Retained) { pub async fn sweep_master(&self, retained: api::Retained) {
assert!(self.master.is_none(), "Not master"); assert!(self.0.master.is_none(), "Not master");
let mut g = self.interners.lock().await; let mut g = self.0.interners.lock().await;
g.strings.sweep_master(retained.strings.into_iter().collect()); g.strings.sweep_master(retained.strings.into_iter().collect());
g.vecs.sweep_master(retained.vecs.into_iter().collect()); g.vecs.sweep_master(retained.vecs.into_iter().collect());
} }
} }
impl fmt::Debug for Interner { impl fmt::Debug for Interner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Interner{{ replica: {} }}", self.master.is_none()) write!(f, "Interner{{ replica: {} }}", self.0.master.is_none())
} }
} }

View File

@@ -1,9 +1,7 @@
use std::num::IntErrorKind; use std::num::IntErrorKind;
use std::ops::Range; use std::ops::Range;
use num_traits::ToPrimitive;
use ordered_float::NotNan; use ordered_float::NotNan;
use rust_decimal::Decimal;
use crate::error::{OrcErr, mk_err}; use crate::error::{OrcErr, mk_err};
use crate::interner::Interner; use crate::interner::Interner;
@@ -12,24 +10,17 @@ use crate::location::Pos;
/// A number, either floating point or unsigned int, parsed by Orchid. /// A number, either floating point or unsigned int, parsed by Orchid.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Numeric { pub enum Numeric {
/// A nonnegative integer /// An integer
Uint(u64), Int(i64),
/// A binary float other than NaN /// A binary float other than NaN
Float(NotNan<f64>), Float(NotNan<f64>),
/// A decimal number
Decimal(Decimal),
} }
impl Numeric { impl Numeric {
pub fn decimal(num: i64, scale: u32) -> Self { Self::Decimal(Decimal::new(num, scale)) }
pub fn float(value: f64) -> Self { Self::Float(NotNan::new(value).unwrap()) } pub fn float(value: f64) -> Self { Self::Float(NotNan::new(value).unwrap()) }
pub fn to_f64(self) -> NotNan<f64> { pub fn to_f64(self) -> NotNan<f64> {
match self { match self {
Self::Float(f) => f, Self::Float(f) => f,
Self::Decimal(d) => { Self::Int(i) => NotNan::new(i as f64).expect("int cannot be NaN"),
let f = d.to_f64().expect("This is apparently always possible");
NotNan::new(f).expect("decimal was nan")
},
Self::Uint(i) => NotNan::new(i as f64).expect("int cannot be NaN"),
} }
} }
} }
@@ -77,31 +68,31 @@ pub async fn num_to_err(NumError { kind, range }: NumError, offset: u32, i: &Int
/// Parse a numbre literal out of text /// Parse a numbre literal out of text
pub fn parse_num(string: &str) -> Result<Numeric, NumError> { pub fn parse_num(string: &str) -> Result<Numeric, NumError> {
let overflow_err = NumError { range: 0..string.len(), kind: NumErrorKind::Overflow }; let overflow_e = NumError { range: 0..string.len(), kind: NumErrorKind::Overflow };
let (radix, noprefix, pos) = (string.strip_prefix("0x").map(|s| (16u8, s, 2))) let (radix, noprefix, pos) = (string.strip_prefix("0x").map(|s| (16u8, s, 2)))
.or_else(|| string.strip_prefix("0b").map(|s| (2u8, s, 2))) .or_else(|| string.strip_prefix("0b").map(|s| (2u8, s, 2)))
.or_else(|| string.strip_prefix("0o").map(|s| (8u8, s, 2))) .or_else(|| string.strip_prefix("0o").map(|s| (8u8, s, 2)))
.unwrap_or((10u8, string, 0)); .unwrap_or((10u8, string, 0));
eprintln!("({radix}, {noprefix}, {pos})"); eprintln!("({radix}, {noprefix}, {pos})");
// identity // identity
let (base, exponent) = match noprefix.split_once('p') { let (base_s, exponent) = match noprefix.split_once('p') {
Some((b, e)) => { Some((b, e)) => {
let (s, d, len) = e.strip_prefix('-').map_or((1, e, 0), |ue| (-1, ue, 1)); let (s, d, len) = e.strip_prefix('-').map_or((1, e, 0), |ue| (-1, ue, 1));
(b, s * int_parse(d, 10, pos + b.len() + 1 + len)? as i32) (b, s * int_parse(d, 10, pos + b.len() + 1 + len)? as i32)
}, },
None => (noprefix, 0), None => (noprefix, 0),
}; };
eprintln!("({base},{exponent})"); eprintln!("({base_s},{exponent})");
match base.split_once('.') { match base_s.split_once('.') {
None => { None => {
let base_usize = int_parse(base, radix, pos)?; let base = int_parse(base_s, radix, pos)?;
if let Ok(pos_exp) = u32::try_from(exponent) { if let Ok(pos_exp) = u32::try_from(exponent) {
if let Some(radical) = u64::from(radix).checked_pow(pos_exp) { if let Some(radical) = u64::from(radix).checked_pow(pos_exp) {
let number = base_usize.checked_mul(radical).ok_or(overflow_err)?; let num = base.checked_mul(radical).and_then(|m| m.try_into().ok()).ok_or(overflow_e)?;
return Ok(Numeric::Uint(number)); return Ok(Numeric::Int(num));
} }
} }
let f = (base_usize as f64) * (radix as f64).powi(exponent); let f = (base as f64) * (radix as f64).powi(exponent);
let err = NumError { range: 0..string.len(), kind: NumErrorKind::NaN }; let err = NumError { range: 0..string.len(), kind: NumErrorKind::NaN };
Ok(Numeric::Float(NotNan::new(f).map_err(|_| err)?)) Ok(Numeric::Float(NotNan::new(f).map_err(|_| err)?))
}, },
@@ -109,25 +100,9 @@ pub fn parse_num(string: &str) -> Result<Numeric, NumError> {
let whole_n = int_parse(whole, radix, pos)?; let whole_n = int_parse(whole, radix, pos)?;
let part_n = int_parse(part, radix, pos + whole.len() + 1)?; let part_n = int_parse(part, radix, pos + whole.len() + 1)?;
let scale = part.chars().filter(|c| *c != '_').count() as u32; let scale = part.chars().filter(|c| *c != '_').count() as u32;
if radix == 10 {
let scaled_unit = 10u64.checked_pow(scale).ok_or(overflow_err.clone())?;
let scaled_n = i128::from(whole_n) * i128::from(scaled_unit) + i128::from(part_n);
let decimal = Decimal::from_i128_with_scale(scaled_n, scale);
let p = if let Ok(uexp) = u32::try_from(exponent) {
let e_multiplier = 10i64.checked_pow(uexp).ok_or(overflow_err)?;
Decimal::new(e_multiplier, 0)
} else {
let inv_oom = u32::try_from(-exponent).map_err(|_| overflow_err)?;
eprintln!("inv_oom: {inv_oom}");
Decimal::new(1, inv_oom)
};
eprintln!("({scaled_n}, {scale}, {p})");
Ok(Numeric::Decimal(decimal * p))
} else {
let real_val = whole_n as f64 + (part_n as f64 / (radix as f64).powi(scale as i32)); let real_val = whole_n as f64 + (part_n as f64 / (radix as f64).powi(scale as i32));
let f = real_val * (radix as f64).powi(exponent); let f = real_val * (radix as f64).powi(exponent);
Ok(Numeric::Float(NotNan::new(f).expect("None of the inputs are NaN"))) Ok(Numeric::Float(NotNan::new(f).expect("None of the inputs are NaN")))
}
}, },
} }
} }
@@ -168,7 +143,7 @@ mod test {
#[test] #[test]
fn just_ints() { fn just_ints() {
let test = |s, n| assert_eq!(parse_num(s), Ok(Numeric::Uint(n))); let test = |s, n| assert_eq!(parse_num(s), Ok(Numeric::Int(n)));
test("12345", 12345); test("12345", 12345);
test("0xcafebabe", 0xcafebabe); test("0xcafebabe", 0xcafebabe);
test("0o751", 0o751); test("0o751", 0o751);
@@ -178,11 +153,11 @@ mod test {
#[test] #[test]
fn decimals() { fn decimals() {
let test = |s, n| assert_eq!(parse_num(s), Ok(n)); let test = |s, n| assert_eq!(parse_num(s), Ok(n));
test("3.1417", Numeric::decimal(31417, 4)); test("3.1417", Numeric::float(3.1417));
test("0xf.cafe", Numeric::float(0xf as f64 + 0xcafe as f64 / 0x10000 as f64)); test("0xf.cafe", Numeric::float(0xf as f64 + 0xcafe as f64 / 0x10000 as f64));
test("34p3", Numeric::Uint(34000)); test("34p3", Numeric::Int(34000));
test("0x2p3", Numeric::Uint(0x2 * 0x1000)); test("0x2p3", Numeric::Int(0x2 * 0x1000));
test("1.5p3", Numeric::decimal(1500, 0)); test("1.5p3", Numeric::float(1500.0));
test("0x2.5p3", Numeric::float((0x25 * 0x100) as f64)); test("0x2.5p3", Numeric::float((0x25 * 0x100) as f64));
} }
} }

View File

@@ -42,6 +42,13 @@ pub trait AtomicVariant {}
pub trait Atomic: 'static + Sized { pub trait Atomic: 'static + Sized {
type Variant: AtomicVariant; type Variant: AtomicVariant;
type Data: Clone + Coding + Sized + 'static; type Data: Clone + Coding + Sized + 'static;
/// Register handlers for IPC calls. If this atom implements [Supports], you
/// should register your implementations here. If this atom doesn't
/// participate in IPC at all, use the below body.
/// ```
/// MethodSetBuilder::new()
/// ```
// this method isn't default-implemented to prevent bugs from forgetting to register IPC requests.
fn reg_reqs() -> MethodSetBuilder<Self>; fn reg_reqs() -> MethodSetBuilder<Self>;
} }
impl<A: Atomic> AtomCard for A { impl<A: Atomic> AtomCard for A {
@@ -106,9 +113,9 @@ impl ForeignAtom<'static> {
ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos } ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos }
} }
pub async fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> { pub async fn request<M: AtomMethod>(&self, m: M) -> Option<M::Response> {
let rep = (self.ctx.reqnot.request(api::Fwd( let rep = (self.ctx.reqnot().request(api::Fwd(
self.atom.clone(), self.atom.clone(),
Sym::parse(M::NAME, &self.ctx.i).await.unwrap().tok().to_api(), Sym::parse(M::NAME, self.ctx.i()).await.unwrap().tok().to_api(),
enc_vec(&m).await, enc_vec(&m).await,
))) )))
.await?; .await?;
@@ -125,7 +132,7 @@ impl fmt::Debug for ForeignAtom<'_> {
} }
impl Format for ForeignAtom<'_> { impl Format for ForeignAtom<'_> {
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 {
FmtUnit::from_api(&self.ctx.reqnot.request(api::ExtAtomPrint(self.atom.clone())).await) FmtUnit::from_api(&self.ctx.reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
} }
} }
impl AtomRepr for ForeignAtom<'_> { impl AtomRepr for ForeignAtom<'_> {
@@ -145,7 +152,7 @@ pub struct NotTypAtom {
impl NotTypAtom { impl NotTypAtom {
pub async fn mk_err(&self) -> OrcErr { pub async fn mk_err(&self) -> OrcErr {
mk_err( mk_err(
self.ctx.i.i("Not the expected type").await, self.ctx.i().i("Not the expected type").await,
format!("This expression is not a {}", self.typ.name()), format!("This expression is not a {}", self.typ.name()),
[self.pos.clone().into()], [self.pos.clone().into()],
) )
@@ -192,7 +199,7 @@ impl<A: AtomCard> MethodSetBuilder<A> {
handlers: stream::from_iter(self.handlers.iter()) handlers: stream::from_iter(self.handlers.iter())
.then(|(k, v)| { .then(|(k, v)| {
clone!(ctx; async move { clone!(ctx; async move {
(Sym::parse(k, &ctx.i).await.unwrap(), v.clone()) (Sym::parse(k, ctx.i()).await.unwrap(), v.clone())
}) })
}) })
.collect() .collect()
@@ -257,9 +264,9 @@ impl<A: AtomicFeatures> TypAtom<'_, A> {
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
where A: Supports<M> { where A: Supports<M> {
M::Response::decode(Pin::new( M::Response::decode(Pin::new(
&mut &(self.data.ctx.reqnot.request(api::Fwd( &mut &(self.data.ctx.reqnot().request(api::Fwd(
self.data.atom.clone(), self.data.atom.clone(),
Sym::parse(M::NAME, &self.data.ctx.i).await.unwrap().tok().to_api(), Sym::parse(M::NAME, self.data.ctx.i()).await.unwrap().tok().to_api(),
enc_vec(&req).await, enc_vec(&req).await,
))) )))
.await .await
@@ -275,7 +282,7 @@ impl<A: AtomicFeatures> Deref for TypAtom<'_, A> {
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx); pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx);
impl FmtCtx for AtomCtx<'_> { impl FmtCtx for AtomCtx<'_> {
fn i(&self) -> &Interner { &self.2.i } fn i(&self) -> &Interner { self.2.i() }
} }
pub trait AtomDynfo: 'static { pub trait AtomDynfo: 'static {

View File

@@ -28,20 +28,23 @@ use crate::atom::{
}; };
use crate::expr::{Expr, ExprHandle}; use crate::expr::{Expr, ExprHandle};
use crate::gen_expr::{GExpr, bot}; use crate::gen_expr::{GExpr, bot};
use crate::system::SysCtx; use crate::system::{SysCtx, SysCtxEntry};
use crate::system_ctor::CtedObj;
pub struct OwnedVariant; pub struct OwnedVariant;
impl AtomicVariant for OwnedVariant {} impl AtomicVariant for OwnedVariant {}
impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A { impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> for A {
fn _factory(self) -> AtomFactory { fn _factory(self) -> AtomFactory {
AtomFactory::new(move |ctx| async move { AtomFactory::new(move |ctx| async move {
let serial = ctx.obj_store.0.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let serial =
ctx.get_or_default::<ObjStore>().next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap()); let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap());
let (typ_id, _) = get_info::<A>(ctx.cted.inst().card()); let (typ_id, _) = get_info::<A>(ctx.get::<CtedObj>().inst().card());
let mut data = enc_vec(&typ_id).await; let mut data = enc_vec(&typ_id).await;
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await; self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
ctx.obj_store.1.read().await.insert(atom_id, Box::new(self)); ctx.get_or_default::<ObjStore>().objects.read().await.insert(atom_id, Box::new(self));
api::Atom { drop: Some(atom_id), data, owner: ctx.id } eprintln!("Created atom {:?} of type {}", atom_id, type_name::<A>());
api::Atom { drop: Some(atom_id), data, owner: ctx.sys_id() }
}) })
} }
fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } } fn _info() -> Self::_Info { OwnedAtomDynfo { msbuild: A::reg_reqs(), ms: OnceCell::new() } }
@@ -55,8 +58,9 @@ pub(crate) struct AtomReadGuard<'a> {
} }
impl<'a> AtomReadGuard<'a> { impl<'a> AtomReadGuard<'a> {
async fn new(id: api::AtomId, ctx: &'a SysCtx) -> Self { async fn new(id: api::AtomId, ctx: &'a SysCtx) -> Self {
let guard = ctx.obj_store.1.read().await; let guard = ctx.get_or_default::<ObjStore>().objects.read().await;
assert!(guard.get(&id).is_some(), "Received invalid atom ID: {}", id.0); let valid = guard.iter().map(|i| i.0).collect_vec();
assert!(guard.get(&id).is_some(), "Received invalid atom ID: {:?} not in {:?}", id, valid);
Self { id, guard } Self { id, guard }
} }
} }
@@ -66,7 +70,7 @@ impl Deref for AtomReadGuard<'_> {
} }
pub(crate) async fn take_atom(id: api::AtomId, ctx: &SysCtx) -> Box<dyn DynOwnedAtom> { pub(crate) async fn take_atom(id: api::AtomId, ctx: &SysCtx) -> Box<dyn DynOwnedAtom> {
let mut g = ctx.obj_store.1.write().await; let mut g = ctx.get_or_default::<ObjStore>().objects.write().await;
g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0)) g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0))
} }
@@ -219,7 +223,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>; fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
#[allow(unused_variables)] #[allow(unused_variables)]
fn call_ref(&self, arg: ExprHandle) -> impl Future<Output = GExpr> { fn call_ref(&self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(&arg.ctx.i).await]) } async move { bot([err_not_callable(arg.ctx.i()).await]) }
} }
fn call(self, arg: ExprHandle) -> impl Future<Output = GExpr> { fn call(self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async { async {
@@ -231,7 +235,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> { fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
async move { Err(err_not_command(&ctx.i).await.into()) } async move { Err(err_not_command(ctx.i()).await.into()) }
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} } fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} }
@@ -313,4 +317,9 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
} }
} }
pub type ObjStore = Rc<(AtomicU64, RwLock<MemoMap<api::AtomId, Box<dyn DynOwnedAtom>>>)>; #[derive(Default)]
struct ObjStore {
next_id: AtomicU64,
objects: RwLock<MemoMap<api::AtomId, Box<dyn DynOwnedAtom>>>,
}
impl SysCtxEntry for ObjStore {}

View File

@@ -19,16 +19,17 @@ use crate::atom::{
use crate::expr::ExprHandle; use crate::expr::ExprHandle;
use crate::gen_expr::{GExpr, bot}; use crate::gen_expr::{GExpr, bot};
use crate::system::SysCtx; use crate::system::SysCtx;
use crate::system_ctor::CtedObj;
pub struct ThinVariant; pub struct ThinVariant;
impl AtomicVariant for ThinVariant {} impl AtomicVariant for ThinVariant {}
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A { impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
fn _factory(self) -> AtomFactory { fn _factory(self) -> AtomFactory {
AtomFactory::new(move |ctx| async move { AtomFactory::new(move |ctx| async move {
let (id, _) = get_info::<A>(ctx.cted.inst().card()); let (id, _) = get_info::<A>(ctx.get::<CtedObj>().inst().card());
let mut buf = enc_vec(&id).await; let mut buf = enc_vec(&id).await;
self.encode(Pin::new(&mut buf)).await; self.encode(Pin::new(&mut buf)).await;
api::Atom { drop: None, data: buf, owner: ctx.id } api::Atom { drop: None, data: buf, owner: ctx.sys_id() }
}) })
} }
fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } } fn _info() -> Self::_Info { ThinAtomDynfo { msbuild: Self::reg_reqs(), ms: OnceCell::new() } }
@@ -106,7 +107,7 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> { fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> {
async move { async move {
let string_self = T::decode(Pin::new(&mut &buf[..])).await.print(ctx.clone()).await; let string_self = T::decode(Pin::new(&mut &buf[..])).await.print(ctx.clone()).await;
writeln!(ctx.logger, "Received drop signal for non-drop atom {string_self:?}"); writeln!(ctx.logger(), "Received drop signal for non-drop atom {string_self:?}");
} }
.boxed_local() .boxed_local()
} }
@@ -117,11 +118,11 @@ pub trait ThinAtom:
{ {
#[allow(unused_variables)] #[allow(unused_variables)]
fn call(&self, arg: ExprHandle) -> impl Future<Output = GExpr> { fn call(&self, arg: ExprHandle) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(&arg.ctx.i).await]) } async move { bot([err_not_callable(arg.ctx.i()).await]) }
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> { fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
async move { Err(err_not_command(&ctx.i).await.into()) } async move { Err(err_not_command(ctx.i()).await.into()) }
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> { fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {

View File

@@ -34,10 +34,10 @@ async fn err_type(pos: Pos, i: &Interner) -> OrcErr {
impl<A: AtomicFeatures> TryFromExpr for TypAtom<'_, A> { impl<A: AtomicFeatures> TryFromExpr for TypAtom<'_, A> {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> { async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match expr.atom().await { match expr.atom().await {
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), &ex.ctx().i).await.into()), Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), ex.ctx().i()).await.into()),
Ok(f) => match downcast_atom::<A>(f).await { Ok(f) => match downcast_atom::<A>(f).await {
Ok(a) => Ok(a), Ok(a) => Ok(a),
Err(f) => Err(err_type(f.pos(), &f.ctx().i).await.into()), Err(f) => Err(err_type(f.pos(), f.ctx().i()).await.into()),
}, },
} }
} }

View File

@@ -15,7 +15,7 @@ use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, StreamExt}; use futures::{FutureExt, StreamExt};
use hashbrown::HashMap; use hashbrown::HashMap;
use itertools::Itertools; use itertools::Itertools;
use orchid_api::ApplyMacro; use orchid_api::{ApplyMacro, ExtMsgSet};
use orchid_api_traits::{Decode, Encode, enc_vec}; use orchid_api_traits::{Decode, Encode, enc_vec};
use orchid_base::builtin::{ExtPort, Spawner}; use orchid_base::builtin::{ExtPort, Spawner};
use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter}; use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter};
@@ -32,14 +32,14 @@ use trait_set::trait_set;
use crate::api; use crate::api;
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId}; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId};
use crate::atom_owned::{ObjStore, take_atom}; use crate::atom_owned::take_atom;
use crate::fs::VirtFS; use crate::fs::VirtFS;
use crate::lexer::{LexContext, err_cascade, err_not_applicable}; use crate::lexer::{LexContext, err_cascade, err_not_applicable};
use crate::macros::{Rule, RuleCtx}; use crate::macros::{Rule, RuleCtx};
use crate::msg::{recv_parent_msg, send_parent_msg}; use crate::msg::{recv_parent_msg, send_parent_msg};
use crate::system::{SysCtx, atom_by_idx}; use crate::system::{SysCtx, atom_by_idx};
use crate::system_ctor::{CtedObj, DynSystemCtor}; use crate::system_ctor::{CtedObj, DynSystemCtor};
use crate::tree::{GenTok, GenTokTree, LazyMemberFactory, TIACtxImpl, do_extra}; use crate::tree::{GenTok, GenTokTree, LazyMemberFactory, TreeIntoApiCtxImpl, do_extra};
pub type ExtReq<'a> = RequestHandle<'a, api::ExtMsgSet>; pub type ExtReq<'a> = RequestHandle<'a, api::ExtMsgSet>;
pub type ExtReqNot = ReqNot<api::ExtMsgSet>; pub type ExtReqNot = ReqNot<api::ExtMsgSet>;
@@ -61,11 +61,11 @@ pub enum MemberRecord {
} }
pub struct SystemRecord { pub struct SystemRecord {
cted: CtedObj,
vfses: HashMap<api::VfsId, &'static dyn VirtFS>, vfses: HashMap<api::VfsId, &'static dyn VirtFS>,
declfs: api::EagerVfs, declfs: api::EagerVfs,
lazy_members: HashMap<api::TreeId, MemberRecord>, lazy_members: HashMap<api::TreeId, MemberRecord>,
rules: HashMap<api::MacroId, Rc<Rule>>, rules: HashMap<api::MacroId, Rc<Rule>>,
ctx: SysCtx,
} }
trait_set! { trait_set! {
@@ -78,14 +78,13 @@ trait_set! {
} }
pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>( pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>(
get_sys_ctx: &impl Fn(api::SysId, ReqNot<api::ExtMsgSet>) -> F, get_sys_ctx: &impl Fn(api::SysId) -> F,
reqnot: ReqNot<api::ExtMsgSet>,
atom: &'a api::Atom, atom: &'a api::Atom,
cb: impl WARCallback<'a, T>, cb: impl WARCallback<'a, T>,
) -> T { ) -> T {
let mut data = &atom.data[..]; let mut data = &atom.data[..];
let ctx = get_sys_ctx(atom.owner, reqnot).await; let ctx = get_sys_ctx(atom.owner).await;
let inst = ctx.cted.inst(); let inst = ctx.get::<CtedObj>().inst();
let id = AtomTypeId::decode(Pin::new(&mut data)).await; let id = AtomTypeId::decode(Pin::new(&mut data)).await;
let atom_record = atom_by_idx(inst.card(), id.clone()).expect("Atom ID reserved"); let atom_record = atom_by_idx(inst.card(), id.clone()).expect("Atom ID reserved");
cb(atom_record, ctx, id, data).await cb(atom_record, ctx, id, data).await
@@ -108,6 +107,7 @@ pub struct ExtensionOwner {
rn: ReqNot<api::ExtMsgSet>, rn: ReqNot<api::ExtMsgSet>,
out_recv: Receiver<Vec<u8>>, out_recv: Receiver<Vec<u8>>,
out_send: Sender<Vec<u8>>, out_send: Sender<Vec<u8>>,
ext_header: api::ExtensionHeader,
} }
impl ExtPort for ExtensionOwner { impl ExtPort for ExtensionOwner {
@@ -125,6 +125,12 @@ impl ExtPort for ExtensionOwner {
.boxed_local() .boxed_local()
} }
} }
impl ExtensionOwner {
pub fn ext_header(&self) -> &api::ExtensionHeader { &self.ext_header }
// pub async fn new(data: ExtensionData, spawner: Spawner, header:
// api::HostHeader) -> Self { let decls =
// }
}
pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) { pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
let api::HostHeader { log_strategy, msg_logs } = let api::HostHeader { log_strategy, msg_logs } =
@@ -134,7 +140,7 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys)) .map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
.map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap()))) .map(|(id, sys)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap())))
.collect_vec(); .collect_vec();
let systems = Rc::new(Mutex::new(HashMap::<api::SysId, SystemRecord>::new())); let systems_lock = Rc::new(Mutex::new(HashMap::<api::SysId, SystemRecord>::new()));
api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() } api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() }
.encode(Pin::new(&mut buf)) .encode(Pin::new(&mut buf))
.await; .await;
@@ -143,41 +149,48 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
let exiting = Arc::new(AtomicBool::new(false)); let exiting = Arc::new(AtomicBool::new(false));
let logger = Logger::new(log_strategy); let logger = Logger::new(log_strategy);
let msg_logger = Logger::new(msg_logs); let msg_logger = Logger::new(msg_logs);
let interner_cell = Rc::new(RefCell::new(None::<Rc<Interner>>)); let interner_cell = Rc::new(RefCell::new(None::<Interner>));
let interner_weak = Rc::downgrade(&interner_cell); let interner_weak = Rc::downgrade(&interner_cell);
let obj_store = ObjStore::default(); let systems_weak = Rc::downgrade(&systems_lock);
let mk_ctx = clone!( let get_ctx = clone!(systems_weak; move |id: api::SysId| clone!(systems_weak; async move {
logger, systems, spawner, obj_store, interner_weak; let systems =
move |id: api::SysId, reqnot: ReqNot<api::ExtMsgSet>| { systems_weak.upgrade().expect("System table dropped before request processing done");
clone!(logger, systems, spawner, obj_store, interner_weak; async move { let x = systems.lock().await.get(&id).expect("System not found").ctx.clone();
let cted = systems.lock().await[&id].cted.clone(); x
let interner_cell = (interner_weak.upgrade()) }));
.expect("mk_ctx called after Interner rc dropped"); let init_ctx = {
let i = (interner_cell.borrow().clone()) clone!(systems_weak, interner_weak, spawner, logger);
.expect("mk_ctx called before interner initialized"); move |id: api::SysId, cted: CtedObj, reqnot: ReqNot<ExtMsgSet>| {
SysCtx { id, cted, logger, reqnot, spawner, obj_store, i: i.clone() } clone!(systems_weak, interner_weak, spawner, logger; async move {
}.boxed_local()) let interner_rc =
}); interner_weak.upgrade().expect("System construction order while shutting down");
let i = interner_rc.borrow().clone().expect("mk_ctx called very early, no interner!");
SysCtx::new(id, i, reqnot, spawner, logger, cted)
})
}
};
let rn = ReqNot::<api::ExtMsgSet>::new( let rn = ReqNot::<api::ExtMsgSet>::new(
msg_logger.clone(), msg_logger.clone(),
move |a, _| async move { send_parent_msg(a).await.unwrap() }.boxed_local(), move |a, _| async move { send_parent_msg(a).await.unwrap() }.boxed_local(),
clone!(systems, exiting, mk_ctx; move |n, reqnot| { clone!(systems_weak, exiting, get_ctx; move |n, reqnot| {
clone!(systems, exiting, mk_ctx; async move { clone!(systems_weak, exiting, get_ctx; async move {
match n { match n {
api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) => api::HostExtNotif::SystemDrop(api::SystemDrop(sys_id)) =>
mem::drop(systems.lock().await.remove(&sys_id)), if let Some(rc) = systems_weak.upgrade() {
mem::drop(rc.lock().await.remove(&sys_id))
},
api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) => { api::HostExtNotif::AtomDrop(api::AtomDrop(sys_id, atom)) => {
let ctx = mk_ctx(sys_id, reqnot).await; let ctx = get_ctx(sys_id).await;
take_atom(atom, &ctx).await.dyn_free(ctx.clone()).await take_atom(atom, &ctx).await.dyn_free(ctx.clone()).await
} }
} }
}.boxed_local()) }.boxed_local())
}), }),
{ {
clone!(systems, logger, mk_ctx, interner_weak, obj_store, spawner, decls, msg_logger); clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, spawner, decls, msg_logger);
move |hand, req| { move |hand, req| {
clone!(systems, logger, mk_ctx, interner_weak, obj_store, spawner, decls, msg_logger); clone!(logger, get_ctx, init_ctx, systems_weak, interner_weak, spawner, decls, msg_logger);
async move { async move {
let interner_cell = interner_weak.upgrade().expect("Interner dropped before request"); let interner_cell = interner_weak.upgrade().expect("Interner dropped before request");
let i = interner_cell.borrow().clone().expect("Request arrived before interner set"); let i = interner_cell.borrow().clone().expect("Request arrived before interner set");
@@ -197,21 +210,13 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
}); });
let lazy_mems = Mutex::new(HashMap::new()); let lazy_mems = Mutex::new(HashMap::new());
let rules = Mutex::new(HashMap::new()); let rules = Mutex::new(HashMap::new());
let ctx = SysCtx { let ctx = init_ctx(new_sys.id, cted.clone(), hand.reqnot()).await;
cted: cted.clone(),
id: new_sys.id,
logger: logger.clone(),
reqnot: hand.reqnot(),
i: i.clone(),
obj_store: obj_store.clone(),
spawner: spawner.clone(),
};
let const_root = stream::from_iter(cted.inst().dyn_env()) let const_root = stream::from_iter(cted.inst().dyn_env())
.then(|(k, v)| { .then(|(k, v)| {
let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules); let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules);
clone!(i, ctx; async move { clone!(i, ctx; async move {
let name = i.i(&k).await.to_api(); let name = i.i(&k).await.to_api();
let value = v.into_api(&mut TIACtxImpl { let value = v.into_api(&mut TreeIntoApiCtxImpl {
lazy_members: &mut *lazy_mems.lock().await, lazy_members: &mut *lazy_mems.lock().await,
rules: &mut *rules.lock().await, rules: &mut *rules.lock().await,
sys: ctx, sys: ctx,
@@ -229,17 +234,19 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
let record = SystemRecord { let record = SystemRecord {
declfs, declfs,
vfses, vfses,
cted, ctx,
lazy_members: lazy_mems.into_inner(), lazy_members: lazy_mems.into_inner(),
rules: rules.into_inner(), rules: rules.into_inner(),
}; };
let systems = systems_weak.upgrade().expect("System constructed during shutdown");
systems.lock().await.insert(new_sys.id, record); systems.lock().await.insert(new_sys.id, record);
hand hand
.handle(&new_sys, &api::SystemInst { lex_filter, const_root, line_types: vec![] }) .handle(&new_sys, &api::SystemInst { lex_filter, const_root, line_types: vec![] })
.await .await
}, },
api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => { api::HostExtReq::GetMember(get_tree @ api::GetMember(sys_id, tree_id)) => {
let sys_ctx = mk_ctx(sys_id, hand.reqnot()).await; let sys_ctx = get_ctx(sys_id).await;
let systems = systems_weak.upgrade().expect("Member queried during shutdown");
let mut systems_g = systems.lock().await; let mut systems_g = systems.lock().await;
let SystemRecord { lazy_members, rules, .. } = let SystemRecord { lazy_members, rules, .. } =
systems_g.get_mut(&sys_id).expect("System not found"); systems_g.get_mut(&sys_id).expect("System not found");
@@ -248,8 +255,8 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
Some(MemberRecord::Res) => panic!("This tree has already been transmitted"), Some(MemberRecord::Res) => panic!("This tree has already been transmitted"),
Some(MemberRecord::Gen(path, cb)) => (path, cb), Some(MemberRecord::Gen(path, cb)) => (path, cb),
}; };
let tree = cb.build(Sym::new(path.clone(), &i).await.unwrap()).await; let tree = cb.build(Sym::new(path.clone(), &i).await.unwrap(), sys_ctx.clone()).await;
let mut tia_ctx = TIACtxImpl { let mut tia_ctx = TreeIntoApiCtxImpl {
sys: sys_ctx, sys: sys_ctx,
path: Substack::Bottom, path: Substack::Bottom,
basepath: &path, basepath: &path,
@@ -260,32 +267,33 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await hand.handle(&get_tree, &tree.into_api(&mut tia_ctx).await).await
}, },
api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs @ api::GetVfs(sys_id))) => { api::HostExtReq::VfsReq(api::VfsReq::GetVfs(get_vfs @ api::GetVfs(sys_id))) => {
let systems = systems_weak.upgrade().expect("VFS root requested during shutdown");
let systems_g = systems.lock().await; let systems_g = systems.lock().await;
hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await
}, },
api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => { api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => {
let api::SysFwded(sys_id, payload) = fwd; let api::SysFwded(sys_id, payload) = fwd;
let ctx = mk_ctx(sys_id, hand.reqnot()).await; let ctx = get_ctx(sys_id).await;
let sys = ctx.cted.inst(); let sys = ctx.cted().inst();
sys.dyn_request(hand, payload).await sys.dyn_request(hand, payload).await
}, },
api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => { api::HostExtReq::VfsReq(api::VfsReq::VfsRead(vfs_read)) => {
let api::VfsRead(sys_id, vfs_id, path) = &vfs_read; let api::VfsRead(sys_id, vfs_id, path) = &vfs_read;
let ctx = mk_ctx(*sys_id, hand.reqnot()).await; let ctx = get_ctx(*sys_id).await;
let systems = systems_weak.upgrade().expect("VFS requested during shutdoown");
let systems_g = systems.lock().await; let systems_g = systems.lock().await;
let path = join_all(path.iter().map(|t| Tok::from_api(*t, &i))).await; let path = join_all(path.iter().map(|t| Tok::from_api(*t, &i))).await;
let vfs = systems_g[sys_id].vfses[vfs_id].load(&path, ctx).await; let vfs = systems_g[sys_id].vfses[vfs_id].load(&path, ctx).await;
hand.handle(&vfs_read, &vfs).await hand.handle(&vfs_read, &vfs).await
}, },
api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, text, pos, id }) => { api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, text, pos, id }) => {
let systems_g = systems.lock().await; let sys_ctx = get_ctx(sys).await;
let lexers = systems_g[&sys].cted.inst().dyn_lexers();
mem::drop(systems_g);
let text = Tok::from_api(text, &i).await; let text = Tok::from_api(text, &i).await;
let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text, i: &i }; let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text, i: &i };
let trigger_char = text.chars().nth(pos as usize).unwrap(); let trigger_char = text.chars().nth(pos as usize).unwrap();
let err_na = err_not_applicable(&i).await; let err_na = err_not_applicable(&i).await;
let err_cascade = err_cascade(&i).await; let err_cascade = err_cascade(&i).await;
let lexers = sys_ctx.cted().inst().dyn_lexers();
for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) { for lx in lexers.iter().filter(|l| char_filter_match(l.char_filter(), trigger_char)) {
match lx.lex(&text[pos as usize..], &ctx).await { match lx.lex(&text[pos as usize..], &ctx).await {
Err(e) if e.any(|e| *e == err_na) => continue, Err(e) if e.any(|e| *e == err_na) => continue,
@@ -294,10 +302,9 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
return hand.handle(&lex, &eopt).await; return hand.handle(&lex, &eopt).await;
}, },
Ok((s, expr)) => { Ok((s, expr)) => {
let ctx = mk_ctx(sys, hand.reqnot()).await;
let expr = expr let expr = expr
.to_api(&mut |f, r| { .to_api(&mut |f, r| {
clone!(ctx; async move { do_extra(f, r, ctx).await }).boxed_local() clone!(sys_ctx; async move { do_extra(f, r, sys_ctx).await }).boxed_local()
}) })
.await; .await;
let pos = (text.len() - s.len()) as u32; let pos = (text.len() - s.len()) as u32;
@@ -310,8 +317,8 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
}, },
api::HostExtReq::ParseLine(pline) => { api::HostExtReq::ParseLine(pline) => {
let api::ParseLine { exported, comments, sys, line } = &pline; let api::ParseLine { exported, comments, sys, line } = &pline;
let mut ctx = mk_ctx(*sys, hand.reqnot()).await; let mut ctx = get_ctx(*sys).await;
let parsers = ctx.cted.inst().dyn_parsers(); let parsers = ctx.cted().inst().dyn_parsers();
let comments = join_all(comments.iter().map(|c| Comment::from_api(c, &i))).await; let comments = join_all(comments.iter().map(|c| Comment::from_api(c, &i))).await;
let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx, &i).await; let line: Vec<GenTokTree> = ttv_from_api(line, &mut ctx, &i).await;
let snip = Snippet::new(line.first().expect("Empty line"), &line, &i); let snip = Snippet::new(line.first().expect("Empty line"), &line, &i);
@@ -337,7 +344,7 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
api::HostExtReq::AtomReq(atom_req) => { api::HostExtReq::AtomReq(atom_req) => {
let atom = atom_req.get_atom(); let atom = atom_req.get_atom();
let atom_req = atom_req.clone(); let atom_req = atom_req.clone();
with_atom_record(&mk_ctx, hand.reqnot(), atom, move |nfo, ctx, id, buf| { with_atom_record(&get_ctx, atom, move |nfo, ctx, id, buf| {
async move { async move {
let actx = AtomCtx(buf, atom.drop, ctx.clone()); let actx = AtomCtx(buf, atom.drop, ctx.clone());
match &atom_req { match &atom_req {
@@ -389,16 +396,16 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
api::HostExtReq::DeserAtom(deser) => { api::HostExtReq::DeserAtom(deser) => {
let api::DeserAtom(sys, buf, refs) = &deser; let api::DeserAtom(sys, buf, refs) = &deser;
let mut read = &mut &buf[..]; let mut read = &mut &buf[..];
let ctx = mk_ctx(*sys, hand.reqnot()).await; let ctx = get_ctx(*sys).await;
let id = AtomTypeId::decode(Pin::new(&mut read)).await; let id = AtomTypeId::decode(Pin::new(&mut read)).await;
let inst = ctx.cted.inst(); let inst = ctx.cted().inst();
let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID"); let nfo = atom_by_idx(inst.card(), id).expect("Deserializing atom with invalid ID");
hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, refs).await).await hand.handle(&deser, &nfo.deserialize(ctx.clone(), read, refs).await).await
}, },
orchid_api::HostExtReq::ApplyMacro(am) => { orchid_api::HostExtReq::ApplyMacro(am) => {
let tok = hand.will_handle_as(&am); let tok = hand.will_handle_as(&am);
let ApplyMacro { id, params, run_id, sys } = am; let ApplyMacro { id, params, run_id, sys } = am;
let sys_ctx = mk_ctx(sys, hand.reqnot()).await; let sys_ctx = get_ctx(sys).await;
let mut ctx = let mut ctx =
RuleCtx { args: ahash::HashMap::default(), run_id, sys: sys_ctx.clone() }; RuleCtx { args: ahash::HashMap::default(), run_id, sys: sys_ctx.clone() };
for (k, v) in params { for (k, v) in params {
@@ -408,6 +415,7 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
); );
} }
let err_cascade = err_cascade(&i).await; let err_cascade = err_cascade(&i).await;
let systems = systems_weak.upgrade().expect("macro call during shutdown");
let systems_g = systems.lock().await; let systems_g = systems.lock().await;
let rule = &systems_g[&sys].rules[&id]; let rule = &systems_g[&sys].rules[&id];
match (rule.apply)(ctx).await { match (rule.apply)(ctx).await {
@@ -432,7 +440,7 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) {
} }
}, },
); );
*interner_cell.borrow_mut() = Some(Rc::new(Interner::new_replica(rn.clone().map()))); *interner_cell.borrow_mut() = Some(Interner::new_replica(rn.clone().map()));
while !exiting.load(Ordering::Relaxed) { while !exiting.load(Ordering::Relaxed) {
let rcvd = recv_parent_msg().await.unwrap(); let rcvd = recv_parent_msg().await.unwrap();
spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd).await }))) spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd).await })))

View File

@@ -23,7 +23,7 @@ impl ExprHandle {
pub(crate) fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } } pub(crate) fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } }
pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() } pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() }
pub async fn clone(&self) -> Self { pub async fn clone(&self) -> Self {
self.ctx.reqnot.notify(api::Acquire(self.ctx.id, self.tk)).await; self.ctx.reqnot().notify(api::Acquire(self.ctx.sys_id(), self.tk)).await;
Self { ctx: self.ctx.clone(), tk: self.tk } Self { ctx: self.ctx.clone(), tk: self.tk }
} }
} }
@@ -34,9 +34,9 @@ impl fmt::Debug for ExprHandle {
} }
impl Drop for ExprHandle { impl Drop for ExprHandle {
fn drop(&mut self) { fn drop(&mut self) {
let notif = api::Release(self.ctx.id, self.tk); let notif = api::Release(self.ctx.sys_id(), self.tk);
let SysCtx { reqnot, spawner, .. } = self.ctx.clone(); let reqnot = self.ctx.reqnot().clone();
spawner(Box::pin(async move { reqnot.notify(notif).await })) self.ctx.spawner()(Box::pin(async move { reqnot.notify(notif).await }))
} }
} }
@@ -53,13 +53,13 @@ impl Expr {
pub async fn data(&self) -> &ExprData { pub async fn data(&self) -> &ExprData {
(self.data.get_or_init(async { (self.data.get_or_init(async {
let details = self.handle.ctx.reqnot.request(api::Inspect { target: self.handle.tk }).await; let details = self.handle.ctx.reqnot().request(api::Inspect { target: self.handle.tk }).await;
let pos = Pos::from_api(&details.location, &self.handle.ctx.i).await; let pos = Pos::from_api(&details.location, self.handle.ctx.i()).await;
let kind = match details.kind { let kind = match details.kind {
api::InspectedKind::Atom(a) => api::InspectedKind::Atom(a) =>
ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())), ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())),
api::InspectedKind::Bottom(b) => api::InspectedKind::Bottom(b) =>
ExprKind::Bottom(OrcErrv::from_api(&b, &self.handle.ctx.i).await), ExprKind::Bottom(OrcErrv::from_api(&b, self.handle.ctx.i()).await),
api::InspectedKind::Opaque => ExprKind::Opaque, api::InspectedKind::Opaque => ExprKind::Opaque,
}; };
ExprData { pos, kind } ExprData { pos, kind }
@@ -83,7 +83,7 @@ impl Format for Expr {
ExprKind::Opaque => "OPAQUE".to_string().into(), ExprKind::Opaque => "OPAQUE".to_string().into(),
ExprKind::Bottom(b) => format!("Bottom({b})").into(), ExprKind::Bottom(b) => format!("Bottom({b})").into(),
ExprKind::Atom(a) => ExprKind::Atom(a) =>
FmtUnit::from_api(&self.handle.ctx.reqnot.request(ExtAtomPrint(a.atom.clone())).await), FmtUnit::from_api(&self.handle.ctx.reqnot().request(ExtAtomPrint(a.atom.clone())).await),
} }
} }
} }

View File

@@ -13,7 +13,6 @@ use never::Never;
use orchid_api_traits::Encode; use orchid_api_traits::Encode;
use orchid_base::clone; use orchid_base::clone;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::name::Sym; use orchid_base::name::Sym;
use trait_set::trait_set; use trait_set::trait_set;
@@ -22,7 +21,7 @@ use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr; use crate::conv::ToExpr;
use crate::expr::{Expr, ExprHandle}; use crate::expr::{Expr, ExprHandle};
use crate::gen_expr::GExpr; use crate::gen_expr::GExpr;
use crate::system::SysCtx; use crate::system::{SysCtx, SysCtxEntry};
trait_set! { trait_set! {
trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static; trait FunCB = Fn(Vec<Expr>) -> LocalBoxFuture<'static, OrcRes<GExpr>> + 'static;
@@ -33,9 +32,9 @@ pub trait ExprFunc<I, O>: Clone + 'static {
fn apply(&self, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>; fn apply(&self, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
} }
thread_local! { #[derive(Default)]
static FUNS: Rc<Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>> = Rc::default(); struct FunsCtx(Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>);
} impl SysCtxEntry for FunsCtx {}
/// An Atom representing a partially applied named native function. These /// An Atom representing a partially applied named native function. These
/// partial calls are serialized into the name of the native function and the /// partial calls are serialized into the name of the native function and the
@@ -50,9 +49,9 @@ pub(crate) struct Fun {
fun: Rc<dyn FunCB>, fun: Rc<dyn FunCB>,
} }
impl Fun { impl Fun {
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, f: F) -> Self { pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, ctx: SysCtx, f: F) -> Self {
let funs = FUNS.with(|funs| funs.clone()); let funs: &FunsCtx = ctx.get_or_default();
let mut fung = funs.lock().await; let mut fung = funs.0.lock().await;
let fun = if let Some(x) = fung.get(&path) { let fun = if let Some(x) = fung.get(&path) {
x.1.clone() x.1.clone()
} else { } else {
@@ -89,8 +88,8 @@ impl OwnedAtom for Fun {
} }
async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self { async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
let sys = ctx.sys(); let sys = ctx.sys();
let path = Sym::from_api(ctx.decode().await, &sys.i).await; let path = Sym::from_api(ctx.decode().await, sys.i()).await;
let (arity, fun) = FUNS.with(|f| f.clone()).lock().await.get(&path).unwrap().clone(); let (arity, fun) = sys.get_or_default::<FunsCtx>().0.lock().await.get(&path).unwrap().clone();
Self { args, arity, path, fun } Self { args, arity, path, fun }
} }
async fn print(&self, _: SysCtx) -> orchid_base::format::FmtUnit { async fn print(&self, _: SysCtx) -> orchid_base::format::FmtUnit {

View File

@@ -4,10 +4,11 @@ use ahash::HashMap;
use futures::future::{LocalBoxFuture, join_all}; use futures::future::{LocalBoxFuture, join_all};
use itertools::Itertools; use itertools::Itertools;
use never::Never; use never::Never;
use orchid_api::ExtMsgSet;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::interner::Tok; use orchid_base::interner::Tok;
use orchid_base::macros::{MTree, mtreev_from_api, mtreev_to_api}; use orchid_base::macros::{MTree, mtreev_from_api, mtreev_to_api};
use orchid_base::reqnot::Requester; use orchid_base::reqnot::{ReqNot, Requester};
use trait_set::trait_set; use trait_set::trait_set;
use crate::api; use crate::api;
@@ -44,11 +45,11 @@ impl<'a> RuleCtx<'a> {
run_id: self.run_id, run_id: self.run_id,
query: mtreev_to_api(tree, &mut |b| match *b {}).await, query: mtreev_to_api(tree, &mut |b| match *b {}).await,
}; };
let Some(treev) = self.sys.reqnot.request(req).await else { let Some(treev) = self.sys.get::<ReqNot<ExtMsgSet>>().request(req).await else {
return Err(err_cascade(&self.sys.i).await.into()); return Err(err_cascade(self.sys.i()).await.into());
}; };
static ATOM_MSG: &str = "Returned atom from Rule recursion"; static ATOM_MSG: &str = "Returned atom from Rule recursion";
Ok(mtreev_from_api(&treev, &self.sys.i, &mut |_| panic!("{ATOM_MSG}")).await) Ok(mtreev_from_api(&treev, self.sys.i(), &mut |_| panic!("{ATOM_MSG}")).await)
} }
pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a, Never>> { pub fn getv(&mut self, key: &Tok<String>) -> Vec<MTree<'a, Never>> {
self.args.remove(key).expect("Key not found") self.args.remove(key).expect("Key not found")
@@ -78,7 +79,7 @@ impl Rule {
pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MacroRule { pub(crate) async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::MacroRule {
api::MacroRule { api::MacroRule {
comments: join_all(self.comments.iter().map(|c| async { comments: join_all(self.comments.iter().map(|c| async {
api::Comment { text: ctx.sys().i.i(c).await.to_api(), location: api::Location::Inherit } api::Comment { text: ctx.sys().i().i(c).await.to_api(), location: api::Location::Inherit }
})) }))
.await, .await,
location: api::Location::Inherit, location: api::Location::Inherit,

View File

@@ -1,5 +1,5 @@
use core::fmt; use std::any::{Any, TypeId, type_name};
use std::any::{TypeId, type_name}; use std::fmt;
use std::future::Future; use std::future::Future;
use std::num::NonZero; use std::num::NonZero;
use std::pin::Pin; use std::pin::Pin;
@@ -7,6 +7,8 @@ use std::rc::Rc;
use futures::future::LocalBoxFuture; use futures::future::LocalBoxFuture;
use hashbrown::HashMap; use hashbrown::HashMap;
use memo_map::MemoMap;
use orchid_api::ExtMsgSet;
use orchid_api_traits::{Coding, Decode}; use orchid_api_traits::{Coding, Decode};
use orchid_base::boxed_iter::BoxedIter; use orchid_base::boxed_iter::BoxedIter;
use orchid_base::builtin::Spawner; use orchid_base::builtin::Spawner;
@@ -16,7 +18,6 @@ use orchid_base::reqnot::{Receipt, ReqNot};
use crate::api; use crate::api;
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info}; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info};
use crate::atom_owned::ObjStore;
use crate::entrypoint::ExtReq; use crate::entrypoint::ExtReq;
use crate::fs::DeclFs; use crate::fs::DeclFs;
use crate::func_atom::Fun; use crate::func_atom::Fun;
@@ -116,11 +117,11 @@ where A: AtomicFeatures {
let mut data = &foreign.atom.data[..]; let mut data = &foreign.atom.data[..];
let ctx = foreign.ctx.clone(); let ctx = foreign.ctx.clone();
let value = AtomTypeId::decode(Pin::new(&mut data)).await; let value = AtomTypeId::decode(Pin::new(&mut data)).await;
let own_inst = ctx.cted.inst(); let own_inst = ctx.get::<CtedObj>().inst();
let owner = if ctx.id == foreign.atom.owner { let owner = if *ctx.get::<api::SysId>() == foreign.atom.owner {
own_inst.card() own_inst.card()
} else { } else {
(ctx.cted.deps().find(|s| s.id() == foreign.atom.owner)) (ctx.get::<CtedObj>().deps().find(|s| s.id() == foreign.atom.owner))
.ok_or_else(|| foreign.clone())? .ok_or_else(|| foreign.clone())?
.get_card() .get_card()
}; };
@@ -133,18 +134,80 @@ where A: AtomicFeatures {
Ok(TypAtom { value, data: foreign }) Ok(TypAtom { value, data: foreign })
} }
// #[derive(Clone)]
// pub struct SysCtx {
// pub reqnot: ReqNot<api::ExtMsgSet>,
// pub spawner: Spawner,
// pub id: api::SysId,
// pub cted: CtedObj,
// pub logger: Logger,
// pub obj_store: ObjStore,
// pub i: Rc<Interner>,
// }
// impl fmt::Debug for SysCtx {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "SysCtx({:?})", self.id)
// }
// }
#[derive(Clone)] #[derive(Clone)]
pub struct SysCtx { pub struct SysCtx(Rc<MemoMap<TypeId, Box<dyn Any>>>);
pub reqnot: ReqNot<api::ExtMsgSet>, impl SysCtx {
pub spawner: Spawner, pub fn new(
pub id: api::SysId, id: api::SysId,
pub cted: CtedObj, i: Interner,
pub logger: Logger, reqnot: ReqNot<ExtMsgSet>,
pub obj_store: ObjStore, spawner: Spawner,
pub i: Rc<Interner>, logger: Logger,
cted: CtedObj,
) -> Self {
let this = Self(Rc::new(MemoMap::new()));
this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted);
this
}
pub fn add<T: SysCtxEntry>(&self, t: T) -> &Self {
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
self
}
pub fn get_or_insert<T: SysCtxEntry>(&self, f: impl FnOnce() -> T) -> &T {
(self.0.get_or_insert_owned(TypeId::of::<T>(), || Box::new(f())).downcast_ref())
.expect("Keyed by TypeId")
}
pub fn get_or_default<T: SysCtxEntry + Default>(&self) -> &T {
self.get_or_insert(|| {
let rc_id = self.0.as_ref() as *const _ as *const () as usize;
eprintln!("Default-initializing {} in {}", type_name::<T>(), rc_id);
T::default()
})
}
pub fn try_get<T: SysCtxEntry>(&self) -> Option<&T> {
Some(self.0.get(&TypeId::of::<T>())?.downcast_ref().expect("Keyed by TypeId"))
}
pub fn get<T: SysCtxEntry>(&self) -> &T {
self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::<T>()))
}
/// Shorthand to get the [Interner] instance
pub fn i(&self) -> &Interner { self.get::<Interner>() }
/// Shorthand to get the messaging link
pub fn reqnot(&self) -> &ReqNot<ExtMsgSet> { self.get::<ReqNot<ExtMsgSet>>() }
/// Shorthand to get the system ID
pub fn sys_id(&self) -> api::SysId { *self.get::<api::SysId>() }
/// Shorthand to get the task spawner callback
pub fn spawner(&self) -> &Spawner { self.get::<Spawner>() }
/// Shorthand to get the logger
pub fn logger(&self) -> &Logger { self.get::<Logger>() }
/// Shorthand to get the constructed system object
pub fn cted(&self) -> &CtedObj { self.get::<CtedObj>() }
} }
impl fmt::Debug for SysCtx { impl fmt::Debug for SysCtx {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SysCtx({:?})", self.id) write!(f, "SysCtx({:?})", self.sys_id())
} }
} }
pub trait SysCtxEntry: 'static + Sized {}
impl SysCtxEntry for api::SysId {}
impl SysCtxEntry for ReqNot<api::ExtMsgSet> {}
impl SysCtxEntry for Spawner {}
impl SysCtxEntry for CtedObj {}
impl SysCtxEntry for Logger {}
impl SysCtxEntry for Interner {}

View File

@@ -46,7 +46,7 @@ pub struct GenItem {
impl GenItem { impl GenItem {
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item { pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item {
let kind = match self.kind { let kind = match self.kind {
GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i.i::<String>(&n).await.to_api()), GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i().i::<String>(&n).await.to_api()),
GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx).await), GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx).await),
GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()), GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()),
GenItemKind::Macro(priority, gen_rules) => { GenItemKind::Macro(priority, gen_rules) => {
@@ -60,7 +60,7 @@ impl GenItem {
let comments = join_all(self.comments.iter().map(|c| async { let comments = join_all(self.comments.iter().map(|c| async {
api::Comment { api::Comment {
location: api::Location::Inherit, location: api::Location::Inherit,
text: ctx.sys().i.i::<String>(c).await.to_api(), text: ctx.sys().i().i::<String>(c).await.to_api(),
} }
})) }))
.await; .await;
@@ -92,8 +92,8 @@ pub fn root_mod(
(name.to_string(), kind) (name.to_string(), kind)
} }
pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> { pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
let fac = LazyMemberFactory::new(move |sym| async { let fac = LazyMemberFactory::new(move |sym, ctx| async {
return MemKind::Const(build_lambdas(Fun::new(sym, xf).await, 0)); return MemKind::Const(build_lambdas(Fun::new(sym, ctx, xf).await, 0));
fn build_lambdas(fun: Fun, i: u64) -> GExpr { fn build_lambdas(fun: Fun, i: u64) -> GExpr {
if i < fun.arity().into() { if i < fun.arity().into() {
return lambda(i, [build_lambdas(fun, i + 1)]); return lambda(i, [build_lambdas(fun, i + 1)]);
@@ -129,16 +129,16 @@ pub fn comments<'a>(
trait_set! { trait_set! {
trait LazyMemberCallback = trait LazyMemberCallback =
FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + DynClone FnOnce(Sym, SysCtx) -> LocalBoxFuture<'static, MemKind> + DynClone
} }
pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>); pub struct LazyMemberFactory(Box<dyn LazyMemberCallback>);
impl LazyMemberFactory { impl LazyMemberFactory {
pub fn new<F: Future<Output = MemKind> + 'static>( pub fn new<F: Future<Output = MemKind> + 'static>(
cb: impl FnOnce(Sym) -> F + Clone + 'static, cb: impl FnOnce(Sym, SysCtx) -> F + Clone + 'static,
) -> Self { ) -> Self {
Self(Box::new(|s| cb(s).boxed_local())) Self(Box::new(|s, ctx| cb(s, ctx).boxed_local()))
} }
pub async fn build(self, path: Sym) -> MemKind { (self.0)(path).await } pub async fn build(self, path: Sym, ctx: SysCtx) -> MemKind { (self.0)(path, ctx).await }
} }
impl Clone for LazyMemberFactory { impl Clone for LazyMemberFactory {
fn clone(&self) -> Self { Self(clone_box(&*self.0)) } fn clone(&self) -> Self { Self(clone_box(&*self.0)) }
@@ -157,7 +157,7 @@ pub struct GenMember {
} }
impl GenMember { impl GenMember {
pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member { pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member {
let name = ctx.sys().i.i::<String>(&self.name).await; let name = ctx.sys().i().i::<String>(&self.name).await;
api::Member { api::Member {
kind: self.kind.into_api(&mut ctx.push_path(name.clone())).await, kind: self.kind.into_api(&mut ctx.push_path(name.clone())).await,
name: name.to_api(), name: name.to_api(),
@@ -197,7 +197,7 @@ pub trait TreeIntoApiCtx {
fn req(&self) -> &impl ReqHandlish; fn req(&self) -> &impl ReqHandlish;
} }
pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> { pub struct TreeIntoApiCtxImpl<'a, 'b, RH: ReqHandlish> {
pub sys: SysCtx, pub sys: SysCtx,
pub basepath: &'a [Tok<String>], pub basepath: &'a [Tok<String>],
pub path: Substack<'a, Tok<String>>, pub path: Substack<'a, Tok<String>>,
@@ -206,10 +206,10 @@ pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> {
pub req: &'a RH, pub req: &'a RH,
} }
impl<RH: ReqHandlish> TreeIntoApiCtx for TIACtxImpl<'_, '_, RH> { impl<RH: ReqHandlish> TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_, RH> {
fn sys(&self) -> SysCtx { self.sys.clone() } fn sys(&self) -> SysCtx { self.sys.clone() }
fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx { fn push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx {
TIACtxImpl { TreeIntoApiCtxImpl {
req: self.req, req: self.req,
lazy_members: self.lazy_members, lazy_members: self.lazy_members,
rules: self.rules, rules: self.rules,

View File

@@ -35,7 +35,7 @@ pub struct ReqPair<R: Request>(R, Sender<R::Response>);
#[derive(destructure)] #[derive(destructure)]
pub struct ExtensionData { pub struct ExtensionData {
ctx: Ctx, ctx: Ctx,
init: ExtInit, init: Rc<ExtInit>,
reqnot: ReqNot<api::HostMsgSet>, reqnot: ReqNot<api::HostMsgSet>,
systems: Vec<SystemCtor>, systems: Vec<SystemCtor>,
logger: Logger, logger: Logger,
@@ -55,7 +55,29 @@ impl Drop for ExtensionData {
pub struct Extension(Rc<ExtensionData>); pub struct Extension(Rc<ExtensionData>);
impl Extension { impl Extension {
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> { pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| ExtensionData { Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| {
let init = Rc::new(init);
(ctx.spawn)(clone!(init, weak, ctx; Box::pin(async move {
let reqnot_opt = weak.upgrade().map(|rc| rc.reqnot.clone());
if let Some(reqnot) = reqnot_opt {
let mut repeat = true;
while repeat {
repeat = false;
(init.recv(Box::new(|msg| {
repeat = true;
Box::pin(clone!(reqnot, ctx; async move {
let msg = msg.to_vec();
let reqnot = reqnot.clone();
(ctx.spawn)(Box::pin(async move {
reqnot.receive(&msg).await;
}))
}))
})))
.await;
}
}
})));
ExtensionData {
exprs: ExprStore::default(), exprs: ExprStore::default(),
ctx: ctx.clone(), ctx: ctx.clone(),
systems: (init.systems.iter().cloned()) systems: (init.systems.iter().cloned())
@@ -118,7 +140,8 @@ impl Extension {
}, },
}, },
api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => { api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => {
let sys = ctx.system_inst(atom.owner).await.expect("owner of live atom dropped"); let sys =
ctx.system_inst(atom.owner).await.expect("owner of live atom dropped");
let reply = let reply =
sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())).await; sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())).await;
hand.handle(fw, &reply).await hand.handle(fw, &reply).await
@@ -136,7 +159,9 @@ impl Extension {
} }
hand.handle(&sl, &rep_out.recv().await.unwrap()).await hand.handle(&sl, &rep_out.recv().await.unwrap()).await
}, },
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => { api::ExtHostReq::ExprReq(api::ExprReq::Inspect(
ins @ api::Inspect { target },
)) => {
let expr = this.exprs().get_expr(target).expect("Invalid ticket"); let expr = this.exprs().get_expr(target).expect("Invalid ticket");
hand hand
.handle(&ins, &api::Inspected { .handle(&ins, &api::Inspected {
@@ -163,6 +188,7 @@ impl Extension {
} }
}, },
), ),
}
}))) })))
} }
pub(crate) fn reqnot(&self) -> &ReqNot<HostMsgSet> { &self.0.reqnot } pub(crate) fn reqnot(&self) -> &ReqNot<HostMsgSet> { &self.0.reqnot }
@@ -212,20 +238,6 @@ impl Extension {
.await; .await;
ret.transpose() ret.transpose()
} }
pub async fn recv_one(&self) {
let reqnot = self.0.reqnot.clone();
let ctx = self.ctx().clone();
(self.0.init.recv(Box::new(move |msg| {
Box::pin(async move {
let msg = msg.to_vec();
let reqnot = reqnot.clone();
(ctx.spawn)(Box::pin(async move {
reqnot.receive(&msg).await;
}))
})
})))
.await;
}
pub fn system_drop(&self, id: api::SysId) { pub fn system_drop(&self, id: api::SysId) {
let rc = self.clone(); let rc = self.clone();
(self.ctx().spawn)(Box::pin(async move { (self.ctx().spawn)(Box::pin(async move {

View File

@@ -1,7 +1,5 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::path::PathBuf;
use std::pin::Pin; use std::pin::Pin;
use std::thread;
use async_process::{self, Child, ChildStdin, ChildStdout}; use async_process::{self, Child, ChildStdin, ChildStdout};
use async_std::io::{self, BufReadExt, BufReader}; use async_std::io::{self, BufReadExt, BufReader};
@@ -22,8 +20,6 @@ pub async fn ext_command(
msg_logs: Logger, msg_logs: Logger,
ctx: Ctx, ctx: Ctx,
) -> io::Result<ExtInit> { ) -> io::Result<ExtInit> {
let prog_pbuf = PathBuf::from(cmd.get_program());
let prog = prog_pbuf.file_stem().unwrap_or(cmd.get_program()).to_string_lossy().to_string();
let mut child = async_process::Command::from(cmd) let mut child = async_process::Command::from(cmd)
.stdin(async_process::Stdio::piped()) .stdin(async_process::Stdio::piped())
.stdout(async_process::Stdio::piped()) .stdout(async_process::Stdio::piped())
@@ -36,8 +32,7 @@ pub async fn ext_command(
let mut stdout = child.stdout.take().unwrap(); let mut stdout = child.stdout.take().unwrap();
let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await; let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await;
let child_stderr = child.stderr.take().unwrap(); let child_stderr = child.stderr.take().unwrap();
thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || { (ctx.spawn)(Box::pin(async move {
async_std::task::block_on(async move {
let mut reader = BufReader::new(child_stderr); let mut reader = BufReader::new(child_stderr);
loop { loop {
let mut buf = String::new(); let mut buf = String::new();
@@ -46,8 +41,7 @@ pub async fn ext_command(
} }
logger.log(buf.strip_suffix('\n').expect("Readline implies this")); logger.log(buf.strip_suffix('\n').expect("Readline implies this"));
} }
}) }));
})?;
Ok(ExtInit { Ok(ExtInit {
header, header,
port: Box::new(Subprocess { port: Box::new(Subprocess {
@@ -87,6 +81,7 @@ impl ExtPort for Subprocess {
match recv_msg(self.stdout.lock().await.as_mut()).await { match recv_msg(self.stdout.lock().await.as_mut()).await {
Ok(msg) => cb(&msg).await, Ok(msg) => cb(&msg).await,
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => (), Err(e) if e.kind() == io::ErrorKind::BrokenPipe => (),
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => (),
Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()), Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()),
} }
}) })

View File

@@ -16,4 +16,5 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
orchid-base = { version = "0.1.0", path = "../orchid-base" } orchid-base = { version = "0.1.0", path = "../orchid-base" }
orchid-extension = { version = "0.1.0", path = "../orchid-extension" } orchid-extension = { version = "0.1.0", path = "../orchid-extension" }
ordered-float = "4.6.0" ordered-float = "4.6.0"
rust_decimal = "1.36.0"
tokio = { version = "1.43.0", features = ["full"] } tokio = { version = "1.43.0", features = ["full"] }

View File

@@ -1,6 +1,7 @@
use orchid_api_derive::Coding; use orchid_api_derive::Coding;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::format::FmtUnit; use orchid_base::format::FmtUnit;
use orchid_base::number::Numeric;
use orchid_extension::atom::{ use orchid_extension::atom::{
AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom, AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom,
}; };
@@ -9,6 +10,7 @@ use orchid_extension::conv::TryFromExpr;
use orchid_extension::expr::Expr; use orchid_extension::expr::Expr;
use orchid_extension::system::SysCtx; use orchid_extension::system::SysCtx;
use ordered_float::NotNan; use ordered_float::NotNan;
use rust_decimal::prelude::Zero;
#[derive(Clone, Debug, Coding)] #[derive(Clone, Debug, Coding)]
pub struct Int(pub i64); pub struct Int(pub i64);
@@ -38,27 +40,56 @@ impl ThinAtom for Float {
} }
impl TryFromExpr for Float { impl TryFromExpr for Float {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> { async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
TypAtom::<Float>::try_from_expr(expr).await.map(|t| t.value) Ok(Self(Num::try_from_expr(expr).await?.0.to_f64()))
} }
} }
pub enum Numeric { pub struct Num(pub Numeric);
Int(i64), impl TryFromExpr for Num {
Float(NotNan<f64>),
}
impl TryFromExpr for Numeric {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> { async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match Int::try_from_expr(expr.clone()).await { let e = match Int::try_from_expr(expr.clone()).await {
Ok(t) => Ok(Numeric::Int(t.0)), Ok(t) => return Ok(Num(Numeric::Int(t.0))),
Err(e) => Float::try_from_expr(expr).await.map(|t| Numeric::Float(t.0)).map_err(|e2| e + e2), Err(e) => e,
};
match TypAtom::<Float>::try_from_expr(expr).await {
Ok(t) => Ok(Num(Numeric::Float(t.0))),
Err(e2) => Err(e + e2),
} }
} }
} }
impl ToAtom for Numeric { impl ToAtom for Num {
fn to_atom_factory(self) -> AtomFactory { fn to_atom_factory(self) -> AtomFactory {
match self { match self.0 {
Self::Float(f) => Float(f).factory(), Numeric::Float(f) => Float(f).factory(),
Self::Int(i) => Int(i).factory(), Numeric::Int(i) => Int(i).factory(),
} }
} }
} }
/// A homogenous fixed length number array that forces all of its elements into
/// the weakest element type. This describes the argument casting behaviour of
/// most numeric operations.
pub enum HomoArray<const N: usize> {
Int([i64; N]),
Float([NotNan<f64>; N]),
}
impl<const N: usize> HomoArray<N> {
pub fn new(n: [Numeric; N]) -> Self {
let mut ints = [0i64; N];
for i in 0..N {
if let Numeric::Int(val) = n[i] {
ints[i] = val
} else {
let mut floats = [NotNan::zero(); N];
for (i, int) in ints.iter().take(i).enumerate() {
floats[i] = NotNan::new(*int as f64).expect("i64 cannot convert to f64 NaN");
}
for j in i..N {
floats[j] = n[j].to_f64();
}
return Self::Float(floats);
}
}
Self::Int(ints)
}
}

View File

@@ -1,13 +1,12 @@
use std::ops::RangeInclusive; use std::ops::RangeInclusive;
use orchid_base::error::OrcRes; use orchid_base::error::OrcRes;
use orchid_base::number::{Numeric, num_to_err, parse_num}; use orchid_base::number::{num_to_err, parse_num};
use orchid_extension::atom::AtomicFeatures; use orchid_extension::atom::ToAtom;
use orchid_extension::lexer::{LexContext, Lexer}; use orchid_extension::lexer::{LexContext, Lexer};
use orchid_extension::tree::{GenTok, GenTokTree}; use orchid_extension::tree::{GenTok, GenTokTree};
use ordered_float::NotNan;
use super::num_atom::{Float, Int}; use super::num_atom::Num;
#[derive(Default)] #[derive(Default)]
pub struct NumLexer; pub struct NumLexer;
@@ -17,9 +16,7 @@ impl Lexer for NumLexer {
let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c)); let ends_at = all.find(|c: char| !c.is_ascii_hexdigit() && !"xX._pP".contains(c));
let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len())); let (chars, tail) = all.split_at(ends_at.unwrap_or(all.len()));
let fac = match parse_num(chars) { let fac = match parse_num(chars) {
Ok(Numeric::Float(f)) => Float(f).factory(), Ok(numeric) => Num(numeric).to_atom_factory(),
Ok(Numeric::Uint(uint)) => Int(uint.try_into().unwrap()).factory(),
Ok(Numeric::Decimal(dec)) => Float(NotNan::new(dec.try_into().unwrap()).unwrap()).factory(),
Err(e) => return Err(num_to_err(e, ctx.pos(all), ctx.i).await.into()), Err(e) => return Err(num_to_err(e, ctx.pos(all), ctx.i).await.into()),
}; };
Ok((tail, GenTok::X(fac).at(ctx.pos(all)..ctx.pos(tail)))) Ok((tail, GenTok::X(fac).at(ctx.pos(all)..ctx.pos(tail))))

View File

@@ -1,6 +1,7 @@
use std::rc::Rc; use std::rc::Rc;
use never::Never; use never::Never;
use orchid_base::number::Numeric;
use orchid_base::reqnot::Receipt; use orchid_base::reqnot::Receipt;
use orchid_extension::atom::{AtomDynfo, AtomicFeatures}; use orchid_extension::atom::{AtomDynfo, AtomicFeatures};
use orchid_extension::entrypoint::ExtReq; use orchid_extension::entrypoint::ExtReq;
@@ -8,9 +9,10 @@ use orchid_extension::fs::DeclFs;
use orchid_extension::system::{System, SystemCard}; use orchid_extension::system::{System, SystemCard};
use orchid_extension::system_ctor::SystemCtor; use orchid_extension::system_ctor::SystemCtor;
use orchid_extension::tree::{MemKind, comments, fun, module, root_mod}; use orchid_extension::tree::{MemKind, comments, fun, module, root_mod};
use ordered_float::NotNan;
use crate::OrcString; use crate::OrcString;
use crate::number::num_atom::{Float, Int}; use crate::number::num_atom::{Float, HomoArray, Int, Num};
use crate::number::num_lexer::NumLexer; use crate::number::num_lexer::NumLexer;
use crate::string::str_atom::{IntStrAtom, StrAtom}; use crate::string::str_atom::{IntStrAtom, StrAtom};
use crate::string::str_lexer::StringLexer; use crate::string::str_lexer::StringLexer;
@@ -37,11 +39,39 @@ impl System for StdSystem {
fn parsers() -> Vec<orchid_extension::parser::ParserObj> { vec![] } fn parsers() -> Vec<orchid_extension::parser::ParserObj> { vec![] }
fn vfs() -> DeclFs { DeclFs::Mod(&[]) } fn vfs() -> DeclFs { DeclFs::Mod(&[]) }
fn env() -> Vec<(String, MemKind)> { fn env() -> Vec<(String, MemKind)> {
vec![root_mod("std", [], [module(true, "string", [], [comments( vec![root_mod("std", [], [
module(true, "string", [], [comments(
["Concatenate two strings"], ["Concatenate two strings"],
fun(true, "concat", |left: OrcString<'static>, right: OrcString<'static>| async move { fun(true, "concat", |left: OrcString<'static>, right: OrcString<'static>| async move {
StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await)) StrAtom::new(Rc::new(left.get_string().await.to_string() + &right.get_string().await))
}), }),
)])])] )]),
module(true, "number", [], [
fun(true, "add", |a: Num, b: Num| async move {
Num(match HomoArray::new([a.0, b.0]) {
HomoArray::Int([a, b]) => Numeric::Int(a + b),
HomoArray::Float([a, b]) => Numeric::Float(a + b),
})
}),
fun(true, "neg", |a: Num| async move {
Num(match a.0 {
Numeric::Int(i) => Numeric::Int(-i),
Numeric::Float(f) => Numeric::Float(-f),
})
}),
fun(true, "mul", |a: Num, b: Num| async move {
Num(match HomoArray::new([a.0, b.0]) {
HomoArray::Int([a, b]) => Numeric::Int(a * b),
HomoArray::Float([a, b]) => Numeric::Float(a * b),
})
}),
fun(true, "idiv", |a: Int, b: Int| async move { Int(a.0 / b.0) }),
fun(true, "imod", |a: Int, b: Int| async move { Int(a.0 % b.0) }),
fun(true, "fdiv", |a: Float, b: Float| async move { Float(a.0 / b.0) }),
fun(true, "fmod", |a: Float, b: Float| async move {
Float(a.0 - NotNan::new((a.0 / b.0).trunc()).unwrap() * b.0)
}),
]),
])]
} }
} }

View File

@@ -74,7 +74,7 @@ impl OwnedAtom for IntStrAtom {
} }
async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self { async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self {
let s = ctx.decode::<String>().await; let s = ctx.decode::<String>().await;
Self(ctx.sys().i.i(&s).await) Self(ctx.sys().i().i(&s).await)
} }
} }
@@ -92,7 +92,7 @@ pub enum OrcStringKind<'a> {
impl OrcString<'_> { impl OrcString<'_> {
pub async fn get_string(&self) -> Rc<String> { pub async fn get_string(&self) -> Rc<String> {
match &self.kind { match &self.kind {
OrcStringKind::Int(tok) => self.ctx.i.ex(**tok).await.rc(), OrcStringKind::Int(tok) => self.ctx.i().ex(**tok).await.rc(),
OrcStringKind::Val(atom) => atom.request(StringGetVal).await, OrcStringKind::Val(atom) => atom.request(StringGetVal).await,
} }
} }
@@ -106,7 +106,7 @@ impl TryFromExpr for OrcString<'static> {
let ctx = expr.ctx(); let ctx = expr.ctx();
match TypAtom::<IntStrAtom>::try_from_expr(expr).await { match TypAtom::<IntStrAtom>::try_from_expr(expr).await {
Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }), Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }),
Err(e) => Err(mk_errv(ctx.i.i("A string was expected").await, "", e.pos_iter())), Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())),
} }
} }
} }

View File

@@ -9,7 +9,6 @@ use async_stream::try_stream;
use camino::Utf8PathBuf; use camino::Utf8PathBuf;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use futures::{Stream, TryStreamExt, io}; use futures::{Stream, TryStreamExt, io};
use orchid_base::clone;
use orchid_base::error::ReporterImpl; use orchid_base::error::ReporterImpl;
use orchid_base::format::{FmtCtxImpl, Format, take_first}; use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::location::Pos; use orchid_base::location::Pos;
@@ -74,7 +73,6 @@ fn get_all_extensions<'a>(
let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()).await let init = ext_command(Command::new(exe.as_os_str()), logger.clone(), msg_logger.clone(), ctx.clone()).await
.unwrap(); .unwrap();
let ext = Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?; let ext = Extension::new(init, logger.clone(), msg_logger.clone(), ctx.clone())?;
spawn_local(clone!(ext; async move { loop { ext.recv_one().await }}));
yield ext yield ext
} }
} }
@@ -168,12 +166,8 @@ async fn main() -> io::Result<ExitCode> {
if args.verbose { if args.verbose {
println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)); println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true));
} }
let mtreev = parse_mtree( let mtreev =
Snippet::new(&lexemes[0], &lexemes, i), parse_mtree(Snippet::new(&lexemes[0], &lexemes, i), Substack::Bottom).await.unwrap();
Substack::Bottom.push(i.i("orcx").await).push(i.i("input").await),
)
.await
.unwrap();
if args.verbose { if args.verbose {
let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await; let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await;
println!("parsed: {}", take_first(&fmt, true)); println!("parsed: {}", take_first(&fmt, true));