github.com/hashicorp/hcl/v2@v2.20.0/hclsyntax/parser.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package hclsyntax
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"strconv"
    10  	"unicode/utf8"
    11  
    12  	"github.com/apparentlymart/go-textseg/v15/textseg"
    13  	"github.com/hashicorp/hcl/v2"
    14  	"github.com/zclconf/go-cty/cty"
    15  )
    16  
    17  type parser struct {
    18  	*peeker
    19  
    20  	// set to true if any recovery is attempted. The parser can use this
    21  	// to attempt to reduce error noise by suppressing "bad token" errors
    22  	// in recovery mode, assuming that the recovery heuristics have failed
    23  	// in this case and left the peeker in a wrong place.
    24  	recovery bool
    25  }
    26  
    27  func (p *parser) ParseBody(end TokenType) (*Body, hcl.Diagnostics) {
    28  	attrs := Attributes{}
    29  	blocks := Blocks{}
    30  	var diags hcl.Diagnostics
    31  
    32  	startRange := p.PrevRange()
    33  	var endRange hcl.Range
    34  
    35  Token:
    36  	for {
    37  		next := p.Peek()
    38  		if next.Type == end {
    39  			endRange = p.NextRange()
    40  			p.Read()
    41  			break Token
    42  		}
    43  
    44  		switch next.Type {
    45  		case TokenNewline:
    46  			p.Read()
    47  			continue
    48  		case TokenIdent:
    49  			item, itemDiags := p.ParseBodyItem()
    50  			diags = append(diags, itemDiags...)
    51  			switch titem := item.(type) {
    52  			case *Block:
    53  				blocks = append(blocks, titem)
    54  			case *Attribute:
    55  				if existing, exists := attrs[titem.Name]; exists {
    56  					diags = append(diags, &hcl.Diagnostic{
    57  						Severity: hcl.DiagError,
    58  						Summary:  "Attribute redefined",
    59  						Detail: fmt.Sprintf(
    60  							"The argument %q was already set at %s. Each argument may be set only once.",
    61  							titem.Name, existing.NameRange.String(),
    62  						),
    63  						Subject: &titem.NameRange,
    64  					})
    65  				} else {
    66  					attrs[titem.Name] = titem
    67  				}
    68  			default:
    69  				// This should never happen for valid input, but may if a
    70  				// syntax error was detected in ParseBodyItem that prevented
    71  				// it from even producing a partially-broken item. In that
    72  				// case, it would've left at least one error in the diagnostics
    73  				// slice we already dealt with above.
    74  				//
    75  				// We'll assume ParseBodyItem attempted recovery to leave
    76  				// us in a reasonable position to try parsing the next item.
    77  				continue
    78  			}
    79  		default:
    80  			bad := p.Read()
    81  			if !p.recovery {
    82  				switch bad.Type {
    83  				case TokenOQuote:
    84  					diags = append(diags, &hcl.Diagnostic{
    85  						Severity: hcl.DiagError,
    86  						Summary:  "Invalid argument name",
    87  						Detail:   "Argument names must not be quoted.",
    88  						Subject:  &bad.Range,
    89  					})
    90  				case TokenEOF:
    91  					switch end {
    92  					case TokenCBrace:
    93  						// If we're looking for a closing brace then we're parsing a block
    94  						diags = append(diags, &hcl.Diagnostic{
    95  							Severity: hcl.DiagError,
    96  							Summary:  "Unclosed configuration block",
    97  							Detail:   "There is no closing brace for this block before the end of the file. This may be caused by incorrect brace nesting elsewhere in this file.",
    98  							Subject:  &startRange,
    99  						})
   100  					default:
   101  						// The only other "end" should itself be TokenEOF (for
   102  						// the top-level body) and so we shouldn't get here,
   103  						// but we'll return a generic error message anyway to
   104  						// be resilient.
   105  						diags = append(diags, &hcl.Diagnostic{
   106  							Severity: hcl.DiagError,
   107  							Summary:  "Unclosed configuration body",
   108  							Detail:   "Found end of file before the end of this configuration body.",
   109  							Subject:  &startRange,
   110  						})
   111  					}
   112  				default:
   113  					diags = append(diags, &hcl.Diagnostic{
   114  						Severity: hcl.DiagError,
   115  						Summary:  "Argument or block definition required",
   116  						Detail:   "An argument or block definition is required here.",
   117  						Subject:  &bad.Range,
   118  					})
   119  				}
   120  			}
   121  			endRange = p.PrevRange() // arbitrary, but somewhere inside the body means better diagnostics
   122  
   123  			p.recover(end) // attempt to recover to the token after the end of this body
   124  			break Token
   125  		}
   126  	}
   127  
   128  	return &Body{
   129  		Attributes: attrs,
   130  		Blocks:     blocks,
   131  
   132  		SrcRange: hcl.RangeBetween(startRange, endRange),
   133  		EndRange: hcl.Range{
   134  			Filename: endRange.Filename,
   135  			Start:    endRange.End,
   136  			End:      endRange.End,
   137  		},
   138  	}, diags
   139  }
   140  
   141  func (p *parser) ParseBodyItem() (Node, hcl.Diagnostics) {
   142  	ident := p.Read()
   143  	if ident.Type != TokenIdent {
   144  		p.recoverAfterBodyItem()
   145  		return nil, hcl.Diagnostics{
   146  			{
   147  				Severity: hcl.DiagError,
   148  				Summary:  "Argument or block definition required",
   149  				Detail:   "An argument or block definition is required here.",
   150  				Subject:  &ident.Range,
   151  			},
   152  		}
   153  	}
   154  
   155  	next := p.Peek()
   156  
   157  	switch next.Type {
   158  	case TokenEqual:
   159  		return p.finishParsingBodyAttribute(ident, false)
   160  	case TokenOQuote, TokenOBrace, TokenIdent:
   161  		return p.finishParsingBodyBlock(ident)
   162  	default:
   163  		p.recoverAfterBodyItem()
   164  		return nil, hcl.Diagnostics{
   165  			{
   166  				Severity: hcl.DiagError,
   167  				Summary:  "Argument or block definition required",
   168  				Detail:   "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.",
   169  				Subject:  &ident.Range,
   170  			},
   171  		}
   172  	}
   173  }
   174  
   175  // parseSingleAttrBody is a weird variant of ParseBody that deals with the
   176  // body of a nested block containing only one attribute value all on a single
   177  // line, like foo { bar = baz } . It expects to find a single attribute item
   178  // immediately followed by the end token type with no intervening newlines.
   179  func (p *parser) parseSingleAttrBody(end TokenType) (*Body, hcl.Diagnostics) {
   180  	ident := p.Read()
   181  	if ident.Type != TokenIdent {
   182  		p.recoverAfterBodyItem()
   183  		return nil, hcl.Diagnostics{
   184  			{
   185  				Severity: hcl.DiagError,
   186  				Summary:  "Argument or block definition required",
   187  				Detail:   "An argument or block definition is required here.",
   188  				Subject:  &ident.Range,
   189  			},
   190  		}
   191  	}
   192  
   193  	var attr *Attribute
   194  	var diags hcl.Diagnostics
   195  
   196  	next := p.Peek()
   197  
   198  	switch next.Type {
   199  	case TokenEqual:
   200  		node, attrDiags := p.finishParsingBodyAttribute(ident, true)
   201  		diags = append(diags, attrDiags...)
   202  		attr = node.(*Attribute)
   203  	case TokenOQuote, TokenOBrace, TokenIdent:
   204  		p.recoverAfterBodyItem()
   205  		return nil, hcl.Diagnostics{
   206  			{
   207  				Severity: hcl.DiagError,
   208  				Summary:  "Argument definition required",
   209  				Detail:   fmt.Sprintf("A single-line block definition can contain only a single argument. If you meant to define argument %q, use an equals sign to assign it a value. To define a nested block, place it on a line of its own within its parent block.", ident.Bytes),
   210  				Subject:  hcl.RangeBetween(ident.Range, next.Range).Ptr(),
   211  			},
   212  		}
   213  	default:
   214  		p.recoverAfterBodyItem()
   215  		return nil, hcl.Diagnostics{
   216  			{
   217  				Severity: hcl.DiagError,
   218  				Summary:  "Argument or block definition required",
   219  				Detail:   "An argument or block definition is required here. To set an argument, use the equals sign \"=\" to introduce the argument value.",
   220  				Subject:  &ident.Range,
   221  			},
   222  		}
   223  	}
   224  
   225  	return &Body{
   226  		Attributes: Attributes{
   227  			string(ident.Bytes): attr,
   228  		},
   229  
   230  		SrcRange: attr.SrcRange,
   231  		EndRange: hcl.Range{
   232  			Filename: attr.SrcRange.Filename,
   233  			Start:    attr.SrcRange.End,
   234  			End:      attr.SrcRange.End,
   235  		},
   236  	}, diags
   237  
   238  }
   239  
   240  func (p *parser) finishParsingBodyAttribute(ident Token, singleLine bool) (Node, hcl.Diagnostics) {
   241  	eqTok := p.Read() // eat equals token
   242  	if eqTok.Type != TokenEqual {
   243  		// should never happen if caller behaves
   244  		panic("finishParsingBodyAttribute called with next not equals")
   245  	}
   246  
   247  	var endRange hcl.Range
   248  
   249  	expr, diags := p.ParseExpression()
   250  	if p.recovery && diags.HasErrors() {
   251  		// recovery within expressions tends to be tricky, so we've probably
   252  		// landed somewhere weird. We'll try to reset to the start of a body
   253  		// item so parsing can continue.
   254  		endRange = p.PrevRange()
   255  		p.recoverAfterBodyItem()
   256  	} else {
   257  		endRange = p.PrevRange()
   258  		if !singleLine {
   259  			end := p.Peek()
   260  			if end.Type != TokenNewline && end.Type != TokenEOF {
   261  				if !p.recovery {
   262  					summary := "Missing newline after argument"
   263  					detail := "An argument definition must end with a newline."
   264  
   265  					if end.Type == TokenComma {
   266  						summary = "Unexpected comma after argument"
   267  						detail = "Argument definitions must be separated by newlines, not commas. " + detail
   268  					}
   269  
   270  					diags = append(diags, &hcl.Diagnostic{
   271  						Severity: hcl.DiagError,
   272  						Summary:  summary,
   273  						Detail:   detail,
   274  						Subject:  &end.Range,
   275  						Context:  hcl.RangeBetween(ident.Range, end.Range).Ptr(),
   276  					})
   277  				}
   278  				endRange = p.PrevRange()
   279  				p.recoverAfterBodyItem()
   280  			} else {
   281  				endRange = p.PrevRange()
   282  				p.Read() // eat newline
   283  			}
   284  		}
   285  	}
   286  
   287  	return &Attribute{
   288  		Name: string(ident.Bytes),
   289  		Expr: expr,
   290  
   291  		SrcRange:    hcl.RangeBetween(ident.Range, endRange),
   292  		NameRange:   ident.Range,
   293  		EqualsRange: eqTok.Range,
   294  	}, diags
   295  }
   296  
   297  func (p *parser) finishParsingBodyBlock(ident Token) (Node, hcl.Diagnostics) {
   298  	var blockType = string(ident.Bytes)
   299  	var diags hcl.Diagnostics
   300  	var labels []string
   301  	var labelRanges []hcl.Range
   302  
   303  	var oBrace Token
   304  
   305  Token:
   306  	for {
   307  		tok := p.Peek()
   308  
   309  		switch tok.Type {
   310  
   311  		case TokenOBrace:
   312  			oBrace = p.Read()
   313  			break Token
   314  
   315  		case TokenOQuote:
   316  			label, labelRange, labelDiags := p.parseQuotedStringLiteral()
   317  			diags = append(diags, labelDiags...)
   318  			labels = append(labels, label)
   319  			labelRanges = append(labelRanges, labelRange)
   320  			// parseQuoteStringLiteral recovers up to the closing quote
   321  			// if it encounters problems, so we can continue looking for
   322  			// more labels and eventually the block body even.
   323  
   324  		case TokenIdent:
   325  			tok = p.Read() // eat token
   326  			label, labelRange := string(tok.Bytes), tok.Range
   327  			labels = append(labels, label)
   328  			labelRanges = append(labelRanges, labelRange)
   329  
   330  		default:
   331  			switch tok.Type {
   332  			case TokenEqual:
   333  				diags = append(diags, &hcl.Diagnostic{
   334  					Severity: hcl.DiagError,
   335  					Summary:  "Invalid block definition",
   336  					Detail:   "The equals sign \"=\" indicates an argument definition, and must not be used when defining a block.",
   337  					Subject:  &tok.Range,
   338  					Context:  hcl.RangeBetween(ident.Range, tok.Range).Ptr(),
   339  				})
   340  			case TokenNewline:
   341  				diags = append(diags, &hcl.Diagnostic{
   342  					Severity: hcl.DiagError,
   343  					Summary:  "Invalid block definition",
   344  					Detail:   "A block definition must have block content delimited by \"{\" and \"}\", starting on the same line as the block header.",
   345  					Subject:  &tok.Range,
   346  					Context:  hcl.RangeBetween(ident.Range, tok.Range).Ptr(),
   347  				})
   348  			default:
   349  				if !p.recovery {
   350  					diags = append(diags, &hcl.Diagnostic{
   351  						Severity: hcl.DiagError,
   352  						Summary:  "Invalid block definition",
   353  						Detail:   "Either a quoted string block label or an opening brace (\"{\") is expected here.",
   354  						Subject:  &tok.Range,
   355  						Context:  hcl.RangeBetween(ident.Range, tok.Range).Ptr(),
   356  					})
   357  				}
   358  			}
   359  
   360  			p.recoverAfterBodyItem()
   361  
   362  			return &Block{
   363  				Type:   blockType,
   364  				Labels: labels,
   365  				Body: &Body{
   366  					SrcRange: ident.Range,
   367  					EndRange: ident.Range,
   368  				},
   369  
   370  				TypeRange:       ident.Range,
   371  				LabelRanges:     labelRanges,
   372  				OpenBraceRange:  ident.Range, // placeholder
   373  				CloseBraceRange: ident.Range, // placeholder
   374  			}, diags
   375  		}
   376  	}
   377  
   378  	// Once we fall out here, the peeker is pointed just after our opening
   379  	// brace, so we can begin our nested body parsing.
   380  	var body *Body
   381  	var bodyDiags hcl.Diagnostics
   382  	switch p.Peek().Type {
   383  	case TokenNewline, TokenEOF, TokenCBrace:
   384  		body, bodyDiags = p.ParseBody(TokenCBrace)
   385  	default:
   386  		// Special one-line, single-attribute block parsing mode.
   387  		body, bodyDiags = p.parseSingleAttrBody(TokenCBrace)
   388  		switch p.Peek().Type {
   389  		case TokenCBrace:
   390  			p.Read() // the happy path - just consume the closing brace
   391  		case TokenComma:
   392  			// User seems to be trying to use the object-constructor
   393  			// comma-separated style, which isn't permitted for blocks.
   394  			diags = append(diags, &hcl.Diagnostic{
   395  				Severity: hcl.DiagError,
   396  				Summary:  "Invalid single-argument block definition",
   397  				Detail:   "Single-line block syntax can include only one argument definition. To define multiple arguments, use the multi-line block syntax with one argument definition per line.",
   398  				Subject:  p.Peek().Range.Ptr(),
   399  			})
   400  			p.recover(TokenCBrace)
   401  		case TokenNewline:
   402  			// We don't allow weird mixtures of single and multi-line syntax.
   403  			diags = append(diags, &hcl.Diagnostic{
   404  				Severity: hcl.DiagError,
   405  				Summary:  "Invalid single-argument block definition",
   406  				Detail:   "An argument definition on the same line as its containing block creates a single-line block definition, which must also be closed on the same line. Place the block's closing brace immediately after the argument definition.",
   407  				Subject:  p.Peek().Range.Ptr(),
   408  			})
   409  			p.recover(TokenCBrace)
   410  		default:
   411  			// Some other weird thing is going on. Since we can't guess a likely
   412  			// user intent for this one, we'll skip it if we're already in
   413  			// recovery mode.
   414  			if !p.recovery {
   415  				switch p.Peek().Type {
   416  				case TokenEOF:
   417  					diags = append(diags, &hcl.Diagnostic{
   418  						Severity: hcl.DiagError,
   419  						Summary:  "Unclosed configuration block",
   420  						Detail:   "There is no closing brace for this block before the end of the file. This may be caused by incorrect brace nesting elsewhere in this file.",
   421  						Subject:  oBrace.Range.Ptr(),
   422  						Context:  hcl.RangeBetween(ident.Range, oBrace.Range).Ptr(),
   423  					})
   424  				default:
   425  					diags = append(diags, &hcl.Diagnostic{
   426  						Severity: hcl.DiagError,
   427  						Summary:  "Invalid single-argument block definition",
   428  						Detail:   "A single-line block definition must end with a closing brace immediately after its single argument definition.",
   429  						Subject:  p.Peek().Range.Ptr(),
   430  					})
   431  				}
   432  			}
   433  			p.recover(TokenCBrace)
   434  		}
   435  	}
   436  	diags = append(diags, bodyDiags...)
   437  	cBraceRange := p.PrevRange()
   438  
   439  	eol := p.Peek()
   440  	if eol.Type == TokenNewline || eol.Type == TokenEOF {
   441  		p.Read() // eat newline
   442  	} else {
   443  		if !p.recovery {
   444  			diags = append(diags, &hcl.Diagnostic{
   445  				Severity: hcl.DiagError,
   446  				Summary:  "Missing newline after block definition",
   447  				Detail:   "A block definition must end with a newline.",
   448  				Subject:  &eol.Range,
   449  				Context:  hcl.RangeBetween(ident.Range, eol.Range).Ptr(),
   450  			})
   451  		}
   452  		p.recoverAfterBodyItem()
   453  	}
   454  
   455  	// We must never produce a nil body, since the caller may attempt to
   456  	// do analysis of a partial result when there's an error, so we'll
   457  	// insert a placeholder if we otherwise failed to produce a valid
   458  	// body due to one of the syntax error paths above.
   459  	if body == nil && diags.HasErrors() {
   460  		body = &Body{
   461  			SrcRange: hcl.RangeBetween(oBrace.Range, cBraceRange),
   462  			EndRange: cBraceRange,
   463  		}
   464  	}
   465  
   466  	return &Block{
   467  		Type:   blockType,
   468  		Labels: labels,
   469  		Body:   body,
   470  
   471  		TypeRange:       ident.Range,
   472  		LabelRanges:     labelRanges,
   473  		OpenBraceRange:  oBrace.Range,
   474  		CloseBraceRange: cBraceRange,
   475  	}, diags
   476  }
   477  
   478  func (p *parser) ParseExpression() (Expression, hcl.Diagnostics) {
   479  	return p.parseTernaryConditional()
   480  }
   481  
   482  func (p *parser) parseTernaryConditional() (Expression, hcl.Diagnostics) {
   483  	// The ternary conditional operator (.. ? .. : ..) behaves somewhat
   484  	// like a binary operator except that the "symbol" is itself
   485  	// an expression enclosed in two punctuation characters.
   486  	// The middle expression is parsed as if the ? and : symbols
   487  	// were parentheses. The "rhs" (the "false expression") is then
   488  	// treated right-associatively so it behaves similarly to the
   489  	// middle in terms of precedence.
   490  
   491  	startRange := p.NextRange()
   492  	var condExpr, trueExpr, falseExpr Expression
   493  	var diags hcl.Diagnostics
   494  
   495  	condExpr, condDiags := p.parseBinaryOps(binaryOps)
   496  	diags = append(diags, condDiags...)
   497  	if p.recovery && condDiags.HasErrors() {
   498  		return condExpr, diags
   499  	}
   500  
   501  	questionMark := p.Peek()
   502  	if questionMark.Type != TokenQuestion {
   503  		return condExpr, diags
   504  	}
   505  
   506  	p.Read() // eat question mark
   507  
   508  	trueExpr, trueDiags := p.ParseExpression()
   509  	diags = append(diags, trueDiags...)
   510  	if p.recovery && trueDiags.HasErrors() {
   511  		return condExpr, diags
   512  	}
   513  
   514  	colon := p.Peek()
   515  	if colon.Type != TokenColon {
   516  		diags = append(diags, &hcl.Diagnostic{
   517  			Severity: hcl.DiagError,
   518  			Summary:  "Missing false expression in conditional",
   519  			Detail:   "The conditional operator (...?...:...) requires a false expression, delimited by a colon.",
   520  			Subject:  &colon.Range,
   521  			Context:  hcl.RangeBetween(startRange, colon.Range).Ptr(),
   522  		})
   523  		return condExpr, diags
   524  	}
   525  
   526  	p.Read() // eat colon
   527  
   528  	falseExpr, falseDiags := p.ParseExpression()
   529  	diags = append(diags, falseDiags...)
   530  	if p.recovery && falseDiags.HasErrors() {
   531  		return condExpr, diags
   532  	}
   533  
   534  	return &ConditionalExpr{
   535  		Condition:   condExpr,
   536  		TrueResult:  trueExpr,
   537  		FalseResult: falseExpr,
   538  
   539  		SrcRange: hcl.RangeBetween(startRange, falseExpr.Range()),
   540  	}, diags
   541  }
   542  
   543  // parseBinaryOps calls itself recursively to work through all of the
   544  // operator precedence groups, and then eventually calls parseExpressionTerm
   545  // for each operand.
   546  func (p *parser) parseBinaryOps(ops []map[TokenType]*Operation) (Expression, hcl.Diagnostics) {
   547  	if len(ops) == 0 {
   548  		// We've run out of operators, so now we'll just try to parse a term.
   549  		return p.parseExpressionWithTraversals()
   550  	}
   551  
   552  	thisLevel := ops[0]
   553  	remaining := ops[1:]
   554  
   555  	var lhs, rhs Expression
   556  	var operation *Operation
   557  	var diags hcl.Diagnostics
   558  
   559  	// Parse a term that might be the first operand of a binary
   560  	// operation or it might just be a standalone term.
   561  	// We won't know until we've parsed it and can look ahead
   562  	// to see if there's an operator token for this level.
   563  	lhs, lhsDiags := p.parseBinaryOps(remaining)
   564  	diags = append(diags, lhsDiags...)
   565  	if p.recovery && lhsDiags.HasErrors() {
   566  		return lhs, diags
   567  	}
   568  
   569  	// We'll keep eating up operators until we run out, so that operators
   570  	// with the same precedence will combine in a left-associative manner:
   571  	// a+b+c => (a+b)+c, not a+(b+c)
   572  	//
   573  	// Should we later want to have right-associative operators, a way
   574  	// to achieve that would be to call back up to ParseExpression here
   575  	// instead of iteratively parsing only the remaining operators.
   576  	for {
   577  		next := p.Peek()
   578  		var newOp *Operation
   579  		var ok bool
   580  		if newOp, ok = thisLevel[next.Type]; !ok {
   581  			break
   582  		}
   583  
   584  		// Are we extending an expression started on the previous iteration?
   585  		if operation != nil {
   586  			lhs = &BinaryOpExpr{
   587  				LHS: lhs,
   588  				Op:  operation,
   589  				RHS: rhs,
   590  
   591  				SrcRange: hcl.RangeBetween(lhs.Range(), rhs.Range()),
   592  			}
   593  		}
   594  
   595  		operation = newOp
   596  		p.Read() // eat operator token
   597  		var rhsDiags hcl.Diagnostics
   598  		rhs, rhsDiags = p.parseBinaryOps(remaining)
   599  		diags = append(diags, rhsDiags...)
   600  		if p.recovery && rhsDiags.HasErrors() {
   601  			return lhs, diags
   602  		}
   603  	}
   604  
   605  	if operation == nil {
   606  		return lhs, diags
   607  	}
   608  
   609  	return &BinaryOpExpr{
   610  		LHS: lhs,
   611  		Op:  operation,
   612  		RHS: rhs,
   613  
   614  		SrcRange: hcl.RangeBetween(lhs.Range(), rhs.Range()),
   615  	}, diags
   616  }
   617  
   618  func (p *parser) parseExpressionWithTraversals() (Expression, hcl.Diagnostics) {
   619  	term, diags := p.parseExpressionTerm()
   620  	ret, moreDiags := p.parseExpressionTraversals(term)
   621  	diags = append(diags, moreDiags...)
   622  	return ret, diags
   623  }
   624  
   625  func (p *parser) parseExpressionTraversals(from Expression) (Expression, hcl.Diagnostics) {
   626  	var diags hcl.Diagnostics
   627  	ret := from
   628  
   629  Traversal:
   630  	for {
   631  		next := p.Peek()
   632  
   633  		switch next.Type {
   634  		case TokenDot:
   635  			// Attribute access or splat
   636  			dot := p.Read()
   637  			attrTok := p.Peek()
   638  
   639  			switch attrTok.Type {
   640  			case TokenIdent:
   641  				attrTok = p.Read() // eat token
   642  				name := string(attrTok.Bytes)
   643  				rng := hcl.RangeBetween(dot.Range, attrTok.Range)
   644  				step := hcl.TraverseAttr{
   645  					Name:     name,
   646  					SrcRange: rng,
   647  				}
   648  
   649  				ret = makeRelativeTraversal(ret, step, rng)
   650  
   651  			case TokenNumberLit:
   652  				// This is a weird form we inherited from HIL, allowing numbers
   653  				// to be used as attributes as a weird way of writing [n].
   654  				// This was never actually a first-class thing in HIL, but
   655  				// HIL tolerated sequences like .0. in its variable names and
   656  				// calling applications like Terraform exploited that to
   657  				// introduce indexing syntax where none existed.
   658  				numTok := p.Read() // eat token
   659  				attrTok = numTok
   660  
   661  				// This syntax is ambiguous if multiple indices are used in
   662  				// succession, like foo.0.1.baz: that actually parses as
   663  				// a fractional number 0.1. Since we're only supporting this
   664  				// syntax for compatibility with legacy Terraform
   665  				// configurations, and Terraform does not tend to have lists
   666  				// of lists, we'll choose to reject that here with a helpful
   667  				// error message, rather than failing later because the index
   668  				// isn't a whole number.
   669  				if dotIdx := bytes.IndexByte(numTok.Bytes, '.'); dotIdx >= 0 {
   670  					first := numTok.Bytes[:dotIdx]
   671  					second := numTok.Bytes[dotIdx+1:]
   672  					diags = append(diags, &hcl.Diagnostic{
   673  						Severity: hcl.DiagError,
   674  						Summary:  "Invalid legacy index syntax",
   675  						Detail:   fmt.Sprintf("When using the legacy index syntax, chaining two indexes together is not permitted. Use the proper index syntax instead, like [%s][%s].", first, second),
   676  						Subject:  &attrTok.Range,
   677  					})
   678  					rng := hcl.RangeBetween(dot.Range, numTok.Range)
   679  					step := hcl.TraverseIndex{
   680  						Key:      cty.DynamicVal,
   681  						SrcRange: rng,
   682  					}
   683  					ret = makeRelativeTraversal(ret, step, rng)
   684  					break
   685  				}
   686  
   687  				numVal, numDiags := p.numberLitValue(numTok)
   688  				diags = append(diags, numDiags...)
   689  
   690  				rng := hcl.RangeBetween(dot.Range, numTok.Range)
   691  				step := hcl.TraverseIndex{
   692  					Key:      numVal,
   693  					SrcRange: rng,
   694  				}
   695  
   696  				ret = makeRelativeTraversal(ret, step, rng)
   697  
   698  			case TokenStar:
   699  				// "Attribute-only" splat expression.
   700  				// (This is a kinda weird construct inherited from HIL, which
   701  				// behaves a bit like a [*] splat except that it is only able
   702  				// to do attribute traversals into each of its elements,
   703  				// whereas foo[*] can support _any_ traversal.
   704  				marker := p.Read() // eat star
   705  				trav := make(hcl.Traversal, 0, 1)
   706  				var firstRange, lastRange hcl.Range
   707  				firstRange = p.NextRange()
   708  				lastRange = marker.Range
   709  				for p.Peek().Type == TokenDot {
   710  					dot := p.Read()
   711  
   712  					if p.Peek().Type == TokenNumberLit {
   713  						// Continuing the "weird stuff inherited from HIL"
   714  						// theme, we also allow numbers as attribute names
   715  						// inside splats and interpret them as indexing
   716  						// into a list, for expressions like:
   717  						// foo.bar.*.baz.0.foo
   718  						numTok := p.Read()
   719  
   720  						// Weird special case if the user writes something
   721  						// like foo.bar.*.baz.0.0.foo, where 0.0 parses
   722  						// as a number.
   723  						if dotIdx := bytes.IndexByte(numTok.Bytes, '.'); dotIdx >= 0 {
   724  							first := numTok.Bytes[:dotIdx]
   725  							second := numTok.Bytes[dotIdx+1:]
   726  							diags = append(diags, &hcl.Diagnostic{
   727  								Severity: hcl.DiagError,
   728  								Summary:  "Invalid legacy index syntax",
   729  								Detail:   fmt.Sprintf("When using the legacy index syntax, chaining two indexes together is not permitted. Use the proper index syntax with a full splat expression [*] instead, like [%s][%s].", first, second),
   730  								Subject:  &attrTok.Range,
   731  							})
   732  							trav = append(trav, hcl.TraverseIndex{
   733  								Key:      cty.DynamicVal,
   734  								SrcRange: hcl.RangeBetween(dot.Range, numTok.Range),
   735  							})
   736  							lastRange = numTok.Range
   737  							continue
   738  						}
   739  
   740  						numVal, numDiags := p.numberLitValue(numTok)
   741  						diags = append(diags, numDiags...)
   742  						trav = append(trav, hcl.TraverseIndex{
   743  							Key:      numVal,
   744  							SrcRange: hcl.RangeBetween(dot.Range, numTok.Range),
   745  						})
   746  						lastRange = numTok.Range
   747  						continue
   748  					}
   749  
   750  					if p.Peek().Type != TokenIdent {
   751  						if !p.recovery {
   752  							if p.Peek().Type == TokenStar {
   753  								diags = append(diags, &hcl.Diagnostic{
   754  									Severity: hcl.DiagError,
   755  									Summary:  "Nested splat expression not allowed",
   756  									Detail:   "A splat expression (*) cannot be used inside another attribute-only splat expression.",
   757  									Subject:  p.Peek().Range.Ptr(),
   758  								})
   759  							} else {
   760  								diags = append(diags, &hcl.Diagnostic{
   761  									Severity: hcl.DiagError,
   762  									Summary:  "Invalid attribute name",
   763  									Detail:   "An attribute name is required after a dot.",
   764  									Subject:  &attrTok.Range,
   765  								})
   766  							}
   767  						}
   768  						p.setRecovery()
   769  						continue Traversal
   770  					}
   771  
   772  					attrTok := p.Read()
   773  					trav = append(trav, hcl.TraverseAttr{
   774  						Name:     string(attrTok.Bytes),
   775  						SrcRange: hcl.RangeBetween(dot.Range, attrTok.Range),
   776  					})
   777  					lastRange = attrTok.Range
   778  				}
   779  
   780  				itemExpr := &AnonSymbolExpr{
   781  					SrcRange: hcl.RangeBetween(dot.Range, marker.Range),
   782  				}
   783  				var travExpr Expression
   784  				if len(trav) == 0 {
   785  					travExpr = itemExpr
   786  				} else {
   787  					travExpr = &RelativeTraversalExpr{
   788  						Source:    itemExpr,
   789  						Traversal: trav,
   790  						SrcRange:  hcl.RangeBetween(firstRange, lastRange),
   791  					}
   792  				}
   793  
   794  				ret = &SplatExpr{
   795  					Source: ret,
   796  					Each:   travExpr,
   797  					Item:   itemExpr,
   798  
   799  					SrcRange:    hcl.RangeBetween(from.Range(), lastRange),
   800  					MarkerRange: hcl.RangeBetween(dot.Range, marker.Range),
   801  				}
   802  
   803  			default:
   804  				diags = append(diags, &hcl.Diagnostic{
   805  					Severity: hcl.DiagError,
   806  					Summary:  "Invalid attribute name",
   807  					Detail:   "An attribute name is required after a dot.",
   808  					Subject:  &attrTok.Range,
   809  				})
   810  				// This leaves the peeker in a bad place, so following items
   811  				// will probably be misparsed until we hit something that
   812  				// allows us to re-sync.
   813  				//
   814  				// We will probably need to do something better here eventually
   815  				// in order to support autocomplete triggered by typing a
   816  				// period.
   817  				p.setRecovery()
   818  			}
   819  
   820  		case TokenOBrack:
   821  			// Indexing of a collection.
   822  			// This may or may not be a hcl.Traverser, depending on whether
   823  			// the key value is something constant.
   824  
   825  			open := p.Read()
   826  			switch p.Peek().Type {
   827  			case TokenStar:
   828  				// This is a full splat expression, like foo[*], which consumes
   829  				// the rest of the traversal steps after it using a recursive
   830  				// call to this function.
   831  				p.Read() // consume star
   832  				close := p.Read()
   833  				if close.Type != TokenCBrack && !p.recovery {
   834  					diags = append(diags, &hcl.Diagnostic{
   835  						Severity: hcl.DiagError,
   836  						Summary:  "Missing close bracket on splat index",
   837  						Detail:   "The star for a full splat operator must be immediately followed by a closing bracket (\"]\").",
   838  						Subject:  &close.Range,
   839  					})
   840  					close = p.recover(TokenCBrack)
   841  				}
   842  				// Splat expressions use a special "anonymous symbol"  as a
   843  				// placeholder in an expression to be evaluated once for each
   844  				// item in the source expression.
   845  				itemExpr := &AnonSymbolExpr{
   846  					SrcRange: hcl.RangeBetween(open.Range, close.Range),
   847  				}
   848  				// Now we'll recursively call this same function to eat any
   849  				// remaining traversal steps against the anonymous symbol.
   850  				travExpr, nestedDiags := p.parseExpressionTraversals(itemExpr)
   851  				diags = append(diags, nestedDiags...)
   852  
   853  				ret = &SplatExpr{
   854  					Source: ret,
   855  					Each:   travExpr,
   856  					Item:   itemExpr,
   857  
   858  					SrcRange:    hcl.RangeBetween(from.Range(), travExpr.Range()),
   859  					MarkerRange: hcl.RangeBetween(open.Range, close.Range),
   860  				}
   861  
   862  			default:
   863  
   864  				var close Token
   865  				p.PushIncludeNewlines(false) // arbitrary newlines allowed in brackets
   866  				keyExpr, keyDiags := p.ParseExpression()
   867  				diags = append(diags, keyDiags...)
   868  				if p.recovery && keyDiags.HasErrors() {
   869  					close = p.recover(TokenCBrack)
   870  				} else {
   871  					close = p.Read()
   872  					if close.Type != TokenCBrack && !p.recovery {
   873  						diags = append(diags, &hcl.Diagnostic{
   874  							Severity: hcl.DiagError,
   875  							Summary:  "Missing close bracket on index",
   876  							Detail:   "The index operator must end with a closing bracket (\"]\").",
   877  							Subject:  &close.Range,
   878  						})
   879  						close = p.recover(TokenCBrack)
   880  					}
   881  				}
   882  				p.PopIncludeNewlines()
   883  
   884  				if lit, isLit := keyExpr.(*LiteralValueExpr); isLit {
   885  					litKey, _ := lit.Value(nil)
   886  					rng := hcl.RangeBetween(open.Range, close.Range)
   887  					step := hcl.TraverseIndex{
   888  						Key:      litKey,
   889  						SrcRange: rng,
   890  					}
   891  					ret = makeRelativeTraversal(ret, step, rng)
   892  				} else if tmpl, isTmpl := keyExpr.(*TemplateExpr); isTmpl && tmpl.IsStringLiteral() {
   893  					litKey, _ := tmpl.Value(nil)
   894  					rng := hcl.RangeBetween(open.Range, close.Range)
   895  					step := hcl.TraverseIndex{
   896  						Key:      litKey,
   897  						SrcRange: rng,
   898  					}
   899  					ret = makeRelativeTraversal(ret, step, rng)
   900  				} else {
   901  					rng := hcl.RangeBetween(open.Range, close.Range)
   902  					ret = &IndexExpr{
   903  						Collection: ret,
   904  						Key:        keyExpr,
   905  
   906  						SrcRange:     hcl.RangeBetween(from.Range(), rng),
   907  						OpenRange:    open.Range,
   908  						BracketRange: rng,
   909  					}
   910  				}
   911  			}
   912  
   913  		default:
   914  			break Traversal
   915  		}
   916  	}
   917  
   918  	return ret, diags
   919  }
   920  
   921  // makeRelativeTraversal takes an expression and a traverser and returns
   922  // a traversal expression that combines the two. If the given expression
   923  // is already a traversal, it is extended in place (mutating it) and
   924  // returned. If it isn't, a new RelativeTraversalExpr is created and returned.
   925  func makeRelativeTraversal(expr Expression, next hcl.Traverser, rng hcl.Range) Expression {
   926  	switch texpr := expr.(type) {
   927  	case *ScopeTraversalExpr:
   928  		texpr.Traversal = append(texpr.Traversal, next)
   929  		texpr.SrcRange = hcl.RangeBetween(texpr.SrcRange, rng)
   930  		return texpr
   931  	case *RelativeTraversalExpr:
   932  		texpr.Traversal = append(texpr.Traversal, next)
   933  		texpr.SrcRange = hcl.RangeBetween(texpr.SrcRange, rng)
   934  		return texpr
   935  	default:
   936  		return &RelativeTraversalExpr{
   937  			Source:    expr,
   938  			Traversal: hcl.Traversal{next},
   939  			SrcRange:  hcl.RangeBetween(expr.Range(), rng),
   940  		}
   941  	}
   942  }
   943  
   944  func (p *parser) parseExpressionTerm() (Expression, hcl.Diagnostics) {
   945  	start := p.Peek()
   946  
   947  	switch start.Type {
   948  	case TokenOParen:
   949  		oParen := p.Read() // eat open paren
   950  
   951  		p.PushIncludeNewlines(false)
   952  
   953  		expr, diags := p.ParseExpression()
   954  		if diags.HasErrors() {
   955  			// attempt to place the peeker after our closing paren
   956  			// before we return, so that the next parser has some
   957  			// chance of finding a valid expression.
   958  			p.recover(TokenCParen)
   959  			p.PopIncludeNewlines()
   960  			return expr, diags
   961  		}
   962  
   963  		close := p.Peek()
   964  		if close.Type != TokenCParen {
   965  			diags = append(diags, &hcl.Diagnostic{
   966  				Severity: hcl.DiagError,
   967  				Summary:  "Unbalanced parentheses",
   968  				Detail:   "Expected a closing parenthesis to terminate the expression.",
   969  				Subject:  &close.Range,
   970  				Context:  hcl.RangeBetween(start.Range, close.Range).Ptr(),
   971  			})
   972  			p.setRecovery()
   973  		}
   974  
   975  		cParen := p.Read() // eat closing paren
   976  		p.PopIncludeNewlines()
   977  
   978  		// Our parser's already taken care of the precedence effect of the
   979  		// parentheses by considering them to be a kind of "term", but we
   980  		// still need to include the parentheses in our AST so we can give
   981  		// an accurate representation of the source range that includes the
   982  		// open and closing parentheses.
   983  		expr = &ParenthesesExpr{
   984  			Expression: expr,
   985  			SrcRange:   hcl.RangeBetween(oParen.Range, cParen.Range),
   986  		}
   987  
   988  		return expr, diags
   989  
   990  	case TokenNumberLit:
   991  		tok := p.Read() // eat number token
   992  
   993  		numVal, diags := p.numberLitValue(tok)
   994  		return &LiteralValueExpr{
   995  			Val:      numVal,
   996  			SrcRange: tok.Range,
   997  		}, diags
   998  
   999  	case TokenIdent:
  1000  		tok := p.Read() // eat identifier token
  1001  
  1002  		if p.Peek().Type == TokenOParen || p.Peek().Type == TokenDoubleColon {
  1003  			return p.finishParsingFunctionCall(tok)
  1004  		}
  1005  
  1006  		name := string(tok.Bytes)
  1007  		switch name {
  1008  		case "true":
  1009  			return &LiteralValueExpr{
  1010  				Val:      cty.True,
  1011  				SrcRange: tok.Range,
  1012  			}, nil
  1013  		case "false":
  1014  			return &LiteralValueExpr{
  1015  				Val:      cty.False,
  1016  				SrcRange: tok.Range,
  1017  			}, nil
  1018  		case "null":
  1019  			return &LiteralValueExpr{
  1020  				Val:      cty.NullVal(cty.DynamicPseudoType),
  1021  				SrcRange: tok.Range,
  1022  			}, nil
  1023  		default:
  1024  			return &ScopeTraversalExpr{
  1025  				Traversal: hcl.Traversal{
  1026  					hcl.TraverseRoot{
  1027  						Name:     name,
  1028  						SrcRange: tok.Range,
  1029  					},
  1030  				},
  1031  				SrcRange: tok.Range,
  1032  			}, nil
  1033  		}
  1034  
  1035  	case TokenOQuote, TokenOHeredoc:
  1036  		open := p.Read() // eat opening marker
  1037  		closer := p.oppositeBracket(open.Type)
  1038  		exprs, passthru, _, diags := p.parseTemplateInner(closer, tokenOpensFlushHeredoc(open))
  1039  
  1040  		closeRange := p.PrevRange()
  1041  
  1042  		if passthru {
  1043  			if len(exprs) != 1 {
  1044  				panic("passthru set with len(exprs) != 1")
  1045  			}
  1046  			return &TemplateWrapExpr{
  1047  				Wrapped:  exprs[0],
  1048  				SrcRange: hcl.RangeBetween(open.Range, closeRange),
  1049  			}, diags
  1050  		}
  1051  
  1052  		return &TemplateExpr{
  1053  			Parts:    exprs,
  1054  			SrcRange: hcl.RangeBetween(open.Range, closeRange),
  1055  		}, diags
  1056  
  1057  	case TokenMinus:
  1058  		tok := p.Read() // eat minus token
  1059  
  1060  		// Important to use parseExpressionWithTraversals rather than parseExpression
  1061  		// here, otherwise we can capture a following binary expression into
  1062  		// our negation.
  1063  		// e.g. -46+5 should parse as (-46)+5, not -(46+5)
  1064  		operand, diags := p.parseExpressionWithTraversals()
  1065  		return &UnaryOpExpr{
  1066  			Op:  OpNegate,
  1067  			Val: operand,
  1068  
  1069  			SrcRange:    hcl.RangeBetween(tok.Range, operand.Range()),
  1070  			SymbolRange: tok.Range,
  1071  		}, diags
  1072  
  1073  	case TokenBang:
  1074  		tok := p.Read() // eat bang token
  1075  
  1076  		// Important to use parseExpressionWithTraversals rather than parseExpression
  1077  		// here, otherwise we can capture a following binary expression into
  1078  		// our negation.
  1079  		operand, diags := p.parseExpressionWithTraversals()
  1080  		return &UnaryOpExpr{
  1081  			Op:  OpLogicalNot,
  1082  			Val: operand,
  1083  
  1084  			SrcRange:    hcl.RangeBetween(tok.Range, operand.Range()),
  1085  			SymbolRange: tok.Range,
  1086  		}, diags
  1087  
  1088  	case TokenOBrack:
  1089  		return p.parseTupleCons()
  1090  
  1091  	case TokenOBrace:
  1092  		return p.parseObjectCons()
  1093  
  1094  	default:
  1095  		var diags hcl.Diagnostics
  1096  		if !p.recovery {
  1097  			switch start.Type {
  1098  			case TokenEOF:
  1099  				diags = append(diags, &hcl.Diagnostic{
  1100  					Severity: hcl.DiagError,
  1101  					Summary:  "Missing expression",
  1102  					Detail:   "Expected the start of an expression, but found the end of the file.",
  1103  					Subject:  &start.Range,
  1104  				})
  1105  			default:
  1106  				diags = append(diags, &hcl.Diagnostic{
  1107  					Severity: hcl.DiagError,
  1108  					Summary:  "Invalid expression",
  1109  					Detail:   "Expected the start of an expression, but found an invalid expression token.",
  1110  					Subject:  &start.Range,
  1111  				})
  1112  			}
  1113  		}
  1114  		p.setRecovery()
  1115  
  1116  		// Return a placeholder so that the AST is still structurally sound
  1117  		// even in the presence of parse errors.
  1118  		return &LiteralValueExpr{
  1119  			Val:      cty.DynamicVal,
  1120  			SrcRange: start.Range,
  1121  		}, diags
  1122  	}
  1123  }
  1124  
  1125  func (p *parser) numberLitValue(tok Token) (cty.Value, hcl.Diagnostics) {
  1126  	// The cty.ParseNumberVal is always the same behavior as converting a
  1127  	// string to a number, ensuring we always interpret decimal numbers in
  1128  	// the same way.
  1129  	numVal, err := cty.ParseNumberVal(string(tok.Bytes))
  1130  	if err != nil {
  1131  		ret := cty.UnknownVal(cty.Number)
  1132  		return ret, hcl.Diagnostics{
  1133  			{
  1134  				Severity: hcl.DiagError,
  1135  				Summary:  "Invalid number literal",
  1136  				// FIXME: not a very good error message, but convert only
  1137  				// gives us "a number is required", so not much help either.
  1138  				Detail:  "Failed to recognize the value of this number literal.",
  1139  				Subject: &tok.Range,
  1140  			},
  1141  		}
  1142  	}
  1143  	return numVal, nil
  1144  }
  1145  
  1146  // finishParsingFunctionCall parses a function call assuming that the function
  1147  // name was already read, and so the peeker should be pointing at the opening
  1148  // parenthesis after the name, or at the double-colon after the initial
  1149  // function scope name.
  1150  func (p *parser) finishParsingFunctionCall(name Token) (Expression, hcl.Diagnostics) {
  1151  	var diags hcl.Diagnostics
  1152  
  1153  	openTok := p.Read()
  1154  	if openTok.Type != TokenOParen && openTok.Type != TokenDoubleColon {
  1155  		// should never happen if callers behave
  1156  		panic("finishParsingFunctionCall called with unsupported next token")
  1157  	}
  1158  
  1159  	nameStr := string(name.Bytes)
  1160  	nameEndPos := name.Range.End
  1161  	for openTok.Type == TokenDoubleColon {
  1162  		nextName := p.Read()
  1163  		if nextName.Type != TokenIdent {
  1164  			diags = append(diags, &hcl.Diagnostic{
  1165  				Severity: hcl.DiagError,
  1166  				Summary:  "Missing function name",
  1167  				Detail:   "Function scope resolution symbol :: must be followed by a function name in this scope.",
  1168  				Subject:  &nextName.Range,
  1169  				Context:  hcl.RangeBetween(name.Range, nextName.Range).Ptr(),
  1170  			})
  1171  			p.recoverOver(TokenOParen)
  1172  			return nil, diags
  1173  		}
  1174  
  1175  		// Initial versions of HCLv2 didn't support function namespaces, and
  1176  		// so for backward compatibility we just treat namespaced functions
  1177  		// as weird names with "::" separators in them, saved as a string
  1178  		// to keep the API unchanged. FunctionCallExpr also has some special
  1179  		// handling of names containing :: when referring to a function that
  1180  		// doesn't exist in EvalContext, to return better error messages
  1181  		// when namespaces are used incorrectly.
  1182  		nameStr = nameStr + "::" + string(nextName.Bytes)
  1183  		nameEndPos = nextName.Range.End
  1184  
  1185  		openTok = p.Read()
  1186  	}
  1187  
  1188  	nameRange := hcl.Range{
  1189  		Filename: name.Range.Filename,
  1190  		Start:    name.Range.Start,
  1191  		End:      nameEndPos,
  1192  	}
  1193  
  1194  	if openTok.Type != TokenOParen {
  1195  		diags = append(diags, &hcl.Diagnostic{
  1196  			Severity: hcl.DiagError,
  1197  			Summary:  "Missing open parenthesis",
  1198  			Detail:   "Function selector must be followed by an open parenthesis to begin the function call.",
  1199  			Subject:  &openTok.Range,
  1200  			Context:  hcl.RangeBetween(name.Range, openTok.Range).Ptr(),
  1201  		})
  1202  		p.recoverOver(TokenOParen)
  1203  		return nil, diags
  1204  	}
  1205  
  1206  	var args []Expression
  1207  	var expandFinal bool
  1208  	var closeTok Token
  1209  
  1210  	// Arbitrary newlines are allowed inside the function call parentheses.
  1211  	p.PushIncludeNewlines(false)
  1212  
  1213  Token:
  1214  	for {
  1215  		tok := p.Peek()
  1216  
  1217  		if tok.Type == TokenCParen {
  1218  			closeTok = p.Read() // eat closing paren
  1219  			break Token
  1220  		}
  1221  
  1222  		arg, argDiags := p.ParseExpression()
  1223  		args = append(args, arg)
  1224  		diags = append(diags, argDiags...)
  1225  		if p.recovery && argDiags.HasErrors() {
  1226  			// if there was a parse error in the argument then we've
  1227  			// probably been left in a weird place in the token stream,
  1228  			// so we'll bail out with a partial argument list.
  1229  			recoveredTok := p.recover(TokenCParen)
  1230  
  1231  			// record the recovered token, if one was found
  1232  			if recoveredTok.Type == TokenCParen {
  1233  				closeTok = recoveredTok
  1234  			}
  1235  			break Token
  1236  		}
  1237  
  1238  		sep := p.Read()
  1239  		if sep.Type == TokenCParen {
  1240  			closeTok = sep
  1241  			break Token
  1242  		}
  1243  
  1244  		if sep.Type == TokenEllipsis {
  1245  			expandFinal = true
  1246  
  1247  			if p.Peek().Type != TokenCParen {
  1248  				if !p.recovery {
  1249  					diags = append(diags, &hcl.Diagnostic{
  1250  						Severity: hcl.DiagError,
  1251  						Summary:  "Missing closing parenthesis",
  1252  						Detail:   "An expanded function argument (with ...) must be immediately followed by closing parentheses.",
  1253  						Subject:  &sep.Range,
  1254  						Context:  hcl.RangeBetween(name.Range, sep.Range).Ptr(),
  1255  					})
  1256  				}
  1257  				closeTok = p.recover(TokenCParen)
  1258  			} else {
  1259  				closeTok = p.Read() // eat closing paren
  1260  			}
  1261  			break Token
  1262  		}
  1263  
  1264  		if sep.Type != TokenComma {
  1265  			switch sep.Type {
  1266  			case TokenEOF:
  1267  				diags = append(diags, &hcl.Diagnostic{
  1268  					Severity: hcl.DiagError,
  1269  					Summary:  "Unterminated function call",
  1270  					Detail:   "There is no closing parenthesis for this function call before the end of the file. This may be caused by incorrect parenthesis nesting elsewhere in this file.",
  1271  					Subject:  hcl.RangeBetween(name.Range, openTok.Range).Ptr(),
  1272  				})
  1273  			default:
  1274  				diags = append(diags, &hcl.Diagnostic{
  1275  					Severity: hcl.DiagError,
  1276  					Summary:  "Missing argument separator",
  1277  					Detail:   "A comma is required to separate each function argument from the next.",
  1278  					Subject:  &sep.Range,
  1279  					Context:  hcl.RangeBetween(name.Range, sep.Range).Ptr(),
  1280  				})
  1281  			}
  1282  			closeTok = p.recover(TokenCParen)
  1283  			break Token
  1284  		}
  1285  
  1286  		if p.Peek().Type == TokenCParen {
  1287  			// A trailing comma after the last argument gets us in here.
  1288  			closeTok = p.Read() // eat closing paren
  1289  			break Token
  1290  		}
  1291  
  1292  	}
  1293  
  1294  	p.PopIncludeNewlines()
  1295  
  1296  	return &FunctionCallExpr{
  1297  		Name: nameStr,
  1298  		Args: args,
  1299  
  1300  		ExpandFinal: expandFinal,
  1301  
  1302  		NameRange:       nameRange,
  1303  		OpenParenRange:  openTok.Range,
  1304  		CloseParenRange: closeTok.Range,
  1305  	}, diags
  1306  }
  1307  
  1308  func (p *parser) parseTupleCons() (Expression, hcl.Diagnostics) {
  1309  	open := p.Read()
  1310  	if open.Type != TokenOBrack {
  1311  		// Should never happen if callers are behaving
  1312  		panic("parseTupleCons called without peeker pointing to open bracket")
  1313  	}
  1314  
  1315  	p.PushIncludeNewlines(false)
  1316  	defer p.PopIncludeNewlines()
  1317  
  1318  	if forKeyword.TokenMatches(p.Peek()) {
  1319  		return p.finishParsingForExpr(open)
  1320  	}
  1321  
  1322  	var close Token
  1323  
  1324  	var diags hcl.Diagnostics
  1325  	var exprs []Expression
  1326  
  1327  	for {
  1328  		next := p.Peek()
  1329  		if next.Type == TokenCBrack {
  1330  			close = p.Read() // eat closer
  1331  			break
  1332  		}
  1333  
  1334  		expr, exprDiags := p.ParseExpression()
  1335  		exprs = append(exprs, expr)
  1336  		diags = append(diags, exprDiags...)
  1337  
  1338  		if p.recovery && exprDiags.HasErrors() {
  1339  			// If expression parsing failed then we are probably in a strange
  1340  			// place in the token stream, so we'll bail out and try to reset
  1341  			// to after our closing bracket to allow parsing to continue.
  1342  			close = p.recover(TokenCBrack)
  1343  			break
  1344  		}
  1345  
  1346  		next = p.Peek()
  1347  		if next.Type == TokenCBrack {
  1348  			close = p.Read() // eat closer
  1349  			break
  1350  		}
  1351  
  1352  		if next.Type != TokenComma {
  1353  			if !p.recovery {
  1354  				switch next.Type {
  1355  				case TokenEOF:
  1356  					diags = append(diags, &hcl.Diagnostic{
  1357  						Severity: hcl.DiagError,
  1358  						Summary:  "Unterminated tuple constructor expression",
  1359  						Detail:   "There is no corresponding closing bracket before the end of the file. This may be caused by incorrect bracket nesting elsewhere in this file.",
  1360  						Subject:  open.Range.Ptr(),
  1361  					})
  1362  				default:
  1363  					diags = append(diags, &hcl.Diagnostic{
  1364  						Severity: hcl.DiagError,
  1365  						Summary:  "Missing item separator",
  1366  						Detail:   "Expected a comma to mark the beginning of the next item.",
  1367  						Subject:  &next.Range,
  1368  						Context:  hcl.RangeBetween(open.Range, next.Range).Ptr(),
  1369  					})
  1370  				}
  1371  			}
  1372  			close = p.recover(TokenCBrack)
  1373  			break
  1374  		}
  1375  
  1376  		p.Read() // eat comma
  1377  
  1378  	}
  1379  
  1380  	return &TupleConsExpr{
  1381  		Exprs: exprs,
  1382  
  1383  		SrcRange:  hcl.RangeBetween(open.Range, close.Range),
  1384  		OpenRange: open.Range,
  1385  	}, diags
  1386  }
  1387  
  1388  func (p *parser) parseObjectCons() (Expression, hcl.Diagnostics) {
  1389  	open := p.Read()
  1390  	if open.Type != TokenOBrace {
  1391  		// Should never happen if callers are behaving
  1392  		panic("parseObjectCons called without peeker pointing to open brace")
  1393  	}
  1394  
  1395  	// We must temporarily stop looking at newlines here while we check for
  1396  	// a "for" keyword, since for expressions are _not_ newline-sensitive,
  1397  	// even though object constructors are.
  1398  	p.PushIncludeNewlines(false)
  1399  	isFor := forKeyword.TokenMatches(p.Peek())
  1400  	p.PopIncludeNewlines()
  1401  	if isFor {
  1402  		return p.finishParsingForExpr(open)
  1403  	}
  1404  
  1405  	p.PushIncludeNewlines(true)
  1406  	defer p.PopIncludeNewlines()
  1407  
  1408  	var close Token
  1409  
  1410  	var diags hcl.Diagnostics
  1411  	var items []ObjectConsItem
  1412  
  1413  	for {
  1414  		next := p.Peek()
  1415  		if next.Type == TokenNewline {
  1416  			p.Read() // eat newline
  1417  			continue
  1418  		}
  1419  
  1420  		if next.Type == TokenCBrace {
  1421  			close = p.Read() // eat closer
  1422  			break
  1423  		}
  1424  
  1425  		// Wrapping parens are not explicitly represented in the AST, but
  1426  		// we want to use them here to disambiguate intepreting a mapping
  1427  		// key as a full expression rather than just a name, and so
  1428  		// we'll remember this was present and use it to force the
  1429  		// behavior of our final ObjectConsKeyExpr.
  1430  		forceNonLiteral := (p.Peek().Type == TokenOParen)
  1431  
  1432  		var key Expression
  1433  		var keyDiags hcl.Diagnostics
  1434  		key, keyDiags = p.ParseExpression()
  1435  		diags = append(diags, keyDiags...)
  1436  
  1437  		if p.recovery && keyDiags.HasErrors() {
  1438  			// If expression parsing failed then we are probably in a strange
  1439  			// place in the token stream, so we'll bail out and try to reset
  1440  			// to after our closing brace to allow parsing to continue.
  1441  			close = p.recover(TokenCBrace)
  1442  			break
  1443  		}
  1444  
  1445  		// We wrap up the key expression in a special wrapper that deals
  1446  		// with our special case that naked identifiers as object keys
  1447  		// are interpreted as literal strings.
  1448  		key = &ObjectConsKeyExpr{
  1449  			Wrapped:         key,
  1450  			ForceNonLiteral: forceNonLiteral,
  1451  		}
  1452  
  1453  		next = p.Peek()
  1454  		if next.Type != TokenEqual && next.Type != TokenColon {
  1455  			if !p.recovery {
  1456  				switch next.Type {
  1457  				case TokenNewline, TokenComma:
  1458  					diags = append(diags, &hcl.Diagnostic{
  1459  						Severity: hcl.DiagError,
  1460  						Summary:  "Missing attribute value",
  1461  						Detail:   "Expected an attribute value, introduced by an equals sign (\"=\").",
  1462  						Subject:  &next.Range,
  1463  						Context:  hcl.RangeBetween(open.Range, next.Range).Ptr(),
  1464  					})
  1465  				case TokenIdent:
  1466  					// Although this might just be a plain old missing equals
  1467  					// sign before a reference, one way to get here is to try
  1468  					// to write an attribute name containing a period followed
  1469  					// by a digit, which was valid in HCL1, like this:
  1470  					//     foo1.2_bar = "baz"
  1471  					// We can't know exactly what the user intended here, but
  1472  					// we'll augment our message with an extra hint in this case
  1473  					// in case it is helpful.
  1474  					diags = append(diags, &hcl.Diagnostic{
  1475  						Severity: hcl.DiagError,
  1476  						Summary:  "Missing key/value separator",
  1477  						Detail:   "Expected an equals sign (\"=\") to mark the beginning of the attribute value. If you intended to given an attribute name containing periods or spaces, write the name in quotes to create a string literal.",
  1478  						Subject:  &next.Range,
  1479  						Context:  hcl.RangeBetween(open.Range, next.Range).Ptr(),
  1480  					})
  1481  				case TokenEOF:
  1482  					diags = append(diags, &hcl.Diagnostic{
  1483  						Severity: hcl.DiagError,
  1484  						Summary:  "Unterminated object constructor expression",
  1485  						Detail:   "There is no corresponding closing brace before the end of the file. This may be caused by incorrect brace nesting elsewhere in this file.",
  1486  						Subject:  open.Range.Ptr(),
  1487  					})
  1488  				default:
  1489  					diags = append(diags, &hcl.Diagnostic{
  1490  						Severity: hcl.DiagError,
  1491  						Summary:  "Missing key/value separator",
  1492  						Detail:   "Expected an equals sign (\"=\") to mark the beginning of the attribute value.",
  1493  						Subject:  &next.Range,
  1494  						Context:  hcl.RangeBetween(open.Range, next.Range).Ptr(),
  1495  					})
  1496  				}
  1497  			}
  1498  			close = p.recover(TokenCBrace)
  1499  			break
  1500  		}
  1501  
  1502  		p.Read() // eat equals sign or colon
  1503  
  1504  		value, valueDiags := p.ParseExpression()
  1505  		diags = append(diags, valueDiags...)
  1506  
  1507  		if p.recovery && valueDiags.HasErrors() {
  1508  			// If expression parsing failed then we are probably in a strange
  1509  			// place in the token stream, so we'll bail out and try to reset
  1510  			// to after our closing brace to allow parsing to continue.
  1511  			close = p.recover(TokenCBrace)
  1512  			break
  1513  		}
  1514  
  1515  		items = append(items, ObjectConsItem{
  1516  			KeyExpr:   key,
  1517  			ValueExpr: value,
  1518  		})
  1519  
  1520  		next = p.Peek()
  1521  		if next.Type == TokenCBrace {
  1522  			close = p.Read() // eat closer
  1523  			break
  1524  		}
  1525  
  1526  		if next.Type != TokenComma && next.Type != TokenNewline {
  1527  			if !p.recovery {
  1528  				switch next.Type {
  1529  				case TokenEOF:
  1530  					diags = append(diags, &hcl.Diagnostic{
  1531  						Severity: hcl.DiagError,
  1532  						Summary:  "Unterminated object constructor expression",
  1533  						Detail:   "There is no corresponding closing brace before the end of the file. This may be caused by incorrect brace nesting elsewhere in this file.",
  1534  						Subject:  open.Range.Ptr(),
  1535  					})
  1536  				default:
  1537  					diags = append(diags, &hcl.Diagnostic{
  1538  						Severity: hcl.DiagError,
  1539  						Summary:  "Missing attribute separator",
  1540  						Detail:   "Expected a newline or comma to mark the beginning of the next attribute.",
  1541  						Subject:  &next.Range,
  1542  						Context:  hcl.RangeBetween(open.Range, next.Range).Ptr(),
  1543  					})
  1544  				}
  1545  			}
  1546  			close = p.recover(TokenCBrace)
  1547  			break
  1548  		}
  1549  
  1550  		p.Read() // eat comma or newline
  1551  
  1552  	}
  1553  
  1554  	return &ObjectConsExpr{
  1555  		Items: items,
  1556  
  1557  		SrcRange:  hcl.RangeBetween(open.Range, close.Range),
  1558  		OpenRange: open.Range,
  1559  	}, diags
  1560  }
  1561  
  1562  func (p *parser) finishParsingForExpr(open Token) (Expression, hcl.Diagnostics) {
  1563  	p.PushIncludeNewlines(false)
  1564  	defer p.PopIncludeNewlines()
  1565  	introducer := p.Read()
  1566  	if !forKeyword.TokenMatches(introducer) {
  1567  		// Should never happen if callers are behaving
  1568  		panic("finishParsingForExpr called without peeker pointing to 'for' identifier")
  1569  	}
  1570  
  1571  	var makeObj bool
  1572  	var closeType TokenType
  1573  	switch open.Type {
  1574  	case TokenOBrace:
  1575  		makeObj = true
  1576  		closeType = TokenCBrace
  1577  	case TokenOBrack:
  1578  		makeObj = false // making a tuple
  1579  		closeType = TokenCBrack
  1580  	default:
  1581  		// Should never happen if callers are behaving
  1582  		panic("finishParsingForExpr called with invalid open token")
  1583  	}
  1584  
  1585  	var diags hcl.Diagnostics
  1586  	var keyName, valName string
  1587  
  1588  	if p.Peek().Type != TokenIdent {
  1589  		if !p.recovery {
  1590  			diags = append(diags, &hcl.Diagnostic{
  1591  				Severity: hcl.DiagError,
  1592  				Summary:  "Invalid 'for' expression",
  1593  				Detail:   "For expression requires variable name after 'for'.",
  1594  				Subject:  p.Peek().Range.Ptr(),
  1595  				Context:  hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
  1596  			})
  1597  		}
  1598  		close := p.recover(closeType)
  1599  		return &LiteralValueExpr{
  1600  			Val:      cty.DynamicVal,
  1601  			SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1602  		}, diags
  1603  	}
  1604  
  1605  	valName = string(p.Read().Bytes)
  1606  
  1607  	if p.Peek().Type == TokenComma {
  1608  		// What we just read was actually the key, then.
  1609  		keyName = valName
  1610  		p.Read() // eat comma
  1611  
  1612  		if p.Peek().Type != TokenIdent {
  1613  			if !p.recovery {
  1614  				diags = append(diags, &hcl.Diagnostic{
  1615  					Severity: hcl.DiagError,
  1616  					Summary:  "Invalid 'for' expression",
  1617  					Detail:   "For expression requires value variable name after comma.",
  1618  					Subject:  p.Peek().Range.Ptr(),
  1619  					Context:  hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
  1620  				})
  1621  			}
  1622  			close := p.recover(closeType)
  1623  			return &LiteralValueExpr{
  1624  				Val:      cty.DynamicVal,
  1625  				SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1626  			}, diags
  1627  		}
  1628  
  1629  		valName = string(p.Read().Bytes)
  1630  	}
  1631  
  1632  	if !inKeyword.TokenMatches(p.Peek()) {
  1633  		if !p.recovery {
  1634  			diags = append(diags, &hcl.Diagnostic{
  1635  				Severity: hcl.DiagError,
  1636  				Summary:  "Invalid 'for' expression",
  1637  				Detail:   "For expression requires the 'in' keyword after its name declarations.",
  1638  				Subject:  p.Peek().Range.Ptr(),
  1639  				Context:  hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
  1640  			})
  1641  		}
  1642  		close := p.recover(closeType)
  1643  		return &LiteralValueExpr{
  1644  			Val:      cty.DynamicVal,
  1645  			SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1646  		}, diags
  1647  	}
  1648  	p.Read() // eat 'in' keyword
  1649  
  1650  	collExpr, collDiags := p.ParseExpression()
  1651  	diags = append(diags, collDiags...)
  1652  	if p.recovery && collDiags.HasErrors() {
  1653  		close := p.recover(closeType)
  1654  		return &LiteralValueExpr{
  1655  			Val:      cty.DynamicVal,
  1656  			SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1657  		}, diags
  1658  	}
  1659  
  1660  	if p.Peek().Type != TokenColon {
  1661  		if !p.recovery {
  1662  			diags = append(diags, &hcl.Diagnostic{
  1663  				Severity: hcl.DiagError,
  1664  				Summary:  "Invalid 'for' expression",
  1665  				Detail:   "For expression requires a colon after the collection expression.",
  1666  				Subject:  p.Peek().Range.Ptr(),
  1667  				Context:  hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
  1668  			})
  1669  		}
  1670  		close := p.recover(closeType)
  1671  		return &LiteralValueExpr{
  1672  			Val:      cty.DynamicVal,
  1673  			SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1674  		}, diags
  1675  	}
  1676  	p.Read() // eat colon
  1677  
  1678  	var keyExpr, valExpr Expression
  1679  	var keyDiags, valDiags hcl.Diagnostics
  1680  	valExpr, valDiags = p.ParseExpression()
  1681  	if p.Peek().Type == TokenFatArrow {
  1682  		// What we just parsed was actually keyExpr
  1683  		p.Read() // eat the fat arrow
  1684  		keyExpr, keyDiags = valExpr, valDiags
  1685  
  1686  		valExpr, valDiags = p.ParseExpression()
  1687  	}
  1688  	diags = append(diags, keyDiags...)
  1689  	diags = append(diags, valDiags...)
  1690  	if p.recovery && (keyDiags.HasErrors() || valDiags.HasErrors()) {
  1691  		close := p.recover(closeType)
  1692  		return &LiteralValueExpr{
  1693  			Val:      cty.DynamicVal,
  1694  			SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1695  		}, diags
  1696  	}
  1697  
  1698  	group := false
  1699  	var ellipsis Token
  1700  	if p.Peek().Type == TokenEllipsis {
  1701  		ellipsis = p.Read()
  1702  		group = true
  1703  	}
  1704  
  1705  	var condExpr Expression
  1706  	var condDiags hcl.Diagnostics
  1707  	if ifKeyword.TokenMatches(p.Peek()) {
  1708  		p.Read() // eat "if"
  1709  		condExpr, condDiags = p.ParseExpression()
  1710  		diags = append(diags, condDiags...)
  1711  		if p.recovery && condDiags.HasErrors() {
  1712  			close := p.recover(p.oppositeBracket(open.Type))
  1713  			return &LiteralValueExpr{
  1714  				Val:      cty.DynamicVal,
  1715  				SrcRange: hcl.RangeBetween(open.Range, close.Range),
  1716  			}, diags
  1717  		}
  1718  	}
  1719  
  1720  	var close Token
  1721  	if p.Peek().Type == closeType {
  1722  		close = p.Read()
  1723  	} else {
  1724  		if !p.recovery {
  1725  			diags = append(diags, &hcl.Diagnostic{
  1726  				Severity: hcl.DiagError,
  1727  				Summary:  "Invalid 'for' expression",
  1728  				Detail:   "Extra characters after the end of the 'for' expression.",
  1729  				Subject:  p.Peek().Range.Ptr(),
  1730  				Context:  hcl.RangeBetween(open.Range, p.Peek().Range).Ptr(),
  1731  			})
  1732  		}
  1733  		close = p.recover(closeType)
  1734  	}
  1735  
  1736  	if !makeObj {
  1737  		if keyExpr != nil {
  1738  			diags = append(diags, &hcl.Diagnostic{
  1739  				Severity: hcl.DiagError,
  1740  				Summary:  "Invalid 'for' expression",
  1741  				Detail:   "Key expression is not valid when building a tuple.",
  1742  				Subject:  keyExpr.Range().Ptr(),
  1743  				Context:  hcl.RangeBetween(open.Range, close.Range).Ptr(),
  1744  			})
  1745  		}
  1746  
  1747  		if group {
  1748  			diags = append(diags, &hcl.Diagnostic{
  1749  				Severity: hcl.DiagError,
  1750  				Summary:  "Invalid 'for' expression",
  1751  				Detail:   "Grouping ellipsis (...) cannot be used when building a tuple.",
  1752  				Subject:  &ellipsis.Range,
  1753  				Context:  hcl.RangeBetween(open.Range, close.Range).Ptr(),
  1754  			})
  1755  		}
  1756  	} else {
  1757  		if keyExpr == nil {
  1758  			diags = append(diags, &hcl.Diagnostic{
  1759  				Severity: hcl.DiagError,
  1760  				Summary:  "Invalid 'for' expression",
  1761  				Detail:   "Key expression is required when building an object.",
  1762  				Subject:  valExpr.Range().Ptr(),
  1763  				Context:  hcl.RangeBetween(open.Range, close.Range).Ptr(),
  1764  			})
  1765  		}
  1766  	}
  1767  
  1768  	return &ForExpr{
  1769  		KeyVar:   keyName,
  1770  		ValVar:   valName,
  1771  		CollExpr: collExpr,
  1772  		KeyExpr:  keyExpr,
  1773  		ValExpr:  valExpr,
  1774  		CondExpr: condExpr,
  1775  		Group:    group,
  1776  
  1777  		SrcRange:   hcl.RangeBetween(open.Range, close.Range),
  1778  		OpenRange:  open.Range,
  1779  		CloseRange: close.Range,
  1780  	}, diags
  1781  }
  1782  
  1783  // parseQuotedStringLiteral is a helper for parsing quoted strings that
  1784  // aren't allowed to contain any interpolations, such as block labels.
  1785  func (p *parser) parseQuotedStringLiteral() (string, hcl.Range, hcl.Diagnostics) {
  1786  	oQuote := p.Read()
  1787  	if oQuote.Type != TokenOQuote {
  1788  		return "", oQuote.Range, hcl.Diagnostics{
  1789  			{
  1790  				Severity: hcl.DiagError,
  1791  				Summary:  "Invalid string literal",
  1792  				Detail:   "A quoted string is required here.",
  1793  				Subject:  &oQuote.Range,
  1794  			},
  1795  		}
  1796  	}
  1797  
  1798  	var diags hcl.Diagnostics
  1799  	ret := &bytes.Buffer{}
  1800  	var endRange hcl.Range
  1801  
  1802  Token:
  1803  	for {
  1804  		tok := p.Read()
  1805  		switch tok.Type {
  1806  
  1807  		case TokenCQuote:
  1808  			endRange = tok.Range
  1809  			break Token
  1810  
  1811  		case TokenQuotedLit:
  1812  			s, sDiags := ParseStringLiteralToken(tok)
  1813  			diags = append(diags, sDiags...)
  1814  			ret.WriteString(s)
  1815  
  1816  		case TokenTemplateControl, TokenTemplateInterp:
  1817  			which := "$"
  1818  			if tok.Type == TokenTemplateControl {
  1819  				which = "%"
  1820  			}
  1821  
  1822  			diags = append(diags, &hcl.Diagnostic{
  1823  				Severity: hcl.DiagError,
  1824  				Summary:  "Invalid string literal",
  1825  				Detail: fmt.Sprintf(
  1826  					"Template sequences are not allowed in this string. To include a literal %q, double it (as \"%s%s\") to escape it.",
  1827  					which, which, which,
  1828  				),
  1829  				Subject: &tok.Range,
  1830  				Context: hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
  1831  			})
  1832  
  1833  			// Now that we're returning an error callers won't attempt to use
  1834  			// the result for any real operations, but they might try to use
  1835  			// the partial AST for other analyses, so we'll leave a marker
  1836  			// to indicate that there was something invalid in the string to
  1837  			// help avoid misinterpretation of the partial result
  1838  			ret.WriteString(which)
  1839  			ret.WriteString("{ ... }")
  1840  
  1841  			p.recover(TokenTemplateSeqEnd) // we'll try to keep parsing after the sequence ends
  1842  
  1843  		case TokenEOF:
  1844  			diags = append(diags, &hcl.Diagnostic{
  1845  				Severity: hcl.DiagError,
  1846  				Summary:  "Unterminated string literal",
  1847  				Detail:   "Unable to find the closing quote mark before the end of the file.",
  1848  				Subject:  &tok.Range,
  1849  				Context:  hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
  1850  			})
  1851  			endRange = tok.Range
  1852  			break Token
  1853  
  1854  		default:
  1855  			// Should never happen, as long as the scanner is behaving itself
  1856  			diags = append(diags, &hcl.Diagnostic{
  1857  				Severity: hcl.DiagError,
  1858  				Summary:  "Invalid string literal",
  1859  				Detail:   "This item is not valid in a string literal.",
  1860  				Subject:  &tok.Range,
  1861  				Context:  hcl.RangeBetween(oQuote.Range, tok.Range).Ptr(),
  1862  			})
  1863  			p.recover(TokenCQuote)
  1864  			endRange = tok.Range
  1865  			break Token
  1866  
  1867  		}
  1868  
  1869  	}
  1870  
  1871  	return ret.String(), hcl.RangeBetween(oQuote.Range, endRange), diags
  1872  }
  1873  
  1874  // ParseStringLiteralToken processes the given token, which must be either a
  1875  // TokenQuotedLit or a TokenStringLit, returning the string resulting from
  1876  // resolving any escape sequences.
  1877  //
  1878  // If any error diagnostics are returned, the returned string may be incomplete
  1879  // or otherwise invalid.
  1880  func ParseStringLiteralToken(tok Token) (string, hcl.Diagnostics) {
  1881  	var quoted bool
  1882  	switch tok.Type {
  1883  	case TokenQuotedLit:
  1884  		quoted = true
  1885  	case TokenStringLit:
  1886  		quoted = false
  1887  	default:
  1888  		panic("ParseStringLiteralToken can only be used with TokenStringLit and TokenQuotedLit tokens")
  1889  	}
  1890  	var diags hcl.Diagnostics
  1891  
  1892  	ret := make([]byte, 0, len(tok.Bytes))
  1893  	slices := scanStringLit(tok.Bytes, quoted)
  1894  
  1895  	// We will mutate rng constantly as we walk through our token slices below.
  1896  	// Any diagnostics must take a copy of this rng rather than simply pointing
  1897  	// to it, e.g. by using rng.Ptr() rather than &rng.
  1898  	rng := tok.Range
  1899  	rng.End = rng.Start
  1900  
  1901  Slices:
  1902  	for _, slice := range slices {
  1903  		if len(slice) == 0 {
  1904  			continue
  1905  		}
  1906  
  1907  		// Advance the start of our range to where the previous token ended
  1908  		rng.Start = rng.End
  1909  
  1910  		// Advance the end of our range to after our token.
  1911  		b := slice
  1912  		for len(b) > 0 {
  1913  			adv, ch, _ := textseg.ScanGraphemeClusters(b, true)
  1914  			rng.End.Byte += adv
  1915  			switch ch[0] {
  1916  			case '\r', '\n':
  1917  				rng.End.Line++
  1918  				rng.End.Column = 1
  1919  			default:
  1920  				rng.End.Column++
  1921  			}
  1922  			b = b[adv:]
  1923  		}
  1924  
  1925  	TokenType:
  1926  		switch slice[0] {
  1927  		case '\\':
  1928  			if !quoted {
  1929  				// If we're not in quoted mode then just treat this token as
  1930  				// normal. (Slices can still start with backslash even if we're
  1931  				// not specifically looking for backslash sequences.)
  1932  				break TokenType
  1933  			}
  1934  			if len(slice) < 2 {
  1935  				diags = append(diags, &hcl.Diagnostic{
  1936  					Severity: hcl.DiagError,
  1937  					Summary:  "Invalid escape sequence",
  1938  					Detail:   "Backslash must be followed by an escape sequence selector character.",
  1939  					Subject:  rng.Ptr(),
  1940  				})
  1941  				break TokenType
  1942  			}
  1943  
  1944  			switch slice[1] {
  1945  
  1946  			case 'n':
  1947  				ret = append(ret, '\n')
  1948  				continue Slices
  1949  			case 'r':
  1950  				ret = append(ret, '\r')
  1951  				continue Slices
  1952  			case 't':
  1953  				ret = append(ret, '\t')
  1954  				continue Slices
  1955  			case '"':
  1956  				ret = append(ret, '"')
  1957  				continue Slices
  1958  			case '\\':
  1959  				ret = append(ret, '\\')
  1960  				continue Slices
  1961  			case 'u', 'U':
  1962  				if slice[1] == 'u' && len(slice) != 6 {
  1963  					diags = append(diags, &hcl.Diagnostic{
  1964  						Severity: hcl.DiagError,
  1965  						Summary:  "Invalid escape sequence",
  1966  						Detail:   "The \\u escape sequence must be followed by four hexadecimal digits.",
  1967  						Subject:  rng.Ptr(),
  1968  					})
  1969  					break TokenType
  1970  				} else if slice[1] == 'U' && len(slice) != 10 {
  1971  					diags = append(diags, &hcl.Diagnostic{
  1972  						Severity: hcl.DiagError,
  1973  						Summary:  "Invalid escape sequence",
  1974  						Detail:   "The \\U escape sequence must be followed by eight hexadecimal digits.",
  1975  						Subject:  rng.Ptr(),
  1976  					})
  1977  					break TokenType
  1978  				}
  1979  
  1980  				numHex := string(slice[2:])
  1981  				num, err := strconv.ParseUint(numHex, 16, 32)
  1982  				if err != nil {
  1983  					// Should never happen because the scanner won't match
  1984  					// a sequence of digits that isn't valid.
  1985  					panic(err)
  1986  				}
  1987  
  1988  				r := rune(num)
  1989  				l := utf8.RuneLen(r)
  1990  				if l == -1 {
  1991  					diags = append(diags, &hcl.Diagnostic{
  1992  						Severity: hcl.DiagError,
  1993  						Summary:  "Invalid escape sequence",
  1994  						Detail:   fmt.Sprintf("Cannot encode character U+%04x in UTF-8.", num),
  1995  						Subject:  rng.Ptr(),
  1996  					})
  1997  					break TokenType
  1998  				}
  1999  				for i := 0; i < l; i++ {
  2000  					ret = append(ret, 0)
  2001  				}
  2002  				rb := ret[len(ret)-l:]
  2003  				utf8.EncodeRune(rb, r)
  2004  
  2005  				continue Slices
  2006  
  2007  			default:
  2008  				diags = append(diags, &hcl.Diagnostic{
  2009  					Severity: hcl.DiagError,
  2010  					Summary:  "Invalid escape sequence",
  2011  					Detail:   fmt.Sprintf("The symbol %q is not a valid escape sequence selector.", slice[1:]),
  2012  					Subject:  rng.Ptr(),
  2013  				})
  2014  				ret = append(ret, slice[1:]...)
  2015  				continue Slices
  2016  			}
  2017  
  2018  		case '$', '%':
  2019  			if len(slice) != 3 {
  2020  				// Not long enough to be our escape sequence, so it's literal.
  2021  				break TokenType
  2022  			}
  2023  
  2024  			if slice[1] == slice[0] && slice[2] == '{' {
  2025  				ret = append(ret, slice[0])
  2026  				ret = append(ret, '{')
  2027  				continue Slices
  2028  			}
  2029  
  2030  			break TokenType
  2031  		}
  2032  
  2033  		// If we fall out here or break out of here from the switch above
  2034  		// then this slice is just a literal.
  2035  		ret = append(ret, slice...)
  2036  	}
  2037  
  2038  	return string(ret), diags
  2039  }
  2040  
  2041  // setRecovery turns on recovery mode without actually doing any recovery.
  2042  // This can be used when a parser knowingly leaves the peeker in a useless
  2043  // place and wants to suppress errors that might result from that decision.
  2044  func (p *parser) setRecovery() {
  2045  	p.recovery = true
  2046  }
  2047  
  2048  // recover seeks forward in the token stream until it finds TokenType "end",
  2049  // then returns with the peeker pointed at the following token.
  2050  //
  2051  // If the given token type is a bracketer, this function will additionally
  2052  // count nested instances of the brackets to try to leave the peeker at
  2053  // the end of the _current_ instance of that bracketer, skipping over any
  2054  // nested instances. This is a best-effort operation and may have
  2055  // unpredictable results on input with bad bracketer nesting.
  2056  func (p *parser) recover(end TokenType) Token {
  2057  	start := p.oppositeBracket(end)
  2058  	p.recovery = true
  2059  
  2060  	nest := 0
  2061  	for {
  2062  		tok := p.Read()
  2063  		ty := tok.Type
  2064  		if end == TokenTemplateSeqEnd && ty == TokenTemplateControl {
  2065  			// normalize so that our matching behavior can work, since
  2066  			// TokenTemplateControl/TokenTemplateInterp are asymmetrical
  2067  			// with TokenTemplateSeqEnd and thus we need to count both
  2068  			// openers if that's the closer we're looking for.
  2069  			ty = TokenTemplateInterp
  2070  		}
  2071  
  2072  		switch ty {
  2073  		case start:
  2074  			nest++
  2075  		case end:
  2076  			if nest < 1 {
  2077  				return tok
  2078  			}
  2079  
  2080  			nest--
  2081  		case TokenEOF:
  2082  			return tok
  2083  		}
  2084  	}
  2085  }
  2086  
  2087  // recoverOver seeks forward in the token stream until it finds a block
  2088  // starting with TokenType "start", then finds the corresponding end token,
  2089  // leaving the peeker pointed at the token after that end token.
  2090  //
  2091  // The given token type _must_ be a bracketer. For example, if the given
  2092  // start token is TokenOBrace then the parser will be left at the _end_ of
  2093  // the next brace-delimited block encountered, or at EOF if no such block
  2094  // is found or it is unclosed.
  2095  func (p *parser) recoverOver(start TokenType) {
  2096  	end := p.oppositeBracket(start)
  2097  
  2098  	// find the opening bracket first
  2099  Token:
  2100  	for {
  2101  		tok := p.Read()
  2102  		switch tok.Type {
  2103  		case start, TokenEOF:
  2104  			break Token
  2105  		}
  2106  	}
  2107  
  2108  	// Now use our existing recover function to locate the _end_ of the
  2109  	// container we've found.
  2110  	p.recover(end)
  2111  }
  2112  
  2113  func (p *parser) recoverAfterBodyItem() {
  2114  	p.recovery = true
  2115  	var open []TokenType
  2116  
  2117  Token:
  2118  	for {
  2119  		tok := p.Read()
  2120  
  2121  		switch tok.Type {
  2122  
  2123  		case TokenNewline:
  2124  			if len(open) == 0 {
  2125  				break Token
  2126  			}
  2127  
  2128  		case TokenEOF:
  2129  			break Token
  2130  
  2131  		case TokenOBrace, TokenOBrack, TokenOParen, TokenOQuote, TokenOHeredoc, TokenTemplateInterp, TokenTemplateControl:
  2132  			open = append(open, tok.Type)
  2133  
  2134  		case TokenCBrace, TokenCBrack, TokenCParen, TokenCQuote, TokenCHeredoc:
  2135  			opener := p.oppositeBracket(tok.Type)
  2136  			for len(open) > 0 && open[len(open)-1] != opener {
  2137  				open = open[:len(open)-1]
  2138  			}
  2139  			if len(open) > 0 {
  2140  				open = open[:len(open)-1]
  2141  			}
  2142  
  2143  		case TokenTemplateSeqEnd:
  2144  			for len(open) > 0 && open[len(open)-1] != TokenTemplateInterp && open[len(open)-1] != TokenTemplateControl {
  2145  				open = open[:len(open)-1]
  2146  			}
  2147  			if len(open) > 0 {
  2148  				open = open[:len(open)-1]
  2149  			}
  2150  
  2151  		}
  2152  	}
  2153  }
  2154  
  2155  // oppositeBracket finds the bracket that opposes the given bracketer, or
  2156  // NilToken if the given token isn't a bracketer.
  2157  //
  2158  // "Bracketer", for the sake of this function, is one end of a matching
  2159  // open/close set of tokens that establish a bracketing context.
  2160  func (p *parser) oppositeBracket(ty TokenType) TokenType {
  2161  	switch ty {
  2162  
  2163  	case TokenOBrace:
  2164  		return TokenCBrace
  2165  	case TokenOBrack:
  2166  		return TokenCBrack
  2167  	case TokenOParen:
  2168  		return TokenCParen
  2169  	case TokenOQuote:
  2170  		return TokenCQuote
  2171  	case TokenOHeredoc:
  2172  		return TokenCHeredoc
  2173  
  2174  	case TokenCBrace:
  2175  		return TokenOBrace
  2176  	case TokenCBrack:
  2177  		return TokenOBrack
  2178  	case TokenCParen:
  2179  		return TokenOParen
  2180  	case TokenCQuote:
  2181  		return TokenOQuote
  2182  	case TokenCHeredoc:
  2183  		return TokenOHeredoc
  2184  
  2185  	case TokenTemplateControl:
  2186  		return TokenTemplateSeqEnd
  2187  	case TokenTemplateInterp:
  2188  		return TokenTemplateSeqEnd
  2189  	case TokenTemplateSeqEnd:
  2190  		// This is ambigous, but we return Interp here because that's
  2191  		// what's assumed by the "recover" method.
  2192  		return TokenTemplateInterp
  2193  
  2194  	default:
  2195  		return TokenNil
  2196  	}
  2197  }
  2198  
  2199  func errPlaceholderExpr(rng hcl.Range) Expression {
  2200  	return &LiteralValueExpr{
  2201  		Val:      cty.DynamicVal,
  2202  		SrcRange: rng,
  2203  	}
  2204  }