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-13163main
parent
7314562671
commit
8685527feb
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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"),
|
||||
],
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)),
|
||||
|
|
Loading…
Reference in New Issue