Commit Graph

278 Commits

Author SHA1 Message Date
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
60b539d5d7 integrationtest: include dynblock usage in the "terraformlike" test
Terraform is the prime use-case for the dynblock extension, so we'll
include this here currently as a proof-of-concept for Terraform's usage,
but eventually (once Terraform is actually using it) this'll give some
insurance that it doesn't get broken.
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
130b3c5105 hcldec: New function ChildBlockTypes
This function returns a map describing all of the child block types
declared inside a spec. This can be used for recursive decoding of bodies
using the low-level HCL API, though in most cases callers should just use
Decode which does recursive decoding of an entire nested structure 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
Fatih Arslan
f87600a7d9 update zclsyntax to hclsyntax in various comments and strings
These are remnants of the project HCL was forked from.
2018-01-23 21:54:38 -08:00
Martin Atkins
613331e829 hcltest: new implementations for ExprList and AbsTraversalForExpr
To assist in testing code that depends on hcl.ExprList and
hcl.AbsTraversalForExpr we now implement the necessary interfaces on our
existing MockExprLiteral and MockExprVariable, as well as adding new
functions MockExprList and MockExprTraversal that more directly serve
those interfaces with full functionality.
2018-01-20 11:20:50 -08:00
Martin Atkins
83451bb547 hcl/hclsyntax: correctly handle %{ sequence escapes
In early prototyping the template control sequence introducer was
specified as !{, but that changed to %{ along the way because it seemed
more intuitive and less likely to collide with literal strings.

However, the parser's string literal handling still had remnants of the
old syntax, causing strange quirks in parsing strings that contained
exclamation points.

Now we correctly expect %{ as the control sequence introducer, %%{ as its
escape sequence, and additionally fix a bug where previously template
sequence introduction characters at the end of a string literal would
be silently dropped due to them representing an unterminated escape
sequence.

This fixes #3.
2018-01-19 08:11:25 -08:00
Martin Atkins
0daeda39ff hcl/hclsyntax: return hcl.TraverseIndex, not pointer to one
Traversals are always passed by value, so returning a pointer here is
inconsistent with how hcl.TraverseIndex is used elsewhere and thus makes
life inconvenient for callers making type assertions.
2018-01-18 08:12:05 -08:00
Martin Atkins
883a81b490 hcl: highlight the subject when printing a diagnostic source snippet
In complex expressions it can be hard to determine which portion is
relevant when we print a diagnostic message. To address this, when color
is enabled we bold and underline the "subject" portion of the source code,
which then makes it stand out within the full lines of code we print
in the snippet.
2018-01-14 12:25:35 -08:00
Martin Atkins
14cfe59a52 hcl: Simplify the text DiagnosticWriter using new Range functions
Now that we have helper methods for computing relationships between
ranges, we can eliminate all of the tricky line-counting and byte-counting
code here and instead use the higher-level operations.

The result is a single loop using the RangeScanner.
2018-01-14 12:07:33 -08:00
Martin Atkins
368a3f81c0 hcl: SourceRange.PartitionAround
This is a convenience wrapper around SourceRange.Overlap that also
calculates the ranges in the receiver that _aren't_ overlapping with the
given range.

This is useful when, for example, partitioning a portion of source code
to insert markers to highlight the location of an error, as we do when
printing code snippets as part of diagnostic output.
2018-01-14 11:51:05 -08:00
Martin Atkins
1365a2cfe5 hcl: RangeOver function
This is a generalization of RangeBetween that finds a single range that
covers the full extent of both given ranges, possibly also including some
additional content between the ranges if they do not overlap.
2018-01-14 11:33:23 -08:00
Martin Atkins
d34d4686fb hcl: RangeScanner helper
RangeScanner has an interface similar to bufio.Scanner for partitioning
a buffer into tokens, but it returns the hcl.Range of each token along
with that token so that the caller can see where the token fits in
relation to the entire source file.

The main intended use-case for this is to partition a source file into
lines for the purpose of printing a source code snippet in diagnostic
output. Having the source location information is important in that case
to recognize which lines belong to the subject and context of each
diagnostic.
2018-01-14 11:24:19 -08:00
Martin Atkins
11e4972f13 hcl: Helper methods for detecting overlaps in ranges
This is useful, for example, when printing source snippets to the terminal
as part of diagnostics, in order to detect the portion of the source code
that coincides with the subject or context of each diagnostic.
2018-01-14 10:08:39 -08:00
Martin Atkins
600e8726ec hcl: Export the source range for a Traversal
This can be useful, for example, when using Expression.Variables to
pre-validate all of the referenced variables before evaluation, so that
the traversal source ranges can be included in any generated diagnostics.
2018-01-13 23:01:11 -08:00
Martin Atkins
f3af67f344 integrationtest: exercise some features Terraform uses
As an extra level of confidence in addition to the unit tests, this
integration test verifies that a certain set of features that Terraform
uses are able to work properly together.

Terraform is used as an example here just because it's a more advanced
consumer of HCL and thus it exercises some codepaths that most
applications don't need, such as ExprList and AbsTraversalForExpr.
2018-01-13 00:58:50 -08:00
Martin Atkins
76b0988d90 hcl/json: detect variable references in string values 2018-01-12 23:35:58 -08:00
Martin Atkins
77c855c5ed hcl: ExprList function
This helper allows a calling application to require a given expression be
some sort of list constructor (tuple constructor in native syntax, or
array in JSON) and peel off that outer level of list to obtain a slice
of the Expression objects inside.

This is useful in rare cases where the calling application needs to
extract the expressions within the list without evaluating the entire list
expression first. For example, the expressions that result from this
function might be passed into AbsTraversalForExpr in situations where the
caller requires a static list of static traversals that will never
actually be evaluated.
2018-01-12 23:30:41 -08:00
Martin Atkins
0949d55133 hcl: AbsTraversalForExpr and RelTraversalForExpr
These functions permit a calling application to recognize when an
expression represents a static absolute traversal and obtain that
traversal. This allows for the unusual-but-valid case where an application
wishes to access the expression source rather than its resulting value,
when the expression source is something that can be understood as a
traversal.

An example use-case is an attribute that takes a list of other attributes
it depends on, expressed as traversals. In this case the calling
application needs to access the attribute names themselves rather than
their values, e.g. to build some sort of dependency graph to gradually
populate the scope for evaluation.
2018-01-12 22:58:55 -08:00
Martin Atkins
44bad6dbf5 hcldec: ImpliedType function
This function returns the type of value that should be returned when
decoding the given spec. As well as being generally useful to the caller
for book-keeping purposes, this also allows us to return correct type
information when we are returning null and empty values, where before we
were leaning a little too much on cty.DynamicPseudoType.
2017-10-03 16:27:34 -07:00
Martin Atkins
0d6247f4cf hcldec: BlockLabelSpec decoding
A BlockLabelSpec can be placed in the nested spec structure of one of the
block specs to require and obtain labels on that block.

This is a more generic methodology than BlockMapSpec since it allows the
result to be a list or set with the labels inside the values, rather than
forcing all the label tuples to be unique and losing the ordering by
collapsing into a map structure.
2017-10-03 15:59:20 -07:00
Martin Atkins
a6dff4e9f9 hcldec: BlockSetSpec and BlockMapSpec decode implementations 2017-10-03 14:44:54 -07:00
Martin Atkins
cbdf66e80a hcldec: register DefaultSpec with gob 2017-10-03 14:43:54 -07:00
Martin Atkins
2892561eed hcldec: Fix MinItems/MaxItems diagnostics messages
These were not previously consistent with the usual style.
2017-10-03 12:17:47 -07:00
Martin Atkins
cc215b6afe hcldec: ImpliedSchema must visit deep specs
Previously, due to a bug, it was only visiting the first two levels of
specifications, missing anything referenced at lower levels.
2017-10-03 12:17:47 -07:00
Martin Atkins
a4ee7188ad hcldec: New DefaultSpec specification
This is a wrapper that allows a default value to be applied if a primary
spec results in a null value.
2017-10-03 11:57:53 -07:00
Martin Atkins
f44382c4fa gohcl: Decode missing hcl.Expression as a cty Null
Rather than writing a nil hcl.Expression, as a special case we'll deal
with this within the cty type system, which we assume is what the caller
wants if they are decoding into a hcl.Expression rather than a native Go
type.
2017-09-20 16:23:23 -07:00
Martin Atkins
339b9cfc34 hcl: StaticExpr function for making synthetic expressions
Sometimes we want an expression that just wraps a static value, e.g. for
testing or to provide a default value for a missing attribute.

StaticExpr gives us a convenient way to do that, returning a value that
implements the Expression interface by returning just the given static
value.
2017-09-20 16:22:05 -07:00
Martin Atkins
9279eec508 build: test with Go 1.9.x 2017-09-11 18:40:02 -07:00
Martin Atkins
5956c82199 More miscellaneous renaming of ZCL to HCL. 2017-09-11 18:36:56 -07:00
Martin Atkins
b638b30f98 Add initial README 2017-09-11 18:32:44 -07: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
a3ec0f1156 specs: zcl -> HCL 2017-09-11 16:49:35 -07:00
Martin Atkins
6c9e11162b Relicense under MPL, rather than MIT
zcl is (was?) my side-project and so licensed the way I usually license
such things, but HashiCorp prefers MPL license and so here we are.
2017-09-11 16:43:03 -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
386f7134f1 zcl/hclhil: remove HCL/HIL support
This package was a prototype of wrapping the HCL/HIL API in the new zcl
API as a compatibility strategy. This avenue wasn't chosen in the end, so
we'll remove this to avoid confusion as we rename everything else in this
repository to be called "hcl" now.
2017-09-11 15:46:01 -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
a414468aac zcltest: package with mock helpers for testing zcl-based apps 2017-07-27 15:59:32 -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
Martin Atkins
4c269b52e2 gozcl: fix redundant error message in DecodeExpression
The convert errors already contain the string "incorrect type", so it's
redundant to add that as a further prefix here.
2017-07-25 18:24:55 -07:00
Martin Atkins
0598a0b79b zcldec: initial implementation of BlockListSpec
This initial pass supports only blocks without labels. BlockLabelSpec will
be wired in here later to allow decoding blocks with labels too.
2017-07-17 17:29:06 -07:00
Martin Atkins
4df9fd1372 zcldec: test that all Spec types actually implement the interface 2017-07-17 16:54:24 -07:00