forked from Orchid/orchid
All sorts of test scaffolding works now
This commit is contained in:
@@ -6,9 +6,11 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
async-once-cell = "0.5.4"
|
||||
async-process = "2.3.0"
|
||||
async-std = "1.13.0"
|
||||
async-stream = "0.3.6"
|
||||
bound = "0.6.0"
|
||||
derive_destructure = "1.0.0"
|
||||
futures = "0.3.31"
|
||||
hashbrown = "0.15.2"
|
||||
|
||||
@@ -10,6 +10,7 @@ use orchid_base::tree::AtomRepr;
|
||||
use crate::api;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::expr::Expr;
|
||||
use crate::extension::Extension;
|
||||
use crate::system::System;
|
||||
|
||||
#[derive(destructure)]
|
||||
@@ -75,6 +76,8 @@ impl AtomHand {
|
||||
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), arg.id())).await,
|
||||
}
|
||||
}
|
||||
pub fn sys(&self) -> &System { &self.0.owner }
|
||||
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
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ use orchid_base::interner::Interner;
|
||||
use crate::api;
|
||||
use crate::atom::WeakAtomHand;
|
||||
use crate::system::{System, WeakSystem};
|
||||
use crate::tree::Module;
|
||||
|
||||
pub struct CtxData {
|
||||
pub i: Rc<Interner>,
|
||||
@@ -19,6 +20,7 @@ pub struct CtxData {
|
||||
pub systems: RwLock<HashMap<api::SysId, WeakSystem>>,
|
||||
pub system_id: RefCell<NonZeroU16>,
|
||||
pub owned_atoms: RwLock<HashMap<api::AtomId, WeakAtomHand>>,
|
||||
pub root: RwLock<Module>,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct Ctx(Rc<CtxData>);
|
||||
@@ -34,6 +36,7 @@ impl Ctx {
|
||||
systems: RwLock::default(),
|
||||
system_id: RefCell::new(NonZero::new(1).unwrap()),
|
||||
owned_atoms: RwLock::default(),
|
||||
root: RwLock::new(Module::default()),
|
||||
}))
|
||||
}
|
||||
pub(crate) async fn system_inst(&self, id: api::SysId) -> Option<System> {
|
||||
|
||||
240
orchid-host/src/dealias.rs
Normal file
240
orchid-host/src/dealias.rs
Normal file
@@ -0,0 +1,240 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use futures::FutureExt;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::{Either, Itertools};
|
||||
use orchid_base::error::{OrcErr, Reporter, mk_err};
|
||||
use orchid_base::format::{FmtCtxImpl, Format, take_first};
|
||||
use orchid_base::interner::{Interner, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::name::{NameLike, Sym, VName};
|
||||
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
use crate::tree::{ItemKind, MemberKind, Module, RuleKind, WalkErrorKind};
|
||||
|
||||
/// Errors produced by absolute_path
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum AbsPathError {
|
||||
/// `super` of root requested, for example, `app::main` referenced
|
||||
/// `super::super::super::std`
|
||||
TooManySupers,
|
||||
/// root selected, for example, `app::main` referenced exactly `super::super`.
|
||||
/// The empty path also triggers this.
|
||||
RootPath,
|
||||
}
|
||||
impl AbsPathError {
|
||||
pub async fn err_obj(self, i: &Interner, pos: Pos, path: &str) -> OrcErr {
|
||||
let (descr, msg) = match self {
|
||||
AbsPathError::RootPath => (
|
||||
i.i("Path ends on root module").await,
|
||||
format!(
|
||||
"{path} is equal to the empty path. You cannot directly reference the root. \
|
||||
Use one fewer 'super::' or add more segments to make it valid."
|
||||
),
|
||||
),
|
||||
AbsPathError::TooManySupers => (
|
||||
i.i("Too many 'super::' steps in path").await,
|
||||
format!("{path} is leading outside the root."),
|
||||
),
|
||||
};
|
||||
mk_err(descr, msg, [pos.into()])
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn a relative (import) path into an absolute path.
|
||||
/// If the import path is empty, the return value is also empty.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// if the relative path contains as many or more `super` segments than the
|
||||
/// length of the absolute path.
|
||||
pub fn absolute_path(
|
||||
mut cwd: &[Tok<String>],
|
||||
mut rel: &[Tok<String>],
|
||||
) -> 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;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
match relative {
|
||||
true => VName::new(cwd.iter().chain(rel).cloned()),
|
||||
false => VName::new(rel.to_vec()),
|
||||
}
|
||||
.map_err(|_| AbsPathError::RootPath)
|
||||
}
|
||||
|
||||
pub async fn resolv_glob(
|
||||
cwd: &[Tok<String>],
|
||||
root: &Module,
|
||||
abs_path: &[Tok<String>],
|
||||
pos: Pos,
|
||||
i: &Interner,
|
||||
r: &impl Reporter,
|
||||
) -> Vec<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(coprefix_len);
|
||||
let co_parent = root.walk(false, co_prefix.iter().cloned()).await.expect("Invalid step in cwd");
|
||||
let target_module = match co_parent.walk(true, diff_path.iter().cloned()).await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
let path = abs_path[..=coprefix_len + e.pos].iter().join("::");
|
||||
let (tk, msg) = match e.kind {
|
||||
WalkErrorKind::Constant =>
|
||||
(i.i("Invalid import path").await, format!("{path} is a constant")),
|
||||
WalkErrorKind::Missing => (i.i("Invalid import path").await, format!("{path} not found")),
|
||||
WalkErrorKind::Private => (i.i("Import inaccessible").await, format!("{path} is private")),
|
||||
};
|
||||
r.report(mk_err(tk, msg, [pos.into()]));
|
||||
return vec![];
|
||||
},
|
||||
};
|
||||
target_module.exports.clone()
|
||||
}
|
||||
|
||||
/// Read import statements and convert them into aliases, rasising any import
|
||||
/// errors in the process
|
||||
pub async fn imports_to_aliases(
|
||||
module: &Module,
|
||||
cwd: &mut Vec<Tok<String>>,
|
||||
root: &Module,
|
||||
alias_map: &mut HashMap<Sym, Sym>,
|
||||
alias_rev_map: &mut HashMap<Sym, HashSet<Sym>>,
|
||||
i: &Interner,
|
||||
rep: &impl Reporter,
|
||||
) {
|
||||
let mut import_locs = HashMap::<Sym, Vec<Pos>>::new();
|
||||
for item in &module.items {
|
||||
match &item.kind {
|
||||
ItemKind::Import(imp) => match absolute_path(cwd, &imp.path) {
|
||||
Err(e) => rep.report(e.err_obj(i, item.pos.clone(), &imp.path.iter().join("::")).await),
|
||||
Ok(abs_path) => {
|
||||
let names = match imp.name.as_ref() {
|
||||
Some(n) => Either::Right([n.clone()].into_iter()),
|
||||
None => Either::Left(
|
||||
resolv_glob(cwd, root, &abs_path, item.pos.clone(), i, rep).await.into_iter(),
|
||||
),
|
||||
};
|
||||
for name in names {
|
||||
let mut tgt = abs_path.clone().suffix([name.clone()]).to_sym(i).await;
|
||||
let src = Sym::new(cwd.iter().cloned().chain([name]), i).await.unwrap();
|
||||
import_locs.entry(src.clone()).or_insert(vec![]).push(item.pos.clone());
|
||||
if let Some(tgt2) = alias_map.get(&tgt) {
|
||||
tgt = tgt2.clone();
|
||||
}
|
||||
if src == tgt {
|
||||
rep.report(mk_err(
|
||||
i.i("Circular references").await,
|
||||
format!("{src} circularly refers to itself"),
|
||||
[item.pos.clone().into()],
|
||||
));
|
||||
continue;
|
||||
}
|
||||
if let Some(fst_val) = alias_map.get(&src) {
|
||||
let locations = (import_locs.get(&src))
|
||||
.expect("The same name could only have appeared in the same module");
|
||||
rep.report(mk_err(
|
||||
i.i("Conflicting imports").await,
|
||||
if fst_val == &src {
|
||||
format!("{src} is imported multiple times")
|
||||
} else {
|
||||
format!("{} could refer to both {fst_val} and {src}", src.last())
|
||||
},
|
||||
locations.iter().map(|p| p.clone().into()).collect_vec(),
|
||||
))
|
||||
}
|
||||
let mut srcv = vec![src.clone()];
|
||||
if let Some(src_extra) = alias_rev_map.remove(&src) {
|
||||
srcv.extend(src_extra);
|
||||
}
|
||||
for src in srcv {
|
||||
alias_map.insert(src.clone(), tgt.clone());
|
||||
alias_rev_map.entry(tgt.clone()).or_insert(HashSet::new()).insert(src);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
ItemKind::Member(mem) => match mem.kind().await {
|
||||
MemberKind::Const(_) => (),
|
||||
MemberKind::Mod(m) => {
|
||||
cwd.push(mem.name());
|
||||
imports_to_aliases(m, cwd, root, alias_map, alias_rev_map, i, rep).boxed_local().await;
|
||||
cwd.pop();
|
||||
},
|
||||
},
|
||||
ItemKind::Export(_) | ItemKind::Macro(..) => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn dealias(module: &mut Module, alias_map: &HashMap<Sym, Sym>, i: &Interner) {
|
||||
for item in &mut module.items {
|
||||
match &mut item.kind {
|
||||
ItemKind::Export(_) | ItemKind::Import(_) => (),
|
||||
ItemKind::Member(mem) => match mem.kind_mut().await {
|
||||
MemberKind::Const(c) => {
|
||||
let Some(source) = c.source() else { continue };
|
||||
let Some(new_source) = dealias_mactreev(source, alias_map, i).await else { continue };
|
||||
c.set_source(new_source);
|
||||
},
|
||||
MemberKind::Mod(m) => dealias(m, alias_map, i).boxed_local().await,
|
||||
},
|
||||
ItemKind::Macro(_, rules) =>
|
||||
for rule in rules.iter_mut() {
|
||||
let RuleKind::Native(c) = &mut rule.kind else { continue };
|
||||
let Some(source) = c.source() else { continue };
|
||||
let Some(new_source) = dealias_mactreev(source, alias_map, i).await else { continue };
|
||||
c.set_source(new_source);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn dealias_mactree(
|
||||
mtree: &MacTree,
|
||||
aliases: &HashMap<Sym, Sym>,
|
||||
i: &Interner,
|
||||
) -> Option<MacTree> {
|
||||
let new_tok = match &*mtree.tok {
|
||||
MacTok::Atom(_) | MacTok::Ph(_) => return None,
|
||||
tok @ (MacTok::Done(_) | MacTok::Ref(_) | MacTok::Slot(_)) => panic!(
|
||||
"{} should not appear in retained pre-macro source",
|
||||
take_first(&tok.print(&FmtCtxImpl { i }).await, true)
|
||||
),
|
||||
MacTok::Name(n) => MacTok::Name(aliases.get(n).unwrap_or(n).clone()),
|
||||
MacTok::Lambda(arg, body) => {
|
||||
match (dealias_mactreev(arg, aliases, i).await, dealias_mactreev(body, aliases, i).await) {
|
||||
(None, None) => return None,
|
||||
(Some(arg), None) => MacTok::Lambda(arg, body.clone()),
|
||||
(None, Some(body)) => MacTok::Lambda(arg.clone(), body),
|
||||
(Some(arg), Some(body)) => MacTok::Lambda(arg, body),
|
||||
}
|
||||
},
|
||||
MacTok::S(p, b) => MacTok::S(*p, dealias_mactreev(b, aliases, i).await?),
|
||||
};
|
||||
Some(MacTree { pos: mtree.pos.clone(), tok: Rc::new(new_tok) })
|
||||
}
|
||||
|
||||
async fn dealias_mactreev(
|
||||
mtreev: &[MacTree],
|
||||
aliases: &HashMap<Sym, Sym>,
|
||||
i: &Interner,
|
||||
) -> Option<Vec<MacTree>> {
|
||||
let mut results = Vec::with_capacity(mtreev.len());
|
||||
let mut any_some = false;
|
||||
for item in mtreev {
|
||||
let out = dealias_mactree(item, aliases, i).boxed_local().await;
|
||||
any_some |= out.is_some();
|
||||
results.push(out.unwrap_or(item.clone()));
|
||||
}
|
||||
any_some.then_some(results)
|
||||
}
|
||||
223
orchid-host/src/execute.rs
Normal file
223
orchid-host/src/execute.rs
Normal file
@@ -0,0 +1,223 @@
|
||||
use std::mem;
|
||||
|
||||
use async_std::sync::RwLockWriteGuard;
|
||||
use bound::Bound;
|
||||
use futures::FutureExt;
|
||||
use orchid_base::error::{OrcErrv, mk_errv};
|
||||
use orchid_base::format::{FmtCtxImpl, Format, take_first};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::logging::Logger;
|
||||
use orchid_base::name::NameLike;
|
||||
|
||||
use crate::ctx::Ctx;
|
||||
use crate::expr::{Expr, ExprKind, PathSet, Step};
|
||||
use crate::tree::{ItemKind, MemberKind};
|
||||
|
||||
type ExprGuard = Bound<RwLockWriteGuard<'static, ExprKind>, Expr>;
|
||||
|
||||
/// The stack operation associated with a transform
|
||||
enum StackOp {
|
||||
Pop,
|
||||
Nop,
|
||||
Push(Expr),
|
||||
Swap(Expr),
|
||||
Unwind(OrcErrv),
|
||||
}
|
||||
|
||||
pub enum ExecResult {
|
||||
Value(Expr),
|
||||
Gas(ExecCtx),
|
||||
Err(OrcErrv),
|
||||
}
|
||||
|
||||
pub struct ExecCtx {
|
||||
ctx: Ctx,
|
||||
gas: Option<u64>,
|
||||
stack: Vec<ExprGuard>,
|
||||
cur: ExprGuard,
|
||||
cur_pos: Pos,
|
||||
did_pop: bool,
|
||||
logger: Logger,
|
||||
}
|
||||
impl ExecCtx {
|
||||
pub async fn new(ctx: Ctx, logger: Logger, 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 }
|
||||
}
|
||||
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 }
|
||||
pub fn idle(&self) -> bool { self.did_pop }
|
||||
pub fn result(self) -> ExecResult {
|
||||
if self.idle() {
|
||||
match &*self.cur {
|
||||
ExprKind::Bottom(errv) => ExecResult::Err(errv.clone()),
|
||||
_ => ExecResult::Value(*self.cur.unbind()),
|
||||
}
|
||||
} else {
|
||||
ExecResult::Gas(self)
|
||||
}
|
||||
}
|
||||
pub fn use_gas(&mut self, amount: u64) -> bool {
|
||||
if let Some(gas) = &mut self.gas {
|
||||
*gas -= amount;
|
||||
}
|
||||
self.gas != Some(0)
|
||||
}
|
||||
pub async fn try_lock(&self, ex: &Expr) -> ExprGuard {
|
||||
Bound::async_new(ex.clone(), |ex| ex.kind().write()).await
|
||||
}
|
||||
pub async fn unpack_ident(&self, ex: &Expr) -> Expr {
|
||||
match ex.kind().try_write().as_deref_mut() {
|
||||
Some(ExprKind::Identity(ex)) => {
|
||||
let val = self.unpack_ident(ex).boxed_local().await;
|
||||
*ex = val.clone();
|
||||
val
|
||||
},
|
||||
Some(_) => ex.clone(),
|
||||
None => panic!("Cycle encountered!"),
|
||||
}
|
||||
}
|
||||
pub async fn execute(&mut self) {
|
||||
while self.use_gas(1) {
|
||||
let mut kind_swap = ExprKind::Missing;
|
||||
mem::swap(&mut kind_swap, &mut self.cur);
|
||||
let unit = kind_swap.print(&FmtCtxImpl { i: &self.ctx.i }).await;
|
||||
writeln!(self.logger, "Exxecute lvl{} {}", self.stack.len(), take_first(&unit, true));
|
||||
let (kind, op) = match kind_swap {
|
||||
ExprKind::Identity(target) => {
|
||||
let inner = self.unpack_ident(&target).await;
|
||||
(ExprKind::Identity(inner.clone()), StackOp::Swap(inner))
|
||||
},
|
||||
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 (cn, mp) = name.split_last();
|
||||
let root_lock = self.ctx.root.read().await;
|
||||
let module = root_lock.walk(true, mp.iter().cloned()).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 => (
|
||||
ExprKind::Bottom(mk_errv(
|
||||
self.ctx.i.i("Constant does not exist").await,
|
||||
format!("{name} does not refer to a constant"),
|
||||
[self.cur_pos.clone().into()],
|
||||
)),
|
||||
StackOp::Pop,
|
||||
),
|
||||
Some(mem) => match mem.kind().await {
|
||||
MemberKind::Mod(_) => (
|
||||
ExprKind::Bottom(mk_errv(
|
||||
self.ctx.i.i("module used as constant").await,
|
||||
format!("{name} is a module"),
|
||||
[self.cur_pos.clone().into()],
|
||||
)),
|
||||
StackOp::Pop,
|
||||
),
|
||||
MemberKind::Const(c) => {
|
||||
let value = c.get_bytecode(&self.ctx).await;
|
||||
(ExprKind::Identity(value.clone()), 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)),
|
||||
ExprKind::Call(f, x) if !self.did_pop => (ExprKind::Call(f.clone(), x), StackOp::Push(f)),
|
||||
ExprKind::Call(f, x) => match f.try_into_owned_atom().await {
|
||||
Ok(atom) => {
|
||||
let mut ext = atom.sys().ext().clone();
|
||||
let x_norm = self.unpack_ident(&x).await;
|
||||
let val = Expr::from_api(&atom.call(x_norm).await, &mut ext).await;
|
||||
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
|
||||
},
|
||||
Err(f) => match &*f.kind().read().await {
|
||||
ExprKind::Arg | ExprKind::Call(..) | ExprKind::Seq(..) | ExprKind::Const(_) =>
|
||||
panic!("This should not appear outside function bodies"),
|
||||
ExprKind::Missing => panic!("Should have been replaced"),
|
||||
ExprKind::Atom(a) => {
|
||||
let mut ext = a.sys().ext().clone();
|
||||
let x_norm = self.unpack_ident(&x).await;
|
||||
let val = Expr::from_api(&a.clone().call(x_norm).await, &mut ext).await;
|
||||
(ExprKind::Identity(val.clone()), StackOp::Swap(val))
|
||||
},
|
||||
ExprKind::Bottom(exprv) => (ExprKind::Bottom(exprv.clone()), StackOp::Pop),
|
||||
ExprKind::Lambda(None, body) =>
|
||||
(ExprKind::Identity(body.clone()), StackOp::Swap(body.clone())),
|
||||
ExprKind::Lambda(Some(path), body) => {
|
||||
let output = substitute(body, &path.steps, path.next(), x).await;
|
||||
(ExprKind::Identity(output.clone()), StackOp::Swap(output))
|
||||
},
|
||||
ExprKind::Identity(f) => (ExprKind::Call(f.clone(), x.clone()), StackOp::Nop),
|
||||
},
|
||||
},
|
||||
l @ ExprKind::Lambda(..) => (l, StackOp::Pop),
|
||||
ExprKind::Missing => panic!("Should have been replaced"),
|
||||
};
|
||||
self.did_pop = matches!(op, StackOp::Pop | StackOp::Unwind(_));
|
||||
*self.cur = kind;
|
||||
match op {
|
||||
StackOp::Nop => (),
|
||||
StackOp::Pop => match self.stack.pop() {
|
||||
Some(top) => self.cur = top,
|
||||
None => return,
|
||||
},
|
||||
StackOp::Push(sub) => {
|
||||
self.cur_pos = sub.pos();
|
||||
let mut new_guard = self.try_lock(&sub).await;
|
||||
mem::swap(&mut self.cur, &mut new_guard);
|
||||
self.stack.push(new_guard);
|
||||
},
|
||||
StackOp::Swap(new) => self.cur = self.try_lock(&new).await,
|
||||
StackOp::Unwind(err) => {
|
||||
for dependent in self.stack.iter_mut() {
|
||||
**dependent = ExprKind::Bottom(err.clone());
|
||||
}
|
||||
*self.cur = ExprKind::Bottom(err.clone());
|
||||
self.stack = vec![];
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn substitute(
|
||||
src: &Expr,
|
||||
path: &[Step],
|
||||
next: Option<(&PathSet, &PathSet)>,
|
||||
val: Expr,
|
||||
) -> Expr {
|
||||
let exk = src.kind().try_read().expect("Cloned function body parts must never be written");
|
||||
let kind = match (&*exk, path.split_first()) {
|
||||
(ExprKind::Identity(x), _) => return substitute(x, path, next, val).boxed_local().await,
|
||||
(ExprKind::Lambda(ps, b), _) =>
|
||||
ExprKind::Lambda(ps.clone(), substitute(b, path, next, val).boxed_local().await),
|
||||
(exk, None) => match (exk, next) {
|
||||
(ExprKind::Arg, None) => return val.clone(),
|
||||
(ExprKind::Call(f, x), Some((l, r))) => ExprKind::Call(
|
||||
substitute(f, &l.steps, l.next(), val.clone()).boxed_local().await,
|
||||
substitute(x, &r.steps, r.next(), val.clone()).boxed_local().await,
|
||||
),
|
||||
(ExprKind::Seq(a, b), Some((l, r))) => ExprKind::Seq(
|
||||
substitute(a, &l.steps, l.next(), val.clone()).boxed_local().await,
|
||||
substitute(b, &r.steps, r.next(), val.clone()).boxed_local().await,
|
||||
),
|
||||
(_, None) => panic!("Can only substitute Arg"),
|
||||
(_, Some(_)) => panic!("Can only fork into Call and Seq"),
|
||||
},
|
||||
(ExprKind::Call(f, x), Some((Step::Left, tail))) =>
|
||||
ExprKind::Call(substitute(f, tail, next, val).boxed_local().await, x.clone()),
|
||||
(ExprKind::Call(f, x), Some((Step::Right, tail))) =>
|
||||
ExprKind::Call(f.clone(), substitute(x, tail, next, val).boxed_local().await),
|
||||
(ExprKind::Seq(f, x), Some((Step::Left, tail))) =>
|
||||
ExprKind::Seq(substitute(f, tail, next, val).boxed_local().await, x.clone()),
|
||||
(ExprKind::Seq(f, x), Some((Step::Right, tail))) =>
|
||||
ExprKind::Seq(f.clone(), substitute(x, tail, next, val).boxed_local().await),
|
||||
(ek, Some(_)) => panic!("Path leads into {ek:?}"),
|
||||
};
|
||||
kind.at(src.pos())
|
||||
}
|
||||
@@ -1,23 +1,28 @@
|
||||
use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::num::NonZeroU64;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::{fmt, mem};
|
||||
|
||||
use async_std::sync::RwLock;
|
||||
use futures::FutureExt;
|
||||
use hashbrown::HashSet;
|
||||
use itertools::Itertools;
|
||||
use orchid_api::ExprTicket;
|
||||
use orchid_base::error::OrcErrv;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use orchid_base::error::{OrcErrv, mk_errv};
|
||||
use orchid_base::format::{FmtCtx, FmtCtxImpl, FmtUnit, Format, Variants, take_first};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::macros::mtreev_fmt;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::tokens::Paren;
|
||||
use orchid_base::tree::{AtomRepr, indent};
|
||||
use orchid_base::{match_mapping, tl_cache};
|
||||
use substack::Substack;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::AtomHand;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::extension::Extension;
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
|
||||
pub type ExprParseCtx = Extension;
|
||||
|
||||
@@ -31,8 +36,19 @@ pub struct ExprData {
|
||||
pub struct Expr(Rc<ExprData>);
|
||||
impl Expr {
|
||||
pub fn pos(&self) -> Pos { self.0.pos.clone() }
|
||||
pub fn as_atom(&self) -> Option<AtomHand> { todo!() }
|
||||
pub fn strong_count(&self) -> usize { todo!() }
|
||||
pub async fn try_into_owned_atom(self) -> Result<AtomHand, Self> {
|
||||
match Rc::try_unwrap(self.0) {
|
||||
Err(e) => Err(Self(e)),
|
||||
Ok(data) => match data.kind.into_inner() {
|
||||
ExprKind::Atom(a) => Ok(a),
|
||||
inner => Err(Self(Rc::new(ExprData { kind: inner.into(), pos: data.pos }))),
|
||||
},
|
||||
}
|
||||
}
|
||||
pub async fn as_atom(&self) -> Option<AtomHand> {
|
||||
if let ExprKind::Atom(a) = &*self.kind().read().await { Some(a.clone()) } else { None }
|
||||
}
|
||||
pub fn strong_count(&self) -> usize { Rc::strong_count(&self.0) }
|
||||
pub fn id(&self) -> api::ExprTicket {
|
||||
api::ExprTicket(
|
||||
NonZeroU64::new(self.0.as_ref() as *const ExprData as usize as u64)
|
||||
@@ -52,52 +68,28 @@ impl Expr {
|
||||
match &*self.0.kind.read().await {
|
||||
ExprKind::Atom(a) => K::Atom(a.to_api().await),
|
||||
ExprKind::Bottom(b) => K::Bottom(b.to_api()),
|
||||
ExprKind::Identity(ex) => ex.to_api().boxed_local().await,
|
||||
_ => K::Opaque,
|
||||
}
|
||||
}
|
||||
pub fn kind(&self) -> &RwLock<ExprKind> { &self.0.kind }
|
||||
}
|
||||
impl Format for Expr {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
return print_expr(self, c, &mut HashSet::new()).await;
|
||||
async fn print_expr<'a>(
|
||||
expr: &'a Expr,
|
||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||
visited: &mut HashSet<ExprTicket>,
|
||||
) -> FmtUnit {
|
||||
if visited.contains(&expr.id()) {
|
||||
return "CYCLIC_EXPR".to_string().into();
|
||||
}
|
||||
visited.insert(expr.id());
|
||||
match &*expr.0.kind.read().await {
|
||||
ExprKind::Arg => "Arg".to_string().into(),
|
||||
ExprKind::Atom(a) => a.print(c).await,
|
||||
ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(),
|
||||
ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),
|
||||
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("{0} {1l}")
|
||||
.bounded("({0} {1b})")))
|
||||
.units([
|
||||
print_expr(f, c, visited).boxed_local().await,
|
||||
print_expr(x, c, visited).boxed_local().await,
|
||||
]),
|
||||
ExprKind::Const(c) => format!("{c}").into(),
|
||||
ExprKind::Lambda(None, body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("\\.{0l}")
|
||||
.bounded("(\\.{0b})")))
|
||||
.units([print_expr(body, c, visited).boxed_local().await]),
|
||||
ExprKind::Lambda(Some(path), body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("\\{0b}. {1l}")
|
||||
.bounded("(\\{0b}. {1b})")))
|
||||
.units([format!("{path}").into(), print_expr(body, c, visited).boxed_local().await]),
|
||||
ExprKind::Seq(l, r) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]{1l}"))).units([
|
||||
print_expr(l, c, visited).boxed_local().await,
|
||||
print_expr(r, c, visited).boxed_local().await,
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
async fn print_expr<'a>(
|
||||
expr: &'a Expr,
|
||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||
visited: &mut HashSet<api::ExprTicket>,
|
||||
) -> FmtUnit {
|
||||
if visited.contains(&expr.id()) {
|
||||
return "CYCLIC_EXPR".to_string().into();
|
||||
}
|
||||
visited.insert(expr.id());
|
||||
print_exprkind(&*expr.kind().read().await, c, visited).boxed_local().await
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ExprKind {
|
||||
@@ -107,7 +99,12 @@ pub enum ExprKind {
|
||||
Arg,
|
||||
Lambda(Option<PathSet>, Expr),
|
||||
Bottom(OrcErrv),
|
||||
Identity(Expr),
|
||||
Const(Sym),
|
||||
/// Temporary expr kind assigned to a write guard to gain ownership of the
|
||||
/// current value during normalization. While this is in place, the guard must
|
||||
/// not be dropped.
|
||||
Missing,
|
||||
}
|
||||
impl ExprKind {
|
||||
pub async fn from_api(api: &api::ExpressionKind, pos: Pos, ctx: &mut ExprParseCtx) -> Self {
|
||||
@@ -127,6 +124,48 @@ impl ExprKind {
|
||||
api::ExpressionKind::Slot(_) => panic!("Handled in Expr"),
|
||||
})
|
||||
}
|
||||
pub fn at(self, pos: Pos) -> Expr { Expr(Rc::new(ExprData { pos, kind: RwLock::new(self) })) }
|
||||
}
|
||||
impl Format for ExprKind {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
print_exprkind(self, c, &mut HashSet::new()).await
|
||||
}
|
||||
}
|
||||
async fn print_exprkind<'a>(
|
||||
ek: &ExprKind,
|
||||
c: &'a (impl FmtCtx + ?Sized + 'a),
|
||||
visited: &mut HashSet<api::ExprTicket>,
|
||||
) -> FmtUnit {
|
||||
match &ek {
|
||||
ExprKind::Arg => "Arg".to_string().into(),
|
||||
ExprKind::Missing =>
|
||||
panic!("This variant is swapped into write guards, so a read can never see it"),
|
||||
ExprKind::Atom(a) => a.print(c).await,
|
||||
ExprKind::Bottom(e) if e.len() == 1 => format!("Bottom({e})").into(),
|
||||
ExprKind::Bottom(e) => format!("Bottom(\n\t{}\n)", indent(&e.to_string())).into(),
|
||||
ExprKind::Call(f, x) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("{0} {1l}")
|
||||
.bounded("({0} {1b})")))
|
||||
.units([print_expr(f, c, visited).await, print_expr(x, c, visited).await]),
|
||||
ExprKind::Identity(id) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("{{{0}}}"))).units([print_expr(
|
||||
id, c, visited,
|
||||
)
|
||||
.boxed_local()
|
||||
.await]),
|
||||
ExprKind::Const(c) => format!("{c}").into(),
|
||||
ExprKind::Lambda(None, body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("\\.{0l}")
|
||||
.bounded("(\\.{0b})")))
|
||||
.units([print_expr(body, c, visited).await]),
|
||||
ExprKind::Lambda(Some(path), body) => tl_cache!(Rc<Variants>: Rc::new(Variants::default()
|
||||
.unbounded("\\{0b}. {1l}")
|
||||
.bounded("(\\{0b}. {1b})")))
|
||||
.units([format!("{path}").into(), print_expr(body, c, visited).await]),
|
||||
ExprKind::Seq(l, r) =>
|
||||
tl_cache!(Rc<Variants>: Rc::new(Variants::default().bounded("[{0b}]{1l}")))
|
||||
.units([print_expr(l, c, visited).await, print_expr(r, c, visited).await]),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
|
||||
@@ -138,31 +177,39 @@ pub enum Step {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PathSet {
|
||||
/// The single steps through [super::nort::Clause::Apply]
|
||||
pub steps: VecDeque<Step>,
|
||||
pub steps: Vec<Step>,
|
||||
/// if Some, it splits at a [super::nort::Clause::Apply]. If None, it ends in
|
||||
/// a [super::nort::Clause::LambdaArg]
|
||||
pub next: Option<(Box<PathSet>, Box<PathSet>)>,
|
||||
}
|
||||
impl PathSet {
|
||||
pub fn after(mut self, step: Step) -> Self {
|
||||
self.steps.push_front(step);
|
||||
self
|
||||
pub fn next(&self) -> Option<(&PathSet, &PathSet)> {
|
||||
self.next.as_ref().map(|(l, r)| (&**l, &**r))
|
||||
}
|
||||
pub fn from_api(id: u64, api: &api::ExpressionKind) -> Option<Self> {
|
||||
use api::ExpressionKind as K;
|
||||
match &api {
|
||||
K::Arg(id2) => (id == *id2).then(|| Self { steps: VecDeque::new(), next: None }),
|
||||
K::Bottom(_) | K::Const(_) | K::NewAtom(_) | K::Slot(_) => None,
|
||||
K::Lambda(_, b) => Self::from_api(id, &b.kind),
|
||||
K::Call(l, r) | K::Seq(l, r) => {
|
||||
match (Self::from_api(id, &l.kind), Self::from_api(id, &r.kind)) {
|
||||
(Some(a), Some(b)) =>
|
||||
Some(Self { steps: VecDeque::new(), next: Some((Box::new(a), Box::new(b))) }),
|
||||
(Some(l), None) => Some(l.after(Step::Left)),
|
||||
(None, Some(r)) => Some(r.after(Step::Right)),
|
||||
(None, None) => None,
|
||||
}
|
||||
},
|
||||
struct Suffix(VecDeque<Step>, Option<(Box<PathSet>, Box<PathSet>)>);
|
||||
fn seal(Suffix(steps, next): Suffix) -> PathSet { PathSet { steps: steps.into(), next } }
|
||||
fn after(step: Step, mut suf: Suffix) -> Suffix {
|
||||
suf.0.push_front(step);
|
||||
suf
|
||||
}
|
||||
return from_api_inner(id, api).map(seal);
|
||||
fn from_api_inner(id: u64, api: &api::ExpressionKind) -> Option<Suffix> {
|
||||
match &api {
|
||||
K::Arg(id2) => (id == *id2).then_some(Suffix(VecDeque::new(), None)),
|
||||
K::Bottom(_) | K::Const(_) | K::NewAtom(_) | K::Slot(_) => None,
|
||||
K::Lambda(_, b) => from_api_inner(id, &b.kind),
|
||||
K::Call(l, r) | K::Seq(l, r) => {
|
||||
match (from_api_inner(id, &l.kind), from_api_inner(id, &r.kind)) {
|
||||
(Some(a), Some(b)) =>
|
||||
Some(Suffix(VecDeque::new(), Some((Box::new(seal(a)), Box::new(seal(b)))))),
|
||||
(Some(l), None) => Some(after(Step::Left, l)),
|
||||
(None, Some(r)) => Some(after(Step::Right, r)),
|
||||
(None, None) => None,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,12 +224,136 @@ impl fmt::Display for PathSet {
|
||||
}
|
||||
write!(f, "({left}|{right})")
|
||||
},
|
||||
None => write!(f, "{step_s}"),
|
||||
None => write!(f, "{step_s}x"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
ExprKind::Bottom(errv).at(pos)
|
||||
}
|
||||
|
||||
pub struct WeakExpr(Weak<ExprData>);
|
||||
impl WeakExpr {
|
||||
pub fn upgrade(&self) -> Option<Expr> { self.0.upgrade().map(Expr) }
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum SrcToExprStep<'a> {
|
||||
Left,
|
||||
Right,
|
||||
Lambda(Sym, &'a RefCell<Option<PathSet>>),
|
||||
}
|
||||
|
||||
pub async fn mtreev_to_expr(
|
||||
src: &[MacTree],
|
||||
stack: Substack<'_, SrcToExprStep<'_>>,
|
||||
ctx: &Ctx,
|
||||
) -> ExprKind {
|
||||
let Some((x, f)) = src.split_last() else { panic!("Empty expression cannot be evaluated") };
|
||||
let x_stack = if f.is_empty() { stack.clone() } else { stack.push(SrcToExprStep::Right) };
|
||||
let x_kind = match &*x.tok {
|
||||
MacTok::Atom(a) => ExprKind::Atom(a.clone()),
|
||||
MacTok::Name(n) => 'name: {
|
||||
let mut steps = VecDeque::new();
|
||||
for step in x_stack.iter() {
|
||||
match step {
|
||||
SrcToExprStep::Left => steps.push_front(Step::Left),
|
||||
SrcToExprStep::Right => steps.push_front(Step::Right),
|
||||
SrcToExprStep::Lambda(name, _) if name != n => continue,
|
||||
SrcToExprStep::Lambda(_, cell) => {
|
||||
let mut ps = cell.borrow_mut();
|
||||
match &mut *ps {
|
||||
val @ None => *val = Some(PathSet { steps: steps.into(), next: None }),
|
||||
Some(val) => {
|
||||
let mut swap = PathSet { steps: Vec::new(), next: None };
|
||||
mem::swap(&mut swap, val);
|
||||
*val = merge(swap, &Vec::from(steps));
|
||||
fn merge(ps: PathSet, steps: &[Step]) -> PathSet {
|
||||
let diff_idx = ps.steps.iter().zip(steps).take_while(|(l, r)| l == r).count();
|
||||
if diff_idx == ps.steps.len() {
|
||||
if diff_idx == steps.len() {
|
||||
match ps.next {
|
||||
Some(_) => panic!("New path ends where old path forks"),
|
||||
None => panic!("New path same as old path"),
|
||||
}
|
||||
}
|
||||
let Some((left, right)) = ps.next else {
|
||||
panic!("Old path ends where new path continues")
|
||||
};
|
||||
let next = match steps[diff_idx] {
|
||||
Step::Left => Some((Box::new(merge(*left, &steps[diff_idx + 1..])), right)),
|
||||
Step::Right => Some((left, Box::new(merge(*right, &steps[diff_idx + 1..])))),
|
||||
};
|
||||
PathSet { steps: ps.steps, next }
|
||||
} else {
|
||||
let shared_steps = ps.steps.iter().take(diff_idx).cloned().collect();
|
||||
let main_steps = ps.steps.iter().skip(diff_idx + 1).cloned().collect();
|
||||
let new_branch = steps[diff_idx + 1..].to_vec();
|
||||
let main_side = PathSet { steps: main_steps, next: ps.next };
|
||||
let new_side = PathSet { steps: new_branch, next: None };
|
||||
let (left, right) = match steps[diff_idx] {
|
||||
Step::Left => (new_side, main_side),
|
||||
Step::Right => (main_side, new_side),
|
||||
};
|
||||
PathSet { steps: shared_steps, next: Some((Box::new(left), Box::new(right))) }
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
break 'name ExprKind::Arg;
|
||||
},
|
||||
}
|
||||
}
|
||||
ExprKind::Const(n.clone())
|
||||
},
|
||||
MacTok::Ph(_) | MacTok::Done(_) | MacTok::Ref(_) | MacTok::Slot(_) =>
|
||||
ExprKind::Bottom(mk_errv(
|
||||
ctx.i.i("placeholder in value").await,
|
||||
"Placeholders cannot appear anywhere outside macro patterns",
|
||||
[x.pos.clone().into()],
|
||||
)),
|
||||
MacTok::S(Paren::Round, b) if b.is_empty() =>
|
||||
return ExprKind::Bottom(mk_errv(
|
||||
ctx.i.i("Empty expression").await,
|
||||
"Empty parens () are illegal",
|
||||
[x.pos.clone().into()],
|
||||
)),
|
||||
MacTok::S(Paren::Round, b) => mtreev_to_expr(b, x_stack, ctx).boxed_local().await,
|
||||
MacTok::S(..) => ExprKind::Bottom(mk_errv(
|
||||
ctx.i.i("non-round parentheses after macros").await,
|
||||
"[] or {} block was not consumed by macros; expressions may only contain ()",
|
||||
[x.pos.clone().into()],
|
||||
)),
|
||||
MacTok::Lambda(_, b) if b.is_empty() =>
|
||||
return ExprKind::Bottom(mk_errv(
|
||||
ctx.i.i("Empty lambda").await,
|
||||
"Lambdas must have a body",
|
||||
[x.pos.clone().into()],
|
||||
)),
|
||||
MacTok::Lambda(arg, b) => 'lambda_converter: {
|
||||
if let [MacTree { tok, .. }] = &**arg {
|
||||
if let MacTok::Name(n) = &**tok {
|
||||
let path = RefCell::new(None);
|
||||
let b = mtreev_to_expr(b, x_stack.push(SrcToExprStep::Lambda(n.clone(), &path)), ctx)
|
||||
.boxed_local()
|
||||
.await;
|
||||
break 'lambda_converter ExprKind::Lambda(path.into_inner(), b.at(x.pos.clone()));
|
||||
}
|
||||
}
|
||||
let argstr = take_first(&mtreev_fmt(arg, &FmtCtxImpl { i: &ctx.i }).await, true);
|
||||
ExprKind::Bottom(mk_errv(
|
||||
ctx.i.i("Malformeed lambda").await,
|
||||
format!("Lambda argument should be single name, found {argstr}"),
|
||||
[x.pos.clone().into()],
|
||||
))
|
||||
},
|
||||
};
|
||||
if f.is_empty() {
|
||||
return x_kind;
|
||||
}
|
||||
let f = mtreev_to_expr(f, stack.push(SrcToExprStep::Left), ctx).boxed_local().await;
|
||||
ExprKind::Call(f.at(Pos::None), x_kind.at(x.pos.clone()))
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use orchid_base::clone;
|
||||
use orchid_base::format::{FmtCtxImpl, Format};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::logging::Logger;
|
||||
use orchid_base::reqnot::{ReqNot, Requester as _};
|
||||
use orchid_base::reqnot::{DynRequester, ReqNot, Requester as _};
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::AtomHand;
|
||||
@@ -54,7 +54,7 @@ impl Drop for ExtensionData {
|
||||
#[derive(Clone)]
|
||||
pub struct Extension(Rc<ExtensionData>);
|
||||
impl Extension {
|
||||
pub fn new(init: ExtInit, logger: Logger, ctx: Ctx) -> io::Result<Self> {
|
||||
pub fn new(init: ExtInit, logger: Logger, msg_logger: Logger, ctx: Ctx) -> io::Result<Self> {
|
||||
Ok(Self(Rc::new_cyclic(|weak: &Weak<ExtensionData>| ExtensionData {
|
||||
exprs: ExprStore::default(),
|
||||
ctx: ctx.clone(),
|
||||
@@ -67,7 +67,7 @@ impl Extension {
|
||||
lex_recur: Mutex::default(),
|
||||
mac_recur: Mutex::default(),
|
||||
reqnot: ReqNot::new(
|
||||
logger,
|
||||
msg_logger,
|
||||
clone!(weak; move |sfn, _| clone!(weak; async move {
|
||||
let data = weak.upgrade().unwrap();
|
||||
data.init.send(sfn).await
|
||||
@@ -100,6 +100,7 @@ impl Extension {
|
||||
clone!(weak, ctx);
|
||||
Box::pin(async move {
|
||||
let this = Self(weak.upgrade().unwrap());
|
||||
writeln!(this.reqnot().logger(), "Host received request {req:?}");
|
||||
let i = this.ctx().i.clone();
|
||||
match req {
|
||||
api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()).await,
|
||||
@@ -213,8 +214,17 @@ impl Extension {
|
||||
}
|
||||
pub async fn recv_one(&self) {
|
||||
let reqnot = self.0.reqnot.clone();
|
||||
(self.0.init.recv(Box::new(move |msg| async move { reqnot.receive(msg).await }.boxed_local())))
|
||||
.await;
|
||||
let ctx = self.ctx().clone();
|
||||
(self.0.init.recv(Box::new(move |msg| {
|
||||
Box::pin(async move {
|
||||
let msg = msg.to_vec();
|
||||
let reqnot = reqnot.clone();
|
||||
(ctx.spawn)(Box::pin(async move {
|
||||
reqnot.receive(&msg).await;
|
||||
}))
|
||||
})
|
||||
})))
|
||||
.await;
|
||||
}
|
||||
pub fn system_drop(&self, id: api::SysId) {
|
||||
let rc = self.clone();
|
||||
|
||||
@@ -8,6 +8,7 @@ use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::match_mapping;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::number::{num_to_err, parse_num};
|
||||
use orchid_base::parse::{name_char, name_start, op_char, unrep_space};
|
||||
use orchid_base::tokens::PARENS;
|
||||
@@ -156,7 +157,7 @@ pub async fn lex_once(ctx: &mut LexCtx<'_>) -> OrcRes<ParsTokTree> {
|
||||
.lex(source, pos, |pos| async move {
|
||||
let mut ctx_g = ctx_lck.lock().await;
|
||||
match lex_once(&mut ctx_g.push(pos)).boxed_local().await {
|
||||
Ok(t) => Some(api::SubLexed { pos, ticket: ctx_g.add_subtree(t) }),
|
||||
Ok(t) => Some(api::SubLexed { pos: t.range.end, ticket: ctx_g.add_subtree(t) }),
|
||||
Err(e) => {
|
||||
errors_lck.lock().await.push(e);
|
||||
None
|
||||
@@ -203,6 +204,7 @@ async fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree
|
||||
Bottom(err => OrcErrv::from_api(err, &ctx.ctx.i).await),
|
||||
LambdaHead(arg => ttv_to_owned(arg, ctx).boxed_local().await),
|
||||
Name(name => Tok::from_api(*name, &ctx.ctx.i).await),
|
||||
Reference(tstrv => Sym::from_api(*tstrv, &ctx.ctx.i).await),
|
||||
S(p.clone(), b => ttv_to_owned(b, ctx).boxed_local().await),
|
||||
BR, NS,
|
||||
Comment(c.clone()),
|
||||
|
||||
@@ -2,6 +2,8 @@ use orchid_api as api;
|
||||
|
||||
pub mod atom;
|
||||
pub mod ctx;
|
||||
pub mod dealias;
|
||||
pub mod execute;
|
||||
pub mod expr;
|
||||
pub mod expr_store;
|
||||
pub mod extension;
|
||||
|
||||
@@ -10,8 +10,7 @@ use orchid_base::location::Pos;
|
||||
use orchid_base::macros::{MTok, MTree};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{
|
||||
Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, strip_fluff,
|
||||
try_pop_no_fluff,
|
||||
Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, try_pop_no_fluff,
|
||||
};
|
||||
use orchid_base::tree::{Paren, TokTree, Token};
|
||||
use substack::Substack;
|
||||
@@ -19,9 +18,7 @@ use substack::Substack;
|
||||
use crate::atom::AtomHand;
|
||||
use crate::macros::MacTree;
|
||||
use crate::system::System;
|
||||
use crate::tree::{
|
||||
Code, CodeLocator, Item, ItemKind, Member, MemberKind, Module, ParsTokTree, Rule, RuleKind,
|
||||
};
|
||||
use crate::tree::{Code, CodeLocator, Item, ItemKind, Member, MemberKind, Module, Rule, RuleKind};
|
||||
|
||||
type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>;
|
||||
|
||||
@@ -66,7 +63,7 @@ pub async fn parse_item(
|
||||
let Parsed { output: exports, tail } = parse_multiname(ctx.reporter(), tail).await?;
|
||||
let mut ok = Vec::new();
|
||||
for (e, pos) in exports {
|
||||
match (&e.path.as_slice(), e.name) {
|
||||
match (&e.path[..], e.name) {
|
||||
([], Some(n)) =>
|
||||
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
|
||||
(_, Some(_)) => ctx.reporter().report(mk_err(
|
||||
@@ -129,7 +126,7 @@ pub async fn parse_exportable_item(
|
||||
let (name, body) = parse_module(ctx, path, tail).await?;
|
||||
ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
|
||||
} else if discr == tail.i().i("const").await {
|
||||
let (name, val) = parse_const(tail).await?;
|
||||
let (name, val) = parse_const(tail, path.clone()).await?;
|
||||
let locator = CodeLocator::to_const(tail.i().i(&path.push(name.clone()).unreverse()).await);
|
||||
ItemKind::Member(Member::new(name, MemberKind::Const(Code::from_code(locator, val))))
|
||||
} else if let Some(sys) = ctx.systems().find(|s| s.can_parse(discr.clone())) {
|
||||
@@ -174,7 +171,10 @@ pub async fn parse_module(
|
||||
Ok((name, Module::new(parse_items(ctx, path, body).await?)))
|
||||
}
|
||||
|
||||
pub async fn parse_const(tail: ParsSnippet<'_>) -> OrcRes<(Tok<String>, Vec<ParsTokTree>)> {
|
||||
pub async fn parse_const(
|
||||
tail: ParsSnippet<'_>,
|
||||
path: Substack<'_, Tok<String>>,
|
||||
) -> OrcRes<(Tok<String>, Vec<MacTree>)> {
|
||||
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
||||
let Some(name) = output.as_name() else {
|
||||
return Err(mk_errv(
|
||||
@@ -192,20 +192,25 @@ pub async fn parse_const(tail: ParsSnippet<'_>) -> OrcRes<(Tok<String>, Vec<Pars
|
||||
));
|
||||
}
|
||||
try_pop_no_fluff(tail).await?;
|
||||
Ok((name, tail.iter().flat_map(strip_fluff).collect_vec()))
|
||||
Ok((name, parse_mtree(tail, path).await?))
|
||||
}
|
||||
|
||||
pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
||||
pub async fn parse_mtree(
|
||||
mut snip: ParsSnippet<'_>,
|
||||
path: Substack<'_, Tok<String>>,
|
||||
) -> OrcRes<Vec<MacTree>> {
|
||||
let mut mtreev = Vec::new();
|
||||
while let Some((ttree, tail)) = snip.pop_front() {
|
||||
snip = tail;
|
||||
let (range, tok, tail) = match &ttree.tok {
|
||||
Token::S(p, b) => (
|
||||
ttree.range.clone(),
|
||||
MTok::S(*p, parse_mtree(Snippet::new(ttree, b, snip.i())).boxed_local().await?),
|
||||
tail,
|
||||
),
|
||||
Token::S(p, b) => {
|
||||
let b = parse_mtree(Snippet::new(ttree, b, snip.i()), path.clone()).boxed_local().await?;
|
||||
(ttree.range.clone(), MTok::S(*p, b), tail)
|
||||
},
|
||||
Token::Reference(name) => (ttree.range.clone(), MTok::Name(name.clone()), tail),
|
||||
Token::Name(tok) => {
|
||||
let mut segments = vec![tok.clone()];
|
||||
let mut segments = path.unreverse();
|
||||
segments.push(tok.clone());
|
||||
let mut end = ttree.range.end;
|
||||
while let Some((TokTree { tok: Token::NS, .. }, tail)) = snip.pop_front() {
|
||||
let Parsed { output, tail } = try_pop_no_fluff(tail).await?;
|
||||
@@ -225,29 +230,27 @@ pub async fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
||||
},
|
||||
Token::NS => {
|
||||
return Err(mk_errv(
|
||||
tail.i().i("Unexpected :: in macro pattern").await,
|
||||
":: can only follow a name outside export statements",
|
||||
tail.i().i("Unexpected :: in expression").await,
|
||||
":: can only follow a name",
|
||||
[Pos::Range(ttree.range.clone()).into()],
|
||||
));
|
||||
},
|
||||
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
|
||||
Token::Atom(_) | Token::Macro(_) => {
|
||||
Token::Macro(_) => {
|
||||
return Err(mk_errv(
|
||||
tail.i().i("Unsupported token in macro patterns").await,
|
||||
format!(
|
||||
"Macro patterns can only contain names, braces, and lambda, not {}.",
|
||||
tail.fmt(ttree).await
|
||||
),
|
||||
tail.i().i("Invalid keyword in expression").await,
|
||||
"Expressions cannot use `macro` as a name.",
|
||||
[Pos::Range(ttree.range.clone()).into()],
|
||||
));
|
||||
},
|
||||
Token::Atom(a) => (ttree.range.clone(), MTok::Atom(a.clone()), tail),
|
||||
Token::BR | Token::Comment(_) => continue,
|
||||
Token::Bottom(e) => return Err(e.clone()),
|
||||
Token::LambdaHead(arg) => (
|
||||
ttree.range.start..snip.pos().end,
|
||||
MTok::Lambda(
|
||||
parse_mtree(Snippet::new(ttree, arg, snip.i())).boxed_local().await?,
|
||||
parse_mtree(tail).boxed_local().await?,
|
||||
parse_mtree(Snippet::new(ttree, arg, snip.i()), path.clone()).boxed_local().await?,
|
||||
parse_mtree(tail, path.clone()).boxed_local().await?,
|
||||
),
|
||||
Snippet::new(ttree, &[], snip.i()),
|
||||
),
|
||||
@@ -303,10 +306,10 @@ pub async fn parse_macro(
|
||||
rules.push(Rule {
|
||||
comments: item.output,
|
||||
pos: Pos::Range(tail.pos()),
|
||||
pattern: parse_mtree(pat).await?,
|
||||
pattern: parse_mtree(pat, path.clone()).await?,
|
||||
kind: RuleKind::Native(Code::from_code(
|
||||
CodeLocator::to_rule(tail.i().i(&path.unreverse()).await, macro_i, i as u16),
|
||||
body.to_vec(),
|
||||
parse_mtree(body, path.clone()).await?,
|
||||
)),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ use crate::ctx::Ctx;
|
||||
pub async fn ext_command(
|
||||
cmd: std::process::Command,
|
||||
logger: Logger,
|
||||
msg_logs: Logger,
|
||||
ctx: Ctx,
|
||||
) -> io::Result<ExtInit> {
|
||||
let prog_pbuf = PathBuf::from(cmd.get_program());
|
||||
@@ -29,7 +30,9 @@ pub async fn ext_command(
|
||||
.stderr(async_process::Stdio::piped())
|
||||
.spawn()?;
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
api::HostHeader { log_strategy: logger.strat() }.encode(Pin::new(&mut stdin)).await;
|
||||
api::HostHeader { log_strategy: logger.strat(), msg_logs: msg_logs.strat() }
|
||||
.encode(Pin::new(&mut stdin))
|
||||
.await;
|
||||
let mut stdout = child.stdout.take().unwrap();
|
||||
let header = api::ExtensionHeader::decode(Pin::new(&mut stdout)).await;
|
||||
let child_stderr = child.stderr.take().unwrap();
|
||||
@@ -41,7 +44,7 @@ pub async fn ext_command(
|
||||
if 0 == reader.read_line(&mut buf).await.unwrap() {
|
||||
break;
|
||||
}
|
||||
logger.log(buf);
|
||||
logger.log(buf.strip_suffix('\n').expect("Readline implies this"));
|
||||
}
|
||||
})
|
||||
})?;
|
||||
@@ -73,9 +76,6 @@ impl Drop for Subprocess {
|
||||
}
|
||||
impl ExtPort for Subprocess {
|
||||
fn send<'a>(&'a self, msg: &'a [u8]) -> LocalBoxFuture<'a, ()> {
|
||||
if msg.starts_with(&[0, 0, 0, 0x1c]) {
|
||||
panic!("Received unnecessary prefix");
|
||||
}
|
||||
async { send_msg(Pin::new(&mut *self.stdin.lock().await), msg).await.unwrap() }.boxed_local()
|
||||
}
|
||||
fn recv<'a>(
|
||||
@@ -83,6 +83,7 @@ impl ExtPort for Subprocess {
|
||||
cb: Box<dyn FnOnce(&[u8]) -> LocalBoxFuture<'_, ()> + 'a>,
|
||||
) -> LocalBoxFuture<'a, ()> {
|
||||
Box::pin(async {
|
||||
std::io::Write::flush(&mut std::io::stderr()).unwrap();
|
||||
match recv_msg(self.stdout.lock().await.as_mut()).await {
|
||||
Ok(msg) => cb(&msg).await,
|
||||
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => (),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::rc::{Rc, Weak};
|
||||
use std::{fmt, mem};
|
||||
|
||||
use async_stream::stream;
|
||||
use derive_destructure::destructure;
|
||||
@@ -9,12 +9,12 @@ use futures::StreamExt;
|
||||
use futures::future::join_all;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::async_once_cell::OnceCell;
|
||||
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::Tok;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::parse::Comment;
|
||||
use orchid_base::reqnot::{ReqNot, Requester};
|
||||
use orchid_base::tree::ttv_from_api;
|
||||
@@ -24,7 +24,7 @@ use substack::{Stackframe, Substack};
|
||||
use crate::api;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::extension::{Extension, WeakExtension};
|
||||
use crate::tree::{Member, ParsTokTree};
|
||||
use crate::tree::{ItemKind, Member, Module, ParsTokTree};
|
||||
|
||||
#[derive(destructure)]
|
||||
struct SystemInstData {
|
||||
@@ -33,7 +33,6 @@ struct SystemInstData {
|
||||
decl_id: api::SysDeclId,
|
||||
lex_filter: api::CharFilter,
|
||||
id: api::SysId,
|
||||
const_root: OnceCell<Vec<Member>>,
|
||||
line_types: Vec<Tok<String>>,
|
||||
}
|
||||
impl Drop for SystemInstData {
|
||||
@@ -45,7 +44,6 @@ impl fmt::Debug for SystemInstData {
|
||||
.field("decl_id", &self.decl_id)
|
||||
.field("lex_filter", &self.lex_filter)
|
||||
.field("id", &self.id)
|
||||
.field("const_root", &self.const_root)
|
||||
.field("line_types", &self.line_types)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
@@ -135,25 +133,26 @@ impl SystemCtor {
|
||||
ext: ext.clone(),
|
||||
ctx: ext.ctx().clone(),
|
||||
lex_filter: sys_inst.lex_filter,
|
||||
const_root: OnceCell::new(),
|
||||
line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i)))
|
||||
.await,
|
||||
id,
|
||||
}));
|
||||
(data.0.const_root.get_or_init(
|
||||
clone!(data, ext; stream! {
|
||||
for (k, v) in sys_inst.const_root {
|
||||
yield Member::from_api(
|
||||
api::Member { name: k, kind: v },
|
||||
&mut vec![Tok::from_api(k, &ext.ctx().i).await],
|
||||
&data,
|
||||
).await
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
let const_root = clone!(data, ext; stream! {
|
||||
for (k, v) in sys_inst.const_root {
|
||||
yield Member::from_api(
|
||||
api::Member { name: k, kind: v },
|
||||
&mut vec![Tok::from_api(k, &ext.ctx().i).await],
|
||||
&data,
|
||||
).await;
|
||||
}
|
||||
})
|
||||
.map(|mem| ItemKind::Member(mem).at(Pos::None))
|
||||
.collect::<Vec<_>>()
|
||||
.await;
|
||||
ext.ctx().systems.write().await.insert(id, data.downgrade());
|
||||
let mut swap = Module::default();
|
||||
mem::swap(&mut swap, &mut *ext.ctx().root.write().await);
|
||||
*ext.ctx().root.write().await = Module::new(swap.items.into_iter().chain(const_root));
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
use std::fmt::Debug;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Mutex, OnceLock};
|
||||
|
||||
use async_once_cell::OnceCell;
|
||||
use async_std::sync::Mutex;
|
||||
use async_stream::stream;
|
||||
use futures::future::join_all;
|
||||
use futures::{FutureExt, StreamExt};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::format::{FmtCtx, FmtUnit, Format, Variants};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::macros::{mtreev_fmt, mtreev_from_api};
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{Comment, Import};
|
||||
use orchid_base::tree::{AtomRepr, TokTree, Token, ttv_fmt};
|
||||
use orchid_base::tree::{AtomRepr, TokTree, Token};
|
||||
use orchid_base::{clone, tl_cache};
|
||||
use ordered_float::NotNan;
|
||||
use substack::Substack;
|
||||
|
||||
use crate::api;
|
||||
use crate::atom::AtomHand;
|
||||
use crate::expr::Expr;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::expr::{Expr, mtreev_to_expr};
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
use crate::system::System;
|
||||
|
||||
@@ -41,13 +43,16 @@ pub enum ItemKind {
|
||||
Import(Import),
|
||||
Macro(Option<NotNan<f64>>, Vec<Rule>),
|
||||
}
|
||||
impl ItemKind {
|
||||
pub fn at(self, pos: Pos) -> Item { Item { comments: vec![], pos, kind: self } }
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub async fn from_api(tree: api::Item, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
|
||||
let kind = match tree.kind {
|
||||
api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys).await),
|
||||
api::ItemKind::Import(name) => ItemKind::Import(Import {
|
||||
path: Sym::from_api(name, &sys.ctx().i).await.iter().collect(),
|
||||
path: Sym::from_api(name, &sys.ctx().i).await.iter().cloned().collect(),
|
||||
name: None,
|
||||
}),
|
||||
api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e, &sys.ctx().i).await),
|
||||
@@ -108,11 +113,23 @@ impl Format for Item {
|
||||
}
|
||||
|
||||
pub struct Member {
|
||||
pub name: Tok<String>,
|
||||
pub kind: OnceLock<MemberKind>,
|
||||
pub lazy: Mutex<Option<LazyMemberHandle>>,
|
||||
name: Tok<String>,
|
||||
kind: OnceCell<MemberKind>,
|
||||
lazy: Mutex<Option<LazyMemberHandle>>,
|
||||
}
|
||||
impl Member {
|
||||
pub fn name(&self) -> Tok<String> { self.name.clone() }
|
||||
pub async fn kind(&self) -> &MemberKind {
|
||||
(self.kind.get_or_init(async {
|
||||
let handle = self.lazy.lock().await.take().expect("Neither known nor lazy");
|
||||
handle.run().await
|
||||
}))
|
||||
.await
|
||||
}
|
||||
pub async fn kind_mut(&mut self) -> &mut MemberKind {
|
||||
self.kind().await;
|
||||
self.kind.get_mut().expect("kind() already filled the cell")
|
||||
}
|
||||
pub async fn from_api(api: api::Member, path: &mut Vec<Tok<String>>, sys: &System) -> Self {
|
||||
path.push(Tok::from_api(api.name, &sys.ctx().i).await);
|
||||
let kind = match api.kind {
|
||||
@@ -127,10 +144,10 @@ impl Member {
|
||||
api::MemberKind::Module(m) => MemberKind::Mod(Module::from_api(m, path, sys).await),
|
||||
};
|
||||
let name = path.pop().unwrap();
|
||||
Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
|
||||
Member { name, kind: OnceCell::from(kind), lazy: Mutex::default() }
|
||||
}
|
||||
pub fn new(name: Tok<String>, kind: MemberKind) -> Self {
|
||||
Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
|
||||
Member { name, kind: OnceCell::from(kind), lazy: Mutex::default() }
|
||||
}
|
||||
}
|
||||
impl Debug for Member {
|
||||
@@ -148,7 +165,7 @@ pub enum MemberKind {
|
||||
Mod(Module),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Module {
|
||||
pub imports: Vec<Sym>,
|
||||
pub exports: Vec<Tok<String>>,
|
||||
@@ -172,6 +189,40 @@ impl Module {
|
||||
.await,
|
||||
)
|
||||
}
|
||||
pub async fn walk(
|
||||
&self,
|
||||
allow_private: bool,
|
||||
path: impl IntoIterator<Item = Tok<String>>,
|
||||
) -> Result<&Module, WalkError> {
|
||||
let mut cur = self;
|
||||
for (pos, step) in path.into_iter().enumerate() {
|
||||
let Some(member) = (cur.items.iter())
|
||||
.filter_map(|it| if let ItemKind::Member(m) = &it.kind { Some(m) } else { None })
|
||||
.find(|m| m.name == step)
|
||||
else {
|
||||
return Err(WalkError { pos, kind: WalkErrorKind::Missing });
|
||||
};
|
||||
if !allow_private && !cur.exports.contains(&step) {
|
||||
return Err(WalkError { pos, kind: WalkErrorKind::Private });
|
||||
}
|
||||
match member.kind().await {
|
||||
MemberKind::Const(_) => return Err(WalkError { pos, kind: WalkErrorKind::Constant }),
|
||||
MemberKind::Mod(m) => cur = m,
|
||||
}
|
||||
}
|
||||
Ok(cur)
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub enum WalkErrorKind {
|
||||
Missing,
|
||||
Private,
|
||||
Constant,
|
||||
}
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct WalkError {
|
||||
pub pos: usize,
|
||||
pub kind: WalkErrorKind,
|
||||
}
|
||||
impl Format for Module {
|
||||
async fn print<'a>(&'a self, c: &'a (impl FmtCtx + ?Sized + 'a)) -> FmtUnit {
|
||||
@@ -185,20 +236,20 @@ impl Format for Module {
|
||||
|
||||
pub struct LazyMemberHandle(api::TreeId, System, Vec<Tok<String>>);
|
||||
impl LazyMemberHandle {
|
||||
pub async fn run(self) -> OrcRes<MemberKind> {
|
||||
pub async fn run(self) -> MemberKind {
|
||||
match self.1.get_tree(self.0).await {
|
||||
api::MemberKind::Const(c) => Ok(MemberKind::Const(Code {
|
||||
api::MemberKind::Const(c) => MemberKind::Const(Code {
|
||||
bytecode: Expr::from_api(&c, &mut self.1.ext().clone()).await.into(),
|
||||
locator: CodeLocator { steps: self.1.ctx().i.i(&self.2).await, rule_loc: None },
|
||||
source: None,
|
||||
})),
|
||||
}),
|
||||
api::MemberKind::Module(m) =>
|
||||
Ok(MemberKind::Mod(Module::from_api(m, &mut { self.2 }, &self.1).await)),
|
||||
MemberKind::Mod(Module::from_api(m, &mut { self.2 }, &self.1).await),
|
||||
api::MemberKind::Lazy(id) => Self(id, self.1, self.2).run().boxed_local().await,
|
||||
}
|
||||
}
|
||||
pub fn into_member(self, name: Tok<String>) -> Member {
|
||||
Member { name, kind: OnceLock::new(), lazy: Mutex::new(Some(self)) }
|
||||
Member { name, kind: OnceCell::new(), lazy: Mutex::new(Some(self)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,15 +288,27 @@ pub enum RuleKind {
|
||||
#[derive(Debug)]
|
||||
pub struct Code {
|
||||
locator: CodeLocator,
|
||||
source: Option<Vec<ParsTokTree>>,
|
||||
bytecode: OnceLock<Expr>,
|
||||
source: Option<Vec<MacTree>>,
|
||||
bytecode: OnceCell<Expr>,
|
||||
}
|
||||
impl Code {
|
||||
pub fn from_expr(locator: CodeLocator, expr: Expr) -> Self {
|
||||
Self { locator, source: None, bytecode: expr.into() }
|
||||
}
|
||||
pub fn from_code(locator: CodeLocator, code: Vec<ParsTokTree>) -> Self {
|
||||
Self { locator, source: Some(code), bytecode: OnceLock::new() }
|
||||
pub fn from_code(locator: CodeLocator, code: Vec<MacTree>) -> Self {
|
||||
Self { locator, source: Some(code), bytecode: OnceCell::new() }
|
||||
}
|
||||
pub fn source(&self) -> Option<&Vec<MacTree>> { self.source.as_ref() }
|
||||
pub fn set_source(&mut self, source: Vec<MacTree>) {
|
||||
self.source = Some(source);
|
||||
self.bytecode = OnceCell::new();
|
||||
}
|
||||
pub async fn get_bytecode(&self, ctx: &Ctx) -> &Expr {
|
||||
(self.bytecode.get_or_init(async {
|
||||
let src = self.source.as_ref().expect("no bytecode or source");
|
||||
mtreev_to_expr(src, Substack::Bottom, ctx).await.at(Pos::None)
|
||||
}))
|
||||
.await
|
||||
}
|
||||
}
|
||||
impl Format for Code {
|
||||
@@ -254,7 +317,7 @@ impl Format for Code {
|
||||
return bc.print(c).await;
|
||||
}
|
||||
if let Some(src) = &self.source {
|
||||
return ttv_fmt(src, c).await;
|
||||
return mtreev_fmt(src, c).await;
|
||||
}
|
||||
panic!("Code must be initialized with at least one state")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user