09f9e6c8e8
Previously we were detecting the exactly-one-part case at parse time and skipping the TemplateExpr node in the AST, but this was problematic because we then misaligned the source range for the expression to the _content_ of the quotes, rather than including the quotes themselves. As well as producing confusing diagnostics, this also caused problems for zclwrite since it relies on source ranges to map our AST back onto the source tokens it came from.
111 lines
4.3 KiB
Go
111 lines
4.3 KiB
Go
package zclsyntax
|
|
|
|
import (
|
|
"github.com/zclconf/go-zcl/zcl"
|
|
)
|
|
|
|
// ParseConfig parses the given buffer as a whole zcl config file, returning
|
|
// a *zcl.File representing its contents. If HasErrors called on the returned
|
|
// diagnostics returns true, the returned body is likely to be incomplete
|
|
// and should therefore be used with care.
|
|
//
|
|
// The body in the returned file has dynamic type *zclsyntax.Body, so callers
|
|
// may freely type-assert this to get access to the full zclsyntax API in
|
|
// situations where detailed access is required. However, most common use-cases
|
|
// should be served using the zcl.Body interface to ensure compatibility with
|
|
// other configurationg syntaxes, such as JSON.
|
|
func ParseConfig(src []byte, filename string, start zcl.Pos) (*zcl.File, zcl.Diagnostics) {
|
|
tokens, diags := LexConfig(src, filename, start)
|
|
peeker := newPeeker(tokens, false)
|
|
parser := &parser{peeker: peeker}
|
|
body, parseDiags := parser.ParseBody(TokenEOF)
|
|
diags = append(diags, parseDiags...)
|
|
return &zcl.File{
|
|
Body: body,
|
|
Bytes: src,
|
|
|
|
Nav: navigation{
|
|
root: body,
|
|
},
|
|
}, diags
|
|
}
|
|
|
|
// ParseExpression parses the given buffer as a standalone zcl expression,
|
|
// returning it as an instance of Expression.
|
|
func ParseExpression(src []byte, filename string, start zcl.Pos) (Expression, zcl.Diagnostics) {
|
|
tokens, diags := LexExpression(src, filename, start)
|
|
peeker := newPeeker(tokens, false)
|
|
parser := &parser{peeker: peeker}
|
|
|
|
// Bare expressions are always parsed in "ignore newlines" mode, as if
|
|
// they were wrapped in parentheses.
|
|
parser.PushIncludeNewlines(false)
|
|
|
|
expr, parseDiags := parser.ParseExpression()
|
|
diags = append(diags, parseDiags...)
|
|
|
|
next := parser.Peek()
|
|
if next.Type != TokenEOF && !parser.recovery {
|
|
diags = append(diags, &zcl.Diagnostic{
|
|
Severity: zcl.DiagError,
|
|
Summary: "Extra characters after expression",
|
|
Detail: "An expression was successfully parsed, but extra characters were found after it.",
|
|
Subject: &next.Range,
|
|
})
|
|
}
|
|
|
|
return expr, diags
|
|
}
|
|
|
|
// ParseTemplate parses the given buffer as a standalone zcl template,
|
|
// returning it as an instance of Expression.
|
|
func ParseTemplate(src []byte, filename string, start zcl.Pos) (Expression, zcl.Diagnostics) {
|
|
tokens, diags := LexTemplate(src, filename, start)
|
|
peeker := newPeeker(tokens, false)
|
|
parser := &parser{peeker: peeker}
|
|
expr, parseDiags := parser.ParseTemplate()
|
|
diags = append(diags, parseDiags...)
|
|
return expr, diags
|
|
}
|
|
|
|
// LexConfig performs lexical analysis on the given buffer, treating it as a
|
|
// whole zcl config file, and returns the resulting tokens.
|
|
//
|
|
// Only minimal validation is done during lexical analysis, so the returned
|
|
// diagnostics may include errors about lexical issues such as bad character
|
|
// encodings or unrecognized characters, but full parsing is required to
|
|
// detect _all_ syntax errors.
|
|
func LexConfig(src []byte, filename string, start zcl.Pos) (Tokens, zcl.Diagnostics) {
|
|
tokens := scanTokens(src, filename, start, scanNormal)
|
|
diags := checkInvalidTokens(tokens)
|
|
return tokens, diags
|
|
}
|
|
|
|
// LexExpression performs lexical analysis on the given buffer, treating it as
|
|
// a standalone zcl expression, and returns the resulting tokens.
|
|
//
|
|
// Only minimal validation is done during lexical analysis, so the returned
|
|
// diagnostics may include errors about lexical issues such as bad character
|
|
// encodings or unrecognized characters, but full parsing is required to
|
|
// detect _all_ syntax errors.
|
|
func LexExpression(src []byte, filename string, start zcl.Pos) (Tokens, zcl.Diagnostics) {
|
|
// This is actually just the same thing as LexConfig, since configs
|
|
// and expressions lex in the same way.
|
|
tokens := scanTokens(src, filename, start, scanNormal)
|
|
diags := checkInvalidTokens(tokens)
|
|
return tokens, diags
|
|
}
|
|
|
|
// LexTemplate performs lexical analysis on the given buffer, treating it as a
|
|
// standalone zcl template, and returns the resulting tokens.
|
|
//
|
|
// Only minimal validation is done during lexical analysis, so the returned
|
|
// diagnostics may include errors about lexical issues such as bad character
|
|
// encodings or unrecognized characters, but full parsing is required to
|
|
// detect _all_ syntax errors.
|
|
func LexTemplate(src []byte, filename string, start zcl.Pos) (Tokens, zcl.Diagnostics) {
|
|
tokens := scanTokens(src, filename, start, scanTemplate)
|
|
diags := checkInvalidTokens(tokens)
|
|
return tokens, diags
|
|
}
|