2017-09-11 23:40:37 +00:00
|
|
|
package hclsyntax
|
2017-05-31 14:39:23 +00:00
|
|
|
|
2017-06-01 01:54:54 +00:00
|
|
|
import (
|
2017-06-12 01:55:27 +00:00
|
|
|
"fmt"
|
|
|
|
|
2019-09-09 23:08:19 +00:00
|
|
|
"github.com/hashicorp/hcl/v2"
|
2017-06-01 01:54:54 +00:00
|
|
|
"github.com/zclconf/go-cty/cty"
|
2017-06-12 01:55:27 +00:00
|
|
|
"github.com/zclconf/go-cty/cty/convert"
|
|
|
|
"github.com/zclconf/go-cty/cty/function"
|
|
|
|
"github.com/zclconf/go-cty/cty/function/stdlib"
|
2017-06-01 01:54:54 +00:00
|
|
|
)
|
|
|
|
|
2017-06-12 14:09:24 +00:00
|
|
|
type Operation struct {
|
|
|
|
Impl function.Function
|
|
|
|
Type cty.Type
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
OpLogicalOr = &Operation{
|
|
|
|
Impl: stdlib.OrFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
OpLogicalAnd = &Operation{
|
|
|
|
Impl: stdlib.AndFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
OpLogicalNot = &Operation{
|
|
|
|
Impl: stdlib.NotFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
OpEqual = &Operation{
|
|
|
|
Impl: stdlib.EqualFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
OpNotEqual = &Operation{
|
|
|
|
Impl: stdlib.NotEqualFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
OpGreaterThan = &Operation{
|
|
|
|
Impl: stdlib.GreaterThanFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
OpGreaterThanOrEqual = &Operation{
|
|
|
|
Impl: stdlib.GreaterThanOrEqualToFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
OpLessThan = &Operation{
|
|
|
|
Impl: stdlib.LessThanFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
OpLessThanOrEqual = &Operation{
|
|
|
|
Impl: stdlib.LessThanOrEqualToFunc,
|
|
|
|
Type: cty.Bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
OpAdd = &Operation{
|
|
|
|
Impl: stdlib.AddFunc,
|
|
|
|
Type: cty.Number,
|
|
|
|
}
|
|
|
|
OpSubtract = &Operation{
|
|
|
|
Impl: stdlib.SubtractFunc,
|
|
|
|
Type: cty.Number,
|
|
|
|
}
|
|
|
|
OpMultiply = &Operation{
|
|
|
|
Impl: stdlib.MultiplyFunc,
|
|
|
|
Type: cty.Number,
|
|
|
|
}
|
|
|
|
OpDivide = &Operation{
|
|
|
|
Impl: stdlib.DivideFunc,
|
|
|
|
Type: cty.Number,
|
|
|
|
}
|
|
|
|
OpModulo = &Operation{
|
|
|
|
Impl: stdlib.ModuloFunc,
|
|
|
|
Type: cty.Number,
|
|
|
|
}
|
|
|
|
OpNegate = &Operation{
|
|
|
|
Impl: stdlib.NegateFunc,
|
|
|
|
Type: cty.Number,
|
|
|
|
}
|
2017-05-31 14:39:23 +00:00
|
|
|
)
|
|
|
|
|
2017-06-12 14:09:24 +00:00
|
|
|
var binaryOps []map[TokenType]*Operation
|
2017-05-31 14:39:23 +00:00
|
|
|
|
|
|
|
func init() {
|
|
|
|
// This operation table maps from the operator's token type
|
|
|
|
// to the AST operation type. All expressions produced from
|
|
|
|
// binary operators are BinaryOp nodes.
|
|
|
|
//
|
|
|
|
// Binary operator groups are listed in order of precedence, with
|
|
|
|
// the *lowest* precedence first. Operators within the same group
|
|
|
|
// have left-to-right associativity.
|
2017-06-12 14:09:24 +00:00
|
|
|
binaryOps = []map[TokenType]*Operation{
|
2017-05-31 14:39:23 +00:00
|
|
|
{
|
|
|
|
TokenOr: OpLogicalOr,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
TokenAnd: OpLogicalAnd,
|
|
|
|
},
|
|
|
|
{
|
2017-06-04 23:00:40 +00:00
|
|
|
TokenEqualOp: OpEqual,
|
2017-05-31 14:39:23 +00:00
|
|
|
TokenNotEqual: OpNotEqual,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
TokenGreaterThan: OpGreaterThan,
|
|
|
|
TokenGreaterThanEq: OpGreaterThanOrEqual,
|
|
|
|
TokenLessThan: OpLessThan,
|
|
|
|
TokenLessThanEq: OpLessThanOrEqual,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
TokenPlus: OpAdd,
|
|
|
|
TokenMinus: OpSubtract,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
TokenStar: OpMultiply,
|
|
|
|
TokenSlash: OpDivide,
|
|
|
|
TokenPercent: OpModulo,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2017-06-01 01:54:54 +00:00
|
|
|
|
|
|
|
type BinaryOpExpr struct {
|
|
|
|
LHS Expression
|
2017-06-12 14:09:24 +00:00
|
|
|
Op *Operation
|
2017-06-01 01:54:54 +00:00
|
|
|
RHS Expression
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
SrcRange hcl.Range
|
2017-06-01 01:54:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) {
|
2018-09-26 14:38:43 +00:00
|
|
|
w(e.LHS)
|
|
|
|
w(e.RHS)
|
2017-06-01 01:54:54 +00:00
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
2017-06-12 14:09:24 +00:00
|
|
|
impl := e.Op.Impl // assumed to be a function taking exactly two arguments
|
2017-06-12 01:55:27 +00:00
|
|
|
params := impl.Params()
|
|
|
|
lhsParam := params[0]
|
|
|
|
rhsParam := params[1]
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
var diags hcl.Diagnostics
|
2017-06-12 01:55:27 +00:00
|
|
|
|
|
|
|
givenLHSVal, lhsDiags := e.LHS.Value(ctx)
|
|
|
|
givenRHSVal, rhsDiags := e.RHS.Value(ctx)
|
|
|
|
diags = append(diags, lhsDiags...)
|
|
|
|
diags = append(diags, rhsDiags...)
|
|
|
|
|
|
|
|
lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type)
|
|
|
|
if err != nil {
|
2017-09-11 23:40:37 +00:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2018-07-28 20:14:36 +00:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Invalid operand",
|
|
|
|
Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err),
|
|
|
|
Subject: e.LHS.Range().Ptr(),
|
|
|
|
Context: &e.SrcRange,
|
2018-07-28 20:36:55 +00:00
|
|
|
Expression: e.LHS,
|
2018-07-28 20:14:36 +00:00
|
|
|
EvalContext: ctx,
|
2017-06-12 01:55:27 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type)
|
|
|
|
if err != nil {
|
2017-09-11 23:40:37 +00:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2018-07-28 20:14:36 +00:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Invalid operand",
|
|
|
|
Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err),
|
|
|
|
Subject: e.RHS.Range().Ptr(),
|
|
|
|
Context: &e.SrcRange,
|
2018-07-28 20:36:55 +00:00
|
|
|
Expression: e.RHS,
|
2018-07-28 20:14:36 +00:00
|
|
|
EvalContext: ctx,
|
2017-06-12 01:55:27 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
// Don't actually try the call if we have errors already, since the
|
|
|
|
// this will probably just produce a confusing duplicative diagnostic.
|
2017-06-12 14:09:24 +00:00
|
|
|
return cty.UnknownVal(e.Op.Type), diags
|
2017-06-12 01:55:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
args := []cty.Value{lhsVal, rhsVal}
|
|
|
|
result, err := impl.Call(args)
|
|
|
|
if err != nil {
|
2017-09-11 23:40:37 +00:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2017-06-12 01:55:27 +00:00
|
|
|
// FIXME: This diagnostic is useless.
|
2018-07-28 20:14:36 +00:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Operation failed",
|
|
|
|
Detail: fmt.Sprintf("Error during operation: %s.", err),
|
|
|
|
Subject: &e.SrcRange,
|
2018-07-28 20:36:55 +00:00
|
|
|
Expression: e,
|
2018-07-28 20:14:36 +00:00
|
|
|
EvalContext: ctx,
|
2017-06-12 01:55:27 +00:00
|
|
|
})
|
2017-06-12 14:09:24 +00:00
|
|
|
return cty.UnknownVal(e.Op.Type), diags
|
2017-06-12 01:55:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result, diags
|
2017-06-01 01:54:54 +00:00
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e *BinaryOpExpr) Range() hcl.Range {
|
2017-06-01 01:54:54 +00:00
|
|
|
return e.SrcRange
|
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e *BinaryOpExpr) StartRange() hcl.Range {
|
2017-06-01 01:54:54 +00:00
|
|
|
return e.LHS.StartRange()
|
|
|
|
}
|
2017-06-01 14:05:22 +00:00
|
|
|
|
|
|
|
type UnaryOpExpr struct {
|
2017-06-12 14:09:24 +00:00
|
|
|
Op *Operation
|
2017-06-01 14:05:22 +00:00
|
|
|
Val Expression
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
SrcRange hcl.Range
|
|
|
|
SymbolRange hcl.Range
|
2017-06-01 14:05:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (e *UnaryOpExpr) walkChildNodes(w internalWalkFunc) {
|
2018-09-26 14:38:43 +00:00
|
|
|
w(e.Val)
|
2017-06-01 14:05:22 +00:00
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) {
|
2017-06-12 14:13:17 +00:00
|
|
|
impl := e.Op.Impl // assumed to be a function taking exactly one argument
|
|
|
|
params := impl.Params()
|
|
|
|
param := params[0]
|
|
|
|
|
|
|
|
givenVal, diags := e.Val.Value(ctx)
|
|
|
|
|
|
|
|
val, err := convert.Convert(givenVal, param.Type)
|
|
|
|
if err != nil {
|
2017-09-11 23:40:37 +00:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2018-07-28 20:14:36 +00:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Invalid operand",
|
|
|
|
Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err),
|
|
|
|
Subject: e.Val.Range().Ptr(),
|
|
|
|
Context: &e.SrcRange,
|
2018-07-28 20:36:55 +00:00
|
|
|
Expression: e.Val,
|
2018-07-28 20:14:36 +00:00
|
|
|
EvalContext: ctx,
|
2017-06-12 14:13:17 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
if diags.HasErrors() {
|
|
|
|
// Don't actually try the call if we have errors already, since the
|
|
|
|
// this will probably just produce a confusing duplicative diagnostic.
|
|
|
|
return cty.UnknownVal(e.Op.Type), diags
|
|
|
|
}
|
|
|
|
|
|
|
|
args := []cty.Value{val}
|
|
|
|
result, err := impl.Call(args)
|
|
|
|
if err != nil {
|
2017-09-11 23:40:37 +00:00
|
|
|
diags = append(diags, &hcl.Diagnostic{
|
2017-06-12 14:13:17 +00:00
|
|
|
// FIXME: This diagnostic is useless.
|
2018-07-28 20:14:36 +00:00
|
|
|
Severity: hcl.DiagError,
|
|
|
|
Summary: "Operation failed",
|
|
|
|
Detail: fmt.Sprintf("Error during operation: %s.", err),
|
|
|
|
Subject: &e.SrcRange,
|
2018-07-28 20:36:55 +00:00
|
|
|
Expression: e,
|
2018-07-28 20:14:36 +00:00
|
|
|
EvalContext: ctx,
|
2017-06-12 14:13:17 +00:00
|
|
|
})
|
|
|
|
return cty.UnknownVal(e.Op.Type), diags
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, diags
|
2017-06-01 14:05:22 +00:00
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e *UnaryOpExpr) Range() hcl.Range {
|
2017-06-01 14:05:22 +00:00
|
|
|
return e.SrcRange
|
|
|
|
}
|
|
|
|
|
2017-09-11 23:40:37 +00:00
|
|
|
func (e *UnaryOpExpr) StartRange() hcl.Range {
|
2017-06-01 14:05:22 +00:00
|
|
|
return e.SymbolRange
|
|
|
|
}
|