TAMER: Extract identifier transitions into Object

The next commit will generalize this further.  This moves logic out of
BaseAsg so that we can implement more sophisticated transitions for
compatability checks.

The logic is still tested as part of BaseAsg; the next commit will change
that as it's generalized further.

* tamer/src/ir/asg/base.rs: Extract object transitions.
* tamer/src/ir/asg/graph.rs (AsgError)[IncompatibleIdent]: New variant.
  (From<TransitionError> for AsgError): Basic type translation.
* tamer/src/ir/asg/object.rs (TransitionResult): New type.
  (impl Object): Transition methods.
  (TransitionError): New enum.
master
Mike Gerwitz 2020-03-12 10:02:22 -04:00
parent 3fe3fc4b84
commit f20120787f
3 changed files with 183 additions and 75 deletions

View File

@ -144,32 +144,25 @@ where
kind: IdentKind,
src: Source<'i>,
) -> AsgResult<ObjectRef<Ix>> {
// TODO: src check
if let Some(existing) = self.lookup(name) {
let node = self.graph.node_weight_mut(existing.0).unwrap();
match node {
Some(Object::Missing(_)) => {
node.replace(Object::Ident(name, kind, src));
return Ok(existing);
}
// TODO: no override-override
Some(Object::Ident(_, _, orig_src))
if orig_src.virtual_ && src.override_ =>
{
*orig_src = src;
return Ok(existing);
}
// TODO: no override-override
Some(Object::IdentFragment(_, _, orig_src, _))
if orig_src.virtual_ && src.override_ =>
{
// clears fragment, which is no longer applicable
node.replace(Object::Ident(name, kind, src));
return Ok(existing);
}
_ => return Ok(existing),
}
let obj = node.take().expect(&format!(
"internal error: missing object for {}",
name
));
// TODO: test inconsistent state (fixed)
return obj
.redeclare(kind, src)
.and_then(|obj| {
node.replace(obj);
Ok(existing)
})
.or_else(|(orig, err)| {
node.replace(orig);
Err(err.into())
});
}
let node = self.graph.add_node(Some(Object::Ident(name, kind, src)));
@ -212,56 +205,17 @@ where
.take()
.expect("internal error: BaseAsg::set_fragment missing Node data");
let result = match ty {
Object::Ident(sym, kind, src) => {
Ok(Object::IdentFragment(sym, kind, src, text))
}
Object::IdentFragment(_, IdentKind::MapHead, _, _) => Ok(ty),
Object::IdentFragment(_, IdentKind::MapTail, _, _) => Ok(ty),
Object::IdentFragment(_, IdentKind::RetMapHead, _, _) => Ok(ty),
Object::IdentFragment(_, IdentKind::RetMapTail, _, _) => Ok(ty),
// TODO remove these ignores when fixed
Object::IdentFragment(
sym,
IdentKind::Map,
Source {
virtual_: true,
override_: true,
..
},
_,
) => {
eprintln!(
"ignoring virtual and overridden map object: {}",
sym
);
Ok(ty)
}
Object::IdentFragment(
sym,
IdentKind::RetMap,
Source {
virtual_: true,
override_: true,
..
},
_,
) => {
eprintln!(
"ignoring virtual and overridden retmap object: {}",
sym
);
Ok(ty)
}
_ => Err(AsgError::BadFragmentDest(format!(
"identifier is not a Object::Ident): {:?}",
ty,
))),
}?;
node.replace(result);
Ok(identi)
// Be sure to restore the previous node if the transition fails,
// otherwise we'll be left in an inconsistent internal state.
ty.set_fragment(text)
.and_then(|obj| {
node.replace(obj);
Ok(identi)
})
.or_else(|(orig, err)| {
node.replace(orig);
Err(err.into())
})
}
#[inline]
@ -390,6 +344,7 @@ where
#[cfg(test)]
mod test {
use super::super::graph::AsgError;
use super::*;
use crate::sym::SymbolIndex;
@ -699,7 +654,7 @@ mod test {
let mut sut = Sut::with_capacity(0, 0);
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let dep = Symbol::new_dummy(SymbolIndex::from_u32(1), "dep");
let dep = Symbol::new_dummy(SymbolIndex::from_u32(2), "dep");
let symnode = sut.declare(&sym, IdentKind::Meta, Source::default())?;
let depnode = sut.declare(&dep, IdentKind::Meta, Source::default())?;

View File

@ -20,7 +20,7 @@
//! Abstract graph as the basis for concrete ASGs.
use super::ident::IdentKind;
use super::object::{FragmentText, Object, Source};
use super::object::{FragmentText, Object, Source, TransitionError};
use super::Sections;
use crate::sym::Symbol;
use petgraph::graph::{IndexType, NodeIndex};
@ -223,6 +223,14 @@ pub enum AsgError {
///
/// See [`Asg::set_fragment`] for more information.
BadFragmentDest(String),
/// An attempt to redeclare an identifier with additional information
/// has failed because the provided information was not compatible
/// with the original declaration.
///
/// See [`Asg::declare`] for more information.
IncompatibleIdent(String),
/// The node was not expected in the current context
UnexpectedNode(String),
}
@ -233,6 +241,9 @@ impl std::fmt::Display for AsgError {
Self::BadFragmentDest(msg) => {
write!(fmt, "bad fragment destination: {}", msg)
}
Self::IncompatibleIdent(msg) => {
write!(fmt, "identifier redeclaration failed: {}", msg)
}
Self::UnexpectedNode(msg) => {
write!(fmt, "unexpected node: {}", msg)
}
@ -246,6 +257,15 @@ impl std::error::Error for AsgError {
}
}
impl From<TransitionError> for AsgError {
fn from(e: TransitionError) -> Self {
match e {
TransitionError::Incompatible(msg) => Self::IncompatibleIdent(msg),
TransitionError::BadFragmentDest(msg) => Self::BadFragmentDest(msg),
}
}
}
#[cfg(test)]
mod test {
use super::*;

View File

@ -25,6 +25,10 @@
use super::ident::IdentKind;
use crate::ir::legacyir::SymAttrs;
use crate::sym::Symbol;
use std::result::Result;
pub type TransitionResult<'i> =
Result<Object<'i>, (Object<'i>, TransitionError)>;
/// Type of object.
///
@ -71,6 +75,135 @@ pub enum Object<'i> {
IdentFragment(&'i Symbol<'i>, IdentKind, Source<'i>, FragmentText),
}
impl<'i> Object<'i> {
/// Attempt to redeclare an identifier with additional information.
///
/// _TODO: Compatibility information._
///
/// The kind if identifier cannot change,
/// but the argument is provided here for convenience so that the
/// caller does not need to perform such a check itself.
pub fn redeclare(
mut self,
kind: IdentKind,
src: Source<'i>,
) -> TransitionResult<'i> {
match self {
Object::Ident(_, _, ref mut orig_src)
if orig_src.virtual_ && src.override_ =>
{
*orig_src = src;
Ok(self)
}
// TODO: no override-override
Object::IdentFragment(name, _, orig_src, _)
if orig_src.virtual_ && src.override_ =>
{
// clears fragment, which is no longer applicable
Ok(Object::Ident(name, kind, src))
}
Object::Missing(name) | Object::Ident(name, _, _) => {
Ok(Object::Ident(name, kind, src))
}
// TODO: incompatible (check now-dangling commits)
_ => Ok(self),
}
}
/// Attach a code fragment (compiled text) to an identifier.
///
/// Only [`Object::Ident`] may receive a fragment.
pub fn set_fragment(self, text: FragmentText) -> TransitionResult<'i> {
match self {
Object::Ident(sym, kind, src) => {
Ok(Object::IdentFragment(sym, kind, src, text))
}
Object::IdentFragment(_, IdentKind::MapHead, _, _)
| Object::IdentFragment(_, IdentKind::MapTail, _, _)
| Object::IdentFragment(_, IdentKind::RetMapHead, _, _)
| Object::IdentFragment(_, IdentKind::RetMapTail, _, _) => Ok(self),
// TODO remove these ignores when fixed
Object::IdentFragment(
sym,
IdentKind::Map,
Source {
virtual_: true,
override_: true,
..
},
_,
) => {
eprintln!(
"ignoring virtual and overridden map object: {}",
sym
);
Ok(self)
}
Object::IdentFragment(
sym,
IdentKind::RetMap,
Source {
virtual_: true,
override_: true,
..
},
_,
) => {
eprintln!(
"ignoring virtual and overridden retmap object: {}",
sym
);
Ok(self)
}
_ => {
let msg =
format!("identifier is not a Object::Ident): {:?}", self,);
Err((self, TransitionError::BadFragmentDest(msg)))
}
}
}
}
/// An error attempting to transition from one [`Object`] state to another.
///
/// TODO: Provide enough information to construct a useful message.
#[derive(Debug, PartialEq)]
pub enum TransitionError {
/// An attempt to redeclare an identifier with additional information
/// has failed because the provided information was not compatible
/// with the original declaration.
///
/// See [`Object::redeclare`].
Incompatible(String),
/// The provided identifier is not in a state that is permitted to
/// receive a fragment.
///
/// See [`Object::set_fragment`].
BadFragmentDest(String),
}
impl std::fmt::Display for TransitionError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Incompatible(msg) => {
write!(fmt, "object incompatible: {}", msg)
}
Self::BadFragmentDest(msg) => {
write!(fmt, "bad fragment destination: {}", msg)
}
}
}
}
/// Compiled fragment for identifier.
///
/// This represents the text associated with an identifier.