TAMER: xmle output changes to support Summary Page
Co-Authored-By: Joseph Frazer <joseph.frazer@ryansg.com>master
parent
6939753ca0
commit
645908e258
|
@ -1,4 +1,4 @@
|
|||
# This number is incremented for every linker change to force rebuilding
|
||||
# of xmle files.
|
||||
1
|
||||
2
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# This number is incremented for every compiler change to force rebuilding
|
||||
# of xmlo files.
|
||||
1
|
||||
2
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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")),
|
||||
|
|
Loading…
Reference in New Issue