zcldec: initial implementation of BlockListSpec

This initial pass supports only blocks without labels. BlockLabelSpec will
be wired in here later to allow decoding blocks with labels too.
This commit is contained in:
Martin Atkins 2017-07-17 17:29:06 -07:00
parent 4df9fd1372
commit 0598a0b79b
3 changed files with 164 additions and 1 deletions

View File

@ -146,6 +146,58 @@ b {}
cty.EmptyObjectVal,
1, // only one "b" block is allowed
},
{
`
b {}
b {}
`,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
0,
},
{
``,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
},
nil,
cty.ListValEmpty(cty.DynamicPseudoType),
0,
},
{
`
b {}
b {}
b {}
`,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
MaxItems: 2,
},
nil,
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal, cty.EmptyObjectVal}),
1, // too many b blocks
},
{
`
b {}
b {}
`,
&BlockListSpec{
TypeName: "b",
Nested: ObjectSpec{},
MinItems: 10,
},
nil,
cty.ListVal([]cty.Value{cty.EmptyObjectVal, cty.EmptyObjectVal}),
1, // insufficient b blocks
},
}
for i, test := range tests {

View File

@ -360,8 +360,80 @@ func (s *BlockListSpec) visitSameBodyChildren(cb visitFunc) {
// leaf node ("Nested" does not use the same body)
}
// blockSpec implementation
func (s *BlockListSpec) blockHeaderSchemata() []zcl.BlockHeaderSchema {
return []zcl.BlockHeaderSchema{
{
Type: s.TypeName,
// FIXME: Need to peek into s.Nested to see if it has any
// BlockLabelSpec instances, which will define then how many
// labels we need.
},
}
}
// specNeedingVariables implementation
func (s *BlockListSpec) variablesNeeded(content *zcl.BodyContent) []zcl.Traversal {
var ret []zcl.Traversal
for _, childBlock := range content.Blocks {
if childBlock.Type != s.TypeName {
continue
}
ret = append(ret, Variables(childBlock.Body, s.Nested)...)
}
return ret
}
func (s *BlockListSpec) decode(content *zcl.BodyContent, block *zcl.Block, ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
panic("BlockListSpec.decode not yet implemented")
var diags zcl.Diagnostics
if s.Nested == nil {
panic("BlockSpec with no Nested Spec")
}
var elems []cty.Value
var sourceRanges []zcl.Range
for _, childBlock := range content.Blocks {
if childBlock.Type != s.TypeName {
continue
}
val, _, childDiags := decode(childBlock.Body, childBlock, ctx, s.Nested, false)
diags = append(diags, childDiags...)
elems = append(elems, val)
sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, childBlock, s.Nested))
}
if len(elems) < s.MinItems {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: fmt.Sprintf("insufficient %s blocks", s.TypeName),
Detail: fmt.Sprintf("at least %d blocks are required", s.MinItems),
Subject: &content.MissingItemRange,
})
} else if s.MaxItems > 0 && len(elems) > s.MaxItems {
diags = append(diags, &zcl.Diagnostic{
Severity: zcl.DiagError,
Summary: fmt.Sprintf("too many %s blocks", s.TypeName),
Detail: fmt.Sprintf("no more than %d blocks are allowed", s.MaxItems),
Subject: &sourceRanges[s.MaxItems],
})
}
var ret cty.Value
if len(elems) == 0 {
// FIXME: We don't currently have enough info to construct a type for
// an empty list, so we'll just stub it out.
ret = cty.ListValEmpty(cty.DynamicPseudoType)
} else {
ret = cty.ListVal(elems)
}
return ret, diags
}
func (s *BlockListSpec) sourceRange(content *zcl.BodyContent, block *zcl.Block) zcl.Range {

View File

@ -85,6 +85,45 @@ b {
},
},
},
{
`
b {
a = foo
}
b {
a = bar
}
c {
a = baz
}
`,
&BlockListSpec{
TypeName: "b",
Nested: &AttrSpec{
Name: "a",
},
},
[]zcl.Traversal{
{
zcl.TraverseRoot{
Name: "foo",
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 3, Column: 7, Byte: 11},
End: zcl.Pos{Line: 3, Column: 10, Byte: 14},
},
},
},
{
zcl.TraverseRoot{
Name: "bar",
SrcRange: zcl.Range{
Start: zcl.Pos{Line: 6, Column: 7, Byte: 27},
End: zcl.Pos{Line: 6, Column: 10, Byte: 30},
},
},
},
},
},
}
for i, test := range tests {