blob: 6ee3b6a9708f310d52838ad5b1514e050696b933 [file] [log] [blame]
Marcel van Lohuizen6a088302020-07-03 14:42:05 +02001// 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 "strconv"
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +020019 "strings"
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020020
21 "cuelang.org/go/cue/ast"
22 "cuelang.org/go/cue/errors"
Marcel van Lohuizenc8860942020-10-01 12:42:56 +020023 "cuelang.org/go/cue/literal"
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020024 "cuelang.org/go/cue/token"
25 "cuelang.org/go/internal"
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
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +020032// TODO: create labels such that list are sorted first (or last with index.)
33
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020034// InvalidLabel is an encoding of an erroneous label.
35const InvalidLabel Feature = 0x7 // 0xb111
36
37// MaxIndex indicates the maximum number of unique strings that are used for
38// labeles within this CUE implementation.
39const MaxIndex int64 = 1<<28 - 1
40
41// A StringIndexer coverts strings to and from an index that is unique for a
42// given string.
43type StringIndexer interface {
44 // ToIndex returns a unique positive index for s (0 < index < 2^28-1).
45 //
46 // For each pair of strings s and t it must return the same index if and
47 // only if s == t.
48 StringToIndex(s string) (index int64)
49
50 // ToString returns a string s for index such that ToIndex(s) == index.
51 IndexToString(index int64) string
52}
53
54// SelectorString reports the shortest string representation of f when used as a
55// selector.
56func (f Feature) SelectorString(index StringIndexer) string {
57 if f == 0 {
58 return "_"
59 }
60 x := f.Index()
61 switch f.Typ() {
62 case IntLabel:
63 return strconv.Itoa(int(x))
64 case StringLabel:
65 s := index.IndexToString(int64(x))
66 if ast.IsValidIdent(s) && !internal.IsDefOrHidden(s) {
67 return s
68 }
Marcel van Lohuizenc8860942020-10-01 12:42:56 +020069 return literal.String.Quote(s)
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020070 default:
71 return index.IndexToString(int64(x))
72 }
73}
74
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +020075// StringValue reports the string value of f, which must be a string label.
76func (f Feature) StringValue(index StringIndexer) string {
77 if !f.IsString() {
78 panic("not a string label")
79 }
80 x := f.Index()
81 return index.IndexToString(int64(x))
82}
83
84// ToValue converts a label to a value, which will be a Num for integer labels
85// and a String for string labels. It panics when f is not a regular label.
86func (f Feature) ToValue(ctx *OpContext) Value {
87 if !f.IsRegular() {
88 panic("not a regular label")
89 }
90 if f.IsInt() {
91 return ctx.NewInt64(int64(f.Index()))
92 }
93 x := f.Index()
94 str := ctx.IndexToString(int64(x))
95 return ctx.NewString(str)
96}
97
98// StringLabel converts s to a string label.
99func (c *OpContext) StringLabel(s string) Feature {
100 return labelFromValue(c, &String{Str: s})
101}
102
103// MakeStringLabel creates a label for the given string.
104func MakeStringLabel(r StringIndexer, s string) Feature {
105 i := r.StringToIndex(s)
106
107 // TODO: set position if it exists.
108 f, err := MakeLabel(nil, i, StringLabel)
109 if err != nil {
110 panic("out of free string slots")
111 }
112 return f
113}
114
115// MakeIdentLabel creates a label for the given identifier.
116func MakeIdentLabel(r StringIndexer, s string) Feature {
117 i := r.StringToIndex(s)
118 t := StringLabel
119 switch {
Marcel van Lohuizen742593f2020-09-30 15:06:59 +0200120 case strings.HasPrefix(s, "_#"):
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200121 t = HiddenDefinitionLabel
122 case strings.HasPrefix(s, "#"):
123 t = DefinitionLabel
124 case strings.HasPrefix(s, "_"):
125 t = HiddenLabel
126 }
127 f, err := MakeLabel(nil, i, t)
128 if err != nil {
129 panic("out of free string slots")
130 }
131 return f
132}
133
134const msgGround = "invalid non-ground value %s (must be concrete %s)"
135
136func labelFromValue(ctx *OpContext, v Value) Feature {
137 var i int64
138 var t FeatureType
139 if isError(v) {
140 return InvalidLabel
141 }
142 switch v.Kind() {
143 case IntKind, NumKind:
144 x, _ := v.(*Num)
145 if x == nil {
146 ctx.addErrf(IncompleteError, pos(v), msgGround, v, "int")
147 return InvalidLabel
148 }
149 t = IntLabel
150 var err error
151 i, err = x.X.Int64()
152 if err != nil || x.K != IntKind {
153 ctx.AddErrf("invalid label %v: %v", v, err)
154 return InvalidLabel
155 }
156 if i < 0 {
157 ctx.AddErrf("invalid negative index %s", ctx.Str(x))
158 return InvalidLabel
159 }
160
161 case StringKind:
162 x, _ := v.(*String)
163 if x == nil {
164 ctx.addErrf(IncompleteError, pos(v), msgGround, v, "string")
165 return InvalidLabel
166 }
167 t = StringLabel
168 i = ctx.StringToIndex(x.Str)
169
170 default:
171 ctx.AddErrf("invalid label type %v", v.Kind())
172 return InvalidLabel
173 }
174
175 // TODO: set position if it exists.
176 f, err := MakeLabel(nil, i, t)
177 if err != nil {
178 ctx.AddErr(err)
179 }
180 return f
181}
182
Marcel van Lohuizen6a088302020-07-03 14:42:05 +0200183// MakeLabel creates a label. It reports an error if the index is out of range.
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200184func MakeLabel(src ast.Node, index int64, f FeatureType) (Feature, errors.Error) {
Marcel van Lohuizen6a088302020-07-03 14:42:05 +0200185 if 0 > index || index > MaxIndex {
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200186 p := token.NoPos
187 if src != nil {
188 p = src.Pos()
189 }
Marcel van Lohuizen6a088302020-07-03 14:42:05 +0200190 return InvalidLabel,
191 errors.Newf(p, "int label out of range (%d not >=0 and <= %d)",
192 index, MaxIndex)
193 }
194 return Feature(index)<<indexShift | Feature(f), nil
195}
196
197// A FeatureType indicates the type of label.
198type FeatureType int8
199
200const (
201 StringLabel FeatureType = 0 // 0b000
202 IntLabel FeatureType = 1 // 0b001
203 DefinitionLabel FeatureType = 3 // 0b011
204 HiddenLabel FeatureType = 6 // 0b110
205 HiddenDefinitionLabel FeatureType = 7 // 0b111
206
207 // letLabel FeatureType = 0b010
208
209 fTypeMask Feature = 7 // 0b111
210
211 indexShift = 3
212)
213
214// IsValid reports whether f is a valid label.
215func (f Feature) IsValid() bool { return f != InvalidLabel }
216
217// Typ reports the type of label.
218func (f Feature) Typ() FeatureType { return FeatureType(f & fTypeMask) }
219
220// IsRegular reports whether a label represents a data field.
221func (f Feature) IsRegular() bool { return f.Typ() <= IntLabel }
222
223// IsString reports whether a label represents a regular field.
224func (f Feature) IsString() bool { return f.Typ() == StringLabel }
225
226// IsDef reports whether the label is a definition (an identifier starting with
227// # or #_.
228func (f Feature) IsDef() bool {
229 if f == InvalidLabel {
230 // TODO(perf): do more mask trickery to avoid this branch.
231 return false
232 }
233 return f.Typ()&DefinitionLabel == DefinitionLabel
234}
235
236// IsInt reports whether this is an integer index.
237func (f Feature) IsInt() bool { return f.Typ() == IntLabel }
238
239// IsHidden reports whether this label is hidden (an identifier starting with
240// _ or #_).
241func (f Feature) IsHidden() bool {
242 if f == InvalidLabel {
243 // TODO(perf): do more mask trickery to avoid this branch.
244 return false
245 }
246 return f.Typ()&HiddenLabel == HiddenLabel
247}
248
249// Index reports the abstract index associated with f.
250func (f Feature) Index() int { return int(f >> indexShift) }
251
252// TODO: should let declarations be implemented as fields?
253// func (f Feature) isLet() bool { return f.typ() == letLabel }