cuelang.org/go@v0.13.0/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 Node: c.vertex, 36 } 37 } 38 if right.Concreteness() > Concrete { 39 return &Bottom{ 40 Code: IncompleteError, 41 Err: c.Newf(msg, right, op), 42 Node: c.vertex, 43 } 44 } 45 46 if err := CombineErrors(c.src, left, right); err != nil { 47 return err 48 } 49 50 switch op { 51 case EqualOp: 52 switch { 53 case leftKind == NullKind && rightKind == NullKind: 54 return c.newBool(true) 55 56 case leftKind == NullKind || rightKind == NullKind: 57 return c.newBool(false) 58 59 case leftKind == BoolKind: 60 return c.newBool(c.BoolValue(left) == c.BoolValue(right)) 61 62 case leftKind == StringKind: 63 // normalize? 64 return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right))) 65 66 case leftKind == BytesKind: 67 return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op))) 68 69 case leftKind&NumberKind != 0 && rightKind&NumberKind != 0: 70 // n := c.newNum() 71 return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X)) 72 73 case leftKind == ListKind && rightKind == ListKind: 74 x := c.Elems(left) 75 y := c.Elems(right) 76 if len(x) != len(y) { 77 return c.newBool(false) 78 } 79 for i, e := range x { 80 a, _ := c.concrete(nil, e, op) 81 b, _ := c.concrete(nil, y[i], op) 82 if !test(c, EqualOp, a, b) { 83 return c.newBool(false) 84 } 85 } 86 return c.newBool(true) 87 } 88 89 case NotEqualOp: 90 switch { 91 case leftKind == NullKind && rightKind == NullKind: 92 return c.newBool(false) 93 94 case leftKind == NullKind || rightKind == NullKind: 95 return c.newBool(true) 96 97 case leftKind == BoolKind: 98 return c.newBool(c.boolValue(left, op) != c.boolValue(right, op)) 99 100 case leftKind == StringKind: 101 // normalize? 102 return cmpTonode(c, op, strings.Compare(c.StringValue(left), c.StringValue(right))) 103 104 case leftKind == BytesKind: 105 return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op))) 106 107 case leftKind&NumberKind != 0 && rightKind&NumberKind != 0: 108 // n := c.newNum() 109 return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X)) 110 111 case leftKind == ListKind && rightKind == ListKind: 112 x := c.Elems(left) 113 y := c.Elems(right) 114 if len(x) != len(y) { 115 return c.newBool(true) 116 } 117 for i, e := range x { 118 a, _ := c.concrete(nil, e, op) 119 b, _ := c.concrete(nil, y[i], op) 120 if !test(c, EqualOp, a, b) { 121 return c.newBool(true) 122 } 123 } 124 return c.newBool(false) 125 } 126 127 case LessThanOp, LessEqualOp, GreaterEqualOp, GreaterThanOp: 128 switch { 129 case leftKind == StringKind && rightKind == StringKind: 130 // normalize? 131 return cmpTonode(c, op, strings.Compare(c.stringValue(left, op), c.stringValue(right, op))) 132 133 case leftKind == BytesKind && rightKind == BytesKind: 134 return cmpTonode(c, op, bytes.Compare(c.bytesValue(left, op), c.bytesValue(right, op))) 135 136 case leftKind&NumberKind != 0 && rightKind&NumberKind != 0: 137 // n := c.newNum(left, right) 138 return cmpTonode(c, op, c.Num(left, op).X.Cmp(&c.Num(right, op).X)) 139 } 140 141 case BoolAndOp: 142 return c.newBool(c.boolValue(left, op) && c.boolValue(right, op)) 143 144 case BoolOrOp: 145 return c.newBool(c.boolValue(left, op) || c.boolValue(right, op)) 146 147 case MatchOp: 148 // if y.re == nil { 149 // // This really should not happen, but leave in for safety. 150 // b, err := Regexp.MatchString(str, x.str) 151 // if err != nil { 152 // return c.Errf(Src, "error parsing Regexp: %v", err) 153 // } 154 // return boolTonode(Src, b) 155 // } 156 return c.newBool(c.regexp(right).MatchString(c.stringValue(left, op))) 157 158 case NotMatchOp: 159 return c.newBool(!c.regexp(right).MatchString(c.stringValue(left, op))) 160 161 case AddOp: 162 switch { 163 case leftKind&NumberKind != 0 && rightKind&NumberKind != 0: 164 return c.Add(c.Num(left, op), c.Num(right, op)) 165 166 case leftKind == StringKind && rightKind == StringKind: 167 return c.NewString(c.StringValue(left) + c.StringValue(right)) 168 169 case leftKind == BytesKind && rightKind == BytesKind: 170 ba := c.bytesValue(left, op) 171 bb := c.bytesValue(right, op) 172 b := make([]byte, len(ba)+len(bb)) 173 copy(b, ba) 174 copy(b[len(ba):], bb) 175 return c.newBytes(b) 176 177 case leftKind == ListKind && rightKind == ListKind: 178 return c.NewErrf("Addition of lists is superseded by list.Concat; see https://cuelang.org/e/v0.11-list-arithmetic") 179 } 180 181 case SubtractOp: 182 return c.Sub(c.Num(left, op), c.Num(right, op)) 183 184 case MultiplyOp: 185 switch { 186 // float 187 case leftKind&NumberKind != 0 && rightKind&NumberKind != 0: 188 return c.Mul(c.Num(left, op), c.Num(right, op)) 189 190 case leftKind == StringKind && rightKind == IntKind: 191 const as = "string multiplication" 192 return c.NewString(strings.Repeat(c.stringValue(left, as), int(c.uint64(right, as)))) 193 194 case leftKind == IntKind && rightKind == StringKind: 195 const as = "string multiplication" 196 return c.NewString(strings.Repeat(c.stringValue(right, as), int(c.uint64(left, as)))) 197 198 case leftKind == BytesKind && rightKind == IntKind: 199 const as = "bytes multiplication" 200 return c.newBytes(bytes.Repeat(c.bytesValue(left, as), int(c.uint64(right, as)))) 201 202 case leftKind == IntKind && rightKind == BytesKind: 203 const as = "bytes multiplication" 204 return c.newBytes(bytes.Repeat(c.bytesValue(right, as), int(c.uint64(left, as)))) 205 206 case leftKind == IntKind && rightKind == ListKind: 207 fallthrough 208 case leftKind == ListKind && rightKind == IntKind: 209 return c.NewErrf("Multiplication of lists is superseded by list.Repeat; see https://cuelang.org/e/v0.11-list-arithmetic") 210 } 211 212 case FloatQuotientOp: 213 if leftKind&NumberKind != 0 && rightKind&NumberKind != 0 { 214 return c.Quo(c.Num(left, op), c.Num(right, op)) 215 } 216 217 case IntDivideOp: 218 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 219 return c.IntDiv(c.Num(left, op), c.Num(right, op)) 220 } 221 222 case IntModuloOp: 223 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 224 return c.IntMod(c.Num(left, op), c.Num(right, op)) 225 } 226 227 case IntQuotientOp: 228 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 229 return c.IntQuo(c.Num(left, op), c.Num(right, op)) 230 } 231 232 case IntRemainderOp: 233 if leftKind&IntKind != 0 && rightKind&IntKind != 0 { 234 return c.IntRem(c.Num(left, op), c.Num(right, op)) 235 } 236 } 237 238 return c.NewErrf("invalid operands %s and %s to '%s' (type %s and %s)", 239 left, right, op, left.Kind(), right.Kind()) 240 } 241 242 func cmpTonode(c *OpContext, op Op, r int) Value { 243 result := false 244 switch op { 245 case LessThanOp: 246 result = r == -1 247 case LessEqualOp: 248 result = r != 1 249 case EqualOp, AndOp: 250 result = r == 0 251 case NotEqualOp: 252 result = r != 0 253 case GreaterEqualOp: 254 result = r != -1 255 case GreaterThanOp: 256 result = r == 1 257 } 258 return c.newBool(result) 259 }