Commit Graph

33 Commits

Author SHA1 Message Date
Martin Atkins
55b607ac30 ext/tryfunc: Extension functions for error handling
The try(...) and can(...) functions are intended to make it more
convenient to work with deep data structures of unknown shape, by allowing
a caller to concisely try a complex traversal operation against a value
without having to guard against each possible failure mode individually.

These rely on the customdecode extension to get access to their argument
expressions directly, rather than only the results of evaluating those
expressions. The expressions can then be evaluated in a controlled manner
so that any resulting errors can be recognized and suppressed as
appropriate.
2019-12-17 07:51:07 -08:00
Martin Atkins
d8ae04bc78 ext/customdecode: Custom expression decoding extension
Most of the time, the standard expression decoding built in to HCL is
sufficient. Sometimes though, it's useful to be able to customize the
decoding of certain arguments where the application intends to use them
in a very specific way, such as in static analysis.

This extension is an approximate analog of gohcl's support for decoding
into an hcl.Expression, allowing hcldec-based applications and
applications with custom functions to similarly capture and manipulate
the physical expressions used in arguments, rather than their values.

This includes one example use-case: the typeexpr extension now includes
a cty.Function called ConvertFunc that takes a type expression as its
second argument. A type expression is not evaluatable in the usual sense,
but thanks to cty capsule types we _can_ produce a cty.Value from one
and then make use of it inside the function implementation, without
exposing this custom type to the broader language:

    convert(["foo"], set(string))

This mechanism is intentionally restricted only to "argument-like"
locations where there is a specific type we are attempting to decode into.
For now, that's hcldec AttrSpec/BlockAttrsSpec -- analogous to gohcl
decoding into hcl.Expression -- and in arguments to functions.
2019-12-17 07:51:07 -08:00
Martin Atkins
dca4accff0 ext/dynblock: Go package documentation 2019-10-01 15:59:10 -07:00
Martin Atkins
691f9eea1a ext/include: Remove
This experimental extension is not ready to be included in a release. It
ought to be reworked so that "include" blocks get replaced with what they
include _in-place_, preserving the relative ordering of blocks.

However, there is no application making use of this yet and so we'll defer
that work until there's a real use-case to evaluate it with.
2019-09-10 10:49:36 -07:00
Martin Atkins
6c4344623b Unfold the "hcl" directory up into the root
The main HCL package is more visible this way, and so it's easier than
having to pick it out from dozens of other package directories.
2019-09-09 16:08:19 -07:00
Martin Atkins
3327dee567 Change module path to github.com/hashicorp/hcl/v2
This is in preparation for the first v2 release from the main HCL
repository.
2019-09-09 15:46:40 -07:00
Masayuki Morita
57bd5f374f Fix typos (#85) 2019-07-22 15:50:19 -04:00
Martin Atkins
3dfebdfc45 ext/dynblock: Stub out contents when for_each is unknown
Previously our behavior for an unknown for_each was to produce a single
block whose content was the result of evaluating content with the iterator
set to cty.DynamicVal. That produced a reasonable idea of the content, but
the number of blocks in the result was still not accurate, and that can
present a problem for applications that use unknown values to predict
the overall shape of a not-yet-complete structure.

We can't return an unknown block via the HCL API, but to make that
situation easier to recognize by callers we'll now go a little further and
force _all_ of the leaf attributes in such a block to be unknown values,
even if they are constants in the configuration. This allows a calling
application that is making predictions to use a single object whose
leaves are all unknown as a heuristic to recognize what is effectively
an unknown set of blocks.

This is still not a perfect heuristic, but is the best we can do here
within the HCL API assumptions. A fundamental assumption of the HCL API
is that it's possible to walk the block structure without evaluating any
expressions and the dynamic block extension is intentionally subverting
that assumption, so some oddities are to be expected. Calling applications
that need a fully reliable sense of the final structure should not use
the dynamic block extension.
2019-04-30 11:30:46 -07:00
Martin Atkins
3fb4ed0d92 ext/dynblock: Allow WalkVariablesChild callers to get the body
In normal situations the block type name alone is enough to determine the
appropriate schema for a child, but when callers are otherwise doing
unusual pre-processing of bodies to dynamically generate schemas during
decoding they are likely to need to take similar steps while analyzing
for variables, to ensure that all of the references can be located in
spite of the not-yet-applied pre-processing.
2019-03-27 15:38:17 -07:00
Martin Atkins
f9f92da699 ext/dynblock: Allow interrogation of _all_ references in blocks
Our API previously had a function only for retrieving the variables used
in the for_each and labels arguments used during an Expand call, and
expected callers to then interrogate the resulting expanded block to find
the other variables required to fully decode the content.

That approach is insufficient for any application that needs to know the
full set of required variables before any evaluation begins, such as when
a dependency graph will be constructed to allow a topological traversal
through blocks while evaluating.

Now we have WalkVariables, which finds both the variables used to expand
_and_ the variables within any blocks. This also renames
WalkForEachVariables to WalkExpandVariables since that name is more
accurate with the addition of the "label" argument into the expand-time
dependency set.

There is also a hcldec-based helper wrapper for each of those, allowing
single-shot analysis of blocks for applications that use hcldec.

This is a breaking change to the dynblock package API, because the old
WalkForEachVariables and ForEachVariablesHCLDec functions are no longer
present.
2019-03-18 16:28:30 -07:00
Martin Atkins
6631d7cd0a ext/dynblock: Make iterator variables visible to nested block for_each
Previously we were incorrectly passing down the original forEachCtx down
to nested child blocks for recursive expansion. Instead, we must use the
iteration-specific constructed EvalContext, which then allows any nested
dynamic blocks to use the parent's iterator variable in their for_each or
labels expressions, and thus unpack nested data structures into
corresponding nested block structures:

    dynamic "parent" {
      for_each = [["a", "b"], []]
      content {
        dynamic "child" {
          for_each = parent.value
          content {}
        }
      }
    }
2018-12-19 17:20:50 -08:00
Martin Atkins
447c39ed03 go fmt updates 2018-12-11 16:49:03 -08:00
Martin Atkins
6356254632 hcl: Include Expression reference in diagnostics
If a diagnostic occurs while we're evaluating an expression, we'll now
include a reference to that expression in the diagnostic object. We
previously added the corresponding EvalContext here too, and so with these
together it is now possible for a diagnostic renderer to see not only
what was in scope when the problem occurred but also what parts of that
scope the expression was relying on (via method Expression.Variables).
2018-07-28 13:36:55 -07:00
Martin Atkins
93562f805f hcl: Annotate diagnostics with expression EvalContext
When we're evaluating expressions, we may end up evaluating the same
source-level expression a number of times in different contexts, such as
in a 'for' expression, where each one may produce a different set of
diagnostic messages.

Now we'll attach the EvalContext to each expression diagnostic so that
a diagnostic renderer can potentially show additional information to help
distinguish the different iterations in rendered diagnostics.
2018-07-28 13:14:36 -07:00
Martin Atkins
9db880accf
ext/typeexpr: correct examples in the README 2018-04-05 19:34:53 -07:00
Martin Atkins
be66a72aa8 ext/typeexpr: HCL extension for "type expressions"
This uses the expression static analysis features to interpret
a combination of static calls and static traversals as the description
of a type.

This is intended for situations where applications need to accept type
information from their end-users, providing a concise syntax for doing
so.

Since this is implemented using static analysis, the type vocabulary is
constrained only to keywords representing primitive types and type
construction functions for complex types. No other expression elements
are allowed.

A separate function is provided for parsing type constraints, which allows
the additonal keyword "any" to represent the dynamic pseudo-type.

Finally, a helper function is provided to convert a type back into a
string representation resembling the original input, as an aid to
applications that need to produce error messages relating to user-entered
types.
2018-03-04 14:45:25 -08:00
Martin Atkins
18a92d222b ext/userfunc: use bare identifiers for param names
Now that we have the necessary functions to deal with this in the
low-level HCL API, it's more intuitive to use bare identifiers for these
parameter names. This reinforces the idea that they are symbols being
defined rather than arbitrary string expressions.
2018-02-04 11:20:42 -08:00
Martin Atkins
b1a8ce3a09 ext/userfunc: update stale references to "zcl" 2018-01-27 11:03:05 -08:00
Martin Atkins
19d9927d4b ext/include: update stale reference to "zcl" 2018-01-27 11:03:05 -08:00
Martin Atkins
34e27c038a hcl: UnwrapExpression and UnwrapExpressionUntil
A pattern has emerged of wrapping Expression instances with other
Expressions in order to subtly modify their behavior. A key example of
this is in ext/dynblock, where wrap an expression in order to introduce
our additional iteration variable for expressions in dynamic blocks.

Rather than having each wrapper expression implement wrapping
implementations for our various syntax-level-analysis functions (like
ExprList and AbsTraversalForExpr), instead we define a standard mechanism
to unwrap expressions back to the lowest-level object -- usually an AST
node -- and then use this in all of our analyses that look at the
expression's structure rather than its value.
2018-01-27 09:10:18 -08:00
Martin Atkins
d6fc633aa0 ext/dynblock: ForEachVariablesHCLDec helper
For applications already using hcldec, a decoder specification can be used
to automatically drive the recursive variable detection walk that begins
with WalkForEachVariables, allowing all "for_each" and "labels" variables
in a recursive block structure to be detected in a single call.
2018-01-27 09:10:18 -08:00
Martin Atkins
45c6cc83f0 ext/dynblock: A more arduous way to find variables required to expand
The previous ForEachVariables method was flawed because it didn't have
enough information to properly analyze child blocks. Since the core HCL
API requires a schema for any body analysis, and since a schema only
describes one level of configuration structure at a time, we must require
callers to drive a recursive walk through their nested block structure so
that the correct schema can be provided at each level.

This API is rather more complex than is ideal, but is the best we can do
with the HCL Body API as currently defined, and it's currently defined
that way in order to properly support ambiguous syntaxes like JSON.
2018-01-27 09:10:18 -08:00
Martin Atkins
da95646a33 ext/dynblock: dynamic blocks extension
This extension allows an application to support dynamic generation of
child blocks based on expressions in certain contexts. This is done using
a new block type called "dynamic", which contains an iteration value
(which must be a collection) and a specification of how to construct a
child block for each element of that collection.
2018-01-27 09:10:18 -08:00
Martin Atkins
bdf1e7c6e6 gohcl: rename struct tag prefix from "zcl:" to "hcl:" 2017-09-11 17:29:56 -07:00
Martin Atkins
b8b5e0be6d hclparse: rename ParseZCL to ParseHCL 2017-09-11 17:22:10 -07:00
Martin Atkins
46b20d40af Update doc comments and readmes for zcl -> HCL. 2017-09-11 16:56:31 -07:00
Martin Atkins
708abb8c97 Move the zcl package and its two parsing subpackages to "hcl" names
This is a super-invasive update since the "zcl" package in particular
is referenced all over.

There are probably still a few zcl references hanging around in comments,
etc but this takes care of most of it.
2017-09-11 16:40:37 -07:00
Martin Atkins
0dc3a6015c Rename the ancillary packages from "zcl" to "hcl".
The main "zcl" package requires a bit more care because of how many
callers it has and because of its two subpackages, so we'll take care
of that one separately.
2017-09-11 16:00:31 -07:00
Martin Atkins
c3ca111fff Adjust import paths for hashicorp/hcl2 repo
This begins 'the great fork' of zcl to HCL.
2017-09-11 15:38:42 -07:00
Martin Atkins
523939034f ext/include: extension for including bodies into other bodies
This package implements a language extension that allows configuration
authors to include the content of another file into a body, using syntax
like this:

  include {
    path = "./foo.zcl"
  }

This is implemented as a transform.Transformer so that it can be used
as part of a transform chain when decoding nested block structures to
allow includes at any arbitrary point.

This capability is not built into the language because certain
applications will offer higher-level constructs for connecting multiple
separate config files, which may e.g. have a separate evaluation scope
for each file, etc.
2017-07-27 18:15:56 -07:00
Martin Atkins
fffca3d205 ext/transform: helper package for applying transforms to bodies
This utility is intended to support the extension packages that are
siblings of this package, along with third-party extensions, by providing
a way to transform bodies in arbitrary ways.

The "Deep" function then provides a means to apply a particular transform
recursively to a nested block tree, allowing a particular extension to
be supported at arbitrary nesting levels.

This functionality is provided in terms of the standard zcl.Body
interface, so that transform results can be used with any code that
operates generically on bodies. This includes the zcldec and gozcl
packages, so files with extensions can still be decoded in the usual
way.
2017-07-27 16:23:20 -07:00
Martin Atkins
26f1e48014 ext/userfunc: extension for user-defined functions
This package provides helper function that looks in a given body for
blocks that define functions, returning a function map suitable for use
in a zcl.EvalContext.
2017-07-25 18:34:56 -07:00
Martin Atkins
f03b4a0acd ext: A directory for packages implementing zcl language extensions
These will provide additional language features that are implemented in
terms of the basic zcl functionality, so that applications can opt-in to
some more-dynamic behavior if desired.

The general pattern here will be to provide a function that
partially-decodes a given zcl.Body to look for certain block types and
then returns its result along with a zcl.Body representing the remaining,
as-yet-unprocessed content.
2017-07-25 18:30:59 -07:00