pulumi/pkg/codegen/hcl2/syntax/parser.go
Pat Gavlin a597934b3c
Updates to the HCL2 syntax helpers. (#4282)
- Fix token mapping for template control structures
- Split tokens out into their own file
- Add factory methods for token types
2020-04-02 20:01:14 -07:00

95 lines
3.3 KiB
Go

// Copyright 2016-2020, Pulumi Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package syntax
import (
"io"
"io/ioutil"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
)
// File represents a single parsed HCL2 source file.
type File struct {
Name string // The name of the file.
Body *hclsyntax.Body // The body of the parsed file.
Bytes []byte // The raw bytes of the source file.
Tokens TokenMap // A map from syntax nodes to token information.
}
// Parser is a parser for HCL2 source files.
type Parser struct {
Files []*File // The parsed files.
Diagnostics hcl.Diagnostics // The diagnostics, if any, produced during parsing.
tokens tokenMap // A map from syntax nodes to token information.
}
// NewParser creates a new HCL2 parser.
func NewParser() *Parser {
return &Parser{tokens: tokenMap{}}
}
// ParseFile attempts to parse the contents of the given io.Reader as HCL2. If parsing fails, any diagnostics generated
// will be added to the parser's diagnostics.
func (p *Parser) ParseFile(r io.Reader, filename string) error {
src, err := ioutil.ReadAll(r)
if err != nil {
return err
}
hclFile, diags := hclsyntax.ParseConfig(src, filename, hcl.Pos{})
if !diags.HasErrors() {
tokens, _ := hclsyntax.LexConfig(src, filename, hcl.Pos{})
mapTokens(tokens, filename, hclFile.Body.(*hclsyntax.Body), hclFile.Bytes, p.tokens, hcl.Pos{})
}
p.Files = append(p.Files, &File{
Name: filename,
Body: hclFile.Body.(*hclsyntax.Body),
Bytes: hclFile.Bytes,
Tokens: p.tokens,
})
p.Diagnostics = append(p.Diagnostics, diags...)
return nil
}
// NewDiagnosticWriter creates a new diagnostic writer for the files parsed by the parser.
func (p *Parser) NewDiagnosticWriter(w io.Writer, width uint, color bool) hcl.DiagnosticWriter {
return NewDiagnosticWriter(w, p.Files, width, color)
}
// NewDiagnosticWriter creates a new diagnostic writer for the given list of HCL2 files.
func NewDiagnosticWriter(w io.Writer, files []*File, width uint, color bool) hcl.DiagnosticWriter {
fileMap := map[string]*hcl.File{}
for _, f := range files {
fileMap[f.Name] = &hcl.File{Body: f.Body, Bytes: f.Bytes}
}
return hcl.NewDiagnosticTextWriter(w, fileMap, width, color)
}
// ParseExpression attempts to parse the given string as an HCL2 expression.
func ParseExpression(expression, filename string, start hcl.Pos) (hclsyntax.Expression, TokenMap, hcl.Diagnostics) {
source := []byte(expression)
hclExpression, diagnostics := hclsyntax.ParseExpression(source, filename, start)
if diagnostics.HasErrors() {
return nil, nil, diagnostics
}
tokens := tokenMap{}
hclTokens, _ := hclsyntax.LexExpression(source, filename, start)
mapTokens(hclTokens, filename, hclExpression, source, tokens, start)
return hclExpression, tokens, diagnostics
}