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