diff --git a/examples/hello-world/main.orc b/examples/hello-world/main.orc index fa6faaf..d8a6b9d 100644 --- a/examples/hello-world/main.orc +++ b/examples/hello-world/main.orc @@ -1,2 +1,2 @@ -const user = "dave" -const main = println "Hello $user!" exit_status::success +let user = "dave" +let main = println "Hello $user!" exit_status::success diff --git a/orchid-api-traits/src/coding.rs b/orchid-api-traits/src/coding.rs index 8e6445d..d8aeb59 100644 --- a/orchid-api-traits/src/coding.rs +++ b/orchid-api-traits/src/coding.rs @@ -270,7 +270,6 @@ impl Encode for bool { } impl Decode for [T; N] { async fn decode(mut read: Pin<&mut R>) -> Self { - // TODO: figure out how to do this in safe rust on the stack let v = stream(async |mut cx| { for _ in 0..N { cx.emit(T::decode(read.as_mut()).await).await diff --git a/orchid-api/src/parser.rs b/orchid-api/src/parser.rs index 190dffc..22da5d2 100644 --- a/orchid-api/src/parser.rs +++ b/orchid-api/src/parser.rs @@ -30,6 +30,7 @@ pub struct ParseLine { pub src: TStrv, pub comments: Vec, pub exported: bool, + pub idx: u16, pub line: Vec, } impl Request for ParseLine { diff --git a/orchid-base/src/number.rs b/orchid-base/src/number.rs index fef744b..83d1ae6 100644 --- a/orchid-base/src/number.rs +++ b/orchid-base/src/number.rs @@ -79,7 +79,6 @@ pub fn parse_num(string: &str) -> Result { .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_s, exponent) = match noprefix.split_once('p') { Some((b, e)) => { @@ -88,7 +87,6 @@ pub fn parse_num(string: &str) -> Result { }, None => (noprefix, 0), }; - eprintln!("({base_s},{exponent})"); match base_s.split_once('.') { None => { let base = int_parse(base_s, radix, pos)?; diff --git a/orchid-base/src/reqnot.rs b/orchid-base/src/reqnot.rs index d7b6035..6c4a59a 100644 --- a/orchid-base/src/reqnot.rs +++ b/orchid-base/src/reqnot.rs @@ -252,15 +252,14 @@ mod test { use futures::FutureExt; use futures::lock::Mutex; - use orchid_api::LogStrategy; use orchid_api_derive::Coding; use orchid_api_traits::{Channel, Request}; use test_executors::spin_on; use super::{MsgSet, ReqNot}; - use crate::clone; use crate::logging::Logger; use crate::reqnot::Requester as _; + use crate::{api, clone}; #[derive(Clone, Debug, Coding, PartialEq)] pub struct TestReq(u8); @@ -283,7 +282,7 @@ mod test { #[test] fn notification() { spin_on(async { - let logger = Logger::new(LogStrategy::StdErr); + let logger = Logger::new(api::LogStrategy::StdErr); let received = Arc::new(Mutex::new(None)); let receiver = ReqNot::::new( logger.clone(), @@ -311,7 +310,7 @@ mod test { #[test] fn request() { spin_on(async { - let logger = Logger::new(LogStrategy::StdErr); + let logger = Logger::new(api::LogStrategy::StdErr); let receiver = Rc::new(Mutex::>>::new(None)); let sender = Rc::new(ReqNot::::new( logger.clone(), diff --git a/orchid-extension/src/atom_owned.rs b/orchid-extension/src/atom_owned.rs index afd938e..c3876c1 100644 --- a/orchid-extension/src/atom_owned.rs +++ b/orchid-extension/src/atom_owned.rs @@ -13,7 +13,6 @@ use futures::{AsyncRead, AsyncWrite, FutureExt}; use itertools::Itertools; use memo_map::MemoMap; use never::Never; -use orchid_api::AtomId; use orchid_api_traits::{Decode, Encode, enc_vec}; use orchid_base::error::OrcRes; use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit}; @@ -41,7 +40,6 @@ impl> AtomicFeaturesImpl>::new(&mut data)).await; ctx.get_or_default::().objects.read().await.insert(atom_id, Box::new(self)); - eprintln!("Created atom {:?} of type {}", atom_id, type_name::()); api::Atom { drop: Some(atom_id), data, owner: ctx.sys_id() } }) } @@ -52,7 +50,7 @@ impl> AtomicFeaturesImpl { id: api::AtomId, - guard: RwLockReadGuard<'a, MemoMap>>, + guard: RwLockReadGuard<'a, MemoMap>>, } impl<'a> AtomReadGuard<'a> { async fn new(id: api::AtomId, ctx: &'a SysCtx) -> Self { diff --git a/orchid-extension/src/coroutine_exec.rs b/orchid-extension/src/coroutine_exec.rs index bf56ca6..f528e92 100644 --- a/orchid-extension/src/coroutine_exec.rs +++ b/orchid-extension/src/coroutine_exec.rs @@ -1,14 +1,14 @@ use std::borrow::Cow; use std::marker::PhantomData; -use std::pin::Pin; use std::rc::Rc; -use futures::channel::mpsc::{Receiver, Sender, channel}; -use futures::future::Fuse; +use futures::channel::mpsc::{Sender, channel}; use futures::lock::Mutex; -use futures::{FutureExt, SinkExt, StreamExt, select}; +use futures::stream::{self, LocalBoxStream}; +use futures::{FutureExt, SinkExt, StreamExt}; use never::Never; use orchid_base::error::OrcRes; +use orchid_base::format::{FmtCtx, FmtUnit}; use crate::atom::Atomic; use crate::atom_owned::{OwnedAtom, OwnedVariant}; @@ -19,35 +19,30 @@ use crate::gen_expr::{GExpr, arg, call, lambda, seq}; enum Command { Execute(GExpr, Sender), Register(GExpr, Sender), + Halt(GExpr), } struct BuilderCoroutineData { - work: Fuse>>>, - cmd_recv: Receiver, + name: Option, + receiver: Mutex>, } + #[derive(Clone)] -struct BuilderCoroutine(Rc>); +struct BuilderCoroutine(Rc); impl BuilderCoroutine { pub async fn run(self) -> GExpr { - let cmd = { - let this = &mut *self.0.lock().await; - select! { - ret_val = &mut this.work => { return ret_val }, - cmd_res = this.cmd_recv.next().fuse() => match cmd_res { - Some(cmd) => cmd, - None => return (&mut this.work).await - }, - } - }; + let cmd = self.0.receiver.lock().await.next().await; match cmd { - Command::Execute(expr, reply) => call([ + None => panic!("Before the stream ends, we should have gotten a Halt"), + Some(Command::Halt(expr)) => expr, + Some(Command::Execute(expr, reply)) => call([ lambda(0, [seq([ arg(0), call([Replier { reply, builder: self }.to_expr().await, arg(0)]), ])]), expr, ]), - Command::Register(expr, reply) => + Some(Command::Register(expr, reply)) => call([Replier { reply, builder: self }.to_expr().await, expr]), } } @@ -66,16 +61,29 @@ impl OwnedAtom for Replier { type Refs = Never; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn call(mut self, arg: Expr) -> GExpr { - let _ = self.reply.send(arg).await; + self.reply.send(arg).await.expect("What the heck"); + std::mem::drop(self.reply); self.builder.run().await } + async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { + match &self.builder.0.name { + None => "BuilderCoroutine".into(), + Some(name) => format!("BuilderCoroutine({name})").into(), + } + } } -pub async fn exec(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr { +pub async fn exec( + debug: impl AsRef, + f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static, +) -> GExpr { let (cmd_snd, cmd_recv) = channel(0); - let work = - async { f(ExecHandle(cmd_snd, PhantomData)).await.to_expr().await }.boxed_local().fuse(); - let coro = BuilderCoroutine(Rc::new(Mutex::new(BuilderCoroutineData { cmd_recv, work }))); + let halt = async { Command::Halt(f(ExecHandle(cmd_snd, PhantomData)).await.to_expr().await) } + .into_stream(); + let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData { + name: Some(debug.as_ref().to_string()), + receiver: Mutex::new(stream::select(halt, cmd_recv).boxed_local()), + })); coro.run().await } @@ -84,12 +92,12 @@ static WEIRD_DROP_ERR: &str = "Coroutine dropped while we are being polled someh pub struct ExecHandle<'a>(Sender, PhantomData<&'a ()>); impl ExecHandle<'_> { pub async fn exec(&mut self, val: impl ToExpr) -> OrcRes { - let (reply_snd, mut reply_recv) = channel(0); + let (reply_snd, mut reply_recv) = channel(1); self.0.send(Command::Execute(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR); T::try_from_expr(reply_recv.next().await.expect(WEIRD_DROP_ERR)).await } pub async fn register(&mut self, val: impl ToExpr) -> Expr { - let (reply_snd, mut reply_recv) = channel(0); + let (reply_snd, mut reply_recv) = channel(1); self.0.send(Command::Register(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR); reply_recv.next().await.expect(WEIRD_DROP_ERR) } diff --git a/orchid-extension/src/entrypoint.rs b/orchid-extension/src/entrypoint.rs index 2986490..5d5699d 100644 --- a/orchid-extension/src/entrypoint.rs +++ b/orchid-extension/src/entrypoint.rs @@ -11,7 +11,6 @@ use futures::lock::Mutex; use futures::{FutureExt, SinkExt, StreamExt, stream, stream_select}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_api::{ExtMsgSet, IntReq}; use orchid_api_traits::{Decode, UnderRoot, enc_vec}; use orchid_base::builtin::{ExtInit, ExtPort, Spawner}; use orchid_base::char_filter::{char_filter_match, char_filter_union, mk_char_filter}; @@ -29,9 +28,9 @@ use trait_set::trait_set; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId}; use crate::atom_owned::take_atom; -use crate::expr::{Expr, ExprHandle}; +use crate::expr::{BorrowedExprStore, Expr, ExprHandle}; use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable}; -use crate::parser::{PTok, PTokTree, ParsCtx, get_const, linev_into_api}; +use crate::parser::{PTokTree, ParsCtx, get_const, linev_into_api}; use crate::system::{SysCtx, atom_by_idx}; use crate::system_ctor::{CtedObj, DynSystemCtor}; use crate::tree::{LazyMemberFactory, TreeIntoApiCtxImpl}; @@ -124,7 +123,7 @@ pub fn extension_init( })); let init_ctx = { clone!(interner_weak, spawner, logger); - move |id: api::SysId, cted: CtedObj, reqnot: ReqNot| { + move |id: api::SysId, cted: CtedObj, reqnot: ReqNot| { clone!(interner_weak, spawner, logger; async move { let interner_rc = interner_weak.upgrade().expect("System construction order while shutting down"); @@ -242,49 +241,62 @@ pub fn extension_init( let text = Tok::from_api(text, &i).await; let src = Sym::from_api(src, sys_ctx.i()).await; let rep = Reporter::new(); - let ctx = LexContext { id, pos, text: &text, src, ctx: sys_ctx.clone(), rep: &rep }; + let expr_store = BorrowedExprStore::new(); let trigger_char = text.chars().nth(pos as usize).unwrap(); let ekey_na = ekey_not_applicable(&i).await; let ekey_cascade = ekey_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)) { + let ctx = LexContext { + id, + pos, + text: &text, + src: src.clone(), + ctx: sys_ctx.clone(), + rep: &rep, + exprs: &expr_store, + }; match lx.lex(&text[pos as usize..], &ctx).await { Err(e) if e.any(|e| *e == ekey_na) => continue, Err(e) => { let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api())); + expr_store.dispose().await; return hand.handle(&lex, &eopt).await; }, Ok((s, expr)) => { let expr = expr.into_api(&mut (), &mut (sys_ctx, &hand)).await; let pos = (text.len() - s.len()) as u32; + expr_store.dispose().await; return hand.handle(&lex, &Some(Ok(api::LexedExpr { pos, expr }))).await; }, } } writeln!(logger, "Got notified about n/a character '{trigger_char}'"); + expr_store.dispose().await; hand.handle(&lex, &None).await }, api::HostExtReq::ParseLine(pline) => { - let api::ParseLine { module, src, exported, comments, sys, line } = &pline; - let mut ctx = get_ctx(*sys).await; + let api::ParseLine { module, src, exported, comments, sys, line, idx } = &pline; + let ctx = get_ctx(*sys).await; let parsers = ctx.cted().inst().dyn_parsers(); let src = Sym::from_api(*src, ctx.i()).await; let comments = join_all(comments.iter().map(|c| Comment::from_api(c, src.clone(), &i))).await; - let line: Vec = ttv_from_api(line, &mut ctx, &mut (), &src, &i).await; + let expr_store = BorrowedExprStore::new(); + let mut from_api_ctx = (ctx.clone(), &expr_store); + let line: Vec = + ttv_from_api(line, &mut from_api_ctx, &mut (), &src, &i).await; let snip = Snippet::new(line.first().expect("Empty line"), &line); - let (head, tail) = snip.pop_front().unwrap(); - let name = if let PTok::Name(n) = &head.tok { n } else { panic!("No line head") }; - let parser = - parsers.iter().find(|p| p.line_head() == **name).expect("No parser candidate"); + let parser = parsers[*idx as usize]; let module = Sym::from_api(*module, ctx.i()).await; let reporter = Reporter::new(); let pctx = ParsCtx::new(ctx.clone(), module, &reporter); - let parse_res = parser.parse(pctx, *exported, comments, tail).await; + let parse_res = parser.parse(pctx, *exported, comments, snip).await; let o_line = match reporter.merge(parse_res) { Err(e) => Err(e.to_api()), Ok(t) => Ok(linev_into_api(t, ctx.clone(), &hand).await), }; + expr_store.dispose().await; hand.handle(&pline, &o_line).await }, api::HostExtReq::FetchParsedConst(ref fpc @ api::FetchParsedConst { id, sys }) => { @@ -297,6 +309,7 @@ pub fn extension_init( let atom_req = atom_req.clone(); with_atom_record(&get_ctx, atom, async move |nfo, ctx, id, buf| { let actx = AtomCtx(buf, atom.drop, ctx.clone()); + match &atom_req { api::AtomReq::SerializeAtom(ser) => { let mut buf = enc_vec(&id).await; @@ -327,16 +340,23 @@ pub fn extension_init( hand.handle(fwded, &some.then_some(reply)).await }, api::AtomReq::CallRef(call @ api::CallRef(_, arg)) => { - // SAFETY: function calls own their argument implicitly - let expr_handle = unsafe { ExprHandle::from_args(ctx.clone(), *arg) }; - let ret = nfo.call_ref(actx, Expr::from_handle(Rc::new(expr_handle))).await; - hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await + // SAFETY: function calls borrow their argument implicitly + let expr_store = BorrowedExprStore::new(); + let expr_handle = ExprHandle::borrowed(ctx.clone(), *arg, &expr_store); + let ret = nfo.call_ref(actx, Expr::from_handle(expr_handle.clone())).await; + expr_handle.drop_one().await; + let api_expr = ret.api_return(ctx.clone(), &hand).await; + expr_store.dispose().await; + hand.handle(call, &api_expr).await }, api::AtomReq::FinalCall(call @ api::FinalCall(_, arg)) => { - // SAFETY: function calls own their argument implicitly - let expr_handle = unsafe { ExprHandle::from_args(ctx.clone(), *arg) }; - let ret = nfo.call(actx, Expr::from_handle(Rc::new(expr_handle))).await; - hand.handle(call, &ret.api_return(ctx.clone(), &hand).await).await + // SAFETY: function calls borrow their argument implicitly + let expr_store = BorrowedExprStore::new(); + let expr_handle = ExprHandle::borrowed(ctx.clone(), *arg, &expr_store); + let ret = nfo.call(actx, Expr::from_handle(expr_handle.clone())).await; + let api_expr = ret.api_return(ctx.clone(), &hand).await; + expr_store.dispose().await; + hand.handle(call, &api_expr).await }, api::AtomReq::Command(cmd @ api::Command(_)) => match nfo.command(actx).await { Err(e) => hand.handle(cmd, &Err(e.to_api())).await, @@ -358,8 +378,7 @@ pub fn extension_init( let ctx = get_ctx(*sys).await; // SAFETY: deserialization implicitly grants ownership to previously owned exprs let refs = (refs.iter()) - .map(|tk| unsafe { ExprHandle::from_args(ctx.clone(), *tk) }) - .map(|handle| Expr::from_handle(Rc::new(handle))) + .map(|tk| Expr::from_handle(ExprHandle::deserialize(ctx.clone(), *tk))) .collect_vec(); let id = AtomTypeId::decode(Pin::new(&mut read)).await; let inst = ctx.cted().inst(); @@ -373,7 +392,7 @@ pub fn extension_init( }, ); *interner_cell.borrow_mut() = - Some(Interner::new_replica(rn.clone().map(|ir: IntReq| ir.into_root()))); + Some(Interner::new_replica(rn.clone().map(|ir: api::IntReq| ir.into_root()))); spawner(Box::pin(clone!(spawner; async move { let mut streams = stream_select! { in_recv.map(Some), exit_recv.map(|_| None) }; while let Some(item) = streams.next().await { diff --git a/orchid-extension/src/expr.rs b/orchid-extension/src/expr.rs index b9dd56b..bb30104 100644 --- a/orchid-extension/src/expr.rs +++ b/orchid-extension/src/expr.rs @@ -1,9 +1,11 @@ +use std::cell::RefCell; use std::fmt; +use std::hash::Hash; use std::rc::Rc; use async_once_cell::OnceCell; use derive_destructure::destructure; -use orchid_api::ExtAtomPrint; +use hashbrown::HashSet; use orchid_base::error::OrcErrv; use orchid_base::format::{FmtCtx, FmtUnit, Format}; use orchid_base::location::Pos; @@ -14,26 +16,61 @@ use crate::atom::ForeignAtom; use crate::gen_expr::{GExpr, GExprKind}; use crate::system::SysCtx; +pub struct BorrowedExprStore(RefCell>>>); +impl BorrowedExprStore { + pub(crate) fn new() -> Self { Self(RefCell::new(Some(HashSet::new()))) } + pub async fn dispose(self) { + let elements = self.0.borrow_mut().take().unwrap(); + for handle in elements { + handle.drop_one().await + } + } +} +impl Drop for BorrowedExprStore { + fn drop(&mut self) { + if self.0.borrow().is_some() { + panic!("This should always be explicitly disposed") + } + } +} + #[derive(destructure)] pub struct ExprHandle { pub tk: api::ExprTicket, pub ctx: SysCtx, } impl ExprHandle { - /// # Safety - /// - /// This function does not signal to take ownership of the expr. It must only - /// be called on tickets that are already implicitly owned. - pub unsafe fn from_args(ctx: SysCtx, tk: api::ExprTicket) -> Self { Self { ctx, tk } } + /// This function does not signal to take ownership of the expr. + pub fn borrowed(ctx: SysCtx, tk: api::ExprTicket, store: &BorrowedExprStore) -> Rc { + let this = Rc::new(Self { ctx, tk }); + store.0.borrow_mut().as_mut().unwrap().insert(this.clone()); + this + } + pub fn deserialize(ctx: SysCtx, tk: api::ExprTicket) -> Rc { Rc::new(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.sys_id(), self.tk)).await; - Self { ctx: self.ctx.clone(), tk: self.tk } + /// Drop one instance of the handle silently; if it's the last one, do + /// nothing, otherwise send an Acquire + pub async fn drop_one(self: Rc) { + if let Err(rc) = Rc::try_unwrap(self) { + rc.ctx.reqnot().notify(api::Acquire(rc.ctx.sys_id(), rc.tk)).await + } } /// Drop the handle and get the ticket without a release notification. /// Use this with messages that imply ownership transfer. This function is /// safe because abusing it is a memory leak. - pub fn into_tk(self) -> api::ExprTicket { self.destructure().0 } + pub fn serialize(self) -> api::ExprTicket { self.destructure().0 } +} +impl Eq for ExprHandle {} +impl PartialEq for ExprHandle { + fn eq(&self, other: &Self) -> bool { + self.ctx.sys_id() == other.ctx.sys_id() && self.tk == other.tk + } +} +impl Hash for ExprHandle { + fn hash(&self, state: &mut H) { + self.ctx.sys_id().hash(state); + self.tk.hash(state); + } } impl fmt::Debug for ExprHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -92,8 +129,9 @@ impl Format for Expr { match &self.data().await.kind { 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), + ExprKind::Atom(a) => FmtUnit::from_api( + &self.handle.ctx.reqnot().request(api::ExtAtomPrint(a.atom.clone())).await, + ), } } } diff --git a/orchid-extension/src/func_atom.rs b/orchid-extension/src/func_atom.rs index b62bc1c..cddc8b4 100644 --- a/orchid-extension/src/func_atom.rs +++ b/orchid-extension/src/func_atom.rs @@ -1,8 +1,3 @@ -//! #TODO: redefine this in terms of [crate::coroutine_exec::exec]. Try -//! differentiating between eager and lazy arguments. [ExprFunc] can remain with -//! tweaks but other techniques probably must go. -//! -//! Notice also that Func must still be resumable use std::any::TypeId; use std::borrow::Cow; use std::collections::HashMap; @@ -48,11 +43,14 @@ struct FunRecord { fun: Rc, } -async fn process_args>(f: F) -> FunRecord { +async fn process_args>( + debug: impl AsRef + Clone + 'static, + f: F, +) -> FunRecord { let argtyps = F::argtyps(); let fun = Rc::new(move |v: Vec| { clone!(f, v mut); - exec(async move |mut hand| { + exec(debug.clone(), async move |mut hand| { let mut norm_args = Vec::with_capacity(v.len()); for (expr, typ) in v.into_iter().zip(argtyps) { if *typ != TypeId::of::() { @@ -85,7 +83,7 @@ impl Fun { let record = if let Some(record) = fung.get(&path) { record.clone() } else { - let record = process_args(f).await; + let record = process_args(path.to_string(), f).await; fung.insert(path.clone(), record.clone()); record }; @@ -101,7 +99,6 @@ impl OwnedAtom for Fun { type Refs = Vec; async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) } async fn call_ref(&self, arg: Expr) -> GExpr { - std::io::Write::flush(&mut std::io::stderr()).unwrap(); let new_args = self.args.iter().cloned().chain([arg]).collect_vec(); if new_args.len() == self.record.argtyps.len() { (self.record.fun)(new_args).await.to_expr().await @@ -137,8 +134,11 @@ pub struct Lambda { record: FunRecord, } impl Lambda { - pub async fn new>(f: F) -> Self { - Self { args: vec![], record: process_args(f).await } + pub async fn new>( + debug: impl AsRef + Clone + 'static, + f: F, + ) -> Self { + Self { args: vec![], record: process_args(debug, f).await } } } impl Atomic for Lambda { diff --git a/orchid-extension/src/gen_expr.rs b/orchid-extension/src/gen_expr.rs index e7efa29..767cf51 100644 --- a/orchid-extension/src/gen_expr.rs +++ b/orchid-extension/src/gen_expr.rs @@ -66,7 +66,6 @@ impl GExprKind { Lambda(arg, body => Box::new(body.api_return(ctx, hand).await)), Arg(arg), Const(name.to_api()), - Const(name.to_api()), Bottom(err.to_api()), NewAtom(fac.clone().build(ctx).await), } { diff --git a/orchid-extension/src/lexer.rs b/orchid-extension/src/lexer.rs index 0d2442f..b41c1b2 100644 --- a/orchid-extension/src/lexer.rs +++ b/orchid-extension/src/lexer.rs @@ -12,6 +12,7 @@ use orchid_base::parse::ParseCtx; use orchid_base::reqnot::Requester; use crate::api; +use crate::expr::BorrowedExprStore; use crate::parser::PTokTree; use crate::system::SysCtx; use crate::tree::GenTokTree; @@ -34,6 +35,7 @@ pub async fn err_not_applicable(i: &Interner) -> OrcErrv { } pub struct LexContext<'a> { + pub(crate) exprs: &'a BorrowedExprStore, pub ctx: SysCtx, pub text: &'a Tok, pub id: api::ParsId, @@ -52,8 +54,14 @@ impl<'a> LexContext<'a> { let Some(lx) = self.ctx.reqnot().request(api::SubLex { pos: start, id: self.id }).await else { return Err(err_cascade(self.ctx.i()).await); }; - let tree = - PTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), &self.src, self.ctx.i()).await; + let tree = PTokTree::from_api( + &lx.tree, + &mut (self.ctx.clone(), self.exprs), + &mut (), + &self.src, + self.ctx.i(), + ) + .await; Ok((&self.text[lx.pos as usize..], tree)) } diff --git a/orchid-extension/src/parser.rs b/orchid-extension/src/parser.rs index fa48e7f..8494123 100644 --- a/orchid-extension/src/parser.rs +++ b/orchid-extension/src/parser.rs @@ -5,7 +5,6 @@ use futures::future::{LocalBoxFuture, join_all}; use futures::{FutureExt, Stream, StreamExt}; use itertools::Itertools; use never::Never; -use orchid_api::ResolveNames; use orchid_base::error::{OrcErrv, OrcRes, Reporter}; use orchid_base::id_store::IdStore; use orchid_base::interner::{Interner, Tok}; @@ -186,21 +185,6 @@ pub enum ParsedMemKind { Mod { lines: Vec, use_prelude: bool }, } -/* TODO: how the macro runner uses the multi-stage loader - -Since the macro runner actually has to invoke the interpreter, -it'll run at const-time and not at postparse-time anyway. - -pasing stage establishes the role of every constant as a macro keyword -postparse / const load links up constants with every macro they can directly invoke - the constants representing the keywords might not actually be postparsed, - \ the connection is instead made by detecting in the macro system that the - \ resolved name is owned by a macro - the returned constant from this call is always an entrypoint call to - \ the macro system - the constants representing the keywords resolve to panic -execute relies on these links detected in the extension to dispatch relevant macros -*/ #[derive(Clone)] pub struct ConstCtx { ctx: SysCtx, @@ -213,7 +197,7 @@ impl ConstCtx { &'b self, names: impl IntoIterator + 'b, ) -> impl Stream> + 'b { - let resolve_names = ResolveNames { + let resolve_names = api::ResolveNames { constid: self.constid, sys: self.ctx.sys_id(), names: names.into_iter().map(|n| n.to_api()).collect_vec(), diff --git a/orchid-extension/src/system.rs b/orchid-extension/src/system.rs index 40b9f6e..abf4972 100644 --- a/orchid-extension/src/system.rs +++ b/orchid-extension/src/system.rs @@ -7,7 +7,6 @@ use std::rc::{Rc, Weak}; use futures::future::LocalBoxFuture; 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; @@ -18,8 +17,9 @@ use orchid_base::reqnot::{Receipt, ReqNot}; use crate::api; use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId, AtomicFeatures, ForeignAtom, TypAtom, get_info}; +use crate::coroutine_exec::Replier; use crate::entrypoint::ExtReq; -use crate::func_atom::Fun; +use crate::func_atom::{Fun, Lambda}; use crate::lexer::LexerObj; use crate::parser::ParserObj; use crate::system_ctor::{CtedObj, SystemCtor}; @@ -43,7 +43,7 @@ pub trait DynSystemCard: Send + Sync + 'static { /// The indices of these are bitwise negated, such that the MSB of an atom index /// marks whether it belongs to this package (0) or the importer (1) fn general_atoms() -> impl Iterator>> { - [Some(Fun::dynfo())].into_iter() + [Some(Fun::dynfo()), Some(Lambda::dynfo()), Some(Replier::dynfo())].into_iter() } pub fn atom_info_for( @@ -149,7 +149,7 @@ impl SysCtx { pub fn new( id: api::SysId, i: Interner, - reqnot: ReqNot, + reqnot: ReqNot, spawner: Spawner, logger: Logger, cted: CtedObj, @@ -167,13 +167,7 @@ impl SysCtx { (self.0.get_or_insert_owned(TypeId::of::(), || Box::new(f())).downcast_ref()) .expect("Keyed by TypeId") } - pub fn get_or_default(&self) -> &T { - self.get_or_insert(|| { - let rc_id = self.0.as_ref() as *const _ as *const () as usize; - eprintln!("Default-initializing {} in {}", type_name::(), rc_id); - T::default() - }) - } + pub fn get_or_default(&self) -> &T { self.get_or_insert(T::default) } pub fn try_get(&self) -> Option<&T> { Some(self.0.get(&TypeId::of::())?.downcast_ref().expect("Keyed by TypeId")) } @@ -183,7 +177,7 @@ impl SysCtx { /// Shorthand to get the [Interner] instance pub fn i(&self) -> &Interner { self.get::() } /// Shorthand to get the messaging link - pub fn reqnot(&self) -> &ReqNot { self.get::>() } + pub fn reqnot(&self) -> &ReqNot { self.get::>() } /// Shorthand to get the system ID pub fn sys_id(&self) -> api::SysId { *self.get::() } /// Shorthand to get the task spawner callback diff --git a/orchid-extension/src/tree.rs b/orchid-extension/src/tree.rs index 73fafaa..89b4dcb 100644 --- a/orchid-extension/src/tree.rs +++ b/orchid-extension/src/tree.rs @@ -1,5 +1,4 @@ use std::num::NonZero; -use std::rc::Rc; use async_fn_stream::stream; use dyn_clone::{DynClone, clone_box}; @@ -18,9 +17,9 @@ use trait_set::trait_set; use crate::api; use crate::conv::ToExpr; use crate::entrypoint::MemberRecord; -use crate::expr::{Expr, ExprHandle}; +use crate::expr::{BorrowedExprStore, Expr, ExprHandle}; use crate::func_atom::{ExprFunc, Fun}; -use crate::gen_expr::{GExpr, arg, call, lambda, seq, sym_ref}; +use crate::gen_expr::{GExpr, sym_ref}; use crate::system::SysCtx; pub type GenTokTree = TokTree; @@ -43,26 +42,18 @@ impl TokenVariant for GExpr { } impl TokenVariant for Expr { - type FromApiCtx<'a> = SysCtx; + type FromApiCtx<'a> = (SysCtx, &'a BorrowedExprStore); async fn from_api( api: &api::ExprTicket, - ctx: &mut Self::FromApiCtx<'_>, + (ctx, exprs): &mut Self::FromApiCtx<'_>, _: SrcRange, _: &Interner, ) -> Self { - // SAFETY: receiving trees from sublexers implies ownership transfer - Expr::from_handle(Rc::new(unsafe { ExprHandle::from_args(ctx.clone(), *api) })) + // SAFETY: receiving trees from sublexers implies borrowing + Expr::from_handle(ExprHandle::borrowed(ctx.clone(), *api, exprs)) } type ToApiCtx<'a> = (); - async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::ExprTicket { - let hand = self.handle(); - std::mem::drop(self); - let h = match Rc::try_unwrap(hand) { - Ok(h) => h, - Err(h) => h.as_ref().clone().await, - }; - h.into_tk() - } + async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::ExprTicket { self.handle().tk } } pub async fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_expr().await) } @@ -96,19 +87,9 @@ pub fn root_mod(name: &str, mems: impl IntoIterator>) -> ( (name.to_string(), kind) } pub fn fun(public: bool, name: &str, xf: impl ExprFunc) -> Vec { - let fac = - LazyMemberFactory::new(move |sym, ctx| async { - return MemKind::Const(build_lambdas(Fun::new(sym, ctx, xf).await, 0).await); - async fn build_lambdas(fun: Fun, i: u64) -> GExpr { - if i < fun.arity().into() { - return lambda(i, [build_lambdas(fun, i + 1).boxed_local().await]); - } - let arity = fun.arity(); - seq((0..arity).map(|i| arg(i as u64)).chain([call( - [fun.to_expr().await].into_iter().chain((0..arity).map(|i| arg(i as u64))), - )])) - } - }); + let fac = LazyMemberFactory::new(async move |sym, ctx| { + MemKind::Const(Fun::new(sym, ctx, xf).await.to_expr().await) + }); vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }] } pub fn prefix(path: &str, items: impl IntoIterator>) -> Vec { diff --git a/orchid-host/src/atom.rs b/orchid-host/src/atom.rs index 2418f72..5487008 100644 --- a/orchid-host/src/atom.rs +++ b/orchid-host/src/atom.rs @@ -1,6 +1,7 @@ use std::fmt; use std::rc::{Rc, Weak}; +use async_lock::OnceCell; use derive_destructure::destructure; use orchid_base::format::{FmtCtx, FmtUnit, Format, take_first_fmt}; use orchid_base::location::Pos; @@ -9,7 +10,7 @@ use orchid_base::tree::AtomRepr; use crate::api; use crate::ctx::Ctx; -use crate::expr::Expr; +use crate::expr::{Expr, ExprParseCtx, PathSetBuilder}; use crate::extension::Extension; use crate::system::System; @@ -18,11 +19,12 @@ pub struct AtomData { owner: System, drop: Option, data: Vec, + pub(crate) display: OnceCell, } impl AtomData { #[must_use] fn api(self) -> api::Atom { - let (owner, drop, data) = self.destructure(); + let (owner, drop, data, _display) = self.destructure(); api::Atom { data, drop, owner: owner.id() } } #[must_use] @@ -50,35 +52,22 @@ impl fmt::Debug for AtomData { #[derive(Clone, Debug)] pub struct AtomHand(Rc); impl AtomHand { - #[must_use] - pub(crate) async fn new(api::Atom { data, drop, owner }: api::Atom, ctx: &Ctx) -> Self { - let create = || async { - let owner = ctx.system_inst(owner).await.expect("Dropped system created atom"); - AtomHand(Rc::new(AtomData { data, owner, drop })) - }; - if let Some(id) = drop { - let mut owned_g = ctx.owned_atoms.write().await; - if let Some(data) = owned_g.get(&id) - && let Some(atom) = data.upgrade() - { - return atom; - } - let new = create().await; - owned_g.insert(id, new.downgrade()); - new - } else { - create().await - } + pub(crate) fn new(data: Vec, owner: System, drop: Option) -> Self { + Self(Rc::new(AtomData { owner, drop, data, display: OnceCell::new() })) } #[must_use] - pub async fn call(self, arg: Expr) -> api::Expression { + pub async fn call(self, arg: Expr) -> Expr { let owner_sys = self.0.owner.clone(); let reqnot = owner_sys.reqnot(); owner_sys.ext().exprs().give_expr(arg.clone()); - match Rc::try_unwrap(self.0) { + 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, - } + }; + let mut parse_ctx = ExprParseCtx { ctx: owner_sys.ctx(), exprs: owner_sys.ext().exprs() }; + let val = Expr::from_api(&ret, PathSetBuilder::new(), &mut parse_ctx).await; + owner_sys.ext().exprs().take_expr(arg.id()); + val } #[must_use] pub fn sys(&self) -> &System { &self.0.owner } @@ -96,13 +85,19 @@ impl AtomHand { } impl Format for AtomHand { async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { - FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await) + (self.0.display.get_or_init(|| async { + FmtUnit::from_api(&self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())).await) + })) + .await + .clone() } } impl AtomRepr for AtomHand { type Ctx = Ctx; - async fn from_api(atom: &orchid_api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self { - Self::new(atom.clone(), ctx).await + async fn from_api(atom: &api::Atom, _: Pos, ctx: &mut Self::Ctx) -> Self { + let api::Atom { data, drop, owner } = atom.clone(); + let sys = ctx.system_inst(owner).await.expect("Dropped system created atom"); + if let Some(id) = drop { sys.new_atom(data, id).await } else { AtomHand::new(data, sys, drop) } } async fn to_api(&self) -> orchid_api::Atom { self.api_ref() } } diff --git a/orchid-host/src/ctx.rs b/orchid-host/src/ctx.rs index bb517bd..f653409 100644 --- a/orchid-host/src/ctx.rs +++ b/orchid-host/src/ctx.rs @@ -5,12 +5,10 @@ use std::{fmt, ops}; use async_lock::RwLock; use hashbrown::HashMap; -use orchid_api::SysId; use orchid_base::builtin::Spawner; use orchid_base::interner::Interner; use crate::api; -use crate::atom::WeakAtomHand; use crate::expr_store::ExprStore; use crate::system::{System, WeakSystem}; use crate::tree::WeakRoot; @@ -20,7 +18,6 @@ pub struct CtxData { pub spawn: Spawner, pub systems: RwLock>, pub system_id: RefCell, - pub owned_atoms: RwLock>, pub common_exprs: ExprStore, pub root: RwLock, } @@ -46,7 +43,6 @@ impl Ctx { i: Interner::default(), systems: RwLock::default(), system_id: RefCell::new(NonZero::new(1).unwrap()), - owned_atoms: RwLock::default(), common_exprs: ExprStore::default(), root: RwLock::default(), })) @@ -59,7 +55,7 @@ impl Ctx { pub(crate) fn next_sys_id(&self) -> api::SysId { let mut g = self.system_id.borrow_mut(); *g = g.checked_add(1).unwrap_or(NonZeroU16::new(1).unwrap()); - SysId(*g) + api::SysId(*g) } #[must_use] pub fn downgrade(&self) -> WeakCtx { WeakCtx(Rc::downgrade(&self.0)) } diff --git a/orchid-host/src/execute.rs b/orchid-host/src/execute.rs index 80b67a1..4744793 100644 --- a/orchid-host/src/execute.rs +++ b/orchid-host/src/execute.rs @@ -9,7 +9,7 @@ use orchid_base::location::Pos; use orchid_base::logging::Logger; use crate::ctx::Ctx; -use crate::expr::{Expr, ExprKind, ExprParseCtx, PathSet, PathSetBuilder, Step}; +use crate::expr::{Expr, ExprKind, PathSet, Step}; use crate::tree::Root; type ExprGuard = Bound, Expr>; @@ -109,11 +109,8 @@ impl ExecCtx { ExprKind::Call(f, x) if !self.did_pop => (ExprKind::Call(f.clone(), x), StackOp::Push(f)), ExprKind::Call(f, x) => match f.try_into_owned_atom().await { Ok(atom) => { - let ext = atom.sys().ext().clone(); let x_norm = self.unpack_ident(&x).await; - let mut parse_ctx = ExprParseCtx { ctx: &self.ctx, exprs: ext.exprs() }; - let val = - Expr::from_api(&atom.call(x_norm).await, PathSetBuilder::new(), &mut parse_ctx).await; + let val = atom.call(x_norm).await; (ExprKind::Identity(val.clone()), StackOp::Swap(val)) }, Err(f) => match &*f.kind().read().await { @@ -121,15 +118,9 @@ impl ExecCtx { panic!("This should not appear outside function bodies"), ExprKind::Missing => panic!("Should have been replaced"), ExprKind::Atom(a) => { - let ext = a.sys().ext().clone(); let x_norm = self.unpack_ident(&x).await; - let val = Expr::from_api( - &a.clone().call(x_norm).await, - PathSetBuilder::new(), - &mut ExprParseCtx { ctx: ext.ctx(), exprs: ext.exprs() }, - ) - .await; - (ExprKind::Identity(val.clone()), StackOp::Swap(val)) + let ret = a.clone().call(x_norm).await; + (ExprKind::Identity(ret.clone()), StackOp::Swap(ret)) }, ExprKind::Bottom(exprv) => (ExprKind::Bottom(exprv.clone()), StackOp::Pop), ExprKind::Lambda(None, body) => diff --git a/orchid-host/src/expr.rs b/orchid-host/src/expr.rs index 5d76c93..36e8e17 100644 --- a/orchid-host/src/expr.rs +++ b/orchid-host/src/expr.rs @@ -6,7 +6,6 @@ use std::{fmt, mem}; use async_lock::RwLock; use futures::FutureExt; -use hashbrown::HashSet; use itertools::Itertools; use orchid_base::error::OrcErrv; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants}; @@ -116,19 +115,18 @@ impl Expr { } impl Format for Expr { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { - return print_expr(self, c, &mut HashSet::new()).await; + return print_expr(self, c, Substack::Bottom).await; } } async fn print_expr<'a>( expr: &'a Expr, c: &'a (impl FmtCtx + ?Sized + 'a), - visited: &mut HashSet, + visited: Substack<'_, api::ExprTicket>, ) -> FmtUnit { - if visited.contains(&expr.id()) { + if visited.iter().any(|id| id == &expr.id()) { return "CYCLIC_EXPR".to_string().into(); } - visited.insert(expr.id()); - print_exprkind(&*expr.kind().read().await, c, visited).boxed_local().await + print_exprkind(&*expr.kind().read().await, c, visited.push(expr.id())).boxed_local().await } #[derive(Clone, Debug)] @@ -152,13 +150,13 @@ impl ExprKind { } impl Format for ExprKind { async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit { - print_exprkind(self, c, &mut HashSet::new()).await + print_exprkind(self, c, Substack::Bottom).await } } async fn print_exprkind<'a>( ek: &ExprKind, c: &'a (impl FmtCtx + ?Sized + 'a), - visited: &mut HashSet, + visited: Substack<'_, api::ExprTicket>, ) -> FmtUnit { match &ek { ExprKind::Arg => "Arg".to_string().into(), diff --git a/orchid-host/src/expr_store.rs b/orchid-host/src/expr_store.rs index f706d13..f786771 100644 --- a/orchid-host/src/expr_store.rs +++ b/orchid-host/src/expr_store.rs @@ -28,9 +28,15 @@ impl ExprStore { }, } } - pub fn take_expr(&self, ticket: api::ExprTicket) { - (self.0.exprs.borrow_mut().entry(ticket)) - .and_replace_entry_with(|_, (rc, rt)| (1 < rc).then_some((rc - 1, rt))); + pub fn take_expr(&self, ticket: api::ExprTicket) -> Option { + match self.0.exprs.borrow_mut().entry(ticket) { + Entry::Vacant(_) => None, + Entry::Occupied(oe) if oe.get().0 == 1 => Some(oe.remove().1), + Entry::Occupied(mut oe) => { + oe.get_mut().0 -= 1; + Some(oe.get().1.clone()) + }, + } } #[must_use] pub fn get_expr(&self, ticket: api::ExprTicket) -> Option { diff --git a/orchid-host/src/extension.rs b/orchid-host/src/extension.rs index 542d09d..2a5d679 100644 --- a/orchid-host/src/extension.rs +++ b/orchid-host/src/extension.rs @@ -18,9 +18,11 @@ use orchid_base::builtin::ExtInit; use orchid_base::clone; use orchid_base::format::{FmtCtxImpl, Format}; use orchid_base::interner::Tok; +use orchid_base::location::Pos; use orchid_base::logging::Logger; use orchid_base::name::Sym; use orchid_base::reqnot::{DynRequester, ReqNot, Requester as _}; +use orchid_base::tree::AtomRepr; use crate::api; use crate::atom::AtomHand; @@ -100,7 +102,7 @@ impl Extension { } api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => { this.assert_own_sys(rel.0).await; - this.0.exprs.take_expr(rel.1) + this.0.exprs.take_expr(rel.1); } api::ExtHostNotif::ExprNotif(api::ExprNotif::Move(mov)) => { this.assert_own_sys(mov.dec).await; @@ -173,8 +175,8 @@ impl Extension { 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 = &mut *root.0.write().await; - let mut walk_ctx = (ctx.clone(), &mut root_data.consts); + 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 @@ -189,7 +191,7 @@ impl Extension { }; let mut members = std::collections::HashMap::new(); for (k, v) in &module.members { - let kind = match v.kind(ctx.clone(), &mut root_data.consts).await { + let kind = match v.kind(ctx.clone(), &root_data.consts).await { MemberKind::Const => api::MemberInfoKind::Constant, MemberKind::Module(_) => api::MemberInfoKind::Module, }; @@ -221,7 +223,7 @@ impl Extension { hand.handle(rn, &responses).await }, api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => { - let atom = AtomHand::new(atom.clone(), &ctx).await; + 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 }, diff --git a/orchid-host/src/lex.rs b/orchid-host/src/lex.rs index 423b5bc..5786a14 100644 --- a/orchid-host/src/lex.rs +++ b/orchid-host/src/lex.rs @@ -8,12 +8,11 @@ use orchid_base::location::SrcRange; use orchid_base::name::Sym; use orchid_base::parse::{name_char, name_start, op_char, unrep_space}; use orchid_base::tokens::PARENS; -use orchid_base::tree::recur; use crate::api; use crate::ctx::Ctx; -use crate::expr::{Expr, ExprParseCtx, ExprWillPanic}; -use crate::parsed::{ParsTok, ParsTokTree}; +use crate::expr::{Expr, ExprParseCtx}; +use crate::parsed::{ParsTok, ParsTokTree, tt_to_api}; use crate::system::System; pub struct LexCtx<'a> { @@ -54,14 +53,7 @@ impl<'a> LexCtx<'a> { } #[must_use] pub async fn ser_subtree(&mut self, subtree: ParsTokTree) -> api::TokenTree { - let mut exprs = self.ctx.common_exprs.clone(); - let without_new_expr = recur(subtree, &|tt, r| { - if let ParsTok::NewExpr(expr) = tt.tok { - return ParsTok::Handle(expr).at(tt.sr); - } - r(tt) - }); - without_new_expr.into_api(&mut exprs, &mut ExprWillPanic).await + tt_to_api(&mut self.ctx.common_exprs.clone(), subtree).await } #[must_use] pub async fn des_subtree(&mut self, tree: &api::TokenTree) -> ParsTokTree { diff --git a/orchid-host/src/lib.rs b/orchid-host/src/lib.rs index eb9617a..929a57d 100644 --- a/orchid-host/src/lib.rs +++ b/orchid-host/src/lib.rs @@ -11,5 +11,6 @@ pub mod lex; pub mod parse; pub mod parsed; pub mod subprocess; +mod sys_parser; pub mod system; pub mod tree; diff --git a/orchid-host/src/parse.rs b/orchid-host/src/parse.rs index ac7d9a8..92d6c79 100644 --- a/orchid-host/src/parse.rs +++ b/orchid-host/src/parse.rs @@ -33,6 +33,7 @@ impl ParseCtx for HostParseCtxImpl<'_> { impl HostParseCtx for HostParseCtxImpl<'_> { fn ctx(&self) -> &Ctx { &self.ctx } fn systems(&self) -> impl Iterator { self.systems.iter() } + fn src_path(&self) -> Sym { self.src.clone() } } pub trait HostParseCtx: ParseCtx { @@ -40,6 +41,8 @@ pub trait HostParseCtx: ParseCtx { fn ctx(&self) -> &Ctx; #[must_use] fn systems(&self) -> impl Iterator; + #[must_use] + fn src_path(&self) -> Sym; } pub async fn parse_items( @@ -109,9 +112,9 @@ pub async fn parse_exportable_item<'a>( let kind = if discr == ctx.i().i("mod").await { let (name, body) = parse_module(ctx, path, tail).await?; ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::Mod(body) }) - } else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) { - return sys - .parse(path, tail.to_vec(), exported, comments, &mut async |stack, lines| { + } 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| { let source = Snippet::new(lines.first().unwrap(), &lines); parse_items(ctx, stack, source).await }) diff --git a/orchid-host/src/parsed.rs b/orchid-host/src/parsed.rs index c2bdc7c..bcb76b6 100644 --- a/orchid-host/src/parsed.rs +++ b/orchid-host/src/parsed.rs @@ -10,11 +10,12 @@ use orchid_base::interner::Tok; use orchid_base::location::SrcRange; use orchid_base::parse::{Comment, Import}; use orchid_base::tl_cache; -use orchid_base::tree::{TokTree, Token}; +use orchid_base::tree::{TokTree, Token, recur}; use crate::api; use crate::dealias::{ChildErrorKind, ChildResult, Tree}; -use crate::expr::Expr; +use crate::expr::{Expr, ExprWillPanic}; +use crate::expr_store::ExprStore; use crate::system::System; pub type ParsTokTree = TokTree; @@ -54,7 +55,7 @@ impl Format for Item { let item_text = match &self.kind { ItemKind::Import(i) => format!("import {i}").into(), ItemKind::Member(mem) => match &mem.kind { - ParsedMemberKind::DeferredConst(_, sys) => + ParsedMemberKind::Const(_, sys) => tl_cache!(Rc: Rc::new(Variants::default().bounded("const {0} via {1}"))) .units([mem.name.rc().into(), sys.print(c).await]), ParsedMemberKind::Mod(module) => @@ -106,7 +107,7 @@ impl fmt::Debug for ParsedExpr { #[derive(Debug)] pub enum ParsedMemberKind { - DeferredConst(api::ParsedConstId, System), + Const(api::ParsedConstId, System), Mod(ParsedModule), } impl From for ParsedMemberKind { @@ -162,7 +163,7 @@ impl Tree for ParsedModule { .find(|m| m.name == key) { match &member.kind { - ParsedMemberKind::DeferredConst(..) => return ChildResult::Err(ChildErrorKind::Constant), + ParsedMemberKind::Const(..) => return ChildResult::Err(ChildErrorKind::Constant), ParsedMemberKind::Mod(m) => return ChildResult::Ok(m), } } @@ -191,15 +192,6 @@ impl Format for ParsedModule { } } -/// TODO: -/// -/// idea, does the host need an IR here or can we figure out a way to transcribe -/// these? Should we spin off a new stage for value parsing so that ParsTokTree -/// doesn't appear in the interpreter's ingress? -pub struct Const { - pub source: Option>, -} - /// Selects a code element /// /// Either the steps point to a constant and rule_loc is None, or the steps @@ -212,3 +204,13 @@ impl ConstPath { #[must_use] pub fn to_const(steps: Tok>>) -> Self { Self { steps } } } + +pub async fn tt_to_api(exprs: &mut ExprStore, subtree: ParsTokTree) -> api::TokenTree { + let without_new_expr = recur(subtree, &|tt, r| { + if let ParsTok::NewExpr(expr) = tt.tok { + return ParsTok::Handle(expr).at(tt.sr); + } + r(tt) + }); + without_new_expr.into_api(exprs, &mut ExprWillPanic).await +} diff --git a/orchid-host/src/sys_parser.rs b/orchid-host/src/sys_parser.rs new file mode 100644 index 0000000..9805498 --- /dev/null +++ b/orchid-host/src/sys_parser.rs @@ -0,0 +1,119 @@ +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::location::SrcRange; +use orchid_base::name::Sym; +use orchid_base::parse::Comment; +use orchid_base::reqnot::Requester; +use orchid_base::tree::ttv_from_api; +use substack::Substack; + +use crate::api; +use crate::expr::ExprParseCtx; +use crate::expr_store::ExprStore; +use crate::parse::HostParseCtx; +use crate::parsed::{ + Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule, tt_to_api, +}; +use crate::system::System; + +pub struct Parser { + pub(crate) system: System, + pub(crate) idx: u16, +} +type ModPath<'a> = Substack<'a, Tok>; + +impl Parser { + pub async fn parse( + &self, + ctx: &impl HostParseCtx, + path: ModPath<'_>, + line: Vec, + exported: bool, + comments: Vec, + callback: &mut impl AsyncFnMut(ModPath<'_>, Vec) -> OrcRes>, + ) -> OrcRes> { + let src_path = line.first().expect("cannot be empty").sr.path(); + let line = join_all( + (line.into_iter()) + .map(|t| async { tt_to_api(&mut self.system.ext().exprs().clone(), t).await }), + ) + .await; + let mod_path = ctx.src_path().suffix(path.unreverse(), self.system.i()).await; + let comments = comments.iter().map(Comment::to_api).collect_vec(); + let req = api::ParseLine { + idx: self.idx, + module: mod_path.to_api(), + src: src_path.to_api(), + exported, + sys: self.system.id(), + comments, + line, + }; + match self.system.reqnot().request(req).await { + Ok(parsed_v) => { + let mut ext_exprs = self.system.ext().exprs().clone(); + conv(parsed_v, path, callback, &mut ConvCtx { + i: self.system.i(), + mod_path: &mod_path, + ext_exprs: &mut ext_exprs, + pctx: &mut ExprParseCtx { ctx: self.system.ctx(), exprs: self.system.ext().exprs() }, + src_path: &src_path, + sys: &self.system, + }) + .await + }, + Err(e) => Err(OrcErrv::from_api(&e, &self.system.ctx().i).await), + } + } +} + +struct ConvCtx<'a> { + sys: &'a System, + mod_path: &'a Sym, + src_path: &'a Sym, + i: &'a Interner, + ext_exprs: &'a mut ExprStore, + pctx: &'a mut ExprParseCtx<'a>, +} +async fn conv( + parsed_v: Vec, + module: Substack<'_, Tok>, + callback: &'_ mut impl AsyncFnMut(Substack<'_, Tok>, Vec) -> OrcRes>, + ctx: &mut ConvCtx<'_>, +) -> OrcRes> { + let mut items = Vec::new(); + for parsed in parsed_v { + let (name, exported, kind) = match parsed.kind { + api::ParsedLineKind::Member(api::ParsedMember { name, exported, kind }) => + (name, exported, kind), + api::ParsedLineKind::Recursive(rec) => { + let tokens = ttv_from_api(rec, ctx.ext_exprs, ctx.pctx, ctx.src_path, ctx.i).await; + items.extend(callback(module.clone(), tokens).await?); + continue; + }, + }; + let name = ctx.i.ex(name).await; + let mkind = match kind { + api::ParsedMemberKind::Module { lines, use_prelude } => { + let items = conv(lines, module.push(name.clone()), callback, ctx).boxed_local().await?; + ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items)) + }, + api::ParsedMemberKind::Constant(cid) => { + ctx.sys.0.const_paths.insert(cid, ctx.mod_path.suffix(module.unreverse(), ctx.i).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)), + ) + .await, + sr: SrcRange::from_api(&parsed.source_range, ctx.i).await, + kind: ItemKind::Member(ParsedMember { name, exported, kind: mkind }), + }) + } + Ok(items) +} diff --git a/orchid-host/src/system.rs b/orchid-host/src/system.rs index 00bf53c..a1019df 100644 --- a/orchid-host/src/system.rs +++ b/orchid-host/src/system.rs @@ -3,38 +3,32 @@ use std::fmt; use std::future::Future; use std::rc::{Rc, Weak}; +use async_lock::RwLock; use derive_destructure::destructure; -use futures::FutureExt; use futures::future::join_all; use hashbrown::HashMap; use itertools::Itertools; use memo_map::MemoMap; use orchid_base::char_filter::char_filter_match; -use orchid_base::error::{OrcErrv, OrcRes, mk_errv_floating}; +use orchid_base::error::{OrcRes, mk_errv_floating}; use orchid_base::format::{FmtCtx, FmtUnit, Format}; use orchid_base::interner::{Interner, Tok}; use orchid_base::iter_utils::IteratorPrint; -use orchid_base::location::SrcRange; use orchid_base::name::{NameLike, Sym, VName, VPath}; -use orchid_base::parse::Comment; use orchid_base::reqnot::{ReqNot, Requester}; -use orchid_base::tree::{recur, ttv_from_api}; use ordered_float::NotNan; use substack::{Stackframe, Substack}; use crate::api; +use crate::atom::{AtomHand, WeakAtomHand}; use crate::ctx::Ctx; use crate::dealias::walk; -use crate::expr::{ExprParseCtx, ExprWillPanic}; -use crate::expr_store::ExprStore; use crate::extension::{Extension, WeakExtension}; -use crate::parsed::{ - Item, ItemKind, ParsTok, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule, -}; +use crate::sys_parser::Parser; use crate::tree::Root; #[derive(destructure)] -struct SystemInstData { +pub(crate) struct SystemInstData { deps: Vec, ctx: Ctx, ext: Extension, @@ -43,6 +37,7 @@ struct SystemInstData { id: api::SysId, line_types: Vec>, prelude: Vec, + owned_atoms: RwLock>, pub(crate) const_paths: MemoMap, } impl Drop for SystemInstData { @@ -60,7 +55,7 @@ impl fmt::Debug for SystemInstData { } #[derive(Clone, Debug)] -pub struct System(Rc); +pub struct System(pub(crate) Rc); impl System { #[must_use] pub fn id(&self) -> api::SysId { self.0.id } @@ -101,108 +96,32 @@ impl System { self.0.ext.lex_req(source, src, pos, self.id(), r).await } #[must_use] - pub fn can_parse(&self, ltyp: Tok) -> bool { self.0.line_types.contains(<yp) } - pub fn line_types(&self) -> impl Iterator> + '_ { self.0.line_types.iter() } - pub async fn parse( - &self, - path: Substack<'_, Tok>, - line: Vec, - exported: bool, - comments: Vec, - callback: &mut impl AsyncFnMut(Substack<'_, Tok>, Vec) -> OrcRes>, - ) -> OrcRes> { - let src_path = line.first().expect("cannot be empty").sr.path(); - let line = join_all(line.into_iter().map(|t| async { - let mut expr_store = self.0.ext.exprs().clone(); - let without_new_expr = recur(t, &|t, r| { - if let ParsTok::NewExpr(expr) = t.tok { - return ParsTok::Handle(expr).at(t.sr); - } - r(t) - }); - without_new_expr.into_api(&mut expr_store, &mut ExprWillPanic).await - })) - .await; - let comments = comments.iter().map(Comment::to_api).collect_vec(); - let req = api::ParseLine { - module: self.i().i(&path.unreverse()).await.to_api(), - src: src_path.to_api(), - exported, - sys: self.id(), - comments, - line, - }; - match self.reqnot().request(req).await { - Ok(parsed_v) => { - let mut ext_exprs = self.ext().exprs().clone(); - struct ConvCtx<'a> { - sys: &'a System, - src_path: &'a Sym, - i: &'a Interner, - ext_exprs: &'a mut ExprStore, - pctx: &'a mut ExprParseCtx<'a>, - } - async fn conv( - parsed_v: Vec, - module: Substack<'_, Tok>, - callback: &'_ mut impl AsyncFnMut( - Substack<'_, Tok>, - Vec, - ) -> OrcRes>, - ctx: &mut ConvCtx<'_>, - ) -> OrcRes> { - let mut items = Vec::new(); - for parsed in parsed_v { - let (name, exported, kind) = match parsed.kind { - api::ParsedLineKind::Member(api::ParsedMember { name, exported, kind }) => - (name, exported, kind), - api::ParsedLineKind::Recursive(rec) => { - let tokens = ttv_from_api(rec, ctx.ext_exprs, ctx.pctx, ctx.src_path, ctx.i).await; - items.extend(callback(module.clone(), tokens).await?); - continue; - }, - }; - let name = ctx.i.ex(name).await; - let mkind = match kind { - api::ParsedMemberKind::Module { lines, use_prelude } => { - let items = - conv(lines, module.push(name.clone()), callback, ctx).boxed_local().await?; - ParsedMemberKind::Mod(ParsedModule::new(use_prelude, items)) - }, - api::ParsedMemberKind::Constant(cid) => - ParsedMemberKind::DeferredConst(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)), - ) - .await, - sr: SrcRange::from_api(&parsed.source_range, ctx.i).await, - kind: ItemKind::Member(ParsedMember { name, exported, kind: mkind }), - }) - } - Ok(items) - } - conv(parsed_v, path, callback, &mut ConvCtx { - i: self.i(), - ext_exprs: &mut ext_exprs, - pctx: &mut ExprParseCtx { ctx: self.ctx(), exprs: self.ext().exprs() }, - src_path: &src_path, - sys: self, - }) - .await - }, - Err(e) => Err(OrcErrv::from_api(&e, &self.ctx().i).await), - } + pub fn get_parser(&self, ltyp: Tok) -> Option { + (self.0.line_types.iter().enumerate()) + .find(|(_, txt)| *txt == <yp) + .map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() }) } + pub fn line_types(&self) -> impl Iterator> + '_ { self.0.line_types.iter() } + #[must_use] pub async fn request(&self, req: Vec) -> Vec { self.reqnot().request(api::SysFwded(self.id(), req)).await } + pub(crate) async fn new_atom(&self, data: Vec, id: api::AtomId) -> AtomHand { + let mut owned_g = self.0.owned_atoms.write().await; + if let Some(data) = owned_g.get(&id) + && let Some(atom) = data.upgrade() + { + return atom; + } + let new = AtomHand::new(data, self.clone(), Some(id)); + owned_g.insert(id, new.downgrade()); + new + } pub(crate) fn drop_atom(&self, drop: api::AtomId) { let this = self.0.clone(); (self.0.ctx.spawn)(Box::pin(async move { - this.ctx.owned_atoms.write().await.remove(&drop); + this.owned_atoms.write().await.remove(&drop); })) } #[must_use] @@ -218,7 +137,7 @@ impl System { async move |rel| { let cwd = orig.split_last_seg().1; let root_data = &mut *root.0.write().await; - let walk_ctx = &mut (ctx.clone(), &mut root_data.consts); + let walk_ctx = &mut (ctx.clone(), &root_data.consts); let cmod = (walk(&root_data.root, false, cwd.iter().cloned(), walk_ctx).await) .expect("the parent module of a constant should exist"); let (selector, tail) = rel.split_first().expect("Names cannot be empty"); @@ -298,6 +217,7 @@ impl SystemCtor { .await, id, prelude: join_all(sys_inst.prelude.iter().map(|tok| Sym::from_api(*tok, &ext.ctx().i))).await, + owned_atoms: RwLock::new(HashMap::new()), const_paths: MemoMap::new(), })); let api_module_root = api::Module { diff --git a/orchid-host/src/tree.rs b/orchid-host/src/tree.rs index 51b10e6..0821bbb 100644 --- a/orchid-host/src/tree.rs +++ b/orchid-host/src/tree.rs @@ -10,6 +10,7 @@ use futures::{FutureExt, StreamExt, stream}; use hashbrown::HashMap; 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; @@ -26,7 +27,7 @@ use crate::system::System; pub struct RootData { pub root: Module, - pub consts: HashMap, + pub consts: MemoMap, pub ctx: Ctx, } #[derive(Clone)] @@ -36,23 +37,25 @@ impl Root { pub fn new(ctx: Ctx) -> Self { Root(Rc::new(RwLock::new(RootData { root: Module::default(), - consts: HashMap::default(), + consts: MemoMap::default(), ctx, }))) } #[must_use] pub async fn from_api(api: api::Module, sys: &System) -> Self { - let mut consts = HashMap::new(); - let mut tfac = TreeFromApiCtx { consts: &mut consts, path: sys.i().i(&[][..]).await, sys }; + let consts = MemoMap::new(); + let mut tfac = TreeFromApiCtx { consts: &consts, path: sys.i().i(&[][..]).await, sys }; let root = Module::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 { - let this = self.0.read().await; - let that = new.0.read().await; - let mut consts = - this.consts.iter().chain(&that.consts).map(|(k, v)| (k.clone(), v.clone())).collect(); - let root = this.root.merge(&that.root, this.ctx.clone(), &mut consts).await?; + let this = self.0.write().await; + let that = new.0.write().await; + let consts = MemoMap::new(); + for (k, v) in this.consts.iter().chain(that.consts.iter()) { + consts.insert(k.clone(), v.clone()); + } + let root = this.root.merge(&that.root, this.ctx.clone(), &consts).await?; Ok(Self(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() })))) } #[must_use] @@ -60,11 +63,11 @@ impl Root { let mut ref_this = self.0.write().await; let this = &mut *ref_this; let mut deferred_consts = HashMap::new(); - let mut consts = this.consts.clone(); + let consts = this.consts.clone(); let mut tfpctx = FromParsedCtx { pars_root: parsed, deferred_consts: &mut deferred_consts, - consts: &mut consts, + consts: &consts, pars_prefix: pars_prefix.clone(), root: &this.root, ctx: &this.ctx, @@ -79,7 +82,7 @@ impl Root { )]); module = Module { imports: HashMap::new(), members } } - let root = (this.root.merge(&module, this.ctx.clone(), &mut consts).await) + let root = (this.root.merge(&module, this.ctx.clone(), &consts).await) .expect("Merge conflict between parsed and existing module"); let new = Root(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() }))); *this.ctx.root.write().await = new.downgrade(); @@ -93,7 +96,7 @@ impl Root { new } pub async fn get_const_value(&self, name: Sym, pos: Pos) -> OrcRes { - let this = &mut *self.0.write().await; + let this = &*self.0.read().await; // shortcut for previously visited if let Some(val) = this.consts.get(&name) { return Ok(val.clone()); @@ -101,7 +104,7 @@ impl Root { // load the node, then check if this "walk" call added it to the map let ctx = this.ctx.clone(); let module = - walk(&this.root, false, name.iter().cloned(), &mut (ctx.clone(), &mut this.consts)).await; + walk(&this.root, false, name.iter().cloned(), &mut (ctx.clone(), &this.consts)).await; if let Some(val) = this.consts.get(&name) { return Ok(val.clone()); } @@ -140,14 +143,14 @@ impl Default for WeakRoot { pub struct TreeFromApiCtx<'a> { pub sys: &'a System, - pub consts: &'a mut HashMap, + pub consts: &'a MemoMap, pub path: Tok>>, } impl<'a> TreeFromApiCtx<'a> { #[must_use] - pub async fn push<'c>(&'c mut self, name: Tok) -> TreeFromApiCtx<'c> { + pub async fn push<'c>(&'c self, name: Tok) -> TreeFromApiCtx<'c> { let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await; - TreeFromApiCtx { path, consts: &mut *self.consts, sys: self.sys } + TreeFromApiCtx { path, consts: self.consts, sys: self.sys } } } @@ -208,7 +211,7 @@ impl Module { Ok(abs_path) => { let names_res = match abs_path.strip_prefix(&ctx.pars_prefix[..]) { None => { - let mut tree_ctx = (ctx.ctx.clone(), &mut *ctx.consts); + let mut tree_ctx = (ctx.ctx.clone(), ctx.consts); resolv_glob(&path, ctx.root, &abs_path, pos, &ctx.ctx.i, &mut tree_ctx).await }, Some(sub_tgt) => { @@ -322,7 +325,7 @@ impl Module { &self, other: &Module, ctx: Ctx, - consts: &mut HashMap, + consts: &MemoMap, ) -> Result { if !self.imports.is_empty() || !other.imports.is_empty() { return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Imports }); @@ -385,12 +388,12 @@ pub struct FromParsedCtx<'a> { root: &'a Module, rep: &'a Reporter, ctx: &'a Ctx, - consts: &'a mut HashMap, + consts: &'a MemoMap, deferred_consts: &'a mut HashMap, } impl Tree for Module { - type Ctx<'a> = (Ctx, &'a mut HashMap); + type Ctx<'a> = (Ctx, &'a MemoMap); async fn child( &self, key: Tok, @@ -420,7 +423,7 @@ pub struct Member { } impl Member { #[must_use] - pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &mut HashMap) -> &'a MemberKind { + pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &MemoMap) -> &'a MemberKind { (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 @@ -437,7 +440,7 @@ impl MemberKind { #[must_use] async fn from_parsed(parsed: &ParsedMemberKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self { match parsed { - ParsedMemberKind::DeferredConst(id, sys) => { + ParsedMemberKind::Const(id, sys) => { ctx.deferred_consts.insert(path, (sys.id(), *id)); MemberKind::Const }, @@ -454,12 +457,13 @@ pub struct LazyMemberHandle { } impl LazyMemberHandle { #[must_use] - pub async fn run(self, ctx: Ctx, consts: &mut HashMap) -> MemberKind { + pub async fn run(self, ctx: Ctx, consts: &MemoMap) -> MemberKind { 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) => { let mut pctx = ExprParseCtx { ctx: &ctx, exprs: sys.ext().exprs() }; - consts.insert(self.path, Expr::from_api(&c, PathSetBuilder::new(), &mut pctx).await); + let expr = Expr::from_api(&c, PathSetBuilder::new(), &mut pctx).await; + consts.insert(self.path, expr); MemberKind::Const }, api::MemberKind::Module(m) => MemberKind::Module( diff --git a/orchid-std/src/macros/let_line.rs b/orchid-std/src/macros/let_line.rs index 636049a..d0d884c 100644 --- a/orchid-std/src/macros/let_line.rs +++ b/orchid-std/src/macros/let_line.rs @@ -3,13 +3,13 @@ use std::pin::pin; use futures::{FutureExt, StreamExt, stream}; use hashbrown::HashMap; use itertools::Itertools; -use orchid_api::Paren; use orchid_base::error::{OrcRes, Reporter}; use orchid_base::name::Sym; use orchid_base::parse::{ Comment, ParseCtx, Parsed, Snippet, expect_tok, token_errv, try_pop_no_fluff, }; use orchid_base::sym; +use orchid_base::tree::Paren; use orchid_extension::gen_expr::{atom, call, sym_ref}; use orchid_extension::parser::{ConstCtx, PSnippet, PTok, PTokTree, ParsCtx, ParsedLine, Parser}; diff --git a/orchid-std/src/macros/macro_lib.rs b/orchid-std/src/macros/macro_lib.rs index a1c01b5..e2507bd 100644 --- a/orchid-std/src/macros/macro_lib.rs +++ b/orchid-std/src/macros/macro_lib.rs @@ -32,7 +32,7 @@ pub fn gen_macro_lib() -> Vec { ), fun(true, "resolve", |tpl: TypAtom| async move { call([ - sym_ref(sym!(macro::resolve_recur; tpl.untyped.ctx().i()).await), + sym_ref(sym!(macros::resolve_recur; tpl.untyped.ctx().i()).await), atom(RecurState::Bottom), tpl.untyped.ex().to_expr().await, ]) @@ -43,7 +43,7 @@ pub fn gen_macro_lib() -> Vec { if let Some(e) = Reporter::new().errv() { Err(e) } else { Ok(res) } }), fun(true, "resolve_recur", |state: TypAtom, tpl: TypAtom| async move { - exec(async move |mut h| { + exec("macros::resolve_recur", async move |mut h| { let ctx = tpl.ctx().clone(); let root = refl(&ctx); let tpl = own(tpl.clone()).await; diff --git a/orchid-std/src/macros/macro_line.rs b/orchid-std/src/macros/macro_line.rs index 12c200a..03dc51a 100644 --- a/orchid-std/src/macros/macro_line.rs +++ b/orchid-std/src/macros/macro_line.rs @@ -7,7 +7,6 @@ use futures::{StreamExt, stream}; use hashbrown::{HashMap, HashSet}; use itertools::Itertools; use never::Never; -use orchid_api::Paren; use orchid_base::error::{OrcRes, Reporter, mk_errv}; use orchid_base::interner::Tok; use orchid_base::location::Pos; @@ -16,7 +15,7 @@ use orchid_base::parse::{ Comment, ParseCtx, Parsed, Snippet, expect_end, expect_tok, line_items, token_errv, try_pop_no_fluff, }; -use orchid_base::tree::Token; +use orchid_base::tree::{Paren, Token}; use orchid_base::{clone, sym}; use orchid_extension::atom::{Atomic, TypAtom}; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant}; @@ -142,7 +141,7 @@ impl Parser for MacroLine { return Err(e); } Ok(call([ - sym_ref(sym!(macro::resolve_recur; ctx.i()).await), + sym_ref(sym!(macros::resolve_recur; ctx.i()).await), atom(RecurState::base(path)), macro_input.to_expr().await, ])) diff --git a/orchid-std/src/macros/mactree.rs b/orchid-std/src/macros/mactree.rs index 6ecd7c6..519c5ab 100644 --- a/orchid-std/src/macros/mactree.rs +++ b/orchid-std/src/macros/mactree.rs @@ -6,14 +6,13 @@ use futures::FutureExt; use futures::future::join_all; use hashbrown::HashSet; use itertools::Itertools; -use orchid_api::Paren; use orchid_base::error::{OrcErrv, Reporter, mk_errv}; use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants, fmt}; use orchid_base::interner::Tok; use orchid_base::location::Pos; use orchid_base::name::Sym; use orchid_base::tl_cache; -use orchid_base::tree::indent; +use orchid_base::tree::{Paren, indent}; use orchid_extension::atom::Atomic; use orchid_extension::atom_owned::{OwnedAtom, OwnedVariant}; use orchid_extension::conv::ToExpr; diff --git a/orchid-std/src/macros/resolve.rs b/orchid-std/src/macros/resolve.rs index 5420459..633f9ab 100644 --- a/orchid-std/src/macros/resolve.rs +++ b/orchid-std/src/macros/resolve.rs @@ -39,26 +39,29 @@ pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option i += 1, - 1 => { - any_changed = true; - let (mac, rule, (state, tail)) = matches.into_iter().exactly_one().unwrap(); - let end = val.len() - tail.len(); - let new_i = val.len() - tail.len(); - let body_call = mk_body_call(mac, rule, &state, &ctx.ctx, ctx.recur.clone()).await; - std::mem::drop(state); - val.splice(i..end, [MacTok::Value(ctx.h.register(body_call).await).at(Pos::None)]); - i = new_i; - }, - 2.. => todo!("Named macros conflict!"), + 'all_named: while i < val.len() { + 'one_named: { + let MacTok::Name(key) = val[i].tok() else { break 'one_named }; + let Some(options) = ctx.named.get(key) else { break 'one_named }; + let matches = (options.iter()) + .filter_map(|r| Some((r.1, r.2, r.0.apply(&val[i..], |_| false)?))) + .collect_vec(); + match matches.len() { + 0 => break 'one_named, + 1 => { + any_changed = true; + let (mac, rule, (state, tail)) = matches.into_iter().exactly_one().unwrap(); + let end = val.len() - tail.len(); + let body_call = mk_body_call(mac, rule, &state, &ctx.ctx, ctx.recur.clone()).await; + std::mem::drop(state); + val.splice(i..end, [MacTok::Value(ctx.h.register(body_call).await).at(Pos::None)]); + i = end; + }, + 2.. => todo!("Named macros conflict!"), + } + continue 'all_named; } + i += 1; } for (matcher, mac, rule) in &ctx.priod { let Some(state) = matcher.apply(&val, |_| false) else { continue }; diff --git a/orcx/src/main.rs b/orcx/src/main.rs index 9214410..bb3ac80 100644 --- a/orcx/src/main.rs +++ b/orcx/src/main.rs @@ -228,7 +228,7 @@ async fn main() -> io::Result { }, Commands::Exec { proj, code } => { let reporter = Reporter::new(); - let path = sym!(usercode::entrypoint; i).await; + let path = sym!(usercode; i).await; let prefix_sr = SrcRange::zw(path.clone(), 0); let (mut root, systems) = init_systems(&args.system, &extensions).await.unwrap(); if let Some(proj_path) = proj { diff --git a/xtask/src/check_api_refs.rs b/xtask/src/check_api_refs.rs index 66b77dc..088871a 100644 --- a/xtask/src/check_api_refs.rs +++ b/xtask/src/check_api_refs.rs @@ -20,7 +20,7 @@ pub fn check_api_refs(_args: &Args) -> io::Result<()> { continue; } let dname = file.path().to_string_lossy().to_string(); - eprintln!("orchid_api imported in {dname} at {};{}", l + 1, c + 1) + eprintln!("orchid_api imported in {dname}:{}:{}", l + 1, c + 1) } } Ok(()) diff --git a/xtask/src/orcx.rs b/xtask/src/orcx.rs index dd9e698..f0d7d5b 100644 --- a/xtask/src/orcx.rs +++ b/xtask/src/orcx.rs @@ -5,18 +5,23 @@ use std::sync::atomic::Ordering; use crate::{Args, EXIT_OK}; pub fn orcx(_args: &Args, argv: &[String]) -> io::Result<()> { - if !Command::new("cargo").args(["build", "-p", "orchid-std"]).status()?.success() { + if !Command::new("cargo").args(["build", "-p", "orchid-std", "--quiet"]).status()?.success() { EXIT_OK.store(false, Ordering::Relaxed); return Ok(()); } - if !Command::new("cargo").args(["run", "-p", "orcx", "--"]).args(argv).status()?.success() { + if !Command::new("cargo") + .args(["run", "-p", "orcx", "--quiet", "--"]) + .args(argv) + .status()? + .success() + { EXIT_OK.store(false, Ordering::Relaxed); } Ok(()) } pub fn orcxdb(_args: &Args, argv: &[String]) -> io::Result<()> { - if !Command::new("cargo").args(["build", "-p", "orchid-std"]).status()?.success() { + if !Command::new("cargo").args(["build", "-p", "orchid-std", "--quiet"]).status()?.success() { EXIT_OK.store(false, Ordering::Relaxed); return Ok(()); }