diff options
author | Mike Gerwitz <mike.gerwitz@ryansg.com> | 2022-03-25 09:56:22 -0400 |
---|---|---|
committer | Mike Gerwitz <mike.gerwitz@ryansg.com> | 2022-03-25 12:28:50 -0400 |
commit | 279ddc79d72680af50f06445c7bdc4216c25f01b (patch) | |
tree | 7c9ddbeb3aa6014affc32f490633b123e7d2eedb /tamer | |
parent | 2e98a69d158c403a7b12b5390c76e86b92f3829d (diff) | |
download | tame-279ddc79d72680af50f06445c7bdc4216c25f01b.tar.gz tame-279ddc79d72680af50f06445c7bdc4216c25f01b.tar.bz2 tame-279ddc79d72680af50f06445c7bdc4216c25f01b.zip |
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-10863
Diffstat (limited to 'tamer')
-rw-r--r-- | tamer/src/lib.rs | 3 | ||||
-rw-r--r-- | tamer/src/parse.rs | 96 | ||||
-rw-r--r-- | tamer/src/xir/attr/parse.rs | 8 | ||||
-rw-r--r-- | tamer/src/xir/flat.rs | 2 | ||||
-rw-r--r-- | tamer/src/xir/tree.rs | 2 |
5 files changed, 87 insertions, 24 deletions
diff --git a/tamer/src/lib.rs b/tamer/src/lib.rs index cd2704f..bee717b 100644 --- a/tamer/src/lib.rs +++ b/tamer/src/lib.rs @@ -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)] diff --git a/tamer/src/parse.rs b/tamer/src/parse.rs index 7e13c04..e54740d 100644 --- a/tamer/src/parse.rs +++ b/tamer/src/parse.rs @@ -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::*; diff --git a/tamer/src/xir/attr/parse.rs b/tamer/src/xir/attr/parse.rs index 4b7bb34..d35ddf5 100644 --- a/tamer/src/xir/attr/parse.rs +++ b/tamer/src/xir/attr/parse.rs @@ -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, diff --git a/tamer/src/xir/flat.rs b/tamer/src/xir/flat.rs index c5f5476..4df2c05 100644 --- a/tamer/src/xir/flat.rs +++ b/tamer/src/xir/flat.rs @@ -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() } diff --git a/tamer/src/xir/tree.rs b/tamer/src/xir/tree.rs index efc5b1f..74150aa 100644 --- a/tamer/src/xir/tree.rs +++ b/tamer/src/xir/tree.rs @@ -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() } |