tamer: parse: More flexible Transition API
This does some cleanup and adds `parse::Object` for use in disambiguating `From` for `ParseStatus`, allowing the `Transition` API to be much more flexible in the data it accepts and automatically converts. This allows us to concisely provide raw output data to be wrapped, or provide `ParseStatus` directly when more convenient. There aren't yet examples in the docs; I'll do so once I make sure this API is actually utilized as intended. DEV-10863main
parent
c0fa89222e
commit
f402e51d04
|
@ -19,7 +19,7 @@
|
|||
|
||||
use super::{SymAttrs, XmloError};
|
||||
use crate::{
|
||||
parse::{ParseState, Transition, TransitionResult},
|
||||
parse::{self, ParseState, Transition, TransitionResult},
|
||||
sym::{st::*, SymbolId},
|
||||
xir::{attr::Attr, flat::Object as Xirf},
|
||||
};
|
||||
|
@ -87,6 +87,8 @@ pub enum XmloEvent {
|
|||
Eoh,
|
||||
}
|
||||
|
||||
impl parse::Object for XmloEvent {}
|
||||
|
||||
/// A [`Result`] with a hard-coded [`XmloError`] error type.
|
||||
///
|
||||
/// This is the result of every [`XmloReader`] operation that could
|
||||
|
@ -126,7 +128,7 @@ impl ParseState for XmloReaderState {
|
|||
(Ready, _) => Transition(Ready).err(XmloError::UnexpectedRoot),
|
||||
|
||||
(Package, Xirf::Attr(Attr(name, value, _))) => {
|
||||
Transition(Package).with(match name {
|
||||
Transition(Package).ok(match name {
|
||||
QN_NAME => XmloEvent::PkgName(value),
|
||||
QN_UUROOTPATH => XmloEvent::PkgRootPath(value),
|
||||
QN_PROGRAM => XmloEvent::PkgProgramFlag,
|
||||
|
|
|
@ -54,6 +54,16 @@ impl<T: Token> From<T> for Span {
|
|||
}
|
||||
}
|
||||
|
||||
/// An IR object produced by a lowering operation on one or more [`Token`]s.
|
||||
///
|
||||
/// Note that an [`Object`] may also be a [`Token`] if it will be in turn
|
||||
/// fed to another [`Parser`] for lowering.
|
||||
///
|
||||
/// This trait exists to disambiguate an otherwise unbounded type for
|
||||
/// [`From`] conversions,
|
||||
/// used in the [`Transition`] API to provide greater flexibility.
|
||||
pub trait Object: Debug + PartialEq + Eq {}
|
||||
|
||||
/// An infallible [`Token`] stream.
|
||||
///
|
||||
/// If the token stream originates from an operation that could potentially
|
||||
|
@ -96,7 +106,7 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
type Token: Token;
|
||||
|
||||
/// Objects produced by a parser utilizing these states.
|
||||
type Object: Debug + PartialEq + Eq;
|
||||
type Object: Object;
|
||||
|
||||
/// Errors specific to this set of states.
|
||||
type Error: Debug + Error + PartialEq + Eq;
|
||||
|
@ -150,10 +160,7 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
///
|
||||
/// This is used by [`ParseState::parse_token`];
|
||||
/// see that function for rationale.
|
||||
pub type ParseStateResult<S> = Result<
|
||||
ParseStatus<<S as ParseState>::Token, <S as ParseState>::Object>,
|
||||
<S as ParseState>::Error,
|
||||
>;
|
||||
pub type ParseStateResult<S> = Result<ParseStatus<S>, <S as ParseState>::Error>;
|
||||
|
||||
/// A state transition with associated data.
|
||||
///
|
||||
|
@ -182,8 +189,31 @@ impl<S: ParseState> Transition<S> {
|
|||
///
|
||||
/// This allows [`ParseState::parse_token`] to emit a parsed object and
|
||||
/// corresponds to [`ParseStatus::Object`].
|
||||
pub fn with(self, obj: S::Object) -> TransitionResult<S> {
|
||||
TransitionResult(self, Ok(ParseStatus::Object(obj)))
|
||||
pub fn ok<T>(self, obj: T) -> TransitionResult<S>
|
||||
where
|
||||
T: Into<ParseStatus<S>>,
|
||||
{
|
||||
TransitionResult(self, Ok(obj.into()))
|
||||
}
|
||||
|
||||
/// A transition with corresponding error.
|
||||
///
|
||||
/// This indicates a parsing failure.
|
||||
/// The state ought to be suitable for error recovery.
|
||||
pub fn err<E: Into<S::Error>>(self, err: E) -> TransitionResult<S> {
|
||||
TransitionResult(self, Err(err.into()))
|
||||
}
|
||||
|
||||
/// A state transition with corresponding [`Result`].
|
||||
///
|
||||
/// This translates the provided [`Result`] in a manner equivalent to
|
||||
/// [`Transition::ok`] and [`Transition::err`].
|
||||
pub fn result<T, E>(self, result: Result<T, E>) -> TransitionResult<S>
|
||||
where
|
||||
T: Into<ParseStatus<S>>,
|
||||
E: Into<S::Error>,
|
||||
{
|
||||
TransitionResult(self, result.map(Into::into).map_err(Into::into))
|
||||
}
|
||||
|
||||
/// A state transition indicating that more data is needed before an
|
||||
|
@ -202,14 +232,6 @@ impl<S: ParseState> Transition<S> {
|
|||
pub fn dead(self, tok: S::Token) -> TransitionResult<S> {
|
||||
TransitionResult(self, Ok(ParseStatus::Dead(tok)))
|
||||
}
|
||||
|
||||
/// A transition with corresponding error.
|
||||
///
|
||||
/// This indicates a parsing failure.
|
||||
/// The state ought to be suitable for error recovery.
|
||||
pub fn err<E: Into<S::Error>>(self, err: E) -> TransitionResult<S> {
|
||||
TransitionResult(self, Err(err.into()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ParseState> Into<(Transition<S>, ParseStateResult<S>)>
|
||||
|
@ -583,7 +605,7 @@ impl<S: ParseState, I: TokenStream<S::Token>> From<I> for Parser<S, I> {
|
|||
|
||||
/// Result of a parsing operation.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum ParseStatus<T, O> {
|
||||
pub enum ParseStatus<S: ParseState> {
|
||||
/// Additional tokens are needed to complete parsing of the next object.
|
||||
Incomplete,
|
||||
|
||||
|
@ -591,7 +613,7 @@ pub enum ParseStatus<T, O> {
|
|||
///
|
||||
/// This does not indicate that the parser is complete,
|
||||
/// as more objects may be able to be emitted.
|
||||
Object(O),
|
||||
Object(S::Object),
|
||||
|
||||
/// Parser encountered a dead state relative to the given token.
|
||||
///
|
||||
|
@ -616,7 +638,13 @@ pub enum ParseStatus<T, O> {
|
|||
///
|
||||
/// If there is no parent context to handle the token,
|
||||
/// [`Parser`] must yield an error.
|
||||
Dead(T),
|
||||
Dead(S::Token),
|
||||
}
|
||||
|
||||
impl<S: ParseState<Object = T>, T: Object> From<T> for ParseStatus<S> {
|
||||
fn from(obj: T) -> Self {
|
||||
Self::Object(obj)
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of a parsing operation.
|
||||
|
@ -636,8 +664,8 @@ pub enum Parsed<O> {
|
|||
Object(O),
|
||||
}
|
||||
|
||||
impl<T: Token, O> From<ParseStatus<T, O>> for Parsed<O> {
|
||||
fn from(status: ParseStatus<T, O>) -> Self {
|
||||
impl<S: ParseState> From<ParseStatus<S>> for Parsed<S::Object> {
|
||||
fn from(status: ParseStatus<S>) -> Self {
|
||||
match status {
|
||||
ParseStatus::Incomplete => Parsed::Incomplete,
|
||||
ParseStatus::Object(x) => Parsed::Object(x),
|
||||
|
@ -677,6 +705,8 @@ pub mod test {
|
|||
}
|
||||
}
|
||||
|
||||
impl Object for TestToken {}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum EchoState {
|
||||
Empty,
|
||||
|
@ -696,7 +726,7 @@ pub mod test {
|
|||
|
||||
fn parse_token(self, tok: TestToken) -> TransitionResult<Self> {
|
||||
match tok {
|
||||
TestToken::Comment(..) => Transition(Self::Done).with(tok),
|
||||
TestToken::Comment(..) => Transition(Self::Done).ok(tok),
|
||||
TestToken::Close(..) => {
|
||||
Transition(self).err(EchoStateError::InnerError(tok))
|
||||
}
|
||||
|
|
|
@ -68,6 +68,8 @@ impl Token for Attr {
|
|||
}
|
||||
}
|
||||
|
||||
impl crate::parse::Object for Attr {}
|
||||
|
||||
impl Display for Attr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "`@{}=\"{}\"` at {}", self.0, self.1, self.2 .0)
|
||||
|
|
|
@ -58,7 +58,7 @@ impl ParseState for AttrParseState {
|
|||
(Empty, invalid) => Transition(Empty).dead(invalid),
|
||||
|
||||
(Name(name, nspan), XirToken::AttrValue(value, vspan)) => {
|
||||
Transition(Empty).with(Attr::new(name, value, (nspan, vspan)))
|
||||
Transition(Empty).ok(Attr::new(name, value, (nspan, vspan)))
|
||||
}
|
||||
|
||||
(Name(name, nspan), invalid) => {
|
||||
|
|
|
@ -44,7 +44,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
parse::{
|
||||
ParseState, ParseStatus, ParsedResult, Token, Transition,
|
||||
self, ParseState, ParseStatus, ParsedResult, Token, Transition,
|
||||
TransitionResult,
|
||||
},
|
||||
span::Span,
|
||||
|
@ -131,6 +131,8 @@ impl Token for Object {
|
|||
}
|
||||
}
|
||||
|
||||
impl parse::Object for Object {}
|
||||
|
||||
impl Display for Object {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use Object::*;
|
||||
|
@ -204,7 +206,7 @@ where
|
|||
match (self, tok) {
|
||||
// Comments are permitted before and after the first root element.
|
||||
(st @ (PreRoot | Done), XirToken::Comment(sym, span)) => {
|
||||
Transition(st).with(Object::Comment(sym, span))
|
||||
Transition(st).ok(Object::Comment(sym, span))
|
||||
}
|
||||
|
||||
(PreRoot, tok @ XirToken::Open(..)) => {
|
||||
|
@ -224,7 +226,7 @@ where
|
|||
}
|
||||
(Transition(sa), Ok(Obj(attr))) => {
|
||||
Transition(AttrExpected(stack, sa))
|
||||
.with(Object::Attr(attr))
|
||||
.ok(Object::Attr(attr))
|
||||
}
|
||||
(_, Ok(Dead(lookahead))) => {
|
||||
Self::parse_node(stack, lookahead)
|
||||
|
@ -280,7 +282,7 @@ where
|
|||
stack.push((qname, span));
|
||||
|
||||
// Delegate to the attribute parser until it is complete.
|
||||
Transition(AttrExpected(stack, SA::default())).with(Open(
|
||||
Transition(AttrExpected(stack, SA::default())).ok(Open(
|
||||
qname,
|
||||
span,
|
||||
Depth(depth),
|
||||
|
@ -303,7 +305,7 @@ where
|
|||
}
|
||||
|
||||
// Final closing tag (for root node) completes the document.
|
||||
(..) if stack.len() == 0 => Transition(Done).with(Close(
|
||||
(..) if stack.len() == 0 => Transition(Done).ok(Close(
|
||||
close_oqname,
|
||||
close_span,
|
||||
Depth(0),
|
||||
|
@ -312,7 +314,7 @@ where
|
|||
(..) => {
|
||||
let depth = stack.len();
|
||||
|
||||
Transition(NodeExpected(stack)).with(Close(
|
||||
Transition(NodeExpected(stack)).ok(Close(
|
||||
close_oqname,
|
||||
close_span,
|
||||
Depth(depth),
|
||||
|
@ -322,16 +324,16 @@ where
|
|||
}
|
||||
|
||||
XirToken::Comment(sym, span) => {
|
||||
Transition(NodeExpected(stack)).with(Comment(sym, span))
|
||||
Transition(NodeExpected(stack)).ok(Comment(sym, span))
|
||||
}
|
||||
XirToken::Text(sym, span) => {
|
||||
Transition(NodeExpected(stack)).with(Text(sym, span))
|
||||
Transition(NodeExpected(stack)).ok(Text(sym, span))
|
||||
}
|
||||
XirToken::CData(sym, span) => {
|
||||
Transition(NodeExpected(stack)).with(CData(sym, span))
|
||||
Transition(NodeExpected(stack)).ok(CData(sym, span))
|
||||
}
|
||||
XirToken::Whitespace(ws, span) => {
|
||||
Transition(NodeExpected(stack)).with(Whitespace(ws, span))
|
||||
Transition(NodeExpected(stack)).ok(Whitespace(ws, span))
|
||||
}
|
||||
|
||||
// We should transition to `State::Attr` before encountering any
|
||||
|
|
|
@ -180,7 +180,7 @@ use super::{
|
|||
|
||||
use crate::{
|
||||
parse::{
|
||||
ParseError, ParseResult, ParseState, ParseStatus, ParsedResult,
|
||||
self, ParseError, ParseResult, ParseState, ParseStatus, ParsedResult,
|
||||
Transition, TransitionResult,
|
||||
},
|
||||
span::Span,
|
||||
|
@ -282,6 +282,8 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
|
||||
impl parse::Object for Tree {}
|
||||
|
||||
/// Element node.
|
||||
///
|
||||
/// This represents an [XML element] beginning with an opening tag that is
|
||||
|
@ -564,7 +566,7 @@ impl<SA: StackAttrParseState> ParseState for Stack<SA> {
|
|||
.map(ElementStack::consume_child_or_complete)
|
||||
.map(|new_stack| match new_stack {
|
||||
Stack::ClosedElement(ele) => {
|
||||
Transition(Empty).with(Tree::Element(ele))
|
||||
Transition(Empty).ok(Tree::Element(ele))
|
||||
}
|
||||
_ => Transition(new_stack).incomplete(),
|
||||
})
|
||||
|
@ -577,7 +579,7 @@ impl<SA: StackAttrParseState> ParseState for Stack<SA> {
|
|||
.map(ElementStack::consume_child_or_complete)
|
||||
.map(|new_stack| match new_stack {
|
||||
Stack::ClosedElement(ele) => {
|
||||
Transition(Empty).with(Tree::Element(ele))
|
||||
Transition(Empty).ok(Tree::Element(ele))
|
||||
}
|
||||
_ => Transition(new_stack).incomplete(),
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue