2017-06-01 15:01:12 +00:00
|
|
|
package zclsyntax
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/zclconf/go-cty/cty"
|
|
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
|
|
"github.com/zclconf/go-zcl/zcl"
|
|
|
|
)
|
|
|
|
|
|
|
|
type TemplateExpr struct {
|
2017-06-18 14:44:57 +00:00
|
|
|
Parts []Expression
|
2017-06-01 15:01:12 +00:00
|
|
|
|
|
|
|
SrcRange zcl.Range
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateExpr) walkChildNodes(w internalWalkFunc) {
|
|
|
|
for i, part := range e.Parts {
|
|
|
|
e.Parts[i] = w(part).(Expression)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
|
|
|
buf := &bytes.Buffer{}
|
|
|
|
var diags zcl.Diagnostics
|
|
|
|
isKnown := true
|
|
|
|
|
|
|
|
for _, part := range e.Parts {
|
|
|
|
partVal, partDiags := part.Value(ctx)
|
|
|
|
diags = append(diags, partDiags...)
|
|
|
|
|
|
|
|
if partVal.IsNull() {
|
|
|
|
diags = append(diags, &zcl.Diagnostic{
|
|
|
|
Severity: zcl.DiagError,
|
|
|
|
Summary: "Invalid template interpolation value",
|
|
|
|
Detail: fmt.Sprintf(
|
|
|
|
"The expression result is null. Cannot include a null value in a string template.",
|
|
|
|
),
|
|
|
|
Subject: part.Range().Ptr(),
|
|
|
|
Context: &e.SrcRange,
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if !partVal.IsKnown() {
|
|
|
|
// If any part is unknown then the result as a whole must be
|
|
|
|
// unknown too. We'll keep on processing the rest of the parts
|
|
|
|
// anyway, because we want to still emit any diagnostics resulting
|
|
|
|
// from evaluating those.
|
|
|
|
isKnown = false
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
strVal, err := convert.Convert(partVal, cty.String)
|
|
|
|
if err != nil {
|
|
|
|
diags = append(diags, &zcl.Diagnostic{
|
|
|
|
Severity: zcl.DiagError,
|
|
|
|
Summary: "Invalid template interpolation value",
|
|
|
|
Detail: fmt.Sprintf(
|
|
|
|
"Cannot include the given value in a string template: %s.",
|
|
|
|
err.Error(),
|
|
|
|
),
|
|
|
|
Subject: part.Range().Ptr(),
|
|
|
|
Context: &e.SrcRange,
|
|
|
|
})
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
buf.WriteString(strVal.AsString())
|
|
|
|
}
|
|
|
|
|
|
|
|
if !isKnown {
|
|
|
|
return cty.UnknownVal(cty.String), diags
|
|
|
|
}
|
|
|
|
|
|
|
|
return cty.StringVal(buf.String()), diags
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateExpr) Range() zcl.Range {
|
|
|
|
return e.SrcRange
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateExpr) StartRange() zcl.Range {
|
|
|
|
return e.Parts[0].StartRange()
|
|
|
|
}
|
2017-06-18 14:44:57 +00:00
|
|
|
|
|
|
|
// TemplateWrapExpr is used instead of a TemplateExpr when a template
|
|
|
|
// consists _only_ of a single interpolation sequence. In that case, the
|
|
|
|
// template's result is the single interpolation's result, verbatim with
|
|
|
|
// no type conversions.
|
|
|
|
type TemplateWrapExpr struct {
|
|
|
|
Wrapped Expression
|
|
|
|
|
|
|
|
SrcRange zcl.Range
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateWrapExpr) walkChildNodes(w internalWalkFunc) {
|
|
|
|
e.Wrapped = w(e.Wrapped).(Expression)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateWrapExpr) Value(ctx *zcl.EvalContext) (cty.Value, zcl.Diagnostics) {
|
|
|
|
return e.Wrapped.Value(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateWrapExpr) Range() zcl.Range {
|
|
|
|
return e.SrcRange
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *TemplateWrapExpr) StartRange() zcl.Range {
|
|
|
|
return e.SrcRange
|
|
|
|
}
|