Skip to content

Commit

Permalink
internal/core/path: package for Selector to Feature conversion
Browse files Browse the repository at this point in the history
The reodering of label types results in slightly nicer presentation
for type strings and results in faster code.

Signed-off-by: Marcel van Lohuizen <[email protected]>
Change-Id: I1f208d70c0f208df7e7f0f490c612d87bd4c2cc0
Reviewed-on: https://review.gerrithub.io/c/cue-lang/cue/+/542924
Unity-Result: CUEcueckoo <[email protected]>
TryBot-Result: CUEcueckoo <[email protected]>
Reviewed-by: Paul Jolly <[email protected]>
  • Loading branch information
mpvl committed Aug 31, 2022
1 parent cd27b12 commit 4c9d0ac
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 22 deletions.
30 changes: 11 additions & 19 deletions cue/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,18 +37,17 @@ type SelectorType uint16
const (
// StringLabel represents a regular non-definition field.
StringLabel SelectorType = 1 << iota
// HiddenLabel represents a hidden non-definition field.
HiddenLabel
// IndexLabel represents a numeric index into an array.
IndexLabel
// DefinitionLabel represents a definition.
DefinitionLabel
// HiddenLabel represents a hidden non-definition field.
HiddenLabel
// HiddenDefinitionLabel represents a hidden definition.
HiddenDefinitionLabel
// IndexLabel represents a numeric index into an array.
IndexLabel

// OptionalConstraint represents an optional constraint (?).
OptionalConstraint

// PatternConstraint represents a selector of fields in a struct
// or array that match a constraint.
PatternConstraint
Expand All @@ -69,10 +68,10 @@ func (t SelectorType) ConstraintType() SelectorType {
var selectorTypeStrings = [...]string{
"InvalidSelectorType",
"StringLabel",
"HiddenLabel",
"IndexLabel",
"DefinitionLabel",
"HiddenLabel",
"HiddenDefinitionLabel",
"IndexLabel",
"OptionalConstraint",
"PatternConstraint",
}
Expand Down Expand Up @@ -560,18 +559,11 @@ type anySelector adt.Feature
func (s anySelector) String() string { return "[_]" }
func (s anySelector) optional() bool { return true }
func (s anySelector) labelType() SelectorType {
var l SelectorType
switch adt.Feature(s) {
case adt.AnyString:
l = StringLabel
case adt.AnyIndex:
l = IndexLabel
case adt.AnyDefinition:
l = DefinitionLabel
case adt.AnyHidden:
l = HiddenLabel
}
return l
// FeatureTypes are numbered sequentially. SelectorType is a bitmap. As they
// are defined in the same order, we can go from FeatureType to SelectorType
// by left shifting. As valid FeatureTypes starts at 1, we need to end with
// a final right shift.
return SelectorType((1 << adt.Feature(s).Typ()) >> 1)
}
func (s anySelector) constraintType() SelectorType { return PatternConstraint }

Expand Down
2 changes: 1 addition & 1 deletion cue/path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ func TestSelectorTypeString(t *testing.T) {
if got, want := (StringLabel | OptionalConstraint).String(), "StringLabel|OptionalConstraint"; got != want {
t.Errorf("unexpected SelectorType.String result; got %q want %q", got, want)
}
if got, want := SelectorType(255).String(), "StringLabel|HiddenLabel|DefinitionLabel|HiddenDefinitionLabel|IndexLabel|OptionalConstraint|PatternConstraint"; got != want {
if got, want := SelectorType(255).String(), "StringLabel|IndexLabel|DefinitionLabel|HiddenLabel|HiddenDefinitionLabel|OptionalConstraint|PatternConstraint"; got != want {
t.Errorf("unexpected SelectorType.String result; got %q want %q", got, want)
}
}
Expand Down
36 changes: 34 additions & 2 deletions internal/core/adt/feature.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,18 @@ func (f Feature) SelectorString(index StringIndexer) string {
x := f.safeIndex()
switch f.Typ() {
case IntLabel:
if f == AnyIndex {
return "_"
}
return strconv.Itoa(int(x))
case StringLabel:
s := index.IndexToString(x)
if ast.IsValidIdent(s) && !internal.IsDefOrHidden(s) {
return s
}
if f == AnyString {
return "_"
}
return literal.String.Quote(s)
default:
return f.IdentString(index)
Expand Down Expand Up @@ -153,11 +159,11 @@ func MakeIdentLabel(r StringIndexer, s, pkgpath string) Feature {
switch {
case strings.HasPrefix(s, "_#"):
t = HiddenDefinitionLabel
s = fmt.Sprintf("%s\x00%s", s, pkgpath)
s = HiddenKey(s, pkgpath)
case strings.HasPrefix(s, "#"):
t = DefinitionLabel
case strings.HasPrefix(s, "_"):
s = fmt.Sprintf("%s\x00%s", s, pkgpath)
s = HiddenKey(s, pkgpath)
t = HiddenLabel
}
i := r.StringToIndex(s)
Expand All @@ -168,6 +174,32 @@ func MakeIdentLabel(r StringIndexer, s, pkgpath string) Feature {
return f
}

// HiddenKey constructs the uniquely identifying string for a hidden fields and
// its package.
func HiddenKey(s, pkgPath string) string {
// TODO: Consider just using space instead of \x00.
return fmt.Sprintf("%s\x00%s", s, pkgPath)
}

// MakeNamedLabel creates a feature for the given name and feature type.
func MakeNamedLabel(r StringIndexer, t FeatureType, s string) Feature {
i := r.StringToIndex(s)
f, err := MakeLabel(nil, i, t)
if err != nil {
panic("out of free string slots")
}
return f
}

// MakeIntLabel creates an integer label.
func MakeIntLabel(t FeatureType, i int64) Feature {
f, err := MakeLabel(nil, i, t)
if err != nil {
panic("index out of range")
}
return f
}

const msgGround = "invalid non-ground value %s (must be concrete %s)"

func labelFromValue(c *OpContext, src Expr, v Value) Feature {
Expand Down
78 changes: 78 additions & 0 deletions internal/core/path/selector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2022 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package path provides utilities for converting cue.Selectors and cue.Paths to
// internal equivalents.
package path

import (
"math/bits"

"cuelang.org/go/cue"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/runtime"
)

// ToFeatureType converts a SelectorType constant to a FeatureType. It assumes a single label bit is set.
func ToFeatureType(t cue.SelectorType) adt.FeatureType {
t = t.LabelType()
return adt.FeatureType(bits.Len16(uint16(t)))
}

// MakeFeature converts a cue.Selector to an adt.Feature for a given runtime.
func MakeFeature(r *runtime.Runtime, s cue.Selector) adt.Feature {
constraintType := s.ConstraintType()
labelType := s.LabelType()

if constraintType == cue.PatternConstraint {
switch labelType {
case cue.StringLabel:
return adt.AnyString
case cue.IndexLabel:
return adt.AnyIndex

// These are not really a thing at the moment:
case cue.DefinitionLabel:
return adt.AnyDefinition
case cue.HiddenLabel:
return adt.AnyHidden // TODO: fix
case cue.HiddenDefinitionLabel:
return adt.AnyHidden // TODO: fix
default:
panic("unreachable")
}
}

switch labelType {
case cue.StringLabel:
return adt.MakeStringLabel(r, s.Unquoted())

case cue.IndexLabel:
return adt.MakeIntLabel(adt.IntLabel, int64(s.Index()))

case cue.DefinitionLabel:
return adt.MakeNamedLabel(r, adt.DefinitionLabel, s.String())

case cue.HiddenLabel:
str := adt.HiddenKey(s.String(), s.PkgPath())
return adt.MakeNamedLabel(r, adt.HiddenLabel, str)

case cue.HiddenDefinitionLabel:
str := adt.HiddenKey(s.String(), s.PkgPath())
return adt.MakeNamedLabel(r, adt.HiddenDefinitionLabel, str)

default:
return adt.InvalidLabel
}
}
100 changes: 100 additions & 0 deletions internal/core/path/selector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2022 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package path

import (
"testing"

"cuelang.org/go/cue"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/runtime"
)

// TestToFeatureType also tests that SelectorType and FeatureType are in sync.
func TestToFeatureType(t *testing.T) {
testCases := []struct {
s cue.SelectorType
f adt.FeatureType
}{{
cue.InvalidSelectorType,
adt.InvalidLabelType,
}, {
cue.StringLabel,
adt.StringLabel,
}, {
cue.IndexLabel,
adt.IntLabel,
}, {
cue.DefinitionLabel,
adt.DefinitionLabel,
}, {
cue.HiddenLabel,
adt.HiddenLabel,
}, {
cue.HiddenDefinitionLabel,
adt.HiddenDefinitionLabel,
}, {
cue.StringLabel | cue.OptionalConstraint,
adt.StringLabel,
}, {
cue.OptionalConstraint,
adt.InvalidLabelType,
}}
for _, tc := range testCases {
t.Run(tc.s.String(), func(t *testing.T) {
if got := ToFeatureType(tc.s); got != tc.f {
t.Errorf("got %v, want %v", got, tc.f)
}
})
}
}

func TestMakeFeature(t *testing.T) {
testCases := []struct {
sel cue.Selector
str string
}{{
sel: cue.Str("s-t"),
str: `"s-t"`,
}, {
// Optional should be disregarded, as it is not part of a Feature.
sel: cue.Str("s-t").Optional(),
str: `"s-t"`,
}, {
sel: cue.Index(5),
str: "5",
}, {
sel: cue.Def("#Foo"),
str: "#Foo",
}, {
sel: cue.Hid("_foo", "pkg"),
str: "_foo",
}, {
sel: cue.Hid("_#foo", "pkg"),
str: "_#foo",
}, {
sel: cue.AnyString,
str: `_`,
}}
for _, tc := range testCases {
r := runtime.New()
t.Run(tc.sel.String(), func(t *testing.T) {
got := MakeFeature(r, tc.sel).SelectorString(r)
if got != tc.str {
t.Errorf("got %v, want %v", got, tc.str)
}
})
}
}

0 comments on commit 4c9d0ac

Please sign in to comment.