cuelang.org/go@v0.10.1/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&NumberKind != 0 && rightKind&NumberKind != 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&NumberKind != 0 && rightKind&NumberKind != 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(true) 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&NumberKind != 0 && rightKind&NumberKind != 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&NumberKind != 0 && rightKind&NumberKind != 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 // for x in expr { x } 186 forClause := func(src Expr) *Comprehension { 187 s := &StructLit{Decls: []Decl{ 188 &FieldReference{UpCount: 1, Label: x}, 189 }} 190 return &Comprehension{ 191 Clauses: []Yielder{ 192 &ForClause{ 193 Value: x, 194 Src: src, 195 }, 196 }, 197 Value: s, 198 } 199 } 200 201 list := &ListLit{ 202 Elems: []Elem{ 203 forClause(left), 204 forClause(right), 205 }, 206 } 207 208 n := c.newInlineVertex(nil, nil, MakeConjunct(c.Env(0), list, c.ci)) 209 n.CompleteArcs(c) 210 211 // NOTE: if we set isData to true, whoever processes the result will 212 // avoid having to process the expressions again. This improves 213 // performance. It also change the a potential cycle error message 214 // to a more concrete messages as if this result was unified as is. 215 // TODO: uncomment this and see if we like the result. 216 // n.isData = true 217 218 return n 219 } 220 221 case SubtractOp: 222 return c.Sub(c.Num(left, op), c.Num(right, op)) 223 224 case MultiplyOp: 225 switch { 226 // float 227 case leftKind&NumberKind != 0 && rightKind&NumberKind != 0: 228 return c.Mul(c.Num(left, op), c.Num(right, op)) 229 230 case leftKind == StringKind && rightKind == IntKind: 231 const as = "string multiplication" 232 return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as)))) 233 234 case leftKind == IntKind && rightKind == StringKind: 235 const as = "string multiplication" 236 return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as)))) 237 238 case leftKind == BytesKind && rightKind == IntKind: 239 const as = "bytes multiplication" 240 return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as)))) 241 242 case leftKind == IntKind && rightKind == BytesKind: 243 const as = "bytes multiplication" 244 return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as)))) 245 246 case leftKind == ListKind && rightKind == IntKind: 247 left, right = right, left 248 fallthrough 249 250 case leftKind == IntKind && rightKind == ListKind: 251 // TODO: get rid of list multiplication. 252 253 list := &ListLit{} 254 x := MakeIdentLabel(c, "x", "") 255 256 for i := c.uint64(left, "list multiplier"); i > 0; i-- { 257 st := &StructLit{Decls: []Decl{ 258 &FieldReference{UpCount: 1, Label: x}, 259 }} 260 list.Elems = append(list.Elems, 261 &Comprehension{ 262 Clauses: []Yielder{ 263 &ForClause{ 264 Value: x, 265 Src: right, 266 }, 267 }, 268 Value: st, 269 }, 270 ) 271 } 272 if err := c.Err(); err != nil { 273 return err 274 } 275 276 n := c.newInlineVertex(nil, nil, MakeConjunct(c.Env(0), list, c.ci)) 277 n.CompleteArcs(c) 278 279 return n 280 } 281 282 case FloatQuotientOp: 283 if leftKind&NumberKind != 0 && rightKind&NumberKind != 0 { 284 return c.Quo(c.Num(left, op), c.Num(right, op)) 285 } 286 287 case IntDivideOp: 288 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 289 return c.IntDiv(c.Num(left, op), c.Num(right, op)) 290 } 291 292 case IntModuloOp: 293 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 294 return c.IntMod(c.Num(left, op), c.Num(right, op)) 295 } 296 297 case IntQuotientOp: 298 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 299 return c.IntQuo(c.Num(left, op), c.Num(right, op)) 300 } 301 302 case IntRemainderOp: 303 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 304 return c.IntRem(c.Num(left, op), c.Num(right, op)) 305 } 306 } 307 308 return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)", 309 left, right, op, left.Kind(), right.Kind()) 310 } 311 312 func cmpTonode(c *OpContext, op Op, r int) Value { 313 result := false 314 switch op { 315 case LessThanOp: 316 result = r == -1 317 case LessEqualOp: 318 result = r != 1 319 case EqualOp, AndOp: 320 result = r == 0 321 case NotEqualOp: 322 result = r != 0 323 case GreaterEqualOp: 324 result = r != -1 325 case GreaterThanOp: 326 result = r == 1 327 } 328 return c.newBool(result) 329 }