blob: ddc3cbbf61171b576e4850e9d7fd66dae4c9ccb4 [file] [log] [blame]
// Copyright 2018 The 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 adt
import (
"fmt"
"math/bits"
"strings"
)
// Concreteness is a measure of the level of concreteness of a value, where
// lower values mean more concrete.
type Concreteness int
const (
BottomLevel Concreteness = iota
// Concrete indicates a concrete scalar value, list or struct.
Concrete
// Constraint indicates a non-concrete scalar value that is more specific,
// than a top-level type.
Constraint
// PrimitiveType indicates a top-level specific type, for instance, string,
// bytes, number, or bool.
Type
// Any indicates any value, or top.
Any
)
// IsConcrete returns whether a value is concrete.
func IsConcrete(v Value) bool {
if x, ok := v.(*Vertex); ok {
v = x.Value
}
if v == nil {
return false
}
return v.Concreteness() <= Concrete
}
// Kind reports the Value kind.
type Kind uint16
const (
NullKind Kind = (1 << iota)
BoolKind
IntKind
FloatKind
StringKind
BytesKind
ListKind
StructKind
allKinds
_numberKind
NumberKind = IntKind | FloatKind
BottomKind Kind = 0
NumKind = IntKind | FloatKind
TopKind Kind = (allKinds - 1) // all kinds, but not references
ScalarKinds = NullKind | BoolKind |
IntKind | FloatKind | StringKind | BytesKind
)
func kind(v Value) Kind {
if v == nil {
return BottomKind
}
return v.Kind()
}
// IsAnyOf reports whether k is any of the given kinds.
//
// For instances, k.IsAnyOf(String|Bytes) reports whether k overlaps with
// the String or Bytes kind.
func (k Kind) IsAnyOf(of Kind) bool {
return k&of != BottomKind
}
// CanString reports whether the given type can convert to a string.
func (k Kind) CanString() bool {
return k&StringKind|ScalarKinds != BottomKind
}
// String returns the representation of the Kind as
// a CUE expression. For example:
//
// (IntKind|ListKind).String()
//
// will return:
//
// (int|[...])
func (k Kind) String() string {
return toString(k, kindStrs)
}
// TypeString is like String, but returns a string representation of a valid
// CUE type.
func (k Kind) TypeString() string {
return toString(k, typeStrs)
}
func toString(k Kind, m map[Kind]string) string {
if k == BottomKind {
return "_|_"
}
if k == TopKind {
return "_"
}
if (k & NumberKind) == NumberKind {
k = (k &^ NumberKind) | _numberKind
}
var buf strings.Builder
multiple := bits.OnesCount(uint(k)) > 1
if multiple {
buf.WriteByte('(')
}
for count := 0; ; count++ {
n := bits.TrailingZeros(uint(k))
if n == bits.UintSize {
break
}
bit := Kind(1 << uint(n))
k &^= bit
s, ok := m[bit]
if !ok {
s = fmt.Sprintf("bad(%d)", n)
}
if count > 0 {
buf.WriteByte('|')
}
buf.WriteString(s)
}
if multiple {
buf.WriteByte(')')
}
return buf.String()
}
var kindStrs = map[Kind]string{
NullKind: "null",
BoolKind: "bool",
IntKind: "int",
FloatKind: "float",
StringKind: "string",
BytesKind: "bytes",
StructKind: "struct",
ListKind: "list",
_numberKind: "number",
}
// used to generate a parseable CUE type.
var typeStrs = map[Kind]string{
NullKind: "null",
BoolKind: "bool",
IntKind: "int",
FloatKind: "float",
StringKind: "string",
BytesKind: "bytes",
StructKind: "{...}",
ListKind: "[...]",
_numberKind: "number",
}