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), AirMeta(mtok)) => ctx.proxy(meta, mtok),
|
||||||
(PkgMeta(meta), AirBind(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)
|
ctx.try_ret_with_lookahead(meta, tok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -854,7 +854,7 @@ sum_ir! {
|
||||||
pub sum enum AirBindableTpl = AirTpl | AirBind | AirDoc;
|
pub sum enum AirBindableTpl = AirTpl | AirBind | AirDoc;
|
||||||
|
|
||||||
/// Tokens that may be used to define metavariables.
|
/// Tokens that may be used to define metavariables.
|
||||||
pub sum enum AirBindableMeta = AirMeta | AirBind;
|
pub sum enum AirBindableMeta = AirMeta | AirBind | AirDoc;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AirBind {
|
impl AirBind {
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl ParseState for AirMetaAggregate {
|
||||||
tok: Self::Token,
|
tok: Self::Token,
|
||||||
ctx: &mut Self::Context,
|
ctx: &mut Self::Context,
|
||||||
) -> TransitionResult<Self::Super> {
|
) -> TransitionResult<Self::Super> {
|
||||||
use super::ir::{AirBind::*, AirMeta::*};
|
use super::ir::{AirBind::*, AirDoc::*, AirMeta::*};
|
||||||
use AirBindableMeta::*;
|
use AirBindableMeta::*;
|
||||||
use AirMetaAggregate::*;
|
use AirMetaAggregate::*;
|
||||||
|
|
||||||
|
@ -91,6 +91,19 @@ impl ParseState for AirMetaAggregate {
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.transition(TplMeta(oi_meta)),
|
.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(..))) => {
|
(TplMeta(..), tok @ AirBind(RefIdent(..))) => {
|
||||||
diagnostic_todo!(
|
diagnostic_todo!(
|
||||||
vec![tok.note("this token")],
|
vec![tok.note("this token")],
|
||||||
|
@ -119,8 +132,10 @@ impl ParseState for AirMetaAggregate {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Maybe the bind can be handled by the parent frame.
|
// Maybe the token can be handled by the parent frame.
|
||||||
(Ready, tok @ AirBind(..)) => Transition(Ready).dead(tok),
|
(Ready, tok @ (AirBind(..) | AirDoc(..))) => {
|
||||||
|
Transition(Ready).dead(tok)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -462,6 +462,7 @@ fn tpl_with_param() {
|
||||||
let id_param1 = SPair("@param1@".into(), S4);
|
let id_param1 = SPair("@param1@".into(), S4);
|
||||||
let pval1 = SPair("value1".into(), S5);
|
let pval1 = SPair("value1".into(), S5);
|
||||||
let id_param2 = SPair("@param2@".into(), S8);
|
let id_param2 = SPair("@param2@".into(), S8);
|
||||||
|
let param_desc = SPair("param desc".into(), S9);
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let toks = vec![
|
let toks = vec![
|
||||||
|
@ -477,8 +478,9 @@ fn tpl_with_param() {
|
||||||
// Required metavariable (no value).
|
// Required metavariable (no value).
|
||||||
Air::MetaStart(S7),
|
Air::MetaStart(S7),
|
||||||
Air::BindIdent(id_param2),
|
Air::BindIdent(id_param2),
|
||||||
Air::MetaEnd(S9),
|
Air::DocIndepClause(param_desc),
|
||||||
Air::TplEnd(S10),
|
Air::MetaEnd(S10),
|
||||||
|
Air::TplEnd(S11),
|
||||||
];
|
];
|
||||||
|
|
||||||
let ctx = air_ctx_from_pkg_body_toks(toks);
|
let ctx = air_ctx_from_pkg_body_toks(toks);
|
||||||
|
@ -494,12 +496,28 @@ fn tpl_with_param() {
|
||||||
oi_tpl
|
oi_tpl
|
||||||
.lookup_local_linear(&asg, *id)
|
.lookup_local_linear(&asg, *id)
|
||||||
.and_then(|oi| oi.edges_filtered::<Meta>(&asg).next())
|
.and_then(|oi| oi.edges_filtered::<Meta>(&asg).next())
|
||||||
.map(ObjectIndex::cresolve(&asg))
|
.map(|oi| (oi, oi.resolve(&asg)))
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
assert_eq!(params[0], Some(&Meta::Lexeme(S3.merge(S6).unwrap(), pval1)));
|
assert_eq!(
|
||||||
assert_eq!(params[1], Some(&Meta::Required(S7.merge(S9).unwrap())));
|
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
|
// A template definition nested within another creates a
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
//! have historically been a feature of the template system.
|
//! have historically been a feature of the template system.
|
||||||
//! The canonical metavariable is the template parameter.
|
//! The canonical metavariable is the template parameter.
|
||||||
|
|
||||||
use super::{prelude::*, Ident};
|
use super::{prelude::*, Doc, Ident};
|
||||||
use crate::{
|
use crate::{
|
||||||
diagnose::Annotate,
|
diagnose::Annotate,
|
||||||
diagnostic_todo,
|
diagnostic_todo,
|
||||||
|
@ -133,6 +133,9 @@ object_rel! {
|
||||||
Meta -> {
|
Meta -> {
|
||||||
tree Meta, // TODO: do we need tree?
|
tree Meta, // TODO: do we need tree?
|
||||||
cross Ident,
|
cross Ident,
|
||||||
|
|
||||||
|
// e.g. template paramater description.
|
||||||
|
tree Doc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,6 +154,10 @@ type TokenStack = ArrayVec<Xirf, TOK_STACK_SIZE>;
|
||||||
pub struct TreeContext<'a> {
|
pub struct TreeContext<'a> {
|
||||||
stack: TokenStack,
|
stack: TokenStack,
|
||||||
asg: &'a Asg,
|
asg: &'a Asg,
|
||||||
|
|
||||||
|
/// Whether the most recently encountered template has been interpreted
|
||||||
|
/// as an application.
|
||||||
|
tpl_apply: Option<ObjectIndex<Tpl>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TreeContext<'a> {
|
impl<'a> TreeContext<'a> {
|
||||||
|
@ -210,9 +214,10 @@ impl<'a> TreeContext<'a> {
|
||||||
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
|
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
Object::Meta((meta, oi_meta)) => {
|
Object::Meta((meta, oi_meta)) => match self.tpl_apply {
|
||||||
self.emit_tpl_arg(meta, *oi_meta, depth)
|
Some(_) => self.emit_tpl_arg(meta, *oi_meta, depth),
|
||||||
}
|
None => self.emit_tpl_param(meta, *oi_meta, depth),
|
||||||
|
},
|
||||||
|
|
||||||
Object::Doc((doc, oi_doc)) => {
|
Object::Doc((doc, oi_doc)) => {
|
||||||
self.emit_doc(doc, *oi_doc, paired_rel.source(), depth)
|
self.emit_doc(doc, *oi_doc, paired_rel.source(), depth)
|
||||||
|
@ -387,6 +392,7 @@ impl<'a> TreeContext<'a> {
|
||||||
) -> Option<Xirf> {
|
) -> Option<Xirf> {
|
||||||
match src {
|
match src {
|
||||||
Object::Ident((ident, _)) => {
|
Object::Ident((ident, _)) => {
|
||||||
|
self.tpl_apply = None;
|
||||||
self.push(attr_name(ident.name()));
|
self.push(attr_name(ident.name()));
|
||||||
|
|
||||||
Some(Xirf::open(
|
Some(Xirf::open(
|
||||||
|
@ -404,6 +410,12 @@ impl<'a> TreeContext<'a> {
|
||||||
// do not have to deal with converting underscore-padded
|
// do not have to deal with converting underscore-padded
|
||||||
// template names back into short-hand form.
|
// template names back into short-hand form.
|
||||||
Object::Pkg(..) | Object::Tpl(..) | Object::Expr(..) => {
|
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,
|
// [`Ident`]s are skipped during traversal,
|
||||||
// so we'll handle it ourselves here.
|
// so we'll handle it ourselves here.
|
||||||
// This also gives us the opportunity to make sure that
|
// 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.
|
/// Emit a long-form template argument.
|
||||||
///
|
///
|
||||||
/// For the parent template application,
|
/// For the parent template application,
|
||||||
|
@ -501,6 +540,13 @@ impl<'a> TreeContext<'a> {
|
||||||
Some(attr_desc(*desc))
|
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)) => {
|
(_, Doc::Text(_text)) => {
|
||||||
// TODO: This isn't utilized by the XSLT parser and
|
// TODO: This isn't utilized by the XSLT parser and
|
||||||
// `xmllint` for system tests does not format with mixed
|
// `xmllint` for system tests does not format with mixed
|
||||||
|
@ -564,6 +610,7 @@ impl<'a> From<&'a Asg> for TreeContext<'a> {
|
||||||
TreeContext {
|
TreeContext {
|
||||||
stack: Default::default(),
|
stack: Default::default(),
|
||||||
asg,
|
asg,
|
||||||
|
tpl_apply: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -263,8 +263,7 @@ impl ParseState for NirToAir {
|
||||||
// Some of these will be permitted in the future.
|
// Some of these will be permitted in the future.
|
||||||
(
|
(
|
||||||
Meta(mspan),
|
Meta(mspan),
|
||||||
tok @ (Open(..) | Close(..) | Ref(..) | RefSubject(..)
|
tok @ (Open(..) | Close(..) | Ref(..) | RefSubject(..)),
|
||||||
| Desc(..)),
|
|
||||||
) => Transition(Meta(mspan))
|
) => Transition(Meta(mspan))
|
||||||
.err(NirToAirError::UnexpectedMetaToken(mspan, tok)),
|
.err(NirToAirError::UnexpectedMetaToken(mspan, tok)),
|
||||||
|
|
||||||
|
@ -285,8 +284,8 @@ impl ParseState for NirToAir {
|
||||||
Transition(Ready).ok(Air::RefIdent(spair))
|
Transition(Ready).ok(Air::RefIdent(spair))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Ready, Desc(clause)) => {
|
(st @ (Ready | Meta(_)), Desc(clause)) => {
|
||||||
Transition(Ready).ok(Air::DocIndepClause(clause))
|
Transition(st).ok(Air::DocIndepClause(clause))
|
||||||
}
|
}
|
||||||
|
|
||||||
(Ready, Import(namespec)) => {
|
(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
|
// Long form takes the actual underscore-padded template name without any
|
||||||
// additional processing.
|
// additional processing.
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1425,9 +1425,10 @@ ele_parse! {
|
||||||
/// expanded.
|
/// expanded.
|
||||||
TplParamStmt := QN_PARAM(_, ospan) {
|
TplParamStmt := QN_PARAM(_, ospan) {
|
||||||
@ {
|
@ {
|
||||||
QN_NAME => TodoAttr,
|
QN_NAME => BindIdent,
|
||||||
QN_DESC => TodoAttr,
|
QN_DESC => Desc,
|
||||||
} => Todo(ospan.into()),
|
} => Nir::Open(NirEntity::TplParam, ospan.into()),
|
||||||
|
/(cspan) => Nir::Close(NirEntity::TplParam, cspan.into()),
|
||||||
|
|
||||||
TplParamDefault,
|
TplParamDefault,
|
||||||
};
|
};
|
||||||
|
|
|
@ -190,5 +190,10 @@
|
||||||
<template name="_match-child_" desc="Template with a match child">
|
<template name="_match-child_" desc="Template with a match child">
|
||||||
<match on="foo" value="TRUE" />
|
<match on="foo" value="TRUE" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template name="_tpl-param_" desc="Template with a param">
|
||||||
|
<param name="@foo@" desc="A parameter" />
|
||||||
|
<param name="@bar@" desc="Another parameter" />
|
||||||
|
</template>
|
||||||
</package>
|
</package>
|
||||||
|
|
||||||
|
|
|
@ -189,5 +189,10 @@
|
||||||
<template name="_match-child_" desc="Template with a match child">
|
<template name="_match-child_" desc="Template with a match child">
|
||||||
<match on="foo" />
|
<match on="foo" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template name="_tpl-param_" desc="Template with a param">
|
||||||
|
<param name="@foo@" desc="A parameter" />
|
||||||
|
<param name="@bar@" desc="Another parameter" />
|
||||||
|
</template>
|
||||||
</package>
|
</package>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue