in internal/parser/hcl_node.go [99:368]
func BuildHclNode(tokens hclsyntax.Tokens) *HclNode {
stack := make([]State, 0)
stack = append(stack, State{
CurrentHclNode: &HclNode{
KeyValueFormat: KeyEqualValue,
Children: make(map[string]*HclNode),
},
Key: utils.String("dummy"),
Index: nil,
Value: nil,
ExpectKey: false,
ExpectEqual: false,
})
for _, token := range tokens {
switch token.Type {
case hclsyntax.TokenOBrace: // {
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
key := state.GetCurrentKey()
if state.ExpectKey {
log.Printf("[WARN] expect key but got {")
}
hclNode := &HclNode{
Key: key,
KeyRange: state.KeyRange,
ValueRange: token.Range,
EqualRange: state.EqualRange,
Children: make(map[string]*HclNode),
KeyValueFormat: stack[top].CurrentHclNode.KeyValueFormat,
}
stack[top].CurrentHclNode.Children[key] = hclNode
if stack[top].Index == nil {
stack[top].ExpectKey = true
stack[top].ExpectEqual = true
} else {
// if there's an index, then expect value because p = [{}]
stack[top].ExpectKey = false
stack[top].ExpectEqual = false
// the hclNode is an object in an array, so use the range of "{" as the key range
hclNode.KeyRange = token.Range
}
stack = append(stack, State{
ExpectKey: true,
ExpectEqual: true,
CurrentHclNode: hclNode,
})
case hclsyntax.TokenCBrace: // }
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
if !state.ExpectKey {
log.Printf("[WARN] expect value but got }")
key := state.GetCurrentKey()
stack[top].CurrentHclNode.Children[key] = &HclNode{
Key: key,
Value: state.Value,
KeyRange: state.KeyRange,
ValueRange: state.ValueRange,
EqualRange: state.EqualRange,
}
}
stack[top].CurrentHclNode.ValueRange = RangeOver(stack[top].CurrentHclNode.ValueRange, token.Range)
stack = stack[0:top]
case hclsyntax.TokenOBrack: // [
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
if state.ExpectKey {
log.Printf("[WARN] expect key but got [")
}
if state.Value != nil && *state.Value != "" {
updateStateValue(&stack[top], token)
break
}
key := state.GetCurrentKey()
hclNode := &HclNode{
Key: key,
KeyRange: state.KeyRange,
EqualRange: state.EqualRange,
ValueRange: token.Range,
KeyValueFormat: state.CurrentHclNode.KeyValueFormat,
Children: make(map[string]*HclNode),
}
stack[top].CurrentHclNode.Children[key] = hclNode
stack[top].ExpectKey = true
stack[top].ExpectEqual = stack[top].ExpectKey
stack = append(stack, State{
ExpectKey: false,
ExpectEqual: false,
Key: state.Key,
Index: utils.Int(0),
CurrentHclNode: hclNode,
})
case hclsyntax.TokenCBrack: // ]
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
if state.Value != nil && *state.Value != "" && strings.Contains(*state.Value, "[") {
updateStateValue(&stack[top], token)
break
}
if _, ok := state.CurrentHclNode.Children[state.GetCurrentKey()]; !ok {
log.Printf("[WARN] expect value but got }")
// avoid adding an empty element
if !state.ValueRange.Empty() {
key := state.GetCurrentKey()
stack[top].CurrentHclNode.Children[key] = &HclNode{
Key: key,
Value: state.Value,
ValueRange: state.ValueRange,
}
}
}
stack[top].CurrentHclNode.ValueRange = RangeOver(stack[top].CurrentHclNode.ValueRange, token.Range)
stack = stack[0:top]
case hclsyntax.TokenQuotedLit:
top := len(stack) - 1
if top < 0 {
return nil
}
if stack[top].ExpectKey {
foundKey(&stack[top], token)
stack[top].CurrentHclNode.KeyValueFormat = QuotedKeyEqualValue
} else {
updateStateValue(&stack[top], token)
}
case hclsyntax.TokenIdent:
top := len(stack) - 1
if top < 0 {
return nil
}
if stack[top].ExpectKey {
foundKey(&stack[top], token)
} else {
updateStateValue(&stack[top], token)
}
case hclsyntax.TokenColon: // :
top := len(stack) - 1
if top < 0 {
return nil
}
if stack[top].ExpectEqual {
if stack[top].ExpectKey {
log.Printf("[WARN] expect key but got =")
stack[top].ExpectKey = false
}
stack[top].CurrentHclNode.KeyValueFormat = QuotedKeyColonValue
stack[top].EqualRange = token.Range
stack[top].ExpectEqual = false
} else {
updateStateValue(&stack[top], token)
}
case hclsyntax.TokenEqual: // =
top := len(stack) - 1
if top < 0 {
return nil
}
if stack[top].ExpectEqual {
if stack[top].ExpectKey {
log.Printf("[WARN] expect key but got =")
stack[top].ExpectKey = false
}
stack[top].EqualRange = token.Range
stack[top].ExpectEqual = false
} else {
updateStateValue(&stack[top], token)
}
case hclsyntax.TokenNewline:
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
if !state.ExpectKey {
if stack[top].Index == nil {
key := state.GetCurrentKey()
stack[top].CurrentHclNode.Children[key] = &HclNode{
Key: key,
Value: state.Value,
KeyRange: state.KeyRange,
ValueRange: state.ValueRange,
EqualRange: state.EqualRange,
}
stack[top].Key = nil
stack[top].Value = nil
stack[top].ExpectKey = true
stack[top].ExpectEqual = stack[top].ExpectKey
}
}
case hclsyntax.TokenComma:
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
if !state.ExpectKey {
if state.Index == nil {
log.Printf("[WARN] unexpected symbol: ,")
break
}
if state.Value != nil {
key := state.GetCurrentKey()
stack[top].CurrentHclNode.Children[key] = &HclNode{
Key: key,
Value: state.Value,
ValueRange: state.ValueRange,
}
}
*stack[top].Index++
stack[top].Value = nil
stack[top].ValueRange = hcl.Range{}
} else {
log.Printf("[WARN] expect key but got ,")
}
case hclsyntax.TokenComment:
comment := string(token.Bytes)
if strings.HasSuffix(comment, "\n") {
top := len(stack) - 1
if top < 0 {
return nil
}
state := stack[top]
if !state.ExpectKey {
if stack[top].Index == nil {
key := state.GetCurrentKey()
stack[top].CurrentHclNode.Children[key] = &HclNode{
Key: key,
Value: state.Value,
KeyRange: state.KeyRange,
ValueRange: state.ValueRange,
EqualRange: state.EqualRange,
}
stack[top].Key = nil
stack[top].Value = nil
stack[top].ExpectKey = true
stack[top].ExpectEqual = stack[top].ExpectKey
}
}
}
default:
top := len(stack) - 1
if top < 0 {
return nil
}
if !stack[top].ExpectEqual {
updateStateValue(&stack[top], token)
}
}
}
if len(stack) == 0 {
return nil
}
root := stack[0].CurrentHclNode
updateValueRange(root)
fixEmptyValueRange(root)
return root
}