github.com/solo-io/cue@v0.4.7/internal/core/adt/binop.go (about) 1 // Copyright 2020 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package adt 16 17 import ( 18 "bytes" 19 "strings" 20 ) 21 22 // BinOp handles all operations except AndOp and OrOp. This includes processing 23 // unary comparators such as '<4' and '=~"foo"'. 24 // 25 // BinOp returns nil if not both left and right are concrete. 26 func BinOp(c *OpContext, op Op, left, right Value) Value { 27 leftKind := left.Kind() 28 rightKind := right.Kind() 29 30 const msg = "non-concrete value '%v' to operation '%s'" 31 if left.Concreteness() > Concrete { 32 return &Bottom{ 33 Code: IncompleteError, 34 Err: c.Newf(msg, left, op), 35 } 36 } 37 if right.Concreteness() > Concrete { 38 return &Bottom{ 39 Code: IncompleteError, 40 Err: c.Newf(msg, right, op), 41 } 42 } 43 44 if err := CombineErrors(c.src, left, right); err != nil { 45 return err 46 } 47 48 switch op { 49 case EqualOp: 50 switch { 51 case leftKind == NullKind && rightKind == NullKind: 52 return c.newBool(true) 53 54 case leftKind == NullKind || rightKind == NullKind: 55 return c.newBool(false) 56 57 case leftKind == BoolKind: 58 return c.newBool(c.BoolValue(left) == c.BoolValue(right)) 59 60 case leftKind == StringKind: 61 // normalize? 62 return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right))) 63 64 case leftKind == BytesKind: 65 return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op))) 66 67 case leftKind&NumKind != 0 && rightKind&NumKind != 0: 68 // n := c.newNum() 69 return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X)) 70 71 case leftKind == ListKind && rightKind == ListKind: 72 x := c.Elems(left) 73 y := c.Elems(right) 74 if len(x) != len(y) { 75 return c.newBool(false) 76 } 77 for i, e := range x { 78 a, _ := c.Concrete(nil, e, op) 79 b, _ := c.Concrete(nil, y[i], op) 80 if !test(c, EqualOp, a, b) { 81 return c.newBool(false) 82 } 83 } 84 return c.newBool(true) 85 } 86 87 case NotEqualOp: 88 switch { 89 case leftKind == NullKind && rightKind == NullKind: 90 return c.newBool(false) 91 92 case leftKind == NullKind || rightKind == NullKind: 93 return c.newBool(true) 94 95 case leftKind == BoolKind: 96 return c.newBool(c.boolValue(left, op) != c.boolValue(right, op)) 97 98 case leftKind == StringKind: 99 // normalize? 100 return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right))) 101 102 case leftKind == BytesKind: 103 return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op))) 104 105 case leftKind&NumKind != 0 && rightKind&NumKind != 0: 106 // n := c.newNum() 107 return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X)) 108 109 case leftKind == ListKind && rightKind == ListKind: 110 x := c.Elems(left) 111 y := c.Elems(right) 112 if len(x) != len(y) { 113 return c.newBool(false) 114 } 115 for i, e := range x { 116 a, _ := c.Concrete(nil, e, op) 117 b, _ := c.Concrete(nil, y[i], op) 118 if !test(c, EqualOp, a, b) { 119 return c.newBool(true) 120 } 121 } 122 return c.newBool(false) 123 } 124 125 case LessThanOp, LessEqualOp, GreaterEqualOp, GreaterThanOp: 126 switch { 127 case leftKind == StringKind && rightKind == StringKind: 128 // normalize? 129 return cmpTonode(c, op, strings.Compare(c.stringValue(left, op), c.stringValue(right, op))) 130 131 case leftKind == BytesKind && rightKind == BytesKind: 132 return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op))) 133 134 case leftKind&NumKind != 0 && rightKind&NumKind != 0: 135 // n := c.newNum(left, right) 136 return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X)) 137 } 138 139 case BoolAndOp: 140 return c.newBool(c.boolValue(left, op) && c.boolValue(right, op)) 141 142 case BoolOrOp: 143 return c.newBool(c.boolValue(left, op) || c.boolValue(right, op)) 144 145 case MatchOp: 146 // if y.re == nil { 147 // // This really should not happen, but leave in for safety. 148 // b, err := Regexp.MatchString(str, x.str) 149 // if err != nil { 150 // return c.Errf(Src, "error parsing Regexp: %v", err) 151 // } 152 // return boolTonode(Src, b) 153 // } 154 return c.newBool(c.regexp(right).MatchString(c.stringValue(left, op))) 155 156 case NotMatchOp: 157 return c.newBool(!c.regexp(right).MatchString(c.stringValue(left, op))) 158 159 case AddOp: 160 switch { 161 case leftKind&NumKind != 0 && rightKind&NumKind != 0: 162 return c.Add(c.Num(left, op), c.Num(right, op)) 163 164 case leftKind == StringKind && rightKind == StringKind: 165 return c.NewString(c.StringValue(left) + c.StringValue(right)) 166 167 case leftKind == BytesKind && rightKind == BytesKind: 168 ba := c.bytesValue(left, op) 169 bb := c.bytesValue(right, op) 170 b := make([]byte, len(ba)+len(bb)) 171 copy(b, ba) 172 copy(b[len(ba):], bb) 173 return c.newBytes(b) 174 175 case leftKind == ListKind && rightKind == ListKind: 176 // TODO: get rid of list addition. Semantically it is somewhat 177 // unclear and, as it turns out, it is also hard to get right. 178 // Simulate addition with comprehensions now. 179 if err := c.Err(); err != nil { 180 return err 181 } 182 183 x := MakeIdentLabel(c, "x", "") 184 185 forClause := func(src Expr) *ForClause { 186 return &ForClause{ 187 Value: x, 188 Src: src, 189 Dst: &ValueClause{&StructLit{Decls: []Decl{ 190 &FieldReference{UpCount: 1, Label: x}, 191 }}}, 192 } 193 } 194 195 list := &ListLit{ 196 Elems: []Elem{ 197 forClause(left), 198 forClause(right), 199 }, 200 } 201 202 n := &Vertex{} 203 n.AddConjunct(MakeRootConjunct(c.Env(0), list)) 204 n.Finalize(c) 205 206 return n 207 } 208 209 case SubtractOp: 210 return c.Sub(c.Num(left, op), c.Num(right, op)) 211 212 case MultiplyOp: 213 switch { 214 // float 215 case leftKind&NumKind != 0 && rightKind&NumKind != 0: 216 return c.Mul(c.Num(left, op), c.Num(right, op)) 217 218 case leftKind == StringKind && rightKind == IntKind: 219 const as = "string multiplication" 220 return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as)))) 221 222 case leftKind == IntKind && rightKind == StringKind: 223 const as = "string multiplication" 224 return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as)))) 225 226 case leftKind == BytesKind && rightKind == IntKind: 227 const as = "bytes multiplication" 228 return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as)))) 229 230 case leftKind == IntKind && rightKind == BytesKind: 231 const as = "bytes multiplication" 232 return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as)))) 233 234 case leftKind == ListKind && rightKind == IntKind: 235 left, right = right, left 236 fallthrough 237 238 case leftKind == IntKind && rightKind == ListKind: 239 // TODO: get rid of list multiplication. 240 241 list := &ListLit{} 242 x := MakeIdentLabel(c, "x", "") 243 244 for i := c.uint64(left, "list multiplier"); i > 0; i-- { 245 list.Elems = append(list.Elems, 246 &ForClause{ 247 Value: x, 248 Src: right, 249 Dst: &ValueClause{&StructLit{Decls: []Decl{ 250 &FieldReference{UpCount: 1, Label: x}, 251 }}}, 252 }, 253 ) 254 } 255 if err := c.Err(); err != nil { 256 return err 257 } 258 259 n := &Vertex{} 260 n.AddConjunct(MakeRootConjunct(c.Env(0), list)) 261 n.Finalize(c) 262 263 return n 264 } 265 266 case FloatQuotientOp: 267 if leftKind&NumKind != 0 && rightKind&NumKind != 0 { 268 return c.Quo(c.Num(left, op), c.Num(right, op)) 269 } 270 271 case IntDivideOp: 272 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 273 return c.IntDiv(c.Num(left, op), c.Num(right, op)) 274 } 275 276 case IntModuloOp: 277 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 278 return c.IntMod(c.Num(left, op), c.Num(right, op)) 279 } 280 281 case IntQuotientOp: 282 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 283 return c.IntQuo(c.Num(left, op), c.Num(right, op)) 284 } 285 286 case IntRemainderOp: 287 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 288 return c.IntRem(c.Num(left, op), c.Num(right, op)) 289 } 290 } 291 292 return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)", 293 left, right, op, left.Kind(), right.Kind()) 294 } 295 296 func cmpTonode(c *OpContext, op Op, r int) Value { 297 result := false 298 switch op { 299 case LessThanOp: 300 result = r == -1 301 case LessEqualOp: 302 result = r != 1 303 case EqualOp, AndOp: 304 result = r == 0 305 case NotEqualOp: 306 result = r != 0 307 case GreaterEqualOp: 308 result = r != -1 309 case GreaterThanOp: 310 result = r == 1 311 } 312 return c.newBool(result) 313 }