tamer: nir::tplshort: Desugar nested template applications

I'm happy with how this ended up turning out---I was able to accomplish this
without having to introduce any additional state to the parser (I _removed_
a state, actually) by tweaking NIR a bit in a previous commit.

We can't update the system test yet, though, because nested templates are
not yet supported by asg::air::tpl; that'll come next.  If you try, you'll
be greeted with this error presently (which is worth showing since you'll
never see it unless you're hacking TAMER):

,=====[ ./tests/xmli/template/ logs ]======
|
| thread 'main' panicked at 'not yet implemented: internal error:
| note: nested tpl open
|    --> ./tests/xmli/template/src.xml:129:5
|     |
| 129 |     <t:inner-short />
|     |     -------------- note: for this template
|
|
| !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!!
| !!!        THIS IS AN UNFINISHED FEATURE IN TAMER         !!!
| !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!!
| !!! This message means that TAMER has encountered an      !!!
| !!! unrecoverable error that forced it to terminate       !!!
| !!! processing.                                           !!!
| !!!                                                       !!!
| !!! TAMER has attempted to provide you with contextual    !!!
| !!! information above that might allow you to work around !!!
| !!! this problem until it can be fixed.                   !!!
| !!!                                                       !!!
| !!! Please report this error, including the above         !!!
| !!! diagnostic output beginning with 'internal error:'.   !!!
| !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!!
| ', src/asg/air/tpl.rs:207:55
| note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
| Command exited with non-zero status 101
| 0/165fault 0/8io 3528rss 14/2ctx
| /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml
|
`====[ end ./tests/xmli/template/ logs ]====

DEV-13708
main
Mike Gerwitz 2023-03-23 22:43:55 -04:00
parent 6581c9946c
commit e1c8e371d5
2 changed files with 73 additions and 11 deletions

View File

@ -19,6 +19,8 @@
//! Shorthand template application desugaring for NIR.
//!
//! Desugaring is performed by [`TplShortDesugar`].
//!
//! A shorthand template application looks something like this,
//! in XML form:
//!
@ -90,21 +92,27 @@ use std::convert::Infallible;
use Nir::*;
use NirEntity::*;
/// Desugar shorthand template applications into long-form template
/// applications.
///
/// This parser is able to handle nested applications without having to
/// track nesting by taking advantage of the parsing that NIR has already
/// performed.
///
/// See the [module-level documentation](super) for more information.
#[derive(Debug, PartialEq, Eq, Default)]
pub enum TplShortDesugar {
/// Waiting for shorthand template application,
/// passing tokens along in the meantime.
///
/// This state is also used when parsing the body of a shorthand
/// template application.
#[default]
Scanning,
/// A shorthand template application associated with the provided
/// [`Span`] was encountered and shorthand params are being desugared.
DesugaringParams(Span),
/// A child element was encountered while desugaring params,
/// indicating a body of the shorthand application that needs
/// desugaring into `@values@`.
DesugaringBody,
}
impl Display for TplShortDesugar {
@ -116,9 +124,6 @@ impl Display for TplShortDesugar {
Self::DesugaringParams(_) => {
write!(f, "desugaring shorthand template application params")
}
Self::DesugaringBody => {
write!(f, "desugaring shorthand template application body")
}
}
}
}
@ -192,7 +197,6 @@ impl ParseState for TplShortDesugar {
// yet-to-be-defined means.
//
// note: reversed (stack)
stack.push(tok);
stack.push(BindIdent(SPair(gen_name, ospan)));
stack.push(Open(Tpl, ospan));
@ -203,13 +207,23 @@ impl ParseState for TplShortDesugar {
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, ospan))
// Note that we must have `tok` as lookahead instead of
// pushing directly on the stack in case it's a
// `TplApplyShort`.
Transition(Scanning)
.ok(Open(TplParam, ospan))
.with_lookahead(tok)
}
(DesugaringBody, Close(TplApplyShort(_), span)) => {
// If we're scanning and find a closing shorthand application,
// then we must have just finished with a shorthand body.
(Scanning, Close(TplApplyShort(_), span)) => {
Transition(Scanning).ok(Close(Tpl, span))
}
// If we complete the shorthand template during param parsing,
// then we have no body and must close the application.
(DesugaringParams(_), Close(TplApplyShort(_), span)) => {
Transition(Scanning).ok(Close(TplApply, span))
}

View File

@ -162,6 +162,54 @@ fn desugars_body_into_tpl_with_ref_in_values_param() {
);
}
// Shorthand within a shorthand.
#[test]
fn desugar_nested_apply() {
let qname_outer = ("t", "outer").unwrap_into();
let name_outer = SPair("_outer_".into(), S1);
let qname_inner = ("t", "inner").unwrap_into();
let name_inner = SPair("_inner_".into(), S2);
#[rustfmt::skip]
let toks = vec![
Open(TplApplyShort(qname_outer), S1),
// Body is a second shorthand template application.
Open(TplApplyShort(qname_inner), S2),
Close(TplApplyShort(qname_inner), S3),
Close(TplApplyShort(qname_outer), S4),
];
let gen_name_outer = gen_tpl_name_at_offset(S1);
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplApply, S1)),
O(Ref(name_outer)),
// @values@
O(Open(TplParam, S1)),
O(BindIdent(SPair(L_TPLP_VALUES, S1))),
O(Text(SPair(gen_name_outer, S1))), //:-.
O(Close(TplParam, S1)), // |
O(Close(TplApply, S1)), // |
// |
O(Open(Tpl, S1)), // /
O(BindIdent(SPair(gen_name_outer, S1))), //<`
// And within this template,
// we generate another application.
// This one has no body and so no `@values@`.
O(Open(TplApply, S2)),
O(Ref(name_inner)),
O(Close(TplApply, S3)),
O(Close(Tpl, S4)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}
// Don't parse what we desugar into!
#[test]
fn does_not_desugar_long_form() {