Mike Gerwitz

Activist for User Freedom

aboutsummaryrefslogtreecommitdiffstats
path: root/tamer
diff options
context:
space:
mode:
authorMike Gerwitz <mike.gerwitz@ryansg.com>2022-03-25 09:56:22 -0400
committerMike Gerwitz <mike.gerwitz@ryansg.com>2022-03-25 12:28:50 -0400
commit279ddc79d72680af50f06445c7bdc4216c25f01b (patch)
tree7c9ddbeb3aa6014affc32f490633b123e7d2eedb /tamer
parent2e98a69d158c403a7b12b5390c76e86b92f3829d (diff)
downloadtame-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.rs3
-rw-r--r--tamer/src/parse.rs96
-rw-r--r--tamer/src/xir/attr/parse.rs8
-rw-r--r--tamer/src/xir/flat.rs2
-rw-r--r--tamer/src/xir/tree.rs2
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()
}