TAMER: Make Asg generic over object

There's a lot here to make the object stored on the `Asg` generic.  This
introduces `ObjectState` for state transitions and `ObjectData` for pure
data retrieval.  This will allow not only for mocking, but will be useful to
enforce compile-time restrictions on the type of objects expected by the
linker vs. the compiler (e.g. the linker will not have expressions).

This commit intentionally leaves the corresponding tests in their original
location to prove that the functionality has not changed; they'll be moved
in a future commit.

This also leaves the names as "Object" to reduce the number the cognative
overhead of this commit.  It will be renamed to something like "IdentObject"
in the near future to clarify the intent of the current object type and to
open the way for expressions and a type that marries both of them in the
future.

Once all of this is done, we'll finally be able to make changes to the
compatibility logic in state transitions to implement extern compatibility
checks during resolution.

DEV-7087
master
Mike Gerwitz 2020-03-14 00:10:03 -04:00
parent f20120787f
commit 5fb68f9b67
9 changed files with 311 additions and 158 deletions

View File

@ -23,7 +23,7 @@ use super::graph::{
Asg, AsgEdge, AsgError, AsgResult, Node, ObjectRef, SortableAsg,
};
use super::ident::IdentKind;
use super::object::{FragmentText, Object, Source};
use super::object::{FragmentText, ObjectData, ObjectState, Source};
use super::Sections;
use crate::sym::Symbol;
use fixedbitset::FixedBitSet;
@ -42,9 +42,12 @@ use petgraph::visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable};
///
/// For more information,
/// see [`Asg`].
pub struct BaseAsg<'i, Ix: IndexType> {
pub struct BaseAsg<O, Ix>
where
Ix: IndexType,
{
/// Directed graph on which objects are stored.
graph: DiGraph<Node<'i>, AsgEdge, Ix>,
graph: DiGraph<Node<O>, AsgEdge, Ix>,
/// Map of [`SymbolIndex`][crate::sym::SymbolIndex] to node indexes.
///
@ -58,9 +61,10 @@ pub struct BaseAsg<'i, Ix: IndexType> {
empty_node: NodeIndex<Ix>,
}
impl<'i, Ix> BaseAsg<'i, Ix>
impl<'i, O, Ix> BaseAsg<O, Ix>
where
Ix: IndexType,
O: ObjectState<'i, O>,
{
/// Create an ASG with the provided initial capacity.
///
@ -121,12 +125,14 @@ where
self.index[i] = node;
}
/// Lookup `ident` or add an [`Object::Missing`] to the graph and
/// return a reference to it.
/// Lookup `ident` or add a missing identifier to the graph and return a
/// reference to it.
///
/// See [`ObjectState::missing`] for more information.
#[inline]
fn lookup_or_missing(&mut self, ident: &'i Symbol<'i>) -> ObjectRef<Ix> {
self.lookup(ident).unwrap_or_else(|| {
let index = self.graph.add_node(Some(Object::Missing(ident)));
let index = self.graph.add_node(Some(O::missing(ident)));
self.index_identifier(ident, index);
ObjectRef(index)
@ -134,9 +140,10 @@ where
}
}
impl<'i, Ix> Asg<'i, Ix> for BaseAsg<'i, Ix>
impl<'i, O, Ix> Asg<'i, O, Ix> for BaseAsg<O, Ix>
where
Ix: IndexType,
O: ObjectState<'i, O>,
{
fn declare(
&mut self,
@ -165,7 +172,7 @@ where
});
}
let node = self.graph.add_node(Some(Object::Ident(name, kind, src)));
let node = self.graph.add_node(Some(O::ident(name, kind, src)));
self.index_identifier(name, node);
@ -178,9 +185,7 @@ where
expected_kind: IdentKind,
) -> AsgResult<ObjectRef<Ix>> {
// TODO: resolution!
let node = self
.graph
.add_node(Some(Object::Extern(name, expected_kind)));
let node = self.graph.add_node(Some(O::extern_(name, expected_kind)));
self.index_identifier(name, node);
@ -219,7 +224,7 @@ where
}
#[inline]
fn get<I: Into<ObjectRef<Ix>>>(&self, index: I) -> Option<&Object<'i>> {
fn get<I: Into<ObjectRef<Ix>>>(&self, index: I) -> Option<&O> {
self.graph.node_weight(index.into().0).map(|node| {
node.as_ref()
.expect("internal error: BaseAsg::get missing Node data")
@ -259,12 +264,13 @@ where
}
}
impl<'a, 'i, Ix> SortableAsg<'a, 'i, Ix> for BaseAsg<'i, Ix>
impl<'i, O, Ix> SortableAsg<'i, O, Ix> for BaseAsg<O, Ix>
where
Ix: IndexType,
O: ObjectData<'i> + ObjectState<'i, O>,
{
fn sort(&'a self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'a, 'i>> {
let mut deps: Sections = Sections::new();
fn sort(&'i self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'i, O>> {
let mut deps = Sections::new();
// This is technically a topological sort, but functions have
// cycles. Once we have more symbol metadata, we can filter them out
@ -278,9 +284,8 @@ where
while let Some(index) = dfs.next(&self.graph) {
let ident = self.get(index).expect("missing node");
match ident {
Object::Ident(_, kind, _)
| Object::IdentFragment(_, kind, _, _) => match kind {
match ident.kind() {
Some(kind) => match kind {
IdentKind::Meta => deps.meta.push_body(ident),
IdentKind::Worksheet => deps.worksheet.push_body(ident),
IdentKind::Param(_, _) => deps.params.push_body(ident),
@ -294,10 +299,10 @@ where
| IdentKind::RetMapTail => deps.retmap.push_body(ident),
_ => deps.rater.push_body(ident),
},
_ => {
None => {
return Err(AsgError::UnexpectedNode(format!(
"{:?}",
ident
ident.ident()
)))
}
}
@ -308,7 +313,7 @@ where
}
// TODO: encapsulate Petgraph API (N.B. this is untested!)
impl<'i, Ix> Visitable for BaseAsg<'i, Ix>
impl<'i, O, Ix> Visitable for BaseAsg<O, Ix>
where
Ix: IndexType,
{
@ -323,7 +328,7 @@ where
}
}
impl<'i, Ix> GraphBase for BaseAsg<'i, Ix>
impl<'i, O, Ix> GraphBase for BaseAsg<O, Ix>
where
Ix: IndexType,
{
@ -331,7 +336,7 @@ where
type EdgeId = EdgeIndex<Ix>;
}
impl<'a, 'i, Ix> IntoNeighbors for &'a BaseAsg<'i, Ix>
impl<'a, 'i, O, Ix> IntoNeighbors for &'a BaseAsg<O, Ix>
where
Ix: IndexType,
{
@ -346,9 +351,11 @@ where
mod test {
use super::super::graph::AsgError;
use super::*;
use crate::ir::asg::Object;
use crate::sym::SymbolIndex;
type Sut<'i> = BaseAsg<'i, u8>;
// TODO: mock Object
type Sut<'i> = BaseAsg<Object<'i>, u8>;
#[test]
fn create_with_capacity() {

View File

@ -20,24 +20,31 @@
//! Abstract graph as the basis for concrete ASGs.
use super::ident::IdentKind;
use super::object::{FragmentText, Object, Source, TransitionError};
use super::object::{
FragmentText, ObjectData, ObjectState, Source, TransitionError,
};
use super::Sections;
use crate::sym::Symbol;
use petgraph::graph::{IndexType, NodeIndex};
use std::result::Result;
/// An abstract semantic graph of [objects][Object].
/// An abstract semantic graph of [objects][super::object].
///
/// This IR focuses on the definition and manipulation of objects and their
/// dependencies.
/// See [`Object`] for a summary of valid object state transitions.
/// See [`Object`](super::object::Object) for a summary of valid object
/// state transitions.
///
/// Objects are never deleted from the graph,
/// so [`ObjectRef`]s will remain valid for the lifetime of the ASG.
///
/// For more information,
/// see the [module-level documentation][self].
pub trait Asg<'i, Ix: IndexType> {
pub trait Asg<'i, O, Ix>
where
Ix: IndexType,
O: ObjectState<'i, O>,
{
/// Declare a concrete identifier.
///
/// An identifier declaration is similar to a declaration in a header
@ -54,22 +61,12 @@ pub trait Asg<'i, Ix: IndexType> {
/// then the operation will fail;
/// otherwise,
/// the existing identifier will be returned.
/// A successful declaration will add a [`Object::Ident`] to the graph
/// For more information on state transitions that can occur when
/// redeclaring an identifier that already exists,
/// see [`ObjectState::redeclare`].
///
/// A successful declaration will add an identifier to the graph
/// and return an [`ObjectRef`] reference.
///
/// If an existing identifier is an extern (see
/// [`Asg::declare_extern`]),
/// then the declaration will be compared just the same,
/// but the identifier will be converted from a
/// [`Object::Extern`] into a [`Object::Ident`].
/// When this happens,
/// the extern is said to be _resolved_.
///
/// If a virtual identifier of type [`Object::IdentFragment`] is
/// overridden,
/// then its fragment is cleared
/// (it returns to a [`Object::Ident`])
/// to make way for the fragment of the override.
fn declare(
&mut self,
name: &'i Symbol<'i>,
@ -81,7 +78,7 @@ pub trait Asg<'i, Ix: IndexType> {
///
/// An _extern_ declaration declares an identifier the same as
/// [`Asg::declare`],
/// but instead as [`Object::Extern`].
/// but omits source information.
/// Externs are identifiers that are expected to be defined somewhere
/// else ("externally"),
/// and are resolved at [link-time][crate::ld].
@ -93,6 +90,9 @@ pub trait Asg<'i, Ix: IndexType> {
/// the identifier will be immediately _resolved_ and the object
/// on the graph will not be altered.
/// Resolution will otherwise fail in error.
///
/// See [`ObjectState::extern_`] and [`ObjectState::redeclare`] for more
/// information on compatibility related to extern resolution.
fn declare_extern(
&mut self,
name: &'i Symbol<'i>,
@ -101,9 +101,9 @@ pub trait Asg<'i, Ix: IndexType> {
/// Set the fragment associated with a concrete identifier.
///
/// This changes the type of the identifier from [`Object::Ident`]
/// into [`Object::IdentFragment`],
/// which is intended for use by the [linker][crate::ld].
/// Fragments are intended for use by the [linker][crate::ld].
/// For more information,
/// see [`ObjectState::set_fragment`].
fn set_fragment(
&mut self,
identi: ObjectRef<Ix>,
@ -117,7 +117,7 @@ pub trait Asg<'i, Ix: IndexType> {
/// this should never fail so long as references are not shared
/// between multiple graphs.
/// It is nevertheless wrapped in an [`Option`] just in case.
fn get<I: Into<ObjectRef<Ix>>>(&self, index: I) -> Option<&Object<'i>>;
fn get<I: Into<ObjectRef<Ix>>>(&self, index: I) -> Option<&O>;
/// Attempt to retrieve an identifier from the graph by name.
///
@ -148,10 +148,10 @@ pub trait Asg<'i, Ix: IndexType> {
/// this method will add the dependency even if one or both of `ident`
/// or `dep` have not yet been declared.
/// In such a case,
/// an [`Object::Missing`] will be added as a placeholder for the
/// missing identifier,
/// a missing identifier will be added as a placeholder,
/// allowing the ASG to be built with partial information as
/// identifiers continue to be discovered.
/// See [`ObjectState::missing`] for more information.
///
/// References to both identifiers are returned in argument order.
fn add_dep_lookup(
@ -165,8 +165,12 @@ pub trait Asg<'i, Ix: IndexType> {
///
/// Allow a graph to be partitioned into different [`Sections`] that can be
/// used as an `Intermediate Representation`.
pub trait SortableAsg<'a, 'i, Ix: IndexType> {
fn sort(&'a self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'a, 'i>>;
pub trait SortableAsg<'i, O, Ix>
where
O: ObjectData<'i>,
Ix: IndexType,
{
fn sort(&'i self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'i, O>>;
}
/// A [`Result`] with a hard-coded [`AsgError`] error type.
@ -175,7 +179,7 @@ pub trait SortableAsg<'a, 'i, Ix: IndexType> {
/// fail in error.
pub type AsgResult<T> = Result<T, AsgError>;
/// Reference to an [object][Object] stored within the [`Asg`].
/// Reference to an [object][super::object] stored within the [`Asg`].
///
/// Object references are integer offsets,
/// not pointers.
@ -208,7 +212,7 @@ pub type AsgEdge = ();
///
/// Enclosed in an [`Option`] to permit moving owned values out of the
/// graph.
pub type Node<'i> = Option<Object<'i>>;
pub type Node<O> = Option<O>;
/// An error from an ASG operation.
///

View File

@ -67,7 +67,10 @@
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Be sure to choose size and initial capacities appropriate for your
//! // situation.
//! let mut asg = DefaultAsg::<global::PkgIdentSize>::with_capacity(1024, 1024);
//! let mut asg = DefaultAsg::<Object, global::PkgIdentSize>::with_capacity(
//! 1024,
//! 1024,
//! );
//!
//! let interner = DefaultInterner::new();
//! let identa_sym = interner.intern("identa");
@ -111,7 +114,10 @@
//! # use tamer::sym::{Interner, DefaultInterner};
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::<global::PkgIdentSize>::with_capacity(1024, 1024);
//! # let mut asg = DefaultAsg::<Object, global::PkgIdentSize>::with_capacity(
//! # 1024,
//! # 1024,
//! # );
//! # let interner = DefaultInterner::new();
//! #
//! let identa_sym = interner.intern("identa");
@ -154,7 +160,10 @@
//! # use tamer::sym::{Interner, DefaultInterner};
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::<global::PkgIdentSize>::with_capacity(1024, 1024);
//! # let mut asg = DefaultAsg::<Object, global::PkgIdentSize>::with_capacity(
//! # 1024,
//! # 1024,
//! # );
//! # let interner = DefaultInterner::new();
//! #
//! // Fragments can be attached to resolved identifiers.
@ -189,8 +198,8 @@ mod section;
pub use graph::{Asg, AsgError, AsgResult, ObjectRef, SortableAsg};
pub use ident::{Dim, IdentKind};
pub use object::{FragmentText, Object, Source};
pub use object::{FragmentText, Object, ObjectData, Source};
pub use section::{Section, SectionIterator, Sections};
/// Default concrete ASG implementation.
pub type DefaultAsg<'i, Ix> = base::BaseAsg<'i, Ix>;
pub type DefaultAsg<'i, O, Ix> = base::BaseAsg<O, Ix>;

View File

@ -27,8 +27,7 @@ use crate::ir::legacyir::SymAttrs;
use crate::sym::Symbol;
use std::result::Result;
pub type TransitionResult<'i> =
Result<Object<'i>, (Object<'i>, TransitionError)>;
pub type TransitionResult<T> = Result<T, (T, TransitionError)>;
/// Type of object.
///
@ -75,19 +74,165 @@ pub enum Object<'i> {
IdentFragment(&'i Symbol<'i>, IdentKind, Source<'i>, FragmentText),
}
impl<'i> Object<'i> {
/// Retrieve information about an [`Object`].
///
/// APIs should adhere to this trait rather than a concrete object type such
/// as [`Object`];
/// this allows other representations to be used,
/// while still permitting the use of matching on [`Object`] through
/// the use of [`ident`](ObjectData::ident).
///
/// Since an object implementing this trait may not be an identifier
/// (e.g. an expression),
/// even [`name`](ObjectData::name)---which
/// is used by all [`Object`] variants---returns
/// an [`Option`].
/// These methods also provide a convenient alternative to `match`ing on
/// data that may not be present in all variants.
pub trait ObjectData<'i> {
/// Identifier name.
///
/// If the object is not an identifier,
/// [`None`] is returned.
fn name(&self) -> Option<&'i Symbol<'i>>;
/// Identifier [`IdentKind`].
///
/// If the object does not have a kind
/// (as is the case with [`Object::Missing`]),
/// [`None`] is returned.
fn kind(&self) -> Option<&IdentKind>;
/// Identifier [`Source`].
///
/// If the object does not have source information
/// (as is the case with [`Object::Extern`]),
/// [`None`] is returned.
fn src(&self) -> Option<&Source<'i>>;
/// Object as an identifier ([`Object`]).
///
/// If the object is not or cannot be faithfully converted into an
/// [`Object`],
/// [`None`] is returned.
/// For example,
/// expressions will always yield [`None`].
///
/// This allows pattern matching on [`Object`] variants regardless of
/// the underlying object type.
fn ident(&self) -> Option<&Object<'i>>;
}
impl<'i> ObjectData<'i> for Object<'i> {
fn name(&self) -> Option<&'i Symbol<'i>> {
match self {
Self::Missing(name)
| Self::Ident(name, _, _)
| Self::Extern(name, _)
| Self::IdentFragment(name, _, _, _) => Some(name),
}
}
fn kind(&self) -> Option<&IdentKind> {
match self {
Self::Missing(_) => None,
Self::Ident(_, kind, _)
| Self::Extern(_, kind)
| Self::IdentFragment(_, kind, _, _) => Some(kind),
}
}
fn src(&self) -> Option<&Source<'i>> {
match self {
Self::Missing(_) | Self::Extern(_, _) => None,
Self::Ident(_, _, src) | Self::IdentFragment(_, _, src, _) => {
Some(src)
}
}
}
/// Expose underlying [`Object`].
///
/// This will never be [`None`] for this implementation.
/// However,
/// other [`ObjectData`] implementations may still result in [`None`],
/// so it's important _not_ to rely on this as an excuse to be lazy
/// with unwrapping.
#[inline]
fn ident(&self) -> Option<&Object<'i>> {
Some(&self)
}
}
/// Objects as a state machine.
pub trait ObjectState<'i, T>
where
T: ObjectState<'i, T>,
{
/// Produce an object representing a missing identifier.
fn missing(ident: &'i Symbol<'i>) -> T;
/// Produce an object representing a concrete identifier.
fn ident(name: &'i Symbol<'i>, kind: IdentKind, src: Source<'i>) -> T;
/// Produce an object representing an extern.
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> T;
/// Attempt to redeclare an identifier with additional information.
///
/// _TODO: Compatibility information._
/// For specific information on compatibility rules,
/// see implementers of this trait,
/// since rules may vary between implementations.
fn redeclare(self, kind: IdentKind, src: Source<'i>)
-> TransitionResult<T>;
/// Attach a code fragment (compiled text) to an identifier.
///
/// The kind if identifier cannot change,
/// This will fail if an identifier already has a fragment,
/// since only the owner of the identifier should be producing
/// compiled code.
/// Note, however, that an identifier's fragment may be cleared under
/// certain circumstances (such as symbol overrides),
/// making way for a new fragment to be set.
fn set_fragment(self, text: FragmentText) -> TransitionResult<T>;
}
impl<'i> ObjectState<'i, Object<'i>> for Object<'i> {
fn missing(ident: &'i Symbol<'i>) -> Self {
Object::Missing(ident)
}
fn ident(name: &'i Symbol<'i>, kind: IdentKind, src: Source<'i>) -> Self {
Object::Ident(name, kind, src)
}
fn extern_(name: &'i Symbol<'i>, kind: IdentKind) -> Self {
Object::Extern(name, kind)
}
/// Attempt to redeclare an identifier with additional information.
///
/// If an existing identifier is an [`Object::Extern`],
/// then the declaration will be compared just the same,
/// but the identifier will be converted from an extern into an
/// identifier.
/// When this happens,
/// the extern is said to be _resolved_.
///
/// If a virtual identifier of type [`Object::IdentFragment`] is
/// overridden,
/// then its fragment is cleared
/// (it returns to a [`Object::Ident`])
/// to make way for the fragment of the override.
///
/// The kind of 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(
fn redeclare(
mut self,
kind: IdentKind,
src: Source<'i>,
) -> TransitionResult<'i> {
) -> TransitionResult<Object<'i>> {
match self {
Object::Ident(_, _, ref mut orig_src)
if orig_src.virtual_ && src.override_ =>
@ -113,10 +258,7 @@ impl<'i> Object<'i> {
}
}
/// 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> {
fn set_fragment(self, text: FragmentText) -> TransitionResult<Object<'i>> {
match self {
Object::Ident(sym, kind, src) => {
Ok(Object::IdentFragment(sym, kind, src, text))
@ -180,13 +322,13 @@ pub enum TransitionError {
/// has failed because the provided information was not compatible
/// with the original declaration.
///
/// See [`Object::redeclare`].
/// See [`ObjectState::redeclare`].
Incompatible(String),
/// The provided identifier is not in a state that is permitted to
/// receive a fragment.
///
/// See [`Object::set_fragment`].
/// See [`ObjectState::set_fragment`].
BadFragmentDest(String),
}

View File

@ -17,31 +17,20 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use crate::ir::asg::Object;
type ObjectRef<'a, 'i> = &'a Object<'i>;
pub type ObjectVec<'a, 'i> = Vec<ObjectRef<'a, 'i>>;
/// A Section that needs to be written to the buffer
/// A section of an [object file](crate::obj).
///
/// Most sections will only need a `body`, but some innlude `head` and `tail`
/// information. Rather than dealing with those differently, each `Section`
/// will have a `head` and `tail` that are empty by default.
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Section<'a, 'i> {
head: ObjectVec<'a, 'i>,
body: ObjectVec<'a, 'i>,
tail: ObjectVec<'a, 'i>,
pub struct Section<'a, T> {
head: Vec<&'a T>,
body: Vec<&'a T>,
tail: Vec<&'a T>,
}
impl<'a, 'i> Section<'a, 'i> {
/// Constructor for Sections
///
/// ```
/// use tamer::ir::asg::Section;
///
/// let section = Section::new();
/// ```
impl<'a, T> Section<'a, T> {
/// New empty section.
pub fn new() -> Self {
Self {
head: Vec::new(),
@ -61,18 +50,18 @@ impl<'a, 'i> Section<'a, 'i> {
}
/// Push an `Object` into a `Section`'s head
pub fn push_head(&mut self, obj: ObjectRef<'a, 'i>) {
self.head.push(&obj)
pub fn push_head(&mut self, obj: &'a T) {
self.head.push(obj)
}
/// Push an `Object` into a `Section`'s body
pub fn push_body(&mut self, obj: ObjectRef<'a, 'i>) {
self.body.push(&obj)
pub fn push_body(&mut self, obj: &'a T) {
self.body.push(obj)
}
/// Push an `Object` into a `Section`'s tail
pub fn push_tail(&mut self, obj: ObjectRef<'a, 'i>) {
self.tail.push(&obj)
pub fn push_tail(&mut self, obj: &'a T) {
self.tail.push(obj)
}
/// Merge the parts of a `Section` into one [`SectionIterator`]
@ -99,7 +88,7 @@ impl<'a, 'i> Section<'a, 'i> {
/// assert_eq!(&obj, object);
/// }
/// ```
pub fn iter(&self) -> SectionIterator {
pub fn iter(&self) -> SectionIterator<T> {
SectionIterator {
inner: Box::new(
self.head
@ -115,12 +104,12 @@ impl<'a, 'i> Section<'a, 'i> {
/// Wrapper for an Iterator
///
/// This allows us to iterate over all parts of a [`Section`].
pub struct SectionIterator<'a, 'i> {
inner: Box<dyn Iterator<Item = &'a Object<'i>> + 'a>,
pub struct SectionIterator<'a, T> {
inner: Box<dyn Iterator<Item = &'a T> + 'a>,
}
impl<'a, 'i> Iterator for SectionIterator<'a, 'i> {
type Item = &'a Object<'i>;
impl<'a, T> Iterator for SectionIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
@ -132,25 +121,19 @@ impl<'a, 'i> Iterator for SectionIterator<'a, 'i> {
/// All the properties are public [`Section`] objects and will be accessed
/// directly by the the objects interacting with them.
#[derive(Debug, Default, PartialEq)]
pub struct Sections<'a, 'i> {
pub map: Section<'a, 'i>,
pub retmap: Section<'a, 'i>,
pub meta: Section<'a, 'i>,
pub worksheet: Section<'a, 'i>,
pub params: Section<'a, 'i>,
pub types: Section<'a, 'i>,
pub funcs: Section<'a, 'i>,
pub rater: Section<'a, 'i>,
pub struct Sections<'a, T> {
pub map: Section<'a, T>,
pub retmap: Section<'a, T>,
pub meta: Section<'a, T>,
pub worksheet: Section<'a, T>,
pub params: Section<'a, T>,
pub types: Section<'a, T>,
pub funcs: Section<'a, T>,
pub rater: Section<'a, T>,
}
impl<'a, 'i> Sections<'a, 'i> {
/// Constructor for Sections
///
/// ```
/// use tamer::ir::asg::Sections;
///
/// let sections = Sections::new();
/// ```
impl<'a, T> Sections<'a, T> {
/// New collection of empty sections.
pub fn new() -> Self {
Self {
map: Section::new(),
@ -168,6 +151,7 @@ impl<'a, 'i> Sections<'a, 'i> {
#[cfg(test)]
mod test {
use super::*;
use crate::ir::asg::Object;
use crate::sym::{Symbol, SymbolIndex};
lazy_static! {
@ -175,9 +159,11 @@ mod test {
Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
}
type Sut<'a, 'i> = Section<'a, Object<'i>>;
#[test]
fn section_empty() {
let section = Section::new();
let section = Sut::new();
assert!(section.head.is_empty());
assert!(section.body.is_empty());
@ -186,7 +172,7 @@ mod test {
#[test]
fn section_head() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert!(section.head.is_empty());
@ -198,7 +184,7 @@ mod test {
#[test]
fn section_body() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert!(section.body.is_empty());
@ -211,7 +197,7 @@ mod test {
#[test]
fn section_tail() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert!(section.tail.is_empty());
@ -223,7 +209,7 @@ mod test {
#[test]
fn section_len() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert_eq!(0, section.len());
@ -237,7 +223,7 @@ mod test {
#[test]
fn section_is_empty_head() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert!(section.is_empty());
@ -247,7 +233,7 @@ mod test {
#[test]
fn section_is_empty_body() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert!(section.is_empty());
@ -257,7 +243,7 @@ mod test {
#[test]
fn section_is_empty_tail() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
assert!(section.is_empty());
@ -267,7 +253,7 @@ mod test {
#[test]
fn section_iterator() {
let mut section = Section::new();
let mut section = Sut::new();
let obj = Object::Missing(&SYM);
let expect = vec![&obj, &obj, &obj];

View File

@ -34,8 +34,8 @@ use std::error::Error;
use std::fs;
use std::io::BufReader;
type LinkerAsg<'i> = DefaultAsg<'i, Object<'i>, global::ProgIdentSize>;
type LinkerObjectRef = ObjectRef<global::ProgIdentSize>;
type LinkerAsg<'i> = DefaultAsg<'i, global::ProgIdentSize>;
type LoadResult<'i> =
Result<Option<(Option<&'i Symbol<'i>>, Option<String>)>, Box<dyn Error>>;
@ -251,7 +251,7 @@ fn get_ident<'a, 'i>(
fn output_xmle<'a, 'i, I: Interner<'i>>(
depgraph: &'a LinkerAsg<'i>,
interner: &'i I,
sorted: &mut Sections<'a, 'i>,
sorted: &mut Sections<'a, Object<'i>>,
name: &'i Symbol<'i>,
relroot: String,
output: &str,

View File

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

View File

@ -30,9 +30,9 @@ pub type Result<T = ()> = result::Result<T, WriterError>;
///
/// This is used to take the [`Sections`] and write out the xmle files.
pub trait Writer<W: Write> {
fn write(
fn write<T>(
&mut self,
sections: &Sections,
sections: &Sections<T>,
name: Symbol,
relroot: &str,
) -> Result<()>

View File

@ -18,7 +18,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use super::writer::{Result, WriterError};
use crate::ir::asg::{IdentKind, Object, SectionIterator, Sections};
use crate::ir::asg::{
IdentKind, Object, ObjectData, SectionIterator, Sections,
};
use crate::sym::Symbol;
use fxhash::FxHashSet;
#[cfg(test)]
@ -70,13 +72,12 @@ impl<W: Write> XmleWriter<W> {
/// ```
/// use std::io::Cursor;
/// use tamer::obj::xmle::writer::XmleWriter;
/// use tamer::ir::asg::Sections;
/// use tamer::sym::{Symbol, SymbolIndex};
/// use tamer::sym::{DefaultInterner, Interner};
/// use tamer::ir::asg::{Sections, Object};
/// use tamer::sym::{Symbol, SymbolIndex, DefaultInterner, Interner};
///
/// let writer = Cursor::new(Vec::new());
/// let mut xmle_writer = XmleWriter::new(writer);
/// let sections = Sections::new();
/// let sections = Sections::<Object>::new();
/// let a = "foo";
/// let interner = DefaultInterner::new();
/// let name = interner.intern(&a);
@ -88,9 +89,9 @@ impl<W: Write> XmleWriter<W> {
/// let buf = xmle_writer.into_inner().into_inner();
/// assert!(!buf.is_empty(), "something was written to the buffer");
/// ```
pub fn write(
pub fn write<'i, T: ObjectData<'i>>(
&mut self,
sections: &Sections,
sections: &Sections<T>,
name: &Symbol,
relroot: &str,
) -> Result {
@ -191,9 +192,9 @@ impl<W: Write> XmleWriter<W> {
///
/// All the [`Sections`] found need to be written out using the `writer`
/// object.
fn write_sections(
fn write_sections<'i, T: ObjectData<'i>>(
&mut self,
sections: &Sections,
sections: &Sections<T>,
relroot: &str,
) -> Result<&mut XmleWriter<W>> {
let all = sections
@ -207,7 +208,11 @@ impl<W: Write> XmleWriter<W> {
.chain(sections.funcs.iter())
.chain(sections.rater.iter());
for ident in all {
for obj in all {
let ident = obj
.ident()
.expect("internal error: encountered non-identifier object");
match ident {
Object::Ident(sym, kind, src)
| Object::IdentFragment(sym, kind, src, _) => {
@ -300,25 +305,21 @@ impl<W: Write> XmleWriter<W> {
///
/// If a `map` object has a `from` attribute in its source, we need to
/// write them using the `writer`'s `write_event`.
fn write_froms(
fn write_froms<'i, T: ObjectData<'i>>(
&mut self,
sections: &Sections,
sections: &Sections<T>,
) -> Result<&mut XmleWriter<W>> {
let mut map_froms: FxHashSet<&str> = Default::default();
let map_iter = sections.map.iter();
for map_ident in map_iter {
match map_ident {
Object::Ident(_, _, src)
| Object::IdentFragment(_, _, src, _) => {
if let Some(froms) = &src.from {
for from in froms {
map_froms.insert(from);
}
}
let src = map_ident.src().expect("internal error: missing map src");
if let Some(froms) = &src.from {
for from in froms {
map_froms.insert(from);
}
_ => unreachable!("filtered out during sorting"),
}
}
@ -338,11 +339,15 @@ impl<W: Write> XmleWriter<W> {
///
/// Iterates through the parts of a `Section` and writes them using the
/// `writer`'s 'write_event`.
fn write_section(
fn write_section<'i, T: ObjectData<'i>>(
&mut self,
idents: SectionIterator,
idents: SectionIterator<T>,
) -> Result<&mut XmleWriter<W>> {
for ident in idents {
for obj in idents {
let ident = obj
.ident()
.expect("internal error: encountered non-identifier object");
match ident {
Object::IdentFragment(_, _, _, frag) => {
self.writer.write_event(Event::Text(