use super::*;
#[derive(Clone)]
pub(crate) struct GraphExt {
pub(crate) node: NodeId,
pub(crate) bindings: Vec<Option<NodeId>>,
pub(crate) arrived: Vec<EdgeCtx>,
pub(crate) unplaced: bool,
}
#[derive(Clone, Copy, Default)]
pub(crate) struct GraphView {
pub(crate) node: Option<NodeId>,
}
impl crate::thread::Attachment for GraphExt {
type View = GraphView;
type Crossing = EdgeCtx;
type Witness = [Option<NodeId>];
type Key = NodeId;
fn view(&self) -> GraphView {
GraphView {
node: (!self.unplaced).then_some(self.node),
}
}
fn key(&self) -> NodeId {
self.node
}
fn bare(&self) -> GraphExt {
GraphExt {
unplaced: self.unplaced,
..GraphExt::at(self.node)
}
}
fn standing(view: &GraphView) -> bool {
view.node.is_some()
}
}
impl GraphExt {
pub(crate) fn at(node: NodeId) -> GraphExt {
GraphExt {
node,
bindings: Vec::new(),
arrived: Vec::new(),
unplaced: false,
}
}
pub(crate) fn unplaced(root: NodeId) -> GraphExt {
GraphExt {
unplaced: true,
..GraphExt::at(root)
}
}
}
pub(crate) fn ref_node(v: &Value) -> Option<NodeId> {
match v {
Value::Node(n) => Some(NodeId(n.id)),
_ => None,
}
}
pub(crate) fn references(register: &[Reg]) -> impl Iterator<Item = NodeId> + '_ {
register.iter().filter_map(|r| ref_node(&r.value))
}
pub(crate) fn node_ref(adapter: &impl AstAdapter, node: NodeId) -> Value {
let locator = resolved_provenance(adapter, node)
.first()
.path
.unwrap_or_else(|| ancestor_path(adapter, node, |a, n| a.name(n)));
Value::Node(crate::value::NodeRef::new(node.0, locator))
}
pub(crate) fn mark_reg(adapter: &impl AstAdapter, node: NodeId, name: Option<String>) -> Reg {
Reg {
name,
value: node_ref(adapter, node),
prov: Prov::at(node.into(), command_now()),
site: Origin {
position: node.into(),
command: command_now(),
},
}
}
impl From<NodeId> for Position {
fn from(n: NodeId) -> Position {
Position(n.0)
}
}
impl From<Position> for NodeId {
fn from(p: Position) -> NodeId {
NodeId(p.0)
}
}
pub(crate) type Capsa = Thread<GraphExt>;
pub(crate) fn operand_from(
adapter: &impl AstAdapter,
anchor: &Anchor,
current: NodeId,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> Vec<NodeId> {
if matches!(anchor, Anchor::Current) && !scope.members.is_empty() {
return scope.members.iter().map(|m| m.ext.node).collect();
}
anchor_nodes(adapter, anchor, current, trace, bound, scope)
}
pub(crate) fn anchor_nodes(
adapter: &impl AstAdapter,
anchor: &Anchor,
current: NodeId,
trace: &Trace,
bound: &[Option<NodeId>],
scope: Scope<'_, GraphExt>,
) -> Vec<NodeId> {
let register = scope.register;
match anchor {
Anchor::Current => vec![current],
Anchor::Root => vec![adapter.root()],
Anchor::Id(op) => {
let vs = discard(|| eval_operand_at(adapter, current, op, trace, bound, scope));
let mut ids: Vec<NodeId> = Vec::new();
fn collect(v: &Value, out: &mut Vec<NodeId>) {
match v {
Value::Int(n) if *n >= 0 => out.push(NodeId(*n as u64)),
Value::Node(n) => out.push(NodeId(n.id)),
Value::Record(fields) => {
if let Some((_, id)) = fields.iter().find(|(k, _)| k == "id") {
collect(id, out);
}
}
Value::List(items) => items.iter().for_each(|i| collect(i, out)),
_ => {}
}
}
for v in &vs {
collect(v, &mut ids);
}
dedup(ids)
.into_iter()
.filter(|&n| adapter.has_node(n))
.collect()
}
Anchor::Mark(m) => register
.iter()
.rev()
.find(|r| r.name.as_deref() == Some(m))
.and_then(|r| ref_node(&r.value))
.into_iter()
.collect(),
Anchor::MarkIndex(n) => references(register).nth(n - 1).into_iter().collect(),
Anchor::MarkTop => references(register).last().into_iter().collect(),
Anchor::MarkBack(n) => references(register)
.collect::<Vec<_>>()
.into_iter()
.rev()
.nth(n - 1)
.into_iter()
.collect(),
Anchor::MarksAll => references(register).collect(),
Anchor::MarksNamed(m) => register
.iter()
.filter(|r| r.name.as_deref() == Some(m))
.filter_map(|r| ref_node(&r.value))
.collect(),
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub(crate) struct EdgeCtx {
pub(crate) source: NodeId,
pub(crate) label: String,
pub(crate) target: NodeId,
}
pub(crate) const CHILD: &str = "[child]";
pub(crate) const PARENT: &str = "[parent]";
pub(crate) const NEXT: &str = "[next]";
pub(crate) const PREV: &str = "[prev]";
pub(crate) const CORE_FIELDS: &[&str] = &[
"name",
"id",
"index",
"depth",
"parent-name",
"parent-id",
"parent-index",
"n-children",
"n-descendants",
"n-siblings",
"traits",
"props",
"meta",
"fields",
"is-leaf",
"is-root",
"is-first-child",
"is-last-child",
"is-top-level",
"is-non-tree",
"name-path",
"index-path",
"id-path",
"source",
"path",
"instant",
"dpid",
"provenance",
"%provenance",
"@provenance",
"origin",
"%origin",
"@origin",
"elided",
];
pub(crate) fn core_meta(adapter: &impl AstAdapter, node: NodeId, key: &str) -> Option<Value> {
let index = || index_of(adapter, node);
let n_siblings = || {
adapter
.parent(node)
.map(|p| adapter.children(p).len().saturating_sub(1))
};
match key {
"name" => Some(adapter.name(node).map_or(Value::Null, Value::Str)),
"id" => Some(Value::Int(node.0 as i64)),
"index" => Some(index().map_or(Value::Null, |i| Value::Int(i as i64))),
"depth" => Some(Value::Int(depth_of(adapter, node) as i64)),
"parent-name" => Some(
adapter
.parent(node)
.and_then(|p| adapter.name(p))
.map_or(Value::Null, Value::Str),
),
"parent-id" => Some(
adapter
.parent(node)
.map_or(Value::Null, |p| Value::Int(p.0 as i64)),
),
"parent-index" => Some(
adapter
.parent(node)
.and_then(|p| index_of(adapter, p))
.map_or(Value::Null, |i| Value::Int(i as i64)),
),
"n-children" => Some(Value::Int(adapter.children(node).len() as i64)),
"n-descendants" => Some(Value::Int(n_descendants(adapter, node) as i64)),
"n-siblings" => Some(n_siblings().map_or(Value::Null, |n| Value::Int(n as i64))),
"props" | "properties" => Some(Value::list(
adapter
.property_names(node)
.into_iter()
.map(|k| Value::projection(2, k))
.collect(),
)),
"meta" | "metadata" => Some(Value::list(
adapter
.metadata_keys(node)
.into_iter()
.map(|k| Value::projection(3, k))
.collect(),
)),
"fields" => Some(Value::list(
CORE_FIELDS.iter().map(|k| Value::projection(1, *k)).collect(),
)),
"traits" => Some(Value::list(
adapter.traits(node).into_iter().map(Value::Str).collect(),
)),
"is-leaf" => Some(Value::Bool(adapter.children(node).is_empty())),
"is-root" => Some(Value::Bool(adapter.parent(node).is_none())),
"is-first-child" => Some(Value::Bool(index() == Some(1))),
"is-last-child" => Some(Value::Bool(
matches!((index(), n_siblings()), (Some(i), Some(n)) if i == n + 1),
)),
"is-top-level" => Some(Value::Bool(depth_of(adapter, node) == 1)),
"is-non-tree" => Some(Value::Bool(adapter.non_tree_nodes().contains(&node))),
"name-path" => Some(Value::Str(ancestor_path(adapter, node, |a, n| a.name(n)))),
"index-path" => Some(Value::Str(ancestor_path(adapter, node, |a, n| {
index_of(a, n).map(|i| i.to_string())
}))),
"id-path" => Some(Value::Str(ancestor_path(adapter, node, |_, n| {
Some(n.0.to_string())
}))),
"source" => Some(
resolved_provenance(adapter, node)
.first()
.source
.map_or(Value::Null, Value::Str),
),
"path" => Some(
resolved_provenance(adapter, node)
.first()
.path
.map_or(Value::Null, Value::Str),
),
"instant" => Some(resolved_provenance(adapter, node).instant_max().map_or(
Value::Null,
|(secs, nanos, offset_min)| Value::Instant {
secs,
nanos,
offset_min,
calendar: Default::default(),
},
)),
"dpid" => Some(
resolved_provenance(adapter, node)
.first()
.dpid
.map_or(Value::Null, Value::Str),
),
"provenance" => Some(
resolved_provenance(adapter, node)
.canonical()
.map_or(Value::Null, Value::Str),
),
"%provenance" => Some(prov_record(&resolved_provenance(adapter, node).first())),
"@provenance" => Some(Value::list(
resolved_provenance(adapter, node)
.entries
.iter()
.map(prov_record)
.collect(),
)),
"origin" => Some(Value::Int(node.0 as i64)),
"%origin" => Some(origin_record(Origin {
position: node.into(),
command: 0,
})),
"@origin" => Some(Value::list(vec![origin_record(Origin {
position: node.into(),
command: 0,
})])),
"elided" => Some(Value::Int(resolved_provenance(adapter, node).elided as i64)),
_ => None,
}
}
pub(crate) fn ancestor_path<A: AstAdapter>(
adapter: &A,
node: NodeId,
token: impl Fn(&A, NodeId) -> Option<String>,
) -> String {
let mut parts = Vec::new();
let mut cur = Some(node);
while let Some(n) = cur {
if adapter.parent(n).is_none() {
break; }
if let Some(t) = token(adapter, n) {
parts.push(t);
}
cur = adapter.parent(n);
}
parts.reverse();
format!("/{}", parts.join("/"))
}
pub(crate) fn index_of(adapter: &impl AstAdapter, node: NodeId) -> Option<usize> {
let parent = adapter.parent(node)?;
siblings_of(adapter, parent)
.iter()
.position(|&c| c == node)
.map(|i| i + 1)
}
pub(crate) fn depth_of(adapter: &impl AstAdapter, node: NodeId) -> usize {
let mut depth = 0;
let mut cur = adapter.parent(node);
while let Some(p) = cur {
depth += 1;
cur = adapter.parent(p);
}
depth
}
pub(crate) fn n_descendants(adapter: &impl AstAdapter, node: NodeId) -> usize {
let mut n = 0;
let mut stack = adapter.children(node);
while let Some(c) = stack.pop() {
n += 1;
stack.extend(adapter.children(c));
}
n
}
pub(crate) fn matches_name(adapter: &impl AstAdapter, node: NodeId, matcher: &Matcher) -> bool {
match matcher {
Matcher::Any | Matcher::Dot => true,
Matcher::Name(n) => adapter.answers_to(node, n),
_ => adapter
.name(node)
.is_some_and(|name| matcher.matches(&name)),
}
}
pub(crate) fn sibling(adapter: &impl AstAdapter, node: NodeId, offset: isize) -> Option<NodeId> {
let parent = adapter.parent(node)?;
let sibs = siblings_of(adapter, parent);
let i = sibs.iter().position(|&s| s == node)? as isize + offset;
if i < 0 {
return None;
}
sibs.get(i as usize).copied()
}
pub(crate) fn siblings_of(adapter: &impl AstAdapter, parent: NodeId) -> std::rc::Rc<Vec<NodeId>> {
if let Some(v) = with_graph_run(|s| s.children.get(&parent).cloned()) {
return v;
}
let v = std::rc::Rc::new(adapter.children(parent));
with_graph_run(|s| {
if s.children.len() >= 65_536 {
s.children.clear();
}
s.children.insert(parent, v.clone());
});
v
}
pub(crate) fn by_reach(matches: Vec<(NodeId, usize)>, reach: Reach) -> Vec<NodeId> {
let extreme = match reach {
Reach::All => None,
Reach::Proximal => matches.iter().map(|&(_, d)| d).min(),
Reach::Distal => matches.iter().map(|&(_, d)| d).max(),
};
matches
.into_iter()
.filter(|&(_, d)| extreme.is_none_or(|e| d == e))
.map(|(n, _)| n)
.collect()
}
pub(crate) fn dedup(nodes: Vec<NodeId>) -> Vec<NodeId> {
let mut seen = HashSet::new();
nodes.into_iter().filter(|n| seen.insert(*n)).collect()
}
pub(crate) fn witness_of<'a>(scope: &Scope<'a, GraphExt>) -> &'a [Option<NodeId>] {
match scope.witness {
Some(w) => w,
None => &scope.ext.bindings,
}
}