forked from Orchid/orchid
very elegant extension API and parts of it used in std as POC
This commit is contained in:
@@ -8,13 +8,17 @@ edition = "2021"
|
||||
[dependencies]
|
||||
ahash = "0.8.11"
|
||||
derive_destructure = "1.0.0"
|
||||
dyn-clone = "1.0.17"
|
||||
hashbrown = "0.14.5"
|
||||
itertools = "0.12.1"
|
||||
itertools = "0.13.0"
|
||||
konst = "0.3.9"
|
||||
never = "0.1.0"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-api-derive = { version = "0.1.0", path = "../orchid-api-derive" }
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
ordered-float = "4.2.0"
|
||||
paste = "1.0.15"
|
||||
substack = "1.1.0"
|
||||
trait-set = "0.3.0"
|
||||
typeid = "1.0.0"
|
||||
|
||||
@@ -1,44 +1,43 @@
|
||||
use std::any::{type_name, Any};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
use std::io::{Read, Write};
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use derive_destructure::destructure;
|
||||
use orchid_api::atom::{Atom, Fwd};
|
||||
use orchid_api::expr::{ExprTicket, Release};
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use orchid_api::atom::{Atom, Fwd, LocalAtom};
|
||||
use orchid_api::expr::ExprTicket;
|
||||
use orchid_api::system::SysId;
|
||||
use orchid_api_traits::{Coding, Decode, Encode, Request};
|
||||
use orchid_base::id_store::{IdRecord, IdStore};
|
||||
use orchid_base::reqnot::Requester as _;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::reqnot::Requester;
|
||||
use trait_set::trait_set;
|
||||
use typeid::ConstTypeId;
|
||||
|
||||
use crate::expr::GenClause;
|
||||
use crate::other_system::SystemHandle;
|
||||
use crate::system::{DynSystemCard, SystemCard};
|
||||
use crate::expr::{bot, ExprHandle, GenClause};
|
||||
use crate::system::{atom_info_for, DynSystem, DynSystemCard, SysCtx};
|
||||
|
||||
pub trait AtomCard: 'static + Sized {
|
||||
type Owner: SystemCard;
|
||||
// type Owner: SystemCard;
|
||||
type Data: Clone + Coding + Sized;
|
||||
type Req: Coding;
|
||||
}
|
||||
|
||||
pub fn get_info<A: AtomCard>(sys: &(impl DynSystemCard + ?Sized)) -> (usize, &AtomInfo) {
|
||||
sys.atom_info_for(ConstTypeId::of::<A>()).unwrap_or_else(|| {
|
||||
pub fn get_info<A: AtomCard>(sys: &(impl DynSystemCard + ?Sized)) -> (u64, &AtomInfo) {
|
||||
atom_info_for(sys, ConstTypeId::of::<A>()).unwrap_or_else(|| {
|
||||
panic!("Atom {} not associated with system {}", type_name::<A>(), sys.name())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encode_atom_nodrop<A: AtomCard>(
|
||||
sys_id: SysId,
|
||||
sys: &(impl DynSystemCard + ?Sized),
|
||||
data: &A::Data,
|
||||
) -> Atom {
|
||||
let (info_pos, _) = get_info::<A>(sys);
|
||||
let mut buf = (info_pos as u64).enc_vec();
|
||||
) -> LocalAtom {
|
||||
let mut buf = get_info::<A>(sys).0.enc_vec();
|
||||
data.encode(&mut buf);
|
||||
Atom { owner: sys_id, drop: false, data: buf }
|
||||
LocalAtom { drop: false, data: buf }
|
||||
}
|
||||
|
||||
pub fn encode_atom_drop<A: AtomCard>(
|
||||
@@ -47,8 +46,7 @@ pub fn encode_atom_drop<A: AtomCard>(
|
||||
atom_id: u64,
|
||||
data: &A::Data,
|
||||
) -> Atom {
|
||||
let (info_pos, _) = get_info::<A>(sys);
|
||||
let mut buf = (info_pos as u64).enc_vec();
|
||||
let mut buf = get_info::<A>(sys).0.enc_vec();
|
||||
atom_id.encode(&mut buf);
|
||||
data.encode(&mut buf);
|
||||
Atom { owner: sys_id, drop: true, data: buf }
|
||||
@@ -56,119 +54,175 @@ pub fn encode_atom_drop<A: AtomCard>(
|
||||
|
||||
pub fn decode_atom<A: AtomCard>(
|
||||
sys: &(impl DynSystemCard + ?Sized),
|
||||
Atom { data, drop, owner: _ }: &Atom,
|
||||
Atom { data, drop: _, owner: _ }: &Atom,
|
||||
) -> Option<A::Data> {
|
||||
let (info_pos, info) = get_info::<A>(sys);
|
||||
let mut data = &data[..];
|
||||
if u64::decode(&mut data) != info_pos as u64 {
|
||||
if u64::decode(&mut data) != info_pos {
|
||||
return None;
|
||||
}
|
||||
let val = (info.decode)(data);
|
||||
Some(*val.downcast().expect("The type-id checked out, the decode should've worked"))
|
||||
}
|
||||
|
||||
#[derive(destructure)]
|
||||
pub struct ForeignAtom<A: AtomCard> {
|
||||
pub(crate) sys: SystemHandle<A::Owner>,
|
||||
pub(crate) ticket: ExprTicket,
|
||||
pub(crate) api: Atom,
|
||||
pub(crate) value: A::Data,
|
||||
#[derive(Clone)]
|
||||
pub struct ForeignAtom {
|
||||
pub expr: ExprHandle,
|
||||
pub atom: Atom,
|
||||
pub position: Pos,
|
||||
}
|
||||
impl<A: AtomCard> ForeignAtom<A> {
|
||||
/// Unpack the object, returning the held atom and expr ticket. This is in
|
||||
/// contrast to dropping the atom which releases the expr ticket.
|
||||
pub fn unpack(self) -> (A::Data, ExprTicket, Atom) {
|
||||
let (_, ticket, api, value) = self.destructure();
|
||||
(value, ticket, api)
|
||||
}
|
||||
pub fn ticket(&self) -> ExprTicket { self.ticket }
|
||||
impl ForeignAtom {}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TypAtom<A: AtomCard> {
|
||||
pub data: ForeignAtom,
|
||||
pub value: A::Data,
|
||||
}
|
||||
impl<A: AtomCard> TypAtom<A> {
|
||||
pub fn request<R: Coding + Into<A::Req> + Request>(&self, req: R) -> R::Response {
|
||||
R::Response::decode(&mut &self.sys.reqnot.request(Fwd(self.api.clone(), req.enc_vec()))[..])
|
||||
R::Response::decode(
|
||||
&mut &self.data.expr.ctx.reqnot.request(Fwd(self.data.atom.clone(), req.enc_vec()))[..],
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<A: AtomCard> Deref for ForeignAtom<A> {
|
||||
impl<A: AtomCard> Deref for TypAtom<A> {
|
||||
type Target = A::Data;
|
||||
fn deref(&self) -> &Self::Target { &self.value }
|
||||
}
|
||||
impl<A: AtomCard> Drop for ForeignAtom<A> {
|
||||
fn drop(&mut self) { self.sys.reqnot.notify(Release(self.sys.id(), self.ticket)) }
|
||||
}
|
||||
|
||||
pub struct AtomInfo {
|
||||
pub tid: ConstTypeId,
|
||||
pub decode: fn(&[u8]) -> Box<dyn Any>,
|
||||
pub call: fn(&[u8], ExprTicket) -> GenClause,
|
||||
pub call_ref: fn(&[u8], ExprTicket) -> GenClause,
|
||||
pub same: fn(&[u8], &[u8]) -> bool,
|
||||
pub handle_req: fn(&[u8], &mut dyn Read, &mut dyn Write),
|
||||
pub drop: fn(&[u8]),
|
||||
pub call: fn(&[u8], SysCtx, ExprTicket) -> GenClause,
|
||||
pub call_ref: fn(&[u8], SysCtx, ExprTicket) -> GenClause,
|
||||
pub same: fn(&[u8], SysCtx, &[u8]) -> bool,
|
||||
pub handle_req: fn(&[u8], SysCtx, &mut dyn Read, &mut dyn Write),
|
||||
pub drop: fn(&[u8], SysCtx),
|
||||
}
|
||||
|
||||
pub trait ThinAtom: AtomCard<Data = Self> + Coding + fmt::Debug {
|
||||
fn call(&self, arg: ExprTicket) -> GenClause;
|
||||
fn same(&self, other: &Self) -> bool;
|
||||
fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized));
|
||||
#[allow(unused_variables)]
|
||||
fn call(&self, arg: ExprHandle) -> GenClause { bot("This atom is not callable") }
|
||||
#[allow(unused_variables)]
|
||||
fn same(&self, ctx: SysCtx, other: &Self) -> bool {
|
||||
eprintln!(
|
||||
"Override ThinAtom::same for {} if it can be generated during parsing",
|
||||
type_name::<Self>()
|
||||
);
|
||||
false
|
||||
}
|
||||
fn handle_req(&self, ctx: SysCtx, req: Self::Req, rep: &mut (impl Write + ?Sized));
|
||||
fn factory(self) -> AtomFactory {
|
||||
AtomFactory::new(move |sys| encode_atom_nodrop::<Self>(sys.dyn_card(), &self))
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn thin_atom_info<T: ThinAtom>() -> AtomInfo {
|
||||
AtomInfo {
|
||||
tid: ConstTypeId::of::<T>(),
|
||||
decode: |mut b| Box::new(T::decode(&mut b)),
|
||||
call: |mut b, extk| T::decode(&mut b).call(extk),
|
||||
call_ref: |mut b, extk| T::decode(&mut b).call(extk),
|
||||
handle_req: |mut b, req, rep| T::decode(&mut b).handle_req(Decode::decode(req), rep),
|
||||
same: |mut b1, mut b2| T::decode(&mut b1).same(&T::decode(&mut b2)),
|
||||
drop: |mut b1| eprintln!("Received drop signal for non-drop atom {:?}", T::decode(&mut b1)),
|
||||
call: |mut b, ctx, extk| T::decode(&mut b).call(ExprHandle::from_args(ctx, extk)),
|
||||
call_ref: |mut b, ctx, extk| T::decode(&mut b).call(ExprHandle::from_args(ctx, extk)),
|
||||
handle_req: |mut b, ctx, req, rep| T::decode(&mut b).handle_req(ctx, Decode::decode(req), rep),
|
||||
same: |mut b1, ctx, mut b2| T::decode(&mut b1).same(ctx, &T::decode(&mut b2)),
|
||||
drop: |mut b1, _| eprintln!("Received drop signal for non-drop atom {:?}", T::decode(&mut b1)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Atoms that have a [Drop]
|
||||
pub trait OwnedAtom: AtomCard + Deref<Target = Self::Data> + Send + Sync + Any + 'static {
|
||||
fn call_ref(&self, arg: ExprTicket) -> GenClause;
|
||||
fn call(self, arg: ExprTicket) -> GenClause;
|
||||
fn same(&self, other: &Self) -> bool;
|
||||
fn handle_req(&self, req: Self::Req, rep: &mut (impl Write + ?Sized));
|
||||
pub trait OwnedAtom: AtomCard + Send + Sync + Any + Clone + 'static {
|
||||
fn val(&self) -> Cow<'_, Self::Data>;
|
||||
#[allow(unused_variables)]
|
||||
fn call_ref(&self, arg: ExprHandle) -> GenClause { bot("This atom is not callable") }
|
||||
fn call(self, arg: ExprHandle) -> GenClause {
|
||||
let ctx = arg.get_ctx();
|
||||
let gcl = self.call_ref(arg);
|
||||
self.free(ctx);
|
||||
gcl
|
||||
}
|
||||
#[allow(unused_variables)]
|
||||
fn same(&self, ctx: SysCtx, other: &Self) -> bool {
|
||||
eprintln!(
|
||||
"Override OwnedAtom::same for {} if it can be generated during parsing",
|
||||
type_name::<Self>()
|
||||
);
|
||||
false
|
||||
}
|
||||
fn handle_req(&self, ctx: SysCtx, req: Self::Req, rep: &mut (impl Write + ?Sized));
|
||||
#[allow(unused_variables)]
|
||||
fn free(self, ctx: SysCtx) {}
|
||||
#[allow(unused_variables)]
|
||||
fn factory(self) -> AtomFactory {
|
||||
AtomFactory::new(move |sys| {
|
||||
let rec = OBJ_STORE.add(Box::new(self));
|
||||
let mut data = atom_info_for(sys.dyn_card(), rec.atom_tid()).expect("obj exists").0.enc_vec();
|
||||
rec.id().encode(&mut data);
|
||||
rec.encode(&mut data);
|
||||
LocalAtom { drop: true, data }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DynOwnedAtom: Send + Sync + 'static {
|
||||
fn atom_tid(&self) -> ConstTypeId;
|
||||
fn as_any_ref(&self) -> &dyn Any;
|
||||
fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause;
|
||||
fn dyn_call(self: Box<Self>, arg: ExprTicket) -> GenClause;
|
||||
fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool;
|
||||
fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write);
|
||||
fn encode(&self, buffer: &mut dyn Write);
|
||||
fn dyn_call_ref(&self, ctx: SysCtx, arg: ExprTicket) -> GenClause;
|
||||
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: ExprTicket) -> GenClause;
|
||||
fn dyn_same(&self, ctx: SysCtx, other: &dyn DynOwnedAtom) -> bool;
|
||||
fn dyn_handle_req(&self, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write);
|
||||
fn dyn_free(self: Box<Self>, ctx: SysCtx);
|
||||
}
|
||||
|
||||
impl<T: OwnedAtom> DynOwnedAtom for T {
|
||||
fn atom_tid(&self) -> ConstTypeId { ConstTypeId::of::<T>() }
|
||||
fn as_any_ref(&self) -> &dyn Any { self }
|
||||
fn dyn_call_ref(&self, arg: ExprTicket) -> GenClause { self.call_ref(arg) }
|
||||
fn dyn_call(self: Box<Self>, arg: ExprTicket) -> GenClause { self.call(arg) }
|
||||
fn dyn_same(&self, other: &dyn DynOwnedAtom) -> bool {
|
||||
fn encode(&self, buffer: &mut dyn Write) { self.val().as_ref().encode(buffer) }
|
||||
fn dyn_call_ref(&self, ctx: SysCtx, arg: ExprTicket) -> GenClause {
|
||||
self.call_ref(ExprHandle::from_args(ctx, arg))
|
||||
}
|
||||
fn dyn_call(self: Box<Self>, ctx: SysCtx, arg: ExprTicket) -> GenClause {
|
||||
self.call(ExprHandle::from_args(ctx, arg))
|
||||
}
|
||||
fn dyn_same(&self, ctx: SysCtx, other: &dyn DynOwnedAtom) -> bool {
|
||||
if ConstTypeId::of::<Self>() != other.as_any_ref().type_id() {
|
||||
return false;
|
||||
}
|
||||
let other_self = other.as_any_ref().downcast_ref().expect("The type_ids are the same");
|
||||
self.same(other_self)
|
||||
self.same(ctx, other_self)
|
||||
}
|
||||
fn dyn_handle_req(&self, req: &mut dyn Read, rep: &mut dyn Write) {
|
||||
self.handle_req(<Self as AtomCard>::Req::decode(req), rep)
|
||||
fn dyn_handle_req(&self, ctx: SysCtx, req: &mut dyn Read, rep: &mut dyn Write) {
|
||||
self.handle_req(ctx, <Self as AtomCard>::Req::decode(req), rep)
|
||||
}
|
||||
fn dyn_free(self: Box<Self>, ctx: SysCtx) { self.free(ctx) }
|
||||
}
|
||||
|
||||
pub(crate) static OBJ_STORE: IdStore<Box<dyn DynOwnedAtom>> = IdStore::new();
|
||||
|
||||
const fn owned_atom_info<T: OwnedAtom>() -> AtomInfo {
|
||||
pub const fn owned_atom_info<T: OwnedAtom>() -> AtomInfo {
|
||||
fn with_atom<U>(mut b: &[u8], f: impl FnOnce(IdRecord<'_, Box<dyn DynOwnedAtom>>) -> U) -> U {
|
||||
f(OBJ_STORE.get(NonZeroU64::decode(&mut b)).expect("Received invalid atom ID"))
|
||||
}
|
||||
AtomInfo {
|
||||
tid: ConstTypeId::of::<T>(),
|
||||
decode: |mut b| Box::new(T::Data::decode(&mut b)),
|
||||
call: |b, arg| with_atom(b, |a| a.remove().dyn_call(arg)),
|
||||
call_ref: |b, arg| with_atom(b, |a| a.dyn_call_ref(arg)),
|
||||
same: |b1, b2| with_atom(b1, |a1| with_atom(b2, |a2| a1.dyn_same(&**a2))),
|
||||
handle_req: |b, req, rep| with_atom(b, |a| a.dyn_handle_req(req, rep)),
|
||||
drop: |b| mem::drop(with_atom(b, |a| a.remove())),
|
||||
call: |b, ctx, arg| with_atom(b, |a| a.remove().dyn_call(ctx, arg)),
|
||||
call_ref: |b, ctx, arg| with_atom(b, |a| a.dyn_call_ref(ctx, arg)),
|
||||
same: |b1, ctx, b2| with_atom(b1, |a1| with_atom(b2, |a2| a1.dyn_same(ctx, &**a2))),
|
||||
handle_req: |b, ctx, req, rep| with_atom(b, |a| a.dyn_handle_req(ctx, req, rep)),
|
||||
drop: |b, ctx| with_atom(b, |a| a.remove().dyn_free(ctx)),
|
||||
}
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait AtomFactoryFn = FnOnce(&dyn DynSystem) -> LocalAtom + DynClone;
|
||||
}
|
||||
pub struct AtomFactory(Box<dyn AtomFactoryFn>);
|
||||
impl AtomFactory {
|
||||
pub fn new(f: impl FnOnce(&dyn DynSystem) -> LocalAtom + Clone + 'static) -> Self {
|
||||
Self(Box::new(f))
|
||||
}
|
||||
pub fn build(self, sys: &dyn DynSystem) -> LocalAtom { (self.0)(sys) }
|
||||
}
|
||||
impl Clone for AtomFactory {
|
||||
fn clone(&self) -> Self { AtomFactory(clone_box(&*self.0)) }
|
||||
}
|
||||
|
||||
@@ -1,92 +1,138 @@
|
||||
use std::num::{NonZeroU16, NonZeroU64};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{mem, thread};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_api::atom::{Atom, AtomReq, AtomSame, CallRef, FinalCall, Fwded};
|
||||
use orchid_api::parser::{CharFilter, Lex, Lexed, ParserReq, SubLex};
|
||||
use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader};
|
||||
use orchid_api::system::{SysId, SystemInst};
|
||||
use orchid_api::vfs::{EagerVfs, VfsId, VfsRead, VfsReq};
|
||||
use orchid_api::atom::{Atom, AtomDrop, AtomReq, AtomSame, CallRef, Command, FinalCall, Fwded};
|
||||
use orchid_api::interner::Sweep;
|
||||
use orchid_api::parser::{CharFilter, Lex, Lexed, ParserReq};
|
||||
use orchid_api::proto::{ExtMsgSet, ExtensionHeader, HostExtNotif, HostExtReq, HostHeader, Ping};
|
||||
use orchid_api::system::{SysId, SystemDrop, SystemInst};
|
||||
use orchid_api::tree::{GetConstTree, Tree, TreeId};
|
||||
use orchid_api::vfs::{EagerVfs, GetVfs, VfsId, VfsRead, VfsReq};
|
||||
use orchid_api_traits::{Decode, Encode};
|
||||
use orchid_base::char_filter::{char_filter_union, mk_char_filter};
|
||||
use orchid_base::clone;
|
||||
use orchid_base::intern::{deintern, init_replica, sweep_replica};
|
||||
use orchid_base::interner::{deintern, init_replica, sweep_replica};
|
||||
use orchid_base::name::PathSlice;
|
||||
use orchid_base::reqnot::{ReqNot, Requester};
|
||||
|
||||
use crate::atom::AtomInfo;
|
||||
use crate::error::{err_to_api, unpack_err};
|
||||
use crate::fs::VirtFS;
|
||||
use crate::lexer::LexContext;
|
||||
use crate::msg::{recv_parent_msg, send_parent_msg};
|
||||
use crate::system::DynSystem;
|
||||
use crate::system_ctor::DynSystemCtor;
|
||||
use crate::system::{atom_by_idx, SysCtx};
|
||||
use crate::system_ctor::{CtedObj, DynSystemCtor};
|
||||
use crate::tree::LazyTreeFactory;
|
||||
|
||||
pub struct ExtensionData {
|
||||
pub systems: Vec<Box<dyn DynSystemCtor>>,
|
||||
pub systems: &'static [&'static dyn DynSystemCtor],
|
||||
}
|
||||
|
||||
pub enum TreeRecord {
|
||||
Gen(LazyTreeFactory),
|
||||
Res(Tree),
|
||||
}
|
||||
|
||||
pub struct SystemRecord {
|
||||
instance: Box<dyn DynSystem>,
|
||||
vfses: HashMap<VfsId, Arc<dyn VirtFS>>,
|
||||
cted: CtedObj,
|
||||
vfses: HashMap<VfsId, &'static dyn VirtFS>,
|
||||
declfs: EagerVfs,
|
||||
tree: Tree,
|
||||
subtrees: HashMap<TreeId, TreeRecord>,
|
||||
}
|
||||
|
||||
pub fn with_atom_record<T>(
|
||||
systems: &Mutex<HashMap<SysId, SystemRecord>>,
|
||||
atom: &Atom,
|
||||
cb: impl FnOnce(&AtomInfo, &[u8]) -> T,
|
||||
cb: impl FnOnce(&AtomInfo, CtedObj, &[u8]) -> T,
|
||||
) -> T {
|
||||
let mut data = &atom.data[..];
|
||||
let systems_g = systems.lock().unwrap();
|
||||
let sys = &systems_g[&atom.owner].instance;
|
||||
let atom_record =
|
||||
(sys.card().atoms()[u64::decode(&mut data) as usize].as_ref()).expect("Atom ID reserved");
|
||||
cb(atom_record, data)
|
||||
let cted = &systems_g[&atom.owner].cted;
|
||||
let sys = cted.inst();
|
||||
let atom_record = atom_by_idx(sys.dyn_card(), u64::decode(&mut data)).expect("Atom ID reserved");
|
||||
cb(atom_record, cted.clone(), data)
|
||||
}
|
||||
|
||||
pub fn main(data: ExtensionData) {
|
||||
pub fn extension_main(data: ExtensionData) {
|
||||
HostHeader::decode(&mut &recv_parent_msg().unwrap()[..]);
|
||||
let mut buf = Vec::new();
|
||||
let decls = data.systems.iter().map(|sys| sys.decl()).collect_vec();
|
||||
let decls = (data.systems.iter().enumerate())
|
||||
.map(|(id, sys)| (u16::try_from(id).expect("more than u16max system ctors"), sys))
|
||||
.map(|(id, sys)| sys.decl(NonZeroU16::new(id + 1).unwrap()))
|
||||
.collect_vec();
|
||||
let systems = Arc::new(Mutex::new(HashMap::<SysId, SystemRecord>::new()));
|
||||
ExtensionHeader { systems: decls.clone() }.encode(&mut buf);
|
||||
send_parent_msg(&buf).unwrap();
|
||||
let exiting = Arc::new(AtomicBool::new(false));
|
||||
let rn = ReqNot::<ExtMsgSet>::new(
|
||||
|a, _| send_parent_msg(a).unwrap(),
|
||||
clone!(systems, exiting; move |n, _| match n {
|
||||
clone!(systems, exiting; move |n, reqnot| match n {
|
||||
HostExtNotif::Exit => exiting.store(true, Ordering::Relaxed),
|
||||
HostExtNotif::SystemDrop(sys) => mem::drop(systems.lock().unwrap().remove(&sys.0)),
|
||||
HostExtNotif::AtomDrop(atom) =>
|
||||
with_atom_record(&systems, &atom.0, |rec, data| (rec.drop)(data)),
|
||||
}),
|
||||
clone!(systems; move |req| match req.req() {
|
||||
HostExtReq::Ping(ping) => req.handle(ping, &()),
|
||||
HostExtReq::Sweep(sweep) => req.handle(sweep, &sweep_replica()),
|
||||
HostExtReq::NewSystem(new_sys) => {
|
||||
let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0;
|
||||
let system = data.systems[i].new_system(new_sys, req.reqnot());
|
||||
let mut vfses = HashMap::new();
|
||||
let lex_filter = system.lexers().iter().fold(CharFilter(vec![]), |cf, lx| {
|
||||
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
|
||||
});
|
||||
systems.lock().unwrap().insert(new_sys.id, SystemRecord {
|
||||
declfs: system.source().to_api_rec(&mut vfses),
|
||||
vfses,
|
||||
instance: system,
|
||||
});
|
||||
req.handle(new_sys, &SystemInst {
|
||||
lex_filter
|
||||
HostExtNotif::SystemDrop(SystemDrop(sys_id)) =>
|
||||
mem::drop(systems.lock().unwrap().remove(&sys_id)),
|
||||
HostExtNotif::AtomDrop(AtomDrop(atom)) => {
|
||||
with_atom_record(&systems, &atom, |rec, cted, data| {
|
||||
(rec.drop)(data, SysCtx{ reqnot, id: atom.owner, cted })
|
||||
})
|
||||
}
|
||||
HostExtReq::GetConstTree(get_tree) => {
|
||||
let systems_g = systems.lock().unwrap();
|
||||
req.handle(get_tree, &systems_g[&get_tree.0].instance.env())
|
||||
}),
|
||||
clone!(systems; move |req| match req.req() {
|
||||
HostExtReq::Ping(ping@Ping) => req.handle(ping, &()),
|
||||
HostExtReq::Sweep(sweep@Sweep) => req.handle(sweep, &sweep_replica()),
|
||||
HostExtReq::NewSystem(new_sys) => {
|
||||
let i = decls.iter().enumerate().find(|(_, s)| s.id == new_sys.system).unwrap().0;
|
||||
let cted = data.systems[i].new_system(new_sys);
|
||||
let mut vfses = HashMap::new();
|
||||
let lex_filter = cted.inst().dyn_lexers().iter().fold(CharFilter(vec![]), |cf, lx| {
|
||||
char_filter_union(&cf, &mk_char_filter(lx.char_filter().iter().cloned()))
|
||||
});
|
||||
let mut subtrees = HashMap::new();
|
||||
systems.lock().unwrap().insert(new_sys.id, SystemRecord {
|
||||
declfs: cted.inst().dyn_vfs().to_api_rec(&mut vfses),
|
||||
vfses,
|
||||
tree: cted.inst().dyn_env().into_api(&*cted.inst(), &mut |gen| {
|
||||
let id = TreeId::new((subtrees.len() + 2) as u64).unwrap();
|
||||
subtrees.insert(id, TreeRecord::Gen(gen.clone()));
|
||||
id
|
||||
}),
|
||||
cted,
|
||||
subtrees
|
||||
});
|
||||
req.handle(new_sys, &SystemInst {
|
||||
lex_filter, const_root_id: NonZeroU64::new(1).unwrap()
|
||||
})
|
||||
}
|
||||
HostExtReq::VfsReq(VfsReq::GetVfs(get_vfs)) => {
|
||||
HostExtReq::GetConstTree(get_tree@GetConstTree(sys_id, tree_id)) => {
|
||||
let mut systems_g = systems.lock().unwrap();
|
||||
let sys = systems_g.get_mut(sys_id).expect("System not found");
|
||||
if tree_id.get() == 1 {
|
||||
req.handle(get_tree, &sys.tree);
|
||||
} else {
|
||||
let subtrees = &mut sys.subtrees;
|
||||
let tree_rec = subtrees.get_mut(tree_id).expect("Tree for ID not found");
|
||||
match tree_rec {
|
||||
TreeRecord::Res(tree) => req.handle(get_tree, tree),
|
||||
TreeRecord::Gen(cb) => {
|
||||
let tree = cb.build();
|
||||
let reply_tree = tree.into_api(&*sys.cted.inst(), &mut |cb| {
|
||||
let id = NonZeroU64::new((subtrees.len() + 2) as u64).unwrap();
|
||||
subtrees.insert(id, TreeRecord::Gen(cb.clone()));
|
||||
id
|
||||
});
|
||||
req.handle(get_tree, &reply_tree);
|
||||
subtrees.insert(*tree_id, TreeRecord::Res(reply_tree));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
HostExtReq::VfsReq(VfsReq::GetVfs(get_vfs@GetVfs(sys_id))) => {
|
||||
let systems_g = systems.lock().unwrap();
|
||||
req.handle(get_vfs, &systems_g[&get_vfs.0].declfs)
|
||||
req.handle(get_vfs, &systems_g[sys_id].declfs)
|
||||
}
|
||||
HostExtReq::VfsReq(VfsReq::VfsRead(vfs_read@VfsRead(sys_id, vfs_id, path))) => {
|
||||
let systems_g = systems.lock().unwrap();
|
||||
@@ -94,30 +140,30 @@ pub fn main(data: ExtensionData) {
|
||||
req.handle(vfs_read, &systems_g[sys_id].vfses[vfs_id].load(PathSlice::new(&path)))
|
||||
}
|
||||
HostExtReq::ParserReq(ParserReq::Lex(lex)) => {
|
||||
let Lex{ sys, text, pos, id } = *lex;
|
||||
let systems_g = systems.lock().unwrap();
|
||||
let Lex{ sys, text, pos } = *lex;
|
||||
let lexers = systems_g[&sys].instance.lexers();
|
||||
let lexers = systems_g[&sys].cted.inst().dyn_lexers();
|
||||
mem::drop(systems_g);
|
||||
let source = deintern(text);
|
||||
let text = deintern(text);
|
||||
let tk = req.will_handle_as(lex);
|
||||
thread::spawn(move || {
|
||||
let reqnot = req.reqnot();
|
||||
let mut recurse = |tail: &str| {
|
||||
let pos = (source.len() - tail.len()) as u32;
|
||||
let lexed = reqnot.request(SubLex{ pos, text })?;
|
||||
Ok((&source[lexed.pos as usize..], lexed.data))
|
||||
};
|
||||
let lex_res = lexers.iter().find_map(|lx| lx.lex(&source[pos as usize..], &mut recurse));
|
||||
req.handle_as(tk, &lex_res.map(|r| r.map(|(s, data)| {
|
||||
let pos = (source.len() - s.len()) as u32;
|
||||
Lexed { data, pos }
|
||||
})))
|
||||
});
|
||||
thread::spawn(clone!(systems; move || {
|
||||
let ctx = LexContext { sys, id, pos, reqnot: req.reqnot(), text: &text };
|
||||
let lex_res = lexers.iter().find_map(|lx| lx.lex(&text[pos as usize..], &ctx));
|
||||
req.handle_as(tk, &lex_res.map(|r| match r {
|
||||
Ok((s, data)) => {
|
||||
let systems_g = systems.lock().unwrap();
|
||||
let data = data.into_api(&*systems_g[&sys].cted.inst());
|
||||
Ok(Lexed { data, pos: (text.len() - s.len()) as u32 })
|
||||
},
|
||||
Err(e) => Err(unpack_err(e).into_iter().map(err_to_api).collect_vec())
|
||||
}))
|
||||
}));
|
||||
},
|
||||
HostExtReq::AtomReq(AtomReq::AtomSame(same@AtomSame(l, r))) => todo!("subsys nimpl"),
|
||||
HostExtReq::AtomReq(AtomReq::Fwded(call@Fwded(atom, req))) => todo!("subsys nimpl"),
|
||||
HostExtReq::AtomReq(AtomReq::CallRef(call@CallRef(atom, arg))) => todo!("subsys nimpl"),
|
||||
HostExtReq::AtomReq(AtomReq::FinalCall(call@FinalCall(atom, arg))) => todo!("subsys nimpl"),
|
||||
HostExtReq::AtomReq(AtomReq::Command(cmd@Command(atom))) => todo!("subsys impl"),
|
||||
}),
|
||||
);
|
||||
init_replica(rn.clone().map());
|
||||
|
||||
323
orchid-extension/src/error.rs
Normal file
323
orchid-extension/src/error.rs
Normal file
@@ -0,0 +1,323 @@
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use std::{fmt, iter};
|
||||
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use itertools::Itertools;
|
||||
use orchid_api::error::{GetErrorDetails, ProjErr, ProjErrId, ProjErrOrRef};
|
||||
use orchid_api::proto::ExtMsgSet;
|
||||
use orchid_base::boxed_iter::{box_once, BoxedIter};
|
||||
use orchid_base::clone;
|
||||
use orchid_base::error::{ErrorDetails, ErrorPosition};
|
||||
use orchid_base::interner::{deintern, intern};
|
||||
use orchid_base::location::{GetSrc, Pos};
|
||||
use orchid_base::reqnot::{ReqNot, Requester};
|
||||
|
||||
/// Errors addressed to the developer which are to be resolved with
|
||||
/// code changes
|
||||
pub trait ProjectError: Sized + Send + Sync + 'static {
|
||||
/// A general description of this type of error
|
||||
const DESCRIPTION: &'static str;
|
||||
/// A formatted message that includes specific parameters
|
||||
#[must_use]
|
||||
fn message(&self) -> String { self.description().to_string() }
|
||||
/// Code positions relevant to this error. If you don't implement this, you
|
||||
/// must implement [ProjectError::one_position]
|
||||
#[must_use]
|
||||
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
|
||||
box_once(ErrorPosition { position: self.one_position(), message: None })
|
||||
}
|
||||
/// Short way to provide a single origin. If you don't implement this, you
|
||||
/// must implement [ProjectError::positions]
|
||||
#[must_use]
|
||||
fn one_position(&self) -> Pos {
|
||||
unimplemented!("Error type did not implement either positions or one_position")
|
||||
}
|
||||
/// Convert the error into an `Arc<dyn DynProjectError>` to be able to
|
||||
/// handle various errors together
|
||||
#[must_use]
|
||||
fn pack(self) -> ProjectErrorObj { Arc::new(self) }
|
||||
}
|
||||
|
||||
/// Object-safe version of [ProjectError]. Implement that instead of this.
|
||||
pub trait DynProjectError: Send + Sync {
|
||||
/// Access type information about this error
|
||||
#[must_use]
|
||||
fn as_any_ref(&self) -> &dyn Any;
|
||||
/// Pack the error into a trait object, or leave it as-is if it's already a
|
||||
/// trait object
|
||||
#[must_use]
|
||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj;
|
||||
/// A general description of this type of error
|
||||
#[must_use]
|
||||
fn description(&self) -> Cow<'_, str>;
|
||||
/// A formatted message that includes specific parameters
|
||||
#[must_use]
|
||||
fn message(&self) -> String { self.description().to_string() }
|
||||
/// Code positions relevant to this error.
|
||||
#[must_use]
|
||||
fn positions(&self) -> BoxedIter<'_, ErrorPosition>;
|
||||
}
|
||||
|
||||
impl<T> DynProjectError for T
|
||||
where T: ProjectError
|
||||
{
|
||||
fn as_any_ref(&self) -> &dyn Any { self }
|
||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
||||
fn description(&self) -> Cow<'_, str> { Cow::Borrowed(T::DESCRIPTION) }
|
||||
fn message(&self) -> String { ProjectError::message(self) }
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
Box::new(ProjectError::positions(self).into_iter())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pretty_print(err: &dyn DynProjectError, get_src: &mut impl GetSrc) -> String {
|
||||
let description = err.description();
|
||||
let message = err.message();
|
||||
let positions = err.positions().collect::<Vec<_>>();
|
||||
let head = format!("Project error: {description}\n{message}");
|
||||
if positions.is_empty() {
|
||||
head + "No origins specified"
|
||||
} else {
|
||||
iter::once(head)
|
||||
.chain(positions.iter().map(|ErrorPosition { position: origin, message }| match message {
|
||||
None => format!("@{}", origin.pretty_print(get_src)),
|
||||
Some(msg) => format!("@{}: {msg}", origin.pretty_print(get_src)),
|
||||
}))
|
||||
.join("\n")
|
||||
}
|
||||
}
|
||||
|
||||
impl DynProjectError for ProjectErrorObj {
|
||||
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
||||
fn description(&self) -> Cow<'_, str> { (**self).description() }
|
||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { (*self).clone() }
|
||||
fn message(&self) -> String { (**self).message() }
|
||||
fn positions(&self) -> BoxedIter<'_, ErrorPosition> { (**self).positions() }
|
||||
}
|
||||
|
||||
/// Type-erased [ProjectError] implementor through the [DynProjectError]
|
||||
/// object-trait
|
||||
pub type ProjectErrorObj = Arc<dyn DynProjectError>;
|
||||
/// Alias for a result with an error of [ProjectErrorObj]. This is the type of
|
||||
/// result most commonly returned by pre-runtime operations.
|
||||
pub type ProjectResult<T> = Result<T, ProjectErrorObj>;
|
||||
|
||||
/// A trait for error types that are only missing an origin. Do not depend on
|
||||
/// this trait, refer to [DynErrorSansOrigin] instead.
|
||||
pub trait ErrorSansOrigin: Clone + Sized + Send + Sync + 'static {
|
||||
/// General description of the error condition
|
||||
const DESCRIPTION: &'static str;
|
||||
/// Specific description of the error including code fragments or concrete
|
||||
/// data if possible
|
||||
fn message(&self) -> String { Self::DESCRIPTION.to_string() }
|
||||
/// Convert the error to a type-erased structure for handling on shared
|
||||
/// channels
|
||||
fn pack(self) -> ErrorSansOriginObj { Box::new(self) }
|
||||
/// A shortcut to streamline switching code between [ErrorSansOriginObj] and
|
||||
/// concrete types
|
||||
fn bundle(self, origin: &Pos) -> ProjectErrorObj { self.pack().bundle(origin) }
|
||||
}
|
||||
|
||||
/// Object-safe equivalent to [ErrorSansOrigin]. Implement that one instead of
|
||||
/// this. Typically found as [ErrorSansOriginObj]
|
||||
pub trait DynErrorSansOrigin: Any + Send + Sync + DynClone {
|
||||
/// Allow to downcast the base object to distinguish between various errors.
|
||||
/// The main intended purpose is to trigger a fallback when [CodeNotFound] is
|
||||
/// encountered, but the possibilities are not limited to that.
|
||||
fn as_any_ref(&self) -> &dyn Any;
|
||||
/// Regularize the type
|
||||
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj;
|
||||
/// Generic description of the error condition
|
||||
fn description(&self) -> Cow<'_, str>;
|
||||
/// Specific description of this particular error
|
||||
fn message(&self) -> String;
|
||||
/// Add an origin
|
||||
fn bundle(self: Box<Self>, origin: &Pos) -> ProjectErrorObj;
|
||||
}
|
||||
|
||||
/// Type-erased [ErrorSansOrigin] implementor through the object-trait
|
||||
/// [DynErrorSansOrigin]. This can be turned into a [ProjectErrorObj] with
|
||||
/// [ErrorSansOriginObj::bundle].
|
||||
pub type ErrorSansOriginObj = Box<dyn DynErrorSansOrigin>;
|
||||
/// A generic project result without origin
|
||||
pub type ResultSansOrigin<T> = Result<T, ErrorSansOriginObj>;
|
||||
|
||||
impl<T: ErrorSansOrigin + 'static> DynErrorSansOrigin for T {
|
||||
fn description(&self) -> Cow<'_, str> { Cow::Borrowed(Self::DESCRIPTION) }
|
||||
fn message(&self) -> String { (*self).message() }
|
||||
fn as_any_ref(&self) -> &dyn Any { self }
|
||||
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { (*self).pack() }
|
||||
fn bundle(self: Box<Self>, origin: &Pos) -> ProjectErrorObj {
|
||||
Arc::new(OriginBundle(origin.clone(), *self))
|
||||
}
|
||||
}
|
||||
impl Clone for ErrorSansOriginObj {
|
||||
fn clone(&self) -> Self { clone_box(&**self) }
|
||||
}
|
||||
impl DynErrorSansOrigin for ErrorSansOriginObj {
|
||||
fn description(&self) -> Cow<'_, str> { (**self).description() }
|
||||
fn message(&self) -> String { (**self).message() }
|
||||
fn as_any_ref(&self) -> &dyn Any { (**self).as_any_ref() }
|
||||
fn into_packed(self: Box<Self>) -> ErrorSansOriginObj { *self }
|
||||
fn bundle(self: Box<Self>, origin: &Pos) -> ProjectErrorObj { (*self).bundle(origin) }
|
||||
}
|
||||
impl fmt::Display for ErrorSansOriginObj {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "{}\nOrigin missing from error", self.message())
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for ErrorSansOriginObj {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{self}") }
|
||||
}
|
||||
|
||||
struct OriginBundle<T: ErrorSansOrigin>(Pos, T);
|
||||
impl<T: ErrorSansOrigin> DynProjectError for OriginBundle<T> {
|
||||
fn as_any_ref(&self) -> &dyn Any { self.1.as_any_ref() }
|
||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
||||
fn description(&self) -> Cow<'_, str> { self.1.description() }
|
||||
fn message(&self) -> String { self.1.message() }
|
||||
fn positions(&self) -> BoxedIter<ErrorPosition> {
|
||||
box_once(ErrorPosition { position: self.0.clone(), message: None })
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection for tracking fatal errors without halting. Participating
|
||||
/// functions return [ProjectResult] even if they only ever construct [Ok]. When
|
||||
/// they call other participating functions, instead of directly forwarding
|
||||
/// errors with `?` they should prefer constructing a fallback value with
|
||||
/// [Reporter::fallback]. If any error is added to a [Reporter] in a function,
|
||||
/// the return value is valid but its meaning need not be related in any way to
|
||||
/// the inputs.
|
||||
///
|
||||
/// Returning [Err] from a function that accepts `&mut Reporter` indicates not
|
||||
/// that there was a fatal error but that it wasn't possible to construct a
|
||||
/// fallback, so if it can, the caller should construct one.
|
||||
pub struct Reporter(RefCell<Vec<ProjectErrorObj>>);
|
||||
impl Reporter {
|
||||
/// Create a new error reporter
|
||||
pub fn new() -> Self { Self(RefCell::new(Vec::new())) }
|
||||
/// Returns true if any errors were regorded. If this ever returns true, it
|
||||
/// will always return true in the future.
|
||||
pub fn failing(&self) -> bool { !self.0.borrow().is_empty() }
|
||||
/// Report a fatal error
|
||||
pub fn report(&self, error: ProjectErrorObj) {
|
||||
match error.as_any_ref().downcast_ref::<MultiError>() {
|
||||
None => self.0.borrow_mut().push(error),
|
||||
Some(me) =>
|
||||
for err in me.0.iter() {
|
||||
self.report(err.clone())
|
||||
},
|
||||
}
|
||||
}
|
||||
/// Catch a fatal error, report it, and substitute the value
|
||||
pub fn fallback<T>(&self, res: ProjectResult<T>, cb: impl FnOnce(ProjectErrorObj) -> T) -> T {
|
||||
res.inspect_err(|e| self.report(e.clone())).unwrap_or_else(cb)
|
||||
}
|
||||
/// Take the errors out of the reporter
|
||||
#[must_use]
|
||||
pub fn into_errors(self) -> Option<Vec<ProjectErrorObj>> {
|
||||
let v = self.0.into_inner();
|
||||
if v.is_empty() { None } else { Some(v) }
|
||||
}
|
||||
/// Raise an error if the reporter contains any errors
|
||||
pub fn bind(self) -> ProjectResult<()> {
|
||||
match self.into_errors() {
|
||||
None => Ok(()),
|
||||
Some(v) if v.len() == 1 => Err(v.into_iter().next().unwrap()),
|
||||
Some(v) => Err(MultiError(v).pack()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Reporter {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
|
||||
fn unpack_into(err: impl DynProjectError, res: &mut Vec<ProjectErrorObj>) {
|
||||
match err.as_any_ref().downcast_ref::<MultiError>() {
|
||||
Some(multi) => multi.0.iter().for_each(|e| unpack_into(e.clone(), res)),
|
||||
None => res.push(Arc::new(err).into_packed()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack_err(err: ProjectErrorObj) -> Vec<ProjectErrorObj> {
|
||||
let mut out = Vec::new();
|
||||
unpack_into(err, &mut out);
|
||||
out
|
||||
}
|
||||
|
||||
pub fn pack_err<E: DynProjectError>(iter: impl IntoIterator<Item = E>) -> ProjectErrorObj {
|
||||
let mut errors = Vec::new();
|
||||
iter.into_iter().for_each(|e| unpack_into(e, &mut errors));
|
||||
if errors.len() == 1 { errors.into_iter().next().unwrap() } else { MultiError(errors).pack() }
|
||||
}
|
||||
|
||||
struct MultiError(Vec<ProjectErrorObj>);
|
||||
impl ProjectError for MultiError {
|
||||
const DESCRIPTION: &'static str = "Multiple errors occurred";
|
||||
fn message(&self) -> String { format!("{} errors occurred", self.0.len()) }
|
||||
fn positions(&self) -> impl IntoIterator<Item = ErrorPosition> + '_ {
|
||||
self.0.iter().flat_map(|e| {
|
||||
e.positions().map(|pos| {
|
||||
let emsg = e.message();
|
||||
let msg = match pos.message {
|
||||
None => emsg,
|
||||
Some(s) if s.is_empty() => emsg,
|
||||
Some(pmsg) => format!("{emsg}: {pmsg}"),
|
||||
};
|
||||
ErrorPosition { position: pos.position, message: Some(Arc::new(msg)) }
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err_to_api(err: ProjectErrorObj) -> ProjErrOrRef {
|
||||
match err.as_any_ref().downcast_ref() {
|
||||
Some(RelayedError { id: Some(id), .. }) => ProjErrOrRef::Known(*id),
|
||||
_ => ProjErrOrRef::New(ProjErr {
|
||||
description: intern(&*err.description()).marker(),
|
||||
message: Arc::new(err.message()),
|
||||
locations: err.positions().map(|e| e.to_api()).collect_vec(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn err_from_api(err: &ProjErrOrRef, reqnot: ReqNot<ExtMsgSet>) -> ProjectErrorObj {
|
||||
Arc::new(match err {
|
||||
ProjErrOrRef::Known(id) => RelayedError { id: Some(*id), reqnot, details: OnceLock::default() },
|
||||
ProjErrOrRef::New(err) =>
|
||||
RelayedError { id: None, reqnot, details: ErrorDetails::from_api(err).into() },
|
||||
})
|
||||
}
|
||||
|
||||
struct RelayedError {
|
||||
pub id: Option<ProjErrId>,
|
||||
pub reqnot: ReqNot<ExtMsgSet>,
|
||||
pub details: OnceLock<ErrorDetails>,
|
||||
}
|
||||
impl RelayedError {
|
||||
fn details(&self) -> &ErrorDetails {
|
||||
let Self { id, reqnot, details: data } = self;
|
||||
data.get_or_init(clone!(reqnot; move || {
|
||||
let id = id.expect("Either data or ID must be initialized");
|
||||
let projerr = reqnot.request(GetErrorDetails(id));
|
||||
ErrorDetails {
|
||||
description: deintern(projerr.description),
|
||||
message: projerr.message,
|
||||
locations: projerr.locations.iter().map(ErrorPosition::from_api).collect_vec(),
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
impl DynProjectError for RelayedError {
|
||||
fn description(&self) -> Cow<'_, str> { Cow::Borrowed(self.details().description.as_str()) }
|
||||
fn message(&self) -> String { self.details().message.to_string() }
|
||||
fn as_any_ref(&self) -> &dyn std::any::Any { self }
|
||||
fn into_packed(self: Arc<Self>) -> ProjectErrorObj { self }
|
||||
fn positions(&self) -> BoxedIter<'_, ErrorPosition> {
|
||||
Box::new(self.details().locations.iter().cloned())
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,173 @@
|
||||
use std::ops::Deref;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
use derive_destructure::destructure;
|
||||
use orchid_api::atom::Atom;
|
||||
use orchid_api::expr::ExprTicket;
|
||||
use orchid_api::system::SysId;
|
||||
use orchid_base::id_store::IdStore;
|
||||
use orchid_base::intern::Token;
|
||||
use orchid_api::expr::{Acquire, Clause, Expr, ExprTicket, Inspect, Release};
|
||||
use orchid_base::interner::{deintern, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::reqnot::Requester;
|
||||
|
||||
use crate::atom::{encode_atom_nodrop, DynOwnedAtom, OwnedAtom, ThinAtom, OBJ_STORE};
|
||||
use crate::system::DynSystem;
|
||||
use crate::atom::{AtomFactory, ForeignAtom, OwnedAtom, ThinAtom};
|
||||
use crate::system::{DynSystem, SysCtx};
|
||||
|
||||
#[derive(destructure)]
|
||||
pub struct ExprHandle {
|
||||
pub tk: ExprTicket,
|
||||
pub ctx: SysCtx,
|
||||
}
|
||||
impl ExprHandle {
|
||||
pub(crate) fn from_args(ctx: SysCtx, tk: ExprTicket) -> Self { Self { ctx, tk } }
|
||||
pub(crate) fn into_tk(self) -> ExprTicket {
|
||||
let (tk, ..) = self.destructure();
|
||||
tk
|
||||
}
|
||||
pub(crate) fn get_tk(&self) -> ExprTicket { self.tk }
|
||||
pub fn get_ctx(&self) -> SysCtx { self.ctx.clone() }
|
||||
}
|
||||
impl Clone for ExprHandle {
|
||||
fn clone(&self) -> Self {
|
||||
self.ctx.reqnot.notify(Acquire(self.ctx.id, self.tk));
|
||||
Self { ctx: self.ctx.clone(), tk: self.tk }
|
||||
}
|
||||
}
|
||||
impl Drop for ExprHandle {
|
||||
fn drop(&mut self) { self.ctx.reqnot.notify(Release(self.ctx.id, self.tk)) }
|
||||
}
|
||||
|
||||
#[derive(Clone, destructure)]
|
||||
pub struct OwnedExpr {
|
||||
pub handle: ExprHandle,
|
||||
pub val: OnceLock<Box<GenExpr>>,
|
||||
}
|
||||
impl OwnedExpr {
|
||||
pub fn new(handle: ExprHandle) -> Self { Self { handle, val: OnceLock::new() } }
|
||||
pub fn get_data(&self) -> &GenExpr {
|
||||
self.val.get_or_init(|| {
|
||||
Box::new(GenExpr::from_api(
|
||||
self.handle.ctx.reqnot.request(Inspect(self.handle.tk)).expr,
|
||||
self.handle.get_ctx(),
|
||||
))
|
||||
})
|
||||
}
|
||||
pub fn foreign_atom(self) -> Result<ForeignAtom, Self> {
|
||||
if let GenExpr { clause: GenClause::Atom(_, atom), position } = self.get_data() {
|
||||
let (atom, position) = (atom.clone(), position.clone());
|
||||
return Ok(ForeignAtom { expr: self.handle, atom, position });
|
||||
}
|
||||
Err(self)
|
||||
}
|
||||
}
|
||||
impl Deref for OwnedExpr {
|
||||
type Target = GenExpr;
|
||||
fn deref(&self) -> &Self::Target { self.get_data() }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenExpr {
|
||||
pub position: Pos,
|
||||
pub clause: GenClause,
|
||||
}
|
||||
impl GenExpr {
|
||||
pub fn to_api(&self, sys: &dyn DynSystem) -> Expr {
|
||||
Expr { location: self.position.to_api(), clause: self.clause.to_api(sys) }
|
||||
}
|
||||
pub fn into_api(self, sys: &dyn DynSystem) -> Expr {
|
||||
Expr { location: self.position.to_api(), clause: self.clause.into_api(sys) }
|
||||
}
|
||||
pub fn from_api(api: Expr, ctx: SysCtx) -> Self {
|
||||
Self { position: Pos::from_api(&api.location), clause: GenClause::from_api(api.clause, ctx) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum GenClause {
|
||||
Call(Box<GenClause>, Box<GenClause>),
|
||||
Lambda(Token<String>, Box<GenClause>),
|
||||
Arg(Token<String>),
|
||||
Slot(ExprTicket),
|
||||
Seq(Box<GenClause>, Box<GenClause>),
|
||||
Const(Token<Vec<Token<String>>>),
|
||||
ThinAtom(Box<dyn Fn(SysId, &dyn DynSystem) -> Atom>),
|
||||
OwnedAtom(u64),
|
||||
Call(Box<GenExpr>, Box<GenExpr>),
|
||||
Lambda(u64, Box<GenExpr>),
|
||||
Arg(u64),
|
||||
Slot(OwnedExpr),
|
||||
Seq(Box<GenExpr>, Box<GenExpr>),
|
||||
Const(Tok<Vec<Tok<String>>>),
|
||||
NewAtom(AtomFactory),
|
||||
Atom(ExprTicket, Atom),
|
||||
Bottom(String),
|
||||
}
|
||||
|
||||
pub fn cnst(path: Token<Vec<Token<String>>>) -> GenClause { GenClause::Const(path) }
|
||||
pub fn val<A: ThinAtom>(atom: A) -> GenClause {
|
||||
GenClause::ThinAtom(Box::new(move |id, sys| encode_atom_nodrop::<A>(id, sys.card(), &atom)))
|
||||
impl GenClause {
|
||||
pub fn to_api(&self, sys: &dyn DynSystem) -> Clause {
|
||||
match self {
|
||||
Self::Call(f, x) => Clause::Call(Box::new(f.to_api(sys)), Box::new(x.to_api(sys))),
|
||||
Self::Seq(a, b) => Clause::Seq(Box::new(a.to_api(sys)), Box::new(b.to_api(sys))),
|
||||
Self::Lambda(arg, body) => Clause::Lambda(*arg, Box::new(body.to_api(sys))),
|
||||
Self::Arg(arg) => Clause::Arg(*arg),
|
||||
Self::Const(name) => Clause::Const(name.marker()),
|
||||
Self::Bottom(msg) => Clause::Bottom(msg.clone()),
|
||||
Self::NewAtom(fac) => Clause::NewAtom(fac.clone().build(sys)),
|
||||
Self::Atom(tk, atom) => Clause::Atom(*tk, atom.clone()),
|
||||
Self::Slot(_) => panic!("Slot is forbidden in const tree"),
|
||||
}
|
||||
}
|
||||
pub fn into_api(self, sys: &dyn DynSystem) -> Clause {
|
||||
match self {
|
||||
Self::Call(f, x) => Clause::Call(Box::new(f.into_api(sys)), Box::new(x.into_api(sys))),
|
||||
Self::Seq(a, b) => Clause::Seq(Box::new(a.into_api(sys)), Box::new(b.into_api(sys))),
|
||||
Self::Lambda(arg, body) => Clause::Lambda(arg, Box::new(body.into_api(sys))),
|
||||
Self::Arg(arg) => Clause::Arg(arg),
|
||||
Self::Slot(extk) => Clause::Slot(extk.handle.into_tk()),
|
||||
Self::Const(name) => Clause::Const(name.marker()),
|
||||
Self::Bottom(msg) => Clause::Bottom(msg.clone()),
|
||||
Self::NewAtom(fac) => Clause::NewAtom(fac.clone().build(sys)),
|
||||
Self::Atom(tk, atom) => Clause::Atom(tk, atom),
|
||||
}
|
||||
}
|
||||
pub fn from_api(api: Clause, ctx: SysCtx) -> Self {
|
||||
match api {
|
||||
Clause::Arg(id) => Self::Arg(id),
|
||||
Clause::Lambda(arg, body) => Self::Lambda(arg, Box::new(GenExpr::from_api(*body, ctx))),
|
||||
Clause::NewAtom(_) => panic!("Clause::NewAtom should never be received, only sent"),
|
||||
Clause::Bottom(s) => Self::Bottom(s),
|
||||
Clause::Call(f, x) => Self::Call(
|
||||
Box::new(GenExpr::from_api(*f, ctx.clone())),
|
||||
Box::new(GenExpr::from_api(*x, ctx)),
|
||||
),
|
||||
Clause::Seq(a, b) => Self::Seq(
|
||||
Box::new(GenExpr::from_api(*a, ctx.clone())),
|
||||
Box::new(GenExpr::from_api(*b, ctx)),
|
||||
),
|
||||
Clause::Const(name) => Self::Const(deintern(name)),
|
||||
Clause::Slot(exi) => Self::Slot(OwnedExpr::new(ExprHandle::from_args(ctx, exi))),
|
||||
Clause::Atom(tk, atom) => Self::Atom(tk, atom),
|
||||
}
|
||||
}
|
||||
}
|
||||
fn inherit(clause: GenClause) -> GenExpr { GenExpr { position: Pos::Inherit, clause } }
|
||||
|
||||
pub fn obj<A: OwnedAtom>(atom: A) -> GenClause {
|
||||
GenClause::OwnedAtom(OBJ_STORE.add(Box::new(atom)))
|
||||
}
|
||||
pub fn cnst(path: Tok<Vec<Tok<String>>>) -> GenExpr { inherit(GenClause::Const(path)) }
|
||||
pub fn val<A: ThinAtom>(atom: A) -> GenExpr { inherit(GenClause::NewAtom(atom.factory())) }
|
||||
pub fn obj<A: OwnedAtom>(atom: A) -> GenExpr { inherit(GenClause::NewAtom(atom.factory())) }
|
||||
|
||||
pub fn seq(ops: impl IntoIterator<Item = GenClause>) -> GenClause {
|
||||
fn recur(mut ops: impl Iterator<Item = GenClause>) -> Option<GenClause> {
|
||||
pub fn seq(ops: impl IntoIterator<Item = GenExpr>) -> GenExpr {
|
||||
fn recur(mut ops: impl Iterator<Item = GenExpr>) -> Option<GenExpr> {
|
||||
let op = ops.next()?;
|
||||
Some(match recur(ops) {
|
||||
None => op,
|
||||
Some(rec) => GenClause::Seq(Box::new(op), Box::new(rec)),
|
||||
Some(rec) => inherit(GenClause::Seq(Box::new(op), Box::new(rec))),
|
||||
})
|
||||
}
|
||||
recur(ops.into_iter()).expect("Empty list provided to seq!")
|
||||
}
|
||||
|
||||
pub fn slot(extk: ExprTicket) -> GenClause { GenClause::Slot(extk) }
|
||||
pub fn slot(extk: OwnedExpr) -> GenClause { GenClause::Slot(extk) }
|
||||
|
||||
pub fn arg(n: Token<String>) -> GenClause { GenClause::Arg(n) }
|
||||
pub fn arg(n: u64) -> GenClause { GenClause::Arg(n) }
|
||||
|
||||
pub fn lambda(n: Token<String>, b: impl IntoIterator<Item = GenClause>) -> GenClause {
|
||||
GenClause::Lambda(n, Box::new(call(b)))
|
||||
pub fn lambda(n: u64, b: impl IntoIterator<Item = GenExpr>) -> GenExpr {
|
||||
inherit(GenClause::Lambda(n, Box::new(call(b))))
|
||||
}
|
||||
|
||||
pub fn call(v: impl IntoIterator<Item = GenClause>) -> GenClause {
|
||||
pub fn call(v: impl IntoIterator<Item = GenExpr>) -> GenExpr {
|
||||
v.into_iter()
|
||||
.reduce(|f, x| GenClause::Call(Box::new(f), Box::new(x)))
|
||||
.reduce(|f, x| inherit(GenClause::Call(Box::new(f), Box::new(x))))
|
||||
.expect("Empty call expression")
|
||||
}
|
||||
|
||||
pub fn bot(msg: &str) -> GenClause { GenClause::Bottom(msg.to_string()) }
|
||||
|
||||
@@ -1,47 +1,30 @@
|
||||
use std::sync::Arc;
|
||||
use std::num::NonZeroU16;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api::error::ProjResult;
|
||||
use orchid_api::vfs::{EagerVfs, Loaded, VfsId};
|
||||
use orchid_base::intern::{intern, Token};
|
||||
use orchid_base::interner::intern;
|
||||
use orchid_base::name::PathSlice;
|
||||
use substack::Substack;
|
||||
use trait_set::trait_set;
|
||||
|
||||
pub trait VirtFS: Send + Sync + 'static {
|
||||
fn load(&self, path: &PathSlice) -> ProjResult<Loaded>;
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
pub trait RecFsHandler<E> = FnMut(Substack<Token<String>>, &Arc<dyn VirtFS>) -> Result<(), E>;
|
||||
}
|
||||
|
||||
pub enum DeclFs {
|
||||
Lazy(Arc<dyn VirtFS>),
|
||||
Mod(HashMap<Token<String>, DeclFs>),
|
||||
Lazy(&'static dyn VirtFS),
|
||||
Mod(&'static [(&'static str, DeclFs)]),
|
||||
}
|
||||
impl DeclFs {
|
||||
pub fn module(entries: impl IntoIterator<Item = (&'static str, Self)>) -> Self {
|
||||
Self::Mod(entries.into_iter().map(|(k, v)| (intern(k), v)).collect())
|
||||
}
|
||||
fn rec<E>(&self, path: Substack<Token<String>>, f: &mut impl RecFsHandler<E>) -> Result<(), E> {
|
||||
match self {
|
||||
DeclFs::Lazy(fs) => f(path, fs),
|
||||
DeclFs::Mod(entries) => entries.iter().try_for_each(|(k, v)| v.rec(path.push(k.clone()), f)),
|
||||
}
|
||||
}
|
||||
pub fn recurse<E>(&self, f: &mut impl RecFsHandler<E>) -> Result<(), E> {
|
||||
self.rec(Substack::Bottom, f)
|
||||
}
|
||||
pub fn to_api_rec(&self, vfses: &mut HashMap<VfsId, Arc<dyn VirtFS>>) -> EagerVfs {
|
||||
pub fn to_api_rec(&self, vfses: &mut HashMap<VfsId, &'static dyn VirtFS>) -> EagerVfs {
|
||||
match self {
|
||||
DeclFs::Lazy(fs) => {
|
||||
let id = vfses.len() as VfsId;
|
||||
vfses.insert(id, fs.clone());
|
||||
let vfsc: u16 = vfses.len().try_into().expect("too many vfses (more than u16::MAX)");
|
||||
let id: VfsId = NonZeroU16::new(vfsc + 1).unwrap();
|
||||
vfses.insert(id, *fs);
|
||||
EagerVfs::Lazy(id)
|
||||
},
|
||||
DeclFs::Mod(children) => EagerVfs::Eager(
|
||||
children.into_iter().map(|(k, v)| (k.marker(), v.to_api_rec(vfses))).collect(),
|
||||
children.iter().map(|(k, v)| (intern(*k).marker(), v.to_api_rec(vfses))).collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
35
orchid-extension/src/fun.rs
Normal file
35
orchid-extension/src/fun.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use never::Never;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::atom::{AtomCard, OwnedAtom};
|
||||
use crate::expr::{ExprHandle, GenClause};
|
||||
use crate::system::SysCtx;
|
||||
|
||||
trait_set! {
|
||||
trait FunCB = FnOnce(ExprHandle) -> GenClause + DynClone + Send + Sync + 'static;
|
||||
}
|
||||
|
||||
pub struct Fun(Box<dyn FunCB>);
|
||||
impl Fun {
|
||||
pub fn new(f: impl FnOnce(ExprHandle) -> GenClause + Clone + Send + Sync + 'static) -> Self {
|
||||
Self(Box::new(f))
|
||||
}
|
||||
}
|
||||
impl Clone for Fun {
|
||||
fn clone(&self) -> Self { Self(clone_box(&*self.0)) }
|
||||
}
|
||||
impl AtomCard for Fun {
|
||||
type Data = ();
|
||||
type Req = Never;
|
||||
}
|
||||
impl OwnedAtom for Fun {
|
||||
fn val(&self) -> Cow<'_, Self::Data> { Cow::Owned(()) }
|
||||
fn call_ref(&self, arg: ExprHandle) -> GenClause { self.clone().call(arg) }
|
||||
fn call(self, arg: ExprHandle) -> GenClause { (self.0)(arg) }
|
||||
fn handle_req(&self, _ctx: SysCtx, req: Self::Req, _rep: &mut (impl std::io::Write + ?Sized)) {
|
||||
match req {}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,53 @@
|
||||
use std::ops::RangeInclusive;
|
||||
use std::ops::{Range, RangeInclusive};
|
||||
|
||||
use orchid_api::error::ProjResult;
|
||||
use orchid_api::tree::TokenTree;
|
||||
use orchid_api::error::ReportError;
|
||||
use orchid_api::parser::{LexId, SubLex};
|
||||
use orchid_api::proto::ExtMsgSet;
|
||||
use orchid_api::system::SysId;
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::reqnot::{ReqNot, Requester};
|
||||
|
||||
use crate::error::{
|
||||
err_from_api, err_to_api, pack_err, unpack_err, ProjectErrorObj, ProjectResult,
|
||||
};
|
||||
use crate::tree::{GenTok, GenTokTree};
|
||||
|
||||
pub struct LexContext<'a> {
|
||||
pub text: &'a Tok<String>,
|
||||
pub sys: SysId,
|
||||
pub id: LexId,
|
||||
pub pos: u32,
|
||||
pub reqnot: ReqNot<ExtMsgSet>,
|
||||
}
|
||||
impl<'a> LexContext<'a> {
|
||||
pub fn recurse(&self, tail: &'a str) -> ProjectResult<(&'a str, GenTokTree)> {
|
||||
let start = self.pos(tail);
|
||||
self
|
||||
.reqnot
|
||||
.request(SubLex { pos: start, id: self.id })
|
||||
.map_err(|e| pack_err(e.iter().map(|e| err_from_api(e, self.reqnot.clone()))))
|
||||
.map(|lx| (&self.text[lx.pos as usize..], GenTok::Slot(lx.ticket).at(start..lx.pos)))
|
||||
}
|
||||
|
||||
pub fn pos(&self, tail: &'a str) -> u32 { (self.text.len() - tail.len()) as u32 }
|
||||
|
||||
pub fn tok_ran(&self, len: u32, tail: &'a str) -> Range<u32> {
|
||||
self.pos(tail) - len..self.pos(tail)
|
||||
}
|
||||
|
||||
pub fn report(&self, e: ProjectErrorObj) {
|
||||
for e in unpack_err(e) {
|
||||
self.reqnot.notify(ReportError(self.sys, err_to_api(e)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Lexer: Send + Sync + Sized + Default + 'static {
|
||||
const CHAR_FILTER: &'static [RangeInclusive<char>];
|
||||
fn lex<'a>(
|
||||
tail: &'a str,
|
||||
recur: impl FnMut(&'a str) -> ProjResult<(&'a str, TokenTree)>,
|
||||
) -> Option<ProjResult<(&'a str, TokenTree)>>;
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> Option<ProjectResult<(&'a str, GenTokTree)>>;
|
||||
}
|
||||
|
||||
pub trait DynLexer: Send + Sync + 'static {
|
||||
@@ -16,8 +55,8 @@ pub trait DynLexer: Send + Sync + 'static {
|
||||
fn lex<'a>(
|
||||
&self,
|
||||
tail: &'a str,
|
||||
recur: &mut dyn FnMut(&'a str) -> ProjResult<(&'a str, TokenTree)>,
|
||||
) -> Option<ProjResult<(&'a str, TokenTree)>>;
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> Option<ProjectResult<(&'a str, GenTokTree)>>;
|
||||
}
|
||||
|
||||
impl<T: Lexer> DynLexer for T {
|
||||
@@ -25,9 +64,9 @@ impl<T: Lexer> DynLexer for T {
|
||||
fn lex<'a>(
|
||||
&self,
|
||||
tail: &'a str,
|
||||
recur: &mut dyn FnMut(&'a str) -> ProjResult<(&'a str, TokenTree)>,
|
||||
) -> Option<ProjResult<(&'a str, TokenTree)>> {
|
||||
T::lex(tail, recur)
|
||||
ctx: &'a LexContext<'a>,
|
||||
) -> Option<ProjectResult<(&'a str, GenTokTree)>> {
|
||||
T::lex(tail, ctx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
pub mod atom;
|
||||
pub mod entrypoint;
|
||||
pub mod error;
|
||||
pub mod expr;
|
||||
pub mod fs;
|
||||
pub mod fun;
|
||||
pub mod lexer;
|
||||
pub mod msg;
|
||||
pub mod other_system;
|
||||
pub mod system;
|
||||
pub mod system_ctor;
|
||||
pub mod tree;
|
||||
pub mod try_from_expr;
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::mem::size_of;
|
||||
|
||||
use orchid_api::atom::Atom;
|
||||
use orchid_api::expr::ExprTicket;
|
||||
use orchid_api::proto::ExtMsgSet;
|
||||
use orchid_api::system::SysId;
|
||||
use orchid_base::reqnot::ReqNot;
|
||||
|
||||
use crate::atom::{decode_atom, AtomCard, ForeignAtom};
|
||||
use crate::system::SystemCard;
|
||||
use crate::system::{DynSystemCard, SystemCard};
|
||||
|
||||
pub struct SystemHandle<C: SystemCard> {
|
||||
pub(crate) _card: PhantomData<C>,
|
||||
pub(crate) id: SysId,
|
||||
pub(crate) reqnot: ReqNot<ExtMsgSet>,
|
||||
}
|
||||
impl<T: SystemCard> SystemHandle<T> {
|
||||
pub(crate) fn new(id: SysId, reqnot: ReqNot<ExtMsgSet>) -> Self {
|
||||
Self { _card: PhantomData, id, reqnot }
|
||||
}
|
||||
impl<C: SystemCard> SystemHandle<C> {
|
||||
pub(crate) fn new(id: SysId) -> Self { Self { _card: PhantomData, id } }
|
||||
pub fn id(&self) -> SysId { self.id }
|
||||
pub fn wrap_atom<A: AtomCard<Owner = T>>(
|
||||
&self,
|
||||
api: Atom,
|
||||
ticket: ExprTicket,
|
||||
) -> Result<ForeignAtom<A>, Atom> {
|
||||
if api.owner == self.id {
|
||||
if let Some(value) = decode_atom::<A>(&T::default(), &api) {
|
||||
return Ok(ForeignAtom { ticket, sys: self.clone(), value, api });
|
||||
}
|
||||
}
|
||||
impl<C: SystemCard> Clone for SystemHandle<C> {
|
||||
fn clone(&self) -> Self { Self::new(self.id) }
|
||||
}
|
||||
|
||||
pub trait DynSystemHandle {
|
||||
fn id(&self) -> SysId;
|
||||
fn get_card(&self) -> &dyn DynSystemCard;
|
||||
}
|
||||
|
||||
pub fn leak_card<T: Default>() -> &'static T {
|
||||
const {
|
||||
if 0 != size_of::<T>() {
|
||||
panic!("Attempted to leak positively sized Card. Card types must always be zero-sized");
|
||||
}
|
||||
Err(api)
|
||||
}
|
||||
Box::leak(Box::default())
|
||||
}
|
||||
impl<T: SystemCard> Clone for SystemHandle<T> {
|
||||
fn clone(&self) -> Self { Self { reqnot: self.reqnot.clone(), _card: PhantomData, id: self.id } }
|
||||
|
||||
impl<C: SystemCard> DynSystemHandle for SystemHandle<C> {
|
||||
fn id(&self) -> SysId { self.id }
|
||||
fn get_card(&self) -> &'static dyn DynSystemCard { leak_card::<C>() }
|
||||
}
|
||||
|
||||
18
orchid-extension/src/proj_error.rs
Normal file
18
orchid-extension/src/proj_error.rs
Normal file
@@ -0,0 +1,18 @@
|
||||
//! Abstractions for handling various code-related errors under a common trait
|
||||
//! object.
|
||||
|
||||
use std::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::Arc;
|
||||
use std::{fmt, iter};
|
||||
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use itertools::Itertools;
|
||||
use orchid_api::error::{ProjErr, ProjErrLocation};
|
||||
|
||||
use crate::boxed_iter::{box_once, BoxedIter};
|
||||
use crate::intern::{deintern, intern, Token};
|
||||
use crate::location::{GetSrc, Position};
|
||||
#[allow(unused)] // for doc
|
||||
use crate::virt_fs::CodeNotFound;
|
||||
@@ -1,53 +1,90 @@
|
||||
use std::any::Any;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use orchid_api::expr::ExprTicket;
|
||||
use orchid_api::tree::TreeModule;
|
||||
use orchid_api::proto::ExtMsgSet;
|
||||
use orchid_api::system::SysId;
|
||||
use orchid_base::reqnot::ReqNot;
|
||||
use typeid::ConstTypeId;
|
||||
|
||||
use crate::atom::AtomInfo;
|
||||
use crate::expr::GenClause;
|
||||
use crate::atom::{decode_atom, owned_atom_info, AtomCard, AtomInfo, ForeignAtom, TypAtom};
|
||||
use crate::fs::DeclFs;
|
||||
use crate::fun::Fun;
|
||||
use crate::lexer::LexerObj;
|
||||
use crate::system_ctor::{CtedObj, SystemCtor};
|
||||
use crate::tree::GenTree;
|
||||
|
||||
/// System as consumed by foreign code
|
||||
pub trait SystemCard: Default + Send + Sync + 'static {
|
||||
const NAME: &'static str;
|
||||
type Ctor: SystemCtor;
|
||||
const ATOM_DEFS: &'static [Option<AtomInfo>];
|
||||
}
|
||||
|
||||
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) -> &'static [Option<AtomInfo>];
|
||||
fn atom_info_for(&self, tid: ConstTypeId) -> Option<(usize, &AtomInfo)> {
|
||||
(self.atoms().iter().enumerate())
|
||||
.filter_map(|(i, o)| o.as_ref().map(|a| (i, a)))
|
||||
.find(|ent| ent.1.tid == tid)
|
||||
}
|
||||
|
||||
/// Atoms supported by this package which may appear in all extensions.
|
||||
/// 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)
|
||||
const GENERAL_ATOMS: &[Option<AtomInfo>] = &[Some(owned_atom_info::<Fun>())];
|
||||
|
||||
pub fn atom_info_for(
|
||||
sys: &(impl DynSystemCard + ?Sized),
|
||||
tid: ConstTypeId,
|
||||
) -> Option<(u64, &AtomInfo)> {
|
||||
(sys.atoms().iter().enumerate().map(|(i, o)| (i as u64, o)))
|
||||
.chain(GENERAL_ATOMS.iter().enumerate().map(|(i, o)| (!(i as u64), o)))
|
||||
.filter_map(|(i, o)| o.as_ref().map(|a| (i, a)))
|
||||
.find(|ent| ent.1.tid == tid)
|
||||
}
|
||||
|
||||
pub fn atom_by_idx(sys: &(impl DynSystemCard + ?Sized), tid: u64) -> Option<&AtomInfo> {
|
||||
if (tid >> (u64::BITS - 1)) & 1 == 1 {
|
||||
GENERAL_ATOMS[!tid as usize].as_ref()
|
||||
} else {
|
||||
sys.atoms()[tid as usize].as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: SystemCard> DynSystemCard for T {
|
||||
fn name(&self) -> &'static str { Self::NAME }
|
||||
fn name(&self) -> &'static str { T::Ctor::NAME }
|
||||
fn atoms(&self) -> &'static [Option<AtomInfo>] { Self::ATOM_DEFS }
|
||||
}
|
||||
|
||||
/// System as defined by author
|
||||
pub trait System: Send + Sync + SystemCard {
|
||||
fn env() -> TreeModule;
|
||||
fn source() -> DeclFs;
|
||||
const LEXERS: &'static [LexerObj];
|
||||
pub trait System: Send + Sync + SystemCard + 'static {
|
||||
fn env() -> GenTree;
|
||||
fn vfs() -> DeclFs;
|
||||
fn lexers() -> Vec<LexerObj>;
|
||||
}
|
||||
|
||||
pub trait DynSystem: Send + Sync + 'static {
|
||||
fn env(&self) -> TreeModule;
|
||||
fn source(&self) -> DeclFs;
|
||||
fn lexers(&self) -> &'static [LexerObj];
|
||||
fn card(&self) -> &dyn DynSystemCard;
|
||||
fn dyn_env(&self) -> GenTree;
|
||||
fn dyn_vfs(&self) -> DeclFs;
|
||||
fn dyn_lexers(&self) -> Vec<LexerObj>;
|
||||
fn dyn_card(&self) -> &dyn DynSystemCard;
|
||||
}
|
||||
|
||||
impl<T: System> DynSystem for T {
|
||||
fn env(&self) -> TreeModule { <Self as System>::env() }
|
||||
fn source(&self) -> DeclFs { <Self as System>::source() }
|
||||
fn lexers(&self) -> &'static [LexerObj] { Self::LEXERS }
|
||||
fn card(&self) -> &dyn DynSystemCard { self }
|
||||
fn dyn_env(&self) -> GenTree { Self::env() }
|
||||
fn dyn_vfs(&self) -> DeclFs { Self::vfs() }
|
||||
fn dyn_lexers(&self) -> Vec<LexerObj> { Self::lexers() }
|
||||
fn dyn_card(&self) -> &dyn DynSystemCard { self }
|
||||
}
|
||||
|
||||
pub fn downcast_atom<A: AtomCard>(foreign: ForeignAtom) -> Result<TypAtom<A>, ForeignAtom> {
|
||||
match (foreign.expr.get_ctx().cted.deps())
|
||||
.find(|s| s.id() == foreign.atom.owner)
|
||||
.and_then(|sys| decode_atom::<A>(sys.get_card(), &foreign.atom))
|
||||
{
|
||||
None => Err(foreign),
|
||||
Some(value) => Ok(TypAtom { value, data: foreign }),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SysCtx {
|
||||
pub reqnot: ReqNot<ExtMsgSet>,
|
||||
pub id: SysId,
|
||||
pub cted: CtedObj,
|
||||
}
|
||||
|
||||
@@ -1,94 +1,136 @@
|
||||
use std::hash::{Hash as _, Hasher as _};
|
||||
use std::any::Any;
|
||||
use std::num::NonZeroU16;
|
||||
use std::sync::Arc;
|
||||
|
||||
use itertools::Itertools as _;
|
||||
use orchid_api::proto::ExtMsgSet;
|
||||
use orchid_api::system::{NewSystem, SysId, SystemDecl};
|
||||
use orchid_base::reqnot::ReqNot;
|
||||
use orchid_base::boxed_iter::{box_empty, box_once, BoxedIter};
|
||||
use ordered_float::NotNan;
|
||||
use typeid::ConstTypeId;
|
||||
|
||||
use crate::other_system::SystemHandle;
|
||||
use crate::other_system::{DynSystemHandle, SystemHandle};
|
||||
use crate::system::{DynSystem, System, SystemCard};
|
||||
|
||||
pub struct SystemParams<Ctor: SystemCtor + ?Sized> {
|
||||
pub deps: <Ctor::Deps as DepSet>::Sat,
|
||||
pub id: SysId,
|
||||
pub reqnot: ReqNot<ExtMsgSet>,
|
||||
pub struct Cted<Ctor: SystemCtor + ?Sized> {
|
||||
pub deps: <Ctor::Deps as DepDef>::Sat,
|
||||
pub inst: Arc<Ctor::Instance>,
|
||||
}
|
||||
impl<C: SystemCtor + ?Sized> Clone for Cted<C> {
|
||||
fn clone(&self) -> Self { Self { deps: self.deps.clone(), inst: self.inst.clone() } }
|
||||
}
|
||||
pub trait DynCted: Send + Sync + 'static {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
||||
fn inst(&self) -> Arc<dyn DynSystem>;
|
||||
}
|
||||
impl<C: SystemCtor + ?Sized> DynCted for Cted<C> {
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn deps<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)> { self.deps.iter() }
|
||||
fn inst(&self) -> Arc<dyn DynSystem> { self.inst.clone() }
|
||||
}
|
||||
pub type CtedObj = Arc<dyn DynCted>;
|
||||
|
||||
pub trait DepSat: Clone + Send + Sync + 'static {
|
||||
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)>;
|
||||
}
|
||||
|
||||
pub trait DepSet {
|
||||
type Sat;
|
||||
pub trait DepDef {
|
||||
type Sat: DepSat;
|
||||
fn report(names: &mut impl FnMut(&'static str));
|
||||
fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat;
|
||||
fn create(take: &mut impl FnMut() -> SysId) -> Self::Sat;
|
||||
}
|
||||
|
||||
impl<T: SystemCard> DepSet for T {
|
||||
impl<T: SystemCard> DepSat for SystemHandle<T> {
|
||||
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)> { box_once(self) }
|
||||
}
|
||||
|
||||
impl<T: SystemCard> DepDef for T {
|
||||
type Sat = SystemHandle<Self>;
|
||||
fn report(names: &mut impl FnMut(&'static str)) { names(T::NAME) }
|
||||
fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat {
|
||||
SystemHandle::new(take(), reqnot)
|
||||
}
|
||||
fn report(names: &mut impl FnMut(&'static str)) { names(T::Ctor::NAME) }
|
||||
fn create(take: &mut impl FnMut() -> SysId) -> Self::Sat { SystemHandle::new(take()) }
|
||||
}
|
||||
|
||||
pub trait SystemCtor: Send + 'static {
|
||||
type Deps: DepSet;
|
||||
impl DepSat for () {
|
||||
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)> { box_empty() }
|
||||
}
|
||||
|
||||
impl DepDef for () {
|
||||
type Sat = ();
|
||||
fn create(_: &mut impl FnMut() -> SysId) -> Self::Sat {}
|
||||
fn report(_: &mut impl FnMut(&'static str)) {}
|
||||
}
|
||||
|
||||
pub trait SystemCtor: Send + Sync + 'static {
|
||||
type Deps: DepDef;
|
||||
type Instance: System;
|
||||
const NAME: &'static str;
|
||||
const VERSION: f64;
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new(params: SystemParams<Self>) -> Self::Instance;
|
||||
fn inst() -> Option<Self::Instance>;
|
||||
}
|
||||
|
||||
pub trait DynSystemCtor: Send + 'static {
|
||||
fn decl(&self) -> SystemDecl;
|
||||
fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn DynSystem>;
|
||||
pub trait DynSystemCtor: Send + Sync + 'static {
|
||||
fn decl(&self, id: NonZeroU16) -> SystemDecl;
|
||||
fn new_system(&self, new: &NewSystem) -> CtedObj;
|
||||
}
|
||||
|
||||
impl<T: SystemCtor> DynSystemCtor for T {
|
||||
fn decl(&self) -> SystemDecl {
|
||||
fn decl(&self, id: NonZeroU16) -> SystemDecl {
|
||||
// Version is equivalent to priority for all practical purposes
|
||||
let priority = NotNan::new(T::VERSION).unwrap();
|
||||
// aggregate depends names
|
||||
let mut depends = Vec::new();
|
||||
T::Deps::report(&mut |n| depends.push(n.to_string()));
|
||||
// generate definitely unique id
|
||||
let mut ahash = ahash::AHasher::default();
|
||||
ConstTypeId::of::<T>().hash(&mut ahash);
|
||||
let id = (ahash.finish().to_be_bytes().into_iter().tuples())
|
||||
.map(|(l, b)| u16::from_be_bytes([l, b]))
|
||||
.fold(0, |a, b| a ^ b);
|
||||
SystemDecl { name: T::NAME.to_string(), depends, id, priority }
|
||||
}
|
||||
fn new_system(&self, new: &NewSystem, reqnot: ReqNot<ExtMsgSet>) -> Box<dyn DynSystem> {
|
||||
let mut ids = new.depends.iter().copied();
|
||||
Box::new(T::new(SystemParams {
|
||||
deps: T::Deps::create(&mut || ids.next().unwrap(), reqnot.clone()),
|
||||
id: new.id,
|
||||
reqnot,
|
||||
}))
|
||||
fn new_system(&self, NewSystem { system: _, id: _, depends }: &NewSystem) -> CtedObj {
|
||||
let mut ids = depends.iter().copied();
|
||||
let inst = Arc::new(T::inst().expect("Constructor did not create system"));
|
||||
let deps = T::Deps::create(&mut || ids.next().unwrap());
|
||||
Arc::new(Cted::<T> { deps, inst })
|
||||
}
|
||||
}
|
||||
|
||||
mod dep_set_tuple_impls {
|
||||
use orchid_api::proto::ExtMsgSet;
|
||||
use orchid_api::system::SysId;
|
||||
use orchid_base::reqnot::ReqNot;
|
||||
use orchid_base::box_chain;
|
||||
use orchid_base::boxed_iter::BoxedIter;
|
||||
use paste::paste;
|
||||
|
||||
use super::DepSet;
|
||||
use super::{DepDef, DepSat};
|
||||
use crate::system_ctor::DynSystemHandle;
|
||||
|
||||
macro_rules! dep_set_tuple_impl {
|
||||
($($name:ident),*) => {
|
||||
impl<$( $name :DepSet ),*> DepSet for ( $( $name , )* ) {
|
||||
impl<$( $name :DepSat ),*> DepSat for ( $( $name , )* ) {
|
||||
fn iter<'a>(&'a self) -> BoxedIter<'a, &'a (dyn DynSystemHandle + 'a)> {
|
||||
// we're using the Paste crate to convert the names to lowercase,
|
||||
// so `dep_set_tuple_impl!(A, B, C)` generates `let (a, b, c,) = self;`
|
||||
// This step isn't really required for correctness, but Rust warns about uppercase
|
||||
// variable names.
|
||||
paste!{
|
||||
let (
|
||||
$(
|
||||
[< $name :lower >] ,
|
||||
)*
|
||||
) = self;
|
||||
box_chain! (
|
||||
$(
|
||||
[< $name :lower >] .iter()
|
||||
),*
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<$( $name :DepDef ),*> DepDef for ( $( $name , )* ) {
|
||||
type Sat = ( $( $name ::Sat , )* );
|
||||
fn report(names: &mut impl FnMut(&'static str)) {
|
||||
$(
|
||||
$name ::report(names);
|
||||
)*
|
||||
}
|
||||
fn create(take: &mut impl FnMut() -> SysId, reqnot: ReqNot<ExtMsgSet>) -> Self::Sat {
|
||||
fn create(take: &mut impl FnMut() -> SysId) -> Self::Sat {
|
||||
(
|
||||
$(
|
||||
$name ::create(take, reqnot.clone()),
|
||||
$name ::create(take),
|
||||
)*
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
pub struct TraitObject<T: ?Sized + Coding + 'static>(Box<T>, Arc<dyn Fn(&mut dyn Read) -> Self>);
|
||||
impl<T: ?Sized + Coding + 'static> TraitObject<T> {
|
||||
fn inner_type_id(&self) -> ConstTypeId { self.0.as_ref().type_id() }
|
||||
fn get_decoder(&self) -> Arc<dyn Fn(&mut dyn Read) -> Self> { self.1.clone() }
|
||||
}
|
||||
pub trait AsTraitObject<T: ?Sized + Coding + 'static>: 'static {
|
||||
fn trait_box(self) -> Box<T>;
|
||||
fn into_trait_object(self) -> TraitObject<T>
|
||||
where Self: Sized + Coding {
|
||||
let decoder = Self::get_decoder(Self::into_trait_object);
|
||||
TraitObject(self.trait_box(), Arc::new(decoder))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TraitObjectCoder<T: ?Sized + Coding + 'static> {
|
||||
entries: HashMap<u64, Box<dyn Fn(&mut dyn Read) -> TraitObject<T>>>,
|
||||
}
|
||||
impl<T: ?Sized + Coding + 'static> TraitObjectCoder<T> {
|
||||
pub fn add_type<U: AsTraitObject<T> + Coding>(&mut self, tid_hash: u64) {
|
||||
self.entries.entry(tid_hash).or_insert_with(|| Box::new(|b| U::decode(b).into_trait_object()));
|
||||
}
|
||||
pub fn add_obj(&mut self, tid_hash: u64, obj: &TraitObject<T>) {
|
||||
self.entries.entry(tid_hash).or_insert_with(|| {
|
||||
let decoder = obj.get_decoder();
|
||||
Box::new(move |b| decoder(b))
|
||||
});
|
||||
}
|
||||
pub fn encode<U: AsTraitObject<T> + Coding>(&mut self, data: U, out: &mut impl Write) {
|
||||
let tid = hash_tid(ConstTypeId::of::<U>());
|
||||
tid.encode(out);
|
||||
self.add_type::<U>(tid);
|
||||
data.encode(out);
|
||||
}
|
||||
pub fn encode_obj(&mut self, data: &TraitObject<T>, out: &mut impl Write) {
|
||||
let tid = hash_tid(data.inner_type_id());
|
||||
self.add_obj(tid, data);
|
||||
tid.encode(out);
|
||||
data.0.as_ref().encode(out);
|
||||
}
|
||||
pub fn decode(&mut self, src: &mut impl Read) -> TraitObject<T> {
|
||||
let tid = u64::decode(src);
|
||||
(self.entries.get(&tid).expect("Received object of unknown ConstTypeId"))(src)
|
||||
}
|
||||
}
|
||||
178
orchid-extension/src/tree.rs
Normal file
178
orchid-extension/src/tree.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use std::ops::Range;
|
||||
|
||||
use ahash::HashMap;
|
||||
use dyn_clone::{clone_box, DynClone};
|
||||
use itertools::Itertools;
|
||||
use orchid_api::tree::{
|
||||
MacroRule, Paren, Placeholder, PlaceholderKind, Token, TokenTree, Tree, TreeId, TreeModule,
|
||||
TreeTicket,
|
||||
};
|
||||
use orchid_base::interner::{intern, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::VName;
|
||||
use ordered_float::NotNan;
|
||||
use trait_set::trait_set;
|
||||
|
||||
use crate::atom::AtomFactory;
|
||||
use crate::expr::GenExpr;
|
||||
use crate::system::DynSystem;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenPh {
|
||||
pub name: Tok<String>,
|
||||
pub kind: PlaceholderKind,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenTokTree {
|
||||
pub tok: GenTok,
|
||||
pub range: Range<u32>,
|
||||
}
|
||||
impl GenTokTree {
|
||||
pub fn into_api(self, sys: &dyn DynSystem) -> TokenTree {
|
||||
TokenTree { token: self.tok.into_api(sys), range: self.range }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ph(s: &str) -> GenPh {
|
||||
match s.strip_prefix("..") {
|
||||
Some(v_tail) => {
|
||||
let (mid, priority) = match v_tail.split_once(':') {
|
||||
Some((h, t)) => (h, t.parse().expect("priority not an u8")),
|
||||
None => (v_tail, 0),
|
||||
};
|
||||
let (name, nonzero) = match mid.strip_prefix(".$") {
|
||||
Some(name) => (name, true),
|
||||
None => (mid.strip_prefix('$').expect("Invalid placeholder"), false),
|
||||
};
|
||||
if konst::string::starts_with(name, "_") {
|
||||
panic!("Names starting with an underscore indicate a single-name scalar placeholder")
|
||||
}
|
||||
GenPh { name: intern(name), kind: PlaceholderKind::Vector { nonzero, priority } }
|
||||
},
|
||||
None => match konst::string::strip_prefix(s, "$_") {
|
||||
Some(name) => GenPh { name: intern(name), kind: PlaceholderKind::Name },
|
||||
None => match konst::string::strip_prefix(s, "$") {
|
||||
None => panic!("Invalid placeholder"),
|
||||
Some(name) => GenPh { name: intern(name), kind: PlaceholderKind::Scalar },
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum GenTok {
|
||||
Lambda(Vec<GenTokTree>, Vec<GenTokTree>),
|
||||
Name(VName),
|
||||
S(Paren, Vec<GenTokTree>),
|
||||
Atom(AtomFactory),
|
||||
Slot(TreeTicket),
|
||||
Ph(GenPh),
|
||||
}
|
||||
impl GenTok {
|
||||
pub fn at(self, range: Range<u32>) -> GenTokTree { GenTokTree { tok: self, range } }
|
||||
pub fn into_api(self, sys: &dyn DynSystem) -> Token {
|
||||
match self {
|
||||
Self::Lambda(x, body) => Token::Lambda(
|
||||
x.into_iter().map(|tt| tt.into_api(sys)).collect_vec(),
|
||||
body.into_iter().map(|tt| tt.into_api(sys)).collect_vec(),
|
||||
),
|
||||
Self::Name(n) => Token::Name(n.into_iter().map(|t| t.marker()).collect_vec()),
|
||||
Self::Ph(GenPh { name, kind }) => Token::Ph(Placeholder { name: name.marker(), kind }),
|
||||
Self::S(p, body) => Token::S(p, body.into_iter().map(|tt| tt.into_api(sys)).collect_vec()),
|
||||
Self::Slot(tk) => Token::Slot(tk),
|
||||
Self::Atom(at) => Token::Atom(at.build(sys)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenMacro {
|
||||
pub pattern: Vec<GenTokTree>,
|
||||
pub priority: NotNan<f64>,
|
||||
pub template: Vec<GenTokTree>,
|
||||
}
|
||||
|
||||
pub fn tokv_into_api(
|
||||
tokv: impl IntoIterator<Item = GenTokTree>,
|
||||
sys: &dyn DynSystem,
|
||||
) -> Vec<TokenTree> {
|
||||
tokv.into_iter().map(|tok| tok.into_api(sys)).collect_vec()
|
||||
}
|
||||
|
||||
pub fn wrap_tokv(items: Vec<GenTokTree>, range: Range<u32>) -> GenTokTree {
|
||||
match items.len() {
|
||||
1 => items.into_iter().next().unwrap(),
|
||||
_ => GenTok::S(Paren::Round, items).at(range),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct GenTree {
|
||||
pub item: GenItem,
|
||||
pub location: Pos,
|
||||
}
|
||||
impl GenTree {
|
||||
pub fn cnst(gc: GenExpr) -> Self { GenItem::Const(gc).at(Pos::Inherit) }
|
||||
pub fn module<'a>(entries: impl IntoIterator<Item = (&'a str, GenTree)>) -> Self {
|
||||
GenItem::Mod(entries.into_iter().map(|(k, v)| (k.to_string(), v)).collect()).at(Pos::Inherit)
|
||||
}
|
||||
pub fn rule(
|
||||
prio: f64,
|
||||
pat: impl IntoIterator<Item = GenTokTree>,
|
||||
tpl: impl IntoIterator<Item = GenTokTree>,
|
||||
) -> Self {
|
||||
GenItem::Rule(GenMacro {
|
||||
pattern: pat.into_iter().collect(),
|
||||
priority: NotNan::new(prio).expect("expected to be static"),
|
||||
template: tpl.into_iter().collect(),
|
||||
})
|
||||
.at(Pos::Inherit)
|
||||
}
|
||||
pub fn into_api(
|
||||
self,
|
||||
sys: &dyn DynSystem,
|
||||
with_lazy: &mut impl FnMut(LazyTreeFactory) -> TreeId,
|
||||
) -> Tree {
|
||||
match self.item {
|
||||
GenItem::Const(gc) => Tree::Const(gc.into_api(sys)),
|
||||
GenItem::Rule(GenMacro { pattern, priority, template }) => Tree::Rule(MacroRule {
|
||||
pattern: tokv_into_api(pattern, sys),
|
||||
priority,
|
||||
template: tokv_into_api(template, sys),
|
||||
}),
|
||||
GenItem::Mod(entv) => Tree::Mod(TreeModule {
|
||||
children: entv
|
||||
.into_iter()
|
||||
.map(|(name, tree)| (name.to_string(), tree.into_api(sys, with_lazy)))
|
||||
.collect(),
|
||||
}),
|
||||
GenItem::Lazy(cb) => Tree::Lazy(with_lazy(cb)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait_set! {
|
||||
trait LazyTreeCallback = FnMut() -> GenTree + Send + Sync + DynClone
|
||||
}
|
||||
pub struct LazyTreeFactory(Box<dyn LazyTreeCallback>);
|
||||
impl LazyTreeFactory {
|
||||
pub fn new(cb: impl FnMut() -> GenTree + Send + Sync + Clone + 'static) -> Self {
|
||||
Self(Box::new(cb))
|
||||
}
|
||||
pub fn build(&mut self) -> GenTree { (self.0)() }
|
||||
}
|
||||
impl Clone for LazyTreeFactory {
|
||||
fn clone(&self) -> Self { Self(clone_box(&*self.0)) }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum GenItem {
|
||||
Const(GenExpr),
|
||||
Mod(HashMap<String, GenTree>),
|
||||
Rule(GenMacro),
|
||||
Lazy(LazyTreeFactory),
|
||||
}
|
||||
impl GenItem {
|
||||
pub fn at(self, position: Pos) -> GenTree { GenTree { item: self, location: position } }
|
||||
}
|
||||
16
orchid-extension/src/try_from_expr.rs
Normal file
16
orchid-extension/src/try_from_expr.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use crate::error::ProjectResult;
|
||||
use crate::expr::{ExprHandle, OwnedExpr};
|
||||
|
||||
pub trait TryFromExpr: Sized {
|
||||
fn try_from_expr(expr: ExprHandle) -> ProjectResult<Self>;
|
||||
}
|
||||
|
||||
impl TryFromExpr for OwnedExpr {
|
||||
fn try_from_expr(expr: ExprHandle) -> ProjectResult<Self> { Ok(OwnedExpr::new(expr)) }
|
||||
}
|
||||
|
||||
impl<T: TryFromExpr, U: TryFromExpr> TryFromExpr for (T, U) {
|
||||
fn try_from_expr(expr: ExprHandle) -> ProjectResult<Self> {
|
||||
Ok((T::try_from_expr(expr.clone())?, U::try_from_expr(expr)?))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user