tamer: parse::ParseState::delegate: Initial state stitching concept
This is the delegation portion of what I've come to call "state stitching"---wiring together two state machines that recognize the same input tokens. This handles the delegation of tokens once the parser has been entered, but does not yet handle the actual stitching part of it: wiring the start and accepting states of the child parser to the parent. This is indirectly tested by the XmloReader, but it will receive its own tests once I further finalize this concept. I'm playing around with some ideas. With that said, a quick visual inspection together with the guarantees provided by the type system should convince any familiar reader of its correctness. DEV-10863main
parent
df05a71508
commit
2a3d5be159
|
@ -170,23 +170,11 @@ impl<SS: XmloSymtableState> ParseState for XmloReaderState<SS> {
|
|||
Transition(Done).incomplete()
|
||||
}
|
||||
|
||||
// TODO: This is all boilerplate; abstract away state stitching.
|
||||
// TOOD: It'd be nice to augment errors with the symbol table
|
||||
// span as well (e.g. "while processing symbol table at <loc>").
|
||||
(Symtable(span, ss), tok) => match ss.parse_token(tok).into() {
|
||||
(Transition(ss), Ok(Incomplete)) => {
|
||||
Transition(Symtable(span, ss)).incomplete()
|
||||
}
|
||||
(Transition(ss), Ok(Obj(obj))) => {
|
||||
Transition(Symtable(span, ss)).ok(Self::Object::from(obj))
|
||||
}
|
||||
(Transition(ss), Ok(Dead(tok))) => {
|
||||
Transition(Symtable(span, ss)).dead(tok)
|
||||
}
|
||||
(Transition(ss), Err(e)) => {
|
||||
Transition(Symtable(span, ss)).err(e)
|
||||
}
|
||||
},
|
||||
(Symtable(span, ss), tok) => {
|
||||
ss.delegate(tok, |ss| Symtable(span, ss))
|
||||
}
|
||||
|
||||
todo => todo!("{todo:?}"),
|
||||
}
|
||||
|
@ -277,7 +265,7 @@ impl ParseState for SymtableState {
|
|||
impl From<(SymbolId, SymAttrs, Span)> for XmloEvent {
|
||||
fn from(tup: (SymbolId, SymAttrs, Span)) -> Self {
|
||||
match tup {
|
||||
(sym, attrs, span) => Self::SymDecl(sym, attrs, span)
|
||||
(sym, attrs, span) => Self::SymDecl(sym, attrs, span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,45 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
/// It is acceptable to attempt to parse just one of those attributes,
|
||||
/// or it is acceptable to parse all the way until the end.
|
||||
fn is_accepting(&self) -> bool;
|
||||
|
||||
/// Delegate parsing to a compatible, stitched [`ParseState`].
|
||||
///
|
||||
/// This helps to combine two state machines that speak the same input
|
||||
/// language
|
||||
/// (share the same [`Self::Token`]),
|
||||
/// handling the boilerplate of delegating [`Self::Token`] from a
|
||||
/// parent state~`SP` to `Self`.
|
||||
///
|
||||
/// Token delegation happens after [`Self`] has been entered from a
|
||||
/// parent [`ParseState`] context~`SP`,
|
||||
/// so stitching the start and accepting states must happen elsewhere
|
||||
/// (for now).
|
||||
///
|
||||
/// This assumes that no lookahead token from [`ParseStatus::Dead`] will
|
||||
/// need to be handled by the parent state~`SP`.
|
||||
///
|
||||
/// _TODO: More documentation once this is finalized._
|
||||
fn delegate<SP>(
|
||||
self,
|
||||
tok: Self::Token,
|
||||
into: impl FnOnce(Self) -> SP,
|
||||
) -> TransitionResult<SP>
|
||||
where
|
||||
SP: ParseState<Token = Self::Token>,
|
||||
Self::Object: Into<<SP as ParseState>::Object>,
|
||||
Self::Error: Into<<SP as ParseState>::Error>,
|
||||
{
|
||||
use ParseStatus::{Dead, Incomplete, Object as Obj};
|
||||
|
||||
let (Transition(newst), result) = self.parse_token(tok).into();
|
||||
|
||||
Transition(into(newst)).result(match result {
|
||||
Ok(Incomplete) => Ok(Incomplete),
|
||||
Ok(Obj(obj)) => Ok(Obj(obj.into())),
|
||||
Ok(Dead(tok)) => Ok(Dead(tok)),
|
||||
Err(e) => Err(e.into()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of applying a [`Token`] to a [`ParseState`].
|
||||
|
|
Loading…
Reference in New Issue