tamer: parse::parser::finalize: Introduce FinalizedParser
This newtype allows a caller to prove (using types) that a parser of a given type (`ParseState`) has been finalized. This will be used by the lowering pipeline to ensure that all parsers in the pipeline end up getting finalized (as you can see from a TODO added in the code, one of them is missing). The lack of such a type was an oversight during the (rather stressed) development of the parsing system, and I shouldn't need to resort to unit tests to verify that parsers have been finalized. DEV-13158main
parent
7e62276907
commit
2087672c47
|
@ -213,7 +213,7 @@ mod test {
|
|||
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next());
|
||||
|
||||
let asg = sut.finalize().unwrap();
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let ident_node =
|
||||
asg.lookup(sym).expect("identifier was not added to graph");
|
||||
|
@ -254,7 +254,7 @@ mod test {
|
|||
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next());
|
||||
|
||||
let asg = sut.finalize().unwrap();
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let ident_node =
|
||||
asg.lookup(sym).expect("identifier was not added to graph");
|
||||
|
@ -292,7 +292,7 @@ mod test {
|
|||
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next());
|
||||
|
||||
let asg = sut.finalize().unwrap();
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let ident_node = asg
|
||||
.lookup(ident)
|
||||
|
@ -325,7 +325,7 @@ mod test {
|
|||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // IdentDecl
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // IdentFragment
|
||||
|
||||
let asg = sut.finalize().unwrap();
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let ident_node =
|
||||
asg.lookup(sym).expect("identifier was not added to graph");
|
||||
|
@ -362,7 +362,7 @@ mod test {
|
|||
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next());
|
||||
|
||||
let asg = sut.finalize().unwrap();
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let ident_node = asg
|
||||
.lookup(sym)
|
||||
|
@ -401,7 +401,7 @@ mod test {
|
|||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // IdentDecl
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // IdentRoot
|
||||
|
||||
let asg = sut.finalize().unwrap();
|
||||
let asg = sut.finalize().unwrap().into_context();
|
||||
|
||||
let ident_node = asg
|
||||
.lookup(sym)
|
||||
|
|
|
@ -423,7 +423,7 @@ mod test {
|
|||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // PkgRootPath
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // Eoh
|
||||
|
||||
let ctx = sut.finalize().unwrap();
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
|
||||
assert_eq!(Some(name), ctx.prog_name);
|
||||
assert_eq!(Some(relroot), ctx.relroot);
|
||||
|
@ -510,7 +510,7 @@ mod test {
|
|||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // SymDecl (@src)
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // Eoh
|
||||
|
||||
let ctx = sut.finalize().unwrap();
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
let mut founds = ctx.found.unwrap().into_iter().collect::<Vec<_>>();
|
||||
|
||||
// Just to remove nondeterminism in case the iteration order happens
|
||||
|
@ -621,7 +621,7 @@ mod test {
|
|||
);
|
||||
assert_eq!(Some(Ok(Parsed::Incomplete)), sut.next()); // Eoh
|
||||
|
||||
let ctx = sut.finalize().unwrap();
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
|
||||
// Both above symbols were local (no `src`),
|
||||
// but note that we don't care if it's None or initialized with a
|
||||
|
|
|
@ -29,7 +29,7 @@ mod trace;
|
|||
|
||||
pub use error::ParseError;
|
||||
pub use lower::{Lower, LowerIter, ParsedObject};
|
||||
pub use parser::{Parsed, ParsedResult, Parser};
|
||||
pub use parser::{FinalizedParser, Parsed, ParsedResult, Parser};
|
||||
pub use state::{
|
||||
context::{Context, Empty as EmptyContext, NoContext},
|
||||
ClosedParseState, ParseResult, ParseState, ParseStatus, Transition,
|
||||
|
@ -376,7 +376,7 @@ pub mod test {
|
|||
let mut toks = vec![TestToken::MarkDone(DS)].into_iter();
|
||||
let mut sut = Sut::from(&mut toks);
|
||||
sut.next().unwrap().unwrap();
|
||||
let ctx = sut.finalize().unwrap();
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
assert_eq!(ctx, Default::default());
|
||||
|
||||
// Next, verify that the context that is manipulated is the context
|
||||
|
@ -385,7 +385,7 @@ pub mod test {
|
|||
let mut toks = vec![TestToken::SetCtxVal(5)].into_iter();
|
||||
let mut sut = Sut::from(&mut toks);
|
||||
sut.next().unwrap().unwrap();
|
||||
let ctx = sut.finalize().unwrap();
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
assert_eq!(ctx, StubContext { val });
|
||||
|
||||
// Finally, verify that the context provided is the context that is
|
||||
|
@ -395,7 +395,7 @@ pub mod test {
|
|||
let mut toks = vec![TestToken::MarkDone(DS)].into_iter();
|
||||
let mut sut = EchoState::parse_with_context(&mut toks, given_ctx);
|
||||
sut.next().unwrap().unwrap();
|
||||
let ctx = sut.finalize().unwrap();
|
||||
let ctx = sut.finalize().unwrap().into_context();
|
||||
assert_eq!(ctx, StubContext { val });
|
||||
}
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
//! IR lowering operation between [`Parser`]s.
|
||||
|
||||
use super::{
|
||||
state::ClosedParseState, NoContext, Object, ParseError, ParseState, Parsed,
|
||||
Parser, Token, TransitionResult, UnknownToken,
|
||||
state::ClosedParseState, FinalizedParser, NoContext, Object, ParseError,
|
||||
ParseState, Parsed, Parser, Token, TransitionResult, UnknownToken,
|
||||
};
|
||||
use crate::diagnose::Diagnostic;
|
||||
use std::{fmt::Display, iter, marker::PhantomData};
|
||||
|
@ -62,7 +62,7 @@ where
|
|||
{
|
||||
/// Consume inner parser and yield its context.
|
||||
#[inline]
|
||||
fn finalize(self) -> Result<LS::Context, E> {
|
||||
fn finalize(self) -> Result<FinalizedParser<LS>, E> {
|
||||
self.lower.finalize().map_err(|(_, e)| e.into())
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +129,8 @@ where
|
|||
_phantom: PhantomData::default(),
|
||||
};
|
||||
f(&mut iter)
|
||||
|
||||
// TODO: Finalize!
|
||||
}
|
||||
|
||||
/// Perform a lowering operation between two parsers where the context
|
||||
|
@ -155,7 +157,10 @@ where
|
|||
};
|
||||
let val = f(&mut iter)?;
|
||||
|
||||
iter.finalize().map(|ctx| (val, ctx))
|
||||
// TODO: Further propagate `FinalizedParser`
|
||||
iter.finalize()
|
||||
.map(FinalizedParser::into_context)
|
||||
.map(|ctx| (val, ctx))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -178,9 +178,10 @@ impl<S: ClosedParseState, I: TokenStream<S::Token>> Parser<S, I> {
|
|||
/// is a decision made by the [`ParseState`].
|
||||
pub fn finalize(
|
||||
self,
|
||||
) -> Result<S::Context, (Self, ParseError<S::Token, S::Error>)> {
|
||||
) -> Result<FinalizedParser<S>, (Self, ParseError<S::Token, S::Error>)>
|
||||
{
|
||||
match self.assert_accepting() {
|
||||
Ok(()) => Ok(self.ctx),
|
||||
Ok(()) => Ok(FinalizedParser(self.ctx)),
|
||||
Err(err) => Err((self, err)),
|
||||
}
|
||||
}
|
||||
|
@ -454,6 +455,26 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Residual state of a parser that has been finalized with
|
||||
/// [`Parser::finalize`].
|
||||
///
|
||||
/// This type can be used to ensure that parsers are always finalized at the
|
||||
/// end of an operation by providing such evidence to a caller.
|
||||
///
|
||||
/// If the inner [`ParseState::Context`] is empty or no longer needed,
|
||||
/// then this can be safely dropped without use.
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct FinalizedParser<S: ParseState>(S::Context);
|
||||
|
||||
impl<S: ParseState> FinalizedParser<S> {
|
||||
/// Take ownership over the inner [`ParseState::Context`].
|
||||
pub fn into_context(self) -> S::Context {
|
||||
match self {
|
||||
Self(ctx) => ctx,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
|
Loading…
Reference in New Issue