tamer: asg: Shorthand and long-form template arguments
This applies to template application only; there's still some work to do for template parameters in definitions (well, for deriving them in `xmli` at least). And, as you can see, there's still a lot of TODO items here. I ended up backtracking on tree edges to Meta, and even on cross edges to Meta, because it complicated xmli derivation with no benefit right now; maybe a cross edge will be re-added in the future, but I need to move on and see where this takes me. But, it works. DEV-13708
parent
b2c6b7f073
commit
c26444666f
|
@ -31,6 +31,7 @@ use super::{
|
|||
AirExprAggregate,
|
||||
};
|
||||
use crate::{
|
||||
asg::graph::object::Meta,
|
||||
diagnose::Annotate,
|
||||
diagnostic_todo,
|
||||
fmt::{DisplayWrapper, TtQuote},
|
||||
|
@ -63,12 +64,26 @@ pub enum AirTplAggregate {
|
|||
/// which simplifies AIR generation.
|
||||
Ready(ObjectIndex<Pkg>),
|
||||
|
||||
/// Toplevel template context.
|
||||
///
|
||||
/// Conceptually,
|
||||
/// tokens that are received in this state are interpreted as directly
|
||||
/// applying to the template itself,
|
||||
/// or creating an object directly owned by the template.
|
||||
Toplevel(
|
||||
ObjectIndex<Pkg>,
|
||||
TplState,
|
||||
AirExprAggregateStoreDangling<Tpl>,
|
||||
),
|
||||
|
||||
/// Defining a template metavariable.
|
||||
TplMeta(
|
||||
ObjectIndex<Pkg>,
|
||||
TplState,
|
||||
AirExprAggregateStoreDangling<Tpl>,
|
||||
ObjectIndex<Meta>,
|
||||
),
|
||||
|
||||
/// Aggregating tokens into a template.
|
||||
TplExpr(
|
||||
ObjectIndex<Pkg>,
|
||||
|
@ -85,6 +100,10 @@ impl Display for AirTplAggregate {
|
|||
Self::Toplevel(_, tpl, expr) | Self::TplExpr(_, tpl, expr) => {
|
||||
write!(f, "building {tpl} with {expr}")
|
||||
}
|
||||
|
||||
Self::TplMeta(_, tpl, _, _) => {
|
||||
write!(f, "building {tpl} metavariable")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -202,17 +221,69 @@ impl ParseState for AirTplAggregate {
|
|||
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete()
|
||||
}
|
||||
|
||||
(Toplevel(..), tok @ AirTpl(TplMetaStart(..) | TplMetaEnd(..))) => {
|
||||
(Toplevel(oi_pkg, tpl, expr), AirTpl(TplMetaStart(span))) => {
|
||||
let oi_meta = asg.create(Meta::new_required(span));
|
||||
Transition(TplMeta(oi_pkg, tpl, expr, oi_meta)).incomplete()
|
||||
}
|
||||
(
|
||||
TplMeta(oi_pkg, tpl, expr, oi_meta),
|
||||
AirTpl(TplMetaEnd(cspan)),
|
||||
) => {
|
||||
oi_meta.close(asg, cspan);
|
||||
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete()
|
||||
}
|
||||
|
||||
(
|
||||
TplMeta(oi_pkg, tpl, expr, oi_meta),
|
||||
AirTpl(TplLexeme(lexeme)),
|
||||
) => Transition(TplMeta(
|
||||
oi_pkg,
|
||||
tpl,
|
||||
expr,
|
||||
oi_meta.assign_lexeme(asg, lexeme),
|
||||
))
|
||||
.incomplete(),
|
||||
|
||||
(TplMeta(oi_pkg, tpl, expr, oi_meta), AirBind(BindIdent(name))) => {
|
||||
oi_meta.identify_as_tpl_param(asg, tpl.oi(), name);
|
||||
Transition(TplMeta(oi_pkg, tpl, expr, oi_meta)).incomplete()
|
||||
}
|
||||
|
||||
(TplMeta(..), tok @ AirBind(RefIdent(..))) => {
|
||||
diagnostic_todo!(
|
||||
vec![tok.note("for this token")],
|
||||
"Toplevel meta"
|
||||
vec![tok.note("this token")],
|
||||
"AirBind in metavar context (param-value)"
|
||||
)
|
||||
}
|
||||
|
||||
(TplMeta(..), tok @ AirExpr(..)) => {
|
||||
diagnostic_todo!(
|
||||
vec![tok.note("this token")],
|
||||
"AirExpr in metavar context (e.g. @values@)"
|
||||
)
|
||||
}
|
||||
|
||||
(
|
||||
TplMeta(..),
|
||||
tok @ AirTpl(
|
||||
TplStart(..) | TplMetaStart(..) | TplEnd(..)
|
||||
| TplEndRef(..),
|
||||
),
|
||||
) => {
|
||||
diagnostic_todo!(vec![tok.note("this token")], "AirTpl variant")
|
||||
}
|
||||
|
||||
(Toplevel(..), tok @ AirTpl(TplMetaEnd(..))) => {
|
||||
diagnostic_todo!(
|
||||
vec![tok.note("this token")],
|
||||
"unbalanced meta"
|
||||
)
|
||||
}
|
||||
|
||||
(Toplevel(..), tok @ AirTpl(TplLexeme(..))) => {
|
||||
diagnostic_todo!(
|
||||
vec![tok.note("for this token")],
|
||||
"err: Toplevel lexeme {tok:?} (must be within metavar)"
|
||||
vec![tok.note("this token")],
|
||||
"err: TplLexeme outside of metavar"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::asg::{
|
|||
test::{asg_from_toks, parse_as_pkg_body},
|
||||
Air, AirAggregate,
|
||||
},
|
||||
graph::object::Meta,
|
||||
Expr, ExprOp, Ident,
|
||||
};
|
||||
use crate::span::dummy::*;
|
||||
|
@ -402,3 +403,48 @@ fn anonymous_tpl_immediate_ref() {
|
|||
|
||||
// TODO: More to come.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tpl_with_param() {
|
||||
let id_tpl = SPair("_tpl_".into(), S2);
|
||||
|
||||
let id_param1 = SPair("@param1@".into(), S4);
|
||||
let pval1 = SPair("value1".into(), S5);
|
||||
let id_param2 = SPair("@param2@".into(), S8);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
Air::TplStart(S1),
|
||||
Air::BindIdent(id_tpl),
|
||||
|
||||
// Metavariable with a value.
|
||||
Air::TplMetaStart(S3),
|
||||
Air::BindIdent(id_param1),
|
||||
Air::TplLexeme(pval1),
|
||||
Air::TplMetaEnd(S6),
|
||||
|
||||
// Required metavariable (no value).
|
||||
Air::TplMetaStart(S7),
|
||||
Air::BindIdent(id_param2),
|
||||
Air::TplMetaEnd(S9),
|
||||
Air::TplEnd(S10),
|
||||
];
|
||||
|
||||
let asg = asg_from_toks(toks);
|
||||
let oi_tpl = asg.expect_ident_oi::<Tpl>(id_tpl);
|
||||
|
||||
// The template should have an edge to each identifier for each
|
||||
// metavariable.
|
||||
let params = [id_param1, id_param2]
|
||||
.iter()
|
||||
.map(|id| {
|
||||
oi_tpl
|
||||
.lookup_local_linear(&asg, *id)
|
||||
.and_then(|oi| oi.edges_filtered::<Meta>(&asg).next())
|
||||
.map(ObjectIndex::cresolve(&asg))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(params[0], Some(&Meta::Lexeme(S3.merge(S6).unwrap(), pval1)));
|
||||
assert_eq!(params[1], Some(&Meta::Required(S7.merge(S9).unwrap())));
|
||||
}
|
||||
|
|
|
@ -640,13 +640,8 @@ impl Asg {
|
|||
ident: SPair,
|
||||
) -> ObjectIndex<O> {
|
||||
self.get_ident_oi(ident).diagnostic_expect(
|
||||
|| diagnostic_opaque_ident_desc(ident),
|
||||
|| {
|
||||
format!(
|
||||
"opaque identifier: {} has no object binding",
|
||||
TtQuote::wrap(ident),
|
||||
)
|
||||
},
|
||||
|| diagnostic_unknown_ident_desc(ident),
|
||||
|| format!("unknown identifier {}", TtQuote::wrap(ident),),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -828,5 +823,15 @@ fn diagnostic_opaque_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
|||
]
|
||||
}
|
||||
|
||||
fn diagnostic_unknown_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
||||
vec![
|
||||
ident.internal_error("reference to an unknown identifier"),
|
||||
ident.help(
|
||||
"the system expects this identifier to be known, \
|
||||
but it could not be found.",
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
|
|
@ -768,6 +768,25 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
|||
asg.create(Ident::declare(name))
|
||||
.add_edge_from(asg, *self, None)
|
||||
}
|
||||
|
||||
/// Retrieve the identifier for this object,
|
||||
/// if any.
|
||||
///
|
||||
/// If there is more than one identifier,
|
||||
/// only one will be returned,
|
||||
/// and the result of the operation is undefined.
|
||||
/// This can be problematic if certain optimizations have been performed
|
||||
/// on the graph,
|
||||
/// like common subexpression elimination,
|
||||
/// in which case it's best not to rely on following edges in reverse.
|
||||
pub fn ident<'a>(&self, asg: &'a Asg) -> Option<&'a Ident>
|
||||
where
|
||||
O: ObjectRelFrom<Ident>,
|
||||
{
|
||||
self.incoming_edges_filtered(asg)
|
||||
.map(ObjectIndex::cresolve(asg))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectIndex<Object> {
|
||||
|
|
|
@ -249,12 +249,4 @@ impl ObjectIndex<Expr> {
|
|||
let identi = asg.lookup_global_or_missing(ident);
|
||||
self.add_edge_to(asg, identi, Some(ident.span()))
|
||||
}
|
||||
|
||||
/// The [`Ident`] bound to this expression,
|
||||
/// if any.
|
||||
pub fn ident(self, asg: &Asg) -> Option<&Ident> {
|
||||
self.incoming_edges_filtered(asg)
|
||||
.map(ObjectIndex::cresolve(asg))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -982,9 +982,7 @@ object_rel! {
|
|||
tree Ident,
|
||||
tree Expr,
|
||||
tree Tpl,
|
||||
|
||||
// A metavariable is directly referenced by a template.
|
||||
cross Meta,
|
||||
tree Meta,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1060,6 +1058,14 @@ impl ObjectIndex<Ident> {
|
|||
self.edges(asg).find_map(ObjectRel::narrow) == Some(oi)
|
||||
}
|
||||
|
||||
/// Whether this identifier is bound to an object of kind `O`.
|
||||
///
|
||||
/// To bind an identifier,
|
||||
/// see [`Self::bind_definition`].
|
||||
pub fn is_bound_to_kind<O: ObjectRelFrom<Ident>>(&self, asg: &Asg) -> bool {
|
||||
self.edges_filtered::<O>(asg).next().is_some()
|
||||
}
|
||||
|
||||
/// The source package defining this identifier,
|
||||
/// if known.
|
||||
pub fn src_pkg(&self, asg: &Asg) -> Option<ObjectIndex<Pkg>> {
|
||||
|
|
|
@ -26,11 +26,15 @@
|
|||
|
||||
use super::{
|
||||
Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
||||
ObjectRelatable,
|
||||
ObjectRelatable, Tpl,
|
||||
};
|
||||
use crate::{
|
||||
asg::Asg,
|
||||
diagnose::Annotate,
|
||||
diagnostic_todo,
|
||||
f::Functor,
|
||||
fmt::{DisplayWrapper, TtQuote},
|
||||
parse::{util::SPair, Token},
|
||||
parse::util::SPair,
|
||||
span::Span,
|
||||
};
|
||||
use std::fmt::Display;
|
||||
|
@ -51,16 +55,54 @@ use std::fmt::Display;
|
|||
pub enum Meta {
|
||||
Required(Span),
|
||||
ConcatList(Span),
|
||||
Lexeme(SPair),
|
||||
Lexeme(Span, SPair),
|
||||
}
|
||||
|
||||
impl Meta {
|
||||
/// Create a new metavariable without a value.
|
||||
///
|
||||
/// Metavariables with no value cannot be used in an expansion context.
|
||||
/// Intuitively,
|
||||
/// they act as required parameters.
|
||||
pub fn new_required(span: Span) -> Self {
|
||||
Self::Required(span)
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
Self::Required(span) | Self::ConcatList(span) => *span,
|
||||
Self::Lexeme(spair) => spair.span(),
|
||||
Self::Required(span)
|
||||
| Self::ConcatList(span)
|
||||
| Self::Lexeme(span, _) => *span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Assign a lexeme to a metavariable.
|
||||
///
|
||||
/// In a template definition context,
|
||||
/// this acts as a default value for this metavariable.
|
||||
/// In an application context,
|
||||
/// this has the effect of binding a value to this metavariable.
|
||||
pub fn assign_lexeme(self, lexeme: SPair) -> Self {
|
||||
match self {
|
||||
Self::Required(span) => Self::Lexeme(span, lexeme),
|
||||
|
||||
Self::ConcatList(_) => diagnostic_todo!(
|
||||
vec![lexeme.note("while parsing this lexeme")],
|
||||
"append to ConcatList",
|
||||
),
|
||||
|
||||
Self::Lexeme(_, _) => diagnostic_todo!(
|
||||
vec![lexeme.note("while parsing this lexeme")],
|
||||
"Lexeme => ConcatList",
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Meta> for Span {
|
||||
fn from(meta: &Meta) -> Self {
|
||||
meta.span()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Meta {
|
||||
|
@ -72,7 +114,19 @@ impl Display for Meta {
|
|||
Self::ConcatList(_) => {
|
||||
write!(f, "metasyntactic concatenation list")
|
||||
}
|
||||
Self::Lexeme(spair) => write!(f, "lexeme {}", TtQuote::wrap(spair)),
|
||||
Self::Lexeme(_, spair) => {
|
||||
write!(f, "lexeme {}", TtQuote::wrap(spair))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Functor<Span> for Meta {
|
||||
fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target {
|
||||
match self {
|
||||
Self::Required(span) => Self::Required(f(span)),
|
||||
Self::ConcatList(span) => Self::ConcatList(f(span)),
|
||||
Self::Lexeme(span, spair) => Self::Lexeme(f(span), spair),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,3 +139,28 @@ object_rel! {
|
|||
cross Ident,
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectIndex<Meta> {
|
||||
pub fn identify_as_tpl_param(
|
||||
&self,
|
||||
asg: &mut Asg,
|
||||
oi_tpl: ObjectIndex<Tpl>,
|
||||
name: SPair,
|
||||
) -> ObjectIndex<Ident> {
|
||||
oi_tpl
|
||||
.declare_local(asg, name)
|
||||
.add_edge_to(asg, *self, None)
|
||||
}
|
||||
|
||||
pub fn assign_lexeme(self, asg: &mut Asg, lexeme: SPair) -> Self {
|
||||
self.map_obj(asg, |meta| meta.assign_lexeme(lexeme))
|
||||
}
|
||||
|
||||
pub fn close(self, asg: &mut Asg, close_span: Span) -> Self {
|
||||
self.map_obj(asg, |meta| {
|
||||
meta.map(|open_span| {
|
||||
open_span.merge(close_span).unwrap_or(open_span)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,8 +22,8 @@
|
|||
use std::fmt::Display;
|
||||
|
||||
use super::{
|
||||
Expr, Ident, Meta, Object, ObjectIndex, ObjectRel, ObjectRelFrom,
|
||||
ObjectRelTo, ObjectRelTy, ObjectRelatable,
|
||||
Expr, Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTo,
|
||||
ObjectRelTy, ObjectRelatable,
|
||||
};
|
||||
use crate::{
|
||||
asg::Asg,
|
||||
|
@ -66,10 +66,13 @@ object_rel! {
|
|||
/// Templates may expand into nearly any context,
|
||||
/// and must therefore be able to contain just about anything.
|
||||
Tpl -> {
|
||||
// Expressions must be able to be anonymous to allow templates in
|
||||
// any `Expr` context.
|
||||
tree Expr,
|
||||
tree Meta,
|
||||
|
||||
dyn Ident,
|
||||
// Identifiers are used for both references and identifiers that
|
||||
// will expand into an application site.
|
||||
dyn Ident,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,13 @@ use super::object::ObjectRel;
|
|||
/// may modify the graph beyond recognition,
|
||||
/// though they should retain ordering where it is important.
|
||||
///
|
||||
/// _Objects that do not have a path from the root will not be visited by
|
||||
/// this traversal._
|
||||
/// These objects are expected to act as additional metadata,
|
||||
/// and must be queried for explicitly.
|
||||
/// Such querying can be done during the traversal since this visitor holds
|
||||
/// only a shared immutable reference to the [`Asg`].
|
||||
///
|
||||
/// For more information,
|
||||
/// see [`ObjectRel::is_cross_edge`].
|
||||
///
|
||||
|
|
|
@ -196,6 +196,8 @@ fn traverses_ontological_tree_tpl_apply() {
|
|||
let name_tpl = "_tpl-to-apply_".into();
|
||||
let id_tpl = SPair(name_tpl, S3);
|
||||
let ref_tpl = SPair(name_tpl, S6);
|
||||
let id_param = SPair("@param@".into(), S8);
|
||||
let value_param = SPair("value".into(), S9);
|
||||
|
||||
#[rustfmt::skip]
|
||||
let toks = vec![
|
||||
|
@ -213,8 +215,13 @@ fn traverses_ontological_tree_tpl_apply() {
|
|||
// metavariables.
|
||||
TplStart(S5),
|
||||
RefIdent(ref_tpl),
|
||||
TplEndRef(S7), // notice the `Ref` at the end
|
||||
PkgEnd(S8),
|
||||
|
||||
TplMetaStart(S7),
|
||||
BindIdent(id_param),
|
||||
TplLexeme(value_param),
|
||||
TplMetaEnd(S10),
|
||||
TplEndRef(S11), // notice the `Ref` at the end
|
||||
PkgEnd(S12),
|
||||
];
|
||||
|
||||
// We need more concise expressions for the below table of values.
|
||||
|
@ -225,11 +232,13 @@ fn traverses_ontological_tree_tpl_apply() {
|
|||
assert_eq!(
|
||||
// A -|-> B | A span -|-> B span | espan | depth
|
||||
vec![//-----|-------|-----------|-----------|--------|-----------------
|
||||
(d(Root, Pkg, SU, m(S1, S8), None ), Depth(1)),
|
||||
(d(Pkg, Ident, m(S1, S8), S3, None ), Depth(2)),
|
||||
(d(Root, Pkg, SU, m(S1, S12), None ), Depth(1)),
|
||||
(d(Pkg, Ident, m(S1, S12), S3, None ), Depth(2)),
|
||||
(d(Ident, Tpl, S3, m(S2, S4), None ), Depth(3)),
|
||||
(d(Pkg, Tpl, m(S1, S8), m(S5, S7), None ), Depth(2)),
|
||||
(d(Tpl, Ident, m(S5, S7), S3, Some(S6)), Depth(3)),
|
||||
(d(Pkg, Tpl, m(S1, S12), m(S5, S11), None ), Depth(2)),
|
||||
/*cross*/ (d(Tpl, Ident, m(S5, S11), S3, Some(S6)), Depth(3)),
|
||||
(d(Tpl, Ident, m(S5, S11), S8, None ), Depth(3)),
|
||||
(d(Ident, Meta, S8, m(S7, S10), None ), Depth(4)),
|
||||
],
|
||||
tree_reconstruction_report(toks),
|
||||
);
|
||||
|
|
|
@ -30,8 +30,8 @@
|
|||
//! or observing template expansions.
|
||||
|
||||
use super::object::{
|
||||
DynObjectRel, Expr, Object, ObjectIndex, ObjectRelTy, OiPairObjectInner,
|
||||
Pkg, Tpl,
|
||||
DynObjectRel, Expr, Meta, Object, ObjectIndex, ObjectRelTy,
|
||||
OiPairObjectInner, Pkg, Tpl,
|
||||
};
|
||||
use crate::{
|
||||
asg::{
|
||||
|
@ -39,7 +39,8 @@ use crate::{
|
|||
Asg, ExprOp, Ident,
|
||||
},
|
||||
diagnose::{panic::DiagnosticPanic, Annotate},
|
||||
diagnostic_panic, diagnostic_unreachable,
|
||||
diagnostic_panic, diagnostic_todo, diagnostic_unreachable,
|
||||
fmt::{DisplayWrapper, TtQuote},
|
||||
parse::{prelude::*, util::SPair, Transitionable},
|
||||
span::{Span, UNKNOWN_SPAN},
|
||||
sym::{
|
||||
|
@ -198,7 +199,9 @@ impl<'a> TreeContext<'a> {
|
|||
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
|
||||
}
|
||||
|
||||
target @ Object::Meta(..) => todo!("Object::Meta: {target:?}"),
|
||||
Object::Meta((meta, oi_meta)) => {
|
||||
self.emit_tpl_arg(meta, *oi_meta, depth)
|
||||
}
|
||||
|
||||
Object::Root(..) => diagnostic_unreachable!(
|
||||
vec![],
|
||||
|
@ -312,7 +315,11 @@ impl<'a> TreeContext<'a> {
|
|||
// This also gives us the opportunity to make sure that
|
||||
// we're deriving something that's actually supported by the
|
||||
// XSLT-based compiler.
|
||||
let mut idents = oi_tpl.edges_filtered::<Ident>(self.asg);
|
||||
// TODO: This doesn't handle `Missing` or invalid (non-`Tpl`)
|
||||
// identifiers.
|
||||
let mut idents = oi_tpl
|
||||
.edges_filtered::<Ident>(self.asg)
|
||||
.filter(|oi| oi.is_bound_to_kind::<Tpl>(self.asg));
|
||||
|
||||
let apply_tpl = idents.next().diagnostic_expect(
|
||||
|| {
|
||||
|
@ -332,7 +339,7 @@ impl<'a> TreeContext<'a> {
|
|||
bad_ident
|
||||
.internal_error("unexpected second identifier"),
|
||||
],
|
||||
"expected only one Ident for template application",
|
||||
"expected only one Ident->Tpl for template application",
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -349,6 +356,47 @@ impl<'a> TreeContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Emit a long-form template argument.
|
||||
///
|
||||
/// For the parent template application,
|
||||
/// see [`Self::emit_template`].
|
||||
fn emit_tpl_arg(
|
||||
&mut self,
|
||||
meta: &Meta,
|
||||
oi_meta: ObjectIndex<Meta>,
|
||||
depth: Depth,
|
||||
) -> Option<Xirf> {
|
||||
let pname = oi_meta.ident(self.asg).map(Ident::name)
|
||||
.diagnostic_unwrap(|| vec![meta.internal_error(
|
||||
"anonymous metavariables are not supported as template arguments"
|
||||
)]);
|
||||
|
||||
let pval = match meta {
|
||||
Meta::Required(span) => diagnostic_todo!(
|
||||
vec![span.error("value expected for this param")],
|
||||
"value missing for param {}",
|
||||
TtQuote::wrap(pname)
|
||||
),
|
||||
|
||||
Meta::ConcatList(span) => diagnostic_todo!(
|
||||
vec![span.error("concatenation occurs here")],
|
||||
"concatenation not yet supported in xmli for param {}",
|
||||
TtQuote::wrap(pname)
|
||||
),
|
||||
|
||||
Meta::Lexeme(_, value) => *value,
|
||||
};
|
||||
|
||||
self.push(attr_value(pval));
|
||||
self.push(attr_name(pname));
|
||||
|
||||
Some(Xirf::open(
|
||||
QN_WITH_PARAM,
|
||||
OpenSpan::without_name_span(meta.span()),
|
||||
depth,
|
||||
))
|
||||
}
|
||||
|
||||
fn push(&mut self, tok: Xirf) {
|
||||
if self.stack.is_full() {
|
||||
diagnostic_panic!(
|
||||
|
@ -408,6 +456,10 @@ fn attr_name(name: SPair) -> Xirf {
|
|||
Xirf::attr(QN_NAME, name, (name.span(), name.span()))
|
||||
}
|
||||
|
||||
fn attr_value(val: SPair) -> Xirf {
|
||||
Xirf::attr(QN_VALUE, val, (val.span(), val.span()))
|
||||
}
|
||||
|
||||
fn expr_ele(expr: &Expr, depth: Depth) -> Xirf {
|
||||
use ExprOp::*;
|
||||
|
||||
|
|
|
@ -1674,21 +1674,34 @@ ele_parse! {
|
|||
/// or even a mix of the two
|
||||
/// (with statements hoisted out of expressions).
|
||||
///
|
||||
/// TODO: This is apparently unused by the current system,
|
||||
/// in favor of a transition to [`TplApplyShort`],
|
||||
/// but this is still needed to support dynamic template application
|
||||
/// (templates whose names are derived from other template inputs).
|
||||
/// See also [`TplApplyShort`],
|
||||
/// which gets desugared into this via [`super::tplshort`].
|
||||
ApplyTemplate := QN_APPLY_TEMPLATE(_, ospan) {
|
||||
@ {
|
||||
QN_NAME => Ref,
|
||||
} => Nir::Open(NirEntity::TplApply(None), ospan.into()),
|
||||
/(cspan) => Nir::Close(NirEntity::TplApply(None), cspan.into()),
|
||||
|
||||
// TODO: This is wrong, we just need something here for now.
|
||||
AnyStmtOrExpr,
|
||||
ApplyTemplateParam,
|
||||
};
|
||||
|
||||
/// Short-hand template application.
|
||||
/// Long-form template argument.
|
||||
///
|
||||
/// Template arguments are lexical.
|
||||
///
|
||||
/// See also [`TplApplyShort`],
|
||||
/// which gets desugared into this via [`super::tplshort`].
|
||||
ApplyTemplateParam := QN_WITH_PARAM(_, ospan) {
|
||||
@ {
|
||||
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@
|
||||
};
|
||||
|
||||
/// Shorthand template application.
|
||||
///
|
||||
/// This expands into an equivalent [`ApplyTemplate`] form where each
|
||||
/// attribute is a template argument,
|
||||
|
|
|
@ -710,6 +710,7 @@ pub mod st {
|
|||
L_VIRTUAL: cid "virtual",
|
||||
L_WARNING: cid "warning",
|
||||
L_WHEN: cid "when",
|
||||
L_WITH_PARAM: tid "with-param",
|
||||
L_WORKSHEET: cid "worksheet",
|
||||
L_XMLNS: cid "xmlns",
|
||||
L_YIELD: cid "yield",
|
||||
|
|
|
@ -225,6 +225,7 @@ pub mod qname {
|
|||
QN_VALUES: :L_VALUES,
|
||||
QN_VIRTUAL: :L_VIRTUAL,
|
||||
QN_WARNING: :L_WARNING,
|
||||
QN_WITH_PARAM: :L_WITH_PARAM,
|
||||
QN_WORKSHEET: :L_WORKSHEET,
|
||||
QN_YIELD: :L_YIELD,
|
||||
QN_YIELDS: :L_YIELDS,
|
||||
|
|
|
@ -66,6 +66,19 @@
|
|||
|
||||
|
||||
|
||||
<template name="_short-hand-nullary_" />
|
||||
<apply-template name="_short-hand-nullary_" />
|
||||
|
||||
<template name="_short-hand-unary_" />
|
||||
<apply-template name="_short-hand-unary_">
|
||||
<with-param name="@foo@" value="bar" />
|
||||
</apply-template>
|
||||
|
||||
<template name="_short-hand-nary_" />
|
||||
<apply-template name="_short-hand-nary_">
|
||||
<with-param name="@foo@" value="bar" />
|
||||
<with-param name="@bar@" value="baz" />
|
||||
<with-param name="@baz@" value="quux" />
|
||||
</apply-template>
|
||||
</package>
|
||||
|
||||
|
|
|
@ -66,8 +66,20 @@
|
|||
`apply-template` form,
|
||||
asserting their equivalency.
|
||||
|
||||
<template name="_short-hand-nullary_" />
|
||||
<t:short-hand-nullary />
|
||||
|
||||
<template name="_short-hand-unary_" />
|
||||
<t:short-hand-unary foo="bar" />
|
||||
|
||||
|
||||
|
||||
<template name="_short-hand-nary_" />
|
||||
<t:short-hand-nary foo="bar" bar="baz" baz="quux" />
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- TODO
|
||||
<t:short-hand-nullary-body>
|
||||
<c:sum />
|
||||
|
@ -77,8 +89,6 @@
|
|||
<t:inner-short />
|
||||
</t:short-hand-nullary-inner>
|
||||
|
||||
<t:short-hand foo="bar" />
|
||||
|
||||
<t:short-hand foo="bar">
|
||||
<c:sum />
|
||||
</t:short-hand>
|
||||
|
|
Loading…
Reference in New Issue