task_local context over context objects

- interner impls logically separate from API in orchid-base (default host interner still in base for testing)
- error reporting, logging, and a variety of other features passed down via context in extension, not yet in host to maintain library-ish profile, should consider options
- no global spawn mechanic, the host has a spawn function but extensions only get a stash for enqueuing async work in sync callbacks which is then explicitly, manually, and with strict order popped and awaited
- still deadlocks nondeterministically for some ungodly reason
This commit is contained in:
2026-01-01 14:54:29 +00:00
parent 06debb3636
commit 32d6237dc5
92 changed files with 2507 additions and 2223 deletions

View File

@@ -5,7 +5,7 @@ use async_once_cell::OnceCell;
use derive_destructure::destructure;
use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt};
use orchid_base::location::Pos;
use orchid_base::reqnot::Requester;
use orchid_base::reqnot::ClientExt;
use orchid_base::tree::AtomRepr;
use crate::api;
@@ -59,11 +59,11 @@ impl AtomHand {
pub async fn call(self, arg: Expr) -> Expr {
let owner_sys = self.0.owner.clone();
let ctx = owner_sys.ctx();
let reqnot = owner_sys.reqnot();
let client = owner_sys.client();
ctx.exprs.give_expr(arg.clone());
let ret = match Rc::try_unwrap(self.0) {
Ok(data) => reqnot.request(api::FinalCall(data.api(), arg.id())).await,
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), arg.id())).await,
Ok(data) => client.request(api::FinalCall(data.api(), arg.id())).await.unwrap(),
Err(hand) => client.request(api::CallRef(hand.api_ref(), arg.id())).await.unwrap(),
};
let val = Expr::from_api(&ret, PathSetBuilder::new(), ctx.clone()).await;
ctx.exprs.take_expr(arg.id());
@@ -74,19 +74,21 @@ impl AtomHand {
#[must_use]
pub fn ext(&self) -> &Extension { self.sys().ext() }
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await
self.0.owner.client().request(api::Fwded(self.0.api_ref(), key, req)).await.unwrap()
}
#[must_use]
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
#[must_use]
pub async fn to_string(&self) -> String { take_first_fmt(self, &self.0.owner.ctx().i).await }
pub async fn to_string(&self) -> String { take_first_fmt(self).await }
#[must_use]
pub fn downgrade(&self) -> WeakAtomHand { WeakAtomHand(Rc::downgrade(&self.0)) }
}
impl Format for AtomHand {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
(self.0.display.get_or_init(async {
FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await)
FmtUnit::from_api(
&self.0.owner.client().request(api::AtomPrint(self.0.api_ref())).await.unwrap(),
)
}))
.await
.clone()

View File

@@ -3,19 +3,28 @@ use std::num::{NonZero, NonZeroU16};
use std::rc::{Rc, Weak};
use std::{fmt, ops};
use futures::future::LocalBoxFuture;
use futures_locks::RwLock;
use hashbrown::HashMap;
use orchid_base::builtin::Spawner;
use orchid_base::interner::Interner;
use orchid_base::logging::Logger;
use crate::api;
use crate::expr_store::ExprStore;
use crate::system::{System, WeakSystem};
use crate::tree::WeakRoot;
pub trait JoinHandle {
fn abort(&self);
fn join(self: Box<Self>) -> LocalBoxFuture<'static, ()>;
}
pub trait Spawner {
fn spawn_obj(&self, fut: LocalBoxFuture<'static, ()>) -> Box<dyn JoinHandle>;
}
pub struct CtxData {
pub i: Interner,
pub spawn: Spawner,
spawner: Rc<dyn Spawner>,
pub msg_logs: Logger,
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>,
pub exprs: ExprStore,
@@ -37,16 +46,25 @@ impl WeakCtx {
}
impl Ctx {
#[must_use]
pub fn new(spawn: Spawner) -> Self {
pub fn new(msg_logs: Logger, spawner: impl Spawner + 'static) -> Self {
Self(Rc::new(CtxData {
spawn,
i: Interner::default(),
msg_logs,
spawner: Rc::new(spawner),
systems: RwLock::default(),
system_id: RefCell::new(NonZero::new(1).unwrap()),
exprs: ExprStore::default(),
root: RwLock::default(),
}))
}
/// Spawn a parallel future that you can join at any later time.
///
/// Don't use this for async Drop, use [orchid_base::stash::stash] instead.
/// If you use this for an actor object, make sure to actually join the
/// handle.
#[must_use]
pub fn spawn(&self, fut: impl Future<Output = ()> + 'static) -> Box<dyn JoinHandle> {
self.spawner.spawn_obj(Box::pin(fut))
}
#[must_use]
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
self.systems.read().await.get(&id).and_then(WeakSystem::upgrade)
@@ -62,9 +80,6 @@ impl Ctx {
}
impl fmt::Debug for Ctx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ctx")
.field("i", &self.i)
.field("system_id", &self.system_id)
.finish_non_exhaustive()
f.debug_struct("Ctx").field("system_id", &self.system_id).finish_non_exhaustive()
}
}

View File

@@ -1,7 +1,7 @@
use hashbrown::HashSet;
use itertools::Itertools;
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv};
use orchid_base::interner::{Interner, Tok};
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
use orchid_base::interner::{IStr, is};
use orchid_base::location::Pos;
use orchid_base::name::VName;
@@ -16,17 +16,17 @@ pub enum AbsPathError {
RootPath,
}
impl AbsPathError {
pub async fn err_obj(self, i: &Interner, pos: Pos, path: &str) -> OrcErrv {
pub async fn err_obj(self, pos: Pos, path: &str) -> OrcErrv {
let (descr, msg) = match self {
AbsPathError::RootPath => (
i.i("Path ends on root module").await,
is("Path ends on root module").await,
format!(
"{path} is equal to the empty path. You cannot directly reference the root. \
Use one fewer 'super::' or add more segments to make it valid."
),
),
AbsPathError::TooManySupers => (
i.i("Too many 'super::' steps in path").await,
is("Too many 'super::' steps in path").await,
format!("{path} is leading outside the root."),
),
};
@@ -41,13 +41,9 @@ impl AbsPathError {
///
/// if the relative path contains as many or more `super` segments than the
/// length of the absolute path.
pub async fn absolute_path(
mut cwd: &[Tok<String>],
mut rel: &[Tok<String>],
i: &Interner,
) -> Result<VName, AbsPathError> {
let i_self = i.i("self").await;
let i_super = i.i("super").await;
pub async fn absolute_path(mut cwd: &[IStr], mut rel: &[IStr]) -> Result<VName, AbsPathError> {
let i_self = is("self").await;
let i_super = is("super").await;
let mut relative = false;
if let Some((_, tail)) = rel.split_first().filter(|(h, _)| **h == i_self) {
rel = tail;
@@ -63,19 +59,13 @@ pub async fn absolute_path(
.map_err(|_| AbsPathError::RootPath)
}
pub struct DealiasCtx<'a> {
pub i: &'a Interner,
pub rep: &'a Reporter,
}
pub async fn resolv_glob<Mod: Tree>(
cwd: &[Tok<String>],
cwd: &[IStr],
root: &Mod,
abs_path: &[Tok<String>],
abs_path: &[IStr],
pos: Pos,
i: &Interner,
ctx: &mut Mod::Ctx<'_>,
) -> OrcRes<HashSet<Tok<String>>> {
) -> OrcRes<HashSet<IStr>> {
let coprefix_len = cwd.iter().zip(abs_path).take_while(|(a, b)| a == b).count();
let (co_prefix, diff_path) = abs_path.split_at(abs_path.len().min(coprefix_len + 1));
let fst_diff =
@@ -89,7 +79,7 @@ pub async fn resolv_glob<Mod: Tree>(
ChildErrorKind::Missing => ("Invalid import path", format!("{path} not found")),
ChildErrorKind::Private => ("Import inaccessible", format!("{path} is private")),
};
return Err(mk_errv(i.i(tk).await, msg, [pos]));
return Err(mk_errv(is(tk).await, msg, [pos]));
},
};
Ok(target_module.children(coprefix_len < abs_path.len()))
@@ -100,11 +90,11 @@ pub type ChildResult<'a, T> = Result<&'a T, ChildErrorKind>;
pub trait Tree {
type Ctx<'a>;
#[must_use]
fn children(&self, public_only: bool) -> HashSet<Tok<String>>;
fn children(&self, public_only: bool) -> HashSet<IStr>;
#[must_use]
fn child(
&self,
key: Tok<String>,
key: IStr,
public_only: bool,
ctx: &mut Self::Ctx<'_>,
) -> impl Future<Output = ChildResult<'_, Self>>;
@@ -135,7 +125,7 @@ pub struct ChildError {
pub async fn walk<'a, T: Tree>(
root: &'a T,
public_only: bool,
path: impl IntoIterator<Item = Tok<String>>,
path: impl IntoIterator<Item = IStr>,
ctx: &mut T::Ctx<'_>,
) -> Result<&'a T, ChildError> {
let mut cur = root;

View File

@@ -4,11 +4,10 @@ use bound::Bound;
use futures::FutureExt;
use futures_locks::{RwLockWriteGuard, TryLockError};
use orchid_base::error::OrcErrv;
use orchid_base::format::{FmtCtxImpl, Format, take_first};
use orchid_base::format::fmt;
use orchid_base::location::Pos;
use orchid_base::logging::Logger;
use orchid_base::logging::logger;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, PathSet, Step};
use crate::tree::Root;
@@ -30,21 +29,19 @@ pub enum ExecResult {
}
pub struct ExecCtx {
ctx: Ctx,
gas: Option<u64>,
stack: Vec<ExprGuard>,
cur: ExprGuard,
cur_pos: Pos,
did_pop: bool,
logger: Logger,
root: Root,
}
impl ExecCtx {
#[must_use]
pub async fn new(ctx: Ctx, logger: Logger, root: Root, init: Expr) -> Self {
pub async fn new(root: Root, init: Expr) -> Self {
let cur_pos = init.pos();
let cur = Bound::async_new(init, |init| init.kind().write()).await;
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger, root }
Self { gas: None, stack: vec![], cur, cur_pos, did_pop: false, root }
}
#[must_use]
pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") }
@@ -89,8 +86,7 @@ impl ExecCtx {
while self.use_gas(1) {
let mut kind_swap = ExprKind::Missing;
mem::swap(&mut kind_swap, &mut self.cur);
let unit = kind_swap.print(&FmtCtxImpl { i: &self.ctx.i }).await;
writeln!(self.logger, "Exxecute lvl{} {}", self.stack.len(), take_first(&unit, true));
writeln!(logger(), "Exxecute lvl{} {}", self.stack.len(), fmt(&kind_swap).await);
let (kind, op) = match kind_swap {
ExprKind::Identity(target) => {
let inner = self.unpack_ident(&target).await;

View File

@@ -9,7 +9,6 @@ use futures_locks::RwLock;
use itertools::Itertools;
use orchid_base::error::OrcErrv;
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::interner::Interner;
use orchid_base::location::{Pos, SrcRange};
use orchid_base::name::Sym;
use orchid_base::tl_cache;
@@ -56,13 +55,13 @@ impl Expr {
}
#[must_use]
pub async fn from_api(api: &api::Expression, psb: PathSetBuilder<'_, u64>, ctx: Ctx) -> Self {
let pos = Pos::from_api(&api.location, &ctx.i).await;
let pos = Pos::from_api(&api.location).await;
let kind = match &api.kind {
api::ExpressionKind::Arg(n) => {
assert!(psb.register_arg(n), "Arguments must be enclosed in a matching lambda");
ExprKind::Arg
},
api::ExpressionKind::Bottom(bot) => ExprKind::Bottom(OrcErrv::from_api(bot, &ctx.i).await),
api::ExpressionKind::Bottom(bot) => ExprKind::Bottom(OrcErrv::from_api(bot).await),
api::ExpressionKind::Call(f, x) => {
let (lpsb, rpsb) = psb.split();
ExprKind::Call(
@@ -70,7 +69,7 @@ impl Expr {
Expr::from_api(x, rpsb, ctx).boxed_local().await,
)
},
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name, &ctx.i).await),
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name).await),
api::ExpressionKind::Lambda(x, body) => {
let lbuilder = psb.lambda(x);
let body = Expr::from_api(body, lbuilder.stack(), ctx).boxed_local().await;
@@ -326,12 +325,7 @@ impl WeakExpr {
impl TokenVariant<api::ExprTicket> for Expr {
type FromApiCtx<'a> = ExprStore;
async fn from_api(
api: &api::ExprTicket,
ctx: &mut Self::FromApiCtx<'_>,
_: SrcRange,
_: &Interner,
) -> Self {
async fn from_api(api: &api::ExprTicket, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
ctx.get_expr(*api).expect("Invalid ticket")
}
type ToApiCtx<'a> = ExprStore;
@@ -348,12 +342,7 @@ pub struct ExprWillPanic;
impl TokenVariant<api::Expression> for Expr {
type FromApiCtx<'a> = Ctx;
async fn from_api(
api: &api::Expression,
ctx: &mut Self::FromApiCtx<'_>,
_: SrcRange,
_: &Interner,
) -> Self {
async fn from_api(api: &api::Expression, ctx: &mut Self::FromApiCtx<'_>, _: SrcRange) -> Self {
Self::from_api(api, PathSetBuilder::new(), ctx.clone()).await
}
type ToApiCtx<'a> = ExprWillPanic;

View File

@@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::future::Future;
use std::io;
use std::num::NonZeroU64;
use std::pin::pin;
use std::pin::Pin;
use std::rc::{Rc, Weak};
use async_fn_stream::stream;
@@ -10,28 +10,33 @@ use derive_destructure::destructure;
use futures::channel::mpsc::{Sender, channel};
use futures::future::{join, join_all};
use futures::lock::Mutex;
use futures::{SinkExt, StreamExt, stream};
use hashbrown::HashMap;
use futures::{AsyncRead, AsyncWrite, AsyncWriteExt, SinkExt, StreamExt};
use hashbrown::{HashMap, HashSet};
use itertools::Itertools;
use orchid_api_traits::Request;
use orchid_base::builtin::ExtInit;
use orchid_api_traits::{Decode, Encode, Request};
use orchid_base::clone;
use orchid_base::format::{FmtCtxImpl, Format};
use orchid_base::interner::Tok;
use orchid_base::interner::{IStr, IStrv, es, ev, is, iv};
use orchid_base::location::Pos;
use orchid_base::logging::Logger;
use orchid_base::logging::logger;
use orchid_base::name::Sym;
use orchid_base::reqnot::{DynRequester, ReqNot, Requester as _};
use orchid_base::reqnot::{Client, ClientExt, MsgReaderExt, ReqHandleExt, ReqReaderExt, io_comm};
use orchid_base::stash::{stash, with_stash};
use orchid_base::tree::AtomRepr;
use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::ctx::{Ctx, JoinHandle};
use crate::dealias::{ChildError, ChildErrorKind, walk};
use crate::expr::{Expr, PathSetBuilder};
use crate::system::SystemCtor;
use crate::tree::MemberKind;
pub struct ExtPort {
pub input: Pin<Box<dyn AsyncWrite>>,
pub output: Pin<Box<dyn AsyncRead>>,
}
pub struct ReqPair<R: Request>(R, Sender<R::Response>);
/// Data held about an Extension. This is refcounted within [Extension]. It's
@@ -42,69 +47,47 @@ pub struct ReqPair<R: Request>(R, Sender<R::Response>);
pub struct ExtensionData {
name: String,
ctx: Ctx,
reqnot: ReqNot<api::HostMsgSet>,
join_ext: Option<Box<dyn JoinHandle>>,
client: Rc<dyn Client>,
systems: Vec<SystemCtor>,
logger: Logger,
next_pars: RefCell<NonZeroU64>,
exiting_snd: Sender<()>,
lex_recur: Mutex<HashMap<api::ParsId, Sender<ReqPair<api::SubLex>>>>,
strings: RefCell<HashSet<IStr>>,
string_vecs: RefCell<HashSet<IStrv>>,
}
impl Drop for ExtensionData {
fn drop(&mut self) {
let reqnot = self.reqnot.clone();
let mut exiting_snd = self.exiting_snd.clone();
(self.ctx.spawn)(Box::pin(async move {
reqnot.notify(api::HostExtNotif::Exit).await;
exiting_snd.send(()).await.unwrap()
}))
let client = self.client.clone();
let join_ext = self.join_ext.take().expect("Only called once in Drop");
stash(async move {
client.notify(api::HostExtNotif::Exit).await.unwrap();
join_ext.join().await;
})
}
}
#[derive(Clone)]
pub struct Extension(Rc<ExtensionData>);
impl Extension {
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
pub async fn new(mut init: ExtPort, ctx: Ctx) -> io::Result<Self> {
api::HostHeader { log_strategy: logger().strat(), msg_logs: ctx.msg_logs.strat() }
.encode(init.input.as_mut())
.await
.unwrap();
init.input.flush().await.unwrap();
let header = api::ExtensionHeader::decode(init.output.as_mut()).await.unwrap();
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| {
let init = Rc::new(init);
let (exiting_snd, exiting_rcv) = channel::<()>(0);
(ctx.spawn)({
clone!(init, weak, ctx);
Box::pin(async move {
let rcv_stream = stream(async |mut cx| {
loop {
cx.emit(init.recv().await).await
}
});
let mut event_stream = pin!(stream::select(exiting_rcv.map(|()| None), rcv_stream));
while let Some(Some(msg)) = event_stream.next().await {
if let Some(reqnot) = weak.upgrade().map(|rc| rc.reqnot.clone()) {
let reqnot = reqnot.clone();
(ctx.spawn)(Box::pin(async move {
reqnot.receive(&msg).await;
}))
}
}
})
});
ExtensionData {
name: init.name.clone(),
exiting_snd,
ctx: ctx.clone(),
systems: (init.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
.collect(),
logger: logger.clone(),
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(),
reqnot: ReqNot::new(
msg_logger,
move |sfn, _| clone!(init; Box::pin(async move { init.send(sfn).await })),
clone!(weak; move |notif, _| {
clone!(weak; Box::pin(async move {
// context not needed because exit is extension-initiated
let (client, _, future) = io_comm(
Rc::new(Mutex::new(init.input)),
Mutex::new(init.output),
clone!(weak; async move |reader| {
with_stash(async {
let this = Extension(weak.upgrade().unwrap());
let notif = reader.read::<api::ExtHostNotif>().await.unwrap();
if !matches!(notif, api::ExtHostNotif::Log(_)) {
writeln!(this.reqnot().logger(), "Host received notif {notif:?}");
writeln!(logger(), "Host received notif {notif:?}");
}
match notif {
api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => {
@@ -115,153 +98,191 @@ impl Extension {
if this.is_own_sys(rel.0).await {
this.0.ctx.exprs.take_expr(rel.1);
} else {
writeln!(this.reqnot().logger(), "Not our system {:?}", rel.0)
writeln!(this.0.ctx.msg_logs, "Not our system {:?}", rel.0)
}
},
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());
if !matches!(req, api::ExtHostReq::ExtAtomPrint(_)) {
writeln!(this.reqnot().logger(), "Host received request {req:?}");
api::ExtHostNotif::Log(api::Log(str)) => logger().log(str),
api::ExtHostNotif::Sweeped(data) => {
for i in join_all(data.strings.into_iter().map(es)).await {
this.0.strings.borrow_mut().remove(&i);
}
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
},
for i in join_all(data.vecs.into_iter().map(ev)).await {
this.0.string_vecs.borrow_mut().remove(&i);
}
},
}
Ok(())
}).await
}),
{
clone!(weak, ctx);
async move |mut reader| {
with_stash(async {
let this = Self(weak.upgrade().unwrap());
let req = reader.read_req::<api::ExtHostReq>().await.unwrap();
let handle = reader.finish().await;
if !matches!(req, api::ExtHostReq::ExtAtomPrint(_)) {
writeln!(logger(), "Host received request {req:?}");
}
match req {
api::ExtHostReq::Ping(ping) => handle.reply(&ping, &()).await,
api::ExtHostReq::IntReq(intreq) => match intreq {
api::IntReq::InternStr(s) => {
let i = is(&s.0).await;
this.0.strings.borrow_mut().insert(i.clone());
handle.reply(&s, &i.to_api()).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::IntReq::InternStrv(v) => {
let tokens = join_all(v.0.iter().map(|m| es(*m))).await;
this.0.strings.borrow_mut().extend(tokens.iter().cloned());
let i = iv(&tokens).await;
this.0.string_vecs.borrow_mut().insert(i.clone());
handle.reply(&v, &i.to_api()).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::IntReq::ExternStr(si) => {
let i = es(si.0).await;
this.0.strings.borrow_mut().insert(i.clone());
handle.reply(&si, &i.to_string()).await
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, mut rep_out) = channel(0);
{
let lex_g = this.0.lex_recur.lock().await;
let mut req_in =
lex_g.get(&sl.id).cloned().expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap();
api::IntReq::ExternStrv(vi) => {
let i = ev(vi.0).await;
this.0.strings.borrow_mut().extend(i.iter().cloned());
this.0.string_vecs.borrow_mut().insert(i.clone());
let markerv = i.iter().map(|t| t.to_api()).collect_vec();
handle.reply(&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 client = sys.client();
let reply =
client.request(api::Fwded(fw.0.clone(), *key, body.clone())).await.unwrap();
handle.reply(fw, &reply).await
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
let sys = ctx.system_inst(id).await.unwrap();
handle.reply(fw, &sys.request(body.clone()).await).await
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, mut rep_out) = channel(0);
{
let lex_g = this.0.lex_recur.lock().await;
let mut req_in =
lex_g.get(&sl.id).cloned().expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).await.unwrap();
}
handle.reply(&sl, &rep_out.next().await.unwrap()).await
},
api::ExtHostReq::ExprReq(expr_req) => match expr_req {
api::ExprReq::Inspect(ins @ api::Inspect { target }) => {
let expr = ctx.exprs.get_expr(target).expect("Invalid ticket");
handle
.reply(&ins, &api::Inspected {
refcount: expr.strong_count() as u32,
location: expr.pos().to_api(),
kind: expr.to_api().await,
})
.await
},
api::ExprReq::Create(ref cre @ api::Create(ref expr)) => {
let expr = Expr::from_api(expr, PathSetBuilder::new(), ctx.clone()).await;
let expr_id = expr.id();
ctx.exprs.give_expr(expr);
handle.reply(cre, &expr_id).await
},
},
api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => {
let reply: <api::LsModule as Request>::Response = 'reply: {
let path = ev(path).await;
let root = (ctx.root.read().await.upgrade())
.expect("LSModule called when root isn't in context");
let root_data = &*root.0.read().await;
let mut walk_ctx = (ctx.clone(), &root_data.consts);
let module =
match walk(&root_data.root, false, path.iter().cloned(), &mut walk_ctx).await
{
Ok(module) => module,
Err(ChildError { kind, .. }) =>
break 'reply Err(match kind {
ChildErrorKind::Private => panic!("Access checking was disabled"),
ChildErrorKind::Constant => api::LsModuleError::IsConstant,
ChildErrorKind::Missing => api::LsModuleError::InvalidPath,
}),
};
let mut members = std::collections::HashMap::new();
for (k, v) in &module.members {
let kind = match v.kind(ctx.clone(), &root_data.consts).await {
MemberKind::Const => api::MemberInfoKind::Constant,
MemberKind::Module(_) => api::MemberInfoKind::Module,
};
members.insert(k.to_api(), api::MemberInfo { public: v.public, kind });
}
hand.handle(&sl, &rep_out.next().await.unwrap()).await
},
api::ExtHostReq::ExprReq(expr_req) => match expr_req {
api::ExprReq::Inspect(ins @ api::Inspect { target }) => {
let expr = ctx.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::ExprReq::Create(ref cre @ api::Create(ref expr)) => {
let expr = Expr::from_api(expr, PathSetBuilder::new(), ctx.clone()).await;
let expr_id = expr.id();
ctx.exprs.give_expr(expr);
hand.handle(cre, &expr_id).await
},
},
api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => {
let reply: <api::LsModule as Request>::Response = 'reply: {
let path = i.ex(path).await;
let root = (ctx.root.read().await.upgrade())
.expect("LSModule called when root isn't in context");
let root_data = &*root.0.read().await;
let mut walk_ctx = (ctx.clone(), &root_data.consts);
let module =
match walk(&root_data.root, false, path.iter().cloned(), &mut walk_ctx)
.await
{
Ok(module) => module,
Err(ChildError { kind, .. }) =>
break 'reply Err(match kind {
ChildErrorKind::Private => panic!("Access checking was disabled"),
ChildErrorKind::Constant => api::LsModuleError::IsConstant,
ChildErrorKind::Missing => api::LsModuleError::InvalidPath,
}),
};
let mut members = std::collections::HashMap::new();
for (k, v) in &module.members {
let kind = match v.kind(ctx.clone(), &root_data.consts).await {
MemberKind::Const => api::MemberInfoKind::Constant,
MemberKind::Module(_) => api::MemberInfoKind::Module,
};
members.insert(k.to_api(), api::MemberInfo { public: v.public, kind });
}
Ok(api::ModuleInfo { members })
};
hand.handle(ls, &reply).await
},
api::ExtHostReq::ResolveNames(ref rn) => {
let api::ResolveNames { constid, names, sys } = rn;
let mut resolver = {
let systems = ctx.systems.read().await;
let weak_sys = systems.get(sys).expect("ResolveNames for invalid sys");
let sys = weak_sys.upgrade().expect("ResolveNames after sys drop");
sys.name_resolver(*constid).await
};
let responses = stream(async |mut cx| {
for name in names {
cx.emit(match resolver(&ctx.i.ex(*name).await[..]).await {
Ok(abs) => Ok(abs.to_sym(&ctx.i).await.to_api()),
Err(e) => Err(e.to_api()),
})
.await
}
})
.collect()
.await;
hand.handle(rn, &responses).await
},
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
let atom = AtomHand::from_api(atom, Pos::None, &mut ctx.clone()).await;
let unit = atom.print(&FmtCtxImpl { i: &this.ctx().i }).await;
hand.handle(eap, &unit.to_api()).await
},
}
})
}
},
),
Ok(api::ModuleInfo { members })
};
handle.reply(ls, &reply).await
},
api::ExtHostReq::ResolveNames(ref rn) => {
let api::ResolveNames { constid, names, sys } = rn;
let mut resolver = {
let systems = ctx.systems.read().await;
let weak_sys = systems.get(sys).expect("ResolveNames for invalid sys");
let sys = weak_sys.upgrade().expect("ResolveNames after sys drop");
sys.name_resolver(*constid).await
};
let responses = stream(async |mut cx| {
for name in names {
cx.emit(match resolver(&ev(*name).await[..]).await {
Ok(abs) => Ok(abs.to_sym().await.to_api()),
Err(e) => Err(e.to_api()),
})
.await
}
})
.collect()
.await;
handle.reply(rn, &responses).await
},
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
let atom = AtomHand::from_api(atom, Pos::None, &mut ctx.clone()).await;
let unit = atom.print(&FmtCtxImpl::default()).await;
handle.reply(eap, &unit.to_api()).await
},
}
})
.await
}
},
);
let join_ext = ctx.spawn(async {
future.await.unwrap();
// extension exited successfully
});
ExtensionData {
name: header.name.clone(),
ctx: ctx.clone(),
systems: (header.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
.collect(),
join_ext: Some(join_ext),
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(),
client: Rc::new(client),
strings: RefCell::default(),
string_vecs: RefCell::default(),
}
})))
}
pub fn name(&self) -> &String { &self.0.name }
#[must_use]
pub fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.reqnot }
pub fn client(&self) -> &dyn Client { &*self.0.client }
#[must_use]
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
#[must_use]
pub fn logger(&self) -> &Logger { &self.0.logger }
pub fn system_ctors(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
#[must_use]
pub async fn is_own_sys(&self, id: api::SysId) -> bool {
let Some(sys) = self.ctx().system_inst(id).await else {
writeln!(self.logger(), "Invalid system ID {id:?}");
writeln!(logger(), "Invalid system ID {id:?}");
return false;
};
Rc::ptr_eq(&self.0, &sys.ext().0)
@@ -274,7 +295,7 @@ impl Extension {
}
pub(crate) async fn lex_req<F: Future<Output = Option<api::SubLexed>>>(
&self,
source: Tok<String>,
source: IStr,
src: Sym,
pos: u32,
sys: api::SysId,
@@ -287,9 +308,10 @@ impl Extension {
self.0.lex_recur.lock().await.insert(id, req_in); // lex_recur released
let (ret, ()) = join(
async {
let res = (self.reqnot())
let res = (self.client())
.request(api::LexExpr { id, pos, sys, src: src.to_api(), text: source.to_api() })
.await;
.await
.unwrap();
// collect sender to unblock recursion handler branch before returning
self.0.lex_recur.lock().await.remove(&id);
res
@@ -306,10 +328,10 @@ impl Extension {
}
pub fn system_drop(&self, id: api::SysId) {
let rc = self.clone();
(self.ctx().spawn)(Box::pin(async move {
rc.reqnot().request(api::SystemDrop(id)).await;
let _ = self.ctx().spawn(with_stash(async move {
rc.client().request(api::SystemDrop(id)).await.unwrap();
rc.ctx().systems.write().await.remove(&id);
}))
}));
}
#[must_use]
pub fn downgrade(&self) -> WeakExtension { WeakExtension(Rc::downgrade(&self.0)) }

View File

@@ -1,10 +1,8 @@
use std::rc::Rc;
use futures::FutureExt;
use futures::lock::Mutex;
use orchid_base::clone;
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
use orchid_base::interner::Tok;
use orchid_base::interner::{IStr, is};
use orchid_base::location::SrcRange;
use orchid_base::name::Sym;
use orchid_base::parse::{name_char, name_start, op_char, unrep_space};
@@ -20,7 +18,7 @@ use crate::system::System;
pub struct LexCtx<'a> {
pub systems: &'a [System],
pub source: &'a Tok<String>,
pub source: &'a IStr,
pub path: &'a Sym,
pub tail: &'a str,
pub sub_trees: &'a mut Vec<Expr>,
@@ -60,7 +58,7 @@ impl<'a> LexCtx<'a> {
}
#[must_use]
pub async fn des_subtree(&mut self, tree: &api::TokenTree, exprs: ExprStore) -> ParsTokTree {
ParsTokTree::from_api(tree, &mut { exprs }, &mut self.ctx.clone(), self.path, &self.ctx.i).await
ParsTokTree::from_api(tree, &mut { exprs }, &mut self.ctx.clone(), self.path).await
}
#[must_use]
pub fn strip_char(&mut self, tgt: char) -> bool {
@@ -98,21 +96,21 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
let name = &ctx.tail[..ctx.tail.len() - tail.len() - "::".len()];
ctx.set_tail(tail);
let body = lex_once(ctx).boxed_local().await?;
ParsTok::NS(ctx.ctx.i.i(name).await, Box::new(body))
ParsTok::NS(is(name).await, Box::new(body))
} else if ctx.strip_prefix("--[") {
let Some((cmt, tail)) = ctx.tail.split_once("]--") else {
return Err(mk_errv(
ctx.ctx.i.i("Unterminated block comment").await,
is("Unterminated block comment").await,
"This block comment has no ending ]--",
[SrcRange::new(start..start + 3, ctx.path)],
));
};
ctx.set_tail(tail);
ParsTok::Comment(Rc::new(cmt.to_string()))
ParsTok::Comment(is(cmt).await)
} else if let Some(tail) = ctx.tail.strip_prefix("--").filter(|t| !t.starts_with(op_char)) {
let end = tail.find(['\n', '\r']).map_or(tail.len(), |n| n - 1);
ctx.push_pos(end as u32);
ParsTok::Comment(Rc::new(tail[2..end].to_string()))
ParsTok::Comment(is(&tail[2..end]).await)
} else if let Some(tail) = ctx.tail.strip_prefix('\\').filter(|t| t.starts_with(name_start)) {
// fanciness like \$placeh in templates is resolved in the macro engine.
ctx.set_tail(tail);
@@ -125,7 +123,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
while !ctx.strip_char(*rp) {
if ctx.tail.is_empty() {
return Err(mk_errv(
ctx.ctx.i.i("unclosed paren").await,
is("unclosed paren").await,
format!("this {lp} has no matching {rp}"),
[SrcRange::new(start..start + 1, ctx.path)],
));
@@ -162,10 +160,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
})
.await;
match lx {
Err(e) =>
return Err(
errors.into_iter().fold(OrcErrv::from_api(&e, &ctx.ctx.i).await, |a, b| a + b),
),
Err(e) => return Err(errors.into_iter().fold(OrcErrv::from_api(&e).await, |a, b| a + b)),
Ok(Some(lexed)) => {
ctx.set_pos(lexed.pos);
let lexed_tree = ctx.des_subtree(&lexed.expr, temp_store).await;
@@ -185,12 +180,12 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
}
}
if ctx.tail.starts_with(name_start) {
ParsTok::Name(ctx.ctx.i.i(ctx.get_start_matches(name_char)).await)
ParsTok::Name(is(ctx.get_start_matches(name_char)).await)
} else if ctx.tail.starts_with(op_char) {
ParsTok::Name(ctx.ctx.i.i(ctx.get_start_matches(op_char)).await)
ParsTok::Name(is(ctx.get_start_matches(op_char)).await)
} else {
return Err(mk_errv(
ctx.ctx.i.i("Unrecognized character").await,
is("Unrecognized character").await,
"The following syntax is meaningless.",
[SrcRange::new(start..start + 1, ctx.path)],
));
@@ -199,12 +194,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
Ok(ParsTokTree { tok, sr: SrcRange::new(start..ctx.get_pos(), ctx.path) })
}
pub async fn lex(
text: Tok<String>,
path: Sym,
systems: &[System],
ctx: &Ctx,
) -> OrcRes<Vec<ParsTokTree>> {
pub async fn lex(text: IStr, path: Sym, systems: &[System], ctx: &Ctx) -> OrcRes<Vec<ParsTokTree>> {
let mut sub_trees = Vec::new();
let mut ctx =
LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems, path: &path, ctx };

View File

@@ -1,12 +1,11 @@
use futures::FutureExt;
use itertools::Itertools;
use orchid_base::error::{OrcRes, Reporter, mk_errv};
use orchid_base::error::{OrcRes, mk_errv, report};
use orchid_base::format::fmt;
use orchid_base::interner::{Interner, Tok};
use orchid_base::interner::{IStr, is};
use orchid_base::name::Sym;
use orchid_base::parse::{
Comment, Import, ParseCtx, Parsed, Snippet, expect_end, line_items, parse_multiname,
try_pop_no_fluff,
Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, try_pop_no_fluff,
};
use orchid_base::tree::{Paren, TokTree, Token};
use substack::Substack;
@@ -22,12 +21,6 @@ pub struct HostParseCtxImpl<'a> {
pub ctx: Ctx,
pub src: Sym,
pub systems: &'a [System],
pub rep: &'a Reporter,
}
impl ParseCtx for HostParseCtxImpl<'_> {
fn rep(&self) -> &Reporter { self.rep }
fn i(&self) -> &Interner { &self.ctx.i }
}
impl HostParseCtx for HostParseCtxImpl<'_> {
@@ -36,7 +29,7 @@ impl HostParseCtx for HostParseCtxImpl<'_> {
fn src_path(&self) -> Sym { self.src.clone() }
}
pub trait HostParseCtx: ParseCtx {
pub trait HostParseCtx {
#[must_use]
fn ctx(&self) -> &Ctx;
#[must_use]
@@ -47,14 +40,14 @@ pub trait HostParseCtx: ParseCtx {
pub async fn parse_items(
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
path: Substack<'_, IStr>,
items: ParsSnippet<'_>,
) -> OrcRes<Vec<Item>> {
let lines = line_items(ctx, items).await;
let lines = line_items(items).await;
let mut line_ok = Vec::new();
for Parsed { output: comments, tail } in lines {
match parse_item(ctx, path.clone(), comments, tail).boxed_local().await {
Err(e) => ctx.rep().report(e),
Err(e) => report(e),
Ok(l) => line_ok.extend(l),
}
}
@@ -63,23 +56,23 @@ pub async fn parse_items(
pub async fn parse_item(
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
path: Substack<'_, IStr>,
comments: Vec<Comment>,
item: ParsSnippet<'_>,
) -> OrcRes<Vec<Item>> {
match item.pop_front() {
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
n if *n == ctx.i().i("export").await => match try_pop_no_fluff(ctx, postdisc).await? {
n if *n == is("export").await => match try_pop_no_fluff(postdisc).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await,
Parsed { output, tail: _ } => Err(mk_errv(
ctx.i().i("Malformed export").await,
is("Malformed export").await,
"`export` can either prefix other lines or list names inside ( )",
[output.sr()],
)),
},
n if *n == ctx.i().i("import").await => {
let imports = parse_import(ctx, postdisc).await?;
n if *n == is("import").await => {
let imports = parse_import(postdisc).await?;
Ok(Vec::from_iter(imports.into_iter().map(|t| Item {
comments: comments.clone(),
sr: t.sr.clone(),
@@ -88,33 +81,29 @@ pub async fn parse_item(
},
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc).await,
},
Some(_) => Err(mk_errv(
ctx.i().i("Expected a line type").await,
"All lines must begin with a keyword",
[item.sr()],
)),
Some(_) =>
Err(mk_errv(is("Expected a line type").await, "All lines must begin with a keyword", [
item.sr()
])),
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
}
}
pub async fn parse_import<'a>(
ctx: &impl HostParseCtx,
tail: ParsSnippet<'a>,
) -> OrcRes<Vec<Import>> {
let Parsed { output: imports, tail } = parse_multiname(ctx, tail).await?;
expect_end(ctx, tail).await?;
pub async fn parse_import<'a>(tail: ParsSnippet<'a>) -> OrcRes<Vec<Import>> {
let Parsed { output: imports, tail } = parse_multiname(tail).await?;
expect_end(tail).await?;
Ok(imports)
}
pub async fn parse_exportable_item<'a>(
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
path: Substack<'_, IStr>,
comments: Vec<Comment>,
exported: bool,
discr: Tok<String>,
discr: IStr,
tail: ParsSnippet<'a>,
) -> OrcRes<Vec<Item>> {
let kind = if discr == ctx.i().i("mod").await {
let kind = if discr == is("mod").await {
let (name, body) = parse_module(ctx, path, tail).await?;
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::Mod(body) })
} else if let Some(parser) = ctx.systems().find_map(|s| s.get_parser(discr.clone())) {
@@ -127,7 +116,7 @@ pub async fn parse_exportable_item<'a>(
} else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv(
ctx.i().i("Unrecognized line type").await,
is("Unrecognized line type").await,
format!("Line types are: mod, {ext_lines}"),
[tail.prev().sr()],
));
@@ -137,25 +126,25 @@ pub async fn parse_exportable_item<'a>(
pub async fn parse_module<'a>(
ctx: &impl HostParseCtx,
path: Substack<'_, Tok<String>>,
path: Substack<'_, IStr>,
tail: ParsSnippet<'a>,
) -> OrcRes<(Tok<String>, ParsedModule)> {
let (name, tail) = match try_pop_no_fluff(ctx, tail).await? {
) -> OrcRes<(IStr, ParsedModule)> {
let (name, tail) = match try_pop_no_fluff(tail).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
Parsed { output, .. } => {
return Err(mk_errv(
ctx.i().i("Missing module name").await,
format!("A name was expected, {} was found", fmt(output, ctx.i()).await),
is("Missing module name").await,
format!("A name was expected, {} was found", fmt(output).await),
[output.sr()],
));
},
};
let Parsed { output, tail: surplus } = try_pop_no_fluff(ctx, tail).await?;
expect_end(ctx, surplus).await?;
let Parsed { output, tail: surplus } = try_pop_no_fluff(tail).await?;
expect_end(surplus).await?;
let Some(body) = output.as_s(Paren::Round) else {
return Err(mk_errv(
ctx.i().i("Expected module body").await,
format!("A ( block ) was expected, {} was found", fmt(output, ctx.i()).await),
is("Expected module body").await,
format!("A ( block ) was expected, {} was found", fmt(output).await),
[output.sr()],
));
};

View File

@@ -6,7 +6,7 @@ use futures::future::{LocalBoxFuture, join_all};
use hashbrown::HashSet;
use itertools::Itertools;
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::interner::Tok;
use orchid_base::interner::{IStr, IStrv};
use orchid_base::location::SrcRange;
use orchid_base::parse::{Comment, Import};
use orchid_base::tl_cache;
@@ -57,10 +57,10 @@ impl Format for Item {
ItemKind::Member(mem) => match &mem.kind {
ParsedMemberKind::Const(_, sys) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}")))
.units([mem.name.rc().into(), sys.print(c).await]),
.units([mem.name.to_string().into(), sys.print(c).await]),
ParsedMemberKind::Mod(module) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
.units([mem.name.rc().into(), module.print(c).boxed_local().await]),
.units([mem.name.to_string().into(), module.print(c).boxed_local().await]),
},
};
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{0}\n{1}")))
@@ -69,14 +69,14 @@ impl Format for Item {
}
pub struct ParsedMember {
pub name: Tok<String>,
pub name: IStr,
pub exported: bool,
pub kind: ParsedMemberKind,
}
impl ParsedMember {
#[must_use]
pub fn name(&self) -> Tok<String> { self.name.clone() }
pub fn new(exported: bool, name: Tok<String>, kind: impl Into<ParsedMemberKind>) -> Self {
pub fn name(&self) -> IStr { self.name.clone() }
pub fn new(exported: bool, name: IStr, kind: impl Into<ParsedMemberKind>) -> Self {
Self { exported, name, kind: kind.into() }
}
}
@@ -89,17 +89,14 @@ impl Debug for ParsedMember {
}
}
pub(crate) type ParsedExprCallback =
Rc<dyn for<'a> Fn(&'a [Tok<String>]) -> LocalBoxFuture<'a, Expr>>;
pub(crate) type ParsedExprCallback = Rc<dyn for<'a> Fn(&'a [IStr]) -> LocalBoxFuture<'a, Expr>>;
pub struct ParsedExpr {
pub(crate) debug: String,
pub(crate) callback: ParsedExprCallback,
}
impl ParsedExpr {
pub async fn run(self, imported_names: &[Tok<String>]) -> Expr {
(self.callback)(imported_names).await
}
pub async fn run(self, imported_names: &[IStr]) -> Expr { (self.callback)(imported_names).await }
}
impl fmt::Debug for ParsedExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.debug) }
@@ -115,7 +112,7 @@ impl From<ParsedModule> for ParsedMemberKind {
}
#[derive(Debug, Default)]
pub struct ParsedModule {
pub exports: Vec<Tok<String>>,
pub exports: Vec<IStr>,
pub items: Vec<Item>,
pub use_prelude: bool,
}
@@ -141,7 +138,7 @@ impl ParsedModule {
(self.items.iter())
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
}
pub fn default_item(self, name: Tok<String>, sr: SrcRange) -> Item {
pub fn default_item(self, name: IStr, sr: SrcRange) -> Item {
let mem = ParsedMember { exported: true, name, kind: ParsedMemberKind::Mod(self) };
Item { comments: vec![], sr, kind: ItemKind::Member(mem) }
}
@@ -150,7 +147,7 @@ impl Tree for ParsedModule {
type Ctx<'a> = ();
async fn child(
&self,
key: Tok<String>,
key: IStr,
public_only: bool,
(): &mut Self::Ctx<'_>,
) -> ChildResult<'_, Self> {
@@ -168,7 +165,7 @@ impl Tree for ParsedModule {
}
ChildResult::Err(ChildErrorKind::Missing)
}
fn children(&self, public_only: bool) -> HashSet<Tok<String>> {
fn children(&self, public_only: bool) -> HashSet<IStr> {
let mut public: HashSet<_> = self.exports.iter().cloned().collect();
if !public_only {
public.extend(
@@ -197,11 +194,11 @@ impl Format for ParsedModule {
/// point to a module and rule_loc selects a macro rule within that module
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct ConstPath {
steps: Tok<Vec<Tok<String>>>,
steps: IStrv,
}
impl ConstPath {
#[must_use]
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> Self { Self { steps } }
pub fn to_const(steps: IStrv) -> Self { Self { steps } }
}
pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree {

View File

@@ -1,102 +1,32 @@
use std::cell::RefCell;
use std::io::{self, Write};
use std::pin::Pin;
use std::io;
use async_process::{self, Child, ChildStdin, ChildStdout};
use futures::future::LocalBoxFuture;
use async_process;
use futures::io::BufReader;
use futures::lock::Mutex;
use futures::{self, AsyncBufReadExt, AsyncWriteExt};
use orchid_api_traits::{Decode, Encode};
use orchid_base::builtin::{ExtInit, ExtPort};
use orchid_base::logging::Logger;
use orchid_base::msg::{recv_msg, send_msg};
use futures::{self, AsyncBufReadExt};
use orchid_base::logging::logger;
use crate::api;
use crate::ctx::Ctx;
use crate::extension::ExtPort;
pub async fn ext_command(
cmd: std::process::Command,
logger: Logger,
msg_logs: Logger,
ctx: Ctx,
) -> io::Result<ExtInit> {
pub async fn ext_command(cmd: std::process::Command, ctx: Ctx) -> io::Result<ExtPort> {
let mut child = async_process::Command::from(cmd)
.stdin(async_process::Stdio::piped())
.stdout(async_process::Stdio::piped())
.stderr(async_process::Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
api::HostHeader { log_strategy: logger.strat(), msg_logs: msg_logs.strat() }
.encode(Pin::new(&mut stdin))
.await;
let mut stdout = child.stdout.take().unwrap();
let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await;
let stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap();
let mut child_stderr = child.stderr.take().unwrap();
(ctx.spawn)(Box::pin(async move {
let _ = ctx.spawn(Box::pin(async move {
let _ = child;
let mut reader = BufReader::new(&mut 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"));
logger().log(buf.strip_suffix('\n').expect("Readline implies this"));
}
}));
Ok(ExtInit {
port: Box::new(Subprocess {
name: header.name.clone(),
child: RefCell::new(Some(child)),
stdin: Some(Mutex::new(Box::pin(stdin))),
stdout: Mutex::new(Box::pin(stdout)),
ctx,
}),
header,
})
}
pub struct Subprocess {
name: String,
child: RefCell<Option<Child>>,
stdin: Option<Mutex<Pin<Box<ChildStdin>>>>,
stdout: Mutex<Pin<Box<ChildStdout>>>,
ctx: Ctx,
}
impl Drop for Subprocess {
fn drop(&mut self) {
let mut child = self.child.borrow_mut().take().unwrap();
let name = self.name.clone();
if std::thread::panicking() {
eprintln!("Killing extension {name}");
// we don't really care to handle errors here
let _: Result<_, _> = std::io::stderr().flush();
let _: Result<_, _> = child.kill();
return;
}
let stdin = self.stdin.take().unwrap();
(self.ctx.spawn)(Box::pin(async move {
stdin.lock().await.close().await.unwrap();
let status = (child.status().await)
.unwrap_or_else(|e| panic!("{e}, extension {name} exited with error"));
assert!(status.success(), "Extension {name} exited with error {status}");
}))
}
}
impl ExtPort for Subprocess {
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> {
Box::pin(async {
send_msg(Pin::new(&mut *self.stdin.as_ref().unwrap().lock().await), msg).await.unwrap()
})
}
fn recv(&self) -> LocalBoxFuture<'_, Option<Vec<u8>>> {
Box::pin(async {
std::io::Write::flush(&mut std::io::stderr()).unwrap();
match recv_msg(self.stdout.lock().await.as_mut()).await {
Ok(msg) => Some(msg),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => None,
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => None,
Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()),
}
})
}
Ok(ExtPort { input: Box::pin(stdin), output: Box::pin(stdout) })
}

View File

@@ -2,11 +2,11 @@ use futures::FutureExt;
use futures::future::join_all;
use itertools::Itertools;
use orchid_base::error::{OrcErrv, OrcRes};
use orchid_base::interner::{Interner, Tok};
use orchid_base::interner::{IStr, es};
use orchid_base::location::SrcRange;
use orchid_base::name::Sym;
use orchid_base::parse::Comment;
use orchid_base::reqnot::Requester;
use orchid_base::reqnot::ClientExt;
use orchid_base::tree::ttv_from_api;
use substack::Substack;
@@ -22,7 +22,7 @@ pub struct Parser {
pub(crate) system: System,
pub(crate) idx: u16,
}
type ModPath<'a> = Substack<'a, Tok<String>>;
type ModPath<'a> = Substack<'a, IStr>;
impl Parser {
pub async fn parse(
@@ -39,7 +39,7 @@ impl Parser {
let line =
join_all((line.into_iter()).map(|t| async { tt_to_api(&mut temp_store.clone(), t).await }))
.await;
let mod_path = ctx.src_path().suffix(path.unreverse(), self.system.i()).await;
let mod_path = ctx.src_path().suffix(path.unreverse()).await;
let comments = comments.iter().map(Comment::to_api).collect_vec();
let req = api::ParseLine {
idx: self.idx,
@@ -50,17 +50,16 @@ impl Parser {
comments,
line,
};
match self.system.reqnot().request(req).await {
match self.system.client().request(req).await.unwrap() {
Ok(parsed_v) =>
conv(parsed_v, path, callback, &mut ConvCtx {
i: self.system.i(),
mod_path: &mod_path,
ext_exprs: &mut temp_store,
src_path: &src_path,
sys: &self.system,
})
.await,
Err(e) => Err(OrcErrv::from_api(&e, &self.system.ctx().i).await),
Err(e) => Err(OrcErrv::from_api(&e).await),
}
}
}
@@ -69,13 +68,12 @@ struct ConvCtx<'a> {
sys: &'a System,
mod_path: &'a Sym,
src_path: &'a Sym,
i: &'a Interner,
ext_exprs: &'a mut ExprStore,
}
async fn conv(
parsed_v: Vec<api::ParsedLine>,
module: Substack<'_, Tok<String>>,
callback: &'_ mut impl AsyncFnMut(Substack<'_, Tok<String>>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
module: Substack<'_, IStr>,
callback: &'_ mut impl AsyncFnMut(Substack<'_, IStr>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
ctx: &mut ConvCtx<'_>,
) -> OrcRes<Vec<Item>> {
let mut items = Vec::new();
@@ -85,12 +83,12 @@ async fn conv(
(name, exported, kind),
api::ParsedLineKind::Recursive(rec) => {
let tokens =
ttv_from_api(rec, ctx.ext_exprs, &mut ctx.sys.ctx().clone(), ctx.src_path, ctx.i).await;
ttv_from_api(rec, ctx.ext_exprs, &mut ctx.sys.ctx().clone(), ctx.src_path).await;
items.extend(callback(module.clone(), tokens).await?);
continue;
},
};
let name = ctx.i.ex(name).await;
let name = es(name).await;
let mem_path = module.push(name.clone());
let mkind = match kind {
api::ParsedMemberKind::Module { lines, use_prelude } => {
@@ -98,16 +96,16 @@ async fn conv(
ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items))
},
api::ParsedMemberKind::Constant(cid) => {
ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse(), ctx.i).await);
ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(mem_path.unreverse()).await);
ParsedMemberKind::Const(cid, ctx.sys.clone())
},
};
items.push(Item {
comments: join_all(
parsed.comments.iter().map(|c| Comment::from_api(c, ctx.src_path.clone(), ctx.i)),
parsed.comments.iter().map(|c| Comment::from_api(c, ctx.src_path.clone())),
)
.await,
sr: SrcRange::from_api(&parsed.source_range, ctx.i).await,
sr: SrcRange::from_api(&parsed.source_range).await,
kind: ItemKind::Member(ParsedMember { name, exported, kind: mkind }),
})
}

View File

@@ -12,10 +12,11 @@ use memo_map::MemoMap;
use orchid_base::char_filter::char_filter_match;
use orchid_base::error::{OrcRes, mk_errv_floating};
use orchid_base::format::{FmtCtx, FmtUnit, Format};
use orchid_base::interner::{Interner, Tok};
use orchid_base::interner::{IStr, es, is};
use orchid_base::iter_utils::IteratorPrint;
use orchid_base::logging::logger;
use orchid_base::name::{NameLike, Sym, VName, VPath};
use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::reqnot::{Client, ClientExt};
use ordered_float::NotNan;
use substack::{Stackframe, Substack};
@@ -35,7 +36,7 @@ pub(crate) struct SystemInstData {
decl_id: api::SysDeclId,
lex_filter: api::CharFilter,
id: api::SysId,
line_types: Vec<Tok<String>>,
line_types: Vec<IStr>,
prelude: Vec<Sym>,
owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub(crate) const_paths: MemoMap<api::ParsedConstId, Sym>,
@@ -68,8 +69,6 @@ impl System {
#[must_use]
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
#[must_use]
pub fn i(&self) -> &Interner { &self.0.ctx.i }
#[must_use]
pub fn deps(&self) -> &[System] { &self.0.deps }
#[must_use]
pub fn ctor(&self) -> SystemCtor {
@@ -77,22 +76,26 @@ impl System {
.expect("Ctor was used to create ext")
}
#[must_use]
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { self.0.ext.reqnot() }
pub(crate) fn client(&self) -> &dyn Client { self.0.ext.client() }
#[must_use]
pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
self.reqnot().request(api::GetMember(self.0.id, id)).await
self.client().request(api::GetMember(self.0.id, id)).await.unwrap()
}
#[must_use]
pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() }
#[must_use]
pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) }
pub fn can_lex(&self, c: char) -> bool {
let ret = char_filter_match(&self.0.lex_filter, c);
writeln!(logger(), "{} can lex {c}: {}", self.ctor().name(), ret);
ret
}
#[must_use]
pub fn prelude(&self) -> Vec<Sym> { self.0.prelude.clone() }
/// Have this system lex a part of the source. It is assumed that
/// [Self::can_lex] was called and returned true.
pub async fn lex<F: Future<Output = Option<api::SubLexed>>>(
&self,
source: Tok<String>,
source: IStr,
src: Sym,
pos: u32,
r: impl FnMut(u32) -> F,
@@ -100,16 +103,16 @@ impl System {
self.0.ext.lex_req(source, src, pos, self.id(), r).await
}
#[must_use]
pub fn get_parser(&self, ltyp: Tok<String>) -> Option<Parser> {
pub fn get_parser(&self, ltyp: IStr) -> Option<Parser> {
(self.0.line_types.iter().enumerate())
.find(|(_, txt)| *txt == &ltyp)
.map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() })
}
pub fn line_types(&self) -> impl Iterator<Item = &Tok<String>> + '_ { self.0.line_types.iter() }
pub fn line_types(&self) -> impl Iterator<Item = &IStr> + '_ { self.0.line_types.iter() }
#[must_use]
pub async fn request(&self, req: Vec<u8>) -> Vec<u8> {
self.reqnot().request(api::SysFwded(self.id(), req)).await
self.client().request(api::SysFwded(self.id(), req)).await.unwrap()
}
pub(crate) async fn new_atom(&self, data: Vec<u8>, id: api::AtomId) -> AtomHand {
let mut owned_g = self.0.owned_atoms.write().await;
@@ -124,10 +127,10 @@ impl System {
}
pub(crate) fn drop_atom(&self, dropped_atom_id: api::AtomId) {
let this = self.0.clone();
(self.0.ctx.spawn)(Box::pin(async move {
this.ext.reqnot().request(api::AtomDrop(this.id, dropped_atom_id)).await;
let _ = self.0.ctx.spawn(Box::pin(async move {
this.ext.client().request(api::AtomDrop(this.id, dropped_atom_id)).await.unwrap();
this.owned_atoms.write().await.remove(&dropped_atom_id);
}))
}));
}
#[must_use]
pub fn downgrade(&self) -> WeakSystem {
@@ -137,7 +140,7 @@ impl System {
pub(crate) async fn name_resolver(
&self,
orig: api::ParsedConstId,
) -> impl AsyncFnMut(&[Tok<String>]) -> OrcRes<VName> + use<> {
) -> impl AsyncFnMut(&[IStr]) -> OrcRes<VName> + use<> {
let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context");
let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone();
let ctx = self.0.ctx.clone();
@@ -155,7 +158,7 @@ impl System {
Some(Ok(dest)) => return Ok(dest.target.to_vname().suffix(tail.iter().cloned())),
Some(Err(dests)) =>
return Err(mk_errv_floating(
ctx.i.i("Ambiguous name").await,
is("Ambiguous name").await,
format!(
"{selector} could refer to {}",
dests.iter().map(|ri| &ri.target).display("or")
@@ -170,7 +173,7 @@ impl System {
return Ok(VPath::new(cwd.iter().cloned()).name_with_suffix(selector.clone()));
}
Err(mk_errv_floating(
ctx.i.i("Invalid name").await,
is("Invalid name").await,
format!("{selector} doesn't refer to a module"),
))
}
@@ -203,8 +206,7 @@ impl SystemCtor {
#[must_use]
pub fn name(&self) -> &str { &self.decl.name }
pub async fn name_tok(&self) -> Sym {
(Sym::parse(&self.decl.name, &self.ext.upgrade().expect("ext dropped early").ctx().i).await)
.expect("System cannot have empty name")
(Sym::parse(&self.decl.name).await).expect("System cannot have empty name")
}
#[must_use]
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
@@ -220,17 +222,17 @@ impl SystemCtor {
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
let id = ext.ctx().next_sys_id();
let sys_inst = ext.reqnot().request(api::NewSystem { depends, id, system: self.decl.id }).await;
let sys_inst =
ext.client().request(api::NewSystem { depends, id, system: self.decl.id }).await.unwrap();
let data = System(Rc::new(SystemInstData {
deps,
decl_id: self.decl.id,
ext: ext.clone(),
ctx: ext.ctx().clone(),
lex_filter: sys_inst.lex_filter,
line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i)))
.await,
line_types: join_all(sys_inst.line_types.iter().map(|m| es(*m))).await,
id,
prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok, &ext.ctx().i))).await,
prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok))).await,
owned_atoms: RwLock::new(HashMap::new()),
const_paths: MemoMap::new(),
}));

View File

@@ -13,11 +13,11 @@ use hashbrown::hash_map::Entry;
use itertools::Itertools;
use memo_map::MemoMap;
use orchid_base::clone;
use orchid_base::error::{OrcRes, Reporter, mk_errv};
use orchid_base::interner::Tok;
use orchid_base::error::{OrcRes, mk_errv, report};
use orchid_base::interner::{IStr, IStrv, es, is, iv};
use orchid_base::location::{CodeGenInfo, Pos};
use orchid_base::name::{NameLike, Sym, VPath};
use orchid_base::reqnot::Requester;
use orchid_base::reqnot::ClientExt;
use crate::api;
use crate::ctx::Ctx;
@@ -45,7 +45,7 @@ impl Root {
#[must_use]
pub async fn from_api(api: api::Module, sys: &System) -> Self {
let consts = MemoMap::new();
let mut tfac = TreeFromApiCtx { consts: &consts, path: sys.i().i(&[][..]).await, sys };
let mut tfac = TreeFromApiCtx { consts: &consts, path: iv(&[][..]).await, sys };
let root = Module::from_api(api, &mut tfac).await;
Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() })))
}
@@ -60,7 +60,7 @@ impl Root {
Ok(Self(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() }))))
}
#[must_use]
pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym, rep: &Reporter) -> Self {
pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym) -> Self {
let mut ref_this = self.0.write().await;
let this = &mut *ref_this;
let mut deferred_consts = HashMap::new();
@@ -72,7 +72,6 @@ impl Root {
pars_prefix: pars_prefix.clone(),
root: &this.root,
ctx: &this.ctx,
rep,
};
let mut module = Module::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await;
for step in pars_prefix.iter().rev() {
@@ -89,7 +88,7 @@ impl Root {
*this.ctx.root.write().await = new.downgrade();
for (path, (sys_id, pc_id)) in deferred_consts {
let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing");
let api_expr = sys.reqnot().request(api::FetchParsedConst(sys.id(), pc_id)).await;
let api_expr = sys.client().request(api::FetchParsedConst(sys.id(), pc_id)).await.unwrap();
let expr = Expr::from_api(&api_expr, PathSetBuilder::new(), this.ctx.clone()).await;
new.0.write().await.consts.insert(path, expr);
}
@@ -110,7 +109,7 @@ impl Root {
}
match module {
Ok(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
is("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos],
)),
@@ -118,7 +117,7 @@ impl Root {
ChildErrorKind::Private => panic!("public_only is false"),
ChildErrorKind::Constant => panic!("Tree refers to constant not in table"),
ChildErrorKind::Missing => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
is("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos],
)),
@@ -144,12 +143,12 @@ impl Default for WeakRoot {
pub struct TreeFromApiCtx<'a> {
pub sys: &'a System,
pub consts: &'a MemoMap<Sym, Expr>,
pub path: Tok<Vec<Tok<String>>>,
pub path: IStrv,
}
impl<'a> TreeFromApiCtx<'a> {
#[must_use]
pub async fn push<'c>(&'c self, name: Tok<String>) -> TreeFromApiCtx<'c> {
let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await;
pub async fn push<'c>(&'c self, name: IStr) -> TreeFromApiCtx<'c> {
let path = iv(&self.path.iter().cloned().chain([name]).collect_vec()).await;
TreeFromApiCtx { path, consts: self.consts, sys: self.sys }
}
}
@@ -162,17 +161,17 @@ pub struct ResolvedImport {
#[derive(Clone, Default)]
pub struct Module {
pub imports: HashMap<Tok<String>, Result<ResolvedImport, Vec<ResolvedImport>>>,
pub members: HashMap<Tok<String>, Rc<Member>>,
pub imports: HashMap<IStr, Result<ResolvedImport, Vec<ResolvedImport>>>,
pub members: HashMap<IStr, Rc<Member>>,
}
impl Module {
#[must_use]
pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self {
let mut members = HashMap::new();
for mem in api.members {
let mem_name = ctx.sys.i().ex(mem.name).await;
let mem_name = es(mem.name).await;
let vname = VPath::new(ctx.path.iter().cloned()).name_with_suffix(mem_name.clone());
let name = vname.to_sym(ctx.sys.i()).await;
let name = vname.to_sym().await;
let (lazy, kind) = match mem.kind {
api::MemberKind::Lazy(id) =>
(Some(LazyMemberHandle { id, sys: ctx.sys.id(), path: name.clone() }), None),
@@ -205,23 +204,23 @@ impl Module {
let mut glob_imports_by_name = HashMap::<_, Vec<_>>::new();
for import in parsed.get_imports().into_iter().filter(|i| i.name.is_none()) {
let pos = import.sr.pos();
match absolute_path(&path, &import.path, &ctx.ctx.i).await {
Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, pos, &import.path.to_string()).await),
match absolute_path(&path, &import.path).await {
Err(e) => report(e.err_obj(pos, &import.path.to_string()).await),
Ok(abs_path) => {
let names_res = match abs_path.strip_prefix(&ctx.pars_prefix[..]) {
None => {
let mut tree_ctx = (ctx.ctx.clone(), ctx.consts);
resolv_glob(&path, ctx.root, &abs_path, pos, &ctx.ctx.i, &mut tree_ctx).await
resolv_glob(&path, ctx.root, &abs_path, pos, &mut tree_ctx).await
},
Some(sub_tgt) => {
let sub_path = (path.strip_prefix(&ctx.pars_prefix[..]))
.expect("from_parsed called with path outside pars_prefix");
resolv_glob(sub_path, ctx.pars_root, sub_tgt, pos, &ctx.ctx.i, &mut ()).await
resolv_glob(sub_path, ctx.pars_root, sub_tgt, pos, &mut ()).await
},
};
let abs_path = abs_path.to_sym(&ctx.ctx.i).await;
let abs_path = abs_path.to_sym().await;
match names_res {
Err(e) => ctx.rep.report(e),
Err(e) => report(e),
Ok(names) =>
for name in names {
match glob_imports_by_name.entry(name) {
@@ -244,30 +243,28 @@ impl Module {
prelude_item.last_seg(),
Ok(ResolvedImport {
target: prelude_item,
pos: CodeGenInfo::new_details(sys.ctor().name_tok().await, "In prelude", &ctx.ctx.i)
.await
.pos(),
pos: CodeGenInfo::new_details(sys.ctor().name_tok().await, "In prelude").await.pos(),
}),
);
}
}
}
let conflicting_imports_msg = ctx.ctx.i.i("Conflicting imports").await;
let conflicting_imports_msg = is("Conflicting imports").await;
for (key, values) in imports_by_name {
if values.len() == 1 {
let import = values.into_iter().next().unwrap();
let sr = import.sr.clone();
let abs_path_res = absolute_path(&path, &import.clone().mspath(), &ctx.ctx.i).await;
let abs_path_res = absolute_path(&path, &import.clone().mspath()).await;
match abs_path_res {
Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, sr.pos(), &import.to_string()).await),
Err(e) => report(e.err_obj(sr.pos(), &import.to_string()).await),
Ok(abs_path) => {
let target = abs_path.to_sym(&ctx.ctx.i).await;
let target = abs_path.to_sym().await;
imports.insert(key, Ok(ResolvedImport { target, pos: sr.pos() }));
},
}
} else {
for item in values {
ctx.rep.report(mk_errv(
report(mk_errv(
conflicting_imports_msg.clone(),
format!("{key} is imported multiple times from different modules"),
[item.sr.pos()],
@@ -277,12 +274,11 @@ impl Module {
}
for (key, values) in glob_imports_by_name {
if !imports.contains_key(&key) {
let i = &ctx.ctx.i;
let values = stream::iter(values)
.then(|(n, sr)| {
clone!(key; async move {
ResolvedImport {
target: n.to_vname().suffix([key.clone()]).to_sym(i).await,
target: n.to_vname().suffix([key.clone()]).to_sym().await,
pos: sr.pos(),
}
})
@@ -292,12 +288,12 @@ impl Module {
imports.insert(key, if values.len() == 1 { Ok(values[0].clone()) } else { Err(values) });
}
}
let self_referential_msg = ctx.ctx.i.i("Self-referential import").await;
let self_referential_msg = is("Self-referential import").await;
for (key, value) in imports.iter() {
let Ok(import) = value else { continue };
if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(slice::from_ref(key)))
{
ctx.rep.report(mk_errv(
report(mk_errv(
self_referential_msg.clone(),
format!("import {} points to itself or a path within itself", &import.target),
[import.pos.clone()],
@@ -308,7 +304,7 @@ impl Module {
for item in &parsed.items {
match &item.kind {
ItemKind::Member(mem) => {
let path = path.to_vname().suffix([mem.name.clone()]).to_sym(&ctx.ctx.i).await;
let path = path.to_vname().suffix([mem.name.clone()]).to_sym().await;
let kind = OnceCell::from(MemberKind::from_parsed(&mem.kind, path.clone(), ctx).await);
members.insert(
mem.name.clone(),
@@ -385,7 +381,6 @@ pub struct FromParsedCtx<'a> {
pars_prefix: Sym,
pars_root: &'a ParsedModule,
root: &'a Module,
rep: &'a Reporter,
ctx: &'a Ctx,
consts: &'a MemoMap<Sym, Expr>,
deferred_consts: &'a mut HashMap<Sym, (api::SysId, api::ParsedConstId)>,
@@ -395,7 +390,7 @@ impl Tree for Module {
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
async fn child(
&self,
key: Tok<String>,
key: IStr,
public_only: bool,
(ctx, consts): &mut Self::Ctx<'_>,
) -> crate::dealias::ChildResult<'_, Self> {
@@ -410,7 +405,7 @@ impl Tree for Module {
MemberKind::Const => Err(ChildErrorKind::Constant),
}
}
fn children(&self, public_only: bool) -> hashbrown::HashSet<Tok<String>> {
fn children(&self, public_only: bool) -> hashbrown::HashSet<IStr> {
self.members.iter().filter(|(_, v)| !public_only || v.public).map(|(k, _)| k.clone()).collect()
}
}