partway through fixes, macro system needs resdesign
Some checks failed
Rust / build (push) Has been cancelled

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

View File

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

View File

@@ -94,7 +94,7 @@ 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.client().request(api::FinalFwded(self.0.api_ref(), key, req)).await.unwrap()
self.0.owner.client().request(api::Fwded(self.0.api_ref(), key, req)).await.unwrap()
}
#[must_use]
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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