tamer: parse::TransitionResult: Alias=>newtype
This converts the tuple type alias into a newtype, so that we may provide our own implementations. This differs from a previous approach that I took, which involved making this type `Result<(S, T), (S, E)>` so that the return values composed well with other functions. But the reality is that this is used only by other `ParseState`s and `Parser`, so it's unnecessary. However, this is also an attempt to utilize the new Try and FromResidual traits; note how the Try associated types match precisely what I was trying to do before, though they're used as intermediate types. I'll see how this evolves. DEV-10863main
parent
2e98a69d15
commit
279ddc79d7
|
@ -54,6 +54,9 @@
|
|||
// This simply removes a boilerplate `Default` impl;
|
||||
// we can do without if this does not get finalized.
|
||||
#![feature(derive_default_enum)]
|
||||
// For `Try` and `FromResidual`,
|
||||
// allowing us to write our own `?`-compatible types.
|
||||
#![feature(try_trait_v2)]
|
||||
// We build docs for private items.
|
||||
#![allow(rustdoc::private_intra_doc_links)]
|
||||
|
||||
|
|
|
@ -24,9 +24,11 @@
|
|||
use crate::iter::{TripIter, TrippableIterator};
|
||||
use crate::span::Span;
|
||||
use std::fmt::Debug;
|
||||
use std::hint::unreachable_unchecked;
|
||||
use std::iter::{self, Empty};
|
||||
use std::mem::take;
|
||||
use std::{error::Error, fmt::Display};
|
||||
use std::ops::{ControlFlow, FromResidual, Try};
|
||||
use std::{convert::Infallible, error::Error, fmt::Display};
|
||||
|
||||
/// Result of applying a [`Token`] to a [`ParseState`],
|
||||
/// with any error having been wrapped in a [`ParseError`].
|
||||
|
@ -94,10 +96,10 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
type Token: Token;
|
||||
|
||||
/// Objects produced by a parser utilizing these states.
|
||||
type Object;
|
||||
type Object: Debug + PartialEq + Eq;
|
||||
|
||||
/// Errors specific to this set of states.
|
||||
type Error: Error + PartialEq + Eq;
|
||||
type Error: Debug + Error + PartialEq + Eq;
|
||||
|
||||
/// Construct a parser.
|
||||
///
|
||||
|
@ -153,6 +155,17 @@ pub type ParseStateResult<S> = Result<
|
|||
<S as ParseState>::Error,
|
||||
>;
|
||||
|
||||
/// A state transition with associated data.
|
||||
///
|
||||
/// Conceptually,
|
||||
/// imagine the act of a state transition producing data.
|
||||
/// See [`Transition`] for convenience methods for producing this tuple.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct TransitionResult<S: ParseState>(
|
||||
pub Transition<S>,
|
||||
pub ParseStateResult<S>,
|
||||
);
|
||||
|
||||
/// Denotes a state transition.
|
||||
///
|
||||
/// This newtype was created to produce clear, self-documenting code;
|
||||
|
@ -169,16 +182,16 @@ 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) -> (Self, ParseStateResult<S>) {
|
||||
(self, Ok(ParseStatus::Object(obj)))
|
||||
pub fn with(self, obj: S::Object) -> TransitionResult<S> {
|
||||
TransitionResult(self, Ok(ParseStatus::Object(obj)))
|
||||
}
|
||||
|
||||
/// A state transition indicating that more data is needed before an
|
||||
/// object can be emitted.
|
||||
///
|
||||
/// This corresponds to [`ParseStatus::Incomplete`].
|
||||
pub fn incomplete(self) -> (Self, ParseStateResult<S>) {
|
||||
(self, Ok(ParseStatus::Incomplete))
|
||||
pub fn incomplete(self) -> TransitionResult<S> {
|
||||
TransitionResult(self, Ok(ParseStatus::Incomplete))
|
||||
}
|
||||
|
||||
/// A dead state transition.
|
||||
|
@ -186,25 +199,72 @@ impl<S: ParseState> Transition<S> {
|
|||
/// This corresponds to [`ParseStatus::Dead`],
|
||||
/// and a calling parser should use the provided [`Token`] as
|
||||
/// lookahead.
|
||||
pub fn dead(self, tok: S::Token) -> (Self, ParseStateResult<S>) {
|
||||
(self, Ok(ParseStatus::Dead(tok)))
|
||||
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) -> (Self, ParseStateResult<S>) {
|
||||
(self, Err(err.into()))
|
||||
pub fn err<E: Into<S::Error>>(self, err: E) -> TransitionResult<S> {
|
||||
TransitionResult(self, Err(err.into()))
|
||||
}
|
||||
}
|
||||
|
||||
/// A state transition with associated data.
|
||||
///
|
||||
/// Conceptually,
|
||||
/// imagine the act of a state transition producing data.
|
||||
/// See [`Transition`] for convenience methods for producing this tuple.
|
||||
pub type TransitionResult<S> = (Transition<S>, ParseStateResult<S>);
|
||||
impl<S: ParseState> Into<(Transition<S>, ParseStateResult<S>)>
|
||||
for TransitionResult<S>
|
||||
{
|
||||
fn into(self) -> (Transition<S>, ParseStateResult<S>) {
|
||||
(self.0, self.1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ParseState> Try for TransitionResult<S> {
|
||||
type Output = (Transition<S>, ParseStateResult<S>);
|
||||
type Residual = (Transition<S>, ParseStateResult<S>);
|
||||
|
||||
fn from_output(output: Self::Output) -> Self {
|
||||
match output {
|
||||
(st, result) => Self(st, result),
|
||||
}
|
||||
}
|
||||
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
|
||||
match self.into() {
|
||||
(st, Ok(x)) => ControlFlow::Continue((st, Ok(x))),
|
||||
(st, Err(e)) => ControlFlow::Break((st, Err(e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ParseState> FromResidual<(Transition<S>, ParseStateResult<S>)>
|
||||
for TransitionResult<S>
|
||||
{
|
||||
fn from_residual(residual: (Transition<S>, ParseStateResult<S>)) -> Self {
|
||||
match residual {
|
||||
(st, result) => Self(st, result),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ParseState> FromResidual<Result<Infallible, TransitionResult<S>>>
|
||||
for TransitionResult<S>
|
||||
{
|
||||
fn from_residual(
|
||||
residual: Result<Infallible, TransitionResult<S>>,
|
||||
) -> Self {
|
||||
match residual {
|
||||
Err(e) => e,
|
||||
// SAFETY: This match arm doesn't seem to be required in
|
||||
// core::result::Result's FromResidual implementation,
|
||||
// but as of 1.61 nightly it is here.
|
||||
// Since this is Infallable,
|
||||
// it cannot occur.
|
||||
Ok(_) => unsafe { unreachable_unchecked() },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A streaming parser defined by a [`ParseState`] with exclusive
|
||||
/// mutable access to an underlying [`TokenStream`].
|
||||
|
@ -275,7 +335,7 @@ impl<S: ParseState, I: TokenStream<S::Token>> Parser<S, I> {
|
|||
self.last_span = Some(tok.span());
|
||||
|
||||
let result;
|
||||
(Transition(self.state), result) =
|
||||
TransitionResult(Transition(self.state), result) =
|
||||
take(&mut self.state).parse_token(tok);
|
||||
|
||||
use ParseStatus::*;
|
||||
|
|
|
@ -143,7 +143,7 @@ mod test {
|
|||
Transition(AttrParseState::default()),
|
||||
Ok(ParseStatus::Dead(tok.clone()))
|
||||
),
|
||||
sut.parse_token(tok)
|
||||
sut.parse_token(tok).into()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -174,12 +174,12 @@ mod test {
|
|||
|
||||
// This token indicates that we're expecting a value to come next in
|
||||
// the token stream.
|
||||
let (Transition(sut), result) =
|
||||
let TransitionResult(Transition(sut), result) =
|
||||
sut.parse_token(XirToken::AttrName(attr, S));
|
||||
assert_eq!(result, Ok(ParseStatus::Incomplete));
|
||||
|
||||
// But we provide something else unexpected.
|
||||
let (Transition(sut), result) =
|
||||
let TransitionResult(Transition(sut), result) =
|
||||
sut.parse_token(XirToken::Close(None, S2));
|
||||
assert_eq!(
|
||||
result,
|
||||
|
@ -200,7 +200,7 @@ mod test {
|
|||
// Rather than checking for that state,
|
||||
// let's actually attempt a recovery.
|
||||
let recover = "value".intern();
|
||||
let (Transition(sut), result) =
|
||||
let TransitionResult(Transition(sut), result) =
|
||||
sut.parse_token(XirToken::AttrValue(recover, S2));
|
||||
assert_eq!(
|
||||
result,
|
||||
|
|
|
@ -217,7 +217,7 @@ where
|
|||
|
||||
(NodeExpected(stack), tok) => Self::parse_node(stack, tok),
|
||||
|
||||
(AttrExpected(stack, sa), tok) => match sa.parse_token(tok) {
|
||||
(AttrExpected(stack, sa), tok) => match sa.parse_token(tok).into() {
|
||||
(Transition(sa), Ok(Incomplete)) => {
|
||||
Transition(AttrExpected(stack, sa)).incomplete()
|
||||
}
|
||||
|
|
|
@ -541,7 +541,7 @@ impl<SA: StackAttrParseState> ParseState for Stack<SA> {
|
|||
// Attribute parsing.
|
||||
(AttrState(estack, attrs, sa), tok) => {
|
||||
use ParseStatus::*;
|
||||
match sa.parse_token(tok) {
|
||||
match sa.parse_token(tok).into() {
|
||||
(Transition(sa), Ok(Incomplete)) => {
|
||||
Transition(AttrState(estack, attrs, sa)).incomplete()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue