cuelang.org/go@v0.10.1/internal/core/export/bounds.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 export 16 17 import ( 18 "cuelang.org/go/cue/ast" 19 "cuelang.org/go/internal/core/adt" 20 "github.com/cockroachdb/apd/v3" 21 ) 22 23 // boundSimplifier simplifies bound values into predeclared identifiers, if 24 // possible. 25 type boundSimplifier struct { 26 e *exporter 27 28 isInt bool 29 min *adt.BoundValue 30 minNum *adt.Num 31 max *adt.BoundValue 32 maxNum *adt.Num 33 } 34 35 func (s *boundSimplifier) add(v adt.Value) (used bool) { 36 switch x := v.(type) { 37 case *adt.BasicType: 38 switch x.K & adt.ScalarKinds { 39 case adt.IntKind: 40 s.isInt = true 41 return true 42 } 43 44 case *adt.BoundValue: 45 if adt.IsConcrete(x.Value) && x.Kind() == adt.IntKind { 46 s.isInt = true 47 } 48 switch x.Op { 49 case adt.GreaterThanOp: 50 if n, ok := x.Value.(*adt.Num); ok { 51 if s.min == nil || s.minNum.X.Cmp(&n.X) != 1 { 52 s.min = x 53 s.minNum = n 54 } 55 return true 56 } 57 58 case adt.GreaterEqualOp: 59 if n, ok := x.Value.(*adt.Num); ok { 60 if s.min == nil || s.minNum.X.Cmp(&n.X) == -1 { 61 s.min = x 62 s.minNum = n 63 } 64 return true 65 } 66 67 case adt.LessThanOp: 68 if n, ok := x.Value.(*adt.Num); ok { 69 if s.max == nil || s.maxNum.X.Cmp(&n.X) != -1 { 70 s.max = x 71 s.maxNum = n 72 } 73 return true 74 } 75 76 case adt.LessEqualOp: 77 if n, ok := x.Value.(*adt.Num); ok { 78 if s.max == nil || s.maxNum.X.Cmp(&n.X) == 1 { 79 s.max = x 80 s.maxNum = n 81 } 82 return true 83 } 84 } 85 } 86 87 return false 88 } 89 90 type builtinRange struct { 91 typ string 92 lo *apd.Decimal 93 hi *apd.Decimal 94 } 95 96 func makeDec(s string) *apd.Decimal { 97 d, _, err := apd.NewFromString(s) 98 if err != nil { 99 panic(err) 100 } 101 return d 102 } 103 104 func (s *boundSimplifier) expr(ctx *adt.OpContext) (e ast.Expr) { 105 if s.min == nil || s.max == nil { 106 return nil 107 } 108 switch { 109 case s.isInt: 110 t := s.matchRange(intRanges) 111 if t != "" { 112 e = ast.NewIdent(t) 113 break 114 } 115 if sign := s.minNum.X.Sign(); sign == -1 { 116 e = ast.NewIdent("int") 117 118 } else { 119 e = ast.NewIdent("uint") 120 if sign == 0 && s.min.Op == adt.GreaterEqualOp { 121 s.min = nil 122 break 123 } 124 } 125 fallthrough 126 default: 127 t := s.matchRange(floatRanges) 128 if t != "" { 129 e = wrapBin(e, ast.NewIdent(t), adt.AndOp) 130 } 131 } 132 133 if s.min != nil { 134 e = wrapBin(e, s.e.expr(nil, s.min), adt.AndOp) 135 } 136 if s.max != nil { 137 e = wrapBin(e, s.e.expr(nil, s.max), adt.AndOp) 138 } 139 return e 140 } 141 142 func (s *boundSimplifier) matchRange(ranges []builtinRange) (t string) { 143 for _, r := range ranges { 144 if !s.minNum.X.IsZero() && s.min.Op == adt.GreaterEqualOp && s.minNum.X.Cmp(r.lo) == 0 { 145 switch s.maxNum.X.Cmp(r.hi) { 146 case 0: 147 if s.max.Op == adt.LessEqualOp { 148 s.max = nil 149 } 150 s.min = nil 151 return r.typ 152 case -1: 153 if !s.minNum.X.IsZero() { 154 s.min = nil 155 return r.typ 156 } 157 case 1: 158 } 159 } else if s.max.Op == adt.LessEqualOp && s.maxNum.X.Cmp(r.hi) == 0 { 160 switch s.minNum.X.Cmp(r.lo) { 161 case -1: 162 case 0: 163 if s.min.Op == adt.GreaterEqualOp { 164 s.min = nil 165 } 166 fallthrough 167 case 1: 168 s.max = nil 169 return r.typ 170 } 171 } 172 } 173 return "" 174 } 175 176 var intRanges = []builtinRange{ 177 {"int8", makeDec("-128"), makeDec("127")}, 178 {"int16", makeDec("-32768"), makeDec("32767")}, 179 {"int32", makeDec("-2147483648"), makeDec("2147483647")}, 180 {"int64", makeDec("-9223372036854775808"), makeDec("9223372036854775807")}, 181 {"int128", makeDec("-170141183460469231731687303715884105728"), 182 makeDec("170141183460469231731687303715884105727")}, 183 184 {"uint8", makeDec("0"), makeDec("255")}, 185 {"uint16", makeDec("0"), makeDec("65535")}, 186 {"uint32", makeDec("0"), makeDec("4294967295")}, 187 {"uint64", makeDec("0"), makeDec("18446744073709551615")}, 188 {"uint128", makeDec("0"), makeDec("340282366920938463463374607431768211455")}, 189 190 // {"rune", makeDec("0"), makeDec(strconv.Itoa(0x10FFFF))}, 191 } 192 193 var floatRanges = []builtinRange{ 194 // 2**127 * (2**24 - 1) / 2**23 195 {"float32", 196 makeDec("-3.40282346638528859811704183484516925440e+38"), 197 makeDec("3.40282346638528859811704183484516925440e+38")}, 198 199 // 2**1023 * (2**53 - 1) / 2**52 200 {"float64", 201 makeDec("-1.797693134862315708145274237317043567981e+308"), 202 makeDec("1.797693134862315708145274237317043567981e+308")}, 203 } 204 205 func wrapBin(a, b ast.Expr, op adt.Op) ast.Expr { 206 if a == nil { 207 return b 208 } 209 if b == nil { 210 return a 211 } 212 return ast.NewBinExpr(op.Token(), a, b) 213 }