exec working up to halt

clean shutdown doesn't for some reason
This commit is contained in:
2025-09-09 16:30:49 +02:00
parent e339350505
commit ce08021e79
36 changed files with 460 additions and 399 deletions

View File

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

View File

@@ -270,7 +270,6 @@ impl Encode for bool {
}
impl<T: Decode, const N: usize> Decode for [T; N] {
async fn decode<R: AsyncRead + ?Sized>(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

View File

@@ -30,6 +30,7 @@ pub struct ParseLine {
pub src: TStrv,
pub comments: Vec<Comment>,
pub exported: bool,
pub idx: u16,
pub line: Vec<TokenTree>,
}
impl Request for ParseLine {

View File

@@ -79,7 +79,6 @@ pub fn parse_num(string: &str) -> Result<Numeric, NumError> {
.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<Numeric, NumError> {
},
None => (noprefix, 0),
};
eprintln!("({base_s},{exponent})");
match base_s.split_once('.') {
None => {
let base = int_parse(base_s, radix, pos)?;

View File

@@ -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::<TestMsgSet>::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::<Option<ReqNot<TestMsgSet>>>::new(None));
let sender = Rc::new(ReqNot::<TestMsgSet>::new(
logger.clone(),

View File

@@ -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<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVari
let mut data = enc_vec(&typ_id).await;
self.encode(Pin::<&mut Vec<u8>>::new(&mut data)).await;
ctx.get_or_default::<ObjStore>().objects.read().await.insert(atom_id, Box::new(self));
eprintln!("Created atom {:?} of type {}", atom_id, type_name::<A>());
api::Atom { drop: Some(atom_id), data, owner: ctx.sys_id() }
})
}
@@ -52,7 +50,7 @@ impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVari
/// While an atom read guard is held, no atom can be removed.
pub(crate) struct AtomReadGuard<'a> {
id: api::AtomId,
guard: RwLockReadGuard<'a, MemoMap<AtomId, Box<dyn DynOwnedAtom>>>,
guard: RwLockReadGuard<'a, MemoMap<api::AtomId, Box<dyn DynOwnedAtom>>>,
}
impl<'a> AtomReadGuard<'a> {
async fn new(id: api::AtomId, ctx: &'a SysCtx) -> Self {

View File

@@ -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<Expr>),
Register(GExpr, Sender<Expr>),
Halt(GExpr),
}
struct BuilderCoroutineData {
work: Fuse<Pin<Box<dyn Future<Output = GExpr>>>>,
cmd_recv: Receiver<Command>,
name: Option<String>,
receiver: Mutex<LocalBoxStream<'static, Command>>,
}
#[derive(Clone)]
struct BuilderCoroutine(Rc<Mutex<BuilderCoroutineData>>);
struct BuilderCoroutine(Rc<BuilderCoroutineData>);
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<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
pub async fn exec<R: ToExpr>(
debug: impl AsRef<str>,
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<Command>, PhantomData<&'a ()>);
impl ExecHandle<'_> {
pub async fn exec<T: TryFromExpr>(&mut self, val: impl ToExpr) -> OrcRes<T> {
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)
}

View File

@@ -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<ExtMsgSet>| {
move |id: api::SysId, cted: CtedObj, reqnot: ReqNot<api::ExtMsgSet>| {
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<PTokTree> = 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<PTokTree> =
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 {

View File

@@ -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<Option<HashSet<Rc<ExprHandle>>>>);
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<Self> {
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<Self> { 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<Self>) {
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<H: std::hash::Hasher>(&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,
),
}
}
}

View File

@@ -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<dyn FunCB>,
}
async fn process_args<I, O, F: ExprFunc<I, O>>(f: F) -> FunRecord {
async fn process_args<I, O, F: ExprFunc<I, O>>(
debug: impl AsRef<str> + Clone + 'static,
f: F,
) -> FunRecord {
let argtyps = F::argtyps();
let fun = Rc::new(move |v: Vec<Expr>| {
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::<Expr>() {
@@ -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<Expr>;
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<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
Self { args: vec![], record: process_args(f).await }
pub async fn new<I, O, F: ExprFunc<I, O>>(
debug: impl AsRef<str> + Clone + 'static,
f: F,
) -> Self {
Self { args: vec![], record: process_args(debug, f).await }
}
}
impl Atomic for Lambda {

View File

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

View File

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

View File

@@ -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<ParsedLine>, 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<Item = &'b Sym> + 'b,
) -> impl Stream<Item = OrcRes<Sym>> + '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(),

View File

@@ -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<Item = Option<Box<dyn AtomDynfo>>> {
[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<ExtMsgSet>,
reqnot: ReqNot<api::ExtMsgSet>,
spawner: Spawner,
logger: Logger,
cted: CtedObj,
@@ -167,13 +167,7 @@ impl SysCtx {
(self.0.get_or_insert_owned(TypeId::of::<T>(), || Box::new(f())).downcast_ref())
.expect("Keyed by TypeId")
}
pub fn get_or_default<T: SysCtxEntry + Default>(&self) -> &T {
self.get_or_insert(|| {
let rc_id = self.0.as_ref() as *const _ as *const () as usize;
eprintln!("Default-initializing {} in {}", type_name::<T>(), rc_id);
T::default()
})
}
pub fn get_or_default<T: SysCtxEntry + Default>(&self) -> &T { self.get_or_insert(T::default) }
pub fn try_get<T: SysCtxEntry>(&self) -> Option<&T> {
Some(self.0.get(&TypeId::of::<T>())?.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::<Interner>() }
/// Shorthand to get the messaging link
pub fn reqnot(&self) -> &ReqNot<ExtMsgSet> { self.get::<ReqNot<ExtMsgSet>>() }
pub fn reqnot(&self) -> &ReqNot<api::ExtMsgSet> { self.get::<ReqNot<api::ExtMsgSet>>() }
/// Shorthand to get the system ID
pub fn sys_id(&self) -> api::SysId { *self.get::<api::SysId>() }
/// Shorthand to get the task spawner callback

View File

@@ -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<Expr, GExpr>;
@@ -43,26 +42,18 @@ impl TokenVariant<api::Expression> for GExpr {
}
impl TokenVariant<api::ExprTicket> 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,18 +87,8 @@ pub fn root_mod(name: &str, mems: impl IntoIterator<Item = Vec<GenMember>>) -> (
(name.to_string(), kind)
}
pub fn fun<I, O>(public: bool, name: &str, xf: impl ExprFunc<I, O>) -> Vec<GenMember> {
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![] }]
}

View File

@@ -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<api::AtomId>,
data: Vec<u8>,
pub(crate) display: OnceCell<FmtUnit>,
}
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<AtomData>);
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<u8>, owner: System, drop: Option<api::AtomId>) -> 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 {
(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() }
}

View File

@@ -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<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>,
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub common_exprs: ExprStore,
pub root: RwLock<WeakRoot>,
}
@@ -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)) }

View File

@@ -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<RwLockWriteGuard<'static, ExprKind>, 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) =>

View File

@@ -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<api::ExprTicket>,
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<api::ExprTicket>,
visited: Substack<'_, api::ExprTicket>,
) -> FmtUnit {
match &ek {
ExprKind::Arg => "Arg".to_string().into(),

View File

@@ -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<Expr> {
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<Expr> {

View File

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

View File

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

View File

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

View File

@@ -33,6 +33,7 @@ impl ParseCtx for HostParseCtxImpl<'_> {
impl HostParseCtx for HostParseCtxImpl<'_> {
fn ctx(&self) -> &Ctx { &self.ctx }
fn systems(&self) -> impl Iterator<Item = &System> { 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<Item = &System>;
#[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
})

View File

@@ -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<Expr, Expr>;
@@ -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<Variants>: 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<ParsedModule> 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<Vec<ParsTokTree>>,
}
/// 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<Vec<Tok<String>>>) -> 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
}

View File

@@ -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<String>>;
impl Parser {
pub async fn parse(
&self,
ctx: &impl HostParseCtx,
path: ModPath<'_>,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
callback: &mut impl AsyncFnMut(ModPath<'_>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
) -> OrcRes<Vec<Item>> {
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<api::ParsedLine>,
module: Substack<'_, Tok<String>>,
callback: &'_ mut impl AsyncFnMut(Substack<'_, Tok<String>>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
ctx: &mut ConvCtx<'_>,
) -> OrcRes<Vec<Item>> {
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)
}

View File

@@ -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<System>,
ctx: Ctx,
ext: Extension,
@@ -43,6 +37,7 @@ struct SystemInstData {
id: api::SysId,
line_types: Vec<Tok<String>>,
prelude: Vec<Sym>,
owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub(crate) const_paths: MemoMap<api::ParsedConstId, Sym>,
}
impl Drop for SystemInstData {
@@ -60,7 +55,7 @@ impl fmt::Debug for SystemInstData {
}
#[derive(Clone, Debug)]
pub struct System(Rc<SystemInstData>);
pub struct System(pub(crate) Rc<SystemInstData>);
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<String>) -> bool { self.0.line_types.contains(&ltyp) }
pub fn get_parser(&self, ltyp: Tok<String>) -> Option<Parser> {
(self.0.line_types.iter().enumerate())
.find(|(_, txt)| *txt == &ltyp)
.map(|(idx, _)| Parser { idx: idx as u16, system: self.clone() })
}
pub fn line_types(&self) -> impl Iterator<Item = &Tok<String>> + '_ { self.0.line_types.iter() }
pub async fn parse(
&self,
path: Substack<'_, Tok<String>>,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
callback: &mut impl AsyncFnMut(Substack<'_, Tok<String>>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
) -> OrcRes<Vec<Item>> {
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<api::ParsedLine>,
module: Substack<'_, Tok<String>>,
callback: &'_ mut impl AsyncFnMut(
Substack<'_, Tok<String>>,
Vec<ParsTokTree>,
) -> OrcRes<Vec<Item>>,
ctx: &mut ConvCtx<'_>,
) -> OrcRes<Vec<Item>> {
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),
}
}
#[must_use]
pub async fn request(&self, req: Vec<u8>) -> Vec<u8> {
self.reqnot().request(api::SysFwded(self.id(), req)).await
}
pub(crate) async fn new_atom(&self, data: Vec<u8>, 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 {

View File

@@ -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<Sym, Expr>,
pub consts: MemoMap<Sym, Expr>,
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<Self, MergeErr> {
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<Expr> {
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<Sym, Expr>,
pub consts: &'a MemoMap<Sym, Expr>,
pub path: Tok<Vec<Tok<String>>>,
}
impl<'a> TreeFromApiCtx<'a> {
#[must_use]
pub async fn push<'c>(&'c mut self, name: Tok<String>) -> TreeFromApiCtx<'c> {
pub async fn push<'c>(&'c self, name: Tok<String>) -> TreeFromApiCtx<'c> {
let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await;
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<Sym, Expr>,
consts: &MemoMap<Sym, Expr>,
) -> Result<Module, MergeErr> {
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<Sym, Expr>,
consts: &'a MemoMap<Sym, Expr>,
deferred_consts: &'a mut HashMap<Sym, (api::SysId, api::ParsedConstId)>,
}
impl Tree for Module {
type Ctx<'a> = (Ctx, &'a mut HashMap<Sym, Expr>);
type Ctx<'a> = (Ctx, &'a MemoMap<Sym, Expr>);
async fn child(
&self,
key: Tok<String>,
@@ -420,7 +423,7 @@ pub struct Member {
}
impl Member {
#[must_use]
pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &mut HashMap<Sym, Expr>) -> &'a MemberKind {
pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> &'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<Sym, Expr>) -> MemberKind {
pub async fn run(self, ctx: Ctx, consts: &MemoMap<Sym, Expr>) -> 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(

View File

@@ -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};

View File

@@ -32,7 +32,7 @@ pub fn gen_macro_lib() -> Vec<GenMember> {
),
fun(true, "resolve", |tpl: TypAtom<MacTree>| 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<GenMember> {
if let Some(e) = Reporter::new().errv() { Err(e) } else { Ok(res) }
}),
fun(true, "resolve_recur", |state: TypAtom<RecurState>, tpl: TypAtom<MacTree>| 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;

View File

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

View File

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

View File

@@ -39,26 +39,29 @@ pub async fn resolve_seq(ctx: &mut ResolveCtx<'_>, val: &[MacTree]) -> Option<Ve
let mut any_changed = false;
let mut i = 0;
let mut val = val.to_vec();
while i < val.len() {
let MacTok::Name(key) = val[i].tok() else { continue };
let Some(options) = ctx.named.get(key) else { continue };
'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 => i += 1,
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 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;
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 };

View File

@@ -228,7 +228,7 @@ async fn main() -> io::Result<ExitCode> {
},
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 {

View File

@@ -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(())

View File

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