tamer: nir: New token BindIdentMeta

The previous commit made me uncomfortable; we're already parsing with great
precision (and effort!) the grammar of NIR, and know for certain whether
we're in a metavariable binding context, so it makes no sense to have to try
to guess at another point in the lowering pipeline.

This introduces a new token to retain that information from XIR->NIR
lowering and then re-simplifies the lowering operation that was just
introduced in the previous commit (`AbstractBindTranslate`).

DEV-13163
main
Mike Gerwitz 2023-06-27 16:55:14 -04:00
parent 7314562671
commit 8685527feb
10 changed files with 59 additions and 82 deletions

View File

@ -117,6 +117,14 @@ pub enum Nir {
/// See also [`Self::BindIdent`] for a concrete identifier.
BindIdentAbstract(SPair),
/// The name should be interpreted as a concrete name of a
/// metavariable.
///
/// This is broadly equivalent to [`Self::BindIdent`] but is intended to
/// convey that no NIR operation should ever translate this token into
/// [`Self::BindIdentAbstract`].
BindIdentMeta(SPair),
/// Reference the value of the given identifier as the subject of the
/// current expression.
///
@ -203,8 +211,10 @@ impl Nir {
Open(_, _) | Close(_, _) => None,
BindIdent(spair) | RefSubject(spair) | Ref(spair) | Desc(spair)
| Text(spair) | Import(spair) => Some(spair.symbol()),
BindIdent(spair) | BindIdentMeta(spair) | RefSubject(spair)
| Ref(spair) | Desc(spair) | Text(spair) | Import(spair) => {
Some(spair.symbol())
}
// An abstract identifier does not yet have a concrete symbol
// assigned;
@ -243,6 +253,7 @@ impl Functor<SymbolId> for Nir {
BindIdent(spair) => BindIdent(spair.map(f)),
BindIdentAbstract(spair) => BindIdentAbstract(spair.map(f)),
BindIdentMeta(spair) => BindIdentMeta(spair.map(f)),
RefSubject(spair) => RefSubject(spair.map(f)),
Ref(spair) => Ref(spair.map(f)),
Desc(spair) => Desc(spair.map(f)),
@ -371,6 +382,7 @@ impl Token for Nir {
BindIdent(spair)
| BindIdentAbstract(spair)
| BindIdentMeta(spair)
| RefSubject(spair)
| Ref(spair)
| Desc(spair)
@ -412,6 +424,13 @@ impl Display for Nir {
TtQuote::wrap(spair)
)
}
BindIdentMeta(spair) => {
write!(
f,
"bind metavariable to concreate identifier {}",
TtQuote::wrap(spair)
)
}
RefSubject(spair) => {
write!(f, "subject ref {}", TtQuote::wrap(spair))
}

View File

@ -128,33 +128,14 @@
//! At the time of writing,
//! no part of TAMER yet enforces metavariable naming conventions.
use super::{Nir, NirEntity};
use super::Nir;
use crate::{parse::prelude::*, sym::GlobalSymbolResolve};
use memchr::memchr;
use Nir::*;
use NirEntity::*;
#[derive(Debug, PartialEq, Default)]
pub enum AbstractBindTranslate {
#[default]
Ready,
/// The next bind is in the context of a metavariable definition
/// (e.g. template parameter).
///
/// This should always be accurate since the grammar of NIR,
/// at least in XML form,
/// both requires binding as part of the attribute list and so before any
/// other opening element and may only contain one such attribute.
/// If this assumption does not hold in the future,
/// then the metavariable identifier will not be defined and will
/// result in a reference instead,
/// leading to errors.
/// Until then,
/// this is a simple and straightforward implementation.
BindingMeta,
}
pub struct AbstractBindTranslate;
impl Display for AbstractBindTranslate {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@ -175,18 +156,12 @@ impl ParseState for AbstractBindTranslate {
tok: Self::Token,
_: &mut Self::Context,
) -> TransitionResult<Self::Super> {
use AbstractBindTranslate::*;
match (self, tok) {
(Ready, BindIdent(name)) if needs_translation(name) => {
Transition(Ready).ok(BindIdentAbstract(name))
match tok {
BindIdent(name) if needs_translation(name) => {
Transition(self).ok(BindIdentAbstract(name))
}
(Ready, tok @ Open(TplParam, _)) => Transition(BindingMeta).ok(tok),
(BindingMeta, tok @ BindIdent(_)) => Transition(Ready).ok(tok),
(st @ (Ready | BindingMeta), tok) => Transition(st).ok(tok),
_ => Transition(self).ok(tok),
}
}

View File

@ -70,35 +70,13 @@ fn non_meta_concrete_bind_with_metavar_naming_translated_to_abstract_bind() {
fn meta_concrete_bind_with_metavar_naming_ignored() {
// This is named as a metavariable.
let name = "@param@".into();
let name_outside = "@foo@".into();
#[rustfmt::skip]
let toks = [
Open(TplParam, S1),
// This identifier utilizes a metavariable naming convention,
// but we're in a template parameter context,
// which defines a metavariable.
BindIdent(SPair(name, S2)),
Close(TplParam, S3),
// Back outside of a param context.
BindIdent(SPair(name_outside, S4)),
];
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplParam, S1)),
// Since we are in a metavariable definition context,
// the bind _stays concrete_.
O(BindIdent(SPair(name, S2))),
O(Close(TplParam, S3)),
// This one is now outside the metavariable context,
// and so we should go back to translating to abstract.
O(BindIdentAbstract(SPair(name_outside, S4))),
]),
Sut::parse(toks.into_iter()).collect()
assert_tok_translate(
// This identifier utilizes a metavariable naming convention,
// but we're in a metavariable definition context.
BindIdentMeta(SPair(name, S2)),
// And so the bind stays concrete.
BindIdentMeta(SPair(name, S2)),
);
}

View File

@ -254,6 +254,9 @@ impl ParseState for NirToAir {
(Ready, Open(TplParam, span)) => {
Transition(Meta(span)).ok(Air::MetaStart(span))
}
(Meta(mspan), BindIdentMeta(spair)) => {
Transition(Meta(mspan)).ok(Air::BindIdent(spair))
}
(Meta(mspan), Text(lexeme)) => {
Transition(Meta(mspan)).ok(Air::MetaLexeme(lexeme))
}
@ -263,9 +266,10 @@ impl ParseState for NirToAir {
// Some of these will be permitted in the future.
(
Meta(mspan),
tok @ (Open(..) | Close(..) | Ref(..) | RefSubject(..)),
tok @ (Open(..) | Close(..) | BindIdent(..) | Ref(..)
| RefSubject(..)),
) => Transition(Meta(mspan))
.err(NirToAirError::UnexpectedMetaToken(mspan, tok)),
.err(NirToAirError::ExpectedMetaToken(mspan, tok)),
(Ready, Text(text)) => Transition(Ready).ok(Air::DocText(text)),
@ -277,8 +281,8 @@ impl ParseState for NirToAir {
),
) => Transition(Ready).ok(Air::ExprEnd(span)),
(st @ (Ready | Meta(_)), BindIdent(spair)) => {
Transition(st).ok(Air::BindIdent(spair))
(Ready, BindIdent(spair)) => {
Transition(Ready).ok(Air::BindIdent(spair))
}
(st @ (Ready | Meta(_)), BindIdentAbstract(spair)) => {
Transition(st).ok(Air::BindIdentAbstract(spair))
@ -300,6 +304,7 @@ impl ParseState for NirToAir {
// This assumption is only valid so long as that's the only
// thing producing NIR.
(st @ Meta(..), tok @ Import(_)) => Transition(st).dead(tok),
(st @ Ready, tok @ BindIdentMeta(_)) => Transition(st).dead(tok),
// Unsupported tokens yield errors.
// There _is_ a risk that this will put us in a wildly
@ -354,7 +359,7 @@ pub enum NirToAirError {
/// The provided [`Nir`] token of input was unexpected for the body of a
/// metavariable that was opened at the provided [`Span`].
UnexpectedMetaToken(Span, Nir),
ExpectedMetaToken(Span, Nir),
}
impl Display for NirToAirError {
@ -374,7 +379,7 @@ impl Display for NirToAirError {
write!(f, "match body is not yet supported by TAMER")
}
UnexpectedMetaToken(_, tok) => {
ExpectedMetaToken(_, tok) => {
write!(
f,
"expected lexical token for metavariable, found {tok}"
@ -423,7 +428,7 @@ impl Diagnostic for NirToAirError {
// The user should have been preempted by the parent parser
// (e.g. XML->Nir),
// and so shouldn't see this.
UnexpectedMetaToken(mspan, given) => vec![
ExpectedMetaToken(mspan, given) => vec![
mspan.note("while parsing the body of this metavariable"),
given.span().error("expected a lexical token here"),
],

View File

@ -207,7 +207,7 @@ fn tpl_with_param() {
BindIdent(name_tpl),
Open(TplParam, S3),
BindIdent(name_param),
BindIdentMeta(name_param),
Desc(desc_param),
Close(TplParam, S6),
Close(Tpl, S7),
@ -267,12 +267,12 @@ fn apply_template_long_form_args() {
RefSubject(name),
Open(TplParam, S3),
BindIdent(p1),
BindIdentMeta(p1),
Text(v1),
Close(TplParam, S6),
Open(TplParam, S7),
BindIdent(p2),
BindIdentMeta(p2),
Text(v2),
Close(TplParam, S10),
Close(TplApply, S11),

View File

@ -282,7 +282,7 @@ impl ParseState for InterpState {
let GenIdentSymbolId(ident_sym) = gen_ident;
Transition(GenDesc(sym, gen_ident))
.ok(Nir::BindIdent(SPair(ident_sym, span)))
.ok(Nir::BindIdentMeta(SPair(ident_sym, span)))
.with_lookahead(tok)
}

View File

@ -99,7 +99,7 @@ fn expect_expanded_header(
);
assert_eq!(
sut.next(),
Some(Ok(Object(Nir::BindIdent(SPair(expect_name_sym, span))))),
Some(Ok(Object(Nir::BindIdentMeta(SPair(expect_name_sym, span))))),
);
assert_matches!(
sut.next(),

View File

@ -1425,7 +1425,7 @@ ele_parse! {
/// expanded.
TplParamStmt := QN_PARAM(_, ospan) {
@ {
QN_NAME => BindIdent,
QN_NAME => BindIdentMeta,
QN_DESC => Desc,
} => Nir::Open(NirEntity::TplParam, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplParam, cspan.into()),
@ -1697,7 +1697,7 @@ ele_parse! {
/// which gets desugared into this via [`super::tplshort`].
ApplyTemplateParam := QN_WITH_PARAM(_, ospan) {
@ {
QN_NAME => BindIdent,
QN_NAME => BindIdentMeta,
QN_VALUE => Text,
} => Nir::Open(NirEntity::TplParam, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplParam, cspan.into()),

View File

@ -179,7 +179,7 @@ impl ParseState for TplShortDesugar {
// note: reversed (stack)
stack.push(Close(TplParam, span));
stack.push(Text(val));
stack.push(BindIdent(SPair(pname, name.span())));
stack.push(BindIdentMeta(SPair(pname, name.span())));
Transition(DesugaringParams(ospan)).ok(Open(TplParam, span))
}
@ -215,7 +215,7 @@ impl ParseState for TplShortDesugar {
stack.push(Close(TplApply, ospan));
stack.push(Close(TplParam, ospan));
stack.push(Text(SPair(gen_name, ospan)));
stack.push(BindIdent(SPair(L_TPLP_VALUES, ospan)));
stack.push(BindIdentMeta(SPair(L_TPLP_VALUES, ospan)));
// Note that we must have `tok` as lookahead instead of
// pushing directly on the stack in case it's a

View File

@ -86,7 +86,7 @@ fn desugars_unary() {
O(Open(TplParam, S2)),
// Derived from `aname` (by padding)
O(BindIdent(pname)),
O(BindIdentMeta(pname)),
// The value is left untouched.
O(Text(pval)),
// Close is derived from open.
@ -137,7 +137,7 @@ fn desugars_body_into_tpl_with_ref_in_values_param() {
// @values@ remains lexical by referencing the name of a
// template we're about to generate.
O(Open(TplParam, S1)),
O(BindIdent(SPair(L_TPLP_VALUES, S1))),
O(BindIdentMeta(SPair(L_TPLP_VALUES, S1))),
O(Text(SPair(gen_name, S1))), //:-.
O(Close(TplParam, S1)), // |
O(Close(TplApply, S1)), // |
@ -193,7 +193,7 @@ fn desugar_nested_apply() {
// @values@
O(Open(TplParam, S1)),
O(BindIdent(SPair(L_TPLP_VALUES, S1))),
O(BindIdentMeta(SPair(L_TPLP_VALUES, S1))),
O(Text(SPair(gen_name_outer, S1))), //:-.
O(Close(TplParam, S1)), // |
O(Close(TplApply, S1)), // |
@ -227,7 +227,7 @@ fn does_not_desugar_long_form() {
BindIdent(name),
Open(TplParam, S3),
BindIdent(pname),
BindIdentMeta(pname),
Text(pval),
Close(TplParam, S6),
Close(TplApply, S7),
@ -244,7 +244,7 @@ fn does_not_desugar_long_form() {
O(BindIdent(name)),
O(Open(TplParam, S3)),
O(BindIdent(pname)),
O(BindIdentMeta(pname)),
O(Text(pval)),
O(Close(TplParam, S6)),
O(Close(TplApply, S7)),