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 }