this repo has no description
0
fork

Configure Feed

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

at master 390 lines 10 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 "fmt" 19 "strconv" 20 "strings" 21 22 "cuelang.org/go/cue/ast" 23 "cuelang.org/go/cue/errors" 24 "cuelang.org/go/cue/literal" 25 "cuelang.org/go/cue/token" 26) 27 28// A Feature is an encoded form of a label which comprises a compact 29// representation of an integer or string label as well as a label type. 30type Feature uint32 31 32// TODO: create labels such that list are sorted first (or last with index.) 33 34// InvalidLabel is an encoding of an erroneous label. 35const ( 36 InvalidLabel Feature = 0 37 38 // MaxIndex indicates the maximum number of unique strings that are used for 39 // labels within this CUE implementation. 40 MaxIndex = 1<<(32-indexShift) - 1 41) 42 43// These labels can be used for wildcard queries. 44var ( 45 AnyDefinition Feature = makeLabel(MaxIndex, DefinitionLabel) 46 AnyHidden Feature = makeLabel(MaxIndex, HiddenLabel) 47 AnyString Feature = makeLabel(MaxIndex, StringLabel) 48 AnyIndex Feature = makeLabel(MaxIndex, IntLabel) 49) 50 51// A StringIndexer coverts strings to and from an index that is unique for a 52// given string. 53type StringIndexer interface { 54 // ToIndex returns a unique positive index for s (0 < index < 2^28-1). 55 // 56 // For each pair of strings s and t it must return the same index if and 57 // only if s == t. 58 StringToIndex(s string) (index int64) 59 60 // ToString returns a string s for index such that ToIndex(s) == index. 61 IndexToString(index int64) string 62 63 // NextUniqueID returns a new unique identifier. 64 NextUniqueID() uint64 65} 66 67// SelectorString reports the shortest string representation of f when used as a 68// selector. 69func (f Feature) SelectorString(index StringIndexer) string { 70 x := f.safeIndex() 71 switch f.Typ() { 72 case IntLabel: 73 if f == AnyIndex { 74 return "_" 75 } 76 return strconv.Itoa(int(x)) 77 case StringLabel: 78 if f == AnyString { 79 return "_" 80 } 81 s := index.IndexToString(x) 82 if ast.StringLabelNeedsQuoting(s) { 83 return literal.Label.Quote(s) 84 } 85 return s 86 default: 87 return f.IdentString(index) 88 } 89} 90 91// IdentString reports the identifier of f. The result is undefined if f 92// is not an identifier label. 93func (f Feature) IdentString(index StringIndexer) string { 94 s := index.IndexToString(f.safeIndex()) 95 if f.IsHidden() || f.IsLet() { 96 if p := strings.IndexByte(s, '\x00'); p >= 0 { 97 s = s[:p] 98 } 99 } 100 return s 101} 102 103// PkgID returns the package identifier, composed of the module and package 104// name, associated with this identifier. It will return "" if this is not 105// a hidden label. 106func (f Feature) PkgID(index StringIndexer) string { 107 if !f.IsHidden() { 108 return "" 109 } 110 s := index.IndexToString(f.safeIndex()) 111 if p := strings.IndexByte(s, '\x00'); p >= 0 { 112 s = s[p+1:] 113 } 114 return s 115} 116 117// StringValue reports the string value of f, which must be a string label. 118func (f Feature) StringValue(index StringIndexer) string { 119 if !f.IsString() { 120 panic("not a string label") 121 } 122 x := f.safeIndex() 123 return index.IndexToString(x) 124} 125 126// RawString reports the underlying string value of f without interpretation. 127func (f Feature) RawString(index StringIndexer) string { 128 x := f.safeIndex() 129 return index.IndexToString(x) 130} 131 132// ToValue converts a label to a value, which will be a Num for integer labels 133// and a String for string labels. It panics when f is not a regular label. 134func (f Feature) ToValue(ctx *OpContext) Value { 135 if !f.IsRegular() { 136 panic("not a regular label") 137 } 138 // TODO: Handle special regular values: invalid and AnyRegular. 139 if f.IsInt() { 140 return ctx.NewInt64(int64(f.Index())) 141 } 142 x := f.safeIndex() 143 str := ctx.IndexToString(x) 144 return ctx.NewString(str) 145} 146 147// StringLabel converts s to a string label. 148func (c *OpContext) StringLabel(s string) Feature { 149 return LabelFromValue(c, nil, &String{Str: s}) 150} 151 152// MakeStringLabel creates a label for the given string. 153func MakeStringLabel(r StringIndexer, s string) Feature { 154 i := r.StringToIndex(s) 155 156 // TODO: set position if it exists. 157 f, err := MakeLabel(nil, i, StringLabel) 158 if err != nil { 159 panic("out of free string slots") 160 } 161 return f 162} 163 164// MakeIdentLabel creates a label for the given identifier. 165func MakeIdentLabel(r StringIndexer, s, pkgpath string) Feature { 166 t := StringLabel 167 switch { 168 case strings.HasPrefix(s, "_#"): 169 t = HiddenDefinitionLabel 170 s = HiddenKey(s, pkgpath) 171 case strings.HasPrefix(s, "#"): 172 t = DefinitionLabel 173 case strings.HasPrefix(s, "_"): 174 s = HiddenKey(s, pkgpath) 175 t = HiddenLabel 176 } 177 i := r.StringToIndex(s) 178 f, err := MakeLabel(nil, i, t) 179 if err != nil { 180 panic("out of free string slots") 181 } 182 return f 183} 184 185// HiddenKey constructs the uniquely identifying string for a hidden fields and 186// its package. 187func HiddenKey(s, pkgPath string) string { 188 // TODO: Consider just using space instead of \x00. 189 return fmt.Sprintf("%s\x00%s", s, pkgPath) 190} 191 192// MakeNamedLabel creates a feature for the given name and feature type. 193func MakeNamedLabel(r StringIndexer, t FeatureType, s string) Feature { 194 i := r.StringToIndex(s) 195 f, err := MakeLabel(nil, i, t) 196 if err != nil { 197 panic("out of free string slots") 198 } 199 return f 200} 201 202// MakeLetLabel creates a label for the given let identifier s. 203// 204// A let declaration is always logically unique within its scope and will never 205// unify with a let field of another struct. This is enforced by ensuring that 206// the let identifier is unique across an entire configuration. This, in turn, 207// is done by adding a unique number to each let identifier. 208func MakeLetLabel(r StringIndexer, s string) Feature { 209 id := r.NextUniqueID() 210 s = fmt.Sprintf("%s\x00%X", s, id) 211 i := r.StringToIndex(s) 212 f, err := MakeLabel(nil, i, LetLabel) 213 if err != nil { 214 panic("out of free string slots") 215 } 216 return f 217} 218 219// MakeIntLabel creates an integer label. 220func MakeIntLabel(t FeatureType, i int64) Feature { 221 f, err := MakeLabel(nil, i, t) 222 if err != nil { 223 panic("index out of range") 224 } 225 return f 226} 227 228const msgGround = "invalid non-ground value %s (must be concrete %s)" 229 230func LabelFromValue(c *OpContext, src Expr, v Value) Feature { 231 v, _ = c.getDefault(v) 232 233 var i int64 234 var t FeatureType 235 if isError(v) { 236 return InvalidLabel 237 } 238 switch v.Kind() { 239 case IntKind, NumberKind: 240 x, _ := Unwrap(v).(*Num) 241 if x == nil { 242 c.addErrf(IncompleteError, Pos(v), msgGround, v, "int") 243 return InvalidLabel 244 } 245 t = IntLabel 246 var err error 247 i, err = x.X.Int64() 248 if err != nil || x.K != IntKind { 249 if src == nil { 250 src = v 251 } 252 c.AddErrf("invalid index %v: %v", src, err) 253 return InvalidLabel 254 } 255 if i < 0 { 256 switch src.(type) { 257 case nil, *Num, *UnaryExpr: 258 // If the value is a constant, we know it is always an error. 259 // UnaryExpr is an approximation for a constant value here. 260 c.AddErrf("invalid index %v (index must be non-negative)", x) 261 default: 262 // Use a different message is it is the result of evaluation. 263 c.AddErrf("index %v out of range [%v]", src, x) 264 } 265 return InvalidLabel 266 } 267 268 case StringKind: 269 x, _ := Unwrap(v).(*String) 270 if x == nil { 271 c.addErrf(IncompleteError, Pos(v), msgGround, v, "string") 272 return InvalidLabel 273 } 274 t = StringLabel 275 i = c.StringToIndex(x.Str) 276 277 default: 278 if src != nil { 279 c.AddErrf("invalid index %s (invalid type %v)", src, v.Kind()) 280 } else { 281 c.AddErrf("invalid index type %v", v.Kind()) 282 } 283 return InvalidLabel 284 } 285 286 // TODO: set position if it exists. 287 f, err := MakeLabel(nil, i, t) 288 if err != nil { 289 c.AddErr(err) 290 } 291 return f 292} 293 294// MakeLabel creates a label. It reports an error if the index is out of range. 295func MakeLabel(src ast.Node, index int64, f FeatureType) (Feature, errors.Error) { 296 if 0 > index || index > MaxIndex-1 { 297 p := token.NoPos 298 if src != nil { 299 p = src.Pos() 300 } 301 return InvalidLabel, 302 errors.Newf(p, "int label out of range (%d not >=0 and <= %d)", 303 index, MaxIndex-1) 304 } 305 return Feature(index)<<indexShift | Feature(f), nil 306} 307 308func makeLabel(index int64, f FeatureType) Feature { 309 return Feature(index)<<indexShift | Feature(f) 310} 311 312// A FeatureType indicates the type of label. 313type FeatureType int8 314 315const ( 316 InvalidLabelType FeatureType = iota 317 StringLabel 318 IntLabel 319 DefinitionLabel 320 HiddenLabel 321 HiddenDefinitionLabel 322 LetLabel 323) 324 325const ( 326 fTypeMask Feature = 0b1111 327 328 indexShift = 4 329) 330 331func (f FeatureType) IsDef() bool { 332 return f == DefinitionLabel || f == HiddenDefinitionLabel 333} 334 335func (f FeatureType) IsHidden() bool { 336 return f == HiddenLabel || f == HiddenDefinitionLabel 337} 338 339func (f FeatureType) IsLet() bool { 340 return f == LetLabel 341} 342 343// IsValid reports whether f is a valid label. 344func (f Feature) IsValid() bool { return f != InvalidLabel } 345 346// Typ reports the type of label. 347func (f Feature) Typ() FeatureType { return FeatureType(f & fTypeMask) } 348 349// IsRegular reports whether a label represents a data field. 350func (f Feature) IsRegular() bool { 351 t := f.Typ() 352 return t == IntLabel || t == StringLabel 353} 354 355// IsString reports whether a label represents a regular field. 356func (f Feature) IsString() bool { return f.Typ() == StringLabel } 357 358// IsDef reports whether the label is a definition (an identifier starting with 359// # or _#. 360func (f Feature) IsDef() bool { 361 return f.Typ().IsDef() 362} 363 364// IsInt reports whether this is an integer index. 365func (f Feature) IsInt() bool { return f.Typ() == IntLabel } 366 367// IsHidden reports whether this label is hidden (an identifier starting with 368// _ or #_). 369func (f Feature) IsHidden() bool { 370 return f.Typ().IsHidden() 371} 372 373// IsLet reports whether this label is a let field (like `let X = value`). 374func (f Feature) IsLet() bool { 375 return f.Typ().IsLet() 376} 377 378// Index reports the abstract index associated with f. 379func (f Feature) Index() int { 380 return int(f >> indexShift) 381} 382 383// SafeIndex reports the abstract index associated with f, setting MaxIndex to 0. 384func (f Feature) safeIndex() int64 { 385 x := int(f >> indexShift) 386 if x == MaxIndex { 387 x = 0 // Safety, MaxIndex means any 388 } 389 return int64(x) 390}