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-11864main
parent
f75f1b605e
commit
6252758730
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
),
|
||||
};
|
||||
|
||||
|
|
|
@ -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<_>>>()
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in New Issue