2cf95f2b60
To allow easir adaptation of data already serialized as JSON, HCL native syntax allows both equals signs _and_ colons for object constructors. This was already implemented, but not reflected in the pseudo-BNF in the specification.
942 lines
36 KiB
Markdown
942 lines
36 KiB
Markdown
# HCL Native Syntax Specification
|
|
|
|
This is the specification of the syntax and semantics of the native syntax
|
|
for HCL. HCL is a system for defining configuration languages for applications.
|
|
The HCL information model is designed to support multiple concrete syntaxes
|
|
for configuration, but this native syntax is considered the primary format
|
|
and is optimized for human authoring and maintenance, as opposed to machine
|
|
generation of configuration.
|
|
|
|
The language consists of three integrated sub-languages:
|
|
|
|
- The _structural_ language defines the overall hierarchical configuration
|
|
structure, and is a serialization of HCL bodies, blocks and attributes.
|
|
|
|
- The _expression_ language is used to express attribute values, either as
|
|
literals or as derivations of other values.
|
|
|
|
- The _template_ language is used to compose values together into strings,
|
|
as one of several types of expression in the expression language.
|
|
|
|
In normal use these three sub-languages are used together within configuration
|
|
files to describe an overall configuration, with the structural language
|
|
being used at the top level. The expression and template languages can also
|
|
be used in isolation, to implement features such as REPLs, debuggers, and
|
|
integration into more limited HCL syntaxes such as the JSON profile.
|
|
|
|
## Syntax Notation
|
|
|
|
Within this specification a semi-formal notation is used to illustrate the
|
|
details of syntax. This notation is intended for human consumption rather
|
|
than machine consumption, with the following conventions:
|
|
|
|
- A naked name starting with an uppercase letter is a global production,
|
|
common to all of the syntax specifications in this document.
|
|
- A naked name starting with a lowercase letter is a local production,
|
|
meaningful only within the specification where it is defined.
|
|
- Double and single quotes (`"` and `'`) are used to mark literal character
|
|
sequences, which may be either punctuation markers or keywords.
|
|
- The default operator for combining items, which has no punctuation,
|
|
is concatenation.
|
|
- The symbol `|` indicates that any one of its left and right operands may
|
|
be present.
|
|
- The `*` symbol indicates zero or more repetitions of the item to its left.
|
|
- The `?` symbol indicates zero or one of the item to its left.
|
|
- Parentheses (`(` and `)`) are used to group items together to apply
|
|
the `|`, `*` and `?` operators to them collectively.
|
|
|
|
The grammar notation does not fully describe the language. The prose may
|
|
augment or conflict with the illustrated grammar. In case of conflict, prose
|
|
has priority.
|
|
|
|
## Source Code Representation
|
|
|
|
Source code is unicode text expressed in the UTF-8 encoding. The language
|
|
itself does not perform unicode normalization, so syntax features such as
|
|
identifiers are sequences of unicode code points and so e.g. a precombined
|
|
accented character is distinct from a letter associated with a combining
|
|
accent. (String literals have some special handling with regard to Unicode
|
|
normalization which will be covered later in the relevant section.)
|
|
|
|
UTF-8 encoded Unicode byte order marks are not permitted. Invalid or
|
|
non-normalized UTF-8 encoding is always a parse error.
|
|
|
|
## Lexical Elements
|
|
|
|
### Comments and Whitespace
|
|
|
|
Comments and Whitespace are recognized as lexical elements but are ignored
|
|
except as described below.
|
|
|
|
Whitespace is defined as a sequence of zero or more space characters
|
|
(U+0020). Newline sequences (either U+000A or U+000D followed by U+000A)
|
|
are _not_ considered whitespace but are ignored as such in certain contexts.
|
|
Horizontal tab characters (U+0009) are also treated as whitespace, but are
|
|
counted only as one "column" for the purpose of reporting source positions.
|
|
|
|
Comments serve as program documentation and come in two forms:
|
|
|
|
- _Line comments_ start with either the `//` or `#` sequences and end with
|
|
the next newline sequence. A line comment is considered equivalent to a
|
|
newline sequence.
|
|
|
|
- _Inline comments_ start with the `/*` sequence and end with the `*/`
|
|
sequence, and may have any characters within except the ending sequence.
|
|
An inline comments is considered equivalent to a whitespace sequence.
|
|
|
|
Comments and whitespace cannot begin within within other comments, or within
|
|
template literals except inside an interpolation sequence or template directive.
|
|
|
|
### Identifiers
|
|
|
|
Identifiers name entities such as blocks, attributes and expression variables.
|
|
Identifiers are interpreted as per [UAX #31][uax31] Section 2. Specifically,
|
|
their syntax is defined in terms of the `ID_Start` and `ID_Continue`
|
|
character properties as follows:
|
|
|
|
```ebnf
|
|
Identifier = ID_Start (ID_Continue | '-')*;
|
|
```
|
|
|
|
The Unicode specification provides the normative requirements for identifier
|
|
parsing. Non-normatively, the spirit of this specification is that `ID_Start`
|
|
consists of Unicode letter and certain unambiguous punctuation tokens, while
|
|
`ID_Continue` augments that set with Unicode digits, combining marks, etc.
|
|
|
|
The dash character `-` is additionally allowed in identifiers, even though
|
|
that is not part of the unicode `ID_Continue` definition. This is to allow
|
|
attribute names and block type names to contain dashes, although underscores
|
|
as word separators are considered the idiomatic usage.
|
|
|
|
[uax31]: http://unicode.org/reports/tr31/ "Unicode Identifier and Pattern Syntax"
|
|
|
|
### Keywords
|
|
|
|
There are no globally-reserved words, but in some contexts certain identifiers
|
|
are reserved to function as keywords. These are discussed further in the
|
|
relevant documentation sections that follow. In such situations, the
|
|
identifier's role as a keyword supersedes any other valid interpretation that
|
|
may be possible. Outside of these specific situations, the keywords have no
|
|
special meaning and are interpreted as regular identifiers.
|
|
|
|
### Operators and Delimiters
|
|
|
|
The following character sequences represent operators, delimiters, and other
|
|
special tokens:
|
|
|
|
```
|
|
+ && == < : { [ ( ${
|
|
- || != > ? } ] ) %{
|
|
* ! <= = .
|
|
/ >= => ,
|
|
% ...
|
|
```
|
|
|
|
### Numeric Literals
|
|
|
|
A numeric literal is a decimal representation of a
|
|
real number. It has an integer part, a fractional part,
|
|
and an exponent part.
|
|
|
|
```ebnf
|
|
NumericLit = decimal+ ("." decimal+)? (expmark decimal+)?;
|
|
decimal = '0' .. '9';
|
|
expmark = ('e' | 'E') ("+" | "-")?;
|
|
```
|
|
|
|
## Structural Elements
|
|
|
|
The structural language consists of syntax representing the following
|
|
constructs:
|
|
|
|
- _Attributes_, which assign a value to a specified name.
|
|
- _Blocks_, which create a child body annotated by a type and optional labels.
|
|
- _Body Content_, which consists of a collection of attributes and blocks.
|
|
|
|
These constructs correspond to the similarly-named concepts in the
|
|
language-agnostic HCL information model.
|
|
|
|
```ebnf
|
|
ConfigFile = Body;
|
|
Body = (Attribute | Block | OneLineBlock)*;
|
|
Attribute = Identifier "=" Expression Newline;
|
|
Block = Identifier (StringLit|Identifier)* "{" Newline Body "}" Newline;
|
|
OneLineBlock = Identifier (StringLit|Identifier)* "{" (Identifier "=" Expression)? "}" Newline;
|
|
```
|
|
|
|
### Configuration Files
|
|
|
|
A _configuration file_ is a sequence of characters whose top-level is
|
|
interpreted as a Body.
|
|
|
|
### Bodies
|
|
|
|
A _body_ is a collection of associated attributes and blocks. The meaning of
|
|
this association is defined by the calling application.
|
|
|
|
### Attribute Definitions
|
|
|
|
An _attribute definition_ assigns a value to a particular attribute name within
|
|
a body. Each distinct attribute name may be defined no more than once within a
|
|
single body.
|
|
|
|
The attribute value is given as an expression, which is retained literally
|
|
for later evaluation by the calling application.
|
|
|
|
### Blocks
|
|
|
|
A _block_ creates a child body that is annotated with a block _type_ and
|
|
zero or more block _labels_. Blocks create a structural hierarchy which can be
|
|
interpreted by the calling application.
|
|
|
|
Block labels can either be quoted literal strings or naked identifiers.
|
|
|
|
## Expressions
|
|
|
|
The expression sub-language is used within attribute definitions to specify
|
|
values.
|
|
|
|
```ebnf
|
|
Expression = (
|
|
ExprTerm |
|
|
Operation |
|
|
Conditional
|
|
);
|
|
```
|
|
|
|
### Types
|
|
|
|
The value types used within the expression language are those defined by the
|
|
syntax-agnostic HCL information model. An expression may return any valid
|
|
type, but only a subset of the available types have first-class syntax.
|
|
A calling application may make other types available via _variables_ and
|
|
_functions_.
|
|
|
|
### Expression Terms
|
|
|
|
Expression _terms_ are the operands for unary and binary expressions, as well
|
|
as acting as expressions in their own right.
|
|
|
|
```ebnf
|
|
ExprTerm = (
|
|
LiteralValue |
|
|
CollectionValue |
|
|
TemplateExpr |
|
|
VariableExpr |
|
|
FunctionCall |
|
|
ForExpr |
|
|
ExprTerm Index |
|
|
ExprTerm GetAttr |
|
|
ExprTerm Splat |
|
|
"(" Expression ")"
|
|
);
|
|
```
|
|
|
|
The productions for these different term types are given in their corresponding
|
|
sections.
|
|
|
|
Between the `(` and `)` characters denoting a sub-expression, newline
|
|
characters are ignored as whitespace.
|
|
|
|
### Literal Values
|
|
|
|
A _literal value_ immediately represents a particular value of a primitive
|
|
type.
|
|
|
|
```ebnf
|
|
LiteralValue = (
|
|
NumericLit |
|
|
"true" |
|
|
"false" |
|
|
"null"
|
|
);
|
|
```
|
|
|
|
- Numeric literals represent values of type _number_.
|
|
- The `true` and `false` keywords represent values of type _bool_.
|
|
- The `null` keyword represents a null value of the dynamic pseudo-type.
|
|
|
|
String literals are not directly available in the expression sub-language, but
|
|
are available via the template sub-language, which can in turn be incorporated
|
|
via _template expressions_.
|
|
|
|
### Collection Values
|
|
|
|
A _collection value_ combines zero or more other expressions to produce a
|
|
collection value.
|
|
|
|
```ebnf
|
|
CollectionValue = tuple | object;
|
|
tuple = "[" (
|
|
(Expression ("," Expression)* ","?)?
|
|
) "]";
|
|
object = "{" (
|
|
(objectelem ("," objectelem)* ","?)?
|
|
) "}";
|
|
objectelem = (Identifier | Expression) ("=" | ":") Expression;
|
|
```
|
|
|
|
Only tuple and object values can be directly constructed via native syntax.
|
|
Tuple and object values can in turn be converted to list, set and map values
|
|
with other operations, which behaves as defined by the syntax-agnostic HCL
|
|
information model.
|
|
|
|
When specifying an object element, an identifier is interpreted as a literal
|
|
attribute name as opposed to a variable reference. To populate an item key
|
|
from a variable, use parentheses to disambiguate:
|
|
|
|
- `{foo = "baz"}` is interpreted as an attribute literally named `foo`.
|
|
- `{(foo) = "baz"}` is interpreted as an attribute whose name is taken
|
|
from the variable named `foo`.
|
|
|
|
Between the open and closing delimiters of these sequences, newline sequences
|
|
are ignored as whitespace.
|
|
|
|
There is a syntax ambiguity between _for expressions_ and collection values
|
|
whose first element is a reference to a variable named `for`. The
|
|
_for expression_ interpretation has priority, so to produce a tuple whose
|
|
first element is the value of a variable named `for`, or an object with a
|
|
key named `for`, use parentheses to disambiguate:
|
|
|
|
- `[for, foo, baz]` is a syntax error.
|
|
- `[(for), foo, baz]` is a tuple whose first element is the value of variable
|
|
`for`.
|
|
- `{for: 1, baz: 2}` is a syntax error.
|
|
- `{(for): 1, baz: 2}` is an object with an attribute literally named `for`.
|
|
- `{baz: 2, for: 1}` is equivalent to the previous example, and resolves the
|
|
ambiguity by reordering.
|
|
|
|
### Template Expressions
|
|
|
|
A _template expression_ embeds a program written in the template sub-language
|
|
as an expression. Template expressions come in two forms:
|
|
|
|
- A _quoted_ template expression is delimited by quote characters (`"`) and
|
|
defines a template as a single-line expression with escape characters.
|
|
- A _heredoc_ template expression is introduced by a `<<` sequence and
|
|
defines a template via a multi-line sequence terminated by a user-chosen
|
|
delimiter.
|
|
|
|
In both cases the template interpolation and directive syntax is available for
|
|
use within the delimiters, and any text outside of these special sequences is
|
|
interpreted as a literal string.
|
|
|
|
In _quoted_ template expressions any literal string sequences within the
|
|
template behave in a special way: literal newline sequences are not permitted
|
|
and instead _escape sequences_ can be included, starting with the
|
|
backslash `\`:
|
|
|
|
```
|
|
\n Unicode newline control character
|
|
\r Unicode carriage return control character
|
|
\t Unicode tab control character
|
|
\" Literal quote mark, used to prevent interpretation as end of string
|
|
\\ Literal backslash, used to prevent interpretation as escape sequence
|
|
\uNNNN Unicode character from Basic Multilingual Plane (NNNN is four hexadecimal digits)
|
|
\UNNNNNNNN Unicode character from supplementary planes (NNNNNNNN is eight hexadecimal digits)
|
|
```
|
|
|
|
The _heredoc_ template expression type is introduced by either `<<` or `<<-`,
|
|
followed by an identifier. The template expression ends when the given
|
|
identifier subsequently appears again on a line of its own.
|
|
|
|
If a heredoc template is introduced with the `<<-` symbol, any literal string
|
|
at the start of each line is analyzed to find the minimum number of leading
|
|
spaces, and then that number of prefix spaces is removed from all line-leading
|
|
literal strings. The final closing marker may also have an arbitrary number
|
|
of spaces preceding it on its line.
|
|
|
|
```ebnf
|
|
TemplateExpr = quotedTemplate | heredocTemplate;
|
|
quotedTemplate = (as defined in prose above);
|
|
heredocTemplate = (
|
|
("<<" | "<<-") Identifier Newline
|
|
(content as defined in prose above)
|
|
Identifier Newline
|
|
);
|
|
```
|
|
|
|
A quoted template expression containing only a single literal string serves
|
|
as a syntax for defining literal string _expressions_. In certain contexts
|
|
the template syntax is restricted in this manner:
|
|
|
|
```ebnf
|
|
StringLit = '"' (quoted literals as defined in prose above) '"';
|
|
```
|
|
|
|
The `StringLit` production permits the escape sequences discussed for quoted
|
|
template expressions as above, but does _not_ permit template interpolation
|
|
or directive sequences.
|
|
|
|
### Variables and Variable Expressions
|
|
|
|
A _variable_ is a value that has been assigned a symbolic name. Variables are
|
|
made available for use in expressions by the calling application, by populating
|
|
the _global scope_ used for expression evaluation.
|
|
|
|
Variables can also be created by expressions themselves, which always creates
|
|
a _child scope_ that incorporates the variables from its parent scope but
|
|
(re-)defines zero or more names with new values.
|
|
|
|
The value of a variable is accessed using a _variable expression_, which is
|
|
a standalone `Identifier` whose name corresponds to a defined variable:
|
|
|
|
```ebnf
|
|
VariableExpr = Identifier;
|
|
```
|
|
|
|
Variables in a particular scope are immutable, but child scopes may _hide_
|
|
a variable from an ancestor scope by defining a new variable of the same name.
|
|
When looking up variables, the most locally-defined variable of the given name
|
|
is used, and ancestor-scoped variables of the same name cannot be accessed.
|
|
|
|
No direct syntax is provided for declaring or assigning variables, but other
|
|
expression constructs implicitly create child scopes and define variables as
|
|
part of their evaluation.
|
|
|
|
### Functions and Function Calls
|
|
|
|
A _function_ is an operation that has been assigned a symbolic name. Functions
|
|
are made available for use in expressions by the calling application, by
|
|
populating the _function table_ used for expression evaluation.
|
|
|
|
The namespace of functions is distinct from the namespace of variables. A
|
|
function and a variable may share the same name with no implication that they
|
|
are in any way related.
|
|
|
|
A function can be executed via a _function call_ expression:
|
|
|
|
```ebnf
|
|
FunctionCall = Identifier "(" arguments ")";
|
|
Arguments = (
|
|
() ||
|
|
(Expression ("," Expression)* ("," | "...")?)
|
|
);
|
|
```
|
|
|
|
The definition of functions and the semantics of calling them are defined by
|
|
the language-agnostic HCL information model. The given arguments are mapped
|
|
onto the function's _parameters_ and the result of a function call expression
|
|
is the return value of the named function when given those arguments.
|
|
|
|
If the final argument expression is followed by the ellipsis symbol (`...`),
|
|
the final argument expression must evaluate to either a list or tuple value.
|
|
The elements of the value are each mapped to a single parameter of the
|
|
named function, beginning at the first parameter remaining after all other
|
|
argument expressions have been mapped.
|
|
|
|
Within the parentheses that delimit the function arguments, newline sequences
|
|
are ignored as whitespace.
|
|
|
|
### For Expressions
|
|
|
|
A _for expression_ is a construct for constructing a collection by projecting
|
|
the items from another collection.
|
|
|
|
```ebnf
|
|
ForExpr = forTupleExpr | forObjectExpr;
|
|
forTupleExpr = "[" forIntro Expression forCond? "]";
|
|
forObjectExpr = "{" forIntro Expression "=>" Expression "..."? forCond? "}";
|
|
forIntro = "for" Identifier ("," Identifier)? "in" Expression ":";
|
|
forCond = "if" Expression;
|
|
```
|
|
|
|
The punctuation used to delimit a for expression decide whether it will produce
|
|
a tuple value (`[` and `]`) or an object value (`{` and `}`).
|
|
|
|
The "introduction" is equivalent in both cases: the keyword `for` followed by
|
|
either one or two identifiers separated by a comma which define the temporary
|
|
variable names used for iteration, followed by the keyword `in` and then
|
|
an expression that must evaluate to a value that can be iterated. The
|
|
introduction is then terminated by the colon (`:`) symbol.
|
|
|
|
If only one identifier is provided, it is the name of a variable that will
|
|
be temporarily assigned the value of each element during iteration. If both
|
|
are provided, the first is the key and the second is the value.
|
|
|
|
Tuple, object, list, map, and set types are iterable. The type of collection
|
|
used defines how the key and value variables are populated:
|
|
|
|
- For tuple and list types, the _key_ is the zero-based index into the
|
|
sequence for each element, and the _value_ is the element value. The
|
|
elements are visited in index order.
|
|
- For object and map types, the _key_ is the string attribute name or element
|
|
key, and the _value_ is the attribute or element value. The elements are
|
|
visited in the order defined by a lexicographic sort of the attribute names
|
|
or keys.
|
|
- For set types, the _key_ and _value_ are both the element value. The elements
|
|
are visited in an undefined but consistent order.
|
|
|
|
The expression after the colon and (in the case of object `for`) the expression
|
|
after the `=>` are both evaluated once for each element of the source
|
|
collection, in a local scope that defines the key and value variable names
|
|
specified.
|
|
|
|
The results of evaluating these expressions for each input element are used
|
|
to populate an element in the new collection. In the case of tuple `for`, the
|
|
single expression becomes an element, appending values to the tuple in visit
|
|
order. In the case of object `for`, the pair of expressions is used as an
|
|
attribute name and value respectively, creating an element in the resulting
|
|
object.
|
|
|
|
In the case of object `for`, it is an error if two input elements produce
|
|
the same result from the attribute name expression, since duplicate
|
|
attributes are not possible. If the ellipsis symbol (`...`) appears
|
|
immediately after the value expression, this activates the grouping mode in
|
|
which each value in the resulting object is a _tuple_ of all of the values
|
|
that were produced against each distinct key.
|
|
|
|
- `[for v in ["a", "b"]: v]` returns `["a", "b"]`.
|
|
- `[for i, v in ["a", "b"]: i]` returns `[0, 1]`.
|
|
- `{for i, v in ["a", "b"]: v => i}` returns `{a = 0, b = 1}`.
|
|
- `{for i, v in ["a", "a", "b"]: k => v}` produces an error, because attribute
|
|
`a` is defined twice.
|
|
- `{for i, v in ["a", "a", "b"]: v => i...}` returns `{a = [0, 1], b = [2]}`.
|
|
|
|
If the `if` keyword is used after the element expression(s), it applies an
|
|
additional predicate that can be used to conditionally filter elements from
|
|
the source collection from consideration. The expression following `if` is
|
|
evaluated once for each source element, in the same scope used for the
|
|
element expression(s). It must evaluate to a boolean value; if `true`, the
|
|
element will be evaluated as normal, while if `false` the element will be
|
|
skipped.
|
|
|
|
- `[for i, v in ["a", "b", "c"]: v if i < 2]` returns `["a", "b"]`.
|
|
|
|
If the collection value, element expression(s) or condition expression return
|
|
unknown values that are otherwise type-valid, the result is a value of the
|
|
dynamic pseudo-type.
|
|
|
|
### Index Operator
|
|
|
|
The _index_ operator returns the value of a single element of a collection
|
|
value. It is a postfix operator and can be applied to any value that has
|
|
a tuple, object, map, or list type.
|
|
|
|
```ebnf
|
|
Index = "[" Expression "]";
|
|
```
|
|
|
|
The expression delimited by the brackets is the _key_ by which an element
|
|
will be looked up.
|
|
|
|
If the index operator is applied to a value of tuple or list type, the
|
|
key expression must be an non-negative integer number representing the
|
|
zero-based element index to access. If applied to a value of object or map
|
|
type, the key expression must be a string representing the attribute name
|
|
or element key. If the given key value is not of the appropriate type, a
|
|
conversion is attempted using the conversion rules from the HCL
|
|
syntax-agnostic information model.
|
|
|
|
An error is produced if the given key expression does not correspond to
|
|
an element in the collection, either because it is of an unconvertable type,
|
|
because it is outside the range of elements for a tuple or list, or because
|
|
the given attribute or key does not exist.
|
|
|
|
If either the collection or the key are an unknown value of an
|
|
otherwise-suitable type, the return value is an unknown value whose type
|
|
matches what type would be returned given known values, or a value of the
|
|
dynamic pseudo-type if type information alone cannot determine a suitable
|
|
return type.
|
|
|
|
Within the brackets that delimit the index key, newline sequences are ignored
|
|
as whitespace.
|
|
|
|
The HCL native syntax also includes a _legacy_ index operator that exists
|
|
only for compatibility with the precursor language HIL:
|
|
|
|
```ebnf
|
|
LegacyIndex = '.' digit+
|
|
```
|
|
|
|
This legacy index operator must be supported by parser for compatibility but
|
|
should not be used in new configurations. This allows an attribute-access-like
|
|
syntax for indexing, must still be interpreted as an index operation rather
|
|
than attribute access.
|
|
|
|
The legacy syntax does not support chaining of index operations, like
|
|
`foo.0.0.bar`, because the interpretation of `0.0` as a number literal token
|
|
takes priority and thus renders the resulting sequence invalid.
|
|
|
|
### Attribute Access Operator
|
|
|
|
The _attribute access_ operator returns the value of a single attribute in
|
|
an object value. It is a postfix operator and can be applied to any value
|
|
that has an object type.
|
|
|
|
```ebnf
|
|
GetAttr = "." Identifier;
|
|
```
|
|
|
|
The given identifier is interpreted as the name of the attribute to access.
|
|
An error is produced if the object to which the operator is applied does not
|
|
have an attribute with the given name.
|
|
|
|
If the object is an unknown value of a type that has the attribute named, the
|
|
result is an unknown value of the attribute's type.
|
|
|
|
### Splat Operators
|
|
|
|
The _splat operators_ allow convenient access to attributes or elements of
|
|
elements in a tuple, list, or set value.
|
|
|
|
There are two kinds of "splat" operator:
|
|
|
|
- The _attribute-only_ splat operator supports only attribute lookups into
|
|
the elements from a list, but supports an arbitrary number of them.
|
|
|
|
- The _full_ splat operator additionally supports indexing into the elements
|
|
from a list, and allows any combination of attribute access and index
|
|
operations.
|
|
|
|
```ebnf
|
|
Splat = attrSplat | fullSplat;
|
|
attrSplat = "." "*" GetAttr*;
|
|
fullSplat = "[" "*" "]" (GetAttr | Index)*;
|
|
```
|
|
|
|
The splat operators can be thought of as shorthands for common operations that
|
|
could otherwise be performed using _for expressions_:
|
|
|
|
- `tuple.*.foo.bar[0]` is approximately equivalent to
|
|
`[for v in tuple: v.foo.bar][0]`.
|
|
- `tuple[*].foo.bar[0]` is approximately equivalent to
|
|
`[for v in tuple: v.foo.bar[0]]`
|
|
|
|
Note the difference in how the trailing index operator is interpreted in
|
|
each case. This different interpretation is the key difference between the
|
|
_attribute-only_ and _full_ splat operators.
|
|
|
|
Splat operators have one additional behavior compared to the equivalent
|
|
_for expressions_ shown above: if a splat operator is applied to a value that
|
|
is _not_ of tuple, list, or set type, the value is coerced automatically into
|
|
a single-value list of the value type:
|
|
|
|
- `any_object.*.id` is equivalent to `[any_object.id]`, assuming that `any_object`
|
|
is a single object.
|
|
- `any_number.*` is equivalent to `[any_number]`, assuming that `any_number`
|
|
is a single number.
|
|
|
|
If applied to a null value that is not tuple, list, or set, the result is always
|
|
an empty tuple, which allows conveniently converting a possibly-null scalar
|
|
value into a tuple of zero or one elements. It is illegal to apply a splat
|
|
operator to a null value of tuple, list, or set type.
|
|
|
|
### Operations
|
|
|
|
Operations apply a particular operator to either one or two expression terms.
|
|
|
|
```ebnf
|
|
Operation = unaryOp | binaryOp;
|
|
unaryOp = ("-" | "!") ExprTerm;
|
|
binaryOp = ExprTerm binaryOperator ExprTerm;
|
|
binaryOperator = compareOperator | arithmeticOperator | logicOperator;
|
|
compareOperator = "==" | "!=" | "<" | ">" | "<=" | ">=";
|
|
arithmeticOperator = "+" | "-" | "*" | "/" | "%";
|
|
logicOperator = "&&" | "||" | "!";
|
|
```
|
|
|
|
The unary operators have the highest precedence.
|
|
|
|
The binary operators are grouped into the following precedence levels:
|
|
|
|
```
|
|
Level Operators
|
|
6 * / %
|
|
5 + -
|
|
4 > >= < <=
|
|
3 == !=
|
|
2 &&
|
|
1 ||
|
|
```
|
|
|
|
Higher values of "level" bind tighter. Operators within the same precedence
|
|
level have left-to-right associativity. For example, `x / y * z` is equivalent
|
|
to `(x / y) * z`.
|
|
|
|
### Comparison Operators
|
|
|
|
Comparison operators always produce boolean values, as a result of testing
|
|
the relationship between two values.
|
|
|
|
The two equality operators apply to values of any type:
|
|
|
|
```
|
|
a == b equal
|
|
a != b not equal
|
|
```
|
|
|
|
Two values are equal if the are of identical types and their values are
|
|
equal as defined in the HCL syntax-agnostic information model. The equality
|
|
operators are commutative and opposite, such that `(a == b) == !(a != b)`
|
|
and `(a == b) == (b == a)` for all values `a` and `b`.
|
|
|
|
The four numeric comparison operators apply only to numbers:
|
|
|
|
```
|
|
a < b less than
|
|
a <= b less than or equal to
|
|
a > b greater than
|
|
a >= b greater than or equal to
|
|
```
|
|
|
|
If either operand of a comparison operator is a correctly-typed unknown value
|
|
or a value of the dynamic pseudo-type, the result is an unknown boolean.
|
|
|
|
### Arithmetic Operators
|
|
|
|
Arithmetic operators apply only to number values and always produce number
|
|
values as results.
|
|
|
|
```
|
|
a + b sum (addition)
|
|
a - b difference (subtraction)
|
|
a * b product (multiplication)
|
|
a / b quotient (division)
|
|
a % b remainder (modulo)
|
|
-a negation
|
|
```
|
|
|
|
Arithmetic operations are considered to be performed in an arbitrary-precision
|
|
number space.
|
|
|
|
If either operand of an arithmetic operator is an unknown number or a value
|
|
of the dynamic pseudo-type, the result is an unknown number.
|
|
|
|
### Logic Operators
|
|
|
|
Logic operators apply only to boolean values and always produce boolean values
|
|
as results.
|
|
|
|
```
|
|
a && b logical AND
|
|
a || b logical OR
|
|
!a logical NOT
|
|
```
|
|
|
|
If either operand of a logic operator is an unknown bool value or a value
|
|
of the dynamic pseudo-type, the result is an unknown bool value.
|
|
|
|
### Conditional Operator
|
|
|
|
The conditional operator allows selecting from one of two expressions based on
|
|
the outcome of a boolean expression.
|
|
|
|
```ebnf
|
|
Conditional = Expression "?" Expression ":" Expression;
|
|
```
|
|
|
|
The first expression is the _predicate_, which is evaluated and must produce
|
|
a boolean result. If the predicate value is `true`, the result of the second
|
|
expression is the result of the conditional. If the predicate value is
|
|
`false`, the result of the third expression is the result of the conditional.
|
|
|
|
The second and third expressions must be of the same type or must be able to
|
|
unify into a common type using the type unification rules defined in the
|
|
HCL syntax-agnostic information model. This unified type is the result type
|
|
of the conditional, with both expressions converted as necessary to the
|
|
unified type.
|
|
|
|
If the predicate is an unknown boolean value or a value of the dynamic
|
|
pseudo-type then the result is an unknown value of the unified type of the
|
|
other two expressions.
|
|
|
|
If either the second or third expressions produce errors when evaluated,
|
|
these errors are passed through only if the erroneous expression is selected.
|
|
This allows for expressions such as
|
|
`length(some_list) > 0 ? some_list[0] : default` (given some suitable `length`
|
|
function) without producing an error when the predicate is `false`.
|
|
|
|
## Templates
|
|
|
|
The template sub-language is used within template expressions to concisely
|
|
combine strings and other values to produce other strings. It can also be
|
|
used in isolation as a standalone template language.
|
|
|
|
```ebnf
|
|
Template = (
|
|
TemplateLiteral |
|
|
TemplateInterpolation |
|
|
TemplateDirective
|
|
)*
|
|
TemplateDirective = TemplateIf | TemplateFor;
|
|
```
|
|
|
|
A template behaves like an expression that always returns a string value.
|
|
The different elements of the template are evaluated and combined into a
|
|
single string to return. If any of the elements produce an unknown string
|
|
or a value of the dynamic pseudo-type, the result is an unknown string.
|
|
|
|
An important use-case for standalone templates is to enable the use of
|
|
expressions in alternative HCL syntaxes where a native expression grammar is
|
|
not available. For example, the HCL JSON profile treats the values of JSON
|
|
strings as standalone templates when attributes are evaluated in expression
|
|
mode.
|
|
|
|
### Template Literals
|
|
|
|
A template literal is a literal sequence of characters to include in the
|
|
resulting string. When the template sub-language is used standalone, a
|
|
template literal can contain any unicode character, with the exception
|
|
of the sequences that introduce interpolations and directives, and for the
|
|
sequences that escape those introductions.
|
|
|
|
The interpolation and directive introductions are escaped by doubling their
|
|
leading characters. The `${` sequence is escaped as `$${` and the `%{`
|
|
sequence is escaped as `%%{`.
|
|
|
|
When the template sub-language is embedded in the expression language via
|
|
_template expressions_, additional constraints and transforms are applied to
|
|
template literals as described in the definition of template expressions.
|
|
|
|
The value of a template literal can be modified by _strip markers_ in any
|
|
interpolations or directives that are adjacent to it. A strip marker is
|
|
a tilde (`~`) placed immediately after the opening `{` or before the closing
|
|
`}` of a template sequence:
|
|
|
|
- `hello ${~ "world" }` produces `"helloworld"`.
|
|
- `%{ if true ~} hello %{~ endif }` produces `"hello"`.
|
|
|
|
When a strip marker is present, any spaces adjacent to it in the corresponding
|
|
string literal (if any) are removed before producing the final value. Space
|
|
characters are interpreted as per Unicode's definition.
|
|
|
|
Stripping is done at syntax level rather than value level. Values returned
|
|
by interpolations or directives are not subject to stripping:
|
|
|
|
- `${"hello" ~}${" world"}` produces `"hello world"`, and not `"helloworld"`,
|
|
because the space is not in a template literal directly adjacent to the
|
|
strip marker.
|
|
|
|
### Template Interpolations
|
|
|
|
An _interpolation sequence_ evaluates an expression (written in the
|
|
expression sub-language), converts the result to a string value, and
|
|
replaces itself with the resulting string.
|
|
|
|
```ebnf
|
|
TemplateInterpolation = ("${" | "${~") Expression ("}" | "~}";
|
|
```
|
|
|
|
If the expression result cannot be converted to a string, an error is
|
|
produced.
|
|
|
|
### Template If Directive
|
|
|
|
The template `if` directive is the template equivalent of the
|
|
_conditional expression_, allowing selection of one of two sub-templates based
|
|
on the value of a predicate expression.
|
|
|
|
```ebnf
|
|
TemplateIf = (
|
|
("%{" | "%{~") "if" Expression ("}" | "~}")
|
|
Template
|
|
(
|
|
("%{" | "%{~") "else" ("}" | "~}")
|
|
Template
|
|
)?
|
|
("%{" | "%{~") "endif" ("}" | "~}")
|
|
);
|
|
```
|
|
|
|
The evaluation of the `if` directive is equivalent to the conditional
|
|
expression, with the following exceptions:
|
|
|
|
- The two sub-templates always produce strings, and thus the result value is
|
|
also always a string.
|
|
- The `else` clause may be omitted, in which case the conditional's third
|
|
expression result is implied to be the empty string.
|
|
|
|
### Template For Directive
|
|
|
|
The template `for` directive is the template equivalent of the _for expression_,
|
|
producing zero or more copies of its sub-template based on the elements of
|
|
a collection.
|
|
|
|
```ebnf
|
|
TemplateFor = (
|
|
("%{" | "%{~") "for" Identifier ("," Identifier) "in" Expression ("}" | "~}")
|
|
Template
|
|
("%{" | "%{~") "endfor" ("}" | "~}")
|
|
);
|
|
```
|
|
|
|
The evaluation of the `for` directive is equivalent to the _for expression_
|
|
when producing a tuple, with the following exceptions:
|
|
|
|
- The sub-template always produces a string.
|
|
- There is no equivalent of the "if" clause on the for expression.
|
|
- The elements of the resulting tuple are all converted to strings and
|
|
concatenated to produce a flat string result.
|
|
|
|
### Template Interpolation Unwrapping
|
|
|
|
As a special case, a template that consists only of a single interpolation,
|
|
with no surrounding literals, directives or other interpolations, is
|
|
"unwrapped". In this case, the result of the interpolation expression is
|
|
returned verbatim, without conversion to string.
|
|
|
|
This special case exists primarily to enable the native template language
|
|
to be used inside strings in alternative HCL syntaxes that lack a first-class
|
|
template or expression syntax. Unwrapping allows arbitrary expressions to be
|
|
used to populate attributes when strings in such languages are interpreted
|
|
as templates.
|
|
|
|
- `${true}` produces the boolean value `true`
|
|
- `${"${true}"}` produces the boolean value `true`, because both the inner
|
|
and outer interpolations are subject to unwrapping.
|
|
- `hello ${true}` produces the string `"hello true"`
|
|
- `${""}${true}` produces the string `"true"` because there are two
|
|
interpolation sequences, even though one produces an empty result.
|
|
- `%{ for v in [true] }${v}%{ endif }` produces the string `true` because
|
|
the presence of the `for` directive circumvents the unwrapping even though
|
|
the final result is a single value.
|
|
|
|
In some contexts this unwrapping behavior may be circumvented by the calling
|
|
application, by converting the final template result to string. This is
|
|
necessary, for example, if a standalone template is being used to produce
|
|
the direct contents of a file, since the result in that case must always be a
|
|
string.
|
|
|
|
## Static Analysis
|
|
|
|
The HCL static analysis operations are implemented for some expression types
|
|
in the native syntax, as described in the following sections.
|
|
|
|
A goal for static analysis of the native syntax is for the interpretation to
|
|
be as consistent as possible with the dynamic evaluation interpretation of
|
|
the given expression, though some deviations are intentionally made in order
|
|
to maximize the potential for analysis.
|
|
|
|
### Static List
|
|
|
|
The tuple construction syntax can be interpreted as a static list. All of
|
|
the expression elements given are returned as the static list elements,
|
|
with no further interpretation.
|
|
|
|
### Static Map
|
|
|
|
The object construction syntax can be interpreted as a static map. All of the
|
|
key/value pairs given are returned as the static pairs, with no further
|
|
interpretation.
|
|
|
|
The usual requirement that an attribute name be interpretable as a string
|
|
does not apply to this static analysis, allowing callers to provide map-like
|
|
constructs with different key types by building on the map syntax.
|
|
|
|
### Static Call
|
|
|
|
The function call syntax can be interpreted as a static call. The called
|
|
function name is returned verbatim and the given argument expressions are
|
|
returned as the static arguments, with no further interpretation.
|
|
|
|
### Static Traversal
|
|
|
|
A variable expression and any attached attribute access operations and
|
|
constant index operations can be interpreted as a static traversal.
|
|
|
|
The keywords `true`, `false` and `null` can also be interpreted as
|
|
static traversals, behaving as if they were references to variables of those
|
|
names, to allow callers to redefine the meaning of those keywords in certain
|
|
contexts.
|