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",
"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",
]

View File

@@ -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.

View File

@@ -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"

View File

@@ -98,7 +98,7 @@ impl Interned for String {
impl InternMarker for api::TStr {
type Interned = String;
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 from_id(id: NonZeroU64) -> Self { Self(id) }
@@ -125,7 +125,7 @@ impl Interned for Vec<Tok<String>> {
impl InternMarker for api::TStrv {
type Interned = Vec<Tok<String>>;
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;
Tok::new(Rc::new(data), self)
}
@@ -217,24 +217,26 @@ pub struct TypedInterners {
}
#[derive(Default)]
pub struct Interner {
pub struct InternerData {
interners: Mutex<TypedInterners>,
master: Option<Box<dyn DynRequester<Transfer = api::IntReq>>>,
}
#[derive(Clone, Default)]
pub struct Interner(Rc<InternerData>);
impl Interner {
pub fn new_master() -> Self { Self::default() }
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
pub async fn i<T: Interned>(&self, t: &(impl Internable<Interned = T> + ?Sized)) -> Tok<T> {
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<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;
}
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())
}
}

View File

@@ -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<f64>),
/// 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<f64> {
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<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)))
.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<Numeric, NumError> {
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));
}
}

View File

@@ -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<Self>;
}
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 }
}
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(),
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<A: AtomCard> MethodSetBuilder<A> {
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<A: AtomicFeatures> TypAtom<'_, A> {
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
where A: Supports<M> {
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<A: AtomicFeatures> Deref for TypAtom<'_, A> {
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx);
impl FmtCtx for AtomCtx<'_> {
fn i(&self) -> &Interner { &self.2.i }
fn i(&self) -> &Interner { self.2.i() }
}
pub trait AtomDynfo: 'static {

View File

@@ -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<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVariant> 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::<ObjStore>().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::<A>(ctx.cted.inst().card());
let (typ_id, _) = get_info::<A>(ctx.get::<CtedObj>().inst().card());
let mut data = enc_vec(&typ_id).await;
self.encode(Pin::<&mut Vec<u8>>::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::<ObjStore>().objects.read().await.insert(atom_id, Box::new(self));
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() } }
@@ -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::<ObjStore>().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<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))
}
@@ -219,7 +223,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
#[allow(unused_variables)]
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> {
async {
@@ -231,7 +235,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
}
#[allow(unused_variables)]
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)]
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::gen_expr::{GExpr, bot};
use crate::system::SysCtx;
use crate::system_ctor::CtedObj;
pub struct ThinVariant;
impl AtomicVariant for ThinVariant {}
impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant> for A {
fn _factory(self) -> AtomFactory {
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;
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<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
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<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)]
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)]
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> {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
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(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 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<api::ExtMsgSet>;
@@ -61,11 +61,11 @@ pub enum MemberRecord {
}
pub struct SystemRecord {
cted: CtedObj,
vfses: HashMap<api::VfsId, &'static dyn VirtFS>,
declfs: api::EagerVfs,
lazy_members: HashMap<api::TreeId, MemberRecord>,
rules: HashMap<api::MacroId, Rc<Rule>>,
ctx: SysCtx,
}
trait_set! {
@@ -78,14 +78,13 @@ trait_set! {
}
pub async fn with_atom_record<'a, F: Future<Output = SysCtx>, T>(
get_sys_ctx: &impl Fn(api::SysId, ReqNot<api::ExtMsgSet>) -> F,
reqnot: ReqNot<api::ExtMsgSet>,
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::<CtedObj>().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<api::ExtMsgSet>,
out_recv: Receiver<Vec<u8>>,
out_send: Sender<Vec<u8>>,
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::<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() }
.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::<Rc<Interner>>));
let interner_cell = Rc::new(RefCell::new(None::<Interner>));
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<api::ExtMsgSet>| {
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<ExtMsgSet>| {
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::<api::ExtMsgSet>::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<GenTokTree> = 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 })))

View File

@@ -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),
}
}
}

View File

@@ -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<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>>;
}
thread_local! {
static FUNS: Rc<Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>> = Rc::default();
}
#[derive(Default)]
struct FunsCtx(Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>);
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<dyn FunCB>,
}
impl Fun {
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, f: F) -> Self {
let funs = FUNS.with(|funs| funs.clone());
let mut fung = funs.lock().await;
pub async fn new<I, O, F: ExprFunc<I, O>>(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::<FunsCtx>().0.lock().await.get(&path).unwrap().clone();
Self { args, arity, path, fun }
}
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 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::<ReqNot<ExtMsgSet>>().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<String>) -> Vec<MTree<'a, Never>> {
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,

View File

@@ -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::<CtedObj>().inst();
let owner = if *ctx.get::<api::SysId>() == foreign.atom.owner {
own_inst.card()
} 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())?
.get_card()
};
@@ -133,18 +134,80 @@ where A: AtomicFeatures {
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)]
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>,
pub struct SysCtx(Rc<MemoMap<TypeId, Box<dyn Any>>>);
impl SysCtx {
pub fn new(
id: api::SysId,
i: Interner,
reqnot: ReqNot<ExtMsgSet>,
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<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 {
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<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 {
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::<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::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::<String>(c).await.to_api(),
text: ctx.sys().i().i::<String>(c).await.to_api(),
}
}))
.await;
@@ -92,8 +92,8 @@ pub fn root_mod(
(name.to_string(), kind)
}
pub fn fun<I, O>(exported: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenItem> {
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<dyn LazyMemberCallback>);
impl LazyMemberFactory {
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(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::<String>(&self.name).await;
let name = ctx.sys().i().i::<String>(&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<String>],
pub path: Substack<'a, Tok<String>>,
@@ -206,10 +206,10 @@ pub struct TIACtxImpl<'a, 'b, RH: ReqHandlish> {
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 push_path(&mut self, seg: Tok<String>) -> impl TreeIntoApiCtx {
TIACtxImpl {
TreeIntoApiCtxImpl {
req: self.req,
lazy_members: self.lazy_members,
rules: self.rules,

View File

@@ -35,7 +35,7 @@ pub struct ReqPair<R: Request>(R, Sender<R::Response>);
#[derive(destructure)]
pub struct ExtensionData {
ctx: Ctx,
init: ExtInit,
init: Rc<ExtInit>,
reqnot: ReqNot<api::HostMsgSet>,
systems: Vec<SystemCtor>,
logger: Logger,
@@ -55,114 +55,140 @@ impl Drop for ExtensionData {
pub struct Extension(Rc<ExtensionData>);
impl Extension {
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| 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<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(),
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::<String>::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::<String>::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<HostMsgSet> { &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 {

View File

@@ -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<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)
.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()),
}
})

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-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"] }

View File

@@ -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<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 {
Int(i64),
Float(NotNan<f64>),
}
impl TryFromExpr for Numeric {
pub struct Num(pub Numeric);
impl TryFromExpr for Num {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match Int::try_from_expr(expr.clone()).await {
Ok(t) => Ok(Numeric::Int(t.0)),
Err(e) => Float::try_from_expr(expr).await.map(|t| Numeric::Float(t.0)).map_err(|e2| e + e2),
let e = match Int::try_from_expr(expr.clone()).await {
Ok(t) => return Ok(Num(Numeric::Int(t.0))),
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 {
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<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 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))))

View File

@@ -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<orchid_extension::parser::ParserObj> { 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)
}),
]),
])]
}
}

View File

@@ -74,7 +74,7 @@ impl OwnedAtom for IntStrAtom {
}
async fn deserialize(mut ctx: impl DeserializeCtx, _: ()) -> Self {
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<'_> {
pub async fn get_string(&self) -> Rc<String> {
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::<IntStrAtom>::try_from_expr(expr).await {
Ok(t) => Ok(OrcString { ctx: t.data.ctx(), kind: OrcStringKind::Int(t) }),
Err(e) => Err(mk_errv(ctx.i.i("A string was expected").await, "", e.pos_iter())),
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 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<ExitCode> {
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));