tamer: obj::xmle::xir: Complete writer functionality

This is a significant milestone, in the sense that it is the culmination of
the past month or so of work to prove that an Iterator-based XIR will be
viable for the system.

This barely had any impact on the performance from the previous commit
reporting the profiling.  This performs at least as well as the quick-xml
based writer.  In isolated benchmarks, it performs better, but in the real
world, the linker spends most of its time reading xmlo files, and so minor
differences in writing do not have a significant overall impact.

With that said, a lot of cleanup and documentation is still needed.  That is
the subject of the upcoming commits, before this writer can finalized.
main
Mike Gerwitz 2021-10-08 16:35:45 -04:00
parent 929a6c9815
commit d616d9475c
3 changed files with 151 additions and 7 deletions

View File

@ -24,7 +24,7 @@ use crate::{
asg::{
IdentKind, IdentObject, IdentObjectData, Sections, SectionsIter,
},
xir::{AttrValue, QName, Token},
xir::{AttrValue, QName, Text, Token},
},
ld::LSPAN,
sym::{st::*, SymbolId},
@ -39,6 +39,12 @@ qname_const! {
QN_DTYPE: :L_DTYPE,
QN_GENERATED: L_PREPROC:L_GENERATED,
QN_L_DEP: L_L:L_DEP,
QN_L_EXEC: L_L:L_EXEC,
QN_L_FROM: L_L:L_FROM,
QN_L_MAP_EXEC: L_L:L_MAP_EXEC,
QN_L_MAP_FROM: L_L:L_MAP_FROM,
QN_L_RETMAP_EXEC: L_L:L_RETMAP_EXEC,
QN_L_STATIC: L_L:L_STATIC,
QN_NAME: :L_NAME,
QN_PACKAGE: :L_PACKAGE,
QN_PARENT: :L_PARENT,
@ -52,8 +58,6 @@ qname_const! {
QN_XMLNS_L: L_XMLNS:L_L,
QN_XMLNS_PREPROC: L_XMLNS:L_PREPROC,
QN_YIELDS: :L_YIELDS,
QN_L_MAP_FROM: L_L:L_MAP_FROM,
QN_L_FROM: L_L:L_FROM,
}
const HEADER_SIZE: usize = 14;
@ -286,6 +290,47 @@ impl Iterator for MapFromsIter {
}
}
/// Produce text fragments associated with objects.
///
/// Here, "text" refers to the compiled program text.
struct FragmentIter<'a, T> {
iter: SectionsIter<'a, T>,
}
impl<'a, T: IdentObjectData> FragmentIter<'a, T> {
#[inline]
fn new(iter: SectionsIter<'a, T>) -> Self {
Self { iter }
}
}
impl<'a, T: IdentObjectData> Iterator for FragmentIter<'a, T> {
type Item = Token;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.iter
.by_ref()
.filter_map(|obj| {
match obj.as_ident().expect("encountered non-identifier object")
{
IdentObject::IdentFragment(_, _, _, frag) => Some(*frag),
// These will never have fragments.
IdentObject::Ident(_, IdentKind::Cgen(_), _)
| IdentObject::Ident(_, IdentKind::Gen(_, _), _)
| IdentObject::Ident(_, IdentKind::Lparam(_, _), _) => None,
// Error, but we really should catch that during Section
// lowering.
_ => todo! {},
}
})
.map(|frag| Token::Text(Text::Escaped(frag), LSPAN))
.next()
}
}
pub struct ElemWrapIter<I: Iterator<Item = Token>>(
Chain<Chain<Once<Token>, I>, Once<Token>>,
);
@ -315,8 +360,20 @@ impl<I: Iterator<Item = Token>> Iterator for ElemWrapIter<I> {
pub struct LowerIter<'a, T: IdentObjectData>(
ElemWrapIter<
Chain<
Chain<HeaderIter, ElemWrapIter<DepListIter<'a, T>>>,
ElemWrapIter<MapFromsIter>,
Chain<
Chain<
Chain<
Chain<
Chain<HeaderIter, ElemWrapIter<DepListIter<'a, T>>>,
ElemWrapIter<MapFromsIter>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
>,
ElemWrapIter<FragmentIter<'a, T>>,
>,
>,
);
@ -355,6 +412,26 @@ pub fn lower_iter<'a, T: IdentObjectData>(
Token::Open(QN_L_MAP_FROM, LSPAN),
MapFromsIter::new(sections),
Token::Close(Some(QN_L_MAP_FROM), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_MAP_EXEC, LSPAN),
FragmentIter::new(sections.iter_map()),
Token::Close(Some(QN_L_MAP_EXEC), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_RETMAP_EXEC, LSPAN),
FragmentIter::new(sections.iter_retmap()),
Token::Close(Some(QN_L_RETMAP_EXEC), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_STATIC, LSPAN),
FragmentIter::new(sections.iter_static()),
Token::Close(Some(QN_L_STATIC), LSPAN),
))
.chain(ElemWrapIter::new(
Token::Open(QN_L_EXEC, LSPAN),
FragmentIter::new(sections.iter_exec()),
Token::Close(Some(QN_L_EXEC), LSPAN),
)),
Token::Close(Some(QN_PACKAGE), LSPAN),
))

View File

@ -406,3 +406,67 @@ fn test_writes_map_froms() -> TestResult {
Ok(())
}
macro_rules! test_exec_sec {
($name:ident, $qn:ident, $sec:ident) => {
#[test]
fn $name() -> TestResult {
let mut sections = Sections::new();
let relroot = "relroot-exec".intern();
let frag_a = "a fragment".intern();
let frag_b = "b fragment".intern();
let a = IdentObject::IdentFragment(
"a".intern(),
IdentKind::Map,
Default::default(),
frag_a,
);
let b = IdentObject::IdentFragment(
"b".intern(),
IdentKind::Map,
Default::default(),
frag_b,
);
sections.$sec.push_body(&a);
sections.$sec.push_body(&b);
let mut iter = parser_from(
lower_iter(&sections, "pkg".intern(), relroot)
.skip_while(not(open($qn))),
);
let given = iter
.next()
.expect("tree object expected")
.unwrap() // Tree
.into_element()
.expect("element expected");
// Sanity check to ensure we have the element we're expecting.
assert_eq!($qn, given.name());
let nodes = given.children();
// The text is considered escaped since the fragment was already escaped
// within the xmlo file it was read from.
// Order _absolutely_ matters,
// since the purpose of the linker is to put things into the correct
// order for execution.
assert_eq!(nodes[0].as_text(), Some(&Text::Escaped(frag_a)));
assert_eq!(nodes[1].as_text(), Some(&Text::Escaped(frag_b)));
assert_eq!(nodes.len(), 2);
Ok(())
}
};
}
test_exec_sec!(test_map_exec, QN_L_MAP_EXEC, map);
test_exec_sec!(test_retmap_exec, QN_L_RETMAP_EXEC, retmap);
test_exec_sec!(test_static, QN_L_STATIC, params); // just pick a static
test_exec_sec!(test_exec, QN_L_EXEC, rater);

View File

@ -386,7 +386,6 @@ pub mod st {
N8: dec "8",
N9: dec "9",
L_BOOLEAN: cid "boolean",
L_CGEN: cid "cgen",
L_CLASS: cid "class",
@ -396,16 +395,18 @@ pub mod st {
L_DIM: cid "dim",
L_DTYPE: cid "dtype",
L_EMPTY: cid "empty",
L_EXEC: cid "exec",
L_FALSE: cid "false",
L_FLOAT: cid "float",
L_FUNC: cid "func",
L_FROM: cid "from",
L_FUNC: cid "func",
L_GEN: cid "gen",
L_GENERATED: cid "generated",
L_INTEGER: cid "integer",
L_L: cid "l",
L_LPARAM: cid "lparam",
L_MAP: cid "map",
L_MAP_EXEC: tid "map-exec",
L_MAP_FROM: tid "map-from",
L_MAP_HEAD: qname "map:head",
L_MAP_TAIL: qname "map:tail",
@ -418,9 +419,11 @@ pub mod st {
L_PROGRAM: cid "program",
L_RATE: cid "rate",
L_RETMAP: cid "retmap",
L_RETMAP_EXEC: tid "retmap-exec",
L_RETMAP_HEAD: qname "retmap:head",
L_RETMAP_TAIL: qname "retmap:tail",
L_SRC: cid "src",
L_STATIC: cid "static",
L_SYM: cid "sym",
L_TITLE: cid "title",
L_TPL: cid "tpl",