govendor fetch github.com/hashicorp/hcl2/...

This includes updates to various diagnostic messages to improve precision
and consistency of terminology.

It also includes some other changes to portions of HCL API that Terraform
isn't yet using.
This commit is contained in:
Martin Atkins 2018-07-18 16:19:46 -07:00
parent aa6b55bb17
commit 3855b79736
15 changed files with 384 additions and 107 deletions

View File

@ -96,6 +96,17 @@ func (d Diagnostics) HasErrors() bool {
return false
}
func (d Diagnostics) Errs() []error {
var errs []error
for _, diag := range d {
if diag.Severity == DiagError {
errs = append(errs, diag)
}
}
return errs
}
// A DiagnosticWriter emits diagnostics somehow.
type DiagnosticWriter interface {
WriteDiagnostic(*Diagnostic) error

View File

@ -55,7 +55,7 @@ Token:
Severity: hcl.DiagError,
Summary: "Attribute redefined",
Detail: fmt.Sprintf(
"The attribute %q was already defined at %s. Each attribute may be defined only once.",
"The argument %q was already set at %s. Each argument may be set only once.",
titem.Name, existing.NameRange.String(),
),
Subject: &titem.NameRange,
@ -80,15 +80,15 @@ Token:
if bad.Type == TokenOQuote {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid attribute name",
Detail: "Attribute names must not be quoted.",
Summary: "Invalid argument name",
Detail: "Argument names must not be quoted.",
Subject: &bad.Range,
})
} else {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Attribute or block definition required",
Detail: "An attribute or block definition is required here.",
Summary: "Argument or block definition required",
Detail: "An argument or block definition is required here.",
Subject: &bad.Range,
})
}
@ -120,8 +120,8 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
return nil, hcl.Diagnostics{
{
Severity: hcl.DiagError,
Summary: "Attribute or block definition required",
Detail: "An attribute or block definition is required here.",
Summary: "Argument or block definition required",
Detail: "An argument or block definition is required here.",
Subject: &ident.Range,
},
}
@ -139,8 +139,8 @@ func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
return nil, hcl.Diagnostics{
{
Severity: hcl.DiagError,
Summary: "Attribute or block definition required",
Detail: "An attribute or block definition is required here. To define an attribute, use the equals sign \"=\" to introduce the attribute value.",
Summary: "Argument or block definition required",
Detail: "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.",
Subject: &ident.Range,
},
}
@ -171,8 +171,8 @@ func (p *parser) finishParsingBodyAttribute(ident Token) (Node, hcl.Diagnostics)
if !p.recovery {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing newline after attribute definition",
Detail: "An attribute definition must end with a newline.",
Summary: "Missing newline after argument",
Detail: "An argument definition must end with a newline.",
Subject: &end.Range,
Context: hcl.RangeBetween(ident.Range, end.Range).Ptr(),
})
@ -244,7 +244,7 @@ Token:
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid block definition",
Detail: "The equals sign \"=\" indicates an attribute definition, and must not be used when defining a block.",
Detail: "The equals sign \"=\" indicates an argument definition, and must not be used when defining a block.",
Subject: &tok.Range,
Context: hcl.RangeBetween(ident.Range, tok.Range).Ptr(),
})
@ -1135,8 +1135,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
if next.Type == TokenNewline || next.Type == TokenComma {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing item value",
Detail: "Expected an item value, introduced by an equals sign (\"=\").",
Summary: "Missing attribute value",
Detail: "Expected an attribute value, introduced by an equals sign (\"=\").",
Subject: &next.Range,
Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
})
@ -1144,7 +1144,7 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing key/value separator",
Detail: "Expected an equals sign (\"=\") to mark the beginning of the item value.",
Detail: "Expected an equals sign (\"=\") to mark the beginning of the attribute value.",
Subject: &next.Range,
Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
})
@ -1182,8 +1182,8 @@ func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
if !p.recovery {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing item separator",
Detail: "Expected a newline or comma to mark the beginning of the next item.",
Summary: "Missing attribute separator",
Detail: "Expected a newline or comma to mark the beginning of the next attribute.",
Subject: &next.Range,
Context: hcl.RangeBetween(open.Range, next.Range).Ptr(),
})
@ -1277,7 +1277,7 @@ func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics)
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid 'for' expression",
Detail: "For expression requires 'in' keyword after names.",
Detail: "For expression requires the 'in' keyword after its name declarations.",
Subject: p.Peek().Range.Ptr(),
Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
})
@ -1305,7 +1305,7 @@ func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics)
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid 'for' expression",
Detail: "For expression requires colon after collection expression.",
Detail: "For expression requires a colon after the collection expression.",
Subject: p.Peek().Range.Ptr(),
Context: hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
})
@ -1459,7 +1459,7 @@ Token:
case TokenTemplateControl, TokenTemplateInterp:
which := "$"
if tok.Type == TokenTemplateControl {
which = "!"
which = "%"
}
diags = append(diags, &hcl.Diagnostic{

View File

@ -4,12 +4,12 @@ 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 maintenence, as opposed to machine
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 heirarchical configuration
* 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
@ -186,7 +186,7 @@ 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 heirachy which can be
zero or more block _labels_. Blocks create a structural hierachy which can be
interpreted by the calling application.
Block labels can either be quoted literal strings or naked identifiers.
@ -296,7 +296,7 @@ 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 paretheses to disambiguate:
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
@ -482,7 +482,7 @@ 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 experssion, this activates the grouping mode in
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.
@ -903,7 +903,7 @@ 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 analyis, allowing callers to provide map-like
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

View File

@ -229,7 +229,7 @@ func checkInvalidTokens(tokens Tokens) hcl.Diagnostics {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid character",
Detail: "The \";\" character is not valid. Use newlines to separate attributes and blocks, and commas to separate items in collection values.",
Detail: "The \";\" character is not valid. Use newlines to separate arguments and blocks, and commas to separate items in collection values.",
Subject: &tok.Range,
})

View File

@ -55,7 +55,7 @@ func parseValue(p *peeker) (node, hcl.Diagnostics) {
return wrapInvalid(nil, hcl.Diagnostics{
{
Severity: hcl.DiagError,
Summary: "Missing attribute value",
Summary: "Missing JSON value",
Detail: "A JSON value must start with a brace, a bracket, a number, a string, or a keyword.",
Subject: &tok.Range,
},
@ -144,8 +144,8 @@ Token:
if !ok {
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid object attribute name",
Detail: "A JSON object attribute name must be a string",
Summary: "Invalid object property name",
Detail: "A JSON object property name must be a string",
Subject: keyNode.StartRange().Ptr(),
})
}
@ -171,7 +171,7 @@ Token:
// Possible confusion with native HCL syntax.
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing attribute value colon",
Summary: "Missing property value colon",
Detail: "JSON uses a colon as its name/value delimiter, not an equals sign.",
Subject: &colon.Range,
})
@ -179,8 +179,8 @@ Token:
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing attribute value colon",
Detail: "A colon must appear between an object attribute's name and its value.",
Summary: "Missing property value colon",
Detail: "A colon must appear between an object property's name and its value.",
Subject: &colon.Range,
})
}
@ -205,7 +205,7 @@ Token:
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Trailing comma in object",
Detail: "JSON does not permit a trailing comma after the final attribute in an object.",
Detail: "JSON does not permit a trailing comma after the final property in an object.",
Subject: &comma.Range,
})
}
@ -234,7 +234,7 @@ Token:
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing attribute seperator comma",
Detail: "A comma must appear between each attribute declaration in an object.",
Detail: "A comma must appear between each property definition in an object.",
Subject: p.Peek().Range.Ptr(),
})
}
@ -301,7 +301,7 @@ Token:
return nil, diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Trailing comma in array",
Detail: "JSON does not permit a trailing comma after the final attribute in an array.",
Detail: "JSON does not permit a trailing comma after the final value in an array.",
Subject: &comma.Range,
})
}

View File

@ -5,7 +5,7 @@ for defining configuration languages for applications. The HCL information
model is designed to support multiple concrete syntaxes for configuration,
and this JSON-based format complements [the native syntax](../hclsyntax/spec.md)
by being easy to machine-generate, whereas the native syntax is oriented
towards human authoring and maintenence.
towards human authoring and maintenance
This syntax is defined in terms of JSON as defined in
[RFC7159](https://tools.ietf.org/html/rfc7159). As such it inherits the JSON
@ -280,9 +280,9 @@ When interpreted as an expression, a JSON array represents a value of a HCL
tuple type.
Each element of the JSON array represents an element of the HCL tuple type.
The tuple type is constructed by enumerationg the JSON array elements, creating
The tuple type is constructed by enumerating the JSON array elements, creating
for each an element whose type is the result of recursively applying the
expression mapping rules. Correspondance is preserved between the array element
expression mapping rules. Correspondence is preserved between the array element
indices and the tuple element indices.
An instance of the constructed tuple type is then created, whose values are
@ -325,7 +325,7 @@ HCL null value of the dynamic pseudo-type.
### Strings
When intepreted as an expression, a JSON string may be interpreted in one of
When interpreted as an expression, a JSON string may be interpreted in one of
two ways depending on the evaluation mode.
If evaluating in literal-only mode (as defined by the syntax-agnostic

View File

@ -64,7 +64,7 @@ func (b *body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostic
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Extraneous JSON object property",
Detail: fmt.Sprintf("No attribute or block type is named %q.%s", k, suggestion),
Detail: fmt.Sprintf("No argument or block type is named %q.%s", k, suggestion),
Subject: &attr.NameRange,
Context: attr.Range().Ptr(),
})
@ -114,8 +114,8 @@ func (b *body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Bod
if existing, exists := content.Attributes[attrName]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate attribute definition",
Detail: fmt.Sprintf("The attribute %q was already defined at %s.", attrName, existing.Range),
Summary: "Duplicate argument",
Detail: fmt.Sprintf("The argument %q was already set at %s.", attrName, existing.Range),
Subject: &jsonAttr.NameRange,
Context: jsonAttr.Range().Ptr(),
})
@ -149,8 +149,8 @@ func (b *body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Bod
if _, defined := content.Attributes[attrS.Name]; !defined {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing required attribute",
Detail: fmt.Sprintf("The attribute %q is required, but no definition was found.", attrS.Name),
Summary: "Missing required argument",
Detail: fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name),
Subject: b.MissingItemRange().Ptr(),
})
}
@ -175,7 +175,7 @@ func (b *body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Incorrect JSON value type",
Detail: "A JSON object is required here, defining the attributes for this block.",
Detail: "A JSON object is required here, setting the arguments for this block.",
Subject: b.val.StartRange().Ptr(),
})
return attrs, diags
@ -197,7 +197,7 @@ func (b *body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Duplicate attribute definition",
Detail: fmt.Sprintf("The attribute %q was already defined at %s.", name, existing.Range),
Detail: fmt.Sprintf("The argument %q was already set at %s.", name, existing.Range),
Subject: &jsonAttr.NameRange,
})
continue
@ -345,7 +345,7 @@ func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.D
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Incorrect JSON value type",
Detail: "A JSON object is required here, to define attributes and child blocks.",
Detail: "A JSON object is required here, to define arguments and child blocks.",
Subject: ev.StartRange().Ptr(),
})
}
@ -364,7 +364,7 @@ func (b *body) collectDeepAttrs(v node, labelName *string) ([]*objectAttr, hcl.D
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Incorrect JSON value type",
Detail: "Either a JSON object or JSON array of objects is required here, to define attributes and child blocks.",
Detail: "Either a JSON object or JSON array of objects is required here, to define arguments and child blocks.",
Subject: v.StartRange().Ptr(),
})
}

View File

@ -109,9 +109,9 @@ func (mb mergedBodies) JustAttributes() (Attributes, Diagnostics) {
if existing := attrs[name]; existing != nil {
diags = diags.Append(&Diagnostic{
Severity: DiagError,
Summary: "Duplicate attribute",
Summary: "Duplicate argument",
Detail: fmt.Sprintf(
"Attribute %q was already assigned at %s",
"Argument %q was already set at %s",
name, existing.NameRange.String(),
),
Subject: &attr.NameRange,
@ -182,9 +182,9 @@ func (mb mergedBodies) mergedContent(schema *BodySchema, partial bool) (*BodyCon
if existing := content.Attributes[name]; existing != nil {
diags = diags.Append(&Diagnostic{
Severity: DiagError,
Summary: "Duplicate attribute",
Summary: "Duplicate argument",
Detail: fmt.Sprintf(
"Attribute %q was already assigned at %s",
"Argument %q was already set at %s",
name, existing.NameRange.String(),
),
Subject: &attr.NameRange,
@ -212,9 +212,9 @@ func (mb mergedBodies) mergedContent(schema *BodySchema, partial bool) (*BodyCon
// use of required attributes on merged bodies.
diags = diags.Append(&Diagnostic{
Severity: DiagError,
Summary: "Missing required attribute",
Summary: "Missing required argument",
Detail: fmt.Sprintf(
"The attribute %q is required, but was not assigned.",
"The argument %q is required, but was not set.",
attrS.Name,
),
})

View File

@ -29,7 +29,7 @@ which are discussed in detail in a later section.
A _block_ is a nested structure that has a _type name_, zero or more string
_labels_ (e.g. identifiers), and a nested body.
Together the structural elements create a heirarchical data structure, with
Together the structural elements create a hierarchical data structure, with
attributes intended to represent the direct properties of a particular object
in the calling application, and blocks intended to represent child objects
of a particular object.
@ -269,7 +269,7 @@ are two structural type _kinds_:
has a type. Attribute names are always strings. (_Object_ attributes are a
distinct idea from _body_ attributes, though calling applications
may choose to blur the distinction by use of common naming schemes.)
* _Tuple tupes_ are constructed of a sequence of elements, each of which
* _Tuple types_ are constructed of a sequence of elements, each of which
has a type.
Values of structural types are compared for equality in terms of their
@ -301,10 +301,10 @@ the same element type.
### Null values
Each type has a null value. The null value of a type represents the absense
Each type has a null value. The null value of a type represents the absence
of a value, but with type information retained to allow for type checking.
Null values are used primarily to represent the conditional absense of a
Null values are used primarily to represent the conditional absence of a
body attribute. In a syntax with a conditional operator, one of the result
values of that conditional may be null to indicate that the attribute should be
considered not present in that case.
@ -458,7 +458,7 @@ If semantic checking succeeds without error, the call is _executed_:
definition is used to determine the call's _result value_.
The result of a function call expression is either an error, if one of the
erroenous conditions above applies, or the _result value_.
erroneous conditions above applies, or the _result value_.
## Type Conversions and Unification
@ -505,7 +505,7 @@ Bidirectional conversions are available between the string and number types,
and between the string and boolean types.
The bool value true corresponds to the string containing the characters "true",
while the bool value false corresponds to teh string containing the characters
while the bool value false corresponds to the string containing the characters
"false". Conversion from bool to string is safe, while the converse is
unsafe. The strings "1" and "0" are alternative string representations
of true and false respectively. It is an error to convert a string other than
@ -671,7 +671,7 @@ The language-agnosticism of this specification assumes that certain behaviors
are implemented separately for each syntax:
* Matching of a body schema with the physical elements of a body in the
source language, to determine correspondance between physical constructs
source language, to determine correspondence between physical constructs
and schema elements.
* Implementing the _dynamic attributes_ body processing mode by either

View File

@ -26,8 +26,8 @@ func (b mockBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnos
for _, attr := range remain.C.Attributes {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Extraneous attribute in mock body",
Detail: fmt.Sprintf("Mock body has extraneous attribute %q.", attr.Name),
Summary: "Extraneous argument in mock body",
Detail: fmt.Sprintf("Mock body has extraneous argument %q.", attr.Name),
Subject: &attr.NameRange,
})
}
@ -63,8 +63,8 @@ func (b mockBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.
if attrS.Required {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Missing required attribute",
Detail: fmt.Sprintf("Mock body doesn't have attribute %q", name),
Summary: "Missing required argument",
Detail: fmt.Sprintf("Mock body doesn't have argument %q", name),
Subject: b.C.MissingItemRange.Ptr(),
})
}

View File

@ -163,11 +163,11 @@ func (n *Block) Tokens() *TokenSeq {
type Expression struct {
AllTokens *TokenSeq
VarRefs []*VarRef
AbsTraversals []*Traversal
}
func (n *Expression) walkChildNodes(w internalWalkFunc) {
for _, name := range n.VarRefs {
for _, name := range n.AbsTraversals {
w(name)
}
}
@ -176,16 +176,30 @@ func (n *Expression) Tokens() *TokenSeq {
return n.AllTokens
}
type VarRef struct {
// Tokens alternate between TokenIdent and TokenDot, with the first
// and last elements always being TokenIdent.
type Traversal struct {
AllTokens *TokenSeq
Steps []*Traverser
}
func (n *VarRef) walkChildNodes(w internalWalkFunc) {
// no child nodes of a variable name
func (n *Traversal) walkChildNodes(w internalWalkFunc) {
for _, step := range n.Steps {
w(step)
}
}
func (n *VarRef) Tokens() *TokenSeq {
func (n *Traversal) Tokens() *TokenSeq {
return n.AllTokens
}
type Traverser struct {
AllTokens *TokenSeq
Logical hcl.Traverser
}
func (n *Traverser) Tokens() *TokenSeq {
return n.AllTokens
}
func (n *Traverser) walkChildNodes(w internalWalkFunc) {
// No child nodes for a traversal step
}

View File

@ -239,6 +239,10 @@ func spaceAfterToken(subject, before, after *Token) bool {
// No space right before a comma in an argument list
return false
case subject.Type == hclsyntax.TokenComma:
// Always a space after a comma
return true
case subject.Type == hclsyntax.TokenQuotedLit || subject.Type == hclsyntax.TokenStringLit || subject.Type == hclsyntax.TokenOQuote || subject.Type == hclsyntax.TokenOHeredoc || after.Type == hclsyntax.TokenQuotedLit || after.Type == hclsyntax.TokenStringLit || after.Type == hclsyntax.TokenCQuote || after.Type == hclsyntax.TokenCHeredoc:
// No extra spaces within templates
return false
@ -296,8 +300,6 @@ func spaceAfterToken(subject, before, after *Token) bool {
func linesForFormat(tokens Tokens) []formatLine {
if len(tokens) == 0 {
// should never happen, since we should always have EOF, but let's
// not crash anyway.
return make([]formatLine, 0)
}
@ -331,6 +333,16 @@ func linesForFormat(tokens Tokens) []formatLine {
}
}
// If a set of tokens doesn't end in TokenEOF (e.g. because it's a
// fragment of tokens from the middle of a file) then we might fall
// out here with a line still pending.
if lineStart < len(tokens) {
lines[li].lead = tokens[lineStart:]
if lines[li].lead[len(lines[li].lead)-1].Type == hclsyntax.TokenEOF {
lines[li].lead = lines[li].lead[:len(lines[li].lead)-1]
}
}
// Now we'll pick off any trailing comments and attribute assignments
// to shuffle off into the "comment" and "assign" cells.
for i := range lines {

195
vendor/github.com/hashicorp/hcl2/hclwrite/generate.go generated vendored Normal file
View File

@ -0,0 +1,195 @@
package hclwrite
import (
"fmt"
"unicode"
"unicode/utf8"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
"github.com/zclconf/go-cty/cty"
)
// TokensForValue returns a sequence of tokens that represents the given
// constant value.
//
// This function only supports types that are used by HCL. In particular, it
// does not support capsule types and will panic if given one.
//
// It is not possible to express an unknown value in source code, so this
// function will panic if the given value is unknown or contains any unknown
// values. A caller can call the value's IsWhollyKnown method to verify that
// no unknown values are present before calling TokensForValue.
func TokensForValue(val cty.Value) Tokens {
toks := appendTokensForValue(val, nil)
format(toks) // fiddle with the SpacesBefore field to get canonical spacing
return toks
}
func appendTokensForValue(val cty.Value, toks Tokens) Tokens {
switch {
case !val.IsKnown():
panic("cannot produce tokens for unknown value")
case val.IsNull():
toks = append(toks, &Token{
Type: hclsyntax.TokenIdent,
Bytes: []byte(`null`),
})
case val.Type() == cty.Bool:
var src []byte
if val.True() {
src = []byte(`true`)
} else {
src = []byte(`false`)
}
toks = append(toks, &Token{
Type: hclsyntax.TokenIdent,
Bytes: src,
})
case val.Type() == cty.Number:
bf := val.AsBigFloat()
srcStr := bf.Text('f', -1)
toks = append(toks, &Token{
Type: hclsyntax.TokenNumberLit,
Bytes: []byte(srcStr),
})
case val.Type() == cty.String:
// TODO: If it's a multi-line string ending in a newline, format
// it as a HEREDOC instead.
src := escapeQuotedStringLit(val.AsString())
toks = append(toks, &Token{
Type: hclsyntax.TokenOQuote,
Bytes: []byte{'"'},
})
if len(src) > 0 {
toks = append(toks, &Token{
Type: hclsyntax.TokenQuotedLit,
Bytes: src,
})
}
toks = append(toks, &Token{
Type: hclsyntax.TokenCQuote,
Bytes: []byte{'"'},
})
case val.Type().IsListType() || val.Type().IsSetType() || val.Type().IsTupleType():
toks = append(toks, &Token{
Type: hclsyntax.TokenOBrack,
Bytes: []byte{'['},
})
i := 0
for it := val.ElementIterator(); it.Next(); {
if i > 0 {
toks = append(toks, &Token{
Type: hclsyntax.TokenComma,
Bytes: []byte{','},
})
}
_, eVal := it.Element()
toks = appendTokensForValue(eVal, toks)
i++
}
toks = append(toks, &Token{
Type: hclsyntax.TokenCBrack,
Bytes: []byte{']'},
})
case val.Type().IsMapType() || val.Type().IsObjectType():
toks = append(toks, &Token{
Type: hclsyntax.TokenOBrace,
Bytes: []byte{'{'},
})
i := 0
for it := val.ElementIterator(); it.Next(); {
if i > 0 {
toks = append(toks, &Token{
Type: hclsyntax.TokenComma,
Bytes: []byte{','},
})
}
eKey, eVal := it.Element()
if hclsyntax.ValidIdentifier(eKey.AsString()) {
toks = append(toks, &Token{
Type: hclsyntax.TokenIdent,
Bytes: []byte(eKey.AsString()),
})
} else {
toks = appendTokensForValue(eKey, toks)
}
toks = append(toks, &Token{
Type: hclsyntax.TokenEqual,
Bytes: []byte{'='},
})
toks = appendTokensForValue(eVal, toks)
i++
}
toks = append(toks, &Token{
Type: hclsyntax.TokenCBrace,
Bytes: []byte{'}'},
})
default:
panic(fmt.Sprintf("cannot produce tokens for %#v", val))
}
return toks
}
func escapeQuotedStringLit(s string) []byte {
if len(s) == 0 {
return nil
}
buf := make([]byte, 0, len(s))
for i, r := range s {
switch r {
case '\n':
buf = append(buf, '\\', 'n')
case '\r':
buf = append(buf, '\\', 'r')
case '\t':
buf = append(buf, '\\', 't')
case '"':
buf = append(buf, '\\', '"')
case '\\':
buf = append(buf, '\\', '\\')
case '$', '%':
buf = appendRune(buf, r)
remain := s[i+1:]
if len(remain) > 0 && remain[0] == '{' {
// Double up our template introducer symbol to escape it.
buf = appendRune(buf, r)
}
default:
if !unicode.IsPrint(r) {
var fmted string
if r < 65536 {
fmted = fmt.Sprintf("\\u%04x", r)
} else {
fmted = fmt.Sprintf("\\U%08x", r)
}
buf = append(buf, fmted...)
} else {
buf = appendRune(buf, r)
}
}
}
return buf
}
func appendRune(b []byte, r rune) []byte {
l := utf8.RuneLen(r)
for i := 0; i < l; i++ {
b = append(b, 0) // make room at the end of our buffer
}
ch := b[len(b)-l:]
utf8.EncodeRune(ch, r)
return b
}

View File

@ -327,9 +327,54 @@ func parseBlock(nativeBlock *hclsyntax.Block, from, leadComments, lineComments,
}
func parseExpression(nativeExpr hclsyntax.Expression, from inputTokens) *Expression {
// TODO: Populate VarRefs by analyzing the result of nativeExpr.Variables()
var allTokens TokenSeq
nativeVars := nativeExpr.Variables()
var absTraversals []*Traversal
for _, nativeTraversal := range nativeVars {
var traversalTokens TokenSeq
var before, traversalFrom inputTokens
before, traversalFrom, from = from.Partition(nativeTraversal.SourceRange())
if before.Len() > 0 {
allTokens = append(allTokens, before.Seq())
}
var steps []*Traverser
for _, nativeStep := range nativeTraversal {
var stepFrom inputTokens
before, stepFrom, traversalFrom = traversalFrom.Partition(nativeStep.SourceRange())
stepTokens := stepFrom.Seq()
if before.Len() > 0 {
traversalTokens = append(traversalTokens, before.Seq())
}
traversalTokens = append(traversalTokens, stepTokens)
step := &Traverser{
AllTokens: stepTokens,
Logical: nativeStep,
}
steps = append(steps, step)
}
// Attach any straggler that don't belong to a step to the traversal itself.
if traversalFrom.Len() > 0 {
traversalTokens = append(traversalTokens, traversalFrom.Seq())
}
allTokens = append(allTokens, &traversalTokens)
absTraversals = append(absTraversals, &Traversal{
AllTokens: &traversalTokens,
Steps: steps,
})
}
// Attach any stragglers that don't belong to a traversal to the expression
// itself. In an expression with no traversals at all, this is just the
// entirety of "from".
if from.Len() > 0 {
allTokens = append(allTokens, from.Seq())
}
return &Expression{
AllTokens: from.Seq(),
AllTokens: &allTokens,
AbsTraversals: absTraversals,
}
}

54
vendor/vendor.json vendored
View File

@ -1915,68 +1915,68 @@
{
"checksumSHA1": "dJPromzLdd492RQjE/09klKRXGs=",
"path": "github.com/hashicorp/hcl2/ext/dynblock",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "IAfC0Xri1iCRgbbiDBgs6ue8/Ic=",
"path": "github.com/hashicorp/hcl2/ext/typeexpr",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "BRJaQcKriVKEirVC7YxBxPufQF0=",
"path": "github.com/hashicorp/hcl2/gohcl",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "v1JCFNvhLqF3ErYcxkJJPboKO8c=",
"checksumSHA1": "3ypdUCoJwZt+XRMV/6FoRhywz8A=",
"path": "github.com/hashicorp/hcl2/hcl",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "6H/LBmIYL/dNjKvlbB2hAsxm2rw=",
"checksumSHA1": "o6XGTmFfazaLQiSVs5cnaNvJhYQ=",
"path": "github.com/hashicorp/hcl2/hcl/hclsyntax",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "G40fCmu1bSWXv4Hw5JXwEUTVThk=",
"checksumSHA1": "Cuhv6UBgimVhWWwYm8v7Moisrhg=",
"path": "github.com/hashicorp/hcl2/hcl/json",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "iIVMnRuvfOy/tJ1zE9rVcjD/01A=",
"path": "github.com/hashicorp/hcl2/hcldec",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "sySYF9Ew71VS/LfrG+s/0jK+1VQ=",
"path": "github.com/hashicorp/hcl2/hcled",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "IzmftuG99BqNhbFGhxZaGwtiMtM=",
"path": "github.com/hashicorp/hcl2/hclparse",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "v5qx2XghQ+EtvFLa4a0Efjiwt9I=",
"checksumSHA1": "UQzPVMlOo/3CJQnShbnNVcTF4EA=",
"path": "github.com/hashicorp/hcl2/hcltest",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "C82otWNczU2S3azjxsjdGH9zW+Y=",
"checksumSHA1": "v2ADtHrkDgQdqsroh9SJRsfANTk=",
"path": "github.com/hashicorp/hcl2/hclwrite",
"revision": "36446359d27574bf110611001414da561731b62d",
"revisionTime": "2018-05-24T19:11:53Z"
"revision": "41cff854d8157be197e6b4698a8d9570ced9476b",
"revisionTime": "2018-07-18T22:41:35Z"
},
{
"checksumSHA1": "M09yxoBoCEtG7EcHR8aEWLzMMJc=",