Updated everything and moved to hard tab indentation
This commit is contained in:
@@ -9,13 +9,13 @@ edition = "2021"
|
||||
derive_destructure = "1.0.0"
|
||||
hashbrown = "0.15.2"
|
||||
itertools = "0.14.0"
|
||||
lazy_static = "1.4.0"
|
||||
lazy_static = "1.5.0"
|
||||
never = "0.1.0"
|
||||
num-traits = "0.2.19"
|
||||
orchid-api = { version = "0.1.0", path = "../orchid-api" }
|
||||
orchid-api-traits = { version = "0.1.0", path = "../orchid-api-traits" }
|
||||
orchid-base = { version = "0.1.0", path = "../orchid-base" }
|
||||
ordered-float = "4.2.0"
|
||||
ordered-float = "4.6.0"
|
||||
paste = "1.0.15"
|
||||
substack = "1.1.1"
|
||||
trait-set = "0.3.0"
|
||||
|
||||
@@ -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()) }
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
¯o_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,
|
||||
¯o_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())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
}
|
||||
|
||||
@@ -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)?),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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})") }
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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}]")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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[..]) }
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user