// Normalized source IR
//
// 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 .
//! Decompose a [XIRF](crate::xir::flat) stream into NIR.
//!
//! TAME's grammar is embedded within the grammar of a document,
//! in this case XML.
//! The purpose of this parser is to extract the grammar of TAME from the
//! XML document and represent it as NIR.
//! This parser merely describes _the permissable structure of the
//! document_,
//! but nothing more.
//! For example,
//! whether an attribute is required depends on the what parsers later in
//! the lowering pipeline require of NIR within a given context;
//! this parser merely describes how to translate an attribute into NIR
//! if it happens to be present,
//! and rejects attributes that it does not know about.
//!
//! For general information about NIR,
//! see the [parent module](super).
//!
//! The entry point for this parser in the lowering pipeline is
//! [`NirParseState`].
//! The grammar is defined declaratively using the [`ele_parse!`]
//! parser-generator,
//! which yields a parser compatible with TAME's [`crate::parse`]
//! framework.
//!
//! Grammar Definition
//! ==================
//! The grammar can be seen in the TAMER sources;
//! if you are viewing the generated documentation,
//! it can be viewed by clicking on "source" in the upper-right-hand
//! corner of this page,
//! or on each individual identifier.
//!
//! The grammar defines nonterminals (NTs) of two forms:
//!
//! 1. [XIR](crate::xir) elements with their attributes and child NTs; and
//! 2. Sum NTs of the form `(NT₀ | NT₁ | … | NTₙ)` which match on any of
//! inner NTs.
//!
//! Terminals are specified in element name and attribute contexts as
//! [static QName](crate::xir::st::qname) constants of the form `QN_*`.
//! These constants are defined in [`crate::xir::st::qname`] and allow the
//! to efficiently match on element and attribute names by comparing a
//! single 64-bit integer value,
//! which in turn may be optimized to compare many possible QName
//! values simultaneously.
//!
//! The style of the grammar is meant to be a combination of a BNF and Rust
//! syntax.
//!
//! Repetition and Templates
//! ------------------------
//! _All NTs are implicitly defined as zero-or-more_
//! (as with the Kleene star),
//! and this behavior cannot be overridden.
//! The rationale for this is somewhat complex,
//! but the tradeoff greatly simplifies the [`ele_parse!`]
//! parser-generator in recognition of a simple fact about NIR:
//! it cannot determine statically whether a source file will conform to
//! TAME's grammar when all templates are expanded.
//!
//! Templates require an interpreter and are expanded later in the lowering
//! pipeline.
//! NIR is unable to perform that expansion,
//! and so we do the best we can do in this situation:
//! verify that templates,
//! when expanded,
//! will expand into primitives known to NIR,
//! and validate those primitives when possible.
//! This can only go so far,
//! given that templates can appear virtually anywhere in the source tree.
//!
//! Because templates are able to expand into anything that is known to
//! NIR's grammar,
//! NIR cannot know whether a required element has been provided or not.
//! Consequently,
//! we cannot require that an element be present as part of NIR's grammar,
//! since it may have been hidden behind a template application.
//! For the same reason,
//! we cannot place _any_ restrictions on the number of repetitions of a
//! particular element.
//!
//! The best we can do is therefore to merely validate that,
//! whatever _is_ present,
//! is conceivably valid at that position within the grammar.
//! It is then the burden of a future lowering operation to validate the
//! grammar post-expansion.
//!
//! What NIR therefore provides is an IR that is _closed_ under template
//! application---this
//! means that,
//! when a template _is_ expanded into an application site,
//! it _will_ expand into a sequence of parsed NIR tokens and cannot
//! possibly expand into anything else.
//! What the template system does with those tokens is beyond our concern.
//!
//! See [`TplKw`] for template tokens that are accepted anywhere.
use super::{Nir::*, *};
use crate::{
ele_parse,
xir::st::{prefix::*, qname::*},
};
ele_parse! {
/// Parser lowering [XIR](crate::xir) into [`Nir`].
///
/// TAME's grammar is embedded within XML.
/// The outer XML document has its own grammar,
/// which is parsed by [XIR](crate::xir);
/// this parser is responsible for taking the TAME grammar within
/// a valid XML document and parsing it into [NIR](crate::nir).
///
/// Limitations of NIR
/// ------------------
/// It is important to understand the purposeful
/// (and practical)
/// limitations of NIR.
/// The grammar of NIR declares what _could acceptably appear_ in
/// various contexts;
/// it is _not_ intended to comprehensively validate what _ought_ to
/// appear in every conceivable context.
/// Because TAME is a metalanguage
/// (through use of its template system),
/// we are not able to know the full grammar of the language without
/// compile-time template evaluation,
/// and so NIR's grammar will always accept a _superset_ of all
/// valid programs.
///
/// With that said,
/// NIR will always lower primitives,
/// including within template definitions.
/// Because of this,
/// all programs _are_ closed under NIR,
/// and we can be confident that all expanded templates will be able
/// to expand into a program that can be represented by NIR.
/// Whether or not a particular expansion is semantically valid is
/// beyond the scope of NIR and should be handled as part of another
/// lowering operation.
///
/// See the [parent module](super) for more information.
///
/// Superstate
/// ----------
pub enum NirParseState;
type AttrValueError = NirAttrParseError;
type Object = Nir;
// Text and template expressions may appear at any point within the
// program;
// see [`NirParseState`] for more information.
[super] {
[text](_sym, _span) => Todo,
TplKw
};
/// All valid root elements declaring valid package types.
///
/// Historically (in XSLT),
/// these packages did not all share the same compiler.
/// This is not the case with TAMER.
///
/// When the term "package" is used without an explicit qualifier,
/// it generally refers to a package containing only calculations and
/// classifications.
PkgTypeStmt := (
RaterStmt
| PackageStmt
| ProgramMapStmt
| ReturnMapStmt
| WorksheetStmt
);
/////////////////////////
////////////////////////
////
//// Package Stmts
////
/// Like a [`PackageStmt`],
/// but producing an executable program.
///
/// The term "rater" is historical,
/// since TAME was designed for producing insurance rating systems.
RaterStmt := QN_RATER {
@ {
QN_XMLNS => TodoAttr,
QN_XMLNS_C => TodoAttr,
QN_XMLNS_T => TodoAttr,
// TODO: Is this still needed?
// TODO: PkgName type
QN_NAME => TodoAttr,
} => Todo,
ImportStmt,
PkgBodyStmt,
};
/// Non-program package for calculations and logic.
///
/// A package is a reusable module that can be imported by other
/// packages.
/// See [`PkgTypeStmt`] for more information on the distinction between
/// different package types.
PackageStmt := QN_PACKAGE(_, ospan) {
@ {
QN_XMLNS => TodoAttr,
QN_XMLNS_C => TodoAttr,
QN_XMLNS_T => TodoAttr,
// TODO: Having trouble getting rid of `@xmlns:lv` using Saxon
// for `progui-pkg`,
// so just allow for now.
// It can't actually be used on nodes.
QN_XMLNS_LV => TodoAttr,
QN_ID => TodoAttr,
QN_TITLE => TodoAttr,
QN_DESC => TodoAttr,
// TODO: When can we get rid of this?
QN_CORE => TodoAttr,
QN_PROGRAM => TodoAttr,
// TODO: Can this go away now?
QN_NAME => TodoAttr,
} => NirEntity::Package.open(ospan),
/(cspan) => NirEntity::Package.close(cspan),
ImportStmt,
PkgBodyStmt,
};
/// Import another package's symbol table into this one.
///
/// Imports allow referencing identifiers from another package and allow
/// for composing larger systems out of smaller components.
ImportStmt := QN_IMPORT {
@ {
QN_PACKAGE => Ref,
QN_EXPORT => TodoAttr,
} => Todo,
};
/// A statement that is accepted within the body of a package.
///
/// The parent context for these statements is most often
/// [`PackageStmt`].
PkgBodyStmt := (
ExternStmt
| ParamStmt
| ConstStmt
| ClassifyStmt
| RateStmt
| RateEachStmt
| TypedefStmt
| YieldStmt
| SectionStmt
| TemplateStmt
| FunctionStmt
);
/// Statements that are valid within the context of a [`PkgBodyStmt`]
/// and may be directly referenced within the body of a template.
///
/// See [`AnyStmtOrExpr`] for more information on why this is needed.
PkgStmtInner := (
ConstStmtBody
| InnerTypedefStmt
);
/// Declare a symbol that must be defined in some other package.
///
/// Externs are effectively the same concept as in C---they
/// declare symbols that we /expect/ to exist at some point,
/// but we do not know where they will be defined.
/// The linker will verify,
/// while linking the program,
/// that /at most one/ other package provides a definition for this
/// symbol and that the definition is compatible with this
/// declaration.
ExternStmt := QN_EXTERN {
@ {
QN_NAME => TodoAttr,
QN_TYPE => TodoAttr,
QN_DTYPE => TodoAttr,
QN_DIM => TodoAttr,
QN_PARENT => TodoAttr,
QN_YIELDS => TodoAttr,
} => Todo,
};
/// Define an input parameter accepting data from an external system.
///
/// Parameters are generally populated via a map,
/// such as [`ProgramMapStmt`].
ParamStmt := QN_PARAM {
@ {
QN_NAME => TodoAttr,
QN_TYPE => TodoAttr,
QN_DESC => TodoAttr,
// This is a misnomer.
QN_SET => TodoAttr,
QN_DEFAULT => TodoAttr,
QN_SYM => TodoAttr,
} => Todo,
};
/// Associate static data with an identifier.
///
/// Constants may be associated with scalar, vector, or matrix values.
/// Since all values in TAME are immutable,
/// constants are a way to denote values that are entirely hard-coded
/// rather than being derived from some external input.
///
/// In the future,
/// constants ought to be defined as expressions that can be evaluated
/// at compile-time,
/// and re-use that familiar syntax.
ConstStmt := QN_CONST {
@ {
QN_NAME => TodoAttr,
QN_DESC => TodoAttr,
QN_VALUE => TodoAttr,
QN_VALUES => TodoAttr,
// TODO: deprecate?
QN_TYPE => TodoAttr,
QN_SYM => TodoAttr,
// TODO: Misnomer
QN_SET => TodoAttr,
} => Todo,
ConstStmtBody,
};
/// Body of a [`ConstStmt`] defining a vector value or a matrix row.
///
/// Scalar constants utilize [`QN_VALUE`] instead of this body.
///
/// See also [`QN_VALUES`],
/// which can be used as a short-hand form of this body.
ConstStmtBody := (ConstMatrixRow | ConstVectorItem);
/// Constant matrix row definition.
///
/// TODO: The use of [`QN_SET`] is a terrible misnomer representing
/// dimensionality and will be changed in future versions.
ConstMatrixRow := QN_SET {
@ {
QN_DESC => TodoAttr,
} => Todo,
ConstVectorItem,
};
/// Constant vector scalar item definition.
ConstVectorItem := QN_ITEM {
@ {
QN_VALUE => TodoAttr,
QN_DESC => TodoAttr,
} => Todo,
};
/// Define a classification and associate it with an identifier.
///
/// A classification is a logic expression yielding a boolean result
/// with the dimensionality matching the largest dimensionality of its
/// inputs.
ClassifyStmt := QN_CLASSIFY(_, ospan) {
@ {
QN_AS => BindIdent,
QN_DESC => TodoAttr,
QN_ANY => TodoAttr,
QN_YIELDS => TodoAttr,
QN_SYM => TodoAttr,
QN_TERMINATE => TodoAttr,
} => NirEntity::Classify.open(ospan),
/(cspan) => NirEntity::Classify.close(cspan),
LogExpr,
};
/// Define a calculation and associate it with an identifier.
///
/// The term "rate" is intended as a verb,
/// and represents an arbitrary calculation;
/// the term originates from TAME's history as an insurance rating
/// system.
/// This will eventually be renamed to a more general term.
RateStmt := QN_RATE(_, ospan) {
@ {
QN_CLASS => TodoAttr,
QN_NO => TodoAttr,
QN_YIELDS => BindIdent,
QN_DESC => TodoAttr,
QN_SYM => TodoAttr,
// TODO: This is still recognized by the XSLT-based compiler,
// so we need to support it until it's removed.
QN_GENTLE_NO => TodoAttr,
// TODO: We'll have private-by-default later.
// This is a kludge.
QN_LOCAL => TodoAttr,
} => NirEntity::Rate.open(ospan),
/(cspan) => NirEntity::Rate.close(cspan),
CalcExpr,
};
/// Define a calculation that maps a calculation to each item of a
/// vector,
/// and associate it with an identifier.
///
/// This expands into an equivalent [`RateStmt`] with a nested
/// [`SumExpr`] serving as the item-wise map.
RateEachStmt := QN_RATE_EACH {
@ {
QN_CLASS => TodoAttr,
QN_NO => TodoAttr,
QN_GENERATES => TodoAttr,
QN_INDEX => TodoAttr,
QN_YIELDS => TodoAttr,
QN_SYM => TodoAttr,
QN_GENSYM => TodoAttr,
} => Todo,
CalcExpr,
};
/// Define a new type that restricts the domain of data.
TypedefStmt := QN_TYPEDEF {
@ {
QN_NAME => TodoAttr,
QN_DESC => TodoAttr,
QN_SYM => TodoAttr,
} => Todo,
InnerTypedefStmt,
};
/// Body of a [`TypedefStmt`].
InnerTypedefStmt := (BaseTypeStmt | EnumStmt | UnionStmt);
/// Indicate that the type is defined by the TAME compiler.
///
/// This is used for primitives and allows for core types to be exposed
/// to the user.
BaseTypeStmt := QN_BASE_TYPE {
@ {} => Todo,
};
/// Define an enumerated type.
///
/// Enums are types that have an explicit set of values,
/// each with associated constant identifiers.
EnumStmt := QN_ENUM {
@ {
QN_TYPE => TodoAttr,
} => Todo,
ItemEnumStmt,
};
/// Define an item of the domain of an enumerated type and associate it
/// with a constant identifier.
ItemEnumStmt := QN_ITEM {
@ {
QN_NAME => TodoAttr,
QN_VALUE => TodoAttr,
QN_DESC => TodoAttr,
} => Todo,
};
/// Define a type whose domain is the union of the domains of multiple
/// other types.
UnionStmt := QN_UNION {
@ {} => Todo,
TypedefStmt,
};
/// A final numerical value to be yielded by a program.
///
/// This value has historical significance,
/// but is slowly being deprecated.
/// Any number of values can be returned to the caller via a return map
/// (see [`ReturnMapStmt`]).
///
/// This is being replaced with the `__yield__` template in `core`
/// (this statement predates the template system in TAME).
YieldStmt := QN_YIELD {
@ {} => Todo,
CalcExpr,
};
/// Declare that the body of this statement ought to be delimited from
/// the surrounding definitions with a heading when visualized.
///
/// This is intended primarily for documentation,
/// and serves as an alternative to using packages for sectioning.
/// Since definitions in TAME are independent from the order of
/// execution of the resulting executable,
/// definitions tend to be linear and can sometimes benefit from
/// grouping for organization and to help guide the reader.
///
/// Otherwise,
/// the body of a section is the same as that of [`PackageStmt`],
/// with the exception of imports,
/// which must appear outside of sections.
SectionStmt := QN_SECTION {
@ {
QN_TITLE => TodoAttr,
} => Todo,
PkgBodyStmt,
};
/// Define a function and associate it with an identifier.
FunctionStmt := QN_FUNCTION {
@ {
QN_NAME => TodoAttr,
QN_DESC => TodoAttr,
QN_SYM => TodoAttr,
} => Todo,
FunctionParamStmt,
CalcExpr,
};
/// Define a function parameter and associate it with an identifier that
/// is scoped to the function body.
FunctionParamStmt := QN_PARAM {
@ {
QN_NAME => TodoAttr,
QN_TYPE => TodoAttr,
// _TODO: This is a misnomer.
QN_SET => TodoAttr,
QN_DESC => TodoAttr,
} => Todo,
};
/////////////////////////
////////////////////////
////
//// Logic Expressions
////
/// A logic expression.
///
/// See _The TAME Programming Language_ document for a formal definition
/// of this subsystem and its syntax.
LogExpr := (MatchExpr | AnyExpr | AllExpr);
/// Scalar value predicate as part of a logic expression.
///
/// The dimensionality of the expression will be automatically
/// determined by the dimensionality of the matches' [`@on`](QN_ON).
MatchExpr := QN_MATCH(_, ospan) {
@ {
QN_ON => RefSubject,
QN_VALUE => Ref,
QN_INDEX => TodoAttr,
QN_ANY_OF => TodoAttr,
} => NirEntity::Match.open(ospan),
/(cspan) => NirEntity::Match.close(cspan),
CalcPredExpr,
};
/// Logical disjunction (∨).
///
/// This represents an expression that matches when _any_ of its inner
/// [`LogExpr`] expressions match.
AnyExpr := QN_ANY(_, ospan) {
@ {} => NirEntity::Any.open(ospan),
/(cspan) => NirEntity::Any.close(cspan),
LogExpr,
};
/// Logical conjunction (∧).
///
/// This represents an expression that matches when _all_ of its inner
/// [`LogExpr`] expressions match.
AllExpr := QN_ALL(_, ospan) {
@ {} => NirEntity::All.open(ospan),
/(cspan) => NirEntity::All.close(cspan),
LogExpr,
};
/////////////////////////
////////////////////////
////
//// Calculations
////
/// An expression producing a scalar result.
///
/// Some expressions may support binding to additional identifiers.
CalcExpr := (
SumExpr
| ProductExpr
| QuotientExpr
| ExptExpr
| ValueOfExpr
| ConstExpr
| VectorExpr
| CasesExpr
| CeilExpr
| FloorExpr
| LengthOfExpr
| LetExpr
| ApplyExpr
| RecurseExpr
| ConsExpr
| CarExpr
| CdrExpr
);
/// Expressions that are valid within the context of one or more
/// [`CalcExpr`] and may be directly referenced within the body of a
/// template.
///
/// See [`AnyStmtOrExpr`] for more information on why this is needed.
CalcExprInner := (
CalcPredExpr
| CaseExpr
| OtherwiseExpr
| LetValues
| LetValue
| WhenExpr
| ApplyArg
);
/// Summation (Σ) expression.
///
/// When using [`@of`](QN_OF),
/// summation can also be used to produce a generator where each
/// iteration over `@of` yields a corresponding element in the vector
/// identified by [`@generates`](QN_GENERATES).
///
/// Summation is generated automatically by [`RateEachStmt`].
SumExpr := QN_C_SUM(_, ospan) {
@ {
QN_OF => TodoAttr,
QN_GENERATES => TodoAttr,
QN_INDEX => TodoAttr,
QN_DESC => TodoAttr,
QN_LABEL => TodoAttr,
QN_SYM => TodoAttr,
QN_DIM => TodoAttr,
} => NirEntity::Sum.open(ospan),
/(cspan) => NirEntity::Sum.close(cspan),
WhenExpr,
CalcExpr,
};
/// Product (Π) expression.
///
/// When using [`@of`](QN_OF),
/// product can also be used to produce a generator where each
/// iteration over `@of` yields a corresponding element in the vector
/// identified by [`@generates`](QN_GENERATES).
ProductExpr := QN_C_PRODUCT(_, ospan) {
@ {
QN_OF => TodoAttr,
QN_GENERATES => TodoAttr,
QN_INDEX => TodoAttr,
QN_DESC => TodoAttr,
QN_LABEL => TodoAttr,
QN_DOT => TodoAttr,
QN_SYM => TodoAttr,
QN_DIM => TodoAttr,
} => NirEntity::Product.open(ospan),
/(cspan) => NirEntity::Product.close(cspan),
WhenExpr,
CalcExpr,
};
/// Quotient (÷) expression.
///
/// Traditionally,
/// TAME expected quotients to contain a numerator and a denominator
/// as only two [`CalcExpr`] expressions
/// (though either could be a [`QuotientExpr`] as well).
/// TAMER will be relaxing that restriction.
QuotientExpr := QN_C_QUOTIENT {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CalcExpr,
};
/// Exponentiation (_xʸ_) expression.
///
/// The first [`CalcExpr`] will be raised to the power of the second
/// [`CalcExpr`],
/// which will be raised to the power of any third,
/// and so on.
/// Traditionally,
/// TAME expected only a base and an exponent
/// (respectively),
/// but TAMER will be relaxing that restriction.
ExptExpr := QN_C_EXPT {
@ {} => Todo,
CalcExpr,
};
/// Expression yielding a scalar value of the provided identifier.
///
/// The identifier is named by [`@name`](QN_NAME),
/// with vectors requiring an [`@index`](QN_INDEX).
/// Matrices require use of a nested [`IndexExpr`] qualifier to resolve
/// a scalar.
ValueOfExpr := QN_C_VALUE_OF {
@ {
QN_NAME => TodoAttr,
QN_INDEX => TodoAttr,
QN_LABEL => TodoAttr,
} => Todo,
IndexExpr,
WhenExpr,
};
/// Expression qualifying an index of a parent expresion.
///
/// The result of the inner [`CalcExpr`] is used as a subscript of the
/// parent expression.
/// Sibling [`IndexExpr`]s evaluate to nested subscripts where the
/// subling applies to the result of the previous index operation
/// such that **M**_ⱼ,ₖ_ ≡ (**M**_ⱼ_)_ₖ_.
IndexExpr := QN_C_INDEX {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CalcExpr,
};
/// Expression yielding a constant scalar value.
ConstExpr := QN_C_CONST {
@ {
QN_VALUE => TodoAttr,
// TODO: Description was historically required to avoid magic
// values,
// but we now have short-hand constants which do not require
// descriptions.
// We should probably require both or neither,
// but requiring `c:value-of` short-hand wouldn't be
// the responsibility of NIR,
// so perhaps then neither should be.
QN_DESC => TodoAttr,
// _TODO: deprecate?
QN_TYPE => TodoAttr,
} => Todo,
WhenExpr,
};
/// Ceiling (⌈_x_⌉) expression.
CeilExpr := QN_C_CEIL(_, ospan) {
@ {
QN_LABEL => TodoAttr,
} => NirEntity::Ceil.open(ospan),
/(cspan) => NirEntity::Ceil.close(cspan),
CalcExpr,
};
/// Floor (⌊_x_⌋) expression.
FloorExpr := QN_C_FLOOR(_, ospan) {
@ {
QN_LABEL => TodoAttr,
} => NirEntity::Floor.open(ospan),
/(cspan) => NirEntity::Floor.close(cspan),
CalcExpr,
};
/// An expression that conditionally evaluates to sub-expressions
/// depending on a list of predicates.
///
/// Individual cases are evaluated in order,
/// and the first case whose predicates
/// (also called "guards")
/// are satisfied will have its expression evaluated and yielded as
/// the result of the entire [`CasesExpr`].
///
/// If no predicates match,
/// [`OtherwiseExpr`] is evaluated,
/// if pressent,
/// otherwise the value `0` is yielded.
CasesExpr := QN_C_CASES {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CaseExpr,
OtherwiseExpr,
};
/// A predicated case of a [`CasesExpr`] with an associated
/// [`CalcExpr`].
///
/// Cases are evaluated in the order in which they appear.
/// If all of the [`WhenExpr`]s evaluate truthfully,
/// then the inner [`CalcExpr`] will be evaluated and its result
/// yielded as the value of this expression
/// (and therefore the result of the parent [`CasesExpr`]).
/// Otherwise,
/// evaluation continues with the next sibling case,
/// if any.
CaseExpr := QN_C_CASE {
@ {
QN_LABEL => TodoAttr,
} => Todo,
WhenExpr,
CalcExpr,
};
/// A case of a [`CasesExpr`] that always matches.
///
/// This should be used as a catch-all when no sibling [`CaseExpr`]
/// matches.
/// The inner [`CalcExpr`] will be evaluated and its result yielded as
/// the result of this expression
/// (and therefore the result of the parent [`CasesExpr`]).
///
/// In absence of this expression,
/// [`CasesExpr`] may fall through with no matching expressions and
/// yield `0`.
/// If this behavior is unclear within a given context,
/// then [`OtherwiseExpr`] ought to be used to make the behavior
/// explicit.
OtherwiseExpr := QN_C_OTHERWISE {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CalcExpr,
};
/// Length of a vector (|**v**|).
///
/// This also yields the number of rows of a matrix,
/// which are vectors of vectors.
/// It is not defined for scalars.
LengthOfExpr := QN_C_LENGTH_OF {
@ {} => Todo,
CalcExpr,
};
/// Let expression.
///
/// This is equivalent to a let expression in the Lisp family of
/// languages,
/// where the inner [`LetValues`] defines a set of mutually
/// independent expressions whose associated identifiers are
/// lexically scoped to the inner [`CalcExpr`].
/// The result of the let expression is the result of the inner
/// [`CalcExpr`].
LetExpr := QN_C_LET {
@ {} => Todo,
LetValues,
CalcExpr,
};
/// A set of mutually independent expressions and associated identifiers
/// to be lexically scoped to the sibling [`CalcExpr`].
///
/// See [`LetExpr`] for more information.
LetValues := QN_C_VALUES {
@ {} => Todo,
LetValue,
};
/// An expression bound to an associated identifier that is lexically
/// scoped to a parent [`LetValues`]' sibling [`CalcExpr`].
///
/// A value cannot observe sibling values,
/// but it can observe values of an ancestor [`LetExpr`] that is not
/// its parent.
LetValue := QN_C_VALUE {
@ {
QN_NAME => TodoAttr,
QN_TYPE => TodoAttr,
// Misnomer
QN_SET => TodoAttr,
QN_DESC => TodoAttr,
} => Todo,
CalcExpr,
};
/// An expression yielding a vector consisting of each of its child
/// expressions' values as respective items.
VectorExpr := QN_C_VECTOR {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CalcExpr,
};
/// Function application.
///
/// The value of the expression is the return value of the function
/// applied to its argument list [`ApplyArg`].
///
/// The attribute [`@name`](QN_NAME) contains the name of the function
/// to apply.
/// All other arguments are desugared into child [`ApplyArg`]s with a
/// body [`ValueOfExpr`] such that `α="x"` expands into
/// `<`[`c:arg`](QN_C_ARG)` name="α"><`[`c:value-of`](QN_C_VALUE_OF)
/// `name="x" />`.
ApplyExpr := QN_C_APPLY {
@ {} => Todo,
[attr](_attr) => Todo,
ApplyArg,
};
/// Argument for function application.
///
/// Alternatively,
/// the parent element [`ApplyExpr`] may contain short-hand arguments
/// as attributes.
ApplyArg := QN_C_ARG {
@ {
QN_NAME => TodoAttr,
} => Todo,
CalcExpr,
};
/// Function application recursing on the parent [`ApplyExpr`].
///
/// This expression desugars into an [`ApplyExpr`] with the same name as
/// the parent [`ApplyExpr`] and copies all parent [`ApplyArg`]
/// expressions.
/// Any child [`ApplyArg`] of this expression will override the
/// arguments of the parent,
/// allowing for concise recursion in terms of only what has changed
/// in that recursive step.
RecurseExpr := QN_C_RECURSE {
@ {} => Todo,
[attr](_attr) => Todo,
ApplyArg,
};
/// Construct a list (vector) by providing a new head ("car") and a
/// (possibly empty) tail ("cdr").
///
/// This terminology originates from Lisp.
/// It is equivalent to an `unshift` operation.
ConsExpr := QN_C_CONS {
@ {} => Todo,
CalcExpr,
};
/// Retrieve the first element in a list (vector).
///
/// This terminology originates from Lisp.
CarExpr := QN_C_CAR {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CalcExpr,
};
/// Retrieve all but the first element of a list (vector).
///
/// This terminology originates from Lisp,
/// and is pronounced "could-er".
/// It is also called "tail".
CdrExpr := QN_C_CDR {
@ {
QN_LABEL => TodoAttr,
} => Todo,
CalcExpr,
};
/// Predicate the parent expression,
/// producing a value of `0` if the predicate does not match.
///
/// In expressions that do not require the use of [`WhenExpr`] as a
/// guard,
/// this is styled and interpreted as Iverson's brackets,
/// but there is no distinction between using [`WhenExpr`] and
/// multiplying by the value of the predicate;
/// the two forms are a matter of style.
///
/// The exception is [`CaseExpr`],
/// which requires [`WhenExpr`] as part of its grammar to define
/// conditions for which case to evaluate.
WhenExpr := QN_C_WHEN {
@ {
QN_NAME => TodoAttr,
QN_INDEX => TodoAttr,
QN_VALUE => TodoAttr,
} => Todo,
CalcPredExpr,
};
/// Calculation predicates.
///
/// These predicates are used to compare two values.
/// They are used by [`WhenExpr`] and [`MatchExpr`].
CalcPredExpr := (
EqCalcPredExpr
| NeCalcPredExpr
| LtCalcPredExpr
| GtCalcPredExpr
| LteCalcPredExpr
| GteCalcPredExpr
);
/// Equality predicate (=).
EqCalcPredExpr := QN_C_EQ {
@ {} => Todo,
CalcExpr,
};
/// Non-equality predicate (≠).
NeCalcPredExpr := QN_C_NE {
@ {} => Todo,
CalcExpr,
};
/// Less-than predicate (<).
LtCalcPredExpr := QN_C_LT {
@ {} => Todo,
CalcExpr,
};
/// Greater-than predicate (>).
GtCalcPredExpr := QN_C_GT {
@ {} => Todo,
CalcExpr,
};
/// Less-than or equality predicate (≤).
LteCalcPredExpr := QN_C_LTE {
@ {} => Todo,
CalcExpr,
};
/// Greater-than or equality predicate (≥).
GteCalcPredExpr := QN_C_GTE {
@ {} => Todo,
CalcExpr,
};
/////////////////////////
////////////////////////
////
//// Map Packages
////
/// Define a mapping from a Liza program definition into TAME
/// parameters.
///
/// The coupling of this mapping is historical,
/// since TAME was developed to work with the Liza data collection
/// framework.
/// The mapping occurs between the bucket and TAME params.
///
/// This will be generalized in the future.
ProgramMapStmt := QN_PROGRAM_MAP {
@ {
QN_XMLNS => TodoAttr,
QN_XMLNS_LV => TodoAttr,
QN_SRC => TodoAttr,
} => Todo,
MapPkgImportStmt,
MapImportStmt,
MapBody,
};
/// Declare a mapping from TAME values into a key/value object to be
/// returned to the caller.
///
/// This decouples TAME's calculations from the interface expected by
/// the caller.
/// This is also the only place where TAME is able to produce dynamic
/// string values.
ReturnMapStmt := QN_RETURN_MAP {
@ {
QN_XMLNS => TodoAttr,
QN_XMLNS_LV => TodoAttr,
} => Todo,
MapPkgImportStmt,
MapImportStmt,
MapBody,
};
/// Alias for [`ImportStmt`].
///
/// This is only necessary because of [`MapImportStmt`];
/// both that and [`MapPkgImportStmt`] will be removed in the future
/// in favor of [`ImportStmt`].
MapPkgImportStmt := QN_LV_IMPORT {
@ {
QN_PACKAGE => TodoAttr,
QN_EXPORT => TodoAttr,
} => Todo,
};
/// Import a map package.
///
/// The distinction between this an [`ImportStmt`] is historical and is
/// no longer meaningful;
/// it will be removed in the future.
MapImportStmt := QN_IMPORT {
@ {
QN_PATH => TodoAttr,
} => Todo,
};
/// Define the value of a key in the destination.
MapBody := (MapPassStmt | MapStmt);
/// Map a value into a key of the destination without modification.
///
/// See also [`MapStmt`] if the value needs to be modified in some way.
MapPassStmt := QN_PASS {
@ {
QN_NAME => TodoAttr,
QN_DEFAULT => TodoAttr,
QN_SCALAR => TodoAttr,
QN_OVERRIDE => TodoAttr,
QN_NOVALIDATE => TodoAttr,
} => Todo,
};
/// Map a value into a key of the destination.
///
/// See also [`MapPassStmt`] if the value does not need modification.
MapStmt := QN_MAP {
@ {
QN_TO => TodoAttr,
QN_FROM => TodoAttr,
// We need to be permissive in what we accept since this may
// match in different contexts;
// downstream IR will validate the against the map
// destination.
QN_VALUE => TodoAttr,
QN_DEFAULT => TodoAttr,
QN_SCALAR => TodoAttr,
QN_OVERRIDE => TodoAttr,
QN_NOVALIDATE => TodoAttr,
} => Todo,
MapStmtBody,
};
/// Methods for mapping a value.
MapStmtBody := (MapFromStmt | MapSetStmt | MapTransformStmt);
/// Source of data for a map operation.
MapFromStmt := QN_FROM {
@ {
QN_NAME => TodoAttr,
QN_DEFAULT => TodoAttr,
QN_SCALAR => TodoAttr,
QN_NOVALIDATE => TodoAttr,
} => Todo,
MapTranslateStmt,
};
/// List of 1:1 value translations for a map.
MapTranslateStmt := QN_TRANSLATE {
@ {
QN_KEY => TodoAttr,
QN_VALUE => TodoAttr,
} => Todo,
};
/// Yield a vector of values where each item corresponds to the
/// respective child expression.
///
/// TODO: This is a misnomer,
/// since the result is a vector,
/// not a set.
MapSetStmt := QN_SET {
@ {} => Todo,
MapSetBody,
};
/// Permitted mappings in a [`MapSetStmt`].
MapSetBody := (MapFromStmt | MapConstStmt);
/// Map from a constant value.
MapConstStmt := QN_CONST {
@ {
QN_VALUE => TodoAttr,
} => Todo,
};
/// Transform a value using some function.
///
/// This is currently only meaningful for string inputs,
/// for example to convert input string case and hash values.
///
/// Transformations may be composed via nesting.
MapTransformStmt := QN_TRANSFORM {
@ {
QN_METHOD => TodoAttr,
} => Todo,
MapStmtBody,
};
/////////////////////////
////////////////////////
////
//// Worksheets
////
/// Define a calculation worksheet.
///
/// This is also referred to as a "rating worksheet" because of TAME's
/// history as an insurance rating system.
///
/// A worksheet displays simplified human-readable calculations and
/// their results.
/// This is an alternative to the Summary Page,
/// which provides a complete view of the system and is likely far too
/// much information for most users.
///
/// Calculations are rendered in the order in which they appear in this
/// definition.
WorksheetStmt := QN_WORKSHEET {
@ {
QN_XMLNS => TodoAttr,
QN_NAME => TodoAttr,
QN_PACKAGE => TodoAttr,
} => Todo,
ExpandFunctionStmt,
DisplayStmt,
};
/// Render function arguments when encountered within a calculation
/// referenced by [`DisplayStmt`].
///
/// If a function is not expanded,
/// then its application is replaced with the name of the function.
/// The default behavior is intended to encapsulate details of functions
/// that happen to be used by the system but that the user is unlikely
/// to care about.
ExpandFunctionStmt := QN_EXPAND_FUNCTION {
@ {
QN_NAME => TodoAttr,
} => Todo,
};
/// Render a simplified, human-readable display of the calculation,
/// along with its result.
DisplayStmt := QN_DISPLAY {
@ {
QN_NAME => TodoAttr,
} => Todo,
};
/////////////////////////
////////////////////////
////
//// Template System
////
/// Any statement or expression that may conceivably be permitted within
/// the expansion context of a template.
///
/// Since templates may be used almost anywhere,
/// NIR must accept any statement or expression that is valid in an
/// expansion context.
/// This must include not only the toplevel statements and expressions,
/// such as [`PkgBodyStmt`],
/// but also _inner_ statements.
/// For example,
/// consider this common pattern:
///
/// ```xml
///
///
///
///
///
///
///
///
/// ```
///
/// In the above [`CasesExpr`],
/// a template appears where a [`WhenExpr`] is expected,
/// within a [`CaseExpr`].
/// The template `__when-gt__` will be defined something like this:
///
/// ```xml
///
///
///
///
///
///
///
///
///
/// ```
///
/// Therefore,
/// [`WhenExpr`] must be permitted as a direct child of
/// [`TemplateStmt`].
/// Whether or not such a thing is semantically valid depends on the
/// context in which the application of `__when-gt__` occurs,
/// which cannot be known by NIR since templates are not evaluated
/// at this stage;
/// that is the responsibility of later lowering stages.
AnyStmtOrExpr := (
PkgBodyStmt
// Until we fix QN_SET ambiguity, this should take precedence.
| InlineTemplateArgSet
| PkgStmtInner
| LogExpr
| CalcExpr
| CalcExprInner
);
/// Define a template.
///
/// Templates are TAME's metaprogramming facility and allow for
/// extending the grammar of TAME.
/// The body of a template is expanded into its application site.
///
/// A template may expand into multiple statements or expressions,
/// or even a mix of both,
/// with statements being hoisted automatically out of an expression
/// context.
///
/// For more information on what may be contained within a template body
/// and the context of its expansion,
/// see [`AnyStmtOrExpr`].
///
/// See also [`InlineTemplate`] for template definitions.
///
/// Templates are applied using [`ApplyTemplate`] or [`TplApplyShort`].
TemplateStmt := QN_TEMPLATE(_, ospan) {
@ {
QN_NAME => BindIdent,
QN_DESC => TodoAttr,
} => NirEntity::Tpl.open(ospan),
/(cspan) => NirEntity::Tpl.close(cspan),
TplHeading,
AnyStmtOrExpr,
};
/// Heading of a template definition.
///
/// The should consist entirely of [`TplParamStmt`],
/// but there is also a convention of placing [`TplIf`] and
/// [`TplUnless`] alongside those params when they perform input
/// validation.
TplHeading := (TplParamStmt | TplIf | TplUnless);
/// Define a template parameter.
///
/// Template parameters have the form `@name@` and represent
/// placeholders for expansion data.
/// Parameters are treated as string data during application,
/// but their final type depends on the context into which they are
/// expanded.
TplParamStmt := QN_PARAM {
@ {
QN_NAME => TodoAttr,
QN_DESC => TodoAttr,
} => Todo,
TplParamDefault,
};
/// Define the default value for a parameter when a value is not
/// provided by a template application.
///
/// When a template is applied using [`ApplyTemplate`] or
/// [`TplApplyShort`],
/// a parameter will evaluate this default expression if there is no
/// argument present with the same name as the parameter.
TplParamDefault := (
TplText
| TplParamValue
| TplParamInherit
| TplParamAdd
| TplParamClassToYields
| TplParamTypedefLookup
| TplParamSymValue
);
/// Default a parameter to a string value.
///
/// All template params are strings until they are expanded into a
/// context,
/// so this can be used for everything from identifier generation to
/// providing constant values.
/// The result will be as if the user typed the text themselves in the
/// associated template application argument.
TplText := QN_TEXT {
@ {
QN_UNIQUE => TodoAttr,
} => Todo,
};
/// Default the param to the value of another template param,
/// optionally transformed.
///
/// This is used primarily for generating identifiers.
/// This list of attributes represent methods to be applied.
///
/// This list will be refined further in TAMER,
/// since manipulation of values in the XSLT-based TAME was
/// cumbersome and slow
TplParamValue := QN_PARAM_VALUE {
@ {
QN_NAME => TodoAttr,
QN_DASH => TodoAttr,
QN_UPPER => TodoAttr,
QN_LOWER => TodoAttr,
QN_UCFIRST => TodoAttr,
QN_RMDASH => TodoAttr,
QN_RMUNDERSCORE => TodoAttr,
QN_IDENTIFIER => TodoAttr,
QN_SNAKE => TodoAttr,
} => Todo,
};
/// Inherit a default value from a metavalue.
///
/// Metavalues allow templates to communicate with one-another in an
/// expansion environment.
/// They are defined using [`TplParamMeta`],
/// and this expression will retrieve the "closest" preceding value
/// from siblings and ancestors,
/// which is defined lexically relative to the expansion position
/// of the template.
TplParamInherit := QN_PARAM_INHERIT {
@ {
QN_META => TodoAttr,
} => Todo,
};
/// Sum a numeric value with a numeric template parameter.
///
/// Combined with [`TplParamInherit`],
/// this can be used to perform bounded recursive template expansion.
TplParamAdd := QN_PARAM_ADD {
@ {
QN_NAME => TodoAttr,
QN_VALUE => TodoAttr,
} => Todo,
};
/// Look up the [`@yields`](QN_YIELDS) of a [`ClassifyStmt`].
///
/// This allows templates to accept classification names and use them in
/// an expression context.
/// This is necessary since,
/// for historical reasons (accumulators),
/// classification names do not represent values.
/// Instead,
/// to treat a classification as a value,
/// its corresponding [`@yields`](QN_YIELDS) must be used.
///
/// Every [`ClassifyStmt`] has a yields generated for it if one is not
/// defined,
/// so this will always produce some valid identifier for a
/// classification.
TplParamClassToYields := QN_PARAM_CLASS_TO_YIELDS {
@ {
QN_NAME => TodoAttr,
} => Todo,
};
/// Given a numeric literal,
/// look up the associated constant item identifier within the
/// provided type [`@name`](QN_NAME).
///
/// The type must have been defined using [`TypedefStmt`] and must
/// utilize [`EnumStmt`].
///
/// Since all values in TAME are referentially transparent,
/// this has no effect at runtime.
/// Instead,
/// the purpose of this template is to allow generated code to
/// do two things:
///
/// 1. Ensure that a numeric value is within the domain of a given
/// type at compile time; and
/// 2. Produce an edge to that item
/// (and consequently type)
/// in TAME's dependency graph.
///
/// By providing an edge in the dependency graph to that item,
/// the graph can be used to query for what parts of the system
/// utilize particular values within the context of a type.
///
/// In this sense,
/// this introduces a form of nominal typing,
/// where the type can be used as a database of values and the
/// dependency graph can be used as a database of references.
///
/// For example,
/// in insurance,
/// a _class code_ is a numeric identifier representing some type of
/// potentially insurable risk.
/// By defining those class codes in types,
/// the system can be used to accurately report on what calculations
/// and classifications are affected by that class code.
/// Without the use of types,
/// querying for a constant numeric value would be ambiguous and
/// potentially yield false matches.
TplParamTypedefLookup := QN_PARAM_TYPEDEF_LOOKUP {
@ {
QN_NAME => TodoAttr,
QN_VALUE => TodoAttr,
} => Todo,
};
/// Look up an attribute from the symbol table for a given identifier.
TplParamSymValue := QN_PARAM_SYM_VALUE {
@ {
QN_NAME => TodoAttr,
QN_VALUE => TodoAttr,
QN_PREFIX => TodoAttr,
QN_IGNORE_MISSING => TodoAttr,
} => Todo,
};
/// Keywords that trigger template expansion.
///
/// These expressions may appear virtually anywhere in NIR,
/// since templates may be used to augment virtually every portion of
/// TAME's grammar.
/// The context into which a template is expanded may not be valid,
/// but this will not be known until templates are evaluated,
/// which is not the responsibility of NIR.
///
/// Note that these are expressions in a compile-time _expansion_
/// context,
/// not a runtime calculation context as other expressions in NIR.
/// The result of a template expression is conceptually an XML tree,
/// as if the user pasted the body of the template into place and
/// manually replaced all parameters with their intended values.
/// Not all expressions yield a tree,
/// and some may yield multiple trees;
/// NIR does not know or care.
TplKw := (
ApplyTemplate
| TplApplyShort
| InlineTemplate
| ExpandSequence
| ExpandGroup
| ExpandBarrier
| TplIf
| TplUnless
| TplParamCopy
| TplParamMeta
| ErrorKw
| WarningKw
| DynNode
);
// TODO: This has to go away so that we can always statically lower all
// primitives without having to perform template expansion in order to
// determine what they may be.
DynNode := QN_DYN_NODE {
@ {
QN_NAME => TodoAttr,
} => Todo,
// But we can at least restrict it for now by ensuring that it's
// used only to contain expressions.
CalcExpr,
};
/// Produce a compiler error whose message is the expansion of the body
/// of this expression.
///
/// This template yields an empty result.
///
/// Errors will result in a compilation failure.
/// See also [`WarningKw`] to provide a message to the user as
/// compiler output without failing compilation.
ErrorKw := QN_ERROR {
@ {} => Todo,
// In addition to text that is globally permitted.
TplParamValue,
};
/// Produce a compiler warning whose message is the expansion of the
/// body of this expression.
///
/// This template yields an empty result.
///
/// Warnings do not result in a compilation failure and may therefore be
/// missed in a sea of build output;
/// you should consider using [`ErrorKw`] whenever possible to
/// ensure that problems are immediately resolved.
WarningKw := QN_WARNING {
@ {} => Todo,
// In addition to text that is globally permitted.
TplParamValue,
};
/// Long-form template application.
///
/// This is neither a statement nor an expression as a part of this
/// grammar,
/// because this application is replaced entirely with its body
/// during expansion.
/// Further,
/// the template could expand into multiple statements or expressions,
/// or even a mix of the two
/// (with statements hoisted out of expressions).
///
/// See also [`TplApplyShort`],
/// which gets desugared into this via [`super::tplshort`].
ApplyTemplate := QN_APPLY_TEMPLATE(_, ospan) {
@ {
QN_NAME => RefSubject,
} => Nir::Open(NirEntity::TplApply, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplApply, cspan.into()),
ApplyTemplateParam,
};
/// Long-form template argument.
///
/// Template arguments are lexical.
///
/// See also [`TplApplyShort`],
/// which gets desugared into this via [`super::tplshort`].
ApplyTemplateParam := QN_WITH_PARAM(_, ospan) {
@ {
QN_NAME => BindIdent,
QN_VALUE => Text,
} => Nir::Open(NirEntity::TplParam, ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplParam, cspan.into()),
};
/// Shorthand template application.
///
/// This expands into an equivalent [`ApplyTemplate`] form where each
/// attribute is a template argument,
/// and where the body of this application is the `@values@`
/// template argument.
/// See [`ApplyTemplate`] for more information.
///
/// The name of the template omits the surrounding `_`s;
/// `t:foo` will desugar into the template name `_foo_`.
/// Params similarly omit `@` and are derived from the _local name
/// only_;
/// so `bar="baz"` will be desugared into a param `@bar@` with a
/// text value `baz`.
TplApplyShort := NS_T(qname, ospan) {
@ {} => Nir::Open(NirEntity::TplApplyShort(qname), ospan.into()),
/(cspan) => Nir::Close(NirEntity::TplApplyShort(qname), cspan.into()),
// Streaming attribute parsing;
// this takes precedence over any attribute parsing above
// (which is used only for emitting the opening object).
[attr](Attr(name, value, AttrSpan(name_span, value_span))) => {
Nir::Open(
// TODO: This simply _ignores_ the namespace prefix.
// If it's not a useful construct,
// it ought to be rejected.
NirEntity::TplParamShort(
SPair(*name.local_name(), name_span),
SPair(value, value_span),
),
name_span,
)
},
// Template bodies depend on context,
// so we have to just accept everything and defer to a future
// lowering operation to validate semantics.
AnyStmtOrExpr,
};
/// Define an anonymous template and immediately apply it zero or more
/// times.
///
/// Inline templates allow for the definition of a template at its
/// expansion site,
/// where a re-usable named template is not necessary.
///
/// Inline templates are also used for iterating over a list defined by
/// [`InlineTemplateForEach`],
/// and have the unique ability to perform symbol table
/// introspection using [`InlineTemplateSymSet`].
InlineTemplate := QN_INLINE_TEMPLATE {
@ {} => Todo,
InlineTemplateForEach,
AnyStmtOrExpr,
};
/// Define a list of [`InlineTemplateArgs`] over which an inline
/// template will be applied.
///
/// If there are N [`InlineTemplateArgs`],
/// then the body of the parent [`InlineTemplate`] will be applied
/// N times,
/// each with the respective [`InlineTemplateArgs`] set as its
/// arguments.
InlineTemplateForEach := QN_FOR_EACH {
@ {} => Todo,
InlineTemplateArgs,
};
/// Inline template argument sets.
InlineTemplateArgs := (InlineTemplateArgSet | InlineTemplateSymSet);
/// Define an argument set for an ancestor [`InlineTemplate`]
/// application.
///
/// Each key represents the name of a template parameter,
/// and the value represents the string value to bind to that
/// parameter as an argument.
///
/// See also parent [`InlineTemplateForEach`].
InlineTemplateArgSet := QN_SET {
@ {} => Todo,
// Streaming attribute parsing.
[attr](_attr) => Todo,
// TODO: REMOVE ME
// (bug in `ele_parse!` requiring at least one NT in this
// context.)
CalcExpr,
};
/// Derive template arguments from symbol table introspection.
///
/// This defines template arguments for the ancestor [`InlineTemplate`]
/// by querying the symbol table and exposing attributes associated
/// with that symbol.
///
/// See also [`ExpandSequence`] to control when symbol table querying
/// takes place to ensure that all identifiers in the same package are
/// defined before querying.
///
/// TODO: This is a really powerful feature that needs plenty of
/// documentation and examples.
InlineTemplateSymSet := QN_SYM_SET {
@ {
QN_NAME_PREFIX => TodoAttr,
QN_TYPE => TodoAttr,
// TODO: Look at XSL sources for others
} => Todo,
};
/// Perform template expansion on each successive child node in order,
/// as if it were a separate template pass each time.
///
/// Each child is recursively expanded before moving on to expansion of
/// the next child.
///
/// The purpose of this sequence is to ensure that identifiers are
/// defined before templates that query the symbol table via
/// [`InlineTemplateSymSet`];
/// otherwise.
/// It is otherwise not possible to guarantee that identifiers produced
/// by template expansions in the same package are complete before
/// the query takes place.
///
/// The XSLT-based version of TAME forced a separate template pass for
/// each and every child in this sequence,
/// which is expensive;
/// [`ExpandGroup`] was added to help mitigate the cost of this
/// operation.
///
/// TAMER hopes to remove the need for expansion sequences entirely,
/// since it makes complex use of the template system difficult to
/// understand,
/// and error-prone.
/// The concept originates from TeX's `\expandafter`, `\edef`, and
/// related macros.
ExpandSequence := QN_EXPAND_SEQUENCE {
@ {} => Todo,
AnyStmtOrExpr,
};
/// Groups nodes to be expanded together during [`ExpandSequence`].
///
/// This exists to work around performance pitfalls of the XSLT-based
/// implementation of [`ExpandSequence`];
/// see that NT for more information.
ExpandGroup := QN_EXPAND_GROUP {
@ {} => Todo,
AnyStmtOrExpr,
};
/// Prohibit template expansion beyond this point.
///
/// An expansion barrier is a seldom-needed feature that stops the
/// template system from expanding its body beyond a certain point,
/// which is sometimes needed for template-producing templates.
ExpandBarrier := QN_EXPAND_BARRIER {
@ {} => Todo,
AnyStmtOrExpr,
};
/// Inline the value of a parameter as a tree.
///
/// This is only useful for the special `@values@` parameter,
/// whose value is (conceptually) an XML tree.
///
/// This allows creating templates that accept children.
TplParamCopy := QN_PARAM_COPY {
@ {
QN_NAME => TodoAttr,
} => Todo,
};
/// Define a metavalue at this point in the expansion environment.
///
/// For more information on how these values are used,
/// see [`TplParamInherit`].
TplParamMeta := QN_PARAM_META {
@ {
QN_NAME => TodoAttr,
QN_VALUE => TodoAttr,
} => Todo,
};
/// Conditionally expand the body if the provided predicate matches.
TplIf := QN_IF {
@ {
QN_NAME => TodoAttr,
QN_EQ => TodoAttr,
QN_GT => TodoAttr,
QN_GTE => TodoAttr,
QN_LT => TodoAttr,
QN_LTE => TodoAttr,
QN_PREFIX => TodoAttr,
QN_SUFFIX => TodoAttr,
} => Todo,
AnyStmtOrExpr,
};
/// Conditionally expand the body if the provided predicate does not
/// match.
///
/// This can be used as a sibling of [`TplIf`] to create the equivalent
/// of an `else` clause.
TplUnless := QN_UNLESS {
@ {
QN_NAME => TodoAttr,
QN_EQ => TodoAttr,
QN_GT => TodoAttr,
QN_GTE => TodoAttr,
QN_LT => TodoAttr,
QN_LTE => TodoAttr,
QN_PREFIX => TodoAttr,
QN_SUFFIX => TodoAttr,
} => Todo,
AnyStmtOrExpr,
};
}