Updated everything and moved to hard tab indentation

This commit is contained in:
2025-01-08 19:20:34 +01:00
parent 7cdfe7e3c4
commit 52c8d1c95a
100 changed files with 5949 additions and 5998 deletions

View File

@@ -4,41 +4,41 @@ use std::{fmt, io, mem, process};
use orchid_base::msg::{recv_msg, send_msg};
pub struct SharedChild {
child: process::Child,
stdin: Mutex<process::ChildStdin>,
stdout: Mutex<process::ChildStdout>,
debug: Option<(String, Mutex<Box<dyn fmt::Write>>)>,
child: process::Child,
stdin: Mutex<process::ChildStdin>,
stdout: Mutex<process::ChildStdout>,
debug: Option<(String, Mutex<Box<dyn fmt::Write>>)>,
}
impl SharedChild {
pub fn new(
command: &mut process::Command,
debug: Option<(&str, impl fmt::Write + 'static)>,
) -> io::Result<Self> {
let mut child =
command.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()).spawn()?;
let stdin = Mutex::new(child.stdin.take().expect("Piped stdin above"));
let stdout = Mutex::new(child.stdout.take().expect("Piped stdout above"));
let debug = debug.map(|(n, w)| (n.to_string(), Mutex::new(Box::new(w) as Box<dyn fmt::Write>)));
Ok(Self { child, stdin, stdout, debug })
}
pub fn new(
command: &mut process::Command,
debug: Option<(&str, impl fmt::Write + 'static)>,
) -> io::Result<Self> {
let mut child =
command.stdin(process::Stdio::piped()).stdout(process::Stdio::piped()).spawn()?;
let stdin = Mutex::new(child.stdin.take().expect("Piped stdin above"));
let stdout = Mutex::new(child.stdout.take().expect("Piped stdout above"));
let debug = debug.map(|(n, w)| (n.to_string(), Mutex::new(Box::new(w) as Box<dyn fmt::Write>)));
Ok(Self { child, stdin, stdout, debug })
}
pub fn send_msg(&self, msg: &[u8]) -> io::Result<()> {
if let Some((n, dbg)) = &self.debug {
let mut dbg = dbg.lock().unwrap();
writeln!(dbg, "To {n}: {msg:?}").unwrap();
}
send_msg(&mut *self.stdin.lock().unwrap(), msg)
}
pub fn send_msg(&self, msg: &[u8]) -> io::Result<()> {
if let Some((n, dbg)) = &self.debug {
let mut dbg = dbg.lock().unwrap();
writeln!(dbg, "To {n}: {msg:?}").unwrap();
}
send_msg(&mut *self.stdin.lock().unwrap(), msg)
}
pub fn recv_msg(&self) -> io::Result<Vec<u8>> {
let msg = recv_msg(&mut *self.stdout.lock().unwrap());
if let Some((n, dbg)) = &self.debug {
let mut dbg = dbg.lock().unwrap();
writeln!(dbg, "From {n}: {msg:?}").unwrap();
}
msg
}
pub fn recv_msg(&self) -> io::Result<Vec<u8>> {
let msg = recv_msg(&mut *self.stdout.lock().unwrap());
if let Some((n, dbg)) = &self.debug {
let mut dbg = dbg.lock().unwrap();
writeln!(dbg, "From {n}: {msg:?}").unwrap();
}
msg
}
}
impl Drop for SharedChild {
fn drop(&mut self) { mem::drop(self.child.kill()) }
fn drop(&mut self) { mem::drop(self.child.kill()) }
}

View File

@@ -18,123 +18,124 @@ pub type ExprParseCtx = ();
#[derive(Clone, Debug)]
pub struct Expr {
is_canonical: Arc<AtomicBool>,
pos: Pos,
kind: Arc<RwLock<ExprKind>>,
is_canonical: Arc<AtomicBool>,
pos: Pos,
kind: Arc<RwLock<ExprKind>>,
}
impl Expr {
pub fn pos(&self) -> Pos { self.pos.clone() }
pub fn as_atom(&self) -> Option<AtomHand> { todo!() }
pub fn strong_count(&self) -> usize { todo!() }
pub fn id(&self) -> api::ExprTicket {
api::ExprTicket(
NonZeroU64::new(self.kind.as_ref() as *const RwLock<_> as usize as u64)
.expect("this is a ref, it cannot be null"),
)
}
pub fn canonicalize(&self) -> api::ExprTicket {
if !self.is_canonical.swap(true, Ordering::Relaxed) {
KNOWN_EXPRS.write().unwrap().entry(self.id()).or_insert_with(|| self.clone());
}
self.id()
}
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 {
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))),
is_canonical: Arc::default(),
pos: Pos::from_api(&api.location),
}
}
pub fn to_api(&self) -> api::InspectedKind {
use api::InspectedKind as K;
match &*self.kind.read().unwrap() {
ExprKind::Atom(a) => K::Atom(a.to_api()),
ExprKind::Bottom(b) => K::Bottom(b.to_api()),
_ => K::Opaque,
}
}
pub fn pos(&self) -> Pos { self.pos.clone() }
pub fn as_atom(&self) -> Option<AtomHand> { todo!() }
pub fn strong_count(&self) -> usize { todo!() }
pub fn id(&self) -> api::ExprTicket {
api::ExprTicket(
NonZeroU64::new(self.kind.as_ref() as *const RwLock<_> as usize as u64)
.expect("this is a ref, it cannot be null"),
)
}
pub fn canonicalize(&self) -> api::ExprTicket {
if !self.is_canonical.swap(true, Ordering::Relaxed) {
KNOWN_EXPRS.write().unwrap().entry(self.id()).or_insert_with(|| self.clone());
}
self.id()
}
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 {
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))),
is_canonical: Arc::default(),
pos: Pos::from_api(&api.location),
}
}
pub fn to_api(&self) -> api::InspectedKind {
use api::InspectedKind as K;
match &*self.kind.read().unwrap() {
ExprKind::Atom(a) => K::Atom(a.to_api()),
ExprKind::Bottom(b) => K::Bottom(b.to_api()),
_ => K::Opaque,
}
}
}
impl Drop for Expr {
fn drop(&mut self) {
// If the only two references left are this and known, remove from known
if Arc::strong_count(&self.kind) == 2 && self.is_canonical.load(Ordering::Relaxed) {
// if known is poisoned, a leak is preferable to a panicking destructor
if let Ok(mut w) = KNOWN_EXPRS.write() {
w.remove(&self.id());
}
}
}
fn drop(&mut self) {
// If the only two references left are this and known, remove from known
if Arc::strong_count(&self.kind) == 2 && self.is_canonical.load(Ordering::Relaxed) {
// if known is poisoned, a leak is preferable to a panicking destructor
if let Ok(mut w) = KNOWN_EXPRS.write() {
w.remove(&self.id());
}
}
}
}
lazy_static! {
static ref KNOWN_EXPRS: RwLock<HashMap<api::ExprTicket, Expr>> = RwLock::default();
static ref KNOWN_EXPRS: RwLock<HashMap<api::ExprTicket, Expr>> = RwLock::default();
}
#[derive(Clone, Debug)]
pub enum ExprKind {
Seq(Expr, Expr),
Call(Expr, Expr),
Atom(AtomHand),
Arg,
Lambda(Option<PathSet>, Expr),
Bottom(OrcErrv),
Const(Sym),
Seq(Expr, Expr),
Call(Expr, Expr),
Atom(AtomHand),
Arg,
Lambda(Option<PathSet>, Expr),
Bottom(OrcErrv),
Const(Sym),
}
impl ExprKind {
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"),
})
}
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"),
})
}
}
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub enum Step {
Left,
Right,
Left,
Right,
}
#[derive(Clone, Debug)]
pub struct PathSet {
/// The single steps through [super::nort::Clause::Apply]
pub steps: VecDeque<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>)>,
/// The single steps through [super::nort::Clause::Apply]
pub steps: VecDeque<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 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,
},
}
}
pub fn after(mut self, step: Step) -> Self {
self.steps.push_front(step);
self
}
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,
}
},
}
}
}

View File

@@ -32,78 +32,78 @@ use crate::tree::{Member, ParsTokTree};
#[derive(Debug, destructure)]
pub struct AtomData {
owner: System,
drop: Option<api::AtomId>,
data: Vec<u8>,
owner: System,
drop: Option<api::AtomId>,
data: Vec<u8>,
}
impl AtomData {
fn api(self) -> api::Atom {
let (owner, drop, data) = self.destructure();
api::Atom { data, drop, owner: owner.id() }
}
fn api_ref(&self) -> api::Atom {
api::Atom { data: self.data.clone(), drop: self.drop, owner: self.owner.id() }
}
fn api(self) -> api::Atom {
let (owner, drop, data) = self.destructure();
api::Atom { data, drop, owner: owner.id() }
}
fn api_ref(&self) -> api::Atom {
api::Atom { data: self.data.clone(), drop: self.drop, owner: self.owner.id() }
}
}
impl Drop for AtomData {
fn drop(&mut self) {
if let Some(id) = self.drop {
self.owner.reqnot().notify(api::AtomDrop(self.owner.id(), id))
}
}
fn drop(&mut self) {
if let Some(id) = self.drop {
self.owner.reqnot().notify(api::AtomDrop(self.owner.id(), id))
}
}
}
#[derive(Clone, Debug)]
pub struct AtomHand(Arc<AtomData>);
impl AtomHand {
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>>> =
Mutex::default();
}
let owner = atom.owner;
let mut owned_g = OWNED_ATOMS.lock().unwrap();
if let Some(data) = owned_g.get(&(owner, id)) {
if let Some(atom) = data.upgrade() {
return Self(atom);
}
}
let new = create_new(atom);
owned_g.insert((owner, id), Arc::downgrade(&new.0));
new
} else {
create_new(atom)
}
}
pub fn call(self, arg: Expr) -> api::Expression {
let owner_sys = self.0.owner.clone();
let reqnot = owner_sys.reqnot();
let ticket = owner_sys.give_expr(arg.canonicalize(), || arg);
match Arc::try_unwrap(self.0) {
Ok(data) => reqnot.request(api::FinalCall(data.api(), ticket)),
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), ticket)),
}
}
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())) }
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>>> =
Mutex::default();
}
let owner = atom.owner;
let mut owned_g = OWNED_ATOMS.lock().unwrap();
if let Some(data) = owned_g.get(&(owner, id)) {
if let Some(atom) = data.upgrade() {
return Self(atom);
}
}
let new = create_new(atom);
owned_g.insert((owner, id), Arc::downgrade(&new.0));
new
} else {
create_new(atom)
}
}
pub fn call(self, arg: Expr) -> api::Expression {
let owner_sys = self.0.owner.clone();
let reqnot = owner_sys.reqnot();
let ticket = owner_sys.give_expr(arg.canonicalize(), || arg);
match Arc::try_unwrap(self.0) {
Ok(data) => reqnot.request(api::FinalCall(data.api(), ticket)),
Err(hand) => reqnot.request(api::CallRef(hand.api_ref(), ticket)),
}
}
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 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() }
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() }
}
impl fmt::Display for AtomHand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.print()) }
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.print()) }
}
pub type OnMessage = Box<dyn FnMut(&[u8]) + Send>;
@@ -115,9 +115,9 @@ pub type OnMessage = Box<dyn FnMut(&[u8]) + Send>;
///
/// There are no ordering guarantees about these
pub trait ExtensionPort: Send + Sync {
fn set_onmessage(&self, callback: OnMessage);
fn send(&self, msg: &[u8]);
fn header(&self) -> &api::ExtensionHeader;
fn set_onmessage(&self, callback: OnMessage);
fn send(&self, msg: &[u8]);
fn header(&self) -> &api::ExtensionHeader;
}
/// Data held about an Extension. This is refcounted within [Extension]. It's
@@ -126,323 +126,323 @@ pub trait ExtensionPort: Send + Sync {
/// upgrading fails.
#[derive(destructure)]
pub struct ExtensionData {
port: Arc<dyn ExtensionPort>,
// child: Mutex<process::Child>,
// child_stdin: Mutex<ChildStdin>,
reqnot: ReqNot<api::HostMsgSet>,
systems: Vec<SystemCtor>,
logger: Logger,
port: Arc<dyn ExtensionPort>,
// child: Mutex<process::Child>,
// child_stdin: Mutex<ChildStdin>,
reqnot: ReqNot<api::HostMsgSet>,
systems: Vec<SystemCtor>,
logger: Logger,
}
impl Drop for ExtensionData {
fn drop(&mut self) { self.reqnot.notify(api::HostExtNotif::Exit); }
fn drop(&mut self) { self.reqnot.notify(api::HostExtNotif::Exit); }
}
fn acq_expr(sys: api::SysId, extk: api::ExprTicket) {
(System::resolve(sys).expect("Expr acq'd by invalid system"))
.give_expr(extk, || Expr::resolve(extk).expect("Invalid expr acq'd"));
(System::resolve(sys).expect("Expr acq'd by invalid system"))
.give_expr(extk, || Expr::resolve(extk).expect("Invalid expr acq'd"));
}
fn rel_expr(sys: api::SysId, extk: api::ExprTicket) {
let sys = System::resolve(sys).unwrap();
let mut exprs = sys.0.exprs.write().unwrap();
exprs.entry(extk).and_replace_entry_with(|_, (rc, rt)| {
(0 < rc.fetch_sub(1, Ordering::Relaxed)).then_some((rc, rt))
});
let sys = System::resolve(sys).unwrap();
let mut exprs = sys.0.exprs.write().unwrap();
exprs.entry(extk).and_replace_entry_with(|_, (rc, rt)| {
(0 < rc.fetch_sub(1, Ordering::Relaxed)).then_some((rc, rt))
});
}
#[derive(Clone)]
pub struct Extension(Arc<ExtensionData>);
impl Extension {
pub fn new_process(port: Arc<dyn ExtensionPort>, logger: Logger) -> io::Result<Self> {
let eh = port.header();
let ret = Arc::new_cyclic(|weak: &Weak<ExtensionData>| ExtensionData {
systems: (eh.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: weak.clone() })
.collect(),
logger,
port: port.clone(),
reqnot: ReqNot::new(
clone!(weak; move |sfn, _| {
let data = weak.upgrade().unwrap();
data.logger.log_buf("Downsending", sfn);
data.port.send(sfn);
}),
clone!(weak; move |notif, _| match notif {
api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => acq_expr(acq.0, acq.1),
api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => rel_expr(rel.0, rel.1),
api::ExtHostNotif::ExprNotif(api::ExprNotif::Move(mov)) => {
acq_expr(mov.inc, mov.expr);
rel_expr(mov.dec, mov.expr);
},
api::ExtHostNotif::Log(api::Log(str)) => weak.upgrade().unwrap().logger.log(str),
}),
|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).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();
hand.handle(fw, &sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())))
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
let sys = System::resolve(id).unwrap();
hand.handle(fw, &sys.request(body.clone()))
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, rep_out) = sync_channel(0);
let lex_g = LEX_RECUR.lock().unwrap();
let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).unwrap();
hand.handle(&sl, &rep_out.recv().unwrap())
},
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => {
let expr = Expr::resolve(target).expect("Invalid ticket");
hand.handle(&ins, &api::Inspected {
refcount: expr.strong_count() as u32,
location: expr.pos().to_api(),
kind: expr.to_api(),
})
},
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros { ref run_id, ref query }) => hand
.handle(
rm,
&macro_recur(
*run_id,
mtreev_from_api(query, &mut |_| panic!("Recursion never contains atoms")),
)
.map(|x| macro_treev_to_api(*run_id, x)),
),
},
),
});
let weak = Arc::downgrade(&ret);
port.set_onmessage(Box::new(move |msg| {
if let Some(xd) = weak.upgrade() {
xd.reqnot.receive(msg)
}
}));
Ok(Self(ret))
}
pub fn systems(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
pub fn new_process(port: Arc<dyn ExtensionPort>, logger: Logger) -> io::Result<Self> {
let eh = port.header();
let ret = Arc::new_cyclic(|weak: &Weak<ExtensionData>| ExtensionData {
systems: (eh.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: weak.clone() })
.collect(),
logger,
port: port.clone(),
reqnot: ReqNot::new(
clone!(weak; move |sfn, _| {
let data = weak.upgrade().unwrap();
data.logger.log_buf("Downsending", sfn);
data.port.send(sfn);
}),
clone!(weak; move |notif, _| match notif {
api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => acq_expr(acq.0, acq.1),
api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => rel_expr(rel.0, rel.1),
api::ExtHostNotif::ExprNotif(api::ExprNotif::Move(mov)) => {
acq_expr(mov.inc, mov.expr);
rel_expr(mov.dec, mov.expr);
},
api::ExtHostNotif::Log(api::Log(str)) => weak.upgrade().unwrap().logger.log(str),
}),
|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).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();
hand.handle(fw, &sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())))
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
let sys = System::resolve(id).unwrap();
hand.handle(fw, &sys.request(body.clone()))
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, rep_out) = sync_channel(0);
let lex_g = LEX_RECUR.lock().unwrap();
let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).unwrap();
hand.handle(&sl, &rep_out.recv().unwrap())
},
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => {
let expr = Expr::resolve(target).expect("Invalid ticket");
hand.handle(&ins, &api::Inspected {
refcount: expr.strong_count() as u32,
location: expr.pos().to_api(),
kind: expr.to_api(),
})
},
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros { ref run_id, ref query }) => hand
.handle(
rm,
&macro_recur(
*run_id,
mtreev_from_api(query, &mut |_| panic!("Recursion never contains atoms")),
)
.map(|x| macro_treev_to_api(*run_id, x)),
),
},
),
});
let weak = Arc::downgrade(&ret);
port.set_onmessage(Box::new(move |msg| {
if let Some(xd) = weak.upgrade() {
xd.reqnot.receive(msg)
}
}));
Ok(Self(ret))
}
pub fn systems(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
}
pub struct SystemCtor {
decl: api::SystemDecl,
ext: Weak<ExtensionData>,
decl: api::SystemDecl,
ext: Weak<ExtensionData>,
}
impl SystemCtor {
pub fn name(&self) -> &str { &self.decl.name }
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
pub fn depends(&self) -> impl ExactSizeIterator<Item = &str> {
self.decl.depends.iter().map(|s| &**s)
}
pub fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> System {
let mut inst_g = SYSTEM_INSTS.write().unwrap();
let depends = depends.into_iter().map(|si| si.id()).collect_vec();
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
static NEXT_ID: AtomicU16 = AtomicU16::new(1);
let id =
api::SysId(NonZero::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).expect("next_id wrapped"));
let sys_inst = ext.reqnot.request(api::NewSystem { depends, id, system: self.decl.id });
let data = System(Arc::new(SystemInstData {
decl_id: self.decl.id,
ext: Extension(ext),
exprs: RwLock::default(),
lex_filter: sys_inst.lex_filter,
const_root: OnceLock::new(),
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(Tok::from_api(k)),
&data,
)
})
.collect_vec();
data.0.const_root.set(root).unwrap();
inst_g.insert(id, data.clone());
data
}
pub fn name(&self) -> &str { &self.decl.name }
pub fn priority(&self) -> NotNan<f64> { self.decl.priority }
pub fn depends(&self) -> impl ExactSizeIterator<Item = &str> {
self.decl.depends.iter().map(|s| &**s)
}
pub fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> System {
let mut inst_g = SYSTEM_INSTS.write().unwrap();
let depends = depends.into_iter().map(|si| si.id()).collect_vec();
debug_assert_eq!(depends.len(), self.decl.depends.len(), "Wrong number of deps provided");
let ext = self.ext.upgrade().expect("SystemCtor should be freed before Extension");
static NEXT_ID: AtomicU16 = AtomicU16::new(1);
let id =
api::SysId(NonZero::new(NEXT_ID.fetch_add(1, Ordering::Relaxed)).expect("next_id wrapped"));
let sys_inst = ext.reqnot.request(api::NewSystem { depends, id, system: self.decl.id });
let data = System(Arc::new(SystemInstData {
decl_id: self.decl.id,
ext: Extension(ext),
exprs: RwLock::default(),
lex_filter: sys_inst.lex_filter,
const_root: OnceLock::new(),
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(Tok::from_api(k)),
&data,
)
})
.collect_vec();
data.0.const_root.set(root).unwrap();
inst_g.insert(id, data.clone());
data
}
}
lazy_static! {
static ref SYSTEM_INSTS: RwLock<HashMap<api::SysId, System>> = RwLock::default();
static ref LEX_RECUR: Mutex<HashMap<api::ParsId, SyncSender<ReqPair<api::SubLex>>>> =
Mutex::default();
static ref SYSTEM_INSTS: RwLock<HashMap<api::SysId, System>> = RwLock::default();
static ref LEX_RECUR: Mutex<HashMap<api::ParsId, SyncSender<ReqPair<api::SubLex>>>> =
Mutex::default();
}
pub struct ReqPair<R: Request>(R, pub SyncSender<R::Response>);
#[derive(destructure)]
pub struct SystemInstData {
exprs: RwLock<HashMap<api::ExprTicket, (AtomicU32, Expr)>>,
ext: Extension,
decl_id: api::SysDeclId,
lex_filter: api::CharFilter,
id: api::SysId,
const_root: OnceLock<Vec<Member>>,
line_types: Vec<Tok<String>>,
exprs: RwLock<HashMap<api::ExprTicket, (AtomicU32, Expr)>>,
ext: Extension,
decl_id: api::SysDeclId,
lex_filter: api::CharFilter,
id: api::SysId,
const_root: OnceLock<Vec<Member>>,
line_types: Vec<Tok<String>>,
}
impl Drop for SystemInstData {
fn drop(&mut self) {
self.ext.0.reqnot.notify(api::SystemDrop(self.id));
if let Ok(mut g) = SYSTEM_INSTS.write() {
g.remove(&self.id);
}
}
fn drop(&mut self) {
self.ext.0.reqnot.notify(api::SystemDrop(self.id));
if let Ok(mut g) = SYSTEM_INSTS.write() {
g.remove(&self.id);
}
}
}
#[derive(Clone)]
pub struct System(Arc<SystemInstData>);
impl System {
pub fn id(&self) -> api::SysId { self.id }
fn resolve(id: api::SysId) -> Option<System> { SYSTEM_INSTS.read().unwrap().get(&id).cloned() }
fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.ext.0.reqnot }
fn give_expr(&self, ticket: api::ExprTicket, get_expr: impl FnOnce() -> Expr) -> api::ExprTicket {
match self.0.exprs.write().unwrap().entry(ticket) {
Entry::Occupied(mut oe) => {
oe.get_mut().0.fetch_add(1, Ordering::Relaxed);
},
Entry::Vacant(v) => {
v.insert((AtomicU32::new(1), get_expr()));
},
}
ticket
}
pub fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
self.reqnot().request(api::GetMember(self.0.id, id))
}
pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() }
pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) }
/// Have this system lex a part of the source. It is assumed that
/// [Self::can_lex] was called and returned true.
pub fn lex(
&self,
source: Tok<String>,
pos: u32,
mut r: impl FnMut(u32) -> Option<api::SubLexed> + Send,
) -> api::OrcResult<Option<api::LexedExpr>> {
// get unique lex ID
static LEX_ID: AtomicU64 = AtomicU64::new(1);
let id = api::ParsId(NonZero::new(LEX_ID.fetch_add(1, Ordering::Relaxed)).unwrap());
thread::scope(|s| {
// create and register channel
let (req_in, req_out) = sync_channel(0);
LEX_RECUR.lock().unwrap().insert(id, req_in); // LEX_RECUR released
// spawn recursion handler which will exit when the sender is collected
s.spawn(move || {
while let Ok(ReqPair(sublex, rep_in)) = req_out.recv() {
rep_in.send(r(sublex.pos)).unwrap()
}
});
// Pass control to extension
let ret =
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()
}) // exit recursion handler thread
}
pub fn can_parse(&self, line_type: Tok<String>) -> bool { self.line_types.contains(&line_type) }
pub fn line_types(&self) -> impl Iterator<Item = Tok<String>> + '_ {
self.line_types.iter().cloned()
}
pub fn parse(
&self,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> {
let line = line.iter().map(|t| t.to_api(&mut |n, _| match *n {})).collect_vec();
let comments = comments.iter().map(Comment::to_api).collect_vec();
let parsed =
(self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }))
.map_err(|e| OrcErrv::from_api(&e))?;
Ok(ttv_from_api(parsed, &mut ()))
}
pub fn request(&self, req: Vec<u8>) -> Vec<u8> {
self.reqnot().request(api::SysFwded(self.id(), req))
}
pub fn id(&self) -> api::SysId { self.id }
fn resolve(id: api::SysId) -> Option<System> { SYSTEM_INSTS.read().unwrap().get(&id).cloned() }
fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.ext.0.reqnot }
fn give_expr(&self, ticket: api::ExprTicket, get_expr: impl FnOnce() -> Expr) -> api::ExprTicket {
match self.0.exprs.write().unwrap().entry(ticket) {
Entry::Occupied(mut oe) => {
oe.get_mut().0.fetch_add(1, Ordering::Relaxed);
},
Entry::Vacant(v) => {
v.insert((AtomicU32::new(1), get_expr()));
},
}
ticket
}
pub fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
self.reqnot().request(api::GetMember(self.0.id, id))
}
pub fn has_lexer(&self) -> bool { !self.0.lex_filter.0.is_empty() }
pub fn can_lex(&self, c: char) -> bool { char_filter_match(&self.0.lex_filter, c) }
/// Have this system lex a part of the source. It is assumed that
/// [Self::can_lex] was called and returned true.
pub fn lex(
&self,
source: Tok<String>,
pos: u32,
mut r: impl FnMut(u32) -> Option<api::SubLexed> + Send,
) -> api::OrcResult<Option<api::LexedExpr>> {
// get unique lex ID
static LEX_ID: AtomicU64 = AtomicU64::new(1);
let id = api::ParsId(NonZero::new(LEX_ID.fetch_add(1, Ordering::Relaxed)).unwrap());
thread::scope(|s| {
// create and register channel
let (req_in, req_out) = sync_channel(0);
LEX_RECUR.lock().unwrap().insert(id, req_in); // LEX_RECUR released
// spawn recursion handler which will exit when the sender is collected
s.spawn(move || {
while let Ok(ReqPair(sublex, rep_in)) = req_out.recv() {
rep_in.send(r(sublex.pos)).unwrap()
}
});
// Pass control to extension
let ret =
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()
}) // exit recursion handler thread
}
pub fn can_parse(&self, line_type: Tok<String>) -> bool { self.line_types.contains(&line_type) }
pub fn line_types(&self) -> impl Iterator<Item = Tok<String>> + '_ {
self.line_types.iter().cloned()
}
pub fn parse(
&self,
line: Vec<ParsTokTree>,
exported: bool,
comments: Vec<Comment>,
) -> OrcRes<Vec<ParsTokTree>> {
let line = line.iter().map(|t| t.to_api(&mut |n, _| match *n {})).collect_vec();
let comments = comments.iter().map(Comment::to_api).collect_vec();
let parsed =
(self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }))
.map_err(|e| OrcErrv::from_api(&e))?;
Ok(ttv_from_api(parsed, &mut ()))
}
pub fn request(&self, req: Vec<u8>) -> Vec<u8> {
self.reqnot().request(api::SysFwded(self.id(), req))
}
}
impl fmt::Debug for System {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ctor = (self.0.ext.0.systems.iter().find(|c| c.decl.id == self.0.decl_id))
.expect("System instance with no associated constructor");
write!(f, "System({} @ {} #{}, ", ctor.decl.name, ctor.decl.priority, self.0.id.0)?;
match self.0.exprs.read() {
Err(_) => write!(f, "expressions unavailable"),
Ok(r) => {
let rc: u32 = r.values().map(|v| v.0.load(Ordering::Relaxed)).sum();
write!(f, "{rc} refs to {} exprs", r.len())
},
}
}
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let ctor = (self.0.ext.0.systems.iter().find(|c| c.decl.id == self.0.decl_id))
.expect("System instance with no associated constructor");
write!(f, "System({} @ {} #{}, ", ctor.decl.name, ctor.decl.priority, self.0.id.0)?;
match self.0.exprs.read() {
Err(_) => write!(f, "expressions unavailable"),
Ok(r) => {
let rc: u32 = r.values().map(|v| v.0.load(Ordering::Relaxed)).sum();
write!(f, "{rc} refs to {} exprs", r.len())
},
}
}
}
impl Deref for System {
type Target = SystemInstData;
fn deref(&self) -> &Self::Target { self.0.as_ref() }
type Target = SystemInstData;
fn deref(&self) -> &Self::Target { self.0.as_ref() }
}
#[derive(Debug, Clone)]
pub enum SysResolvErr {
Loop(Vec<String>),
Missing(String),
Loop(Vec<String>),
Missing(String),
}
pub fn init_systems(tgts: &[String], exts: &[Extension]) -> Result<Vec<System>, SysResolvErr> {
let mut to_load = HashMap::<&str, &SystemCtor>::new();
let mut to_find = tgts.iter().map(|s| s.as_str()).collect::<VecDeque<&str>>();
while let Some(target) = to_find.pop_front() {
if to_load.contains_key(target) {
continue;
}
let ctor = (exts.iter())
.flat_map(|e| e.systems().filter(|c| c.decl.name == target))
.max_by_key(|c| c.decl.priority)
.ok_or_else(|| SysResolvErr::Missing(target.to_string()))?;
to_load.insert(target, ctor);
to_find.extend(ctor.decl.depends.iter().map(|s| s.as_str()));
}
let mut to_load_ordered = Vec::new();
fn walk_deps<'a>(
graph: &mut HashMap<&str, &'a SystemCtor>,
list: &mut Vec<&'a SystemCtor>,
chain: Stackframe<&str>,
) -> Result<(), SysResolvErr> {
if let Some(ctor) = graph.remove(chain.item) {
// if the above is none, the system is already queued. Missing systems are
// detected above
for dep in ctor.decl.depends.iter() {
if Substack::Frame(chain).iter().any(|c| c == dep) {
let mut circle = vec![dep.to_string()];
circle.extend(Substack::Frame(chain).iter().map(|s| s.to_string()));
return Err(SysResolvErr::Loop(circle));
}
walk_deps(graph, list, Substack::Frame(chain).new_frame(dep))?
}
list.push(ctor);
}
Ok(())
}
for tgt in tgts {
walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?;
}
let mut systems = HashMap::<&str, System>::new();
for ctor in to_load_ordered.iter() {
let sys = ctor.run(ctor.depends().map(|n| &systems[n]));
systems.insert(ctor.name(), sys);
}
Ok(systems.into_values().collect_vec())
let mut to_load = HashMap::<&str, &SystemCtor>::new();
let mut to_find = tgts.iter().map(|s| s.as_str()).collect::<VecDeque<&str>>();
while let Some(target) = to_find.pop_front() {
if to_load.contains_key(target) {
continue;
}
let ctor = (exts.iter())
.flat_map(|e| e.systems().filter(|c| c.decl.name == target))
.max_by_key(|c| c.decl.priority)
.ok_or_else(|| SysResolvErr::Missing(target.to_string()))?;
to_load.insert(target, ctor);
to_find.extend(ctor.decl.depends.iter().map(|s| s.as_str()));
}
let mut to_load_ordered = Vec::new();
fn walk_deps<'a>(
graph: &mut HashMap<&str, &'a SystemCtor>,
list: &mut Vec<&'a SystemCtor>,
chain: Stackframe<&str>,
) -> Result<(), SysResolvErr> {
if let Some(ctor) = graph.remove(chain.item) {
// if the above is none, the system is already queued. Missing systems are
// detected above
for dep in ctor.decl.depends.iter() {
if Substack::Frame(chain).iter().any(|c| c == dep) {
let mut circle = vec![dep.to_string()];
circle.extend(Substack::Frame(chain).iter().map(|s| s.to_string()));
return Err(SysResolvErr::Loop(circle));
}
walk_deps(graph, list, Substack::Frame(chain).new_frame(dep))?
}
list.push(ctor);
}
Ok(())
}
for tgt in tgts {
walk_deps(&mut to_load, &mut to_load_ordered, Substack::Bottom.new_frame(tgt))?;
}
let mut systems = HashMap::<&str, System>::new();
for ctor in to_load_ordered.iter() {
let sys = ctor.run(ctor.depends().map(|n| &systems[n]));
systems.insert(ctor.name(), sys);
}
Ok(systems.into_values().collect_vec())
}

View File

@@ -2,208 +2,208 @@ use std::num::NonZeroU64;
use std::sync::Arc;
use hashbrown::HashMap;
use orchid_base::error::{mk_errv, OrcErrv, OrcRes};
use orchid_base::{intern, match_mapping};
use orchid_base::interner::{intern, Tok};
use orchid_base::error::{OrcErrv, OrcRes, mk_errv};
use orchid_base::interner::{Tok, intern};
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};
use orchid_base::tokens::PARENS;
use orchid_base::tree::Ph;
use orchid_base::{intern, match_mapping};
use crate::api;
use crate::extension::{AtomHand, System};
use crate::tree::{ParsTok, ParsTokTree};
pub struct LexCtx<'a> {
pub systems: &'a [System],
pub source: &'a Tok<String>,
pub tail: &'a str,
pub sub_trees: &'a mut HashMap<api::TreeTicket, ParsTokTree>,
pub systems: &'a [System],
pub source: &'a Tok<String>,
pub tail: &'a str,
pub sub_trees: &'a mut HashMap<api::TreeTicket, ParsTokTree>,
}
impl<'a> LexCtx<'a> {
pub fn push<'b>(&'b mut self, pos: u32) -> LexCtx<'b>
where 'a: 'b {
LexCtx {
source: self.source,
tail: &self.source[pos as usize..],
systems: self.systems,
sub_trees: &mut *self.sub_trees,
}
}
pub fn get_pos(&self) -> u32 { self.end_pos() - self.tail.len() as u32 }
pub fn end_pos(&self) -> u32 { self.source.len() as u32 }
pub fn set_pos(&mut self, pos: u32) { self.tail = &self.source[pos as usize..] }
pub fn push_pos(&mut self, delta: u32) { self.set_pos(self.get_pos() + delta) }
pub fn set_tail(&mut self, tail: &'a str) { self.tail = tail }
pub fn strip_prefix(&mut self, tgt: &str) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
self.tail = src;
return true;
}
false
}
pub fn add_subtree(&mut self, subtree: ParsTokTree) -> api::TreeTicket {
let next_idx = api::TreeTicket(NonZeroU64::new(self.sub_trees.len() as u64 + 1).unwrap());
self.sub_trees.insert(next_idx, subtree);
next_idx
}
pub fn rm_subtree(&mut self, ticket: api::TreeTicket) -> ParsTokTree {
self.sub_trees.remove(&ticket).unwrap()
}
pub fn strip_char(&mut self, tgt: char) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
self.tail = src;
return true;
}
false
}
pub fn trim(&mut self, filter: impl Fn(char) -> bool) {
self.tail = self.tail.trim_start_matches(filter);
}
pub fn trim_ws(&mut self) { self.trim(|c| c.is_whitespace() && !"\r\n".contains(c)) }
pub fn get_start_matches(&mut self, filter: impl Fn(char) -> bool) -> &'a str {
let rest = self.tail.trim_start_matches(filter);
let matches = &self.tail[..self.tail.len() - rest.len()];
self.tail = rest;
matches
}
pub fn push<'b>(&'b mut self, pos: u32) -> LexCtx<'b>
where 'a: 'b {
LexCtx {
source: self.source,
tail: &self.source[pos as usize..],
systems: self.systems,
sub_trees: &mut *self.sub_trees,
}
}
pub fn get_pos(&self) -> u32 { self.end_pos() - self.tail.len() as u32 }
pub fn end_pos(&self) -> u32 { self.source.len() as u32 }
pub fn set_pos(&mut self, pos: u32) { self.tail = &self.source[pos as usize..] }
pub fn push_pos(&mut self, delta: u32) { self.set_pos(self.get_pos() + delta) }
pub fn set_tail(&mut self, tail: &'a str) { self.tail = tail }
pub fn strip_prefix(&mut self, tgt: &str) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
self.tail = src;
return true;
}
false
}
pub fn add_subtree(&mut self, subtree: ParsTokTree) -> api::TreeTicket {
let next_idx = api::TreeTicket(NonZeroU64::new(self.sub_trees.len() as u64 + 1).unwrap());
self.sub_trees.insert(next_idx, subtree);
next_idx
}
pub fn rm_subtree(&mut self, ticket: api::TreeTicket) -> ParsTokTree {
self.sub_trees.remove(&ticket).unwrap()
}
pub fn strip_char(&mut self, tgt: char) -> bool {
if let Some(src) = self.tail.strip_prefix(tgt) {
self.tail = src;
return true;
}
false
}
pub fn trim(&mut self, filter: impl Fn(char) -> bool) {
self.tail = self.tail.trim_start_matches(filter);
}
pub fn trim_ws(&mut self) { self.trim(|c| c.is_whitespace() && !"\r\n".contains(c)) }
pub fn get_start_matches(&mut self, filter: impl Fn(char) -> bool) -> &'a str {
let rest = self.tail.trim_start_matches(filter);
let matches = &self.tail[..self.tail.len() - rest.len()];
self.tail = rest;
matches
}
}
pub fn lex_once(ctx: &mut LexCtx) -> OrcRes<ParsTokTree> {
let start = ctx.get_pos();
assert!(
!ctx.tail.is_empty() && !ctx.tail.starts_with(unrep_space),
"Lexing empty string or whitespace to token!\n\
let start = ctx.get_pos();
assert!(
!ctx.tail.is_empty() && !ctx.tail.starts_with(unrep_space),
"Lexing empty string or whitespace to token!\n\
Invocations of lex_tok should check for empty string"
);
let tok = if ctx.strip_prefix("\r\n") || ctx.strip_prefix("\r") || ctx.strip_prefix("\n") {
ParsTok::BR
} else if ctx.strip_prefix("::") {
ParsTok::NS
} else if ctx.strip_prefix("--[") {
let (cmt, tail) = ctx.tail.split_once("]--").ok_or_else(|| {
mk_errv(intern!(str: "Unterminated block comment"), "This block comment has no ending ]--", [
Pos::Range(start..start + 3).into(),
])
})?;
ctx.set_tail(tail);
ParsTok::Comment(Arc::new(cmt.to_string()))
} else if let Some(tail) = ctx.tail.strip_prefix("--").filter(|t| !t.starts_with(op_char)) {
let end = tail.find(['\n', '\r']).map_or(tail.len(), |n| n - 1);
ctx.push_pos(end as u32);
ParsTok::Comment(Arc::new(tail[2..end].to_string()))
} else if ctx.strip_char('\\') {
let mut arg = Vec::new();
ctx.trim_ws();
while !ctx.strip_char('.') {
if ctx.tail.is_empty() {
return Err(mk_errv(
intern!(str: "Unclosed lambda"),
"Lambdae started with \\ should separate arguments from body with .",
[Pos::Range(start..start + 1).into()],
));
}
arg.push(lex_once(ctx)?);
ctx.trim_ws();
}
ParsTok::LambdaHead(arg)
} else if let Some((lp, rp, paren)) = PARENS.iter().find(|(lp, ..)| ctx.strip_char(*lp)) {
let mut body = Vec::new();
ctx.trim_ws();
while !ctx.strip_char(*rp) {
if ctx.tail.is_empty() {
return Err(mk_errv(
intern!(str: "unclosed paren"),
format!("this {lp} has no matching {rp}"),
[Pos::Range(start..start + 1).into()],
));
}
body.push(lex_once(ctx)?);
ctx.trim_ws();
}
ParsTok::S(*paren, body)
} else if ctx.strip_prefix("macro") &&
!ctx.tail.chars().next().is_some_and(|x| x.is_ascii_alphabetic())
{
ctx.strip_prefix("macro");
if ctx.strip_char('(') {
let pos = ctx.get_pos();
let numstr = ctx.get_start_matches(|x| x != ')').trim();
let num = parse_num(numstr).map_err(|e| num_to_err(e, pos))?;
ParsTok::Macro(Some(num.to_f64()))
} else {
ParsTok::Macro(None)
}
} else {
for sys in ctx.systems {
let mut errors = Vec::new();
if ctx.tail.starts_with(|c| sys.can_lex(c)) {
let lx =
sys.lex(ctx.source.clone(), ctx.get_pos(), |pos| match lex_once(&mut ctx.push(pos)) {
Ok(t) => Some(api::SubLexed { pos, ticket: ctx.add_subtree(t) }),
Err(e) => {
errors.push(e);
None
},
});
match lx {
Err(e) => return Err(errors.into_iter().fold(OrcErrv::from_api(&e), |a, b| a + b)),
Ok(Some(lexed)) => return Ok(tt_to_owned(&lexed.expr, &mut ctx.push(lexed.pos))),
Ok(None) => match errors.into_iter().reduce(|a, b| a + b) {
Some(errors) => return Err(errors),
None => continue,
},
}
}
}
if ctx.tail.starts_with(name_start) {
ParsTok::Name(intern(ctx.get_start_matches(name_char)))
} else if ctx.tail.starts_with(op_char) {
ParsTok::Name(intern(ctx.get_start_matches(op_char)))
} else {
return Err(mk_errv(
intern!(str: "Unrecognized character"),
"The following syntax is meaningless.",
[Pos::Range(start..start + 1).into()],
));
}
};
Ok(ParsTokTree { tok, range: start..ctx.get_pos() })
);
let tok = if ctx.strip_prefix("\r\n") || ctx.strip_prefix("\r") || ctx.strip_prefix("\n") {
ParsTok::BR
} else if ctx.strip_prefix("::") {
ParsTok::NS
} else if ctx.strip_prefix("--[") {
let (cmt, tail) = ctx.tail.split_once("]--").ok_or_else(|| {
mk_errv(intern!(str: "Unterminated block comment"), "This block comment has no ending ]--", [
Pos::Range(start..start + 3).into(),
])
})?;
ctx.set_tail(tail);
ParsTok::Comment(Arc::new(cmt.to_string()))
} else if let Some(tail) = ctx.tail.strip_prefix("--").filter(|t| !t.starts_with(op_char)) {
let end = tail.find(['\n', '\r']).map_or(tail.len(), |n| n - 1);
ctx.push_pos(end as u32);
ParsTok::Comment(Arc::new(tail[2..end].to_string()))
} else if ctx.strip_char('\\') {
let mut arg = Vec::new();
ctx.trim_ws();
while !ctx.strip_char('.') {
if ctx.tail.is_empty() {
return Err(mk_errv(
intern!(str: "Unclosed lambda"),
"Lambdae started with \\ should separate arguments from body with .",
[Pos::Range(start..start + 1).into()],
));
}
arg.push(lex_once(ctx)?);
ctx.trim_ws();
}
ParsTok::LambdaHead(arg)
} else if let Some((lp, rp, paren)) = PARENS.iter().find(|(lp, ..)| ctx.strip_char(*lp)) {
let mut body = Vec::new();
ctx.trim_ws();
while !ctx.strip_char(*rp) {
if ctx.tail.is_empty() {
return Err(mk_errv(
intern!(str: "unclosed paren"),
format!("this {lp} has no matching {rp}"),
[Pos::Range(start..start + 1).into()],
));
}
body.push(lex_once(ctx)?);
ctx.trim_ws();
}
ParsTok::S(*paren, body)
} else if ctx.strip_prefix("macro")
&& !ctx.tail.chars().next().is_some_and(|x| x.is_ascii_alphabetic())
{
ctx.strip_prefix("macro");
if ctx.strip_char('(') {
let pos = ctx.get_pos();
let numstr = ctx.get_start_matches(|x| x != ')').trim();
let num = parse_num(numstr).map_err(|e| num_to_err(e, pos))?;
ParsTok::Macro(Some(num.to_f64()))
} else {
ParsTok::Macro(None)
}
} else {
for sys in ctx.systems {
let mut errors = Vec::new();
if ctx.tail.starts_with(|c| sys.can_lex(c)) {
let lx =
sys.lex(ctx.source.clone(), ctx.get_pos(), |pos| match lex_once(&mut ctx.push(pos)) {
Ok(t) => Some(api::SubLexed { pos, ticket: ctx.add_subtree(t) }),
Err(e) => {
errors.push(e);
None
},
});
match lx {
Err(e) => return Err(errors.into_iter().fold(OrcErrv::from_api(&e), |a, b| a + b)),
Ok(Some(lexed)) => return Ok(tt_to_owned(&lexed.expr, &mut ctx.push(lexed.pos))),
Ok(None) => match errors.into_iter().reduce(|a, b| a + b) {
Some(errors) => return Err(errors),
None => continue,
},
}
}
}
if ctx.tail.starts_with(name_start) {
ParsTok::Name(intern(ctx.get_start_matches(name_char)))
} else if ctx.tail.starts_with(op_char) {
ParsTok::Name(intern(ctx.get_start_matches(op_char)))
} else {
return Err(mk_errv(
intern!(str: "Unrecognized character"),
"The following syntax is meaningless.",
[Pos::Range(start..start + 1).into()],
));
}
};
Ok(ParsTokTree { tok, range: start..ctx.get_pos() })
}
fn tt_to_owned(api: &api::TokenTree, ctx: &mut LexCtx<'_>) -> ParsTokTree {
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),
});
ParsTokTree { range: api.range.clone(), tok }
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),
});
ParsTokTree { range: api.range.clone(), tok }
}
fn ttv_to_owned<'a>(
api: impl IntoIterator<Item = &'a api::TokenTree>,
ctx: &mut LexCtx<'_>
api: impl IntoIterator<Item = &'a api::TokenTree>,
ctx: &mut LexCtx<'_>,
) -> Vec<ParsTokTree> {
api.into_iter().map(|t| tt_to_owned(t, ctx)).collect()
api.into_iter().map(|t| tt_to_owned(t, ctx)).collect()
}
pub fn lex(text: Tok<String>, systems: &[System]) -> OrcRes<Vec<ParsTokTree>> {
let mut sub_trees = HashMap::new();
let mut ctx = LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems };
let mut tokv = Vec::new();
ctx.trim(unrep_space);
while !ctx.tail.is_empty() {
tokv.push(lex_once(&mut ctx)?);
ctx.trim(unrep_space);
}
Ok(tokv)
let mut sub_trees = HashMap::new();
let mut ctx = LexCtx { source: &text, sub_trees: &mut sub_trees, tail: &text[..], systems };
let mut tokv = Vec::new();
ctx.trim(unrep_space);
while !ctx.tail.is_empty() {
tokv.push(lex_once(&mut ctx)?);
ctx.trim(unrep_space);
}
Ok(tokv)
}

View File

@@ -4,8 +4,8 @@ pub mod child;
pub mod expr;
pub mod extension;
pub mod lex;
pub mod macros;
pub mod parse;
pub mod rule;
pub mod subprocess;
pub mod tree;
pub mod macros;
pub mod rule;

View File

@@ -17,158 +17,158 @@ pub type MacTok = MTok<'static, AtomHand>;
pub type MacTree = MTree<'static, AtomHand>;
trait_set! {
trait MacroCB = Fn(Vec<MacTree>) -> Option<Vec<MacTree>> + Send + Sync;
trait MacroCB = Fn(Vec<MacTree>) -> Option<Vec<MacTree>> + Send + Sync;
}
lazy_static! {
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();
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: api::ParsId, input: Vec<MacTree>) -> Option<Vec<MacTree>> {
(RECURSION.read().unwrap()[&run_id])(input)
(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)
})
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())))
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::Done(_) => panic!("Created and removed by matcher"),
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()
})
}
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::Done(_) => panic!("Created and removed by matcher"),
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 Macro<Matcher> {
deps: HashSet<Sym>,
cases: Vec<(Matcher, Code)>,
deps: HashSet<Sym>,
cases: Vec<(Matcher, Code)>,
}
pub struct MacroRepo {
named: HashMap<Sym, Vec<Macro<NamedMatcher>>>,
prio: Vec<Macro<PriodMatcher>>,
named: HashMap<Sym, Vec<Macro<NamedMatcher>>>,
prio: Vec<Macro<PriodMatcher>>,
}
impl MacroRepo {
/// TODO: the recursion inside this function needs to be moved into Orchid.
/// See the markdown note
pub fn process_exprv(&self, target: &[MacTree]) -> Option<Vec<MacTree>> {
let mut workcp = target.to_vec();
let mut lexicon;
/// TODO: the recursion inside this function needs to be moved into Orchid.
/// See the markdown note
pub fn process_exprv(&self, target: &[MacTree]) -> Option<Vec<MacTree>> {
let mut workcp = target.to_vec();
let mut lexicon;
'try_named: loop {
lexicon = HashSet::new();
target.iter().for_each(|tgt| fill_lexicon(tgt, &mut lexicon));
'try_named: loop {
lexicon = HashSet::new();
target.iter().for_each(|tgt| fill_lexicon(tgt, &mut lexicon));
for (i, tree) in workcp.iter().enumerate() {
let MacTok::Name(name) = &*tree.tok else { continue };
let matches = (self.named.get(name).into_iter().flatten())
.filter(|m| m.deps.is_subset(&lexicon))
.filter_map(|mac| {
mac.cases.iter().find_map(|cas| cas.0.apply(&workcp[i..], |_| false).map(|s| (cas, s)))
})
.collect_vec();
assert!(
matches.len() < 2,
"Multiple conflicting matches on {:?}: {:?}",
&workcp[i..],
matches
);
let Some((case, (state, tail))) = matches.into_iter().next() else { continue };
let inj = (run_body(&case.1, state).into_iter())
.map(|MacTree { pos, tok }| MacTree { pos, tok: Arc::new(MacTok::Done(tok)) });
workcp.splice(i..(workcp.len() - tail.len()), inj);
continue 'try_named;
}
break;
}
for (i, tree) in workcp.iter().enumerate() {
let MacTok::Name(name) = &*tree.tok else { continue };
let matches = (self.named.get(name).into_iter().flatten())
.filter(|m| m.deps.is_subset(&lexicon))
.filter_map(|mac| {
mac.cases.iter().find_map(|cas| cas.0.apply(&workcp[i..], |_| false).map(|s| (cas, s)))
})
.collect_vec();
assert!(
matches.len() < 2,
"Multiple conflicting matches on {:?}: {:?}",
&workcp[i..],
matches
);
let Some((case, (state, tail))) = matches.into_iter().next() else { continue };
let inj = (run_body(&case.1, state).into_iter())
.map(|MacTree { pos, tok }| MacTree { pos, tok: Arc::new(MacTok::Done(tok)) });
workcp.splice(i..(workcp.len() - tail.len()), inj);
continue 'try_named;
}
break;
}
if let Some(((_, body), state)) = (self.prio.iter())
.filter(|mac| mac.deps.is_subset(&lexicon))
.flat_map(|mac| &mac.cases)
.find_map(|case| case.0.apply(&workcp, |_| false).map(|state| (case, state)))
{
return Some(run_body(body, state));
}
if let Some(((_, body), state)) = (self.prio.iter())
.filter(|mac| mac.deps.is_subset(&lexicon))
.flat_map(|mac| &mac.cases)
.find_map(|case| case.0.apply(&workcp, |_| false).map(|state| (case, state)))
{
return Some(run_body(body, state));
}
let results = (workcp.into_iter())
.map(|mt| match &*mt.tok {
MTok::S(p, body) => self.process_exprv(body).map(|body| MTok::S(*p, body).at(mt.pos)),
MTok::Lambda(arg, body) => match (self.process_exprv(arg), self.process_exprv(body)) {
(Some(arg), Some(body)) => Some(MTok::Lambda(arg, body).at(mt.pos)),
(Some(arg), None) => Some(MTok::Lambda(arg, body.to_vec()).at(mt.pos)),
(None, Some(body)) => Some(MTok::Lambda(arg.to_vec(), body).at(mt.pos)),
(None, None) => None,
},
_ => None,
})
.collect_vec();
results.iter().any(Option::is_some).then(|| {
(results.into_iter().zip(target))
.map(|(opt, fb)| opt.unwrap_or_else(|| fb.clone()))
.collect_vec()
})
}
let results = (workcp.into_iter())
.map(|mt| match &*mt.tok {
MTok::S(p, body) => self.process_exprv(body).map(|body| MTok::S(*p, body).at(mt.pos)),
MTok::Lambda(arg, body) => match (self.process_exprv(arg), self.process_exprv(body)) {
(Some(arg), Some(body)) => Some(MTok::Lambda(arg, body).at(mt.pos)),
(Some(arg), None) => Some(MTok::Lambda(arg, body.to_vec()).at(mt.pos)),
(None, Some(body)) => Some(MTok::Lambda(arg.to_vec(), body).at(mt.pos)),
(None, None) => None,
},
_ => None,
})
.collect_vec();
results.iter().any(Option::is_some).then(|| {
(results.into_iter().zip(target))
.map(|(opt, fb)| opt.unwrap_or_else(|| fb.clone()))
.collect_vec()
})
}
}
fn fill_lexicon(tgt: &MacTree, lexicon: &mut HashSet<Sym>) {
match &*tgt.tok {
MTok::Name(n) => {
lexicon.insert(n.clone());
},
MTok::Lambda(arg, body) => {
arg.iter().for_each(|t| fill_lexicon(t, lexicon));
body.iter().for_each(|t| fill_lexicon(t, lexicon))
},
MTok::S(_, body) => body.iter().for_each(|t| fill_lexicon(t, lexicon)),
_ => (),
}
match &*tgt.tok {
MTok::Name(n) => {
lexicon.insert(n.clone());
},
MTok::Lambda(arg, body) => {
arg.iter().for_each(|t| fill_lexicon(t, lexicon));
body.iter().for_each(|t| fill_lexicon(t, lexicon))
},
MTok::S(_, body) => body.iter().for_each(|t| fill_lexicon(t, lexicon)),
_ => (),
}
}
fn run_body(body: &Code, mut state: MatchState<'_>) -> Vec<MacTree> {
let inject: Vec<MacTree> = todo!("Call the interpreter with bindings");
inject
.into_iter()
.map(|MTree { pos, tok }| MTree { pos, tok: Arc::new(MTok::Done(tok)) })
.collect_vec()
let inject: Vec<MacTree> = todo!("Call the interpreter with bindings");
inject
.into_iter()
.map(|MTree { pos, tok }| MTree { pos, tok: Arc::new(MTok::Done(tok)) })
.collect_vec()
}

View File

@@ -3,279 +3,295 @@ use std::{iter, thread};
use itertools::Itertools;
use never::Never;
use orchid_base::error::{mk_err, mk_errv, OrcErrv, OrcRes, Reporter};
use orchid_base::error::{OrcErrv, OrcRes, Reporter, mk_err, mk_errv};
use orchid_base::intern;
use orchid_base::interner::Tok;
use orchid_base::location::Pos;
use orchid_base::macros::{MTok, MTree};
use orchid_base::name::Sym;
use orchid_base::parse::{
expect_end, line_items, parse_multiname, strip_fluff, try_pop_no_fluff, Comment, Import,
Parsed, Snippet,
Comment, Import, Parsed, Snippet, expect_end, line_items, parse_multiname, strip_fluff,
try_pop_no_fluff,
};
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};
use crate::tree::{
Code, CodeLocator, Item, ItemKind, Member, MemberKind, Module, ParsTokTree, Rule, RuleKind,
};
type ParsSnippet<'a> = Snippet<'a, 'static, AtomHand, Never>;
pub trait ParseCtx: Send + Sync {
fn systems(&self) -> impl Iterator<Item = &System>;
fn reporter(&self) -> &impl Reporter;
fn systems(&self) -> impl Iterator<Item = &System>;
fn reporter(&self) -> &impl Reporter;
}
pub fn parse_items(
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
items: ParsSnippet
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
items: ParsSnippet,
) -> OrcRes<Vec<Item>> {
let lines = line_items(items);
let mut ok = iter::from_fn(|| None).take(lines.len()).collect_vec();
thread::scope(|s| {
let mut threads = Vec::new();
for (slot, Parsed { output: cmts, tail }) in ok.iter_mut().zip(lines.into_iter()) {
let path = &path;
threads.push(s.spawn(move || {
*slot = Some(parse_item(ctx, path.clone(), cmts, tail)?);
Ok::<(), OrcErrv>(())
}))
}
for t in threads {
t.join().unwrap().err().into_iter().flatten().for_each(|e| ctx.reporter().report(e))
}
});
Ok(ok.into_iter().flatten().flatten().collect_vec())
let lines = line_items(items);
let mut ok = iter::from_fn(|| None).take(lines.len()).collect_vec();
thread::scope(|s| {
let mut threads = Vec::new();
for (slot, Parsed { output: cmts, tail }) in ok.iter_mut().zip(lines.into_iter()) {
let path = &path;
threads.push(s.spawn(move || {
*slot = Some(parse_item(ctx, path.clone(), cmts, tail)?);
Ok::<(), OrcErrv>(())
}))
}
for t in threads {
t.join().unwrap().err().into_iter().flatten().for_each(|e| ctx.reporter().report(e))
}
});
Ok(ok.into_iter().flatten().flatten().collect_vec())
}
pub fn parse_item(
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
comments: Vec<Comment>,
item: ParsSnippet,
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
comments: Vec<Comment>,
item: ParsSnippet,
) -> OrcRes<Vec<Item>> {
match item.pop_front() {
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
n if *n == intern!(str: "export") => match try_pop_no_fluff(postdisc)? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_exportable_item(ctx, path, comments, true, n.clone(), tail),
Parsed { output: TokTree { tok: Token::NS, .. }, tail } => {
let Parsed { output: exports, tail } = parse_multiname(ctx.reporter(), tail)?;
let mut ok = Vec::new();
exports.into_iter().for_each(|(e, pos)| match (&e.path.as_slice(), e.name) {
([], Some(n)) =>
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
(_, Some(_)) => ctx.reporter().report(mk_err(
intern!(str: "Compound export"),
"Cannot export compound names (names containing the :: separator)",
[pos.into()],
)),
(_, None) => ctx.reporter().report(mk_err(
intern!(str: "Wildcard export"),
"Exports cannot contain the globstar *",
[pos.into()],
)),
});
expect_end(tail)?;
Ok(ok)
},
Parsed { output, .. } => Err(mk_errv(
intern!(str: "Malformed export"),
"`export` can either prefix other lines or list names inside ::( ) or ::[ ]",
[Pos::Range(output.range.clone()).into()],
)),
},
n if *n == intern!(str: "import") => parse_import(ctx, postdisc).map(|v| {
Vec::from_iter(v.into_iter().map(|(t, pos)| Item {
comments: comments.clone(),
pos,
kind: ItemKind::Import(t),
}))
}),
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc),
},
Some(_) =>
Err(mk_errv(intern!(str: "Expected a line type"), "All lines must begin with a keyword", [
Pos::Range(item.pos()).into(),
])),
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
}
match item.pop_front() {
Some((TokTree { tok: Token::Name(n), .. }, postdisc)) => match n {
n if *n == intern!(str: "export") => match try_pop_no_fluff(postdisc)? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } =>
parse_exportable_item(ctx, path, comments, true, n.clone(), tail),
Parsed { output: TokTree { tok: Token::NS, .. }, tail } => {
let Parsed { output: exports, tail } = parse_multiname(ctx.reporter(), tail)?;
let mut ok = Vec::new();
exports.into_iter().for_each(|(e, pos)| match (&e.path.as_slice(), e.name) {
([], Some(n)) =>
ok.push(Item { comments: comments.clone(), pos, kind: ItemKind::Export(n) }),
(_, Some(_)) => ctx.reporter().report(mk_err(
intern!(str: "Compound export"),
"Cannot export compound names (names containing the :: separator)",
[pos.into()],
)),
(_, None) => ctx.reporter().report(mk_err(
intern!(str: "Wildcard export"),
"Exports cannot contain the globstar *",
[pos.into()],
)),
});
expect_end(tail)?;
Ok(ok)
},
Parsed { output, .. } => Err(mk_errv(
intern!(str: "Malformed export"),
"`export` can either prefix other lines or list names inside ::( ) or ::[ ]",
[Pos::Range(output.range.clone()).into()],
)),
},
n if *n == intern!(str: "import") => parse_import(ctx, postdisc).map(|v| {
Vec::from_iter(v.into_iter().map(|(t, pos)| Item {
comments: comments.clone(),
pos,
kind: ItemKind::Import(t),
}))
}),
n => parse_exportable_item(ctx, path, comments, false, n.clone(), postdisc),
},
Some(_) =>
Err(mk_errv(intern!(str: "Expected a line type"), "All lines must begin with a keyword", [
Pos::Range(item.pos()).into(),
])),
None => unreachable!("These lines are filtered and aggregated in earlier stages"),
}
}
pub fn parse_import(ctx: &impl ParseCtx, tail: ParsSnippet) -> OrcRes<Vec<(Import, Pos)>> {
let Parsed { output: imports, tail } = parse_multiname(ctx.reporter(), tail)?;
expect_end(tail)?;
Ok(imports)
let Parsed { output: imports, tail } = parse_multiname(ctx.reporter(), tail)?;
expect_end(tail)?;
Ok(imports)
}
pub fn parse_exportable_item(
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
comments: Vec<Comment>,
exported: bool,
discr: Tok<String>,
tail: ParsSnippet,
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
comments: Vec<Comment>,
exported: bool,
discr: Tok<String>,
tail: ParsSnippet,
) -> OrcRes<Vec<Item>> {
let kind = if discr == intern!(str: "mod") {
let (name, body) = parse_module(ctx, path, tail)?;
ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
} else if discr == intern!(str: "const") {
let (name, val) = parse_const(tail)?;
let locator = CodeLocator::to_const(path.push(name.clone()).unreverse());
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())) {
let line = sys.parse(tail.to_vec(), exported, comments)?;
return parse_items(ctx, path, Snippet::new(tail.prev(), &line));
} else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv(
intern!(str: "Unrecognized line type"),
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
[Pos::Range(tail.prev().range.clone()).into()],
));
};
Ok(vec![Item { comments, pos: Pos::Range(tail.pos()), kind }])
let kind = if discr == intern!(str: "mod") {
let (name, body) = parse_module(ctx, path, tail)?;
ItemKind::Member(Member::new(name, MemberKind::Mod(body)))
} else if discr == intern!(str: "const") {
let (name, val) = parse_const(tail)?;
let locator = CodeLocator::to_const(path.push(name.clone()).unreverse());
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())) {
let line = sys.parse(tail.to_vec(), exported, comments)?;
return parse_items(ctx, path, Snippet::new(tail.prev(), &line));
} else {
let ext_lines = ctx.systems().flat_map(System::line_types).join(", ");
return Err(mk_errv(
intern!(str: "Unrecognized line type"),
format!("Line types are: const, mod, macro, grammar, {ext_lines}"),
[Pos::Range(tail.prev().range.clone()).into()],
));
};
Ok(vec![Item { comments, pos: Pos::Range(tail.pos()), kind }])
}
pub fn parse_module(
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
tail: ParsSnippet
ctx: &impl ParseCtx,
path: Substack<Tok<String>>,
tail: ParsSnippet,
) -> OrcRes<(Tok<String>, Module)> {
let (name, tail) = match try_pop_no_fluff(tail)? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
Parsed { output, .. } =>
return Err(mk_errv(
intern!(str: "Missing module name"),
format!("A name was expected, {output} was found"),
[Pos::Range(output.range.clone()).into()],
)),
};
let Parsed { output, tail: surplus } = try_pop_no_fluff(tail)?;
expect_end(surplus)?;
let body = output.as_s(Paren::Round).ok_or_else(|| mk_errv(
intern!(str: "Expected module body"),
format!("A ( block ) was expected, {output} was found"),
[Pos::Range(output.range.clone()).into()],
))?;
let path = path.push(name.clone());
Ok((name, Module::new(parse_items(ctx, path, body)?)))
let (name, tail) = match try_pop_no_fluff(tail)? {
Parsed { output: TokTree { tok: Token::Name(n), .. }, tail } => (n.clone(), tail),
Parsed { output, .. } => {
return Err(mk_errv(
intern!(str: "Missing module name"),
format!("A name was expected, {output} was found"),
[Pos::Range(output.range.clone()).into()],
));
},
};
let Parsed { output, tail: surplus } = try_pop_no_fluff(tail)?;
expect_end(surplus)?;
let body = output.as_s(Paren::Round).ok_or_else(|| {
mk_errv(
intern!(str: "Expected module body"),
format!("A ( block ) was expected, {output} was found"),
[Pos::Range(output.range.clone()).into()],
)
})?;
let path = path.push(name.clone());
Ok((name, Module::new(parse_items(ctx, path, body)?)))
}
pub fn parse_const(tail: ParsSnippet) -> OrcRes<(Tok<String>, Vec<ParsTokTree>)> {
let Parsed { output, tail } = try_pop_no_fluff(tail)?;
let name = output.as_name().ok_or_else(|| mk_errv(
intern!(str: "Missing module name"),
format!("A name was expected, {output} was found"),
[Pos::Range(output.range.clone()).into()],
))?;
let Parsed { output, tail } = try_pop_no_fluff(tail)?;
if !output.is_kw(intern!(str: "=")) {
return Err(mk_errv(
intern!(str: "Missing walrus := separator"),
format!("Expected operator := , found {output}"),
[Pos::Range(output.range.clone()).into()],
))
}
try_pop_no_fluff(tail)?;
Ok((name, tail.iter().flat_map(strip_fluff).collect_vec()))
let Parsed { output, tail } = try_pop_no_fluff(tail)?;
let name = output.as_name().ok_or_else(|| {
mk_errv(
intern!(str: "Missing module name"),
format!("A name was expected, {output} was found"),
[Pos::Range(output.range.clone()).into()],
)
})?;
let Parsed { output, tail } = try_pop_no_fluff(tail)?;
if !output.is_kw(intern!(str: "=")) {
return Err(mk_errv(
intern!(str: "Missing walrus := separator"),
format!("Expected operator := , found {output}"),
[Pos::Range(output.range.clone()).into()],
));
}
try_pop_no_fluff(tail)?;
Ok((name, tail.iter().flat_map(strip_fluff).collect_vec()))
}
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 {
Token::S(p, b) => (
ttree.range.clone(),
MTok::S(*p, parse_mtree(Snippet::new(ttree, b))?),
tail,
),
Token::Name(tok) => {
let mut segments = vec![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)?;
segments.push(output.as_name().ok_or_else(|| mk_errv(
intern!(str: "Namespaced name interrupted"),
"In expression context, :: must always be followed by a name.\n\
::() is permitted only in import and export items",
[Pos::Range(output.range.clone()).into()]
))?);
snip = tail;
end = output.range.end;
}
(ttree.range.start..end, MTok::Name(Sym::new(segments).unwrap()), snip)
},
Token::NS => return Err(mk_errv(
intern!(str: "Unexpected :: in macro pattern"),
":: can only follow a name outside export statements",
[Pos::Range(ttree.range.clone()).into()]
)),
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
Token::Atom(_) | Token::Macro(_) => return Err(mk_errv(
intern!(str: "Unsupported token in macro patterns"),
format!("Macro patterns can only contain names, braces, and lambda, not {ttree}."),
[Pos::Range(ttree.range.clone()).into()]
)),
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))?, 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: Arc::new(tok) });
snip = tail;
}
Ok(mtreev)
let mut mtreev = Vec::new();
while let Some((ttree, tail)) = snip.pop_front() {
let (range, tok, tail) = match &ttree.tok {
Token::S(p, b) =>
(ttree.range.clone(), MTok::S(*p, parse_mtree(Snippet::new(ttree, b))?), tail),
Token::Name(tok) => {
let mut segments = vec![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)?;
segments.push(output.as_name().ok_or_else(|| {
mk_errv(
intern!(str: "Namespaced name interrupted"),
"In expression context, :: must always be followed by a name.\n\
::() is permitted only in import and export items",
[Pos::Range(output.range.clone()).into()],
)
})?);
snip = tail;
end = output.range.end;
}
(ttree.range.start..end, MTok::Name(Sym::new(segments).unwrap()), snip)
},
Token::NS => {
return Err(mk_errv(
intern!(str: "Unexpected :: in macro pattern"),
":: can only follow a name outside export statements",
[Pos::Range(ttree.range.clone()).into()],
));
},
Token::Ph(ph) => (ttree.range.clone(), MTok::Ph(ph.clone()), tail),
Token::Atom(_) | Token::Macro(_) => {
return Err(mk_errv(
intern!(str: "Unsupported token in macro patterns"),
format!("Macro patterns can only contain names, braces, and lambda, not {ttree}."),
[Pos::Range(ttree.range.clone()).into()],
));
},
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))?, 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: Arc::new(tok) });
snip = tail;
}
Ok(mtreev)
}
pub fn parse_macro(tail: ParsSnippet, macro_i: u16, path: Substack<Tok<String>>) -> OrcRes<Vec<Rule>> {
let (surplus, prev, block) = match try_pop_no_fluff(tail)? {
Parsed { tail, output: o@TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
Parsed { output, .. } => return Err(mk_errv(
intern!(str: "m"),
"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() {
let Parsed { tail, output } = try_pop_no_fluff(item.tail)?;
if !output.is_kw(intern!(str: "rule")) {
errors.extend(mk_errv(
intern!(str: "non-rule in macro"),
format!("Expected `rule`, got {output}"),
[Pos::Range(output.range.clone()).into()]
));
continue
};
let (pat, body) = match tail.split_once(|t| t.is_kw(intern!(str: "=>"))) {
Some((a, b)) => (a, b),
None => {
errors.extend(mk_errv(
intern!(str: "no => in macro rule"),
"The pattern and body of a rule must be separated by a =>",
[Pos::Range(tail.pos()).into()],
));
continue
}
};
rules.push(Rule {
comments: item.output,
pos: Pos::Range(tail.pos()),
pattern: parse_mtree(pat)?,
kind: RuleKind::Native(Code::from_code(
CodeLocator::to_rule(path.unreverse(), macro_i, i as u16),
body.to_vec(),
))
})
}
if let Ok(e) = OrcErrv::new(errors) { Err(e) } else { Ok(rules) }
pub fn parse_macro(
tail: ParsSnippet,
macro_i: u16,
path: Substack<Tok<String>>,
) -> OrcRes<Vec<Rule>> {
let (surplus, prev, block) = match try_pop_no_fluff(tail)? {
Parsed { tail, output: o @ TokTree { tok: Token::S(Paren::Round, b), .. } } => (tail, o, b),
Parsed { output, .. } => {
return Err(mk_errv(
intern!(str: "m"),
"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() {
let Parsed { tail, output } = try_pop_no_fluff(item.tail)?;
if !output.is_kw(intern!(str: "rule")) {
errors.extend(mk_errv(
intern!(str: "non-rule in macro"),
format!("Expected `rule`, got {output}"),
[Pos::Range(output.range.clone()).into()],
));
continue;
};
let (pat, body) = match tail.split_once(|t| t.is_kw(intern!(str: "=>"))) {
Some((a, b)) => (a, b),
None => {
errors.extend(mk_errv(
intern!(str: "no => in macro rule"),
"The pattern and body of a rule must be separated by a =>",
[Pos::Range(tail.pos()).into()],
));
continue;
},
};
rules.push(Rule {
comments: item.output,
pos: Pos::Range(tail.pos()),
pattern: parse_mtree(pat)?,
kind: RuleKind::Native(Code::from_code(
CodeLocator::to_rule(path.unreverse(), macro_i, i as u16),
body.to_vec(),
)),
})
}
if let Ok(e) = OrcErrv::new(errors) { Err(e) } else { Ok(rules) }
}

View File

@@ -1,29 +1,30 @@
use orchid_base::name::Sym;
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,
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)?),
)
},
}
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)?),
)
},
}
}

View File

@@ -1,6 +1,6 @@
use itertools::Itertools;
use orchid_api::PhKind;
use orchid_base::interner::Tok;
use itertools::Itertools;
use orchid_base::side::Side;
use orchid_base::tree::Ph;
@@ -14,30 +14,30 @@ pub type MaxVecSplit<'a> = (&'a [MacTree], (Tok<String>, u8, bool), &'a [MacTree
/// 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))
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()
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) }
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
@@ -47,105 +47,103 @@ fn mk_scalv(pattern: &[MacTree]) -> Vec<ScalMatcher> { pattern.iter().map(mk_sca
/// Pattern MUST start and end with a vectorial placeholder
#[must_use]
pub 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(),
}
},
}
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(_) | MacTok::Done(_) => panic!("Atoms and Done 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"),
}
match &*pattern.tok {
MacTok::Atom(_) | MacTok::Done(_) => panic!("Atoms and Done 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 std::sync::Arc;
use orchid_api::PhKind;
use orchid_base::{intern, location::SourceRange, sym, tree::Ph, tokens::Paren};
use orchid_api::PhKind;
use orchid_base::location::SourceRange;
use orchid_base::tokens::Paren;
use orchid_base::tree::Ph;
use orchid_base::{intern, sym};
use crate::macros::{MacTok, MacTree};
use super::mk_any;
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}");
}
#[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}");
}
}

View File

@@ -21,65 +21,66 @@ pub fn last_is_vec(pattern: &[MacTree]) -> bool { vec_attrs(pattern.last().unwra
pub struct NamedMatcher(AnyMatcher);
impl NamedMatcher {
pub fn new(pattern: &[MacTree]) -> Self {
assert!(
matches!(pattern.first().map(|tree| &*tree.tok), Some(MacTok::Name(_))),
"Named matchers must begin with a name"
);
pub fn new(pattern: &[MacTree]) -> Self {
assert!(
matches!(pattern.first().map(|tree| &*tree.tok), Some(MacTok::Name(_))),
"Named matchers must begin with a name"
);
match last_is_vec(pattern) {
true => Self(mk_any(pattern)),
false => {
let kind: PhKind = PhKind::Vector { priority: 0, at_least_one: false };
let suffix = [MacTok::Ph(Ph { name: intern!(str: "::after"), kind }).at(Pos::None)];
Self(mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec()))
},
}
}
/// Also returns the tail, if any, which should be matched further
/// Note that due to how priod works below, the main usable information from the tail is
/// its length
pub fn apply<'a>(
&self,
seq: &'a [MacTree],
save_loc: impl Fn(Sym) -> bool,
) -> Option<(MatchState<'a>, &'a [MacTree])> {
any_match(&self.0, seq, &save_loc).map(|mut state| match state.remove(intern!(str: "::after")) {
Some(StateEntry::Scalar(_)) => panic!("::after can never be a scalar entry!"),
Some(StateEntry::Vec(v)) => (state, v),
None => (state, &[][..]),
})
}
match last_is_vec(pattern) {
true => Self(mk_any(pattern)),
false => {
let kind: PhKind = PhKind::Vector { priority: 0, at_least_one: false };
let suffix = [MacTok::Ph(Ph { name: intern!(str: "::after"), kind }).at(Pos::None)];
Self(mk_any(&pattern.iter().chain(&suffix).cloned().collect_vec()))
},
}
}
/// Also returns the tail, if any, which should be matched further
/// Note that due to how priod works below, the main usable information from
/// the tail is its length
pub fn apply<'a>(
&self,
seq: &'a [MacTree],
save_loc: impl Fn(Sym) -> bool,
) -> Option<(MatchState<'a>, &'a [MacTree])> {
any_match(&self.0, seq, &save_loc).map(|mut state| {
match state.remove(intern!(str: "::after")) {
Some(StateEntry::Scalar(_)) => panic!("::after can never be a scalar entry!"),
Some(StateEntry::Vec(v)) => (state, v),
None => (state, &[][..]),
}
})
}
}
impl fmt::Display for NamedMatcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
}
impl fmt::Debug for NamedMatcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "NamedMatcher({self})") }
}
pub struct PriodMatcher(VecMatcher);
impl PriodMatcher {
pub fn new(pattern: &[MacTree]) -> Self {
assert!(
pattern.first().and_then(vec_attrs).is_some()
&& pattern.last().and_then(vec_attrs).is_some(),
"Prioritized matchers must start and end with a vectorial",
);
Self(mk_vec(pattern))
}
/// tokens before the offset always match the prefix
pub fn apply<'a>(
&self,
seq: &'a [MacTree],
save_loc: impl Fn(Sym) -> bool,
) -> Option<MatchState<'a>> {
vec_match(&self.0, seq, &save_loc)
}
pub fn new(pattern: &[MacTree]) -> Self {
assert!(
pattern.first().and_then(vec_attrs).is_some() && pattern.last().and_then(vec_attrs).is_some(),
"Prioritized matchers must start and end with a vectorial",
);
Self(mk_vec(pattern))
}
/// tokens before the offset always match the prefix
pub fn apply<'a>(
&self,
seq: &'a [MacTree],
save_loc: impl Fn(Sym) -> bool,
) -> Option<MatchState<'a>> {
vec_match(&self.0, seq, &save_loc)
}
}
impl fmt::Display for PriodMatcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) }
}
impl fmt::Debug for PriodMatcher {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "PriodMatcher({self})") }
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "PriodMatcher({self})") }
}

View File

@@ -4,10 +4,9 @@
//!
//! 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
//! - 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
//!
@@ -16,10 +15,10 @@
mod any_match;
mod build;
pub mod matcher;
mod scal_match;
pub mod shared;
mod vec_match;
pub mod state;
mod vec_attrs;
pub mod matcher;
// pub mod matcher;
mod vec_match;
// pub mod matcher;

View File

@@ -7,38 +7,38 @@ use crate::rule::state::{MatchState, StateEntry};
#[must_use]
pub fn scal_match<'a>(
matcher: &ScalMatcher,
expr: &'a MacTree,
save_loc: &impl Fn(Sym) -> bool,
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 { .. }, MacTok::Done(_)) => None,
(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,
}
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 { .. }, MacTok::Done(_)) => None,
(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,
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)
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)
}

View File

@@ -9,93 +9,93 @@ use orchid_base::side::Side;
use orchid_base::tokens::{PARENS, Paren};
pub enum ScalMatcher {
Name(Sym),
S(Paren, Box<AnyMatcher>),
Lambda(Box<AnyMatcher>, Box<AnyMatcher>),
Placeh { key: Tok<String> },
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>>,
},
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> },
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}"),
}
}
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}}}")
},
}
}
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}]")
},
}
}
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}]")
},
}
}
}

View File

@@ -11,83 +11,81 @@ use orchid_base::name::Sym;
use crate::macros::MacTree;
enum StackAction {
Return(Box<dyn Any>),
Call {
target: Box<dyn FnOnce(Box<dyn Any>) -> StackAction>,
param: Box<dyn Any>,
tail: Box<dyn FnOnce(Box<dyn Any>) -> StackAction>
}
Return(Box<dyn Any>),
Call {
target: Box<dyn FnOnce(Box<dyn Any>) -> StackAction>,
param: Box<dyn Any>,
tail: Box<dyn FnOnce(Box<dyn Any>) -> StackAction>,
},
}
struct Trampoline {
stack: Vec<Box<dyn FnOnce(Box<dyn Any>) -> StackAction>>
stack: Vec<Box<dyn FnOnce(Box<dyn Any>) -> StackAction>>,
}
#[derive(Clone, Copy, Debug)]
pub enum StateEntry<'a> {
Vec(&'a [MacTree]),
Scalar(&'a MacTree),
Vec(&'a [MacTree]),
Scalar(&'a MacTree),
}
#[derive(Clone, Debug)]
pub struct MatchState<'a> {
placeholders: HashMap<Tok<String>, StateEntry<'a>>,
name_posv: HashMap<Sym, Vec<Pos>>,
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() }
}
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
self.placeholders.remove(&name)
}
pub fn mk_owned(self) -> OwnedState {
OwnedState {
placeholders: (self.placeholders.into_iter())
.map(|(k, v)| {
(
k.clone(),
match_mapping!(v, StateEntry => OwnedEntry {
Scalar(tree.clone()),
Vec(v.to_vec()),
}),
)
})
.collect(),
name_posv: self.name_posv,
}
}
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() }
}
pub fn remove(&mut self, name: Tok<String>) -> Option<StateEntry<'a>> {
self.placeholders.remove(&name)
}
pub fn mk_owned(self) -> OwnedState {
OwnedState {
placeholders: (self.placeholders.into_iter())
.map(|(k, v)| {
(
k.clone(),
match_mapping!(v, StateEntry => OwnedEntry {
Scalar(tree.clone()),
Vec(v.to_vec()),
}),
)
})
.collect(),
name_posv: self.name_posv,
}
}
}
impl Default for MatchState<'static> {
fn default() -> Self { Self { name_posv: HashMap::new(), placeholders: HashMap::new() } }
fn default() -> Self { Self { name_posv: HashMap::new(), placeholders: HashMap::new() } }
}
#[derive(Clone, Debug)]
pub enum OwnedEntry {
Vec(Vec<MacTree>),
Scalar(MacTree),
Vec(Vec<MacTree>),
Scalar(MacTree),
}
pub struct OwnedState {
placeholders: HashMap<Tok<String>, OwnedEntry>,
name_posv: HashMap<Sym, Vec<Pos>>,
placeholders: HashMap<Tok<String>, OwnedEntry>,
name_posv: HashMap<Sym, Vec<Pos>>,
}
impl OwnedState {
pub fn get(&self, key: &Tok<String>) -> Option<&OwnedEntry> { self.placeholders.get(key) }
pub fn positions(&self, name: &Sym) -> &[Pos] { self.name_posv.get(name).map_or(&[], |v| &v[..]) }
pub fn get(&self, key: &Tok<String>) -> Option<&OwnedEntry> { self.placeholders.get(key) }
pub fn positions(&self, name: &Sym) -> &[Pos] { self.name_posv.get(name).map_or(&[], |v| &v[..]) }
}

View File

@@ -8,9 +8,9 @@ use crate::macros::{MacTok, MacTree};
/// 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,
}
match (*expr.tok).clone() {
MacTok::Ph(Ph { kind: PhKind::Vector { priority, at_least_one }, name }) =>
Some((name, priority, at_least_one)),
_ => None,
}
}

View File

@@ -1,94 +1,95 @@
use std::cmp::Ordering;
use itertools::Itertools;
use orchid_base::name::Sym;
use super::scal_match::scalv_match;
use super::shared::VecMatcher;
use orchid_base::name::Sym;
use crate::{macros::MacTree, rule::state::{MatchState, StateEntry}};
use crate::macros::MacTree;
use crate::rule::state::{MatchState, StateEntry};
#[must_use]
pub fn vec_match<'a>(
matcher: &VecMatcher,
seq: &'a [MacTree],
save_loc: &impl Fn(Sym) -> bool,
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;
}
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
},
}
match matcher {
VecMatcher::Placeh { key, nonzero } => {
if *nonzero && seq.is_empty() {
return None;
}
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
},
}
}

View File

@@ -1,7 +1,7 @@
use std::io::{self, BufRead as _, Write};
use std::path::PathBuf;
use std::sync::mpsc::{sync_channel, SyncSender};
use std::sync::Mutex;
use std::sync::mpsc::{SyncSender, sync_channel};
use std::{process, thread};
use orchid_api_traits::{Decode, Encode};
@@ -12,68 +12,61 @@ use crate::api;
use crate::extension::{ExtensionPort, OnMessage};
pub struct Subprocess {
child: Mutex<process::Child>,
stdin: Mutex<process::ChildStdin>,
set_onmessage: SyncSender<OnMessage>,
header: api::ExtensionHeader,
child: Mutex<process::Child>,
stdin: Mutex<process::ChildStdin>,
set_onmessage: SyncSender<OnMessage>,
header: api::ExtensionHeader,
}
impl Subprocess {
pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> {
let prog_pbuf = PathBuf::from(cmd.get_program());
let prog = prog_pbuf.file_stem().unwrap_or(cmd.get_program()).to_string_lossy().to_string();
let mut child = cmd
.stdin(process::Stdio::piped())
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
api::HostHeader { log_strategy: logger.strat() }.encode(&mut stdin);
stdin.flush()?;
let mut stdout = child.stdout.take().unwrap();
let header = api::ExtensionHeader::decode(&mut stdout);
let child_stderr = child.stderr.take().unwrap();
let (set_onmessage, recv_onmessage) = sync_channel(0);
thread::Builder::new().name(format!("stdout-fwd:{prog}")).spawn(move || {
let mut onmessage: Box<dyn FnMut(&[u8]) + Send> = recv_onmessage.recv().unwrap();
drop(recv_onmessage);
loop {
match recv_msg(&mut stdout) {
Ok(msg) => onmessage(&msg[..]),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break,
Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()),
}
}
})?;
thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || {
let mut reader = io::BufReader::new(child_stderr);
loop {
let mut buf = String::new();
if 0 == reader.read_line(&mut buf).unwrap() {
break;
}
logger.log(buf);
}
})?;
Ok(Self {
child: Mutex::new(child),
stdin: Mutex::new(stdin),
set_onmessage,
header,
})
}
pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> {
let prog_pbuf = PathBuf::from(cmd.get_program());
let prog = prog_pbuf.file_stem().unwrap_or(cmd.get_program()).to_string_lossy().to_string();
let mut child = cmd
.stdin(process::Stdio::piped())
.stdout(process::Stdio::piped())
.stderr(process::Stdio::piped())
.spawn()?;
let mut stdin = child.stdin.take().unwrap();
api::HostHeader { log_strategy: logger.strat() }.encode(&mut stdin);
stdin.flush()?;
let mut stdout = child.stdout.take().unwrap();
let header = api::ExtensionHeader::decode(&mut stdout);
let child_stderr = child.stderr.take().unwrap();
let (set_onmessage, recv_onmessage) = sync_channel(0);
thread::Builder::new().name(format!("stdout-fwd:{prog}")).spawn(move || {
let mut onmessage: Box<dyn FnMut(&[u8]) + Send> = recv_onmessage.recv().unwrap();
drop(recv_onmessage);
loop {
match recv_msg(&mut stdout) {
Ok(msg) => onmessage(&msg[..]),
Err(e) if e.kind() == io::ErrorKind::BrokenPipe => break,
Err(e) => panic!("Failed to read from stdout: {}, {e}", e.kind()),
}
}
})?;
thread::Builder::new().name(format!("stderr-fwd:{prog}")).spawn(move || {
let mut reader = io::BufReader::new(child_stderr);
loop {
let mut buf = String::new();
if 0 == reader.read_line(&mut buf).unwrap() {
break;
}
logger.log(buf);
}
})?;
Ok(Self { child: Mutex::new(child), stdin: Mutex::new(stdin), set_onmessage, header })
}
}
impl Drop for Subprocess {
fn drop(&mut self) { self.child.lock().unwrap().wait().expect("Extension exited with error"); }
fn drop(&mut self) { self.child.lock().unwrap().wait().expect("Extension exited with error"); }
}
impl ExtensionPort for Subprocess {
fn set_onmessage(&self, callback: OnMessage) {
self.set_onmessage.send(callback).unwrap();
}
fn header(&self) -> &orchid_api::ExtensionHeader { &self.header }
fn send(&self, msg: &[u8]) {
if msg.starts_with(&[0, 0, 0, 0x1c]) {
panic!("Received unnecessary prefix");
}
send_msg(&mut *self.stdin.lock().unwrap(), msg).unwrap()
}
fn set_onmessage(&self, callback: OnMessage) { self.set_onmessage.send(callback).unwrap(); }
fn header(&self) -> &orchid_api::ExtensionHeader { &self.header }
fn send(&self, msg: &[u8]) {
if msg.starts_with(&[0, 0, 0, 0x1c]) {
panic!("Received unnecessary prefix");
}
send_msg(&mut *self.stdin.lock().unwrap(), msg).unwrap()
}
}

View File

@@ -4,14 +4,14 @@ use std::sync::{Mutex, OnceLock};
use itertools::Itertools;
use never::Never;
use orchid_base::error::OrcRes;
use orchid_base::interner::{intern, Tok};
use orchid_base::interner::{Tok, intern};
use orchid_base::location::Pos;
use orchid_base::macros::mtreev_from_api;
use orchid_base::name::Sym;
use orchid_base::parse::{Comment, Import};
use orchid_base::tree::{TokTree, Token};
use ordered_float::NotNan;
use substack::{with_iter_stack, Substack};
use substack::{Substack, with_iter_stack};
use crate::api;
use crate::expr::Expr;
@@ -23,172 +23,168 @@ pub type ParsTok = Token<'static, AtomHand, Never>;
#[derive(Debug)]
pub struct Item {
pub pos: Pos,
pub comments: Vec<Comment>,
pub kind: ItemKind,
pub pos: Pos,
pub comments: Vec<Comment>,
pub kind: ItemKind,
}
#[derive(Debug)]
pub enum ItemKind {
Member(Member),
Export(Tok<String>),
Import(Import),
Macro(Option<NotNan<f64>>, Vec<Rule>)
Member(Member),
Export(Tok<String>),
Import(Import),
Macro(Option<NotNan<f64>>, Vec<Rule>),
}
impl Item {
pub fn from_api(
tree: api::Item,
path: Substack<Tok<String>>,
sys: &System
) -> Self {
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::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, &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()
}))
})
};
let comments = tree.comments.iter().map(Comment::from_api).collect_vec();
Self { pos: Pos::from_api(&tree.location), comments, kind }
}
pub fn from_api(tree: api::Item, path: Substack<Tok<String>>, sys: &System) -> Self {
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::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, &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(),
}))
}),
};
let comments = tree.comments.iter().map(Comment::from_api).collect_vec();
Self { pos: Pos::from_api(&tree.location), comments, kind }
}
}
#[derive(Debug)]
pub struct Member {
pub name: Tok<String>,
pub kind: OnceLock<MemberKind>,
pub lazy: Mutex<Option<LazyMemberHandle>>,
pub name: Tok<String>,
pub kind: OnceLock<MemberKind>,
pub lazy: Mutex<Option<LazyMemberHandle>>,
}
impl Member {
pub fn from_api(
api: api::Member,
path: Substack<Tok<String>>,
sys: &System,
) -> Self {
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())).into_member(name),
api::MemberKind::Const(c) => MemberKind::Const(Code::from_expr(
CodeLocator::to_const(full_path.unreverse()),
Expr::from_api(&c, &mut ())
)),
api::MemberKind::Module(m) => MemberKind::Mod(Module::from_api(m, full_path, sys)),
};
Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
}
pub fn new(name: Tok<String>, kind: MemberKind) -> Self {
Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
}
pub fn from_api(api: api::Member, path: Substack<Tok<String>>, sys: &System) -> Self {
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())).into_member(name);
},
api::MemberKind::Const(c) => MemberKind::Const(Code::from_expr(
CodeLocator::to_const(full_path.unreverse()),
Expr::from_api(&c, &mut ()),
)),
api::MemberKind::Module(m) => MemberKind::Mod(Module::from_api(m, full_path, sys)),
};
Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
}
pub fn new(name: Tok<String>, kind: MemberKind) -> Self {
Member { name, kind: OnceLock::from(kind), lazy: Mutex::default() }
}
}
#[derive(Debug)]
pub enum MemberKind {
Const(Code),
Mod(Module),
Const(Code),
Mod(Module),
}
#[derive(Debug)]
pub struct Module {
pub imports: Vec<Sym>,
pub exports: Vec<Tok<String>>,
pub items: Vec<Item>,
pub imports: Vec<Sym>,
pub exports: Vec<Tok<String>>,
pub items: Vec<Item>,
}
impl Module {
pub fn new(items: impl IntoIterator<Item = Item>) -> Self {
let items = items.into_iter().collect_vec();
let exports = (items.iter())
.filter_map(|i| match &i.kind {
ItemKind::Export(e) => Some(e.clone()),
_ => None,
})
.collect_vec();
Self { imports: vec![], exports, items }
}
pub fn from_api(m: api::Module, path: Substack<Tok<String>>, sys: &System) -> Self {
let mut output = Vec::new();
for item in m.items.into_iter() {
let next = Item::from_api(item, path.clone(), sys);
output.push(next);
}
Self::new(output)
}
pub fn new(items: impl IntoIterator<Item = Item>) -> Self {
let items = items.into_iter().collect_vec();
let exports = (items.iter())
.filter_map(|i| match &i.kind {
ItemKind::Export(e) => Some(e.clone()),
_ => None,
})
.collect_vec();
Self { imports: vec![], exports, items }
}
pub fn from_api(m: api::Module, path: Substack<Tok<String>>, sys: &System) -> Self {
let mut output = Vec::new();
for item in m.items.into_iter() {
let next = Item::from_api(item, path.clone(), sys);
output.push(next);
}
Self::new(output)
}
}
#[derive(Debug)]
pub struct LazyMemberHandle(api::TreeId, System, Tok<Vec<Tok<String>>>);
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(),
locator: CodeLocator { steps: self.2, rule_loc: None },
source: None,
})),
api::MemberKind::Module(m) => with_iter_stack(self.2.iter().cloned(), |path| {
Ok(MemberKind::Mod(Module::from_api(m, path, &self.1)))
}),
api::MemberKind::Lazy(id) => Self(id, self.1, self.2).run(),
}
}
pub fn into_member(self, name: Tok<String>) -> Member {
Member { name, kind: OnceLock::new(), lazy: Mutex::new(Some(self)) }
}
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(),
locator: CodeLocator { steps: self.2, rule_loc: None },
source: None,
})),
api::MemberKind::Module(m) => with_iter_stack(self.2.iter().cloned(), |path| {
Ok(MemberKind::Mod(Module::from_api(m, path, &self.1)))
}),
api::MemberKind::Lazy(id) => Self(id, self.1, self.2).run(),
}
}
pub fn into_member(self, name: Tok<String>) -> Member {
Member { name, kind: OnceLock::new(), lazy: Mutex::new(Some(self)) }
}
}
#[derive(Debug)]
pub struct Rule {
pub pos: Pos,
pub comments: Vec<Comment>,
pub pattern: Vec<MacTree>,
pub kind: RuleKind,
pub pos: Pos,
pub comments: Vec<Comment>,
pub pattern: Vec<MacTree>,
pub kind: RuleKind,
}
#[derive(Debug)]
pub enum RuleKind {
Remote(System, api::MacroId),
Native(Code),
Remote(System, api::MacroId),
Native(Code),
}
#[derive(Debug)]
pub struct Code {
locator: CodeLocator,
source: Option<Vec<ParsTokTree>>,
bytecode: OnceLock<Expr>,
locator: CodeLocator,
source: Option<Vec<ParsTokTree>>,
bytecode: OnceLock<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_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() }
}
}
/// Selects a code element
///
/// Either the steps point to a constant and rule_loc is None, or the steps point to a module and
/// rule_loc selects a macro rule within that module
///
/// Either the steps point to a constant and rule_loc is None, or the steps
/// point to a module and rule_loc selects a macro rule within that module
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct CodeLocator {
steps: Tok<Vec<Tok<String>>>,
/// Index of a macro block in the module demarked by the steps, and a rule in that macro
rule_loc: Option<(u16, u16)>,
steps: Tok<Vec<Tok<String>>>,
/// Index of a macro block in the module demarked by the steps, and a rule in
/// that macro
rule_loc: Option<(u16, u16)>,
}
impl CodeLocator {
pub fn to_const(path: impl IntoIterator<Item = Tok<String>>) -> Self {
Self { steps: intern(&path.into_iter().collect_vec()), rule_loc: None }
}
pub fn to_rule(path: impl IntoIterator<Item = Tok<String>>, macro_i: u16, rule_i: u16) -> Self {
Self { steps: intern(&path.into_iter().collect_vec()), rule_loc: Some((macro_i, rule_i)) }
}
}
pub fn to_const(path: impl IntoIterator<Item = Tok<String>>) -> Self {
Self { steps: intern(&path.into_iter().collect_vec()), rule_loc: None }
}
pub fn to_rule(path: impl IntoIterator<Item = Tok<String>>, macro_i: u16, rule_i: u16) -> Self {
Self { steps: intern(&path.into_iter().collect_vec()), rule_loc: Some((macro_i, rule_i)) }
}
}