json: parsing of arrays

(and assorted bugfixes in parsing of objects that were found in the
process of adapting that code for arrays.)
This commit is contained in:
Martin Atkins 2017-05-17 07:55:21 -07:00
parent b073937523
commit 0ef4932962
2 changed files with 215 additions and 5 deletions

View File

@ -58,6 +58,15 @@ func parseValue(p *peeker) (node, zcl.Diagnostics) {
Subject: &tok.Range, Subject: &tok.Range,
}, },
} }
case tokenEOF:
return nil, zcl.Diagnostics{
{
Severity: zcl.DiagError,
Summary: "Missing value",
Detail: "The JSON data ends prematurely.",
Subject: &tok.Range,
},
}
default: default:
return nil, zcl.Diagnostics{ return nil, zcl.Diagnostics{
{ {
@ -132,7 +141,7 @@ Token:
valNode, valDiags := parseValue(p) valNode, valDiags := parseValue(p)
diags = diags.Extend(valDiags) diags = diags.Extend(valDiags)
if keyNode == nil { if valNode == nil {
return nil, diags return nil, diags
} }
@ -157,14 +166,14 @@ Token:
switch p.Peek().Type { switch p.Peek().Type {
case tokenComma: case tokenComma:
p.Read() comma := p.Read()
if p.Peek().Type == tokenBraceC { if p.Peek().Type == tokenBraceC {
// Special error message for this common mistake // Special error message for this common mistake
return nil, diags.Append(&zcl.Diagnostic{ return nil, diags.Append(&zcl.Diagnostic{
Severity: zcl.DiagError, Severity: zcl.DiagError,
Summary: "Trailing comma in object", Summary: "Trailing comma in object",
Detail: "JSON does not permit a trailing comma after the final attribute in an object.", Detail: "JSON does not permit a trailing comma after the final attribute in an object.",
Subject: &colon.Range, Subject: &comma.Range,
}) })
} }
continue Token continue Token
@ -189,7 +198,7 @@ Token:
Severity: zcl.DiagError, Severity: zcl.DiagError,
Summary: "Missing attribute seperator comma", Summary: "Missing attribute seperator comma",
Detail: "A comma must appear between each attribute declaration in an object.", Detail: "A comma must appear between each attribute declaration in an object.",
Subject: &colon.Range, Subject: p.Peek().Range.Ptr(),
}) })
} }
@ -204,7 +213,78 @@ Token:
} }
func parseArray(p *peeker) (node, zcl.Diagnostics) { func parseArray(p *peeker) (node, zcl.Diagnostics) {
return nil, nil var diags zcl.Diagnostics
open := p.Read()
vals := []node{}
Token:
for {
if p.Peek().Type == tokenBrackC {
break Token
}
valNode, valDiags := parseValue(p)
diags = diags.Extend(valDiags)
if valNode == nil {
return nil, diags
}
vals = append(vals, valNode)
switch p.Peek().Type {
case tokenComma:
comma := p.Read()
if p.Peek().Type == tokenBrackC {
// Special error message for this common mistake
return nil, diags.Append(&zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Trailing comma in array",
Detail: "JSON does not permit a trailing comma after the final attribute in an array.",
Subject: &comma.Range,
})
}
continue Token
case tokenColon:
return nil, diags.Append(&zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Invalid array value",
Detail: "A colon is not used to introduce values in a JSON array.",
Subject: p.Peek().Range.Ptr(),
})
case tokenEOF:
return nil, diags.Append(&zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Unclosed object",
Detail: "No closing bracket was found for this JSON array.",
Subject: &open.Range,
})
case tokenBraceC:
return nil, diags.Append(&zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Mismatched brackets",
Detail: "A JSON array must be closed with a bracket, not a brace.",
Subject: p.Peek().Range.Ptr(),
})
case tokenBrackC:
break Token
default:
return nil, diags.Append(&zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: "Missing attribute seperator comma",
Detail: "A comma must appear between each value in an array.",
Subject: p.Peek().Range.Ptr(),
})
}
}
close := p.Read()
return &arrayVal{
Values: vals,
SrcRange: zcl.RangeBetween(open.Range, close.Range),
OpenRange: open.Range,
}, diags
} }
func parseNumber(p *peeker) (node, zcl.Diagnostics) { func parseNumber(p *peeker) (node, zcl.Diagnostics) {

View File

@ -314,6 +314,136 @@ func TestParse(t *testing.T) {
nil, nil,
1, 1,
}, },
{
`[]`,
&arrayVal{
Values: []node{},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 3, Byte: 2},
},
OpenRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
},
0,
},
{
`[true]`,
&arrayVal{
Values: []node{
&booleanVal{
Value: true,
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 2, Byte: 1},
End: zcl.Pos{Line: 1, Column: 6, Byte: 5},
},
},
},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 7, Byte: 6},
},
OpenRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
},
0,
},
{
`[true, false]`,
&arrayVal{
Values: []node{
&booleanVal{
Value: true,
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 2, Byte: 1},
End: zcl.Pos{Line: 1, Column: 6, Byte: 5},
},
},
&booleanVal{
Value: false,
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 8, Byte: 7},
End: zcl.Pos{Line: 1, Column: 13, Byte: 12},
},
},
},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 14, Byte: 13},
},
OpenRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
},
0,
},
{
`[[]]`,
&arrayVal{
Values: []node{
&arrayVal{
Values: []node{},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 2, Byte: 1},
End: zcl.Pos{Line: 1, Column: 4, Byte: 3},
},
OpenRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 2, Byte: 1},
End: zcl.Pos{Line: 1, Column: 3, Byte: 2},
},
},
},
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 5, Byte: 4},
},
OpenRange: zcl.Range{
Start: zcl.Pos{Line: 1, Column: 1, Byte: 0},
End: zcl.Pos{Line: 1, Column: 2, Byte: 1},
},
},
0,
},
{
`[`,
nil,
1,
},
{
`[true`,
nil,
1,
},
{
`]`,
nil,
1,
},
{
`[true,]`,
nil,
1,
},
{
`[[],]`,
nil,
1,
},
{
`["hello":true]`,
nil,
1,
},
{
`[true}`,
nil,
1,
},
} }
for _, test := range tests { for _, test := range tests {