diff --git a/Cargo.lock b/Cargo.lock index d875cdf..a6ae50a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1124,7 +1124,6 @@ dependencies = [ "ordered-float", "regex", "rust-embed", - "rust_decimal", "some_executor 0.4.0", "substack", "test_executors", @@ -1201,6 +1200,7 @@ dependencies = [ "orchid-base", "orchid-extension", "ordered-float", + "rust_decimal", "tokio", ] diff --git a/SWAP.md b/SWAP.md index f74cbe1..0603be1 100644 --- a/SWAP.md +++ b/SWAP.md @@ -1,9 +1,5 @@ ## 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 align fn atom and macros on both sides with new design. No global state. diff --git a/orchid-base/Cargo.toml b/orchid-base/Cargo.toml index a6cb15a..4944ceb 100644 --- a/orchid-base/Cargo.toml +++ b/orchid-base/Cargo.toml @@ -23,7 +23,6 @@ orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" } ordered-float = "4.6.0" regex = "1.11.1" rust-embed = "8.5.0" -rust_decimal = "1.36.0" some_executor = "0.4.0" substack = "1.1.1" test_executors = "0.3.2" diff --git a/orchid-base/src/interner.rs b/orchid-base/src/interner.rs index c0c4afa..6e70360 100644 --- a/orchid-base/src/interner.rs +++ b/orchid-base/src/interner.rs @@ -98,7 +98,7 @@ impl Interned for String { impl InternMarker for api::TStr { type Interned = String; async fn resolve(self, i: &Interner) -> Tok { - 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 from_id(id: NonZeroU64) -> Self { Self(id) } @@ -125,7 +125,7 @@ impl Interned for Vec> { impl InternMarker for api::TStrv { type Interned = Vec>; async fn resolve(self, i: &Interner) -> Tok { - 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; Tok::new(Rc::new(data), self) } @@ -217,24 +217,26 @@ pub struct TypedInterners { } #[derive(Default)] -pub struct Interner { +pub struct InternerData { interners: Mutex, master: Option>>, } +#[derive(Clone, Default)] +pub struct Interner(Rc); impl Interner { pub fn new_master() -> Self { Self::default() } pub fn new_replica(req: impl DynRequester + '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 pub async fn i(&self, t: &(impl Internable + ?Sized)) -> Tok { 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); if let Some(tok) = typed.by_value(&data) { return tok; } - let marker = match &self.master { + let marker = match &self.0.master { Some(c) => data.clone().intern(&**c).await, None => 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 pub async fn ex(&self, marker: M) -> Tok { - 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; } - 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; - M::Interned::bimap(&mut *self.interners.lock().await).insert(token.clone()); + M::Interned::bimap(&mut *self.0.interners.lock().await).insert(token.clone()); token } pub async fn sweep_replica(&self) -> api::Retained { - assert!(self.master.is_some(), "Not a replica"); - let mut g = self.interners.lock().await; + assert!(self.0.master.is_some(), "Not a replica"); + let mut g = self.0.interners.lock().await; api::Retained { strings: g.strings.sweep_replica(), vecs: g.vecs.sweep_replica() } } pub async fn sweep_master(&self, retained: api::Retained) { - assert!(self.master.is_none(), "Not master"); - let mut g = self.interners.lock().await; + assert!(self.0.master.is_none(), "Not master"); + let mut g = self.0.interners.lock().await; g.strings.sweep_master(retained.strings.into_iter().collect()); g.vecs.sweep_master(retained.vecs.into_iter().collect()); } } impl fmt::Debug for Interner { 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()) } } diff --git a/orchid-base/src/number.rs b/orchid-base/src/number.rs index 527da4a..a902a79 100644 --- a/orchid-base/src/number.rs +++ b/orchid-base/src/number.rs @@ -1,9 +1,7 @@ use std::num::IntErrorKind; use std::ops::Range; -use num_traits::ToPrimitive; use ordered_float::NotNan; -use rust_decimal::Decimal; use crate::error::{OrcErr, mk_err}; use crate::interner::Interner; @@ -12,24 +10,17 @@ use crate::location::Pos; /// A number, either floating point or unsigned int, parsed by Orchid. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Numeric { - /// A nonnegative integer - Uint(u64), + /// An integer + Int(i64), /// A binary float other than NaN Float(NotNan), - /// A decimal number - Decimal(Decimal), } 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 to_f64(self) -> NotNan { match self { Self::Float(f) => f, - Self::Decimal(d) => { - 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"), + Self::Int(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 pub fn parse_num(string: &str) -> Result { - 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))) .or_else(|| string.strip_prefix("0b").map(|s| (2u8, s, 2))) .or_else(|| string.strip_prefix("0o").map(|s| (8u8, s, 2))) .unwrap_or((10u8, string, 0)); eprintln!("({radix}, {noprefix}, {pos})"); // identity - let (base, exponent) = match noprefix.split_once('p') { + let (base_s, exponent) = match noprefix.split_once('p') { Some((b, e)) => { 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) }, None => (noprefix, 0), }; - eprintln!("({base},{exponent})"); - match base.split_once('.') { + eprintln!("({base_s},{exponent})"); + match base_s.split_once('.') { 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 Some(radical) = u64::from(radix).checked_pow(pos_exp) { - let number = base_usize.checked_mul(radical).ok_or(overflow_err)?; - return Ok(Numeric::Uint(number)); + let num = base.checked_mul(radical).and_then(|m| m.try_into().ok()).ok_or(overflow_e)?; + 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 }; Ok(Numeric::Float(NotNan::new(f).map_err(|_| err)?)) }, @@ -109,25 +100,9 @@ pub fn parse_num(string: &str) -> Result { let whole_n = int_parse(whole, radix, pos)?; let part_n = int_parse(part, radix, pos + whole.len() + 1)?; 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 f = real_val * (radix as f64).powi(exponent); - Ok(Numeric::Float(NotNan::new(f).expect("None of the inputs are NaN"))) - } + 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); + Ok(Numeric::Float(NotNan::new(f).expect("None of the inputs are NaN"))) }, } } @@ -168,7 +143,7 @@ mod test { #[test] 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("0xcafebabe", 0xcafebabe); test("0o751", 0o751); @@ -178,11 +153,11 @@ mod test { #[test] fn decimals() { 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("34p3", Numeric::Uint(34000)); - test("0x2p3", Numeric::Uint(0x2 * 0x1000)); - test("1.5p3", Numeric::decimal(1500, 0)); + test("34p3", Numeric::Int(34000)); + test("0x2p3", Numeric::Int(0x2 * 0x1000)); + test("1.5p3", Numeric::float(1500.0)); test("0x2.5p3", Numeric::float((0x25 * 0x100) as f64)); } } diff --git a/orchid-extension/src/atom.rs b/orchid-extension/src/atom.rs index bccda8e..439cc48 100644 --- a/orchid-extension/src/atom.rs +++ b/orchid-extension/src/atom.rs @@ -42,6 +42,13 @@ pub trait AtomicVariant {} pub trait Atomic: 'static + Sized { type Variant: AtomicVariant; 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; } impl AtomCard for A { @@ -106,9 +113,9 @@ impl ForeignAtom<'static> { ForeignAtom { _life: PhantomData, atom, ctx: handle.ctx.clone(), expr: Some(handle), pos } } pub async fn request(&self, m: M) -> Option { - let rep = (self.ctx.reqnot.request(api::Fwd( + let rep = (self.ctx.reqnot().request(api::Fwd( 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, ))) .await?; @@ -125,7 +132,7 @@ impl fmt::Debug for ForeignAtom<'_> { } impl Format for ForeignAtom<'_> { 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<'_> { @@ -145,7 +152,7 @@ pub struct NotTypAtom { impl NotTypAtom { pub async fn mk_err(&self) -> OrcErr { 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()), [self.pos.clone().into()], ) @@ -192,7 +199,7 @@ impl MethodSetBuilder { handlers: stream::from_iter(self.handlers.iter()) .then(|(k, v)| { clone!(ctx; async move { - (Sym::parse(k, &ctx.i).await.unwrap(), v.clone()) + (Sym::parse(k, ctx.i()).await.unwrap(), v.clone()) }) }) .collect() @@ -257,9 +264,9 @@ impl TypAtom<'_, A> { pub async fn request(&self, req: M) -> M::Response where A: Supports { 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(), - 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, ))) .await @@ -275,7 +282,7 @@ impl Deref for TypAtom<'_, A> { pub struct AtomCtx<'a>(pub &'a [u8], pub Option, pub SysCtx); impl FmtCtx for AtomCtx<'_> { - fn i(&self) -> &Interner { &self.2.i } + fn i(&self) -> &Interner { self.2.i() } } pub trait AtomDynfo: 'static { diff --git a/orchid-extension/src/atom_owned.rs b/orchid-extension/src/atom_owned.rs index 9815577..8336b67 100644 --- a/orchid-extension/src/atom_owned.rs +++ b/orchid-extension/src/atom_owned.rs @@ -28,20 +28,23 @@ use crate::atom::{ }; use crate::expr::{Expr, ExprHandle}; use crate::gen_expr::{GExpr, bot}; -use crate::system::SysCtx; +use crate::system::{SysCtx, SysCtxEntry}; +use crate::system_ctor::CtedObj; pub struct OwnedVariant; impl AtomicVariant for OwnedVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { 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::().next_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); let atom_id = api::AtomId(NonZero::new(serial + 1).unwrap()); - let (typ_id, _) = get_info::(ctx.cted.inst().card()); + let (typ_id, _) = get_info::(ctx.get::().inst().card()); let mut data = enc_vec(&typ_id).await; self.encode(Pin::<&mut Vec>::new(&mut data)).await; - ctx.obj_store.1.read().await.insert(atom_id, Box::new(self)); - api::Atom { drop: Some(atom_id), data, owner: ctx.id } + ctx.get_or_default::().objects.read().await.insert(atom_id, Box::new(self)); + eprintln!("Created atom {:?} of type {}", atom_id, type_name::()); + api::Atom { drop: Some(atom_id), data, owner: ctx.sys_id() } }) } 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> { async fn new(id: api::AtomId, ctx: &'a SysCtx) -> Self { - let guard = ctx.obj_store.1.read().await; - assert!(guard.get(&id).is_some(), "Received invalid atom ID: {}", id.0); + let guard = ctx.get_or_default::().objects.read().await; + 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 } } } @@ -66,7 +70,7 @@ impl Deref for AtomReadGuard<'_> { } pub(crate) async fn take_atom(id: api::AtomId, ctx: &SysCtx) -> Box { - let mut g = ctx.obj_store.1.write().await; + let mut g = ctx.get_or_default::().objects.write().await; g.remove(&id).unwrap_or_else(|| panic!("Received invalid atom ID: {}", id.0)) } @@ -219,7 +223,7 @@ pub trait OwnedAtom: Atomic + Any + Clone + 'static { fn val(&self) -> impl Future>; #[allow(unused_variables)] fn call_ref(&self, arg: ExprHandle) -> impl Future { - 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 { async { @@ -231,7 +235,7 @@ pub trait OwnedAtom: Atomic + Any + Clone + 'static { } #[allow(unused_variables)] fn command(self, ctx: SysCtx) -> impl Future>> { - async move { Err(err_not_command(&ctx.i).await.into()) } + async move { Err(err_not_command(ctx.i()).await.into()) } } #[allow(unused_variables)] fn free(self, ctx: SysCtx) -> impl Future { async {} } @@ -313,4 +317,9 @@ impl DynOwnedAtom for T { } } -pub type ObjStore = Rc<(AtomicU64, RwLock>>)>; +#[derive(Default)] +struct ObjStore { + next_id: AtomicU64, + objects: RwLock>>, +} +impl SysCtxEntry for ObjStore {} diff --git a/orchid-extension/src/atom_thin.rs b/orchid-extension/src/atom_thin.rs index 33f0629..6742ae6 100644 --- a/orchid-extension/src/atom_thin.rs +++ b/orchid-extension/src/atom_thin.rs @@ -19,16 +19,17 @@ use crate::atom::{ use crate::expr::ExprHandle; use crate::gen_expr::{GExpr, bot}; use crate::system::SysCtx; +use crate::system_ctor::CtedObj; pub struct ThinVariant; impl AtomicVariant for ThinVariant {} impl> AtomicFeaturesImpl for A { fn _factory(self) -> AtomFactory { AtomFactory::new(move |ctx| async move { - let (id, _) = get_info::(ctx.cted.inst().card()); + let (id, _) = get_info::(ctx.get::().inst().card()); let mut buf = enc_vec(&id).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() } } @@ -106,7 +107,7 @@ impl AtomDynfo for ThinAtomDynfo { fn drop<'a>(&'a self, AtomCtx(buf, _, ctx): AtomCtx<'a>) -> LocalBoxFuture<'a, ()> { async move { 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() } @@ -117,11 +118,11 @@ pub trait ThinAtom: { #[allow(unused_variables)] fn call(&self, arg: ExprHandle) -> impl Future { - async move { bot([err_not_callable(&arg.ctx.i).await]) } + async move { bot([err_not_callable(arg.ctx.i()).await]) } } #[allow(unused_variables)] fn command(&self, ctx: SysCtx) -> impl Future>> { - async move { Err(err_not_command(&ctx.i).await.into()) } + async move { Err(err_not_command(ctx.i()).await.into()) } } #[allow(unused_variables)] fn print(&self, ctx: SysCtx) -> impl Future { diff --git a/orchid-extension/src/conv.rs b/orchid-extension/src/conv.rs index 0d7cbaa..0130e7b 100644 --- a/orchid-extension/src/conv.rs +++ b/orchid-extension/src/conv.rs @@ -34,10 +34,10 @@ async fn err_type(pos: Pos, i: &Interner) -> OrcErr { impl TryFromExpr for TypAtom<'_, A> { async fn try_from_expr(expr: Expr) -> OrcRes { 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::(f).await { 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()), }, } } diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 7a36099..f12aed4 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -15,7 +15,7 @@ use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, StreamExt}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_api::ApplyMacro; +use orchid_api::{ApplyMacro, ExtMsgSet}; use orchid_api_traits::{Decode, Encode, enc_vec}; use orchid_base::builtin::{ExtPort, Spawner}; 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::atom::{AtomCtx, AtomDynfo, AtomTypeId}; -use crate::atom_owned::{ObjStore, take_atom}; +use crate::atom_owned::take_atom; use crate::fs::VirtFS; use crate::lexer::{LexContext, err_cascade, err_not_applicable}; use crate::macros::{Rule, RuleCtx}; use crate::msg::{recv_parent_msg, send_parent_msg}; use crate::system::{SysCtx, atom_by_idx}; 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 ExtReqNot = ReqNot; @@ -61,11 +61,11 @@ pub enum MemberRecord { } pub struct SystemRecord { - cted: CtedObj, vfses: HashMap, declfs: api::EagerVfs, lazy_members: HashMap, rules: HashMap>, + ctx: SysCtx, } trait_set! { @@ -78,14 +78,13 @@ trait_set! { } pub async fn with_atom_record<'a, F: Future, T>( - get_sys_ctx: &impl Fn(api::SysId, ReqNot) -> F, - reqnot: ReqNot, + get_sys_ctx: &impl Fn(api::SysId) -> F, atom: &'a api::Atom, cb: impl WARCallback<'a, T>, ) -> T { let mut data = &atom.data[..]; - let ctx = get_sys_ctx(atom.owner, reqnot).await; - let inst = ctx.cted.inst(); + let ctx = get_sys_ctx(atom.owner).await; + let inst = ctx.get::().inst(); let id = AtomTypeId::decode(Pin::new(&mut data)).await; let atom_record = atom_by_idx(inst.card(), id.clone()).expect("Atom ID reserved"); cb(atom_record, ctx, id, data).await @@ -108,6 +107,7 @@ pub struct ExtensionOwner { rn: ReqNot, out_recv: Receiver>, out_send: Sender>, + ext_header: api::ExtensionHeader, } impl ExtPort for ExtensionOwner { @@ -125,6 +125,12 @@ impl ExtPort for ExtensionOwner { .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) { 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)| sys.decl(api::SysDeclId(NonZero::new(id + 1).unwrap()))) .collect_vec(); - let systems = Rc::new(Mutex::new(HashMap::::new())); + let systems_lock = Rc::new(Mutex::new(HashMap::::new())); api::ExtensionHeader { name: data.name.to_string(), systems: decls.clone() } .encode(Pin::new(&mut buf)) .await; @@ -143,41 +149,48 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) { let exiting = Arc::new(AtomicBool::new(false)); let logger = Logger::new(log_strategy); let msg_logger = Logger::new(msg_logs); - let interner_cell = Rc::new(RefCell::new(None::>)); + let interner_cell = Rc::new(RefCell::new(None::)); let interner_weak = Rc::downgrade(&interner_cell); - let obj_store = ObjStore::default(); - let mk_ctx = clone!( - logger, systems, spawner, obj_store, interner_weak; - move |id: api::SysId, reqnot: ReqNot| { - clone!(logger, systems, spawner, obj_store, interner_weak; async move { - let cted = systems.lock().await[&id].cted.clone(); - let interner_cell = (interner_weak.upgrade()) - .expect("mk_ctx called after Interner rc dropped"); - let i = (interner_cell.borrow().clone()) - .expect("mk_ctx called before interner initialized"); - SysCtx { id, cted, logger, reqnot, spawner, obj_store, i: i.clone() } - }.boxed_local()) - }); + let systems_weak = Rc::downgrade(&systems_lock); + let get_ctx = clone!(systems_weak; move |id: api::SysId| clone!(systems_weak; async move { + let systems = + systems_weak.upgrade().expect("System table dropped before request processing done"); + let x = systems.lock().await.get(&id).expect("System not found").ctx.clone(); + x + })); + let init_ctx = { + clone!(systems_weak, interner_weak, spawner, logger); + move |id: api::SysId, cted: CtedObj, reqnot: ReqNot| { + clone!(systems_weak, interner_weak, spawner, logger; async move { + 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::::new( msg_logger.clone(), move |a, _| async move { send_parent_msg(a).await.unwrap() }.boxed_local(), - clone!(systems, exiting, mk_ctx; move |n, reqnot| { - clone!(systems, exiting, mk_ctx; async move { + clone!(systems_weak, exiting, get_ctx; move |n, reqnot| { + clone!(systems_weak, exiting, get_ctx; async move { match n { api::HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed), 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)) => { - 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 } } }.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| { - 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 { let interner_cell = interner_weak.upgrade().expect("Interner dropped before request"); 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 rules = Mutex::new(HashMap::new()); - let ctx = SysCtx { - 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 ctx = init_ctx(new_sys.id, cted.clone(), hand.reqnot()).await; let const_root = stream::from_iter(cted.inst().dyn_env()) .then(|(k, v)| { let (req, lazy_mems, rules) = (&hand, &lazy_mems, &rules); clone!(i, ctx; async move { 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, rules: &mut *rules.lock().await, sys: ctx, @@ -229,17 +234,19 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) { let record = SystemRecord { declfs, vfses, - cted, + ctx, lazy_members: lazy_mems.into_inner(), rules: rules.into_inner(), }; + let systems = systems_weak.upgrade().expect("System constructed during shutdown"); systems.lock().await.insert(new_sys.id, record); hand .handle(&new_sys, &api::SystemInst { lex_filter, const_root, line_types: vec![] }) .await }, 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 SystemRecord { lazy_members, rules, .. } = 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::Gen(path, cb)) => (path, cb), }; - let tree = cb.build(Sym::new(path.clone(), &i).await.unwrap()).await; - let mut tia_ctx = TIACtxImpl { + let tree = cb.build(Sym::new(path.clone(), &i).await.unwrap(), sys_ctx.clone()).await; + let mut tia_ctx = TreeIntoApiCtxImpl { sys: sys_ctx, path: Substack::Bottom, 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 }, 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; hand.handle(&get_vfs, &systems_g[&sys_id].declfs).await }, api::HostExtReq::SysReq(api::SysReq::SysFwded(fwd)) => { let api::SysFwded(sys_id, payload) = fwd; - let ctx = mk_ctx(sys_id, hand.reqnot()).await; - let sys = ctx.cted.inst(); + let ctx = get_ctx(sys_id).await; + let sys = ctx.cted().inst(); sys.dyn_request(hand, payload).await }, api::HostExtReq::VfsReq(api::VfsReq::VfsRead(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 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; hand.handle(&vfs_read, &vfs).await }, api::HostExtReq::LexExpr(lex @ api::LexExpr { sys, text, pos, id }) => { - let systems_g = systems.lock().await; - let lexers = systems_g[&sys].cted.inst().dyn_lexers(); - mem::drop(systems_g); + let sys_ctx = get_ctx(sys).await; let text = Tok::from_api(text, &i).await; let ctx = LexContext { sys, id, pos, reqnot: hand.reqnot(), text: &text, i: &i }; let trigger_char = text.chars().nth(pos as usize).unwrap(); let err_na = err_not_applicable(&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)) { match lx.lex(&text[pos as usize..], &ctx).await { 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; }, Ok((s, expr)) => { - let ctx = mk_ctx(sys, hand.reqnot()).await; let expr = expr .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; 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) => { let api::ParseLine { exported, comments, sys, line } = &pline; - let mut ctx = mk_ctx(*sys, hand.reqnot()).await; - let parsers = ctx.cted.inst().dyn_parsers(); + let mut ctx = get_ctx(*sys).await; + let parsers = ctx.cted().inst().dyn_parsers(); let comments = join_all(comments.iter().map(|c| Comment::from_api(c, &i))).await; let line: Vec = ttv_from_api(line, &mut ctx, &i).await; 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) => { let atom = atom_req.get_atom(); 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 { let actx = AtomCtx(buf, atom.drop, ctx.clone()); match &atom_req { @@ -389,16 +396,16 @@ pub async fn extension_main_logic(data: ExtensionData, spawner: Spawner) { api::HostExtReq::DeserAtom(deser) => { let api::DeserAtom(sys, buf, refs) = &deser; 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 inst = ctx.cted.inst(); + let inst = ctx.cted().inst(); 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 }, orchid_api::HostExtReq::ApplyMacro(am) => { let tok = hand.will_handle_as(&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 = RuleCtx { args: ahash::HashMap::default(), run_id, sys: sys_ctx.clone() }; 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 systems = systems_weak.upgrade().expect("macro call during shutdown"); let systems_g = systems.lock().await; let rule = &systems_g[&sys].rules[&id]; 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) { let rcvd = recv_parent_msg().await.unwrap(); spawner(Box::pin(clone!(rn; async move { rn.receive(&rcvd).await }))) diff --git a/orchid-extension/src/expr.rs b/orchid-extension/src/expr.rs index 701efd6..18c1f88 100644 --- a/orchid-extension/src/expr.rs +++ b/orchid-extension/src/expr.rs @@ -23,7 +23,7 @@ impl ExprHandle { pub(crate) fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } } pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() } 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 } } } @@ -34,9 +34,9 @@ impl fmt::Debug for ExprHandle { } impl Drop for ExprHandle { fn drop(&mut self) { - let notif = api::Release(self.ctx.id, self.tk); - let SysCtx { reqnot, spawner, .. } = self.ctx.clone(); - spawner(Box::pin(async move { reqnot.notify(notif).await })) + let notif = api::Release(self.ctx.sys_id(), self.tk); + let reqnot = self.ctx.reqnot().clone(); + self.ctx.spawner()(Box::pin(async move { reqnot.notify(notif).await })) } } @@ -53,13 +53,13 @@ impl Expr { pub async fn data(&self) -> &ExprData { (self.data.get_or_init(async { - 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 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 kind = match details.kind { api::InspectedKind::Atom(a) => ExprKind::Atom(ForeignAtom::new(self.handle.clone(), a, pos.clone())), 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, }; ExprData { pos, kind } @@ -83,7 +83,7 @@ impl Format for Expr { ExprKind::Opaque => "OPAQUE".to_string().into(), ExprKind::Bottom(b) => format!("Bottom({b})").into(), 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), } } } diff --git a/orchid-extension/src/func_atom.rs b/orchid-extension/src/func_atom.rs index aa7d5f2..69e397d 100644 --- a/orchid-extension/src/func_atom.rs +++ b/orchid-extension/src/func_atom.rs @@ -13,7 +13,6 @@ use never::Never; use orchid_api_traits::Encode; use orchid_base::clone; use orchid_base::error::OrcRes; -use orchid_base::format::{FmtCtxImpl, Format, take_first}; use orchid_base::name::Sym; use trait_set::trait_set; @@ -22,7 +21,7 @@ use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant}; use crate::conv::ToExpr; use crate::expr::{Expr, ExprHandle}; use crate::gen_expr::GExpr; -use crate::system::SysCtx; +use crate::system::{SysCtx, SysCtxEntry}; trait_set! { trait FunCB = Fn(Vec) -> LocalBoxFuture<'static, OrcRes> + 'static; @@ -33,9 +32,9 @@ pub trait ExprFunc: Clone + 'static { fn apply(&self, v: Vec) -> impl Future>; } -thread_local! { - static FUNS: Rc)>>> = Rc::default(); -} +#[derive(Default)] +struct FunsCtx(Mutex)>>); +impl SysCtxEntry for FunsCtx {} /// An Atom representing a partially applied named native function. These /// partial calls are serialized into the name of the native function and the @@ -50,9 +49,9 @@ pub(crate) struct Fun { fun: Rc, } impl Fun { - pub async fn new>(path: Sym, f: F) -> Self { - let funs = FUNS.with(|funs| funs.clone()); - let mut fung = funs.lock().await; + pub async fn new>(path: Sym, ctx: SysCtx, f: F) -> Self { + let funs: &FunsCtx = ctx.get_or_default(); + let mut fung = funs.0.lock().await; let fun = if let Some(x) = fung.get(&path) { x.1.clone() } else { @@ -89,8 +88,8 @@ impl OwnedAtom for Fun { } async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self { let sys = ctx.sys(); - let path = Sym::from_api(ctx.decode().await, &sys.i).await; - let (arity, fun) = FUNS.with(|f| f.clone()).lock().await.get(&path).unwrap().clone(); + let path = Sym::from_api(ctx.decode().await, sys.i()).await; + let (arity, fun) = sys.get_or_default::().0.lock().await.get(&path).unwrap().clone(); Self { args, arity, path, fun } } async fn print(&self, _: SysCtx) -> orchid_base::format::FmtUnit { diff --git a/orchid-extension/src/macros.rs b/orchid-extension/src/macros.rs index bb507a2..dbd102f 100644 --- a/orchid-extension/src/macros.rs +++ b/orchid-extension/src/macros.rs @@ -4,10 +4,11 @@ use ahash::HashMap; use futures::future::{LocalBoxFuture, join_all}; use itertools::Itertools; use never::Never; +use orchid_api::ExtMsgSet; use orchid_base::error::OrcRes; use orchid_base::interner::Tok; 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 crate::api; @@ -44,11 +45,11 @@ impl<'a> RuleCtx<'a> { run_id: self.run_id, query: mtreev_to_api(tree, &mut |b| match *b {}).await, }; - let Some(treev) = self.sys.reqnot.request(req).await else { - return Err(err_cascade(&self.sys.i).await.into()); + let Some(treev) = self.sys.get::>().request(req).await else { + return Err(err_cascade(self.sys.i()).await.into()); }; 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) -> Vec> { 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 { api::MacroRule { 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, location: api::Location::Inherit, diff --git a/orchid-extension/src/system.rs b/orchid-extension/src/system.rs index 7b390aa..c9524a6 100644 --- a/orchid-extension/src/system.rs +++ b/orchid-extension/src/system.rs @@ -1,5 +1,5 @@ -use core::fmt; -use std::any::{TypeId, type_name}; +use std::any::{Any, TypeId, type_name}; +use std::fmt; use std::future::Future; use std::num::NonZero; use std::pin::Pin; @@ -7,6 +7,8 @@ use std::rc::Rc; use futures::future::LocalBoxFuture; use hashbrown::HashMap; +use memo_map::MemoMap; +use orchid_api::ExtMsgSet; use orchid_api_traits::{Coding, Decode}; use orchid_base::boxed_iter::BoxedIter; use orchid_base::builtin::Spawner; @@ -16,7 +18,6 @@ use orchid_base::reqnot::{Receipt, ReqNot}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info}; -use crate::atom_owned::ObjStore; use crate::entrypoint::ExtReq; use crate::fs::DeclFs; use crate::func_atom::Fun; @@ -116,11 +117,11 @@ where A: AtomicFeatures { let mut data = &foreign.atom.data[..]; let ctx = foreign.ctx.clone(); let value = AtomTypeId::decode(Pin::new(&mut data)).await; - let own_inst = ctx.cted.inst(); - let owner = if ctx.id == foreign.atom.owner { + let own_inst = ctx.get::().inst(); + let owner = if *ctx.get::() == foreign.atom.owner { own_inst.card() } else { - (ctx.cted.deps().find(|s| s.id() == foreign.atom.owner)) + (ctx.get::().deps().find(|s| s.id() == foreign.atom.owner)) .ok_or_else(|| foreign.clone())? .get_card() }; @@ -133,18 +134,80 @@ where A: AtomicFeatures { Ok(TypAtom { value, data: foreign }) } +// #[derive(Clone)] +// pub struct SysCtx { +// pub reqnot: ReqNot, +// pub spawner: Spawner, +// pub id: api::SysId, +// pub cted: CtedObj, +// pub logger: Logger, +// pub obj_store: ObjStore, +// pub i: Rc, +// } +// impl fmt::Debug for SysCtx { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// write!(f, "SysCtx({:?})", self.id) +// } +// } + #[derive(Clone)] -pub struct SysCtx { - pub reqnot: ReqNot, - pub spawner: Spawner, - pub id: api::SysId, - pub cted: CtedObj, - pub logger: Logger, - pub obj_store: ObjStore, - pub i: Rc, +pub struct SysCtx(Rc>>); +impl SysCtx { + pub fn new( + id: api::SysId, + i: Interner, + reqnot: ReqNot, + spawner: Spawner, + 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(&self, t: T) -> &Self { + assert!(self.0.insert(TypeId::of::(), Box::new(t)), "Key already exists"); + self + } + pub fn get_or_insert(&self, f: impl FnOnce() -> T) -> &T { + (self.0.get_or_insert_owned(TypeId::of::(), || Box::new(f())).downcast_ref()) + .expect("Keyed by TypeId") + } + pub fn get_or_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::(), rc_id); + T::default() + }) + } + pub fn try_get(&self) -> Option<&T> { + Some(self.0.get(&TypeId::of::())?.downcast_ref().expect("Keyed by TypeId")) + } + pub fn get(&self) -> &T { + self.try_get().unwrap_or_else(|| panic!("Context {} missing", type_name::())) + } + /// Shorthand to get the [Interner] instance + pub fn i(&self) -> &Interner { self.get::() } + /// Shorthand to get the messaging link + pub fn reqnot(&self) -> &ReqNot { self.get::>() } + /// Shorthand to get the system ID + pub fn sys_id(&self) -> api::SysId { *self.get::() } + /// Shorthand to get the task spawner callback + pub fn spawner(&self) -> &Spawner { self.get::() } + /// Shorthand to get the logger + pub fn logger(&self) -> &Logger { self.get::() } + /// Shorthand to get the constructed system object + pub fn cted(&self) -> &CtedObj { self.get::() } } impl fmt::Debug for SysCtx { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "SysCtx({:?})", self.id) + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SysCtx({:?})", self.sys_id()) } } +pub trait SysCtxEntry: 'static + Sized {} +impl SysCtxEntry for api::SysId {} +impl SysCtxEntry for ReqNot {} +impl SysCtxEntry for Spawner {} +impl SysCtxEntry for CtedObj {} +impl SysCtxEntry for Logger {} +impl SysCtxEntry for Interner {} diff --git a/orchid-extension/src/tree.rs b/orchid-extension/src/tree.rs index dc3b49e..4918491 100644 --- a/orchid-extension/src/tree.rs +++ b/orchid-extension/src/tree.rs @@ -46,7 +46,7 @@ pub struct GenItem { impl GenItem { pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Item { let kind = match self.kind { - GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i.i::(&n).await.to_api()), + GenItemKind::Export(n) => api::ItemKind::Export(ctx.sys().i().i::(&n).await.to_api()), GenItemKind::Member(mem) => api::ItemKind::Member(mem.into_api(ctx).await), GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()), GenItemKind::Macro(priority, gen_rules) => { @@ -60,7 +60,7 @@ impl GenItem { let comments = join_all(self.comments.iter().map(|c| async { api::Comment { location: api::Location::Inherit, - text: ctx.sys().i.i::(c).await.to_api(), + text: ctx.sys().i().i::(c).await.to_api(), } })) .await; @@ -92,8 +92,8 @@ pub fn root_mod( (name.to_string(), kind) } pub fn fun(exported: bool, name: &str, xf: impl ExprFunc) -> Vec { - let fac = LazyMemberFactory::new(move |sym| async { - return MemKind::Const(build_lambdas(Fun::new(sym, xf).await, 0)); + let fac = LazyMemberFactory::new(move |sym, ctx| async { + return MemKind::Const(build_lambdas(Fun::new(sym, ctx, xf).await, 0)); fn build_lambdas(fun: Fun, i: u64) -> GExpr { if i < fun.arity().into() { return lambda(i, [build_lambdas(fun, i + 1)]); @@ -129,16 +129,16 @@ pub fn comments<'a>( trait_set! { trait LazyMemberCallback = - FnOnce(Sym) -> LocalBoxFuture<'static, MemKind> + DynClone + FnOnce(Sym, SysCtx) -> LocalBoxFuture<'static, MemKind> + DynClone } pub struct LazyMemberFactory(Box); impl LazyMemberFactory { pub fn new + 'static>( - cb: impl FnOnce(Sym) -> F + Clone + 'static, + cb: impl FnOnce(Sym, SysCtx) -> F + Clone + 'static, ) -> 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 { fn clone(&self) -> Self { Self(clone_box(&*self.0)) } @@ -157,7 +157,7 @@ pub struct GenMember { } impl GenMember { pub async fn into_api(self, ctx: &mut impl TreeIntoApiCtx) -> api::Member { - let name = ctx.sys().i.i::(&self.name).await; + let name = ctx.sys().i().i::(&self.name).await; api::Member { kind: self.kind.into_api(&mut ctx.push_path(name.clone())).await, name: name.to_api(), @@ -197,7 +197,7 @@ pub trait TreeIntoApiCtx { fn req(&self) -> &impl ReqHandlish; } -pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> { +pub struct TreeIntoApiCtxImpl<'a, 'b, RH: ReqHandlish> { pub sys: SysCtx, pub basepath: &'a [Tok], pub path: Substack<'a, Tok>, @@ -206,10 +206,10 @@ pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> { pub req: &'a RH, } -impl TreeIntoApiCtx for TIACtxImpl<'_, '_, RH> { +impl TreeIntoApiCtx for TreeIntoApiCtxImpl<'_, '_, RH> { fn sys(&self) -> SysCtx { self.sys.clone() } fn push_path(&mut self, seg: Tok) -> impl TreeIntoApiCtx { - TIACtxImpl { + TreeIntoApiCtxImpl { req: self.req, lazy_members: self.lazy_members, rules: self.rules, diff --git a/orchid-host/src/extension.rs b/orchid-host/src/extension.rs index a5e1c59..9e88e53 100644 --- a/orchid-host/src/extension.rs +++ b/orchid-host/src/extension.rs @@ -35,7 +35,7 @@ pub struct ReqPair(R, Sender); #[derive(destructure)] pub struct ExtensionData { ctx: Ctx, - init: ExtInit, + init: Rc, reqnot: ReqNot, systems: Vec, logger: Logger, @@ -55,114 +55,140 @@ impl Drop for ExtensionData { pub struct Extension(Rc); impl Extension { pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result { - Ok(Self(Rc::new_cyclic(|weak: &Weak| ExtensionData { - exprs: ExprStore::default(), - ctx: ctx.clone(), - systems: (init.systems.iter().cloned()) - .map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) }) - .collect(), - logger: logger.clone(), - init, - next_pars: RefCell::new(NonZeroU64::new(1).unwrap()), - lex_recur: Mutex::default(), - mac_recur: Mutex::default(), - reqnot: ReqNot::new( - msg_logger, - clone!(weak; move |sfn, _| clone!(weak; async move { + Ok(Self(Rc::new_cyclic(|weak: &Weak| { + 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(), + ctx: ctx.clone(), + systems: (init.systems.iter().cloned()) + .map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) }) + .collect(), + logger: logger.clone(), + init, + next_pars: RefCell::new(NonZeroU64::new(1).unwrap()), + lex_recur: Mutex::default(), + mac_recur: Mutex::default(), + reqnot: ReqNot::new( + msg_logger, + clone!(weak; move |sfn, _| clone!(weak; async move { let data = weak.upgrade().unwrap(); data.init.send(sfn).await }.boxed_local())), - clone!(weak; move |notif, _| { - clone!(weak; Box::pin(async move { - let this = Extension(weak.upgrade().unwrap()); - match notif { - api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => { - let target = this.0.exprs.get_expr(acq.1).expect("Invalid ticket"); - this.0.exprs.give_expr(target) - } - api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => { - this.assert_own_sys(rel.0).await; - this.0.exprs.take_expr(rel.1) - } - api::ExtHostNotif::ExprNotif(api::ExprNotif::Move(mov)) => { - this.assert_own_sys(mov.dec).await; - let recp = this.ctx().system_inst(mov.inc).await.expect("invallid recipient sys id"); - let expr = this.0.exprs.get_expr(mov.expr).expect("invalid ticket"); - recp.ext().0.exprs.give_expr(expr); - this.0.exprs.take_expr(mov.expr); - }, - api::ExtHostNotif::Log(api::Log(str)) => this.logger().log(str), - } - }))}), - { - clone!(weak, ctx); - move |hand, req| { - clone!(weak, ctx); - Box::pin(async move { - let this = Self(weak.upgrade().unwrap()); - writeln!(this.reqnot().logger(), "Host received request {req:?}"); - let i = this.ctx().i.clone(); - match req { - api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()).await, - api::ExtHostReq::IntReq(intreq) => match intreq { - api::IntReq::InternStr(s) => hand.handle(&s, &i.i(&*s.0).await.to_api()).await, - api::IntReq::InternStrv(v) => { - let tokens = join_all(v.0.iter().map(|m| i.ex(*m))).await; - hand.handle(&v, &i.i(&tokens).await.to_api()).await - }, - api::IntReq::ExternStr(si) => - hand.handle(&si, &Tok::::from_api(si.0, &i).await.rc()).await, - api::IntReq::ExternStrv(vi) => { - let markerv = (i.ex(vi.0).await.iter()).map(|t| t.to_api()).collect_vec(); - hand.handle(&vi, &markerv).await - }, - }, - 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 reply = - sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())).await; - hand.handle(fw, &reply).await - }, - api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => { - let sys = ctx.system_inst(id).await.unwrap(); - hand.handle(fw, &sys.request(body.clone()).await).await - }, - api::ExtHostReq::SubLex(sl) => { - let (rep_in, rep_out) = channel::bounded(1); - { - let lex_g = this.0.lex_recur.lock().await; - let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid"); - req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap(); - } - hand.handle(&sl, &rep_out.recv().await.unwrap()).await - }, - api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => { - let expr = this.exprs().get_expr(target).expect("Invalid ticket"); - hand - .handle(&ins, &api::Inspected { - refcount: expr.strong_count() as u32, - location: expr.pos().to_api(), - kind: expr.to_api().await, - }) - .await - }, - api::ExtHostReq::RunMacros(rm) => { - let (rep_in, rep_out) = channel::bounded(1); - let lex_g = this.0.mac_recur.lock().await; - let req_in = lex_g.get(&rm.run_id).expect("Sublex for nonexistent lexid"); - req_in.send(ReqPair(rm.clone(), rep_in)).await.unwrap(); - hand.handle(&rm, &rep_out.recv().await.unwrap()).await - }, - api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => { - let atom = AtomHand::new(atom.clone(), &ctx).await; - let unit = atom.print(&FmtCtxImpl { i: &this.ctx().i }).await; - hand.handle(eap, &unit.to_api()).await - }, + clone!(weak; move |notif, _| { + clone!(weak; Box::pin(async move { + let this = Extension(weak.upgrade().unwrap()); + match notif { + api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => { + let target = this.0.exprs.get_expr(acq.1).expect("Invalid ticket"); + this.0.exprs.give_expr(target) } - }) - } - }, - ), + api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => { + this.assert_own_sys(rel.0).await; + this.0.exprs.take_expr(rel.1) + } + api::ExtHostNotif::ExprNotif(api::ExprNotif::Move(mov)) => { + this.assert_own_sys(mov.dec).await; + let recp = this.ctx().system_inst(mov.inc).await.expect("invallid recipient sys id"); + let expr = this.0.exprs.get_expr(mov.expr).expect("invalid ticket"); + recp.ext().0.exprs.give_expr(expr); + this.0.exprs.take_expr(mov.expr); + }, + api::ExtHostNotif::Log(api::Log(str)) => this.logger().log(str), + } + }))}), + { + clone!(weak, ctx); + move |hand, req| { + clone!(weak, ctx); + Box::pin(async move { + let this = Self(weak.upgrade().unwrap()); + writeln!(this.reqnot().logger(), "Host received request {req:?}"); + let i = this.ctx().i.clone(); + match req { + api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()).await, + api::ExtHostReq::IntReq(intreq) => match intreq { + api::IntReq::InternStr(s) => hand.handle(&s, &i.i(&*s.0).await.to_api()).await, + api::IntReq::InternStrv(v) => { + let tokens = join_all(v.0.iter().map(|m| i.ex(*m))).await; + hand.handle(&v, &i.i(&tokens).await.to_api()).await + }, + api::IntReq::ExternStr(si) => + hand.handle(&si, &Tok::::from_api(si.0, &i).await.rc()).await, + api::IntReq::ExternStrv(vi) => { + let markerv = (i.ex(vi.0).await.iter()).map(|t| t.to_api()).collect_vec(); + hand.handle(&vi, &markerv).await + }, + }, + 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 reply = + sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())).await; + hand.handle(fw, &reply).await + }, + api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => { + let sys = ctx.system_inst(id).await.unwrap(); + hand.handle(fw, &sys.request(body.clone()).await).await + }, + api::ExtHostReq::SubLex(sl) => { + let (rep_in, rep_out) = channel::bounded(1); + { + let lex_g = this.0.lex_recur.lock().await; + let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid"); + req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap(); + } + hand.handle(&sl, &rep_out.recv().await.unwrap()).await + }, + api::ExtHostReq::ExprReq(api::ExprReq::Inspect( + ins @ api::Inspect { target }, + )) => { + let expr = this.exprs().get_expr(target).expect("Invalid ticket"); + hand + .handle(&ins, &api::Inspected { + refcount: expr.strong_count() as u32, + location: expr.pos().to_api(), + kind: expr.to_api().await, + }) + .await + }, + api::ExtHostReq::RunMacros(rm) => { + let (rep_in, rep_out) = channel::bounded(1); + let lex_g = this.0.mac_recur.lock().await; + let req_in = lex_g.get(&rm.run_id).expect("Sublex for nonexistent lexid"); + req_in.send(ReqPair(rm.clone(), rep_in)).await.unwrap(); + hand.handle(&rm, &rep_out.recv().await.unwrap()).await + }, + api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => { + let atom = AtomHand::new(atom.clone(), &ctx).await; + let unit = atom.print(&FmtCtxImpl { i: &this.ctx().i }).await; + hand.handle(eap, &unit.to_api()).await + }, + } + }) + } + }, + ), + } }))) } pub(crate) fn reqnot(&self) -> &ReqNot { &self.0.reqnot } @@ -212,20 +238,6 @@ impl Extension { .await; 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) { let rc = self.clone(); (self.ctx().spawn)(Box::pin(async move { diff --git a/orchid-host/src/subprocess.rs b/orchid-host/src/subprocess.rs index dfb06dd..e0b7aad 100644 --- a/orchid-host/src/subprocess.rs +++ b/orchid-host/src/subprocess.rs @@ -1,7 +1,5 @@ use std::cell::RefCell; -use std::path::PathBuf; use std::pin::Pin; -use std::thread; use async_process::{self, Child, ChildStdin, ChildStdout}; use async_std::io::{self, BufReadExt, BufReader}; @@ -22,8 +20,6 @@ pub async fn ext_command( msg_logs: Logger, ctx: Ctx, ) -> io::Result { - 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) .stdin(async_process::Stdio::piped()) .stdout(async_process::Stdio::piped()) @@ -36,18 +32,16 @@ pub async fn ext_command( let mut stdout = child.stdout.take().unwrap(); let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await; let child_stderr = child.stderr.take().unwrap(); - thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || { - async_std::task::block_on(async move { - let mut reader = BufReader::new(child_stderr); - loop { - let mut buf = String::new(); - if 0 == reader.read_line(&mut buf).await.unwrap() { - break; - } - logger.log(buf.strip_suffix('\n').expect("Readline implies this")); + (ctx.spawn)(Box::pin(async move { + let mut reader = BufReader::new(child_stderr); + loop { + let mut buf = String::new(); + if 0 == reader.read_line(&mut buf).await.unwrap() { + break; } - }) - })?; + logger.log(buf.strip_suffix('\n').expect("Readline implies this")); + } + })); Ok(ExtInit { header, port: Box::new(Subprocess { @@ -87,6 +81,7 @@ impl ExtPort for Subprocess { match recv_msg(self.stdout.lock().await.as_mut()).await { Ok(msg) => cb(&msg).await, 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()), } }) diff --git a/orchid-std/Cargo.toml b/orchid-std/Cargo.toml index 9762ea7..46affd6 100644 --- a/orchid-std/Cargo.toml +++ b/orchid-std/Cargo.toml @@ -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-extension = { version = "0.1.0", path = "../orchid-extension" } ordered-float = "4.6.0" +rust_decimal = "1.36.0" tokio = { version = "1.43.0", features = ["full"] } diff --git a/orchid-std/src/number/num_atom.rs b/orchid-std/src/number/num_atom.rs index 78228b1..c64bcde 100644 --- a/orchid-std/src/number/num_atom.rs +++ b/orchid-std/src/number/num_atom.rs @@ -1,6 +1,7 @@ use orchid_api_derive::Coding; use orchid_base::error::OrcRes; use orchid_base::format::FmtUnit; +use orchid_base::number::Numeric; use orchid_extension::atom::{ AtomFactory, Atomic, AtomicFeatures, MethodSetBuilder, ToAtom, TypAtom, }; @@ -9,6 +10,7 @@ use orchid_extension::conv::TryFromExpr; use orchid_extension::expr::Expr; use orchid_extension::system::SysCtx; use ordered_float::NotNan; +use rust_decimal::prelude::Zero; #[derive(Clone, Debug, Coding)] pub struct Int(pub i64); @@ -38,27 +40,56 @@ impl ThinAtom for Float { } impl TryFromExpr for Float { async fn try_from_expr(expr: Expr) -> OrcRes { - TypAtom::::try_from_expr(expr).await.map(|t| t.value) + Ok(Self(Num::try_from_expr(expr).await?.0.to_f64())) } } -pub enum Numeric { - Int(i64), - Float(NotNan), -} -impl TryFromExpr for Numeric { +pub struct Num(pub Numeric); +impl TryFromExpr for Num { async fn try_from_expr(expr: Expr) -> OrcRes { - match Int::try_from_expr(expr.clone()).await { - Ok(t) => Ok(Numeric::Int(t.0)), - Err(e) => Float::try_from_expr(expr).await.map(|t| Numeric::Float(t.0)).map_err(|e2| e + e2), + let e = match Int::try_from_expr(expr.clone()).await { + Ok(t) => return Ok(Num(Numeric::Int(t.0))), + Err(e) => e, + }; + match TypAtom::::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 { - match self { - Self::Float(f) => Float(f).factory(), - Self::Int(i) => Int(i).factory(), + match self.0 { + Numeric::Float(f) => Float(f).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 { + Int([i64; N]), + Float([NotNan; N]), +} +impl HomoArray { + 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) + } +} diff --git a/orchid-std/src/number/num_lexer.rs b/orchid-std/src/number/num_lexer.rs index a30277f..e154a9d 100644 --- a/orchid-std/src/number/num_lexer.rs +++ b/orchid-std/src/number/num_lexer.rs @@ -1,13 +1,12 @@ use std::ops::RangeInclusive; use orchid_base::error::OrcRes; -use orchid_base::number::{Numeric, num_to_err, parse_num}; -use orchid_extension::atom::AtomicFeatures; +use orchid_base::number::{num_to_err, parse_num}; +use orchid_extension::atom::ToAtom; use orchid_extension::lexer::{LexContext, Lexer}; use orchid_extension::tree::{GenTok, GenTokTree}; -use ordered_float::NotNan; -use super::num_atom::{Float, Int}; +use super::num_atom::Num; #[derive(Default)] 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 (chars, tail) = all.split_at(ends_at.unwrap_or(all.len())); let fac = match parse_num(chars) { - Ok(Numeric::Float(f)) => Float(f).factory(), - Ok(Numeric::Uint(uint)) => Int(uint.try_into().unwrap()).factory(), - Ok(Numeric::Decimal(dec)) => Float(NotNan::new(dec.try_into().unwrap()).unwrap()).factory(), + Ok(numeric) => Num(numeric).to_atom_factory(), 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)))) diff --git a/orchid-std/src/std.rs b/orchid-std/src/std.rs index c4789fb..821e4a1 100644 --- a/orchid-std/src/std.rs +++ b/orchid-std/src/std.rs @@ -1,6 +1,7 @@ use std::rc::Rc; use never::Never; +use orchid_base::number::Numeric; use orchid_base::reqnot::Receipt; use orchid_extension::atom::{AtomDynfo, AtomicFeatures}; use orchid_extension::entrypoint::ExtReq; @@ -8,9 +9,10 @@ use orchid_extension::fs::DeclFs; use orchid_extension::system::{System, SystemCard}; use orchid_extension::system_ctor::SystemCtor; use orchid_extension::tree::{MemKind, comments, fun, module, root_mod}; +use ordered_float::NotNan; 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::string::str_atom::{IntStrAtom, StrAtom}; use crate::string::str_lexer::StringLexer; @@ -37,11 +39,39 @@ impl System for StdSystem { fn parsers() -> Vec { vec![] } fn vfs() -> DeclFs { DeclFs::Mod(&[]) } fn env() -> Vec<(String, MemKind)> { - vec![root_mod("std", [], [module(true, "string", [], [comments( - ["Concatenate two strings"], - 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)) - }), - )])])] + vec![root_mod("std", [], [ + module(true, "string", [], [comments( + ["Concatenate two strings"], + 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)) + }), + )]), + 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) + }), + ]), + ])] } } diff --git a/orchid-std/src/string/str_atom.rs b/orchid-std/src/string/str_atom.rs index 35c06dd..157392c 100644 --- a/orchid-std/src/string/str_atom.rs +++ b/orchid-std/src/string/str_atom.rs @@ -74,7 +74,7 @@ impl OwnedAtom for IntStrAtom { } async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self { let s = ctx.decode::().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<'_> { pub async fn get_string(&self) -> Rc { 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, } } @@ -106,7 +106,7 @@ impl TryFromExpr for OrcString<'static> { let ctx = expr.ctx(); match TypAtom::::try_from_expr(expr).await { Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }), - Err(e) => Err(mk_errv(ctx.i.i("A string was expected").await, "", e.pos_iter())), + Err(e) => Err(mk_errv(ctx.i().i("A string was expected").await, "", e.pos_iter())), } } } diff --git a/orcx/src/main.rs b/orcx/src/main.rs index dbe26b2..b38c96b 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -9,7 +9,6 @@ use async_stream::try_stream; use camino::Utf8PathBuf; use clap::{Parser, Subcommand}; use futures::{Stream, TryStreamExt, io}; -use orchid_base::clone; use orchid_base::error::ReporterImpl; use orchid_base::format::{FmtCtxImpl, Format, take_first}; 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 .unwrap(); 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 } } @@ -168,12 +166,8 @@ async fn main() -> io::Result { if args.verbose { println!("lexed: {}", take_first(&ttv_fmt(&lexemes, &FmtCtxImpl { i }).await, true)); } - let mtreev = parse_mtree( - Snippet::new(&lexemes[0], &lexemes, i), - Substack::Bottom.push(i.i("orcx").await).push(i.i("input").await), - ) - .await - .unwrap(); + let mtreev = + parse_mtree(Snippet::new(&lexemes[0], &lexemes, i), Substack::Bottom).await.unwrap(); if args.verbose { let fmt = mtreev_fmt(&mtreev, &FmtCtxImpl { i }).await; println!("parsed: {}", take_first(&fmt, true));