2023-02-21 23:58:58 -05:00
|
|
|
|
// XML representation of graph objects
|
|
|
|
|
//
|
|
|
|
|
// Copyright (C) 2014-2023 Ryan Specialty, LLC.
|
|
|
|
|
//
|
|
|
|
|
// This file is part of TAME.
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
|
|
//! XML representation of graph objects via [XIR](crate::xir).
|
|
|
|
|
//!
|
|
|
|
|
//! Attempts to produce a representation of graph [`Object`]s that is
|
|
|
|
|
//! familiar to those writing TAME's XML-based source language.
|
|
|
|
|
//!
|
|
|
|
|
//! _This representation will change over time_ as TAME's source language
|
|
|
|
|
//! evolves.
|
|
|
|
|
//! There is no guarantee that this representation will stay over time;
|
|
|
|
|
//! it was written for transitional purposes,
|
|
|
|
|
//! but may be useful in the future for concrete code suggestions/fixes,
|
|
|
|
|
//! or observing template expansions.
|
|
|
|
|
|
2023-02-22 23:16:53 -05:00
|
|
|
|
use super::object::{
|
2023-02-24 13:17:16 -05:00
|
|
|
|
DynObjectRel, Expr, Object, ObjectIndex, ObjectRelTy, OiPairObjectInner,
|
2023-02-28 15:31:49 -05:00
|
|
|
|
Pkg, Tpl,
|
2023-02-22 23:16:53 -05:00
|
|
|
|
};
|
2023-02-21 23:58:58 -05:00
|
|
|
|
use crate::{
|
2023-02-22 23:03:42 -05:00
|
|
|
|
asg::{
|
|
|
|
|
visit::{Depth, TreeWalkRel},
|
2023-02-24 23:01:02 -05:00
|
|
|
|
Asg, ExprOp, Ident,
|
2023-02-22 23:03:42 -05:00
|
|
|
|
},
|
tamer: Very basic support for template application NIR -> xmli
This this a big change that's difficult to break up, and I don't have the
energy after it.
This introduces nullary template application, short- and long-form. Note
that a body of the short form is a `@values@` argument, so that's not
supported yet.
This continues to formalize the idea of what "template application" and
"template expansion" mean in TAMER. It makes a separate `TplApply`
unnecessary, because now application is simply a reference to a
template. Expansion and application are one and the same: when a template
expands, it'll re-bind metavariables to the parent context. So in a
template context, this amounts to application.
But applying a closed template will have nothing to bind, and so is
equivalent to expansion. And since `Meta` objects are not valid outside of
a `Tpl` context, applying a non-closed template outside of another template
will be invalid.
So we get all of this with a single primitive (getting the "value" of a
template).
The expansion is conceptually like `,@` in Lisp, where we're splicing trees.
It's a mess in some spots, but I want to get this committed before I do a
little bit of cleanup.
2023-03-17 10:25:56 -04:00
|
|
|
|
diagnose::{panic::DiagnosticPanic, Annotate},
|
2023-02-22 23:03:42 -05:00
|
|
|
|
diagnostic_panic, diagnostic_unreachable,
|
2023-02-28 15:31:49 -05:00
|
|
|
|
parse::{prelude::*, util::SPair, Transitionable},
|
2023-02-22 23:03:42 -05:00
|
|
|
|
span::{Span, UNKNOWN_SPAN},
|
|
|
|
|
sym::{
|
|
|
|
|
st::{URI_LV_CALC, URI_LV_RATER, URI_LV_TPL},
|
|
|
|
|
UriStaticSymbolId,
|
|
|
|
|
},
|
2023-02-21 23:58:58 -05:00
|
|
|
|
xir::{
|
|
|
|
|
flat::{Text, XirfToken},
|
2023-02-22 23:03:42 -05:00
|
|
|
|
st::qname::*,
|
2023-03-08 23:44:40 -05:00
|
|
|
|
CloseSpan, OpenSpan, QName,
|
2023-02-21 23:58:58 -05:00
|
|
|
|
},
|
|
|
|
|
};
|
2023-02-22 10:45:48 -05:00
|
|
|
|
use arrayvec::ArrayVec;
|
|
|
|
|
use std::{convert::Infallible, fmt::Display, marker::PhantomData};
|
2023-02-21 23:58:58 -05:00
|
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
|
|
|
pub enum AsgTreeToXirf<'a> {
|
|
|
|
|
Ready(PhantomData<&'a ()>),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Default for AsgTreeToXirf<'a> {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::Ready(PhantomData::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> Display for AsgTreeToXirf<'a> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
write!(f, "generating XIRF sources from ASG tree")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-22 23:03:42 -05:00
|
|
|
|
type Xirf = XirfToken<Text>;
|
|
|
|
|
|
2023-02-21 23:58:58 -05:00
|
|
|
|
impl<'a> ParseState for AsgTreeToXirf<'a> {
|
|
|
|
|
type Token = TreeWalkRel;
|
2023-02-22 23:03:42 -05:00
|
|
|
|
type Object = Xirf;
|
2023-02-21 23:58:58 -05:00
|
|
|
|
type Error = Infallible;
|
2023-02-22 09:00:41 -05:00
|
|
|
|
type Context = TreeContext<'a>;
|
2023-02-21 23:58:58 -05:00
|
|
|
|
|
|
|
|
|
fn parse_token(
|
|
|
|
|
self,
|
2023-02-24 13:54:25 -05:00
|
|
|
|
TreeWalkRel(dyn_rel, depth): Self::Token,
|
2023-02-24 15:05:37 -05:00
|
|
|
|
ctx: &mut TreeContext,
|
2023-02-21 23:58:58 -05:00
|
|
|
|
) -> TransitionResult<Self::Super> {
|
2023-02-24 15:05:37 -05:00
|
|
|
|
match (ctx.pop(), depth) {
|
2023-02-24 13:54:25 -05:00
|
|
|
|
// Empty the token stack before processing any further.
|
|
|
|
|
// Note that we must yield the token as lookahead to ensure that
|
|
|
|
|
// we do eventually process it.
|
|
|
|
|
(Some(emit), _) => Transition(self)
|
|
|
|
|
.ok(emit)
|
|
|
|
|
.with_lookahead(TreeWalkRel(dyn_rel, depth)),
|
|
|
|
|
|
|
|
|
|
// Used by `eof_tok` only to empty the token stack,
|
|
|
|
|
// which we're now done with.
|
|
|
|
|
// We consume the token by not yielding any lookahead.
|
|
|
|
|
(None, Depth(0)) => Transition(self).incomplete(),
|
|
|
|
|
|
|
|
|
|
// The stack is empty,
|
|
|
|
|
// so proceed with processing the provided relation.
|
2023-02-24 15:05:37 -05:00
|
|
|
|
(None, depth) => ctx.derive_src(dyn_rel, depth).transition(self),
|
2023-02-24 13:54:25 -05:00
|
|
|
|
}
|
2023-02-21 23:58:58 -05:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn is_accepting(&self, ctx: &Self::Context) -> bool {
|
|
|
|
|
ctx.stack_is_empty()
|
2023-02-22 23:03:42 -05:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn eof_tok(&self, ctx: &Self::Context) -> Option<Self::Token> {
|
2023-02-22 23:03:42 -05:00
|
|
|
|
// If the stack is not empty on EOF,
|
|
|
|
|
// yield a dummy token just to invoke `parse_token` to finish
|
|
|
|
|
// emptying it.
|
2023-02-24 15:05:37 -05:00
|
|
|
|
(!ctx.stack_is_empty()).then_some(TreeWalkRel(
|
2023-02-22 23:03:42 -05:00
|
|
|
|
DynObjectRel::new(
|
|
|
|
|
ObjectRelTy::Root,
|
|
|
|
|
ObjectRelTy::Root,
|
|
|
|
|
ObjectIndex::new(0.into(), UNKNOWN_SPAN),
|
tamer: asg::graph::object::rel::DynObjectRel: Store source data
This is generic over the source, just as the target, defaulting just the
same to `ObjectIndex`.
This allows us to use only the edge information provided rather than having
to perform another lookup on the graph and then assert that we found the
correct edge. In this case, we're dealing with an `Ident->Expr` edge, of
which there is only one, but in other cases, there may be many such edges,
and it wouldn't be possible to know _which_ was referred to without also
keeping context of the previous edge in the walk.
So, in addition to avoiding more indirection and being more immune to logic
bugs, this also allows us to avoid states in `AsgTreeToXirf` for the purpose
of tracking previous edges in the current path. And it means that the tree
walk can seed further traversals in conjunction with it, if that is so
needed for deriving sources.
More cleanup will be needed, but this does well to set us up for moving
forward; I was too uncomfortable with having to do the separate
lookup. This is also a more intuitive API.
But it does have the awkward effect that now I don't need the pair---I just
need the `Object`---but I'm not going to remove it because I suspect I may
need it in the future. We'll see.
The TODO references the fact that I'm using a convenient `resolve_oi_pairs`
instead of resolving only the target first and then the source only in the
code path that needs it. I'll want to verify that Rust will properly
optimize to avoid the source resolution in branches that do not need it.
DEV-13708
2023-02-23 22:45:09 -05:00
|
|
|
|
ObjectIndex::new(0.into(), UNKNOWN_SPAN),
|
2023-02-22 23:03:42 -05:00
|
|
|
|
None,
|
|
|
|
|
),
|
|
|
|
|
// This is the only part that really matters;
|
|
|
|
|
// the tree walk will never yield a depth of 0.
|
|
|
|
|
Depth(0),
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-24 13:17:16 -05:00
|
|
|
|
|
2023-02-24 13:54:25 -05:00
|
|
|
|
/// Size of the token stack.
|
|
|
|
|
///
|
|
|
|
|
/// See [`TokenStack`] for more information.
|
|
|
|
|
const TOK_STACK_SIZE: usize = 8;
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
/// Token stack to hold generated tokens between [`AsgTreeToXirf`]
|
|
|
|
|
/// iterations.
|
2023-02-24 13:54:25 -05:00
|
|
|
|
///
|
2023-02-24 15:05:37 -05:00
|
|
|
|
/// The token stack is used to avoid having to create separate states for
|
|
|
|
|
/// emitting each individual token.
|
|
|
|
|
/// It is populated by [`AsgTreeToXirf`] if more than a single [`XirfToken`]
|
|
|
|
|
/// needs to be emitted,
|
|
|
|
|
/// and tokens are removed on each subsequent iteration until empty.
|
2023-02-24 13:54:25 -05:00
|
|
|
|
///
|
2023-02-24 15:05:37 -05:00
|
|
|
|
/// This need only be big enough to accommodate [`AsgTreeToXirf`]'s
|
|
|
|
|
/// implementation;
|
|
|
|
|
/// the size is independent of user input.
|
|
|
|
|
type TokenStack = ArrayVec<Xirf, TOK_STACK_SIZE>;
|
2023-02-24 13:54:25 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
pub struct TreeContext<'a> {
|
|
|
|
|
stack: TokenStack,
|
|
|
|
|
asg: &'a Asg,
|
2023-02-24 13:54:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
impl<'a> TreeContext<'a> {
|
|
|
|
|
/// Given a [`DynObjectRel`],
|
|
|
|
|
/// derive a legacy TAME representation that will faithfully represent
|
|
|
|
|
/// an equivalent program when compiled by the XSLT-based TAME
|
|
|
|
|
/// compiler.
|
|
|
|
|
///
|
|
|
|
|
/// The [`TokenStack`] may be used to pre-generate [XIRF](Xirf) to be
|
|
|
|
|
/// yielded on subsequent iterations rather than having to introduce
|
|
|
|
|
/// [`AsgTreeToXirf`] states for each individual token.
|
|
|
|
|
/// Adjust [`TOK_STACK_SIZE`] as necessary.
|
|
|
|
|
///
|
|
|
|
|
/// The provided [`Depth`] represent the depth of the tree at the
|
|
|
|
|
/// position of the provided [`DynObjectRel`].
|
|
|
|
|
/// See [`TreeWalkRel`] for more information.
|
|
|
|
|
fn derive_src(
|
|
|
|
|
&mut self,
|
|
|
|
|
dyn_rel: DynObjectRel,
|
|
|
|
|
depth: Depth,
|
|
|
|
|
) -> Option<Xirf> {
|
|
|
|
|
// TODO: Verify that the binary does not perform unnecessary
|
|
|
|
|
// resolution in branches that do not utilize the source.
|
|
|
|
|
let paired_rel = dyn_rel.resolve_oi_pairs(self.asg);
|
|
|
|
|
|
|
|
|
|
match paired_rel.target() {
|
|
|
|
|
Object::Pkg((pkg, _)) => self.emit_package(pkg, depth),
|
|
|
|
|
|
|
|
|
|
// Identifiers will be considered in context;
|
|
|
|
|
// pass over it for now.
|
2023-03-08 23:44:40 -05:00
|
|
|
|
// 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,
|
|
|
|
|
)),
|
2023-02-24 15:05:37 -05:00
|
|
|
|
|
|
|
|
|
Object::Expr((expr, _)) => {
|
|
|
|
|
self.emit_expr(expr, paired_rel.source(), depth)
|
|
|
|
|
}
|
2023-02-24 13:17:16 -05:00
|
|
|
|
|
tamer: Very basic support for template application NIR -> xmli
This this a big change that's difficult to break up, and I don't have the
energy after it.
This introduces nullary template application, short- and long-form. Note
that a body of the short form is a `@values@` argument, so that's not
supported yet.
This continues to formalize the idea of what "template application" and
"template expansion" mean in TAMER. It makes a separate `TplApply`
unnecessary, because now application is simply a reference to a
template. Expansion and application are one and the same: when a template
expands, it'll re-bind metavariables to the parent context. So in a
template context, this amounts to application.
But applying a closed template will have nothing to bind, and so is
equivalent to expansion. And since `Meta` objects are not valid outside of
a `Tpl` context, applying a non-closed template outside of another template
will be invalid.
So we get all of this with a single primitive (getting the "value" of a
template).
The expansion is conceptually like `,@` in Lisp, where we're splicing trees.
It's a mess in some spots, but I want to get this committed before I do a
little bit of cleanup.
2023-03-17 10:25:56 -04:00
|
|
|
|
Object::Tpl((tpl, oi_tpl)) => {
|
|
|
|
|
self.emit_template(tpl, *oi_tpl, paired_rel.source(), depth)
|
2023-02-28 15:31:49 -05:00
|
|
|
|
}
|
2023-02-24 23:54:01 -05:00
|
|
|
|
|
tamer: src::asg: Scaffolding for metasyntactic variables
Also known as metavariables or template parameters.
This is a bit of a tortured excursion, trying to figure out how I want to
best represent this. I have a number of pages of hand-written notes that
I'd like to distill over time, but the rendered graph ontology (via
`asg-ontviz`) demonstrates the broad idea.
`AirTpl::TplApply` highlights some remaining questions. What I had _wanted_
to do is to separate the concepts of application and expansion, and support
partial application and such. But it's going to be too much work for now,
when it isn't needed---partial application can be worked around by simply
creating new templates and duplicating params, as we do today, although that
sucks and is a maintenance issue. But I'd rather address that head-on in
the future.
So it's looking like Option B is going to be the approach for now, with
templates being closed (as in, no free metavariables) and expanded at the
same time. This simplifies the parser and error conditions significantly
and makes it easier to utilize anonymous templates, since it'll still be the
active context.
My intent is to get at least the graph construction sorted out---not the
actual expansion and binding yet---enough that I can use templates to
represent parts of NIR that do not have proper graph representations or
desugaring yet, so that I can spit them back out again in the `xmli` file
and incrementally handle them. That was an option I had considered some
months ago, but didn't want to entertain it at the time because I wasn't
sure what doing so would look like; while it was an attractive approach
since it pushes existing primitives into the template system (something I've
wanted to do for years), I didn't want to potentially tank performance or
compromise the design for it after I had spent so much effort on all of this
so far.
But my efforts have yielded a system that significantly exceeds my initial
performance expectations, with a decent abstractions, and so this seems
viable.
DEV-13708
2023-03-15 11:49:13 -04:00
|
|
|
|
target @ Object::Meta(..) => todo!("Object::Meta: {target:?}"),
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
Object::Root(..) => diagnostic_unreachable!(
|
|
|
|
|
vec![],
|
|
|
|
|
"tree walk is not expected to emit Root",
|
|
|
|
|
),
|
2023-02-24 13:17:16 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-22 23:03:42 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
/// Emit tokens representing the root package element.
|
|
|
|
|
fn emit_package(&mut self, pkg: &Pkg, depth: Depth) -> Option<Xirf> {
|
|
|
|
|
let span = pkg.span();
|
2023-02-22 23:03:42 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
self.push_all([
|
|
|
|
|
ns(QN_XMLNS_T, URI_LV_TPL, span),
|
|
|
|
|
ns(QN_XMLNS_C, URI_LV_CALC, span),
|
|
|
|
|
ns(QN_XMLNS, URI_LV_RATER, span),
|
|
|
|
|
]);
|
2023-02-22 23:03:42 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
Some(package(pkg, depth))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Emit an expression as a legacy TAME statement or expression.
|
|
|
|
|
///
|
|
|
|
|
/// Identified expressions must be represented using statements in
|
|
|
|
|
/// legacy TAME,
|
|
|
|
|
/// such as `<rate>`.
|
|
|
|
|
/// Anonymous expressions are nested within statements.
|
|
|
|
|
///
|
|
|
|
|
/// This system will emit statements and expressions that are compatible
|
|
|
|
|
/// with the information on the [ASG](crate::asg) and recognized by the
|
|
|
|
|
/// downstream XSLT-based compiler.
|
|
|
|
|
/// There is no guarantee,
|
|
|
|
|
/// however,
|
|
|
|
|
/// that what is emitted is exactly representative of what the user
|
|
|
|
|
/// originally entered.
|
|
|
|
|
///
|
|
|
|
|
/// Please ensure that the system matches your expectations using the system
|
|
|
|
|
/// tests in `:tamer/tests/xmli`.
|
|
|
|
|
fn emit_expr(
|
|
|
|
|
&mut self,
|
|
|
|
|
expr: &Expr,
|
|
|
|
|
src: &Object<OiPairObjectInner>,
|
|
|
|
|
depth: Depth,
|
|
|
|
|
) -> Option<Xirf> {
|
|
|
|
|
match src {
|
|
|
|
|
Object::Ident((ident, _)) => {
|
2023-02-24 23:01:02 -05:00
|
|
|
|
self.emit_expr_ident(expr, ident, depth)
|
2023-02-24 15:05:37 -05:00
|
|
|
|
}
|
|
|
|
|
_ => Some(expr_ele(expr, depth)),
|
2023-02-22 23:03:42 -05:00
|
|
|
|
}
|
2023-02-24 15:05:37 -05:00
|
|
|
|
}
|
2023-02-22 23:03:42 -05:00
|
|
|
|
|
2023-02-24 23:01:02 -05:00
|
|
|
|
/// Emit an identified expression.
|
|
|
|
|
///
|
|
|
|
|
/// Legacy TAME is only able to bind certain identifiers via statements
|
|
|
|
|
/// such as `rate` and `classify`.
|
|
|
|
|
fn emit_expr_ident(
|
|
|
|
|
&mut self,
|
|
|
|
|
expr: &Expr,
|
|
|
|
|
ident: &Ident,
|
|
|
|
|
depth: Depth,
|
|
|
|
|
) -> Option<Xirf> {
|
|
|
|
|
let (qname, ident_qname) = match expr.op() {
|
|
|
|
|
ExprOp::Sum => (QN_RATE, QN_YIELDS),
|
|
|
|
|
ExprOp::Conj => (QN_CLASSIFY, QN_AS),
|
|
|
|
|
|
2023-03-09 22:28:26 -05:00
|
|
|
|
ExprOp::Product | ExprOp::Ceil | ExprOp::Floor | ExprOp::Disj => {
|
|
|
|
|
todo!("stmt: {expr:?}")
|
|
|
|
|
}
|
2023-02-24 23:01:02 -05:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let ispan = ident.span();
|
|
|
|
|
self.push(Xirf::attr(ident_qname, ident.name(), (ispan, ispan)));
|
|
|
|
|
|
|
|
|
|
Some(Xirf::open(
|
|
|
|
|
qname,
|
|
|
|
|
OpenSpan::without_name_span(expr.span()),
|
|
|
|
|
depth,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
tamer: Very basic support for template application NIR -> xmli
This this a big change that's difficult to break up, and I don't have the
energy after it.
This introduces nullary template application, short- and long-form. Note
that a body of the short form is a `@values@` argument, so that's not
supported yet.
This continues to formalize the idea of what "template application" and
"template expansion" mean in TAMER. It makes a separate `TplApply`
unnecessary, because now application is simply a reference to a
template. Expansion and application are one and the same: when a template
expands, it'll re-bind metavariables to the parent context. So in a
template context, this amounts to application.
But applying a closed template will have nothing to bind, and so is
equivalent to expansion. And since `Meta` objects are not valid outside of
a `Tpl` context, applying a non-closed template outside of another template
will be invalid.
So we get all of this with a single primitive (getting the "value" of a
template).
The expansion is conceptually like `,@` in Lisp, where we're splicing trees.
It's a mess in some spots, but I want to get this committed before I do a
little bit of cleanup.
2023-03-17 10:25:56 -04:00
|
|
|
|
/// Emit a template definition or application.
|
2023-02-28 15:31:49 -05:00
|
|
|
|
fn emit_template(
|
|
|
|
|
&mut self,
|
|
|
|
|
tpl: &Tpl,
|
tamer: Very basic support for template application NIR -> xmli
This this a big change that's difficult to break up, and I don't have the
energy after it.
This introduces nullary template application, short- and long-form. Note
that a body of the short form is a `@values@` argument, so that's not
supported yet.
This continues to formalize the idea of what "template application" and
"template expansion" mean in TAMER. It makes a separate `TplApply`
unnecessary, because now application is simply a reference to a
template. Expansion and application are one and the same: when a template
expands, it'll re-bind metavariables to the parent context. So in a
template context, this amounts to application.
But applying a closed template will have nothing to bind, and so is
equivalent to expansion. And since `Meta` objects are not valid outside of
a `Tpl` context, applying a non-closed template outside of another template
will be invalid.
So we get all of this with a single primitive (getting the "value" of a
template).
The expansion is conceptually like `,@` in Lisp, where we're splicing trees.
It's a mess in some spots, but I want to get this committed before I do a
little bit of cleanup.
2023-03-17 10:25:56 -04:00
|
|
|
|
oi_tpl: ObjectIndex<Tpl>,
|
2023-02-28 15:31:49 -05:00
|
|
|
|
src: &Object<OiPairObjectInner>,
|
|
|
|
|
depth: Depth,
|
|
|
|
|
) -> Option<Xirf> {
|
|
|
|
|
match src {
|
|
|
|
|
Object::Ident((ident, _)) => {
|
|
|
|
|
self.push(attr_name(ident.name()));
|
|
|
|
|
|
|
|
|
|
Some(Xirf::open(
|
|
|
|
|
QN_TEMPLATE,
|
|
|
|
|
OpenSpan::without_name_span(tpl.span()),
|
|
|
|
|
depth,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
tamer: Very basic support for template application NIR -> xmli
This this a big change that's difficult to break up, and I don't have the
energy after it.
This introduces nullary template application, short- and long-form. Note
that a body of the short form is a `@values@` argument, so that's not
supported yet.
This continues to formalize the idea of what "template application" and
"template expansion" mean in TAMER. It makes a separate `TplApply`
unnecessary, because now application is simply a reference to a
template. Expansion and application are one and the same: when a template
expands, it'll re-bind metavariables to the parent context. So in a
template context, this amounts to application.
But applying a closed template will have nothing to bind, and so is
equivalent to expansion. And since `Meta` objects are not valid outside of
a `Tpl` context, applying a non-closed template outside of another template
will be invalid.
So we get all of this with a single primitive (getting the "value" of a
template).
The expansion is conceptually like `,@` in Lisp, where we're splicing trees.
It's a mess in some spots, but I want to get this committed before I do a
little bit of cleanup.
2023-03-17 10:25:56 -04:00
|
|
|
|
// If we're not behind an Ident,
|
|
|
|
|
// then this is a direct template reference,
|
|
|
|
|
// which indicates application of a closed template
|
|
|
|
|
// (template expansion).
|
|
|
|
|
// Convert this into a long-hand template expansion so that we
|
|
|
|
|
// do not have to deal with converting underscore-padded
|
|
|
|
|
// template names back into short-hand form.
|
|
|
|
|
Object::Pkg(..) => {
|
|
|
|
|
// [`Ident`]s are skipped during traversal,
|
|
|
|
|
// so we'll handle it ourselves here.
|
|
|
|
|
// This also gives us the opportunity to make sure that
|
|
|
|
|
// we're deriving something that's actually supported by the
|
|
|
|
|
// XSLT-based compiler.
|
|
|
|
|
let mut idents = oi_tpl.edges_filtered::<Ident>(self.asg);
|
|
|
|
|
|
|
|
|
|
let apply_tpl = idents.next().diagnostic_expect(
|
|
|
|
|
|| {
|
|
|
|
|
vec![tpl
|
|
|
|
|
.span()
|
|
|
|
|
.internal_error("missing target Tpl Ident")]
|
|
|
|
|
},
|
|
|
|
|
"cannot derive name of template for application",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if let Some(bad_ident) = idents.next() {
|
|
|
|
|
diagnostic_panic!(
|
|
|
|
|
vec![
|
|
|
|
|
tpl.span().note(
|
|
|
|
|
"while processing this template application"
|
|
|
|
|
),
|
|
|
|
|
bad_ident
|
|
|
|
|
.internal_error("unexpected second identifier"),
|
|
|
|
|
],
|
|
|
|
|
"expected only one Ident for template application",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.push(attr_name(apply_tpl.resolve(self.asg).name()));
|
|
|
|
|
|
|
|
|
|
Some(Xirf::open(
|
|
|
|
|
QN_APPLY_TEMPLATE,
|
|
|
|
|
OpenSpan::without_name_span(tpl.span()),
|
|
|
|
|
depth,
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-28 15:31:49 -05:00
|
|
|
|
_ => todo!("emit_template: {src:?}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn push(&mut self, tok: Xirf) {
|
|
|
|
|
if self.stack.is_full() {
|
|
|
|
|
diagnostic_panic!(
|
|
|
|
|
vec![tok
|
|
|
|
|
.internal_error("while emitting a token for this object")],
|
|
|
|
|
"token stack exhausted (increase TOK_STACK_SIZE)",
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self.stack.push(tok)
|
2023-02-22 23:03:42 -05:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn pop(&mut self) -> Option<Xirf> {
|
|
|
|
|
self.stack.pop()
|
|
|
|
|
}
|
2023-02-22 23:03:42 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn stack_is_empty(&self) -> bool {
|
|
|
|
|
self.stack.is_empty()
|
|
|
|
|
}
|
2023-02-22 23:45:23 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn push_all(&mut self, toks: impl IntoIterator<Item = Xirf>) {
|
|
|
|
|
toks.into_iter().for_each(|x| self.push(x))
|
|
|
|
|
}
|
2023-02-22 23:45:23 -05:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-22 23:03:42 -05:00
|
|
|
|
// Custom `Debug` impl to omit ASG rendering,
|
|
|
|
|
// since it's large and already included while rendering other parts of
|
|
|
|
|
// the lowering pipeline.
|
|
|
|
|
// Of course,
|
|
|
|
|
// that's assuming this is part of the lowering pipeline.
|
|
|
|
|
impl<'a> std::fmt::Debug for TreeContext<'a> {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
2023-02-24 15:05:37 -05:00
|
|
|
|
f.debug_struct("TreeContext")
|
|
|
|
|
.field("stack", &self.stack)
|
|
|
|
|
.finish_non_exhaustive()
|
2023-02-22 23:03:42 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> From<&'a Asg> for TreeContext<'a> {
|
|
|
|
|
fn from(asg: &'a Asg) -> Self {
|
2023-02-24 15:05:37 -05:00
|
|
|
|
TreeContext {
|
|
|
|
|
stack: Default::default(),
|
|
|
|
|
asg,
|
|
|
|
|
}
|
2023-02-21 23:58:58 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-22 09:00:41 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn package(pkg: &Pkg, depth: Depth) -> Xirf {
|
|
|
|
|
Xirf::open(QN_PACKAGE, OpenSpan::without_name_span(pkg.span()), depth)
|
|
|
|
|
}
|
2023-02-22 09:00:41 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn ns(qname: QName, uri: UriStaticSymbolId, span: Span) -> Xirf {
|
|
|
|
|
Xirf::attr(qname, uri, (span, span))
|
|
|
|
|
}
|
2023-02-23 09:31:21 -05:00
|
|
|
|
|
2023-02-28 15:31:49 -05:00
|
|
|
|
fn attr_name(name: SPair) -> Xirf {
|
|
|
|
|
Xirf::attr(QN_NAME, name, (name.span(), name.span()))
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
fn expr_ele(expr: &Expr, depth: Depth) -> Xirf {
|
2023-02-24 23:01:02 -05:00
|
|
|
|
use ExprOp::*;
|
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
let qname = match expr.op() {
|
2023-02-24 23:01:02 -05:00
|
|
|
|
Sum => QN_C_SUM,
|
|
|
|
|
Product => QN_C_PRODUCT,
|
2023-03-09 22:28:26 -05:00
|
|
|
|
Ceil => QN_C_CEIL,
|
|
|
|
|
Floor => QN_C_FLOOR,
|
2023-02-24 23:01:02 -05:00
|
|
|
|
Conj => QN_ALL,
|
|
|
|
|
Disj => QN_ANY,
|
2023-02-24 15:05:37 -05:00
|
|
|
|
};
|
2023-02-23 09:31:21 -05:00
|
|
|
|
|
2023-02-24 15:05:37 -05:00
|
|
|
|
Xirf::open(qname, OpenSpan::without_name_span(expr.span()), depth)
|
2023-02-22 09:00:41 -05:00
|
|
|
|
}
|
2023-02-22 23:03:42 -05:00
|
|
|
|
|
|
|
|
|
// System tests covering this functionality can be found in
|
2023-02-24 15:55:25 -05:00
|
|
|
|
// `:tamer/tests/xir/`.
|