tamer: Initial template/param support through xmli

This introduces template/param and regenerates it in the xmli output.  Note
that this does not check that applications reference known params; that's a
later phase.

DEV-13163
main
Mike Gerwitz 2023-06-14 16:38:05 -04:00
parent 9887abd037
commit d10bf00f5d
11 changed files with 149 additions and 21 deletions

View File

@ -229,7 +229,8 @@ impl ParseState for AirAggregate {
}
(PkgMeta(meta), AirMeta(mtok)) => ctx.proxy(meta, mtok),
(PkgMeta(meta), AirBind(mtok)) => ctx.proxy(meta, mtok),
(PkgMeta(meta), tok @ (AirExpr(..) | AirTpl(..) | AirDoc(..))) => {
(PkgMeta(meta), AirDoc(mtok)) => ctx.proxy(meta, mtok),
(PkgMeta(meta), tok @ (AirExpr(..) | AirTpl(..))) => {
ctx.try_ret_with_lookahead(meta, tok)
}

View File

@ -854,7 +854,7 @@ sum_ir! {
pub sum enum AirBindableTpl = AirTpl | AirBind | AirDoc;
/// Tokens that may be used to define metavariables.
pub sum enum AirBindableMeta = AirMeta | AirBind;
pub sum enum AirBindableMeta = AirMeta | AirBind | AirDoc;
}
impl AirBind {

View File

@ -64,7 +64,7 @@ impl ParseState for AirMetaAggregate {
tok: Self::Token,
ctx: &mut Self::Context,
) -> TransitionResult<Self::Super> {
use super::ir::{AirBind::*, AirMeta::*};
use super::ir::{AirBind::*, AirDoc::*, AirMeta::*};
use AirBindableMeta::*;
use AirMetaAggregate::*;
@ -91,6 +91,19 @@ impl ParseState for AirMetaAggregate {
.map(|_| ())
.transition(TplMeta(oi_meta)),
(TplMeta(oi_meta), AirDoc(DocIndepClause(clause))) => {
oi_meta.desc_short(ctx.asg_mut(), clause);
Transition(TplMeta(oi_meta)).incomplete()
}
(TplMeta(..), tok @ AirDoc(DocText(..))) => {
diagnostic_todo!(
vec![tok.note("this token")],
"AirDoc in metavar context \
(is this something we want to support?)"
)
}
(TplMeta(..), tok @ AirBind(RefIdent(..))) => {
diagnostic_todo!(
vec![tok.note("this token")],
@ -119,8 +132,10 @@ impl ParseState for AirMetaAggregate {
)
}
// Maybe the bind can be handled by the parent frame.
(Ready, tok @ AirBind(..)) => Transition(Ready).dead(tok),
// Maybe the token can be handled by the parent frame.
(Ready, tok @ (AirBind(..) | AirDoc(..))) => {
Transition(Ready).dead(tok)
}
}
}

View File

@ -462,6 +462,7 @@ fn tpl_with_param() {
let id_param1 = SPair("@param1@".into(), S4);
let pval1 = SPair("value1".into(), S5);
let id_param2 = SPair("@param2@".into(), S8);
let param_desc = SPair("param desc".into(), S9);
#[rustfmt::skip]
let toks = vec![
@ -477,8 +478,9 @@ fn tpl_with_param() {
// Required metavariable (no value).
Air::MetaStart(S7),
Air::BindIdent(id_param2),
Air::MetaEnd(S9),
Air::TplEnd(S10),
Air::DocIndepClause(param_desc),
Air::MetaEnd(S10),
Air::TplEnd(S11),
];
let ctx = air_ctx_from_pkg_body_toks(toks);
@ -494,12 +496,28 @@ fn tpl_with_param() {
oi_tpl
.lookup_local_linear(&asg, *id)
.and_then(|oi| oi.edges_filtered::<Meta>(&asg).next())
.map(ObjectIndex::cresolve(&asg))
.map(|oi| (oi, oi.resolve(&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())));
assert_eq!(
params[0].unwrap().1,
&Meta::Lexeme(S3.merge(S6).unwrap(), pval1)
);
assert_eq!(
params[1].unwrap().1,
&Meta::Required(S7.merge(S10).unwrap())
);
// The second param should have a description.
let doc = params[1]
.unwrap()
.0
.edges_filtered::<Doc>(&asg)
.last()
.map(ObjectIndex::cresolve(&asg));
assert_eq!(doc, Some(&Doc::new_indep_clause(param_desc)));
}
// A template definition nested within another creates a

View File

@ -24,7 +24,7 @@
//! have historically been a feature of the template system.
//! The canonical metavariable is the template parameter.
use super::{prelude::*, Ident};
use super::{prelude::*, Doc, Ident};
use crate::{
diagnose::Annotate,
diagnostic_todo,
@ -133,6 +133,9 @@ object_rel! {
Meta -> {
tree Meta, // TODO: do we need tree?
cross Ident,
// e.g. template paramater description.
tree Doc,
}
}

View File

@ -154,6 +154,10 @@ type TokenStack = ArrayVec<Xirf, TOK_STACK_SIZE>;
pub struct TreeContext<'a> {
stack: TokenStack,
asg: &'a Asg,
/// Whether the most recently encountered template has been interpreted
/// as an application.
tpl_apply: Option<ObjectIndex<Tpl>>,
}
impl<'a> TreeContext<'a> {
@ -210,9 +214,10 @@ impl<'a> TreeContext<'a> {
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
}
Object::Meta((meta, oi_meta)) => {
self.emit_tpl_arg(meta, *oi_meta, depth)
}
Object::Meta((meta, oi_meta)) => match self.tpl_apply {
Some(_) => self.emit_tpl_arg(meta, *oi_meta, depth),
None => self.emit_tpl_param(meta, *oi_meta, depth),
},
Object::Doc((doc, oi_doc)) => {
self.emit_doc(doc, *oi_doc, paired_rel.source(), depth)
@ -387,6 +392,7 @@ impl<'a> TreeContext<'a> {
) -> Option<Xirf> {
match src {
Object::Ident((ident, _)) => {
self.tpl_apply = None;
self.push(attr_name(ident.name()));
Some(Xirf::open(
@ -404,6 +410,12 @@ impl<'a> TreeContext<'a> {
// do not have to deal with converting underscore-padded
// template names back into short-hand form.
Object::Pkg(..) | Object::Tpl(..) | Object::Expr(..) => {
// This really ought to be a state transition;
// this is a sheer act of laziness.
// If we introduce states for other things,
// let's convert this as well.
self.tpl_apply = Some(oi_tpl);
// [`Ident`]s are skipped during traversal,
// so we'll handle it ourselves here.
// This also gives us the opportunity to make sure that
@ -444,6 +456,33 @@ impl<'a> TreeContext<'a> {
}
}
/// Emit a metavariable as a template parameter.
///
/// For the parent template,
/// see [`Self::emit_template`].
fn emit_tpl_param(
&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("missing param name")]
});
self.push(attr_name(pname));
Some(Xirf::open(
QN_PARAM,
OpenSpan::without_name_span(meta.span()),
depth,
))
}
/// Emit a long-form template argument.
///
/// For the parent template application,
@ -501,6 +540,13 @@ impl<'a> TreeContext<'a> {
Some(attr_desc(*desc))
}
// template/param/@desc
(Object::Meta(_), Doc::IndepClause(desc))
if self.tpl_apply.is_none() =>
{
Some(attr_desc(*desc))
}
(_, Doc::Text(_text)) => {
// TODO: This isn't utilized by the XSLT parser and
// `xmllint` for system tests does not format with mixed
@ -564,6 +610,7 @@ impl<'a> From<&'a Asg> for TreeContext<'a> {
TreeContext {
stack: Default::default(),
asg,
tpl_apply: None,
}
}
}

View File

@ -263,8 +263,7 @@ impl ParseState for NirToAir {
// Some of these will be permitted in the future.
(
Meta(mspan),
tok @ (Open(..) | Close(..) | Ref(..) | RefSubject(..)
| Desc(..)),
tok @ (Open(..) | Close(..) | Ref(..) | RefSubject(..)),
) => Transition(Meta(mspan))
.err(NirToAirError::UnexpectedMetaToken(mspan, tok)),
@ -285,8 +284,8 @@ impl ParseState for NirToAir {
Transition(Ready).ok(Air::RefIdent(spair))
}
(Ready, Desc(clause)) => {
Transition(Ready).ok(Air::DocIndepClause(clause))
(st @ (Ready | Meta(_)), Desc(clause)) => {
Transition(st).ok(Air::DocIndepClause(clause))
}
(Ready, Import(namespec)) => {

View File

@ -195,6 +195,40 @@ fn tpl_with_name() {
);
}
#[test]
fn tpl_with_param() {
let name_tpl = SPair("_tpl_".into(), S2);
let name_param = SPair("@param@".into(), S4);
let desc_param = SPair("param desc".into(), S5);
#[rustfmt::skip]
let toks = vec![
Open(Tpl, S1),
BindIdent(name_tpl),
Open(TplParam, S3),
BindIdent(name_param),
Desc(desc_param),
Close(TplParam, S6),
Close(Tpl, S7),
];
assert_eq!(
#[rustfmt::skip]
Ok(vec![
O(Air::TplStart(S1)),
O(Air::BindIdent(name_tpl)),
O(Air::MetaStart(S3)),
O(Air::BindIdent(name_param)),
O(Air::DocIndepClause(desc_param)),
O(Air::MetaEnd(S6)),
O(Air::TplEnd(S7)),
]),
sut_parse(toks.into_iter()).collect(),
);
}
// Long form takes the actual underscore-padded template name without any
// additional processing.
#[test]

View File

@ -1425,9 +1425,10 @@ ele_parse! {
/// expanded.
TplParamStmt := QN_PARAM(_, ospan) {
@ {
QN_NAME => TodoAttr,
QN_DESC => TodoAttr,
} => Todo(ospan.into()),
QN_NAME => BindIdent,
QN_DESC => Desc,
} => Nir::Open(NirEntity::TplParam, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplParam, cspan.into()),
TplParamDefault,
};

View File

@ -190,5 +190,10 @@
<template name="_match-child_" desc="Template with a match child">
<match on="foo" value="TRUE" />
</template>
<template name="_tpl-param_" desc="Template with a param">
<param name="@foo@" desc="A parameter" />
<param name="@bar@" desc="Another parameter" />
</template>
</package>

View File

@ -189,5 +189,10 @@
<template name="_match-child_" desc="Template with a match child">
<match on="foo" />
</template>
<template name="_tpl-param_" desc="Template with a param">
<param name="@foo@" desc="A parameter" />
<param name="@bar@" desc="Another parameter" />
</template>
</package>