Gitbutler >:(
I don't understand this piece of software at all
This commit is contained in:
@@ -6,10 +6,10 @@ use std::sync::{Arc, RwLock};
|
||||
use hashbrown::HashMap;
|
||||
use lazy_static::lazy_static;
|
||||
use orchid_base::error::OrcErrv;
|
||||
use orchid_base::interner::deintern;
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::match_mapping;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::tree::AtomTok;
|
||||
use orchid_base::tree::AtomRepr;
|
||||
|
||||
use crate::api;
|
||||
use crate::extension::AtomHand;
|
||||
@@ -41,12 +41,12 @@ impl Expr {
|
||||
pub fn resolve(tk: api::ExprTicket) -> Option<Self> {
|
||||
KNOWN_EXPRS.read().unwrap().get(&tk).cloned()
|
||||
}
|
||||
pub fn from_api(api: api::Expression, ctx: &mut ExprParseCtx) -> Self {
|
||||
pub fn from_api(api: &api::Expression, ctx: &mut ExprParseCtx) -> Self {
|
||||
if let api::ExpressionKind::Slot(tk) = &api.kind {
|
||||
return Self::resolve(*tk).expect("Invalid slot");
|
||||
}
|
||||
Self {
|
||||
kind: Arc::new(RwLock::new(ExprKind::from_api(api.kind, ctx))),
|
||||
kind: Arc::new(RwLock::new(ExprKind::from_api(&api.kind, ctx))),
|
||||
is_canonical: Arc::default(),
|
||||
pos: Pos::from_api(&api.location),
|
||||
}
|
||||
@@ -81,24 +81,24 @@ pub enum ExprKind {
|
||||
Seq(Expr, Expr),
|
||||
Call(Expr, Expr),
|
||||
Atom(AtomHand),
|
||||
Argument,
|
||||
Arg,
|
||||
Lambda(Option<PathSet>, Expr),
|
||||
Bottom(OrcErrv),
|
||||
Const(Sym),
|
||||
}
|
||||
impl ExprKind {
|
||||
pub fn from_api(api: api::ExpressionKind, ctx: &mut ExprParseCtx) -> Self {
|
||||
use api::ExpressionKind as K;
|
||||
match api {
|
||||
K::Slot(_) => panic!("Handled in Expr"),
|
||||
K::Lambda(id, b) => ExprKind::Lambda(PathSet::from_api(id, &b), Expr::from_api(*b, ctx)),
|
||||
K::Arg(_) => ExprKind::Argument,
|
||||
K::Bottom(b) => ExprKind::Bottom(OrcErrv::from_api(&b)),
|
||||
K::Call(f, x) => ExprKind::Call(Expr::from_api(*f, ctx), Expr::from_api(*x, ctx)),
|
||||
K::Const(c) => ExprKind::Const(Sym::from_tok(deintern(c)).unwrap()),
|
||||
K::NewAtom(a) => ExprKind::Atom(AtomHand::from_api(a)),
|
||||
K::Seq(a, b) => ExprKind::Seq(Expr::from_api(*a, ctx), Expr::from_api(*b, ctx)),
|
||||
}
|
||||
pub fn from_api(api: &api::ExpressionKind, ctx: &mut ExprParseCtx) -> Self {
|
||||
match_mapping!(api, api::ExpressionKind => ExprKind {
|
||||
Lambda(id => PathSet::from_api(*id, api), b => Expr::from_api(b, ctx)),
|
||||
Bottom(b => OrcErrv::from_api(b)),
|
||||
Call(f => Expr::from_api(f, ctx), x => Expr::from_api(x, ctx)),
|
||||
Const(c => Sym::from_api(*c)),
|
||||
Seq(a => Expr::from_api(a, ctx), b => Expr::from_api(b, ctx)),
|
||||
} {
|
||||
api::ExpressionKind::Arg(_) => ExprKind::Arg,
|
||||
api::ExpressionKind::NewAtom(a) => ExprKind::Atom(AtomHand::from_api(a.clone())),
|
||||
api::ExpressionKind::Slot(_) => panic!("Handled in Expr"),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,19 +121,20 @@ impl PathSet {
|
||||
self.steps.push_front(step);
|
||||
self
|
||||
}
|
||||
pub fn from_api(id: u64, b: &api::Expression) -> Option<Self> {
|
||||
pub fn from_api(id: u64, api: &api::ExpressionKind) -> Option<Self> {
|
||||
use api::ExpressionKind as K;
|
||||
match &b.kind {
|
||||
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),
|
||||
K::Call(l, r) | K::Seq(l, r) => match (Self::from_api(id, l), Self::from_api(id, r)) {
|
||||
(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,
|
||||
},
|
||||
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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::num::NonZero;
|
||||
use std::ops::{Deref, Range};
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::{AtomicU16, AtomicU32, AtomicU64, Ordering};
|
||||
use std::sync::mpsc::{sync_channel, SyncSender};
|
||||
use std::sync::{Arc, Mutex, OnceLock, RwLock, Weak};
|
||||
@@ -11,23 +11,23 @@ use hashbrown::hash_map::Entry;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use orchid_api::TStrv;
|
||||
use orchid_api_traits::Request;
|
||||
use orchid_base::char_filter::char_filter_match;
|
||||
use orchid_base::error::{OrcErrv, OrcRes};
|
||||
use orchid_base::interner::{deintern, intern, Tok};
|
||||
use orchid_base::interner::{intern, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::logging::Logger;
|
||||
use orchid_base::macros::{mtreev_from_api, mtreev_to_api};
|
||||
use orchid_base::macros::mtreev_from_api;
|
||||
use orchid_base::parse::Comment;
|
||||
use orchid_base::reqnot::{ReqNot, Requester as _};
|
||||
use orchid_base::tree::{ttv_from_api, AtomTok};
|
||||
use orchid_base::tree::{ttv_from_api, AtomRepr};
|
||||
use orchid_base::clone;
|
||||
use ordered_float::NotNan;
|
||||
use substack::{Stackframe, Substack};
|
||||
|
||||
use crate::api;
|
||||
use crate::expr::Expr;
|
||||
use crate::macros::macro_recur;
|
||||
use crate::macros::{macro_recur, macro_treev_to_api};
|
||||
use crate::tree::{Member, ParsTokTree};
|
||||
|
||||
#[derive(Debug, destructure)]
|
||||
@@ -56,11 +56,11 @@ impl Drop for AtomData {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AtomHand(Arc<AtomData>);
|
||||
impl AtomHand {
|
||||
fn create_new(api::Atom { data, drop, owner }: api::Atom) -> Self {
|
||||
let owner = System::resolve(owner).expect("Atom owned by non-existing system");
|
||||
Self(Arc::new(AtomData { data, drop, owner }))
|
||||
}
|
||||
pub fn from_api(atom: api::Atom) -> Self {
|
||||
fn create_new(api::Atom { data, drop, owner }: api::Atom) -> AtomHand {
|
||||
let owner = System::resolve(owner).expect("Atom owned by non-existing system");
|
||||
AtomHand(Arc::new(AtomData { data, drop, owner }))
|
||||
}
|
||||
if let Some(id) = atom.drop {
|
||||
lazy_static! {
|
||||
static ref OWNED_ATOMS: Mutex<HashMap<(api::SysId, api::AtomId), Weak<AtomData>>> =
|
||||
@@ -73,11 +73,11 @@ impl AtomHand {
|
||||
return Self(atom);
|
||||
}
|
||||
}
|
||||
let new = Self::create_new(atom);
|
||||
let new = create_new(atom);
|
||||
owned_g.insert((owner, id), Arc::downgrade(&new.0));
|
||||
new
|
||||
} else {
|
||||
Self::create_new(atom)
|
||||
create_new(atom)
|
||||
}
|
||||
}
|
||||
pub fn call(self, arg: Expr) -> api::Expression {
|
||||
@@ -89,15 +89,15 @@ impl AtomHand {
|
||||
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), ticket)),
|
||||
}
|
||||
}
|
||||
pub fn req(&self, key: TStrv, req: Vec<u8>) -> Option<Vec<u8>> {
|
||||
pub 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))
|
||||
}
|
||||
pub fn api_ref(&self) -> api::Atom { self.0.api_ref() }
|
||||
pub fn print(&self) -> String { self.0.owner.reqnot().request(api::AtomPrint(self.0.api_ref())) }
|
||||
}
|
||||
impl AtomTok for AtomHand {
|
||||
type Context = ();
|
||||
fn from_api(atom: &orchid_api::Atom, _: Range<u32>, (): &mut Self::Context) -> Self {
|
||||
impl AtomRepr for AtomHand {
|
||||
type Ctx = ();
|
||||
fn from_api(atom: &orchid_api::Atom, _: Pos, (): &mut Self::Ctx) -> Self {
|
||||
Self::from_api(atom.clone())
|
||||
}
|
||||
fn to_api(&self) -> orchid_api::Atom { self.api_ref() }
|
||||
@@ -177,11 +177,15 @@ impl Extension {
|
||||
|hand, req| match req {
|
||||
api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()),
|
||||
api::ExtHostReq::IntReq(intreq) => match intreq {
|
||||
api::IntReq::InternStr(s) => hand.handle(&s, &intern(&**s.0).marker()),
|
||||
api::IntReq::InternStrv(v) => hand.handle(&v, &intern(&*v.0).marker()),
|
||||
api::IntReq::ExternStr(si) => hand.handle(&si, &deintern(si.0).arc()),
|
||||
api::IntReq::ExternStrv(vi) =>
|
||||
hand.handle(&vi, &Arc::new(deintern(vi.0).iter().map(|t| t.marker()).collect_vec())),
|
||||
api::IntReq::InternStr(s) => hand.handle(&s, &intern(&**s.0).to_api()),
|
||||
api::IntReq::InternStrv(v) => hand.handle(&v, &intern(&*v.0).to_api()),
|
||||
api::IntReq::ExternStr(si) => hand.handle(&si, &Tok::<String>::from_api(si.0).arc()),
|
||||
api::IntReq::ExternStrv(vi) => hand.handle(&vi, &Arc::new(
|
||||
Tok::<Vec<Tok<String>>>::from_api(vi.0)
|
||||
.iter()
|
||||
.map(|t| t.to_api())
|
||||
.collect_vec()
|
||||
)),
|
||||
},
|
||||
api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => {
|
||||
let sys = System::resolve(atom.owner).unwrap();
|
||||
@@ -207,7 +211,12 @@ impl Extension {
|
||||
})
|
||||
},
|
||||
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros{ ref run_id, ref query }) => {
|
||||
hand.handle(rm, ¯o_recur(*run_id, mtreev_from_api(query)).map(|x| mtreev_to_api(&x)))
|
||||
hand.handle(rm,
|
||||
¯o_recur(*run_id,
|
||||
mtreev_from_api(query, &mut |_| panic!("Recursion never contains atoms"))
|
||||
)
|
||||
.map(|x| macro_treev_to_api(*run_id, x))
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
@@ -255,13 +264,13 @@ impl SystemCtor {
|
||||
exprs: RwLock::default(),
|
||||
lex_filter: sys_inst.lex_filter,
|
||||
const_root: OnceLock::new(),
|
||||
line_types: sys_inst.line_types.into_iter().map(deintern).collect(),
|
||||
line_types: sys_inst.line_types.into_iter().map(Tok::from_api).collect(),
|
||||
id,
|
||||
}));
|
||||
let root = (sys_inst.const_root.into_iter())
|
||||
.map(|(k, v)| Member::from_api(
|
||||
api::Member { name: k, kind: v },
|
||||
Substack::Bottom.push(deintern(k)),
|
||||
Substack::Bottom.push(Tok::from_api(k)),
|
||||
&data
|
||||
))
|
||||
.collect_vec();
|
||||
@@ -342,7 +351,7 @@ impl System {
|
||||
});
|
||||
// Pass control to extension
|
||||
let ret =
|
||||
self.reqnot().request(api::LexExpr { id, pos, sys: self.id(), text: source.marker() });
|
||||
self.reqnot().request(api::LexExpr { id, pos, sys: self.id(), text: source.to_api() });
|
||||
// collect sender to unblock recursion handler thread before returning
|
||||
LEX_RECUR.lock().unwrap().remove(&id);
|
||||
ret.transpose()
|
||||
|
||||
@@ -3,8 +3,8 @@ use std::sync::Arc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_base::error::{mk_errv, OrcErrv, OrcRes};
|
||||
use orchid_base::intern;
|
||||
use orchid_base::interner::{deintern, intern, Tok};
|
||||
use orchid_base::{intern, match_mapping};
|
||||
use orchid_base::interner::{intern, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::number::{num_to_err, parse_num};
|
||||
use orchid_base::parse::{name_char, name_start, op_char, unrep_space};
|
||||
@@ -122,7 +122,7 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
|
||||
body.push(lex_once(ctx)?);
|
||||
ctx.trim_ws();
|
||||
}
|
||||
ParsTok::S(paren.clone(), body)
|
||||
ParsTok::S(*paren, body)
|
||||
} else if ctx.strip_prefix("macro") &&
|
||||
!ctx.tail.chars().next().is_some_and(|x| x.is_ascii_alphabetic())
|
||||
{
|
||||
@@ -173,20 +173,19 @@ pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
|
||||
}
|
||||
|
||||
fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree {
|
||||
let tok = match &api.token {
|
||||
api::Token::Atom(atom) => ParsTok::Atom(AtomHand::from_api(atom.clone())),
|
||||
api::Token::Bottom(err) => ParsTok::Bottom(OrcErrv::from_api(err)),
|
||||
api::Token::LambdaHead(arg) => ParsTok::LambdaHead(ttv_to_owned(arg, ctx)),
|
||||
api::Token::Lambda(arg, b) => ParsTok::Lambda(ttv_to_owned(arg, ctx), ttv_to_owned(b, ctx)),
|
||||
api::Token::Name(name) => ParsTok::Name(deintern(*name)),
|
||||
api::Token::S(p, b) => ParsTok::S(p.clone(), b.iter().map(|t| tt_to_owned(t, ctx)).collect()),
|
||||
let tok = match_mapping!(&api.token, api::Token => ParsTok {
|
||||
Atom(atom => AtomHand::from_api(atom.clone())),
|
||||
Bottom(err => OrcErrv::from_api(err)),
|
||||
LambdaHead(arg => ttv_to_owned(arg, ctx)),
|
||||
Name(name => Tok::from_api(*name)),
|
||||
S(p.clone(), b.iter().map(|t| tt_to_owned(t, ctx)).collect()),
|
||||
BR, NS,
|
||||
Comment(c.clone()),
|
||||
Ph(ph => Ph::from_api(ph)),
|
||||
Macro(*prio),
|
||||
} {
|
||||
api::Token::Slot(id) => return ctx.rm_subtree(*id),
|
||||
api::Token::BR => ParsTok::BR,
|
||||
api::Token::NS => ParsTok::NS,
|
||||
api::Token::Comment(c) => ParsTok::Comment(c.clone()),
|
||||
api::Token::Ph(ph) => ParsTok::Ph(Ph::from_api(ph)),
|
||||
api::Token::Macro(prio) => ParsTok::Macro(*prio)
|
||||
};
|
||||
});
|
||||
ParsTokTree { range: api.range.clone(), tok }
|
||||
}
|
||||
|
||||
|
||||
@@ -8,3 +8,4 @@ pub mod parse;
|
||||
pub mod subprocess;
|
||||
pub mod tree;
|
||||
pub mod macros;
|
||||
pub mod rule;
|
||||
|
||||
@@ -1,20 +1,85 @@
|
||||
use std::sync::RwLock;
|
||||
use crate::{api, rule::shared::Matcher, tree::Code};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use orchid_base::macros::MTree;
|
||||
use orchid_base::{macros::{mtreev_from_api, mtreev_to_api, MTok, MTree}, name::Sym};
|
||||
use ordered_float::NotNan;
|
||||
use trait_set::trait_set;
|
||||
use crate::api::ParsId;
|
||||
use crate::extension::AtomHand;
|
||||
|
||||
pub type MacTok = MTok<'static, AtomHand>;
|
||||
pub type MacTree = MTree<'static, AtomHand>;
|
||||
|
||||
trait_set!{
|
||||
trait MacroCB = Fn(Vec<MTree>) -> Option<Vec<MTree>> + Send + Sync;
|
||||
trait MacroCB = Fn(Vec<MacTree>) -> Option<Vec<MacTree>> + Send + Sync;
|
||||
}
|
||||
|
||||
lazy_static!{
|
||||
static ref RECURSION: RwLock<HashMap<ParsId, Box<dyn MacroCB>>> = RwLock::default();
|
||||
static ref RECURSION: RwLock<HashMap<api::ParsId, Box<dyn MacroCB>>> = RwLock::default();
|
||||
static ref MACRO_SLOTS: RwLock<HashMap<api::ParsId,
|
||||
HashMap<api::MacroTreeId, Arc<MacTok>>
|
||||
>> = RwLock::default();
|
||||
}
|
||||
|
||||
pub fn macro_recur(run_id: ParsId, input: Vec<MTree>) -> Option<Vec<MTree>> {
|
||||
pub fn macro_recur(run_id: api::ParsId, input: Vec<MacTree>) -> Option<Vec<MacTree>> {
|
||||
(RECURSION.read().unwrap()[&run_id])(input)
|
||||
}
|
||||
|
||||
pub fn macro_treev_to_api(run_id: api::ParsId, mtree: Vec<MacTree>) -> Vec<api::MacroTree> {
|
||||
let mut g = MACRO_SLOTS.write().unwrap();
|
||||
let run_cache = g.get_mut(&run_id).expect("Parser run not found");
|
||||
mtreev_to_api(&mtree, &mut |a: &AtomHand| {
|
||||
let id = api::MacroTreeId((run_cache.len() as u64 + 1).try_into().unwrap());
|
||||
run_cache.insert(id, Arc::new(MacTok::Atom(a.clone())));
|
||||
api::MacroToken::Slot(id)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn macro_treev_from_api(api: Vec<api::MacroTree>) -> Vec<MacTree> {
|
||||
mtreev_from_api(&api, &mut |atom| MacTok::Atom(AtomHand::from_api(atom.clone())))
|
||||
}
|
||||
|
||||
pub fn deslot_macro(run_id: api::ParsId, tree: &[MacTree]) -> Option<Vec<MacTree>> {
|
||||
let mut slots = (MACRO_SLOTS.write().unwrap())
|
||||
.remove(&run_id).expect("Run not found");
|
||||
return work(&mut slots, tree);
|
||||
fn work(
|
||||
slots: &mut HashMap<api::MacroTreeId, Arc<MacTok>>,
|
||||
tree: &[MacTree]
|
||||
) -> Option<Vec<MacTree>> {
|
||||
let items = (tree.iter())
|
||||
.map(|t| Some(MacTree {
|
||||
tok: match &*t.tok {
|
||||
MacTok::Atom(_) | MacTok::Name(_) | MacTok::Ph(_) => return None,
|
||||
MacTok::Ref(_) => panic!("Ref is an extension-local optimization"),
|
||||
MacTok::Slot(slot) => slots.get(&slot.id()).expect("Slot not found").clone(),
|
||||
MacTok::S(paren, b) => Arc::new(MacTok::S(*paren, work(slots, b)?)),
|
||||
MacTok::Lambda(a, b) => Arc::new(match (work(slots, a), work(slots, b)) {
|
||||
(None, None) => return None,
|
||||
(Some(a), None) => MacTok::Lambda(a, b.clone()),
|
||||
(None, Some(b)) => MacTok::Lambda(a.clone(), b),
|
||||
(Some(a), Some(b)) => MacTok::Lambda(a, b),
|
||||
}),
|
||||
},
|
||||
pos: t.pos.clone()
|
||||
}))
|
||||
.collect_vec();
|
||||
let any_changed = items.iter().any(Option::is_some);
|
||||
any_changed.then(|| {
|
||||
(items.into_iter().enumerate())
|
||||
.map(|(i, opt)| opt.unwrap_or_else(|| tree[i].clone()))
|
||||
.collect_vec()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MacroRepo{
|
||||
no_prio: Vec<(HashSet<Sym>, Matcher, Code)>,
|
||||
prio: Vec<(HashSet<Sym>, NotNan<f64>, Matcher, Code)>,
|
||||
}
|
||||
|
||||
pub fn match_on_exprv<'a>(target: &'a [MacTree], pattern: &[MacTree]) -> Option<MatchhState<'a>> {
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::sync::Arc;
|
||||
use std::{iter, thread};
|
||||
|
||||
use itertools::Itertools;
|
||||
@@ -16,6 +17,7 @@ use orchid_base::tree::{Paren, TokTree, Token};
|
||||
use substack::Substack;
|
||||
|
||||
use crate::extension::{AtomHand, System};
|
||||
use crate::macros::MacTree;
|
||||
use crate::tree::{Code, CodeLocator, Item, ItemKind, Member, MemberKind, Module, ParsTokTree, Rule, RuleKind};
|
||||
|
||||
type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>;
|
||||
@@ -181,9 +183,7 @@ pub fn parse_const(tail: ParsSnippet) -> OrcRes<(Tok<String>, Vec<ParsTokTree>)>
|
||||
Ok((name, tail.iter().flat_map(strip_fluff).collect_vec()))
|
||||
}
|
||||
|
||||
pub fn parse_mtree<'a>(
|
||||
mut snip: ParsSnippet<'a>
|
||||
) -> OrcRes<Vec<MTree<'static>>> {
|
||||
pub fn parse_mtree(mut snip: ParsSnippet<'_>) -> OrcRes<Vec<MacTree>> {
|
||||
let mut mtreev = Vec::new();
|
||||
while let Some((ttree, tail)) = snip.pop_front() {
|
||||
let (range, tok, tail) = match &ttree.tok {
|
||||
@@ -221,21 +221,14 @@ pub fn parse_mtree<'a>(
|
||||
)),
|
||||
Token::BR | Token::Comment(_) => continue,
|
||||
Token::Bottom(e) => return Err(e.clone()),
|
||||
Token::Lambda(arg, body) => {
|
||||
let tok = MTok::Lambda(
|
||||
parse_mtree(Snippet::new(&ttree, &arg))?,
|
||||
parse_mtree(Snippet::new(&ttree, &body))?,
|
||||
);
|
||||
(ttree.range.clone(), tok, tail)
|
||||
},
|
||||
Token::LambdaHead(arg) => (
|
||||
ttree.range.start..snip.pos().end,
|
||||
MTok::Lambda(parse_mtree(Snippet::new(&ttree, &arg))?, parse_mtree(tail)?),
|
||||
MTok::Lambda(parse_mtree(Snippet::new(ttree, arg))?, parse_mtree(tail)?),
|
||||
Snippet::new(ttree, &[]),
|
||||
),
|
||||
Token::Slot(_) | Token::X(_) => panic!("Did not expect {} in parsed token tree", &ttree.tok),
|
||||
};
|
||||
mtreev.push(MTree { pos: Pos::Range(range.clone()), tok });
|
||||
mtreev.push(MTree { pos: Pos::Range(range.clone()), tok: Arc::new(tok) });
|
||||
snip = tail;
|
||||
}
|
||||
Ok(mtreev)
|
||||
@@ -246,14 +239,14 @@ pub fn parse_macro(tail: ParsSnippet, macro_i: u16, path: Substack<Tok<String>>)
|
||||
Parsed { tail, output: o@TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
|
||||
Parsed { output, .. } => return Err(mk_errv(
|
||||
intern!(str: "m"),
|
||||
format!("Macro blocks must either start with a block or a ..$:number"),
|
||||
"Macro blocks must either start with a block or a ..$:number",
|
||||
[Pos::Range(output.range.clone()).into()]
|
||||
)),
|
||||
};
|
||||
expect_end(surplus)?;
|
||||
let mut errors = Vec::new();
|
||||
let mut rules = Vec::new();
|
||||
for (i, item) in line_items(Snippet::new(prev, &block)).into_iter().enumerate() {
|
||||
for (i, item) in line_items(Snippet::new(prev, block)).into_iter().enumerate() {
|
||||
let Parsed { tail, output } = try_pop_no_fluff(item.tail)?;
|
||||
if !output.is_kw(intern!(str: "rule")) {
|
||||
errors.extend(mk_errv(
|
||||
|
||||
29
orchid-host/src/rule/any_match.rs
Normal file
29
orchid-host/src/rule/any_match.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
use super::scal_match::scalv_match;
|
||||
use super::shared::AnyMatcher;
|
||||
use super::vec_match::vec_match;
|
||||
use orchid_base::name::Sym;
|
||||
use crate::macros::MacTree;
|
||||
use crate::rule::state::MatchState;
|
||||
|
||||
#[must_use]
|
||||
pub fn any_match<'a>(
|
||||
matcher: &AnyMatcher,
|
||||
seq: &'a [MacTree],
|
||||
save_loc: &impl Fn(Sym) -> bool,
|
||||
) -> Option<MatchState<'a>> {
|
||||
match matcher {
|
||||
AnyMatcher::Scalar(scalv) => scalv_match(scalv, seq, save_loc),
|
||||
AnyMatcher::Vec { left, mid, right } => {
|
||||
if seq.len() < left.len() + right.len() {
|
||||
return None;
|
||||
};
|
||||
let left_split = left.len();
|
||||
let right_split = seq.len() - right.len();
|
||||
Some(
|
||||
scalv_match(left, &seq[..left_split], save_loc)?
|
||||
.combine(scalv_match(right, &seq[right_split..], save_loc)?)
|
||||
.combine(vec_match(mid, &seq[left_split..right_split], save_loc)?),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
151
orchid-host/src/rule/build.rs
Normal file
151
orchid-host/src/rule/build.rs
Normal file
@@ -0,0 +1,151 @@
|
||||
use orchid_api::PhKind;
|
||||
use orchid_base::interner::Tok;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::side::Side;
|
||||
use orchid_base::tree::Ph;
|
||||
|
||||
use super::shared::{AnyMatcher, ScalMatcher, VecMatcher};
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
use crate::rule::vec_attrs::vec_attrs;
|
||||
|
||||
pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok<String>, u8, bool), &'a [MacTree]);
|
||||
|
||||
/// Derive the details of the central vectorial and the two sides from a
|
||||
/// slice of Expr's
|
||||
#[must_use]
|
||||
fn split_at_max_vec(pattern: &[MacTree]) -> Option<MaxVecSplit> {
|
||||
let rngidx = pattern
|
||||
.iter()
|
||||
.position_max_by_key(|expr| vec_attrs(expr).map(|attrs| attrs.1 as i64).unwrap_or(-1))?;
|
||||
let (left, not_left) = pattern.split_at(rngidx);
|
||||
let (placeh, right) =
|
||||
not_left.split_first().expect("The index of the greatest element must be less than the length");
|
||||
vec_attrs(placeh).map(|attrs| (left, attrs, right))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
fn scal_cnt<'a>(iter: impl Iterator<Item = &'a MacTree>) -> usize {
|
||||
iter.take_while(|expr| vec_attrs(expr).is_none()).count()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn mk_any(pattern: &[MacTree]) -> AnyMatcher {
|
||||
let left_split = scal_cnt(pattern.iter());
|
||||
if pattern.len() <= left_split {
|
||||
return AnyMatcher::Scalar(mk_scalv(pattern));
|
||||
}
|
||||
let (left, not_left) = pattern.split_at(left_split);
|
||||
let right_split = not_left.len() - scal_cnt(pattern.iter().rev());
|
||||
let (mid, right) = not_left.split_at(right_split);
|
||||
AnyMatcher::Vec { left: mk_scalv(left), mid: mk_vec(mid), right: mk_scalv(right) }
|
||||
}
|
||||
|
||||
/// Pattern MUST NOT contain vectorial placeholders
|
||||
#[must_use]
|
||||
fn mk_scalv(pattern: &[MacTree]) -> Vec<ScalMatcher> { pattern.iter().map(mk_scalar).collect() }
|
||||
|
||||
/// Pattern MUST start and end with a vectorial placeholder
|
||||
#[must_use]
|
||||
fn mk_vec(pattern: &[MacTree]) -> VecMatcher {
|
||||
debug_assert!(!pattern.is_empty(), "pattern cannot be empty");
|
||||
debug_assert!(pattern.first().map(vec_attrs).is_some(), "pattern must start with a vectorial");
|
||||
debug_assert!(pattern.last().map(vec_attrs).is_some(), "pattern must end with a vectorial");
|
||||
let (left, (key, _, nonzero), right) = split_at_max_vec(pattern)
|
||||
.expect("pattern must have vectorial placeholders at least at either end");
|
||||
let r_sep_size = scal_cnt(right.iter());
|
||||
let (r_sep, r_side) = right.split_at(r_sep_size);
|
||||
let l_sep_size = scal_cnt(left.iter().rev());
|
||||
let (l_side, l_sep) = left.split_at(left.len() - l_sep_size);
|
||||
let main = VecMatcher::Placeh { key: key.clone(), nonzero };
|
||||
match (left, right) {
|
||||
(&[], &[]) => VecMatcher::Placeh { key, nonzero },
|
||||
(&[], _) => VecMatcher::Scan {
|
||||
direction: Side::Left,
|
||||
left: Box::new(main),
|
||||
sep: mk_scalv(r_sep),
|
||||
right: Box::new(mk_vec(r_side)),
|
||||
},
|
||||
(_, &[]) => VecMatcher::Scan {
|
||||
direction: Side::Right,
|
||||
left: Box::new(mk_vec(l_side)),
|
||||
sep: mk_scalv(l_sep),
|
||||
right: Box::new(main),
|
||||
},
|
||||
(..) => {
|
||||
let mut key_order =
|
||||
l_side.iter().chain(r_side.iter()).filter_map(vec_attrs).collect::<Vec<_>>();
|
||||
key_order.sort_by_key(|(_, prio, _)| -(*prio as i64));
|
||||
VecMatcher::Middle {
|
||||
left: Box::new(mk_vec(l_side)),
|
||||
left_sep: mk_scalv(l_sep),
|
||||
mid: Box::new(main),
|
||||
right_sep: mk_scalv(r_sep),
|
||||
right: Box::new(mk_vec(r_side)),
|
||||
key_order: key_order.into_iter().map(|(n, ..)| n).collect(),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Pattern MUST NOT be a vectorial placeholder
|
||||
#[must_use]
|
||||
fn mk_scalar(pattern: &MacTree) -> ScalMatcher {
|
||||
match &*pattern.tok {
|
||||
MacTok::Atom(_) => panic!("Atoms aren't supported in matchers"),
|
||||
MacTok::Name(n) => ScalMatcher::Name(n.clone()),
|
||||
MacTok::Ph(Ph { name, kind }) => match kind {
|
||||
PhKind::Vector { .. } => {
|
||||
panic!("Scalar matcher cannot be built from vector pattern")
|
||||
},
|
||||
PhKind::Scalar =>
|
||||
ScalMatcher::Placeh { key: name.clone() },
|
||||
},
|
||||
MacTok::S(c, body) => ScalMatcher::S(*c, Box::new(mk_any(body))),
|
||||
MacTok::Lambda(arg, body) => ScalMatcher::Lambda(Box::new(mk_any(arg)), Box::new(mk_any(body))),
|
||||
MacTok::Ref(_) | MacTok::Slot(_) => panic!("Extension-only variants"),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::sync::Arc;
|
||||
|
||||
use orchid_api::PhKind;
|
||||
use orchid_base::{intern, location::SourceRange, sym, tree::Ph, tokens::Paren};
|
||||
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
|
||||
use super::mk_any;
|
||||
|
||||
#[test]
|
||||
fn test_scan() {
|
||||
let ex = |tok: MacTok| MacTree{ tok: Arc::new(tok), pos: SourceRange::mock().pos() };
|
||||
let pattern = vec![
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: intern!(str: "::prefix"),
|
||||
})),
|
||||
ex(MacTok::Name(sym!(prelude::do))),
|
||||
ex(MacTok::S(
|
||||
Paren::Round,
|
||||
vec![
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: intern!(str: "expr"),
|
||||
})),
|
||||
ex(MacTok::Name(sym!(prelude::;))),
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 1, at_least_one: false },
|
||||
name: intern!(str: "rest"),
|
||||
})),
|
||||
],
|
||||
)),
|
||||
ex(MacTok::Ph(Ph {
|
||||
kind: PhKind::Vector { priority: 0, at_least_one: false },
|
||||
name: intern!(str: "::suffix"),
|
||||
})),
|
||||
];
|
||||
let matcher = mk_any(&pattern);
|
||||
println!("{matcher}");
|
||||
}
|
||||
}
|
||||
21
orchid-host/src/rule/matcher.rs
Normal file
21
orchid-host/src/rule/matcher.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
//! Abstract definition of a rule matcher, so that the implementation can
|
||||
//! eventually be swapped out for a different one.
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use orchid_base::name::Sym;
|
||||
|
||||
use super::state::State;
|
||||
use crate::macros::MacTree;
|
||||
|
||||
/// Cacheable optimized structures for matching patterns on slices. This is
|
||||
/// injected to allow experimentation in the matcher implementation.
|
||||
pub trait Matcher {
|
||||
/// Build matcher for a pattern
|
||||
#[must_use]
|
||||
fn new(pattern: Rc<Vec<MacTree>>) -> Self;
|
||||
/// Apply matcher to a token sequence
|
||||
#[must_use]
|
||||
fn apply<'a>(&self, source: &'a [MacTree], save_loc: &impl Fn(Sym) -> bool)
|
||||
-> Option<State<'a>>;
|
||||
}
|
||||
24
orchid-host/src/rule/mod.rs
Normal file
24
orchid-host/src/rule/mod.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
//! Optimized form of macro pattern that can be quickly tested against the AST.
|
||||
//!
|
||||
//! # Construction
|
||||
//!
|
||||
//! convert pattern into hierarchy of plain, scan, middle
|
||||
//! - plain: accept any sequence or any non-empty sequence
|
||||
//! - scan: a single scalar pattern moves LTR or RTL, submatchers on either
|
||||
//! side
|
||||
//! - middle: two scalar patterns walk over all permutations of matches
|
||||
//! while getting progressively closer to each other
|
||||
//!
|
||||
//! # Application
|
||||
//!
|
||||
//! walk over the current matcher's valid options and poll the submatchers
|
||||
//! for each of them
|
||||
|
||||
mod any_match;
|
||||
mod build;
|
||||
mod scal_match;
|
||||
pub mod shared;
|
||||
mod vec_match;
|
||||
pub mod state;
|
||||
mod vec_attrs;
|
||||
// pub mod matcher;
|
||||
42
orchid-host/src/rule/scal_match.rs
Normal file
42
orchid-host/src/rule/scal_match.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use orchid_base::name::Sym;
|
||||
|
||||
use super::any_match::any_match;
|
||||
use super::shared::ScalMatcher;
|
||||
use crate::{macros::{MacTok, MacTree}, rule::state::{MatchState, StateEntry}};
|
||||
|
||||
#[must_use]
|
||||
pub fn scal_match<'a>(
|
||||
matcher: &ScalMatcher,
|
||||
expr: &'a MacTree,
|
||||
save_loc: &impl Fn(Sym) -> bool,
|
||||
) -> Option<MatchState<'a>> {
|
||||
match (matcher, &*expr.tok) {
|
||||
(ScalMatcher::Name(n1), MacTok::Name(n2)) if n1 == n2 => Some(match save_loc(n1.clone()) {
|
||||
true => MatchState::from_name(n1.clone(), expr.pos.clone()),
|
||||
false => MatchState::default(),
|
||||
}),
|
||||
(ScalMatcher::Placeh { key }, _) =>
|
||||
Some(MatchState::from_ph(key.clone(), StateEntry::Scalar(expr))),
|
||||
(ScalMatcher::S(c1, b_mat), MacTok::S(c2, body)) if c1 == c2 =>
|
||||
any_match(b_mat, &body[..], save_loc),
|
||||
(ScalMatcher::Lambda(arg_mat, b_mat), MacTok::Lambda(arg, body)) =>
|
||||
Some(any_match(arg_mat, arg, save_loc)?.combine(any_match(b_mat, body, save_loc)?)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn scalv_match<'a>(
|
||||
matchers: &[ScalMatcher],
|
||||
seq: &'a [MacTree],
|
||||
save_loc: &impl Fn(Sym) -> bool,
|
||||
) -> Option<MatchState<'a>> {
|
||||
if seq.len() != matchers.len() {
|
||||
return None;
|
||||
}
|
||||
let mut state = MatchState::default();
|
||||
for (matcher, expr) in matchers.iter().zip(seq.iter()) {
|
||||
state = state.combine(scal_match(matcher, expr, save_loc)?);
|
||||
}
|
||||
Some(state)
|
||||
}
|
||||
121
orchid-host/src/rule/shared.rs
Normal file
121
orchid-host/src/rule/shared.rs
Normal file
@@ -0,0 +1,121 @@
|
||||
//! Datastructures for cached pattern
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use itertools::Itertools;
|
||||
use orchid_base::interner::Tok;
|
||||
|
||||
use super::any_match::any_match;
|
||||
use super::build::mk_any;
|
||||
use orchid_base::name::Sym;
|
||||
use crate::macros::MacTree;
|
||||
use crate::rule::state::MatchState;
|
||||
use orchid_base::side::Side;
|
||||
use orchid_base::tokens::{Paren, PARENS};
|
||||
|
||||
pub enum ScalMatcher {
|
||||
Name(Sym),
|
||||
S(Paren, Box<AnyMatcher>),
|
||||
Lambda(Box<AnyMatcher>, Box<AnyMatcher>),
|
||||
Placeh { key: Tok<String> },
|
||||
}
|
||||
|
||||
pub enum VecMatcher {
|
||||
Placeh {
|
||||
key: Tok<String>,
|
||||
nonzero: bool,
|
||||
},
|
||||
Scan {
|
||||
left: Box<VecMatcher>,
|
||||
sep: Vec<ScalMatcher>,
|
||||
right: Box<VecMatcher>,
|
||||
/// The separator traverses the sequence towards this side
|
||||
direction: Side,
|
||||
},
|
||||
Middle {
|
||||
/// Matches the left outer region
|
||||
left: Box<VecMatcher>,
|
||||
/// Matches the left separator
|
||||
left_sep: Vec<ScalMatcher>,
|
||||
/// Matches the middle - can only ever be a plain placeholder
|
||||
mid: Box<VecMatcher>,
|
||||
/// Matches the right separator
|
||||
right_sep: Vec<ScalMatcher>,
|
||||
/// Matches the right outer region
|
||||
right: Box<VecMatcher>,
|
||||
/// Order of significance for sorting equally good projects based on
|
||||
/// the length of matches on either side.
|
||||
///
|
||||
/// Vectorial keys that appear on either side, in priority order
|
||||
key_order: Vec<Tok<String>>,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum AnyMatcher {
|
||||
Scalar(Vec<ScalMatcher>),
|
||||
Vec { left: Vec<ScalMatcher>, mid: VecMatcher, right: Vec<ScalMatcher> },
|
||||
}
|
||||
|
||||
// ################ Display ################
|
||||
|
||||
impl fmt::Display for ScalMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Placeh { key } => write!(f, "${key}"),
|
||||
Self::Name(n) => write!(f, "{n}"),
|
||||
Self::S(t, body) => {
|
||||
let (l, r, _) = PARENS.iter().find(|r| r.2 == *t).unwrap();
|
||||
write!(f, "{l}{body}{r}")
|
||||
},
|
||||
Self::Lambda(arg, body) => write!(f, "\\{arg}.{body}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for VecMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Placeh { key, nonzero: true } => write!(f, "...${key}"),
|
||||
Self::Placeh { key, nonzero: false } => write!(f, "..${key}"),
|
||||
Self::Scan { left, sep, right, direction } => {
|
||||
let arrow = if direction == &Side::Left { "<==" } else { "==>" };
|
||||
write!(f, "Scan{{{left} {arrow} {} {arrow} {right}}}", sep.iter().join(" "))
|
||||
},
|
||||
Self::Middle { left, left_sep, mid, right_sep, right, .. } => {
|
||||
let left_sep_s = left_sep.iter().join(" ");
|
||||
let right_sep_s = right_sep.iter().join(" ");
|
||||
write!(f, "Middle{{{left}|{left_sep_s}|{mid}|{right_sep_s}|{right}}}")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for AnyMatcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Scalar(s) => {
|
||||
write!(f, "({})", s.iter().join(" "))
|
||||
},
|
||||
Self::Vec { left, mid, right } => {
|
||||
let lefts = left.iter().join(" ");
|
||||
let rights = right.iter().join(" ");
|
||||
write!(f, "[{lefts}|{mid}|{rights}]")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ################ External ################
|
||||
|
||||
/// A priority-order tree of the vectorial placeholders with scalars as leaves.
|
||||
pub struct Matcher(AnyMatcher);
|
||||
impl Matcher {
|
||||
pub fn new(pattern: &[MacTree]) -> Self { Self(mk_any(pattern)) }
|
||||
pub fn apply<'a>(&self, seq: &'a [MacTree], save_loc: &impl Fn(Sym) -> bool) -> Option<MatchState<'a>> {
|
||||
any_match(&self.0, seq, save_loc)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Matcher {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
|
||||
}
|
||||
84
orchid-host/src/rule/state.rs
Normal file
84
orchid-host/src/rule/state.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use orchid_api::PhKind;
|
||||
use orchid_base::tree::Ph;
|
||||
use orchid_base::{interner::Tok, join::join_maps};
|
||||
use orchid_base::location::Pos;
|
||||
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
use orchid_base::name::Sym;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum StateEntry<'a> {
|
||||
Vec(&'a [MacTree]),
|
||||
Scalar(&'a MacTree),
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct MatchState<'a> {
|
||||
placeholders: HashMap<Tok<String>, StateEntry<'a>>,
|
||||
name_posv: HashMap<Sym, Vec<Pos>>,
|
||||
}
|
||||
impl<'a> MatchState<'a> {
|
||||
pub fn from_ph(key: Tok<String>, entry: StateEntry<'a>) -> Self {
|
||||
Self { placeholders: HashMap::from([(key, entry)]), name_posv: HashMap::new() }
|
||||
}
|
||||
pub fn combine(self, s: Self) -> Self {
|
||||
Self {
|
||||
placeholders: self.placeholders.into_iter().chain(s.placeholders).collect(),
|
||||
name_posv: join_maps(self.name_posv, s.name_posv, |_, l, r| {
|
||||
l.into_iter().chain(r).collect()
|
||||
}),
|
||||
}
|
||||
}
|
||||
pub fn ph_len(&self, key: &Tok<String>) -> Option<usize> {
|
||||
match self.placeholders.get(key)? {
|
||||
StateEntry::Vec(slc) => Some(slc.len()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn from_name(name: Sym, location: Pos) -> Self {
|
||||
Self { name_posv: HashMap::from([(name, vec![location])]), placeholders: HashMap::new() }
|
||||
}
|
||||
}
|
||||
impl Default for MatchState<'static> {
|
||||
fn default() -> Self { Self { name_posv: HashMap::new(), placeholders: HashMap::new() } }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn apply_exprv(template: &[MacTree], state: &MatchState) -> Vec<MacTree> {
|
||||
template.iter().map(|e| apply_expr(e, state)).flat_map(Vec::into_iter).collect()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn apply_expr(template: &MacTree, state: &MatchState) -> Vec<MacTree> {
|
||||
let MacTree { pos, tok } = template;
|
||||
match &**tok {
|
||||
MacTok::Name(n) => match state.name_posv.get(n) {
|
||||
None => vec![template.clone()],
|
||||
Some(locs) => vec![MacTree { tok: tok.clone(), pos: locs[0].clone() }],
|
||||
},
|
||||
MacTok::Atom(_) => vec![template.clone()],
|
||||
MacTok::S(c, body) => vec![MacTree {
|
||||
pos: pos.clone(), tok: Arc::new(MacTok::S(*c, apply_exprv(body.as_slice(), state))),
|
||||
}],
|
||||
MacTok::Ph(Ph { name, kind }) => {
|
||||
let Some(value) = state.placeholders.get(name) else {
|
||||
panic!("Placeholder does not have a value in state")
|
||||
};
|
||||
match (kind, value) {
|
||||
(PhKind::Scalar, StateEntry::Scalar(item)) => vec![(*item).clone()],
|
||||
(PhKind::Vector { .. }, StateEntry::Vec(chunk)) => chunk.to_vec(),
|
||||
_ => panic!("Type mismatch between template and state"),
|
||||
}
|
||||
},
|
||||
MacTok::Lambda(arg, body) => vec![MacTree {
|
||||
pos: pos.clone(),
|
||||
tok: Arc::new(MacTok::Lambda(
|
||||
apply_exprv(arg, state),
|
||||
apply_exprv(&body[..], state),
|
||||
)),
|
||||
}],
|
||||
MacTok::Slot(_) | MacTok::Ref(_) => panic!("Extension-only variants"),
|
||||
}
|
||||
}
|
||||
16
orchid-host/src/rule/vec_attrs.rs
Normal file
16
orchid-host/src/rule/vec_attrs.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
use orchid_api::PhKind;
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::tree::Ph;
|
||||
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
|
||||
/// Returns the name, priority and at_least_one of the expression if it is
|
||||
/// a vectorial placeholder
|
||||
#[must_use]
|
||||
pub fn vec_attrs(expr: &MacTree) -> Option<(Tok<String>, u8, bool)> {
|
||||
match (*expr.tok).clone() {
|
||||
MacTok::Ph(Ph { kind: PhKind::Vector { priority, at_least_one }, name }) =>
|
||||
Some((name, priority, at_least_one)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
94
orchid-host/src/rule/vec_match.rs
Normal file
94
orchid-host/src/rule/vec_match.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use itertools::Itertools;
|
||||
|
||||
use super::scal_match::scalv_match;
|
||||
use super::shared::VecMatcher;
|
||||
use orchid_base::name::Sym;
|
||||
use crate::{macros::MacTree, rule::state::{MatchState, StateEntry}};
|
||||
|
||||
#[must_use]
|
||||
pub fn vec_match<'a>(
|
||||
matcher: &VecMatcher,
|
||||
seq: &'a [MacTree],
|
||||
save_loc: &impl Fn(Sym) -> bool,
|
||||
) -> Option<MatchState<'a>> {
|
||||
match matcher {
|
||||
VecMatcher::Placeh { key, nonzero } => {
|
||||
if *nonzero && seq.is_empty() {
|
||||
return None;
|
||||
}
|
||||
return Some(MatchState::from_ph(key.clone(), StateEntry::Vec(seq)));
|
||||
},
|
||||
VecMatcher::Scan { left, sep, right, direction } => {
|
||||
if seq.len() < sep.len() {
|
||||
return None;
|
||||
}
|
||||
for lpos in direction.walk(0..=seq.len() - sep.len()) {
|
||||
let rpos = lpos + sep.len();
|
||||
let state = vec_match(left, &seq[..lpos], save_loc)
|
||||
.and_then(|s| Some(s.combine(scalv_match(sep, &seq[lpos..rpos], save_loc)?)))
|
||||
.and_then(|s| Some(s.combine(vec_match(right, &seq[rpos..], save_loc)?)));
|
||||
if let Some(s) = state {
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
// XXX predict heap space usage and allocation count
|
||||
VecMatcher::Middle { left, left_sep, mid, right_sep, right, key_order } => {
|
||||
if seq.len() < left_sep.len() + right_sep.len() {
|
||||
return None;
|
||||
}
|
||||
// Valid locations for the left separator
|
||||
let lposv = seq[..seq.len() - right_sep.len()]
|
||||
.windows(left_sep.len())
|
||||
.enumerate()
|
||||
.filter_map(|(i, window)| scalv_match(left_sep, window, save_loc).map(|s| (i, s)))
|
||||
.collect::<Vec<_>>();
|
||||
// Valid locations for the right separator
|
||||
let rposv = seq[left_sep.len()..]
|
||||
.windows(right_sep.len())
|
||||
.enumerate()
|
||||
.filter_map(|(i, window)| scalv_match(right_sep, window, save_loc).map(|s| (i, s)))
|
||||
.collect::<Vec<_>>();
|
||||
// Valid combinations of locations for the separators
|
||||
let mut pos_pairs = lposv
|
||||
.into_iter()
|
||||
.cartesian_product(rposv)
|
||||
.filter(|((lpos, _), (rpos, _))| lpos + left_sep.len() <= *rpos)
|
||||
.map(|((lpos, lstate), (rpos, rstate))| (lpos, rpos, lstate.combine(rstate)))
|
||||
.collect::<Vec<_>>();
|
||||
// In descending order of size
|
||||
pos_pairs.sort_by_key(|(l, r, _)| -((r - l) as i64));
|
||||
let eql_clusters = pos_pairs.into_iter().chunk_by(|(al, ar, _)| ar - al);
|
||||
for (_gap_size, cluster) in eql_clusters.into_iter() {
|
||||
let best_candidate = cluster
|
||||
.into_iter()
|
||||
.filter_map(|(lpos, rpos, state)| {
|
||||
Some(
|
||||
state
|
||||
.combine(vec_match(left, &seq[..lpos], save_loc)?)
|
||||
.combine(vec_match(mid, &seq[lpos + left_sep.len()..rpos], save_loc)?)
|
||||
.combine(vec_match(right, &seq[rpos + right_sep.len()..], save_loc)?),
|
||||
)
|
||||
})
|
||||
.max_by(|a, b| {
|
||||
for key in key_order {
|
||||
let alen = a.ph_len(key).expect("key_order references scalar or missing");
|
||||
let blen = b.ph_len(key).expect("key_order references scalar or missing");
|
||||
match alen.cmp(&blen) {
|
||||
Ordering::Equal => (),
|
||||
any => return any,
|
||||
}
|
||||
}
|
||||
Ordering::Equal
|
||||
});
|
||||
if let Some(state) = best_candidate {
|
||||
return Some(state);
|
||||
}
|
||||
}
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ use std::path::PathBuf;
|
||||
use std::sync::Mutex;
|
||||
use std::{process, thread};
|
||||
|
||||
use orchid_api::ExtensionHeader;
|
||||
use orchid_api_traits::{Decode, Encode};
|
||||
use orchid_base::logging::Logger;
|
||||
use orchid_base::msg::{recv_msg, send_msg};
|
||||
@@ -15,7 +14,7 @@ pub struct Subprocess {
|
||||
child: Mutex<process::Child>,
|
||||
stdin: Mutex<process::ChildStdin>,
|
||||
stdout: Mutex<process::ChildStdout>,
|
||||
header: ExtensionHeader,
|
||||
header: api::ExtensionHeader,
|
||||
}
|
||||
impl Subprocess {
|
||||
pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> {
|
||||
@@ -30,7 +29,7 @@ impl Subprocess {
|
||||
api::HostHeader { log_strategy: logger.strat() }.encode(&mut stdin);
|
||||
stdin.flush()?;
|
||||
let mut stdout = child.stdout.take().unwrap();
|
||||
let header = ExtensionHeader::decode(&mut stdout);
|
||||
let header = api::ExtensionHeader::decode(&mut stdout);
|
||||
let child_stderr = child.stderr.take().unwrap();
|
||||
thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || {
|
||||
let mut reader = io::BufReader::new(child_stderr);
|
||||
|
||||
@@ -4,9 +4,9 @@ use std::sync::{Mutex, OnceLock};
|
||||
use itertools::Itertools;
|
||||
use never::Never;
|
||||
use orchid_base::error::OrcRes;
|
||||
use orchid_base::interner::{deintern, intern, Tok};
|
||||
use orchid_base::interner::{intern, Tok};
|
||||
use orchid_base::location::Pos;
|
||||
use orchid_base::macros::{mtreev_from_api, MTree};
|
||||
use orchid_base::macros::mtreev_from_api;
|
||||
use orchid_base::name::Sym;
|
||||
use orchid_base::parse::{Comment, Import};
|
||||
use orchid_base::tree::{TokTree, Token};
|
||||
@@ -16,6 +16,7 @@ use substack::{with_iter_stack, Substack};
|
||||
use crate::api;
|
||||
use crate::expr::Expr;
|
||||
use crate::extension::{AtomHand, System};
|
||||
use crate::macros::{MacTok, MacTree};
|
||||
|
||||
pub type ParsTokTree = TokTree<'static, AtomHand, Never>;
|
||||
pub type ParsTok = Token<'static, AtomHand, Never>;
|
||||
@@ -36,7 +37,7 @@ pub enum ItemKind {
|
||||
}
|
||||
|
||||
impl Item {
|
||||
pub fn from_api<'a>(
|
||||
pub fn from_api(
|
||||
tree: api::Item,
|
||||
path: Substack<Tok<String>>,
|
||||
sys: &System
|
||||
@@ -44,12 +45,12 @@ impl Item {
|
||||
let kind = match tree.kind {
|
||||
api::ItemKind::Member(m) => ItemKind::Member(Member::from_api(m, path, sys)),
|
||||
api::ItemKind::Import(i) =>
|
||||
ItemKind::Import(Import{ path: Sym::deintern(i).iter().collect(), name: None }),
|
||||
api::ItemKind::Export(e) => ItemKind::Export(deintern(e)),
|
||||
ItemKind::Import(Import{ path: Sym::from_api(i).iter().collect(), name: None }),
|
||||
api::ItemKind::Export(e) => ItemKind::Export(Tok::from_api(e)),
|
||||
api::ItemKind::Macro(api::MacroBlock { priority, rules }) => ItemKind::Macro(priority, {
|
||||
Vec::from_iter(rules.into_iter().map(|api| Rule {
|
||||
pos: Pos::from_api(&api.location),
|
||||
pattern: mtreev_from_api(&api.pattern),
|
||||
pattern: mtreev_from_api(&api.pattern, &mut |a| MacTok::Atom(AtomHand::from_api(a.clone()))),
|
||||
kind: RuleKind::Remote(sys.clone(), api.id),
|
||||
comments: api.comments.iter().map(Comment::from_api).collect_vec()
|
||||
}))
|
||||
@@ -67,19 +68,19 @@ pub struct Member {
|
||||
pub lazy: Mutex<Option<LazyMemberHandle>>,
|
||||
}
|
||||
impl Member {
|
||||
pub fn from_api<'a>(
|
||||
pub fn from_api(
|
||||
api: api::Member,
|
||||
path: Substack<Tok<String>>,
|
||||
sys: &System,
|
||||
) -> Self {
|
||||
let name = deintern(api.name);
|
||||
let name = Tok::from_api(api.name);
|
||||
let full_path = path.push(name.clone());
|
||||
let kind = match api.kind {
|
||||
api::MemberKind::Lazy(id) =>
|
||||
return LazyMemberHandle(id, sys.clone(), intern(&full_path.unreverse())).to_member(name),
|
||||
return LazyMemberHandle(id, sys.clone(), intern(&full_path.unreverse())).into_member(name),
|
||||
api::MemberKind::Const(c) => MemberKind::Const(Code::from_expr(
|
||||
CodeLocator::to_const(full_path.unreverse()),
|
||||
Expr::from_api(c, &mut ())
|
||||
Expr::from_api(&c, &mut ())
|
||||
)),
|
||||
api::MemberKind::Module(m) => MemberKind::Mod(Module::from_api(m, full_path, sys)),
|
||||
};
|
||||
@@ -129,7 +130,7 @@ impl LazyMemberHandle {
|
||||
pub fn run(self) -> OrcRes<MemberKind> {
|
||||
match self.1.get_tree(self.0) {
|
||||
api::MemberKind::Const(c) => Ok(MemberKind::Const(Code {
|
||||
bytecode: Expr::from_api(c, &mut ()).into(),
|
||||
bytecode: Expr::from_api(&c, &mut ()).into(),
|
||||
locator: CodeLocator { steps: self.2, rule_loc: None },
|
||||
source: None,
|
||||
})),
|
||||
@@ -139,7 +140,7 @@ impl LazyMemberHandle {
|
||||
api::MemberKind::Lazy(id) => Self(id, self.1, self.2).run(),
|
||||
}
|
||||
}
|
||||
pub fn to_member(self, name: Tok<String>) -> Member {
|
||||
pub fn into_member(self, name: Tok<String>) -> Member {
|
||||
Member { name, kind: OnceLock::new(), lazy: Mutex::new(Some(self)) }
|
||||
}
|
||||
}
|
||||
@@ -148,7 +149,7 @@ impl LazyMemberHandle {
|
||||
pub struct Rule {
|
||||
pub pos: Pos,
|
||||
pub comments: Vec<Comment>,
|
||||
pub pattern: Vec<MTree<'static>>,
|
||||
pub pattern: Vec<MacTree>,
|
||||
pub kind: RuleKind,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user