blob: 0356e02a3272ef3ccdbf6f53ee3a03159c04aef5 [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 (
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +020018 "fmt"
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020019 "strconv"
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +020020 "strings"
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020021
22 "cuelang.org/go/cue/ast"
23 "cuelang.org/go/cue/errors"
Marcel van Lohuizenc8860942020-10-01 12:42:56 +020024 "cuelang.org/go/cue/literal"
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020025 "cuelang.org/go/cue/token"
26 "cuelang.org/go/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.
31type Feature uint32
32
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +020033// TODO: create labels such that list are sorted first (or last with index.)
34
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020035// InvalidLabel is an encoding of an erroneous label.
36const InvalidLabel Feature = 0x7 // 0xb111
37
38// MaxIndex indicates the maximum number of unique strings that are used for
39// labeles within this CUE implementation.
40const MaxIndex int64 = 1<<28 - 1
41
42// A StringIndexer coverts strings to and from an index that is unique for a
43// given string.
44type StringIndexer interface {
45 // ToIndex returns a unique positive index for s (0 < index < 2^28-1).
46 //
47 // For each pair of strings s and t it must return the same index if and
48 // only if s == t.
49 StringToIndex(s string) (index int64)
50
51 // ToString returns a string s for index such that ToIndex(s) == index.
52 IndexToString(index int64) string
53}
54
55// SelectorString reports the shortest string representation of f when used as a
56// selector.
57func (f Feature) SelectorString(index StringIndexer) string {
58 if f == 0 {
59 return "_"
60 }
61 x := f.Index()
62 switch f.Typ() {
63 case IntLabel:
64 return strconv.Itoa(int(x))
65 case StringLabel:
66 s := index.IndexToString(int64(x))
67 if ast.IsValidIdent(s) && !internal.IsDefOrHidden(s) {
68 return s
69 }
Marcel van Lohuizenc8860942020-10-01 12:42:56 +020070 return literal.String.Quote(s)
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020071 default:
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +020072 return f.IdentString(index)
Marcel van Lohuizen6a088302020-07-03 14:42:05 +020073 }
74}
75
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +020076// IdentString reports the identifier of f. The result is undefined if f
77// is not an identifier label.
78func (f Feature) IdentString(index StringIndexer) string {
79 s := index.IndexToString(int64(f.Index()))
80 if f.IsHidden() {
81 if p := strings.IndexByte(s, '\x00'); p >= 0 {
82 s = s[:p]
83 }
84 }
85 return s
86}
87
88// PkgID returns the package identifier, composed of the module and package
89// name, associated with this identifier. It will return "" if this is not
90// a hidden label.
91func (f Feature) PkgID(index StringIndexer) string {
92 if !f.IsHidden() {
93 return ""
94 }
95 s := index.IndexToString(int64(f.Index()))
96 if p := strings.IndexByte(s, '\x00'); p >= 0 {
97 return s[p+1:]
98 }
99 return s
100}
101
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200102// StringValue reports the string value of f, which must be a string label.
103func (f Feature) StringValue(index StringIndexer) string {
104 if !f.IsString() {
105 panic("not a string label")
106 }
107 x := f.Index()
108 return index.IndexToString(int64(x))
109}
110
111// ToValue converts a label to a value, which will be a Num for integer labels
112// and a String for string labels. It panics when f is not a regular label.
113func (f Feature) ToValue(ctx *OpContext) Value {
114 if !f.IsRegular() {
115 panic("not a regular label")
116 }
117 if f.IsInt() {
118 return ctx.NewInt64(int64(f.Index()))
119 }
120 x := f.Index()
121 str := ctx.IndexToString(int64(x))
122 return ctx.NewString(str)
123}
124
125// StringLabel converts s to a string label.
126func (c *OpContext) StringLabel(s string) Feature {
127 return labelFromValue(c, &String{Str: s})
128}
129
130// MakeStringLabel creates a label for the given string.
131func MakeStringLabel(r StringIndexer, s string) Feature {
132 i := r.StringToIndex(s)
133
134 // TODO: set position if it exists.
135 f, err := MakeLabel(nil, i, StringLabel)
136 if err != nil {
137 panic("out of free string slots")
138 }
139 return f
140}
141
142// MakeIdentLabel creates a label for the given identifier.
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +0200143func MakeIdentLabel(r StringIndexer, s, pkgpath string) Feature {
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200144 t := StringLabel
145 switch {
Marcel van Lohuizen742593f2020-09-30 15:06:59 +0200146 case strings.HasPrefix(s, "_#"):
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200147 t = HiddenDefinitionLabel
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +0200148 s = fmt.Sprintf("%s\x00%s", s, pkgpath)
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200149 case strings.HasPrefix(s, "#"):
150 t = DefinitionLabel
151 case strings.HasPrefix(s, "_"):
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +0200152 s = fmt.Sprintf("%s\x00%s", s, pkgpath)
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200153 t = HiddenLabel
154 }
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +0200155 i := r.StringToIndex(s)
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200156 f, err := MakeLabel(nil, i, t)
157 if err != nil {
158 panic("out of free string slots")
159 }
160 return f
161}
162
163const msgGround = "invalid non-ground value %s (must be concrete %s)"
164
165func labelFromValue(ctx *OpContext, v Value) Feature {
166 var i int64
167 var t FeatureType
168 if isError(v) {
169 return InvalidLabel
170 }
171 switch v.Kind() {
172 case IntKind, NumKind:
173 x, _ := v.(*Num)
174 if x == nil {
175 ctx.addErrf(IncompleteError, pos(v), msgGround, v, "int")
176 return InvalidLabel
177 }
178 t = IntLabel
179 var err error
180 i, err = x.X.Int64()
181 if err != nil || x.K != IntKind {
182 ctx.AddErrf("invalid label %v: %v", v, err)
183 return InvalidLabel
184 }
185 if i < 0 {
186 ctx.AddErrf("invalid negative index %s", ctx.Str(x))
187 return InvalidLabel
188 }
189
190 case StringKind:
191 x, _ := v.(*String)
192 if x == nil {
193 ctx.addErrf(IncompleteError, pos(v), msgGround, v, "string")
194 return InvalidLabel
195 }
196 t = StringLabel
197 i = ctx.StringToIndex(x.Str)
198
199 default:
200 ctx.AddErrf("invalid label type %v", v.Kind())
201 return InvalidLabel
202 }
203
204 // TODO: set position if it exists.
205 f, err := MakeLabel(nil, i, t)
206 if err != nil {
207 ctx.AddErr(err)
208 }
209 return f
210}
211
Marcel van Lohuizen6a088302020-07-03 14:42:05 +0200212// MakeLabel creates a label. It reports an error if the index is out of range.
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200213func MakeLabel(src ast.Node, index int64, f FeatureType) (Feature, errors.Error) {
Marcel van Lohuizen6a088302020-07-03 14:42:05 +0200214 if 0 > index || index > MaxIndex {
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200215 p := token.NoPos
216 if src != nil {
217 p = src.Pos()
218 }
Marcel van Lohuizen6a088302020-07-03 14:42:05 +0200219 return InvalidLabel,
220 errors.Newf(p, "int label out of range (%d not >=0 and <= %d)",
221 index, MaxIndex)
222 }
223 return Feature(index)<<indexShift | Feature(f), nil
224}
225
226// A FeatureType indicates the type of label.
227type FeatureType int8
228
229const (
230 StringLabel FeatureType = 0 // 0b000
231 IntLabel FeatureType = 1 // 0b001
232 DefinitionLabel FeatureType = 3 // 0b011
233 HiddenLabel FeatureType = 6 // 0b110
234 HiddenDefinitionLabel FeatureType = 7 // 0b111
235
236 // letLabel FeatureType = 0b010
237
238 fTypeMask Feature = 7 // 0b111
239
240 indexShift = 3
241)
242
243// IsValid reports whether f is a valid label.
244func (f Feature) IsValid() bool { return f != InvalidLabel }
245
246// Typ reports the type of label.
247func (f Feature) Typ() FeatureType { return FeatureType(f & fTypeMask) }
248
249// IsRegular reports whether a label represents a data field.
250func (f Feature) IsRegular() bool { return f.Typ() <= IntLabel }
251
252// IsString reports whether a label represents a regular field.
253func (f Feature) IsString() bool { return f.Typ() == StringLabel }
254
255// IsDef reports whether the label is a definition (an identifier starting with
256// # or #_.
257func (f Feature) IsDef() bool {
258 if f == InvalidLabel {
259 // TODO(perf): do more mask trickery to avoid this branch.
260 return false
261 }
262 return f.Typ()&DefinitionLabel == DefinitionLabel
263}
264
265// IsInt reports whether this is an integer index.
266func (f Feature) IsInt() bool { return f.Typ() == IntLabel }
267
268// IsHidden reports whether this label is hidden (an identifier starting with
269// _ or #_).
270func (f Feature) IsHidden() bool {
271 if f == InvalidLabel {
272 // TODO(perf): do more mask trickery to avoid this branch.
273 return false
274 }
275 return f.Typ()&HiddenLabel == HiddenLabel
276}
277
278// Index reports the abstract index associated with f.
279func (f Feature) Index() int { return int(f >> indexShift) }
280
281// TODO: should let declarations be implemented as fields?
282// func (f Feature) isLet() bool { return f.typ() == letLabel }