tamer: parser::ParseState::delegate_lookahead: New concept
This introduces a new method similar to the previous `delegate`, but with another closure that allows for handling lookahead tokens from the child parser. Admittedly, this isn't exactly what I was going for---a list of arguments isn't exactly self-documenting, especially with the brevity when the arguments line up---but this was easy to do and so I'll run with this for now. This also modified `delegate` to accept a context, even though it wasn't necessary, both for consistency with its lookup counterpart and for brevity with the `into` argument (allowing, in our case, to just pass the name of the variant, rather than a closure). I'm not going to handle the actual starting and accepting state stitching abstraction for now; I'd like to observe future boilerplate more before I consider the best way to handle it, though I do have some ideas. DEV-10863main
parent
2a3d5be159
commit
4cb478a42d
|
@ -132,7 +132,6 @@ impl<SS: XmloSymtableState> ParseState for XmloReaderState<SS> {
|
|||
type Error = XmloError;
|
||||
|
||||
fn parse_token(self, tok: Self::Token) -> TransitionResult<Self> {
|
||||
use ParseStatus::{Dead, Incomplete, Object as Obj};
|
||||
use XmloReaderState::*;
|
||||
|
||||
match (self, tok) {
|
||||
|
@ -172,9 +171,7 @@ impl<SS: XmloSymtableState> ParseState for XmloReaderState<SS> {
|
|||
|
||||
// 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) => {
|
||||
ss.delegate(tok, |ss| Symtable(span, ss))
|
||||
}
|
||||
(Symtable(span, ss), tok) => ss.delegate(span, tok, Symtable),
|
||||
|
||||
todo => todo!("{todo:?}"),
|
||||
}
|
||||
|
@ -187,7 +184,7 @@ impl<SS: XmloSymtableState> ParseState for XmloReaderState<SS> {
|
|||
|
||||
/// Symbol table parser operating within a delimited context.
|
||||
///
|
||||
/// This parser expects a parent [`ParserState`] to indicate when symtable
|
||||
/// This parser expects a parent [`ParseState`] to indicate when symtable
|
||||
/// parsing ought to start and end—
|
||||
/// this parser does not recognize any opening or closing tags.
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
|
|
|
@ -155,7 +155,7 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
/// or it is acceptable to parse all the way until the end.
|
||||
fn is_accepting(&self) -> bool;
|
||||
|
||||
/// Delegate parsing to a compatible, stitched [`ParseState`].
|
||||
/// Delegate parsing from a compatible, stitched [`ParseState`]~`SP`.
|
||||
///
|
||||
/// This helps to combine two state machines that speak the same input
|
||||
/// language
|
||||
|
@ -170,12 +170,15 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
///
|
||||
/// This assumes that no lookahead token from [`ParseStatus::Dead`] will
|
||||
/// need to be handled by the parent state~`SP`.
|
||||
/// To handle a token of lookahead,
|
||||
/// use [`Self::delegate_lookahead`] instead.
|
||||
///
|
||||
/// _TODO: More documentation once this is finalized._
|
||||
fn delegate<SP>(
|
||||
fn delegate<C, SP>(
|
||||
self,
|
||||
context: C,
|
||||
tok: Self::Token,
|
||||
into: impl FnOnce(Self) -> SP,
|
||||
into: impl FnOnce(C, Self) -> SP,
|
||||
) -> TransitionResult<SP>
|
||||
where
|
||||
SP: ParseState<Token = Self::Token>,
|
||||
|
@ -186,13 +189,47 @@ pub trait ParseState: Default + PartialEq + Eq + Debug {
|
|||
|
||||
let (Transition(newst), result) = self.parse_token(tok).into();
|
||||
|
||||
Transition(into(newst)).result(match result {
|
||||
// This does not use `delegate_lookahead` so that we can have
|
||||
// `into: impl FnOnce` instead of `Fn`.
|
||||
Transition(into(context, 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()),
|
||||
})
|
||||
}
|
||||
|
||||
/// Delegate parsing from a compatible, stitched [`ParseState`]~`SP` with
|
||||
/// support for a lookahead token.
|
||||
///
|
||||
/// This does the same thing as [`Self::delegate`],
|
||||
/// but allows for the handling of a lookahead token from [`Self`]
|
||||
/// rather than simply proxying [`ParseStatus::Dead`].
|
||||
///
|
||||
/// _TODO: More documentation once this is finalized._
|
||||
fn delegate_lookahead<C, SP>(
|
||||
self,
|
||||
context: C,
|
||||
tok: Self::Token,
|
||||
into: impl FnOnce(C, Self) -> SP,
|
||||
lookahead: impl FnOnce(C, Self, Self::Token) -> TransitionResult<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();
|
||||
|
||||
match result {
|
||||
Ok(Incomplete) => Transition(into(context, newst)).incomplete(),
|
||||
Ok(Obj(obj)) => Transition(into(context, newst)).ok(obj.into()),
|
||||
Ok(Dead(tok)) => lookahead(context, newst, tok),
|
||||
Err(e) => Transition(into(context, newst)).err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of applying a [`Token`] to a [`ParseState`].
|
||||
|
|
|
@ -44,8 +44,7 @@ use super::{
|
|||
};
|
||||
use crate::{
|
||||
parse::{
|
||||
self, ParseState, ParseStatus, ParsedResult, Token, Transition,
|
||||
TransitionResult,
|
||||
self, ParseState, ParsedResult, Token, Transition, TransitionResult,
|
||||
},
|
||||
span::Span,
|
||||
sym::SymbolId,
|
||||
|
@ -157,6 +156,12 @@ impl Display for Object {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Attr> for Object {
|
||||
fn from(attr: Attr) -> Self {
|
||||
Self::Attr(attr)
|
||||
}
|
||||
}
|
||||
|
||||
/// XIRF-compatible attribute parser.
|
||||
pub trait FlatAttrParseState = ParseState<Token = XirToken, Object = Attr>
|
||||
where
|
||||
|
@ -200,7 +205,6 @@ where
|
|||
type Error = StateError;
|
||||
|
||||
fn parse_token(self, tok: Self::Token) -> TransitionResult<Self> {
|
||||
use ParseStatus::{Dead, Incomplete, Object as Obj};
|
||||
use State::{AttrExpected, Done, NodeExpected, PreRoot};
|
||||
|
||||
match (self, tok) {
|
||||
|
@ -219,23 +223,12 @@ where
|
|||
|
||||
(NodeExpected(stack), tok) => Self::parse_node(stack, tok),
|
||||
|
||||
(AttrExpected(stack, sa), tok) => {
|
||||
match sa.parse_token(tok).into() {
|
||||
(Transition(sa), Ok(Incomplete)) => {
|
||||
Transition(AttrExpected(stack, sa)).incomplete()
|
||||
}
|
||||
(Transition(sa), Ok(Obj(attr))) => {
|
||||
Transition(AttrExpected(stack, sa))
|
||||
.ok(Object::Attr(attr))
|
||||
}
|
||||
(_, Ok(Dead(lookahead))) => {
|
||||
Self::parse_node(stack, lookahead)
|
||||
}
|
||||
(Transition(sa), Err(x)) => {
|
||||
Transition(AttrExpected(stack, sa)).err(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
(AttrExpected(stack, sa), tok) => sa.delegate_lookahead(
|
||||
stack,
|
||||
tok,
|
||||
AttrExpected,
|
||||
|stack, _, lookahead| Self::parse_node(stack, lookahead),
|
||||
),
|
||||
|
||||
(Done, tok) => Transition(Done).dead(tok),
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue