tamer: obj::xmlo::reader: Parse root package node attributes

Well, parse to the extent that it was being parsed before, anyway.

The core of this change demonstrates how well TAMER's abstractions work well
together.  (As long as you have an e.g. LSP to help you make sense of all of
the inference, I suppose.)

  Token::Open(QN_LV_PACKAGE | QN_PACKAGE, _) => {
      return Ok(XmloEvent::Package(
          attr_parser_from(&mut self.reader)
              .try_collect_ok()??,
      ));
  }

This finally makes use of `attr_parser_from` and `try_collect_ok`.  All of
the types are inferred---from the iterator transformations, to the error
conversions, to the destination PackageAttrs type.

DEV-10863
main
Mike Gerwitz 2021-11-18 00:59:10 -05:00
parent d421112f35
commit ba4c32383f
5 changed files with 133 additions and 3 deletions

View File

@ -21,6 +21,7 @@
use crate::sym::SymbolId;
use crate::tpwrap::quick_xml::{Error as XmlError, InnerXmlError};
use crate::xir::tree::ParseError as XirtParseError;
use std::fmt::Display;
/// Error during `xmlo` processing.
@ -36,6 +37,8 @@ use std::fmt::Display;
pub enum XmloError {
/// XML parsing error (legacy, quick-xml).
XmlError(XmlError),
/// XIR parsing error.
XirtError(XirtParseError),
/// The root node was not an `lv:package`.
UnexpectedRoot,
/// A `preproc:sym` node was found, but is missing `@name`.
@ -67,10 +70,17 @@ impl From<InnerXmlError> for XmloError {
}
}
impl From<XirtParseError> for XmloError {
fn from(e: XirtParseError) -> Self {
Self::XirtError(e)
}
}
impl Display for XmloError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::XmlError(e) => e.fmt(fmt),
Self::XirtError(e) => e.fmt(fmt),
Self::UnexpectedRoot => {
write!(fmt, "unexpected package root (is this a package?)")
}
@ -115,6 +125,7 @@ impl std::error::Error for XmloError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::XmlError(e) => Some(e),
Self::XirtError(e) => Some(e),
_ => None,
}
}

View File

@ -18,7 +18,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use super::{PackageAttrs, SymAttrs, XmloError};
use crate::sym::SymbolId;
use crate::iter::TryFromIterator;
use crate::sym::{st::*, SymbolId};
use crate::xir::tree::Attr;
// While the _use_ is gated, this isn't, to ensure that we still try to
// compile it while the flag is off (and so it's parsed by the language
@ -37,6 +39,13 @@ pub use new::XmloReader;
/// potentially fail in error.
pub type XmloResult<T> = Result<T, XmloError>;
qname_const! {
QN_NAME: :L_NAME,
QN_ROOTPATH: :L_UUROOTPATH,
QN_PROGRAM: :L_PROGRAM,
QN_PREPROC_ELIG_CLASS_YIELDS: L_PREPROC:L_ELIG_CLASS_YIELDS,
}
#[cfg(feature = "wip-xmlo-xir-reader")]
mod new {
//! Re-implementation of `XmloReader` using a [`TokenStream`].
@ -45,7 +54,9 @@ mod new {
//! it exists to make feature-flagging less confusing and error-prone.
use super::{XmloError, XmloEvent, XmloResult};
use crate::iter::TryCollect;
use crate::sym::st::*;
use crate::xir::tree::attr_parser_from;
use crate::xir::{Token, TokenStream};
qname_const! {
@ -53,6 +64,7 @@ mod new {
QN_PACKAGE: :L_PACKAGE,
}
#[derive(Debug)]
pub struct XmloReader<I: TokenStream> {
reader: I,
seen_root: bool,
@ -75,7 +87,10 @@ mod new {
if !self.seen_root {
match token {
Token::Open(QN_LV_PACKAGE | QN_PACKAGE, _) => {
//self.seen_root = true;
return Ok(XmloEvent::Package(
attr_parser_from(&mut self.reader)
.try_collect_ok()??,
));
}
_ => return Err(XmloError::UnexpectedRoot),
@ -155,6 +170,35 @@ pub enum XmloEvent {
Eoh,
}
impl TryFromIterator<Attr> for PackageAttrs {
type Error = XmloError;
fn try_from_iter<I: IntoIterator<Item = Attr>>(
iter: I,
) -> Result<Self, Self::Error> {
let mut attrs: Self = Default::default();
for attr in iter.into_iter() {
let val = attr.value_atom();
match attr.name() {
QN_NAME => attrs.name = Some(val),
QN_ROOTPATH => attrs.relroot = Some(val),
QN_PROGRAM => attrs.program = val == raw::L_TRUE,
QN_PREPROC_ELIG_CLASS_YIELDS => attrs.elig = Some(val),
// Ignore anything else on the root node.
// TODO: After tamec is fully migrated from XSLT,
// before we move to a different format,
// we should error here.
_ => (),
}
}
Ok(attrs)
}
}
#[cfg(feature = "wip-xmlo-xir-reader")]
#[cfg(test)]
mod test;

View File

@ -99,6 +99,7 @@ xmlo_tests! {
}
}
// DONE
fn recognizes_valid_roots(sut) {
// xmlo_tests macro sets this for us, so we need to clear it to
// be able to perform the check
@ -129,6 +130,7 @@ xmlo_tests! {
sut.read_event()?;
}
// DONE
fn package_event_program(sut) {
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Start(MockBytesStart::new(
@ -154,6 +156,7 @@ xmlo_tests! {
);
}
// DONE
fn package_event_nonprogram(sut) {
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Start(MockBytesStart::new(
@ -173,6 +176,7 @@ xmlo_tests! {
);
}
// DONE
fn package_event_name(sut) {
sut.reader.next_event = Some(Box::new(|_, _| {
Ok(XmlEvent::Start(MockBytesStart::new(

View File

@ -42,3 +42,68 @@ fn fails_on_invalid_root() {
assert_matches!(sut.next(), Some(Err(XmloError::UnexpectedRoot)));
}
#[test]
fn parses_package_attrs() {
let name = "pkgroot".into();
let relroot = "../../".into();
let elig = "elig-class-yields".into();
let mut sut = Sut::from_reader(
[
Token::Open("package".unwrap_into(), DUMMY_SPAN),
Token::AttrName("name".unwrap_into(), DUMMY_SPAN),
Token::AttrValue(name, DUMMY_SPAN),
Token::AttrName("__rootpath".unwrap_into(), DUMMY_SPAN),
Token::AttrValue(relroot, DUMMY_SPAN),
Token::AttrName("program".unwrap_into(), DUMMY_SPAN),
Token::AttrValue(raw::L_TRUE, DUMMY_SPAN),
Token::AttrName(
("preproc", "elig-class-yields").unwrap_into(),
DUMMY_SPAN,
),
Token::AttrValue(elig, DUMMY_SPAN),
Token::AttrEnd,
]
.into_iter(),
);
let result = sut.next();
assert_eq!(
Some(Ok(XmloEvent::Package(PackageAttrs {
name: Some(name),
relroot: Some(relroot),
program: true,
elig: Some(elig),
}))),
result
);
}
// The linker does not [yet] parse namespaces.
#[test]
fn parses_package_attrs_with_ns_prefix() {
let name = "pkgrootns".into();
let mut sut = Sut::from_reader(
[
Token::Open(("lv", "package").unwrap_into(), DUMMY_SPAN),
Token::AttrName("name".unwrap_into(), DUMMY_SPAN),
Token::AttrValue(name, DUMMY_SPAN),
Token::AttrEnd,
]
.into_iter(),
);
let result = sut.next();
// NB: This also tests defaults and non-program.
assert_eq!(
Some(Ok(XmloEvent::Package(PackageAttrs {
name: Some(name),
..Default::default()
}))),
result
);
}

View File

@ -196,7 +196,7 @@
use super::{QName, Token, TokenResultStream, TokenStream};
use crate::{span::Span, sym::SymbolId};
use std::{fmt::Display, iter, mem::take};
use std::{error::Error, fmt::Display, iter, mem::take};
mod attr;
pub use attr::{Attr, AttrList, AttrParts, SimpleAttr};
@ -940,6 +940,12 @@ impl Display for ParseError {
}
}
impl Error for ParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
}
/// Either a parsed [`Tree`] or an indication that more tokens are needed to
/// complete the active context.
///