tamer: parse::util (ExpandableParseState, ExpandableInto): Clarifying traits
These traits serve to abstract away some of the type-level details and clearly state what the end result is (something stitchable with a parent). I'm admittedly battling myself on this concept a bit. The proper layer of abstraction is the concept of expansion, which is an abstraction that is likely to be maintained all the way through, but we strip the abstraction for the sake of delegation. Maybe the better option is to provide a different method of delegation and avoid the stripping at all, and avoid the awkward interaction with the dead state. The awkwardness comes from the fact that delegating right now is so rigid and defined in terms of a method on state rather than a mapping between `TransitionResult`s. But I really need to move on... ;_; The original design was trying to generalize this such that composition at the attribute parser level (for NIR) would be able to just accept any sitchable parser with the convention that the dead state is the replacement token. But that is the wrong layer of abstraction, which not only makes it confusing, but is asking for trouble when someone inevitably violates that contract. With all of that said, `StitchableExpansionState` _is_ a delegation. It could just as easily be a function (`is_accepting` always delegates too), so perhaps that should just be generalized as reifying delegation as a `ParseState`. DEV-13156main
parent
03cf652c41
commit
ddb4f24ea5
|
@ -29,7 +29,7 @@ use crate::{diagnose::Annotate, diagnostic_panic, span::Span, sym::SymbolId};
|
|||
|
||||
use super::{
|
||||
prelude::*,
|
||||
state::{Lookahead, TransitionData},
|
||||
state::{Lookahead, StitchableParseState, TransitionData},
|
||||
};
|
||||
use std::{fmt::Display, marker::PhantomData};
|
||||
|
||||
|
@ -52,6 +52,27 @@ pub enum Expansion<T, O: Object> {
|
|||
|
||||
impl<T: Token, O: Object> Object for Expansion<T, O> {}
|
||||
|
||||
/// A [`ClosedParseState`] that is able to serve as an expansion parser.
|
||||
///
|
||||
/// An expansion parser is a parser yielding [`Expansion`],
|
||||
/// intended to be integrated into another token stream.
|
||||
pub trait ExpandableParseState<O: Object> = ClosedParseState
|
||||
where
|
||||
O: Token + Eq,
|
||||
Self: ParseState<Object = Expansion<<Self as ParseState>::Token, O>>;
|
||||
|
||||
/// An [`ExpandableParseState`] capable of expanding into the token stream
|
||||
/// of a parent [`ParseState`] `SP`.
|
||||
///
|
||||
/// This trait asserts that an [`ExpandableParseState`] is a
|
||||
/// [`StitchableParseState<SP>`](StitchableParseState) after being wrapped
|
||||
/// by [`StitchableExpansionState`].
|
||||
pub trait ExpandableInto<SP: ParseState> =
|
||||
ExpandableParseState<<SP as ParseState>::Object>
|
||||
where
|
||||
StitchableExpansionState<Self, <SP as ParseState>::Object>:
|
||||
StitchableParseState<SP>;
|
||||
|
||||
/// Convert a [`ClosedParseState`] yielding an [`Expansion<T,O>`](Expansion)
|
||||
/// object into a parser yielding `O` with a dead state yielding `T`.
|
||||
///
|
||||
|
@ -91,8 +112,7 @@ where
|
|||
impl<S: ClosedParseState, O: Object> ParseState
|
||||
for StitchableExpansionState<S, O>
|
||||
where
|
||||
O: Token + Eq,
|
||||
S: ParseState<Object = Expansion<<S as ParseState>::Token, O>>,
|
||||
S: ExpandableParseState<O>,
|
||||
{
|
||||
type Token = S::Token;
|
||||
type Object = O;
|
||||
|
|
|
@ -141,3 +141,50 @@ fn expansion_stripping_panics_if_lookahead() {
|
|||
// The above token will trigger the panic on the first call.
|
||||
let _ = ExpansionSut::parse(toks.into_iter()).next();
|
||||
}
|
||||
|
||||
// This test would fail at compile-time.
|
||||
#[test]
|
||||
fn expandable_into_is_stitchable_with_target() {
|
||||
// This is utilized only for its types in the below assertions.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct TargetParseState;
|
||||
|
||||
impl ParseState for TargetParseState {
|
||||
type Token = SPair;
|
||||
type Object = TestObject;
|
||||
type Error = Infallible;
|
||||
|
||||
fn parse_token(
|
||||
self,
|
||||
_tok: Self::Token,
|
||||
_ctx: &mut Self::Context,
|
||||
) -> TransitionResult<Self::Super> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn is_accepting(&self, _ctx: &Self::Context) -> bool {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for TargetParseState {
|
||||
fn fmt(&self, _f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
// Asserts that the wrapping `StitchableParseState` has transformed the
|
||||
// `TestParseState` into something stitchable.
|
||||
//
|
||||
// This serves as a sanity check for the below.
|
||||
assert_impl_all!(ExpansionSut: StitchableParseState<TargetParseState>);
|
||||
|
||||
// The `ExpandableInto` trait alias is responsible for asserting that a
|
||||
// given parser is an expansion parser that is able to be converted
|
||||
// into a parser stitchable with the target.
|
||||
//
|
||||
// If this fails but the above assertion succeeds,
|
||||
// then the compatibility is working but something is wrong with the
|
||||
// definition of `ExpandableInto`.
|
||||
assert_impl_all!(TestParseState: ExpandableInto<TargetParseState>);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue