Eventually zcl will have its own native template format that we'll use
by default, but for applications migrating from HCL/HIL we can instead
parse strings as HIL templates, for compatibility with how JSON configs
would've been processed in a HCL/HIL app.
When this mode is not enabled, we still just treat strings as literals,
pending the implementation of the zcl template parser.
Currently only deals with the literal HCL structure. Later we will also
support HIL parsing and evaluation within strings, to achieve parity with
existing uses of HCL/HIL together. For now, this has parity with uses of
HCL alone, with the exception that float and int values are not
distinguished, because cty does not make this distinction.
This is intended as a backward-compatibility interface, allowing
applications that previously used HCL/HIL to adopt zcl while still being
able to parse their old HCL/HIL-based configuration file formats.
When producing diagnostics about missing attributes or bodies it's
necessary to have a range representing the place where the missing thing
might be inserted.
There's not always a single reasonable value for this, so some liberty
can be taken about what exactly is returned as long as it's somewhere
the user can relate back to the construct producing the error.
This is really just a wrapper around converting the value to a Go native
value with gocty, but is convenient to provide since it can attempt
type conversion and produce diagnostics automatically.
Expressions can now be evaluated within an "EvalContext", which provides
the variable and function scopes. The JSON implementation of this
currently ignores the context entirely and just returns literal values,
since we've not yet implemented the template language parser that would
be needed for the JSON parser to properly support expressions.
The Content and PartialContent methods deal with the case where the caller
knows what structure is expected within the body, but sometimes the
structure of a body is just a free-form set of attributes that the caller
needs to enumerate.
The idea here is that the block in question must contain only attributes,
and no child blocks. For JSON this just entails interpreting every
property as an attribute. For native syntax later this will mean
producing an error diagnostic if any blocks appear within the body.
When using the parser to do static analysis and editor integrations, it's
still useful to be able to get the incomplete AST resulting from a parse
error.
Callers that intend to use the returned value to take real actions (as
opposed to just analysis) must check diags.HasError() to determine if
the returned file can be considered valid.
Its implementation calls into each of the child bodies in turn and merges
the result to produce a single BodyContent. This is intended to support
the case of a directory being the unit of configuration rather than a
file, with the calling application discovering and parsing each of the
files in its workspace and then merging them together for processing as
a single configuration.
We need to be careful to keep straight the distinction between JSON
properties and zcl body attributes here, since properties can represent
both attributes _and_ blocks.
This is a wrapper around Body.PartialContent that generates additional
error diagnostics if any object properties are left over after decoding,
helping the config author to catch typos that would otherwise have caused
a property to be silently ignored.
Even if errors were encountered during parsing, it is helpful to still
return a partial AST if possible since that allows for the source code
analysis features to still (partially) work in the face of errors.
The new "Nav" member on a zcl.File is an opaque object that can be
populated by parsers with an object that supports certain interfaces
that are not part of the main API but are useful for integration with
editors and other tooling.
As a first example of this, we replace the hack for getting context in
the diagnostic package with a new ContextString interface, which can
then be optionally implemented by a given parser to return a contextual
string native to the source language.
The indication of specific characters in the source code that are in
error is not yet implemented, but this gets at the main functionality
of printing diagnostics.
Although the actual parsing code will be split across multiple packages,
it's convenient for the public interface to be in a single package. The
parsers are accessed through a "Parser" type, which both wraps the
real parsers and maintains a registry of parsed files to ensure that
the same file isn't parsed more than once (which might be confusing if
there are different results) and to provide raw source code for
diagnostic-printing functions.
The parser API is in a separate package both due to the fundamental
problem that it would create an import cycle if included in the main
package and also because it's expected that calling applications will
deal with parsing once in a central place (a single file depending on
zclparse) but the resulting objects will be used broadly across the
application, causing many files to depend on the main zcl package.
Some applications treat an entire directory as a configuration, merging
the configurations from all of the files in a directory and treating them
as one.
MergeFiles supports this idea by wrapping the bodies of the several files.
It's not yet implemented here, but once implemented it will act as an
aggregator of the content of the wrapped bodies, delegating to them for
actual body content and then merging the returned body content in a
well-defined way.
The term "element" is already used for an item from a collection in cty,
so we'll use "block" to talk about the nested blocks in a zcl config to
reduce confusion.