TAMER: xmle output changes to support Summary Page

Co-Authored-By: Joseph Frazer <joseph.frazer@ryansg.com>
master
Mike Gerwitz 2020-01-14 16:26:36 -05:00
parent 6939753ca0
commit 645908e258
10 changed files with 415 additions and 89 deletions

View File

@ -1,4 +1,4 @@
# This number is incremented for every linker change to force rebuilding
# of xmle files.
1
2

View File

@ -1,4 +1,4 @@
# This number is incremented for every compiler change to force rebuilding
# of xmlo files.
1
2

View File

@ -80,7 +80,8 @@
$displays )" />
<lv:package name="{@name}"
lvmc:type="worksheet">
__rootpath="{$__relroot}"
lvmc:type="worksheet">
<!-- we provide one special symbol -->
<preproc:symtable>
<preproc:sym name="___worksheet"

View File

@ -115,6 +115,18 @@ where
self.index[i] = node;
}
/// Lookup `ident` or add an [`Object::Missing`] to the graph and
/// return a reference to it.
#[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)));
self.index_identifier(ident, index);
ObjectRef(index)
})
}
}
impl<'i, Ix> Asg<'i, Ix> for BaseAsg<'i, Ix>
@ -129,7 +141,14 @@ where
) -> AsgResult<ObjectRef<Ix>> {
// TODO: src check
if let Some(existing) = self.lookup(name) {
return Ok(existing);
match self.graph.node_weight_mut(existing.0) {
Some(node @ Some(Object::Missing(_))) => {
node.replace(Object::Ident(name, kind, src));
return Ok(existing);
}
Some(_) => return Ok(existing),
_ => (),
}
}
let node = self.graph.add_node(Some(Object::Ident(name, kind, src)));
@ -218,6 +237,19 @@ where
fn has_dep(&self, ident: ObjectRef<Ix>, dep: ObjectRef<Ix>) -> bool {
self.graph.contains_edge(ident.0, dep.0)
}
fn add_dep_lookup(
&mut self,
ident: &'i Symbol<'i>,
dep: &'i Symbol<'i>,
) -> (ObjectRef<Ix>, ObjectRef<Ix>) {
let identi = self.lookup_or_missing(ident);
let depi = self.lookup_or_missing(dep);
self.graph.update_edge(identi.0, depi.0, Default::default());
(identi, depi)
}
}
// TODO: encapsulate Petgraph API (N.B. this is untested!)
@ -464,4 +496,66 @@ mod test {
Ok(())
}
// same as above test
#[test]
fn add_dep_lookup_existing() -> 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.declare(&sym, IdentKind::Meta, Source::default())?;
let _ = sut.declare(&dep, IdentKind::Meta, Source::default())?;
let (symnode, depnode) = sut.add_dep_lookup(&sym, &dep);
assert!(sut.has_dep(symnode, depnode));
Ok(())
}
#[test]
fn add_dep_lookup_missing() -> 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");
// both of these are missing
let (symnode, depnode) = sut.add_dep_lookup(&sym, &dep);
assert!(sut.has_dep(symnode, depnode));
assert_eq!(Some(&Object::Missing(&sym)), sut.get(symnode));
assert_eq!(Some(&Object::Missing(&dep)), sut.get(depnode));
Ok(())
}
#[test]
fn declare_return_missing_symbol() -> 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");
// both of these are missing, see add_dep_lookup_missing
let (symnode, _) = sut.add_dep_lookup(&sym, &dep);
let src = Source {
desc: Some("Tamer is NOT lamer.".to_string()),
..Default::default()
};
// Check with a declared value
let declared = sut.declare(&sym, IdentKind::Meta, src.clone())?;
assert_eq!(symnode, declared);
assert_eq!(
Some(&Object::Ident(&sym, IdentKind::Meta, src)),
sut.get(declared),
);
Ok(())
}
}

View File

@ -123,10 +123,33 @@ pub trait Asg<'i, Ix: IndexType> {
/// An object must be declared as a dependency if its value must be
/// computed before computing the value of `ident`.
/// The [linker][crate::ld] will ensure this ordering.
///
/// See [`add_dep_lookup`][Asg::add_dep_lookup] if identifiers have to
/// be looked up by [`Symbol`] or if they may not yet have been
/// declared.
fn add_dep(&mut self, ident: ObjectRef<Ix>, dep: ObjectRef<Ix>);
/// Check whether `dep` is a dependency of `ident`.
fn has_dep(&self, ident: ObjectRef<Ix>, dep: ObjectRef<Ix>) -> bool;
/// Declare that `dep` is a dependency of `ident`,
/// regardless of whether they are known.
///
/// In contrast to [`add_dep`][Asg::add_dep],
/// 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,
/// allowing the ASG to be built with partial information as
/// identifiers continue to be discovered.
///
/// References to both identifiers are returned in argument order.
fn add_dep_lookup(
&mut self,
ident: &'i Symbol<'i>,
dep: &'i Symbol<'i>,
) -> (ObjectRef<Ix>, ObjectRef<Ix>);
}
/// A [`Result`] with a hard-coded [`AsgError`] error type.

View File

@ -93,6 +93,51 @@
//! # }
//! ```
//!
//! Missing Identifiers
//! -------------------
//! Since identifiers in TAME can be defined in any order relative to their
//! dependencies within a source file,
//! it is often the case that a dependency will have to be added to the
//! graph before it is resolved.
//! For example,
//! [`Asg::add_dep_lookup`] will add an [`Object::Missing`] to the graph
//! if either identifier has not yet been declared.
//!
//! ```
//! # use tamer::global;
//! # use tamer::ir::asg::{Asg, DefaultAsg, IdentKind, Object, FragmentText, Source};
//! # use tamer::sym::{Interner, DefaultInterner};
//! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut asg = DefaultAsg::<global::PkgIdentSize>::with_capacity(1024, 1024);
//! # let interner = DefaultInterner::new();
//! #
//! let identa_sym = interner.intern("identa");
//! let identb_sym = interner.intern("identb");
//! let (identa, identb) = asg.add_dep_lookup(identa_sym, identb_sym);
//!
//! assert_eq!(Some(&Object::Missing(identa_sym)), asg.get(identa));
//! assert_eq!(Some(&Object::Missing(identb_sym)), asg.get(identb));
//!
//! // The identifiers returned above are proper objects on the graph.
//! assert_eq!(Some(identa), asg.lookup(identa_sym));
//! assert_eq!(Some(identb), asg.lookup(identb_sym));
//!
//! // Once declared, the missing identifier changes state and dependencies
//! // are retained.
//! asg.declare(identa_sym, IdentKind::Meta, Source::default())?;
//!
//! assert_eq!(
//! Some(&Object::Ident(identa_sym, IdentKind::Meta, Source::default())),
//! asg.get(identa),
//! );
//!
//! assert!(asg.has_dep(identa, identb));
//! #
//! # Ok(()) // main
//! # }
//! ```
//!
//! Fragments
//! ---------
//! A compiled fragment can be attached to any resolved identifier (see

View File

@ -29,6 +29,9 @@ use crate::sym::Symbol;
/// These types represent object states:
///
/// ```text
/// ,-> (Missing) -------.
/// / \ \
/// / v v
/// ((Empty)) -> (Extern) -> ((Ident)) -> ((IdentFragment)).
/// \ ^
/// \ /
@ -37,10 +40,21 @@ use crate::sym::Symbol;
///
/// The [`Empty`][Object::Empty] state is never directly accessable
/// through [`Asg`][super::Asg]'s public API,
/// as it represents the absence of an object at that node within the
/// as it represents the _absence_ of an object at that node within the
/// ASG.
#[derive(Debug, PartialEq)]
pub enum Object<'i> {
/// An identifier is expected to be defined but is not yet available.
///
/// This variant contains the symbol representing the name of the
/// expected identifier.
/// By defining an object as missing,
/// this allows the graph to be built incrementally as objects are
/// discovered.
///
/// Note that this is different than [`Empty`][Object::Empty].
Missing(&'i Symbol<'i>),
/// A resolved identifier.
///
/// This represents an identifier that has been declared with certain
@ -67,6 +81,8 @@ pub enum Object<'i> {
/// The empty node (default value for indexer).
///
/// This is not a valid state accessible via [`Asg`][super::Asg].
///
/// Note that this is different than [`Missing`][Object::Missing].
Empty,
}
@ -86,6 +102,13 @@ pub type FragmentText = String;
/// this will provide that information in the future.
#[derive(Debug, Default, PartialEq, Clone)]
pub struct Source<'i> {
/// Name of package containing reference to this object.
pub pkg_name: Option<&'i Symbol<'i>>,
/// Relative path to the source of this object,
/// if not present in the current package.
pub src: Option<&'i Symbol<'i>>,
/// The identifier from which this one is derived.
///
/// See [`IdentKind`] for more information on parents.
@ -134,6 +157,8 @@ impl<'i> From<SymAttrs<'i>> for Source<'i> {
/// This simply extracts a subset of fields from the source attributes.
fn from(attrs: SymAttrs<'i>) -> Self {
Source {
pkg_name: attrs.pkg_name,
src: attrs.src,
generated: attrs.generated,
parent: attrs.parent,
yields: attrs.yields,
@ -150,11 +175,15 @@ mod test {
#[test]
fn source_from_sym_attrs() {
let psym = Symbol::new_dummy(SymbolIndex::from_u32(1), "parent");
let ysym = Symbol::new_dummy(SymbolIndex::from_u32(2), "yields");
let fsym = Symbol::new_dummy(SymbolIndex::from_u32(2), "from");
let nsym = Symbol::new_dummy(SymbolIndex::from_u32(1), "name");
let ssym = Symbol::new_dummy(SymbolIndex::from_u32(2), "src");
let psym = Symbol::new_dummy(SymbolIndex::from_u32(3), "parent");
let ysym = Symbol::new_dummy(SymbolIndex::from_u32(4), "yields");
let fsym = Symbol::new_dummy(SymbolIndex::from_u32(5), "from");
let attrs = SymAttrs {
pkg_name: Some(&nsym),
src: Some(&ssym),
generated: true,
parent: Some(&psym),
yields: Some(&ysym),
@ -165,6 +194,8 @@ mod test {
assert_eq!(
Source {
pkg_name: Some(&nsym),
src: Some(&ssym),
generated: attrs.generated,
parent: attrs.parent,
yields: attrs.yields,

View File

@ -32,6 +32,15 @@ use std::result::Result;
/// Toplevel package attributes.
#[derive(Debug, Default, PartialEq, Eq)]
pub struct PackageAttrs<'i> {
/// Unique package identifier.
///
/// The package name is derived from the filename relative to the
/// project root during compilation (see `relroot`).
pub name: Option<&'i Symbol<'i>>,
/// Relative path from package to project root.
pub relroot: Option<String>,
/// Whether this package is a program.
///
/// A _program_ is a package intended to be linked into a final
@ -105,6 +114,14 @@ pub struct SymAttrs<'i> {
/// extern is satisfied and properly located in the final executable.
pub extern_: bool,
/// Unique package identifier.
///
/// The name of a package is automatically derived from the package path
/// relative to the project root.
/// _Note that this is problematic if one wants to compile the equivalent
/// of shared libraries._
pub pkg_name: Option<&'i Symbol<'i>>,
/// The identifier from which this one is derived.
///
/// For example,

View File

@ -20,9 +20,9 @@
use crate::global;
use crate::ir::asg::IdentKind;
use crate::ir::asg::{Asg, DefaultAsg, Object, ObjectRef};
use crate::ir::asg::{Asg, DefaultAsg, Object, ObjectRef, Source};
use crate::obj::xmlo::reader::{XmloError, XmloEvent, XmloReader};
use crate::sym::{DefaultInterner, Interner};
use crate::sym::{DefaultInterner, Interner, Symbol};
use petgraph::visit::DfsPostOrder;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Writer;
@ -47,14 +47,15 @@ pub fn main() -> Result<(), Box<dyn Error>> {
println!("WARNING: This is proof-of-concept; do not use!");
load_xmlo(
let (name, relroot) = load_xmlo(
&abs_path.to_str().unwrap().to_string(),
&mut pkgs_seen,
&mut fragments,
&mut depgraph,
&interner,
&mut roots,
)?;
)?
.expect("missing root package information");
// println!(
// "Graph {:?}",
@ -77,7 +78,13 @@ pub fn main() -> Result<(), Box<dyn Error>> {
//println!("Sorted ({}): {:?}", sorted.len(), sorted);
output_xmle(&depgraph, &interner, sorted)?;
output_xmle(
&depgraph,
&interner,
sorted,
name.expect("missing root package name"),
relroot.expect("missing root package relroot"),
)?;
Ok(())
}
@ -89,12 +96,14 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
depgraph: &mut LinkerAsg<'i>,
interner: &'i I,
roots: &mut Vec<LinkerObjectRef>,
) -> Result<(), Box<dyn Error>> {
) -> Result<Option<(Option<&'i Symbol<'i>>, Option<String>)>, Box<dyn Error>> {
let path = fs::canonicalize(path_str)?;
let path_str = path.to_str().unwrap();
let first = pkgs_seen.len() == 0;
if !pkgs_seen.insert(path_str.to_string()) {
return Ok(());
return Ok(None);
}
//println!("processing {}", path_str);
@ -106,33 +115,28 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
let mut xmlo = XmloReader::new(reader, interner);
let mut elig = None;
let mut name: Option<&'i Symbol<'i>> = None;
let mut relroot: Option<String> = None;
loop {
match xmlo.read_event() {
Ok(XmloEvent::Package(attrs)) => {
if first {
name = attrs.name;
relroot = attrs.relroot;
}
elig = attrs.elig;
}
Ok(XmloEvent::SymDeps(sym, deps)) => {
// TODO: API needs to expose whether a symbol is already
// known so that we can warn on them
//
// note: using from_utf8_unchecked here did _not_ improve
// performance
let sym_node = depgraph
.lookup(sym)
.expect(&format!("missing sym for deps: `{}`", sym));
// Maps should not pull in symbols since we may end up
// mapping to params that are never actually used
if !sym.starts_with(":map:") {
for dep_sym in deps {
let dep_node =
depgraph.lookup(dep_sym).expect(&format!(
"missing dep sym for deps: `{}` -> `{}`",
sym, dep_sym
));
depgraph.add_dep(sym_node, dep_node);
depgraph.add_dep_lookup(sym, dep_sym);
}
}
}
@ -140,33 +144,43 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
Ok(XmloEvent::SymDecl(sym, attrs)) => {
if let Some(sym_src) = attrs.src {
found.insert(sym_src);
}
} else if attrs.extern_ {
// TODO: externs (they're implicitly handled, without
// checks, by Missing)
// depgraph.declare_extern(sym, kind);
} else {
let owned = attrs.src.is_none();
let owned = attrs.src.is_none();
let kind = (&attrs).try_into().map_err(|err| {
format!("sym `{}` attrs error: {}", sym, err)
});
let kind = (&attrs).try_into().map_err(|err| {
format!("sym `{}` attrs error: {}", sym, err)
});
let mut src: Source = attrs.into();
let src = attrs.into();
// TODO: should probably track these down in the XSLT linker...
match kind {
Ok(kindval) => {
// TODO: inefficient
let link_root = owned
&& (kindval == IdentKind::Meta
|| kindval == IdentKind::Map
|| kindval == IdentKind::RetMap);
let node = depgraph.declare(sym, kindval, src)?;
if link_root {
roots.push(node);
}
// Existing convention is to omit @src of local package
// (in this case, the program being linked)
if first {
src.pkg_name = None;
}
Err(e) => println!("{:?}; skipping...", e),
};
// TODO: should probably track these down in the XSLT linker...
match kind {
Ok(kindval) => {
// TODO: inefficient
let link_root = owned
&& (kindval == IdentKind::Meta
|| kindval == IdentKind::Map
|| kindval == IdentKind::RetMap);
let node = depgraph.declare(sym, kindval, src)?;
if link_root {
roots.push(node);
}
}
Err(e) => println!("{:?}; skipping...", e),
};
}
}
Ok(XmloEvent::Fragment(sym, text)) => {
@ -210,14 +224,18 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
path_buf.push(relpath);
path_buf.set_extension("xmlo");
//println!("Trying {:?}", path_buf);
// println!("Trying {:?}", path_buf);
let path_abs = path_buf.canonicalize().unwrap();
let path = path_abs.to_str().unwrap();
load_xmlo(path, pkgs_seen, fragments, depgraph, interner, roots)?;
}
Ok(())
if first {
Ok(Some((name, relroot)))
} else {
Ok(None)
}
}
type ObjectVec<'a, 'i> = Vec<&'a Object<'i>>;
@ -288,6 +306,8 @@ fn output_xmle<'a, 'i, I: Interner<'i>>(
depgraph: &'a LinkerAsg<'i>,
interner: &'i I,
sorted: SortedDeps<'a, 'i>,
name: &'i Symbol<'i>,
relroot: String,
) -> Result<(), Box<dyn Error>> {
use std::io::Cursor;
@ -299,9 +319,10 @@ fn output_xmle<'a, 'i, I: Interner<'i>>(
("xmlns", "http://www.lovullo.com/rater"),
("xmlns:preproc", "http://www.lovullo.com/rater/preproc"),
("xmlns:l", "http://www.lovullo.com/rater/linker"),
("title", "Title TODO"), // TODO
("title", &name), // TODO
("program", "true"),
("name", "name/todo"), // TODO
("name", &name),
("__rootpath", &relroot),
]);
writer.write_event(Event::Start(root))?;
@ -384,6 +405,11 @@ fn output_xmle<'a, 'i, I: Interner<'i>>(
attrs.push(("preproc:generated", "true"));
}
let srcpath: String;
if let Some(pkg_name) = src.pkg_name {
srcpath = relroot.clone() + pkg_name;
attrs.push(("src", &srcpath));
}
if let Some(parent) = src.parent {
attrs.push(("parent", parent));
}

View File

@ -52,7 +52,7 @@
//! use tamer::ir::legacyir::SymType;
//! use tamer::sym::{DefaultInterner, Interner};
//!
//! let xmlo = br#"<package program="true">
//! let xmlo = br#"<package name="foo">
//! <preproc:symtable>
//! <preproc:sym name="syma" type="class" />
//! <preproc:sym name="symb" type="cgen" />
@ -77,14 +77,14 @@
//! let interner = DefaultInterner::new();
//! let mut reader = XmloReader::new(xmlo as &[u8], &interner);
//!
//! let mut prog = None;
//! let mut pkgname = None;
//! let mut syms = Vec::new();
//! let mut deps = Vec::new();
//! let mut fragments = Vec::new();
//!
//! loop {
//! match reader.read_event()? {
//! XmloEvent::Package(attrs) => prog = Some(attrs.program),
//! XmloEvent::Package(attrs) => pkgname = attrs.name,
//! XmloEvent::SymDecl(sym, attrs) => syms.push((sym, attrs.ty)),
//! XmloEvent::SymDeps(sym, symdeps) => deps.push((sym, symdeps)),
//! XmloEvent::Fragment(sym, text) => fragments.push((sym, text)),
@ -94,7 +94,7 @@
//! }
//! }
//!
//! assert_eq!(Some(true), prog);
//! assert_eq!(Some(interner.intern("foo")), pkgname);
//!
//! assert_eq!(
//! vec![
@ -194,6 +194,12 @@ where
/// This is used to ensure that we provide an error early on if we try
/// to process something that isn't a package.
seen_root: bool,
/// Name of the package currently being read.
///
/// This is known after processing the root `package` element,
/// provided that it's a proper root node.
pkg_name: Option<&'i Symbol<'i>>,
}
impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
@ -211,6 +217,7 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
sub_buffer: Vec::new(),
interner,
seen_root: false,
pkg_name: None,
}
}
@ -258,12 +265,17 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
match event {
XmlEvent::Empty(ele) if ele.name() == b"preproc:sym" => {
Self::process_sym(&ele, self.interner)
Self::process_sym(&self.pkg_name, &ele, self.interner)
}
XmlEvent::Start(ele) => match ele.name() {
b"package" => Self::process_package(&ele, self.interner),
b"lv:package" => Self::process_package(&ele, self.interner),
b"package" | b"lv:package" => {
let attrs = Self::process_package(&ele, self.interner)?;
self.pkg_name = attrs.name;
Ok(XmloEvent::Package(attrs))
}
b"preproc:sym-dep" => Self::process_dep(
&ele,
@ -284,7 +296,8 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
// source field information which we want to keep. (We
// don't care about `retmap` for our purposes.)
b"preproc:sym" => {
let mut event = Self::process_sym(&ele, self.interner)?;
let mut event =
Self::process_sym(&self.pkg_name, &ele, self.interner)?;
match &mut event {
XmloEvent::SymDecl(_, attrs)
@ -330,12 +343,26 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
fn process_package<'a>(
ele: &'a BytesStart<'a>,
interner: &'i I,
) -> XmloResult<XmloEvent<'i>> {
) -> XmloResult<PackageAttrs<'i>> {
let mut program = false;
let mut elig: Option<&'i Symbol<'i>> = None;
let mut name: Option<&'i Symbol<'i>> = None;
let mut relroot: Option<String> = None;
for attr in ele.attributes().with_checks(false).filter_map(Result::ok) {
match attr.key {
b"name" => {
name = Some(unsafe {
interner.intern_utf8_unchecked(&attr.value)
});
}
b"__rootpath" => {
relroot = Some(unsafe {
String::from_utf8_unchecked(attr.value.to_vec())
});
}
b"program" => {
program = &*attr.value == b"true";
}
@ -350,7 +377,14 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
}
}
Ok(XmloEvent::Package(PackageAttrs { program, elig }))
// TODO: proper errors, no panic
Ok(PackageAttrs {
name,
relroot,
program,
elig,
..Default::default()
})
}
/// Process `preproc:sym` element attributes.
@ -366,6 +400,7 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
/// ======
/// - [`XmloError::UnassociatedSym`] if missing `preproc:sym/@name`.
fn process_sym<'a>(
pkg_name: &Option<&'i Symbol<'i>>,
ele: &'a BytesStart<'a>,
interner: &'i I,
) -> XmloResult<XmloEvent<'i>> {
@ -441,6 +476,8 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
}
}
sym_attrs.pkg_name = *pkg_name;
name.map(|name_sym| XmloEvent::SymDecl(name_sym, sym_attrs))
.ok_or(XmloError::UnassociatedSym)
}
@ -993,6 +1030,7 @@ mod test {
// We don't want to have to output a proper root node
// for every one of our tests.
$sut.seen_root = true;
$sut.pkg_name = Some($interner.intern("pkg/name"));
$body;
@ -1098,6 +1136,7 @@ mod test {
XmloEvent::Package(PackageAttrs {
program: true,
elig: Some(interner.intern("eligClassYields")),
..Default::default()
}),
result
);
@ -1122,6 +1161,30 @@ mod test {
);
}
fn package_event_name(sut, interner) {
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Start(MockBytesStart::new(
b"package",
Some(MockAttributes::new(vec![
MockAttribute::new(b"name", b"pkg/name"),
MockAttribute::new(b"__rootpath", b"../../"),
])),
)))
}));
let result = sut.read_event()?;
assert_eq!(
XmloEvent::Package(PackageAttrs {
name: Some(interner.intern("pkg/name")),
relroot: Some("../../".into()),
program: false,
..Default::default()
}),
result
);
}
fn sym_dep_event(sut, interner) {
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
0 => Ok(XmlEvent::Start(MockBytesStart::new(
@ -1351,7 +1414,10 @@ mod test {
assert_eq!(
XmloEvent::SymDecl(
interner.intern("sym-expected"),
Default::default()
SymAttrs {
pkg_name: Some(interner.intern("pkg/name")),
..Default::default()
},
),
result
);
@ -1384,6 +1450,7 @@ mod test {
interner.intern("sym-nonempty"),
SymAttrs {
dim: Some(2),
pkg_name: Some(interner.intern("pkg/name")),
..Default::default()
},
),
@ -1456,6 +1523,7 @@ mod test {
interner.intern("from-a"),
interner.intern("from-b"),
]),
pkg_name: Some(interner.intern("pkg/name")),
..Default::default()
},
),
@ -1538,29 +1606,45 @@ mod test {
}
macro_rules! sym_test_reader_event {
($sut:ident, $name:ident, $($key:ident=$val:literal),*) => {
($sut:ident, $interner:ident, $name:ident, $($key:ident=$val:literal),*) => {
// See xmlo_tests macro for explanation
$sut.seen_root = true;
$sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Empty(MockBytesStart::new(
b"preproc:sym",
Some(MockAttributes::new(
vec![
MockAttribute::new(
b"name",
stringify!($name).as_bytes(),
),
$(
$sut.reader.next_event = Some(Box::new(|_, event_i| {
match event_i {
0 => Ok(XmlEvent::Start(MockBytesStart::new(
b"package",
Some(MockAttributes::new(vec![
MockAttribute::new(b"name", b"pkg/name")
])),
))),
1 => Ok(XmlEvent::Empty(MockBytesStart::new(
b"preproc:sym",
Some(MockAttributes::new(
vec![
MockAttribute::new(
stringify!($key).as_bytes(),
$val.as_bytes(),
b"name",
stringify!($name).as_bytes(),
),
)*
],
$(
MockAttribute::new(
stringify!($key).as_bytes(),
$val.as_bytes(),
),
)*
],
)),
))),
_ => Err(XmlError::UnexpectedEof(
format!("MockXmlReader out of events: {}", event_i).into(),
)),
)))
}
}));
// consume the package to set the name
let _ = $sut.read_event();
}
}
@ -1573,11 +1657,12 @@ mod test {
let $interner = DefaultInterner::new();
let mut sut = Sut::new(stub_data, &$interner);
sym_test_reader_event!(sut, $name, $( $key=$val ),*);
sym_test_reader_event!(sut, $interner, $name, $( $key=$val ),*);
let result = sut.read_event()?;
let expected_attrs = $expect;
let mut expected_attrs = $expect;
expected_attrs.pkg_name = Some($interner.intern("pkg/name"));
assert_eq!(
XmloEvent::SymDecl(
@ -1596,6 +1681,7 @@ mod test {
(interner)
src: [src="foo/bar/baz"] => SymAttrs {
// see macro for src relpath
src: Some(interner.intern("foo/bar/baz")),
..Default::default()
}
@ -1668,6 +1754,7 @@ mod test {
// Multiple attributes at once
multi: [src="foo", type="class", dim="1", dtype="float", extern="true"]
=> SymAttrs {
// see macro for src relpath
src: Some(interner.intern("foo")),
ty: Some(SymType::Class),
dim: Some(1),
@ -1686,6 +1773,7 @@ mod test {
// See xmlo_tests macro for explanation
sut.seen_root = true;
sut.pkg_name = Some(interner.intern("pkg/name"));
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Empty(MockBytesStart::new(
@ -1701,6 +1789,7 @@ mod test {
let expected_attrs = SymAttrs {
generated: true,
pkg_name: Some(interner.intern("pkg/name")),
..Default::default()
};
@ -1721,7 +1810,7 @@ mod test {
let interner = DefaultInterner::new();
let mut sut = Sut::new(stub_data, &interner);
sym_test_reader_event!(sut, fail_sym, dim = "X1");
sym_test_reader_event!(sut, interner, fail_sym, dim = "X1");
match sut.read_event() {
Err(XmloError::InvalidDim(msg)) => assert!(msg.contains("X1")),
@ -1735,7 +1824,7 @@ mod test {
let interner = DefaultInterner::new();
let mut sut = Sut::new(stub_data, &interner);
sym_test_reader_event!(sut, fail_sym, dim = "11");
sym_test_reader_event!(sut, interner, fail_sym, dim = "11");
match sut.read_event() {
Err(XmloError::InvalidDim(msg)) => assert!(msg.contains("11")),
@ -1749,7 +1838,7 @@ mod test {
let interner = DefaultInterner::new();
let mut sut = Sut::new(stub_data, &interner);
sym_test_reader_event!(sut, fail_sym, type = "foo");
sym_test_reader_event!(sut, interner, fail_sym, type = "foo");
match sut.read_event() {
Err(XmloError::InvalidType(msg)) => assert!(msg.contains("foo")),
@ -1763,7 +1852,7 @@ mod test {
let interner = DefaultInterner::new();
let mut sut = Sut::new(stub_data, &interner);
sym_test_reader_event!(sut, fail_sym, dtype = "foo");
sym_test_reader_event!(sut, interner, fail_sym, dtype = "foo");
match sut.read_event() {
Err(XmloError::InvalidDtype(msg)) => assert!(msg.contains("foo")),
@ -1777,7 +1866,7 @@ mod test {
let interner = DefaultInterner::new();
let mut sut = Sut::new(stub_data, &interner);
sym_test_reader_event!(sut, fail_sym, dtype = "foo");
sym_test_reader_event!(sut, interner, fail_sym, dtype = "foo");
match sut.read_event() {
Err(XmloError::InvalidDtype(msg)) => assert!(msg.contains("foo")),