Macro processing factored into Orchid functions

This commit is contained in:
2025-01-08 01:34:40 +00:00
parent e780969c6c
commit 7cdfe7e3c4
36 changed files with 631 additions and 289 deletions

View File

@@ -77,7 +77,7 @@ pub struct ForeignAtom<'a> {
pub atom: api::Atom,
pub pos: Pos,
}
impl<'a> ForeignAtom<'a> {
impl ForeignAtom<'_> {
pub fn oex_opt(self) -> Option<Expr> {
let (handle, pos) = (self.expr.as_ref()?.clone(), self.pos.clone());
let data = ExprData { pos, kind: ExprKind::Atom(ForeignAtom { _life: PhantomData, ..self }) };
@@ -98,15 +98,15 @@ impl ForeignAtom<'static> {
Some(M::Response::decode(&mut &rep[..]))
}
}
impl<'a> fmt::Display for ForeignAtom<'a> {
impl fmt::Display for ForeignAtom<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}::{:?}", if self.expr.is_some() { "Clause" } else { "Tok" }, self.atom)
}
}
impl<'a> fmt::Debug for ForeignAtom<'a> {
impl fmt::Debug for ForeignAtom<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ForeignAtom({self})") }
}
impl<'a> AtomRepr for ForeignAtom<'a> {
impl AtomRepr for ForeignAtom<'_> {
type Ctx = SysCtx;
fn from_api(atom: &api::Atom, pos: Pos, ctx: &mut Self::Ctx) -> Self {
Self { atom: atom.clone(), _life: PhantomData, ctx: ctx.clone(), expr: None, pos }
@@ -197,7 +197,7 @@ impl<A: AtomicFeatures> TypAtom<'static, A> {
}
}
}
impl<'a, A: AtomicFeatures> TypAtom<'a, A> {
impl<A: AtomicFeatures> TypAtom<'_, A> {
pub fn request<M: AtomMethod>(&self, req: M) -> M::Response where A: Supports<M> {
M::Response::decode(
&mut &self.data.ctx.reqnot.request(api::Fwd(
@@ -208,7 +208,7 @@ impl<'a, A: AtomicFeatures> TypAtom<'a, A> {
)
}
}
impl<'a, A: AtomicFeatures> Deref for TypAtom<'a, A> {
impl<A: AtomicFeatures> Deref for TypAtom<'_, A> {
type Target = A::Data;
fn deref(&self) -> &Self::Target { &self.value }
}
@@ -224,7 +224,7 @@ pub trait AtomDynfo: Send + Sync + 'static {
fn print(&self, ctx: AtomCtx<'_>) -> String;
fn handle_req(&self, ctx: AtomCtx<'_>, key: Sym, req: &mut dyn Read, rep: &mut dyn Write) -> bool;
fn command(&self, ctx: AtomCtx<'_>) -> OrcRes<Option<Expr>>;
fn serialize(&self, ctx: AtomCtx<'_>, write: &mut dyn Write) -> Vec<api::ExprTicket>;
fn serialize(&self, ctx: AtomCtx<'_>, write: &mut dyn Write) -> Option<Vec<api::ExprTicket>>;
fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> api::Atom;
fn drop(&self, ctx: AtomCtx<'_>);
}

View File

@@ -1,19 +1,21 @@
use std::any::{type_name, Any, TypeId};
use std::any::{Any, TypeId, type_name};
use std::borrow::Cow;
use std::io::{Read, Write};
use std::sync::Arc;
use itertools::Itertools;
use orchid_api_traits::{enc_vec, Decode, Encode};
use never::Never;
use orchid_api_traits::{Decode, Encode, enc_vec};
use orchid_base::error::OrcRes;
use orchid_base::id_store::{IdRecord, IdStore};
use orchid_base::name::Sym;
use crate::api;
use crate::atom::{
err_not_callable, err_not_command, get_info, AtomCard, AtomCtx, AtomDynfo, AtomFactory, MethodSet, Atomic, AtomicFeaturesImpl, AtomicVariant,
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
err_not_callable, err_not_command, get_info,
};
use crate::expr::{bot, Expr, ExprHandle};
use crate::expr::{Expr, ExprHandle, bot};
use crate::system::SysCtx;
pub struct OwnedVariant;
@@ -28,9 +30,7 @@ impl<A: OwnedAtom + Atomic<Variant = OwnedVariant>> AtomicFeaturesImpl<OwnedVari
api::Atom { drop: Some(api::AtomId(rec.id())), data, owner: ctx.id }
})
}
fn _info() -> Self::_Info {
OwnedAtomDynfo(A::reg_reqs())
}
fn _info() -> Self::_Info { OwnedAtomDynfo(A::reg_reqs()) }
type _Info = OwnedAtomDynfo<A>;
}
@@ -54,7 +54,13 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
fn call_ref(&self, AtomCtx(_, id, ctx): AtomCtx, arg: api::ExprTicket) -> Expr {
with_atom(id.unwrap(), |a| a.dyn_call_ref(ctx, arg))
}
fn handle_req(&self, AtomCtx(_, id, ctx): AtomCtx, key: Sym, req: &mut dyn Read, rep: &mut dyn Write) -> bool {
fn handle_req(
&self,
AtomCtx(_, id, ctx): AtomCtx,
key: Sym,
req: &mut dyn Read,
rep: &mut dyn Write,
) -> bool {
with_atom(id.unwrap(), |a| {
self.0.dispatch(a.as_any_ref().downcast_ref().unwrap(), ctx, key, req, rep)
})
@@ -69,13 +75,11 @@ impl<T: OwnedAtom> AtomDynfo for OwnedAtomDynfo<T> {
&self,
AtomCtx(_, id, ctx): AtomCtx<'_>,
write: &mut dyn Write,
) -> Vec<api::ExprTicket> {
) -> Option<Vec<api::ExprTicket>> {
let id = id.unwrap();
id.encode(write);
with_atom(id, |a| a.dyn_serialize(ctx, write))
.into_iter()
.map(|t| t.handle.unwrap().tk)
.collect_vec()
.map(|v| v.into_iter().map(|t| t.handle.unwrap().tk).collect_vec())
}
fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> orchid_api::Atom {
let refs = refs.iter().map(|tk| Expr::new(Arc::new(ExprHandle::from_args(ctx.clone(), *tk))));
@@ -97,7 +101,7 @@ pub trait DeserializeCtx: Sized {
}
struct DeserCtxImpl<'a>(&'a [u8], &'a SysCtx);
impl<'a> DeserializeCtx for DeserCtxImpl<'a> {
impl DeserializeCtx for DeserCtxImpl<'_> {
fn read<T: Decode>(&mut self) -> T { T::decode(&mut self.0) }
fn is_empty(&self) -> bool { self.0.is_empty() }
fn sys(&self) -> SysCtx { self.1.clone() }
@@ -108,6 +112,13 @@ pub trait RefSet {
fn to_vec(self) -> Vec<Expr>;
}
static E_NON_SER: &str = "Never is a stand-in refset for non-serializable atoms";
impl RefSet for Never {
fn from_iter<I>(_: I) -> Self { panic!("{E_NON_SER}") }
fn to_vec(self) -> Vec<Expr> { panic!("{E_NON_SER}") }
}
impl RefSet for () {
fn to_vec(self) -> Vec<Expr> { Vec::new() }
fn from_iter<I: Iterator<Item = Expr> + ExactSizeIterator>(refs: I) -> Self {
@@ -130,6 +141,16 @@ impl<const N: usize> RefSet for [Expr; N] {
/// Atoms that have a [Drop]
pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Send + Sync + Any + Clone + 'static {
/// If serializable, the collection that best stores subexpression references
/// for this atom.
///
/// - `()` for no subexppressions,
/// - `[Expr; N]` for a static number of subexpressions
/// - `Vec<Expr>` for a variable number of subexpressions
/// - `Never` if not serializable
///
/// If this isn't `Never`, you must override the default, panicking
/// `serialize` and `deserialize` implementation
type Refs: RefSet;
fn val(&self) -> Cow<'_, Self::Data>;
#[allow(unused_variables)]
@@ -147,8 +168,21 @@ pub trait OwnedAtom: Atomic<Variant = OwnedVariant> + Send + Sync + Any + Clone
#[allow(unused_variables)]
fn print(&self, ctx: SysCtx) -> String { format!("OwnedAtom({})", type_name::<Self>()) }
#[allow(unused_variables)]
fn serialize(&self, ctx: SysCtx, write: &mut (impl Write + ?Sized)) -> Self::Refs;
fn deserialize(ctx: impl DeserializeCtx, refs: Self::Refs) -> Self;
fn serialize(&self, ctx: SysCtx, write: &mut (impl Write + ?Sized)) -> Self::Refs {
assert!(
TypeId::of::<Self::Refs>() != TypeId::of::<Never>(),
"The extension scaffold is broken, this function should never be called on Never Refs"
);
panic!("Either implement serialize or set Refs to Never for {}", type_name::<Self>())
}
#[allow(unused_variables)]
fn deserialize(ctx: impl DeserializeCtx, refs: Self::Refs) -> Self {
assert!(
TypeId::of::<Self::Refs>() != TypeId::of::<Never>(),
"The extension scaffold is broken, this function should never be called on Never Refs"
);
panic!("Either implement deserialize or set Refs to Never for {}", type_name::<Self>())
}
}
pub trait DynOwnedAtom: Send + Sync + 'static {
fn atom_tid(&self) -> TypeId;
@@ -159,7 +193,7 @@ pub trait DynOwnedAtom: Send + Sync + 'static {
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> OrcRes<Option<Expr>>;
fn dyn_free(self: Box<Self>, ctx: SysCtx);
fn dyn_print(&self, ctx: SysCtx) -> String;
fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Vec<Expr>;
fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Option<Vec<Expr>>;
}
impl<T: OwnedAtom> DynOwnedAtom for T {
fn atom_tid(&self) -> TypeId { TypeId::of::<T>() }
@@ -174,8 +208,9 @@ impl<T: OwnedAtom> DynOwnedAtom for T {
fn dyn_command(self: Box<Self>, ctx: SysCtx) -> OrcRes<Option<Expr>> { self.command(ctx) }
fn dyn_free(self: Box<Self>, ctx: SysCtx) { self.free(ctx) }
fn dyn_print(&self, ctx: SysCtx) -> String { self.print(ctx) }
fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Vec<Expr> {
self.serialize(ctx, sink).to_vec()
fn dyn_serialize(&self, ctx: SysCtx, sink: &mut dyn Write) -> Option<Vec<Expr>> {
(TypeId::of::<Never>() != TypeId::of::<<Self as OwnedAtom>::Refs>())
.then(|| self.serialize(ctx, sink).to_vec())
}
}

View File

@@ -1,15 +1,16 @@
use std::any::{type_name, Any, TypeId};
use std::any::{Any, TypeId, type_name};
use std::io::Write;
use orchid_api_traits::{enc_vec, Coding};
use orchid_api_traits::{Coding, enc_vec};
use orchid_base::error::OrcRes;
use orchid_base::name::Sym;
use crate::api;
use crate::atom::{
err_not_callable, err_not_command, get_info, AtomCard, AtomCtx, AtomDynfo, AtomFactory, MethodSet, Atomic, AtomicFeaturesImpl, AtomicVariant
AtomCard, AtomCtx, AtomDynfo, AtomFactory, Atomic, AtomicFeaturesImpl, AtomicVariant, MethodSet,
err_not_callable, err_not_command, get_info,
};
use crate::expr::{bot, Expr, ExprHandle};
use crate::expr::{Expr, ExprHandle, bot};
use crate::system::SysCtx;
pub struct ThinVariant;
@@ -23,9 +24,7 @@ impl<A: ThinAtom + Atomic<Variant = ThinVariant>> AtomicFeaturesImpl<ThinVariant
api::Atom { drop: None, data: buf, owner: ctx.id }
})
}
fn _info() -> Self::_Info {
ThinAtomDynfo(Self::reg_reqs())
}
fn _info() -> Self::_Info { ThinAtomDynfo(Self::reg_reqs()) }
type _Info = ThinAtomDynfo<Self>;
}
@@ -55,9 +54,9 @@ impl<T: ThinAtom> AtomDynfo for ThinAtomDynfo<T> {
fn command(&self, AtomCtx(buf, _, ctx): AtomCtx<'_>) -> OrcRes<Option<Expr>> {
T::decode(&mut &buf[..]).command(ctx)
}
fn serialize(&self, actx: AtomCtx<'_>, write: &mut dyn Write) -> Vec<api::ExprTicket> {
fn serialize(&self, actx: AtomCtx<'_>, write: &mut dyn Write) -> Option<Vec<api::ExprTicket>> {
T::decode(&mut &actx.0[..]).encode(write);
Vec::new()
Some(Vec::new())
}
fn deserialize(&self, ctx: SysCtx, data: &[u8], refs: &[api::ExprTicket]) -> api::Atom {
assert!(refs.is_empty(), "Refs found when deserializing thin atom");

View File

@@ -28,7 +28,7 @@ fn err_type(pos: Pos) -> OrcErr {
mk_err(intern!(str: "Type error"), "The atom is a different type than expected", [pos.into()])
}
impl<'a, A: AtomicFeatures> TryFromExpr for TypAtom<'a, A> {
impl<A: AtomicFeatures> TryFromExpr for TypAtom<'_, A> {
fn try_from_expr(expr: Expr) -> OrcRes<Self> {
(expr.foreign_atom())
.map_err(|ex| err_not_atom(ex.pos.clone()).into())

View File

@@ -232,8 +232,8 @@ fn extension_main_logic(data: ExtensionData) {
match &atom_req {
api::AtomReq::SerializeAtom(ser) => {
let mut buf = enc_vec(&id);
let refs = nfo.serialize(actx, &mut buf);
hand.handle(ser, &(buf, refs))
let refs_opt = nfo.serialize(actx, &mut buf);
hand.handle(ser, &refs_opt.map(|refs| (buf, refs)))
}
api::AtomReq::AtomPrint(print@api::AtomPrint(_)) =>
hand.handle(print, &nfo.print(actx)),
@@ -299,6 +299,6 @@ fn extension_main_logic(data: ExtensionData) {
init_replica(rn.clone().map());
while !exiting.load(Ordering::Relaxed) {
let rcvd = recv_parent_msg().unwrap();
rn.receive(rcvd)
rn.receive(&rcvd)
}
}

View File

@@ -1,8 +1,9 @@
use std::fmt;
use std::ops::Deref;
use std::sync::{Arc, OnceLock};
use std::{backtrace, fmt};
use derive_destructure::destructure;
use orchid_base::clone;
use orchid_base::error::{OrcErr, OrcErrv};
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
@@ -10,6 +11,8 @@ use orchid_base::reqnot::Requester;
use crate::api;
use crate::atom::{AtomFactory, ForeignAtom, ToAtom};
use crate::conv::{ToExpr, TryFromExpr};
use crate::func_atom::Lambda;
use crate::system::SysCtx;
#[derive(destructure)]
@@ -147,7 +150,7 @@ pub fn seq(ops: impl IntoIterator<Item = Expr>) -> Expr {
recur(ops.into_iter()).expect("Empty list provided to seq!")
}
pub fn arg(n: u64) -> ExprKind { ExprKind::Arg(n) }
pub fn arg(n: u64) -> Expr { inherit(ExprKind::Arg(n)) }
pub fn lambda(n: u64, b: impl IntoIterator<Item = Expr>) -> Expr {
inherit(ExprKind::Lambda(n, Box::new(call(b))))
@@ -162,3 +165,10 @@ pub fn call(v: impl IntoIterator<Item = Expr>) -> Expr {
pub fn bot(ev: impl IntoIterator<Item = OrcErr>) -> Expr {
inherit(ExprKind::Bottom(OrcErrv::new(ev).unwrap()))
}
pub fn with<I: TryFromExpr, O: ToExpr>(
expr: Expr,
cont: impl Fn(I) -> O + Clone + Send + Sync + 'static,
) -> Expr {
call([lambda(0, [seq([arg(0), call([Lambda::new(cont).to_expr(), arg(0)])])]), expr])
}

View File

@@ -5,13 +5,14 @@ use std::sync::{Arc, Mutex};
use itertools::Itertools;
use lazy_static::lazy_static;
use never::Never;
use orchid_api_traits::Encode;
use orchid_base::error::OrcRes;
use orchid_base::interner::Tok;
use orchid_base::name::Sym;
use trait_set::trait_set;
use crate::atom::{MethodSet, Atomic};
use crate::atom::{Atomic, MethodSet};
use crate::atom_owned::{DeserializeCtx, OwnedAtom, OwnedVariant};
use crate::conv::ToExpr;
use crate::expr::{Expr, ExprHandle};
@@ -30,6 +31,11 @@ lazy_static! {
static ref FUNS: Mutex<HashMap<Sym, (u8, Arc<dyn FunCB>)>> = Mutex::default();
}
/// An Atom representing a partially applied named native function. These
/// partial calls are serialized into the name of the native function and the
/// argument list.
///
/// See [Lambda] for the non-serializable variant
#[derive(Clone)]
pub(crate) struct Fun {
path: Sym,
@@ -79,6 +85,40 @@ impl OwnedAtom for Fun {
}
}
/// An Atom representing a partially applied native lambda. These are not serializable.
///
/// See [Fun] for the serializable variant
#[derive(Clone)]
pub struct Lambda {
args: Vec<Expr>,
arity: u8,
fun: Arc<dyn FunCB>,
}
impl Lambda {
pub fn new<I, O, F: ExprFunc<I, O>>(f: F) -> Self {
let fun = Arc::new(move |v| f.apply(v));
Self { args: vec![], arity: F::ARITY, fun }
}
}
impl Atomic for Lambda {
type Data = ();
type Variant = OwnedVariant;
fn reg_reqs() -> MethodSet<Self> { MethodSet::new() }
}
impl OwnedAtom for Lambda {
type Refs = Never;
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
fn call_ref(&self, arg: ExprHandle) -> Expr {
let new_args = self.args.iter().cloned().chain([Expr::new(Arc::new(arg))]).collect_vec();
if new_args.len() == self.arity.into() {
(self.fun)(new_args).to_expr()
} else {
Self { args: new_args, arity: self.arity, fun: self.fun.clone() }.to_expr()
}
}
fn call(self, arg: ExprHandle) -> Expr { self.call_ref(arg) }
}
mod expr_func_derives {
use orchid_base::error::OrcRes;

View File

@@ -5,7 +5,6 @@ pub mod atom_owned;
pub mod atom_thin;
pub mod conv;
pub mod entrypoint;
// pub mod error;
pub mod expr;
pub mod fs;
pub mod func_atom;

View File

@@ -13,6 +13,7 @@ use crate::api;
use crate::atom::{get_info, AtomCtx, AtomDynfo, AtomicFeatures, ForeignAtom, TypAtom};
use crate::entrypoint::ExtReq;
use crate::fs::DeclFs;
use crate::func_atom::Fun;
// use crate::fun::Fun;
use crate::lexer::LexerObj;
use crate::parser::ParserObj;
@@ -37,7 +38,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::INFO)*/].into_iter()
[Some(Fun::dynfo())].into_iter()
}
pub fn atom_info_for(

View File

@@ -1,10 +1,10 @@
use std::num::NonZero;
use std::ops::Range;
use dyn_clone::{clone_box, DynClone};
use dyn_clone::{DynClone, clone_box};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_base::interner::{intern, Tok};
use orchid_base::interner::{Tok, intern};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::parse::Comment;
@@ -15,7 +15,7 @@ use trait_set::trait_set;
use crate::api;
use crate::atom::{AtomFactory, ForeignAtom};
use crate::conv::ToExpr;
use crate::conv::{ToExpr, TryFromExpr};
use crate::entrypoint::MemberRecord;
use crate::expr::Expr;
use crate::func_atom::{ExprFunc, Fun};
@@ -48,8 +48,8 @@ impl GenItem {
GenItemKind::Import(cn) => api::ItemKind::Import(cn.tok().to_api()),
GenItemKind::Macro(prio, rules) => api::ItemKind::Macro(api::MacroBlock {
priority: prio,
rules: rules.into_iter().map(|r| r.to_api() ).collect_vec(),
})
rules: rules.into_iter().map(|r| r.to_api()).collect_vec(),
}),
};
let comments = self.comments.into_iter().map(|c| c.to_api()).collect_vec();
api::Item { location: self.pos.to_api(), comments, kind }