tamer: asg: Shorthand and long-form template arguments
This applies to template application only; there's still some work to do for template parameters in definitions (well, for deriving them in `xmli` at least). And, as you can see, there's still a lot of TODO items here. I ended up backtracking on tree edges to Meta, and even on cross edges to Meta, because it complicated xmli derivation with no benefit right now; maybe a cross edge will be re-added in the future, but I need to move on and see where this takes me. But, it works. DEV-13708
parent
b2c6b7f073
commit
c26444666f
|
@ -31,6 +31,7 @@ use super::{
|
||||||
AirExprAggregate,
|
AirExprAggregate,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
asg::graph::object::Meta,
|
||||||
diagnose::Annotate,
|
diagnose::Annotate,
|
||||||
diagnostic_todo,
|
diagnostic_todo,
|
||||||
fmt::{DisplayWrapper, TtQuote},
|
fmt::{DisplayWrapper, TtQuote},
|
||||||
|
@ -63,12 +64,26 @@ pub enum AirTplAggregate {
|
||||||
/// which simplifies AIR generation.
|
/// which simplifies AIR generation.
|
||||||
Ready(ObjectIndex<Pkg>),
|
Ready(ObjectIndex<Pkg>),
|
||||||
|
|
||||||
|
/// Toplevel template context.
|
||||||
|
///
|
||||||
|
/// Conceptually,
|
||||||
|
/// tokens that are received in this state are interpreted as directly
|
||||||
|
/// applying to the template itself,
|
||||||
|
/// or creating an object directly owned by the template.
|
||||||
Toplevel(
|
Toplevel(
|
||||||
ObjectIndex<Pkg>,
|
ObjectIndex<Pkg>,
|
||||||
TplState,
|
TplState,
|
||||||
AirExprAggregateStoreDangling<Tpl>,
|
AirExprAggregateStoreDangling<Tpl>,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
/// Defining a template metavariable.
|
||||||
|
TplMeta(
|
||||||
|
ObjectIndex<Pkg>,
|
||||||
|
TplState,
|
||||||
|
AirExprAggregateStoreDangling<Tpl>,
|
||||||
|
ObjectIndex<Meta>,
|
||||||
|
),
|
||||||
|
|
||||||
/// Aggregating tokens into a template.
|
/// Aggregating tokens into a template.
|
||||||
TplExpr(
|
TplExpr(
|
||||||
ObjectIndex<Pkg>,
|
ObjectIndex<Pkg>,
|
||||||
|
@ -85,6 +100,10 @@ impl Display for AirTplAggregate {
|
||||||
Self::Toplevel(_, tpl, expr) | Self::TplExpr(_, tpl, expr) => {
|
Self::Toplevel(_, tpl, expr) | Self::TplExpr(_, tpl, expr) => {
|
||||||
write!(f, "building {tpl} with {expr}")
|
write!(f, "building {tpl} with {expr}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Self::TplMeta(_, tpl, _, _) => {
|
||||||
|
write!(f, "building {tpl} metavariable")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -202,17 +221,69 @@ impl ParseState for AirTplAggregate {
|
||||||
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete()
|
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
(Toplevel(..), tok @ AirTpl(TplMetaStart(..) | TplMetaEnd(..))) => {
|
(Toplevel(oi_pkg, tpl, expr), AirTpl(TplMetaStart(span))) => {
|
||||||
|
let oi_meta = asg.create(Meta::new_required(span));
|
||||||
|
Transition(TplMeta(oi_pkg, tpl, expr, oi_meta)).incomplete()
|
||||||
|
}
|
||||||
|
(
|
||||||
|
TplMeta(oi_pkg, tpl, expr, oi_meta),
|
||||||
|
AirTpl(TplMetaEnd(cspan)),
|
||||||
|
) => {
|
||||||
|
oi_meta.close(asg, cspan);
|
||||||
|
Transition(Toplevel(oi_pkg, tpl, expr)).incomplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
TplMeta(oi_pkg, tpl, expr, oi_meta),
|
||||||
|
AirTpl(TplLexeme(lexeme)),
|
||||||
|
) => Transition(TplMeta(
|
||||||
|
oi_pkg,
|
||||||
|
tpl,
|
||||||
|
expr,
|
||||||
|
oi_meta.assign_lexeme(asg, lexeme),
|
||||||
|
))
|
||||||
|
.incomplete(),
|
||||||
|
|
||||||
|
(TplMeta(oi_pkg, tpl, expr, oi_meta), AirBind(BindIdent(name))) => {
|
||||||
|
oi_meta.identify_as_tpl_param(asg, tpl.oi(), name);
|
||||||
|
Transition(TplMeta(oi_pkg, tpl, expr, oi_meta)).incomplete()
|
||||||
|
}
|
||||||
|
|
||||||
|
(TplMeta(..), tok @ AirBind(RefIdent(..))) => {
|
||||||
diagnostic_todo!(
|
diagnostic_todo!(
|
||||||
vec![tok.note("for this token")],
|
vec![tok.note("this token")],
|
||||||
"Toplevel meta"
|
"AirBind in metavar context (param-value)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
(TplMeta(..), tok @ AirExpr(..)) => {
|
||||||
|
diagnostic_todo!(
|
||||||
|
vec![tok.note("this token")],
|
||||||
|
"AirExpr in metavar context (e.g. @values@)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
(
|
||||||
|
TplMeta(..),
|
||||||
|
tok @ AirTpl(
|
||||||
|
TplStart(..) | TplMetaStart(..) | TplEnd(..)
|
||||||
|
| TplEndRef(..),
|
||||||
|
),
|
||||||
|
) => {
|
||||||
|
diagnostic_todo!(vec![tok.note("this token")], "AirTpl variant")
|
||||||
|
}
|
||||||
|
|
||||||
|
(Toplevel(..), tok @ AirTpl(TplMetaEnd(..))) => {
|
||||||
|
diagnostic_todo!(
|
||||||
|
vec![tok.note("this token")],
|
||||||
|
"unbalanced meta"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
(Toplevel(..), tok @ AirTpl(TplLexeme(..))) => {
|
(Toplevel(..), tok @ AirTpl(TplLexeme(..))) => {
|
||||||
diagnostic_todo!(
|
diagnostic_todo!(
|
||||||
vec![tok.note("for this token")],
|
vec![tok.note("this token")],
|
||||||
"err: Toplevel lexeme {tok:?} (must be within metavar)"
|
"err: TplLexeme outside of metavar"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ use crate::asg::{
|
||||||
test::{asg_from_toks, parse_as_pkg_body},
|
test::{asg_from_toks, parse_as_pkg_body},
|
||||||
Air, AirAggregate,
|
Air, AirAggregate,
|
||||||
},
|
},
|
||||||
|
graph::object::Meta,
|
||||||
Expr, ExprOp, Ident,
|
Expr, ExprOp, Ident,
|
||||||
};
|
};
|
||||||
use crate::span::dummy::*;
|
use crate::span::dummy::*;
|
||||||
|
@ -402,3 +403,48 @@ fn anonymous_tpl_immediate_ref() {
|
||||||
|
|
||||||
// TODO: More to come.
|
// TODO: More to come.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tpl_with_param() {
|
||||||
|
let id_tpl = SPair("_tpl_".into(), S2);
|
||||||
|
|
||||||
|
let id_param1 = SPair("@param1@".into(), S4);
|
||||||
|
let pval1 = SPair("value1".into(), S5);
|
||||||
|
let id_param2 = SPair("@param2@".into(), S8);
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
let toks = vec![
|
||||||
|
Air::TplStart(S1),
|
||||||
|
Air::BindIdent(id_tpl),
|
||||||
|
|
||||||
|
// Metavariable with a value.
|
||||||
|
Air::TplMetaStart(S3),
|
||||||
|
Air::BindIdent(id_param1),
|
||||||
|
Air::TplLexeme(pval1),
|
||||||
|
Air::TplMetaEnd(S6),
|
||||||
|
|
||||||
|
// Required metavariable (no value).
|
||||||
|
Air::TplMetaStart(S7),
|
||||||
|
Air::BindIdent(id_param2),
|
||||||
|
Air::TplMetaEnd(S9),
|
||||||
|
Air::TplEnd(S10),
|
||||||
|
];
|
||||||
|
|
||||||
|
let asg = asg_from_toks(toks);
|
||||||
|
let oi_tpl = asg.expect_ident_oi::<Tpl>(id_tpl);
|
||||||
|
|
||||||
|
// The template should have an edge to each identifier for each
|
||||||
|
// metavariable.
|
||||||
|
let params = [id_param1, id_param2]
|
||||||
|
.iter()
|
||||||
|
.map(|id| {
|
||||||
|
oi_tpl
|
||||||
|
.lookup_local_linear(&asg, *id)
|
||||||
|
.and_then(|oi| oi.edges_filtered::<Meta>(&asg).next())
|
||||||
|
.map(ObjectIndex::cresolve(&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())));
|
||||||
|
}
|
||||||
|
|
|
@ -640,13 +640,8 @@ impl Asg {
|
||||||
ident: SPair,
|
ident: SPair,
|
||||||
) -> ObjectIndex<O> {
|
) -> ObjectIndex<O> {
|
||||||
self.get_ident_oi(ident).diagnostic_expect(
|
self.get_ident_oi(ident).diagnostic_expect(
|
||||||
|| diagnostic_opaque_ident_desc(ident),
|
|| diagnostic_unknown_ident_desc(ident),
|
||||||
|| {
|
|| format!("unknown identifier {}", TtQuote::wrap(ident),),
|
||||||
format!(
|
|
||||||
"opaque identifier: {} has no object binding",
|
|
||||||
TtQuote::wrap(ident),
|
|
||||||
)
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -828,5 +823,15 @@ fn diagnostic_opaque_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn diagnostic_unknown_ident_desc(ident: SPair) -> Vec<AnnotatedSpan<'static>> {
|
||||||
|
vec![
|
||||||
|
ident.internal_error("reference to an unknown identifier"),
|
||||||
|
ident.help(
|
||||||
|
"the system expects this identifier to be known, \
|
||||||
|
but it could not be found.",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
|
|
|
@ -768,6 +768,25 @@ impl<O: ObjectKind> ObjectIndex<O> {
|
||||||
asg.create(Ident::declare(name))
|
asg.create(Ident::declare(name))
|
||||||
.add_edge_from(asg, *self, None)
|
.add_edge_from(asg, *self, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve the identifier for this object,
|
||||||
|
/// if any.
|
||||||
|
///
|
||||||
|
/// If there is more than one identifier,
|
||||||
|
/// only one will be returned,
|
||||||
|
/// and the result of the operation is undefined.
|
||||||
|
/// This can be problematic if certain optimizations have been performed
|
||||||
|
/// on the graph,
|
||||||
|
/// like common subexpression elimination,
|
||||||
|
/// in which case it's best not to rely on following edges in reverse.
|
||||||
|
pub fn ident<'a>(&self, asg: &'a Asg) -> Option<&'a Ident>
|
||||||
|
where
|
||||||
|
O: ObjectRelFrom<Ident>,
|
||||||
|
{
|
||||||
|
self.incoming_edges_filtered(asg)
|
||||||
|
.map(ObjectIndex::cresolve(asg))
|
||||||
|
.next()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectIndex<Object> {
|
impl ObjectIndex<Object> {
|
||||||
|
|
|
@ -249,12 +249,4 @@ impl ObjectIndex<Expr> {
|
||||||
let identi = asg.lookup_global_or_missing(ident);
|
let identi = asg.lookup_global_or_missing(ident);
|
||||||
self.add_edge_to(asg, identi, Some(ident.span()))
|
self.add_edge_to(asg, identi, Some(ident.span()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`Ident`] bound to this expression,
|
|
||||||
/// if any.
|
|
||||||
pub fn ident(self, asg: &Asg) -> Option<&Ident> {
|
|
||||||
self.incoming_edges_filtered(asg)
|
|
||||||
.map(ObjectIndex::cresolve(asg))
|
|
||||||
.next()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -982,9 +982,7 @@ object_rel! {
|
||||||
tree Ident,
|
tree Ident,
|
||||||
tree Expr,
|
tree Expr,
|
||||||
tree Tpl,
|
tree Tpl,
|
||||||
|
tree Meta,
|
||||||
// A metavariable is directly referenced by a template.
|
|
||||||
cross Meta,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1060,6 +1058,14 @@ impl ObjectIndex<Ident> {
|
||||||
self.edges(asg).find_map(ObjectRel::narrow) == Some(oi)
|
self.edges(asg).find_map(ObjectRel::narrow) == Some(oi)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether this identifier is bound to an object of kind `O`.
|
||||||
|
///
|
||||||
|
/// To bind an identifier,
|
||||||
|
/// see [`Self::bind_definition`].
|
||||||
|
pub fn is_bound_to_kind<O: ObjectRelFrom<Ident>>(&self, asg: &Asg) -> bool {
|
||||||
|
self.edges_filtered::<O>(asg).next().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// The source package defining this identifier,
|
/// The source package defining this identifier,
|
||||||
/// if known.
|
/// if known.
|
||||||
pub fn src_pkg(&self, asg: &Asg) -> Option<ObjectIndex<Pkg>> {
|
pub fn src_pkg(&self, asg: &Asg) -> Option<ObjectIndex<Pkg>> {
|
||||||
|
|
|
@ -26,11 +26,15 @@
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTy,
|
||||||
ObjectRelatable,
|
ObjectRelatable, Tpl,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
asg::Asg,
|
||||||
|
diagnose::Annotate,
|
||||||
|
diagnostic_todo,
|
||||||
|
f::Functor,
|
||||||
fmt::{DisplayWrapper, TtQuote},
|
fmt::{DisplayWrapper, TtQuote},
|
||||||
parse::{util::SPair, Token},
|
parse::util::SPair,
|
||||||
span::Span,
|
span::Span,
|
||||||
};
|
};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
@ -51,16 +55,54 @@ use std::fmt::Display;
|
||||||
pub enum Meta {
|
pub enum Meta {
|
||||||
Required(Span),
|
Required(Span),
|
||||||
ConcatList(Span),
|
ConcatList(Span),
|
||||||
Lexeme(SPair),
|
Lexeme(Span, SPair),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Meta {
|
impl Meta {
|
||||||
|
/// Create a new metavariable without a value.
|
||||||
|
///
|
||||||
|
/// Metavariables with no value cannot be used in an expansion context.
|
||||||
|
/// Intuitively,
|
||||||
|
/// they act as required parameters.
|
||||||
|
pub fn new_required(span: Span) -> Self {
|
||||||
|
Self::Required(span)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn span(&self) -> Span {
|
pub fn span(&self) -> Span {
|
||||||
match self {
|
match self {
|
||||||
Self::Required(span) | Self::ConcatList(span) => *span,
|
Self::Required(span)
|
||||||
Self::Lexeme(spair) => spair.span(),
|
| Self::ConcatList(span)
|
||||||
|
| Self::Lexeme(span, _) => *span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assign a lexeme to a metavariable.
|
||||||
|
///
|
||||||
|
/// In a template definition context,
|
||||||
|
/// this acts as a default value for this metavariable.
|
||||||
|
/// In an application context,
|
||||||
|
/// this has the effect of binding a value to this metavariable.
|
||||||
|
pub fn assign_lexeme(self, lexeme: SPair) -> Self {
|
||||||
|
match self {
|
||||||
|
Self::Required(span) => Self::Lexeme(span, lexeme),
|
||||||
|
|
||||||
|
Self::ConcatList(_) => diagnostic_todo!(
|
||||||
|
vec![lexeme.note("while parsing this lexeme")],
|
||||||
|
"append to ConcatList",
|
||||||
|
),
|
||||||
|
|
||||||
|
Self::Lexeme(_, _) => diagnostic_todo!(
|
||||||
|
vec![lexeme.note("while parsing this lexeme")],
|
||||||
|
"Lexeme => ConcatList",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Meta> for Span {
|
||||||
|
fn from(meta: &Meta) -> Self {
|
||||||
|
meta.span()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Meta {
|
impl Display for Meta {
|
||||||
|
@ -72,7 +114,19 @@ impl Display for Meta {
|
||||||
Self::ConcatList(_) => {
|
Self::ConcatList(_) => {
|
||||||
write!(f, "metasyntactic concatenation list")
|
write!(f, "metasyntactic concatenation list")
|
||||||
}
|
}
|
||||||
Self::Lexeme(spair) => write!(f, "lexeme {}", TtQuote::wrap(spair)),
|
Self::Lexeme(_, spair) => {
|
||||||
|
write!(f, "lexeme {}", TtQuote::wrap(spair))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Functor<Span> for Meta {
|
||||||
|
fn map(self, f: impl FnOnce(Span) -> Span) -> Self::Target {
|
||||||
|
match self {
|
||||||
|
Self::Required(span) => Self::Required(f(span)),
|
||||||
|
Self::ConcatList(span) => Self::ConcatList(f(span)),
|
||||||
|
Self::Lexeme(span, spair) => Self::Lexeme(f(span), spair),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -85,3 +139,28 @@ object_rel! {
|
||||||
cross Ident,
|
cross Ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ObjectIndex<Meta> {
|
||||||
|
pub fn identify_as_tpl_param(
|
||||||
|
&self,
|
||||||
|
asg: &mut Asg,
|
||||||
|
oi_tpl: ObjectIndex<Tpl>,
|
||||||
|
name: SPair,
|
||||||
|
) -> ObjectIndex<Ident> {
|
||||||
|
oi_tpl
|
||||||
|
.declare_local(asg, name)
|
||||||
|
.add_edge_to(asg, *self, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn assign_lexeme(self, asg: &mut Asg, lexeme: SPair) -> Self {
|
||||||
|
self.map_obj(asg, |meta| meta.assign_lexeme(lexeme))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn close(self, asg: &mut Asg, close_span: Span) -> Self {
|
||||||
|
self.map_obj(asg, |meta| {
|
||||||
|
meta.map(|open_span| {
|
||||||
|
open_span.merge(close_span).unwrap_or(open_span)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
Expr, Ident, Meta, Object, ObjectIndex, ObjectRel, ObjectRelFrom,
|
Expr, Ident, Object, ObjectIndex, ObjectRel, ObjectRelFrom, ObjectRelTo,
|
||||||
ObjectRelTo, ObjectRelTy, ObjectRelatable,
|
ObjectRelTy, ObjectRelatable,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
asg::Asg,
|
asg::Asg,
|
||||||
|
@ -66,10 +66,13 @@ object_rel! {
|
||||||
/// Templates may expand into nearly any context,
|
/// Templates may expand into nearly any context,
|
||||||
/// and must therefore be able to contain just about anything.
|
/// and must therefore be able to contain just about anything.
|
||||||
Tpl -> {
|
Tpl -> {
|
||||||
|
// Expressions must be able to be anonymous to allow templates in
|
||||||
|
// any `Expr` context.
|
||||||
tree Expr,
|
tree Expr,
|
||||||
tree Meta,
|
|
||||||
|
|
||||||
dyn Ident,
|
// Identifiers are used for both references and identifiers that
|
||||||
|
// will expand into an application site.
|
||||||
|
dyn Ident,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -110,6 +110,13 @@ use super::object::ObjectRel;
|
||||||
/// may modify the graph beyond recognition,
|
/// may modify the graph beyond recognition,
|
||||||
/// though they should retain ordering where it is important.
|
/// though they should retain ordering where it is important.
|
||||||
///
|
///
|
||||||
|
/// _Objects that do not have a path from the root will not be visited by
|
||||||
|
/// this traversal._
|
||||||
|
/// These objects are expected to act as additional metadata,
|
||||||
|
/// and must be queried for explicitly.
|
||||||
|
/// Such querying can be done during the traversal since this visitor holds
|
||||||
|
/// only a shared immutable reference to the [`Asg`].
|
||||||
|
///
|
||||||
/// For more information,
|
/// For more information,
|
||||||
/// see [`ObjectRel::is_cross_edge`].
|
/// see [`ObjectRel::is_cross_edge`].
|
||||||
///
|
///
|
||||||
|
|
|
@ -196,6 +196,8 @@ fn traverses_ontological_tree_tpl_apply() {
|
||||||
let name_tpl = "_tpl-to-apply_".into();
|
let name_tpl = "_tpl-to-apply_".into();
|
||||||
let id_tpl = SPair(name_tpl, S3);
|
let id_tpl = SPair(name_tpl, S3);
|
||||||
let ref_tpl = SPair(name_tpl, S6);
|
let ref_tpl = SPair(name_tpl, S6);
|
||||||
|
let id_param = SPair("@param@".into(), S8);
|
||||||
|
let value_param = SPair("value".into(), S9);
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
let toks = vec![
|
let toks = vec![
|
||||||
|
@ -213,8 +215,13 @@ fn traverses_ontological_tree_tpl_apply() {
|
||||||
// metavariables.
|
// metavariables.
|
||||||
TplStart(S5),
|
TplStart(S5),
|
||||||
RefIdent(ref_tpl),
|
RefIdent(ref_tpl),
|
||||||
TplEndRef(S7), // notice the `Ref` at the end
|
|
||||||
PkgEnd(S8),
|
TplMetaStart(S7),
|
||||||
|
BindIdent(id_param),
|
||||||
|
TplLexeme(value_param),
|
||||||
|
TplMetaEnd(S10),
|
||||||
|
TplEndRef(S11), // notice the `Ref` at the end
|
||||||
|
PkgEnd(S12),
|
||||||
];
|
];
|
||||||
|
|
||||||
// We need more concise expressions for the below table of values.
|
// We need more concise expressions for the below table of values.
|
||||||
|
@ -225,11 +232,13 @@ fn traverses_ontological_tree_tpl_apply() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
// A -|-> B | A span -|-> B span | espan | depth
|
// A -|-> B | A span -|-> B span | espan | depth
|
||||||
vec![//-----|-------|-----------|-----------|--------|-----------------
|
vec![//-----|-------|-----------|-----------|--------|-----------------
|
||||||
(d(Root, Pkg, SU, m(S1, S8), None ), Depth(1)),
|
(d(Root, Pkg, SU, m(S1, S12), None ), Depth(1)),
|
||||||
(d(Pkg, Ident, m(S1, S8), S3, None ), Depth(2)),
|
(d(Pkg, Ident, m(S1, S12), S3, None ), Depth(2)),
|
||||||
(d(Ident, Tpl, S3, m(S2, S4), None ), Depth(3)),
|
(d(Ident, Tpl, S3, m(S2, S4), None ), Depth(3)),
|
||||||
(d(Pkg, Tpl, m(S1, S8), m(S5, S7), None ), Depth(2)),
|
(d(Pkg, Tpl, m(S1, S12), m(S5, S11), None ), Depth(2)),
|
||||||
(d(Tpl, Ident, m(S5, S7), S3, Some(S6)), Depth(3)),
|
/*cross*/ (d(Tpl, Ident, m(S5, S11), S3, Some(S6)), Depth(3)),
|
||||||
|
(d(Tpl, Ident, m(S5, S11), S8, None ), Depth(3)),
|
||||||
|
(d(Ident, Meta, S8, m(S7, S10), None ), Depth(4)),
|
||||||
],
|
],
|
||||||
tree_reconstruction_report(toks),
|
tree_reconstruction_report(toks),
|
||||||
);
|
);
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
//! or observing template expansions.
|
//! or observing template expansions.
|
||||||
|
|
||||||
use super::object::{
|
use super::object::{
|
||||||
DynObjectRel, Expr, Object, ObjectIndex, ObjectRelTy, OiPairObjectInner,
|
DynObjectRel, Expr, Meta, Object, ObjectIndex, ObjectRelTy,
|
||||||
Pkg, Tpl,
|
OiPairObjectInner, Pkg, Tpl,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
asg::{
|
asg::{
|
||||||
|
@ -39,7 +39,8 @@ use crate::{
|
||||||
Asg, ExprOp, Ident,
|
Asg, ExprOp, Ident,
|
||||||
},
|
},
|
||||||
diagnose::{panic::DiagnosticPanic, Annotate},
|
diagnose::{panic::DiagnosticPanic, Annotate},
|
||||||
diagnostic_panic, diagnostic_unreachable,
|
diagnostic_panic, diagnostic_todo, diagnostic_unreachable,
|
||||||
|
fmt::{DisplayWrapper, TtQuote},
|
||||||
parse::{prelude::*, util::SPair, Transitionable},
|
parse::{prelude::*, util::SPair, Transitionable},
|
||||||
span::{Span, UNKNOWN_SPAN},
|
span::{Span, UNKNOWN_SPAN},
|
||||||
sym::{
|
sym::{
|
||||||
|
@ -198,7 +199,9 @@ impl<'a> TreeContext<'a> {
|
||||||
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
|
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
target @ Object::Meta(..) => todo!("Object::Meta: {target:?}"),
|
Object::Meta((meta, oi_meta)) => {
|
||||||
|
self.emit_tpl_arg(meta, *oi_meta, depth)
|
||||||
|
}
|
||||||
|
|
||||||
Object::Root(..) => diagnostic_unreachable!(
|
Object::Root(..) => diagnostic_unreachable!(
|
||||||
vec![],
|
vec![],
|
||||||
|
@ -312,7 +315,11 @@ impl<'a> TreeContext<'a> {
|
||||||
// This also gives us the opportunity to make sure that
|
// This also gives us the opportunity to make sure that
|
||||||
// we're deriving something that's actually supported by the
|
// we're deriving something that's actually supported by the
|
||||||
// XSLT-based compiler.
|
// XSLT-based compiler.
|
||||||
let mut idents = oi_tpl.edges_filtered::<Ident>(self.asg);
|
// TODO: This doesn't handle `Missing` or invalid (non-`Tpl`)
|
||||||
|
// identifiers.
|
||||||
|
let mut idents = oi_tpl
|
||||||
|
.edges_filtered::<Ident>(self.asg)
|
||||||
|
.filter(|oi| oi.is_bound_to_kind::<Tpl>(self.asg));
|
||||||
|
|
||||||
let apply_tpl = idents.next().diagnostic_expect(
|
let apply_tpl = idents.next().diagnostic_expect(
|
||||||
|| {
|
|| {
|
||||||
|
@ -332,7 +339,7 @@ impl<'a> TreeContext<'a> {
|
||||||
bad_ident
|
bad_ident
|
||||||
.internal_error("unexpected second identifier"),
|
.internal_error("unexpected second identifier"),
|
||||||
],
|
],
|
||||||
"expected only one Ident for template application",
|
"expected only one Ident->Tpl for template application",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +356,47 @@ impl<'a> TreeContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Emit a long-form template argument.
|
||||||
|
///
|
||||||
|
/// For the parent template application,
|
||||||
|
/// see [`Self::emit_template`].
|
||||||
|
fn emit_tpl_arg(
|
||||||
|
&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(
|
||||||
|
"anonymous metavariables are not supported as template arguments"
|
||||||
|
)]);
|
||||||
|
|
||||||
|
let pval = match meta {
|
||||||
|
Meta::Required(span) => diagnostic_todo!(
|
||||||
|
vec![span.error("value expected for this param")],
|
||||||
|
"value missing for param {}",
|
||||||
|
TtQuote::wrap(pname)
|
||||||
|
),
|
||||||
|
|
||||||
|
Meta::ConcatList(span) => diagnostic_todo!(
|
||||||
|
vec![span.error("concatenation occurs here")],
|
||||||
|
"concatenation not yet supported in xmli for param {}",
|
||||||
|
TtQuote::wrap(pname)
|
||||||
|
),
|
||||||
|
|
||||||
|
Meta::Lexeme(_, value) => *value,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.push(attr_value(pval));
|
||||||
|
self.push(attr_name(pname));
|
||||||
|
|
||||||
|
Some(Xirf::open(
|
||||||
|
QN_WITH_PARAM,
|
||||||
|
OpenSpan::without_name_span(meta.span()),
|
||||||
|
depth,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
fn push(&mut self, tok: Xirf) {
|
fn push(&mut self, tok: Xirf) {
|
||||||
if self.stack.is_full() {
|
if self.stack.is_full() {
|
||||||
diagnostic_panic!(
|
diagnostic_panic!(
|
||||||
|
@ -408,6 +456,10 @@ fn attr_name(name: SPair) -> Xirf {
|
||||||
Xirf::attr(QN_NAME, name, (name.span(), name.span()))
|
Xirf::attr(QN_NAME, name, (name.span(), name.span()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn attr_value(val: SPair) -> Xirf {
|
||||||
|
Xirf::attr(QN_VALUE, val, (val.span(), val.span()))
|
||||||
|
}
|
||||||
|
|
||||||
fn expr_ele(expr: &Expr, depth: Depth) -> Xirf {
|
fn expr_ele(expr: &Expr, depth: Depth) -> Xirf {
|
||||||
use ExprOp::*;
|
use ExprOp::*;
|
||||||
|
|
||||||
|
|
|
@ -1674,21 +1674,34 @@ ele_parse! {
|
||||||
/// or even a mix of the two
|
/// or even a mix of the two
|
||||||
/// (with statements hoisted out of expressions).
|
/// (with statements hoisted out of expressions).
|
||||||
///
|
///
|
||||||
/// TODO: This is apparently unused by the current system,
|
/// See also [`TplApplyShort`],
|
||||||
/// in favor of a transition to [`TplApplyShort`],
|
/// which gets desugared into this via [`super::tplshort`].
|
||||||
/// but this is still needed to support dynamic template application
|
|
||||||
/// (templates whose names are derived from other template inputs).
|
|
||||||
ApplyTemplate := QN_APPLY_TEMPLATE(_, ospan) {
|
ApplyTemplate := QN_APPLY_TEMPLATE(_, ospan) {
|
||||||
@ {
|
@ {
|
||||||
QN_NAME => Ref,
|
QN_NAME => Ref,
|
||||||
} => Nir::Open(NirEntity::TplApply(None), ospan.into()),
|
} => Nir::Open(NirEntity::TplApply(None), ospan.into()),
|
||||||
/(cspan) => Nir::Close(NirEntity::TplApply(None), cspan.into()),
|
/(cspan) => Nir::Close(NirEntity::TplApply(None), cspan.into()),
|
||||||
|
|
||||||
// TODO: This is wrong, we just need something here for now.
|
ApplyTemplateParam,
|
||||||
AnyStmtOrExpr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Short-hand template application.
|
/// Long-form template argument.
|
||||||
|
///
|
||||||
|
/// Template arguments are lexical.
|
||||||
|
///
|
||||||
|
/// See also [`TplApplyShort`],
|
||||||
|
/// which gets desugared into this via [`super::tplshort`].
|
||||||
|
ApplyTemplateParam := QN_WITH_PARAM(_, ospan) {
|
||||||
|
@ {
|
||||||
|
QN_NAME => BindIdent,
|
||||||
|
QN_VALUE => Text,
|
||||||
|
} => Nir::Open(NirEntity::TplParam(None), ospan.into()),
|
||||||
|
/(cspan) => Nir::Close(NirEntity::TplParam(None), cspan.into()),
|
||||||
|
|
||||||
|
// TODO: Need to support children, e.g. @values@
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Shorthand template application.
|
||||||
///
|
///
|
||||||
/// This expands into an equivalent [`ApplyTemplate`] form where each
|
/// This expands into an equivalent [`ApplyTemplate`] form where each
|
||||||
/// attribute is a template argument,
|
/// attribute is a template argument,
|
||||||
|
|
|
@ -710,6 +710,7 @@ pub mod st {
|
||||||
L_VIRTUAL: cid "virtual",
|
L_VIRTUAL: cid "virtual",
|
||||||
L_WARNING: cid "warning",
|
L_WARNING: cid "warning",
|
||||||
L_WHEN: cid "when",
|
L_WHEN: cid "when",
|
||||||
|
L_WITH_PARAM: tid "with-param",
|
||||||
L_WORKSHEET: cid "worksheet",
|
L_WORKSHEET: cid "worksheet",
|
||||||
L_XMLNS: cid "xmlns",
|
L_XMLNS: cid "xmlns",
|
||||||
L_YIELD: cid "yield",
|
L_YIELD: cid "yield",
|
||||||
|
|
|
@ -225,6 +225,7 @@ pub mod qname {
|
||||||
QN_VALUES: :L_VALUES,
|
QN_VALUES: :L_VALUES,
|
||||||
QN_VIRTUAL: :L_VIRTUAL,
|
QN_VIRTUAL: :L_VIRTUAL,
|
||||||
QN_WARNING: :L_WARNING,
|
QN_WARNING: :L_WARNING,
|
||||||
|
QN_WITH_PARAM: :L_WITH_PARAM,
|
||||||
QN_WORKSHEET: :L_WORKSHEET,
|
QN_WORKSHEET: :L_WORKSHEET,
|
||||||
QN_YIELD: :L_YIELD,
|
QN_YIELD: :L_YIELD,
|
||||||
QN_YIELDS: :L_YIELDS,
|
QN_YIELDS: :L_YIELDS,
|
||||||
|
|
|
@ -66,6 +66,19 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template name="_short-hand-nullary_" />
|
||||||
<apply-template name="_short-hand-nullary_" />
|
<apply-template name="_short-hand-nullary_" />
|
||||||
|
|
||||||
|
<template name="_short-hand-unary_" />
|
||||||
|
<apply-template name="_short-hand-unary_">
|
||||||
|
<with-param name="@foo@" value="bar" />
|
||||||
|
</apply-template>
|
||||||
|
|
||||||
|
<template name="_short-hand-nary_" />
|
||||||
|
<apply-template name="_short-hand-nary_">
|
||||||
|
<with-param name="@foo@" value="bar" />
|
||||||
|
<with-param name="@bar@" value="baz" />
|
||||||
|
<with-param name="@baz@" value="quux" />
|
||||||
|
</apply-template>
|
||||||
</package>
|
</package>
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,20 @@
|
||||||
`apply-template` form,
|
`apply-template` form,
|
||||||
asserting their equivalency.
|
asserting their equivalency.
|
||||||
|
|
||||||
|
<template name="_short-hand-nullary_" />
|
||||||
<t:short-hand-nullary />
|
<t:short-hand-nullary />
|
||||||
|
|
||||||
|
<template name="_short-hand-unary_" />
|
||||||
|
<t:short-hand-unary foo="bar" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<template name="_short-hand-nary_" />
|
||||||
|
<t:short-hand-nary foo="bar" bar="baz" baz="quux" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<!-- TODO
|
<!-- TODO
|
||||||
<t:short-hand-nullary-body>
|
<t:short-hand-nullary-body>
|
||||||
<c:sum />
|
<c:sum />
|
||||||
|
@ -77,8 +89,6 @@
|
||||||
<t:inner-short />
|
<t:inner-short />
|
||||||
</t:short-hand-nullary-inner>
|
</t:short-hand-nullary-inner>
|
||||||
|
|
||||||
<t:short-hand foo="bar" />
|
|
||||||
|
|
||||||
<t:short-hand foo="bar">
|
<t:short-hand foo="bar">
|
||||||
<c:sum />
|
<c:sum />
|
||||||
</t:short-hand>
|
</t:short-hand>
|
||||||
|
|
Loading…
Reference in New Issue