tamer: xir::parse: Fixes for {ele,attr}_parse! outside of module

The tests had certain things in scope, but now that I'm trying to use it
outside of those modules, some fixes are needed.

This is admittedly a sloppy commit, with a number of miscellaneous fixes.  I
didn't bother separating it more because most of them are type fixes, and
the `From<Attr>` stuff is going to have to change into, likely,
`TryFrom<Attr>` so that parse failures can occur when attributes do not
match certain patterns.

DEV-7145
main
Mike Gerwitz 2022-07-20 15:36:28 -04:00
parent e517e15a29
commit 184ff6bdcc
4 changed files with 88 additions and 20 deletions

View File

@ -25,4 +25,5 @@
mod attr;
mod ele;
pub use attr::{AttrParseError, AttrParseState};
pub use attr::{parse_attrs, AttrParseError, AttrParseState};
pub use ele::{EleParseCfg, EleParseState};

View File

@ -199,7 +199,6 @@ pub trait AttrParseState: ParseState {
/// This function is useful when the type of [`AttrParseState`] `S` can be
/// inferred,
/// so that the expression reads more like natural language.
#[cfg(test)] // currently only used by tests; remove when ready
pub fn parse_attrs<S: AttrParseState>(ele: QName, span: OpenSpan) -> S {
S::with_element(ele, span)
}
@ -219,7 +218,7 @@ macro_rules! attr_parse {
// rather than relying on `Into::into` to cause the error
// later on,
// which places the error inside the macro definition.
assert_impl_all!($ty: From<crate::xir::attr::Attr>);
$crate::attr_parse!(@ty_assert $($fmod)? $ty);
)*
#[doc=concat!("Parser producing [`", stringify!($struct_name), "`].")]
@ -240,7 +239,7 @@ macro_rules! attr_parse {
___done: bool,
$(
// Value + key span
pub $field: Option<($ty, Span)>,
pub $field: Option<($ty, crate::span::Span)>,
)*
}
@ -313,8 +312,11 @@ macro_rules! attr_parse {
}
impl $state_name {
fn done_with_element(ele: crate::xir::QName, span: OpenSpan) -> Self {
use crate::xir::parse::attr::AttrParseState;
fn done_with_element(
ele: crate::xir::QName,
span: crate::xir::OpenSpan,
) -> Self {
use crate::xir::parse::AttrParseState;
let mut new = Self::with_element(ele, span);
new.___done = true;
@ -374,7 +376,7 @@ macro_rules! attr_parse {
parse::{AttrParseError, AttrParseState}
};
#[allow(unused_imports)]
use crate::xir::attr::Attr; // unused if no attrs
use crate::xir::attr::{Attr, AttrSpan}; // unused if no attrs
match tok {
$(
@ -402,7 +404,9 @@ macro_rules! attr_parse {
// First time seeing attribute name
None => {
self.$field.replace((
attr.into(),
$crate::attr_parse!(
@into_value $($fmod)? attr
),
kspan,
));
@ -447,6 +451,26 @@ macro_rules! attr_parse {
}
};
// Optional attribute if input above is of the form `(QN_FOO?) => ...`.
(@ty_assert ? $ty:ty) => {
// This type assertion isn't supported by `assert_impl_all!`.
// The error isn't the most clear,
// but it's better than nothing and we can improve upon it later
// on.
const _: fn() = || {
trait OptionFromAttr {}
impl<T: From<Attr>> OptionFromAttr for Option<T> {}
// Fail when `$ty` is not Option<impl From<Attr>>.
fn assert_attr_option<T: OptionFromAttr>() {}
assert_attr_option::<$ty>();
};
};
(@ty_assert $ty:ty) => {
assert_impl_all!($ty: From<crate::xir::attr::Attr>);
};
// Optional attribute if input above is of the form `(QN_FOO?) => ...`.
(@if_missing_req ? $from:ident.$field:ident $body:block) => {
// This is an optional field;
@ -460,6 +484,15 @@ macro_rules! attr_parse {
}
};
// Optional attribute if input above is of the form `(QN_FOO?) => ...`.
(@into_value ? $from:ident) => {
Some($from.into())
};
(@into_value $from:ident) => {
$from.into()
};
// Optional attribute if input above is of the form `(QN_FOO?) => ...`.
(@maybe_value ? $from:ident.$field:ident) => {
// This does not produce a great error if the user forgets to use an

View File

@ -185,6 +185,26 @@ fn optional_with_values() {
);
}
// This test would fail at compile time.
#[test]
fn attr_value_into() {
#[derive(Debug, PartialEq, Eq)]
struct Foo;
impl From<Attr> for Foo {
fn from(_: Attr) -> Self {
unimplemented!()
}
}
attr_parse! {
struct OptValuesState -> OptValues {
name: (QN_NAME) => Foo,
yields: (QN_YIELDS?) => Option<Foo>,
}
}
}
#[test]
fn optional_with_all_missing() {
attr_parse! {

View File

@ -34,13 +34,13 @@ pub struct EleParseCfg {
/// Whether to allow zero-or-more repetition for this element.
///
/// This is the Kleene star modifier (`*`).
repeat: bool,
pub repeat: bool,
}
#[macro_export]
macro_rules! ele_parse {
(type Object = $objty:ty; $($rest:tt)*) => {
ele_parse!(@!nonterm_decl <$objty> $($rest)*)
ele_parse!(@!nonterm_decl <$objty> $($rest)*);
};
(@!nonterm_decl <$objty:ty> $nt:ident := $($rest:tt)*) => {
@ -138,7 +138,7 @@ macro_rules! ele_parse {
// `$attrmap`.
$(
$(#[$fattr:meta])*
$field:ident: ($fmatch:tt) => $fty:ty,
$field:ident: ($($fmatch:tt)+) => $fty:ty,
)*
} => $attrmap:expr,
@ -165,7 +165,7 @@ macro_rules! ele_parse {
struct [<$nt AttrsState_>] -> [<$nt Attrs_>] {
$(
$(#[$fattr])*
$field: ($fmatch) => $fty,
$field: ($($fmatch)+) => $fty,
)*
}
}
@ -182,7 +182,11 @@ macro_rules! ele_parse {
Expecting_,
/// Recovery state ignoring all remaining tokens for this
/// element.
RecoverEleIgnore_(crate::xir::QName, crate::xir::OpenSpan, Depth),
RecoverEleIgnore_(
crate::xir::QName,
crate::xir::OpenSpan,
crate::xir::flat::Depth
),
// Recovery completed because end tag corresponding to the
// invalid element has been found.
RecoverEleIgnoreClosed_(crate::xir::QName, crate::xir::CloseSpan),
@ -193,19 +197,28 @@ macro_rules! ele_parse {
/// may be a child element,
/// but it may be text,
/// for example.
CloseRecoverIgnore_((crate::span::Span, Depth), crate::span::Span),
CloseRecoverIgnore_(
(crate::span::Span, crate::xir::flat::Depth),
crate::span::Span
),
/// Parsing element attributes.
Attrs_((crate::span::Span, Depth), [<$nt AttrsState_>]),
Attrs_(
(crate::span::Span, crate::xir::flat::Depth),
[<$nt AttrsState_>]
),
$(
$ntref((crate::span::Span, Depth), $ntref),
$ntref(
(crate::span::Span, crate::xir::flat::Depth),
$ntref
),
)*
ExpectClose_((crate::span::Span, Depth), ()),
ExpectClose_((crate::span::Span, crate::xir::flat::Depth), ()),
/// Closing tag found and parsing of the element is
/// complete.
Closed_(crate::span::Span),
}
impl crate::xir::parse::ele::EleParseState for $nt {}
impl crate::xir::parse::EleParseState for $nt {}
impl $nt {
/// [`QName`](crate::xir::QName) of the element recognized
@ -376,7 +389,7 @@ macro_rules! ele_parse {
type Token = crate::xir::flat::XirfToken;
type Object = $objty;
type Error = [<$nt Error_>];
type Context = crate::parse::Context<crate::xir::parse::ele::EleParseCfg>;
type Context = crate::parse::Context<crate::xir::parse::EleParseCfg>;
fn parse_token(
self,
@ -386,8 +399,9 @@ macro_rules! ele_parse {
use crate::{
parse::{EmptyContext, Transition, Transitionable},
xir::{
EleSpan,
flat::XirfToken,
parse::attr::parse_attrs,
parse::parse_attrs,
},
};