Committing for reference

This commit is contained in:
2025-01-12 02:02:01 +01:00
parent 52c8d1c95a
commit e0d246fe1f
17 changed files with 826 additions and 168 deletions

View File

@@ -6,7 +6,9 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
async-std = "1.13.0"
derive_destructure = "1.0.0"
futures = "0.3.31"
hashbrown = "0.15.2"
itertools = "0.14.0"
lazy_static = "1.5.0"

View File

@@ -12,6 +12,7 @@ use hashbrown::hash_map::Entry;
use itertools::Itertools;
use lazy_static::lazy_static;
use orchid_api_traits::Request;
use orchid_base::builtin::{ExtFactory, ExtPort};
use orchid_base::char_filter::char_filter_match;
use orchid_base::clone;
use orchid_base::error::{OrcErrv, OrcRes};
@@ -106,27 +107,13 @@ impl fmt::Display for AtomHand {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(&self.print()) }
}
pub type OnMessage = Box<dyn FnMut(&[u8]) + Send>;
/// The 3 primary contact points with an extension are
/// - send a message
/// - wait for a message to arrive
/// - wait for the extension to stop after exit (this is the implicit Drop)
///
/// 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;
}
/// Data held about an Extension. This is refcounted within [Extension]. It's
/// important to only ever access parts of this struct through the [Arc] because
/// the components reference each other through [Weak]s of it, and will panic if
/// upgrading fails.
#[derive(destructure)]
pub struct ExtensionData {
port: Arc<dyn ExtensionPort>,
port: Mutex<Box<dyn ExtPort>>,
// child: Mutex<process::Child>,
// child_stdin: Mutex<ChildStdin>,
reqnot: ReqNot<api::HostMsgSet>,
@@ -153,84 +140,81 @@ fn rel_expr(sys: api::SysId, extk: api::ExprTicket) {
#[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(),
pub fn new(fac: Box<dyn ExtFactory>, logger: Logger) -> io::Result<Self> {
Ok(Self(Arc::new_cyclic(|weak: &Weak<ExtensionData>| {
let (eh, port) = fac.run(Box::new(clone!(weak; move |msg| {
weak.upgrade().inspect(|xd| xd.reqnot.receive(msg));
})));
ExtensionData {
systems: (eh.systems.iter().cloned())
.map(|decl| SystemCtor { decl, ext: weak.clone() })
.collect(),
logger,
port: Mutex::new(port),
reqnot: ReqNot::new(
clone!(weak; move |sfn, _| {
let data = weak.upgrade().unwrap();
data.logger.log_buf("Downsending", sfn);
data.port.lock().unwrap().send(sfn);
}),
clone!(weak; move |notif, _| match notif {
api::ExtHostNotif::ExprNotif(api::ExprNotif::Acquire(acq)) => acq_expr(acq.0, acq.1),
api::ExtHostNotif::ExprNotif(api::ExprNotif::Release(rel)) => rel_expr(rel.0, rel.1),
api::ExtHostNotif::ExprNotif(api::ExprNotif::Move(mov)) => {
acq_expr(mov.inc, mov.expr);
rel_expr(mov.dec, mov.expr);
},
api::ExtHostNotif::Log(api::Log(str)) => weak.upgrade().unwrap().logger.log(str),
}),
|hand, req| match req {
api::ExtHostReq::Ping(ping) => hand.handle(&ping, &()),
api::ExtHostReq::IntReq(intreq) => match intreq {
api::IntReq::InternStr(s) => hand.handle(&s, &intern(&**s.0).to_api()),
api::IntReq::InternStrv(v) => hand.handle(&v, &intern(&*v.0).to_api()),
api::IntReq::ExternStr(si) => hand.handle(&si, &Tok::<String>::from_api(si.0).arc()),
api::IntReq::ExternStrv(vi) => hand.handle(
&vi,
&Arc::new(
Tok::<Vec<Tok<String>>>::from_api(vi.0).iter().map(|t| t.to_api()).collect_vec(),
),
),
},
api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => {
let sys = System::resolve(atom.owner).unwrap();
hand.handle(fw, &sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())))
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
let sys = System::resolve(id).unwrap();
hand.handle(fw, &sys.request(body.clone()))
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, rep_out) = sync_channel(0);
let lex_g = LEX_RECUR.lock().unwrap();
let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).unwrap();
hand.handle(&sl, &rep_out.recv().unwrap())
},
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => {
let expr = Expr::resolve(target).expect("Invalid ticket");
hand.handle(&ins, &api::Inspected {
refcount: expr.strong_count() as u32,
location: expr.pos().to_api(),
kind: expr.to_api(),
})
},
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros { ref run_id, ref query }) => hand
.handle(
rm,
&macro_recur(
*run_id,
mtreev_from_api(query, &mut |_| panic!("Recursion never contains atoms")),
)
.map(|x| macro_treev_to_api(*run_id, x)),
),
),
},
api::ExtHostReq::Fwd(ref fw @ api::Fwd(ref atom, ref key, ref body)) => {
let sys = System::resolve(atom.owner).unwrap();
hand.handle(fw, &sys.reqnot().request(api::Fwded(fw.0.clone(), *key, body.clone())))
},
api::ExtHostReq::SysFwd(ref fw @ api::SysFwd(id, ref body)) => {
let sys = System::resolve(id).unwrap();
hand.handle(fw, &sys.request(body.clone()))
},
api::ExtHostReq::SubLex(sl) => {
let (rep_in, rep_out) = sync_channel(0);
let lex_g = LEX_RECUR.lock().unwrap();
let req_in = lex_g.get(&sl.id).expect("Sublex for nonexistent lexid");
req_in.send(ReqPair(sl.clone(), rep_in)).unwrap();
hand.handle(&sl, &rep_out.recv().unwrap())
},
api::ExtHostReq::ExprReq(api::ExprReq::Inspect(ins @ api::Inspect { target })) => {
let expr = Expr::resolve(target).expect("Invalid ticket");
hand.handle(&ins, &api::Inspected {
refcount: expr.strong_count() as u32,
location: expr.pos().to_api(),
kind: expr.to_api(),
})
},
api::ExtHostReq::RunMacros(ref rm @ api::RunMacros { ref run_id, ref query }) => hand
.handle(
rm,
&macro_recur(
*run_id,
mtreev_from_api(query, &mut |_| panic!("Recursion never contains atoms")),
)
.map(|x| macro_treev_to_api(*run_id, x)),
),
},
),
});
let weak = Arc::downgrade(&ret);
port.set_onmessage(Box::new(move |msg| {
if let Some(xd) = weak.upgrade() {
xd.reqnot.receive(msg)
),
}
}));
Ok(Self(ret))
})))
}
pub fn systems(&self) -> impl Iterator<Item = &SystemCtor> { self.0.systems.iter() }
}
@@ -380,7 +364,7 @@ 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)?;
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) => {

View File

@@ -10,7 +10,7 @@ use trait_set::trait_set;
use crate::api;
use crate::extension::AtomHand;
use crate::rule::matcher::{NamedMatcher, PriodMatcher};
use crate::rule::state::{MatchState, OwnedState};
use crate::rule::state::MatchState;
use crate::tree::Code;
pub type MacTok = MTok<'static, AtomHand>;

View File

@@ -1,24 +1,20 @@
use std::io::{self, BufRead as _, Write};
use std::path::PathBuf;
use std::sync::Mutex;
use std::sync::mpsc::{SyncSender, sync_channel};
use std::sync::mpsc::sync_channel;
use std::{process, thread};
use async_std::sync::Mutex;
use orchid_api_traits::{Decode, Encode};
use orchid_base::builtin::{ExtInit, ExtPort};
use orchid_base::logging::Logger;
use orchid_base::msg::{recv_msg, send_msg};
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,
}
impl Subprocess {
pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> {
pub struct ExtensionCommand(pub process::Command, pub Logger);
impl ExtFactory for ExtensionCommand {
fn run(self: Box<Self>, onmessage: OnMessage) -> ExtInit {
let Self(mut cmd, logger) = *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
@@ -54,19 +50,32 @@ impl Subprocess {
logger.log(buf);
}
})?;
Ok(Self { child: Mutex::new(child), stdin: Mutex::new(stdin), set_onmessage, header })
Ok(Subprocess { child: Mutex::new(child), stdin: Mutex::new(stdin), set_onmessage, header })
}
}
pub struct Subprocess {
child: Mutex<process::Child>,
stdin: Mutex<process::ChildStdin>,
stdout: Mutex<process::ChildStdout>,
header: api::ExtensionHeader,
}
impl Subprocess {
pub fn new(mut cmd: process::Command, logger: Logger) -> io::Result<Self> {}
}
impl Drop for Subprocess {
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 }
impl ExtPort for Subprocess {
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 recv<'a>(&self, cb: Box<dyn FnOnce(&[u8]) + Send + 'a>) -> futures::future::BoxFuture<()> {
async {
}
}
}