tame/tamer/src/nir/tplshort.rs

288 lines
10 KiB
Rust
Raw Normal View History

// Shorthand template application desugaring for NIR
//
// 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/>.
//! Shorthand template application desugaring for NIR.
//!
tamer: nir::tplshort: Desugar nested template applications I'm happy with how this ended up turning out---I was able to accomplish this without having to introduce any additional state to the parser (I _removed_ a state, actually) by tweaking NIR a bit in a previous commit. We can't update the system test yet, though, because nested templates are not yet supported by asg::air::tpl; that'll come next. If you try, you'll be greeted with this error presently (which is worth showing since you'll never see it unless you're hacking TAMER): ,=====[ ./tests/xmli/template/ logs ]====== | | thread 'main' panicked at 'not yet implemented: internal error: | note: nested tpl open | --> ./tests/xmli/template/src.xml:129:5 | | | 129 | <t:inner-short /> | | -------------- note: for this template | | | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! THIS IS AN UNFINISHED FEATURE IN TAMER !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! This message means that TAMER has encountered an !!! | !!! unrecoverable error that forced it to terminate !!! | !!! processing. !!! | !!! !!! | !!! TAMER has attempted to provide you with contextual !!! | !!! information above that might allow you to work around !!! | !!! this problem until it can be fixed. !!! | !!! !!! | !!! Please report this error, including the above !!! | !!! diagnostic output beginning with 'internal error:'. !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | ', src/asg/air/tpl.rs:207:55 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace | Command exited with non-zero status 101 | 0/165fault 0/8io 3528rss 14/2ctx | /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml | `====[ end ./tests/xmli/template/ logs ]==== DEV-13708
2023-03-23 22:43:55 -04:00
//! Desugaring is performed by [`TplShortDesugar`].
//!
//! A shorthand template application looks something like this,
//! in XML form:
//!
//! ```xml
//! <t:foo bar="baz">
//! <c:sum />
//! </t:foo>
//!
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
//! <!-- the above desugars into the below -->
//!
//! <apply-template name="_foo_">
//! <with-param name="@bar@" value="baz" />
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
//! <with-param name="@values@" value="___dsgr-01___" />
//! </apply-template>
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
//!
//! <template name="___dsgr-01___">
//! <c:sum />
//! </template>
//! ```
//!
//! The shorthand syntax makes templates look like another language
//! primitive,
//! with the exception of the namespace prefix.
//!
//! Note how desugaring also wraps template names in `'_'` and param names
//! in `'@'`.
//! These naming requirements were intended to eliminate conflicts with
//! other types of identifiers and to make it obvious when templates and
//! metavariables were being used,
//! but it works against the goal of making template applications look
//! like language primitives.
//! Shorthand form was added well after the long `apply-template` form.
//!
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
//! The body of a shorthand template becomes the body of a new template,
//! and its id referenced as the lexical value of the param `@values@`.
//! (This poor name is a historical artifact.)
//! Since the template is closed
//! (has no free metavariables),
//! it will be expanded on reference,
//! inlining its body into the reference site.
//! This is a different and generalized approach to the `param-copy`
//! behavior of the XLST-based TAME.
//!
//! This shorthand version does not permit metavariables for template or
//! param names,
//! so the long form is still a useful language feature for more
//! sophisticated cases.
//!
//! This was originally handled in the XSLT compiler in
//! `:src/current/include/preproc/template.xsl`.
//! You may need to consult the Git history if this file is no longer
//! available or if the XSLT template was since removed.
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
//! The XSLT-based compiler did not produce a separate template for
//! `@values@`.
use arrayvec::ArrayVec;
use super::{Nir, NirEntity};
use crate::{
fmt::{DisplayWrapper, TtQuote},
parse::prelude::*,
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
span::Span,
sym::{
st::raw::L_TPLP_VALUES, GlobalSymbolIntern, GlobalSymbolResolve,
SymbolId,
},
};
use std::convert::Infallible;
use Nir::*;
use NirEntity::*;
tamer: nir::tplshort: Desugar nested template applications I'm happy with how this ended up turning out---I was able to accomplish this without having to introduce any additional state to the parser (I _removed_ a state, actually) by tweaking NIR a bit in a previous commit. We can't update the system test yet, though, because nested templates are not yet supported by asg::air::tpl; that'll come next. If you try, you'll be greeted with this error presently (which is worth showing since you'll never see it unless you're hacking TAMER): ,=====[ ./tests/xmli/template/ logs ]====== | | thread 'main' panicked at 'not yet implemented: internal error: | note: nested tpl open | --> ./tests/xmli/template/src.xml:129:5 | | | 129 | <t:inner-short /> | | -------------- note: for this template | | | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! THIS IS AN UNFINISHED FEATURE IN TAMER !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! This message means that TAMER has encountered an !!! | !!! unrecoverable error that forced it to terminate !!! | !!! processing. !!! | !!! !!! | !!! TAMER has attempted to provide you with contextual !!! | !!! information above that might allow you to work around !!! | !!! this problem until it can be fixed. !!! | !!! !!! | !!! Please report this error, including the above !!! | !!! diagnostic output beginning with 'internal error:'. !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | ', src/asg/air/tpl.rs:207:55 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace | Command exited with non-zero status 101 | 0/165fault 0/8io 3528rss 14/2ctx | /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml | `====[ end ./tests/xmli/template/ logs ]==== DEV-13708
2023-03-23 22:43:55 -04:00
/// Desugar shorthand template applications into long-form template
/// applications.
///
/// This parser is able to handle nested applications without having to
/// track nesting by taking advantage of the parsing that NIR has already
/// performed.
///
/// See the [module-level documentation](super) for more information.
#[derive(Debug, PartialEq, Eq, Default)]
pub enum TplShortDesugar {
/// Waiting for shorthand template application,
/// passing tokens along in the meantime.
tamer: nir::tplshort: Desugar nested template applications I'm happy with how this ended up turning out---I was able to accomplish this without having to introduce any additional state to the parser (I _removed_ a state, actually) by tweaking NIR a bit in a previous commit. We can't update the system test yet, though, because nested templates are not yet supported by asg::air::tpl; that'll come next. If you try, you'll be greeted with this error presently (which is worth showing since you'll never see it unless you're hacking TAMER): ,=====[ ./tests/xmli/template/ logs ]====== | | thread 'main' panicked at 'not yet implemented: internal error: | note: nested tpl open | --> ./tests/xmli/template/src.xml:129:5 | | | 129 | <t:inner-short /> | | -------------- note: for this template | | | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! THIS IS AN UNFINISHED FEATURE IN TAMER !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! This message means that TAMER has encountered an !!! | !!! unrecoverable error that forced it to terminate !!! | !!! processing. !!! | !!! !!! | !!! TAMER has attempted to provide you with contextual !!! | !!! information above that might allow you to work around !!! | !!! this problem until it can be fixed. !!! | !!! !!! | !!! Please report this error, including the above !!! | !!! diagnostic output beginning with 'internal error:'. !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | ', src/asg/air/tpl.rs:207:55 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace | Command exited with non-zero status 101 | 0/165fault 0/8io 3528rss 14/2ctx | /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml | `====[ end ./tests/xmli/template/ logs ]==== DEV-13708
2023-03-23 22:43:55 -04:00
///
/// This state is also used when parsing the body of a shorthand
/// template application.
#[default]
Scanning,
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
/// A shorthand template application associated with the provided
/// [`SPair`] was encountered and shorthand params are being
/// desugared.
DesugaringParams(SPair),
}
impl Display for TplShortDesugar {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Scanning => {
write!(f, "awaiting shorthand template application")
}
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
Self::DesugaringParams(_) => {
write!(f, "desugaring shorthand template application params")
}
}
}
}
impl ParseState for TplShortDesugar {
type Token = Nir;
type Object = Nir;
type Error = Infallible;
type Context = Stack;
fn parse_token(
self,
tok: Self::Token,
stack: &mut Self::Context,
) -> TransitionResult<Self::Super> {
use TplShortDesugar::*;
if let Some(obj) = stack.pop() {
return Transition(self).ok(obj).with_lookahead(tok);
}
match (self, tok) {
// Shorthand template applications are identified by a `Some`
// QName in the `TplApply` entity.
//
// The name of the template _without_ the underscore padding is
// the local part of the QName.
(Scanning, Open(TplApplyShort(qname), span)) => {
// TODO: Determine whether caching these has any notable
// benefit over repeated heap allocations,
// comparing packages with very few applications and
// packages with thousands
// (we'd still have to hit the heap for the cache).
let tpl_name =
format!("_{}_", qname.local_name().lookup_str()).intern();
let name = SPair(tpl_name, span);
stack.push(Ref(name));
Transition(DesugaringParams(name)).ok(Open(TplApply, span))
}
// Shorthand template params' names do not contain the
// surrounding `@`s.
(DesugaringParams(ospan), Open(TplParamShort(name, val), span)) => {
let pname = format!("@{name}@").intern();
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
// note: reversed (stack)
stack.push(Close(TplParam, span));
stack.push(Text(val));
stack.push(BindIdent(SPair(pname, name.span())));
Transition(DesugaringParams(ospan)).ok(Open(TplParam, span))
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
}
// A child element while we're desugaring template params
// means that we have reached the body,
// which is to desugar into `@values@`.
// We generate a name for a new template,
// set `@values@` to the name of the template,
// close our active template application,
// and then place the body into that template.
//
// TODO: This does not handle nested template applications.
(DesugaringParams(name), tok @ Open(..)) => {
let ospan = name.span();
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
let gen_name = gen_tpl_name_at_offset(ospan);
let gen_desc = values_tpl_desc(name);
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
// The spans are awkward here because we are streaming,
// and so don't have much choice but to use the opening
// span for everything.
// If this ends up being unhelpful for diagnostics,
// we can have AIR do some adjustment through some
// yet-to-be-defined means.
//
// note: reversed (stack)
stack.push(Desc(gen_desc));
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
stack.push(BindIdent(SPair(gen_name, ospan)));
stack.push(Open(Tpl, ospan));
// Application ends here,
// and the new template (above) will absorb both this
// token `tok` and all tokens that come after.
stack.push(Close(TplApply, ospan));
stack.push(Close(TplParam, ospan));
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
stack.push(Text(SPair(gen_name, ospan)));
stack.push(BindIdent(SPair(L_TPLP_VALUES, ospan)));
tamer: nir::tplshort: Desugar nested template applications I'm happy with how this ended up turning out---I was able to accomplish this without having to introduce any additional state to the parser (I _removed_ a state, actually) by tweaking NIR a bit in a previous commit. We can't update the system test yet, though, because nested templates are not yet supported by asg::air::tpl; that'll come next. If you try, you'll be greeted with this error presently (which is worth showing since you'll never see it unless you're hacking TAMER): ,=====[ ./tests/xmli/template/ logs ]====== | | thread 'main' panicked at 'not yet implemented: internal error: | note: nested tpl open | --> ./tests/xmli/template/src.xml:129:5 | | | 129 | <t:inner-short /> | | -------------- note: for this template | | | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! THIS IS AN UNFINISHED FEATURE IN TAMER !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! This message means that TAMER has encountered an !!! | !!! unrecoverable error that forced it to terminate !!! | !!! processing. !!! | !!! !!! | !!! TAMER has attempted to provide you with contextual !!! | !!! information above that might allow you to work around !!! | !!! this problem until it can be fixed. !!! | !!! !!! | !!! Please report this error, including the above !!! | !!! diagnostic output beginning with 'internal error:'. !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | ', src/asg/air/tpl.rs:207:55 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace | Command exited with non-zero status 101 | 0/165fault 0/8io 3528rss 14/2ctx | /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml | `====[ end ./tests/xmli/template/ logs ]==== DEV-13708
2023-03-23 22:43:55 -04:00
// Note that we must have `tok` as lookahead instead of
// pushing directly on the stack in case it's a
// `TplApplyShort`.
Transition(Scanning)
.ok(Open(TplParam, ospan))
.with_lookahead(tok)
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
}
tamer: nir::tplshort: Desugar nested template applications I'm happy with how this ended up turning out---I was able to accomplish this without having to introduce any additional state to the parser (I _removed_ a state, actually) by tweaking NIR a bit in a previous commit. We can't update the system test yet, though, because nested templates are not yet supported by asg::air::tpl; that'll come next. If you try, you'll be greeted with this error presently (which is worth showing since you'll never see it unless you're hacking TAMER): ,=====[ ./tests/xmli/template/ logs ]====== | | thread 'main' panicked at 'not yet implemented: internal error: | note: nested tpl open | --> ./tests/xmli/template/src.xml:129:5 | | | 129 | <t:inner-short /> | | -------------- note: for this template | | | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! THIS IS AN UNFINISHED FEATURE IN TAMER !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! This message means that TAMER has encountered an !!! | !!! unrecoverable error that forced it to terminate !!! | !!! processing. !!! | !!! !!! | !!! TAMER has attempted to provide you with contextual !!! | !!! information above that might allow you to work around !!! | !!! this problem until it can be fixed. !!! | !!! !!! | !!! Please report this error, including the above !!! | !!! diagnostic output beginning with 'internal error:'. !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | ', src/asg/air/tpl.rs:207:55 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace | Command exited with non-zero status 101 | 0/165fault 0/8io 3528rss 14/2ctx | /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml | `====[ end ./tests/xmli/template/ logs ]==== DEV-13708
2023-03-23 22:43:55 -04:00
// If we're scanning and find a closing shorthand application,
// then we must have just finished with a shorthand body.
(Scanning, Close(TplApplyShort(_), span)) => {
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
Transition(Scanning).ok(Close(Tpl, span))
}
tamer: nir::tplshort: Desugar nested template applications I'm happy with how this ended up turning out---I was able to accomplish this without having to introduce any additional state to the parser (I _removed_ a state, actually) by tweaking NIR a bit in a previous commit. We can't update the system test yet, though, because nested templates are not yet supported by asg::air::tpl; that'll come next. If you try, you'll be greeted with this error presently (which is worth showing since you'll never see it unless you're hacking TAMER): ,=====[ ./tests/xmli/template/ logs ]====== | | thread 'main' panicked at 'not yet implemented: internal error: | note: nested tpl open | --> ./tests/xmli/template/src.xml:129:5 | | | 129 | <t:inner-short /> | | -------------- note: for this template | | | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! THIS IS AN UNFINISHED FEATURE IN TAMER !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | !!! This message means that TAMER has encountered an !!! | !!! unrecoverable error that forced it to terminate !!! | !!! processing. !!! | !!! !!! | !!! TAMER has attempted to provide you with contextual !!! | !!! information above that might allow you to work around !!! | !!! this problem until it can be fixed. !!! | !!! !!! | !!! Please report this error, including the above !!! | !!! diagnostic output beginning with 'internal error:'. !!! | !!! ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! | ', src/asg/air/tpl.rs:207:55 | note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace | Command exited with non-zero status 101 | 0/165fault 0/8io 3528rss 14/2ctx | /home/[...]/tame/tamer/target/debug/tamec -o ./tests/xmli/template/out.xmli --emit xmlo ./tests/xmli/template/src.xml | `====[ end ./tests/xmli/template/ logs ]==== DEV-13708
2023-03-23 22:43:55 -04:00
// If we complete the shorthand template during param parsing,
// then we have no body and must close the application.
(DesugaringParams(_), Close(TplApplyShort(_), span)) => {
Transition(Scanning).ok(Close(TplApply, span))
}
// Any tokens that we don't recognize will be passed on unchanged.
(st, tok) => Transition(st).ok(tok),
}
}
fn is_accepting(&self, stack: &Self::Context) -> bool {
matches!(self, Self::Scanning) && stack.is_empty()
}
}
tamer: nir::tplshort: Desugar body into @values@ This represents a significant departure from how the XSLT-based TAME handles the `@values@` param, but it will end up having the same effect. It builds upon prior work, utilizing the fact that referencing a template in TAMER will expand it. The problem is this: allowing trees in `Meta` would add yet another container; we have `Pkg` and `Tpl` already. This was the same problem with template application---I didn't want to add support for binding arguments separately, and so re-used templates themselves, reaching the generalization I just mentioned above. `Meta` is intended to be a lexical metasyntatic variable. That keeps its implementation quite simple. But if we start allowing trees, that gets rather complicated really quickly, and starts to require much more complex AIR parser state. But we can accomplish the same behavior by desugaring into an existing container---a template---and placing the body within it. Then, in the future, we'll parse `param-copy` into a simple `Air::RefIdent`, which will expand the closed template and produce the same result as it does today in the XSLT-based system. This leaves open issues of closure (variable binding) in complex scenarios, such as in templates that introduce metavariables to be utilized by the body. That's never a practice I liked, but we'll see how things evolve. Further, this does not yet handle nested template applications. But this saved me a ton of work. Desugaring is much simpler. The question is going to be how the XSLT-based compiler responds to this for large packages with thousands of template applications. I'll have to see if it's worth the hit at that time, or if we should inline it when generating the `xmli` file, producing the same `@values@` as before. But as it stands at this moment, the output is _not_ compatible with the current compiler, as it expects `@values@` to be a tree, so a modification would have to be made there. DEV-13708
2023-03-23 14:40:40 -04:00
type Stack = ArrayVec<Nir, 7>;
/// Generate a deterministic template identifier name that is unique
/// relative to the offset in the source context (file) of the given
/// [`Span`].
///
/// Hygiene is not a concern since identifiers cannot be redeclared,
/// so conflicts with manually-created identifiers will result in a
/// compilation error
/// (albeit a cryptic one);
/// the hope is that the informally-compiler-reserved `___` convention
/// mitigates that unlikely occurrence.
/// Consequently,
/// we _must_ intern to ensure that error can occur
/// (we cannot use [`GlobalSymbolIntern::clone_uninterned`]).
#[inline]
fn gen_tpl_name_at_offset(span: Span) -> SymbolId {
format!("___dsgr-{:x}___", span.offset()).intern()
}
/// Generate a description for the template generated from the body of a
/// shorthand template application.
///
/// Descriptions are required on templates,
/// but we should also provide something that is useful in debugging.
/// The description will contain the name of the template being applied and
/// will share the same span as the provided [`SPair`] `applying`'s,
/// having been derived from it.
fn values_tpl_desc(applying: SPair) -> SPair {
SPair(
format!(
"Desugared body of shorthand template application of {}",
TtQuote::wrap(applying.symbol())
)
.intern(),
applying.span(),
)
}
#[cfg(test)]
mod test;