tamer: asg::graph: object_gen and object_rel macros
The previous commit demonstrated the amount of boilerplate necessary for introducing new `ObjectKind`s; this abstracts away a lot of that boilerplate, and allows for declarative relationship definition for the ASG's ontology. DEV-13708main
parent
454b91dfce
commit
dd2232b58b
|
@ -117,10 +117,12 @@ use crate::{
|
|||
use petgraph::graph::NodeIndex;
|
||||
use std::{convert::Infallible, fmt::Display, marker::PhantomData};
|
||||
|
||||
#[macro_use]
|
||||
mod rel;
|
||||
|
||||
pub mod expr;
|
||||
pub mod ident;
|
||||
pub mod pkg;
|
||||
mod rel;
|
||||
pub mod root;
|
||||
pub mod tpl;
|
||||
|
||||
|
@ -134,131 +136,174 @@ pub use rel::{
|
|||
pub use root::Root;
|
||||
pub use tpl::Tpl;
|
||||
|
||||
/// An object on the ASG.
|
||||
/// Given a list of [`ObjectKind`]s,
|
||||
/// generate [`Object`],
|
||||
/// associated types,
|
||||
/// and various [`From`]/[`AsRef`] implementations for [`ObjectKind`]
|
||||
/// widening and narrowing.
|
||||
///
|
||||
/// This is generic over its inner values to support using [`Object`] as a
|
||||
/// sum type in a variety of different contexts where [`ObjectKind`] may
|
||||
/// be used.
|
||||
/// The concrete [`ObjectInner`] that is stored on the ASG itself is
|
||||
/// [`OnlyObjectInner`].
|
||||
///
|
||||
/// See the [module-level documentation](super) for more information.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Object<T: ObjectInner = OnlyObjectInner> {
|
||||
/// This macro must be applied only once.
|
||||
macro_rules! object_gen {
|
||||
(
|
||||
$(
|
||||
$(#[$attr:meta])*
|
||||
$kind:ident,
|
||||
)+
|
||||
) => {
|
||||
/// An object on the ASG.
|
||||
///
|
||||
/// This is generic over its inner values to support using
|
||||
/// [`Object`] as a sum type in a variety of different contexts
|
||||
/// where [`ObjectKind`] may be used.
|
||||
/// The concrete [`ObjectInner`] that is stored on the ASG itself is
|
||||
/// [`OnlyObjectInner`].
|
||||
///
|
||||
/// See the [module-level documentation](super) for more
|
||||
/// information.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Object<T: ObjectInner = OnlyObjectInner> {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
$kind(T::$kind),
|
||||
)+
|
||||
}
|
||||
|
||||
/// Object types corresponding to variants in [`Object`].
|
||||
///
|
||||
/// These are used as small tags for [`ObjectRelatable`].
|
||||
/// Rust unfortunately makes working with its internal tags
|
||||
/// difficult,
|
||||
/// despite their efforts with [`std::mem::Discriminant`],
|
||||
/// which requires a _value_ to produce.
|
||||
///
|
||||
/// TODO: Encapsulate within `crate::asg` when the graph can be better
|
||||
/// encapsulated.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ObjectTy {
|
||||
$($kind,)+
|
||||
}
|
||||
|
||||
/// The collection of potential objects of [`Object`].
|
||||
pub trait ObjectInner {
|
||||
$(type $kind;)+
|
||||
}
|
||||
|
||||
/// An [`ObjectInner`] where each constituent type implements
|
||||
/// [`Display`].
|
||||
trait DisplayableObjectInner = ObjectInner
|
||||
where
|
||||
$(<Self as ObjectInner>::$kind: Display,)+;
|
||||
|
||||
/// Concrete [`ObjectKind`]s and nothing more.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct OnlyObjectInner;
|
||||
|
||||
impl ObjectInner for OnlyObjectInner {
|
||||
$(type $kind = $kind;)+
|
||||
}
|
||||
|
||||
/// References to [`OnlyObjectInner`].
|
||||
///
|
||||
/// This allows for `Object<&T>`.
|
||||
/// See [`Object::inner_as_ref`] for more information.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RefObjectInner<'a>(PhantomData<&'a ()>);
|
||||
|
||||
impl<'a> ObjectInner for RefObjectInner<'a> {
|
||||
$(type $kind = &'a $kind;)+
|
||||
}
|
||||
|
||||
/// A [`RefObjectInner`] paired with an [`ObjectIndex`] that
|
||||
/// represents it.
|
||||
///
|
||||
/// This is desirable when an [`ObjectIndex`] is resolved but is
|
||||
/// still needed for graph operations.
|
||||
/// The pair ensures that,
|
||||
/// when the inner [`ObjectKind`] is narrowed,
|
||||
/// the paired [`ObjectIndex`] will too be narrowed to the same
|
||||
/// kind,
|
||||
/// limiting logic bugs that may result from otherwise having
|
||||
/// to manually narrow types.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct OiPairObjectInner<'a>(PhantomData<&'a ()>);
|
||||
|
||||
impl<'a> ObjectInner for OiPairObjectInner<'a> {
|
||||
$(type $kind = (&'a $kind, ObjectIndex<$kind>);)+
|
||||
}
|
||||
|
||||
impl<T: DisplayableObjectInner> Display for Object<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
$(Self::$kind(x) => Display::fmt(x, f),)+
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<$kind> for Object {
|
||||
fn from(x: $kind) -> Self {
|
||||
Self::$kind(x)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Object> for $kind {
|
||||
/// Narrow an object into an [`Root`],
|
||||
/// panicing if the object is not of that type.
|
||||
fn from(obj: Object) -> Self {
|
||||
match obj {
|
||||
Object::$kind(x) => x,
|
||||
_ => obj.narrowing_panic(stringify!($kind)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<$kind> for Object {
|
||||
fn as_ref(&self) -> &$kind {
|
||||
match self {
|
||||
Object::$kind(ref x) => x,
|
||||
_ => self.narrowing_panic(stringify!($kind)),
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
|
||||
/// Generate boilerplate `match`es for individual [`Object`]
|
||||
/// variants.
|
||||
///
|
||||
/// Rust will infer the [`Object`]'s [`ObjectInner`] from the
|
||||
/// surrounding context.
|
||||
macro_rules! map_object {
|
||||
($obj:expr, $inner:ident => $map:expr) => {
|
||||
match $obj {
|
||||
$(Object::$kind($inner) => Object::$kind($map),)+
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object_gen! {
|
||||
/// Represents the root of all reachable identifiers.
|
||||
///
|
||||
/// Any identifier not reachable from the root will not be linked into
|
||||
/// the final executable.
|
||||
///
|
||||
/// There should be only one object of this kind.
|
||||
Root(T::Root),
|
||||
Root,
|
||||
|
||||
/// A package of identifiers.
|
||||
Pkg(T::Pkg),
|
||||
Pkg,
|
||||
|
||||
/// Identifier (a named object).
|
||||
Ident(T::Ident),
|
||||
Ident,
|
||||
|
||||
/// Expression.
|
||||
///
|
||||
/// An expression may optionally be named by one or more [`Ident`]s.
|
||||
Expr(T::Expr),
|
||||
Expr,
|
||||
|
||||
/// A template definition.
|
||||
Tpl(T::Tpl),
|
||||
}
|
||||
|
||||
/// The collection of potential objects of [`Object`].
|
||||
pub trait ObjectInner {
|
||||
type Root;
|
||||
type Pkg;
|
||||
type Ident;
|
||||
type Expr;
|
||||
type Tpl;
|
||||
}
|
||||
|
||||
/// An [`ObjectInner`] where each constituent type implements [`Display`].
|
||||
trait DisplayableObjectInner = ObjectInner
|
||||
where
|
||||
<Self as ObjectInner>::Root: Display,
|
||||
<Self as ObjectInner>::Pkg: Display,
|
||||
<Self as ObjectInner>::Ident: Display,
|
||||
<Self as ObjectInner>::Expr: Display,
|
||||
<Self as ObjectInner>::Tpl: Display;
|
||||
|
||||
/// Concrete [`ObjectKind`]s and nothing more.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct OnlyObjectInner;
|
||||
|
||||
impl ObjectInner for OnlyObjectInner {
|
||||
type Root = Root;
|
||||
type Pkg = Pkg;
|
||||
type Ident = Ident;
|
||||
type Expr = Expr;
|
||||
type Tpl = Tpl;
|
||||
}
|
||||
|
||||
/// References to [`OnlyObjectInner`].
|
||||
///
|
||||
/// This allows for `Object<&T>`.
|
||||
/// See [`Object::inner_as_ref`] for more information.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct RefObjectInner<'a>(PhantomData<&'a ()>);
|
||||
|
||||
impl<'a> ObjectInner for RefObjectInner<'a> {
|
||||
type Root = &'a Root;
|
||||
type Pkg = &'a Pkg;
|
||||
type Ident = &'a Ident;
|
||||
type Expr = &'a Expr;
|
||||
type Tpl = &'a Tpl;
|
||||
}
|
||||
|
||||
/// A [`RefObjectInner`] paired with an [`ObjectIndex`] representing it.
|
||||
///
|
||||
/// This is desirable when an [`ObjectIndex`] is resolved but is still
|
||||
/// needed for graph operations.
|
||||
/// The pair ensures that,
|
||||
/// when the inner [`ObjectKind`] is narrowed,
|
||||
/// the paired [`ObjectIndex`] will too be narrowed to the same kind,
|
||||
/// limiting logic bugs that may result from otherwise having to
|
||||
/// manually narrow types.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct OiPairObjectInner<'a>(PhantomData<&'a ()>);
|
||||
|
||||
impl<'a> ObjectInner for OiPairObjectInner<'a> {
|
||||
type Root = (&'a Root, ObjectIndex<Root>);
|
||||
type Pkg = (&'a Pkg, ObjectIndex<Pkg>);
|
||||
type Ident = (&'a Ident, ObjectIndex<Ident>);
|
||||
type Expr = (&'a Expr, ObjectIndex<Expr>);
|
||||
type Tpl = (&'a Tpl, ObjectIndex<Tpl>);
|
||||
}
|
||||
|
||||
impl<T: DisplayableObjectInner> Display for Object<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Root(root) => Display::fmt(root, f),
|
||||
Self::Pkg(pkg) => Display::fmt(pkg, f),
|
||||
Self::Ident(ident) => Display::fmt(ident, f),
|
||||
Self::Expr(expr) => Display::fmt(expr, f),
|
||||
Self::Tpl(tpl) => Display::fmt(tpl, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate boilerplate `match`es for individual [`Object`] variants.
|
||||
///
|
||||
/// Rust will infer the [`Object`]'s [`ObjectInner`] from the surrounding
|
||||
/// context.
|
||||
macro_rules! map_object {
|
||||
($obj:expr, $inner:ident => $map:expr) => {
|
||||
match $obj {
|
||||
Object::Root($inner) => Object::Root($map),
|
||||
Object::Pkg($inner) => Object::Pkg($map),
|
||||
Object::Ident($inner) => Object::Ident($map),
|
||||
Object::Expr($inner) => Object::Expr($map),
|
||||
Object::Tpl($inner) => Object::Tpl($map),
|
||||
}
|
||||
};
|
||||
Tpl,
|
||||
}
|
||||
|
||||
impl Object<OnlyObjectInner> {
|
||||
|
@ -364,142 +409,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)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ident> for Object {
|
||||
fn from(ident: Ident) -> Self {
|
||||
Self::Ident(ident)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Expr> for Object {
|
||||
fn from(expr: Expr) -> Self {
|
||||
Self::Expr(expr)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tpl> for Object {
|
||||
fn from(tpl: Tpl) -> Self {
|
||||
Self::Tpl(tpl)
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
fn from(val: Object) -> Self {
|
||||
match val {
|
||||
Object::Pkg(pkg) => pkg,
|
||||
_ => val.narrowing_panic("a package"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Object> for Ident {
|
||||
/// Narrow an object into an [`Ident`],
|
||||
/// panicing if the object is not of that type.
|
||||
fn from(val: Object) -> Self {
|
||||
match val {
|
||||
Object::Ident(ident) => ident,
|
||||
_ => val.narrowing_panic("an identifier"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Object> for Expr {
|
||||
/// Narrow an object into an [`Expr`],
|
||||
/// panicing if the object is not of that type.
|
||||
fn from(val: Object) -> Self {
|
||||
match val {
|
||||
Object::Expr(expr) => expr,
|
||||
_ => val.narrowing_panic("an expression"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Object> for Tpl {
|
||||
/// Narrow an object into an [`Expr`],
|
||||
/// panicing if the object is not of that type.
|
||||
fn from(val: Object) -> Self {
|
||||
match val {
|
||||
Object::Tpl(tpl) => tpl,
|
||||
_ => val.narrowing_panic("a template"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Object> for Object {
|
||||
fn as_ref(&self) -> &Object {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
Object::Pkg(ref pkg) => pkg,
|
||||
_ => self.narrowing_panic("a package"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Ident> for Object {
|
||||
fn as_ref(&self) -> &Ident {
|
||||
match self {
|
||||
Object::Ident(ref ident) => ident,
|
||||
_ => self.narrowing_panic("an identifier"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Expr> for Object {
|
||||
fn as_ref(&self) -> &Expr {
|
||||
match self {
|
||||
Object::Expr(ref expr) => expr,
|
||||
_ => self.narrowing_panic("an expression"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Tpl> for Object {
|
||||
fn as_ref(&self) -> &Tpl {
|
||||
match self {
|
||||
Object::Tpl(ref tpl) => tpl,
|
||||
_ => self.narrowing_panic("a template"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`Object`]-compatbile entity.
|
||||
///
|
||||
/// See [`ObjectIndex`] for more information.
|
||||
|
|
|
@ -207,70 +207,14 @@ impl Display for DimState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
||||
/// [`Expr`].
|
||||
///
|
||||
/// See [`ObjectRel`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ExprRel {
|
||||
Ident(ObjectIndex<Ident>),
|
||||
Expr(ObjectIndex<Expr>),
|
||||
}
|
||||
|
||||
impl ObjectRel<Expr> for ExprRel {
|
||||
fn narrow<OB: ObjectRelFrom<Expr> + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
match self {
|
||||
Self::Ident(oi) => oi.filter_rel(),
|
||||
Self::Expr(oi) => oi.filter_rel(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this is a cross edge to another tree.
|
||||
///
|
||||
object_rel! {
|
||||
/// An expression is inherently a tree,
|
||||
/// however it may contain references to other identifiers which
|
||||
/// represent their own trees.
|
||||
/// Any [`Ident`] reference is a cross edge.
|
||||
fn is_cross_edge(&self) -> bool {
|
||||
match self {
|
||||
Self::Ident(..) => true,
|
||||
Self::Expr(..) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectRelatable for Expr {
|
||||
type Rel = ExprRel;
|
||||
|
||||
fn rel_ty() -> ObjectRelTy {
|
||||
ObjectRelTy::Expr
|
||||
}
|
||||
|
||||
fn new_rel_dyn(
|
||||
ty: ObjectRelTy,
|
||||
oi: ObjectIndex<Object>,
|
||||
) -> 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::Tpl => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Ident>> for ExprRel {
|
||||
fn from(value: ObjectIndex<Ident>) -> Self {
|
||||
Self::Ident(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Expr>> for ExprRel {
|
||||
fn from(value: ObjectIndex<Expr>) -> Self {
|
||||
Self::Expr(value)
|
||||
Expr -> {
|
||||
cross Ident,
|
||||
tree Expr,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -967,28 +967,7 @@ pub struct Source {
|
|||
pub override_: bool,
|
||||
}
|
||||
|
||||
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
||||
/// [`Ident`].
|
||||
///
|
||||
/// See [`ObjectRel`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum IdentRel {
|
||||
Ident(ObjectIndex<Ident>),
|
||||
Expr(ObjectIndex<Expr>),
|
||||
}
|
||||
|
||||
impl ObjectRel<Ident> for IdentRel {
|
||||
fn narrow<OB: ObjectRelFrom<Ident> + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
match self {
|
||||
Self::Ident(oi) => oi.filter_rel(),
|
||||
Self::Expr(oi) => oi.filter_rel(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this edge is a cross edge to another tree.
|
||||
///
|
||||
object_rel! {
|
||||
/// Identifiers are either transparent
|
||||
/// (bound to a definition)
|
||||
/// or opaque.
|
||||
|
@ -1001,41 +980,9 @@ impl ObjectRel<Ident> for IdentRel {
|
|||
/// (again at the time of writing).
|
||||
/// Consequently,
|
||||
/// this will always return [`false`].
|
||||
fn is_cross_edge(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectRelatable for Ident {
|
||||
type Rel = IdentRel;
|
||||
|
||||
fn rel_ty() -> ObjectRelTy {
|
||||
ObjectRelTy::Ident
|
||||
}
|
||||
|
||||
fn new_rel_dyn(
|
||||
ty: ObjectRelTy,
|
||||
oi: ObjectIndex<Object>,
|
||||
) -> 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::Tpl => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Ident>> for IdentRel {
|
||||
fn from(value: ObjectIndex<Ident>) -> Self {
|
||||
Self::Ident(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Expr>> for IdentRel {
|
||||
fn from(value: ObjectIndex<Expr>) -> Self {
|
||||
Self::Expr(value)
|
||||
Ident -> {
|
||||
tree Ident,
|
||||
tree Expr,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -58,56 +58,13 @@ impl Functor<Span> for Pkg {
|
|||
}
|
||||
}
|
||||
|
||||
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
||||
/// [`Ident`].
|
||||
///
|
||||
/// See [`ObjectRel`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum PkgRel {
|
||||
Ident(ObjectIndex<Ident>),
|
||||
}
|
||||
|
||||
impl ObjectRel<Pkg> for PkgRel {
|
||||
fn narrow<OB: ObjectRelFrom<Pkg> + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
match self {
|
||||
Self::Ident(oi) => oi.filter_rel(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether this is a cross edge to another package tree.
|
||||
///
|
||||
object_rel! {
|
||||
/// Packages serve as a root for all identifiers defined therein,
|
||||
/// and so an edge to [`Ident`] will never be a cross edge.
|
||||
///
|
||||
/// Imported [`Ident`]s do not have edges from this package.
|
||||
fn is_cross_edge(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectRelatable for Pkg {
|
||||
type Rel = PkgRel;
|
||||
|
||||
fn rel_ty() -> ObjectRelTy {
|
||||
ObjectRelTy::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::Tpl => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ObjectIndex<Ident>> for PkgRel {
|
||||
fn from(value: ObjectIndex<Ident>) -> Self {
|
||||
Self::Ident(value)
|
||||
Pkg -> {
|
||||
tree Ident,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,23 +30,7 @@ use crate::{
|
|||
};
|
||||
use std::fmt::Display;
|
||||
|
||||
/// Object types corresponding to variants in [`Object`].
|
||||
///
|
||||
/// These are used as small tags for [`ObjectRelatable`].
|
||||
/// Rust unfortunately makes working with its internal tags difficult,
|
||||
/// despite their efforts with [`std::mem::Discriminant`],
|
||||
/// which requires a _value_ to produce.
|
||||
///
|
||||
/// TODO: Encapsulate within `crate::asg` when the graph can be better
|
||||
/// encapsulated.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||
pub enum ObjectRelTy {
|
||||
Root,
|
||||
Pkg,
|
||||
Ident,
|
||||
Expr,
|
||||
Tpl,
|
||||
}
|
||||
pub use super::ObjectTy as ObjectRelTy;
|
||||
|
||||
impl Display for ObjectRelTy {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
|
@ -56,6 +40,95 @@ impl Display for ObjectRelTy {
|
|||
}
|
||||
}
|
||||
|
||||
/// Declare relations for an [`ObjectKind`].
|
||||
///
|
||||
/// This generates an [`ObjectRel`] type for the provided [`ObjectKind`] and
|
||||
/// binds it to the kind using [`ObjectRelatable`].
|
||||
///
|
||||
/// Each relationship must be explicitly specified as either a `tree` or
|
||||
/// `cross` edge.
|
||||
/// For more information on cross edges,
|
||||
/// see [`ObjectRel::is_cross_edge`].
|
||||
macro_rules! object_rel {
|
||||
(
|
||||
$(#[$attr:meta])+
|
||||
$from:ident -> {
|
||||
$($ety:ident $kind:ident,)*
|
||||
}
|
||||
) => {paste::paste! {
|
||||
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
||||
#[doc=concat!("[`", stringify!($from), "`].")]
|
||||
///
|
||||
$(#[$attr])+
|
||||
///
|
||||
/// See [`ObjectRel`] for more information.
|
||||
///
|
||||
/// [`ObjectKind`]: crate::asg::ObjectKind
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum [<$from Rel>] {
|
||||
$($kind(ObjectIndex<$kind>),)*
|
||||
}
|
||||
|
||||
impl ObjectRel<$from> for [<$from Rel>] {
|
||||
fn narrow<OB: ObjectRelFrom<$from> + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
match self {
|
||||
$(Self::$kind(oi) => oi.filter_rel(),)*
|
||||
}
|
||||
}
|
||||
|
||||
/// The root of the graph by definition has no cross edges.
|
||||
fn is_cross_edge(&self) -> bool {
|
||||
match self {
|
||||
$(
|
||||
Self::$kind(..) => object_rel!(@is_cross_edge $ety),
|
||||
)*
|
||||
|
||||
#[allow(unreachable_patterns)] // for empty Rel types
|
||||
_ => unreachable!(
|
||||
concat!(stringify!($from), "Rel is empty")
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectRelatable for $from {
|
||||
type Rel = [<$from Rel>];
|
||||
|
||||
fn rel_ty() -> ObjectRelTy {
|
||||
ObjectRelTy::$from
|
||||
}
|
||||
|
||||
fn new_rel_dyn(
|
||||
ty: ObjectRelTy,
|
||||
#[allow(unused_variables)] // for empty Rel
|
||||
oi: ObjectIndex<Object>,
|
||||
) -> Option<[<$from Rel>]> {
|
||||
match ty {
|
||||
$(
|
||||
ObjectRelTy::$kind => {
|
||||
Some(Self::Rel::$kind(oi.must_narrow_into()))
|
||||
},
|
||||
)*
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<ObjectIndex<$kind>> for [<$from Rel>] {
|
||||
fn from(value: ObjectIndex<$kind>) -> Self {
|
||||
Self::$kind(value)
|
||||
}
|
||||
}
|
||||
)*
|
||||
}};
|
||||
|
||||
(@is_cross_edge cross) => { true };
|
||||
(@is_cross_edge tree) => { false };
|
||||
}
|
||||
|
||||
/// A dynamic relationship (edge) from one object to another before it has
|
||||
/// been narrowed.
|
||||
///
|
||||
|
|
|
@ -42,61 +42,10 @@ impl Display for 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<Root> for RootRel {
|
||||
fn narrow<OB: ObjectRelFrom<Root> + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
match self {
|
||||
Self::Ident(oi) => oi.filter_rel(),
|
||||
Self::Pkg(oi) => oi.filter_rel(),
|
||||
}
|
||||
}
|
||||
|
||||
object_rel! {
|
||||
/// The root of the graph by definition has no cross edges.
|
||||
fn is_cross_edge(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
ObjectRelTy::Tpl => 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)
|
||||
Root -> {
|
||||
tree Pkg,
|
||||
tree Ident,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,54 +42,9 @@ impl Display for Tpl {
|
|||
}
|
||||
}
|
||||
|
||||
/// Subset of [`ObjectKind`]s that are valid targets for edges from
|
||||
/// [`Tpl`].
|
||||
///
|
||||
/// See [`ObjectRel`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum TplRel {
|
||||
// TODO
|
||||
}
|
||||
|
||||
impl ObjectRel<Tpl> for TplRel {
|
||||
fn narrow<OB: ObjectRelFrom<Tpl> + ObjectRelatable>(
|
||||
self,
|
||||
) -> Option<ObjectIndex<OB>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Whether this is a cross edge to another tree.
|
||||
///
|
||||
/// An expression is inherently a tree,
|
||||
/// however it may contain references to other identifiers which
|
||||
/// represent their own trees.
|
||||
/// Any [`Ident`] reference is a cross edge.
|
||||
fn is_cross_edge(&self) -> bool {
|
||||
false
|
||||
object_rel! {
|
||||
/// TODO
|
||||
Tpl -> {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectRelatable for Tpl {
|
||||
type Rel = TplRel;
|
||||
|
||||
fn rel_ty() -> ObjectRelTy {
|
||||
ObjectRelTy::Tpl
|
||||
}
|
||||
|
||||
fn new_rel_dyn(
|
||||
ty: ObjectRelTy,
|
||||
_oi: ObjectIndex<Object>,
|
||||
) -> Option<TplRel> {
|
||||
match ty {
|
||||
ObjectRelTy::Root => None,
|
||||
ObjectRelTy::Pkg => None,
|
||||
ObjectRelTy::Ident => None,
|
||||
ObjectRelTy::Expr => None,
|
||||
ObjectRelTy::Tpl => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectIndex<Tpl> {
|
||||
// TODO
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue