tamer: xmli reconstruction of desugared interpolated metavars
Well, this is both good news and bad news. The good news is that this finally produces the expected output and reconstructs sources from interpolated values on the ASG. Yay! ...the bad news is that it's wrong. Notice how the fixpoint test is disabled. So, my plan was originally to commit it like this first and see if I was comfortable relaxing the convention that `<param>` nodes had to appear in the header. That's nice to do, that's cleaner to do, but would the XSLT-based compiler really care? I had to investigate. Well, turns out that TAMER does care. Because, well over a decade ago, I re-used `<param>`, which could represent not only a template param, but also a global param, or a function param. So, XML->NIR considers all `<param>` nodes at the head of a template to be template parameters. But after the first non-header element, we transition to another state that allows it to be pretty much anything. And so, I can't relax that restriction. And because of that, I can't just stream the tree to the xmli generator, I'll have to queue up nodes and order them. Oh well, I tried. DEV-13163main
parent
85892caeb2
commit
b30018c23b
|
@ -118,6 +118,20 @@ impl Meta {
|
|||
),
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve a concrete lexeme,
|
||||
/// if any.
|
||||
///
|
||||
/// If this metavariable represents a concatenation list,
|
||||
/// this will return [`None`].
|
||||
/// This method _does not_ expand metavariables,
|
||||
/// and does not have the context necessary to do so.
|
||||
pub fn lexeme(&self) -> Option<SPair> {
|
||||
match self {
|
||||
Self::Required(_) | Self::ConcatList(_) => None,
|
||||
Self::Lexeme(_, lex) => Some(*lex),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Meta> for Span {
|
||||
|
|
|
@ -193,18 +193,30 @@ impl<'a> TreeContext<'a> {
|
|||
),
|
||||
},
|
||||
|
||||
// Identifiers will be considered in context;
|
||||
// pass over it for now.
|
||||
// But we must not skip over its depth,
|
||||
// otherwise we parent a following sibling at a matching
|
||||
// depth;
|
||||
// this close will force the auto-closing system to close
|
||||
// any siblings in preparation for the object to follow.
|
||||
Object::Ident((ident, _)) => Some(Xirf::Close(
|
||||
None,
|
||||
CloseSpan::without_name_span(ident.span()),
|
||||
depth,
|
||||
)),
|
||||
Object::Ident((ident, oi_ident)) => match paired_rel.source() {
|
||||
Object::Meta(..) => {
|
||||
self.emit_tpl_param_value(ident, oi_ident, depth)
|
||||
}
|
||||
|
||||
// All other identifiers will be considered in context;
|
||||
// pass over it for now.
|
||||
// But we must not skip over its depth,
|
||||
// otherwise we parent a following sibling at a matching
|
||||
// depth;
|
||||
// this close will force the auto-closing system to
|
||||
// close any siblings in preparation for the object to
|
||||
// follow.
|
||||
Object::Root(..)
|
||||
| Object::Pkg(..)
|
||||
| Object::Ident(..)
|
||||
| Object::Expr(..)
|
||||
| Object::Tpl(..)
|
||||
| Object::Doc(..) => Some(Xirf::Close(
|
||||
None,
|
||||
CloseSpan::without_name_span(ident.span()),
|
||||
depth,
|
||||
)),
|
||||
},
|
||||
|
||||
Object::Expr((expr, oi_expr)) => {
|
||||
self.emit_expr(expr, *oi_expr, paired_rel.source(), depth)
|
||||
|
@ -282,8 +294,9 @@ impl<'a> TreeContext<'a> {
|
|||
depth: Depth,
|
||||
) -> Option<Xirf> {
|
||||
match src {
|
||||
Object::Ident((ident, oi_ident)) => {
|
||||
self.emit_expr_ident(expr, *oi_ident, ident, depth)
|
||||
Object::Ident((_, oi_ident)) => {
|
||||
let name = oi_ident.name_or_meta(self.asg);
|
||||
self.emit_expr_ident(expr, name, depth)
|
||||
}
|
||||
Object::Expr((pexpr, _)) => match (pexpr.op(), expr.op()) {
|
||||
(ExprOp::Conj | ExprOp::Disj, ExprOp::Eq) => {
|
||||
|
@ -315,8 +328,7 @@ impl<'a> TreeContext<'a> {
|
|||
fn emit_expr_ident(
|
||||
&mut self,
|
||||
expr: &Expr,
|
||||
oi_ident: ObjectIndex<Ident>,
|
||||
ident: &Ident,
|
||||
name: SPair,
|
||||
depth: Depth,
|
||||
) -> Option<Xirf> {
|
||||
let (qname, ident_qname) = match expr.op() {
|
||||
|
@ -332,9 +344,8 @@ impl<'a> TreeContext<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let name = oi_ident.name_or_meta(self.asg);
|
||||
let ispan = ident.span();
|
||||
self.push(Xirf::attr(ident_qname, name, (ispan, ispan)));
|
||||
let span = name.span();
|
||||
self.push(Xirf::attr(ident_qname, name, (span, span)));
|
||||
|
||||
Some(Xirf::open(
|
||||
qname,
|
||||
|
@ -462,18 +473,57 @@ impl<'a> TreeContext<'a> {
|
|||
oi_meta: ObjectIndex<Meta>,
|
||||
depth: Depth,
|
||||
) -> Option<Xirf> {
|
||||
let pname = oi_meta
|
||||
.ident(self.asg)
|
||||
.map(|oi| oi.name_or_meta(self.asg))
|
||||
.diagnostic_unwrap(|| {
|
||||
vec![meta.internal_error("missing param name")]
|
||||
});
|
||||
if let Some(pname) =
|
||||
oi_meta.ident(self.asg).map(|oi| oi.name_or_meta(self.asg))
|
||||
{
|
||||
self.push(attr_name(pname));
|
||||
|
||||
self.push(attr_name(pname));
|
||||
Some(Xirf::open(
|
||||
QN_PARAM,
|
||||
OpenSpan::without_name_span(meta.span()),
|
||||
depth,
|
||||
))
|
||||
} else if let Some(lexeme) = meta.lexeme() {
|
||||
self.push(Xirf::close(
|
||||
Some(QN_TEXT),
|
||||
CloseSpan::without_name_span(meta.span()),
|
||||
depth,
|
||||
));
|
||||
|
||||
self.push(Xirf::text(
|
||||
Text(lexeme.symbol(), lexeme.span()),
|
||||
depth.child_depth(),
|
||||
));
|
||||
|
||||
Some(Xirf::open(
|
||||
QN_TEXT,
|
||||
OpenSpan::without_name_span(meta.span()),
|
||||
depth,
|
||||
))
|
||||
} else {
|
||||
// TODO: Rewrite the above to be an exhaustive match, perhaps,
|
||||
// so we know what we'll error on.
|
||||
diagnostic_todo!(
|
||||
vec![oi_meta.internal_error("unsupported Meta type")],
|
||||
"xmli output does not yet support this Meta object",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit a `<param-value>` node assumed to be within a template param
|
||||
/// body.
|
||||
fn emit_tpl_param_value(
|
||||
&mut self,
|
||||
ident: &Ident,
|
||||
oi_ident: &ObjectIndex<Ident>,
|
||||
depth: Depth,
|
||||
) -> Option<Xirf> {
|
||||
let name = oi_ident.name_or_meta(self.asg);
|
||||
self.push(attr_name(name));
|
||||
|
||||
Some(Xirf::open(
|
||||
QN_PARAM,
|
||||
OpenSpan::without_name_span(meta.span()),
|
||||
QN_PARAM_VALUE,
|
||||
OpenSpan::without_name_span(ident.span()),
|
||||
depth,
|
||||
))
|
||||
}
|
||||
|
|
|
@ -255,6 +255,9 @@ impl ParseState for NirToAir {
|
|||
(Meta(mspan), BindIdentMeta(spair)) => {
|
||||
Transition(Meta(mspan)).ok(Air::BindIdent(spair))
|
||||
}
|
||||
(Meta(mspan), Ref(spair)) => {
|
||||
Transition(Meta(mspan)).ok(Air::RefIdent(spair))
|
||||
}
|
||||
(Meta(mspan), Text(lexeme)) => {
|
||||
Transition(Meta(mspan)).ok(Air::MetaLexeme(lexeme))
|
||||
}
|
||||
|
@ -264,8 +267,7 @@ impl ParseState for NirToAir {
|
|||
// Some of these will be permitted in the future.
|
||||
(
|
||||
Meta(mspan),
|
||||
tok @ (Open(..) | Close(..) | BindIdent(..) | Ref(..)
|
||||
| RefSubject(..)),
|
||||
tok @ (Open(..) | Close(..) | BindIdent(..) | RefSubject(..)),
|
||||
) => Transition(Meta(mspan))
|
||||
.err(NirToAirError::ExpectedMetaToken(mspan, tok)),
|
||||
|
||||
|
|
|
@ -269,6 +269,7 @@ fn apply_template_long_form_args() {
|
|||
Open(TplParam, S3),
|
||||
BindIdentMeta(p1),
|
||||
Text(v1),
|
||||
Ref(p2),
|
||||
Close(TplParam, S6),
|
||||
|
||||
Open(TplParam, S7),
|
||||
|
@ -287,6 +288,7 @@ fn apply_template_long_form_args() {
|
|||
O(Air::MetaStart(S3)),
|
||||
O(Air::BindIdent(p1)),
|
||||
O(Air::MetaLexeme(v1)),
|
||||
O(Air::RefIdent(p2)),
|
||||
O(Air::MetaEnd(S6)),
|
||||
|
||||
O(Air::MetaStart(S7)),
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
|
||||
<package xmlns="http://www.lovullo.com/rater"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:t="http://www.lovullo.com/rater/apply-template">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<template name="_interp-non-bind_"
|
||||
desc="Interpolation in non-binding position">
|
||||
|
||||
<classify as="only" desc="@___dsgr_335@"/>
|
||||
<param name="@___dsgr_335@"
|
||||
desc="Generated from interpolated string `{@bar@}`">
|
||||
<param-value name="@bar@" />
|
||||
</param>
|
||||
|
||||
<classify as="prefixed" desc="@___dsgr_368@"/>
|
||||
<param name="@___dsgr_368@"
|
||||
desc="Generated from interpolated string `Prefix {@bar@}`">
|
||||
<text>Prefix </text>
|
||||
<param-value name="@bar@" />
|
||||
</param>
|
||||
|
||||
<classify as="suffixed" desc="@___dsgr_3a3@"/>
|
||||
<param name="@___dsgr_3a3@"
|
||||
desc="Generated from interpolated string `{@bar@} suffix`">
|
||||
<param-value name="@bar@" />
|
||||
<text> suffix</text>
|
||||
</param>
|
||||
|
||||
<classify as="both" desc="@___dsgr_3da@" />
|
||||
<param name="@___dsgr_3da@"
|
||||
desc="Generated from interpolated string `Prefix {@bar@} suffix`">
|
||||
<text>Prefix </text>
|
||||
<param-value name="@bar@" />
|
||||
<text> suffix</text>
|
||||
</param>
|
||||
</template>
|
||||
|
||||
|
||||
<template name="_with-abstract-ident_"
|
||||
desc="Metavariable interpolation in binding position">
|
||||
|
||||
<param name="@___dsgr_4a8@"
|
||||
desc="Generated from interpolated string `{@as@}`">
|
||||
<param-value name="@as@" />
|
||||
</param>
|
||||
<classify as="@___dsgr_4a8@" />
|
||||
|
||||
<param name="@___dsgr_4ca@"
|
||||
desc="Generated from interpolated string `prefix-{@as@}`">
|
||||
<text>prefix-</text>
|
||||
<param-value name="@as@" />
|
||||
</param>
|
||||
<classify as="@___dsgr_4ca@" />
|
||||
|
||||
<param name="@___dsgr_4f4@"
|
||||
desc="Generated from interpolated string `{@as@}-suffix`">
|
||||
<param-value name="@as@" />
|
||||
<text>-suffix</text>
|
||||
</param>
|
||||
<classify as="@___dsgr_4f4@" />
|
||||
|
||||
<param name="@___dsgr_51e@"
|
||||
desc="Generated from interpolated string `prefix-{@as@}-suffix`">
|
||||
<text>prefix-</text>
|
||||
<param-value name="@as@" />
|
||||
<text>-suffix</text>
|
||||
</param>
|
||||
<classify as="@___dsgr_51e@" />
|
||||
</template>
|
||||
</package>
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0"?>
|
||||
<package xmlns="http://www.lovullo.com/rater"
|
||||
xmlns:c="http://www.lovullo.com/calc"
|
||||
xmlns:t="http://www.lovullo.com/rater/apply-template">
|
||||
|
||||
<!-- note: the extra vertical space is for alignment with expected.xml;
|
||||
open them side-by-side in your editor of choice -->
|
||||
|
||||
<!-- because the output contains identifiers derived from spans, this test
|
||||
is exceptionally fragile; if you add or remove a single byte, you're
|
||||
bound to break things. If that happens, it is safe to update the
|
||||
span portion of identifier names. In the future, a tool may be
|
||||
created to help with this tedious chore. -->
|
||||
|
||||
<template name="_interp-non-bind_"
|
||||
desc="Interpolation in non-binding position">
|
||||
<!-- note the `{}` here -->
|
||||
<classify as="only" desc="{@bar@}" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<classify as="prefixed" desc="Prefix {@bar@}" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<classify as="suffixed" desc="{@bar@} suffix" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<classify as="both" desc="Prefix {@bar@} suffix" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
|
||||
|
||||
<template name="_with-abstract-ident_"
|
||||
desc="Metavariable interpolation in binding position">
|
||||
<!-- note the `{}` here -->
|
||||
<classify as="{@as@}" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<classify as="prefix-{@as@}" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<classify as="{@as@}-suffix" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<classify as="prefix-{@as@}-suffix" />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</template>
|
||||
</package>
|
||||
|
|
@ -69,7 +69,7 @@ timed-tamec() {
|
|||
header() {
|
||||
# allocate enough space based on the path we'll output
|
||||
local -i mypath_len=${#mypath}
|
||||
local -i dirlen=$((mypath_len + 12))
|
||||
local -i dirlen=$((mypath_len + 14))
|
||||
|
||||
# newline intentionally omitted
|
||||
printf "%-${dirlen}s %-20s " "$@"
|
||||
|
@ -104,6 +104,11 @@ test-derive-from-src() {
|
|||
test-fixpoint() {
|
||||
local dir="${1?Missing directory name}"
|
||||
|
||||
if [ -f "$dir/no-fixpoint" ]; then
|
||||
echo -n '!!!WARNING!!! test skipped: `no-fixpoint` file '
|
||||
return
|
||||
fi
|
||||
|
||||
timed-tamec "$dir" out.xmli out-2.xmli || return
|
||||
|
||||
diff <("$P_XMLLINT" --format "$dir/expected.xml" || echo 'ERR expected.xml') \
|
||||
|
|
Loading…
Reference in New Issue