temp commit

This commit is contained in:
2025-07-12 00:46:10 +02:00
parent 1868f1a506
commit fe89188c4b
60 changed files with 1536 additions and 709 deletions

View File

@@ -20,10 +20,12 @@ pub struct AtomData {
data: Vec<u8>,
}
impl AtomData {
#[must_use]
fn api(self) -> api::Atom {
let (owner, drop, data) = self.destructure();
api::Atom { data, drop, owner: owner.id() }
}
#[must_use]
fn api_ref(&self) -> api::Atom {
api::Atom { data: self.data.clone(), drop: self.drop, owner: self.owner.id() }
}
@@ -48,6 +50,7 @@ impl fmt::Debug for AtomData {
#[derive(Clone, Debug)]
pub struct AtomHand(Rc<AtomData>);
impl AtomHand {
#[must_use]
pub(crate) async fn new(api::Atom { data, drop, owner }: api::Atom, ctx: &Ctx) -> Self {
let create = || async {
let owner = ctx.system_inst(owner).await.expect("Dropped system created atom");
@@ -67,6 +70,7 @@ impl AtomHand {
create().await
}
}
#[must_use]
pub async fn call(self, arg: Expr) -> api::Expression {
let owner_sys = self.0.owner.clone();
let reqnot = owner_sys.reqnot();
@@ -76,13 +80,18 @@ impl AtomHand {
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), arg.id())).await,
}
}
#[must_use]
pub fn sys(&self) -> &System { &self.0.owner }
#[must_use]
pub fn ext(&self) -> &Extension { self.sys().ext() }
pub async fn req(&self, key: api::TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
self.0.owner.reqnot().request(api::Fwded(self.0.api_ref(), key, req)).await
}
#[must_use]
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
#[must_use]
pub async fn to_string(&self) -> String { take_first_fmt(self, &self.0.owner.ctx().i).await }
#[must_use]
pub fn downgrade(&self) -> WeakAtomHand { WeakAtomHand(Rc::downgrade(&self.0)) }
}
impl Format for AtomHand {
@@ -100,5 +109,6 @@ impl AtomRepr for AtomHand {
pub struct WeakAtomHand(Weak<AtomData>);
impl WeakAtomHand {
#[must_use]
pub fn upgrade(&self) -> Option<AtomHand> { self.0.upgrade().map(AtomHand) }
}

View File

@@ -3,7 +3,6 @@ use std::num::{NonZero, NonZeroU16};
use std::rc::{Rc, Weak};
use std::{fmt, ops};
use async_once_cell::OnceCell;
use async_std::sync::RwLock;
use hashbrown::HashMap;
use orchid_api::SysId;
@@ -13,17 +12,17 @@ use orchid_base::interner::Interner;
use crate::api;
use crate::atom::WeakAtomHand;
use crate::expr_store::ExprStore;
use crate::parsed::Root;
use crate::system::{System, WeakSystem};
use crate::tree::WeakRoot;
pub struct CtxData {
pub i: Rc<Interner>,
pub i: Interner,
pub spawn: Spawner,
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
pub system_id: RefCell<NonZeroU16>,
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
pub common_exprs: ExprStore,
pub root: OnceCell<Weak<Root>>,
pub root: RwLock<WeakRoot>,
}
#[derive(Clone)]
pub struct Ctx(Rc<CtxData>);
@@ -31,30 +30,39 @@ impl ops::Deref for Ctx {
type Target = CtxData;
fn deref(&self) -> &Self::Target { &self.0 }
}
#[derive(Clone)]
pub struct WeakCtx(Weak<CtxData>);
impl WeakCtx {
#[must_use]
pub fn try_upgrade(&self) -> Option<Ctx> { Some(Ctx(self.0.upgrade()?)) }
#[must_use]
pub fn upgrade(&self) -> Ctx { self.try_upgrade().expect("Ctx manually kept alive until exit") }
}
impl Ctx {
#[must_use]
pub fn new(spawn: Spawner) -> Self {
Self(Rc::new(CtxData {
spawn,
i: Rc::default(),
i: Interner::default(),
systems: RwLock::default(),
system_id: RefCell::new(NonZero::new(1).unwrap()),
owned_atoms: RwLock::default(),
common_exprs: ExprStore::default(),
root: OnceCell::default(),
root: RwLock::default(),
}))
}
#[must_use]
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
self.systems.read().await.get(&id).and_then(WeakSystem::upgrade)
}
#[must_use]
pub(crate) fn next_sys_id(&self) -> api::SysId {
let mut g = self.system_id.borrow_mut();
*g = g.checked_add(1).unwrap_or(NonZeroU16::new(1).unwrap());
SysId(*g)
}
pub async fn set_root(&self, root: Weak<Root>) {
assert!(self.root.get().is_none(), "Root already assigned");
self.root.get_or_init(async { root }).await;
}
#[must_use]
pub fn downgrade(&self) -> WeakCtx { WeakCtx(Rc::downgrade(&self.0)) }
}
impl fmt::Debug for Ctx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@@ -1,16 +1,9 @@
use std::collections::VecDeque;
use futures::FutureExt;
use hashbrown::{HashMap, HashSet};
use itertools::{Either, Itertools};
use hashbrown::HashSet;
use itertools::Itertools;
use orchid_base::error::{OrcErr, OrcRes, Reporter, mk_err, mk_errv};
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::name::{NameLike, Sym, VName};
use substack::Substack;
use crate::expr::Expr;
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule};
use orchid_base::name::VName;
/// Errors produced by absolute_path
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
@@ -48,29 +41,24 @@ impl AbsPathError {
///
/// if the relative path contains as many or more `super` segments than the
/// length of the absolute path.
pub fn absolute_path(
pub async fn absolute_path(
mut cwd: &[Tok<String>],
mut rel: &[Tok<String>],
i: &Interner,
) -> Result<VName, AbsPathError> {
let mut relative = false;
if rel.first().map(|t| t.as_str()) == Some("self") {
relative = true;
rel = rel.split_first().expect("checked above").1;
let i_self = i.i("self").await;
let i_super = i.i("super").await;
let relative = rel.first().is_some_and(|s| *s != i_self && *s != i_super);
if let Some((_, tail)) = rel.split_first().filter(|(h, _)| **h != i_self) {
rel = tail;
} else {
while rel.first().map(|t| t.as_str()) == Some("super") {
match cwd.split_last() {
Some((_, torso)) => cwd = torso,
None => return Err(AbsPathError::TooManySupers),
};
rel = rel.split_first().expect("checked above").1;
relative = true;
while let Some((_, tail)) = rel.split_first().filter(|(h, _)| **h == i_super) {
cwd = cwd.split_last().ok_or(AbsPathError::TooManySupers)?.1;
rel = tail;
}
}
match relative {
true => VName::new(cwd.iter().chain(rel).cloned()),
false => VName::new(rel.to_vec()),
}
.map_err(|_| AbsPathError::RootPath)
if relative { VName::new(cwd.iter().chain(rel).cloned()) } else { VName::new(rel.to_vec()) }
.map_err(|_| AbsPathError::RootPath)
}
pub struct DealiasCtx<'a> {
@@ -84,8 +72,7 @@ pub async fn resolv_glob<Mod: Tree>(
abs_path: &[Tok<String>],
pos: Pos,
i: &Interner,
rep: &Reporter,
ctx: &mut Mod::Ctx,
ctx: &mut Mod::Ctx<'_>,
) -> OrcRes<HashSet<Tok<String>>> {
let coprefix_len = cwd.iter().zip(abs_path).take_while(|(a, b)| a == b).count();
let (co_prefix, diff_path) = abs_path.split_at(abs_path.len().min(coprefix_len + 1));
@@ -96,35 +83,34 @@ pub async fn resolv_glob<Mod: Tree>(
Err(e) => {
let path = abs_path[..=coprefix_len + e.pos].iter().join("::");
let (tk, msg) = match e.kind {
ChildErrorKind::Constant =>
(i.i("Invalid import path").await, format!("{path} is a const")),
ChildErrorKind::Missing => (i.i("Invalid import path").await, format!("{path} not found")),
ChildErrorKind::Private => (i.i("Import inaccessible").await, format!("{path} is private")),
ChildErrorKind::Constant => ("Invalid import path", format!("{path} is a const")),
ChildErrorKind::Missing => ("Invalid import path", format!("{path} not found")),
ChildErrorKind::Private => ("Import inaccessible", format!("{path} is private")),
};
return Err(mk_errv(tk, msg, [pos.into()]));
return Err(mk_errv(i.i(tk).await, msg, [pos]));
},
};
Ok(target_module.children(coprefix_len < abs_path.len()))
}
pub enum ChildResult<'a, T: Tree + ?Sized> {
Value(&'a T),
Err(ChildErrorKind),
Alias(&'a [Tok<String>]),
}
pub type ChildResult<'a, T> = Result<&'a T, ChildErrorKind>;
pub trait Tree {
type Ctx;
type Ctx<'a>;
#[must_use]
fn children(&self, public_only: bool) -> HashSet<Tok<String>>;
#[must_use]
fn child(
&self,
key: Tok<String>,
public_only: bool,
ctx: &mut Self::Ctx,
ctx: &mut Self::Ctx<'_>,
) -> impl Future<Output = ChildResult<'_, Self>>;
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub enum ChildErrorKind {
Missing,
/// Only thrown if public_only is true
Private,
Constant,
}
@@ -144,42 +130,17 @@ pub struct ChildError {
//
// caveat: we need to check EVERY IMPORT to ensure that all
// errors are raised
async fn walk_no_access_chk<'a, T: Tree>(
root: &'a T,
cur: &mut &'a T,
path: impl IntoIterator<Item = Tok<String>, IntoIter: DoubleEndedIterator>,
ctx: &mut T::Ctx,
) -> Result<(), ChildErrorKind> {
// this VecDeque is used like a stack to leverage its Extend implementation.
let mut path: VecDeque<Tok<String>> = path.into_iter().rev().collect();
while let Some(step) = path.pop_back() {
match cur.child(step, false, ctx).await {
ChildResult::Alias(target) => {
path.extend(target.iter().cloned().rev());
*cur = root;
},
ChildResult::Err(e) => return Err(e),
ChildResult::Value(v) => *cur = v,
}
}
Ok(())
}
async fn walk<'a, T: Tree>(
pub async fn walk<'a, T: Tree>(
root: &'a T,
public_only: bool,
path: impl IntoIterator<Item = Tok<String>>,
ctx: &mut T::Ctx,
ctx: &mut T::Ctx<'_>,
) -> Result<&'a T, ChildError> {
let mut cur = root;
for (i, item) in path.into_iter().enumerate() {
match cur.child(item, public_only, ctx).await {
ChildResult::Value(v) => cur = v,
ChildResult::Ok(v) => cur = v,
ChildResult::Err(kind) => return Err(ChildError { pos: i, kind }),
ChildResult::Alias(path) => (walk_no_access_chk(root, &mut cur, path.iter().cloned(), ctx)
.await)
.map_err(|kind| ChildError { kind, pos: i })?,
}
}
Ok(cur)

View File

@@ -10,6 +10,7 @@ use orchid_base::logging::Logger;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, ExprParseCtx, PathSet, PathSetBuilder, Step};
use crate::tree::Root;
type ExprGuard = Bound<RwLockWriteGuard<'static, ExprKind>, Expr>;
@@ -36,16 +37,21 @@ pub struct ExecCtx {
cur_pos: Pos,
did_pop: bool,
logger: Logger,
root: Root,
}
impl ExecCtx {
pub async fn new(ctx: Ctx, logger: Logger, init: Expr) -> Self {
#[must_use]
pub async fn new(ctx: Ctx, logger: Logger, root: Root, init: Expr) -> Self {
let cur_pos = init.pos();
let cur = Bound::async_new(init, |init| init.kind().write()).await;
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger }
Self { ctx, gas: None, stack: vec![], cur, cur_pos, did_pop: false, logger, root }
}
#[must_use]
pub fn remaining_gas(&self) -> u64 { self.gas.expect("queried remaining_gas but no gas was set") }
pub fn set_gas(&mut self, gas: Option<u64>) { self.gas = gas }
#[must_use]
pub fn idle(&self) -> bool { self.did_pop }
#[must_use]
pub fn result(self) -> ExecResult {
if self.idle() {
match &*self.cur {
@@ -56,15 +62,18 @@ impl ExecCtx {
ExecResult::Gas(self)
}
}
#[must_use]
pub fn use_gas(&mut self, amount: u64) -> bool {
if let Some(gas) = &mut self.gas {
*gas -= amount;
}
self.gas != Some(0)
}
#[must_use]
pub async fn try_lock(&self, ex: &Expr) -> ExprGuard {
Bound::async_new(ex.clone(), |ex| ex.kind().write()).await
}
#[must_use]
pub async fn unpack_ident(&self, ex: &Expr) -> Expr {
match ex.kind().try_write().as_deref_mut() {
Some(ExprKind::Identity(ex)) => {
@@ -89,14 +98,11 @@ impl ExecCtx {
},
ExprKind::Seq(a, b) if !self.did_pop => (ExprKind::Seq(a.clone(), b), StackOp::Push(a)),
ExprKind::Seq(_, b) => (ExprKind::Identity(b), StackOp::Nop),
ExprKind::Const(name) => {
let root = (self.ctx.root.get().and_then(|v| v.upgrade()))
.expect("Root not assigned before execute call");
match root.get_const_value(name, self.cur_pos.clone(), self.ctx.clone()).await {
ExprKind::Const(name) =>
match self.root.get_const_value(name, self.cur_pos.clone()).await {
Err(e) => (ExprKind::Bottom(e), StackOp::Pop),
Ok(v) => (ExprKind::Identity(v), StackOp::Nop),
}
},
},
ExprKind::Arg => panic!("This should not appear outside function bodies"),
ek @ ExprKind::Atom(_) => (ek, StackOp::Pop),
ExprKind::Bottom(bot) => (ExprKind::Bottom(bot.clone()), StackOp::Unwind(bot)),
@@ -105,7 +111,7 @@ impl ExecCtx {
Ok(atom) => {
let ext = atom.sys().ext().clone();
let x_norm = self.unpack_ident(&x).await;
let mut parse_ctx = ExprParseCtx { ctx: self.ctx.clone(), exprs: ext.exprs().clone() };
let mut parse_ctx = ExprParseCtx { ctx: &self.ctx, exprs: ext.exprs() };
let val =
Expr::from_api(&atom.call(x_norm).await, PathSetBuilder::new(), &mut parse_ctx).await;
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
@@ -117,12 +123,10 @@ impl ExecCtx {
ExprKind::Atom(a) => {
let ext = a.sys().ext().clone();
let x_norm = self.unpack_ident(&x).await;
let mut parse_ctx =
ExprParseCtx { ctx: ext.ctx().clone(), exprs: ext.exprs().clone() };
let val = Expr::from_api(
&a.clone().call(x_norm).await,
PathSetBuilder::new(),
&mut parse_ctx,
&mut ExprParseCtx { ctx: ext.ctx(), exprs: ext.exprs() },
)
.await;
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
@@ -168,6 +172,7 @@ impl ExecCtx {
}
}
#[must_use]
async fn substitute(
src: &Expr,
path: &[Step],

View File

@@ -10,10 +10,11 @@ use hashbrown::HashSet;
use itertools::Itertools;
use orchid_base::error::OrcErrv;
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::location::Pos;
use orchid_base::interner::Interner;
use orchid_base::location::{Pos, SrcRange};
use orchid_base::name::Sym;
use orchid_base::tl_cache;
use orchid_base::tree::{AtomRepr, indent};
use orchid_base::tree::{AtomRepr, TokenVariant, indent};
use substack::Substack;
use crate::api;
@@ -22,9 +23,9 @@ use crate::ctx::Ctx;
use crate::expr_store::ExprStore;
#[derive(Clone)]
pub struct ExprParseCtx {
pub ctx: Ctx,
pub exprs: ExprStore,
pub struct ExprParseCtx<'a> {
pub ctx: &'a Ctx,
pub exprs: &'a ExprStore,
}
#[derive(Debug)]
@@ -36,6 +37,7 @@ pub struct ExprData {
#[derive(Clone, Debug)]
pub struct Expr(Rc<ExprData>);
impl Expr {
#[must_use]
pub fn pos(&self) -> Pos { self.0.pos.clone() }
pub async fn try_into_owned_atom(self) -> Result<AtomHand, Self> {
match Rc::try_unwrap(self.0) {
@@ -46,25 +48,29 @@ impl Expr {
},
}
}
#[must_use]
pub async fn as_atom(&self) -> Option<AtomHand> {
if let ExprKind::Atom(a) = &*self.kind().read().await { Some(a.clone()) } else { None }
}
#[must_use]
pub fn strong_count(&self) -> usize { Rc::strong_count(&self.0) }
#[must_use]
pub fn id(&self) -> api::ExprTicket {
api::ExprTicket(
NonZeroU64::new(self.0.as_ref() as *const ExprData as usize as u64)
.expect("this is a ref, it cannot be null"),
)
}
#[must_use]
pub async fn from_api(
api: &api::Expression,
psb: PathSetBuilder<'_, u64>,
ctx: &mut ExprParseCtx,
ctx: &mut ExprParseCtx<'_>,
) -> Self {
let pos = Pos::from_api(&api.location, &ctx.ctx.i).await;
let kind = match &api.kind {
api::ExpressionKind::Arg(n) => {
assert!(psb.register_arg(&n), "Arguments must be enclosed in a matching lambda");
assert!(psb.register_arg(n), "Arguments must be enclosed in a matching lambda");
ExprKind::Arg
},
api::ExpressionKind::Bottom(bot) =>
@@ -72,14 +78,14 @@ impl Expr {
api::ExpressionKind::Call(f, x) => {
let (lpsb, rpsb) = psb.split();
ExprKind::Call(
Expr::from_api(&f, lpsb, ctx).boxed_local().await,
Expr::from_api(&x, rpsb, ctx).boxed_local().await,
Expr::from_api(f, lpsb, ctx).boxed_local().await,
Expr::from_api(x, rpsb, ctx).boxed_local().await,
)
},
api::ExpressionKind::Const(name) => ExprKind::Const(Sym::from_api(*name, &ctx.ctx.i).await),
api::ExpressionKind::Lambda(x, body) => {
let lbuilder = psb.lambda(&x);
let body = Expr::from_api(&body, lbuilder.stack(), ctx).boxed_local().await;
let lbuilder = psb.lambda(x);
let body = Expr::from_api(body, lbuilder.stack(), ctx).boxed_local().await;
ExprKind::Lambda(lbuilder.collect(), body)
},
api::ExpressionKind::NewAtom(a) =>
@@ -88,13 +94,14 @@ impl Expr {
api::ExpressionKind::Seq(a, b) => {
let (apsb, bpsb) = psb.split();
ExprKind::Seq(
Expr::from_api(&a, apsb, ctx).boxed_local().await,
Expr::from_api(&b, bpsb, ctx).boxed_local().await,
Expr::from_api(a, apsb, ctx).boxed_local().await,
Expr::from_api(b, bpsb, ctx).boxed_local().await,
)
},
};
Self(Rc::new(ExprData { pos, kind: RwLock::new(kind) }))
}
#[must_use]
pub async fn to_api(&self) -> api::InspectedKind {
use api::InspectedKind as K;
match &*self.0.kind.read().await {
@@ -104,6 +111,7 @@ impl Expr {
_ => K::Opaque,
}
}
#[must_use]
pub fn kind(&self) -> &RwLock<ExprKind> { &self.0.kind }
}
impl Format for Expr {
@@ -139,6 +147,7 @@ pub enum ExprKind {
Missing,
}
impl ExprKind {
#[must_use]
pub fn at(self, pos: Pos) -> Expr { Expr(Rc::new(ExprData { pos, kind: RwLock::new(self) })) }
}
impl Format for ExprKind {
@@ -198,13 +207,16 @@ pub enum PathSetFrame<'a, T: PartialEq> {
#[derive(Clone)]
pub struct PathSetBuilder<'a, T: PartialEq>(Substack<'a, PathSetFrame<'a, T>>);
impl<'a, T: PartialEq> PathSetBuilder<'a, T> {
#[must_use]
pub fn new() -> Self { Self(Substack::Bottom) }
#[must_use]
pub fn split(&'a self) -> (Self, Self) {
(
Self(self.0.push(PathSetFrame::Step(Step::Left))),
Self(self.0.push(PathSetFrame::Step(Step::Right))),
)
}
#[must_use]
pub fn lambda<'b>(self, arg: &'b T) -> LambdaBuilder<'b, T>
where 'a: 'b {
LambdaBuilder { arg, path: RefCell::default(), stack: self }
@@ -264,15 +276,21 @@ impl<'a, T: PartialEq> PathSetBuilder<'a, T> {
}
}
impl<'a, T: PartialEq> Default for PathSetBuilder<'a, T> {
fn default() -> Self { Self::new() }
}
pub struct LambdaBuilder<'a, T: PartialEq> {
arg: &'a T,
path: RefCell<Option<PathSet>>,
stack: PathSetBuilder<'a, T>,
}
impl<'a, T: PartialEq> LambdaBuilder<'a, T> {
#[must_use]
pub fn stack(&'a self) -> PathSetBuilder<'a, T> {
PathSetBuilder(self.stack.0.push(PathSetFrame::Lambda(self.arg, &self.path)))
}
#[must_use]
pub fn collect(self) -> Option<PathSet> { self.path.into_inner() }
}
@@ -285,6 +303,7 @@ pub struct PathSet {
pub next: Option<(Box<PathSet>, Box<PathSet>)>,
}
impl PathSet {
#[must_use]
pub fn next(&self) -> Option<(&PathSet, &PathSet)> {
self.next.as_ref().map(|(l, r)| (&**l, &**r))
}
@@ -305,6 +324,7 @@ impl fmt::Display for PathSet {
}
}
#[must_use]
pub fn bot_expr(err: impl Into<OrcErrv>) -> Expr {
let errv: OrcErrv = err.into();
let pos = errv.pos_iter().next().map_or(Pos::None, |ep| ep.position.clone());
@@ -313,5 +333,44 @@ pub fn bot_expr(err: impl Into<OrcErrv>) -> Expr {
pub struct WeakExpr(Weak<ExprData>);
impl WeakExpr {
#[must_use]
pub fn upgrade(&self) -> Option<Expr> { self.0.upgrade().map(Expr) }
}
impl TokenVariant<api::ExprTicket> for Expr {
type FromApiCtx<'a> = ExprStore;
async fn from_api(
api: &api::ExprTicket,
ctx: &mut Self::FromApiCtx<'_>,
_: SrcRange,
_: &Interner,
) -> Self {
ctx.get_expr(*api).expect("Invalid ticket")
}
type ToApiCtx<'a> = ExprStore;
async fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> api::ExprTicket {
let id = self.id();
ctx.give_expr(self);
id
}
}
/// Acknowledgment that expr serialization is impossible and thus will panic.
#[derive(Debug, Clone, Copy, Default)]
pub struct ExprWillPanic;
impl TokenVariant<api::Expression> for Expr {
type FromApiCtx<'a> = ExprParseCtx<'a>;
async fn from_api(
api: &api::Expression,
ctx: &mut Self::FromApiCtx<'_>,
_: SrcRange,
_: &Interner,
) -> Self {
Self::from_api(api, PathSetBuilder::new(), ctx).await
}
type ToApiCtx<'a> = ExprWillPanic;
async fn into_api(self, ExprWillPanic: &mut Self::ToApiCtx<'_>) -> api::Expression {
panic!("Cannot serialize expr!")
}
}

View File

@@ -16,6 +16,7 @@ pub struct ExprStoreData {
#[derive(Clone, Default)]
pub struct ExprStore(Rc<ExprStoreData>);
impl ExprStore {
#[must_use]
pub fn derive(&self) -> Self {
Self(Rc::new(ExprStoreData { exprs: RefCell::default(), parent: Some(self.clone()) }))
}
@@ -31,6 +32,7 @@ impl ExprStore {
(self.0.exprs.borrow_mut().entry(ticket))
.and_replace_entry_with(|_, (rc, rt)| (1 < rc).then_some((rc - 1, rt)));
}
#[must_use]
pub fn get_expr(&self, ticket: api::ExprTicket) -> Option<Expr> {
(self.0.exprs.borrow().get(&ticket).map(|(_, expr)| expr.clone()))
.or_else(|| self.0.parent.as_ref()?.get_expr(ticket))

View File

@@ -13,7 +13,6 @@ use futures::future::{join, join_all};
use futures::{StreamExt, stream};
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_api::{HostMsgSet, LsModule};
use orchid_api_traits::Request;
use orchid_base::builtin::ExtInit;
use orchid_base::clone;
@@ -26,8 +25,10 @@ use orchid_base::reqnot::{DynRequester, ReqNot, Requester as _};
use crate::api;
use crate::atom::AtomHand;
use crate::ctx::Ctx;
use crate::dealias::{ChildError, ChildErrorKind, walk};
use crate::expr_store::ExprStore;
use crate::system::SystemCtor;
use crate::tree::MemberKind;
pub struct ReqPair<R: Request>(R, Sender<R::Response>);
@@ -38,7 +39,6 @@ pub struct ReqPair<R: Request>(R, Sender<R::Response>);
#[derive(destructure)]
pub struct ExtensionData {
ctx: Ctx,
init: Rc<ExtInit>,
reqnot: ReqNot<api::HostMsgSet>,
systems: Vec<SystemCtor>,
logger: Logger,
@@ -85,7 +85,6 @@ impl Extension {
.map(|decl| SystemCtor { decl, ext: WeakExtension(weak.clone()) })
.collect(),
logger: logger.clone(),
init: init.clone(),
next_pars: RefCell::new(NonZeroU64::new(1).unwrap()),
lex_recur: Mutex::default(),
reqnot: ReqNot::new(
@@ -168,8 +167,52 @@ impl Extension {
})
.await
},
api::ExtHostReq::LsModule(ref ls @ LsModule(ref sys, ref path)) => {
todo!() // TODO
api::ExtHostReq::LsModule(ref ls @ api::LsModule(_sys, path)) => {
let reply: <api::LsModule as Request>::Response = 'reply: {
let path = i.ex(path).await;
let root = (ctx.root.read().await.upgrade())
.expect("LSModule called when root isn't in context");
let root_data = &mut *root.0.write().await;
let mut walk_ctx = (ctx.clone(), &mut root_data.consts);
let module =
match walk(&root_data.root, false, path.iter().cloned(), &mut walk_ctx)
.await
{
Ok(module) => module,
Err(ChildError { kind, .. }) =>
break 'reply Err(match kind {
ChildErrorKind::Private => panic!("Access checking was disabled"),
ChildErrorKind::Constant => api::LsModuleError::IsConstant,
ChildErrorKind::Missing => api::LsModuleError::InvalidPath,
}),
};
let mut members = std::collections::HashMap::new();
for (k, v) in &module.members {
let kind = match v.kind(ctx.clone(), &mut root_data.consts).await {
MemberKind::Const => api::MemberInfoKind::Constant,
MemberKind::Module(_) => api::MemberInfoKind::Module,
};
members.insert(k.to_api(), api::MemberInfo { public: v.public, kind });
}
Ok(api::ModuleInfo { members })
};
hand.handle(ls, &reply).await
},
api::ExtHostReq::ResolveNames(ref rn) => {
let api::ResolveNames { constid, names, sys } = rn;
let mut resolver = {
let systems = ctx.systems.read().await;
let weak_sys = systems.get(sys).expect("ResolveNames for invalid sys");
let sys = weak_sys.upgrade().expect("ResolveNames after sys drop");
sys.name_resolver(*constid).await
};
let mut responses = vec![const { None }; names.len()];
for (i, name) in names.iter().enumerate() {
if let Some(abs) = resolver(&ctx.i.ex(*name).await[..]).await {
responses[i] = Some(abs.to_sym(&ctx.i).await.to_api())
}
}
hand.handle(rn, &responses).await
},
api::ExtHostReq::ExtAtomPrint(ref eap @ api::ExtAtomPrint(ref atom)) => {
let atom = AtomHand::new(atom.clone(), &ctx).await;
@@ -184,11 +227,16 @@ impl Extension {
}
})))
}
pub(crate) fn reqnot(&self) -> &ReqNot<HostMsgSet> { &self.0.reqnot }
#[must_use]
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.reqnot }
#[must_use]
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
#[must_use]
pub fn logger(&self) -> &Logger { &self.0.logger }
pub fn system_ctors(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
#[must_use]
pub fn exprs(&self) -> &ExprStore { &self.0.exprs }
#[must_use]
pub async fn is_own_sys(&self, id: api::SysId) -> bool {
let sys = self.ctx().system_inst(id).await.expect("invalid sender sys id");
Rc::ptr_eq(&self.0, &sys.ext().0)
@@ -196,6 +244,7 @@ impl Extension {
pub async fn assert_own_sys(&self, id: api::SysId) {
assert!(self.is_own_sys(id).await, "Incoming message impersonates separate system");
}
#[must_use]
pub fn next_pars(&self) -> NonZeroU64 {
let mut next_pars = self.0.next_pars.borrow_mut();
*next_pars = next_pars.checked_add(1).unwrap_or(NonZeroU64::new(1).unwrap());
@@ -240,10 +289,12 @@ impl Extension {
rc.ctx().systems.write().await.remove(&id);
}))
}
#[must_use]
pub fn downgrade(&self) -> WeakExtension { WeakExtension(Rc::downgrade(&self.0)) }
}
pub struct WeakExtension(Weak<ExtensionData>);
impl WeakExtension {
#[must_use]
pub fn upgrade(&self) -> Option<Extension> { self.0.upgrade().map(Extension) }
}

93
orchid-host/src/fs.rs Normal file
View File

@@ -0,0 +1,93 @@
use std::rc::Rc;
use async_std::path::PathBuf;
use async_stream::stream;
use futures::{FutureExt, StreamExt};
use hashbrown::HashMap;
use orchid_base::interner::Tok;
use orchid_base::name::Sym;
use orchid_base::pure_seq::pushed;
use crate::api;
use crate::system::System;
#[derive(Clone)]
pub struct DeclMod(Rc<HashMap<Tok<String>, DeclFS>>);
impl DeclMod {
pub async fn from_api(
api: &std::collections::HashMap<api::TStr, api::EagerVfs>,
owner: System,
) -> Self {
let item_stream = stream! {
for (key, value) in api {
yield (
owner.i().ex(*key).await,
DeclFS::from_api(value, owner.clone()).boxed_local().await
)
}
};
Self(Rc::new(item_stream.collect().await))
}
}
#[derive(Clone)]
pub enum DeclFS {
Lazy(System, api::VfsId),
Eager(DeclMod),
Path(PathBuf),
}
impl DeclFS {
pub async fn from_api(api: &api::EagerVfs, owner: System) -> Self {
match api {
api::EagerVfs::Eager(items) => Self::Eager(DeclMod::from_api(items, owner.clone()).await),
api::EagerVfs::Lazy(id) => Self::Lazy(owner.clone(), *id),
}
}
pub fn merge(&self, other: &Self) -> Result<Self, Vec<Tok<String>>> {
let (Self::Eager(m1), Self::Eager(m2)) = (self, other) else { return Err(Vec::new()) };
let mut mix = m1.0.iter().map(|(k, v)| (k.clone(), v.clone())).collect::<HashMap<_, _>>();
for (key, value) in m2.0.iter() {
match mix.entry(key.clone()) {
hashbrown::hash_map::Entry::Vacant(ent) => {
ent.insert(value.clone());
},
hashbrown::hash_map::Entry::Occupied(mut ent) => match ent.get().merge(value) {
Err(e) => return Err(pushed(e, key.clone())),
Ok(new) => {
ent.insert(new);
},
},
}
}
Ok(Self::Eager(DeclMod(Rc::new(mix))))
}
/// Walk through eager fs. Returns if the path ends, if it hits anything other
/// than eager, or if the path is invalid.
pub fn walk<'a, 'b>(&'a self, path: &'b [Tok<String>]) -> (&'a DeclFS, &'b [Tok<String>]) {
let mut cur = self;
for (i, step) in path.iter().enumerate() {
match self {
fs @ (DeclFS::Path(_) | DeclFS::Lazy(..)) => return (fs, &path[i..]),
fs @ DeclFS::Eager(m) => match &m.0.get(step) {
None => return (fs, &path[i..]),
Some(next) => cur = next,
},
}
}
(cur, &[])
}
}
pub async fn load_code(fs: &DeclFS, systems: &[System], path: &[Tok<String>]) -> OrcRes<Parsed
pub async fn gather_fs(systems: &[System]) -> Result<DeclFS, Sym> {
let (head, tail) = systems.split_first().expect("Empty system list");
let mut vfs = head.vfs().await;
for sys in tail {
match vfs.merge(&sys.vfs().await) {
Err(e) => return Err(Sym::new(e.iter().rev().cloned(), head.i()).await.unwrap()),
Ok(next) => vfs = next,
}
}
Ok(vfs)
}

View File

@@ -12,7 +12,7 @@ use orchid_base::tree::recur;
use crate::api;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprParseCtx};
use crate::expr::{Expr, ExprParseCtx, ExprWillPanic};
use crate::parsed::{ParsTok, ParsTokTree};
use crate::system::System;
@@ -25,6 +25,7 @@ pub struct LexCtx<'a> {
pub ctx: &'a Ctx,
}
impl<'a> LexCtx<'a> {
#[must_use]
pub fn push<'b>(&'b mut self, pos: u32) -> LexCtx<'b>
where 'a: 'b {
LexCtx {
@@ -36,11 +37,14 @@ impl<'a> LexCtx<'a> {
ctx: self.ctx,
}
}
#[must_use]
pub fn get_pos(&self) -> u32 { self.end_pos() - self.tail.len() as u32 }
#[must_use]
pub fn end_pos(&self) -> u32 { self.source.len() as u32 }
pub fn set_pos(&mut self, pos: u32) { self.tail = &self.source[pos as usize..] }
pub fn push_pos(&mut self, delta: u32) { self.set_pos(self.get_pos() + delta) }
pub fn set_tail(&mut self, tail: &'a str) { self.tail = tail }
#[must_use]
pub fn strip_prefix(&mut self, tgt: &str) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
self.tail = src;
@@ -48,26 +52,29 @@ impl<'a> LexCtx<'a> {
}
false
}
#[must_use]
pub async fn ser_subtree(&mut self, subtree: ParsTokTree) -> api::TokenTree {
let mut exprs = self.ctx.common_exprs.clone();
let foo = recur(subtree, &|tt, r| {
let without_new_expr = recur(subtree, &|tt, r| {
if let ParsTok::NewExpr(expr) = tt.tok {
return ParsTok::Handle(expr).at(tt.sr);
}
r(tt)
});
foo.into_api(&mut exprs, &mut ()).await
without_new_expr.into_api(&mut exprs, &mut ExprWillPanic).await
}
#[must_use]
pub async fn des_subtree(&mut self, tree: &api::TokenTree) -> ParsTokTree {
ParsTokTree::from_api(
&tree,
tree,
&mut self.ctx.common_exprs.clone(),
&mut ExprParseCtx { ctx: self.ctx.clone(), exprs: self.ctx.common_exprs.clone() },
&mut ExprParseCtx { ctx: self.ctx, exprs: &self.ctx.common_exprs },
self.path,
&self.ctx.i,
)
.await
}
#[must_use]
pub fn strip_char(&mut self, tgt: char) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
self.tail = src;
@@ -79,6 +86,7 @@ impl<'a> LexCtx<'a> {
self.tail = self.tail.trim_start_matches(filter);
}
pub fn trim_ws(&mut self) { self.trim(|c| c.is_whitespace() && !"\r\n".contains(c)) }
#[must_use]
pub fn get_start_matches(&mut self, filter: impl Fn(char) -> bool) -> &'a str {
let rest = self.tail.trim_start_matches(filter);
let matches = &self.tail[..self.tail.len() - rest.len()];
@@ -100,6 +108,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
.and_then(|t| t.trim_start_matches(name_char).strip_prefix("::"))
{
let name = &ctx.tail[..ctx.tail.len() - tail.len() - "::".len()];
ctx.set_tail(tail);
let body = lex_once(ctx).boxed_local().await?;
ParsTok::NS(ctx.ctx.i.i(name).await, Box::new(body))
} else if ctx.strip_prefix("--[") {
@@ -107,7 +116,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
return Err(mk_errv(
ctx.ctx.i.i("Unterminated block comment").await,
"This block comment has no ending ]--",
[SrcRange::new(start..start + 3, ctx.path).pos().into()],
[SrcRange::new(start..start + 3, ctx.path)],
));
};
ctx.set_tail(tail);
@@ -124,7 +133,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
return Err(mk_errv(
ctx.ctx.i.i("Unclosed lambda").await,
"Lambdae started with \\ should separate arguments from body with .",
[SrcRange::new(start..start + 1, ctx.path).pos().into()],
[SrcRange::new(start..start + 1, ctx.path)],
));
}
arg.push(lex_once(ctx).boxed_local().await?);
@@ -139,7 +148,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
return Err(mk_errv(
ctx.ctx.i.i("unclosed paren").await,
format!("this {lp} has no matching {rp}"),
[SrcRange::new(start..start + 1, ctx.path).pos().into()],
[SrcRange::new(start..start + 1, ctx.path)],
));
}
body.push(lex_once(ctx).boxed_local().await?);
@@ -189,7 +198,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
return Err(mk_errv(
ctx.ctx.i.i("Unrecognized character").await,
"The following syntax is meaningless.",
[SrcRange::new(start..start + 1, ctx.path).pos().into()],
[SrcRange::new(start..start + 1, ctx.path)],
));
}
};

View File

@@ -7,6 +7,7 @@ pub mod execute;
pub mod expr;
pub mod expr_store;
pub mod extension;
pub mod fs;
pub mod lex;
pub mod parse;
pub mod parsed;

View File

@@ -1,13 +1,10 @@
use std::cell::RefCell;
use futures::FutureExt;
use futures::future::join_all;
use hashbrown::HashMap;
use itertools::Itertools;
use orchid_base::error::{OrcRes, Reporter, mk_err, mk_errv};
use orchid_base::error::{OrcRes, Reporter, mk_errv};
use orchid_base::format::fmt;
use orchid_base::interner::{Interner, Tok};
use orchid_base::name::Sym;
use orchid_base::name::{Sym, VPath};
use orchid_base::parse::{
Comment, Import, ParseCtx, Parsed, Snippet, expect_end, line_items, parse_multiname,
try_pop_no_fluff,
@@ -17,7 +14,7 @@ use substack::Substack;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprKind, PathSetBuilder};
use crate::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule};
use crate::parsed::{Item, ItemKind, ParsedMember, ParsedMemberKind, ParsedModule};
use crate::system::System;
type ParsSnippet<'a> = Snippet<'a, Expr, Expr>;
@@ -27,28 +24,23 @@ pub struct HostParseCtxImpl<'a> {
pub src: Sym,
pub systems: &'a [System],
pub reporter: &'a Reporter,
pub interner: &'a Interner,
pub consts: RefCell<HashMap<Sym, Vec<ParsTokTree>>>,
}
impl ParseCtx for HostParseCtxImpl<'_> {
fn reporter(&self) -> &Reporter { self.reporter }
fn i(&self) -> &Interner { self.interner }
fn i(&self) -> &Interner { &self.ctx.i }
}
impl HostParseCtx for HostParseCtxImpl<'_> {
fn ctx(&self) -> &Ctx { &self.ctx }
fn systems(&self) -> impl Iterator<Item = &System> { self.systems.iter() }
async fn save_const(&self, path: Substack<'_, Tok<String>>, value: Vec<ParsTokTree>) {
let name = Sym::new(path.unreverse(), self.interner).await.unwrap();
self.consts.borrow_mut().insert(name, value);
}
}
pub trait HostParseCtx: ParseCtx {
#[must_use]
fn ctx(&self) -> &Ctx;
#[must_use]
fn systems(&self) -> impl Iterator<Item = &System>;
async fn save_const(&self, path: Substack<'_, Tok<String>>, value: Vec<ParsTokTree>);
}
pub async fn parse_items(
@@ -73,33 +65,10 @@ pub async fn parse_item(
n if *n == ctx.i().i("export").await => match try_pop_no_fluff(ctx, postdisc).await? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_exportable_item(ctx, path, comments, true, n.clone(), tail).await,
Parsed { output: TokTree { tok: Token::S(Paren::Round, body), .. }, tail } => {
expect_end(ctx, tail).await?;
let mut ok = Vec::new();
for tt in body {
let sr = tt.sr.clone();
match &tt.tok {
Token::Name(n) =>
ok.push(Item { comments: comments.clone(), sr, kind: ItemKind::Export(n.clone()) }),
Token::NS(..) => ctx.reporter().report(mk_err(
ctx.i().i("Compound export").await,
"Cannot export compound names (names containing the :: separator)",
[sr.pos().into()],
)),
t => ctx.reporter().report(mk_err(
ctx.i().i("Invalid export").await,
format!("Invalid export target {}", fmt(t, ctx.i()).await),
[sr.pos().into()],
)),
}
}
expect_end(ctx, tail).await?;
Ok(ok)
},
Parsed { output, tail: _ } => Err(mk_errv(
ctx.i().i("Malformed export").await,
"`export` can either prefix other lines or list names inside ( )",
[output.sr.pos().into()],
[output.sr()],
)),
},
n if *n == ctx.i().i("import").await => {
@@ -115,7 +84,7 @@ pub async fn parse_item(
Some(_) => Err(mk_errv(
ctx.i().i("Expected a line type").await,
"All lines must begin with a keyword",
[item.sr().pos().into()],
[item.sr()],
)),
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
}
@@ -138,22 +107,25 @@ pub async fn parse_exportable_item<'a>(
discr: Tok<String>,
tail: ParsSnippet<'a>,
) -> OrcRes<Vec<Item>> {
let path_sym = Sym::new(path.unreverse(), ctx.i()).await.expect("Files should have a namespace");
let kind = if discr == ctx.i().i("mod").await {
let (name, body) = parse_module(ctx, path, tail).await?;
ItemKind::Member(ParsedMember { name, full_name: path_sym, kind: ParsedMemberKind::Mod(body) })
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::Mod(body) })
} else if discr == ctx.i().i("const").await {
let name = parse_const(ctx, tail, path.clone()).await?;
ItemKind::Member(ParsedMember { name, full_name: path_sym, kind: ParsedMemberKind::Const })
let (name, expr) = parse_const(ctx, tail, path.clone()).await?;
ItemKind::Member(ParsedMember { name, exported, kind: ParsedMemberKind::ParsedConst(expr) })
} else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) {
let line = sys.parse(path_sym, tail.to_vec(), exported, comments).await?;
return parse_items(ctx, path, Snippet::new(tail.prev(), &line)).await;
return sys
.parse(path, tail.to_vec(), exported, comments, &mut async |stack, lines| {
let source = Snippet::new(lines.first().unwrap(), &lines);
parse_items(ctx, stack, source).await
})
.await;
} else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv(
ctx.i().i("Unrecognized line type").await,
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
[tail.prev().sr.pos().into()],
[tail.prev().sr()],
));
};
Ok(vec![Item { comments, sr: tail.sr(), kind }])
@@ -170,7 +142,7 @@ pub async fn parse_module<'a>(
return Err(mk_errv(
ctx.i().i("Missing module name").await,
format!("A name was expected, {} was found", fmt(output, ctx.i()).await),
[output.sr.pos().into()],
[output.sr()],
));
},
};
@@ -180,7 +152,7 @@ pub async fn parse_module<'a>(
return Err(mk_errv(
ctx.i().i("Expected module body").await,
format!("A ( block ) was expected, {} was found", fmt(output, ctx.i()).await),
[output.sr.pos().into()],
[output.sr()],
));
};
let path = path.push(name.clone());
@@ -191,13 +163,13 @@ pub async fn parse_const<'a>(
ctx: &impl HostParseCtx,
tail: ParsSnippet<'a>,
path: Substack<'_, Tok<String>>,
) -> OrcRes<Tok<String>> {
) -> OrcRes<(Tok<String>, Expr)> {
let Parsed { output, tail } = try_pop_no_fluff(ctx, tail).await?;
let Some(name) = output.as_name() else {
return Err(mk_errv(
ctx.i().i("Missing module name").await,
format!("A name was expected, {} was found", fmt(output, ctx.i()).await),
[output.sr.pos().into()],
[output.sr()],
));
};
let Parsed { output, tail } = try_pop_no_fluff(ctx, tail).await?;
@@ -205,12 +177,15 @@ pub async fn parse_const<'a>(
return Err(mk_errv(
ctx.i().i("Missing = separator").await,
format!("Expected = , found {}", fmt(output, ctx.i()).await),
[output.sr.pos().into()],
[output.sr()],
));
}
try_pop_no_fluff(ctx, tail).await?;
ctx.save_const(path, tail[..].to_vec()).await;
Ok(name)
// ctx.save_const(path, tail[..].to_vec()).await;
let final_path =
VPath::new(path.unreverse()).name_with_suffix(name.clone()).to_sym(ctx.i()).await;
let val = parse_expr(ctx, final_path, PathSetBuilder::new(), tail).await?;
Ok((name, val))
}
pub async fn parse_expr(
@@ -223,7 +198,7 @@ pub async fn parse_expr(
.or_else(|| tail.iter().enumerate().rev().find(|(_, tt)| !tt.is_fluff()))
else {
return Err(mk_errv(ctx.i().i("Empty expression").await, "Expression ends abruptly here", [
tail.sr().pos().into(),
tail.sr(),
]));
};
let (function, value) = tail.split_at(last_idx as u32);
@@ -241,7 +216,7 @@ pub async fn parse_expr(
Token::Handle(expr) => Ok(expr.clone()),
Token::NS(n, nametail) => {
let mut nametail = nametail;
let mut segments = path.iter().chain([n]).cloned().collect_vec();
let mut segments = vec![n.clone()];
while let Token::NS(n, newtail) = &nametail.tok {
segments.push(n.clone());
nametail = newtail;
@@ -250,7 +225,7 @@ pub async fn parse_expr(
return Err(mk_errv(
ctx.i().i("Loose namespace prefix in constant").await,
"Namespace prefixes in constants must be followed by names",
[pos.into()],
[pos],
));
};
segments.push(n.clone());
@@ -261,13 +236,27 @@ pub async fn parse_expr(
return Err(mk_errv(
ctx.i().i("Complex lambda binding in constant").await,
"Lambda args in constants must be identified by a single name",
[pos.into()],
[pos],
));
};
let lambda_builder = psb.lambda(arg);
let body = parse_expr(ctx, path.clone(), lambda_builder.stack(), tail).boxed_local().await?;
Ok(ExprKind::Lambda(lambda_builder.collect(), body).at(pos.clone()))
},
_ => todo!("AAAAAA"), // TODO: todo
Token::S(Paren::Round, body) =>
parse_expr(ctx, path, psb, Snippet::new(head, body)).boxed_local().await,
Token::S(..) =>
return Err(mk_errv(
ctx.i().i("Constants may only contain (), not [] or {}").await,
"It seems like you are trying to call a macro. Consider a 'let' line",
[pos],
)),
Token::Name(n) =>
if psb.register_arg(n) {
Ok(ExprKind::Arg.at(pos))
} else {
Ok(ExprKind::Const(Sym::new([n.clone()], ctx.i()).await.unwrap()).at(pos))
},
Token::NewExpr(ex) => Ok(ex.clone()),
}
}

View File

@@ -1,67 +1,25 @@
use std::fmt::Debug;
use std::rc::Rc;
use async_once_cell::OnceCell;
use async_std::sync::{Mutex, RwLock};
use async_stream::stream;
use futures::FutureExt;
use futures::future::join_all;
use futures::{FutureExt, StreamExt};
use hashbrown::{HashMap, HashSet};
use hashbrown::HashSet;
use itertools::Itertools;
use orchid_base::error::{OrcRes, mk_errv};
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
use orchid_base::interner::Tok;
use orchid_base::location::{Pos, SrcRange};
use orchid_base::name::{NameLike, Sym};
use orchid_base::location::SrcRange;
use orchid_base::parse::{Comment, Import};
use orchid_base::tl_cache;
use orchid_base::tree::{TokTree, Token, TokenVariant};
use orchid_base::tree::{TokTree, Token};
use crate::api;
use crate::ctx::Ctx;
use crate::dealias::{ChildErrorKind, ChildResult, Tree};
use crate::expr::{Expr, ExprParseCtx, PathSetBuilder};
use crate::expr_store::ExprStore;
use crate::expr::Expr;
use crate::system::System;
pub type ParsTokTree = TokTree<Expr, Expr>;
pub type ParsTok = Token<Expr, Expr>;
impl TokenVariant<api::ExprTicket> for Expr {
type ToApiCtx<'a> = ExprStore;
async fn into_api(self, ctx: &mut Self::ToApiCtx<'_>) -> api::ExprTicket {
ctx.give_expr(self.clone());
self.id()
}
type FromApiCtx<'a> = ExprStore;
async fn from_api(
api: &api::ExprTicket,
ctx: &mut Self::FromApiCtx<'_>,
_: SrcRange,
_: &orchid_base::interner::Interner,
) -> Self {
let expr = ctx.get_expr(*api).expect("Dangling expr");
ctx.take_expr(*api);
expr
}
}
impl TokenVariant<api::Expression> for Expr {
type FromApiCtx<'a> = ExprParseCtx;
async fn from_api(
api: &api::Expression,
ctx: &mut Self::FromApiCtx<'_>,
_: SrcRange,
_: &orchid_base::interner::Interner,
) -> Self {
Expr::from_api(api, PathSetBuilder::new(), ctx).await
}
type ToApiCtx<'a> = ();
async fn into_api(self, (): &mut Self::ToApiCtx<'_>) -> api::Expression {
panic!("Failed to replace NewExpr before returning sublexer value")
}
}
#[derive(Debug)]
pub struct Item {
pub sr: SrcRange,
@@ -72,10 +30,10 @@ pub struct Item {
#[derive(Debug)]
pub enum ItemKind {
Member(ParsedMember),
Export(Tok<String>),
Import(Import),
}
impl ItemKind {
#[must_use]
pub fn at(self, sr: SrcRange) -> Item { Item { comments: vec![], sr, kind: self } }
}
@@ -84,11 +42,13 @@ impl Format for Item {
let comment_text = self.comments.iter().join("\n");
let item_text = match &self.kind {
ItemKind::Import(i) => format!("import {i}").into(),
ItemKind::Export(e) => format!("export {e}").into(),
ItemKind::Member(mem) => match &mem.kind {
ParsedMemberKind::Const =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0}")))
.units([mem.name.rc().into()]),
ParsedMemberKind::ParsedConst(expr) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} = {1l}")))
.units([mem.name.rc().into(), expr.print(c).await]),
ParsedMemberKind::DeferredConst(_, sys) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("const {0} via {1}")))
.units([mem.name.rc().into(), sys.print(c).await]),
ParsedMemberKind::Mod(module) =>
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("module {0} {{\n\t{1}\n}}")))
.units([mem.name.rc().into(), module.print(c).boxed_local().await]),
@@ -101,10 +61,11 @@ impl Format for Item {
pub struct ParsedMember {
pub name: Tok<String>,
pub full_name: Sym,
pub exported: bool,
pub kind: ParsedMemberKind,
}
impl ParsedMember {
#[must_use]
pub fn name(&self) -> Tok<String> { self.name.clone() }
}
impl Debug for ParsedMember {
@@ -118,7 +79,8 @@ impl Debug for ParsedMember {
#[derive(Debug)]
pub enum ParsedMemberKind {
Const,
DeferredConst(api::ParsedConstId, System),
ParsedConst(Expr),
Mod(ParsedModule),
}
@@ -131,13 +93,13 @@ pub struct ParsedModule {
pub items: Vec<Item>,
}
impl ParsedModule {
#[must_use]
pub fn new(items: impl IntoIterator<Item = Item>) -> Self {
let items = items.into_iter().collect_vec();
let exports = (items.iter())
.filter_map(|i| match &i.kind {
ItemKind::Export(e) => Some(e.clone()),
_ => None,
})
.filter_map(|i| if let ItemKind::Member(m) = &i.kind { Some(m) } else { None })
.filter(|m| m.exported)
.map(|m| m.name.clone())
.collect_vec();
Self { exports, items }
}
@@ -146,32 +108,34 @@ impl ParsedModule {
std::mem::swap(self, &mut swap);
*self = ParsedModule::new(swap.items.into_iter().chain(other.items))
}
#[must_use]
pub fn get_imports(&self) -> impl IntoIterator<Item = &Import> {
(self.items.iter())
.filter_map(|it| if let ItemKind::Import(i) = &it.kind { Some(i) } else { None })
}
}
impl Tree for ParsedModule {
type Ctx = ();
type Ctx<'a> = ();
async fn child(
&self,
key: Tok<String>,
public_only: bool,
ctx: &mut Self::Ctx,
(): &mut Self::Ctx<'_>,
) -> ChildResult<'_, Self> {
let Some(member) = (self.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name == key)
else {
return ChildResult::Err(ChildErrorKind::Missing);
};
if public_only && !self.exports.contains(&key) {
return ChildResult::Err(ChildErrorKind::Private);
}
match &member.kind {
ParsedMemberKind::Const => return ChildResult::Err(ChildErrorKind::Constant),
ParsedMemberKind::Mod(m) => return ChildResult::Value(m),
if let Some(member) = (self.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name == key)
{
match &member.kind {
ParsedMemberKind::DeferredConst(..) | ParsedMemberKind::ParsedConst(_) =>
return ChildResult::Err(ChildErrorKind::Constant),
ParsedMemberKind::Mod(m) => return ChildResult::Ok(m),
}
}
ChildResult::Err(ChildErrorKind::Missing)
}
fn children(&self, public_only: bool) -> HashSet<Tok<String>> {
let mut public: HashSet<_> = self.exports.iter().cloned().collect();
@@ -214,44 +178,6 @@ pub struct ConstPath {
steps: Tok<Vec<Tok<String>>>,
}
impl ConstPath {
#[must_use]
pub fn to_const(steps: Tok<Vec<Tok<String>>>) -> Self { Self { steps } }
}
#[derive(Clone)]
pub struct Root {
tree: Rc<ParsedModule>,
consts: Rc<RwLock<HashMap<Sym, Expr>>>,
}
impl Root {
pub fn new(module: ParsedModule, consts: HashMap<Sym, Expr>) -> Self {
Self { tree: Rc::new(module), consts: Rc::new(RwLock::new(consts)) }
}
pub async fn get_const_value(&self, name: Sym, pos: Pos, ctx: Ctx) -> OrcRes<Expr> {
if let Some(val) = self.consts.read().await.get(&name) {
return Ok(val.clone());
}
let (cn, mp) = name.split_last();
let module = self.tree.walk(false, mp.iter().cloned(), &mut ()).await.unwrap();
let member = (module.items.iter())
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
.find(|m| m.name() == cn);
match member {
None => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos.clone().into()],
)),
Some(mem) => match &mem.kind {
ParsedMemberKind::Mod(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos.clone().into()],
)),
ParsedMemberKind::Const => Ok(
(self.consts.read().await.get(&name).cloned())
.expect("Tree says the path is correct but no value was found"),
),
},
}
}
}

View File

@@ -1,4 +1,5 @@
use std::cell::RefCell;
use std::io::Write;
use std::pin::Pin;
use async_process::{self, Child, ChildStdin, ChildStdout};
@@ -43,17 +44,19 @@ pub async fn ext_command(
}
}));
Ok(ExtInit {
header,
port: Box::new(Subprocess {
name: header.name.clone(),
child: RefCell::new(Some(child)),
stdin: Some(Mutex::new(Box::pin(stdin))),
stdout: Mutex::new(Box::pin(stdout)),
ctx,
}),
header,
})
}
pub struct Subprocess {
name: String,
child: RefCell<Option<Child>>,
stdin: Option<Mutex<Pin<Box<ChildStdin>>>>,
stdout: Mutex<Pin<Box<ChildStdout>>>,
@@ -62,11 +65,20 @@ pub struct Subprocess {
impl Drop for Subprocess {
fn drop(&mut self) {
let mut child = self.child.borrow_mut().take().unwrap();
let name = self.name.clone();
if std::thread::panicking() {
eprintln!("Killing extension {name}");
// we don't really care to handle errors here
let _: Result<_, _> = std::io::stderr().flush();
let _: Result<_, _> = child.kill();
return;
}
let stdin = self.stdin.take().unwrap();
(self.ctx.spawn)(Box::pin(async move {
stdin.lock().await.close().await.unwrap();
let status = child.status().await.expect("Extension exited with error");
assert!(status.success(), "Extension exited with error {status}");
let status = (child.status().await)
.unwrap_or_else(|e| panic!("{e}, extension {name} exited with error"));
assert!(status.success(), "Extension {name} exited with error {status}");
}))
}
}

View File

@@ -3,19 +3,18 @@ use std::fmt;
use std::future::Future;
use std::rc::{Rc, Weak};
use async_stream::stream;
use derive_destructure::destructure;
use futures::StreamExt;
use futures::FutureExt;
use futures::future::join_all;
use hashbrown::HashMap;
use itertools::Itertools;
use memo_map::MemoMap;
use orchid_base::char_filter::char_filter_match;
use orchid_base::clone;
use orchid_base::error::{OrcErrv, OrcRes};
use orchid_base::format::{FmtCtx, FmtUnit, Format};
use orchid_base::interner::{Interner, Tok};
use orchid_base::location::Pos;
use orchid_base::name::Sym;
use orchid_base::location::SrcRange;
use orchid_base::name::{NameLike, Sym, VName};
use orchid_base::parse::Comment;
use orchid_base::reqnot::{ReqNot, Requester};
use orchid_base::tree::ttv_from_api;
@@ -24,19 +23,25 @@ use substack::{Stackframe, Substack};
use crate::api;
use crate::ctx::Ctx;
use crate::expr::{Expr, ExprParseCtx};
use crate::dealias::{absolute_path, walk};
use crate::expr::{ExprParseCtx, ExprWillPanic};
use crate::expr_store::ExprStore;
use crate::extension::{Extension, WeakExtension};
use crate::parsed::{ItemKind, ParsTokTree, ParsedModule, Root};
use crate::tree::Module;
use crate::fs::{DeclFS, DeclMod};
use crate::parsed::{Item, ItemKind, ParsTokTree, ParsedMember, ParsedMemberKind, ParsedModule};
use crate::tree::Root;
#[derive(destructure)]
struct SystemInstData {
deps: Vec<System>,
ctx: Ctx,
ext: Extension,
decl_id: api::SysDeclId,
lex_filter: api::CharFilter,
id: api::SysId,
line_types: Vec<Tok<String>>,
vfs: std::collections::HashMap<api::TStr, api::EagerVfs>,
pub(crate) const_paths: MemoMap<api::ParsedConstId, Sym>,
}
impl Drop for SystemInstData {
fn drop(&mut self) { self.ext.system_drop(self.id); }
@@ -55,15 +60,29 @@ impl fmt::Debug for SystemInstData {
#[derive(Clone, Debug)]
pub struct System(Rc<SystemInstData>);
impl System {
#[must_use]
pub fn id(&self) -> api::SysId { self.0.id }
#[must_use]
pub fn ext(&self) -> &Extension { &self.0.ext }
#[must_use]
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
#[must_use]
pub fn i(&self) -> &Interner { &self.0.ctx.i }
#[must_use]
pub fn deps(&self) -> &[System] { &self.0.deps }
#[must_use]
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { self.0.ext.reqnot() }
#[must_use]
pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
self.reqnot().request(api::GetMember(self.0.id, id)).await
}
#[must_use]
pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() }
#[must_use]
pub async fn vfs(&self) -> DeclFS {
DeclFS::Eager(DeclMod::from_api(&self.0.vfs, self.clone()).await)
}
#[must_use]
pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) }
/// Have this system lex a part of the source. It is assumed that
/// [Self::can_lex] was called and returned true.
@@ -76,24 +95,26 @@ impl System {
) -> api::OrcResult<Option<api::LexedExpr>> {
self.0.ext.lex_req(source, src, pos, self.id(), r).await
}
#[must_use]
pub fn can_parse(&self, ltyp: Tok<String>) -> bool { self.0.line_types.contains(&ltyp) }
pub fn line_types(&self) -> impl Iterator<Item = &Tok<String>> + '_ { self.0.line_types.iter() }
pub async fn parse(
&self,
module: Sym,
path: Substack<'_, Tok<String>>,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> {
callback: &mut impl AsyncFnMut(Substack<'_, Tok<String>>, Vec<ParsTokTree>) -> OrcRes<Vec<Item>>,
) -> OrcRes<Vec<Item>> {
let src_path = line.first().expect("cannot be empty").sr.path();
let line = join_all(line.into_iter().map(|t| async {
let mut expr_store = self.0.ext.exprs().clone();
t.into_api(&mut expr_store, &mut ()).await
t.into_api(&mut expr_store, &mut ExprWillPanic).await
}))
.await;
let comments = comments.iter().map(Comment::to_api).collect_vec();
let req = api::ParseLine {
module: module.to_api(),
module: self.i().i(&path.unreverse()).await.to_api(),
src: src_path.to_api(),
exported,
sys: self.id(),
@@ -101,14 +122,69 @@ impl System {
line,
};
match self.reqnot().request(req).await {
Ok(parsed) => {
let mut pctx = ExprParseCtx { ctx: self.ctx().clone(), exprs: self.ext().exprs().clone() };
Ok(parsed_v) => {
let mut ext_exprs = self.ext().exprs().clone();
Ok(ttv_from_api(parsed, &mut ext_exprs, &mut pctx, &src_path, self.i()).await)
struct ConvCtx<'a> {
sys: &'a System,
src_path: &'a Sym,
i: &'a Interner,
ext_exprs: &'a mut ExprStore,
pctx: &'a mut ExprParseCtx<'a>,
}
async fn conv(
parsed_v: Vec<api::ParsedLine>,
module: Substack<'_, Tok<String>>,
callback: &'_ mut impl AsyncFnMut(
Substack<'_, Tok<String>>,
Vec<ParsTokTree>,
) -> OrcRes<Vec<Item>>,
ctx: &mut ConvCtx<'_>,
) -> OrcRes<Vec<Item>> {
let mut items = Vec::new();
for parsed in parsed_v {
let (name, exported, kind) = match parsed.kind {
api::ParsedLineKind::Member(api::ParsedMember { name, exported, kind }) =>
(name, exported, kind),
api::ParsedLineKind::Recursive(rec) => {
let tokens = ttv_from_api(rec, ctx.ext_exprs, ctx.pctx, ctx.src_path, ctx.i).await;
items.extend(callback(module.clone(), tokens).await?);
continue;
},
};
let name = ctx.i.ex(name).await;
let mkind = match kind {
api::ParsedMemberKind::Module(items) => {
let items =
conv(items, module.push(name.clone()), callback, ctx).boxed_local().await?;
ParsedMemberKind::Mod(ParsedModule::new(items))
},
api::ParsedMemberKind::Constant(cid) =>
ParsedMemberKind::DeferredConst(cid, ctx.sys.clone()),
};
items.push(Item {
comments: join_all(
parsed.comments.iter().map(|c| Comment::from_api(c, ctx.src_path.clone(), ctx.i)),
)
.await,
sr: SrcRange::from_api(&parsed.source_range, ctx.i).await,
kind: ItemKind::Member(ParsedMember { name, exported, kind: mkind }),
})
}
Ok(items)
}
conv(parsed_v, path, callback, &mut ConvCtx {
i: self.i(),
ext_exprs: &mut ext_exprs,
pctx: &mut ExprParseCtx { ctx: self.ctx(), exprs: self.ext().exprs() },
src_path: &src_path,
sys: self,
})
.await
},
Err(e) => Err(OrcErrv::from_api(&e, &self.ctx().i).await),
}
}
#[must_use]
pub async fn request(&self, req: Vec<u8>) -> Vec<u8> {
self.reqnot().request(api::SysFwded(self.id(), req)).await
}
@@ -118,7 +194,24 @@ impl System {
this.ctx.owned_atoms.write().await.remove(&drop);
}))
}
#[must_use]
pub fn downgrade(&self) -> WeakSystem { WeakSystem(Rc::downgrade(&self.0)) }
/// Implementation of [api::ResolveNames]
pub(crate) async fn name_resolver(
&self,
orig: api::ParsedConstId,
) -> impl AsyncFnMut(&[Tok<String>]) -> Option<VName> + use<> {
let root = self.0.ctx.root.read().await.upgrade().expect("find_names when root not in context");
let orig = self.0.const_paths.get(&orig).expect("origin for find_names invalid").clone();
let ctx = self.0.ctx.clone();
async move |rel| {
let cwd = orig.split_last().1;
let abs = absolute_path(cwd, rel, &ctx.i).await.ok()?;
let root_data = &mut *root.0.write().await;
let walk_ctx = &mut (ctx.clone(), &mut root_data.consts);
walk(&root_data.root, false, abs.iter(), walk_ctx).await.is_ok().then_some(abs)
}
}
}
impl Format for System {
async fn print<'a>(&'a self, _c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
@@ -130,6 +223,7 @@ impl Format for System {
pub struct WeakSystem(Weak<SystemInstData>);
impl WeakSystem {
#[must_use]
pub fn upgrade(&self) -> Option<System> { self.0.upgrade().map(System) }
}
@@ -138,56 +232,42 @@ pub struct SystemCtor {
pub(crate) ext: WeakExtension,
}
impl SystemCtor {
#[must_use]
pub fn name(&self) -> &str { &self.decl.name }
#[must_use]
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
#[must_use]
pub fn depends(&self) -> impl ExactSizeIterator<Item = &str> {
self.decl.depends.iter().map(|s| &**s)
}
#[must_use]
pub fn id(&self) -> api::SysDeclId { self.decl.id }
pub async fn run<'a>(
&self,
depends: impl IntoIterator<Item = &'a System>,
consts: &mut HashMap<Sym, Expr>,
) -> (Module, System) {
let depends = depends.into_iter().map(|si| si.id()).collect_vec();
#[must_use]
pub async fn run(&self, deps: Vec<System>) -> (Root, System) {
let depends = deps.iter().map(|si| si.id()).collect_vec();
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
let id = ext.ctx().next_sys_id();
let sys_inst = ext.reqnot().request(api::NewSystem { depends, id, system: self.decl.id }).await;
let data = System(Rc::new(SystemInstData {
deps,
decl_id: self.decl.id,
ext: ext.clone(),
ctx: ext.ctx().clone(),
lex_filter: sys_inst.lex_filter,
vfs: sys_inst.vfs,
line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i)))
.await,
id,
const_paths: MemoMap::new(),
}));
let const_root =
Module::from_api((sys_inst.const_root.into_iter()).map(|(k, v)| api::Member {
name: k,
kind: v,
comments: vec![],
exported: true,
}))
.await;
let const_root = clone!(data, ext; stream! {
for (k, v) in sys_inst.const_root {
yield Member::from_api(
,
&mut ParsedFromApiCx {
consts,
path: ext.ctx().i.i(&[]).await,
sys: &data,
}
).await;
}
})
.map(|mem| ItemKind::Member(mem).at(Pos::None))
.collect::<Vec<_>>()
.await;
let api_module_root = api::Module {
members: (sys_inst.const_root.into_iter())
.map(|(k, v)| api::Member { name: k, kind: v, comments: vec![], exported: true })
.collect_vec(),
};
let root = Root::from_api(api_module_root, &data).await;
ext.ctx().systems.write().await.insert(id, data.downgrade());
let root = ParsedModule::new(const_root);
(root, data)
}
}
@@ -219,7 +299,6 @@ pub async fn init_systems(
fn walk_deps<'a>(
graph: &mut HashMap<&str, &'a SystemCtor>,
list: &mut Vec<&'a SystemCtor>,
consts: &mut HashMap<Sym, Expr>,
chain: Stackframe<&str>,
) -> Result<(), SysResolvErr> {
if let Some(ctor) = graph.remove(chain.item) {
@@ -231,22 +310,21 @@ pub async fn init_systems(
circle.extend(Substack::Frame(chain).iter().map(|s| s.to_string()));
return Err(SysResolvErr::Loop(circle));
}
walk_deps(graph, list, consts, Substack::Frame(chain).new_frame(dep))?
walk_deps(graph, list, Substack::Frame(chain).new_frame(dep))?
}
list.push(ctor);
}
Ok(())
}
let mut consts = HashMap::new();
for tgt in tgts {
walk_deps(&mut to_load, &mut to_load_ordered, &mut consts, Substack::Bottom.new_frame(tgt))?;
walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?;
}
let mut systems = HashMap::<&str, System>::new();
let mut root_mod = ParsedModule::default();
let mut root = Root::new(exts.first().unwrap().ctx().clone());
for ctor in to_load_ordered.iter() {
let (sys_root, sys) = ctor.run(ctor.depends().map(|n| &systems[n]), &mut consts).await;
let (sys_root, sys) = ctor.run(ctor.depends().map(|n| systems[n].clone()).collect()).await;
systems.insert(ctor.name(), sys);
root_mod.merge(sys_root);
root = root.merge(&sys_root).await.expect("Conflicting roots");
}
Ok((Root::new(root_mod, consts), systems.into_values().collect_vec()))
Ok((root, systems.into_values().collect_vec()))
}

View File

@@ -1,24 +1,142 @@
//! This tree isn't Clone because lazy subtrees are guaranteed to only be loaded
//! once
use std::cell::RefCell;
use std::rc::{Rc, Weak};
use async_once_cell::OnceCell;
use futures::FutureExt;
use async_std::sync::RwLock;
use futures::{FutureExt, StreamExt, stream};
use hashbrown::HashMap;
use hashbrown::hash_map::Entry;
use itertools::Itertools;
use orchid_base::error::{OrcRes, Reporter};
use orchid_api::FetchParsedConst;
use orchid_base::clone;
use orchid_base::error::{OrcRes, Reporter, mk_err, mk_errv};
use orchid_base::interner::Tok;
use orchid_base::location::{Pos, SrcRange};
use orchid_base::name::{Sym, VPath};
use orchid_base::reqnot::Requester;
use crate::api;
use crate::ctx::Ctx;
use crate::dealias::{DealiasCtx, absolute_path, resolv_glob};
use crate::dealias::{ChildErrorKind, Tree, absolute_path, resolv_glob, walk};
use crate::expr::{Expr, ExprParseCtx, PathSetBuilder};
use crate::parsed::{ParsedMemberKind, ParsedModule, Tree, WalkError, WalkErrorKind};
use crate::parsed::{ItemKind, ParsedMemberKind, ParsedModule};
use crate::system::System;
pub struct Tree(Rc<RefCell<Module>>);
pub struct RootData {
pub root: Module,
pub consts: HashMap<Sym, Expr>,
pub ctx: Ctx,
}
#[derive(Clone)]
pub struct Root(pub Rc<RwLock<RootData>>);
impl Root {
#[must_use]
pub fn new(ctx: Ctx) -> Self {
Root(Rc::new(RwLock::new(RootData {
root: Module::default(),
consts: HashMap::default(),
ctx,
})))
}
#[must_use]
pub async fn from_api(api: api::Module, sys: &System) -> Self {
let mut consts = HashMap::new();
let mut tfac = TreeFromApiCtx { consts: &mut consts, path: sys.i().i(&[][..]).await, sys };
let root = Module::from_api(api, &mut tfac).await;
Root(Rc::new(RwLock::new(RootData { root, consts, ctx: sys.ctx().clone() })))
}
pub async fn merge(&self, new: &Root) -> Result<Self, MergeErr> {
let this = self.0.read().await;
let that = new.0.read().await;
let mut consts =
this.consts.iter().chain(&that.consts).map(|(k, v)| (k.clone(), v.clone())).collect();
let root = this.root.merge(&that.root, this.ctx.clone(), &mut consts).await?;
Ok(Self(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() }))))
}
#[must_use]
pub async fn add_parsed(&self, parsed: &ParsedModule, pars_prefix: Sym, rep: &Reporter) -> Self {
let mut ref_this = self.0.write().await;
let this = &mut *ref_this;
let mut deferred_consts = HashMap::new();
let mut tfpctx = FromParsedCtx {
pars_root: parsed,
deferred_consts: &mut deferred_consts,
pars_prefix: pars_prefix.clone(),
consts: &mut this.consts,
root: &this.root,
ctx: &this.ctx,
rep,
};
let mut module = Module::from_parsed(parsed, pars_prefix.clone(), &mut tfpctx).await;
for step in pars_prefix.iter().rev() {
let kind = OnceCell::from(MemberKind::Module(module));
let members = HashMap::from([(
step.clone(),
Rc::new(Member { public: true, lazy: RefCell::new(None), kind }),
)]);
module = Module { imports: HashMap::new(), members }
}
let mut consts = this.consts.clone();
let root = (this.root.merge(&module, this.ctx.clone(), &mut consts).await)
.expect("Merge conflict between parsed and existing module");
let new = Root(Rc::new(RwLock::new(RootData { root, consts, ctx: this.ctx.clone() })));
*this.ctx.root.write().await = new.downgrade();
for (path, (sys_id, pc_id)) in deferred_consts {
let sys = this.ctx.system_inst(sys_id).await.expect("System dropped since parsing");
let api_expr = sys.reqnot().request(FetchParsedConst { id: pc_id, sys: sys.id() }).await;
let mut xp_ctx = ExprParseCtx { ctx: &this.ctx, exprs: sys.ext().exprs() };
let expr = Expr::from_api(&api_expr, PathSetBuilder::new(), &mut xp_ctx).await;
new.0.write().await.consts.insert(path, expr);
}
new
}
pub async fn get_const_value(&self, name: Sym, pos: Pos) -> OrcRes<Expr> {
let this = &mut *self.0.write().await;
// shortcut for previously visited
if let Some(val) = this.consts.get(&name) {
return Ok(val.clone());
}
// load the node, then check if this "walk" call added it to the map
let ctx = this.ctx.clone();
let module =
walk(&this.root, false, name.iter().cloned(), &mut (ctx.clone(), &mut this.consts)).await;
if let Some(val) = this.consts.get(&name) {
return Ok(val.clone());
}
match module {
Ok(_) => Err(mk_errv(
ctx.i.i("module used as constant").await,
format!("{name} is a module, not a constant"),
[pos],
)),
Err(e) => match e.kind {
ChildErrorKind::Private => panic!("public_only is false"),
ChildErrorKind::Constant => panic!("Tree refers to constant not in table"),
ChildErrorKind::Missing => Err(mk_errv(
ctx.i.i("Constant does not exist").await,
format!("{name} does not refer to a constant"),
[pos],
)),
},
}
}
#[must_use]
pub fn downgrade(&self) -> WeakRoot { WeakRoot(Rc::downgrade(&self.0)) }
}
pub struct WeakTree(Weak<RefCell<Module>>);
#[derive(Clone)]
pub struct WeakRoot(Weak<RwLock<RootData>>);
impl WeakRoot {
#[must_use]
pub fn new() -> Self { Self(Weak::new()) }
#[must_use]
pub fn upgrade(&self) -> Option<Root> { Some(Root(self.0.upgrade()?)) }
}
impl Default for WeakRoot {
fn default() -> Self { Self::new() }
}
pub struct TreeFromApiCtx<'a> {
pub sys: &'a System,
@@ -26,16 +144,26 @@ pub struct TreeFromApiCtx<'a> {
pub path: Tok<Vec<Tok<String>>>,
}
impl<'a> TreeFromApiCtx<'a> {
#[must_use]
pub async fn push<'c>(&'c mut self, name: Tok<String>) -> TreeFromApiCtx<'c> {
let path = self.sys.ctx().i.i(&self.path.iter().cloned().chain([name]).collect_vec()).await;
TreeFromApiCtx { path, consts: &mut *self.consts, sys: self.sys }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolvedImport {
target: Sym,
sr: SrcRange,
}
#[derive(Clone, Default)]
pub struct Module {
pub imports: HashMap<Tok<String>, Result<ResolvedImport, Vec<ResolvedImport>>>,
pub members: HashMap<Tok<String>, Rc<Member>>,
}
impl Module {
#[must_use]
pub async fn from_api(api: api::Module, ctx: &mut TreeFromApiCtx<'_>) -> Self {
let mut members = HashMap::new();
for mem in api.members {
@@ -44,10 +172,9 @@ impl Module {
let name = vname.to_sym(ctx.sys.i()).await;
let (lazy, kind) = match mem.kind {
api::MemberKind::Lazy(id) =>
(Some(LazyMemberHandle { id, sys: ctx.sys.clone(), path: name.clone() }), None),
(Some(LazyMemberHandle { id, sys: ctx.sys.id(), path: name.clone() }), None),
api::MemberKind::Const(val) => {
let mut expr_ctx =
ExprParseCtx { ctx: ctx.sys.ctx().clone(), exprs: ctx.sys.ext().exprs().clone() };
let mut expr_ctx = ExprParseCtx { ctx: ctx.sys.ctx(), exprs: ctx.sys.ext().exprs() };
let expr = Expr::from_api(&val, PathSetBuilder::new(), &mut expr_ctx).await;
ctx.consts.insert(name.clone(), expr);
(None, Some(MemberKind::Const))
@@ -56,99 +183,227 @@ impl Module {
let m = Self::from_api(m, &mut ctx.push(mem_name.clone()).await).boxed_local().await;
(None, Some(MemberKind::Module(m)))
},
api::MemberKind::Import(import_path) =>
(None, Some(MemberKind::Alias(Sym::from_api(import_path, ctx.sys.i()).await))),
};
members.insert(
mem_name.clone(),
Rc::new(Member {
path: name.clone(),
public: mem.exported,
lazy: RefCell::new(lazy),
kind: kind.map_or_else(OnceCell::new, OnceCell::from),
}),
);
}
Self { members }
Self { members, imports: HashMap::new() }
}
async fn walk(&self, mut path: impl Iterator<Item = Tok<String>>) -> &Self { todo!() }
async fn from_parsed(
parsed: &ParsedModule,
path: Sym,
pars_root_path: Sym,
pars_root: &ParsedModule,
root: &Module,
preload: &mut HashMap<Sym, Module>,
ctx: &Ctx,
rep: &Reporter,
) -> Self {
let mut imported_names = HashMap::new();
for import in parsed.get_imports() {
if let Some(n) = import.name.clone() {
imported_names.push(n);
continue;
}
// the path in a wildcard import has to be a module
if import.path.is_empty() {
panic!("Imported root")
}
let abs_path = match absolute_path(&path, &import.path) {
Ok(p) => p,
Err(e) => {
rep.report(e.err_obj(&ctx.i, import.sr.pos(), &path.to_string()).await);
continue;
#[must_use]
async fn from_parsed(parsed: &ParsedModule, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self {
let imports_by_name = (parsed.get_imports().into_iter())
.filter_map(|i| Some((i.name.clone()?, i)))
.into_group_map();
let mut glob_imports_by_name = HashMap::<_, Vec<_>>::new();
for import in parsed.get_imports().into_iter().filter(|i| i.name.is_none()) {
let pos = import.sr.pos();
match absolute_path(&path, &import.path, &ctx.ctx.i).await {
Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, pos, &import.path.to_string()).await),
Ok(abs_path) => {
let names_res = match abs_path.strip_prefix(&ctx.pars_prefix[..]) {
None => {
let mut tree_ctx = (ctx.ctx.clone(), &mut *ctx.consts);
resolv_glob(&path, ctx.root, &abs_path, pos, &ctx.ctx.i, &mut tree_ctx).await
},
Some(sub_tgt) => {
let sub_path = (path.strip_prefix(&ctx.pars_prefix[..]))
.expect("from_parsed called with path outside pars_prefix");
resolv_glob(sub_path, ctx.pars_root, sub_tgt, pos, &ctx.ctx.i, &mut ()).await
},
};
let abs_path = abs_path.to_sym(&ctx.ctx.i).await;
match names_res {
Err(e) => ctx.rep.report(e),
Ok(names) =>
for name in names {
match glob_imports_by_name.entry(name) {
Entry::Occupied(mut o) => o.get_mut().push((abs_path.clone(), import.sr.clone())),
Entry::Vacant(v) => {
v.insert_entry(vec![(abs_path.clone(), import.sr.clone())]);
},
}
},
}
},
};
let names = if let Some(subpath) = abs_path.strip_prefix(&pars_root_path[..]) {
let pars_path = (path.strip_prefix(&pars_root_path[..]))
.expect("pars path outside pars root");
resolv_glob(&pars_path, pars_root, &subpath, import.sr.pos(), &ctx.i, rep, &mut ()).await
} else {
resolv_glob(&path, root, &abs_path, import.sr.pos(), &ctx.i, rep, &mut ()).await
}
}
todo!()
let mut imports = HashMap::new();
let conflicting_imports_msg = ctx.ctx.i.i("Conflicting imports").await;
for (key, values) in imports_by_name {
if values.len() == 1 {
let import = values.into_iter().next().unwrap();
let sr = import.sr.clone();
let abs_path_res = absolute_path(&path, &import.clone().mspath(), &ctx.ctx.i).await;
match abs_path_res {
Err(e) => ctx.rep.report(e.err_obj(&ctx.ctx.i, sr.pos(), &import.to_string()).await),
Ok(abs_path) => {
imports
.insert(key, Ok(ResolvedImport { target: abs_path.to_sym(&ctx.ctx.i).await, sr }));
},
}
} else {
for item in values {
ctx.rep.report(mk_err(
conflicting_imports_msg.clone(),
format!("{key} is imported multiple times from different modules"),
[item.sr.pos().into()],
));
}
}
}
for (key, values) in glob_imports_by_name {
if !imports.contains_key(&key) {
let i = &ctx.ctx.i;
let values = stream::iter(values)
.then(|(n, sr)| {
clone!(key; async move {
ResolvedImport { target: n.to_vname().suffix([key.clone()]).to_sym(i).await, sr }
})
})
.collect::<Vec<_>>()
.await;
imports.insert(key, if values.len() == 1 { Ok(values[0].clone()) } else { Err(values) });
}
}
let self_referential_msg = ctx.ctx.i.i("Self-referential import").await;
for (key, value) in imports.iter() {
let Ok(import) = value else { continue };
if import.target.strip_prefix(&path[..]).is_some_and(|t| t.starts_with(&[key.clone()])) {
ctx.rep.report(mk_err(
self_referential_msg.clone(),
format!("import {} points to itself or a path within itself", &import.target),
[import.sr.pos().into()],
));
}
}
let mut members = HashMap::new();
for item in &parsed.items {
match &item.kind {
ItemKind::Member(mem) => {
let path = path.to_vname().suffix([mem.name.clone()]).to_sym(&ctx.ctx.i).await;
let kind = OnceCell::from(MemberKind::from_parsed(&mem.kind, path.clone(), ctx).await);
members.insert(
mem.name.clone(),
Rc::new(Member { kind, lazy: RefCell::default(), public: mem.exported }),
);
},
ItemKind::Import(_) => (),
}
}
Module { imports, members }
}
pub async fn merge(
&self,
other: &Module,
ctx: Ctx,
consts: &mut HashMap<Sym, Expr>,
) -> Result<Module, MergeErr> {
if !self.imports.is_empty() || !other.imports.is_empty() {
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Imports });
}
let mut members = HashMap::new();
for (key, mem) in &other.members {
let Some(own) = self.members.get(key) else {
members.insert(key.clone(), mem.clone());
continue;
};
if own.public != mem.public {
return Err(MergeErr { path: VPath::new([]), kind: MergeErrKind::Visibility });
}
match (own.kind(ctx.clone(), consts).await, mem.kind(ctx.clone(), consts).await) {
(MemberKind::Module(own_sub), MemberKind::Module(sub)) => {
match own_sub.merge(sub, ctx.clone(), consts).boxed_local().await {
Ok(module) => {
members.insert(
key.clone(),
Rc::new(Member {
lazy: RefCell::new(None),
public: own.public,
kind: OnceCell::from(MemberKind::Module(module)),
}),
);
},
Err(mut e) => {
e.path = e.path.prefix([key.clone()]);
return Err(e);
},
}
},
_ => return Err(MergeErr { path: VPath::new([key.clone()]), kind: MergeErrKind::Const }),
}
}
for (key, mem) in &self.members {
if let Entry::Vacant(slot) = members.entry(key.clone()) {
slot.insert(mem.clone());
}
}
Ok(Module { imports: HashMap::new(), members })
}
}
#[derive(Debug)]
pub struct MergeErr {
pub path: VPath,
pub kind: MergeErrKind,
}
#[derive(Debug)]
pub enum MergeErrKind {
Imports,
Visibility,
Const,
}
pub struct FromParsedCtx<'a> {
pars_prefix: Sym,
pars_root: &'a ParsedModule,
root: &'a Module,
consts: &'a mut HashMap<Sym, Expr>,
rep: &'a Reporter,
ctx: &'a Ctx,
deferred_consts: &'a mut HashMap<Sym, (api::SysId, api::ParsedConstId)>,
}
impl Tree for Module {
type Ctx = HashMap<Sym, Expr>;
async fn walk<I: IntoIterator<Item = Tok<String>>>(
type Ctx<'a> = (Ctx, &'a mut HashMap<Sym, Expr>);
async fn child(
&self,
key: Tok<String>,
public_only: bool,
path: I,
ctx: &'_ mut Self::Ctx,
) -> Result<&Self, crate::parsed::WalkError> {
let mut cur = self;
for (pos, step) in path.into_iter().enumerate() {
let Some(member) = self.members.get(&step) else {
return Err(WalkError{ pos, kind: WalkErrorKind::Missing })
};
if public_only && !member.public {
return Err(WalkError { pos, kind: WalkErrorKind::Private })
}
match &member.kind {
MemberKind::Module(m) => cur = m,
MemberKind::Alias()
}
(ctx, consts): &mut Self::Ctx<'_>,
) -> crate::dealias::ChildResult<'_, Self> {
let Some(member) = self.members.get(&key) else {
return Err(ChildErrorKind::Missing);
};
if public_only && !member.public {
return Err(ChildErrorKind::Private);
}
match &member.kind(ctx.clone(), consts).await {
MemberKind::Module(m) => Ok(m),
MemberKind::Const => Err(ChildErrorKind::Constant),
}
}
fn children(&self, public_only: bool) -> hashbrown::HashSet<Tok<String>> {
self.members.iter().filter(|(_, v)| !public_only || v.public).map(|(k, _)| k.clone()).collect()
}
}
pub struct Member {
pub public: bool,
pub path: Sym,
pub lazy: RefCell<Option<LazyMemberHandle>>,
pub kind: OnceCell<MemberKind>,
}
impl Member {
pub async fn kind_mut(&mut self, consts: &mut HashMap<Sym, Expr>) -> &mut MemberKind {
self.kind(consts).await;
self.kind.get_mut().expect("Thhe above line should have initialized it")
}
pub async fn kind(&self, consts: &mut HashMap<Sym, Expr>) -> &MemberKind {
#[must_use]
pub async fn kind<'a>(&'a self, ctx: Ctx, consts: &mut HashMap<Sym, Expr>) -> &'a MemberKind {
(self.kind.get_or_init(async {
let handle = self.lazy.borrow_mut().take().expect("If kind is uninit, lazy must be Some");
handle.run(consts).await
handle.run(ctx, consts).await
}))
.await
}
@@ -157,65 +412,48 @@ impl Member {
pub enum MemberKind {
Const,
Module(Module),
/// This must be pointing at the final value, not a second alias.
Alias(Sym),
}
impl MemberKind {
async fn from_parsed(parsed: &ParsedMemberKind, root: &ParsedModule) -> Self {
#[must_use]
async fn from_parsed(parsed: &ParsedMemberKind, path: Sym, ctx: &mut FromParsedCtx<'_>) -> Self {
match parsed {
ParsedMemberKind::Const => MemberKind::Const,
ParsedMemberKind::Mod(m) => MemberKind::Module(Module::from_parsed(m, root).await),
ParsedMemberKind::ParsedConst(expr) => {
ctx.consts.insert(path, expr.clone());
MemberKind::Const
},
ParsedMemberKind::DeferredConst(id, sys) => {
ctx.deferred_consts.insert(path, (sys.id(), *id));
MemberKind::Const
},
ParsedMemberKind::Mod(m) =>
MemberKind::Module(Module::from_parsed(m, path, ctx).boxed_local().await),
}
}
}
pub struct LazyMemberHandle {
id: api::TreeId,
sys: System,
sys: api::SysId,
path: Sym,
}
impl LazyMemberHandle {
pub async fn run(self, consts: &mut HashMap<Sym, Expr>) -> MemberKind {
match self.sys.get_tree(self.id).await {
#[must_use]
pub async fn run(self, ctx: Ctx, consts: &mut HashMap<Sym, Expr>) -> MemberKind {
let sys = ctx.system_inst(self.sys).await.expect("Missing system for lazy member");
match sys.get_tree(self.id).await {
api::MemberKind::Const(c) => {
let mut pctx =
ExprParseCtx { ctx: self.sys.ctx().clone(), exprs: self.sys.ext().exprs().clone() };
let mut pctx = ExprParseCtx { ctx: &ctx, exprs: sys.ext().exprs() };
consts.insert(self.path, Expr::from_api(&c, PathSetBuilder::new(), &mut pctx).await);
MemberKind::Const
},
api::MemberKind::Module(m) => MemberKind::Module(
Module::from_api(m, &mut TreeFromApiCtx { sys: &self.sys, consts, path: self.path.tok() })
.await,
Module::from_api(m, &mut TreeFromApiCtx { sys: &sys, consts, path: self.path.tok() }).await,
),
api::MemberKind::Lazy(id) => Self { id, ..self }.run(consts).boxed_local().await,
api::MemberKind::Import(path) => MemberKind::Alias(Sym::from_api(path, self.sys.i()).await),
api::MemberKind::Lazy(id) => Self { id, ..self }.run(ctx, consts).boxed_local().await,
}
}
pub async fn into_member(self, public: bool, path: Sym) -> Member {
Member { public, path, kind: OnceCell::new(), lazy: RefCell::new(Some(self)) }
#[must_use]
pub async fn into_member(self, public: bool) -> Member {
Member { public, kind: OnceCell::new(), lazy: RefCell::new(Some(self)) }
}
}
// TODO: this one should own but not execute the lazy handle.
// Lazy handles should run
// - in the tree converter function as needed to resolve imports
// - in the tree itself when a constant is loaded
// - when a different lazy subtree references them in a wildcard import and
// forces the enumeration.
//
// do we actually need to allow wildcard imports in lazy trees? maybe a
// different kind of import is sufficient. Source code never becomes a lazy
// tree. What does?
// - Systems subtrees rarely reference each other at all. They can't use macros
// and they usually point to constants with an embedded expr.
// - Compiled libraries on the long run. The code as written may reference
// constants by indirect path. But this is actually the same as the above,
// they also wouldn't use regular imports because they are distributed as
// exprs.
//
// Everything is distributed either as source code or as exprs. Line parsers
// also operate on tokens.
//
// TODO: The trees produced by systems can be safely changed
// to the new kind of tree. This datastructure does not need to support the lazy
// handle.