forked from Orchid/orchid
base and extension fully compiles, host in good shape
This commit is contained in:
213
orchid-host/src/system.rs
Normal file
213
orchid-host/src/system.rs
Normal file
@@ -0,0 +1,213 @@
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::rc::{Rc, Weak};
|
||||
|
||||
use async_stream::stream;
|
||||
use derive_destructure::destructure;
|
||||
use futures::StreamExt;
|
||||
use futures::future::join_all;
|
||||
use futures::task::LocalSpawnExt;
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
use orchid_base::async_once_cell::OnceCell;
|
||||
use orchid_base::char_filter::char_filter_match;
|
||||
use orchid_base::clone;
|
||||
use orchid_base::error::{OrcErrv, OrcRes};
|
||||
use orchid_base::interner::Tok;
|
||||
use orchid_base::parse::Comment;
|
||||
use orchid_base::reqnot::{ReqNot, Requester};
|
||||
use orchid_base::tree::ttv_from_api;
|
||||
use ordered_float::NotNan;
|
||||
use substack::{Stackframe, Substack};
|
||||
|
||||
use crate::api;
|
||||
use crate::ctx::Ctx;
|
||||
use crate::extension::{Extension, WeakExtension};
|
||||
use crate::tree::{Member, ParsTokTree};
|
||||
|
||||
#[derive(destructure)]
|
||||
struct SystemInstData {
|
||||
ctx: Ctx,
|
||||
ext: Extension,
|
||||
decl_id: api::SysDeclId,
|
||||
lex_filter: api::CharFilter,
|
||||
id: api::SysId,
|
||||
const_root: OnceCell<Vec<Member>>,
|
||||
line_types: Vec<Tok<String>>,
|
||||
}
|
||||
impl Drop for SystemInstData {
|
||||
fn drop(&mut self) { self.ext.system_drop(self.id); }
|
||||
}
|
||||
impl fmt::Debug for SystemInstData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SystemInstData")
|
||||
.field("decl_id", &self.decl_id)
|
||||
.field("lex_filter", &self.lex_filter)
|
||||
.field("id", &self.id)
|
||||
.field("const_root", &self.const_root)
|
||||
.field("line_types", &self.line_types)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct System(Rc<SystemInstData>);
|
||||
impl System {
|
||||
pub fn id(&self) -> api::SysId { self.0.id }
|
||||
pub fn ext(&self) -> &Extension { &self.0.ext }
|
||||
pub fn ctx(&self) -> &Ctx { &self.0.ctx }
|
||||
pub(crate) fn reqnot(&self) -> &ReqNot<api::HostMsgSet> { &self.0.ext.reqnot() }
|
||||
pub async fn get_tree(&self, id: api::TreeId) -> api::MemberKind {
|
||||
self.reqnot().request(api::GetMember(self.0.id, id)).await
|
||||
}
|
||||
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 async fn lex<F: Future<Output = Option<api::SubLexed>>>(
|
||||
&self,
|
||||
source: Tok<String>,
|
||||
pos: u32,
|
||||
r: impl FnMut(u32) -> F,
|
||||
) -> api::OrcResult<Option<api::LexedExpr>> {
|
||||
self.0.ext.lex_req(source, pos, self.id(), r).await
|
||||
}
|
||||
pub fn can_parse(&self, ltyp: Tok<String>) -> bool { self.0.line_types.contains(<yp) }
|
||||
pub fn line_types(&self) -> impl Iterator<Item = &Tok<String>> + '_ { self.0.line_types.iter() }
|
||||
pub async fn parse(
|
||||
&self,
|
||||
line: Vec<ParsTokTree>,
|
||||
exported: bool,
|
||||
comments: Vec<Comment>,
|
||||
) -> OrcRes<Vec<ParsTokTree>> {
|
||||
let line =
|
||||
join_all(line.iter().map(|t| async { t.to_api(&mut |n, _| match *n {}).await })).await;
|
||||
let comments = comments.iter().map(Comment::to_api).collect_vec();
|
||||
match self.reqnot().request(api::ParseLine { exported, sys: self.id(), comments, line }).await {
|
||||
Ok(parsed) => Ok(ttv_from_api(parsed, &mut self.ctx().clone(), &self.ctx().i).await),
|
||||
Err(e) => Err(OrcErrv::from_api(&e, &self.ctx().i).await),
|
||||
}
|
||||
}
|
||||
pub async fn request(&self, req: Vec<u8>) -> Vec<u8> {
|
||||
self.reqnot().request(api::SysFwded(self.id(), req)).await
|
||||
}
|
||||
pub(crate) fn drop_atom(&self, drop: api::AtomId) {
|
||||
let this = self.0.clone();
|
||||
(self.0.ctx.spawn.spawn_local(async move {
|
||||
this.ctx.owned_atoms.write().await.remove(&drop);
|
||||
}))
|
||||
.expect("Failed to drop atom");
|
||||
}
|
||||
pub async fn print(&self) -> String {
|
||||
let ctor = (self.0.ext.system_ctors().find(|c| c.id() == self.0.decl_id))
|
||||
.expect("System instance with no associated constructor");
|
||||
format!("System({} @ {} #{})", ctor.name(), ctor.priority(), self.0.id.0)
|
||||
}
|
||||
pub fn downgrade(&self) -> WeakSystem { WeakSystem(Rc::downgrade(&self.0)) }
|
||||
}
|
||||
|
||||
pub struct WeakSystem(Weak<SystemInstData>);
|
||||
impl WeakSystem {
|
||||
pub fn upgrade(&self) -> Option<System> { self.0.upgrade().map(System) }
|
||||
}
|
||||
|
||||
pub struct SystemCtor {
|
||||
pub(crate) decl: api::SystemDecl,
|
||||
pub(crate) ext: WeakExtension,
|
||||
}
|
||||
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 id(&self) -> api::SysDeclId { self.decl.id }
|
||||
pub async fn run<'a>(&self, depends: impl IntoIterator<Item = &'a System>) -> System {
|
||||
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");
|
||||
let id = ext.ctx().next_sys_id();
|
||||
let sys_inst = ext.reqnot().request(api::NewSystem { depends, id, system: self.decl.id }).await;
|
||||
let data = System(Rc::new(SystemInstData {
|
||||
decl_id: self.decl.id,
|
||||
ext: ext.clone(),
|
||||
ctx: ext.ctx().clone(),
|
||||
lex_filter: sys_inst.lex_filter,
|
||||
const_root: OnceCell::new(),
|
||||
line_types: join_all(sys_inst.line_types.iter().map(|m| Tok::from_api(*m, &ext.ctx().i)))
|
||||
.await,
|
||||
id,
|
||||
}));
|
||||
(data.0.const_root.get_or_init(
|
||||
clone!(data, ext; stream! {
|
||||
for (k, v) in sys_inst.const_root {
|
||||
yield Member::from_api(
|
||||
api::Member { name: k, kind: v },
|
||||
&mut vec![Tok::from_api(k, &ext.ctx().i).await],
|
||||
&data,
|
||||
).await
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
))
|
||||
.await;
|
||||
ext.ctx().systems.write().await.insert(id, data.downgrade());
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum SysResolvErr {
|
||||
Loop(Vec<String>),
|
||||
Missing(String),
|
||||
}
|
||||
|
||||
pub async 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.system_ctors().filter(|c| c.name() == target))
|
||||
.max_by_key(|c| c.priority())
|
||||
.ok_or_else(|| SysResolvErr::Missing(target.to_string()))?;
|
||||
to_load.insert(target, ctor);
|
||||
to_find.extend(ctor.depends());
|
||||
}
|
||||
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.depends() {
|
||||
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])).await;
|
||||
systems.insert(ctor.name(), sys);
|
||||
}
|
||||
Ok(systems.into_values().collect_vec())
|
||||
}
|
||||
Reference in New Issue
Block a user