tamer: parse::util::expand: Move expansion into own module
This has evolved into a more robust and independent concept, but it is still a utility in the sense that it's utilizing existing parsing framework features and making them more convenient. DEV-13156main
parent
ddb4f24ea5
commit
55c55cabd3
|
@ -105,7 +105,7 @@ use crate::{
|
|||
fmt::{DisplayWrapper, TtQuote},
|
||||
parse::{
|
||||
prelude::*,
|
||||
util::{Expansion, SPair},
|
||||
util::{expand::Expansion, SPair},
|
||||
NoContext,
|
||||
},
|
||||
span::Span,
|
||||
|
|
|
@ -25,179 +25,11 @@
|
|||
//! they provide wrappers around core functionality that make it easier
|
||||
//! to use outside of the domain of the parsing system itself.
|
||||
|
||||
use crate::{diagnose::Annotate, diagnostic_panic, span::Span, sym::SymbolId};
|
||||
pub mod expand;
|
||||
|
||||
use super::{
|
||||
prelude::*,
|
||||
state::{Lookahead, StitchableParseState, TransitionData},
|
||||
};
|
||||
use std::{fmt::Display, marker::PhantomData};
|
||||
|
||||
pub trait ExpandingParseState<T: Token, O: Object> =
|
||||
ParseState<Token = T, Object = Expansion<T, O>>;
|
||||
|
||||
/// Represents an expansion operation on some source token of type `T`.
|
||||
///
|
||||
/// See variants and [`ExpandingParseState`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Expansion<T, O: Object> {
|
||||
/// A token of type `O` has been derived from the source token and
|
||||
/// should be merged into the target token stream.
|
||||
Expanded(O),
|
||||
|
||||
/// Expansion is complete and the source token should be replaced with
|
||||
/// the inner `T`.
|
||||
DoneExpanding(T),
|
||||
}
|
||||
|
||||
impl<T: Token, O: Object> Object for Expansion<T, O> {}
|
||||
|
||||
/// A [`ClosedParseState`] that is able to serve as an expansion parser.
|
||||
///
|
||||
/// An expansion parser is a parser yielding [`Expansion`],
|
||||
/// intended to be integrated into another token stream.
|
||||
pub trait ExpandableParseState<O: Object> = ClosedParseState
|
||||
where
|
||||
O: Token + Eq,
|
||||
Self: ParseState<Object = Expansion<<Self as ParseState>::Token, O>>;
|
||||
|
||||
/// An [`ExpandableParseState`] capable of expanding into the token stream
|
||||
/// of a parent [`ParseState`] `SP`.
|
||||
///
|
||||
/// This trait asserts that an [`ExpandableParseState`] is a
|
||||
/// [`StitchableParseState<SP>`](StitchableParseState) after being wrapped
|
||||
/// by [`StitchableExpansionState`].
|
||||
pub trait ExpandableInto<SP: ParseState> =
|
||||
ExpandableParseState<<SP as ParseState>::Object>
|
||||
where
|
||||
StitchableExpansionState<Self, <SP as ParseState>::Object>:
|
||||
StitchableParseState<SP>;
|
||||
|
||||
/// Convert a [`ClosedParseState`] yielding an [`Expansion<T,O>`](Expansion)
|
||||
/// object into a parser yielding `O` with a dead state yielding `T`.
|
||||
///
|
||||
/// It is more convenient and clear to write parsers using [`Expansion`],
|
||||
/// since those variants not only state directly what the intent of the
|
||||
/// operations are,
|
||||
/// but also avoid having to work with dead states.
|
||||
/// However,
|
||||
/// their wrapping in [`Expansion`] makes them difficult to delegate to
|
||||
/// (compose with)
|
||||
/// other parsers using [`ParseState`]'s `delegate_*` family of
|
||||
/// functions.
|
||||
///
|
||||
/// This parser handles this translation by stripping away the
|
||||
/// [`Expansion`] abstraction and producing a [`ParseState`] that looks
|
||||
/// and acts like what would have been implemented in the absence of such
|
||||
/// an abstraction.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct StitchableExpansionState<S: ClosedParseState, O: Object> {
|
||||
st: S,
|
||||
_phantom: PhantomData<O>,
|
||||
}
|
||||
|
||||
// We implement Default if the parser `S` that we're wrapping does.
|
||||
impl<S: ClosedParseState, O: Object> Default for StitchableExpansionState<S, O>
|
||||
where
|
||||
S: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
st: Default::default(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ClosedParseState, O: Object> ParseState
|
||||
for StitchableExpansionState<S, O>
|
||||
where
|
||||
S: ExpandableParseState<O>,
|
||||
{
|
||||
type Token = S::Token;
|
||||
type Object = O;
|
||||
type Error = S::Error;
|
||||
type Context = S::Context;
|
||||
|
||||
#[inline]
|
||||
fn parse_token(
|
||||
self,
|
||||
tok: Self::Token,
|
||||
ctx: &mut Self::Context,
|
||||
) -> TransitionResult<Self::Super> {
|
||||
use Expansion::*;
|
||||
|
||||
match self {
|
||||
Self { st, _phantom } => {
|
||||
let TransitionResult(Transition(st_new), data) =
|
||||
st.parse_token(tok, ctx);
|
||||
|
||||
let data_new = data.map_when_obj(|obj, la| match (obj, la) {
|
||||
(Expanded(obj), la) => {
|
||||
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), None) => {
|
||||
TransitionData::Dead(Lookahead(tok))
|
||||
}
|
||||
});
|
||||
|
||||
TransitionResult(
|
||||
Transition(Self {
|
||||
st: st_new,
|
||||
_phantom,
|
||||
}),
|
||||
data_new,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_accepting(&self, ctx: &Self::Context) -> bool {
|
||||
self.st.is_accepting(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ClosedParseState, O: Object> Display
|
||||
for StitchableExpansionState<S, O>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self {
|
||||
st: parser,
|
||||
_phantom,
|
||||
} => {
|
||||
write!(f, "{parser}, with Expansion stripped")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use super::prelude::*;
|
||||
use crate::{span::Span, sym::SymbolId};
|
||||
use std::fmt::Display;
|
||||
|
||||
/// A [`SymbolId`] with a corresponding [`Span`].
|
||||
///
|
||||
|
@ -240,6 +72,3 @@ impl Into<(SymbolId, Span)> for SPair {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
|
|
@ -0,0 +1,197 @@
|
|||
// TAMER parsing framework utilities for token expansion
|
||||
//
|
||||
// Copyright (C) 2014-2022 Ryan Specialty Group, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Token expansion utilities.
|
||||
//!
|
||||
//! _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},
|
||||
};
|
||||
use std::{fmt::Display, marker::PhantomData};
|
||||
|
||||
/// Represents an expansion operation on some source token of type `T`.
|
||||
///
|
||||
/// See variants and [`ExpandableParseState`] for more information.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Expansion<T, O: Object> {
|
||||
/// A token of type `O` has been derived from the source token and
|
||||
/// should be merged into the target token stream.
|
||||
Expanded(O),
|
||||
|
||||
/// Expansion is complete and the source token should be replaced with
|
||||
/// the inner `T`.
|
||||
DoneExpanding(T),
|
||||
}
|
||||
|
||||
impl<T: Token, O: Object> Object for Expansion<T, O> {}
|
||||
|
||||
/// A [`ClosedParseState`] that is able to serve as an expansion parser.
|
||||
///
|
||||
/// An expansion parser is a parser yielding [`Expansion`],
|
||||
/// intended to be integrated into another token stream.
|
||||
pub trait ExpandableParseState<O: Object> = ClosedParseState
|
||||
where
|
||||
O: Token + Eq,
|
||||
Self: ParseState<Object = Expansion<<Self as ParseState>::Token, O>>;
|
||||
|
||||
/// An [`ExpandableParseState`] capable of expanding into the token stream
|
||||
/// of a parent [`ParseState`] `SP`.
|
||||
///
|
||||
/// This trait asserts that an [`ExpandableParseState`] is a
|
||||
/// [`StitchableParseState<SP>`](StitchableParseState) after being wrapped
|
||||
/// by [`StitchableExpansionState`].
|
||||
pub trait ExpandableInto<SP: ParseState> =
|
||||
ExpandableParseState<<SP as ParseState>::Object>
|
||||
where
|
||||
StitchableExpansionState<Self, <SP as ParseState>::Object>:
|
||||
StitchableParseState<SP>;
|
||||
|
||||
/// Convert a [`ClosedParseState`] yielding an [`Expansion<T,O>`](Expansion)
|
||||
/// object into a parser yielding `O` with a dead state yielding `T`.
|
||||
///
|
||||
/// It is more convenient and clear to write parsers using [`Expansion`],
|
||||
/// since those variants not only state directly what the intent of the
|
||||
/// operations are,
|
||||
/// but also avoid having to work with dead states.
|
||||
/// However,
|
||||
/// their wrapping in [`Expansion`] makes them difficult to delegate to
|
||||
/// (compose with)
|
||||
/// other parsers using [`ParseState`]'s `delegate_*` family of
|
||||
/// functions.
|
||||
///
|
||||
/// This parser handles this translation by stripping away the
|
||||
/// [`Expansion`] abstraction and producing a [`ParseState`] that looks
|
||||
/// and acts like what would have been implemented in the absence of such
|
||||
/// an abstraction.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct StitchableExpansionState<S: ClosedParseState, O: Object> {
|
||||
st: S,
|
||||
_phantom: PhantomData<O>,
|
||||
}
|
||||
|
||||
// We implement Default if the parser `S` that we're wrapping does.
|
||||
impl<S: ClosedParseState, O: Object> Default for StitchableExpansionState<S, O>
|
||||
where
|
||||
S: Default,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
st: Default::default(),
|
||||
_phantom: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ClosedParseState, O: Object> ParseState
|
||||
for StitchableExpansionState<S, O>
|
||||
where
|
||||
S: ExpandableParseState<O>,
|
||||
{
|
||||
type Token = S::Token;
|
||||
type Object = O;
|
||||
type Error = S::Error;
|
||||
type Context = S::Context;
|
||||
|
||||
#[inline]
|
||||
fn parse_token(
|
||||
self,
|
||||
tok: Self::Token,
|
||||
ctx: &mut Self::Context,
|
||||
) -> TransitionResult<Self::Super> {
|
||||
use Expansion::*;
|
||||
|
||||
match self {
|
||||
Self { st, _phantom } => {
|
||||
let TransitionResult(Transition(st_new), data) =
|
||||
st.parse_token(tok, ctx);
|
||||
|
||||
let data_new = data.map_when_obj(|obj, la| match (obj, la) {
|
||||
(Expanded(obj), la) => {
|
||||
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), None) => {
|
||||
TransitionData::Dead(Lookahead(tok))
|
||||
}
|
||||
});
|
||||
|
||||
TransitionResult(
|
||||
Transition(Self {
|
||||
st: st_new,
|
||||
_phantom,
|
||||
}),
|
||||
data_new,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_accepting(&self, ctx: &Self::Context) -> bool {
|
||||
self.st.is_accepting(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: ClosedParseState, O: Object> Display
|
||||
for StitchableExpansionState<S, O>
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Self {
|
||||
st: parser,
|
||||
_phantom,
|
||||
} => {
|
||||
write!(f, "{parser}, with Expansion stripped")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
|
@ -17,8 +17,12 @@
|
|||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::super::SPair;
|
||||
use super::*;
|
||||
use crate::{span::dummy::*, sym::st::raw};
|
||||
use crate::{
|
||||
span::{dummy::*, Span},
|
||||
sym::{st::raw, SymbolId},
|
||||
};
|
||||
use std::{assert_matches::assert_matches, convert::Infallible};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
Loading…
Reference in New Issue