github.com/hashicorp/hcl/v2@v2.20.0/hclsyntax/parser_traversal.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hclsyntax 5 6 import ( 7 "github.com/hashicorp/hcl/v2" 8 "github.com/zclconf/go-cty/cty" 9 ) 10 11 // ParseTraversalAbs parses an absolute traversal that is assumed to consume 12 // all of the remaining tokens in the peeker. The usual parser recovery 13 // behavior is not supported here because traversals are not expected to 14 // be parsed as part of a larger program. 15 func (p *parser) ParseTraversalAbs() (hcl.Traversal, hcl.Diagnostics) { 16 var ret hcl.Traversal 17 var diags hcl.Diagnostics 18 19 // Absolute traversal must always begin with a variable name 20 varTok := p.Read() 21 if varTok.Type != TokenIdent { 22 diags = append(diags, &hcl.Diagnostic{ 23 Severity: hcl.DiagError, 24 Summary: "Variable name required", 25 Detail: "Must begin with a variable name.", 26 Subject: &varTok.Range, 27 }) 28 return ret, diags 29 } 30 31 varName := string(varTok.Bytes) 32 ret = append(ret, hcl.TraverseRoot{ 33 Name: varName, 34 SrcRange: varTok.Range, 35 }) 36 37 for { 38 next := p.Peek() 39 40 if next.Type == TokenEOF { 41 return ret, diags 42 } 43 44 switch next.Type { 45 case TokenDot: 46 // Attribute access 47 dot := p.Read() // eat dot 48 nameTok := p.Read() 49 if nameTok.Type != TokenIdent { 50 if nameTok.Type == TokenStar { 51 diags = append(diags, &hcl.Diagnostic{ 52 Severity: hcl.DiagError, 53 Summary: "Attribute name required", 54 Detail: "Splat expressions (.*) may not be used here.", 55 Subject: &nameTok.Range, 56 Context: hcl.RangeBetween(varTok.Range, nameTok.Range).Ptr(), 57 }) 58 } else { 59 diags = append(diags, &hcl.Diagnostic{ 60 Severity: hcl.DiagError, 61 Summary: "Attribute name required", 62 Detail: "Dot must be followed by attribute name.", 63 Subject: &nameTok.Range, 64 Context: hcl.RangeBetween(varTok.Range, nameTok.Range).Ptr(), 65 }) 66 } 67 return ret, diags 68 } 69 70 attrName := string(nameTok.Bytes) 71 ret = append(ret, hcl.TraverseAttr{ 72 Name: attrName, 73 SrcRange: hcl.RangeBetween(dot.Range, nameTok.Range), 74 }) 75 case TokenOBrack: 76 // Index 77 open := p.Read() // eat open bracket 78 next := p.Peek() 79 80 switch next.Type { 81 case TokenNumberLit: 82 tok := p.Read() // eat number 83 numVal, numDiags := p.numberLitValue(tok) 84 diags = append(diags, numDiags...) 85 86 close := p.Read() 87 if close.Type != TokenCBrack { 88 diags = append(diags, &hcl.Diagnostic{ 89 Severity: hcl.DiagError, 90 Summary: "Unclosed index brackets", 91 Detail: "Index key must be followed by a closing bracket.", 92 Subject: &close.Range, 93 Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), 94 }) 95 } 96 97 ret = append(ret, hcl.TraverseIndex{ 98 Key: numVal, 99 SrcRange: hcl.RangeBetween(open.Range, close.Range), 100 }) 101 102 if diags.HasErrors() { 103 return ret, diags 104 } 105 106 case TokenOQuote: 107 str, _, strDiags := p.parseQuotedStringLiteral() 108 diags = append(diags, strDiags...) 109 110 close := p.Read() 111 if close.Type != TokenCBrack { 112 diags = append(diags, &hcl.Diagnostic{ 113 Severity: hcl.DiagError, 114 Summary: "Unclosed index brackets", 115 Detail: "Index key must be followed by a closing bracket.", 116 Subject: &close.Range, 117 Context: hcl.RangeBetween(open.Range, close.Range).Ptr(), 118 }) 119 } 120 121 ret = append(ret, hcl.TraverseIndex{ 122 Key: cty.StringVal(str), 123 SrcRange: hcl.RangeBetween(open.Range, close.Range), 124 }) 125 126 if diags.HasErrors() { 127 return ret, diags 128 } 129 130 default: 131 if next.Type == TokenStar { 132 diags = append(diags, &hcl.Diagnostic{ 133 Severity: hcl.DiagError, 134 Summary: "Attribute name required", 135 Detail: "Splat expressions ([*]) may not be used here.", 136 Subject: &next.Range, 137 Context: hcl.RangeBetween(varTok.Range, next.Range).Ptr(), 138 }) 139 } else { 140 diags = append(diags, &hcl.Diagnostic{ 141 Severity: hcl.DiagError, 142 Summary: "Index value required", 143 Detail: "Index brackets must contain either a literal number or a literal string.", 144 Subject: &next.Range, 145 Context: hcl.RangeBetween(varTok.Range, next.Range).Ptr(), 146 }) 147 } 148 return ret, diags 149 } 150 151 default: 152 diags = append(diags, &hcl.Diagnostic{ 153 Severity: hcl.DiagError, 154 Summary: "Invalid character", 155 Detail: "Expected an attribute access or an index operator.", 156 Subject: &next.Range, 157 Context: hcl.RangeBetween(varTok.Range, next.Range).Ptr(), 158 }) 159 return ret, diags 160 } 161 } 162 }