github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/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) *Comprehension { 186 s := &StructLit{Decls: []Decl{ 187 &FieldReference{UpCount: 1, Label: x}, 188 }} 189 return &Comprehension{ 190 Clauses: &ForClause{ 191 Value: x, 192 Src: src, 193 Dst: &ValueClause{s}, 194 }, 195 Value: s, 196 } 197 } 198 199 list := &ListLit{ 200 Elems: []Elem{ 201 forClause(left), 202 forClause(right), 203 }, 204 } 205 206 n := &Vertex{} 207 n.AddConjunct(MakeRootConjunct(c.Env(0), list)) 208 n.Finalize(c) 209 210 return n 211 } 212 213 case SubtractOp: 214 return c.Sub(c.Num(left, op), c.Num(right, op)) 215 216 case MultiplyOp: 217 switch { 218 // float 219 case leftKind&NumKind != 0 && rightKind&NumKind != 0: 220 return c.Mul(c.Num(left, op), c.Num(right, op)) 221 222 case leftKind == StringKind && rightKind == IntKind: 223 const as = "string multiplication" 224 return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as)))) 225 226 case leftKind == IntKind && rightKind == StringKind: 227 const as = "string multiplication" 228 return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as)))) 229 230 case leftKind == BytesKind && rightKind == IntKind: 231 const as = "bytes multiplication" 232 return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as)))) 233 234 case leftKind == IntKind && rightKind == BytesKind: 235 const as = "bytes multiplication" 236 return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as)))) 237 238 case leftKind == ListKind && rightKind == IntKind: 239 left, right = right, left 240 fallthrough 241 242 case leftKind == IntKind && rightKind == ListKind: 243 // TODO: get rid of list multiplication. 244 245 list := &ListLit{} 246 x := MakeIdentLabel(c, "x", "") 247 248 for i := c.uint64(left, "list multiplier"); i > 0; i-- { 249 st := &StructLit{Decls: []Decl{ 250 &FieldReference{UpCount: 1, Label: x}, 251 }} 252 list.Elems = append(list.Elems, 253 &Comprehension{ 254 Clauses: &ForClause{ 255 Value: x, 256 Src: right, 257 Dst: &ValueClause{st}, 258 }, 259 Value: st, 260 }, 261 ) 262 } 263 if err := c.Err(); err != nil { 264 return err 265 } 266 267 n := &Vertex{} 268 n.AddConjunct(MakeRootConjunct(c.Env(0), list)) 269 n.Finalize(c) 270 271 return n 272 } 273 274 case FloatQuotientOp: 275 if leftKind&NumKind != 0 && rightKind&NumKind != 0 { 276 return c.Quo(c.Num(left, op), c.Num(right, op)) 277 } 278 279 case IntDivideOp: 280 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 281 return c.IntDiv(c.Num(left, op), c.Num(right, op)) 282 } 283 284 case IntModuloOp: 285 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 286 return c.IntMod(c.Num(left, op), c.Num(right, op)) 287 } 288 289 case IntQuotientOp: 290 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 291 return c.IntQuo(c.Num(left, op), c.Num(right, op)) 292 } 293 294 case IntRemainderOp: 295 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 296 return c.IntRem(c.Num(left, op), c.Num(right, op)) 297 } 298 } 299 300 return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)", 301 left, right, op, left.Kind(), right.Kind()) 302 } 303 304 func cmpTonode(c *OpContext, op Op, r int) Value { 305 result := false 306 switch op { 307 case LessThanOp: 308 result = r == -1 309 case LessEqualOp: 310 result = r != 1 311 case EqualOp, AndOp: 312 result = r == 0 313 case NotEqualOp: 314 result = r != 0 315 case GreaterEqualOp: 316 result = r != -1 317 case GreaterThanOp: 318 result = r == 1 319 } 320 return c.newBool(result) 321 }