tamer: asg: Root package definition
This causes a package definition to be rooted (so that it can be easily accessed for a graph walk). This keeps consistent with the new `ObjectIndex`-based API by introducing a unit `Root` `ObjectKind` and the boilerplate that goes with it. This boilerplate, now glaringly obvious, will be refactored at some point, since its repetition is onerous and distracting. DEV-13159main
parent
a7fee3071d
commit
2d3b27ac01
|
@ -488,7 +488,7 @@ impl ParseState for AirAggregate {
|
|||
(st, Todo) => Transition(st).incomplete(),
|
||||
|
||||
(Empty(es), PkgOpen(span)) => {
|
||||
let oi_pkg = asg.create(Pkg::new(span));
|
||||
let oi_pkg = asg.create(Pkg::new(span)).root(asg);
|
||||
Transition(PkgDfn(oi_pkg, es)).incomplete()
|
||||
}
|
||||
|
||||
|
|
|
@ -247,14 +247,22 @@ fn ident_root_existing() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn empty_pkg() {
|
||||
fn pkg_is_rooted() {
|
||||
let toks = vec![Air::PkgOpen(S1), Air::PkgClose(S2)];
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
assert!(sut.all(|x| x.is_ok()));
|
||||
|
||||
// TODO: Packages aren't named via AIR at the time of writing,
|
||||
// so we can't look anything up.
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let oi_root = asg.root(S3);
|
||||
let pkg = oi_root
|
||||
.edges_filtered::<Pkg>(&asg)
|
||||
.next()
|
||||
.expect("missing rooted package")
|
||||
.resolve(&asg);
|
||||
|
||||
assert_eq!(pkg.span(), S1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -785,8 +793,7 @@ fn sibling_subexprs_have_ordered_edges_to_parent() {
|
|||
let oi_root = asg.expect_ident_oi::<Expr>(id_root);
|
||||
|
||||
let siblings = oi_root
|
||||
.edges(&asg)
|
||||
.filter_map(ObjectRel::narrow::<Expr>)
|
||||
.edges_filtered::<Expr>(&asg)
|
||||
.map(ObjectIndex::cresolve(&asg))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
|
@ -987,10 +994,7 @@ fn expr_ref_to_ident() {
|
|||
|
||||
let oi_foo = asg.expect_ident_oi::<Expr>(id_foo);
|
||||
|
||||
let foo_refs = oi_foo
|
||||
.edges(&asg)
|
||||
.filter_map(ObjectRel::narrow::<Ident>)
|
||||
.collect::<Vec<_>>();
|
||||
let foo_refs = oi_foo.edges_filtered::<Ident>(&asg).collect::<Vec<_>>();
|
||||
|
||||
// We should have only a single reference (to `id_bar`).
|
||||
assert_eq!(foo_refs.len(), 1);
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
//! Abstract semantic graph.
|
||||
|
||||
use self::object::{ObjectRelFrom, ObjectRelTy, ObjectRelatable};
|
||||
use self::object::{ObjectRelFrom, ObjectRelTy, ObjectRelatable, Root};
|
||||
|
||||
use super::{
|
||||
AsgError, FragmentText, Ident, IdentKind, Object, ObjectIndex, ObjectKind,
|
||||
|
@ -31,6 +31,7 @@ use crate::{
|
|||
fmt::{DisplayWrapper, TtQuote},
|
||||
global,
|
||||
parse::{util::SPair, Token},
|
||||
span::Span,
|
||||
sym::SymbolId,
|
||||
};
|
||||
use petgraph::{
|
||||
|
@ -155,13 +156,13 @@ impl Asg {
|
|||
|
||||
// Exhaust the first index to be used as a placeholder
|
||||
// (its value does not matter).
|
||||
let empty_node = graph.add_node(Object::Root.into());
|
||||
let empty_node = graph.add_node(Object::Root(Root).into());
|
||||
index.push(empty_node);
|
||||
|
||||
// Automatically add the root which will be used to determine what
|
||||
// identifiers ought to be retained by the final program.
|
||||
// This is not indexed and is not accessable by name.
|
||||
let root_node = graph.add_node(Object::Root.into());
|
||||
let root_node = graph.add_node(Object::Root(Root).into());
|
||||
|
||||
Self {
|
||||
graph,
|
||||
|
@ -276,10 +277,16 @@ impl Asg {
|
|||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
// TODO: This is transitional;
|
||||
// remove once [`crate::xmlo::asg_builder`] is removed.
|
||||
pub fn root(&self) -> NodeIndex<Ix> {
|
||||
self.root_node
|
||||
/// Root object.
|
||||
///
|
||||
/// All [`Object`]s reachable from the root will be included in the
|
||||
/// compilation unit or linked executable.
|
||||
///
|
||||
/// The `witness` is used in the returned [`ObjectIndex`] and is
|
||||
/// intended for diagnostic purposes to highlight the source entity that
|
||||
/// triggered the request of the root.
|
||||
pub fn root(&self, witness: Span) -> ObjectIndex<Root> {
|
||||
ObjectIndex::new(self.root_node, witness)
|
||||
}
|
||||
|
||||
/// Add an object as a root.
|
||||
|
|
|
@ -120,10 +120,12 @@ use std::{convert::Infallible, fmt::Display, marker::PhantomData};
|
|||
pub mod expr;
|
||||
pub mod ident;
|
||||
pub mod pkg;
|
||||
pub mod root;
|
||||
|
||||
pub use expr::Expr;
|
||||
pub use ident::Ident;
|
||||
pub use pkg::Pkg;
|
||||
pub use root::Root;
|
||||
|
||||
/// An object on the ASG.
|
||||
///
|
||||
|
@ -136,7 +138,7 @@ pub enum Object {
|
|||
/// the final executable.
|
||||
///
|
||||
/// There should be only one object of this kind.
|
||||
Root,
|
||||
Root(Root),
|
||||
|
||||
/// A package of identifiers.
|
||||
Pkg(Pkg),
|
||||
|
@ -169,7 +171,7 @@ pub enum ObjectRelTy {
|
|||
impl Display for Object {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Root => write!(f, "root ASG node"),
|
||||
Self::Root(_) => write!(f, "root ASG node"),
|
||||
Self::Pkg(pkg) => Display::fmt(pkg, f),
|
||||
Self::Ident(ident) => Display::fmt(ident, f),
|
||||
Self::Expr(expr) => Display::fmt(expr, f),
|
||||
|
@ -180,7 +182,7 @@ impl Display for Object {
|
|||
impl Object {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::Root => UNKNOWN_SPAN,
|
||||
Self::Root(_) => UNKNOWN_SPAN,
|
||||
Self::Pkg(pkg) => pkg.span(),
|
||||
Self::Ident(ident) => ident.span(),
|
||||
Self::Expr(expr) => expr.span(),
|
||||
|
@ -245,6 +247,12 @@ impl From<&Object> for Span {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Root> for Object {
|
||||
fn from(root: Root) -> Self {
|
||||
Self::Root(root)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pkg> for Object {
|
||||
fn from(pkg: Pkg) -> Self {
|
||||
Self::Pkg(pkg)
|
||||
|
@ -263,6 +271,17 @@ impl From<Expr> for Object {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Object> for Root {
|
||||
/// Narrow an object into an [`Root`],
|
||||
/// panicing if the object is not of that type.
|
||||
fn from(val: Object) -> Self {
|
||||
match val {
|
||||
Object::Root(root) => root,
|
||||
_ => val.narrowing_panic("the root"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Object> for Pkg {
|
||||
/// Narrow an object into an [`Ident`],
|
||||
/// panicing if the object is not of that type.
|
||||
|
@ -302,6 +321,15 @@ impl AsRef<Object> for Object {
|
|||
}
|
||||
}
|
||||
|
||||
impl AsRef<Root> for Object {
|
||||
fn as_ref(&self) -> &Root {
|
||||
match self {
|
||||
Object::Root(ref root) => root,
|
||||
_ => self.narrowing_panic("the root"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Pkg> for Object {
|
||||
fn as_ref(&self) -> &Pkg {
|
||||
match self {
|
||||
|
@ -449,6 +477,21 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
asg.edges(self)
|
||||
}
|
||||
|
||||
/// Iterate over the [`ObjectIndex`]es of the outgoing edges of `self`
|
||||
/// that match the [`ObjectKind`] `OB`.
|
||||
///
|
||||
/// This is simply a shorthand for applying [`ObjectRel::narrow`] via
|
||||
/// [`Iterator::filter_map`].
|
||||
pub fn edges_filtered<'a, OB: ObjectKind + ObjectRelatable + 'a>(
|
||||
self,
|
||||
asg: &'a Asg,
|
||||
) -> impl Iterator<Item = ObjectIndex<OB>> + 'a
|
||||
where
|
||||
O: ObjectRelTo<OB> + 'a,
|
||||
{
|
||||
self.edges(asg).filter_map(ObjectRel::narrow::<OB>)
|
||||
}
|
||||
|
||||
/// Incoming edges to self filtered by [`ObjectKind`] `OI`.
|
||||
///
|
||||
/// For filtering rationale,
|
||||
|
@ -542,6 +585,22 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
Some(ObjectIndex::<OB>(index, span, PhantomData::default()))
|
||||
.filter(|_| O::rel_ty() == OB::rel_ty())
|
||||
}
|
||||
|
||||
/// Root this object in the ASG.
|
||||
///
|
||||
/// A rooted object is forced to be reachable.
|
||||
/// This should only be utilized when necessary for toplevel objects;
|
||||
/// other objects should be reachable via their relations to other
|
||||
/// objects.
|
||||
/// Forcing objects to be reachable can prevent them from being
|
||||
/// optimized away if they are not used.
|
||||
pub fn root(self, asg: &mut Asg) -> Self
|
||||
where
|
||||
Root: ObjectRelTo<O>,
|
||||
{
|
||||
asg.root(self.span()).add_edge_to(asg, self);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectIndex<Object> {
|
||||
|
|
|
@ -219,9 +219,9 @@ impl ObjectRelatable for Expr {
|
|||
) -> Option<ExprRel> {
|
||||
match ty {
|
||||
ObjectRelTy::Root => None,
|
||||
ObjectRelTy::Pkg => None,
|
||||
ObjectRelTy::Ident => Some(ExprRel::Ident(oi.must_narrow_into())),
|
||||
ObjectRelTy::Expr => Some(ExprRel::Expr(oi.must_narrow_into())),
|
||||
ObjectRelTy::Pkg => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1000,9 +1000,9 @@ impl ObjectRelatable for Ident {
|
|||
) -> Option<IdentRel> {
|
||||
match ty {
|
||||
ObjectRelTy::Root => None,
|
||||
ObjectRelTy::Pkg => None,
|
||||
ObjectRelTy::Ident => Some(IdentRel::Ident(oi.must_narrow_into())),
|
||||
ObjectRelTy::Expr => Some(IdentRel::Expr(oi.must_narrow_into())),
|
||||
ObjectRelTy::Pkg => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,9 +78,9 @@ impl ObjectRelatable for Pkg {
|
|||
fn new_rel_dyn(ty: ObjectRelTy, oi: ObjectIndex<Object>) -> Option<PkgRel> {
|
||||
match ty {
|
||||
ObjectRelTy::Root => None,
|
||||
ObjectRelTy::Pkg => None,
|
||||
ObjectRelTy::Ident => Some(PkgRel::Ident(oi.must_narrow_into())),
|
||||
ObjectRelTy::Expr => None,
|
||||
ObjectRelTy::Pkg => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ impl From<ObjectIndex<Ident>> for PkgRel {
|
|||
|
||||
impl ObjectIndex<Pkg> {
|
||||
/// Indicate that the given identifier `oi` is defined in this package.
|
||||
pub fn defines(&self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self {
|
||||
pub fn defines(self, asg: &mut Asg, oi: ObjectIndex<Ident>) -> Self {
|
||||
self.add_edge_to(asg, oi)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
// Root of the ASG.
|
||||
//
|
||||
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Root node of the ASG.
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use super::{
|
||||
Ident, Object, ObjectIndex, ObjectKind, ObjectRel, ObjectRelTy,
|
||||
ObjectRelatable, Pkg,
|
||||
};
|
||||
|
||||
/// A unit [`Object`] type representing the root node.
|
||||
///
|
||||
/// This exists for consistency with the rest of the object API,
|
||||
/// and for use with [`ObjectIndex`].
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Root;
|
||||
|
||||
impl Display for Root {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "ASG root")
|
||||
}
|
||||
}
|
||||
|
||||
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
||||
/// [`Ident`].
|
||||
///
|
||||
/// See [`ObjectRel`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum RootRel {
|
||||
Pkg(ObjectIndex<Pkg>),
|
||||
Ident(ObjectIndex<Ident>),
|
||||
}
|
||||
|
||||
impl ObjectRel for RootRel {
|
||||
fn narrow<OB: ObjectKind + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
match self {
|
||||
Self::Ident(oi) => oi.filter_rel(),
|
||||
Self::Pkg(oi) => oi.filter_rel(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectRelatable for Root {
|
||||
type Rel = RootRel;
|
||||
|
||||
fn rel_ty() -> ObjectRelTy {
|
||||
ObjectRelTy::Root
|
||||
}
|
||||
|
||||
fn new_rel_dyn(
|
||||
ty: ObjectRelTy,
|
||||
oi: ObjectIndex<Object>,
|
||||
) -> Option<RootRel> {
|
||||
match ty {
|
||||
ObjectRelTy::Root => None,
|
||||
ObjectRelTy::Pkg => Some(RootRel::Pkg(oi.must_narrow_into())),
|
||||
ObjectRelTy::Ident => Some(RootRel::Ident(oi.must_narrow_into())),
|
||||
ObjectRelTy::Expr => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Pkg>> for RootRel {
|
||||
fn from(value: ObjectIndex<Pkg>) -> Self {
|
||||
Self::Pkg(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Ident>> for RootRel {
|
||||
fn from(value: ObjectIndex<Ident>) -> Self {
|
||||
Self::Ident(value)
|
||||
}
|
||||
}
|
|
@ -55,7 +55,7 @@ where
|
|||
|
||||
// TODO: Encapsulate petgraph.
|
||||
// This is technically a topological sort, but functions have cycles.
|
||||
let mut dfs = DfsPostOrder::new(&asg.graph, asg.root());
|
||||
let mut dfs = DfsPostOrder::new(&asg.graph, asg.root(UNKNOWN_SPAN).into());
|
||||
|
||||
// These are always generated by the map compiler,
|
||||
// but do not have edges that would allow them to be properly ordered
|
||||
|
@ -69,7 +69,7 @@ where
|
|||
let oi = ObjectIndex::<Object>::new(index, UNKNOWN_SPAN);
|
||||
|
||||
match asg.get(oi).expect("missing object") {
|
||||
Object::Root => (),
|
||||
Object::Root(_) => (),
|
||||
Object::Ident(ident) => dest.push(ident)?,
|
||||
|
||||
obj @ (Object::Pkg(_) | Object::Expr(_)) => {
|
||||
|
|
Loading…
Reference in New Issue