json: Add json.ParseWithStartPos function

This is like json.Parse but allows specifying a non-default start position,
in case the caller is parsing a fragment from a larger JSON document.
This commit is contained in:
Kazuma Watanabe 2020-08-25 02:53:10 +09:00 committed by GitHub
parent b41530cb36
commit e72341df8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 125 additions and 11 deletions

View File

@ -8,15 +8,8 @@ import (
"github.com/zclconf/go-cty/cty"
)
func parseFileContent(buf []byte, filename string) (node, hcl.Diagnostics) {
tokens := scan(buf, pos{
Filename: filename,
Pos: hcl.Pos{
Byte: 0,
Line: 1,
Column: 1,
},
})
func parseFileContent(buf []byte, filename string, start hcl.Pos) (node, hcl.Diagnostics) {
tokens := scan(buf, pos{Filename: filename, Pos: start})
p := newPeeker(tokens)
node, diags := parseValue(p)
if len(diags) == 0 && p.Peek().Type != tokenEOF {

View File

@ -598,7 +598,49 @@ func TestParse(t *testing.T) {
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
got, diag := parseFileContent([]byte(test.Input), "")
got, diag := parseFileContent([]byte(test.Input), "", hcl.Pos{Byte: 0, Line: 1, Column: 1})
if len(diag) != test.DiagCount {
t.Errorf("got %d diagnostics; want %d", len(diag), test.DiagCount)
for _, d := range diag {
t.Logf(" - %s", d.Error())
}
}
if diff := deep.Equal(got, test.Want); diff != nil {
for _, problem := range diff {
t.Error(problem)
}
}
})
}
}
func TestParseWithPos(t *testing.T) {
tests := []struct {
Input string
StartPos hcl.Pos
Want node
DiagCount int
}{
// Simple, single-token constructs
{
`true`,
hcl.Pos{Byte: 0, Line: 3, Column: 10},
&booleanVal{
Value: true,
SrcRange: hcl.Range{
Start: hcl.Pos{Line: 3, Column: 10, Byte: 0},
End: hcl.Pos{Line: 3, Column: 14, Byte: 4},
},
},
0,
},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
got, diag := parseFileContent([]byte(test.Input), "", test.StartPos)
if len(diag) != test.DiagCount {
t.Errorf("got %d diagnostics; want %d", len(diag), test.DiagCount)

View File

@ -18,7 +18,16 @@ import (
// from its HasErrors method. If HasErrors returns true, the file represents
// the subset of data that was able to be parsed, which may be none.
func Parse(src []byte, filename string) (*hcl.File, hcl.Diagnostics) {
rootNode, diags := parseFileContent(src, filename)
return ParseWithStartPos(src, filename, hcl.Pos{Byte: 0, Line: 1, Column: 1})
}
// ParseWithStartPos attempts to parse like json.Parse, but unlike json.Parse
// you can pass a start position of the given JSON as a hcl.Pos.
//
// In most cases json.Parse should be sufficient, but it can be useful for parsing
// a part of JSON with correct positions.
func ParseWithStartPos(src []byte, filename string, start hcl.Pos) (*hcl.File, hcl.Diagnostics) {
rootNode, diags := parseFileContent(src, filename, start)
switch rootNode.(type) {
case *objectVal, *arrayVal:

View File

@ -112,3 +112,73 @@ func TestParse_malformed(t *testing.T) {
t.Errorf("got nil File; want actual file")
}
}
func TestParseWithStartPos(t *testing.T) {
src := `{
"foo": {
"bar": "baz"
}
}`
part := `{
"bar": "baz"
}`
file, diags := Parse([]byte(src), "")
partFile, partDiags := ParseWithStartPos([]byte(part), "", hcl.Pos{Byte: 0, Line: 2, Column: 10})
if len(diags) != 0 {
t.Errorf("got %d diagnostics on parse src; want 0", len(diags))
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
}
if len(partDiags) != 0 {
t.Errorf("got %d diagnostics on parse part src; want 0", len(partDiags))
for _, diag := range partDiags {
t.Logf("- %s", diag.Error())
}
}
if file == nil {
t.Errorf("got nil File; want actual file")
}
if file.Body == nil {
t.Fatalf("got nil Body; want actual body")
}
if partFile == nil {
t.Errorf("got nil part File; want actual file")
}
if partFile.Body == nil {
t.Fatalf("got nil part Body; want actual body")
}
content, diags := file.Body.Content(&hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{{Type: "foo"}},
})
if len(diags) != 0 {
t.Errorf("got %d diagnostics on decode; want 0", len(diags))
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
}
attrs, diags := content.Blocks[0].Body.JustAttributes()
if len(diags) != 0 {
t.Errorf("got %d diagnostics on decode; want 0", len(diags))
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
}
srcRange := attrs["bar"].Expr.Range()
partAttrs, diags := partFile.Body.JustAttributes()
if len(diags) != 0 {
t.Errorf("got %d diagnostics on decode; want 0", len(diags))
for _, diag := range diags {
t.Logf("- %s", diag.Error())
}
}
partRange := partAttrs["bar"].Expr.Range()
if srcRange.String() != partRange.String() {
t.Errorf("The two ranges did not match: src=%s, part=%s", srcRange, partRange)
}
}