Macro system done in theory

too afraid to begin debugging, resting for a moment
This commit is contained in:
2025-09-03 16:05:26 +02:00
parent 051b5e666f
commit 7031f3a7d8
51 changed files with 1463 additions and 458 deletions

View File

@@ -15,7 +15,7 @@ use futures::{FutureExt, StreamExt};
use orchid_api_derive::Coding;
use orchid_api_traits::{Coding, Decode, Encode, Request, enc_vec};
use orchid_base::clone;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::error::{OrcErrv, OrcRes, mk_errv, mk_errv_floating};
use orchid_base::format::{FmtCtx, FmtUnit, Format};
use orchid_base::interner::Interner;
use orchid_base::location::Pos;
@@ -24,6 +24,7 @@ use orchid_base::reqnot::Requester;
use trait_set::trait_set;
use crate::api;
use crate::conv::ToExpr;
// use crate::error::{ProjectError, ProjectResult};
use crate::expr::{Expr, ExprData, ExprHandle, ExprKind};
use crate::gen_expr::GExpr;
@@ -92,7 +93,7 @@ pub struct ForeignAtom {
}
impl ForeignAtom {
pub fn pos(&self) -> Pos { self.pos.clone() }
pub fn ctx(&self) -> SysCtx { self.expr.ctx.clone() }
pub fn ctx(&self) -> &SysCtx { &self.expr.ctx }
pub fn ex(self) -> Expr {
let (handle, pos) = (self.expr.clone(), self.pos.clone());
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { ..self }) };
@@ -110,6 +111,9 @@ impl ForeignAtom {
.await?;
Some(M::Response::decode(Pin::new(&mut &rep[..])).await)
}
pub async fn downcast<T: AtomicFeatures>(self) -> Result<TypAtom<T>, NotTypAtom> {
TypAtom::downcast(self.ex().handle()).await
}
}
impl fmt::Display for ForeignAtom {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Atom::{:?}", self.atom) }
@@ -122,6 +126,9 @@ impl Format for ForeignAtom {
FmtUnit::from_api(&self.ctx().reqnot().request(api::ExtAtomPrint(self.atom.clone())).await)
}
}
impl ToExpr for ForeignAtom {
async fn to_expr(self) -> GExpr { self.ex().to_expr().await }
}
pub struct NotTypAtom {
pub pos: Pos,
@@ -130,11 +137,11 @@ pub struct NotTypAtom {
pub ctx: SysCtx,
}
impl NotTypAtom {
pub async fn mk_err(&self) -> OrcErr {
mk_err(
pub async fn mk_err(&self) -> OrcErrv {
mk_errv(
self.ctx.i().i("Not the expected type").await,
format!("This expression is not a {}", self.typ.name()),
[self.pos.clone().into()],
[self.pos.clone()],
)
}
}
@@ -216,10 +223,12 @@ impl<A: AtomCard> Default for MethodSetBuilder<A> {
#[derive(Clone)]
pub struct TypAtom<A: AtomicFeatures> {
pub data: ForeignAtom,
pub untyped: ForeignAtom,
pub value: A::Data,
}
impl<A: AtomicFeatures> TypAtom<A> {
pub fn ctx(&self) -> &SysCtx { self.untyped.ctx() }
pub fn i(&self) -> &Interner { self.ctx().i() }
pub async fn downcast(expr: Rc<ExprHandle>) -> Result<Self, NotTypAtom> {
match Expr::from_handle(expr).atom().await {
Err(expr) => Err(NotTypAtom {
@@ -242,9 +251,9 @@ impl<A: AtomicFeatures> TypAtom<A> {
pub async fn request<M: AtomMethod>(&self, req: M) -> M::Response
where A: Supports<M> {
M::Response::decode(Pin::new(
&mut &(self.data.ctx().reqnot().request(api::Fwd(
self.data.atom.clone(),
Sym::parse(M::NAME, self.data.ctx().i()).await.unwrap().tok().to_api(),
&mut &(self.untyped.ctx().reqnot().request(api::Fwd(
self.untyped.atom.clone(),
Sym::parse(M::NAME, self.untyped.ctx().i()).await.unwrap().tok().to_api(),
enc_vec(&req).await,
)))
.await
@@ -257,6 +266,9 @@ impl<A: AtomicFeatures> Deref for TypAtom<A> {
type Target = A::Data;
fn deref(&self) -> &Self::Target { &self.value }
}
impl<A: AtomicFeatures> ToExpr for TypAtom<A> {
async fn to_expr(self) -> GExpr { self.untyped.to_expr().await }
}
pub struct AtomCtx<'a>(pub &'a [u8], pub Option<api::AtomId>, pub SysCtx);
impl FmtCtx for AtomCtx<'_> {
@@ -317,10 +329,10 @@ impl Format for AtomFactory {
}
}
pub async fn err_not_callable(i: &Interner) -> OrcErr {
mk_err(i.i("This atom is not callable").await, "Attempted to apply value as function", [])
pub async fn err_not_callable(i: &Interner) -> OrcErrv {
mk_errv_floating(i.i("This atom is not callable").await, "Attempted to apply value as function")
}
pub async fn err_not_command(i: &Interner) -> OrcErr {
mk_err(i.i("This atom is not a command").await, "Settled on an inactionable value", [])
pub async fn err_not_command(i: &Interner) -> OrcErrv {
mk_errv_floating(i.i("This atom is not a command").await, "Settled on an inactionable value")
}

View File

@@ -217,7 +217,7 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
fn val(&self) -> impl Future<Output = Cow<'_, Self::Data>>;
#[allow(unused_variables)]
fn call_ref(&self, arg: Expr) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(arg.ctx().i()).await]) }
async move { bot(err_not_callable(arg.ctx().i()).await) }
}
fn call(self, arg: Expr) -> impl Future<Output = GExpr> {
async {
@@ -229,12 +229,12 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Any + Clone + 'static {
}
#[allow(unused_variables)]
fn command(self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
async move { Err(err_not_command(ctx.i()).await.into()) }
async move { Err(err_not_command(ctx.i()).await) }
}
#[allow(unused_variables)]
fn free(self, ctx: SysCtx) -> impl Future<Output = ()> { async {} }
#[allow(unused_variables)]
fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
fn print_atom<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> impl Future<Output = FmtUnit> {
async { format!("OwnedAtom({})", type_name::<Self>()).into() }
}
#[allow(unused_variables)]
@@ -294,7 +294,7 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
self.free(ctx).boxed_local()
}
fn dyn_print(&self, ctx: SysCtx) -> LocalBoxFuture<'_, FmtUnit> {
async move { self.print(&FmtCtxImpl { i: ctx.i() }).await }.boxed_local()
async move { self.print_atom(&FmtCtxImpl { i: ctx.i() }).await }.boxed_local()
}
fn dyn_serialize<'a>(
&'a self,
@@ -315,10 +315,10 @@ struct ObjStore {
}
impl SysCtxEntry for ObjStore {}
pub async fn get_own_instance<A: OwnedAtom>(typ: TypAtom<A>) -> A {
let ctx = typ.data.ctx();
pub async fn own<A: OwnedAtom>(typ: TypAtom<A>) -> A {
let ctx = typ.untyped.ctx();
let g = ctx.get_or_default::<ObjStore>().objects.read().await;
let dyn_atom = (g.get(&typ.data.atom.drop.expect("Owned atoms always have a drop ID")))
let dyn_atom = (g.get(&typ.untyped.atom.drop.expect("Owned atoms always have a drop ID")))
.expect("Atom ID invalid; atom type probably not owned by this crate");
dyn_atom.as_any_ref().downcast_ref().cloned().expect("The ID should imply a type as well")
}

View File

@@ -105,11 +105,11 @@ pub trait ThinAtom:
{
#[allow(unused_variables)]
fn call(&self, arg: Expr) -> impl Future<Output = GExpr> {
async move { bot([err_not_callable(arg.ctx().i()).await]) }
async move { bot(err_not_callable(arg.ctx().i()).await) }
}
#[allow(unused_variables)]
fn command(&self, ctx: SysCtx) -> impl Future<Output = OrcRes<Option<GExpr>>> {
async move { Err(err_not_command(ctx.i()).await.into()) }
async move { Err(err_not_command(ctx.i()).await) }
}
#[allow(unused_variables)]
fn print(&self, ctx: SysCtx) -> impl Future<Output = FmtUnit> {

View File

@@ -1,11 +1,11 @@
use std::future::Future;
use never::Never;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
use orchid_base::interner::Interner;
use orchid_base::location::Pos;
use crate::atom::{AtomicFeatures, ToAtom, TypAtom};
use crate::atom::{AtomicFeatures, ForeignAtom, ToAtom, TypAtom};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, atom, bot};
use crate::system::{SysCtx, downcast_atom};
@@ -24,22 +24,29 @@ impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
}
}
async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErr {
mk_err(i.i("Expected an atom").await, "This expression is not an atom", [pos.into()])
async fn err_not_atom(pos: Pos, i: &Interner) -> OrcErrv {
mk_errv(i.i("Expected an atom").await, "This expression is not an atom", [pos])
}
async fn err_type(pos: Pos, i: &Interner) -> OrcErr {
mk_err(i.i("Type error").await, "The atom is a different type than expected", [pos.into()])
async fn err_type(pos: Pos, i: &Interner) -> OrcErrv {
mk_errv(i.i("Type error").await, "The atom is a different type than expected", [pos])
}
impl TryFromExpr for ForeignAtom {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match expr.atom().await {
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), ex.ctx().i()).await),
Ok(f) => Ok(f),
}
}
}
impl<A: AtomicFeatures> TryFromExpr for TypAtom<A> {
async fn try_from_expr(expr: Expr) -> OrcRes<Self> {
match expr.atom().await {
Err(ex) => Err(err_not_atom(ex.data().await.pos.clone(), ex.ctx().i()).await.into()),
Ok(f) => match downcast_atom::<A>(f).await {
Ok(a) => Ok(a),
Err(f) => Err(err_type(f.pos(), f.ctx().i()).await.into()),
},
let f = ForeignAtom::try_from_expr(expr).await?;
match downcast_atom::<A>(f).await {
Ok(a) => Ok(a),
Err(f) => Err(err_type(f.pos(), f.ctx().i()).await),
}
}
}
@@ -49,29 +56,29 @@ impl TryFromExpr for SysCtx {
}
pub trait ToExpr {
fn to_expr(self) -> GExpr;
fn to_expr(self) -> impl Future<Output = GExpr>;
}
impl ToExpr for GExpr {
fn to_expr(self) -> GExpr { self }
async fn to_expr(self) -> GExpr { self }
}
impl ToExpr for Expr {
fn to_expr(self) -> GExpr { self.slot() }
async fn to_expr(self) -> GExpr { self.slot() }
}
impl<T: ToExpr> ToExpr for OrcRes<T> {
fn to_expr(self) -> GExpr {
async fn to_expr(self) -> GExpr {
match self {
Err(e) => bot(e),
Ok(t) => t.to_expr(),
Ok(t) => t.to_expr().await,
}
}
}
impl<A: ToAtom> ToExpr for A {
fn to_expr(self) -> GExpr { atom(self) }
async fn to_expr(self) -> GExpr { atom(self) }
}
impl ToExpr for Never {
fn to_expr(self) -> GExpr { match self {} }
async fn to_expr(self) -> GExpr { match self {} }
}

View File

@@ -0,0 +1,96 @@
use std::borrow::Cow;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
use async_std::channel::{Receiver, RecvError, Sender, bounded};
use async_std::sync::Mutex;
use futures::future::Fuse;
use futures::{FutureExt, select};
use never::Never;
use orchid_base::error::OrcRes;
use crate::atom::Atomic;
use crate::atom_owned::{OwnedAtom, OwnedVariant};
use crate::conv::{ToExpr, TryFromExpr};
use crate::expr::Expr;
use crate::gen_expr::{GExpr, arg, call, lambda, seq};
enum Command {
Execute(GExpr, Sender<Expr>),
Register(GExpr, Sender<Expr>),
}
struct BuilderCoroutineData {
work: Mutex<Fuse<Pin<Box<dyn Future<Output = GExpr>>>>>,
cmd_recv: Receiver<Command>,
}
#[derive(Clone)]
struct BuilderCoroutine(Rc<BuilderCoroutineData>);
impl BuilderCoroutine {
pub async fn run(self) -> GExpr {
let cmd = {
let mut work = self.0.work.lock().await;
select! {
ret_val = &mut *work => { return ret_val },
cmd_res = self.0.cmd_recv.recv().fuse() => match cmd_res {
Ok(cmd) => cmd,
Err(RecvError) => return (&mut *work).await
},
}
};
match cmd {
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) =>
call([Replier { reply, builder: self }.to_expr().await, expr]),
}
}
}
#[derive(Clone)]
pub struct Replier {
reply: Sender<Expr>,
builder: BuilderCoroutine,
}
impl Atomic for Replier {
type Data = ();
type Variant = OwnedVariant;
}
impl OwnedAtom for Replier {
type Refs = Never;
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call(self, arg: Expr) -> GExpr {
let _ = self.reply.send(arg).await;
self.builder.run().await
}
}
pub async fn exec<R: ToExpr>(f: impl for<'a> AsyncFnOnce(ExecHandle<'a>) -> R + 'static) -> GExpr {
let (cmd_snd, cmd_recv) = bounded(1);
let work =
async { f(ExecHandle(cmd_snd, PhantomData)).await.to_expr().await }.boxed_local().fuse();
let coro = BuilderCoroutine(Rc::new(BuilderCoroutineData { cmd_recv, work: Mutex::new(work) }));
coro.run().await
}
static WEIRD_DROP_ERR: &str = "Coroutine dropped while we are being polled somehow";
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, reply_recv) = bounded(1);
self.0.send(Command::Execute(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
T::try_from_expr(reply_recv.recv().await.expect(WEIRD_DROP_ERR)).await
}
pub async fn register(&mut self, val: impl ToExpr) -> Expr {
let (reply_snd, reply_recv) = bounded(1);
self.0.send(Command::Register(val.to_expr().await, reply_snd)).await.expect(WEIRD_DROP_ERR);
reply_recv.recv().await.expect(WEIRD_DROP_ERR)
}
}

View File

@@ -31,7 +31,7 @@ use crate::api;
use crate::atom::{AtomCtx, AtomDynfo, AtomTypeId};
use crate::atom_owned::take_atom;
use crate::expr::{Expr, ExprHandle};
use crate::lexer::{LexContext, err_cascade, err_not_applicable};
use crate::lexer::{LexContext, ekey_cascade, ekey_not_applicable};
use crate::parser::{PTok, PTokTree, ParsCtx, get_const, linev_into_api};
use crate::system::{SysCtx, atom_by_idx};
use crate::system_ctor::{CtedObj, DynSystemCtor};
@@ -230,16 +230,17 @@ pub fn extension_init(
let sys_ctx = get_ctx(sys).await;
let text = Tok::from_api(text, &i).await;
let src = Sym::from_api(src, sys_ctx.i()).await;
let ctx = LexContext { id, pos, text: &text, src, ctx: sys_ctx.clone() };
let rep = Reporter::new();
let ctx = LexContext { id, pos, text: &text, src, ctx: sys_ctx.clone(), rep: &rep };
let trigger_char = text.chars().nth(pos as usize).unwrap();
let err_na = err_not_applicable(&i).await;
let err_cascade = err_cascade(&i).await;
let 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)) {
match lx.lex(&text[pos as usize..], &ctx).await {
Err(e) if e.any(|e| *e == err_na) => continue,
Err(e) if e.any(|e| *e == ekey_na) => continue,
Err(e) => {
let eopt = e.keep_only(|e| *e != err_cascade).map(|e| Err(e.to_api()));
let eopt = e.keep_only(|e| *e != ekey_cascade).map(|e| Err(e.to_api()));
return hand.handle(&lex, &eopt).await;
},
Ok((s, expr)) => {

View File

@@ -1,3 +1,9 @@
//! #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;
use std::future::Future;
@@ -20,6 +26,7 @@ use trait_set::trait_set;
use crate::atom::Atomic;
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr;
use crate::coroutine_exec::{ExecHandle, exec};
use crate::expr::Expr;
use crate::gen_expr::GExpr;
use crate::system::{SysCtx, SysCtxEntry};
@@ -29,13 +36,37 @@ trait_set! {
}
pub trait ExprFunc<I, O>: Clone + 'static {
const ARITY: u8;
fn apply(&self, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
fn argtyps() -> &'static [TypeId];
fn apply<'a>(&self, hand: ExecHandle<'a>, v: Vec<Expr>) -> impl Future<Output = OrcRes<GExpr>>;
}
#[derive(Default)]
struct FunsCtx(Mutex<HashMap<Sym, (u8, Rc<dyn FunCB>)>>);
struct FunsCtx(Mutex<HashMap<Sym, FunRecord>>);
impl SysCtxEntry for FunsCtx {}
#[derive(Clone)]
struct FunRecord {
argtyps: &'static [TypeId],
fun: Rc<dyn FunCB>,
}
async fn process_args<I, O, F: ExprFunc<I, O>>(f: F) -> FunRecord {
let argtyps = F::argtyps();
let fun = Rc::new(move |v: Vec<Expr>| {
clone!(f, v mut);
exec(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>() {
norm_args.push(hand.exec(expr).await?);
}
}
f.apply(hand, norm_args).await
})
.map(Ok)
.boxed_local()
});
FunRecord { argtyps, fun }
}
/// An Atom representing a partially applied named native function. These
/// partial calls are serialized into the name of the native function and the
@@ -46,23 +77,22 @@ impl SysCtxEntry for FunsCtx {}
pub(crate) struct Fun {
path: Sym,
args: Vec<Expr>,
arity: u8,
fun: Rc<dyn FunCB>,
record: FunRecord,
}
impl Fun {
pub async fn new<I, O, F: ExprFunc<I, O>>(path: Sym, ctx: SysCtx, f: F) -> Self {
let funs: &FunsCtx = ctx.get_or_default();
let mut fung = funs.0.lock().await;
let fun = if let Some(x) = fung.get(&path) {
x.1.clone()
let record = if let Some(record) = fung.get(&path) {
record.clone()
} else {
let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
fung.insert(path.clone(), (F::ARITY, fun.clone()));
fun
let record = process_args(f).await;
fung.insert(path.clone(), record.clone());
record
};
Self { args: vec![], arity: F::ARITY, path, fun }
Self { args: vec![], path, record }
}
pub fn arity(&self) -> u8 { self.arity }
pub fn arity(&self) -> u8 { self.record.argtyps.len() as u8 }
}
impl Atomic for Fun {
type Data = ();
@@ -74,11 +104,10 @@ impl OwnedAtom for Fun {
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.arity.into() {
(self.fun)(new_args).await.to_expr()
if new_args.len() == self.record.argtyps.len() {
(self.record.fun)(new_args).await.to_expr().await
} else {
Self { args: new_args, arity: self.arity, fun: self.fun.clone(), path: self.path.clone() }
.to_expr()
Self { args: new_args, record: self.record.clone(), path: self.path.clone() }.to_expr().await
}
}
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
@@ -89,11 +118,13 @@ impl OwnedAtom for Fun {
async fn deserialize(mut ctx: impl DeserializeCtx, args: Self::Refs) -> Self {
let sys = ctx.sys();
let path = Sym::from_api(ctx.decode().await, sys.i()).await;
let (arity, fun) = sys.get_or_default::<FunsCtx>().0.lock().await.get(&path).unwrap().clone();
Self { args, arity, path, fun }
let record = (sys.get::<FunsCtx>().0.lock().await.get(&path))
.expect("Function missing during deserialization")
.clone();
Self { args, path, record }
}
async fn print<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("{}:{}/{}", self.path, self.args.len(), self.arity).into()
async fn print_atom<'a>(&'a self, _: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
format!("{}:{}/{}", self.path, self.args.len(), self.arity()).into()
}
}
@@ -104,13 +135,11 @@ impl OwnedAtom for Fun {
#[derive(Clone)]
pub struct Lambda {
args: Vec<Expr>,
arity: u8,
fun: Rc<dyn FunCB>,
record: FunRecord,
}
impl Lambda {
pub fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
let fun = Rc::new(move |v| clone!(f; async move { f.apply(v).await }.boxed_local()));
Self { args: vec![], arity: F::ARITY, fun }
pub async fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
Self { args: vec![], record: process_args(f).await }
}
}
impl Atomic for Lambda {
@@ -122,53 +151,59 @@ impl OwnedAtom for Lambda {
async fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
async fn call_ref(&self, arg: Expr) -> GExpr {
let new_args = self.args.iter().cloned().chain([arg]).collect_vec();
if new_args.len() == self.arity.into() {
(self.fun)(new_args).await.to_expr()
if new_args.len() == self.record.argtyps.len() {
(self.record.fun)(new_args).await.to_expr().await
} else {
Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr()
Self { args: new_args, record: self.record.clone() }.to_expr().await
}
}
async fn call(self, arg: Expr) -> GExpr { self.call_ref(arg).await }
}
mod expr_func_derives {
use std::any::TypeId;
use std::sync::OnceLock;
use orchid_base::error::OrcRes;
use super::ExprFunc;
use crate::conv::{ToExpr, TryFromExpr};
use crate::func_atom::Expr;
use crate::func_atom::{ExecHandle, Expr};
use crate::gen_expr::GExpr;
macro_rules! expr_func_derive {
($arity: tt, $($t:ident),*) => {
($($t:ident),*) => {
pastey::paste!{
impl<
$($t: TryFromExpr, )*
$($t: TryFromExpr + 'static, )*
Out: ToExpr,
Func: AsyncFn($($t,)*) -> Out + Clone + Send + Sync + 'static
> ExprFunc<($($t,)*), Out> for Func {
const ARITY: u8 = $arity;
async fn apply(&self, v: Vec<Expr>) -> OrcRes<GExpr> {
assert_eq!(v.len(), Self::ARITY.into(), "Arity mismatch");
fn argtyps() -> &'static [TypeId] {
static STORE: OnceLock<Vec<TypeId>> = OnceLock::new();
&*STORE.get_or_init(|| vec![$(TypeId::of::<$t>()),*])
}
async fn apply<'a>(&self, _: ExecHandle<'a>, v: Vec<Expr>) -> OrcRes<GExpr> {
assert_eq!(v.len(), Self::argtyps().len(), "Arity mismatch");
let [$([< $t:lower >],)*] = v.try_into().unwrap_or_else(|_| panic!("Checked above"));
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_expr())
Ok(self($($t::try_from_expr([< $t:lower >]).await?,)*).await.to_expr().await)
}
}
}
};
}
expr_func_derive!(1, A);
expr_func_derive!(2, A, B);
expr_func_derive!(3, A, B, C);
expr_func_derive!(4, A, B, C, D);
expr_func_derive!(5, A, B, C, D, E);
expr_func_derive!(6, A, B, C, D, E, F);
expr_func_derive!(7, A, B, C, D, E, F, G);
expr_func_derive!(8, A, B, C, D, E, F, G, H);
expr_func_derive!(9, A, B, C, D, E, F, G, H, I);
expr_func_derive!(10, A, B, C, D, E, F, G, H, I, J);
expr_func_derive!(11, A, B, C, D, E, F, G, H, I, J, K);
expr_func_derive!(12, A, B, C, D, E, F, G, H, I, J, K, L);
expr_func_derive!(13, A, B, C, D, E, F, G, H, I, J, K, L, M);
expr_func_derive!(14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
expr_func_derive!(A);
expr_func_derive!(A, B);
expr_func_derive!(A, B, C);
expr_func_derive!(A, B, C, D);
expr_func_derive!(A, B, C, D, E);
// expr_func_derive!(A, B, C, D, E, F);
// expr_func_derive!(A, B, C, D, E, F, G);
// expr_func_derive!(A, B, C, D, E, F, G, H);
// expr_func_derive!(A, B, C, D, E, F, G, H, I);
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J);
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K);
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K, L);
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K, L, M);
// expr_func_derive!(A, B, C, D, E, F, G, H, I, J, K, L, M, N);
}

View File

@@ -10,9 +10,7 @@ use orchid_base::{match_mapping, tl_cache};
use crate::api;
use crate::atom::{AtomFactory, ToAtom};
use crate::conv::{ToExpr, TryFromExpr};
use crate::expr::Expr;
use crate::func_atom::Lambda;
use crate::system::SysCtx;
#[derive(Clone, Debug)]
@@ -35,6 +33,7 @@ impl GExpr {
}
}
}
pub fn at(self, pos: Pos) -> Self { GExpr { pos, kind: self.kind } }
}
impl Format for GExpr {
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
@@ -129,10 +128,3 @@ pub fn call(v: impl IntoIterator<Item = GExpr>) -> GExpr {
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> GExpr {
inherit(GExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}
pub fn with<I: TryFromExpr, O: ToExpr>(
expr: GExpr,
cont: impl AsyncFn(I) -> O + Clone + Send + Sync + 'static,
) -> GExpr {
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
}

View File

@@ -4,31 +4,33 @@ use std::ops::RangeInclusive;
use futures::FutureExt;
use futures::future::LocalBoxFuture;
use orchid_base::error::{OrcErr, OrcRes, mk_err};
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_errv};
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::{Pos, SrcRange};
use orchid_base::name::Sym;
use orchid_base::parse::ParseCtx;
use orchid_base::reqnot::Requester;
use crate::api;
use crate::parser::PTokTree;
use crate::system::SysCtx;
use crate::tree::GenTokTree;
pub async fn err_cascade(i: &Interner) -> OrcErr {
mk_err(
i.i("An error cascading from a recursive call").await,
"This error is a sentinel for the extension library.\
it should not be emitted by the extension.",
[Pos::None.into()],
)
pub async fn ekey_cascade(i: &Interner) -> Tok<String> {
i.i("An error cascading from a recursive call").await
}
pub async fn ekey_not_applicable(i: &Interner) -> Tok<String> {
i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await
}
const MSG_INTERNAL_ERROR: &str = "This error is a sentinel for the extension library.\
it should not be emitted by the extension.";
pub async fn err_cascade(i: &Interner) -> OrcErrv {
mk_errv(ekey_cascade(i).await, MSG_INTERNAL_ERROR, [Pos::None])
}
pub async fn err_not_applicable(i: &Interner) -> OrcErr {
mk_err(
i.i("Pseudo-error to communicate that the current branch in a dispatch doesn't apply").await,
&*err_cascade(i).await.message,
[Pos::None.into()],
)
pub async fn err_not_applicable(i: &Interner) -> OrcErrv {
mk_errv(ekey_not_applicable(i).await, MSG_INTERNAL_ERROR, [Pos::None])
}
pub struct LexContext<'a> {
@@ -36,16 +38,22 @@ pub struct LexContext<'a> {
pub text: &'a Tok<String>,
pub id: api::ParsId,
pub pos: u32,
pub src: Sym,
pub(crate) src: Sym,
pub(crate) rep: &'a Reporter,
}
impl<'a> LexContext<'a> {
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, GenTokTree)> {
pub fn src(&self) -> &Sym { &self.src }
/// This function returns [PTokTree] because it can never return
/// [orchid_base::tree::Token::NewExpr]. You can use
/// [crate::parser::p_tree2gen] to convert this to [crate::tree::GenTokTree]
/// for embedding in the return value.
pub async fn recurse(&self, tail: &'a str) -> OrcRes<(&'a str, PTokTree)> {
let start = self.pos(tail);
let Some(lx) = self.ctx.reqnot().request(api::SubLex { pos: start, id: self.id }).await else {
return Err(err_cascade(self.ctx.i()).await.into());
return Err(err_cascade(self.ctx.i()).await);
};
let tree =
GenTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), &self.src, self.ctx.i()).await;
PTokTree::from_api(&lx.tree, &mut self.ctx.clone(), &mut (), &self.src, self.ctx.i()).await;
Ok((&self.text[lx.pos as usize..], tree))
}
@@ -57,8 +65,10 @@ impl<'a> LexContext<'a> {
pub fn pos_lt(&self, len: impl TryInto<u32, Error: fmt::Debug>, tail: &'a str) -> SrcRange {
SrcRange::new(self.pos(tail) - len.try_into().unwrap()..self.pos(tail), &self.src)
}
pub fn i(&self) -> &Interner { self.ctx.i() }
}
impl ParseCtx for LexContext<'_> {
fn i(&self) -> &Interner { self.ctx.i() }
fn rep(&self) -> &Reporter { self.rep }
}
pub trait Lexer: Send + Sync + Sized + Default + 'static {

View File

@@ -4,6 +4,7 @@ pub mod atom;
pub mod atom_owned;
pub mod atom_thin;
pub mod conv;
pub mod coroutine_exec;
pub mod entrypoint;
pub mod expr;
pub mod func_atom;
@@ -12,6 +13,7 @@ pub mod lexer;
pub mod msg;
pub mod other_system;
pub mod parser;
pub mod reflection;
pub mod system;
pub mod system_ctor;
pub mod tokio;

View File

@@ -2,29 +2,48 @@ use std::marker::PhantomData;
use async_stream::stream;
use futures::future::{LocalBoxFuture, join_all};
use futures::{FutureExt, Stream, StreamExt, pin_mut};
use futures::{FutureExt, Stream, StreamExt};
use itertools::Itertools;
use never::Never;
use orchid_api::ResolveNames;
use orchid_base::error::{OrcRes, Reporter};
use orchid_base::error::{OrcErrv, OrcRes, Reporter};
use orchid_base::id_store::IdStore;
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::SrcRange;
use orchid_base::match_mapping;
use orchid_base::name::Sym;
use orchid_base::parse::{Comment, ParseCtx, Snippet};
use orchid_base::reqnot::{ReqHandlish, Requester};
use orchid_base::tree::{TokTree, Token, ttv_into_api};
use crate::api;
use crate::conv::ToExpr;
use crate::expr::Expr;
use crate::gen_expr::GExpr;
use crate::system::{SysCtx, SysCtxEntry};
use crate::tree::GenTokTree;
use crate::tree::{GenTok, GenTokTree};
pub type PTok = Token<Expr, Never>;
pub type PTokTree = TokTree<Expr, Never>;
pub type PSnippet<'a> = Snippet<'a, Expr, Never>;
pub fn p_tok2gen(tok: PTok) -> GenTok {
match_mapping!(tok, PTok => GenTok {
Comment(s), Name(n), BR, Handle(ex), Bottom(err),
LambdaHead(arg => Box::new(p_tree2gen(*arg))),
NS(n, arg => Box::new(p_tree2gen(*arg))),
S(p, body () p_v2gen),
} {
PTok::NewExpr(never) => match never {}
})
}
pub fn p_tree2gen(tree: PTokTree) -> GenTokTree {
TokTree { tok: p_tok2gen(tree.tok), sr: tree.sr }
}
pub fn p_v2gen(v: impl IntoIterator<Item = PTokTree>) -> Vec<GenTokTree> {
v.into_iter().map(p_tree2gen).collect_vec()
}
pub trait Parser: Send + Sync + Sized + Default + 'static {
const LINE_HEAD: &'static str;
fn parse<'a>(
@@ -93,6 +112,31 @@ pub struct ParsedLine {
pub kind: ParsedLineKind,
}
impl ParsedLine {
pub fn cnst<'a, R: ToExpr + 'static, F: AsyncFnOnce(ConstCtx) -> R + 'static>(
sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>,
exported: bool,
name: Tok<String>,
f: F,
) -> Self {
let cb = Box::new(|ctx| async move { f(ctx).await.to_expr().await }.boxed_local());
let kind = ParsedLineKind::Mem(ParsedMem { name, exported, kind: ParsedMemKind::Const(cb) });
let comments = comments.into_iter().cloned().collect();
ParsedLine { comments, sr: sr.clone(), kind }
}
pub fn module<'a>(
sr: &SrcRange,
comments: impl IntoIterator<Item = &'a Comment>,
exported: bool,
name: &Tok<String>,
use_prelude: bool,
lines: impl IntoIterator<Item = ParsedLine>,
) -> Self {
let mem_kind = ParsedMemKind::Mod { lines: lines.into_iter().collect(), use_prelude };
let line_kind = ParsedLineKind::Mem(ParsedMem { name: name.clone(), exported, kind: mem_kind });
let comments = comments.into_iter().cloned().collect();
ParsedLine { comments, sr: sr.clone(), kind: line_kind }
}
pub async fn into_api(self, ctx: SysCtx, hand: &dyn ReqHandlish) -> api::ParsedLine {
api::ParsedLine {
comments: self.comments.into_iter().map(|c| c.to_api()).collect(),
@@ -142,18 +186,6 @@ pub enum ParsedMemKind {
Mod { lines: Vec<ParsedLine>, use_prelude: bool },
}
impl ParsedMemKind {
pub fn cnst<F: AsyncFnOnce(ConstCtx) -> GExpr + 'static>(f: F) -> Self {
Self::Const(Box::new(|ctx| Box::pin(f(ctx))))
}
pub fn module(lines: impl IntoIterator<Item = ParsedLine>) -> Self {
Self::Mod { lines: lines.into_iter().collect(), use_prelude: true }
}
pub fn clean_module(lines: impl IntoIterator<Item = ParsedLine>) -> Self {
Self::Mod { lines: lines.into_iter().collect(), use_prelude: false }
}
}
/* TODO: how the macro runner uses the multi-stage loader
Since the macro runner actually has to invoke the interpreter,
@@ -169,16 +201,18 @@ postparse / const load links up constants with every macro they can directly inv
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,
constid: api::ParsedConstId,
}
impl ConstCtx {
pub fn names<'a>(
&'a self,
names: impl IntoIterator<Item = &'a Sym> + 'a,
) -> impl Stream<Item = Option<Sym>> + 'a {
pub fn ctx(&self) -> &SysCtx { &self.ctx }
pub fn i(&self) -> &Interner { self.ctx.i() }
pub fn names<'b>(
&'b self,
names: impl IntoIterator<Item = &'b Sym> + 'b,
) -> impl Stream<Item = OrcRes<Sym>> + 'b {
let resolve_names = ResolveNames {
constid: self.constid,
sys: self.ctx.sys_id(),
@@ -187,20 +221,14 @@ impl ConstCtx {
stream! {
for name_opt in self.ctx.reqnot().request(resolve_names).await {
yield match name_opt {
None => None,
Some(name) => Some(Sym::from_api(name, self.ctx.i()).await)
Err(e) => Err(OrcErrv::from_api(&e, self.ctx.i()).await),
Ok(name) => Ok(Sym::from_api(name, self.ctx.i()).await)
}
}
}
}
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [Option<Sym>; N] {
let mut results = [const { None }; N];
let names = self.names(names).enumerate().filter_map(|(i, n)| async move { Some((i, n?)) });
pin_mut!(names);
while let Some((i, name)) = names.next().await {
results[i] = Some(name);
}
results
pub async fn names_n<const N: usize>(&self, names: [&Sym; N]) -> [OrcRes<Sym>; N] {
self.names(names).collect::<Vec<_>>().await.try_into().expect("Lengths must match")
}
}

View File

@@ -0,0 +1,163 @@
use std::cell::OnceCell;
use std::rc::Rc;
use async_std::sync::Mutex;
use futures::FutureExt;
use memo_map::MemoMap;
use orchid_base::interner::Tok;
use orchid_base::name::{NameLike, VPath};
use orchid_base::reqnot::Requester;
use crate::api;
use crate::system::{SysCtx, SysCtxEntry, WeakSysCtx};
pub struct ReflMemData {
// None for inferred steps
public: OnceCell<bool>,
kind: ReflMemKind,
}
#[derive(Clone)]
pub struct ReflMem(Rc<ReflMemData>);
impl ReflMem {
pub fn kind(&self) -> ReflMemKind { self.0.kind.clone() }
}
#[derive(Clone)]
pub enum ReflMemKind {
Const,
Mod(ReflMod),
}
pub struct ReflModData {
inferred: Mutex<bool>,
path: VPath,
ctx: WeakSysCtx,
members: MemoMap<Tok<String>, ReflMem>,
}
#[derive(Clone)]
pub struct ReflMod(Rc<ReflModData>);
impl ReflMod {
fn ctx(&self) -> SysCtx {
self.0.ctx.upgrade().expect("ReflectedModule accessed after context drop")
}
pub fn path(&self) -> &[Tok<String>] { &self.0.path[..] }
pub fn is_root(&self) -> bool { self.0.path.is_empty() }
async fn try_populate(&self) -> Result<(), api::LsModuleError> {
let ctx = self.ctx();
let path_tok = ctx.i().i(&self.0.path[..]).await;
let reply = match ctx.reqnot().request(api::LsModule(ctx.sys_id(), path_tok.to_api())).await {
Err(api::LsModuleError::TreeUnavailable) =>
panic!("Reflected tree accessed outside an interpreter call. This extension is faulty."),
Err(err) => return Err(err),
Ok(details) => details,
};
for (k, v) in reply.members {
let k = ctx.i().ex(k).await;
let mem = match self.0.members.get(&k) {
Some(mem) => mem,
None => {
let path = self.0.path.clone().name_with_suffix(k.clone()).to_sym(ctx.i()).await;
let kind = match v.kind {
api::MemberInfoKind::Constant => ReflMemKind::Const,
api::MemberInfoKind::Module =>
ReflMemKind::Mod(default_module(&ctx, VPath::new(path.segs()))),
};
self.0.members.get_or_insert(&k, || default_member(self.is_root(), kind))
},
};
let _ = mem.0.public.set(v.public);
}
Ok(())
}
pub async fn get_child(&self, key: &Tok<String>) -> Option<ReflMem> {
let inferred_g = self.0.inferred.lock().await;
if let Some(mem) = self.0.members.get(key) {
return Some(mem.clone());
}
if !*inferred_g {
return None;
}
match self.try_populate().await {
Err(api::LsModuleError::InvalidPath) =>
panic!("Path became invalid since module was created"),
Err(api::LsModuleError::IsConstant) =>
panic!("Path previously contained a module but now contains a constant"),
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
Ok(()) => (),
}
self.0.members.get(key).cloned()
}
pub async fn get_by_path(&self, path: &[Tok<String>]) -> Result<ReflMem, InvalidPathError> {
let ctx = self.ctx();
let (next, tail) = path.split_first().expect("Attempted to walk by empty path");
let inferred_g = self.0.inferred.lock().await;
if let Some(next) = self.0.members.get(next) {
return if tail.is_empty() {
Ok(next.clone())
} else {
match next.kind() {
ReflMemKind::Const => Err(InvalidPathError { keep_ancestry: true }),
ReflMemKind::Mod(m) => m.get_by_path(tail).boxed_local().await,
}
};
}
if !*inferred_g {
return Err(InvalidPathError { keep_ancestry: true });
}
let candidate = default_module(&ctx, self.0.path.clone().suffix([next.clone()]));
if tail.is_empty() {
return match candidate.try_populate().await {
Ok(()) => {
let tgt_mem = default_member(self.is_root(), ReflMemKind::Mod(candidate));
self.0.members.insert(next.clone(), tgt_mem.clone());
Ok(tgt_mem)
},
Err(api::LsModuleError::InvalidPath) => Err(InvalidPathError { keep_ancestry: false }),
Err(api::LsModuleError::IsConstant) => {
let const_mem = default_member(self.is_root(), ReflMemKind::Const);
self.0.members.insert(next.clone(), const_mem);
Err(InvalidPathError { keep_ancestry: true })
},
Err(api::LsModuleError::TreeUnavailable) => unreachable!(),
};
}
match candidate.get_by_path(tail).boxed_local().await {
e @ Err(InvalidPathError { keep_ancestry: false }) => e,
res @ Err(InvalidPathError { keep_ancestry: true }) | res @ Ok(_) => {
let tgt_mem = default_member(self.is_root(), ReflMemKind::Mod(candidate));
self.0.members.insert(next.clone(), tgt_mem);
res
},
}
}
}
struct ReflRoot(ReflMod);
impl SysCtxEntry for ReflRoot {}
pub struct InvalidPathError {
keep_ancestry: bool,
}
fn default_module(ctx: &SysCtx, path: VPath) -> ReflMod {
ReflMod(Rc::new(ReflModData {
ctx: ctx.downgrade(),
inferred: Mutex::new(true),
path,
members: MemoMap::new(),
}))
}
fn default_member(is_root: bool, kind: ReflMemKind) -> ReflMem {
ReflMem(Rc::new(ReflMemData {
public: if is_root { true.into() } else { OnceCell::new() },
kind,
}))
}
fn get_root(ctx: &SysCtx) -> &ReflRoot {
ctx.get_or_insert(|| ReflRoot(default_module(ctx, VPath::new([]))))
}
pub fn refl(ctx: &SysCtx) -> ReflMod { get_root(ctx).0.clone() }

View File

@@ -3,7 +3,7 @@ use std::fmt;
use std::future::Future;
use std::num::NonZero;
use std::pin::Pin;
use std::rc::Rc;
use std::rc::{Rc, Weak};
use futures::future::LocalBoxFuture;
use memo_map::MemoMap;
@@ -36,7 +36,7 @@ pub trait DynSystemCard: Send + Sync + 'static {
fn name(&self) -> &'static str;
/// Atoms explicitly defined by the system card. Do not rely on this for
/// querying atoms as it doesn't include the general atom types
fn atoms(&self) -> BoxedIter<Option<Box<dyn AtomDynfo>>>;
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomDynfo>>>;
}
/// Atoms supported by this package which may appear in all extensions.
@@ -77,7 +77,9 @@ pub async fn resolv_atom(
impl<T: SystemCard> DynSystemCard for T {
fn name(&self) -> &'static str { T::Ctor::NAME }
fn atoms(&self) -> BoxedIter<Option<Box<dyn AtomDynfo>>> { Box::new(Self::atoms().into_iter()) }
fn atoms(&'_ self) -> BoxedIter<'_, Option<Box<dyn AtomDynfo>>> {
Box::new(Self::atoms().into_iter())
}
}
/// System as defined by author
@@ -91,7 +93,7 @@ pub trait System: Send + Sync + SystemCard + 'static {
pub trait DynSystem: Send + Sync + DynSystemCard + 'static {
fn dyn_prelude<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec<Sym>>;
fn dyn_env(&self) -> Vec<GenMember>;
fn dyn_env(&'_ self) -> Vec<GenMember>;
fn dyn_lexers(&self) -> Vec<LexerObj>;
fn dyn_parsers(&self) -> Vec<ParserObj>;
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>>;
@@ -102,7 +104,7 @@ impl<T: System> DynSystem for T {
fn dyn_prelude<'a>(&'a self, i: &'a Interner) -> LocalBoxFuture<'a, Vec<Sym>> {
Box::pin(Self::prelude(i))
}
fn dyn_env(&self) -> Vec<GenMember> { Self::env() }
fn dyn_env(&'_ self) -> Vec<GenMember> { Self::env() }
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
fn dyn_parsers(&self) -> Vec<ParserObj> { Self::parsers() }
fn dyn_request<'a>(&self, hand: ExtReq<'a>, req: Vec<u8>) -> LocalBoxFuture<'a, Receipt<'a>> {
@@ -132,7 +134,13 @@ where A: AtomicFeatures {
}
let val = dynfo.decode(AtomCtx(data, foreign.atom.drop, ctx)).await;
let value = *val.downcast::<A::Data>().expect("atom decode returned wrong type");
Ok(TypAtom { value, data: foreign })
Ok(TypAtom { value, untyped: foreign })
}
#[derive(Clone)]
pub struct WeakSysCtx(Weak<MemoMap<TypeId, Box<dyn Any>>>);
impl WeakSysCtx {
pub fn upgrade(&self) -> Option<SysCtx> { Some(SysCtx(self.0.upgrade()?)) }
}
#[derive(Clone)]
@@ -150,6 +158,7 @@ impl SysCtx {
this.add(id).add(i).add(reqnot).add(spawner).add(logger).add(cted);
this
}
pub fn downgrade(&self) -> WeakSysCtx { WeakSysCtx(Rc::downgrade(&self.0)) }
pub fn add<T: SysCtxEntry>(&self, t: T) -> &Self {
assert!(self.0.insert(TypeId::of::<T>(), Box::new(t)), "Key already exists");
self

View File

@@ -65,17 +65,24 @@ impl TokenVariant<api::ExprTicket> for Expr {
}
}
pub fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_expr()) }
pub fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(sym_ref(path)) }
pub async fn x_tok(x: impl ToExpr) -> GenTok { GenTok::NewExpr(x.to_expr().await) }
pub async fn ref_tok(path: Sym) -> GenTok { GenTok::NewExpr(sym_ref(path)) }
pub fn cnst(public: bool, name: &str, value: impl ToExpr) -> Vec<GenMember> {
pub fn lazy(
public: bool,
name: &str,
cb: impl AsyncFnOnce(Sym, SysCtx) -> MemKind + Clone + 'static,
) -> Vec<GenMember> {
vec![GenMember {
name: name.to_string(),
kind: MemKind::Const(value.to_expr()),
kind: MemKind::Lazy(LazyMemberFactory::new(cb)),
comments: vec![],
public,
}]
}
pub fn cnst(public: bool, name: &str, value: impl ToExpr + Clone + 'static) -> Vec<GenMember> {
lazy(public, name, async |_, _| MemKind::Const(value.to_expr().await))
}
pub fn module(
public: bool,
name: &str,
@@ -89,20 +96,19 @@ 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));
fn build_lambdas(fun: Fun, i: u64) -> GExpr {
if i < fun.arity().into() {
return lambda(i, [build_lambdas(fun, i + 1)]);
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 arity = fun.arity();
seq(
(0..arity)
.map(|i| arg(i as u64))
.chain([call([fun.to_expr()].into_iter().chain((0..arity).map(|i| arg(i as u64))))]),
)
}
});
});
vec![GenMember { name: name.to_string(), kind: MemKind::Lazy(fac), public, comments: vec![] }]
}
pub fn prefix(path: &str, items: impl IntoIterator<Item = Vec<GenMember>>) -> Vec<GenMember> {