tamer: asg::Object: Introduce Object::Ident

This wraps `Ident` in a new `Object` variant and modifies `Asg` so that its
nodes are of type `Object`.

This unfortunately requires runtime type checking.  Whether or not that's
worth alleviating in the future depends on a lot of different things, since
it'll require my own graph implementation, and I have to focus on other
things right now.  Maybe it'll be worth it in the future.

Note that this also gets rid of some doc examples that simply aren't worth
maintaining as the API evolves.

DEV-11864
main
Mike Gerwitz 2022-05-19 12:31:37 -04:00
parent f75f1b605e
commit 6252758730
6 changed files with 114 additions and 169 deletions

View File

@ -20,7 +20,7 @@
//! Abstract graph as the basis for concrete ASGs.
use super::{
AsgError, FragmentText, Ident, IdentKind, Source, TransitionResult,
AsgError, FragmentText, Ident, IdentKind, Object, Source, TransitionResult,
};
use crate::global;
use crate::sym::SymbolId;
@ -44,7 +44,7 @@ pub type AsgEdge = ();
///
/// Enclosed in an [`Option`] to permit moving owned values out of the
/// graph.
pub type Node = Option<Ident>;
pub type Node = Option<Object>;
/// Index size for Graph nodes and edges.
type Ix = global::ProgSymSize;
@ -118,7 +118,7 @@ impl Asg {
// 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(Some(Ident::Root));
let root_node = graph.add_node(Some(Ident::Root.into()));
Self {
graph,
@ -169,7 +169,7 @@ impl Asg {
/// See [`Ident::declare`] for more information.
fn lookup_or_missing(&mut self, ident: SymbolId) -> ObjectRef {
self.lookup(ident).unwrap_or_else(|| {
let index = self.graph.add_node(Some(Ident::declare(ident)));
let index = self.graph.add_node(Some(Ident::declare(ident).into()));
self.index_identifier(ident, index);
ObjectRef::new(index)
@ -212,15 +212,16 @@ impl Asg {
let obj = node
.take()
.expect(&format!("internal error: missing object"));
.expect("internal error: missing object")
.unwrap_ident();
f(obj)
.and_then(|obj| {
node.replace(obj);
node.replace(obj.into());
Ok(identi)
})
.or_else(|(orig, err)| {
node.replace(orig);
node.replace(orig.into());
Err(err.into())
})
}
@ -343,13 +344,22 @@ impl Asg {
/// between multiple graphs.
/// It is nevertheless wrapped in an [`Option`] just in case.
#[inline]
pub fn get<I: Into<ObjectRef>>(&self, index: I) -> Option<&Ident> {
pub fn get<I: Into<ObjectRef>>(&self, index: I) -> Option<&Object> {
self.graph.node_weight(index.into().into()).map(|node| {
node.as_ref()
.expect("internal error: Asg::get missing Node data")
})
}
/// Retrieve an identifier from the graph by [`ObjectRef`].
///
/// If the object exists but is not an identifier,
/// [`None`] will be returned.
#[inline]
pub fn get_ident<I: Into<ObjectRef>>(&self, index: I) -> Option<&Ident> {
self.get(index).and_then(Object::as_ident_ref)
}
/// Attempt to retrieve an identifier from the graph by name.
///
/// Since only identifiers carry a name,
@ -493,7 +503,7 @@ mod test {
assert_ne!(nodea, nodeb);
let givena = sut.get(nodea).unwrap();
let givena = sut.get_ident(nodea).unwrap();
assert_eq!(syma, givena.name());
assert_eq!(Some(&IdentKind::Meta), givena.kind());
assert_eq!(
@ -504,7 +514,7 @@ mod test {
givena.src()
);
let givenb = sut.get(nodeb).unwrap();
let givenb = sut.get_ident(nodeb).unwrap();
assert_eq!(symb, givenb.name());
assert_eq!(Some(&IdentKind::Worksheet), givenb.kind());
assert_eq!(
@ -587,7 +597,7 @@ mod test {
assert_matches!(result, Err(AsgError::ObjectTransition(..)));
// The node should have been restored.
assert_eq!(Some(&src), sut.get(node).unwrap().src());
assert_eq!(Some(&src), sut.get_ident(node).unwrap().src());
Ok(())
}
@ -632,7 +642,7 @@ mod test {
assert_matches!(result, Err(AsgError::ObjectTransition(..)));
// The node should have been restored.
assert_eq!(Some(&src), sut.get(node).unwrap().src());
assert_eq!(Some(&src), sut.get_ident(node).unwrap().src());
Ok(())
}
@ -658,7 +668,7 @@ mod test {
"fragment node does not match original node"
);
let obj = sut.get(node).unwrap();
let obj = sut.get_ident(node).unwrap();
assert_eq!(sym, obj.name());
assert_eq!(Some(&IdentKind::Meta), obj.kind());
@ -688,7 +698,7 @@ mod test {
let result = sut.set_fragment(sym, "".into());
// The node should have been restored.
let obj = sut.get(node).unwrap();
let obj = sut.get_ident(node).unwrap();
assert_eq!(sym, obj.name());
assert_matches!(result, Err(AsgError::ObjectTransition(..)));
@ -744,8 +754,8 @@ mod test {
let (symnode, depnode) = sut.add_dep_lookup(sym, dep);
assert!(sut.has_dep(symnode, depnode));
assert_eq!(sym, sut.get(symnode).unwrap().name());
assert_eq!(dep, sut.get(depnode).unwrap().name());
assert_eq!(sym, sut.get_ident(symnode).unwrap().name());
assert_eq!(dep, sut.get_ident(depnode).unwrap().name());
Ok(())
}
@ -770,7 +780,7 @@ mod test {
assert_eq!(symnode, declared);
let obj = sut.get(declared).unwrap();
let obj = sut.get_ident(declared).unwrap();
assert_eq!(sym, obj.name());
assert_eq!(Some(&IdentKind::Meta), obj.kind());

View File

@ -37,7 +37,7 @@
//!
//! Graph Structure
//! ===============
//! Each node (vector) in the graph represents an [object][Ident],
//! Each node (vector) in the graph represents an [`Object`],
//! such as an identifier or an expression.
//! Each directed edge `(A->B)` represents that `A` depends upon `B`.
//!
@ -53,51 +53,6 @@
//! Each object may have a number of valid states;
//! see [`Ident`] for valid object states and transitions.
//!
//!
//! How To Use
//! ==========
//! A suitable concrete [`Asg`] implementation is provided by
//! [`DefaultAsg`].
//!
//! ```
//! use tamer::global;
//! use tamer::asg::{DefaultAsg, IdentKind, Ident, Source};
//! use tamer::sym::{Interner, DefaultProgInterner};
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Be sure to choose size and initial capacities appropriate for your
//! // situation.
//! let mut asg = DefaultAsg::with_capacity(
//! 1024,
//! 1024,
//! );
//!
//! let interner = DefaultProgInterner::new();
//! let identa_sym = interner.intern("identa");
//! let identb_sym = interner.intern("identb");
//!
//! let identa = asg.declare(identa_sym, IdentKind::Meta, Source::default())?;
//! let identb = asg.declare_extern(identb_sym, IdentKind::Meta, Source::default())?;
//!
//! assert_eq!(
//! Some(&Ident::Extern(identb_sym, IdentKind::Meta, Source::default())),
//! asg.get(identb),
//! );
//!
//! // Dependencies can be declared even if an identifier is
//! // unresolved. This declares `(identa)->(identb)`.
//! asg.add_dep(identa, identb);
//! assert!(asg.has_dep(identa, identb));
//!
//! // TODO: extern resolution
//!
//! // Identifiers are indexed by symbol name.
//! assert_eq!(Some(identa), asg.lookup(identa_sym));
//! #
//! # Ok(()) // main
//! # }
//! ```
//!
//! Missing Identifiers
//! -------------------
//! Since identifiers in TAME can be defined in any order relative to their
@ -107,89 +62,6 @@
//! For example,
//! [`Asg::add_dep_lookup`] will add an [`Ident::Missing`] to the graph
//! if either identifier has not yet been declared.
//!
//! ```
//! # use tamer::global;
//! # use tamer::asg::{DefaultAsg, IdentKind, Ident, FragmentText, Source};
//! # use tamer::sym::{Interner, DefaultProgInterner};
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::with_capacity(
//! # 1024,
//! # 1024,
//! # );
//! # let interner = DefaultProgInterner::new();
//! #
//! let identa_sym = interner.intern("identa");
//! let identb_sym = interner.intern("identb");
//! let (identa, identb) = asg.add_dep_lookup(identa_sym, identb_sym);
//!
//! assert_eq!(Some(&Ident::Missing(identa_sym)), asg.get(identa));
//! assert_eq!(Some(&Ident::Missing(identb_sym)), asg.get(identb));
//!
//! // The identifiers returned above are proper objects on the graph.
//! assert_eq!(Some(identa), asg.lookup(identa_sym));
//! assert_eq!(Some(identb), asg.lookup(identb_sym));
//!
//! // Once declared, the missing identifier changes state and dependencies
//! // are retained.
//! asg.declare(identa_sym, IdentKind::Meta, Source::default())?;
//!
//! assert_eq!(
//! Some(&Ident::Ident(identa_sym, IdentKind::Meta, Source::default())),
//! asg.get(identa),
//! );
//!
//! assert!(asg.has_dep(identa, identb));
//! #
//! # Ok(()) // main
//! # }
//! ```
//!
//! Fragments
//! ---------
//! A compiled fragment can be attached to any resolved identifier (see
//! [`Ident::Ident`]) using [`Asg::set_fragment`].
//! Doing so changes the state of the identifier to [`Ident::IdentFragment`],
//! and it is an error to attempt to overwrite that fragment once it is
//! set.
//!
//! ```
//! # use tamer::global;
//! # use tamer::asg::{DefaultAsg, IdentKind, Ident, FragmentText, Source};
//! # use tamer::sym::{Interner, GlobalSymbolIntern};
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::with_capacity(
//! # 1024,
//! # 1024,
//! # );
//! #
//! let sym = "ident".intern();
//!
//! // Fragments can be attached to resolved identifiers.
//! let ident = asg.declare(
//! sym, IdentKind::Meta, Source::default()
//! )?;
//! asg.set_fragment(sym, FragmentText::from("test fragment"))?;
//!
//! assert_eq!(
//! Some(&Ident::IdentFragment(
//! sym,
//! IdentKind::Meta,
//! Source::default(),
//! FragmentText::from("test fragment"),
//! )),
//! asg.get(ident),
//! );
//!
//! // But overwriting will fail
//! let bad = asg.set_fragment(sym, FragmentText::from("overwrite"));
//! assert!(bad.is_err());
//! #
//! # Ok(()) // main
//! # }
//! ```
mod error;
mod graph;
@ -202,6 +74,7 @@ pub use ident::{
FragmentText, Ident, IdentKind, Source, TransitionError, TransitionResult,
UnresolvedError,
};
pub use object::Object;
/// Default concrete ASG implementation.
pub type DefaultAsg = graph::Asg;

View File

@ -19,5 +19,54 @@
//! Objects represented by the ASG.
// This is temporarily empty;
// see `ident` until then.
use super::Ident;
/// Some object on the ASG.
#[derive(Debug, PartialEq)]
pub enum Object {
Ident(Ident),
}
impl Object {
/// Retrieve an [`Ident`] reference,
/// or [`None`] if the object is not an identifier.
pub fn as_ident_ref(&self) -> Option<&Ident> {
match self {
Self::Ident(ident) => Some(ident),
}
}
/// Unwraps an object as an [`Ident`],
/// panicing if the object is of a different type.
///
/// This should be used only when a panic would represent an internal
/// error resulting from state inconsistency on the graph.
/// Ideally,
/// the graph would be typed in such a way to prevent this type of
/// thing from occurring in the future.
pub fn unwrap_ident(self) -> Ident {
match self {
Self::Ident(ident) => ident,
}
}
/// Unwraps an object as an [`&Ident`](Ident),
/// panicing if the object is of a different type.
///
/// This should be used only when a panic would represent an internal
/// error resulting from state inconsistency on the graph.
/// Ideally,
/// the graph would be typed in such a way to prevent this type of
/// thing from occurring in the future.
pub fn unwrap_ident_ref(&self) -> &Ident {
match self {
Self::Ident(ident) => ident,
}
}
}
impl From<Ident> for Object {
fn from(ident: Ident) -> Self {
Self::Ident(ident)
}
}

View File

@ -26,7 +26,7 @@ use super::xmle::{
XmleSections,
};
use crate::{
asg::DefaultAsg,
asg::{DefaultAsg, Ident, Object},
diagnose::{AnnotatedSpan, Diagnostic},
fs::{
Filesystem, FsCanonicalizer, PathFile, VisitOnceFile,
@ -88,7 +88,13 @@ pub fn xmle(package_path: &str, output: &str) -> Result<(), TameldError> {
.map(|cycle| {
let mut path: Vec<SymbolId> = cycle
.into_iter()
.map(|obj| depgraph.get(obj).unwrap().name())
.filter_map(|obj| {
depgraph
.get(obj)
.unwrap()
.as_ident_ref()
.map(Ident::name)
})
.collect();
path.reverse();
@ -132,7 +138,7 @@ pub fn graphml(package_path: &str, output: &str) -> Result<(), TameldError> {
.pretty_print(true)
.export_node_weights(Box::new(|node| {
let (name, kind, generated) = match node {
Some(n) => {
Some(Object::Ident(n)) => {
let generated = match n.src() {
Some(src) => src.generated,
None => false,
@ -147,7 +153,7 @@ pub fn graphml(package_path: &str, output: &str) -> Result<(), TameldError> {
None => (
String::from("missing"),
"missing".into(),
format!("{}", false),
"false".into(),
),
};

View File

@ -55,7 +55,7 @@ where
while let Some(index) = dfs.next(&asg.graph) {
let ident = asg.get(index).expect("missing node");
dest.push(ident)?;
dest.push(ident.unwrap_ident_ref())?;
}
dest.push(get_ident(asg, st::L_MAP_UUUTAIL))?;
@ -77,6 +77,7 @@ where
"missing internal identifier: {}",
sym.lookup_str()
))
.unwrap_ident_ref()
}
/// Check graph for cycles
@ -109,7 +110,10 @@ fn check_cycles(asg: &Asg) -> SortResult<()> {
let is_all_funcs = scc.iter().all(|nx| {
let ident = asg.get(*nx).expect("missing node");
matches!(ident.kind(), Some(IdentKind::Func(..)))
matches!(
ident.as_ident_ref().and_then(Ident::kind),
Some(IdentKind::Func(..))
)
});
if is_all_funcs {
@ -276,22 +280,22 @@ mod test {
vec![
// Static head
asg.lookup(st::L_MAP_UUUHEAD.into())
.and_then(|id| asg.get(id)),
.and_then(|id| asg.get_ident(id)),
asg.lookup(st::L_RETMAP_UUUHEAD.into())
.and_then(|id| asg.get(id)),
.and_then(|id| asg.get_ident(id)),
// Post-order
asg.get(adepdep),
asg.get(adep),
asg.get(a),
asg.get_ident(adepdep),
asg.get_ident(adep),
asg.get_ident(a),
// TODO: This will go away once Root is no longer considered
// an identifier.
// The real Sections filters this out.
Some(&Ident::Root),
// Static tail
asg.lookup(st::L_MAP_UUUTAIL.into())
.and_then(|id| asg.get(id)),
.and_then(|id| asg.get_ident(id)),
asg.lookup(st::L_RETMAP_UUUTAIL.into())
.and_then(|id| asg.get(id)),
.and_then(|id| asg.get_ident(id)),
]
.into_iter()
.collect::<Option<Vec<_>>>()

View File

@ -625,7 +625,7 @@ mod test {
..Default::default()
},
),
sut.get(sut.lookup(sym_extern).unwrap()).unwrap(),
sut.get_ident(sut.lookup(sym_extern).unwrap()).unwrap(),
);
assert_eq!(
@ -637,7 +637,7 @@ mod test {
..Default::default()
},
),
sut.get(sut.lookup(sym_non_extern).unwrap()).unwrap(),
sut.get_ident(sut.lookup(sym_non_extern).unwrap()).unwrap(),
);
assert_eq!(
@ -649,7 +649,7 @@ mod test {
..Default::default()
},
),
sut.get(sut.lookup(sym_map).unwrap()).unwrap(),
sut.get_ident(sut.lookup(sym_map).unwrap()).unwrap(),
);
assert_eq!(
@ -661,7 +661,7 @@ mod test {
..Default::default()
},
),
sut.get(sut.lookup(sym_retmap).unwrap()).unwrap(),
sut.get_ident(sut.lookup(sym_retmap).unwrap()).unwrap(),
);
}
@ -702,7 +702,7 @@ mod test {
..Default::default()
},
),
sut.get(sut.lookup(sym).unwrap()).unwrap(),
sut.get_ident(sut.lookup(sym).unwrap()).unwrap(),
);
}
@ -744,7 +744,7 @@ mod test {
..Default::default()
},
),
sut.get(sut.lookup(sym).unwrap()).unwrap(),
sut.get_ident(sut.lookup(sym).unwrap()).unwrap(),
);
}
@ -861,7 +861,7 @@ mod test {
Default::default(),
frag
)),
sut.get(node),
sut.get_ident(node),
);
}
@ -913,7 +913,10 @@ mod test {
.expect("ident/fragment was not added to graph");
// The identifier should not have been modified on failure.
assert!(matches!(sut.get(node).unwrap(), Ident::Extern(_, _, _)));
assert!(matches!(
sut.get_ident(node).unwrap(),
Ident::Extern(_, _, _)
));
}
#[test]