2017-09-11 23:49:35 +00:00
|
|
|
# HCL JSON Syntax Specification
|
2017-06-20 15:07:10 +00:00
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
This is the specification for the JSON serialization for hcl. HCL is a system
|
|
|
|
for defining configuration languages for applications. The HCL information
|
2017-06-20 15:07:10 +00:00
|
|
|
model is designed to support multiple concrete syntaxes for configuration,
|
2018-01-24 05:54:38 +00:00
|
|
|
and this JSON-based format complements [the native syntax](../hclsyntax/spec.md)
|
2017-06-20 15:07:10 +00:00
|
|
|
by being easy to machine-generate, whereas the native syntax is oriented
|
2018-07-01 17:35:20 +00:00
|
|
|
towards human authoring and maintenance
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
This syntax is defined in terms of JSON as defined in
|
|
|
|
[RFC7159](https://tools.ietf.org/html/rfc7159). As such it inherits the JSON
|
|
|
|
grammar as-is, and merely defines a specific methodology for interpreting
|
2017-09-11 23:49:35 +00:00
|
|
|
JSON constructs into HCL structural elements and expressions.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
This mapping is defined such that valid JSON-serialized HCL input can be
|
2018-02-17 18:26:58 +00:00
|
|
|
_produced_ using standard JSON implementations in various programming languages.
|
2017-06-20 15:07:10 +00:00
|
|
|
_Parsing_ such JSON has some additional constraints not beyond what is normally
|
2018-02-17 18:26:58 +00:00
|
|
|
supported by JSON parsers, so a specialized parser may be required that
|
|
|
|
is able to:
|
|
|
|
|
2019-01-03 16:46:02 +00:00
|
|
|
- Preserve the relative ordering of properties defined in an object.
|
|
|
|
- Preserve multiple definitions of the same property name.
|
|
|
|
- Preserve numeric values to the precision required by the number type
|
2018-02-17 18:26:58 +00:00
|
|
|
in [the HCL syntax-agnostic information model](../spec.md).
|
2019-01-03 16:46:02 +00:00
|
|
|
- Retain source location information for parsed tokens/constructs in order
|
2018-02-17 18:26:58 +00:00
|
|
|
to produce good error messages.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
## Structural Elements
|
|
|
|
|
2018-02-17 18:26:58 +00:00
|
|
|
[The HCL syntax-agnostic information model](../spec.md) defines a _body_ as an
|
|
|
|
abstract container for attribute definitions and child blocks. A body is
|
|
|
|
represented in JSON as either a single JSON object or a JSON array of objects.
|
|
|
|
|
|
|
|
Body processing is in terms of JSON object properties, visited in the order
|
|
|
|
they appear in the input. Where a body is represented by a single JSON object,
|
|
|
|
the properties of that object are visited in order. Where a body is
|
|
|
|
represented by a JSON array, each of its elements are visited in order and
|
|
|
|
each element has its properties visited in order. If any element of the array
|
|
|
|
is not a JSON object then the input is erroneous.
|
|
|
|
|
|
|
|
When a body is being processed in the _dynamic attributes_ mode, the allowance
|
|
|
|
of a JSON array in the previous paragraph does not apply and instead a single
|
|
|
|
JSON object is always required.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
2018-02-17 18:26:58 +00:00
|
|
|
As defined in the language-agnostic model, body processing is in terms
|
2017-06-20 15:07:10 +00:00
|
|
|
of a schema which provides context for interpreting the body's content. For
|
|
|
|
JSON bodies, the schema is crucial to allow differentiation of attribute
|
|
|
|
definitions and block definitions, both of which are represented via object
|
|
|
|
properties.
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
The special property name `"//"`, when used in an object representing a HCL
|
2017-06-20 15:07:10 +00:00
|
|
|
body, is parsed and ignored. A property with this name can be used to
|
|
|
|
include human-readable comments. (This special property name is _not_
|
2017-09-11 23:49:35 +00:00
|
|
|
processed in this way for any _other_ HCL constructs that are represented as
|
2017-06-20 15:07:10 +00:00
|
|
|
JSON objects.)
|
|
|
|
|
|
|
|
### Attributes
|
|
|
|
|
|
|
|
Where the given schema describes an attribute with a given name, the object
|
|
|
|
property with the matching name — if present — serves as the attribute's
|
|
|
|
definition.
|
|
|
|
|
|
|
|
When a body is being processed in the _dynamic attributes_ mode, each object
|
|
|
|
property serves as an attribute definition for the attribute whose name
|
|
|
|
matches the property name.
|
|
|
|
|
|
|
|
The value of an attribute definition property is interpreted as an _expression_,
|
|
|
|
as described in a later section.
|
|
|
|
|
|
|
|
Given a schema that calls for an attribute named "foo", a JSON object like
|
|
|
|
the following provides a definition for that attribute:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": "bar baz"
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Blocks
|
|
|
|
|
2018-02-17 18:26:58 +00:00
|
|
|
Where the given schema describes a block with a given type name, each object
|
|
|
|
property with the matching name serves as a definition of zero or more blocks
|
|
|
|
of that type.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
Processing of child blocks is in terms of nested JSON objects and arrays.
|
2018-02-17 18:26:58 +00:00
|
|
|
If the schema defines one or more _labels_ for the block type, a nested JSON
|
|
|
|
object or JSON array of objects is required for each labelling level. These
|
|
|
|
are flattened to a single ordered sequence of object properties using the
|
|
|
|
same algorithm as for body content as defined above. Each object property
|
|
|
|
serves as a label value at the corresponding level.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
After any labelling levels, the next nested value is either a JSON object
|
|
|
|
representing a single block body, or a JSON array of JSON objects that each
|
|
|
|
represent a single block body. Use of an array accommodates the definition
|
|
|
|
of multiple blocks that have identical type and labels.
|
|
|
|
|
|
|
|
Given a schema that calls for a block type named "foo" with no labels, the
|
|
|
|
following JSON objects are all valid definitions of zero or more blocks of this
|
|
|
|
type:
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": [
|
|
|
|
{
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"child_attr": "boz"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
2019-01-03 16:46:02 +00:00
|
|
|
|
2017-06-20 15:07:10 +00:00
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": []
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The first of these defines a single child block of type "foo". The second
|
|
|
|
defines _two_ such blocks. The final example shows a degenerate definition
|
|
|
|
of zero blocks, though generators should prefer to omit the property entirely
|
|
|
|
in this scenario.
|
|
|
|
|
|
|
|
Given a schema that calls for a block type named "foo" with _two_ labels, the
|
2018-02-17 18:26:58 +00:00
|
|
|
extra label levels must be represented as objects or arrays of objects as in
|
|
|
|
the following examples:
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": {
|
|
|
|
"bar": {
|
|
|
|
"baz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
"boz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"boz": {
|
|
|
|
"baz": {
|
|
|
|
"child_attr": "baz"
|
2019-01-03 16:46:02 +00:00
|
|
|
}
|
2017-06-20 15:07:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
2018-02-17 18:26:58 +00:00
|
|
|
|
2017-06-20 15:07:10 +00:00
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": {
|
|
|
|
"bar": {
|
|
|
|
"baz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
"boz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"boz": {
|
|
|
|
"baz": [
|
|
|
|
{
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"child_attr": "boz"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-02-17 18:26:58 +00:00
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": [
|
|
|
|
{
|
|
|
|
"bar": {
|
|
|
|
"baz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
"boz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
}
|
2019-01-03 16:46:02 +00:00
|
|
|
}
|
2018-02-17 18:26:58 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
"bar": {
|
|
|
|
"baz": [
|
|
|
|
{
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"child_attr": "boz"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
```json
|
|
|
|
{
|
|
|
|
"foo": {
|
|
|
|
"bar": {
|
|
|
|
"baz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
"boz": {
|
|
|
|
"child_attr": "baz"
|
|
|
|
}
|
|
|
|
},
|
|
|
|
"bar": {
|
|
|
|
"baz": [
|
|
|
|
{
|
|
|
|
"child_attr": "baz"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"child_attr": "boz"
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Arrays can be introduced at either the label definition or block body
|
|
|
|
definition levels to define multiple definitions of the same block type
|
|
|
|
or labels while preserving order.
|
|
|
|
|
|
|
|
A JSON HCL parser _must_ support duplicate definitions of the same property
|
|
|
|
name within a single object, preserving all of them and the relative ordering
|
|
|
|
between them. The array-based forms are also required so that JSON HCL
|
|
|
|
configurations can be produced with JSON producing libraries that are not
|
|
|
|
able to preserve property definition order and multiple definitions of
|
|
|
|
the same property.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
## Expressions
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
JSON lacks a native expression syntax, so the HCL JSON syntax instead defines
|
2017-06-20 15:07:10 +00:00
|
|
|
a mapping for each of the JSON value types, including a special mapping for
|
|
|
|
strings that allows optional use of arbitrary expressions.
|
|
|
|
|
|
|
|
### Objects
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
When interpreted as an expression, a JSON object represents a value of a HCL
|
2017-06-20 15:07:10 +00:00
|
|
|
object type.
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
Each property of the JSON object represents an attribute of the HCL object type.
|
2018-02-17 18:26:58 +00:00
|
|
|
The property name string given in the JSON input is interpreted as a string
|
|
|
|
expression as described below, and its result is converted to string as defined
|
|
|
|
by the syntax-agnostic information model. If such a conversion is not possible,
|
|
|
|
an error is produced and evaluation fails.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
An instance of the constructed object type is then created, whose values
|
|
|
|
are interpreted by again recursively applying the mapping rules defined in
|
2018-02-17 18:26:58 +00:00
|
|
|
this section to each of the property values.
|
|
|
|
|
|
|
|
If any evaluated property name strings produce null values, an error is
|
|
|
|
produced and evaluation fails. If any produce _unknown_ values, the _entire
|
|
|
|
object's_ result is an unknown value of the dynamic pseudo-type, signalling
|
|
|
|
that the type of the object cannot be determined.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
It is an error to define the same property name multiple times within a single
|
2018-02-17 18:26:58 +00:00
|
|
|
JSON object interpreted as an expression. In full expression mode, this
|
|
|
|
constraint applies to the name expression results after conversion to string,
|
|
|
|
rather than the raw string that may contain interpolation expressions.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
### Arrays
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
When interpreted as an expression, a JSON array represents a value of a HCL
|
2017-06-20 15:07:10 +00:00
|
|
|
tuple type.
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
Each element of the JSON array represents an element of the HCL tuple type.
|
2018-07-01 17:35:20 +00:00
|
|
|
The tuple type is constructed by enumerating the JSON array elements, creating
|
2017-06-20 15:07:10 +00:00
|
|
|
for each an element whose type is the result of recursively applying the
|
2018-07-01 17:35:20 +00:00
|
|
|
expression mapping rules. Correspondence is preserved between the array element
|
2017-06-20 15:07:10 +00:00
|
|
|
indices and the tuple element indices.
|
|
|
|
|
|
|
|
An instance of the constructed tuple type is then created, whose values are
|
|
|
|
interpreted by again recursively applying the mapping rules defined in this
|
|
|
|
section.
|
|
|
|
|
|
|
|
### Numbers
|
|
|
|
|
2017-09-11 23:49:35 +00:00
|
|
|
When interpreted as an expression, a JSON number represents a HCL number value.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
2018-02-17 18:26:58 +00:00
|
|
|
HCL numbers are arbitrary-precision decimal values, so a JSON HCL parser must
|
|
|
|
be able to translate exactly the value given to a number of corresponding
|
|
|
|
precision, within the constraints set by the HCL syntax-agnostic information
|
|
|
|
model.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
2018-02-17 18:26:58 +00:00
|
|
|
In practice, off-the-shelf JSON serializers often do not support customizing the
|
2017-06-20 15:07:10 +00:00
|
|
|
processing of numbers, and instead force processing as 32-bit or 64-bit
|
2018-02-17 18:26:58 +00:00
|
|
|
floating point values.
|
|
|
|
|
|
|
|
A _producer_ of JSON HCL that uses such a serializer can provide numeric values
|
|
|
|
as JSON strings where they have precision too great for representation in the
|
|
|
|
serializer's chosen numeric type in situations where the result will be
|
|
|
|
converted to number (using the standard conversion rules) by a calling
|
|
|
|
application.
|
|
|
|
|
|
|
|
Alternatively, for expressions that are evaluated in full expression mode an
|
|
|
|
embedded template interpolation can be used to faithfully represent a number,
|
|
|
|
such as `"${1e150}"`, which will then be evaluated by the underlying HCL native
|
|
|
|
syntax expression evaluator.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
### Boolean Values
|
|
|
|
|
|
|
|
The JSON boolean values `true` and `false`, when interpreted as expressions,
|
2017-09-11 23:49:35 +00:00
|
|
|
represent the corresponding HCL boolean values.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
### The Null Value
|
|
|
|
|
|
|
|
The JSON value `null`, when interpreted as an expression, represents a
|
2017-09-11 23:49:35 +00:00
|
|
|
HCL null value of the dynamic pseudo-type.
|
2017-06-20 15:07:10 +00:00
|
|
|
|
|
|
|
### Strings
|
|
|
|
|
2018-07-01 17:35:20 +00:00
|
|
|
When interpreted as an expression, a JSON string may be interpreted in one of
|
2017-06-20 15:07:10 +00:00
|
|
|
two ways depending on the evaluation mode.
|
|
|
|
|
|
|
|
If evaluating in literal-only mode (as defined by the syntax-agnostic
|
2017-09-11 23:49:35 +00:00
|
|
|
information model) the literal string is intepreted directly as a HCL string
|
2017-06-20 15:07:10 +00:00
|
|
|
value, by directly using the exact sequence of unicode characters represented.
|
|
|
|
Template interpolations and directives MUST NOT be processed in this mode,
|
|
|
|
allowing any characters that appear as introduction sequences to pass through
|
|
|
|
literally:
|
|
|
|
|
|
|
|
```json
|
|
|
|
"Hello world! Template sequences like ${ are not intepreted here."
|
|
|
|
```
|
|
|
|
|
|
|
|
When evaluating in full expression mode (again, as defined by the syntax-
|
|
|
|
agnostic information model) the literal string is instead interpreted as a
|
2017-09-11 23:49:35 +00:00
|
|
|
_standalone template_ in the HCL Native Syntax. The expression evaluation
|
2017-06-20 15:07:10 +00:00
|
|
|
result is then the direct result of evaluating that template with the current
|
|
|
|
variable scope and function table.
|
|
|
|
|
|
|
|
```json
|
|
|
|
"Hello, ${name}! Template sequences are interpreted in full expression mode."
|
|
|
|
```
|
|
|
|
|
|
|
|
In particular the _Template Interpolation Unwrapping_ requirement from the
|
2017-09-11 23:49:35 +00:00
|
|
|
HCL native syntax specification must be implemented, allowing the use of
|
2017-06-20 15:07:10 +00:00
|
|
|
single-interpolation templates to represent expressions that would not
|
|
|
|
otherwise be representable in JSON, such as the following example where
|
|
|
|
the result must be a number, rather than a string representation of a number:
|
|
|
|
|
|
|
|
```json
|
|
|
|
"${ a + b }"
|
|
|
|
```
|
2018-03-04 22:35:16 +00:00
|
|
|
|
|
|
|
## Static Analysis
|
|
|
|
|
|
|
|
The HCL static analysis operations are implemented for JSON values that
|
|
|
|
represent expressions, as described in the following sections.
|
|
|
|
|
|
|
|
Due to the limited expressive power of the JSON syntax alone, use of these
|
|
|
|
static analyses functions rather than normal expression evaluation is used
|
|
|
|
as additional context for how a JSON value is to be interpreted, which means
|
|
|
|
that static analyses can result in a different interpretation of a given
|
|
|
|
expression than normal evaluation.
|
|
|
|
|
|
|
|
### Static List
|
|
|
|
|
|
|
|
An expression interpreted as a static list must be a JSON array. Each of the
|
|
|
|
values in the array is interpreted as an expression and returned.
|
|
|
|
|
|
|
|
### Static Map
|
|
|
|
|
|
|
|
An expression interpreted as a static map must be a JSON object. Each of the
|
|
|
|
key/value pairs in the object is presented as a pair of expressions. Since
|
|
|
|
object property names are always strings, evaluating the key expression with
|
|
|
|
a non-`nil` evaluation context will evaluate any template sequences given
|
|
|
|
in the property name.
|
|
|
|
|
|
|
|
### Static Call
|
|
|
|
|
|
|
|
An expression interpreted as a static call must be a string. The content of
|
|
|
|
the string is interpreted as a native syntax expression (not a _template_,
|
|
|
|
unlike normal evaluation) and then the static call analysis is delegated to
|
|
|
|
that expression.
|
|
|
|
|
|
|
|
If the original expression is not a string or its contents cannot be parsed
|
|
|
|
as a native syntax expression then static call analysis is not supported.
|
|
|
|
|
|
|
|
### Static Traversal
|
|
|
|
|
|
|
|
An expression interpreted as a static traversal must be a string. The content
|
|
|
|
of the string is interpreted as a native syntax expression (not a _template_,
|
|
|
|
unlike normal evaluation) and then static traversal analysis is delegated
|
|
|
|
to that expression.
|
|
|
|
|
|
|
|
If the original expression is not a string or its contents cannot be parsed
|
|
|
|
as a native syntax expression then static call analysis is not supported.
|