[DEV-7087] TAMER: {=>Ident}Object{,State,Data}

This is essential to clarify what exactly the different object types
represent with the new generic abstractions.  For example, we will have
expressions as an object type.
master
Mike Gerwitz 2020-03-16 11:49:41 -04:00
parent 5fb68f9b67
commit f969877324
9 changed files with 671 additions and 298 deletions

View File

@ -23,7 +23,7 @@ use super::graph::{
Asg, AsgEdge, AsgError, AsgResult, Node, ObjectRef, SortableAsg, Asg, AsgEdge, AsgError, AsgResult, Node, ObjectRef, SortableAsg,
}; };
use super::ident::IdentKind; use super::ident::IdentKind;
use super::object::{FragmentText, ObjectData, ObjectState, Source}; use super::object::{FragmentText, IdentObjectData, IdentObjectState, Source};
use super::Sections; use super::Sections;
use crate::sym::Symbol; use crate::sym::Symbol;
use fixedbitset::FixedBitSet; use fixedbitset::FixedBitSet;
@ -64,7 +64,7 @@ where
impl<'i, O, Ix> BaseAsg<O, Ix> impl<'i, O, Ix> BaseAsg<O, Ix>
where where
Ix: IndexType, Ix: IndexType,
O: ObjectState<'i, O>, O: IdentObjectState<'i, O>,
{ {
/// Create an ASG with the provided initial capacity. /// Create an ASG with the provided initial capacity.
/// ///
@ -128,7 +128,7 @@ where
/// Lookup `ident` or add a missing identifier to the graph and return a /// Lookup `ident` or add a missing identifier to the graph and return a
/// reference to it. /// reference to it.
/// ///
/// See [`ObjectState::missing`] for more information. /// See [`IdentObjectState::missing`] for more information.
#[inline] #[inline]
fn lookup_or_missing(&mut self, ident: &'i Symbol<'i>) -> ObjectRef<Ix> { fn lookup_or_missing(&mut self, ident: &'i Symbol<'i>) -> ObjectRef<Ix> {
self.lookup(ident).unwrap_or_else(|| { self.lookup(ident).unwrap_or_else(|| {
@ -143,7 +143,7 @@ where
impl<'i, O, Ix> Asg<'i, O, Ix> for BaseAsg<O, Ix> impl<'i, O, Ix> Asg<'i, O, Ix> for BaseAsg<O, Ix>
where where
Ix: IndexType, Ix: IndexType,
O: ObjectState<'i, O>, O: IdentObjectState<'i, O>,
{ {
fn declare( fn declare(
&mut self, &mut self,
@ -267,7 +267,7 @@ where
impl<'i, O, Ix> SortableAsg<'i, O, Ix> for BaseAsg<O, Ix> impl<'i, O, Ix> SortableAsg<'i, O, Ix> for BaseAsg<O, Ix>
where where
Ix: IndexType, Ix: IndexType,
O: ObjectData<'i> + ObjectState<'i, O>, O: IdentObjectData<'i> + IdentObjectState<'i, O>,
{ {
fn sort(&'i self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'i, O>> { fn sort(&'i self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'i, O>> {
let mut deps = Sections::new(); let mut deps = Sections::new();
@ -302,7 +302,7 @@ where
None => { None => {
return Err(AsgError::UnexpectedNode(format!( return Err(AsgError::UnexpectedNode(format!(
"{:?}", "{:?}",
ident.ident() ident.as_ident()
))) )))
} }
} }
@ -351,11 +351,99 @@ where
mod test { mod test {
use super::super::graph::AsgError; use super::super::graph::AsgError;
use super::*; use super::*;
use crate::ir::asg::Object; use crate::ir::asg::{Dim, IdentObject, TransitionError, TransitionResult};
use crate::sym::SymbolIndex; use crate::sym::SymbolIndex;
use std::cell::RefCell;
// TODO: mock Object #[derive(Debug, Default, PartialEq)]
type Sut<'i> = BaseAsg<Object<'i>, u8>; struct StubIdentObject<'i> {
given_missing: Option<&'i Symbol<'i>>,
given_ident: Option<(&'i Symbol<'i>, IdentKind, Source<'i>)>,
given_extern: Option<(&'i Symbol<'i>, IdentKind)>,
given_redeclare: Option<(IdentKind, Source<'i>)>,
given_set_fragment: Option<FragmentText>,
fail_redeclare: RefCell<Option<TransitionError>>,
}
impl<'i> IdentObjectData<'i> for StubIdentObject<'i> {
fn name(&self) -> Option<&'i Symbol<'i>> {
self.given_missing
.or(self.given_ident.as_ref().map(|args| args.0))
.or(self.given_extern.as_ref().map(|args| args.0))
}
fn kind(&self) -> Option<&IdentKind> {
self.given_ident
.as_ref()
.map(|args| &args.1)
.or(self.given_extern.as_ref().map(|args| &args.1))
.or(self.given_redeclare.as_ref().map(|args| &args.0))
}
fn src(&self) -> Option<&Source<'i>> {
None
}
fn fragment(&self) -> Option<&FragmentText> {
None
}
fn as_ident(&self) -> Option<&IdentObject<'i>> {
None
}
}
impl<'i> IdentObjectState<'i, StubIdentObject<'i>> for StubIdentObject<'i> {
fn missing(ident: &'i Symbol<'i>) -> Self {
Self {
given_missing: Some(ident),
..Default::default()
}
}
fn ident(
name: &'i Symbol<'i>,
kind: IdentKind,
src: Source<'i>,
) -> Self {
Self {
given_ident: Some((name, kind, src)),
..Default::default()
}
}
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> Self {
Self {
given_extern: Some((name, kind)),
..Default::default()
}
}
fn redeclare(
mut self,
kind: IdentKind,
src: Source<'i>,
) -> TransitionResult<StubIdentObject<'i>> {
if self.fail_redeclare.borrow().is_some() {
let err = self.fail_redeclare.replace(None).unwrap();
return Err((self, err));
}
self.given_redeclare = Some((kind, src));
Ok(self)
}
fn set_fragment(
mut self,
text: FragmentText,
) -> TransitionResult<StubIdentObject<'i>> {
self.given_set_fragment.replace(text);
Ok(self)
}
}
// TODO: mock IdentObject
type Sut<'i> = BaseAsg<StubIdentObject<'i>, u8>;
#[test] #[test]
fn create_with_capacity() { fn create_with_capacity() {
@ -404,7 +492,7 @@ mod test {
assert_ne!(nodea, nodeb); assert_ne!(nodea, nodeb);
assert_eq!( assert_eq!(
Some(&Object::Ident( Some((
&syma, &syma,
IdentKind::Meta, IdentKind::Meta,
Source { Source {
@ -412,11 +500,11 @@ mod test {
..Default::default() ..Default::default()
}, },
)), )),
sut.get(nodea), sut.get(nodea).unwrap().given_ident
); );
assert_eq!( assert_eq!(
Some(&Object::Ident( Some((
&symb, &symb,
IdentKind::Worksheet, IdentKind::Worksheet,
Source { Source {
@ -424,7 +512,7 @@ mod test {
..Default::default() ..Default::default()
}, },
)), )),
sut.get(nodeb), sut.get(nodeb).unwrap().given_ident
); );
Ok(()) Ok(())
@ -456,100 +544,74 @@ mod test {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "extern"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "extern");
let node = sut.declare_extern(&sym, IdentKind::Meta)?; let node = sut.declare_extern(&sym, IdentKind::Meta)?;
assert_eq!(Some(&Object::Extern(&sym, IdentKind::Meta)), sut.get(node),); assert_eq!(
Some((&sym, IdentKind::Meta)),
sut.get(node).unwrap().given_extern,
);
Ok(()) Ok(())
} }
// TODO: incompatible
#[test] #[test]
fn declare_returns_existing_compatible() -> AsgResult<()> { fn declare_returns_existing() -> AsgResult<()> {
let mut sut = Sut::with_capacity(0, 0); let mut sut = Sut::with_capacity(0, 0);
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "symdup"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "symdup");
let node = sut.declare(&sym, IdentKind::Meta, Source::default())?; let src = Source::default();
let node = sut.declare(&sym, IdentKind::Meta, src.clone())?;
// Same declaration a second time // Remember that our stub does not care about compatibility.
let redeclare = let rekind = IdentKind::Class(Dim::from_u8(3));
sut.declare(&sym, IdentKind::Meta, Source::default())?; let resrc = Source {
desc: Some("redeclare".into()),
..Default::default()
};
let redeclare = sut.declare(&sym, rekind.clone(), resrc.clone())?;
// We don't care what the objects are for this test, just that the
// same node is referenced.
assert_eq!(node, redeclare); assert_eq!(node, redeclare);
Ok(())
}
// TODO: incompatible
#[test]
fn declare_override_virtual_ident() -> AsgResult<()> {
let mut sut = Sut::with_capacity(0, 0);
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "virtual");
let over_src = Symbol::new_dummy(SymbolIndex::from_u32(2), "src");
let virt_node = sut.declare(
&sym,
IdentKind::Meta,
Source {
virtual_: true,
..Default::default()
},
)?;
let over_src = Source {
override_: true,
src: Some(&over_src),
..Default::default()
};
let over_node = sut.declare(&sym, IdentKind::Meta, over_src.clone())?;
assert_eq!(virt_node, over_node);
assert_eq!( assert_eq!(
sut.get(over_node), Some((rekind, resrc)),
Some(&Object::Ident(&sym, IdentKind::Meta, over_src,)) sut.get(node).unwrap().given_redeclare,
); );
Ok(()) Ok(())
} }
// TODO: incompatible // Builds upon declare_returns_existing.
#[test] #[test]
fn declare_override_virtual_ident_fragment() -> AsgResult<()> { fn declare_fails_if_transition_fails() -> AsgResult<()> {
let mut sut = Sut::with_capacity(0, 0); let mut sut = Sut::with_capacity(0, 0);
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "virtual"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "symdup");
let over_src = Symbol::new_dummy(SymbolIndex::from_u32(2), "src"); let src = Source {
desc: Some("orig".into()),
let virt_node = sut.declare(
&sym,
IdentKind::Meta,
Source {
virtual_: true,
..Default::default()
},
)?;
sut.set_fragment(virt_node, FragmentText::from("remove me"))?;
let over_src = Source {
override_: true,
src: Some(&over_src),
..Default::default() ..Default::default()
}; };
let over_node = sut.declare(&sym, IdentKind::Meta, over_src.clone())?; // Set up an object to fail redeclaration.
let node = sut.declare(&sym, IdentKind::Meta, src.clone())?;
let obj = sut.get(node).unwrap();
let msg = String::from("test fail");
obj.fail_redeclare
.replace(Some(TransitionError::Incompatible(msg.clone())));
assert_eq!(virt_node, over_node); // Should invoke StubIdentObject::redeclare on the above `obj`.
let result = sut.declare(&sym, IdentKind::Meta, Source::default());
// The act of overriding the node should have cleared any existing if let Err(err) = result {
// fragment, making way for a new fragment to take its place as soon // The node should have been restored.
// as it is discovered. (So, back to an Object::Ident.) let obj = sut.get(node).unwrap();
assert_eq!( assert_eq!(src, obj.given_ident.as_ref().unwrap().2);
sut.get(over_node),
Some(&Object::Ident(&sym, IdentKind::Meta, over_src,))
);
Ok(()) assert_eq!(AsgError::IncompatibleIdent(msg), err);
Ok(())
} else {
panic!("failure expected: {:?}", result);
}
} }
#[test] #[test]
@ -573,88 +635,15 @@ mod test {
"fragment node does not match original node" "fragment node does not match original node"
); );
assert_eq!( let obj = sut.get(node).unwrap();
Some(&Object::IdentFragment(&sym, IdentKind::Meta, src, fragment)),
sut.get(node) assert_eq!(Some((&sym, IdentKind::Meta, src,)), obj.given_ident);
); assert_eq!(Some(fragment), obj.given_set_fragment);
Ok(()) Ok(())
} }
fn add_ident_kind_ignores( // TODO: fragment fail
given: IdentKind,
expected: IdentKind,
) -> AsgResult<()> {
let mut sut = Sut::with_capacity(0, 0);
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "tofrag");
let src = Source {
generated: true,
..Default::default()
};
let node = sut.declare(&sym, given, src.clone())?;
let fragment = "a fragment".to_string();
let node_with_frag = sut.set_fragment(node, fragment.clone())?;
// Attaching a fragment should _replace_ the node, not create a
// new one
assert_eq!(
node, node_with_frag,
"fragment node does not match original node"
);
assert_eq!(
Some(&Object::IdentFragment(&sym, expected, src, fragment)),
sut.get(node)
);
Ok(())
}
#[test]
fn add_fragment_to_ident_map_head() -> AsgResult<()> {
add_ident_kind_ignores(IdentKind::MapHead, IdentKind::MapHead)
}
#[test]
fn add_fragment_to_ident_map_tail() -> AsgResult<()> {
add_ident_kind_ignores(IdentKind::MapTail, IdentKind::MapTail)
}
#[test]
fn add_fragment_to_ident_retmap_head() -> AsgResult<()> {
add_ident_kind_ignores(IdentKind::RetMapHead, IdentKind::RetMapHead)
}
#[test]
fn add_fragment_to_ident_retmap_tail() -> AsgResult<()> {
add_ident_kind_ignores(IdentKind::RetMapTail, IdentKind::RetMapTail)
}
#[test]
fn add_fragment_to_fragment_fails() -> AsgResult<()> {
let mut sut = Sut::with_capacity(0, 0);
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let node = sut.declare(&sym, IdentKind::Meta, Source::default())?;
let fragment = "orig fragment".to_string();
sut.set_fragment(node, fragment.clone())?;
// Since it's already a fragment, this should fail.
let err = sut
.set_fragment(node, "replacement".to_string())
.expect_err("Expected failure");
match err {
AsgError::BadFragmentDest(str) if str.contains("sym") => (),
_ => panic!("expected AsgError::BadFragmentDest: {:?}", err),
}
Ok(())
}
#[test] #[test]
fn add_ident_dep_to_ident() -> AsgResult<()> { fn add_ident_dep_to_ident() -> AsgResult<()> {
@ -704,8 +693,8 @@ mod test {
let (symnode, depnode) = sut.add_dep_lookup(&sym, &dep); let (symnode, depnode) = sut.add_dep_lookup(&sym, &dep);
assert!(sut.has_dep(symnode, depnode)); assert!(sut.has_dep(symnode, depnode));
assert_eq!(Some(&Object::Missing(&sym)), sut.get(symnode)); assert_eq!(Some(&sym), sut.get(symnode).unwrap().given_missing);
assert_eq!(Some(&Object::Missing(&dep)), sut.get(depnode)); assert_eq!(Some(&dep), sut.get(depnode).unwrap().given_missing);
Ok(()) Ok(())
} }
@ -721,7 +710,7 @@ mod test {
let (symnode, _) = sut.add_dep_lookup(&sym, &dep); let (symnode, _) = sut.add_dep_lookup(&sym, &dep);
let src = Source { let src = Source {
desc: Some("Tamer is NOT lamer.".to_string()), desc: Some("redeclare missing".into()),
..Default::default() ..Default::default()
}; };
@ -730,10 +719,10 @@ mod test {
assert_eq!(symnode, declared); assert_eq!(symnode, declared);
assert_eq!( let obj = sut.get(declared).unwrap();
Some(&Object::Ident(&sym, IdentKind::Meta, src)),
sut.get(declared), assert_eq!(Some(&sym), obj.given_missing);
); assert_eq!(Some((IdentKind::Meta, src)), obj.given_redeclare);
Ok(()) Ok(())
} }
@ -742,13 +731,8 @@ mod test {
( $iter:expr, $s:ident ) => {{ ( $iter:expr, $s:ident ) => {{
let mut pos = 0; let mut pos = 0;
for obj in $iter { for obj in $iter {
match obj { let sym = obj.name().expect("missing object");
Object::Ident(sym, _, _) assert_eq!($s.get(pos), Some(sym));
| Object::IdentFragment(sym, _, _, _) => {
assert_eq!($s.get(pos), Some(*sym));
}
_ => panic!("unexpected object"),
}
pos = pos + 1; pos = pos + 1;
} }

View File

@ -21,7 +21,7 @@
use super::ident::IdentKind; use super::ident::IdentKind;
use super::object::{ use super::object::{
FragmentText, ObjectData, ObjectState, Source, TransitionError, FragmentText, IdentObjectData, IdentObjectState, Source, TransitionError,
}; };
use super::Sections; use super::Sections;
use crate::sym::Symbol; use crate::sym::Symbol;
@ -32,8 +32,8 @@ use std::result::Result;
/// ///
/// This IR focuses on the definition and manipulation of objects and their /// This IR focuses on the definition and manipulation of objects and their
/// dependencies. /// dependencies.
/// See [`Object`](super::object::Object) for a summary of valid object /// See [`IdentObject`](super::object::IdentObject) for a summary of valid
/// state transitions. /// identifier object state transitions.
/// ///
/// Objects are never deleted from the graph, /// Objects are never deleted from the graph,
/// so [`ObjectRef`]s will remain valid for the lifetime of the ASG. /// so [`ObjectRef`]s will remain valid for the lifetime of the ASG.
@ -43,7 +43,7 @@ use std::result::Result;
pub trait Asg<'i, O, Ix> pub trait Asg<'i, O, Ix>
where where
Ix: IndexType, Ix: IndexType,
O: ObjectState<'i, O>, O: IdentObjectState<'i, O>,
{ {
/// Declare a concrete identifier. /// Declare a concrete identifier.
/// ///
@ -63,7 +63,7 @@ where
/// the existing identifier will be returned. /// the existing identifier will be returned.
/// For more information on state transitions that can occur when /// For more information on state transitions that can occur when
/// redeclaring an identifier that already exists, /// redeclaring an identifier that already exists,
/// see [`ObjectState::redeclare`]. /// see [`IdentObjectState::redeclare`].
/// ///
/// A successful declaration will add an identifier to the graph /// A successful declaration will add an identifier to the graph
/// and return an [`ObjectRef`] reference. /// and return an [`ObjectRef`] reference.
@ -91,8 +91,9 @@ where
/// on the graph will not be altered. /// on the graph will not be altered.
/// Resolution will otherwise fail in error. /// Resolution will otherwise fail in error.
/// ///
/// See [`ObjectState::extern_`] and [`ObjectState::redeclare`] for more /// See [`IdentObjectState::extern_`] and
/// information on compatibility related to extern resolution. /// [`IdentObjectState::redeclare`] for more information on
/// compatibility related to extern resolution.
fn declare_extern( fn declare_extern(
&mut self, &mut self,
name: &'i Symbol<'i>, name: &'i Symbol<'i>,
@ -103,7 +104,7 @@ where
/// ///
/// Fragments are intended for use by the [linker][crate::ld]. /// Fragments are intended for use by the [linker][crate::ld].
/// For more information, /// For more information,
/// see [`ObjectState::set_fragment`]. /// see [`IdentObjectState::set_fragment`].
fn set_fragment( fn set_fragment(
&mut self, &mut self,
identi: ObjectRef<Ix>, identi: ObjectRef<Ix>,
@ -151,7 +152,7 @@ where
/// a missing identifier will be added as a placeholder, /// a missing identifier will be added as a placeholder,
/// allowing the ASG to be built with partial information as /// allowing the ASG to be built with partial information as
/// identifiers continue to be discovered. /// identifiers continue to be discovered.
/// See [`ObjectState::missing`] for more information. /// See [`IdentObjectState::missing`] for more information.
/// ///
/// References to both identifiers are returned in argument order. /// References to both identifiers are returned in argument order.
fn add_dep_lookup( fn add_dep_lookup(
@ -167,7 +168,7 @@ where
/// used as an `Intermediate Representation`. /// used as an `Intermediate Representation`.
pub trait SortableAsg<'i, O, Ix> pub trait SortableAsg<'i, O, Ix>
where where
O: ObjectData<'i>, O: IdentObjectData<'i>,
Ix: IndexType, Ix: IndexType,
{ {
fn sort(&'i self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'i, O>>; fn sort(&'i self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'i, O>>;
@ -181,7 +182,7 @@ pub type AsgResult<T> = Result<T, AsgError>;
/// Reference to an [object][super::object] stored within the [`Asg`]. /// Reference to an [object][super::object] stored within the [`Asg`].
/// ///
/// Object references are integer offsets, /// IdentObject references are integer offsets,
/// not pointers. /// not pointers.
/// See the [module-level documentation][self] for more information. /// See the [module-level documentation][self] for more information.
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)] #[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]

View File

@ -17,7 +17,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
//! Identifiers (a type of [object][super::object::Object]). //! Identifiers (a type of [object][super::object::IdentObject]).
use crate::ir::legacyir::{SymAttrs, SymDtype, SymType}; use crate::ir::legacyir::{SymAttrs, SymDtype, SymType};
use std::convert::TryFrom; use std::convert::TryFrom;
@ -29,7 +29,7 @@ use std::convert::TryFrom;
/// ///
/// These are derived from [`legacyir::SymType`][crate::ir::legacyir::SymType] /// These are derived from [`legacyir::SymType`][crate::ir::legacyir::SymType]
/// and will be generalized in the future. /// and will be generalized in the future.
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Clone)]
pub enum IdentKind { pub enum IdentKind {
/// Classification generator. /// Classification generator.
/// ///
@ -219,6 +219,13 @@ impl<'i> TryFrom<&SymAttrs<'i>> for IdentKind {
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct Dim(u8); pub struct Dim(u8);
impl Dim {
pub fn from_u8(value: u8) -> Self {
// TODO: 0≤n<10
Self(value)
}
}
/// Underlying datatype of identifier. /// Underlying datatype of identifier.
/// ///
/// TODO: This will always be 0≤n≤9, so let's introduce a newtype for it. /// TODO: This will always be 0≤n≤9, so let's introduce a newtype for it.
@ -248,6 +255,13 @@ mod test {
use super::*; use super::*;
use std::convert::TryInto; use std::convert::TryInto;
#[test]
fn dim_from_u8() {
let n = 5u8;
assert_eq!(Dim(n), Dim::from_u8(n));
}
#[test] #[test]
fn dim_to_str() { fn dim_to_str() {
// we'll just test high and low // we'll just test high and low

View File

@ -37,7 +37,7 @@
//! //!
//! Graph Structure //! Graph Structure
//! =============== //! ===============
//! Each node (vector) in the graph represents an [object][Object], //! Each node (vector) in the graph represents an [object][IdentObject],
//! such as an identifier or an expression. //! such as an identifier or an expression.
//! Each directed edge `(A->B)` represents that `A` depends upon `B`. //! Each directed edge `(A->B)` represents that `A` depends upon `B`.
//! //!
@ -51,7 +51,7 @@
//! [scc]: https://en.wikipedia.org/wiki/Strongly_connected_component //! [scc]: https://en.wikipedia.org/wiki/Strongly_connected_component
//! //!
//! Each object may have a number of valid states; //! Each object may have a number of valid states;
//! see [`Object`] for valid object states and transitions. //! see [`IdentObject`] for valid object states and transitions.
//! //!
//! //!
//! How To Use //! How To Use
@ -61,13 +61,13 @@
//! //!
//! ``` //! ```
//! use tamer::global; //! use tamer::global;
//! use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, Object, Source}; //! use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, IdentObject, Source};
//! use tamer::sym::{Interner, DefaultInterner}; //! use tamer::sym::{Interner, DefaultInterner};
//! //!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Be sure to choose size and initial capacities appropriate for your //! // Be sure to choose size and initial capacities appropriate for your
//! // situation. //! // situation.
//! let mut asg = DefaultAsg::<Object, global::PkgIdentSize>::with_capacity( //! let mut asg = DefaultAsg::<IdentObject, global::PkgIdentSize>::with_capacity(
//! 1024, //! 1024,
//! 1024, //! 1024,
//! ); //! );
@ -80,7 +80,7 @@
//! let identb = asg.declare_extern(identb_sym, IdentKind::Meta)?; //! let identb = asg.declare_extern(identb_sym, IdentKind::Meta)?;
//! //!
//! assert_eq!( //! assert_eq!(
//! Some(&Object::Extern(identb_sym, IdentKind::Meta)), //! Some(&IdentObject::Extern(identb_sym, IdentKind::Meta)),
//! asg.get(identb), //! asg.get(identb),
//! ); //! );
//! //!
@ -105,16 +105,16 @@
//! it is often the case that a dependency will have to be added to the //! it is often the case that a dependency will have to be added to the
//! graph before it is resolved. //! graph before it is resolved.
//! For example, //! For example,
//! [`Asg::add_dep_lookup`] will add an [`Object::Missing`] to the graph //! [`Asg::add_dep_lookup`] will add an [`IdentObject::Missing`] to the graph
//! if either identifier has not yet been declared. //! if either identifier has not yet been declared.
//! //!
//! ``` //! ```
//! # use tamer::global; //! # use tamer::global;
//! # use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, Object, FragmentText, Source}; //! # use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, IdentObject, FragmentText, Source};
//! # use tamer::sym::{Interner, DefaultInterner}; //! # use tamer::sym::{Interner, DefaultInterner};
//! # //! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::<Object, global::PkgIdentSize>::with_capacity( //! # let mut asg = DefaultAsg::<IdentObject, global::PkgIdentSize>::with_capacity(
//! # 1024, //! # 1024,
//! # 1024, //! # 1024,
//! # ); //! # );
@ -124,8 +124,8 @@
//! let identb_sym = interner.intern("identb"); //! let identb_sym = interner.intern("identb");
//! let (identa, identb) = asg.add_dep_lookup(identa_sym, identb_sym); //! let (identa, identb) = asg.add_dep_lookup(identa_sym, identb_sym);
//! //!
//! assert_eq!(Some(&Object::Missing(identa_sym)), asg.get(identa)); //! assert_eq!(Some(&IdentObject::Missing(identa_sym)), asg.get(identa));
//! assert_eq!(Some(&Object::Missing(identb_sym)), asg.get(identb)); //! assert_eq!(Some(&IdentObject::Missing(identb_sym)), asg.get(identb));
//! //!
//! // The identifiers returned above are proper objects on the graph. //! // The identifiers returned above are proper objects on the graph.
//! assert_eq!(Some(identa), asg.lookup(identa_sym)); //! assert_eq!(Some(identa), asg.lookup(identa_sym));
@ -136,7 +136,7 @@
//! asg.declare(identa_sym, IdentKind::Meta, Source::default())?; //! asg.declare(identa_sym, IdentKind::Meta, Source::default())?;
//! //!
//! assert_eq!( //! assert_eq!(
//! Some(&Object::Ident(identa_sym, IdentKind::Meta, Source::default())), //! Some(&IdentObject::Ident(identa_sym, IdentKind::Meta, Source::default())),
//! asg.get(identa), //! asg.get(identa),
//! ); //! );
//! //!
@ -149,18 +149,18 @@
//! Fragments //! Fragments
//! --------- //! ---------
//! A compiled fragment can be attached to any resolved identifier (see //! A compiled fragment can be attached to any resolved identifier (see
//! [`Object::Ident`]) using [`Asg::set_fragment`]. //! [`IdentObject::Ident`]) using [`Asg::set_fragment`].
//! Doing so changes the state of the identifier to [`Object::IdentFragment`], //! Doing so changes the state of the identifier to [`IdentObject::IdentFragment`],
//! and it is an error to attempt to overwrite that fragment once it is //! and it is an error to attempt to overwrite that fragment once it is
//! set. //! set.
//! //!
//! ``` //! ```
//! # use tamer::global; //! # use tamer::global;
//! # use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, Object, FragmentText, Source}; //! # use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, IdentObject, FragmentText, Source};
//! # use tamer::sym::{Interner, DefaultInterner}; //! # use tamer::sym::{Interner, DefaultInterner};
//! # //! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::<Object, global::PkgIdentSize>::with_capacity( //! # let mut asg = DefaultAsg::<IdentObject, global::PkgIdentSize>::with_capacity(
//! # 1024, //! # 1024,
//! # 1024, //! # 1024,
//! # ); //! # );
@ -173,7 +173,7 @@
//! asg.set_fragment(ident, FragmentText::from("test fragment"))?; //! asg.set_fragment(ident, FragmentText::from("test fragment"))?;
//! //!
//! assert_eq!( //! assert_eq!(
//! Some(&Object::IdentFragment( //! Some(&IdentObject::IdentFragment(
//! interner.intern("ident"), //! interner.intern("ident"),
//! IdentKind::Meta, //! IdentKind::Meta,
//! Source::default(), //! Source::default(),
@ -198,7 +198,10 @@ mod section;
pub use graph::{Asg, AsgError, AsgResult, ObjectRef, SortableAsg}; pub use graph::{Asg, AsgError, AsgResult, ObjectRef, SortableAsg};
pub use ident::{Dim, IdentKind}; pub use ident::{Dim, IdentKind};
pub use object::{FragmentText, Object, ObjectData, Source}; pub use object::{
FragmentText, IdentObject, IdentObjectData, Source, TransitionError,
TransitionResult,
};
pub use section::{Section, SectionIterator, Sections}; pub use section::{Section, SectionIterator, Sections};
/// Default concrete ASG implementation. /// Default concrete ASG implementation.

View File

@ -39,8 +39,8 @@ pub type TransitionResult<T> = Result<T, (T, TransitionError)>;
/// \ / \ / /// \ / \ /
/// `--------------------` `-----------' /// `--------------------` `-----------'
/// ``` /// ```
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Object<'i> { pub enum IdentObject<'i> {
/// An identifier is expected to be defined but is not yet available. /// An identifier is expected to be defined but is not yet available.
/// ///
/// This variant contains the symbol representing the name of the /// This variant contains the symbol representing the name of the
@ -58,7 +58,7 @@ pub enum Object<'i> {
/// An identifier that has not yet been resolved. /// An identifier that has not yet been resolved.
/// ///
/// Externs are upgraded to [`Object::Ident`] once an identifier of /// Externs are upgraded to [`IdentObject::Ident`] once an identifier of
/// the same name is loaded. /// the same name is loaded.
/// It is an error if the loaded identifier does not have a compatible /// It is an error if the loaded identifier does not have a compatible
/// [`IdentKind`]. /// [`IdentKind`].
@ -74,22 +74,22 @@ pub enum Object<'i> {
IdentFragment(&'i Symbol<'i>, IdentKind, Source<'i>, FragmentText), IdentFragment(&'i Symbol<'i>, IdentKind, Source<'i>, FragmentText),
} }
/// Retrieve information about an [`Object`]. /// Retrieve information about an [`IdentObject`].
/// ///
/// APIs should adhere to this trait rather than a concrete object type such /// APIs should adhere to this trait rather than a concrete object type such
/// as [`Object`]; /// as [`IdentObject`];
/// this allows other representations to be used, /// this allows other representations to be used,
/// while still permitting the use of matching on [`Object`] through /// while still permitting the use of matching on [`IdentObject`]
/// the use of [`ident`](ObjectData::ident). /// through the use of [`ident`](IdentObjectState::ident).
/// ///
/// Since an object implementing this trait may not be an identifier /// Since an object implementing this trait may not be an identifier
/// (e.g. an expression), /// (e.g. an expression),
/// even [`name`](ObjectData::name)---which /// even [`name`](IdentObjectData::name)---which
/// is used by all [`Object`] variants---returns /// is used by all [`IdentObject`] variants---returns
/// an [`Option`]. /// an [`Option`].
/// These methods also provide a convenient alternative to `match`ing on /// These methods also provide a convenient alternative to `match`ing on
/// data that may not be present in all variants. /// data that may not be present in all variants.
pub trait ObjectData<'i> { pub trait IdentObjectData<'i> {
/// Identifier name. /// Identifier name.
/// ///
/// If the object is not an identifier, /// If the object is not an identifier,
@ -99,31 +99,37 @@ pub trait ObjectData<'i> {
/// Identifier [`IdentKind`]. /// Identifier [`IdentKind`].
/// ///
/// If the object does not have a kind /// If the object does not have a kind
/// (as is the case with [`Object::Missing`]), /// (as is the case with [`IdentObject::Missing`]),
/// [`None`] is returned. /// [`None`] is returned.
fn kind(&self) -> Option<&IdentKind>; fn kind(&self) -> Option<&IdentKind>;
/// Identifier [`Source`]. /// Identifier [`Source`].
/// ///
/// If the object does not have source information /// If the object does not have source information
/// (as is the case with [`Object::Extern`]), /// (as is the case with [`IdentObject::Extern`]),
/// [`None`] is returned. /// [`None`] is returned.
fn src(&self) -> Option<&Source<'i>>; fn src(&self) -> Option<&Source<'i>>;
/// Object as an identifier ([`Object`]). /// Identifier [`FragmentText`].
///
/// If the object does not have an associated code fragment,
/// [`None`] is returned.
fn fragment(&self) -> Option<&FragmentText>;
/// IdentObject as an identifier ([`IdentObject`]).
/// ///
/// If the object is not or cannot be faithfully converted into an /// If the object is not or cannot be faithfully converted into an
/// [`Object`], /// [`IdentObject`],
/// [`None`] is returned. /// [`None`] is returned.
/// For example, /// For example,
/// expressions will always yield [`None`]. /// expressions will always yield [`None`].
/// ///
/// This allows pattern matching on [`Object`] variants regardless of /// This allows pattern matching on [`IdentObject`] variants regardless
/// the underlying object type. /// of the underlying object type.
fn ident(&self) -> Option<&Object<'i>>; fn as_ident(&self) -> Option<&IdentObject<'i>>;
} }
impl<'i> ObjectData<'i> for Object<'i> { impl<'i> IdentObjectData<'i> for IdentObject<'i> {
fn name(&self) -> Option<&'i Symbol<'i>> { fn name(&self) -> Option<&'i Symbol<'i>> {
match self { match self {
Self::Missing(name) Self::Missing(name)
@ -151,23 +157,33 @@ impl<'i> ObjectData<'i> for Object<'i> {
} }
} }
/// Expose underlying [`Object`]. fn fragment(&self) -> Option<&FragmentText> {
match self {
Self::Missing(_) | Self::Ident(_, _, _) | Self::Extern(_, _) => {
None
}
Self::IdentFragment(_, _, _, text) => Some(text),
}
}
/// Expose underlying [`IdentObject`].
/// ///
/// This will never be [`None`] for this implementation. /// This will never be [`None`] for this implementation.
/// However, /// However,
/// other [`ObjectData`] implementations may still result in [`None`], /// other [`IdentObjectData`] implementations may still result in
/// so it's important _not_ to rely on this as an excuse to be lazy /// [`None`],
/// with unwrapping. /// so it's important _not_ to rely on this as an excuse to be lazy
/// with unwrapping.
#[inline] #[inline]
fn ident(&self) -> Option<&Object<'i>> { fn as_ident(&self) -> Option<&IdentObject<'i>> {
Some(&self) Some(&self)
} }
} }
/// Objects as a state machine. /// Objects as a state machine.
pub trait ObjectState<'i, T> pub trait IdentObjectState<'i, T>
where where
T: ObjectState<'i, T>, T: IdentObjectState<'i, T>,
{ {
/// Produce an object representing a missing identifier. /// Produce an object representing a missing identifier.
fn missing(ident: &'i Symbol<'i>) -> T; fn missing(ident: &'i Symbol<'i>) -> T;
@ -197,32 +213,32 @@ where
fn set_fragment(self, text: FragmentText) -> TransitionResult<T>; fn set_fragment(self, text: FragmentText) -> TransitionResult<T>;
} }
impl<'i> ObjectState<'i, Object<'i>> for Object<'i> { impl<'i> IdentObjectState<'i, IdentObject<'i>> for IdentObject<'i> {
fn missing(ident: &'i Symbol<'i>) -> Self { fn missing(ident: &'i Symbol<'i>) -> Self {
Object::Missing(ident) IdentObject::Missing(ident)
} }
fn ident(name: &'i Symbol<'i>, kind: IdentKind, src: Source<'i>) -> Self { fn ident(name: &'i Symbol<'i>, kind: IdentKind, src: Source<'i>) -> Self {
Object::Ident(name, kind, src) IdentObject::Ident(name, kind, src)
} }
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> Self { fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> Self {
Object::Extern(name, kind) IdentObject::Extern(name, kind)
} }
/// Attempt to redeclare an identifier with additional information. /// Attempt to redeclare an identifier with additional information.
/// ///
/// If an existing identifier is an [`Object::Extern`], /// If an existing identifier is an [`IdentObject::Extern`],
/// then the declaration will be compared just the same, /// then the declaration will be compared just the same,
/// but the identifier will be converted from an extern into an /// but the identifier will be converted from an extern into an
/// identifier. /// identifier.
/// When this happens, /// When this happens,
/// the extern is said to be _resolved_. /// the extern is said to be _resolved_.
/// ///
/// If a virtual identifier of type [`Object::IdentFragment`] is /// If a virtual identifier of type [`IdentObject::IdentFragment`] is
/// overridden, /// overridden,
/// then its fragment is cleared /// then its fragment is cleared
/// (it returns to a [`Object::Ident`]) /// (it returns to a [`IdentObject::Ident`])
/// to make way for the fragment of the override. /// to make way for the fragment of the override.
/// ///
/// The kind of identifier cannot change, /// The kind of identifier cannot change,
@ -232,9 +248,9 @@ impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
mut self, mut self,
kind: IdentKind, kind: IdentKind,
src: Source<'i>, src: Source<'i>,
) -> TransitionResult<Object<'i>> { ) -> TransitionResult<IdentObject<'i>> {
match self { match self {
Object::Ident(_, _, ref mut orig_src) IdentObject::Ident(_, _, ref mut orig_src)
if orig_src.virtual_ && src.override_ => if orig_src.virtual_ && src.override_ =>
{ {
*orig_src = src; *orig_src = src;
@ -242,15 +258,15 @@ impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
} }
// TODO: no override-override // TODO: no override-override
Object::IdentFragment(name, _, orig_src, _) IdentObject::IdentFragment(name, _, orig_src, _)
if orig_src.virtual_ && src.override_ => if orig_src.virtual_ && src.override_ =>
{ {
// clears fragment, which is no longer applicable // clears fragment, which is no longer applicable
Ok(Object::Ident(name, kind, src)) Ok(IdentObject::Ident(name, kind, src))
} }
Object::Missing(name) | Object::Ident(name, _, _) => { IdentObject::Missing(name) | IdentObject::Ident(name, _, _) => {
Ok(Object::Ident(name, kind, src)) Ok(IdentObject::Ident(name, kind, src))
} }
// TODO: incompatible (check now-dangling commits) // TODO: incompatible (check now-dangling commits)
@ -258,19 +274,24 @@ impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
} }
} }
fn set_fragment(self, text: FragmentText) -> TransitionResult<Object<'i>> { fn set_fragment(
self,
text: FragmentText,
) -> TransitionResult<IdentObject<'i>> {
match self { match self {
Object::Ident(sym, kind, src) => { IdentObject::Ident(sym, kind, src) => {
Ok(Object::IdentFragment(sym, kind, src, text)) Ok(IdentObject::IdentFragment(sym, kind, src, text))
} }
Object::IdentFragment(_, IdentKind::MapHead, _, _) IdentObject::IdentFragment(_, IdentKind::MapHead, _, _)
| Object::IdentFragment(_, IdentKind::MapTail, _, _) | IdentObject::IdentFragment(_, IdentKind::MapTail, _, _)
| Object::IdentFragment(_, IdentKind::RetMapHead, _, _) | IdentObject::IdentFragment(_, IdentKind::RetMapHead, _, _)
| Object::IdentFragment(_, IdentKind::RetMapTail, _, _) => Ok(self), | IdentObject::IdentFragment(_, IdentKind::RetMapTail, _, _) => {
Ok(self)
}
// TODO remove these ignores when fixed // TODO remove these ignores when fixed
Object::IdentFragment( IdentObject::IdentFragment(
sym, sym,
IdentKind::Map, IdentKind::Map,
Source { Source {
@ -286,7 +307,7 @@ impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
); );
Ok(self) Ok(self)
} }
Object::IdentFragment( IdentObject::IdentFragment(
sym, sym,
IdentKind::RetMap, IdentKind::RetMap,
Source { Source {
@ -304,8 +325,10 @@ impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
} }
_ => { _ => {
let msg = let msg = format!(
format!("identifier is not a Object::Ident): {:?}", self,); "identifier is not a IdentObject::Ident): {:?}",
self,
);
Err((self, TransitionError::BadFragmentDest(msg))) Err((self, TransitionError::BadFragmentDest(msg)))
} }
@ -313,7 +336,8 @@ impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
} }
} }
/// An error attempting to transition from one [`Object`] state to another. /// An error attempting to transition from one [`IdentObject`] state to
/// another.
/// ///
/// TODO: Provide enough information to construct a useful message. /// TODO: Provide enough information to construct a useful message.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -322,13 +346,13 @@ pub enum TransitionError {
/// has failed because the provided information was not compatible /// has failed because the provided information was not compatible
/// with the original declaration. /// with the original declaration.
/// ///
/// See [`ObjectState::redeclare`]. /// See [`IdentObjectState::redeclare`].
Incompatible(String), Incompatible(String),
/// The provided identifier is not in a state that is permitted to /// The provided identifier is not in a state that is permitted to
/// receive a fragment. /// receive a fragment.
/// ///
/// See [`ObjectState::set_fragment`]. /// See [`IdentObjectState::set_fragment`].
BadFragmentDest(String), BadFragmentDest(String),
} }
@ -448,9 +472,355 @@ impl<'i> From<SymAttrs<'i>> for Source<'i> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::super::ident::Dim;
use super::*; use super::*;
use crate::sym::SymbolIndex; use crate::sym::SymbolIndex;
mod ident_object_data {
use super::*;
// Note that IdentObject has no variants capable of None
#[test]
fn ident_object_name() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
assert_eq!(Some(&sym), IdentObject::Missing(&sym).name());
assert_eq!(
Some(&sym),
IdentObject::Ident(&sym, IdentKind::Meta, Source::default())
.name()
);
assert_eq!(
Some(&sym),
IdentObject::Extern(&sym, IdentKind::Meta).name()
);
assert_eq!(
Some(&sym),
IdentObject::IdentFragment(
&sym,
IdentKind::Meta,
Source::default(),
FragmentText::default(),
)
.name()
);
}
#[test]
fn ident_object_kind() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let kind = IdentKind::Class(Dim::from_u8(5));
assert_eq!(None, IdentObject::Missing(&sym).kind());
assert_eq!(
Some(&kind),
IdentObject::Ident(&sym, kind.clone(), Source::default())
.kind()
);
assert_eq!(
Some(&kind),
IdentObject::Extern(&sym, kind.clone()).kind()
);
assert_eq!(
Some(&kind),
IdentObject::IdentFragment(
&sym,
kind.clone(),
Source::default(),
FragmentText::default()
)
.kind()
);
}
#[test]
fn ident_object_src() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let src = Source {
desc: Some("test source".into()),
..Default::default()
};
assert_eq!(None, IdentObject::Missing(&sym).src());
assert_eq!(
Some(&src),
IdentObject::Ident(&sym, IdentKind::Meta, src.clone()).src()
);
assert_eq!(None, IdentObject::Extern(&sym, IdentKind::Meta).src());
assert_eq!(
Some(&src),
IdentObject::IdentFragment(
&sym,
IdentKind::Meta,
src.clone(),
FragmentText::default()
)
.src()
);
}
#[test]
fn ident_object_fragment() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let text: FragmentText = "foo".into();
assert_eq!(None, IdentObject::Missing(&sym).fragment());
assert_eq!(
None,
IdentObject::Ident(&sym, IdentKind::Meta, Source::default())
.fragment()
);
assert_eq!(
None,
IdentObject::Extern(&sym, IdentKind::Meta).fragment()
);
assert_eq!(
Some(&text),
IdentObject::IdentFragment(
&sym,
IdentKind::Meta,
Source::default(),
text.clone(),
)
.fragment()
);
}
#[test]
fn ident_object_as_ident() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let ident = IdentObject::Missing(&sym);
// Since we _are_ an IdentObject, we should return a reference
// to ourselves. We want this, not a clone.
assert!(std::ptr::eq(
&ident as *const _,
ident.as_ident().unwrap() as *const _,
));
}
}
mod ident_object_state {
use super::*;
#[test]
fn ident_object_missing() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "missing");
assert_eq!(IdentObject::Missing(&sym), IdentObject::missing(&sym));
}
#[test]
fn ident_object_ident() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "missing");
let kind = IdentKind::Meta;
let src = Source {
desc: Some("ident ctor".into()),
..Default::default()
};
assert_eq!(
IdentObject::Ident(&sym, kind.clone(), src.clone()),
IdentObject::ident(&sym, kind.clone(), src.clone()),
);
}
#[test]
fn ident_object_extern() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "missing");
let kind = IdentKind::Class(Dim::from_u8(1));
assert_eq!(
IdentObject::Extern(&sym, kind.clone()),
IdentObject::extern_(&sym, kind.clone()),
);
}
// TODO: incompatible
#[test]
fn redeclare_returns_existing_compatible() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "symdup");
let first =
IdentObject::ident(&sym, IdentKind::Meta, Source::default());
// Same declaration a second time
assert_eq!(
Ok(first.clone()),
first.clone().redeclare(
first.kind().unwrap().clone(),
first.src().unwrap().clone(),
)
);
}
#[test]
fn add_fragment_to_ident() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "tofrag");
let src = Source {
generated: true,
..Default::default()
};
let kind = IdentKind::Meta;
let ident = IdentObject::ident(&sym, kind.clone(), src.clone());
let text = FragmentText::from("a fragment");
let ident_with_frag = ident.set_fragment(text.clone());
assert_eq!(
Ok(IdentObject::IdentFragment(&sym, kind, src, text)),
ident_with_frag,
);
}
#[test]
fn add_fragment_to_fragment_fails() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "badsym");
let ident =
IdentObject::ident(&sym, IdentKind::Meta, Source::default());
let ident_with_frag = ident
.set_fragment("orig fragment".into())
.expect("set_fragment failed");
// Since it's already a fragment, this should fail.
let err = ident_with_frag
.clone()
.set_fragment("replacement".to_string())
.expect_err("Expected failure");
match err {
(orig, TransitionError::BadFragmentDest(str))
if str.contains("badsym") =>
{
assert_eq!(ident_with_frag, orig);
}
_ => panic!(
"expected TransitionError::BadFragmentDest: {:?}",
err
),
}
}
// TODO: incompatible
#[test]
fn declare_override_virtual_ident() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "virtual");
let over_src = Symbol::new_dummy(SymbolIndex::from_u32(2), "src");
let kind = IdentKind::Meta;
let virt = IdentObject::ident(
&sym,
kind.clone(),
Source {
virtual_: true,
..Default::default()
},
);
let over_src = Source {
override_: true,
src: Some(&over_src),
..Default::default()
};
let result = virt.redeclare(kind.clone(), over_src.clone());
assert_eq!(Ok(IdentObject::Ident(&sym, kind, over_src)), result);
}
// TODO: incompatible
#[test]
fn declare_override_virtual_ident_fragment() {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "virtual");
let over_src = Symbol::new_dummy(SymbolIndex::from_u32(2), "src");
let kind = IdentKind::Meta;
let virt_src = Source {
virtual_: true,
..Default::default()
};
let virt = IdentObject::ident(&sym, kind.clone(), virt_src.clone());
let text = FragmentText::from("remove me");
let virt_frag = virt.set_fragment(text.clone());
assert_eq!(
Ok(IdentObject::IdentFragment(
&sym,
kind.clone(),
virt_src,
text
)),
virt_frag,
);
let over_src = Source {
override_: true,
src: Some(&over_src),
..Default::default()
};
let result =
virt_frag.unwrap().redeclare(kind.clone(), over_src.clone());
// The act of overriding the object should have cleared any
// existing fragment, making way for a new fragment to take its
// place as soon as it is discovered. (So, back to an
// IdentObject::Ident.)
assert_eq!(Ok(IdentObject::Ident(&sym, kind, over_src)), result);
}
fn add_ident_kind_ignores(given: IdentKind, expected: IdentKind) {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "tofrag");
let src = Source {
generated: true,
..Default::default()
};
let obj = IdentObject::ident(&sym, given, src.clone());
let fragment = "a fragment".to_string();
let obj_with_frag = obj.set_fragment(fragment.clone());
assert_eq!(
Ok(IdentObject::IdentFragment(&sym, expected, src, fragment)),
obj_with_frag,
);
}
#[test]
fn add_fragment_to_ident_map_head() {
add_ident_kind_ignores(IdentKind::MapHead, IdentKind::MapHead)
}
#[test]
fn add_fragment_to_ident_map_tail() {
add_ident_kind_ignores(IdentKind::MapTail, IdentKind::MapTail)
}
#[test]
fn add_fragment_to_ident_retmap_head() {
add_ident_kind_ignores(IdentKind::RetMapHead, IdentKind::RetMapHead)
}
#[test]
fn add_fragment_to_ident_retmap_tail() {
add_ident_kind_ignores(IdentKind::RetMapTail, IdentKind::RetMapTail)
}
}
#[test] #[test]
fn source_from_sym_attrs() { fn source_from_sym_attrs() {
let nsym = Symbol::new_dummy(SymbolIndex::from_u32(1), "name"); let nsym = Symbol::new_dummy(SymbolIndex::from_u32(1), "name");

View File

@ -49,17 +49,17 @@ impl<'a, T> Section<'a, T> {
self.len() == 0 self.len() == 0
} }
/// Push an `Object` into a `Section`'s head /// Push an `IdentObject` into a `Section`'s head
pub fn push_head(&mut self, obj: &'a T) { pub fn push_head(&mut self, obj: &'a T) {
self.head.push(obj) self.head.push(obj)
} }
/// Push an `Object` into a `Section`'s body /// Push an `IdentObject` into a `Section`'s body
pub fn push_body(&mut self, obj: &'a T) { pub fn push_body(&mut self, obj: &'a T) {
self.body.push(obj) self.body.push(obj)
} }
/// Push an `Object` into a `Section`'s tail /// Push an `IdentObject` into a `Section`'s tail
pub fn push_tail(&mut self, obj: &'a T) { pub fn push_tail(&mut self, obj: &'a T) {
self.tail.push(obj) self.tail.push(obj)
} }
@ -71,12 +71,12 @@ impl<'a, T> Section<'a, T> {
/// method allows us to access the iterator. /// method allows us to access the iterator.
/// ///
/// ``` /// ```
/// use tamer::ir::asg::{Object, Section}; /// use tamer::ir::asg::{IdentObject, Section};
/// use tamer::sym::{DefaultInterner, Interner}; /// use tamer::sym::{DefaultInterner, Interner};
/// ///
/// let interner = DefaultInterner::new(); /// let interner = DefaultInterner::new();
/// let mut section = Section::new(); /// let mut section = Section::new();
/// let obj = Object::Missing(interner.intern("ident")); /// let obj = IdentObject::Missing(interner.intern("ident"));
/// let expect = vec![&obj, &obj, &obj]; /// let expect = vec![&obj, &obj, &obj];
/// ///
/// section.push_head(&obj); /// section.push_head(&obj);
@ -151,7 +151,7 @@ impl<'a, T> Sections<'a, T> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::ir::asg::Object; use crate::ir::asg::IdentObject;
use crate::sym::{Symbol, SymbolIndex}; use crate::sym::{Symbol, SymbolIndex};
lazy_static! { lazy_static! {
@ -159,7 +159,7 @@ mod test {
Symbol::new_dummy(SymbolIndex::from_u32(1), "sym"); Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
} }
type Sut<'a, 'i> = Section<'a, Object<'i>>; type Sut<'a, 'i> = Section<'a, IdentObject<'i>>;
#[test] #[test]
fn section_empty() { fn section_empty() {
@ -173,7 +173,7 @@ mod test {
#[test] #[test]
fn section_head() { fn section_head() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert!(section.head.is_empty()); assert!(section.head.is_empty());
@ -185,7 +185,7 @@ mod test {
#[test] #[test]
fn section_body() { fn section_body() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert!(section.body.is_empty()); assert!(section.body.is_empty());
@ -198,7 +198,7 @@ mod test {
#[test] #[test]
fn section_tail() { fn section_tail() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert!(section.tail.is_empty()); assert!(section.tail.is_empty());
@ -210,7 +210,7 @@ mod test {
#[test] #[test]
fn section_len() { fn section_len() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert_eq!(0, section.len()); assert_eq!(0, section.len());
section.push_head(&obj); section.push_head(&obj);
@ -224,7 +224,7 @@ mod test {
#[test] #[test]
fn section_is_empty_head() { fn section_is_empty_head() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert!(section.is_empty()); assert!(section.is_empty());
section.push_head(&obj); section.push_head(&obj);
@ -234,7 +234,7 @@ mod test {
#[test] #[test]
fn section_is_empty_body() { fn section_is_empty_body() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert!(section.is_empty()); assert!(section.is_empty());
section.push_body(&obj); section.push_body(&obj);
@ -244,7 +244,7 @@ mod test {
#[test] #[test]
fn section_is_empty_tail() { fn section_is_empty_tail() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
assert!(section.is_empty()); assert!(section.is_empty());
section.push_tail(&obj); section.push_tail(&obj);
@ -254,7 +254,7 @@ mod test {
#[test] #[test]
fn section_iterator() { fn section_iterator() {
let mut section = Sut::new(); let mut section = Sut::new();
let obj = Object::Missing(&SYM); let obj = IdentObject::Missing(&SYM);
let expect = vec![&obj, &obj, &obj]; let expect = vec![&obj, &obj, &obj];
section.push_head(&obj); section.push_head(&obj);

View File

@ -22,7 +22,7 @@
use crate::global; use crate::global;
use crate::ir::asg::{ use crate::ir::asg::{
Asg, DefaultAsg, IdentKind, Object, ObjectRef, Sections, SortableAsg, Asg, DefaultAsg, IdentKind, IdentObject, ObjectRef, Sections, SortableAsg,
Source, Source,
}; };
use crate::obj::xmle::writer::XmleWriter; use crate::obj::xmle::writer::XmleWriter;
@ -34,7 +34,7 @@ use std::error::Error;
use std::fs; use std::fs;
use std::io::BufReader; use std::io::BufReader;
type LinkerAsg<'i> = DefaultAsg<'i, Object<'i>, global::ProgIdentSize>; type LinkerAsg<'i> = DefaultAsg<'i, IdentObject<'i>, global::ProgIdentSize>;
type LinkerObjectRef = ObjectRef<global::ProgIdentSize>; type LinkerObjectRef = ObjectRef<global::ProgIdentSize>;
type LoadResult<'i> = type LoadResult<'i> =
@ -241,7 +241,7 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
fn get_ident<'a, 'i>( fn get_ident<'a, 'i>(
depgraph: &'a LinkerAsg<'i>, depgraph: &'a LinkerAsg<'i>,
name: &'i Symbol<'i>, name: &'i Symbol<'i>,
) -> Result<&'a Object<'i>, XmloError> { ) -> Result<&'a IdentObject<'i>, XmloError> {
depgraph depgraph
.lookup(name) .lookup(name)
.and_then(|id| depgraph.get(id)) .and_then(|id| depgraph.get(id))
@ -251,7 +251,7 @@ fn get_ident<'a, 'i>(
fn output_xmle<'a, 'i, I: Interner<'i>>( fn output_xmle<'a, 'i, I: Interner<'i>>(
depgraph: &'a LinkerAsg<'i>, depgraph: &'a LinkerAsg<'i>,
interner: &'i I, interner: &'i I,
sorted: &mut Sections<'a, Object<'i>>, sorted: &mut Sections<'a, IdentObject<'i>>,
name: &'i Symbol<'i>, name: &'i Symbol<'i>,
relroot: String, relroot: String,
output: &str, output: &str,

View File

@ -1,4 +1,4 @@
// Object file writer // IdentObject file writer
// //
// Copyright (C) 2014-2020 Ryan Specialty Group, LLC. // Copyright (C) 2014-2020 Ryan Specialty Group, LLC.
// //
@ -29,14 +29,14 @@
//! //!
//! ``` //! ```
//! use tamer::obj::xmle::writer::XmleWriter; //! use tamer::obj::xmle::writer::XmleWriter;
//! use tamer::ir::asg::{Object, Sections}; //! use tamer::ir::asg::{IdentObject, Sections};
//! use tamer::sym::{DefaultInterner, Interner, Symbol}; //! use tamer::sym::{DefaultInterner, Interner, Symbol};
//! use std::io::Cursor; //! use std::io::Cursor;
//! //!
//! let interner = DefaultInterner::new(); //! let interner = DefaultInterner::new();
//! let name = interner.intern(&String::from("foo")); //! let name = interner.intern(&String::from("foo"));
//! //!
//! let sections = Sections::<Object>::new(); //! let sections = Sections::<IdentObject>::new();
//! let writer = Cursor::new(Vec::new()); //! let writer = Cursor::new(Vec::new());
//! let mut xmle_writer = XmleWriter::new(writer); //! let mut xmle_writer = XmleWriter::new(writer);
//! xmle_writer.write(&sections, name, &String::from("")); //! xmle_writer.write(&sections, name, &String::from(""));

View File

@ -19,7 +19,7 @@
use super::writer::{Result, WriterError}; use super::writer::{Result, WriterError};
use crate::ir::asg::{ use crate::ir::asg::{
IdentKind, Object, ObjectData, SectionIterator, Sections, IdentKind, IdentObject, IdentObjectData, SectionIterator, Sections,
}; };
use crate::sym::Symbol; use crate::sym::Symbol;
use fxhash::FxHashSet; use fxhash::FxHashSet;
@ -72,12 +72,12 @@ impl<W: Write> XmleWriter<W> {
/// ``` /// ```
/// use std::io::Cursor; /// use std::io::Cursor;
/// use tamer::obj::xmle::writer::XmleWriter; /// use tamer::obj::xmle::writer::XmleWriter;
/// use tamer::ir::asg::{Sections, Object}; /// use tamer::ir::asg::{Sections, IdentObject};
/// use tamer::sym::{Symbol, SymbolIndex, DefaultInterner, Interner}; /// use tamer::sym::{Symbol, SymbolIndex, DefaultInterner, Interner};
/// ///
/// let writer = Cursor::new(Vec::new()); /// let writer = Cursor::new(Vec::new());
/// let mut xmle_writer = XmleWriter::new(writer); /// let mut xmle_writer = XmleWriter::new(writer);
/// let sections = Sections::<Object>::new(); /// let sections = Sections::<IdentObject>::new();
/// let a = "foo"; /// let a = "foo";
/// let interner = DefaultInterner::new(); /// let interner = DefaultInterner::new();
/// let name = interner.intern(&a); /// let name = interner.intern(&a);
@ -89,7 +89,7 @@ impl<W: Write> XmleWriter<W> {
/// let buf = xmle_writer.into_inner().into_inner(); /// let buf = xmle_writer.into_inner().into_inner();
/// assert!(!buf.is_empty(), "something was written to the buffer"); /// assert!(!buf.is_empty(), "something was written to the buffer");
/// ``` /// ```
pub fn write<'i, T: ObjectData<'i>>( pub fn write<'i, T: IdentObjectData<'i>>(
&mut self, &mut self,
sections: &Sections<T>, sections: &Sections<T>,
name: &Symbol, name: &Symbol,
@ -192,7 +192,7 @@ impl<W: Write> XmleWriter<W> {
/// ///
/// All the [`Sections`] found need to be written out using the `writer` /// All the [`Sections`] found need to be written out using the `writer`
/// object. /// object.
fn write_sections<'i, T: ObjectData<'i>>( fn write_sections<'i, T: IdentObjectData<'i>>(
&mut self, &mut self,
sections: &Sections<T>, sections: &Sections<T>,
relroot: &str, relroot: &str,
@ -210,12 +210,12 @@ impl<W: Write> XmleWriter<W> {
for obj in all { for obj in all {
let ident = obj let ident = obj
.ident() .as_ident()
.expect("internal error: encountered non-identifier object"); .expect("internal error: encountered non-identifier object");
match ident { match ident {
Object::Ident(sym, kind, src) IdentObject::Ident(sym, kind, src)
| Object::IdentFragment(sym, kind, src, _) => { | IdentObject::IdentFragment(sym, kind, src, _) => {
let name: &str = sym; let name: &str = sym;
// this'll be formalized more sanely // this'll be formalized more sanely
@ -305,7 +305,7 @@ impl<W: Write> XmleWriter<W> {
/// ///
/// If a `map` object has a `from` attribute in its source, we need to /// If a `map` object has a `from` attribute in its source, we need to
/// write them using the `writer`'s `write_event`. /// write them using the `writer`'s `write_event`.
fn write_froms<'i, T: ObjectData<'i>>( fn write_froms<'i, T: IdentObjectData<'i>>(
&mut self, &mut self,
sections: &Sections<T>, sections: &Sections<T>,
) -> Result<&mut XmleWriter<W>> { ) -> Result<&mut XmleWriter<W>> {
@ -339,26 +339,26 @@ impl<W: Write> XmleWriter<W> {
/// ///
/// Iterates through the parts of a `Section` and writes them using the /// Iterates through the parts of a `Section` and writes them using the
/// `writer`'s 'write_event`. /// `writer`'s 'write_event`.
fn write_section<'i, T: ObjectData<'i>>( fn write_section<'i, T: IdentObjectData<'i>>(
&mut self, &mut self,
idents: SectionIterator<T>, idents: SectionIterator<T>,
) -> Result<&mut XmleWriter<W>> { ) -> Result<&mut XmleWriter<W>> {
for obj in idents { for obj in idents {
let ident = obj let ident = obj
.ident() .as_ident()
.expect("internal error: encountered non-identifier object"); .expect("internal error: encountered non-identifier object");
match ident { match ident {
Object::IdentFragment(_, _, _, frag) => { IdentObject::IdentFragment(_, _, _, frag) => {
self.writer.write_event(Event::Text( self.writer.write_event(Event::Text(
BytesText::from_plain_str(frag), BytesText::from_plain_str(frag),
))?; ))?;
} }
// Cgen, Gen, and Lparam are not expected to be present, so we // Cgen, Gen, and Lparam are not expected to be present, so we
// can ignore them when we determeing when to return an Err. // can ignore them when we determeing when to return an Err.
Object::Ident(_, IdentKind::Cgen(_), _) IdentObject::Ident(_, IdentKind::Cgen(_), _)
| Object::Ident(_, IdentKind::Gen(_, _), _) | IdentObject::Ident(_, IdentKind::Gen(_, _), _)
| Object::Ident(_, IdentKind::Lparam(_, _), _) => (), | IdentObject::Ident(_, IdentKind::Lparam(_, _), _) => (),
obj => { obj => {
return Err(WriterError::ExpectedFragment(format!( return Err(WriterError::ExpectedFragment(format!(
"fragment expected: {:?}", "fragment expected: {:?}",
@ -509,7 +509,7 @@ mod test {
})); }));
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let obj = Object::IdentFragment( let obj = IdentObject::IdentFragment(
&sym, &sym,
IdentKind::Meta, IdentKind::Meta,
Source::default(), Source::default(),
@ -531,7 +531,7 @@ mod test {
})); }));
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let obj = Object::Ident( let obj = IdentObject::Ident(
&sym, &sym,
IdentKind::Cgen(Dim::default()), IdentKind::Cgen(Dim::default()),
Source::default(), Source::default(),
@ -552,7 +552,7 @@ mod test {
})); }));
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
let obj = Object::Missing(&sym); let obj = IdentObject::Missing(&sym);
let mut section = Section::new(); let mut section = Section::new();
section.push_body(&obj); section.push_body(&obj);
@ -592,7 +592,7 @@ mod test {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "random_symbol"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "random_symbol");
let object = let object =
Object::Ident(&sym, IdentKind::Worksheet, Source::default()); IdentObject::Ident(&sym, IdentKind::Worksheet, Source::default());
let mut sections = Sections::new(); let mut sections = Sections::new();
sections.map.push_body(&object); sections.map.push_body(&object);
sut.write_sections(&sections, &String::from(""))?; sut.write_sections(&sections, &String::from(""))?;
@ -662,7 +662,8 @@ mod test {
virtual_: true, virtual_: true,
..Default::default() ..Default::default()
}; };
let object = Object::Ident(&nsym, IdentKind::Worksheet, attrs.into()); let object =
IdentObject::Ident(&nsym, IdentKind::Worksheet, attrs.into());
let mut sections = Sections::new(); let mut sections = Sections::new();
sections.map.push_body(&object); sections.map.push_body(&object);
sut.write_sections(&sections, &String::from("root"))?; sut.write_sections(&sections, &String::from("root"))?;
@ -696,7 +697,7 @@ mod test {
let mut src = Source::default(); let mut src = Source::default();
src.from = Some(vec![&symb]); src.from = Some(vec![&symb]);
let object = Object::Ident(&sym, IdentKind::Worksheet, src); let object = IdentObject::Ident(&sym, IdentKind::Worksheet, src);
let mut sections = Sections::new(); let mut sections = Sections::new();
sections.map.push_body(&object); sections.map.push_body(&object);
sut.write_froms(&sections)?; sut.write_froms(&sections)?;
@ -715,7 +716,7 @@ mod test {
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "random_symbol"); let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "random_symbol");
let object = let object =
Object::Ident(&sym, IdentKind::Worksheet, Source::default()); IdentObject::Ident(&sym, IdentKind::Worksheet, Source::default());
let mut sections = Sections::new(); let mut sections = Sections::new();
sections.map.push_body(&object); sections.map.push_body(&object);
sut.write_froms(&sections)?; sut.write_froms(&sections)?;