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
parent
3fe3fc4b84
commit
f20120787f
|
@ -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())?;
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue