tame/tamer/src/nir/tplshort/test.rs

255 lines
8.0 KiB
Rust

// Tests shorthand template application desugaring for NIR
//
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
//
// This file is part of TAME.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use crate::{convert::ExpectInto, span::dummy::*};
use Parsed::Object as O;
type Sut = TplShortDesugar;
#[test]
fn desugars_nullary() {
// Shorthand converts `t:tpl-name` into `_tpl-name_`.
let qname = ("t", "tpl-name").unwrap_into();
let tpl = "_tpl-name_".into();
let toks = [
Open(TplApplyShort(qname), S1),
Close(TplApplyShort(qname), S2),
];
#[rustfmt::skip]
assert_eq!(
Ok(vec![
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
// `_foo_`,
// and so the `t:` is a necessary part of the
// representation of the name of the template;
// `foo` is not in itself a valid template name at the
// time of writing.
O(Ref(SPair(tpl, S1))),
O(Close(TplApply, S2)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}
#[test]
fn desugars_unary() {
// Shorthand converts `t:tpl-name` into `_tpl-name_`.
let qname = ("t", "tpl-name").unwrap_into();
let name = SPair("_tpl-name_".into(), S1);
let aname = SPair("foo".into(), S3);
let pval = SPair("foo value".into(), S4);
// The attribute name gets padded with '@',
// much like the template does with underscores.
let pname = SPair("@foo@".into(), S3);
#[rustfmt::skip]
let toks = vec![
// <t:qname
Open(TplApplyShort(qname), S1),
// foo="foo value"
Open(TplParamShort(aname, pval), S2),
// Implicit close.
// />
Close(TplApplyShort(qname), S6),
];
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplApply, S1)),
O(Ref(name)),
O(Open(TplParam, S2)),
// Derived from `aname` (by padding)
O(BindIdentMeta(pname)),
// The value is left untouched.
O(Text(pval)),
// Close is derived from open.
O(Close(TplParam, S2)),
O(Close(TplApply, S6)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}
// Body of shorthand is desugared into `@values@` param.
#[test]
fn desugars_body_into_tpl_with_ref_in_values_param() {
// Shorthand converts `t:tpl-name` into `_tpl-name_`.
let qname = ("t", "short").unwrap_into();
let name = SPair("_short_".into(), S1);
#[rustfmt::skip]
let toks = vec![
// <t:qname>
Open(TplApplyShort(qname), S1),
// Body to desugar into own template (@values@).
Open(Sum, S2),
Open(Product, S3),
Close(Product, S4),
Close(Sum, S5),
// Body can contain siblings.
Open(Product, S6),
Close(Product, S7),
// </t:qname>
Close(TplApplyShort(qname), S8),
];
// The name of the generated template.
// This test is a bit too friendly with implementation details,
// but it does allow us to be perfectly precise in the output
// assertion.
let gen_name = gen_tpl_name_at_offset(S1);
let gen_desc = values_tpl_desc(name);
#[rustfmt::skip]
assert_eq!(
Ok(vec![
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, S1)),
O(BindIdentMeta(SPair(L_TPLP_VALUES, S1))),
O(Text(SPair(gen_name, S1))), //:-.
O(Close(TplParam, S1)), // |
O(Close(TplApply, S1)), // |
// |
// Generate a template to hold the // |
// body of `@values@`. // |
// It is closed and so expandable. // |
O(Open(Tpl, S1)), // /
O(BindIdent(SPair(gen_name, S1))), //<`
O(Desc(gen_desc)),
// And here we have the body of the above
// shorthand application.
O(Open(Sum, S2)),
O(Open(Product, S3)),
O(Close(Product, S4)),
O(Close(Sum, S5)),
O(Open(Product, S6)),
O(Close(Product, S7)),
O(Close(Tpl, S8)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}
// 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);
let gen_desc = values_tpl_desc(name_outer);
#[rustfmt::skip]
assert_eq!(
Ok(vec![
O(Open(TplApply, S1)),
O(Ref(name_outer)),
// @values@
O(Open(TplParam, S1)),
O(BindIdentMeta(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))), //<`
O(Desc(gen_desc)),
// 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() {
let name = SPair("_tpl-name_".into(), S2);
let pname = SPair("@param@".into(), S4);
let pval = SPair("value".into(), S5);
#[rustfmt::skip]
let toks = [
Open(TplApply, S1),
BindIdent(name),
Open(TplParam, S3),
BindIdentMeta(pname),
Text(pval),
Close(TplParam, S6),
Close(TplApply, S7),
];
#[rustfmt::skip]
assert_eq!(
// We avoid #[derive(Clone)] on Nir so that we have confidence that
// we're not doing anything suspicious with tokens.
// So this is a duplicate of the above,
// mapped over `O`.
Ok(vec![
O(Open(TplApply, S1)),
O(BindIdent(name)),
O(Open(TplParam, S3)),
O(BindIdentMeta(pname)),
O(Text(pval)),
O(Close(TplParam, S6)),
O(Close(TplApply, S7)),
]),
Sut::parse(toks.into_iter()).collect(),
);
}