this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 310 lines 7.4 kB view raw
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 15package adt 16 17import ( 18 "bytes" 19 "strings" 20 21 "github.com/cockroachdb/apd/v3" 22 23 "cuelang.org/go/internal" 24) 25 26// SimplifyBounds collapses bounds if possible. The bound values must be 27// concrete. It returns nil if the bound values cannot be collapsed. 28// 29// k represents additional type constraints, such as `int`. 30func SimplifyBounds(ctx *OpContext, k Kind, x, y *BoundValue) Value { 31 xv := x.Value 32 yv := y.Value 33 34 cmp, xCat := opInfo(x.Op) 35 _, yCat := opInfo(y.Op) 36 37 // k := x.Kind() & y.Kind() 38 39 switch { 40 case xCat == yCat: 41 switch x.Op { 42 // NOTE: EqualOp should not happen, but include it defensively. 43 // Maybe an API would use it, for instance. 44 case EqualOp, NotEqualOp, MatchOp, NotMatchOp: 45 if BinOpBool(ctx, nil, EqualOp, xv, yv) { 46 return x 47 } 48 return nil // keep both bounds 49 } 50 51 // xCat == yCat && x.Op != NotEqualOp 52 // > a & >= b 53 // > a if a >= b 54 // >= b if a < b 55 // > a & > b 56 // > a if a >= b 57 // > b if a < b 58 // >= a & > b 59 // >= a if a > b 60 // > b if a <= b 61 // >= a & >= b 62 // >= a if a > b 63 // >= b if a <= b 64 // inverse is true as well. 65 66 // Tighten bound. 67 if BinOpBool(ctx, nil, cmp, xv, yv) { 68 return x 69 } 70 return y 71 72 case xCat == -yCat && k == StringKind: 73 if xCat == -1 { 74 x, y = y, x 75 xv, yv = yv, xv 76 } 77 78 a, aOK := xv.(*String) 79 b, bOK := yv.(*String) 80 81 if !aOK || !bOK { 82 break 83 } 84 85 switch diff := strings.Compare(a.Str, b.Str); diff { 86 case -1: 87 case 0: 88 if x.Op == GreaterEqualOp && y.Op == LessEqualOp { 89 return nil 90 } 91 fallthrough 92 93 case 1: 94 return ctx.NewErrf("incompatible string bounds %v and %v", y, x) 95 } 96 97 case xCat == -yCat && k == BytesKind: 98 if xCat == -1 { 99 x, y = y, x 100 xv, yv = yv, xv 101 } 102 103 a, aOK := xv.(*Bytes) 104 b, bOK := yv.(*Bytes) 105 106 if !aOK || !bOK { 107 break 108 } 109 110 switch diff := bytes.Compare(a.B, b.B); diff { 111 case -1: 112 case 0: 113 if x.Op == GreaterEqualOp && y.Op == LessEqualOp { 114 return nil 115 } 116 fallthrough 117 118 case 1: 119 return ctx.NewErrf("incompatible bytes bounds %v and %v", y, x) 120 } 121 122 case xCat == -yCat: 123 if xCat == -1 { 124 x, y = y, x 125 xv, yv = yv, xv 126 } 127 a, aOK := xv.(*Num) 128 b, bOK := yv.(*Num) 129 if !aOK || !bOK || a.X.Form != apd.Finite || b.X.Form != apd.Finite { 130 // Nothing to do if either bound is not a finite number. 131 break 132 } 133 134 var d apd.Decimal 135 lo, hi := a.X, b.X 136 if k&FloatKind == 0 { 137 // Readjust bounds for integers if the bounds have decimal places, 138 // which are represented as a negative exponent to multiply with the coefficient. 139 // We reset lo and hi to empty apd.Decimal values to not modify the originals, 140 // given that apd.Decimal contains pointers which are carried with shallow copies. 141 if a.X.Exponent < 0 { 142 lo = apd.Decimal{} 143 if x.Op == GreaterEqualOp { 144 // >=3.4 ==> >=4 145 internal.BaseContext.Ceil(&lo, &a.X) 146 } else { 147 // >3.4 ==> >3 148 internal.BaseContext.Floor(&lo, &a.X) 149 } 150 } 151 if b.X.Exponent < 0 { 152 hi = apd.Decimal{} 153 if y.Op == LessEqualOp { 154 // <=2.3 ==> <= 2 155 internal.BaseContext.Floor(&hi, &b.X) 156 } else { 157 // <2.3 ==> < 3 158 internal.BaseContext.Ceil(&hi, &b.X) 159 } 160 } 161 } 162 163 // Negative or zero minimum with a large positive maximum 164 // common with e.g. int32 being ">=-2147483648 & <=2147483647" 165 // or uint16 being ">=0 & <=65535". 166 // 167 // We detect a large positive maximum by checking that the exponent isn't negative 168 // and that the coefficient has at least two digits, so its value must be at least 10. 169 // When the maximum is at least 10 and the minimum is at most 0, 170 // the subtraction will be positive and larger than 2, so we can avoid all the work below. 171 // This is important given that a subtraction allocates a new number for the result. 172 hiSign, loSign := hi.Sign(), lo.Sign() 173 if hiSign > 0 && loSign <= 0 && hi.Exponent >= 0 && hi.NumDigits() > 1 { 174 break 175 } 176 177 cond, err := internal.BaseContext.Sub(&d, &hi, &lo) 178 if cond.Inexact() || err != nil { 179 break 180 } 181 182 // attempt simplification 183 // numbers 184 // >=a & <=b 185 // a if a == b 186 // _|_ if b < a 187 // >=a & <b 188 // _|_ if b <= a 189 // >a & <=b 190 // _|_ if b <= a 191 // >a & <b 192 // _|_ if b <= a 193 194 // integers 195 // >=a & <=b 196 // a if b-a == 0 197 // _|_ if b < a 198 // >=a & <b 199 // a if b-a == 1 200 // _|_ if b <= a 201 // >a & <=b 202 // b if b-a == 1 203 // _|_ if b <= a 204 // >a & <b 205 // a+1 if b-a == 2 206 // _|_ if b <= a 207 208 if d.Negative { 209 return errIncompatibleBounds(ctx, k, x, y) 210 } 211 switch diff, err := d.Int64(); { 212 case diff == 1: 213 if k&FloatKind == 0 { 214 if x.Op == GreaterEqualOp && y.Op == LessThanOp { 215 return nil 216 } 217 if x.Op == GreaterThanOp && y.Op == LessEqualOp { 218 return nil 219 } 220 if x.Op == GreaterThanOp && y.Op == LessThanOp { 221 return ctx.NewErrf("incompatible integer bounds %v and %v", x, y) 222 } 223 } 224 225 case diff == 2: 226 if k&FloatKind == 0 && x.Op == GreaterThanOp && y.Op == LessThanOp { 227 return nil 228 } 229 230 case diff == 0 && err == nil: 231 if x.Op == GreaterEqualOp && y.Op == LessEqualOp { 232 return nil 233 } 234 return errIncompatibleBounds(ctx, k, x, y) 235 } 236 237 case x.Op == NotEqualOp: 238 if !BinOpBool(ctx, nil, y.Op, xv, yv) { 239 return y 240 } 241 242 case y.Op == NotEqualOp: 243 if !BinOpBool(ctx, nil, x.Op, yv, xv) { 244 return x 245 } 246 } 247 return nil 248} 249 250func errIncompatibleBounds(ctx *OpContext, k Kind, x, y *BoundValue) *Bottom { 251 if k == IntKind { 252 return ctx.NewErrf("incompatible integer bounds %v and %v", y, x) 253 } else { 254 return ctx.NewErrf("incompatible number bounds %v and %v", y, x) 255 } 256} 257 258func opInfo(op Op) (cmp Op, norm int) { 259 switch op { 260 case GreaterThanOp: 261 return GreaterEqualOp, 1 262 case GreaterEqualOp: 263 return GreaterThanOp, 1 264 case LessThanOp: 265 return LessEqualOp, -1 266 case LessEqualOp: 267 return LessThanOp, -1 268 case NotEqualOp: 269 return NotEqualOp, 0 270 case MatchOp: 271 return MatchOp, 2 272 case NotMatchOp: 273 return NotMatchOp, 3 274 } 275 panic("cue: unreachable") 276} 277 278// SimplifyValidator simplifies non-bound validators. 279// 280// Currently this only checks for pure equality. In the future this can be used 281// to simplify certain builtin validators analogously to how we simplify bounds 282// now. 283func SimplifyValidator(ctx *OpContext, v, w Conjunct) (c Conjunct, ok bool) { 284 switch x := v.x.(type) { 285 case *BuiltinValidator: 286 switch y := w.x.(type) { 287 case *BuiltinValidator: 288 if x == y { 289 return v, true 290 } 291 if x.Builtin != y.Builtin || len(x.Args) != len(y.Args) { 292 return c, false 293 } 294 for i, a := range x.Args { 295 b := y.Args[i] 296 if v, ok := a.(*Vertex); ok { 297 v.Finalize(ctx) 298 } 299 if v, ok := b.(*Vertex); ok { 300 v.Finalize(ctx) 301 } 302 if !Equal(ctx, a, b, CheckStructural) { 303 return c, false 304 } 305 } 306 return v, true 307 } 308 } 309 return c, false 310}