diff --git a/tamer/src/nir.rs b/tamer/src/nir.rs index 74e34fe5..4040a008 100644 --- a/tamer/src/nir.rs +++ b/tamer/src/nir.rs @@ -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 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)) } diff --git a/tamer/src/nir/abstract_bind.rs b/tamer/src/nir/abstract_bind.rs index ae7c3821..bebe6e05 100644 --- a/tamer/src/nir/abstract_bind.rs +++ b/tamer/src/nir/abstract_bind.rs @@ -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 { - 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), } } diff --git a/tamer/src/nir/abstract_bind/test.rs b/tamer/src/nir/abstract_bind/test.rs index 0699d50f..a377ff27 100644 --- a/tamer/src/nir/abstract_bind/test.rs +++ b/tamer/src/nir/abstract_bind/test.rs @@ -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)), ); } diff --git a/tamer/src/nir/air.rs b/tamer/src/nir/air.rs index f0cb571c..5b2e49a9 100644 --- a/tamer/src/nir/air.rs +++ b/tamer/src/nir/air.rs @@ -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"), ], diff --git a/tamer/src/nir/air/test.rs b/tamer/src/nir/air/test.rs index a88b0606..603665af 100644 --- a/tamer/src/nir/air/test.rs +++ b/tamer/src/nir/air/test.rs @@ -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), diff --git a/tamer/src/nir/interp.rs b/tamer/src/nir/interp.rs index ba51c0b7..98104ebf 100644 --- a/tamer/src/nir/interp.rs +++ b/tamer/src/nir/interp.rs @@ -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) } diff --git a/tamer/src/nir/interp/test.rs b/tamer/src/nir/interp/test.rs index 7d53051e..2ef87562 100644 --- a/tamer/src/nir/interp/test.rs +++ b/tamer/src/nir/interp/test.rs @@ -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(), diff --git a/tamer/src/nir/parse.rs b/tamer/src/nir/parse.rs index 8f477268..d4f13424 100644 --- a/tamer/src/nir/parse.rs +++ b/tamer/src/nir/parse.rs @@ -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()), diff --git a/tamer/src/nir/tplshort.rs b/tamer/src/nir/tplshort.rs index cc1f9072..6145ed60 100644 --- a/tamer/src/nir/tplshort.rs +++ b/tamer/src/nir/tplshort.rs @@ -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 diff --git a/tamer/src/nir/tplshort/test.rs b/tamer/src/nir/tplshort/test.rs index 6e442e85..3003f29a 100644 --- a/tamer/src/nir/tplshort/test.rs +++ b/tamer/src/nir/tplshort/test.rs @@ -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)),