diff --git a/tamer/src/parse/state/transition.rs b/tamer/src/parse/state/transition.rs index d1ba217d..9d39fd0b 100644 --- a/tamer/src/parse/state/transition.rs +++ b/tamer/src/parse/state/transition.rs @@ -79,6 +79,17 @@ impl TransitionResult { /// Indicate that this transition include a single token of lookahead, /// which should be provided back to the parser in place of the /// next token from the input stream. + /// + /// Panics + /// ====== + /// A critical invariant of this system is that lookahead tokens must + /// never be discarded without explicit handling. + /// If this [`TransitionResult`] contains an existing token of lookahead, + /// the system will panic when attempting to overwrite it. + /// This represents a bug in the system, + /// since parsers should never permit this to occur. + /// + /// Ideally this will be enforced using the type system in the future. pub fn with_lookahead(self, lookahead: S::Token) -> Self { match self { Self(transition, TransitionData::Result(result, None)) => Self( @@ -92,22 +103,12 @@ impl TransitionResult { // ever such a thing is deemed to be worth doing. Self( .., - TransitionData::Result(_, Some(Lookahead(prev))) - | TransitionData::Dead(Lookahead(prev)), - ) => { - let desc = vec![ - prev.span().note("this token of lookahead would be lost"), - lookahead.span().internal_error( - "attempting to replace previous \ - lookahead token with this one", - ), - ]; - - diagnostic_panic!( - desc, - "cannot overwrite unused lookahead token" - ) - } + TransitionData::Result(_, Some(prev)) + | TransitionData::Dead(prev), + ) => prev.overwrite_panic( + lookahead, + "cannot overwrite unused lookahead token", + ), } } @@ -231,6 +232,29 @@ impl TransitionResult { #[derive(Debug, PartialEq)] pub struct Lookahead(pub(in super::super) T); +impl Lookahead { + /// Panic with diagnostic information about a lookup token and its + /// attempted replacement. + /// + /// A critical system invariant is that lookahead tokens must never be + /// lost without explicit handling. + /// Since this is not yet enforced using the type system, + /// these checks must be performed at runtime. + pub(in super::super) fn overwrite_panic(self, other: T, msg: &str) -> ! { + let Self(prev) = self; + + let desc = vec![ + prev.span().note("this token of lookahead would be lost"), + other.span().internal_error( + "attempting to replace previous lookahead token \ + with this one", + ), + ]; + + diagnostic_panic!(desc, "{msg}",) + } +} + /// Information about the state transition. /// /// Note: Ideally a state wouldn't even be required for diff --git a/tamer/src/parse/util/expand.rs b/tamer/src/parse/util/expand.rs index 87adaaf2..21fa2b34 100644 --- a/tamer/src/parse/util/expand.rs +++ b/tamer/src/parse/util/expand.rs @@ -22,8 +22,6 @@ //! _Expansion_ refers to the production of many [`Object`]s that are //! derived from a single [`Token`]. -use crate::{diagnose::Annotate, diagnostic_panic}; - use super::super::{ prelude::*, state::{Lookahead, StitchableParseState, TransitionData}, @@ -131,31 +129,15 @@ where TransitionData::Result(Ok(ParseStatus::Object(obj)), la) } - // A parser must never throw away lookahead tokens. // Since we are converting the `DoneExpanding` variant // into a lookahead token, // we would have nothing to do with a token of // lookahead if one were provided to us. - // Ideally this would be prevented using types, - // but such a change is too much effort at the time of - // writing. - (DoneExpanding(tok), Some(Lookahead(la_tok))) => { - let desc = vec![ - tok.span().note( - "while processing this \ - Expansion::DoneExpanding token", - ), - la_tok.span().internal_error( - "encountered this unexpected lookahead token", - ), - ]; - - diagnostic_panic!( - desc, - "cannot provide lookahead token with \ - Expansion::DoneExpanding", - ) - } + (DoneExpanding(tok), Some(la)) => la.overwrite_panic( + tok, + "cannot provide lookahead token with \ + Expansion::DoneExpanding", + ), (DoneExpanding(tok), None) => { TransitionData::Dead(Lookahead(tok))