From 833ff9ecd7446b016a5a28c82f5764d5a25dcf3f Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Sun, 18 Jun 2017 08:14:36 -0700 Subject: [PATCH] zclsyntax: evaluation of ForExpr into a tuple --- zcl/zclsyntax/expression.go | 71 +++++++++++++++++++++++++++++++- zcl/zclsyntax/expression_test.go | 8 ++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/zcl/zclsyntax/expression.go b/zcl/zclsyntax/expression.go index 5070be9..65a038b 100644 --- a/zcl/zclsyntax/expression.go +++ b/zcl/zclsyntax/expression.go @@ -672,7 +672,7 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) { }) return cty.DynamicVal, diags } - if !collVal.IsKnown() { + if collVal.Type() == cty.DynamicPseudoType { return cty.DynamicVal, diags } if !collVal.CanIterateElements() { @@ -688,6 +688,9 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) { }) return cty.DynamicVal, diags } + if !collVal.IsKnown() { + return cty.DynamicVal, diags + } childCtx := ctx.NewChild() childCtx.Variables = map[string]cty.Value{} @@ -800,7 +803,71 @@ func (e *ForExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) { } else { // Producing a tuple vals := []cty.Value{} - panic("for into a tuple is not yet implemented") + + it := collVal.ElementIterator() + + known := true + for it.Next() { + k, v := it.Element() + if e.KeyVar != "" { + childCtx.Variables[e.KeyVar] = k + } + childCtx.Variables[e.ValVar] = v + + if e.CondExpr != nil { + includeRaw, condDiags := e.CondExpr.Value(childCtx) + diags = append(diags, condDiags...) + if includeRaw.IsNull() { + if known { + diags = append(diags, &zcl.Diagnostic{ + Severity: zcl.DiagError, + Summary: "Condition is null", + Detail: "The value of the 'if' clause must not be null.", + Subject: e.CondExpr.Range().Ptr(), + Context: &e.SrcRange, + }) + } + known = false + continue + } + if !includeRaw.IsKnown() { + // We will eventually return DynamicVal, but we'll continue + // iterating in case there are other diagnostics to gather + // for later elements. + known = false + continue + } + + include, err := convert.Convert(includeRaw, cty.Bool) + if err != nil { + if known { + diags = append(diags, &zcl.Diagnostic{ + Severity: zcl.DiagError, + Summary: "Invalid 'for' condition", + Detail: fmt.Sprintf("The 'if' clause value is invalid: %s.", err.Error()), + Subject: e.CondExpr.Range().Ptr(), + Context: &e.SrcRange, + }) + } + known = false + continue + } + + if include.False() { + // Skip this element + continue + } + } + + val, valDiags := e.ValExpr.Value(childCtx) + diags = append(diags, valDiags...) + vals = append(vals, val) + } + + if !known { + return cty.DynamicVal, diags + } + return cty.TupleVal(vals), diags } } diff --git a/zcl/zclsyntax/expression_test.go b/zcl/zclsyntax/expression_test.go index 56891e0..06d9bfd 100644 --- a/zcl/zclsyntax/expression_test.go +++ b/zcl/zclsyntax/expression_test.go @@ -485,6 +485,14 @@ upper( }), 0, }, + { + `[for k, v in {hello: "world"}: "${k}=${v}"]`, + nil, + cty.TupleVal([]cty.Value{ + cty.StringVal("hello=world"), + }), + 0, + }, { `[{name: "Steve"}, {name: "Ermintrude"}].*.name`,