tamer: parse::lower: Handle EOF token

This was missed (because it was not used) when EOF tokens were originally
introduced via `ParseState::eof_tok`---`LowerIter` also needs to consider
the token.

This separation betwen the two iterators is a maintenance burden that needs
to be taken care of; I knew that at the time, and then I forgot about it,
and here we are.

This was caught while beginning to wire together a POC graph lowering
pipeline to emit derived sources.

DEV-13708
main
Mike Gerwitz 2023-02-21 12:05:46 -05:00
parent 79cc61f996
commit a68930589e
2 changed files with 38 additions and 6 deletions

View File

@ -194,12 +194,16 @@ where
/// Errors from `LS` are widened into `E`. /// Errors from `LS` are widened into `E`.
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
// TODO: This is a maintenance burden with Parser's Iterator impl;
// they can easily get out of sync,
// as evidenced by the commit introducing this comment.
let tok = self let tok = self
.lower .lower
.take_lookahead_tok() .take_lookahead_tok()
.map(Parsed::Object) .map(Parsed::Object)
.map(Ok) .map(Ok)
.or_else(|| self.toks.next()); .or_else(|| self.toks.next())
.or_else(|| self.lower.eof_tok().map(Parsed::Object).map(Ok));
match tok { match tok {
// We are done when no tokens remain. // We are done when no tokens remain.
@ -311,7 +315,11 @@ mod test {
use super::*; use super::*;
#[derive(Debug, PartialEq, Eq, Default)] #[derive(Debug, PartialEq, Eq, Default)]
struct StubEchoParseState {} enum StubEchoParseState {
#[default]
PreEof,
PostEof,
}
impl Display for StubEchoParseState { impl Display for StubEchoParseState {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -329,17 +337,24 @@ mod test {
tok: Self::Token, tok: Self::Token,
_: &mut Self::Context, _: &mut Self::Context,
) -> TransitionResult<Self> { ) -> TransitionResult<Self> {
Transition(self).ok(tok) match tok {
StubToken::Foo => Transition(Self::PostEof).ok(tok),
_ => Transition(self).ok(tok),
}
} }
fn is_accepting(&self, _: &Self::Context) -> bool { fn is_accepting(&self, _: &Self::Context) -> bool {
true true
} }
fn eof_tok(&self, _ctx: &Self::Context) -> Option<Self::Token> {
matches!(self, Self::PreEof).then_some(StubToken::Foo)
}
} }
// Similar to tests in parse::parser::test. // Similar to tests in parse::parser::test.
#[test] #[test]
fn can_emit_object_with_lookahead_for_lower_iter() { fn can_emit_object_with_lookahead_and_eof_for_lower_iter() {
let given = 27; // some value let given = 27; // some value
let toks = vec![StubToken::YieldWithLookahead(given)]; let toks = vec![StubToken::YieldWithLookahead(given)];
@ -362,6 +377,14 @@ mod test {
"lookahead token did not take effect" "lookahead token did not take effect"
); );
// Prior to end,
// we give parsers the opportunity to emit an EOF token.
assert_eq!(
sut.next(),
Some(Ok(Parsed::Object(StubObject::FromFoo))),
"EOF token was note emitted",
);
// And now this should be the end, // And now this should be the end,
// provided that the lookahead token was actually consumed and not // provided that the lookahead token was actually consumed and not
// copied and retained. // copied and retained.

View File

@ -368,6 +368,14 @@ impl<S: ClosedParseState, I: TokenStream<S::Token>> Parser<S, I> {
pub(super) fn take_lookahead_tok(&mut self) -> Option<S::Token> { pub(super) fn take_lookahead_tok(&mut self) -> Option<S::Token> {
self.lookahead.take().map(|Lookahead(tok)| tok) self.lookahead.take().map(|Lookahead(tok)| tok)
} }
/// An optional token to feed to `Self::feed_tok` after the
/// [`TokenStream`] has ended.
///
/// See [`ParseState::eof_tok`] for more information.
pub(super) fn eof_tok(&self) -> Option<S::Token> {
self.state.as_ref().unwrap().eof_tok(&self.ctx)
}
} }
impl<S: ClosedParseState, I: TokenStream<S::Token>> Iterator for Parser<S, I> { impl<S: ClosedParseState, I: TokenStream<S::Token>> Iterator for Parser<S, I> {
@ -390,7 +398,7 @@ impl<S: ClosedParseState, I: TokenStream<S::Token>> Iterator for Parser<S, I> {
let otok = self let otok = self
.take_lookahead_tok() .take_lookahead_tok()
.or_else(|| self.toks.next()) .or_else(|| self.toks.next())
.or_else(|| self.state.as_ref().unwrap().eof_tok(&self.ctx)); .or_else(|| self.eof_tok());
match otok { match otok {
None => match self.assert_accepting() { None => match self.assert_accepting() {
@ -569,6 +577,7 @@ pub mod test {
pub enum StubObject { pub enum StubObject {
FromYield(usize), FromYield(usize),
FromLookahead(usize), FromLookahead(usize),
FromFoo,
} }
impl Object for StubObject {} impl Object for StubObject {}
@ -600,7 +609,7 @@ pub mod test {
StubToken::Lookahead(val) => { StubToken::Lookahead(val) => {
Transition(self).ok(StubObject::FromLookahead(val)) Transition(self).ok(StubObject::FromLookahead(val))
} }
_ => Transition(self).incomplete(), StubToken::Foo => Transition(self).ok(StubObject::FromFoo),
} }
} }