tame/tamer/src/nir/parse.rs

1794 lines
58 KiB
Rust
Raw Normal View History

// Normalized source IR
//
// Copyright (C) 2014-2022 Ryan Specialty Group, 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/>.
//! Normalized source IR.
//!
//! This IR is "near" the source code written by the user,
//! performing only normalization tasks like desugaring.
//! The hope is that all desugaring will be done by templates in the future.
//!
//! This is a streaming IR,
//! meaning that the equivalent AST is not explicitly represented as a
//! tree structure in memory.
//!
//! This IR is lossy and does not retain enough information for code
//! formatting---that
//! type of operation will require a mapping between
//! XIRF and NIR,
//! where the latter is used to gather enough context for formatting
//! and the former is used as a concrete representation of what the user
//! actually typed.
use super::*;
use crate::{
ele_parse,
sym::st::raw::*,
xir::{
attr::Attr,
st::{prefix::*, qname::*},
},
};
ele_parse! {
/// Parser lowering [XIR](crate::xir) into [NIR](crate::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.
///
/// 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) => Nir::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 {
@ {
_xmlns: (QN_XMLNS) => Literal<URI_LV_RATER>,
_xmlns_c: (QN_XMLNS_C) => Literal<URI_LV_CALC>,
_xmlns_t: (QN_XMLNS_T) => Literal<URI_LV_TPL>,
// TODO: Is this still needed?
// TODO: PkgName type
_name: (QN_NAME) => PkgPath,
} => Nir::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 {
@ {
_xmlns: (QN_XMLNS) => Literal<URI_LV_RATER>,
_xmlns_c: (QN_XMLNS_C) => Literal<URI_LV_CALC>,
_xmlns_t: (QN_XMLNS_T) => Literal<URI_LV_TPL>,
// 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.
_xmlns_lv: (QN_XMLNS_LV?) => Option<Literal<URI_LV_RATER>>,
_id: (QN_ID?) => Option<PkgPath>,
_title: (QN_TITLE?) => Option<PkgTitle>,
_desc: (QN_DESC?) => Option<DescLiteral>,
// TODO: When can we get rid of this?
_core: (QN_CORE?) => Option<BooleanLiteral>,
_program: (QN_PROGRAM?) => Option<BooleanLiteral>,
// TODO: Can this go away now?
_name: (QN_NAME?) => Option<PkgPath>,
} => Nir::Todo,
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 {
@ {
_pkg: (QN_PACKAGE) => PkgPath,
_export: (QN_EXPORT?) => Option<BooleanLiteral>,
} => Nir::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 {
@ {
_name: (QN_NAME) => AnyIdent,
_ty: (QN_TYPE) => IdentType,
_dtype: (QN_DTYPE?) => Option<IdentDtype>,
_dim: (QN_DIM) => NumLiteral,
_parent: (QN_PARENT?) => Option<AnyIdent>,
_yields: (QN_YIELDS?) => Option<ValueIdent>,
} => Nir::Todo,
};
/// Define an input parameter accepting data from an external system.
///
/// Parameters are generally populated via a map,
/// such as [`ProgramMapStmt`].
ParamStmt := QN_PARAM {
@ {
_name: (QN_NAME) => ParamName,
_ty: (QN_TYPE) => ParamType,
_desc: (QN_DESC) => DescLiteral,
// This is a misnomer.
_set: (QN_SET?) => Option<Dim>,
_default: (QN_DEFAULT?) => Option<ParamDefault>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
} => Nir::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 {
@ {
_name: (QN_NAME) => ConstIdent,
_desc: (QN_DESC) => DescLiteral,
_value: (QN_VALUE?) => Option<NumLiteral>,
_values: (QN_VALUES?) => Option<ShortDimNumLiteral>,
// TODO: deprecate?
_ty: (QN_TYPE?) => Option<TypeIdent>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
// TODO: Misnomer
_set: (QN_SET?) => Option<Dim>,
} => Nir::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 {
@ {
_desc: (QN_DESC) => DescLiteral,
} => Nir::Todo,
ConstVectorItem,
};
/// Constant vector scalar item definition.
ConstVectorItem := QN_ITEM {
@ {
_value: (QN_VALUE) => NumLiteral,
_desc: (QN_DESC) => DescLiteral,
} => Nir::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 {
@ {
_name: (QN_AS) => ClassIdent,
_desc: (QN_DESC) => DescLiteral,
_any: (QN_ANY?) => Option<BooleanLiteral>,
_yields: (QN_YIELDS?) => Option<ValueIdent>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
_terminate: (QN_TERMINATE?) => Option<BooleanLiteral>,
} => Nir::Todo,
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 {
@ {
_class: (QN_CLASS?) => Option<ClassIdent>,
_no: (QN_NO?) => Option<ClassIdentList>,
_yields: (QN_YIELDS) => CalcIdent,
_desc: (QN_DESC?) => Option<DescLiteral>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
// TODO: This is still recognized by the XSLT-based compiler,
// so we need to support it until it's removed.
_gentle_no: (QN_GENTLE_NO?) => Option<BooleanLiteral>,
// TODO: We'll have private-by-default later.
// This is a kludge.
_local: (QN_LOCAL?) => Option<BooleanLiteral>,
} => Nir::Todo,
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 {
@ {
_class: (QN_CLASS) => ClassIdentList,
_no: (QN_NO?) => Option<ClassIdentList>,
_generates: (QN_GENERATES?) => Option<ValueIdent>,
_index: (QN_INDEX) => ValueIdent,
_yields: (QN_YIELDS?) => Option<ValueIdent>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
_gensym: (QN_GENSYM?) => Option<TexMathLiteral>,
} => Nir::Todo,
CalcExpr,
};
/// Define a new type that restricts the domain of data.
TypedefStmt := QN_TYPEDEF {
@ {
_name: (QN_NAME) => TypeIdent,
_desc: (QN_DESC) => DescLiteral,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
} => Nir::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 {
@ {} => Nir::Todo,
};
/// Define an enumerated type.
///
/// Enums are types that have an explicit set of values,
/// each with associated constant identifiers.
EnumStmt := QN_ENUM {
@ {
_ty: (QN_TYPE) => TypeIdent,
} => Nir::Todo,
ItemEnumStmt,
};
/// Define an item of the domain of an enumerated type and associate it
/// with a constant identifier.
ItemEnumStmt := QN_ITEM {
@ {
_name: (QN_NAME) => ConstIdent,
_value: (QN_VALUE) => NumLiteral,
_desc: (QN_DESC) => DescLiteral,
} => Nir::Todo,
};
/// Define a type whose domain is the union of the domains of multiple
/// other types.
UnionStmt := QN_UNION {
@ {} => Nir::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 {
@ {} => Nir::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 {
@ {
_title: (QN_TITLE) => Title,
} => Nir::Todo,
PkgBodyStmt,
};
/// Define a function and associate it with an identifier.
FunctionStmt := QN_FUNCTION {
@ {
_name: (QN_NAME) => FuncIdent,
_desc: (QN_DESC) => DescLiteral,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
} => Nir::Todo,
FunctionParamStmt,
CalcExpr,
};
/// Define a function parameter and associate it with an identifier that
/// is scoped to the function body.
FunctionParamStmt := QN_PARAM {
@ {
_name: (QN_NAME) => ParamIdent,
_ty: (QN_TYPE) => TypeIdent,
// _TODO: This is a misnomer.
_set: (QN_SET?) => Option<Dim>,
_desc: (QN_DESC) => DescLiteral,
} => Nir::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 {
@ {
_on: (QN_ON) => ValueIdent,
_value: (QN_VALUE?) => Option<ValueIdent>,
_index: (QN_INDEX?) => Option<ValueIdent>,
_anyof: (QN_ANY_OF?) => Option<TypeIdent>,
} => Nir::Todo,
CalcPredExpr,
};
/// Logical disjunction ().
///
/// This represents an expression that matches when _any_ of its inner
/// [`LogExpr`] expressions match.
AnyExpr := QN_ANY {
@ {} => Nir::Todo,
LogExpr,
};
/// Logical conjunction (∧).
///
/// This represents an expression that matches when _all_ of its inner
/// [`LogExpr`] expressions match.
AllExpr := QN_ALL {
@ {} => Nir::Todo,
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 {
@ {
_of: (QN_OF?) => Option<ValueIdent>,
_generates: (QN_GENERATES?) => Option<CalcIdent>,
_index: (QN_INDEX?) => Option<CalcIdent>,
_desc: (QN_DESC?) => Option<DescLiteral>,
_label: (QN_LABEL?) => Option<DescLiteral>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
_dim: (QN_DIM?) => Option<Dim>,
} => Nir::Todo,
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 {
@ {
_of: (QN_OF?) => Option<ValueIdent>,
_generates: (QN_GENERATES?) => Option<CalcIdent>,
_index: (QN_INDEX?) => Option<CalcIdent>,
_desc: (QN_DESC?) => Option<DescLiteral>,
_label: (QN_LABEL?) => Option<DescLiteral>,
_dot: (QN_DOT?) => Option<BooleanLiteral>,
_sym: (QN_SYM?) => Option<TexMathLiteral>,
_dim: (QN_DIM?) => Option<Dim>,
} => Nir::Todo,
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 {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {} => Nir::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 {
@ {
_name: (QN_NAME) => ValueIdent,
_index: (QN_INDEX?) => Option<ValueIdent>,
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::Todo,
CalcExpr,
};
/// Expression yielding a constant scalar value.
ConstExpr := QN_C_CONST {
@ {
_value: (QN_VALUE) => NumLiteral,
// 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.
_desc: (QN_DESC?) => Option<DescLiteral>,
// _TODO: deprecate?
_ty: (QN_TYPE?) => Option<TypeIdent>,
} => Nir::Todo,
WhenExpr,
};
/// Ceiling (⌈_x_⌉) expression.
CeilExpr := QN_C_CEIL {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::Todo,
CalcExpr,
};
/// Floor (⌊_x_⌋) expression.
FloorExpr := QN_C_FLOOR {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::Todo,
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 {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {} => Nir::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 {
@ {} => Nir::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 {
@ {} => Nir::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 {
@ {
_name: (QN_NAME) => ParamIdent,
_ty: (QN_TYPE) => TypeIdent,
// Misnomer
_set: (QN_SET?) => Option<Dim>,
_desc: (QN_DESC?) => Option<DescLiteral>,
} => Nir::Todo,
CalcExpr,
};
/// An expression yielding a vector consisting of each of its child
/// expressions' values as respective items.
VectorExpr := QN_C_VECTOR {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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" /></c:arg>`.
ApplyExpr := QN_C_APPLY {
@ {} => Nir::Todo,
[attr](_attr) => Nir::Todo,
ApplyArg,
};
/// Argument for function application.
///
/// Alternatively,
/// the parent element [`ApplyExpr`] may contain short-hand arguments
/// as attributes.
ApplyArg := QN_C_ARG {
@ {
_name: (QN_NAME) => ParamIdent,
} => Nir::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 {
@ {} => Nir::Todo,
[attr](_attr) => Nir::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 {
@ {} => Nir::Todo,
CalcExpr,
};
/// Retrieve the first element in a list (vector).
///
/// This terminology originates from Lisp.
CarExpr := QN_C_CAR {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {
_label: (QN_LABEL?) => Option<DescLiteral>,
} => Nir::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 {
@ {
_name: (QN_NAME) => ValueIdent,
_index: (QN_INDEX?) => Option<ValueIdent>,
_value: (QN_VALUE?) => Option<ValueIdent>,
} => Nir::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 {
@ {} => Nir::Todo,
CalcExpr,
};
/// Non-equality predicate (≠).
NeCalcPredExpr := QN_C_NE {
@ {} => Nir::Todo,
CalcExpr,
};
/// Less-than predicate (<).
LtCalcPredExpr := QN_C_LT {
@ {} => Nir::Todo,
CalcExpr,
};
/// Greater-than predicate (>).
GtCalcPredExpr := QN_C_GT {
@ {} => Nir::Todo,
CalcExpr,
};
/// Less-than or equality predicate (≤).
LteCalcPredExpr := QN_C_LTE {
@ {} => Nir::Todo,
CalcExpr,
};
/// Greater-than or equality predicate (≥).
GteCalcPredExpr := QN_C_GTE {
@ {} => Nir::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 {
@ {
_xmlns: (QN_XMLNS) => Literal<URI_LV_PROGRAM_MAP>,
_xmlnslv: (QN_XMLNS_LV) => Literal<URI_LV_RATER>,
_src: (QN_SRC) => PkgPath,
} => Nir::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 {
@ {
_xmlns: (QN_XMLNS) => Literal<URI_LV_PROGRAM_MAP>,
_xmlnslv: (QN_XMLNS_LV) => Literal<URI_LV_RATER>,
} => Nir::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 {
@ {
_package: (QN_PACKAGE) => PkgPath,
_export: (QN_EXPORT?) => Option<BooleanLiteral>,
} => Nir::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 {
@ {
_path: (QN_PATH) => PkgPath,
} => Nir::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 {
@ {
_name: (QN_NAME) => AnyIdent,
_default: (QN_DEFAULT?) => Option<NumLiteral>,
_scalar: (QN_SCALAR?) => Option<BooleanLiteral>,
_override: (QN_OVERRIDE?) => Option<BooleanLiteral>,
_novalidate: (QN_NOVALIDATE?) => Option<BooleanLiteral>,
} => Nir::Todo,
};
/// Map a value into a key of the destination.
///
/// See also [`MapPassStmt`] if the value does not need modification.
MapStmt := QN_MAP {
@ {
_to: (QN_TO) => AnyIdent,
_from: (QN_FROM?) => Option<AnyIdent>,
// 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.
_value: (QN_VALUE?) => Option<StringLiteral>,
_default: (QN_DEFAULT?) => Option<NumLiteral>,
_scalar: (QN_SCALAR?) => Option<BooleanLiteral>,
_override: (QN_OVERRIDE?) => Option<BooleanLiteral>,
_novalidate: (QN_NOVALIDATE?) => Option<BooleanLiteral>,
} => Nir::Todo,
MapStmtBody,
};
/// Methods for mapping a value.
MapStmtBody := (MapFromStmt | MapSetStmt | MapTransformStmt);
/// Source of data for a map operation.
MapFromStmt := QN_FROM {
@ {
_name: (QN_NAME) => AnyIdent,
_default: (QN_DEFAULT?) => Option<NumLiteral>,
_scalar: (QN_SCALAR?) => Option<BooleanLiteral>,
_novalidate: (QN_NOVALIDATE?) => Option<BooleanLiteral>,
} => Nir::Todo,
MapTranslateStmt,
};
/// List of 1:1 value translations for a map.
MapTranslateStmt := QN_TRANSLATE {
@ {
_key: (QN_KEY) => StringLiteral,
_value: (QN_VALUE) => NumLiteral,
} => Nir::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 {
@ {} => Nir::Todo,
MapSetBody,
};
/// Permitted mappings in a [`MapSetStmt`].
MapSetBody := (MapFromStmt | MapConstStmt);
/// Map from a constant value.
MapConstStmt := QN_CONST {
@ {
_value: (QN_VALUE) => StringLiteral,
} => Nir::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 {
@ {
_method: (QN_METHOD) => MapTransformLiteral,
} => Nir::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 {
@ {
_xmlns: (QN_XMLNS) => Literal<URI_LV_WORKSHEET>,
_name: (QN_NAME) => PkgPath,
_pkg: (QN_PACKAGE) => PkgPath,
} => Nir::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 {
@ {
_name: (QN_NAME) => FuncIdent,
} => Nir::Todo,
};
/// Render a simplified, human-readable display of the calculation,
/// along with its result.
DisplayStmt := QN_DISPLAY {
@ {
_name: (QN_NAME) => ValueIdent,
} => Nir::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
/// <c:cases>
/// <c:case>
/// <t:when-gt name="foo" value="#5" />
/// <c:value-of name="bar" />
/// </c:case>
///
/// <!-- ... -->
/// </c:cases>
/// ```
///
/// 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
/// <template name="__when-gt__" desc="...">
/// <!-- params ... -->
///
/// <c:when name="@name@">
/// <c:gt>
/// <c:value-of name="@value@" />
/// </c:gt>
/// </c:when>
/// </template>
/// ```
///
/// 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 {
@ {
_name: (QN_NAME) => TplName,
_desc: (QN_DESC) => DescLiteral,
} => Nir::Todo,
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 {
@ {
_name: (QN_NAME) => TplParamIdent,
_desc: (QN_DESC) => DescLiteral,
} => Nir::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 {
@ {
_unique: (QN_UNIQUE?) => Option<BooleanLiteral>,
} => Nir::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 {
@ {
_name: (QN_NAME) => ParamIdent,
_dash: (QN_DASH?) => Option<BooleanLiteral>,
_upper: (QN_UPPER?) => Option<BooleanLiteral>,
_lower: (QN_LOWER?) => Option<BooleanLiteral>,
_ucfirst: (QN_UCFIRST?) => Option<BooleanLiteral>,
_rmdash: (QN_RMDASH?) => Option<BooleanLiteral>,
_rmunderscore: (QN_RMUNDERSCORE?) => Option<BooleanLiteral>,
_identifier: (QN_IDENTIFIER?) => Option<BooleanLiteral>,
_snake: (QN_SNAKE?) => Option<BooleanLiteral>,
} => Nir::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 {
@ {
_meta: (QN_META) => TplMetaIdent,
} => Nir::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 {
@ {
_name: (QN_NAME) => TplParamIdent,
_value: (QN_VALUE) => NumLiteral,
} => Nir::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 {
@ {
_name: (QN_NAME) => ClassIdent,
} => Nir::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 {
@ {
_name: (QN_NAME) => TypeIdent,
_value: (QN_VALUE) => NumLiteral,
} => Nir::Todo,
};
/// Look up an attribute from the symbol table for a given identifier.
TplParamSymValue := QN_PARAM_SYM_VALUE {
@ {
_name: (QN_NAME) => AnyIdent,
_value: (QN_VALUE) => SymbolTableKey,
_prefix: (QN_PREFIX?) => Option<AnyIdent>,
_ignore_missing: (QN_IGNORE_MISSING?) => Option<BooleanLiteral>,
} => Nir::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 {
@ {
_name: (QN_NAME) => DynNodeLiteral,
} => Nir::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 {
@ {} => Nir::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 {
@ {} => Nir::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).
///
/// TODO: This is apparently unused by the current system,
/// in favor of a transition to [`TplApplyShort`],
/// but this is still needed to support dynamic template application
/// (templates whose names are derived from other template inputs).
ApplyTemplate := QN_APPLY_TEMPLATE {
@ {} => Nir::Todo,
// TODO
};
/// Short-hand 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.
TplApplyShort := NS_T {
@ {} => Nir::Todo,
// Streaming attribute parsing;
// this takes precedence over any attribute parsing above
// (which is used only for emitting the opening object).
[attr](_attr) => Nir::Todo,
// 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 {
@ {} => Nir::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 {
@ {} => Nir::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 {
@ {} => Nir::Todo,
// Streaming attribute parsing.
[attr](_attr) => Nir::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 {
@ {
_name_prefix: (QN_NAME_PREFIX?) => Option<StringLiteral>,
_type: (QN_TYPE?) => Option<IdentType>,
// TODO: Look at XSL sources for others
} => Nir::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 {
@ {} => Nir::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 {
@ {} => Nir::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 {
@ {} => Nir::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 {
@ {
_name: (QN_NAME) => TplParamIdent,
} => Nir::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 {
@ {
_name: (QN_NAME) => TplParamIdent,
_value: (QN_VALUE) => StringLiteral,
} => Nir::Todo,
};
/// Conditionally expand the body if the provided predicate matches.
TplIf := QN_IF {
@ {
_name: (QN_NAME) => TplParamIdent,
_eq: (QN_EQ?) => Option<StringLiteral>,
_prefix: (QN_PREFIX?) => Option<StringLiteral>,
_suffix: (QN_SUFFIX?) => Option<StringLiteral>,
} => Nir::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 {
@ {
_name: (QN_NAME) => TplParamIdent,
_eq: (QN_EQ?) => Option<StringLiteral>,
_prefix: (QN_PREFIX?) => Option<StringLiteral>,
_suffix: (QN_SUFFIX?) => Option<StringLiteral>,
} => Nir::Todo,
AnyStmtOrExpr,
};
}