tamer: nir: Apply*Short variants

This adds explicit variants for shorthand template application.  This is
less cryptic, and we'll be able to check for the close directly during
desugaring.

DEV-13708
main
Mike Gerwitz 2023-03-23 22:08:21 -04:00
parent 975f60bff9
commit e595698309
9 changed files with 95 additions and 97 deletions

View File

@ -204,22 +204,21 @@ pub enum NirEntity {
/// Template.
Tpl,
/// Template parameter (metavariable).
///
/// If a pair of [`SPair`]s is provided,
/// then the param is shorthand,
/// with the non-`@`-padded name as the first of the pair and the
/// value as the second.
///
/// A shorthand entity is implicitly closed and should not have a
/// matching [`Nir::Close`] token.
TplParam(Option<(SPair, SPair)>),
/// Template application (long form).
TplApply,
/// Template parameter (long form).
TplParam,
/// Full application and expansion a template.
/// Template application (shorthand).
TplApplyShort(QName),
/// Template parameter (shorthand).
///
/// If a name is provided,
/// then this template application is shorthand.
TplApply(Option<QName>),
/// The non-`@`-padded name is the first of the pair and the value as
/// the second.
///
/// A shorthand param is implicitly closed and should not have a
/// matching [`Nir::Close`] token.
TplParamShort(SPair, SPair),
}
impl NirEntity {
@ -250,19 +249,19 @@ impl Display for NirEntity {
Any => write!(f, "disjunctive () expression"),
Tpl => write!(f, "template"),
TplParam(None) => write!(f, "template param (metavariable)"),
TplParam(Some((name, val))) => write!(
TplParam => write!(f, "template param (metavariable)"),
TplParamShort(name, val) => write!(
f,
"shorthand template param key {} with value {}",
TtQuote::wrap(name),
TtQuote::wrap(val),
),
TplApply(None) => {
TplApply => {
write!(f, "full template application and expansion")
}
TplApply(Some(qname)) => write!(
TplApplyShort(qname) => write!(
f,
"full template application and expansion of {}",
"full shorthand template application and expansion of {}",
TtQuote::wrap(qname.local_name())
),
}

View File

@ -117,7 +117,7 @@ impl ParseState for NirToAir {
Transition(Ready).ok(Air::TplEnd(span))
}
(Ready, Nir::Open(NirEntity::TplApply(None), span)) => {
(Ready, Nir::Open(NirEntity::TplApply, span)) => {
Transition(Ready).ok(Air::TplStart(span))
}
@ -127,7 +127,11 @@ impl ParseState for NirToAir {
(
Ready,
Nir::Open(
NirEntity::TplApply(Some(_)) | NirEntity::TplParam(Some(_)),
NirEntity::TplApplyShort(..) | NirEntity::TplParamShort(..),
span,
)
| Nir::Close(
NirEntity::TplApplyShort(..) | NirEntity::TplParamShort(..),
span,
),
) => {
@ -149,14 +153,14 @@ impl ParseState for NirToAir {
build of TAMER"
)
}
(Ready, Nir::Close(NirEntity::TplApply(_), span)) => {
(Ready, Nir::Close(NirEntity::TplApply, span)) => {
Transition(Ready).ok(Air::TplEndRef(span))
}
(Ready, Nir::Open(NirEntity::TplParam(None), span)) => {
(Ready, Nir::Open(NirEntity::TplParam, span)) => {
Transition(Ready).ok(Air::TplMetaStart(span))
}
(Ready, Nir::Close(NirEntity::TplParam(_), span)) => {
(Ready, Nir::Close(NirEntity::TplParam, span)) => {
Transition(Ready).ok(Air::TplMetaEnd(span))
}
(Ready, Nir::Text(lexeme)) => {

View File

@ -146,9 +146,9 @@ fn apply_template_long_form_nullary() {
#[rustfmt::skip]
let toks = vec![
Nir::Open(NirEntity::TplApply(None), S1),
Nir::Open(NirEntity::TplApply, S1),
Nir::Ref(name),
Nir::Close(NirEntity::TplApply(None), S3),
Nir::Close(NirEntity::TplApply, S3),
];
#[rustfmt::skip]
@ -172,19 +172,19 @@ fn apply_template_long_form_args() {
#[rustfmt::skip]
let toks = vec![
Nir::Open(NirEntity::TplApply(None), S1),
Nir::Open(NirEntity::TplApply, S1),
Nir::Ref(name),
Nir::Open(NirEntity::TplParam(None), S3),
Nir::Open(NirEntity::TplParam, S3),
Nir::BindIdent(p1),
Nir::Text(v1),
Nir::Close(NirEntity::TplParam(None), S6),
Nir::Close(NirEntity::TplParam, S6),
Nir::Open(NirEntity::TplParam(None), S7),
Nir::Open(NirEntity::TplParam, S7),
Nir::BindIdent(p2),
Nir::Text(v2),
Nir::Close(NirEntity::TplParam(None), S10),
Nir::Close(NirEntity::TplApply(None), S11),
Nir::Close(NirEntity::TplParam, S10),
Nir::Close(NirEntity::TplApply, S11),
];
#[rustfmt::skip]

View File

@ -262,7 +262,7 @@ impl ParseState for InterpState {
Ready => match tok.symbol() {
Some(sym) if needs_interpolation(sym) => {
Transition(GenIdent(sym))
.ok(Nir::Open(NirEntity::TplParam(None), span))
.ok(Nir::Open(NirEntity::TplParam, span))
.with_lookahead(tok)
}
@ -315,7 +315,7 @@ impl ParseState for InterpState {
// symbol that we've been fed
// (the specification string).
Transition(FinishSym(s, gen_param))
.ok(Nir::Close(NirEntity::TplParam(None), span))
.ok(Nir::Close(NirEntity::TplParam, span))
.with_lookahead(tok)
};
}

View File

@ -79,7 +79,7 @@ fn expect_expanded_header(
// helpful information to a human reader.
assert_eq!(
sut.next(),
Some(Ok(Object(Nir::Open(NirEntity::TplParam(None), span)))),
Some(Ok(Object(Nir::Open(NirEntity::TplParam, span)))),
);
assert_eq!(
sut.next(),
@ -132,7 +132,7 @@ fn desugars_literal_with_ending_var() {
Object(Nir::Ref(SPair("@bar@".into(), c))),
// This is an object generated from user input, so the closing
// span has to identify what were generated from.
Object(Nir::Close(NirEntity::TplParam(None), a)),
Object(Nir::Close(NirEntity::TplParam, a)),
// Finally,
// we replace the original provided attribute
// (the interpolation specification)
@ -172,7 +172,7 @@ fn desugars_var_with_ending_literal() {
Ok(vec![
Object(Nir::Ref(SPair("@foo@".into(), b))),
Object(Nir::Text(SPair("bar".into(), c))),
Object(Nir::Close(NirEntity::TplParam(None), a)),
Object(Nir::Close(NirEntity::TplParam, a)),
Object(Nir::Ref(SPair(expect_name, a))),
]),
sut.collect(),
@ -216,7 +216,7 @@ fn desugars_many_vars_and_literals() {
// offsets.
Object(Nir::Text(SPair("baz".into(), d))),
Object(Nir::Ref(SPair("@quux@".into(), e))),
Object(Nir::Close(NirEntity::TplParam(None), a)),
Object(Nir::Close(NirEntity::TplParam, a)),
Object(Nir::Ref(SPair(expect_name, a))),
]),
sut.collect(),
@ -266,7 +266,7 @@ fn proper_multibyte_handling() {
// offsets.
Object(Nir::Text(SPair("βaζ".into(), d))),
Object(Nir::Ref(SPair("@qμuχ@".into(), e))),
Object(Nir::Close(NirEntity::TplParam(None), a)),
Object(Nir::Close(NirEntity::TplParam, a)),
Object(Nir::Ref(SPair(expect_name, a))),
]),
sut.collect(),
@ -301,7 +301,7 @@ fn desugars_adjacent_interpolated_vars() {
Object(Nir::Ref(SPair("@foo@".into(), b))),
Object(Nir::Ref(SPair("@bar@".into(), c))),
Object(Nir::Ref(SPair("@baz@".into(), d))),
Object(Nir::Close(NirEntity::TplParam(None), a)),
Object(Nir::Close(NirEntity::TplParam, a)),
Object(Nir::Ref(SPair(expect_name, a))),
]),
sut.collect(),
@ -347,7 +347,7 @@ fn error_missing_closing_interp_delim() {
// Having omitted the above token,
// we're able to proceed as if the user didn't provide it at
// all.
Ok(Object(Nir::Close(NirEntity::TplParam(None), a))),
Ok(Object(Nir::Close(NirEntity::TplParam, a))),
Ok(Object(Nir::Ref(SPair(expect_name, a)))),
],
sut.collect::<Vec<ParsedResult<Sut>>>(),
@ -395,7 +395,7 @@ fn error_nested_delim() {
// (end of the specification string)
// and ignore everything that follows rather than
// potentially interpret it in confusing ways.
Ok(Object(Nir::Close(NirEntity::TplParam(None), a))),
Ok(Object(Nir::Close(NirEntity::TplParam, a))),
Ok(Object(Nir::Ref(SPair(expect_name, a)))),
],
sut.collect::<Vec<ParsedResult<Sut>>>(),
@ -438,7 +438,7 @@ fn error_empty_interp() {
// It wouldn't have had any effect anyway,
// being empty.
Ok(Object(Nir::Text(SPair("cow".into(), d)))),
Ok(Object(Nir::Close(NirEntity::TplParam(None), a))),
Ok(Object(Nir::Close(NirEntity::TplParam, a))),
Ok(Object(Nir::Ref(SPair(expect_name, a)))),
],
sut.collect::<Vec<ParsedResult<Sut>>>(),
@ -477,7 +477,7 @@ fn error_close_before_open() {
// was supposed to be a literal or a param.
// Just bail out;
// maybe in the future we can do something better.
Ok(Object(Nir::Close(NirEntity::TplParam(None), a))),
Ok(Object(Nir::Close(NirEntity::TplParam, a))),
Ok(Object(Nir::Ref(SPair(expect_name, a)))),
],
sut.collect::<Vec<ParsedResult<Sut>>>(),

View File

@ -1679,8 +1679,8 @@ ele_parse! {
ApplyTemplate := QN_APPLY_TEMPLATE(_, ospan) {
@ {
QN_NAME => Ref,
} => Nir::Open(NirEntity::TplApply(None), ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplApply(None), cspan.into()),
} => Nir::Open(NirEntity::TplApply, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplApply, cspan.into()),
ApplyTemplateParam,
};
@ -1695,10 +1695,8 @@ ele_parse! {
@ {
QN_NAME => BindIdent,
QN_VALUE => Text,
} => Nir::Open(NirEntity::TplParam(None), ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplParam(None), cspan.into()),
// TODO: Need to support children, e.g. @values@
} => Nir::Open(NirEntity::TplParam, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplParam, cspan.into()),
};
/// Shorthand template application.
@ -1716,8 +1714,8 @@ ele_parse! {
/// so `bar="baz"` will be desugared into a param `@bar@` with a
/// text value `baz`.
TplApplyShort := NS_T(qname, ospan) {
@ {} => Nir::Open(NirEntity::TplApply(Some(qname)), ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplApply(None), cspan.into()),
@ {} => Nir::Open(NirEntity::TplApplyShort(qname), ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplApplyShort(qname), cspan.into()),
// Streaming attribute parsing;
// this takes precedence over any attribute parsing above
@ -1727,10 +1725,10 @@ ele_parse! {
// TODO: This simply _ignores_ the namespace prefix.
// If it's not a useful construct,
// it ought to be rejected.
NirEntity::TplParam(Some((
NirEntity::TplParamShort(
SPair(*name.local_name(), name_span),
SPair(value, value_span),
))),
),
name_span,
)
},

View File

@ -146,7 +146,7 @@ impl ParseState for TplShortDesugar {
//
// The name of the template _without_ the underscore padding is
// the local part of the QName.
(Scanning, Open(TplApply(Some(qname)), span)) => {
(Scanning, Open(TplApplyShort(qname), span)) => {
// TODO: Determine whether caching these has any notable
// benefit over repeated heap allocations,
// comparing packages with very few applications and
@ -157,24 +157,19 @@ impl ParseState for TplShortDesugar {
stack.push(Ref(SPair(tpl_name, span)));
Transition(DesugaringParams(span))
.ok(Open(TplApply(None), span))
Transition(DesugaringParams(span)).ok(Open(TplApply, span))
}
// Shorthand template params' names do not contain the
// surrounding `@`s.
(
DesugaringParams(ospan),
Open(TplParam(Some((name, val))), span),
) => {
(DesugaringParams(ospan), Open(TplParamShort(name, val), span)) => {
let pname = format!("@{name}@").intern();
// note: reversed (stack)
stack.push(Close(TplParam(None), span));
stack.push(Close(TplParam, span));
stack.push(Text(val));
stack.push(BindIdent(SPair(pname, name.span())));
Transition(DesugaringParams(ospan))
.ok(Open(TplParam(None), span))
Transition(DesugaringParams(ospan)).ok(Open(TplParam, span))
}
// A child element while we're desugaring template params
@ -204,19 +199,19 @@ impl ParseState for TplShortDesugar {
// Application ends here,
// and the new template (above) will absorb both this
// token `tok` and all tokens that come after.
stack.push(Close(TplApply(None), ospan));
stack.push(Close(TplParam(None), ospan));
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)));
Transition(DesugaringBody).ok(Open(TplParam(None), ospan))
Transition(DesugaringBody).ok(Open(TplParam, ospan))
}
(DesugaringBody, Close(TplApply(_), span)) => {
(DesugaringBody, Close(TplApplyShort(_), span)) => {
Transition(Scanning).ok(Close(Tpl, span))
}
(DesugaringParams(_), tok @ Close(TplApply(_), _)) => {
Transition(Scanning).ok(tok)
(DesugaringParams(_), Close(TplApplyShort(_), span)) => {
Transition(Scanning).ok(Close(TplApply, span))
}
// Any tokens that we don't recognize will be passed on unchanged.

View File

@ -30,12 +30,15 @@ fn desugars_nullary() {
let qname = ("t", "tpl-name").unwrap_into();
let tpl = "_tpl-name_".into();
let toks = [Open(TplApply(Some(qname)), S1), Close(TplApply(None), S2)];
let toks = [
Open(TplApplyShort(qname), S1),
Close(TplApplyShort(qname), S2),
];
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplApply(None), S1)),
O(Open(TplApply, S1)),
// The span associated with the name of the template to be
// applied is the span of the entire QName from NIR.
// The reason for this is that `t:foo` is transformed into
@ -45,7 +48,7 @@ fn desugars_nullary() {
// `foo` is not in itself a valid template name at the
// time of writing.
O(Ref(SPair(tpl, S1))),
O(Close(TplApply(None), S2)),
O(Close(TplApply, S2)),
]),
Sut::parse(toks.into_iter()).collect(),
);
@ -67,28 +70,28 @@ fn desugars_unary() {
#[rustfmt::skip]
let toks = vec![
// <t:qname
Open(TplApply(Some(qname)), S1),
Open(TplApplyShort(qname), S1),
// foo="foo value"
Open(TplParam(Some((aname, pval))), S2),
Open(TplParamShort(aname, pval), S2),
// Implicit close.
// />
Close(TplApply(None), S6),
Close(TplApplyShort(qname), S6),
];
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplApply(None), S1)),
O(Open(TplApply, S1)),
O(Ref(name)),
O(Open(TplParam(None), S2)),
O(Open(TplParam, S2)),
// Derived from `aname` (by padding)
O(BindIdent(pname)),
// The value is left untouched.
O(Text(pval)),
// Close is derived from open.
O(Close(TplParam(None), S2)),
O(Close(TplApply(None), S6)),
O(Close(TplParam, S2)),
O(Close(TplApply, S6)),
]),
Sut::parse(toks.into_iter()).collect(),
);
@ -104,7 +107,7 @@ fn desugars_body_into_tpl_with_ref_in_values_param() {
#[rustfmt::skip]
let toks = vec![
// <t:qname>
Open(TplApply(Some(qname)), S1),
Open(TplApplyShort(qname), S1),
// Body to desugar into own template (@values@).
Open(Sum, S2),
Open(Product, S3),
@ -115,7 +118,7 @@ fn desugars_body_into_tpl_with_ref_in_values_param() {
Open(Product, S6),
Close(Product, S7),
// </t:qname>
Close(TplApply(None), S8),
Close(TplApplyShort(qname), S8),
];
// The name of the generated template.
@ -127,16 +130,16 @@ fn desugars_body_into_tpl_with_ref_in_values_param() {
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplApply(None), S1)),
O(Open(TplApply, S1)),
O(Ref(name)),
// @values@ remains lexical by referencing the name of a
// template we're about to generate.
O(Open(TplParam(None), S1)),
O(Open(TplParam, S1)),
O(BindIdent(SPair(L_TPLP_VALUES, S1))),
O(Text(SPair(gen_name, S1))), //:-.
O(Close(TplParam(None), S1)), // |
O(Close(TplApply(None), S1)), // |
O(Close(TplParam, S1)), // |
O(Close(TplApply, S1)), // |
// |
// Generate a template to hold the // |
// body of `@values@`. // |
@ -168,14 +171,14 @@ fn does_not_desugar_long_form() {
#[rustfmt::skip]
let toks = [
Open(TplApply(None), S1),
Open(TplApply, S1),
BindIdent(name),
Open(TplParam(None), S3),
Open(TplParam, S3),
BindIdent(pname),
Text(pval),
Close(TplParam(None), S6),
Close(TplApply(None), S7),
Close(TplParam, S6),
Close(TplApply, S7),
];
#[rustfmt::skip]
@ -185,14 +188,14 @@ fn does_not_desugar_long_form() {
// So this is a duplicate of the above,
// mapped over `O`.
Ok(vec![
O(Open(TplApply(None), S1)),
O(Open(TplApply, S1)),
O(BindIdent(name)),
O(Open(TplParam(None), S3)),
O(Open(TplParam, S3)),
O(BindIdent(pname)),
O(Text(pval)),
O(Close(TplParam(None), S6)),
O(Close(TplApply(None), S7)),
O(Close(TplParam, S6)),
O(Close(TplApply, S7)),
]),
Sut::parse(toks.into_iter()).collect(),
);

View File

@ -813,9 +813,8 @@ macro_rules! ele_parse {
| CloseRecoverIgnore((qname, _, depth), _),
XirfToken::Close(_, span, tok_depth)
) if tok_depth == depth => {
$(
let $close_span = span;
)?
$(#[allow(unused_variables)] let $qname_matched = qname;)?
$(let $close_span = span;)?
$closemap.transition(Self(Closed(Some(qname), span.tag_span())))
},