github.com/solo-io/cue@v0.4.7/internal/core/adt/feature.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 "fmt" 19 "strconv" 20 "strings" 21 22 "github.com/solo-io/cue/cue/ast" 23 "github.com/solo-io/cue/cue/errors" 24 "github.com/solo-io/cue/cue/literal" 25 "github.com/solo-io/cue/cue/token" 26 "github.com/solo-io/cue/internal" 27 ) 28 29 // A Feature is an encoded form of a label which comprises a compact 30 // representation of an integer or string label as well as a label type. 31 type Feature uint32 32 33 // TODO: create labels such that list are sorted first (or last with index.) 34 35 // InvalidLabel is an encoding of an erroneous label. 36 const ( 37 InvalidLabel Feature = 0x7 // 0xb111 38 39 // MaxIndex indicates the maximum number of unique strings that are used for 40 // labeles within this CUE implementation. 41 MaxIndex = 1<<28 - 1 42 ) 43 44 // These labels can be used for wildcard queries. 45 var ( 46 // AnyLabel represents any label or index. 47 AnyLabel Feature = 0 48 49 AnyDefinition Feature = makeLabel(MaxIndex, DefinitionLabel) 50 AnyHidden Feature = makeLabel(MaxIndex, HiddenLabel) 51 AnyRegular Feature = makeLabel(MaxIndex, StringLabel) 52 AnyIndex Feature = makeLabel(MaxIndex, IntLabel) 53 ) 54 55 // A StringIndexer coverts strings to and from an index that is unique for a 56 // given string. 57 type StringIndexer interface { 58 // ToIndex returns a unique positive index for s (0 < index < 2^28-1). 59 // 60 // For each pair of strings s and t it must return the same index if and 61 // only if s == t. 62 StringToIndex(s string) (index int64) 63 64 // ToString returns a string s for index such that ToIndex(s) == index. 65 IndexToString(index int64) string 66 } 67 68 // SelectorString reports the shortest string representation of f when used as a 69 // selector. 70 func (f Feature) SelectorString(index StringIndexer) string { 71 if f == 0 { 72 return "_" 73 } 74 x := f.safeIndex() 75 switch f.Typ() { 76 case IntLabel: 77 return strconv.Itoa(int(x)) 78 case StringLabel: 79 s := index.IndexToString(x) 80 if ast.IsValidIdent(s) && !internal.IsDefOrHidden(s) { 81 return s 82 } 83 return literal.String.Quote(s) 84 default: 85 return f.IdentString(index) 86 } 87 } 88 89 // IdentString reports the identifier of f. The result is undefined if f 90 // is not an identifier label. 91 func (f Feature) IdentString(index StringIndexer) string { 92 s := index.IndexToString(f.safeIndex()) 93 if f.IsHidden() { 94 if p := strings.IndexByte(s, '\x00'); p >= 0 { 95 s = s[:p] 96 } 97 } 98 return s 99 } 100 101 // PkgID returns the package identifier, composed of the module and package 102 // name, associated with this identifier. It will return "" if this is not 103 // a hidden label. 104 func (f Feature) PkgID(index StringIndexer) string { 105 if !f.IsHidden() { 106 return "" 107 } 108 s := index.IndexToString(f.safeIndex()) 109 if p := strings.IndexByte(s, '\x00'); p >= 0 { 110 s = s[p+1:] 111 } 112 return s 113 } 114 115 // StringValue reports the string value of f, which must be a string label. 116 func (f Feature) StringValue(index StringIndexer) string { 117 if !f.IsString() { 118 panic("not a string label") 119 } 120 x := f.safeIndex() 121 return index.IndexToString(x) 122 } 123 124 // ToValue converts a label to a value, which will be a Num for integer labels 125 // and a String for string labels. It panics when f is not a regular label. 126 func (f Feature) ToValue(ctx *OpContext) Value { 127 if !f.IsRegular() { 128 panic("not a regular label") 129 } 130 // TODO: Handle special regular values: invalid and AnyRegular. 131 if f.IsInt() { 132 return ctx.NewInt64(int64(f.Index())) 133 } 134 x := f.safeIndex() 135 str := ctx.IndexToString(x) 136 return ctx.NewString(str) 137 } 138 139 // StringLabel converts s to a string label. 140 func (c *OpContext) StringLabel(s string) Feature { 141 return labelFromValue(c, nil, &String{Str: s}) 142 } 143 144 // MakeStringLabel creates a label for the given string. 145 func MakeStringLabel(r StringIndexer, s string) Feature { 146 i := r.StringToIndex(s) 147 148 // TODO: set position if it exists. 149 f, err := MakeLabel(nil, i, StringLabel) 150 if err != nil { 151 panic("out of free string slots") 152 } 153 return f 154 } 155 156 // MakeIdentLabel creates a label for the given identifier. 157 func MakeIdentLabel(r StringIndexer, s, pkgpath string) Feature { 158 t := StringLabel 159 switch { 160 case strings.HasPrefix(s, "_#"): 161 t = HiddenDefinitionLabel 162 s = fmt.Sprintf("%s\x00%s", s, pkgpath) 163 case strings.HasPrefix(s, "#"): 164 t = DefinitionLabel 165 case strings.HasPrefix(s, "_"): 166 s = fmt.Sprintf("%s\x00%s", s, pkgpath) 167 t = HiddenLabel 168 } 169 i := r.StringToIndex(s) 170 f, err := MakeLabel(nil, i, t) 171 if err != nil { 172 panic("out of free string slots") 173 } 174 return f 175 } 176 177 const msgGround = "invalid non-ground value %s (must be concrete %s)" 178 179 func labelFromValue(c *OpContext, src Expr, v Value) Feature { 180 var i int64 181 var t FeatureType 182 if isError(v) { 183 return InvalidLabel 184 } 185 switch v.Kind() { 186 case IntKind, NumKind: 187 x, _ := Unwrap(v).(*Num) 188 if x == nil { 189 c.addErrf(IncompleteError, pos(v), msgGround, v, "int") 190 return InvalidLabel 191 } 192 t = IntLabel 193 var err error 194 i, err = x.X.Int64() 195 if err != nil || x.K != IntKind { 196 if src == nil { 197 src = v 198 } 199 c.AddErrf("invalid index %v: %v", src, err) 200 return InvalidLabel 201 } 202 if i < 0 { 203 switch src.(type) { 204 case nil, *Num, *UnaryExpr: 205 // If the value is a constant, we know it is always an error. 206 // UnaryExpr is an approximation for a constant value here. 207 c.AddErrf("invalid index %s (index must be non-negative)", x) 208 default: 209 // Use a different message is it is the result of evaluation. 210 c.AddErrf("index %s out of range [%s]", src, x) 211 } 212 return InvalidLabel 213 } 214 215 case StringKind: 216 x, _ := Unwrap(v).(*String) 217 if x == nil { 218 c.addErrf(IncompleteError, pos(v), msgGround, v, "string") 219 return InvalidLabel 220 } 221 t = StringLabel 222 i = c.StringToIndex(x.Str) 223 224 default: 225 if src != nil { 226 c.AddErrf("invalid index %s (invalid type %v)", src, v.Kind()) 227 } else { 228 c.AddErrf("invalid index type %v", v.Kind()) 229 } 230 return InvalidLabel 231 } 232 233 // TODO: set position if it exists. 234 f, err := MakeLabel(nil, i, t) 235 if err != nil { 236 c.AddErr(err) 237 } 238 return f 239 } 240 241 // MakeLabel creates a label. It reports an error if the index is out of range. 242 func MakeLabel(src ast.Node, index int64, f FeatureType) (Feature, errors.Error) { 243 if 0 > index || index > MaxIndex-1 { 244 p := token.NoPos 245 if src != nil { 246 p = src.Pos() 247 } 248 return InvalidLabel, 249 errors.Newf(p, "int label out of range (%d not >=0 and <= %d)", 250 index, MaxIndex-1) 251 } 252 return Feature(index)<<indexShift | Feature(f), nil 253 } 254 255 func makeLabel(index int64, f FeatureType) Feature { 256 return Feature(index)<<indexShift | Feature(f) 257 } 258 259 // A FeatureType indicates the type of label. 260 type FeatureType int8 261 262 const ( 263 StringLabel FeatureType = 0 // 0b000 264 IntLabel FeatureType = 1 // 0b001 265 DefinitionLabel FeatureType = 3 // 0b011 266 HiddenLabel FeatureType = 6 // 0b110 267 HiddenDefinitionLabel FeatureType = 7 // 0b111 268 269 // letLabel FeatureType = 0b010 270 271 fTypeMask Feature = 7 // 0b111 272 273 indexShift = 3 274 ) 275 276 func (f FeatureType) IsDef() bool { 277 return f&DefinitionLabel == DefinitionLabel 278 } 279 280 func (f FeatureType) IsHidden() bool { 281 return f&HiddenLabel == HiddenLabel 282 } 283 284 // IsValid reports whether f is a valid label. 285 func (f Feature) IsValid() bool { return f != InvalidLabel } 286 287 // Typ reports the type of label. 288 func (f Feature) Typ() FeatureType { return FeatureType(f & fTypeMask) } 289 290 // IsRegular reports whether a label represents a data field. 291 func (f Feature) IsRegular() bool { return f.Typ() <= IntLabel } 292 293 // IsString reports whether a label represents a regular field. 294 func (f Feature) IsString() bool { return f.Typ() == StringLabel } 295 296 // IsDef reports whether the label is a definition (an identifier starting with 297 // # or #_. 298 func (f Feature) IsDef() bool { 299 if f == InvalidLabel { 300 // TODO(perf): do more mask trickery to avoid this branch. 301 return false 302 } 303 return f.Typ().IsDef() 304 } 305 306 // IsInt reports whether this is an integer index. 307 func (f Feature) IsInt() bool { return f.Typ() == IntLabel } 308 309 // IsHidden reports whether this label is hidden (an identifier starting with 310 // _ or #_). 311 func (f Feature) IsHidden() bool { 312 if f == InvalidLabel { 313 // TODO(perf): do more mask trickery to avoid this branch. 314 return false 315 } 316 return f.Typ().IsHidden() 317 } 318 319 // Index reports the abstract index associated with f. 320 func (f Feature) Index() int { 321 return int(f >> indexShift) 322 } 323 324 // SafeIndex reports the abstract index associated with f, setting MaxIndex to 0. 325 func (f Feature) safeIndex() int64 { 326 x := int(f >> indexShift) 327 if x == MaxIndex { 328 x = 0 // Safety, MaxIndex means any 329 } 330 return int64(x) 331 } 332 333 // TODO: should let declarations be implemented as fields? 334 // func (f Feature) isLet() bool { return f.typ() == letLabel }