2023-02-24 23:54:01 -05:00
|
|
|
|
// Templates represented on the ASG
|
|
|
|
|
//
|
|
|
|
|
// 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/>.
|
|
|
|
|
|
|
|
|
|
//! Templates on the ASG.
|
|
|
|
|
|
|
|
|
|
use std::fmt::Display;
|
|
|
|
|
|
2023-04-12 10:03:37 -04:00
|
|
|
|
use super::{prelude::*, Doc, Expr, Ident};
|
2023-07-27 01:44:12 -04:00
|
|
|
|
use crate::{
|
|
|
|
|
f::{Map, TryMap},
|
|
|
|
|
parse::util::SPair,
|
|
|
|
|
span::Span,
|
|
|
|
|
};
|
2023-02-24 23:54:01 -05:00
|
|
|
|
|
2023-02-28 15:31:49 -05:00
|
|
|
|
/// Template with associated name.
|
2023-02-24 23:54:01 -05:00
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
2023-07-25 12:27:17 -04:00
|
|
|
|
pub struct Tpl(Span, TplShape);
|
2023-02-24 23:54:01 -05:00
|
|
|
|
|
|
|
|
|
impl Tpl {
|
2023-07-25 12:27:17 -04:00
|
|
|
|
pub fn new(span: Span) -> Self {
|
|
|
|
|
Self(span, TplShape::default())
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-24 23:54:01 -05:00
|
|
|
|
pub fn span(&self) -> Span {
|
2023-02-28 15:31:49 -05:00
|
|
|
|
match self {
|
2023-07-25 12:27:17 -04:00
|
|
|
|
Self(span, _) => *span,
|
2023-02-28 15:31:49 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-25 12:27:17 -04:00
|
|
|
|
pub fn shape(&self) -> TplShape {
|
|
|
|
|
match self {
|
|
|
|
|
Self(_, shape) => *shape,
|
|
|
|
|
}
|
2023-02-28 15:31:49 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-27 02:35:47 -04:00
|
|
|
|
impl_mono_map! {
|
|
|
|
|
Span => Tpl(@, shape),
|
|
|
|
|
TplShape => Tpl(span, @),
|
2023-02-24 23:54:01 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Display for Tpl {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
2023-07-25 12:27:17 -04:00
|
|
|
|
let Self(_, shape) = self;
|
|
|
|
|
write!(f, "template with {shape}")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// The "shape" of a template when expanded into an expression context.
|
|
|
|
|
///
|
|
|
|
|
/// The shape of a template can be thought of like a puzzle piece.
|
|
|
|
|
/// Each application context permits a particular type of puzzle piece,
|
|
|
|
|
/// and a compatible template must be expanded into it,
|
|
|
|
|
/// or otherwise be made to be compatible.
|
|
|
|
|
///
|
|
|
|
|
/// Template shapes must be known statically by the time the definition has
|
|
|
|
|
/// completed.
|
|
|
|
|
/// A definition is not complete until all missing identifier references
|
|
|
|
|
/// have been defined.
|
|
|
|
|
/// A corollary of this is that templates applied _within_ templates will
|
|
|
|
|
/// be able to determine their shape because the shape of the applied
|
|
|
|
|
/// template will be known,
|
|
|
|
|
/// allowing them to compose without compromising this property.
|
|
|
|
|
///
|
|
|
|
|
/// Objects that would typically be hoisted out of an expression context do
|
|
|
|
|
/// not contribute to the shape of a template.
|
|
|
|
|
/// That is---
|
|
|
|
|
/// if an object would not typically be parented to the expansion context
|
|
|
|
|
/// if manually written at that source location,
|
|
|
|
|
/// then it will not be parented by a template expansion,
|
|
|
|
|
/// and so will not contribute to its shape.
|
|
|
|
|
///
|
|
|
|
|
/// Dynamic Inner Template Application
|
|
|
|
|
/// ==================================
|
|
|
|
|
/// Sometimes the shape of inner applications cannot be known because their
|
|
|
|
|
/// application depends on values of metavariables that are provided by
|
|
|
|
|
/// the caller.
|
|
|
|
|
/// One such example is that the body of the template is conditional
|
|
|
|
|
/// depending on what values are provided to the template.
|
|
|
|
|
///
|
|
|
|
|
/// In this case,
|
|
|
|
|
/// it may be necessary for the body of the template to _coerce_ into a
|
|
|
|
|
/// statically known shape by wrapping the dynamic application in a known
|
|
|
|
|
/// object.
|
|
|
|
|
/// For example,
|
|
|
|
|
/// if a template's body can conditionally expand into one of a set of
|
|
|
|
|
/// [`TplShape::Expr`] templates,
|
|
|
|
|
/// then that condition can be wrapped in an [`Expr`] object so that,
|
|
|
|
|
/// no matter what the expansion,
|
|
|
|
|
/// we'll always have a shape of [`TplShape::Expr`].
|
|
|
|
|
///
|
|
|
|
|
/// Expansion Ordering
|
|
|
|
|
/// ==================
|
|
|
|
|
/// By requiring a shape to be available by the time the definition of a
|
|
|
|
|
/// template is completed,
|
|
|
|
|
/// a system like [`AIR`](crate::asg::air) is able to pre-allocate an
|
|
|
|
|
/// [`Object`] at the application site.
|
|
|
|
|
/// This ensures that we are able to generate a graph with the proper edge
|
|
|
|
|
/// ordering,
|
|
|
|
|
/// which is important for non-commutative objects.
|
|
|
|
|
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
|
|
|
|
|
pub enum TplShape {
|
|
|
|
|
/// The template will not inline any objects.
|
|
|
|
|
#[default]
|
|
|
|
|
Empty,
|
|
|
|
|
|
|
|
|
|
/// The template is non-[`Empty`](Self::Empty),
|
|
|
|
|
/// but its shape cannot yet be determined.
|
|
|
|
|
///
|
|
|
|
|
/// A template's shape must be known by the time its definition has been
|
|
|
|
|
/// completed.
|
|
|
|
|
/// Note that a definition is not complete until all missing identifiers
|
|
|
|
|
/// have been defined.
|
|
|
|
|
Unknown,
|
|
|
|
|
|
|
|
|
|
/// The template can be expanded inline into a single [`Expr`].
|
|
|
|
|
///
|
|
|
|
|
/// This allows a template to be expanded into an expression context and
|
|
|
|
|
/// provides assurances that it will not take the place of more than a
|
|
|
|
|
/// single expression.
|
|
|
|
|
///
|
|
|
|
|
/// The associated span provides rationale for this shape assertion.
|
|
|
|
|
/// The [`ObjectIndex`] is not cached here to avoid having to keep them
|
|
|
|
|
/// in sync if the graph changes,
|
|
|
|
|
/// in which case this rationale may represent the _original_
|
|
|
|
|
/// rationale before any graph rewriting.
|
|
|
|
|
Expr(Span),
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-26 16:09:17 -04:00
|
|
|
|
impl TplShape {
|
2023-07-27 01:44:12 -04:00
|
|
|
|
/// Attempt to adapt a template shape to that of another.
|
|
|
|
|
///
|
|
|
|
|
/// If the shape of `other` is a refinement of the shape of `self`,
|
|
|
|
|
/// then `other` will be chosen.
|
|
|
|
|
/// If the shape of `other` conflicts with `self`,
|
|
|
|
|
/// an appropriate [`AsgError`] will describe the problem.
|
2023-07-26 16:09:17 -04:00
|
|
|
|
fn try_adapt_to(
|
|
|
|
|
self,
|
|
|
|
|
other: TplShape,
|
|
|
|
|
tpl_name: Option<SPair>,
|
2023-07-27 01:44:12 -04:00
|
|
|
|
) -> Result<Self, (Self, AsgError)> {
|
2023-07-26 16:09:17 -04:00
|
|
|
|
match (self, other) {
|
2023-07-27 01:44:12 -04:00
|
|
|
|
(TplShape::Expr(first_span), TplShape::Expr(bad_span)) => Err((
|
|
|
|
|
self,
|
|
|
|
|
AsgError::TplShapeExprMulti(tpl_name, bad_span, first_span),
|
|
|
|
|
)),
|
2023-07-26 16:09:17 -04:00
|
|
|
|
|
|
|
|
|
// Higher levels of specificity take precedence.
|
|
|
|
|
(shape @ TplShape::Expr(_), TplShape::Empty)
|
|
|
|
|
| (TplShape::Empty, shape @ TplShape::Expr(_))
|
|
|
|
|
| (shape @ TplShape::Empty, TplShape::Empty) => Ok(shape),
|
|
|
|
|
|
|
|
|
|
// Unknown is not yet handled.
|
|
|
|
|
(
|
|
|
|
|
TplShape::Unknown,
|
|
|
|
|
TplShape::Empty | TplShape::Unknown | TplShape::Expr(_),
|
|
|
|
|
)
|
|
|
|
|
| (TplShape::Empty | TplShape::Expr(_), TplShape::Unknown) => {
|
|
|
|
|
todo!("TplShape::Unknown")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-27 01:44:12 -04:00
|
|
|
|
/// If the shape stores [`Span`] information as evidence of inference,
|
|
|
|
|
/// overwrite it with the provided `span`.
|
|
|
|
|
///
|
|
|
|
|
/// This is most commonly used to encapsulate a previous shape
|
|
|
|
|
/// inference.
|
|
|
|
|
/// For example,
|
|
|
|
|
/// a template application's span may overwrite the inferred shape of
|
|
|
|
|
/// its own body.
|
2023-07-26 16:09:17 -04:00
|
|
|
|
fn overwrite_span_if_any(self, span: Span) -> Self {
|
|
|
|
|
match self {
|
|
|
|
|
TplShape::Empty | TplShape::Unknown => self,
|
|
|
|
|
TplShape::Expr(_) => TplShape::Expr(span),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-25 12:27:17 -04:00
|
|
|
|
impl Display for TplShape {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
// phrase as "template with ..."
|
|
|
|
|
match self {
|
|
|
|
|
TplShape::Unknown => write!(f, "unknown shape"),
|
|
|
|
|
TplShape::Empty => write!(f, "empty shape"),
|
|
|
|
|
TplShape::Expr(_) => write!(f, "shape of a single expression"),
|
|
|
|
|
}
|
2023-02-24 23:54:01 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-25 23:56:05 -05:00
|
|
|
|
object_rel! {
|
2023-03-08 11:18:51 -05:00
|
|
|
|
/// Templates may expand into nearly any context,
|
|
|
|
|
/// and must therefore be able to contain just about anything.
|
2023-02-25 23:56:05 -05:00
|
|
|
|
Tpl -> {
|
2023-03-23 00:04:53 -04:00
|
|
|
|
// Expressions must be able to be anonymous to allow templates in
|
|
|
|
|
// any `Expr` context.
|
2023-07-25 15:35:57 -04:00
|
|
|
|
tree Expr {
|
|
|
|
|
fn pre_add_edge(
|
|
|
|
|
asg: &mut Asg,
|
|
|
|
|
from_oi: ObjectIndex<Self>,
|
|
|
|
|
to_oi: ObjectIndex<Expr>,
|
|
|
|
|
_ctx_span: Option<Span>,
|
|
|
|
|
commit: impl FnOnce(&mut Asg),
|
|
|
|
|
) -> Result<(), AsgError> {
|
2023-07-26 16:09:17 -04:00
|
|
|
|
let tpl_name = from_oi.name(asg);
|
2023-07-25 15:35:57 -04:00
|
|
|
|
let span = to_oi.resolve(asg).span();
|
2023-07-26 16:09:17 -04:00
|
|
|
|
|
|
|
|
|
from_oi.try_map_obj(asg, |tpl| {
|
2023-07-27 02:35:47 -04:00
|
|
|
|
tpl.try_map(|shape: TplShape| {
|
2023-07-27 01:44:12 -04:00
|
|
|
|
shape.try_adapt_to(TplShape::Expr(span), tpl_name)
|
|
|
|
|
})
|
tamer: asg::air::object::tpl: Reject multi-expression shape
This enforces the new constraint that templates expanding into an `Expr`
context must only inline a single `Expr`.
Perhaps in the future we'll support explicit splicing, like `,@` in
Lisp. But this new restriction is intended for two purposes:
- To make templates more predictable (if you have a list of expressions
inlined then they will act differently depending on the type of
expression that they are inlined into, which means that more defensive
programming would otherwise be required); and
- To make expansion easier, since we're going to have to set aside an
expansion workspace ahead of time to ensure ordering (Petgraph can't
replace edges in-place). If we support multi-expansion, we'd have to
handle associativity in all expression contexts.
This'll become more clear in future commits.
It's nice to see all this hard work coming together now, though; it's easy
now to perform static analysis on the system, and any part of the graph
construction can throw errors with rich diagnostic information and still
recover properly. And, importantly, the system enforces its own state, and
the compiler helps us with that (the previous commits).
DEV-13163
2023-07-26 03:43:24 -04:00
|
|
|
|
})?;
|
2023-07-25 15:35:57 -04:00
|
|
|
|
|
|
|
|
|
Ok(commit(asg))
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-03-17 12:53:53 -04:00
|
|
|
|
|
2023-03-23 00:04:53 -04:00
|
|
|
|
// Identifiers are used for both references and identifiers that
|
|
|
|
|
// will expand into an application site.
|
|
|
|
|
dyn Ident,
|
2023-04-05 10:45:54 -04:00
|
|
|
|
|
|
|
|
|
// Template application.
|
2023-07-26 16:09:17 -04:00
|
|
|
|
tree Tpl {
|
|
|
|
|
fn pre_add_edge(
|
|
|
|
|
asg: &mut Asg,
|
|
|
|
|
from_oi: ObjectIndex<Self>,
|
|
|
|
|
to_oi: ObjectIndex<Tpl>,
|
|
|
|
|
_ctx_span: Option<Span>,
|
|
|
|
|
commit: impl FnOnce(&mut Asg),
|
|
|
|
|
) -> Result<(), AsgError> {
|
|
|
|
|
let tpl_name = from_oi.name(asg);
|
|
|
|
|
let apply = to_oi.resolve(asg);
|
|
|
|
|
let apply_shape = apply
|
|
|
|
|
.shape()
|
|
|
|
|
.overwrite_span_if_any(apply.span());
|
|
|
|
|
|
|
|
|
|
// TODO: Refactor; very similar to Expr edge above.
|
|
|
|
|
from_oi.try_map_obj(asg, |tpl| {
|
2023-07-27 02:35:47 -04:00
|
|
|
|
tpl.try_map(|shape: TplShape| {
|
2023-07-27 01:44:12 -04:00
|
|
|
|
shape.try_adapt_to(apply_shape, tpl_name)
|
|
|
|
|
})
|
2023-07-26 16:09:17 -04:00
|
|
|
|
})?;
|
|
|
|
|
|
|
|
|
|
Ok(commit(asg))
|
|
|
|
|
}
|
|
|
|
|
},
|
2023-04-12 10:03:37 -04:00
|
|
|
|
|
2023-04-12 15:53:16 -04:00
|
|
|
|
// Short template description and arbitrary documentation to be
|
|
|
|
|
// expanded into the application site.
|
2023-04-12 10:03:37 -04:00
|
|
|
|
tree Doc,
|
2023-02-24 23:54:01 -05:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-02-28 15:31:49 -05:00
|
|
|
|
|
|
|
|
|
impl ObjectIndex<Tpl> {
|
2023-07-26 16:09:17 -04:00
|
|
|
|
/// Name of template,
|
|
|
|
|
/// if any.
|
|
|
|
|
///
|
|
|
|
|
/// A template may either be anonymous,
|
|
|
|
|
/// or it may not yet have a name because it is still under
|
|
|
|
|
/// construction.
|
|
|
|
|
pub fn name(&self, asg: &Asg) -> Option<SPair> {
|
|
|
|
|
self.ident(asg).and_then(|oi| oi.resolve(asg).name())
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
/// Attempt to complete a template definition.
|
2023-02-28 15:31:49 -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
|
|
|
|
/// This updates the span of the template to encompass the entire
|
2023-02-28 15:31:49 -05:00
|
|
|
|
/// definition.
|
|
|
|
|
pub fn close(self, asg: &mut Asg, close_span: Span) -> Self {
|
|
|
|
|
self.map_obj(asg, |tpl| {
|
2023-07-25 12:27:17 -04:00
|
|
|
|
tpl.map(|open_span: Span| {
|
2023-02-28 15:31:49 -05:00
|
|
|
|
open_span.merge(close_span).unwrap_or(open_span)
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
}
|
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
|
|
|
|
|
|
|
|
|
/// Apply a named template `id` to the context of `self`.
|
|
|
|
|
///
|
|
|
|
|
/// During evaluation,
|
|
|
|
|
/// this application will expand the template in place,
|
|
|
|
|
/// re-binding metavariables to the context of `self`.
|
2023-04-05 10:45:54 -04:00
|
|
|
|
pub fn apply_named_tpl(
|
|
|
|
|
self,
|
|
|
|
|
asg: &mut Asg,
|
|
|
|
|
oi_apply: ObjectIndex<Ident>,
|
|
|
|
|
ref_span: Span,
|
tamer: asg::graph::AsgObjectMut: Allow objects to assert ownership over relationships
There's a lot to say about this; it's been a bit of a struggle figuring out
what I wanted to do here.
First: this allows objects to use `AsgObjectMut` to control whether an edge
is permitted to be added, or to cache information about an edge that is
about to be added. But no object does that yet; it just uses the default
trait implementation, and so this _does not change any current
behavior_. It also is approximately equivalent cycle-count-wise, according
to Valgrind (within ~100 cycles out of hundreds of millions on large package
tests).
Adding edges to the graph is still infallible _after having received
permission_ from an `ObjectIndexRelTo`, but the object is free to reject the
edge with an `AsgError`.
As an example of where this will be useful: the template system needs to
keep track of what is in the body of a template as it is defined. But the
`TplAirAggregate` parser is sidelined while expressions in the body are
parsed, and edges are added to a dynamic source using
`ObjectIndexRelTo`. Consequently, we cannot rely on a static API to cache
information; we have to be able to react dynamically. This will allow `Tpl`
objects to know any time edges are added and, therefore, determine their
shape as the graph is being built, rather than having to traverse the tree
after encountering a close.
(I _could_ change this, but `ObjectIndexRelTo` removes a significant amount
of complexity for the caller, so I'd rather not.)
I did explore other options. I rejected the first one, then rejected this
one, then rejected the first one again before returning back to this one
after having previously sidelined the entire thing, because of the above
example. The core point is: I need confidence that the graph isn't being
changed in ways that I forgot about, and because of the complexity of the
system and the heavy refactoring that I do, I need the compiler's help;
otherwise I risk introducing subtle bugs as objects get out of sync with the
actual state of the graph.
(I wish the graph supported these things directly, but that's a project well
outside the scope of my TAMER work. So I have to make do, as I have been
all this time, by layering atop of Petgraph.)
(...I'm beginning to ramble.)
(...beginning?)
Anyway: my other rejected idea was to provide attestation via the
`ObjectIndex` APIs to force callers to go through those APIs to add an edge
to the graph; it would use sealed objects that are inaccessible to any
modules other than the objects, and assert that the caller is able to
provide a zero-sized object of that sealed type.
The problem with this is...exactly what was mentioned above:
`ObjectIndexRelTo` is dynamic. We don't always know the source object type
statically, and so we cannot make those static assertions.
I could have tried the same tricks to store attestation at some other time,
but what a confusing mess it would be.
And so here we are.
Most of this work is cleaning up the callers---adding edges is now fallible,
from the `ObjectIndex` API standpoint, and so AIR needed to be set up to
handle those failures. There _aren't_ any failures yet, but again, since
things are dynamic, they could appear at any moment. Furthermore, since
ref/def is commutative (things can be defined and referenced in any order),
there could be surprise errors on edge additions in places that might not
otherwise expect it in the future. We're now ready for that, and I'll be
able to e.g. traverse incoming edges on a `Missing->Transparent` definition
to notify dependents.
This project is going to be the end of me. As interesting as it is.
I can see why Rust just chose to require macro definitions _before_ use. So
much less work.
DEV-13163
2023-07-24 16:41:32 -04:00
|
|
|
|
) -> Result<Self, AsgError> {
|
2023-04-05 10:45:54 -04:00
|
|
|
|
self.add_edge_to(asg, oi_apply, Some(ref_span))
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Directly reference this template from another object
|
|
|
|
|
/// `oi_target_parent`,
|
|
|
|
|
/// indicating the intent to expand the template in place.
|
|
|
|
|
///
|
|
|
|
|
/// This direct reference allows applying anonymous templates.
|
|
|
|
|
///
|
|
|
|
|
/// The term "expansion" is equivalent to the application of a closed
|
|
|
|
|
/// template.
|
|
|
|
|
/// If this template is _not_ closed,
|
|
|
|
|
/// it will result in an error during evaluation.
|
2023-03-28 16:14:09 -04:00
|
|
|
|
pub fn expand_into<OP: ObjectIndexRelTo<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
|
|
|
|
self,
|
|
|
|
|
asg: &mut Asg,
|
2023-03-28 16:14:09 -04:00
|
|
|
|
oi_target_parent: OP,
|
tamer: asg::graph::AsgObjectMut: Allow objects to assert ownership over relationships
There's a lot to say about this; it's been a bit of a struggle figuring out
what I wanted to do here.
First: this allows objects to use `AsgObjectMut` to control whether an edge
is permitted to be added, or to cache information about an edge that is
about to be added. But no object does that yet; it just uses the default
trait implementation, and so this _does not change any current
behavior_. It also is approximately equivalent cycle-count-wise, according
to Valgrind (within ~100 cycles out of hundreds of millions on large package
tests).
Adding edges to the graph is still infallible _after having received
permission_ from an `ObjectIndexRelTo`, but the object is free to reject the
edge with an `AsgError`.
As an example of where this will be useful: the template system needs to
keep track of what is in the body of a template as it is defined. But the
`TplAirAggregate` parser is sidelined while expressions in the body are
parsed, and edges are added to a dynamic source using
`ObjectIndexRelTo`. Consequently, we cannot rely on a static API to cache
information; we have to be able to react dynamically. This will allow `Tpl`
objects to know any time edges are added and, therefore, determine their
shape as the graph is being built, rather than having to traverse the tree
after encountering a close.
(I _could_ change this, but `ObjectIndexRelTo` removes a significant amount
of complexity for the caller, so I'd rather not.)
I did explore other options. I rejected the first one, then rejected this
one, then rejected the first one again before returning back to this one
after having previously sidelined the entire thing, because of the above
example. The core point is: I need confidence that the graph isn't being
changed in ways that I forgot about, and because of the complexity of the
system and the heavy refactoring that I do, I need the compiler's help;
otherwise I risk introducing subtle bugs as objects get out of sync with the
actual state of the graph.
(I wish the graph supported these things directly, but that's a project well
outside the scope of my TAMER work. So I have to make do, as I have been
all this time, by layering atop of Petgraph.)
(...I'm beginning to ramble.)
(...beginning?)
Anyway: my other rejected idea was to provide attestation via the
`ObjectIndex` APIs to force callers to go through those APIs to add an edge
to the graph; it would use sealed objects that are inaccessible to any
modules other than the objects, and assert that the caller is able to
provide a zero-sized object of that sealed type.
The problem with this is...exactly what was mentioned above:
`ObjectIndexRelTo` is dynamic. We don't always know the source object type
statically, and so we cannot make those static assertions.
I could have tried the same tricks to store attestation at some other time,
but what a confusing mess it would be.
And so here we are.
Most of this work is cleaning up the callers---adding edges is now fallible,
from the `ObjectIndex` API standpoint, and so AIR needed to be set up to
handle those failures. There _aren't_ any failures yet, but again, since
things are dynamic, they could appear at any moment. Furthermore, since
ref/def is commutative (things can be defined and referenced in any order),
there could be surprise errors on edge additions in places that might not
otherwise expect it in the future. We're now ready for that, and I'll be
able to e.g. traverse incoming edges on a `Missing->Transparent` definition
to notify dependents.
This project is going to be the end of me. As interesting as it is.
I can see why Rust just chose to require macro definitions _before_ use. So
much less work.
DEV-13163
2023-07-24 16:41:32 -04:00
|
|
|
|
) -> Result<Self, AsgError> {
|
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
|
|
|
|
self.add_edge_from(asg, oi_target_parent, None)
|
|
|
|
|
}
|
2023-04-12 10:03:37 -04:00
|
|
|
|
|
|
|
|
|
/// Arbitrary text serving as documentation in a literate style,
|
|
|
|
|
/// to be expanded into the application site.
|
tamer: asg::graph::AsgObjectMut: Allow objects to assert ownership over relationships
There's a lot to say about this; it's been a bit of a struggle figuring out
what I wanted to do here.
First: this allows objects to use `AsgObjectMut` to control whether an edge
is permitted to be added, or to cache information about an edge that is
about to be added. But no object does that yet; it just uses the default
trait implementation, and so this _does not change any current
behavior_. It also is approximately equivalent cycle-count-wise, according
to Valgrind (within ~100 cycles out of hundreds of millions on large package
tests).
Adding edges to the graph is still infallible _after having received
permission_ from an `ObjectIndexRelTo`, but the object is free to reject the
edge with an `AsgError`.
As an example of where this will be useful: the template system needs to
keep track of what is in the body of a template as it is defined. But the
`TplAirAggregate` parser is sidelined while expressions in the body are
parsed, and edges are added to a dynamic source using
`ObjectIndexRelTo`. Consequently, we cannot rely on a static API to cache
information; we have to be able to react dynamically. This will allow `Tpl`
objects to know any time edges are added and, therefore, determine their
shape as the graph is being built, rather than having to traverse the tree
after encountering a close.
(I _could_ change this, but `ObjectIndexRelTo` removes a significant amount
of complexity for the caller, so I'd rather not.)
I did explore other options. I rejected the first one, then rejected this
one, then rejected the first one again before returning back to this one
after having previously sidelined the entire thing, because of the above
example. The core point is: I need confidence that the graph isn't being
changed in ways that I forgot about, and because of the complexity of the
system and the heavy refactoring that I do, I need the compiler's help;
otherwise I risk introducing subtle bugs as objects get out of sync with the
actual state of the graph.
(I wish the graph supported these things directly, but that's a project well
outside the scope of my TAMER work. So I have to make do, as I have been
all this time, by layering atop of Petgraph.)
(...I'm beginning to ramble.)
(...beginning?)
Anyway: my other rejected idea was to provide attestation via the
`ObjectIndex` APIs to force callers to go through those APIs to add an edge
to the graph; it would use sealed objects that are inaccessible to any
modules other than the objects, and assert that the caller is able to
provide a zero-sized object of that sealed type.
The problem with this is...exactly what was mentioned above:
`ObjectIndexRelTo` is dynamic. We don't always know the source object type
statically, and so we cannot make those static assertions.
I could have tried the same tricks to store attestation at some other time,
but what a confusing mess it would be.
And so here we are.
Most of this work is cleaning up the callers---adding edges is now fallible,
from the `ObjectIndex` API standpoint, and so AIR needed to be set up to
handle those failures. There _aren't_ any failures yet, but again, since
things are dynamic, they could appear at any moment. Furthermore, since
ref/def is commutative (things can be defined and referenced in any order),
there could be surprise errors on edge additions in places that might not
otherwise expect it in the future. We're now ready for that, and I'll be
able to e.g. traverse incoming edges on a `Missing->Transparent` definition
to notify dependents.
This project is going to be the end of me. As interesting as it is.
I can see why Rust just chose to require macro definitions _before_ use. So
much less work.
DEV-13163
2023-07-24 16:41:32 -04:00
|
|
|
|
pub fn append_doc_text(
|
|
|
|
|
&self,
|
|
|
|
|
asg: &mut Asg,
|
|
|
|
|
text: SPair,
|
|
|
|
|
) -> Result<Self, AsgError> {
|
2023-04-12 10:03:37 -04:00
|
|
|
|
let oi_doc = asg.create(Doc::new_text(text));
|
|
|
|
|
self.add_edge_to(asg, oi_doc, None)
|
|
|
|
|
}
|
2023-02-28 15:31:49 -05:00
|
|
|
|
}
|