[DEV-7085] Create `SortableAsg` trait
Merge branch 'jira-7085' * jira-7085: TAMER: Tidy up graph_sort test [DEV-7085] Create `SortableAsg` trait [DEV-7085] Implement `PartialEq` for `Sections` [DEV-7085] Move sections to IR modulemaster
commit
bc976b43cd
|
@ -19,15 +19,18 @@
|
|||
|
||||
//! Base concrete [`Asg`] implementation.
|
||||
|
||||
use super::graph::{Asg, AsgEdge, AsgError, AsgResult, Node, ObjectRef};
|
||||
use super::graph::{
|
||||
Asg, AsgEdge, AsgError, AsgResult, Node, ObjectRef, SortableAsg,
|
||||
};
|
||||
use super::ident::IdentKind;
|
||||
use super::object::{FragmentText, Object, Source};
|
||||
use super::Sections;
|
||||
use crate::sym::Symbol;
|
||||
use fixedbitset::FixedBitSet;
|
||||
use petgraph::graph::{
|
||||
DiGraph, EdgeIndex, Graph, IndexType, Neighbors, NodeIndex,
|
||||
};
|
||||
use petgraph::visit::{GraphBase, IntoNeighbors, Visitable};
|
||||
use petgraph::visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable};
|
||||
|
||||
/// Concrete ASG.
|
||||
///
|
||||
|
@ -302,6 +305,54 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, 'i, Ix> SortableAsg<'a, 'i, Ix> for BaseAsg<'i, Ix>
|
||||
where
|
||||
Ix: IndexType,
|
||||
{
|
||||
fn sort(&'a self, roots: &[ObjectRef<Ix>]) -> AsgResult<Sections<'a, 'i>> {
|
||||
let mut deps: Sections = Sections::new();
|
||||
|
||||
// This is technically a topological sort, but functions have
|
||||
// cycles. Once we have more symbol metadata, we can filter them out
|
||||
// and actually invoke toposort.
|
||||
let mut dfs = DfsPostOrder::empty(&self.graph);
|
||||
|
||||
for index in roots {
|
||||
dfs.stack.push((*index).into());
|
||||
}
|
||||
|
||||
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 {
|
||||
IdentKind::Meta => deps.meta.push_body(ident),
|
||||
IdentKind::Worksheet => deps.worksheet.push_body(ident),
|
||||
IdentKind::Param(_, _) => deps.params.push_body(ident),
|
||||
IdentKind::Type(_) => deps.types.push_body(ident),
|
||||
IdentKind::Func(_, _) => deps.funcs.push_body(ident),
|
||||
IdentKind::MapHead
|
||||
| IdentKind::Map
|
||||
| IdentKind::MapTail => deps.map.push_body(ident),
|
||||
IdentKind::RetMapHead
|
||||
| IdentKind::RetMap
|
||||
| IdentKind::RetMapTail => deps.retmap.push_body(ident),
|
||||
_ => deps.rater.push_body(ident),
|
||||
},
|
||||
_ => {
|
||||
return Err(AsgError::UnexpectedNode(format!(
|
||||
"{:?}",
|
||||
ident
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(deps)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: encapsulate Petgraph API (N.B. this is untested!)
|
||||
impl<'i, Ix> Visitable for BaseAsg<'i, Ix>
|
||||
where
|
||||
|
@ -724,4 +775,132 @@ mod test {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! assert_section_sym {
|
||||
( $iter:expr, $s:ident ) => {{
|
||||
let mut pos = 0;
|
||||
for obj in $iter {
|
||||
match obj {
|
||||
Object::Ident(sym, _, _)
|
||||
| Object::IdentFragment(sym, _, _, _) => {
|
||||
assert_eq!($s.get(pos), Some(*sym));
|
||||
}
|
||||
_ => panic!("unexpected object"),
|
||||
}
|
||||
|
||||
pos = pos + 1;
|
||||
}
|
||||
};};
|
||||
}
|
||||
|
||||
macro_rules! add_syms {
|
||||
($sut:ident, $base:expr, {$($dest:ident <- $name:ident: $kind:path,)*}) => {
|
||||
let mut i = 1;
|
||||
|
||||
$(
|
||||
i += 1;
|
||||
|
||||
let sym = Symbol::new_dummy(
|
||||
SymbolIndex::from_u32(i),
|
||||
stringify!($name)
|
||||
);
|
||||
|
||||
$sut.declare(&sym, $kind, Source::default())?;
|
||||
let (_, _) = $sut.add_dep_lookup($base, &sym);
|
||||
|
||||
$dest.push(sym);
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_sort() -> AsgResult<()> {
|
||||
let mut sut = Sut::with_capacity(0, 0);
|
||||
|
||||
let mut meta = vec![];
|
||||
let mut worksheet = vec![];
|
||||
let mut map = vec![];
|
||||
let mut retmap = vec![];
|
||||
|
||||
let base = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym1");
|
||||
let base_node =
|
||||
sut.declare(&base, IdentKind::Map, Source::default())?;
|
||||
|
||||
add_syms!(sut, &base, {
|
||||
meta <- meta1: IdentKind::Meta,
|
||||
worksheet <- work1: IdentKind::Worksheet,
|
||||
map <- map1: IdentKind::MapHead,
|
||||
map <- map2: IdentKind::Map,
|
||||
map <- map3: IdentKind::MapTail,
|
||||
retmap <- retmap1: IdentKind::RetMapHead,
|
||||
retmap <- retmap2: IdentKind::RetMap,
|
||||
retmap <- retmap3: IdentKind::RetMapTail,
|
||||
retmap <- retmap4: IdentKind::RetMapTail,
|
||||
retmap <- retmap5: IdentKind::RetMap,
|
||||
map <- map4: IdentKind::MapHead,
|
||||
map <- map5: IdentKind::Map,
|
||||
meta <- meta2: IdentKind::Meta,
|
||||
worksheet <- work2: IdentKind::Worksheet,
|
||||
map <- map6: IdentKind::MapTail,
|
||||
retmap <- retmap6: IdentKind::RetMapHead,
|
||||
});
|
||||
|
||||
map.push(base);
|
||||
|
||||
let sections = sut.sort(&vec![base_node])?;
|
||||
|
||||
assert_section_sym!(sections.meta.iter(), meta);
|
||||
assert_section_sym!(sections.worksheet.iter(), worksheet);
|
||||
assert_section_sym!(sections.map.iter(), map);
|
||||
assert_section_sym!(sections.retmap.iter(), retmap);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_sort_missing_node() -> AsgResult<()> {
|
||||
let mut sut = Sut::with_capacity(0, 0);
|
||||
|
||||
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
|
||||
let dep = Symbol::new_dummy(SymbolIndex::from_u32(2), "dep");
|
||||
|
||||
let sym_node = sut.declare(
|
||||
&sym,
|
||||
IdentKind::Tpl,
|
||||
Source {
|
||||
virtual_: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
sut.set_fragment(sym_node, FragmentText::from("foo"))?;
|
||||
|
||||
let (_, _) = sut.add_dep_lookup(&sym, &dep);
|
||||
|
||||
match sut.sort(&vec![sym_node]) {
|
||||
Ok(_) => panic!("Unexpected success - dependency is not in graph"),
|
||||
Err(AsgError::UnexpectedNode(_)) => (),
|
||||
_ => {
|
||||
panic!("Incorrect error result when dependency is not in graph")
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn graph_sort_no_roots() -> AsgResult<()> {
|
||||
let mut sut = Sut::with_capacity(0, 0);
|
||||
|
||||
let sym = Symbol::new_dummy(SymbolIndex::from_u32(1), "sym");
|
||||
let dep = Symbol::new_dummy(SymbolIndex::from_u32(2), "dep");
|
||||
|
||||
let (_, _) = sut.add_dep_lookup(&sym, &dep);
|
||||
|
||||
let sections = sut.sort(&vec![])?;
|
||||
|
||||
assert_eq!(Sections::new(), sections);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
use super::ident::IdentKind;
|
||||
use super::object::{FragmentText, Object, Source};
|
||||
use super::Sections;
|
||||
use crate::sym::Symbol;
|
||||
use petgraph::graph::{IndexType, NodeIndex};
|
||||
use std::result::Result;
|
||||
|
@ -160,6 +161,14 @@ pub trait Asg<'i, Ix: IndexType> {
|
|||
) -> (ObjectRef<Ix>, ObjectRef<Ix>);
|
||||
}
|
||||
|
||||
/// Sort a graph into different [`Sections`]
|
||||
///
|
||||
/// 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>>;
|
||||
}
|
||||
|
||||
/// A [`Result`] with a hard-coded [`AsgError`] error type.
|
||||
///
|
||||
/// This is the result of every [`Asg`] operation that could potentially
|
||||
|
|
|
@ -185,10 +185,12 @@ mod base;
|
|||
mod graph;
|
||||
mod ident;
|
||||
mod object;
|
||||
mod section;
|
||||
|
||||
pub use graph::{Asg, AsgError, AsgResult, ObjectRef};
|
||||
pub use graph::{Asg, AsgError, AsgResult, ObjectRef, SortableAsg};
|
||||
pub use ident::{Dim, IdentKind};
|
||||
pub use object::{FragmentText, Object, Source};
|
||||
pub use section::{Section, SectionIterator, Sections};
|
||||
|
||||
/// Default concrete ASG implementation.
|
||||
pub type DefaultAsg<'i, Ix> = base::BaseAsg<'i, Ix>;
|
||||
|
|
|
@ -0,0 +1,274 @@
|
|||
// Section/Sections IR representation
|
||||
//
|
||||
// Copyright (C) 2014-2020 Ryan Specialty Group, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// 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
|
||||
///
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Section<'a, 'i> {
|
||||
/// Constructor for Sections
|
||||
///
|
||||
/// ```
|
||||
/// use tamer::ir::asg::Section;
|
||||
///
|
||||
/// let section = Section::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
head: Vec::new(),
|
||||
body: Vec::new(),
|
||||
tail: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The length of the `Section`
|
||||
pub fn len(&self) -> usize {
|
||||
self.head.len() + self.body.len() + self.tail.len()
|
||||
}
|
||||
|
||||
/// Check if the `Section` is empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Push an `Object` into a `Section`'s head
|
||||
pub fn push_head(&mut self, obj: ObjectRef<'a, 'i>) {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Push an `Object` into a `Section`'s tail
|
||||
pub fn push_tail(&mut self, obj: ObjectRef<'a, 'i>) {
|
||||
self.tail.push(&obj)
|
||||
}
|
||||
|
||||
/// Merge the parts of a `Section` into one [`SectionIterator`]
|
||||
///
|
||||
/// The `Section` internals need to be iterated as a group so we needed to
|
||||
/// create a custom iterator, [`SectionIterator`] to do this for us. This
|
||||
/// method allows us to access the iterator.
|
||||
///
|
||||
/// ```
|
||||
/// use tamer::ir::asg::{Object, Section};
|
||||
///
|
||||
/// let mut section = Section::new();
|
||||
/// let obj = Object::Empty;
|
||||
/// let expect = vec![&obj, &obj, &obj];
|
||||
///
|
||||
/// section.push_head(&obj);
|
||||
/// section.push_body(&obj);
|
||||
/// section.push_tail(&obj);
|
||||
/// let section_iter = section.iter();
|
||||
///
|
||||
/// for object in section_iter {
|
||||
/// assert_eq!(&obj, object);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn iter(&self) -> SectionIterator {
|
||||
SectionIterator {
|
||||
inner: Box::new(
|
||||
self.head
|
||||
.iter()
|
||||
.chain(self.body.iter())
|
||||
.chain(self.tail.iter())
|
||||
.copied(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Iterator for SectionIterator<'a, 'i> {
|
||||
type Item = &'a Object<'i>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sections that need to be written to a buffer
|
||||
///
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Sections<'a, 'i> {
|
||||
/// Constructor for Sections
|
||||
///
|
||||
/// ```
|
||||
/// use tamer::ir::asg::Sections;
|
||||
///
|
||||
/// let sections = Sections::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: Section::new(),
|
||||
retmap: Section::new(),
|
||||
meta: Section::new(),
|
||||
worksheet: Section::new(),
|
||||
params: Section::new(),
|
||||
types: Section::new(),
|
||||
funcs: Section::new(),
|
||||
rater: Section::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn section_empty() {
|
||||
let section = Section::new();
|
||||
|
||||
assert!(section.head.is_empty());
|
||||
assert!(section.body.is_empty());
|
||||
assert!(section.tail.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_head() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.head.is_empty());
|
||||
|
||||
section.push_head(&obj);
|
||||
|
||||
assert_eq!(Some(&&obj), section.head.get(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_body() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.body.is_empty());
|
||||
|
||||
section.push_body(&obj);
|
||||
|
||||
let body = section.body;
|
||||
assert_eq!(Some(&&obj), body.get(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_tail() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.tail.is_empty());
|
||||
|
||||
section.push_tail(&obj);
|
||||
|
||||
assert_eq!(Some(&&obj), section.tail.get(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_len() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert_eq!(0, section.len());
|
||||
section.push_head(&obj);
|
||||
assert_eq!(1, section.len());
|
||||
section.push_body(&obj);
|
||||
assert_eq!(2, section.len());
|
||||
section.push_tail(&obj);
|
||||
assert_eq!(3, section.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_is_empty_head() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.is_empty());
|
||||
section.push_head(&obj);
|
||||
assert!(!section.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_is_empty_body() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.is_empty());
|
||||
section.push_body(&obj);
|
||||
assert!(!section.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_is_empty_tail() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.is_empty());
|
||||
section.push_tail(&obj);
|
||||
assert!(!section.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_iterator() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
let expect = vec![&obj, &obj, &obj];
|
||||
|
||||
section.push_head(&obj);
|
||||
section.push_body(&obj);
|
||||
section.push_tail(&obj);
|
||||
|
||||
let collection: Vec<_> = section.iter().collect();
|
||||
|
||||
assert_eq!(expect, collection);
|
||||
}
|
||||
}
|
|
@ -22,20 +22,20 @@
|
|||
|
||||
use crate::global;
|
||||
use crate::ir::asg::{
|
||||
Asg, AsgError, DefaultAsg, IdentKind, Object, ObjectRef, Source,
|
||||
Asg, DefaultAsg, IdentKind, Object, ObjectRef, Sections, SortableAsg,
|
||||
Source,
|
||||
};
|
||||
use crate::obj::xmle::writer::{Sections, XmleWriter};
|
||||
use crate::obj::xmle::writer::XmleWriter;
|
||||
use crate::obj::xmlo::reader::{XmloError, XmloEvent, XmloReader};
|
||||
use crate::sym::{DefaultInterner, Interner, Symbol};
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use petgraph::visit::DfsPostOrder;
|
||||
use std::convert::TryInto;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::io::BufReader;
|
||||
|
||||
type LinkerAsg<'i> = DefaultAsg<'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>>;
|
||||
|
@ -78,7 +78,7 @@ pub fn main(package_path: &str, output: &str) -> Result<(), Box<dyn Error>> {
|
|||
.filter_map(|sym| depgraph.lookup(sym)),
|
||||
);
|
||||
|
||||
let mut sorted = sort_deps(&depgraph, &roots)?;
|
||||
let mut sorted = depgraph.sort(&roots)?;
|
||||
|
||||
//println!("Sorted ({}): {:?}", sorted.len(), sorted);
|
||||
|
||||
|
@ -238,58 +238,6 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
|
|||
}
|
||||
}
|
||||
|
||||
fn sort_deps<'a, 'i>(
|
||||
depgraph: &'a LinkerAsg<'i>,
|
||||
roots: &Vec<LinkerObjectRef>,
|
||||
) -> Result<Sections<'a, 'i>, Box<dyn Error>> {
|
||||
// @type=meta, @preproc:elig-class-yields
|
||||
// @type={ret}map{,:head,:tail}
|
||||
|
||||
let mut deps: Sections = Sections::new();
|
||||
|
||||
// This is technically a topological sort, but functions have
|
||||
// cycles. Once we have more symbol metadata, we can filter them out
|
||||
// and actually invoke toposort.
|
||||
let mut dfs = DfsPostOrder::empty(&depgraph);
|
||||
|
||||
//println!("discovered roots: {:?}", roots);
|
||||
|
||||
// TODO: we'll be processing various roots separately
|
||||
for index in roots {
|
||||
dfs.stack.push((*index).into());
|
||||
}
|
||||
|
||||
// TODO: can we encapsulate NodeIndex?
|
||||
while let Some(index) = dfs.next(&depgraph) {
|
||||
let ident = depgraph.get(index).expect("missing node");
|
||||
|
||||
match ident {
|
||||
Object::Ident(_, kind, _)
|
||||
| Object::IdentFragment(_, kind, _, _) => match kind {
|
||||
IdentKind::Meta => deps.meta.push_body(ident),
|
||||
IdentKind::Worksheet => deps.worksheet.push_body(ident),
|
||||
IdentKind::Param(_, _) => deps.params.push_body(ident),
|
||||
IdentKind::Type(_) => deps.types.push_body(ident),
|
||||
IdentKind::Func(_, _) => deps.funcs.push_body(ident),
|
||||
IdentKind::MapHead | IdentKind::Map | IdentKind::MapTail => {
|
||||
deps.map.push_body(ident)
|
||||
}
|
||||
IdentKind::RetMapHead
|
||||
| IdentKind::RetMap
|
||||
| IdentKind::RetMapTail => deps.retmap.push_body(ident),
|
||||
_ => deps.rater.push_body(ident),
|
||||
},
|
||||
_ => {
|
||||
return Err(
|
||||
AsgError::UnexpectedNode(format!("{:?}", ident)).into()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(deps)
|
||||
}
|
||||
|
||||
fn get_interner_value<'a, 'i, I: Interner<'i>>(
|
||||
depgraph: &'a LinkerAsg<'i>,
|
||||
interner: &'i I,
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
//! The example below is incomplete, but shows the general usage.
|
||||
//!
|
||||
//! ```
|
||||
//! use tamer::obj::xmle::writer::{Sections, XmleWriter};
|
||||
//! use tamer::obj::xmle::writer::XmleWriter;
|
||||
//! use tamer::ir::asg::Sections;
|
||||
//! use tamer::sym::{DefaultInterner, Interner, Symbol};
|
||||
//! use std::io::Cursor;
|
||||
//!
|
||||
|
@ -44,6 +45,6 @@
|
|||
mod writer;
|
||||
mod xmle;
|
||||
|
||||
pub use writer::{Result, Section, Sections, Writer, WriterError};
|
||||
pub use writer::{Result, Writer, WriterError};
|
||||
|
||||
pub use xmle::XmleWriter;
|
||||
|
|
|
@ -17,16 +17,14 @@
|
|||
// 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;
|
||||
use crate::ir::asg::Sections;
|
||||
use crate::sym::Symbol;
|
||||
use quick_xml::Error as XmlError;
|
||||
use std::io::{Error as IoError, Write};
|
||||
use std::result;
|
||||
use std::str::Utf8Error;
|
||||
|
||||
type ObjectRef<'a, 'i> = &'a Object<'i>;
|
||||
pub type Result<T = ()> = result::Result<T, WriterError>;
|
||||
pub type ObjectVec<'a, 'i> = Vec<ObjectRef<'a, 'i>>;
|
||||
|
||||
/// A wrapper around a `Write` object
|
||||
///
|
||||
|
@ -42,148 +40,6 @@ pub trait Writer<W: Write> {
|
|||
Self: Sized;
|
||||
}
|
||||
|
||||
/// A Section that needs to be written to the buffer
|
||||
///
|
||||
/// 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, Default)]
|
||||
pub struct Section<'a, 'i> {
|
||||
head: ObjectVec<'a, 'i>,
|
||||
body: ObjectVec<'a, 'i>,
|
||||
tail: ObjectVec<'a, 'i>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Section<'a, 'i> {
|
||||
/// Constructor for Sections
|
||||
///
|
||||
/// ```
|
||||
/// use tamer::obj::xmle::writer::Section;
|
||||
///
|
||||
/// let section = Section::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
head: Vec::new(),
|
||||
body: Vec::new(),
|
||||
tail: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The length of the `Section`
|
||||
pub fn len(&self) -> usize {
|
||||
self.head.len() + self.body.len() + self.tail.len()
|
||||
}
|
||||
|
||||
/// Check if the `Section` is empty
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Push an `Object` into a `Section`'s head
|
||||
pub fn push_head(&mut self, obj: ObjectRef<'a, 'i>) {
|
||||
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)
|
||||
}
|
||||
|
||||
/// Push an `Object` into a `Section`'s tail
|
||||
pub fn push_tail(&mut self, obj: ObjectRef<'a, 'i>) {
|
||||
self.tail.push(&obj)
|
||||
}
|
||||
|
||||
/// Merge the parts of a `Section` into one [`SectionIterator`]
|
||||
///
|
||||
/// The `Section` internals need to be iterated as a group so we needed to
|
||||
/// create a custom iterator, [`SectionIterator`] to do this for us. This
|
||||
/// method allows us to access the iterator.
|
||||
///
|
||||
/// ```
|
||||
/// use tamer::obj::xmle::writer::Section;
|
||||
/// use tamer::ir::asg::Object;
|
||||
///
|
||||
/// let mut section = Section::new();
|
||||
/// let obj = Object::Empty;
|
||||
/// let expect = vec![&obj, &obj, &obj];
|
||||
///
|
||||
/// section.push_head(&obj);
|
||||
/// section.push_body(&obj);
|
||||
/// section.push_tail(&obj);
|
||||
/// let section_iter = section.iter();
|
||||
///
|
||||
/// for object in section_iter {
|
||||
/// assert_eq!(&obj, object);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn iter(&self) -> SectionIterator {
|
||||
SectionIterator {
|
||||
inner: Box::new(
|
||||
self.head
|
||||
.iter()
|
||||
.chain(self.body.iter())
|
||||
.chain(self.tail.iter())
|
||||
.copied(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Iterator for SectionIterator<'a, 'i> {
|
||||
type Item = &'a Object<'i>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.inner.next()
|
||||
}
|
||||
}
|
||||
|
||||
/// Sections that need to be written to a buffer
|
||||
///
|
||||
/// All the properties are public [`Section`] objects and will be accessed
|
||||
/// directly by the [`Writer`].
|
||||
#[derive(Default)]
|
||||
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>,
|
||||
}
|
||||
|
||||
impl<'a, 'i> Sections<'a, 'i> {
|
||||
/// Constructor for Sections
|
||||
///
|
||||
/// ```
|
||||
/// use tamer::obj::xmle::writer::Sections;
|
||||
///
|
||||
/// let sections = Sections::new();
|
||||
/// ```
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: Section::new(),
|
||||
retmap: Section::new(),
|
||||
meta: Section::new(),
|
||||
worksheet: Section::new(),
|
||||
params: Section::new(),
|
||||
types: Section::new(),
|
||||
funcs: Section::new(),
|
||||
rater: Section::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error implementations for the writer
|
||||
#[derive(Debug)]
|
||||
pub enum WriterError {
|
||||
|
@ -233,113 +89,3 @@ impl From<XmlError> for WriterError {
|
|||
WriterError::XmlError(err)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn section_empty() {
|
||||
let section = Section::new();
|
||||
|
||||
assert!(section.head.is_empty());
|
||||
assert!(section.body.is_empty());
|
||||
assert!(section.tail.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_head() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.head.is_empty());
|
||||
|
||||
section.push_head(&obj);
|
||||
|
||||
assert_eq!(Some(&&obj), section.head.get(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_body() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.body.is_empty());
|
||||
|
||||
section.push_body(&obj);
|
||||
|
||||
let body = section.body;
|
||||
assert_eq!(Some(&&obj), body.get(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_tail() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.tail.is_empty());
|
||||
|
||||
section.push_tail(&obj);
|
||||
|
||||
assert_eq!(Some(&&obj), section.tail.get(0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_len() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert_eq!(0, section.len());
|
||||
section.push_head(&obj);
|
||||
assert_eq!(1, section.len());
|
||||
section.push_body(&obj);
|
||||
assert_eq!(2, section.len());
|
||||
section.push_tail(&obj);
|
||||
assert_eq!(3, section.len());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_is_empty_head() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.is_empty());
|
||||
section.push_head(&obj);
|
||||
assert!(!section.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_is_empty_body() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.is_empty());
|
||||
section.push_body(&obj);
|
||||
assert!(!section.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_is_empty_tail() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
|
||||
assert!(section.is_empty());
|
||||
section.push_tail(&obj);
|
||||
assert!(!section.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn section_iterator() {
|
||||
let mut section = Section::new();
|
||||
let obj = Object::Empty;
|
||||
let expect = vec![&obj, &obj, &obj];
|
||||
|
||||
section.push_head(&obj);
|
||||
section.push_body(&obj);
|
||||
section.push_tail(&obj);
|
||||
|
||||
let collection: Vec<_> = section.iter().collect();
|
||||
|
||||
assert_eq!(expect, collection);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
// 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 super::writer::{Result, SectionIterator, Sections, WriterError};
|
||||
use crate::ir::asg::{IdentKind, Object};
|
||||
use super::writer::{Result, WriterError};
|
||||
use crate::ir::asg::{IdentKind, Object, SectionIterator, Sections};
|
||||
use crate::sym::Symbol;
|
||||
use fxhash::FxHashSet;
|
||||
#[cfg(test)]
|
||||
|
@ -69,7 +69,8 @@ impl<W: Write> XmleWriter<W> {
|
|||
///
|
||||
/// ```
|
||||
/// use std::io::Cursor;
|
||||
/// use tamer::obj::xmle::writer::{Sections, XmleWriter};
|
||||
/// use tamer::obj::xmle::writer::XmleWriter;
|
||||
/// use tamer::ir::asg::Sections;
|
||||
/// use tamer::sym::{Symbol, SymbolIndex};
|
||||
/// use tamer::sym::{DefaultInterner, Interner};
|
||||
///
|
||||
|
@ -410,10 +411,8 @@ mod mock {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::ir::asg::Dim;
|
||||
use crate::ir::asg::Source;
|
||||
use crate::ir::asg::{Dim, Section, Source};
|
||||
use crate::ir::legacyir::SymAttrs;
|
||||
use crate::obj::xmle::writer::Section;
|
||||
use crate::sym::{Symbol, SymbolIndex};
|
||||
use std::str;
|
||||
|
||||
|
|
Loading…
Reference in New Issue