diff --git a/staging/hcl/doc.go b/staging/hcl/doc.go index 665ad6c..acab03b 100644 --- a/staging/hcl/doc.go +++ b/staging/hcl/doc.go @@ -1,6 +1,12 @@ +// Copyright (c) 2024 Seal, Inc. // Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: MPL-2.0 +// Package hcl borrows from the https://github.com/hashicorp/hcl/tree/v2.19.1. +// +// This package adds some modelling types and general utility functions, +// it is not exactly the same with the original implementation but works well for us. + // Package hcl contains the main modelling types and general utility functions // for HCL. // diff --git a/staging/hcl/hclsyntax/parser.go b/staging/hcl/hclsyntax/parser.go index aa147af..a1c95ee 100644 --- a/staging/hcl/hclsyntax/parser.go +++ b/staging/hcl/hclsyntax/parser.go @@ -10,8 +10,9 @@ import ( "unicode/utf8" "github.com/apparentlymart/go-textseg/v15/textseg" - "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/hcl/v2" ) type parser struct { @@ -244,6 +245,7 @@ func (p *parser) finishParsingBodyAttribute(ident Token, singleLine bool) (Node, panic("finishParsingBodyAttribute called with next not equals") } + var startIndex = p.NextIndex var endRange hcl.Range expr, diags := p.ParseExpression() @@ -291,6 +293,7 @@ func (p *parser) finishParsingBodyAttribute(ident Token, singleLine bool) (Node, SrcRange: hcl.RangeBetween(ident.Range, endRange), NameRange: ident.Range, EqualsRange: eqTok.Range, + Tokens: p.Tokens[startIndex:p.NextIndex], }, diags } @@ -300,6 +303,7 @@ func (p *parser) finishParsingBodyBlock(ident Token) (Node, hcl.Diagnostics) { var labels []string var labelRanges []hcl.Range + var startIndex int var oBrace Token Token: @@ -309,6 +313,7 @@ Token: switch tok.Type { case TokenOBrace: + startIndex = p.NextIndex oBrace = p.Read() break Token @@ -371,6 +376,7 @@ Token: LabelRanges: labelRanges, OpenBraceRange: ident.Range, // placeholder CloseBraceRange: ident.Range, // placeholder + Tokens: p.Tokens[startIndex:p.NextIndex], }, diags } } @@ -472,6 +478,7 @@ Token: LabelRanges: labelRanges, OpenBraceRange: oBrace.Range, CloseBraceRange: cBraceRange, + Tokens: p.Tokens[startIndex:p.NextIndex], }, diags } diff --git a/staging/hcl/hclsyntax/structure.go b/staging/hcl/hclsyntax/structure.go index ff27263..46300e4 100644 --- a/staging/hcl/hclsyntax/structure.go +++ b/staging/hcl/hclsyntax/structure.go @@ -24,6 +24,7 @@ func (b *Block) AsHCLBlock() *hcl.Block { DefRange: b.DefRange(), TypeRange: b.TypeRange, LabelRanges: b.LabelRanges, + Tokens: b.Tokens.AsHCLTokens(), } } @@ -320,6 +321,7 @@ type Attribute struct { SrcRange hcl.Range NameRange hcl.Range EqualsRange hcl.Range + Tokens Tokens } func (a *Attribute) walkChildNodes(w internalWalkFunc) { @@ -341,6 +343,7 @@ func (a *Attribute) AsHCLAttribute() *hcl.Attribute { Range: a.SrcRange, NameRange: a.NameRange, + Tokens: a.Tokens.AsHCLTokens(), } } @@ -377,6 +380,7 @@ type Block struct { LabelRanges []hcl.Range OpenBraceRange hcl.Range CloseBraceRange hcl.Range + Tokens Tokens } func (b *Block) walkChildNodes(w internalWalkFunc) { diff --git a/staging/hcl/hclsyntax/token_ext.go b/staging/hcl/hclsyntax/token_ext.go new file mode 100644 index 0000000..522a153 --- /dev/null +++ b/staging/hcl/hclsyntax/token_ext.go @@ -0,0 +1,18 @@ +package hclsyntax + +import "github.com/hashicorp/hcl/v2" + +func (t Token) AsHCLToken() hcl.Token { + return hcl.Token{ + Type: rune(t.Type), + Bytes: t.Bytes, + } +} + +func (ts Tokens) AsHCLTokens() hcl.Tokens { + ret := make(hcl.Tokens, len(ts)) + for i, t := range ts { + ret[i] = t.AsHCLToken() + } + return ret +} diff --git a/staging/hcl/hclwrite/ast_body_ext.go b/staging/hcl/hclwrite/ast_body_ext.go new file mode 100644 index 0000000..7da5231 --- /dev/null +++ b/staging/hcl/hclwrite/ast_body_ext.go @@ -0,0 +1,46 @@ +package hclwrite + +import ( + "sort" + + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" +) + +// AppendHCLBody appends an existing hcl.Body (which must not be already attached +// to a body) to the end of the receiving body, +// +// If the given hcl.Body is not *hclsyntax.Body, this method is a no-op. +func (b *Body) AppendHCLBody(body hcl.Body) { + if body == nil { + return + } + + bd, ok := body.(*hclsyntax.Body) + if !ok { + return + } + + attrs := make([]*hclsyntax.Attribute, 0, len(bd.Attributes)) + { + for k := range bd.Attributes { + attrs = append(attrs, bd.Attributes[k]) + } + + sort.Slice(attrs, func(i, j int) bool { + if attrs[i].SrcRange.Filename == attrs[j].SrcRange.Filename { + return attrs[i].SrcRange.Start.Line < attrs[j].SrcRange.Start.Line + } + + return attrs[i].SrcRange.Filename < attrs[j].SrcRange.Filename + }) + } + for i := range attrs { + b.SetAttributeRaw(attrs[i].Name, TokensForExpression(attrs[i].Expr)) + } + + for i := range bd.Blocks { + wb := b.AppendNewBlock(bd.Blocks[i].Type, bd.Blocks[i].Labels).Body() + wb.AppendHCLBody(bd.Blocks[i].Body) + } +} diff --git a/staging/hcl/hclwrite/generate_expr.go b/staging/hcl/hclwrite/generate_expr.go new file mode 100644 index 0000000..a058ca2 --- /dev/null +++ b/staging/hcl/hclwrite/generate_expr.go @@ -0,0 +1,611 @@ +package hclwrite + +import ( + "github.com/zclconf/go-cty/cty" + + "github.com/hashicorp/hcl/v2/hclsyntax" +) + +func TokensForExpression(expr hclsyntax.Expression) Tokens { + tks := tokensForExpression(expr, false) + + ret := make(Tokens, len(tks)) + for i := range tks { + ret[i] = &Token{ + Type: tks[i].Type, + Bytes: tks[i].Bytes, + } + } + return ret +} + +func tokensForExpression(expr hclsyntax.Expression, quoted bool) (tks hclsyntax.Tokens) { + switch e := expr.(type) { + case *hclsyntax.AnonSymbolExpr: + return TokensForAnonSymbolExpr(e) + case *hclsyntax.BinaryOpExpr: + return TokensForBinaryOpExpr(e) + case *hclsyntax.ConditionalExpr: + return TokensForConditionalExpr(e) + case *hclsyntax.ForExpr: + return TokensForForExpr(e) + case *hclsyntax.FunctionCallExpr: + return TokensForFunctionCallExpr(e) + case *hclsyntax.IndexExpr: + return TokensForIndexExpr(e) + case *hclsyntax.LiteralValueExpr: + return TokensForLiteralValueExpr(e, quoted) + case *hclsyntax.ObjectConsExpr: + return TokensForObjectConsExpr(e) + case *hclsyntax.ObjectConsKeyExpr: + return TokensForObjectConsKeyExpr(e) + case *hclsyntax.RelativeTraversalExpr: + return TokensForRelativeTraversalExpr(e) + case *hclsyntax.ScopeTraversalExpr: + return TokensForScopeTraversalExpr(e) + case *hclsyntax.SplatExpr: + return TokensForSplatExpr(e) + case *hclsyntax.TemplateExpr: + return TokensForTemplateExpr(e, quoted) + case *hclsyntax.TemplateJoinExpr: + return TokensForTemplateJoinExpr(e) + case *hclsyntax.TemplateWrapExpr: + return TokensForTemplateWrapExpr(e) + case *hclsyntax.TupleConsExpr: + return TokensForTupleConsExpr(e) + case *hclsyntax.UnaryOpExpr: + return TokensForUnaryOpExpr(e) + case *hclsyntax.ParenthesesExpr: + return tokensForExpression(e.Expression, quoted) + } + + return +} + +func TokensForAnonSymbolExpr(e *hclsyntax.AnonSymbolExpr) (tks hclsyntax.Tokens) { + return +} + +func TokensForBinaryOpExpr(e *hclsyntax.BinaryOpExpr) (tks hclsyntax.Tokens) { + tks = append(tks, tokensForExpression(e.LHS, false)...) + + switch e.Op { + case hclsyntax.OpLogicalOr: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOr, + Bytes: []byte("||"), + }) + case hclsyntax.OpLogicalAnd: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenAnd, + Bytes: []byte("&&"), + }) + case hclsyntax.OpEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenEqualOp, + Bytes: []byte("=="), + }) + case hclsyntax.OpNotEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenNotEqual, + Bytes: []byte("!="), + }) + case hclsyntax.OpGreaterThan: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenGreaterThan, + Bytes: []byte{'>'}, + }) + case hclsyntax.OpGreaterThanOrEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenGreaterThanEq, + Bytes: []byte(">="), + }) + case hclsyntax.OpLessThan: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenLessThan, + Bytes: []byte{'<'}, + }) + case hclsyntax.OpLessThanOrEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenLessThanEq, + Bytes: []byte("<="), + }) + case hclsyntax.OpAdd: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenPlus, + Bytes: []byte{'+'}, + }) + case hclsyntax.OpSubtract: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenMinus, + Bytes: []byte{'-'}, + }) + case hclsyntax.OpMultiply: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenStar, + Bytes: []byte{'*'}, + }) + case hclsyntax.OpDivide: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenSlash, + Bytes: []byte{'/'}, + }) + case hclsyntax.OpModulo: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenPercent, + Bytes: []byte{'%'}, + }) + } + + tks = append(tks, tokensForExpression(e.RHS, false)...) + + return +} + +func TokensForConditionalExpr(e *hclsyntax.ConditionalExpr) (tks hclsyntax.Tokens) { + tks = append(tks, tokensForExpression(e.Condition, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenQuestion, + Bytes: []byte{'?'}, + }) + + tks = append(tks, tokensForExpression(e.TrueResult, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenColon, + Bytes: []byte{':'}, + }) + + tks = append(tks, tokensForExpression(e.FalseResult, false)...) + + return +} + +func TokensForForExpr(e *hclsyntax.ForExpr) (tks hclsyntax.Tokens) { + if e.KeyExpr == nil { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOBrack, + Bytes: []byte{'['}, + }) + } else { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOBrace, + Bytes: []byte{'{'}, + }) + } + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenNewline, + Bytes: []byte{'\n'}, + }) + + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte("for"), + }, + hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte(e.KeyVar), + }) + + if e.ValVar != "" { + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenComma, + Bytes: []byte{','}, + }, + hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte(e.ValVar), + }) + } + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte("in"), + }) + + tks = append(tks, tokensForExpression(e.CollExpr, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenColon, + Bytes: []byte{':'}, + }) + + if e.KeyExpr != nil { + tks = append(tks, tokensForExpression(e.KeyExpr, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenFatArrow, + Bytes: []byte("=>"), + }) + } + + tks = append(tks, tokensForExpression(e.ValExpr, false)...) + + if e.Group { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenEllipsis, + Bytes: []byte("..."), + }) + } + + if e.CondExpr != nil { + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenNewline, + Bytes: []byte{'\n'}, + }, + hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte("if"), + }) + + tks = append(tks, tokensForExpression(e.CondExpr, false)...) + } + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenNewline, + Bytes: []byte{'\n'}, + }) + + if e.KeyExpr == nil { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCBrack, + Bytes: []byte{']'}, + }) + } else { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCBrace, + Bytes: []byte{'}'}, + }) + } + + return +} + +func TokensForFunctionCallExpr(e *hclsyntax.FunctionCallExpr) (tks hclsyntax.Tokens) { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte(e.Name), + }) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOParen, + Bytes: []byte{'('}, + }) + + for i, arg := range e.Args { + if i > 0 { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenComma, + Bytes: []byte{','}, + }) + } + + tks = append(tks, tokensForExpression(arg, false)...) + + if e.ExpandFinal && i == len(e.Args)-1 { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenEllipsis, + Bytes: []byte("..."), + }) + } + } + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCParen, + Bytes: []byte{')'}, + }) + + return +} + +func TokensForIndexExpr(e *hclsyntax.IndexExpr) (tks hclsyntax.Tokens) { + tks = append(tks, tokensForExpression(e.Collection, true)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOBrack, + Bytes: []byte{'['}, + }) + + tks = append(tks, tokensForExpression(e.Key, false)...) + + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenCBrack, + Bytes: []byte{']'}, + }) + + return +} + +func TokensForLiteralValueExpr(e *hclsyntax.LiteralValueExpr, quoted bool) (tks hclsyntax.Tokens) { + ss := TokensForValue(e.Val) + + for i, s := range ss { + if quoted && e.Val.Type() == cty.String && (i == 0 || i == len(ss)-1) { + continue + } + + tks = append(tks, hclsyntax.Token{ + Type: s.Type, + Bytes: s.Bytes, + }) + } + + return +} + +func TokensForObjectConsExpr(e *hclsyntax.ObjectConsExpr) (tks hclsyntax.Tokens) { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOBrace, + Bytes: []byte{'{'}, + }) + + for i := range e.Items { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenNewline, + Bytes: []byte{'\n'}, + }) + + tks = append(tks, tokensForExpression(e.Items[i].KeyExpr, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenEqual, + Bytes: []byte{'='}, + }) + + tks = append(tks, tokensForExpression(e.Items[i].ValueExpr, false)...) + } + + if len(e.Items) > 0 { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenNewline, + Bytes: []byte{'\n'}, + }) + } + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCBrace, + Bytes: []byte{'}'}, + }) + + return +} + +func TokensForObjectConsKeyExpr(e *hclsyntax.ObjectConsKeyExpr) (tks hclsyntax.Tokens) { + return tokensForExpression(e.Wrapped, false) +} + +func TokensForRelativeTraversalExpr(e *hclsyntax.RelativeTraversalExpr) (tks hclsyntax.Tokens) { + tks = append(tks, tokensForExpression(e.Source, true)...) + + for _, s := range TokensForTraversal(e.Traversal) { + tks = append(tks, hclsyntax.Token{ + Type: s.Type, + Bytes: s.Bytes, + }) + } + + return +} + +func TokensForScopeTraversalExpr(e *hclsyntax.ScopeTraversalExpr) (tks hclsyntax.Tokens) { + for _, s := range TokensForTraversal(e.Traversal) { + tks = append(tks, hclsyntax.Token{ + Type: s.Type, + Bytes: s.Bytes, + }) + } + + return +} + +func TokensForSplatExpr(e *hclsyntax.SplatExpr) (tks hclsyntax.Tokens) { + tks = append(tks, tokensForExpression(e.Source, true)...) + + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenOBrack, + Bytes: []byte{'['}, + }, + hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte{'*'}, + }, + hclsyntax.Token{ + Type: hclsyntax.TokenCBrack, + Bytes: []byte{']'}, + }) + + return +} + +func TokensForTemplateExpr(e *hclsyntax.TemplateExpr, quoted bool) (tks hclsyntax.Tokens) { + if !quoted { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOQuote, + Bytes: []byte{'"'}, + }) + } + + for i := range e.Parts { + switch t := e.Parts[i].(type) { + case *hclsyntax.LiteralValueExpr: + tks = append(tks, TokensForLiteralValueExpr(t, true)...) + case *hclsyntax.ScopeTraversalExpr: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenTemplateInterp, + Bytes: []byte("${"), + }) + tks = append(tks, TokensForScopeTraversalExpr(t)...) + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenTemplateSeqEnd, + Bytes: []byte{'}'}, + }) + case *hclsyntax.ConditionalExpr: + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenTemplateControl, + Bytes: []byte("%{"), + }, + hclsyntax.Token{ + Type: hclsyntax.TokenIdent, + Bytes: []byte("if"), + }) + tks = append(tks, tokensForExpression(t.Condition, true)...) + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenTemplateSeqEnd, + Bytes: []byte{'}'}, + }) + tks = append(tks, tokensForExpression(t.TrueResult, true)...) + + if t.FalseResult != nil { + ss := tokensForExpression(t.FalseResult, true) + if len(ss) != 0 { + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenTemplateControl, + Bytes: []byte("%{"), + }, + hclsyntax.Token{ + Type: hclsyntax.TokenStringLit, + Bytes: []byte("else"), + }, + hclsyntax.Token{ + Type: hclsyntax.TokenTemplateSeqEnd, + Bytes: []byte{'}'}, + }) + tks = append(tks, ss...) + } + } + + tks = append(tks, + hclsyntax.Token{ + Type: hclsyntax.TokenTemplateControl, + Bytes: []byte("%{"), + }, + hclsyntax.Token{ + Type: hclsyntax.TokenStringLit, + Bytes: []byte("endif"), + }, + hclsyntax.Token{ + Type: hclsyntax.TokenTemplateSeqEnd, + Bytes: []byte{'}'}, + }) + } + } + + if !quoted { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCQuote, + Bytes: []byte{'"'}, + }) + } + + return +} + +func TokensForTemplateJoinExpr(e *hclsyntax.TemplateJoinExpr) (tks hclsyntax.Tokens) { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOBrack, + Bytes: []byte("["), + }) + + tks = append(tks, tokensForExpression(e.Tuple, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCBrack, + Bytes: []byte("]"), + }) + + return +} + +func TokensForTemplateWrapExpr(e *hclsyntax.TemplateWrapExpr) (tks hclsyntax.Tokens) { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenTemplateInterp, + Bytes: []byte("${"), + }) + + tks = append(tks, tokensForExpression(e.Wrapped, false)...) + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenTemplateSeqEnd, + Bytes: []byte("}"), + }) + + return +} + +func TokensForTupleConsExpr(e *hclsyntax.TupleConsExpr) (tks hclsyntax.Tokens) { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenOBrack, + Bytes: []byte{'['}, + }) + + for i := range e.Exprs { + if i > 0 { + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenComma, + Bytes: []byte{','}, + }) + } + + tks = append(tks, tokensForExpression(e.Exprs[i], false)...) + } + + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenCBrack, + Bytes: []byte{']'}, + }) + + return +} + +func TokensForUnaryOpExpr(e *hclsyntax.UnaryOpExpr) (tks hclsyntax.Tokens) { + switch e.Op { + case hclsyntax.OpLogicalNot: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenBang, + Bytes: []byte{'!'}, + }) + case hclsyntax.OpEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenEqual, + Bytes: []byte{'='}, + }) + case hclsyntax.OpGreaterThan: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenGreaterThan, + Bytes: []byte{'>'}, + }) + case hclsyntax.OpGreaterThanOrEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenGreaterThanEq, + Bytes: []byte(">="), + }) + case hclsyntax.OpLessThan: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenLessThan, + Bytes: []byte{'<'}, + }) + case hclsyntax.OpLessThanOrEqual: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenLessThanEq, + Bytes: []byte("<="), + }) + case hclsyntax.OpNegate: + tks = append(tks, hclsyntax.Token{ + Type: hclsyntax.TokenMinus, + Bytes: []byte{'-'}, + }) + } + + tks = append(tks, tokensForExpression(e.Val, false)...) + + return +} diff --git a/staging/hcl/pos_ext.go b/staging/hcl/pos_ext.go new file mode 100644 index 0000000..9092cb7 --- /dev/null +++ b/staging/hcl/pos_ext.go @@ -0,0 +1,12 @@ +package hcl + +// Token represents a sequence of bytes from some HCL code that has been +// tagged with a type and its range within the source file. +type ( + Token struct { + Type rune + Bytes []byte + } + + Tokens []Token +) diff --git a/staging/hcl/structure.go b/staging/hcl/structure.go index 79a8160..7b0d05a 100644 --- a/staging/hcl/structure.go +++ b/staging/hcl/structure.go @@ -27,6 +27,7 @@ type Block struct { DefRange Range // Range that can be considered the "definition" for seeking in an editor TypeRange Range // Range for the block type declaration specifically. LabelRanges []Range // Ranges for the label values specifically. + Tokens Tokens } // Blocks is a sequence of Block. @@ -91,6 +92,7 @@ type Attribute struct { Range Range NameRange Range + Tokens Tokens } // Expression is a literal value or an expression provided in the