tamer: NIR->xmli interpolation and template param

The fixpoint tests for `meta-interp` are finally working.  I could have
broken this up more, but I'm exhausted with this process, so, you get what
you get.

NIR will now recognize basic `<text>` and `<param-value>` nodes (note the
caveat for `<text>` in the comment, for now), and I finally include abstract
binding in the lowering pipeline.  `xmli` output is also now able to cope
with metavariables with a single lexical association, and continues to
become more of a mess.

DEV-13163
main
Mike Gerwitz 2023-07-18 12:31:28 -04:00
parent 85b08eb45e
commit 0f93f3a498
10 changed files with 116 additions and 26 deletions

View File

@ -167,7 +167,7 @@ impl ParseState for AirExprAggregate {
}
(BuildingExpr(es, oi), AirDoc(DocIndepClause(clause))) => {
oi.desc_short(ctx.asg_mut(), clause);
oi.add_desc_short(ctx.asg_mut(), clause);
Transition(BuildingExpr(es, oi)).incomplete()
}

View File

@ -102,7 +102,7 @@ impl ParseState for AirMetaAggregate {
}
(TplMeta(oi_meta), AirDoc(DocIndepClause(clause))) => {
oi_meta.desc_short(ctx.asg_mut(), clause);
oi_meta.add_desc_short(ctx.asg_mut(), clause);
Transition(TplMeta(oi_meta)).incomplete()
}

View File

@ -205,7 +205,7 @@ impl ParseState for AirTplAggregate {
}
(Toplevel(tpl), AirDoc(DocIndepClause(clause))) => {
tpl.oi().desc_short(ctx.asg_mut(), clause);
tpl.oi().add_desc_short(ctx.asg_mut(), clause);
Transition(Toplevel(tpl)).incomplete()
}

View File

@ -837,13 +837,27 @@ impl<O: ObjectKind> ObjectIndex<O> {
/// simple sentence or as part of a compound sentence.
/// There should only be one such clause for any given object,
/// but that is not enforced here.
pub fn desc_short(&self, asg: &mut Asg, clause: SPair) -> Self
pub fn add_desc_short(&self, asg: &mut Asg, clause: SPair) -> Self
where
O: ObjectRelTo<Doc>,
{
let oi_doc = asg.create(Doc::new_indep_clause(clause));
self.add_edge_to(asg, oi_doc, None)
}
/// Retrieve a description of this expression using a short independent
/// clause,
/// if one has been set.
///
/// See [`Self::add_desc_short`] to add such a description.
pub fn desc_short(&self, asg: &Asg) -> Option<SPair>
where
O: ObjectRelTo<Doc>,
{
self.edges_filtered::<Doc>(asg)
.map(ObjectIndex::cresolve(asg))
.find_map(Doc::indep_clause)
}
}
impl ObjectIndex<Object> {

View File

@ -476,6 +476,28 @@ impl<'a> TreeContext<'a> {
if let Some(pname) =
oi_meta.ident(self.asg).map(|oi| oi.name_or_meta(self.asg))
{
// This may have a body that is a single lexeme,
// representing a default value for the parameter.
if let Some(lexeme) = meta.lexeme() {
let open = self.emit_text_node(
lexeme,
lexeme.span(),
depth.child_depth(),
);
self.push(open);
}
// Because of the above,
// we must check here if we have a description rather than
// waiting to encounter it during the traversal;
// otherwise we'd be outputting child nodes before a
// description attribute,
// which would result in invalid XML that is rejected by
// the XIR writer.
if let Some(desc_short) = oi_meta.desc_short(self.asg) {
self.push(attr_desc(desc_short));
}
self.push(attr_name(pname));
Some(Xirf::open(
@ -484,22 +506,7 @@ impl<'a> TreeContext<'a> {
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,
))
Some(self.emit_text_node(lexeme, meta.span(), depth))
} else {
// TODO: Rewrite the above to be an exhaustive match, perhaps,
// so we know what we'll error on.
@ -510,6 +517,27 @@ impl<'a> TreeContext<'a> {
}
}
/// Emit a `<text>` node containing a lexeme.
fn emit_text_node(
&mut self,
lexeme: SPair,
node_span: Span,
depth: Depth,
) -> Xirf {
self.push(Xirf::close(
Some(QN_TEXT),
CloseSpan::without_name_span(node_span),
depth,
));
self.push(Xirf::text(
Text(lexeme.symbol(), lexeme.span()),
depth.child_depth(),
));
Xirf::open(QN_TEXT, OpenSpan::without_name_span(node_span), depth)
}
/// Emit a `<param-value>` node assumed to be within a template param
/// body.
fn emit_tpl_param_value(
@ -586,10 +614,11 @@ impl<'a> TreeContext<'a> {
}
// template/param/@desc
(Object::Meta(_), Doc::IndepClause(desc))
(Object::Meta(_), Doc::IndepClause(_desc))
if self.tpl_apply.is_none() =>
{
Some(attr_desc(*desc))
// This is already covered in `emit_tpl_param`
None
}
(_, Doc::Text(_text)) => {

View File

@ -1458,10 +1458,16 @@ ele_parse! {
/// providing constant values.
/// The result will be as if the user typed the text themselves in the
/// associated template application argument.
///
/// TODO: This just produces a no-op right now and lets the text hander
/// produce text for the inner character data.
/// This is consequently ambiguous with omitting this node entirely;
/// this might be okay,
/// but this needs explicit design.
TplText := QN_TEXT(_, ospan) {
@ {
QN_UNIQUE => TodoAttr,
} => Todo(ospan.into()),
} => Noop(ospan.into()),
};
/// Default the param to the value of another template param,
@ -1475,7 +1481,7 @@ ele_parse! {
/// cumbersome and slow
TplParamValue := QN_PARAM_VALUE(_, ospan) {
@ {
QN_NAME => TodoAttr,
QN_NAME => Ref,
QN_DASH => TodoAttr,
QN_UPPER => TodoAttr,
QN_LOWER => TodoAttr,
@ -1484,7 +1490,7 @@ ele_parse! {
QN_RMUNDERSCORE => TodoAttr,
QN_IDENTIFIER => TodoAttr,
QN_SNAKE => TodoAttr,
} => Todo(ospan.into()),
} => Noop(ospan.into()),
};
/// Inherit a default value from a metavalue.

View File

@ -110,7 +110,10 @@
use crate::{
asg::{air::AirAggregate, AsgTreeToXirf},
diagnose::Diagnostic,
nir::{InterpolateNir, NirToAir, TplShortDesugar, XirfToNir},
nir::{
AbstractBindTranslate, InterpolateNir, NirToAir, TplShortDesugar,
XirfToNir,
},
obj::xmlo::{XmloReader, XmloToAir, XmloToken},
parse::{
terminal, FinalizeError, Lower, LowerSource, ParseError, ParseState,
@ -150,6 +153,7 @@ lower_pipeline! {
|> XirfToNir
|> TplShortDesugar
|> InterpolateNir
|> AbstractBindTranslate
|> NirToAir[nir_air_ty]
|> AirAggregate[air_ctx];

View File

@ -195,5 +195,23 @@
<param name="@foo@" desc="A parameter" />
<param name="@bar@" desc="Another parameter" />
</template>
<template name="_tpl-param_body_"
desc="Template with params with bodies">
<param name="@text@" desc="A param with a literal">
<text>lonely foo</text>
</param>
<param name="@ref@" desc="A param with a ref">
<param-value name="@text@" />
</param>
<param name="@both@" desc="A param with both literal and ref">
<text>foo </text>
<param-value name="@text@" />
<text> bar</text>
</param>
</template>
</package>

View File

@ -186,6 +186,7 @@
we cannot support the generation of each of those things within
templates.
<template name="_match-child_" desc="Template with a match child">
<match on="foo" />
</template>
@ -194,5 +195,23 @@
<param name="@foo@" desc="A parameter" />
<param name="@bar@" desc="Another parameter" />
</template>
<template name="_tpl-param_body_"
desc="Template with params with bodies">
<param name="@text@" desc="A param with a literal">
<text>lonely foo</text>
</param>
<param name="@ref@" desc="A param with a ref">
<param-value name="@text@" />
</param>
<param name="@both@" desc="A param with both literal and ref">
<text>foo </text>
<param-value name="@text@" />
<text> bar</text>
</param>
</template>
</package>