github.com/hashicorp/hcl/v2@v2.20.0/hclsyntax/expression_ops.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hclsyntax 5 6 import ( 7 "fmt" 8 9 "github.com/hashicorp/hcl/v2" 10 "github.com/zclconf/go-cty/cty" 11 "github.com/zclconf/go-cty/cty/convert" 12 "github.com/zclconf/go-cty/cty/function" 13 "github.com/zclconf/go-cty/cty/function/stdlib" 14 ) 15 16 type Operation struct { 17 Impl function.Function 18 Type cty.Type 19 } 20 21 var ( 22 OpLogicalOr = &Operation{ 23 Impl: stdlib.OrFunc, 24 Type: cty.Bool, 25 } 26 OpLogicalAnd = &Operation{ 27 Impl: stdlib.AndFunc, 28 Type: cty.Bool, 29 } 30 OpLogicalNot = &Operation{ 31 Impl: stdlib.NotFunc, 32 Type: cty.Bool, 33 } 34 35 OpEqual = &Operation{ 36 Impl: stdlib.EqualFunc, 37 Type: cty.Bool, 38 } 39 OpNotEqual = &Operation{ 40 Impl: stdlib.NotEqualFunc, 41 Type: cty.Bool, 42 } 43 44 OpGreaterThan = &Operation{ 45 Impl: stdlib.GreaterThanFunc, 46 Type: cty.Bool, 47 } 48 OpGreaterThanOrEqual = &Operation{ 49 Impl: stdlib.GreaterThanOrEqualToFunc, 50 Type: cty.Bool, 51 } 52 OpLessThan = &Operation{ 53 Impl: stdlib.LessThanFunc, 54 Type: cty.Bool, 55 } 56 OpLessThanOrEqual = &Operation{ 57 Impl: stdlib.LessThanOrEqualToFunc, 58 Type: cty.Bool, 59 } 60 61 OpAdd = &Operation{ 62 Impl: stdlib.AddFunc, 63 Type: cty.Number, 64 } 65 OpSubtract = &Operation{ 66 Impl: stdlib.SubtractFunc, 67 Type: cty.Number, 68 } 69 OpMultiply = &Operation{ 70 Impl: stdlib.MultiplyFunc, 71 Type: cty.Number, 72 } 73 OpDivide = &Operation{ 74 Impl: stdlib.DivideFunc, 75 Type: cty.Number, 76 } 77 OpModulo = &Operation{ 78 Impl: stdlib.ModuloFunc, 79 Type: cty.Number, 80 } 81 OpNegate = &Operation{ 82 Impl: stdlib.NegateFunc, 83 Type: cty.Number, 84 } 85 ) 86 87 var binaryOps []map[TokenType]*Operation 88 89 func init() { 90 // This operation table maps from the operator's token type 91 // to the AST operation type. All expressions produced from 92 // binary operators are BinaryOp nodes. 93 // 94 // Binary operator groups are listed in order of precedence, with 95 // the *lowest* precedence first. Operators within the same group 96 // have left-to-right associativity. 97 binaryOps = []map[TokenType]*Operation{ 98 { 99 TokenOr: OpLogicalOr, 100 }, 101 { 102 TokenAnd: OpLogicalAnd, 103 }, 104 { 105 TokenEqualOp: OpEqual, 106 TokenNotEqual: OpNotEqual, 107 }, 108 { 109 TokenGreaterThan: OpGreaterThan, 110 TokenGreaterThanEq: OpGreaterThanOrEqual, 111 TokenLessThan: OpLessThan, 112 TokenLessThanEq: OpLessThanOrEqual, 113 }, 114 { 115 TokenPlus: OpAdd, 116 TokenMinus: OpSubtract, 117 }, 118 { 119 TokenStar: OpMultiply, 120 TokenSlash: OpDivide, 121 TokenPercent: OpModulo, 122 }, 123 } 124 } 125 126 type BinaryOpExpr struct { 127 LHS Expression 128 Op *Operation 129 RHS Expression 130 131 SrcRange hcl.Range 132 } 133 134 func (e *BinaryOpExpr) walkChildNodes(w internalWalkFunc) { 135 w(e.LHS) 136 w(e.RHS) 137 } 138 139 func (e *BinaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { 140 impl := e.Op.Impl // assumed to be a function taking exactly two arguments 141 params := impl.Params() 142 lhsParam := params[0] 143 rhsParam := params[1] 144 145 var diags hcl.Diagnostics 146 147 givenLHSVal, lhsDiags := e.LHS.Value(ctx) 148 givenRHSVal, rhsDiags := e.RHS.Value(ctx) 149 diags = append(diags, lhsDiags...) 150 diags = append(diags, rhsDiags...) 151 152 lhsVal, err := convert.Convert(givenLHSVal, lhsParam.Type) 153 if err != nil { 154 diags = append(diags, &hcl.Diagnostic{ 155 Severity: hcl.DiagError, 156 Summary: "Invalid operand", 157 Detail: fmt.Sprintf("Unsuitable value for left operand: %s.", err), 158 Subject: e.LHS.Range().Ptr(), 159 Context: &e.SrcRange, 160 Expression: e.LHS, 161 EvalContext: ctx, 162 }) 163 } 164 rhsVal, err := convert.Convert(givenRHSVal, rhsParam.Type) 165 if err != nil { 166 diags = append(diags, &hcl.Diagnostic{ 167 Severity: hcl.DiagError, 168 Summary: "Invalid operand", 169 Detail: fmt.Sprintf("Unsuitable value for right operand: %s.", err), 170 Subject: e.RHS.Range().Ptr(), 171 Context: &e.SrcRange, 172 Expression: e.RHS, 173 EvalContext: ctx, 174 }) 175 } 176 177 if diags.HasErrors() { 178 // Don't actually try the call if we have errors already, since the 179 // this will probably just produce a confusing duplicative diagnostic. 180 return cty.UnknownVal(e.Op.Type), diags 181 } 182 183 args := []cty.Value{lhsVal, rhsVal} 184 result, err := impl.Call(args) 185 if err != nil { 186 diags = append(diags, &hcl.Diagnostic{ 187 // FIXME: This diagnostic is useless. 188 Severity: hcl.DiagError, 189 Summary: "Operation failed", 190 Detail: fmt.Sprintf("Error during operation: %s.", err), 191 Subject: &e.SrcRange, 192 Expression: e, 193 EvalContext: ctx, 194 }) 195 return cty.UnknownVal(e.Op.Type), diags 196 } 197 198 return result, diags 199 } 200 201 func (e *BinaryOpExpr) Range() hcl.Range { 202 return e.SrcRange 203 } 204 205 func (e *BinaryOpExpr) StartRange() hcl.Range { 206 return e.LHS.StartRange() 207 } 208 209 type UnaryOpExpr struct { 210 Op *Operation 211 Val Expression 212 213 SrcRange hcl.Range 214 SymbolRange hcl.Range 215 } 216 217 func (e *UnaryOpExpr) walkChildNodes(w internalWalkFunc) { 218 w(e.Val) 219 } 220 221 func (e *UnaryOpExpr) Value(ctx *hcl.EvalContext) (cty.Value, hcl.Diagnostics) { 222 impl := e.Op.Impl // assumed to be a function taking exactly one argument 223 params := impl.Params() 224 param := params[0] 225 226 givenVal, diags := e.Val.Value(ctx) 227 228 val, err := convert.Convert(givenVal, param.Type) 229 if err != nil { 230 diags = append(diags, &hcl.Diagnostic{ 231 Severity: hcl.DiagError, 232 Summary: "Invalid operand", 233 Detail: fmt.Sprintf("Unsuitable value for unary operand: %s.", err), 234 Subject: e.Val.Range().Ptr(), 235 Context: &e.SrcRange, 236 Expression: e.Val, 237 EvalContext: ctx, 238 }) 239 } 240 241 if diags.HasErrors() { 242 // Don't actually try the call if we have errors already, since the 243 // this will probably just produce a confusing duplicative diagnostic. 244 return cty.UnknownVal(e.Op.Type), diags 245 } 246 247 args := []cty.Value{val} 248 result, err := impl.Call(args) 249 if err != nil { 250 diags = append(diags, &hcl.Diagnostic{ 251 // FIXME: This diagnostic is useless. 252 Severity: hcl.DiagError, 253 Summary: "Operation failed", 254 Detail: fmt.Sprintf("Error during operation: %s.", err), 255 Subject: &e.SrcRange, 256 Expression: e, 257 EvalContext: ctx, 258 }) 259 return cty.UnknownVal(e.Op.Type), diags 260 } 261 262 return result, diags 263 } 264 265 func (e *UnaryOpExpr) Range() hcl.Range { 266 return e.SrcRange 267 } 268 269 func (e *UnaryOpExpr) StartRange() hcl.Range { 270 return e.SymbolRange 271 }