tamer: obj::xmlo::reader: Parse preproc:sym/preproc:from

Ideally this would just be an attribute, but I guess I never got around to
making that change in the compiler and I don't want a detour right now.

DEV-10863
main
Mike Gerwitz 2022-03-30 12:06:38 -04:00
parent 9b429b6fc3
commit 3f8e397e57
5 changed files with 162 additions and 50 deletions

View File

@ -53,8 +53,10 @@ pub enum XmloError {
InvalidDim(SymbolId, Span),
/// A `preproc:sym-dep` element was found, but is missing `@name`.
UnassociatedSymDep,
/// The `preproc:sym[@type="map"]` contains unexpected or invalid data.
InvalidMapFrom(String),
/// The `preproc:sym[@type="map"]` is missing a @name.
MapFromNameMissing(SymbolId, Span),
/// Multiple `preproc:from` nodes found.
MapFromMultiple(SymbolId, Span),
/// Invalid dependency in adjacency list
/// (`preproc:sym-dep/preproc:sym-ref`).
MalformedSymRef(String),
@ -100,8 +102,19 @@ impl Display for XmloError {
Self::InvalidDim(dim, span) => {
write!(fmt, "invalid preproc:sym/@dim `{dim}` at {span}")
}
Self::InvalidMapFrom(msg) => {
write!(fmt, "invalid preproc:sym[@type=\"map\"]: {}", msg)
Self::MapFromNameMissing(sym, span) => {
write!(
fmt,
"preproc:sym[@type=\"map\"]/preproc:from/@name missing \
for symbol `{sym}` at {span}"
)
}
Self::MapFromMultiple(sym, span) => {
write!(
fmt,
"preproc:sym[@type=\"map\"]/preproc:from must appear \
only once for symbol `{sym}` at {span}"
)
}
Self::UnassociatedSymDep => write!(
fmt,

View File

@ -117,6 +117,7 @@ qname_const! {
QN_UUROOTPATH: :L_UUROOTPATH,
QN_VIRTUAL: :L_VIRTUAL,
QN_YIELDS: :L_YIELDS,
QN_FROM: L_PREPROC:L_FROM,
}
pub trait XmloSymtableState =
@ -205,6 +206,8 @@ pub enum SymtableState {
Ready,
/// Processing a symbol.
Sym(Span, Option<SymbolId>, SymAttrs),
/// Awaiting a symbol map name.
SymMapFrom(Span, SymbolId, SymAttrs, Span),
}
impl parse::Object for (SymbolId, SymAttrs, Span) {}
@ -243,6 +246,37 @@ impl ParseState for SymtableState {
) => Self::parse_sym_attr(&mut attrs, key, value, span_attrval)
.transition(Sym(span_sym, name, attrs)),
// `preproc:from` supported only for `type="map"`.
// TODO: The compiler really ought to just make this an
// attribute now so we can simplify parsing here.
(
Sym(span_sym, Some(name), attrs),
Xirf::Open(QN_FROM, span_from, _),
) if attrs.ty == Some(SymType::Map) => {
Transition(SymMapFrom(span_sym, name, attrs, span_from))
.incomplete()
}
(
SymMapFrom(span_sym, name, mut attrs, span_from),
Xirf::Attr(Attr(QN_NAME, from_name, _)),
) => match attrs.from.replace(from_name) {
Some(_) => Err(XmloError::MapFromMultiple(name, span_from)),
None => Ok(()),
}
.transition(SymMapFrom(span_sym, name, attrs, span_from)),
(SymMapFrom(span_sym, name, attrs, span_from), Xirf::Close(..)) => {
if attrs.from.is_none() {
return Transition(SymMapFrom(
span_sym, name, attrs, span_from,
))
.err(XmloError::MapFromNameMissing(name, span_from));
}
Transition(Sym(span_sym, Some(name), attrs)).incomplete()
}
todo => todo!("{todo:?}"),
}
}

View File

@ -231,12 +231,13 @@ where
let mut event = Self::process_sym(&self.pkg_name, &ele)?;
match &mut event {
XmloEvent::SymDecl(_, attrs, _)
XmloEvent::SymDecl(name, attrs, _)
if attrs.ty == Some(SymType::Map) =>
{
attrs.from = Self::process_map_from(
&mut self.reader,
&mut self.sub_buffer,
*name,
)?;
Ok(event)
@ -432,6 +433,7 @@ where
fn process_map_from<'a>(
reader: &mut XmlReader<B>,
buffer: &mut Vec<u8>,
name: SymbolId,
) -> XmloResult<Option<SymbolId>> {
let mut from = None;
@ -441,9 +443,9 @@ where
if from.is_some() {
// This feature isn't actually utilized for the
// input map.
return Err(XmloError::InvalidMapFrom(
"multiple preproc:from found for input map entry"
.into(),
return Err(XmloError::MapFromMultiple(
name,
UNKNOWN_SPAN,
));
}
@ -453,8 +455,9 @@ where
.filter_map(Result::ok)
.find(|attr| attr.key == b"name")
.map_or(
Err(XmloError::InvalidMapFrom(
"preproc:from/@name missing".into(),
Err(XmloError::MapFromNameMissing(
name,
UNKNOWN_SPAN,
)),
|attr| {
Ok(unsafe {
@ -470,7 +473,7 @@ where
// Note that whitespace counts as text
XmlEvent::Text(_) => (),
_ => Err(XmloError::InvalidMapFrom("unexpected data".into()))?,
_ => todo!("unexpected preproc:sym[type=\"map\"] input"),
};
}

View File

@ -463,6 +463,7 @@ xmlo_tests! {
assert_eq!(Some("preproc:sym".into()), sut.reader.read_to_end_name);
}
// DONE
// `map` symbols include information about their source
// fields.
fn sym_map_from(sut) {
@ -525,6 +526,7 @@ xmlo_tests! {
assert_eq!(None, sut.reader.read_to_end_name);
}
// DONE
fn sym_map_from_missing_name(sut) {
sut.reader.next_event = Some(Box::new(|_, event_i| match event_i {
// Notice Start, not Empty
@ -555,44 +557,10 @@ xmlo_tests! {
)),
}));
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) {
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(InnerXmlError::UnexpectedEof(
format!("MockXmlReader out of events: {}", event_i).into(),
)),
}));
match sut.read_event() {
Err(XmloError::InvalidMapFrom(_)) => (),
bad => panic!("expected XmloError: {:?}", bad),
}
assert_eq!(
sut.read_event(),
Err(XmloError::MapFromNameMissing("sym-map-from-bad".into(), UNKNOWN_SPAN))
);
}
fn read_events_via_iterator(sut) {

View File

@ -23,7 +23,7 @@ use super::*;
use crate::{
convert::ExpectInto,
obj::xmlo::{SymDtype, SymType},
parse::{ParseError, ParseState, Parsed},
parse::{ParseError, ParseState, ParseStatus, Parsed},
span::{Span, DUMMY_SPAN},
sym::GlobalSymbolIntern,
xir::{
@ -416,3 +416,97 @@ fn symtable_sym_generated_true() {
SymtableState::parse(toks).collect(),
);
}
// `map` symbols include information about their source
// fields.
#[test]
fn symtable_map_from() {
let name = "sym-map-from".into();
let map_from = "from-a".into();
let toks = [
Xirf::Open(QN_SYM, SSYM, Depth(0)),
Xirf::Attr(Attr(QN_NAME, name, (S2, S3))),
Xirf::Attr(Attr(QN_TYPE, raw::L_MAP, (S3, S4))),
// <preproc:from>
Xirf::Open(QN_FROM, S2, Depth(1)),
Xirf::Attr(Attr(QN_NAME, map_from, (S2, S3))),
Xirf::Close(None, S4, Depth(1)),
// />
Xirf::Close(Some(QN_SYM), S2, Depth(0)),
]
.into_iter();
let expected = SymAttrs {
ty: Some(SymType::Map),
from: Some(map_from),
..Default::default()
};
assert_eq!(
Ok(vec![
Parsed::Incomplete, // Opening tag
Parsed::Incomplete, // @name
Parsed::Incomplete, // @type
Parsed::Incomplete, // <preproc:from
Parsed::Incomplete, // @name
Parsed::Incomplete, // />
Parsed::Object((name, expected, SSYM)),
]),
SymtableState::parse(toks).collect(),
);
}
#[test]
fn symtable_map_from_missing_name() {
let name = "sym-map-from-missing".into();
let toks = [
Xirf::Open(QN_SYM, SSYM, Depth(0)),
Xirf::Attr(Attr(QN_NAME, name, (S2, S3))),
Xirf::Attr(Attr(QN_TYPE, raw::L_MAP, (S3, S4))),
// <preproc:from>
Xirf::Open(QN_FROM, S2, Depth(1)),
// @name missing
Xirf::Close(None, S4, Depth(1)),
// />
Xirf::Close(Some(QN_SYM), S2, Depth(0)),
]
.into_iter();
assert_eq!(
Err(ParseError::StateError(XmloError::MapFromNameMissing(name, S2))), // />
SymtableState::parse(toks)
.collect::<Result<Vec<Parsed<<SymtableState as ParseState>::Object>>, _>>(),
);
}
// Multiple `from` nodes used to be a thing but are no longer utilized.
#[test]
fn symtable_map_from_multiple() {
let name = "sym-map-from-missing".into();
let toks = [
Xirf::Open(QN_SYM, SSYM, Depth(0)),
Xirf::Attr(Attr(QN_NAME, name, (S2, S3))),
Xirf::Attr(Attr(QN_TYPE, raw::L_MAP, (S3, S4))),
// <preproc:from>
Xirf::Open(QN_FROM, S2, Depth(1)),
Xirf::Attr(Attr(QN_NAME, "ok".into(), (S2, S3))),
Xirf::Close(None, S4, Depth(1)),
// />
// <preproc:from> again (err)
Xirf::Open(QN_FROM, S3, Depth(1)),
Xirf::Attr(Attr(QN_NAME, "bad".into(), (S2, S3))),
Xirf::Close(None, S4, Depth(1)),
// />
Xirf::Close(Some(QN_SYM), S2, Depth(0)),
]
.into_iter();
assert_eq!(
Err(ParseError::StateError(XmloError::MapFromMultiple(name, S3))), // />
SymtableState::parse(toks)
.collect::<Result<Vec<Parsed<<SymtableState as ParseState>::Object>>, _>>(),
);
}