TAMER: POC: Output xmle
This is a working proof-of-concept that will be finalized in future commits.master
parent
85a4934db5
commit
6939753ca0
|
@ -1,4 +1,4 @@
|
||||||
# This number is incremented for every linker change to force rebuilding
|
# This number is incremented for every linker change to force rebuilding
|
||||||
# of xmle files.
|
# of xmle files.
|
||||||
0
|
1
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,8 @@ standalones: $(dest_standalone)
|
||||||
strip: $(dest_standalone_strip) ui/package.strip.js
|
strip: $(dest_standalone_strip) ui/package.strip.js
|
||||||
%.xmle: %.xmlo $(path_tame)/.rev-xmle
|
%.xmle: %.xmlo $(path_tame)/.rev-xmle
|
||||||
$(TAME_TS)
|
$(TAME_TS)
|
||||||
$(TAME) link $< $@
|
@echo "WARNING: using WIP proof-of-concept linker!"
|
||||||
|
set -o pipefail; $(path_tame)/tamer/target/release/tameld $< | awk '/^<package/{p=1};p' > $@
|
||||||
%.js: %.xmle
|
%.js: %.xmle
|
||||||
$(TAME_TS)
|
$(TAME_TS)
|
||||||
$(TAME) standalone $< $@
|
$(TAME) standalone $< $@
|
||||||
|
|
|
@ -62,7 +62,7 @@
|
||||||
|
|
||||||
@return compiled JS
|
@return compiled JS
|
||||||
-->
|
-->
|
||||||
<template match="lv:package" mode="compiler:entry">
|
<template name="compiler:entry">
|
||||||
<!-- enclose everything in a self-executing function to sandbox our data -->
|
<!-- enclose everything in a self-executing function to sandbox our data -->
|
||||||
<text>( function() { </text>
|
<text>( function() { </text>
|
||||||
<!-- to store debug information for equations (we have to put this out here
|
<!-- to store debug information for equations (we have to put this out here
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<template match="lv:package" mode="compiler:entry-rater">
|
<template name="compiler:entry-rater">
|
||||||
<!-- the rater itself -->
|
<!-- the rater itself -->
|
||||||
<value-of select="$compiler:nl" />
|
<value-of select="$compiler:nl" />
|
||||||
<text>function rater( arglist, _canterm ) {</text>
|
<text>function rater( arglist, _canterm ) {</text>
|
||||||
|
@ -107,16 +107,13 @@
|
||||||
<text>/**@expose*/var genclasses = {};</text>
|
<text>/**@expose*/var genclasses = {};</text>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template match="lv:package" mode="compiler:entry-classifier">
|
<template name="compiler:classifier">
|
||||||
<!-- allow classification of any arbitrary dataset -->
|
<!-- allow classification of any arbitrary dataset -->
|
||||||
<value-of select="$compiler:nl" />
|
<value-of select="$compiler:nl" />
|
||||||
<text>rater.classify = function( args, _canterm ) {</text>
|
<text>rater.classify = function( args, _canterm ) {</text>
|
||||||
return rater( args, _canterm ).classes;
|
return rater( args, _canterm ).classes;
|
||||||
<text> };</text>
|
<text> };</text>
|
||||||
</template>
|
|
||||||
|
|
||||||
<template match="lv:package" mode="compiler:exit-classifier">
|
|
||||||
<!-- TODO: make sure fromMap has actually been compiled -->
|
|
||||||
<text>rater.classify.fromMap = function( args_base, _canterm ) { </text>
|
<text>rater.classify.fromMap = function( args_base, _canterm ) { </text>
|
||||||
<text>var ret = {}; </text>
|
<text>var ret = {}; </text>
|
||||||
<text>rater.fromMap( args_base, function( args ) {</text>
|
<text>rater.fromMap( args_base, function( args ) {</text>
|
||||||
|
@ -136,8 +133,10 @@
|
||||||
<text> }; </text>
|
<text> }; </text>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template match="lv:package" mode="compiler:exit-rater">
|
<template name="compiler:exit-rater">
|
||||||
|
<param name="name" as="xs:string "/>
|
||||||
<param name="symbols" as="element( preproc:sym )*" />
|
<param name="symbols" as="element( preproc:sym )*" />
|
||||||
|
<param name="mapfrom" as="element()*" />
|
||||||
|
|
||||||
<value-of select="$compiler:nl" />
|
<value-of select="$compiler:nl" />
|
||||||
<text>return { </text>
|
<text>return { </text>
|
||||||
|
@ -152,7 +151,7 @@
|
||||||
|
|
||||||
<!-- make the name of the supplier available -->
|
<!-- make the name of the supplier available -->
|
||||||
<text>/**@expose*/rater.supplier = '</text>
|
<text>/**@expose*/rater.supplier = '</text>
|
||||||
<value-of select="substring-after( @name, '/' )" />
|
<value-of select="substring-after( $name, '/' )" />
|
||||||
<text>'; </text>
|
<text>'; </text>
|
||||||
|
|
||||||
<text>/**@expose*/rater.meta = meta;</text>
|
<text>/**@expose*/rater.meta = meta;</text>
|
||||||
|
@ -174,20 +173,8 @@
|
||||||
$symbols[ @type='class' ] )" />
|
$symbols[ @type='class' ] )" />
|
||||||
<text> }; </text>
|
<text> }; </text>
|
||||||
|
|
||||||
<variable name="mapfrom" select="
|
|
||||||
preproc:symtable/preproc:sym[
|
|
||||||
@type='map'
|
|
||||||
]/preproc:from[
|
|
||||||
not(
|
|
||||||
@name = parent::preproc:sym
|
|
||||||
/preceding-sibling::preproc:sym[
|
|
||||||
@type='map'
|
|
||||||
]/preproc:from/@name
|
|
||||||
)
|
|
||||||
]
|
|
||||||
" />
|
|
||||||
|
|
||||||
<!-- mapped fields (external names) -->
|
<!-- mapped fields (external names) -->
|
||||||
|
<value-of select="$compiler:nl" />
|
||||||
<text>/**@expose*/rater.knownFields = {</text>
|
<text>/**@expose*/rater.knownFields = {</text>
|
||||||
<for-each select="$mapfrom">
|
<for-each select="$mapfrom">
|
||||||
<if test="position() > 1">
|
<if test="position() > 1">
|
||||||
|
@ -221,6 +208,7 @@
|
||||||
<text>'</text>
|
<text>'</text>
|
||||||
<value-of select="substring-after( @name, ':class:' )" />
|
<value-of select="substring-after( @name, ':class:' )" />
|
||||||
<text>':'</text>
|
<text>':'</text>
|
||||||
|
<!-- yields -->
|
||||||
<value-of select="@yields" />
|
<value-of select="@yields" />
|
||||||
<text>'</text>
|
<text>'</text>
|
||||||
</for-each>
|
</for-each>
|
||||||
|
@ -239,7 +227,7 @@
|
||||||
<value-of select="substring-after( @name, ':class:' )" />
|
<value-of select="substring-after( @name, ':class:' )" />
|
||||||
<text>':'</text>
|
<text>':'</text>
|
||||||
<!-- todo: escape -->
|
<!-- todo: escape -->
|
||||||
<value-of select="translate( @desc, "'", '' )" />
|
<value-of select="translate( normalize-space(@desc), "'", '' )" />
|
||||||
<text>'</text>
|
<text>'</text>
|
||||||
</for-each>
|
</for-each>
|
||||||
</function>
|
</function>
|
||||||
|
|
|
@ -799,8 +799,6 @@
|
||||||
</with-param>
|
</with-param>
|
||||||
</call-template>
|
</call-template>
|
||||||
|
|
||||||
<apply-templates select="." mode="compiler:entry" />
|
|
||||||
|
|
||||||
|
|
||||||
<apply-templates select="." mode="l:link-meta">
|
<apply-templates select="." mode="l:link-meta">
|
||||||
<with-param name="deps" select="$deps" />
|
<with-param name="deps" select="$deps" />
|
||||||
|
@ -808,9 +806,6 @@
|
||||||
<apply-templates select="." mode="l:link-worksheet">
|
<apply-templates select="." mode="l:link-worksheet">
|
||||||
<with-param name="deps" select="$deps" />
|
<with-param name="deps" select="$deps" />
|
||||||
</apply-templates>
|
</apply-templates>
|
||||||
<apply-templates select="." mode="l:link-classifier">
|
|
||||||
<with-param name="deps" select="$deps" />
|
|
||||||
</apply-templates>
|
|
||||||
<apply-templates select="." mode="l:link-params">
|
<apply-templates select="." mode="l:link-params">
|
||||||
<with-param name="deps" select="$deps" />
|
<with-param name="deps" select="$deps" />
|
||||||
</apply-templates>
|
</apply-templates>
|
||||||
|
@ -824,9 +819,6 @@
|
||||||
<with-param name="deps" select="$deps" />
|
<with-param name="deps" select="$deps" />
|
||||||
</apply-templates>
|
</apply-templates>
|
||||||
|
|
||||||
<!-- common stuff -->
|
|
||||||
<call-template name="compiler:static" />
|
|
||||||
|
|
||||||
<!-- finally, finish up -->
|
<!-- finally, finish up -->
|
||||||
<call-template name="log:info">
|
<call-template name="log:info">
|
||||||
<with-param name="name" select="'link'" />
|
<with-param name="name" select="'link'" />
|
||||||
|
@ -908,21 +900,6 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<template match="lv:package" mode="l:link-classifier">
|
|
||||||
<call-template name="log:info">
|
|
||||||
<with-param name="name" select="'link'" />
|
|
||||||
<with-param name="msg">
|
|
||||||
<text>** linking classifier...</text>
|
|
||||||
</with-param>
|
|
||||||
</call-template>
|
|
||||||
|
|
||||||
<!-- link everything that shall be a part of the classifier -->
|
|
||||||
<apply-templates select="." mode="compiler:entry-classifier" />
|
|
||||||
<!-- TODO: get rid of me completely! -->
|
|
||||||
<apply-templates select="." mode="compiler:exit-classifier" />
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<template match="lv:package" mode="l:link-params">
|
<template match="lv:package" mode="l:link-params">
|
||||||
<param name="deps" />
|
<param name="deps" />
|
||||||
|
|
||||||
|
@ -990,8 +967,6 @@
|
||||||
</with-param>
|
</with-param>
|
||||||
</call-template>
|
</call-template>
|
||||||
|
|
||||||
<apply-templates select="." mode="compiler:entry-rater" />
|
|
||||||
|
|
||||||
<!-- TODO: this list of exclusions is a mess -->
|
<!-- TODO: this list of exclusions is a mess -->
|
||||||
<apply-templates select="." mode="l:do-link">
|
<apply-templates select="." mode="l:do-link">
|
||||||
<with-param name="symbols" select="
|
<with-param name="symbols" select="
|
||||||
|
@ -1008,10 +983,6 @@
|
||||||
<sequence select="l:link-exit-fragments(
|
<sequence select="l:link-exit-fragments(
|
||||||
$rater-exit-fragments,
|
$rater-exit-fragments,
|
||||||
. )" />
|
. )" />
|
||||||
|
|
||||||
<apply-templates select="." mode="compiler:exit-rater">
|
|
||||||
<with-param name="symbols" select="$deps" />
|
|
||||||
</apply-templates>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -36,10 +36,7 @@
|
||||||
xmlns:preproc="http://www.lovullo.com/rater/preproc">
|
xmlns:preproc="http://www.lovullo.com/rater/preproc">
|
||||||
|
|
||||||
|
|
||||||
<output
|
<output method="text" />
|
||||||
indent="yes"
|
|
||||||
omit-xml-declaration="yes"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<include href="include/dslc-base.xsl" />
|
<include href="include/dslc-base.xsl" />
|
||||||
|
|
||||||
|
@ -54,9 +51,22 @@
|
||||||
<template match="/" priority="5">
|
<template match="/" priority="5">
|
||||||
<!-- the rater itself -->
|
<!-- the rater itself -->
|
||||||
<text>var rater = </text>
|
<text>var rater = </text>
|
||||||
|
<!-- (moved from linker during TAMER POC linker) -->
|
||||||
|
<call-template name="compiler:entry" />
|
||||||
|
<call-template name="compiler:classifier" />
|
||||||
|
<call-template name="compiler:entry-rater" />
|
||||||
|
|
||||||
<value-of disable-output-escaping="yes" select="/lv:package/l:exec/text()" />
|
<value-of disable-output-escaping="yes" select="/lv:package/l:exec/text()" />
|
||||||
<text>; </text>
|
<text>; </text>
|
||||||
|
|
||||||
|
<!--(moved from linker during TAMER POC linker) -->
|
||||||
|
<call-template name="compiler:exit-rater">
|
||||||
|
<with-param name="name" select="/*/@name" />
|
||||||
|
<with-param name="symbols" select="/*/l:dep/preproc:sym" />
|
||||||
|
<with-param name="mapfrom" select="/*/l:map-from/l:from" />
|
||||||
|
</call-template>
|
||||||
|
<call-template name="compiler:static" />
|
||||||
|
|
||||||
<!-- maps may or may not exist -->
|
<!-- maps may or may not exist -->
|
||||||
<variable name="map" select="/lv:package/l:map-exec" />
|
<variable name="map" select="/lv:package/l:map-exec" />
|
||||||
<variable name="retmap" select="/lv:package/l:retmap-exec" />
|
<variable name="retmap" select="/lv:package/l:retmap-exec" />
|
||||||
|
@ -64,7 +74,7 @@
|
||||||
<!-- store a reference to the mapper in rater.fromMap() -->
|
<!-- store a reference to the mapper in rater.fromMap() -->
|
||||||
<text>rater.fromMap = </text>
|
<text>rater.fromMap = </text>
|
||||||
<choose>
|
<choose>
|
||||||
<when test="$map">
|
<when test="/lv:package/l:dep/preproc:sym[@type='map'][1]">
|
||||||
<value-of disable-output-escaping="yes" select="$map/text()" />
|
<value-of disable-output-escaping="yes" select="$map/text()" />
|
||||||
</when>
|
</when>
|
||||||
|
|
||||||
|
@ -79,7 +89,7 @@
|
||||||
<!-- return map -->
|
<!-- return map -->
|
||||||
<text>rater._retmap = </text>
|
<text>rater._retmap = </text>
|
||||||
<choose>
|
<choose>
|
||||||
<when test="$retmap">
|
<when test="/lv:package/l:dep/preproc:sym[@type='retmap'][1]">
|
||||||
<value-of disable-output-escaping="yes" select="$retmap/text()" />
|
<value-of disable-output-escaping="yes" select="$retmap/text()" />
|
||||||
</when>
|
</when>
|
||||||
|
|
||||||
|
|
|
@ -113,6 +113,19 @@ pub struct Source<'i> {
|
||||||
///
|
///
|
||||||
/// Identifiers created by templates are not considered to be generated.
|
/// Identifiers created by templates are not considered to be generated.
|
||||||
pub generated: bool,
|
pub generated: bool,
|
||||||
|
|
||||||
|
/// Related identifiers.
|
||||||
|
///
|
||||||
|
/// These data represent a kluge created to add additional symbol
|
||||||
|
/// information in two different contexts:
|
||||||
|
///
|
||||||
|
/// - [`IdentKind::Map`] includes the name of the source field; and
|
||||||
|
/// - [`IdentKind::Func`] lists params in order (so that the compiler
|
||||||
|
/// knows application order).
|
||||||
|
///
|
||||||
|
/// TODO: We have `parent`, `yields`, and `from`.
|
||||||
|
/// We should begin to consolodate.
|
||||||
|
pub from: Option<Vec<&'i Symbol<'i>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'i> From<SymAttrs<'i>> for Source<'i> {
|
impl<'i> From<SymAttrs<'i>> for Source<'i> {
|
||||||
|
@ -125,6 +138,7 @@ impl<'i> From<SymAttrs<'i>> for Source<'i> {
|
||||||
parent: attrs.parent,
|
parent: attrs.parent,
|
||||||
yields: attrs.yields,
|
yields: attrs.yields,
|
||||||
desc: attrs.desc,
|
desc: attrs.desc,
|
||||||
|
from: attrs.from,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,12 +152,14 @@ mod test {
|
||||||
fn source_from_sym_attrs() {
|
fn source_from_sym_attrs() {
|
||||||
let psym = Symbol::new_dummy(SymbolIndex::from_u32(1), "parent");
|
let psym = Symbol::new_dummy(SymbolIndex::from_u32(1), "parent");
|
||||||
let ysym = Symbol::new_dummy(SymbolIndex::from_u32(2), "yields");
|
let ysym = Symbol::new_dummy(SymbolIndex::from_u32(2), "yields");
|
||||||
|
let fsym = Symbol::new_dummy(SymbolIndex::from_u32(2), "from");
|
||||||
|
|
||||||
let attrs = SymAttrs {
|
let attrs = SymAttrs {
|
||||||
generated: true,
|
generated: true,
|
||||||
parent: Some(&psym),
|
parent: Some(&psym),
|
||||||
yields: Some(&ysym),
|
yields: Some(&ysym),
|
||||||
desc: Some("sym desc".to_string()),
|
desc: Some("sym desc".to_string()),
|
||||||
|
from: Some(vec![&fsym]),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,6 +169,7 @@ mod test {
|
||||||
parent: attrs.parent,
|
parent: attrs.parent,
|
||||||
yields: attrs.yields,
|
yields: attrs.yields,
|
||||||
desc: Some("sym desc".to_string()),
|
desc: Some("sym desc".to_string()),
|
||||||
|
from: Some(vec![&fsym]),
|
||||||
},
|
},
|
||||||
attrs.into(),
|
attrs.into(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -131,6 +131,16 @@ pub struct SymAttrs<'i> {
|
||||||
///
|
///
|
||||||
/// This is used primarily by [`SymType::Class`] and [`SymType::Gen`].
|
/// This is used primarily by [`SymType::Class`] and [`SymType::Gen`].
|
||||||
pub desc: Option<String>,
|
pub desc: Option<String>,
|
||||||
|
|
||||||
|
/// Related identifiers.
|
||||||
|
///
|
||||||
|
/// These data represent a kluge created to add additional symbol
|
||||||
|
/// information in two different contexts:
|
||||||
|
///
|
||||||
|
/// - [`SymType::Map`] includes the name of the source field; and
|
||||||
|
/// - [`SymType::Func`] lists params in order (so that the compiler
|
||||||
|
/// knows application order).
|
||||||
|
pub from: Option<Vec<&'i Symbol<'i>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Legacy symbol types.
|
/// Legacy symbol types.
|
||||||
|
@ -232,6 +242,18 @@ pub enum SymDtype {
|
||||||
Empty,
|
Empty,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for SymDtype {
|
||||||
|
/// Produce `xmlo`-compatible representation.
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
SymDtype::Boolean => &"boolean",
|
||||||
|
SymDtype::Integer => &"integer",
|
||||||
|
SymDtype::Float => &"float",
|
||||||
|
SymDtype::Empty => &"empty",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl TryFrom<&[u8]> for SymDtype {
|
impl TryFrom<&[u8]> for SymDtype {
|
||||||
type Error = String;
|
type Error = String;
|
||||||
|
|
||||||
|
@ -289,4 +311,10 @@ mod test {
|
||||||
bad => panic!("expected error: {:?}", bad),
|
bad => panic!("expected error: {:?}", bad),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn symdtype_as_str() {
|
||||||
|
let boolean: &str = SymDtype::Boolean.as_ref();
|
||||||
|
assert_eq!("boolean", boolean);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,11 +24,13 @@ use crate::ir::asg::{Asg, DefaultAsg, Object, ObjectRef};
|
||||||
use crate::obj::xmlo::reader::{XmloError, XmloEvent, XmloReader};
|
use crate::obj::xmlo::reader::{XmloError, XmloEvent, XmloReader};
|
||||||
use crate::sym::{DefaultInterner, Interner};
|
use crate::sym::{DefaultInterner, Interner};
|
||||||
use petgraph::visit::DfsPostOrder;
|
use petgraph::visit::DfsPostOrder;
|
||||||
|
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
|
||||||
|
use quick_xml::Writer;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::BufReader;
|
use std::io::{BufReader, Write};
|
||||||
|
|
||||||
type LinkerAsg<'i> = DefaultAsg<'i, global::ProgIdentSize>;
|
type LinkerAsg<'i> = DefaultAsg<'i, global::ProgIdentSize>;
|
||||||
type LinkerObjectRef = ObjectRef<global::ProgIdentSize>;
|
type LinkerObjectRef = ObjectRef<global::ProgIdentSize>;
|
||||||
|
@ -73,7 +75,9 @@ pub fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
|
||||||
let sorted = sort_deps(&depgraph, &roots);
|
let sorted = sort_deps(&depgraph, &roots);
|
||||||
|
|
||||||
println!("Sorted ({}): {:?}", sorted.len(), sorted);
|
//println!("Sorted ({}): {:?}", sorted.len(), sorted);
|
||||||
|
|
||||||
|
output_xmle(&depgraph, &interner, sorted)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -118,13 +122,18 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
|
||||||
.lookup(sym)
|
.lookup(sym)
|
||||||
.expect(&format!("missing sym for deps: `{}`", sym));
|
.expect(&format!("missing sym for deps: `{}`", sym));
|
||||||
|
|
||||||
for dep_sym in deps {
|
// Maps should not pull in symbols since we may end up
|
||||||
let dep_node = depgraph.lookup(dep_sym).expect(&format!(
|
// mapping to params that are never actually used
|
||||||
"missing dep sym for deps: `{}` -> `{}`",
|
if !sym.starts_with(":map:") {
|
||||||
sym, dep_sym
|
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(sym_node, dep_node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,8 +156,8 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
|
||||||
// TODO: inefficient
|
// TODO: inefficient
|
||||||
let link_root = owned
|
let link_root = owned
|
||||||
&& (kindval == IdentKind::Meta
|
&& (kindval == IdentKind::Meta
|
||||||
|| sym.starts_with(":map:")
|
|| kindval == IdentKind::Map
|
||||||
|| sym.starts_with(":retmap:"));
|
|| kindval == IdentKind::RetMap);
|
||||||
|
|
||||||
let node = depgraph.declare(sym, kindval, src)?;
|
let node = depgraph.declare(sym, kindval, src)?;
|
||||||
|
|
||||||
|
@ -211,18 +220,35 @@ fn load_xmlo<'a, 'i, I: Interner<'i>>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ObjectVec<'a, 'i> = Vec<&'a Object<'i>>;
|
||||||
|
|
||||||
|
// Note that the classifier has nothing in it anymore; it's only there for
|
||||||
|
// API compability, so we don't include it here.
|
||||||
|
#[derive(Default)]
|
||||||
|
struct SortedDeps<'a, 'i> {
|
||||||
|
map: ObjectVec<'a, 'i>,
|
||||||
|
retmap: ObjectVec<'a, 'i>,
|
||||||
|
meta: ObjectVec<'a, 'i>,
|
||||||
|
worksheet: ObjectVec<'a, 'i>,
|
||||||
|
params: ObjectVec<'a, 'i>,
|
||||||
|
types: ObjectVec<'a, 'i>,
|
||||||
|
funcs: ObjectVec<'a, 'i>,
|
||||||
|
rater: ObjectVec<'a, 'i>,
|
||||||
|
}
|
||||||
|
|
||||||
fn sort_deps<'a, 'i>(
|
fn sort_deps<'a, 'i>(
|
||||||
depgraph: &'a LinkerAsg<'i>,
|
depgraph: &'a LinkerAsg<'i>,
|
||||||
roots: &Vec<LinkerObjectRef>,
|
roots: &Vec<LinkerObjectRef>,
|
||||||
) -> Vec<&'a Object<'i>> {
|
) -> SortedDeps<'a, 'i> {
|
||||||
// @type=meta, @preproc:elig-class-yields
|
// @type=meta, @preproc:elig-class-yields
|
||||||
// @type={ret}map{,:head,:tail}
|
// @type={ret}map{,:head,:tail}
|
||||||
|
|
||||||
|
let mut deps: SortedDeps = Default::default();
|
||||||
|
|
||||||
// This is technically a topological sort, but functions have
|
// This is technically a topological sort, but functions have
|
||||||
// cycles. Once we have more symbol metadata, we can filter them out
|
// cycles. Once we have more symbol metadata, we can filter them out
|
||||||
// and actually invoke toposort.
|
// and actually invoke toposort.
|
||||||
let mut dfs = DfsPostOrder::empty(&depgraph);
|
let mut dfs = DfsPostOrder::empty(&depgraph);
|
||||||
let mut sorted = Vec::new();
|
|
||||||
|
|
||||||
//println!("discovered roots: {:?}", roots);
|
//println!("discovered roots: {:?}", roots);
|
||||||
|
|
||||||
|
@ -233,10 +259,266 @@ fn sort_deps<'a, 'i>(
|
||||||
|
|
||||||
// TODO: can we encapsulate NodeIndex?
|
// TODO: can we encapsulate NodeIndex?
|
||||||
while let Some(index) = dfs.next(&depgraph) {
|
while let Some(index) = dfs.next(&depgraph) {
|
||||||
sorted.push(depgraph.get(index).unwrap());
|
let ident = depgraph.get(index).unwrap();
|
||||||
|
|
||||||
|
match ident {
|
||||||
|
Object::Ident(_, kind, _)
|
||||||
|
| Object::IdentFragment(_, kind, _, _) => match kind {
|
||||||
|
IdentKind::Meta => deps.meta.push(ident),
|
||||||
|
IdentKind::Worksheet => deps.worksheet.push(ident),
|
||||||
|
IdentKind::Param(_, _) => deps.params.push(ident),
|
||||||
|
IdentKind::Type(_) => deps.types.push(ident),
|
||||||
|
IdentKind::Func(_, _) => deps.funcs.push(ident),
|
||||||
|
IdentKind::MapHead | IdentKind::Map | IdentKind::MapTail => {
|
||||||
|
deps.map.push(ident)
|
||||||
|
}
|
||||||
|
IdentKind::RetMapHead
|
||||||
|
| IdentKind::RetMap
|
||||||
|
| IdentKind::RetMapTail => deps.retmap.push(ident),
|
||||||
|
_ => deps.rater.push(ident),
|
||||||
|
},
|
||||||
|
_ => panic!("unexpected node: {:?}", ident),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sorted
|
deps
|
||||||
|
}
|
||||||
|
|
||||||
|
fn output_xmle<'a, 'i, I: Interner<'i>>(
|
||||||
|
depgraph: &'a LinkerAsg<'i>,
|
||||||
|
interner: &'i I,
|
||||||
|
sorted: SortedDeps<'a, 'i>,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
use std::io::Cursor;
|
||||||
|
|
||||||
|
let mut writer =
|
||||||
|
Writer::new_with_indent(Cursor::new(Vec::new()), ' ' as u8, 2);
|
||||||
|
|
||||||
|
let root =
|
||||||
|
BytesStart::owned_name(b"package".to_vec()).with_attributes(vec![
|
||||||
|
("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
|
||||||
|
("program", "true"),
|
||||||
|
("name", "name/todo"), // TODO
|
||||||
|
]);
|
||||||
|
|
||||||
|
writer.write_event(Event::Start(root))?;
|
||||||
|
|
||||||
|
// All of the other namespaces output in the existing xmle files are
|
||||||
|
// unneeded.
|
||||||
|
writer.write_event(Event::Start(BytesStart::borrowed_name(b"l:dep")))?;
|
||||||
|
|
||||||
|
let all = sorted
|
||||||
|
.meta
|
||||||
|
.iter()
|
||||||
|
.chain(sorted.map.iter())
|
||||||
|
.chain(sorted.retmap.iter())
|
||||||
|
.chain(sorted.worksheet.iter())
|
||||||
|
.chain(sorted.params.iter())
|
||||||
|
.chain(sorted.types.iter())
|
||||||
|
.chain(sorted.funcs.iter())
|
||||||
|
.chain(sorted.rater.iter());
|
||||||
|
|
||||||
|
for ident in all {
|
||||||
|
// TODO: we're doing this in two places!
|
||||||
|
match ident {
|
||||||
|
Object::Ident(sym, kind, src)
|
||||||
|
| Object::IdentFragment(sym, kind, src, _) => {
|
||||||
|
let name: &str = sym;
|
||||||
|
|
||||||
|
// this'll be formalized more sanely
|
||||||
|
let mut attrs = match kind {
|
||||||
|
IdentKind::Cgen(dim) => {
|
||||||
|
vec![("type", "cgen"), ("dim", dim.as_ref())]
|
||||||
|
}
|
||||||
|
IdentKind::Class(dim) => {
|
||||||
|
vec![("type", "class"), ("dim", dim.as_ref())]
|
||||||
|
}
|
||||||
|
IdentKind::Const(dim, dtype) => vec![
|
||||||
|
("type", "const"),
|
||||||
|
("dim", dim.as_ref()),
|
||||||
|
("dtype", dtype.as_ref()),
|
||||||
|
],
|
||||||
|
IdentKind::Func(dim, dtype) => vec![
|
||||||
|
("type", "func"),
|
||||||
|
("dim", dim.as_ref()),
|
||||||
|
("dtype", dtype.as_ref()),
|
||||||
|
],
|
||||||
|
IdentKind::Gen(dim, dtype) => vec![
|
||||||
|
("type", "gen"),
|
||||||
|
("dim", dim.as_ref()),
|
||||||
|
("dtype", dtype.as_ref()),
|
||||||
|
],
|
||||||
|
IdentKind::Lparam(dim, dtype) => vec![
|
||||||
|
("type", "lparam"),
|
||||||
|
("dim", dim.as_ref()),
|
||||||
|
("dtype", dtype.as_ref()),
|
||||||
|
],
|
||||||
|
IdentKind::Param(dim, dtype) => vec![
|
||||||
|
("type", "param"),
|
||||||
|
("dim", dim.as_ref()),
|
||||||
|
("dtype", dtype.as_ref()),
|
||||||
|
],
|
||||||
|
IdentKind::Rate(dtype) => {
|
||||||
|
vec![("type", "rate"), ("dtype", dtype.as_ref())]
|
||||||
|
}
|
||||||
|
IdentKind::Tpl => vec![("type", "tpl")],
|
||||||
|
IdentKind::Type(dtype) => {
|
||||||
|
vec![("type", "type"), ("dtype", dtype.as_ref())]
|
||||||
|
}
|
||||||
|
IdentKind::MapHead => vec![("type", "map:head")],
|
||||||
|
IdentKind::Map => vec![("type", "map")],
|
||||||
|
IdentKind::MapTail => vec![("type", "map:tail")],
|
||||||
|
IdentKind::RetMapHead => vec![("type", "retmap:head")],
|
||||||
|
IdentKind::RetMap => vec![("type", "retmap")],
|
||||||
|
IdentKind::RetMapTail => vec![("type", "retmap:tail")],
|
||||||
|
IdentKind::Meta => vec![("type", "meta")],
|
||||||
|
IdentKind::Worksheet => vec![("type", "worksheet")],
|
||||||
|
};
|
||||||
|
|
||||||
|
attrs.push(("name", name));
|
||||||
|
|
||||||
|
if src.generated {
|
||||||
|
attrs.push(("preproc:generated", "true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(parent) = src.parent {
|
||||||
|
attrs.push(("parent", parent));
|
||||||
|
}
|
||||||
|
if let Some(yields) = src.yields {
|
||||||
|
attrs.push(("yields", yields));
|
||||||
|
}
|
||||||
|
if let Some(desc) = &src.desc {
|
||||||
|
attrs.push(("desc", &desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sym = BytesStart::owned_name(b"preproc:sym".to_vec())
|
||||||
|
.with_attributes(attrs);
|
||||||
|
|
||||||
|
writer.write_event(Event::Empty(sym))?;
|
||||||
|
}
|
||||||
|
_ => unreachable!("filtered out during sorting"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_event(Event::End(BytesEnd::borrowed(b"l:dep")))?;
|
||||||
|
|
||||||
|
// This was not in the original linker, but we need to be able to convey
|
||||||
|
// this information for `standalones` (which has received some logic
|
||||||
|
// from the old linker for the time being).
|
||||||
|
writer
|
||||||
|
.write_event(Event::Start(BytesStart::borrowed_name(b"l:map-from")))?;
|
||||||
|
|
||||||
|
let mut map_froms = HashSet::<&str>::new();
|
||||||
|
|
||||||
|
for map_ident in &sorted.map {
|
||||||
|
match map_ident {
|
||||||
|
Object::Ident(_, _, src) | Object::IdentFragment(_, _, src, _) => {
|
||||||
|
if let Some(froms) = &src.from {
|
||||||
|
for from in froms {
|
||||||
|
map_froms.insert(from);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => unreachable!("filtered out during sorting"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for from in map_froms {
|
||||||
|
let name: &str = from;
|
||||||
|
|
||||||
|
writer.write_event(Event::Empty(
|
||||||
|
BytesStart::borrowed_name(b"l:from")
|
||||||
|
.with_attributes(vec![("name", name)]),
|
||||||
|
))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_event(Event::End(BytesEnd::borrowed(b"l:map-from")))?;
|
||||||
|
writer
|
||||||
|
.write_event(Event::Start(BytesStart::borrowed_name(b"l:map-exec")))?;
|
||||||
|
|
||||||
|
if sorted.map.len() > 0 {
|
||||||
|
write_fragments(
|
||||||
|
&mut writer,
|
||||||
|
&vec![depgraph
|
||||||
|
.get(depgraph.lookup(interner.intern(":map:___head")).unwrap())
|
||||||
|
.unwrap()],
|
||||||
|
)?;
|
||||||
|
write_fragments(&mut writer, &sorted.map)?;
|
||||||
|
write_fragments(
|
||||||
|
&mut writer,
|
||||||
|
&vec![depgraph
|
||||||
|
.get(depgraph.lookup(interner.intern(":map:___tail")).unwrap())
|
||||||
|
.unwrap()],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_event(Event::End(BytesEnd::borrowed(b"l:map-exec")))?;
|
||||||
|
writer.write_event(Event::Start(BytesStart::borrowed_name(
|
||||||
|
b"l:retmap-exec",
|
||||||
|
)))?;
|
||||||
|
|
||||||
|
if sorted.retmap.len() > 0 {
|
||||||
|
write_fragments(
|
||||||
|
&mut writer,
|
||||||
|
&vec![depgraph
|
||||||
|
.get(
|
||||||
|
depgraph
|
||||||
|
.lookup(interner.intern(":retmap:___head"))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()],
|
||||||
|
)?;
|
||||||
|
write_fragments(&mut writer, &sorted.retmap)?;
|
||||||
|
write_fragments(
|
||||||
|
&mut writer,
|
||||||
|
&vec![depgraph
|
||||||
|
.get(
|
||||||
|
depgraph
|
||||||
|
.lookup(interner.intern(":retmap:___tail"))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.unwrap()],
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.write_event(Event::End(BytesEnd::borrowed(b"l:retmap-exec")))?;
|
||||||
|
writer.write_event(Event::Start(BytesStart::borrowed_name(b"l:exec")))?;
|
||||||
|
|
||||||
|
write_fragments(&mut writer, &sorted.meta)?;
|
||||||
|
write_fragments(&mut writer, &sorted.worksheet)?;
|
||||||
|
write_fragments(&mut writer, &sorted.params)?;
|
||||||
|
write_fragments(&mut writer, &sorted.types)?;
|
||||||
|
write_fragments(&mut writer, &sorted.funcs)?;
|
||||||
|
write_fragments(&mut writer, &sorted.rater)?;
|
||||||
|
|
||||||
|
writer.write_event(Event::End(BytesEnd::borrowed(b"l:exec")))?;
|
||||||
|
writer.write_event(Event::End(BytesEnd::borrowed(b"package")))?;
|
||||||
|
|
||||||
|
print!("{}", String::from_utf8(writer.into_inner().into_inner())?);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_fragments<W: Write>(
|
||||||
|
writer: &mut Writer<W>,
|
||||||
|
idents: &ObjectVec,
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
|
for ident in idents {
|
||||||
|
match ident {
|
||||||
|
Object::IdentFragment(_, _, _, frag) => {
|
||||||
|
writer.write_event(Event::Text(BytesText::from_plain_str(
|
||||||
|
frag,
|
||||||
|
)))?;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -129,7 +129,7 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
use crate::ir::legacyir::{PackageAttrs, SymAttrs};
|
use crate::ir::legacyir::{PackageAttrs, SymAttrs, SymType};
|
||||||
use crate::sym::{Interner, Symbol};
|
use crate::sym::{Interner, Symbol};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use mock::MockBytesStart as BytesStart;
|
use mock::MockBytesStart as BytesStart;
|
||||||
|
@ -233,6 +233,8 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
|
||||||
/// ======
|
/// ======
|
||||||
/// - Any of [`XmloError`].
|
/// - Any of [`XmloError`].
|
||||||
/// See private methods for more information.
|
/// See private methods for more information.
|
||||||
|
///
|
||||||
|
/// TODO: Augment failures with context
|
||||||
pub fn read_event<'a>(&mut self) -> XmloResult<XmloEvent<'i>> {
|
pub fn read_event<'a>(&mut self) -> XmloResult<XmloEvent<'i>> {
|
||||||
let event = self.reader.read_event(&mut self.buffer)?;
|
let event = self.reader.read_event(&mut self.buffer)?;
|
||||||
|
|
||||||
|
@ -278,15 +280,33 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
|
||||||
),
|
),
|
||||||
|
|
||||||
// `func` symbols include additional data for param
|
// `func` symbols include additional data for param
|
||||||
// ordering. We don't care about that, so process the
|
// ordering, which we don't care about. But `map` includes
|
||||||
// declaration and then skip the rest.
|
// source field information which we want to keep. (We
|
||||||
|
// don't care about `retmap` for our purposes.)
|
||||||
b"preproc:sym" => {
|
b"preproc:sym" => {
|
||||||
let event = Self::process_sym(&ele, self.interner);
|
let mut event = Self::process_sym(&ele, self.interner)?;
|
||||||
|
|
||||||
self.reader
|
match &mut event {
|
||||||
.read_to_end(ele.name(), &mut self.sub_buffer)?;
|
XmloEvent::SymDecl(_, attrs)
|
||||||
|
if attrs.ty == Some(SymType::Map) =>
|
||||||
|
{
|
||||||
|
attrs.from = Some(Self::process_map_from(
|
||||||
|
self.interner,
|
||||||
|
&mut self.reader,
|
||||||
|
&mut self.sub_buffer,
|
||||||
|
)?);
|
||||||
|
|
||||||
event
|
Ok(event)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.reader.read_to_end(
|
||||||
|
ele.name(),
|
||||||
|
&mut self.sub_buffer,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just like the outer match, recurse
|
// Just like the outer match, recurse
|
||||||
|
@ -425,6 +445,56 @@ impl<'i, B: BufRead, I: Interner<'i>> XmloReader<'i, B, I> {
|
||||||
.ok_or(XmloError::UnassociatedSym)
|
.ok_or(XmloError::UnassociatedSym)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Process `preproc:from` for `preproc:sym[@type="map"]` elements.
|
||||||
|
///
|
||||||
|
/// Map symbols contain additional information describing source
|
||||||
|
/// inputs external to the system.
|
||||||
|
///
|
||||||
|
/// Errors
|
||||||
|
/// ======
|
||||||
|
/// - [`XmloError::InvalidMapFrom`] if `@name` missing or if unexpected
|
||||||
|
/// data (e.g. elements) are encountered.
|
||||||
|
/// - [`XmloError::XmlError`] on XML parsing failure.
|
||||||
|
fn process_map_from<'a>(
|
||||||
|
interner: &'i I,
|
||||||
|
reader: &mut XmlReader<B>,
|
||||||
|
buffer: &mut Vec<u8>,
|
||||||
|
) -> XmloResult<Vec<&'i Symbol<'i>>> {
|
||||||
|
let mut froms = Vec::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match reader.read_event(buffer)? {
|
||||||
|
XmlEvent::Empty(ele) if ele.name() == b"preproc:from" => froms
|
||||||
|
.push(
|
||||||
|
ele.attributes()
|
||||||
|
.with_checks(false)
|
||||||
|
.filter_map(Result::ok)
|
||||||
|
.find(|attr| attr.key == b"name")
|
||||||
|
.map_or(
|
||||||
|
Err(XmloError::InvalidMapFrom(
|
||||||
|
"preproc:from/@name missing".into(),
|
||||||
|
)),
|
||||||
|
|attr| {
|
||||||
|
Ok(unsafe {
|
||||||
|
interner
|
||||||
|
.intern_utf8_unchecked(&attr.value)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
)?,
|
||||||
|
),
|
||||||
|
|
||||||
|
XmlEvent::End(ele) if ele.name() == b"preproc:sym" => break,
|
||||||
|
|
||||||
|
// Note that whitespace counts as text
|
||||||
|
XmlEvent::Text(_) => (),
|
||||||
|
|
||||||
|
_ => Err(XmloError::InvalidMapFrom("unexpected data".into()))?,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(froms)
|
||||||
|
}
|
||||||
|
|
||||||
/// Process `preproc:sym-dep` element.
|
/// Process `preproc:sym-dep` element.
|
||||||
///
|
///
|
||||||
/// This represents an adjacency list for a given identifier in the
|
/// This represents an adjacency list for a given identifier in the
|
||||||
|
@ -638,6 +708,8 @@ pub enum XmloError {
|
||||||
InvalidDim(String),
|
InvalidDim(String),
|
||||||
/// A `preproc:sym-dep` element was found, but is missing `@name`.
|
/// A `preproc:sym-dep` element was found, but is missing `@name`.
|
||||||
UnassociatedSymDep,
|
UnassociatedSymDep,
|
||||||
|
/// The `preproc:sym[@type="map"]` contains unexpected or invalid data.
|
||||||
|
InvalidMapFrom(String),
|
||||||
/// Invalid dependency in adjacency list
|
/// Invalid dependency in adjacency list
|
||||||
/// (`preproc:sym-dep/preproc:sym-ref`).
|
/// (`preproc:sym-dep/preproc:sym-ref`).
|
||||||
MalformedSymRef(String),
|
MalformedSymRef(String),
|
||||||
|
@ -673,6 +745,9 @@ impl Display for XmloError {
|
||||||
XmloError::InvalidDim(dim) => {
|
XmloError::InvalidDim(dim) => {
|
||||||
write!(fmt, "invalid preproc:sym/@dim `{}`", dim)
|
write!(fmt, "invalid preproc:sym/@dim `{}`", dim)
|
||||||
}
|
}
|
||||||
|
XmloError::InvalidMapFrom(msg) => {
|
||||||
|
write!(fmt, "invalid preproc:sym[@type=\"map\"]: {}", msg)
|
||||||
|
}
|
||||||
XmloError::UnassociatedSymDep => write!(
|
XmloError::UnassociatedSymDep => write!(
|
||||||
fmt,
|
fmt,
|
||||||
"unassociated dependency list: preproc:sym-dep/@name missing"
|
"unassociated dependency list: preproc:sym-dep/@name missing"
|
||||||
|
@ -1282,7 +1357,9 @@ mod test {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some preproc:sym nodes have children (`func` symbols, specifically)
|
// Some preproc:sym nodes have children (`func` symbols,
|
||||||
|
// specifically) that we choose to ignore. See next test for
|
||||||
|
// data we do care about.
|
||||||
fn sym_nonempty_element(sut, interner) {
|
fn sym_nonempty_element(sut, interner) {
|
||||||
sut.reader.next_event = Some(Box::new(|_, _| {
|
sut.reader.next_event = Some(Box::new(|_, _| {
|
||||||
// Notice Start, not Empty
|
// Notice Start, not Empty
|
||||||
|
@ -1318,6 +1395,146 @@ mod test {
|
||||||
// next symbol.
|
// next symbol.
|
||||||
assert_eq!(Some("preproc:sym".into()), sut.reader.read_to_end_name);
|
assert_eq!(Some("preproc:sym".into()), sut.reader.read_to_end_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `map` symbols include information about their source
|
||||||
|
// fields.
|
||||||
|
fn sym_map_from(sut, interner) {
|
||||||
|
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
|
||||||
|
// Notice Start, not Empty
|
||||||
|
0 => Ok(XmlEvent::Start(MockBytesStart::new(
|
||||||
|
b"preproc:sym",
|
||||||
|
Some(MockAttributes::new(vec![
|
||||||
|
MockAttribute::new(
|
||||||
|
b"name", b"sym-map-from",
|
||||||
|
),
|
||||||
|
MockAttribute::new(
|
||||||
|
b"type", b"map",
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
1 => Ok(XmlEvent::Empty(MockBytesStart::new(
|
||||||
|
b"preproc:from",
|
||||||
|
Some(MockAttributes::new(vec![
|
||||||
|
MockAttribute::new(
|
||||||
|
b"name", b"from-a",
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
// make sure that whitespace is permitted
|
||||||
|
2 => Ok(XmlEvent::Text(MockBytesText::new(
|
||||||
|
b" ",
|
||||||
|
))),
|
||||||
|
|
||||||
|
3 => Ok(XmlEvent::Empty(MockBytesStart::new(
|
||||||
|
b"preproc:from",
|
||||||
|
Some(MockAttributes::new(vec![
|
||||||
|
MockAttribute::new(
|
||||||
|
b"name", b"from-b",
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
4 => Ok(XmlEvent::End(MockBytesEnd::new(
|
||||||
|
b"preproc:sym",
|
||||||
|
))),
|
||||||
|
|
||||||
|
_ => Err(XmlError::UnexpectedEof(
|
||||||
|
format!("MockXmlReader out of events: {}", event_i).into(),
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let result = sut.read_event()?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
XmloEvent::SymDecl(
|
||||||
|
interner.intern("sym-map-from"),
|
||||||
|
SymAttrs {
|
||||||
|
ty: Some(SymType::Map),
|
||||||
|
from: Some(vec![
|
||||||
|
interner.intern("from-a"),
|
||||||
|
interner.intern("from-b"),
|
||||||
|
]),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
),
|
||||||
|
result
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should _not_ have read to the end.
|
||||||
|
assert_eq!(None, sut.reader.read_to_end_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sym_map_from_missing_name(sut, interner) {
|
||||||
|
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
|
||||||
|
// Notice Start, not Empty
|
||||||
|
0 => Ok(XmlEvent::Start(MockBytesStart::new(
|
||||||
|
b"preproc:sym",
|
||||||
|
Some(MockAttributes::new(vec![
|
||||||
|
MockAttribute::new(
|
||||||
|
b"name", b"sym-map-from-bad",
|
||||||
|
),
|
||||||
|
MockAttribute::new(
|
||||||
|
b"type", b"map",
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
// missing @name
|
||||||
|
1 => Ok(XmlEvent::Empty(MockBytesStart::new(
|
||||||
|
b"preproc:from",
|
||||||
|
Some(MockAttributes::new(vec![])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
2 => Ok(XmlEvent::End(MockBytesEnd::new(
|
||||||
|
b"preproc:sym",
|
||||||
|
))),
|
||||||
|
|
||||||
|
_ => Err(XmlError::UnexpectedEof(
|
||||||
|
format!("MockXmlReader out of events: {}", event_i).into(),
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
match sut.read_event() {
|
||||||
|
Err(XmloError::InvalidMapFrom(msg)) => {
|
||||||
|
assert!(msg.contains("preproc:from"))
|
||||||
|
}
|
||||||
|
bad => panic!("expected XmloError: {:?}", bad),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sym_map_from_unexpected_data(sut, interner) {
|
||||||
|
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
|
||||||
|
// Notice Start, not Empty
|
||||||
|
0 => Ok(XmlEvent::Start(MockBytesStart::new(
|
||||||
|
b"preproc:sym",
|
||||||
|
Some(MockAttributes::new(vec![
|
||||||
|
MockAttribute::new(
|
||||||
|
b"name", b"sym-map-from-bad",
|
||||||
|
),
|
||||||
|
MockAttribute::new(
|
||||||
|
b"type", b"map",
|
||||||
|
),
|
||||||
|
])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
// garbage
|
||||||
|
1 => Ok(XmlEvent::Empty(MockBytesStart::new(
|
||||||
|
b"preproc:nonsense",
|
||||||
|
Some(MockAttributes::new(vec![])),
|
||||||
|
))),
|
||||||
|
|
||||||
|
_ => Err(XmlError::UnexpectedEof(
|
||||||
|
format!("MockXmlReader out of events: {}", event_i).into(),
|
||||||
|
)),
|
||||||
|
}));
|
||||||
|
|
||||||
|
match sut.read_event() {
|
||||||
|
Err(XmloError::InvalidMapFrom(_)) => (),
|
||||||
|
bad => panic!("expected XmloError: {:?}", bad),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! sym_test_reader_event {
|
macro_rules! sym_test_reader_event {
|
||||||
|
|
Loading…
Reference in New Issue