tamer: xir::parse::ele: Initial namespace prefix matching support
This allows matching on a namespace prefix by providing a `Prefix` instead of a `QName`. This works, but is missing a couple notable things (and possibly more): 1. Tracking the QName that is _actually_ matched so that it can be used in messages stating what the expected closing tag is; and 2. Making that QName available via a binding. This will be used to match on `t:*` in NIR. If you're wondering how attribute parsing is supposed to work with that (of course you're wondering that, random person reading this)---that'll have to work differently for those matches, since template shorthand application contains argument names as attributes. DEV-7145main
parent
f9fe4aa13b
commit
8cb03d8d16
|
@ -509,6 +509,7 @@ pub mod st {
|
|||
L_DTYPE: cid "dtype",
|
||||
L_ELIG_CLASS_YIELDS: tid "elig-class-yields",
|
||||
L_EMPTY: cid "empty",
|
||||
L_EQ: cid "eq",
|
||||
L_EXEC: cid "exec",
|
||||
L_EXPORT: cid "export",
|
||||
L_EXTERN: cid "extern",
|
||||
|
|
|
@ -19,7 +19,9 @@
|
|||
|
||||
//! XIR formatting types for use with [`crate::fmt`]
|
||||
|
||||
use crate::fmt::{AndQualConjList, Delim, OrQualConjList, Prefix, Raw, Tt};
|
||||
use crate::fmt::{
|
||||
AndQualConjList, Delim, OrQualConjList, Prefix, Raw, Suffix, Tt,
|
||||
};
|
||||
|
||||
/// Denote an XML attribute by prefixing the value with `@`.
|
||||
pub type XmlAttr = Prefix<"@", Raw>;
|
||||
|
@ -33,6 +35,12 @@ pub type OpenXmlEle = Delim<"<", ">", Raw>;
|
|||
/// Opening tag for XML element.
|
||||
pub type CloseXmlEle = Delim<"</", ">", Raw>;
|
||||
|
||||
/// "`ns:*`" given a namespace prefix `ns`.
|
||||
///
|
||||
/// TODO: It'd be nice to be able to have Raw require a specific type to
|
||||
/// ensure that we're given a prefix.
|
||||
pub type XmlPrefixAnyLocal = Suffix<":*", Raw>;
|
||||
|
||||
/// Opening tag for XML element as teletypewriter
|
||||
/// (for use in sentences).
|
||||
pub type TtOpenXmlEle = Tt<OpenXmlEle>;
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::{
|
|||
ClosedParseState, Context, ParseState, Token, Transition,
|
||||
TransitionResult,
|
||||
},
|
||||
xir::QName,
|
||||
xir::{Prefix, QName},
|
||||
};
|
||||
|
||||
#[cfg(doc)]
|
||||
|
@ -201,15 +201,18 @@ impl<S: ClosedParseState> StateStack<S> {
|
|||
pub enum NodeMatcher {
|
||||
/// Static [`QName`] with a simple equality check.
|
||||
QName(QName),
|
||||
/// Any element with a matching [`Prefix`].
|
||||
Prefix(Prefix),
|
||||
}
|
||||
|
||||
impl NodeMatcher {
|
||||
/// Match against the provided [`QName`].
|
||||
pub fn matches(&self, qname: QName) -> bool {
|
||||
matches!(
|
||||
self,
|
||||
Self::QName(qn_match) if qn_match == &qname
|
||||
)
|
||||
match self {
|
||||
Self::QName(qn_match) if qn_match == &qname => true,
|
||||
Self::Prefix(prefix) if Some(*prefix) == qname.prefix() => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -219,10 +222,19 @@ impl From<QName> for NodeMatcher {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Prefix> for NodeMatcher {
|
||||
fn from(prefix: Prefix) -> Self {
|
||||
Self::Prefix(prefix)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for NodeMatcher {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use crate::xir::fmt::XmlPrefixAnyLocal;
|
||||
|
||||
match self {
|
||||
Self::QName(qname) => Display::fmt(qname, f),
|
||||
Self::Prefix(prefix) => XmlPrefixAnyLocal::fmt(prefix, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -516,8 +528,7 @@ macro_rules! ele_parse {
|
|||
impl crate::xir::parse::EleParseState for $nt {}
|
||||
|
||||
impl $nt {
|
||||
/// [`QName`](crate::xir::QName) of the element recognized
|
||||
/// by this parser.
|
||||
/// Matcher describing the node recognized by this parser.
|
||||
#[allow(dead_code)] // used by sum parser
|
||||
fn matcher() -> crate::xir::parse::NodeMatcher {
|
||||
crate::xir::parse::NodeMatcher::from($qname)
|
||||
|
@ -548,7 +559,7 @@ macro_rules! ele_parse {
|
|||
Self::Expecting_(_) => write!(
|
||||
f,
|
||||
"expecting opening tag {}",
|
||||
TtOpenXmlEle::wrap($qname),
|
||||
TtOpenXmlEle::wrap(Self::matcher()),
|
||||
),
|
||||
Self::RecoverEleIgnore_(_, name, _, _)
|
||||
| Self::RecoverEleIgnoreClosed_(_, name, _) => write!(
|
||||
|
@ -557,26 +568,28 @@ macro_rules! ele_parse {
|
|||
with unexpected name {given} \
|
||||
(expected {expected})",
|
||||
given = TtQuote::wrap(name),
|
||||
expected = TtQuote::wrap($qname),
|
||||
expected = TtQuote::wrap(Self::matcher()),
|
||||
),
|
||||
Self::CloseRecoverIgnore_((_, _, depth), _) => write!(
|
||||
f,
|
||||
"attempting to recover by ignoring input \
|
||||
until the expected end tag {expected} \
|
||||
at depth {depth}",
|
||||
expected = TtCloseXmlEle::wrap($qname),
|
||||
expected = TtCloseXmlEle::wrap(Self::matcher()),
|
||||
),
|
||||
|
||||
Self::Attrs_(_, sa) => std::fmt::Display::fmt(sa, f),
|
||||
Self::ExpectClose_((_, _, depth)) => write!(
|
||||
f,
|
||||
"expecting closing element {} at depth {depth}",
|
||||
TtCloseXmlEle::wrap($qname)
|
||||
// TODO: Actual closing tag name
|
||||
// (this may be a prefix).
|
||||
TtCloseXmlEle::wrap(Self::matcher())
|
||||
),
|
||||
Self::Closed_(_, _cfg) => write!(
|
||||
f,
|
||||
"done parsing element {}",
|
||||
TtQuote::wrap($qname)
|
||||
TtQuote::wrap(Self::matcher())
|
||||
),
|
||||
$(
|
||||
// TODO: A better description.
|
||||
|
@ -639,12 +652,13 @@ macro_rules! ele_parse {
|
|||
f,
|
||||
"unexpected {unexpected} (expecting {expected})",
|
||||
unexpected = TtOpenXmlEle::wrap(name),
|
||||
expected = TtOpenXmlEle::wrap($qname),
|
||||
expected = TtOpenXmlEle::wrap($nt::matcher()),
|
||||
),
|
||||
Self::CloseExpected_(_, tok) => write!(
|
||||
f,
|
||||
"expected {}, but found {}",
|
||||
TtCloseXmlEle::wrap($qname),
|
||||
// TODO: Actual close name.
|
||||
TtCloseXmlEle::wrap($nt::matcher()),
|
||||
TtQuote::wrap(tok)
|
||||
),
|
||||
Self::Attrs_(e) => std::fmt::Display::fmt(e, f),
|
||||
|
@ -665,7 +679,7 @@ macro_rules! ele_parse {
|
|||
Self::UnexpectedEle_(_, ospan) => ospan.error(
|
||||
format!(
|
||||
"expected {ele_name} here",
|
||||
ele_name = TtQuote::wrap($qname)
|
||||
ele_name = TtQuote::wrap($nt::matcher())
|
||||
)
|
||||
).into(),
|
||||
|
||||
|
@ -673,7 +687,8 @@ macro_rules! ele_parse {
|
|||
span.note("element starts here"),
|
||||
tok.span().error(format!(
|
||||
"expected {}",
|
||||
TtCloseXmlEle::wrap($qname),
|
||||
// TODO: Actual close name
|
||||
TtCloseXmlEle::wrap($nt::matcher()),
|
||||
)),
|
||||
],
|
||||
|
||||
|
@ -730,7 +745,7 @@ macro_rules! ele_parse {
|
|||
(
|
||||
Closed_(cfg, ..),
|
||||
XirfToken::Open(qname, span, depth)
|
||||
) if cfg.repeat && qname == $qname => {
|
||||
) if cfg.repeat && Self::matcher().matches(qname) => {
|
||||
Transition(Attrs_(
|
||||
(cfg, span.tag_span(), depth),
|
||||
parse_attrs(qname, span)
|
||||
|
|
|
@ -43,7 +43,7 @@ use crate::{
|
|||
xir::{
|
||||
attr::{Attr, AttrSpan},
|
||||
flat::{Depth, RefinedText, Text, Whitespace, XirfToken},
|
||||
st::qname::*,
|
||||
st::{prefix::*, qname::*},
|
||||
CloseSpan, EleNameLen, EleSpan, OpenSpan, QName,
|
||||
},
|
||||
};
|
||||
|
@ -159,6 +159,77 @@ fn empty_element_no_attrs_with_close_with_spans() {
|
|||
);
|
||||
}
|
||||
|
||||
// Match on a namespace prefix rather than a static QName.
|
||||
#[test]
|
||||
fn empty_element_ns_prefix() {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Foo;
|
||||
impl Object for Foo {}
|
||||
|
||||
ele_parse! {
|
||||
enum Sut;
|
||||
type Object = Foo;
|
||||
|
||||
// This matches `c:*`.
|
||||
Root := NS_C {
|
||||
@ {} => Foo,
|
||||
};
|
||||
}
|
||||
|
||||
let toks = vec![
|
||||
// Just some `c:*`.
|
||||
XirfToken::Open(QN_C_EQ, OpenSpan(S1, N), Depth(0)),
|
||||
XirfToken::Close(None, CloseSpan::empty(S2), Depth(0)),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
Ok(vec![
|
||||
Parsed::Incomplete, // [Root] Open
|
||||
Parsed::Object(Foo), // [Root@] Close (>LA)
|
||||
Parsed::Incomplete, // [Root] Close (<LA)
|
||||
]),
|
||||
Sut::parse(toks.into_iter()).collect(),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_element_ns_prefix_nomatch() {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct Foo;
|
||||
impl Object for Foo {}
|
||||
|
||||
ele_parse! {
|
||||
enum Sut;
|
||||
type Object = Foo;
|
||||
|
||||
// This matches `c:*`.
|
||||
Root := NS_C {
|
||||
@ {} => Foo,
|
||||
};
|
||||
}
|
||||
|
||||
let span = OpenSpan(S1, N);
|
||||
// Non `c:*` element.
|
||||
let unexpected = QN_PACKAGE;
|
||||
|
||||
let toks = vec![
|
||||
XirfToken::Open(unexpected, span, Depth(0)),
|
||||
XirfToken::Close(None, CloseSpan::empty(S2), Depth(0)),
|
||||
];
|
||||
|
||||
let mut sut = Sut::parse(toks.into_iter());
|
||||
|
||||
let err = sut.next().unwrap().unwrap_err();
|
||||
assert_eq!(
|
||||
// TODO: This references generated identifiers.
|
||||
ParseError::StateError(SutError_::Root(RootError_::UnexpectedEle_(
|
||||
unexpected,
|
||||
span.name_span()
|
||||
))),
|
||||
err,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_element_with_attr_bindings() {
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -140,5 +140,7 @@ pub mod qname {
|
|||
QN_XMLNS_L: L_XMLNS:L_L,
|
||||
QN_XMLNS_PREPROC: L_XMLNS:L_PREPROC,
|
||||
QN_YIELDS: :L_YIELDS,
|
||||
|
||||
QN_C_EQ: L_C:L_EQ,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue