tamer: parse::state: EchoState and TransitionResult constituent primitives

This beings to introduce more primitive operations to `TransitionResult` and
its components so that I can actually work with them without having to write
a bunch of concrete, boilerplate implementations.  This is demonstrated in
part by `EchoState` (which is nearly all boilerplate, but whose correctness
should be verifiable at a glance), which will be used going forward as a
basis for default implementations for parsers (e.g. expansion delegation).

DEV-13156
main
Mike Gerwitz 2022-11-15 16:50:11 -05:00
parent 55c55cabd3
commit fc425ff1d5
3 changed files with 132 additions and 1 deletions

View File

@ -54,6 +54,20 @@ impl<S: ParseState> ParseStatus<S> {
Self::Object(obj) => ParseStatus::Object(obj),
}
}
/// Asserts a reflexive relationship between the [`ParseStatus`]es of
/// of `S` and `SB`.
pub fn reflexivity<SB: ParseState>(self) -> ParseStatus<SB>
where
SB: ParseState<Object = <S as ParseState>::Object>,
{
use ParseStatus::*;
match self {
Incomplete => Incomplete,
Object(obj) => Object(obj),
}
}
}
impl<S: ParseState<Object = T>, T: Object> From<T> for ParseStatus<S> {

View File

@ -116,6 +116,30 @@ impl<S: ParseState> TransitionResult<S> {
None => self,
}
}
/// Map over both the [`Transition`] and its associated
/// [`TransitionData`],
/// translating to another [`ParseState`] `SB`.
///
/// The inner [`Transition`]'s [`ParseState`] is mapped over for
/// convenience and brevity,
/// despite the verbose convention of mandating the use of
/// [`Transition`] elsewhere.
/// However,
/// [`TransitionData`] is too complex of a structure,
/// so determining how to map over its data is left as an exercise
/// for `fdata`.
pub(in super::super) fn bimap<SB: ParseState>(
self,
fst: impl FnOnce(S) -> SB,
fdata: impl FnOnce(TransitionData<S>) -> TransitionData<SB>,
) -> TransitionResult<SB> {
match self {
Self(Transition(st), data) => {
TransitionResult(Transition(fst(st)), fdata(data))
}
}
}
}
/// Token to use as a lookahead token in place of the next token from the
@ -246,6 +270,42 @@ impl<S: ParseState> TransitionData<S> {
TransitionData::Dead(la) => TransitionData::Dead(la),
}
}
/// Asserts a reflexive relationship between the [`TransitionData`] of
/// our own [`ParseState`] `S` and a target [`ParseState`] `SB`.
///
/// This is intended not just for translating between types,
/// but also documentation,
/// as an affirmative way to state "these two [`ParseState`]s
/// represent the same underlying data".
/// For example,
/// this may be appropriate when `SB` wraps `S`.
///
/// This is a stronger statement than saying two [`ParseState`]s are
/// _compatible_ withe one-another in some way,
/// which is the assertion made by
/// [`StitchableParseState`](super::StitchableParseState) and may
/// require data to be translated.
///
/// While this method refers to the mathematical reflexive relation,
/// its exact name originates from the Coq tactic.
pub fn reflexivity<SB: ParseState>(self) -> TransitionData<SB>
where
SB: ParseState<
Token = <S as ParseState>::Token,
Object = <S as ParseState>::Object,
Error = <S as ParseState>::Error,
>,
{
use TransitionData::*;
match self {
Result(result, la) => {
Result(result.map(ParseStatus::reflexivity), la)
}
Dead(la) => Dead(la),
}
}
}
/// A verb denoting a state transition.
@ -362,6 +422,27 @@ impl<S: ParseState> Transition<S> {
TransitionData::Dead(Lookahead(tok)),
)
}
/// Map over the inner [`ParseState`] `S` to another
/// [`ParseState`] `SB`.
///
/// Unlike other parts of this API which mandate explicit instantiation
/// of [`Transition`] for self-documentation,
/// this maps over the inner value since [`Transition`] is already
/// apparent.
/// This is consequently much less verbose,
/// as it allows using tuple constructions for `f`,
/// and most [`ParseState`]s are implemented as tuples
/// (or tuple enums)
/// in practice.
pub fn map<SB: ParseState>(
self,
f: impl FnOnce(S) -> SB,
) -> Transition<SB> {
match self {
Self(st) => Transition(f(st)),
}
}
}
impl<S: ClosedParseState> FromResidual<(Transition<S>, ParseStateResult<S>)>

View File

@ -27,7 +27,7 @@
pub mod expand;
use super::prelude::*;
use super::{prelude::*, state::TransitionData};
use crate::{span::Span, sym::SymbolId};
use std::fmt::Display;
@ -72,3 +72,39 @@ impl Into<(SymbolId, Span)> for SPair {
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct EchoParseState<S: ClosedParseState>(S);
impl<S: ClosedParseState> ParseState for EchoParseState<S> {
type Token = S::Token;
type Object = S::Object;
type Error = S::Error;
type Context = S::Context;
fn parse_token(
self,
tok: Self::Token,
ctx: &mut Self::Context,
) -> TransitionResult<Self::Super> {
match self {
Self(st) => st
.parse_token(tok, ctx)
.bimap(Self, TransitionData::reflexivity),
}
}
fn is_accepting(&self, ctx: &Self::Context) -> bool {
match self {
Self(st) => st.is_accepting(ctx),
}
}
}
impl<S: ClosedParseState> Display for EchoParseState<S> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self(st) => Display::fmt(st, f),
}
}
}