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-13163main
parent
9887abd037
commit
d10bf00f5d
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)) => {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
Loading…
Reference in New Issue