cue: make internal cue port main API implementation
- rm cue/*.go
- mv internal/legacy/cue/*.go cue/
- rename imports
- go
Change-Id: I553ce372ad457b1d73126b0a3f7be8224e415e80
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6742
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/add.go b/cmd/cue/cmd/add.go
index 812e642..d13035c 100644
--- a/cmd/cue/cmd/add.go
+++ b/cmd/cue/cmd/add.go
@@ -27,12 +27,12 @@
"github.com/spf13/cobra"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/load"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
func newAddCmd(c *Command) *cobra.Command {
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index c63e2b5..bf0321e 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -25,6 +25,7 @@
"golang.org/x/text/language"
"golang.org/x/text/message"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
@@ -34,7 +35,6 @@
"cuelang.org/go/internal"
"cuelang.org/go/internal/encoding"
"cuelang.org/go/internal/filetypes"
- "cuelang.org/go/internal/legacy/cue"
)
// Disallow
diff --git a/cmd/cue/cmd/custom.go b/cmd/cue/cmd/custom.go
index 7bd85f0..9a6976c 100644
--- a/cmd/cue/cmd/custom.go
+++ b/cmd/cue/cmd/custom.go
@@ -28,9 +28,9 @@
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
itask "cuelang.org/go/internal/task"
"cuelang.org/go/internal/walk"
_ "cuelang.org/go/pkg/tool/cli" // Register tasks
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index bcbf383..8d94aa8 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -19,12 +19,12 @@
"github.com/spf13/cobra"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/format"
"cuelang.org/go/internal"
"cuelang.org/go/internal/encoding"
"cuelang.org/go/internal/filetypes"
- "cuelang.org/go/internal/legacy/cue"
)
// newEvalCmd creates a new eval command
diff --git a/cmd/cue/cmd/orphans.go b/cmd/cue/cmd/orphans.go
index eb5ca75..de54d92 100644
--- a/cmd/cue/cmd/orphans.go
+++ b/cmd/cue/cmd/orphans.go
@@ -20,6 +20,7 @@
"regexp"
"strconv"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/build"
@@ -28,7 +29,6 @@
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
"cuelang.org/go/internal/encoding"
- "cuelang.org/go/internal/legacy/cue"
)
// This file contains logic for placing orphan files within a CUE namespace.
diff --git a/cmd/cue/cmd/tags.go b/cmd/cue/cmd/tags.go
index 5d4beec..00912ef 100644
--- a/cmd/cue/cmd/tags.go
+++ b/cmd/cue/cmd/tags.go
@@ -17,13 +17,13 @@
import (
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
"cuelang.org/go/internal/cli"
- "cuelang.org/go/internal/legacy/cue"
)
func decorateInstances(cmd *Command, tags []string, a []*build.Instance) {
diff --git a/cmd/cue/cmd/vet.go b/cmd/cue/cmd/vet.go
index 8020055..deb623d 100644
--- a/cmd/cue/cmd/vet.go
+++ b/cmd/cue/cmd/vet.go
@@ -18,8 +18,8 @@
"github.com/spf13/cobra"
"golang.org/x/text/message"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/internal/legacy/cue"
)
const vetDoc = `vet validates CUE and other data files
diff --git a/internal/legacy/cue/alias.go b/cue/alias.go
similarity index 100%
rename from internal/legacy/cue/alias.go
rename to cue/alias.go
diff --git a/cue/ast.go b/cue/ast.go
deleted file mode 100644
index 815865c..0000000
--- a/cue/ast.go
+++ /dev/null
@@ -1,838 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "golang.org/x/xerrors"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/build"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/literal"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
-)
-
-// insertFile inserts the given file at the root of the instance.
-//
-// The contents will be merged (unified) with any pre-existing value. In this
-// case an error may be reported, but only if the merge failed at the top-level.
-// Other errors will be recorded at the respective values in the tree.
-//
-// There should be no unresolved identifiers in file, meaning the Node field
-// of all identifiers should be set to a non-nil value.
-func (inst *Instance) insertFile(f *ast.File) errors.Error {
- // TODO: insert by converting to value first so that the trim command can
- // also remove top-level fields.
- // First process single file.
- v := newVisitor(inst.index, inst.inst, inst.rootStruct, inst.scope, false)
- v.astState.astMap[f] = inst.rootStruct
- // TODO: fix cmd/import to resolve references in the AST before
- // inserting. For now, we accept errors that did not make it up to the tree.
- result := v.walk(f)
- if isBottom(result) {
- val := newValueRoot(v.ctx(), result)
- v.errors = errors.Append(v.errors, val.toErr(result.(*bottom)))
- }
- return v.errors
-}
-
-type astVisitor struct {
- *astState
- object *structLit
-
- parent *astVisitor
- sel string // label or index; may be '*'
- // For single line fields, the doc comment is applied to the inner-most
- // field value.
- //
- // // This comment is for bar.
- // foo bar: value
- //
- doc *docNode
-
- inSelector int
-}
-
-func (v *astVisitor) ctx() *context {
- return v.astState.ctx
-}
-
-type astState struct {
- ctx *context
- *index
- inst *build.Instance
-
- litParser *litParser
- resolveRoot *structLit
- allowAuto bool // allow builtin packages without import
-
- // make unique per level to avoid reuse of structs being an issue.
- astMap map[ast.Node]scope
- aliasMap map[ast.Node]value
-
- errors errors.Error
-}
-
-func (s *astState) mapScope(n ast.Node) (m scope) {
- if m = s.astMap[n]; m == nil {
- m = newStruct(newNode(n))
- s.astMap[n] = m
- }
- return m
-}
-
-func (s *astState) setScope(n ast.Node, v scope) {
- if m, ok := s.astMap[n]; ok && m != v {
- panic("already defined")
- }
- s.astMap[n] = v
-}
-
-func newVisitor(idx *index, inst *build.Instance, obj, resolveRoot *structLit, allowAuto bool) *astVisitor {
- ctx := idx.newContext()
- return newVisitorCtx(ctx, inst, obj, resolveRoot, allowAuto)
-}
-
-func newVisitorCtx(ctx *context, inst *build.Instance, obj, resolveRoot *structLit, allowAuto bool) *astVisitor {
- v := &astVisitor{
- object: obj,
- }
- v.astState = &astState{
- ctx: ctx,
- index: ctx.index,
- inst: inst,
- litParser: &litParser{ctx: ctx},
- resolveRoot: resolveRoot,
- allowAuto: allowAuto,
- astMap: map[ast.Node]scope{},
- aliasMap: map[ast.Node]value{},
- }
- return v
-}
-
-func (v *astVisitor) errf(n ast.Node, format string, args ...interface{}) evaluated {
- v.astState.errors = errors.Append(v.astState.errors, &nodeError{
- path: v.appendPath(nil),
- n: n,
- Message: errors.NewMessage(format, args),
- })
- arguments := append([]interface{}{format}, args...)
- return v.mkErr(newNode(n), arguments...)
-}
-
-func (v *astVisitor) appendPath(a []string) []string {
- if v.parent != nil {
- a = v.parent.appendPath(a)
- }
- if v.sel != "" {
- a = append(a, v.sel)
- }
- return a
-}
-
-func (v *astVisitor) resolve(n *ast.Ident) value {
- ctx := v.ctx()
- name := v.ident(n)
- label := v.Label(name, true)
- if r := v.resolveRoot; r != nil {
- for _, a := range r.Arcs {
- if a.Label == label {
- return &selectorExpr{newExpr(n),
- &nodeRef{baseValue: newExpr(n), node: r, label: label}, label}
- }
- }
- if v.inSelector > 0 && v.allowAuto {
- if p := getBuiltinShorthandPkg(ctx, name); p != nil {
- return &nodeRef{newExpr(n), p, label}
- }
- }
- }
- return nil
-}
-
-func (v *astVisitor) loadImport(imp *ast.ImportSpec) evaluated {
- ctx := v.ctx()
- path, err := literal.Unquote(imp.Path.Value)
- if err != nil {
- return v.errf(imp, "illformed import spec")
- }
- // TODO: allow builtin *and* imported package. The result is a unified
- // struct.
- if p := getBuiltinPkg(ctx, path); p != nil {
- return p
- }
- bimp := v.inst.LookupImport(path)
- if bimp == nil {
- return v.errf(imp, "package %q not found", path)
- }
- impInst := v.index.loadInstance(bimp)
- return impInst.rootValue.evalPartial(ctx)
-}
-
-func (v *astVisitor) ident(n *ast.Ident) string {
- str, err := ast.ParseIdent(n)
- if err != nil {
- v.errf(n, "invalid literal: %v", err)
- return n.Name
- }
- return str
-}
-
-// We probably don't need to call Walk.s
-func (v *astVisitor) walk(astNode ast.Node) (ret value) {
- switch n := astNode.(type) {
- case *ast.File:
- obj := v.object
- v1 := &astVisitor{
- astState: v.astState,
- object: obj,
- }
- for _, e := range n.Decls {
- switch x := e.(type) {
- case *ast.EmbedDecl:
- if v1.object.emit == nil {
- v1.object.emit = v1.walk(x.Expr)
- } else {
- v1.object.emit = mkBin(v.ctx(), token.NoPos, opUnify, v1.object.emit, v1.walk(x.Expr))
- }
- case *ast.Ellipsis:
- // handled elsewhere
-
- default:
- v1.walk(e)
- }
- }
- ret = obj
-
- case *ast.Package:
- // NOTE: Do NOT walk the identifier of the package here, as it is not
- // supposed to resolve to anything.
-
- case *ast.ImportDecl:
- for _, s := range n.Specs {
- v.walk(s)
- }
-
- case *ast.ImportSpec:
- val := v.loadImport(n)
- if !isBottom(val) {
- v.setScope(n, val.(*structLit))
- }
-
- case *ast.StructLit:
- obj := v.mapScope(n).(*structLit)
- v1 := &astVisitor{
- astState: v.astState,
- object: obj,
- parent: v,
- }
- passDoc := len(n.Elts) == 1 && !n.Lbrace.IsValid() && v.doc != nil
- if passDoc {
- v1.doc = v.doc
- }
- ret = obj
- for i, e := range n.Elts {
- switch x := e.(type) {
- case *ast.Ellipsis:
- if i != len(n.Elts)-1 {
- return v1.walk(x.Type) // Generate an error
- }
- f := v.ctx().Label("_", true)
- sig := ¶ms{}
- sig.add(f, &basicType{newNode(x), stringKind})
- template := &lambdaExpr{newNode(x), sig, &top{newNode(x)}}
- v1.object.addTemplate(v.ctx(), x.Pos(), nil, template)
-
- case *ast.EmbedDecl:
- old := v.ctx().inDefinition
- v.ctx().inDefinition = 0
- e := v1.walk(x.Expr)
- v.ctx().inDefinition = old
- if isBottom(e) {
- return e
- }
- if e.Kind()&structKind == 0 {
- return v1.errf(x, "can only embed structs (found %v)", e.Kind())
- }
- ret = mkBin(v1.ctx(), x.Pos(), opUnifyUnchecked, ret, e)
- // TODO: preserve order of embedded fields. We cannot split into
- // separate unifications here, as recursive references point to
- // obj and would have to be dereferenced and copied.
- // Solving this is best done with a generic topological sort
- // mechanism.
-
- case *ast.Field, *ast.Alias, *ast.LetClause:
- v1.walk(e)
-
- case *ast.Comprehension:
- v1.walk(x)
-
- case *ast.Attribute:
- // Nothing to do.
- }
- }
- if v.ctx().inDefinition > 0 && !obj.optionals.isFull() {
- // For embeddings this is handled in binOp, in which case the
- // isClosed bit is cleared if a template is introduced.
- obj.closeStatus = toClose
- }
- if passDoc {
- v.doc = v1.doc // signal usage of document back to parent.
- }
-
- case *ast.ListLit:
- v1 := &astVisitor{
- astState: v.astState,
- object: v.object,
- parent: v,
- }
-
- if len(n.Elts) == 1 {
- if c, ok := n.Elts[0].(*ast.Comprehension); ok {
- yielder := &yield{baseValue: newExpr(c.Value)}
- lc := &listComprehension{
- newExpr(c),
- wrapClauses(v, yielder, c.Clauses),
- }
- // we don't support key for lists (yet?)
-
- // TODO(hack): unwrap struct lit if embedding of one element.
- // We do this as we do not yet support embedding of scalar
- // values in general. This prohibits:
- // - having fields alongside embedded values
- // - having more than one embedded value.
- // The latter would not be too hard to circumvent.
- expr := c.Value
- if s, ok := expr.(*ast.StructLit); ok && len(s.Elts) == 1 {
- if e, ok := s.Elts[0].(*ast.EmbedDecl); ok {
- expr = e.Expr
- }
- }
- yielder.value = v.walk(expr)
- return lc
- }
- }
-
- elts, ellipsis := internal.ListEllipsis(n)
-
- arcs := []arc{}
- for i, e := range elts {
- if _, ok := e.(*ast.Comprehension); ok {
- return v.errf(e, "comprehensions must be a single element within list (for now)")
- }
- elem := v1.walk(e)
- if elem == nil {
- // TODO: it would be consistent to allow aliasing in lists
- // as well, with a similar meaning as alias declarations in
- // structs.
- return v.errf(n, "alias not allowed in list")
- }
- v1.sel = strconv.Itoa(i)
- arcs = append(arcs, arc{Label: label(i), v: elem})
- }
- s := &structLit{baseValue: newExpr(n), Arcs: arcs}
- list := &list{baseValue: newExpr(n), elem: s}
- list.initLit()
- if ellipsis != nil {
- list.len = newBound(v.ctx(), list.baseValue, opGeq, intKind, list.len)
- if ellipsis.Type != nil {
- list.typ = v1.walk(ellipsis.Type)
- }
- }
- ret = list
-
- case *ast.Ellipsis:
- return v.errf(n, "ellipsis (...) only allowed at end of list or struct")
-
- case *ast.Comprehension:
- yielder := &yield{baseValue: newExpr(n.Value)}
- sc := &structComprehension{
- newNode(n),
- wrapClauses(v, yielder, n.Clauses),
- }
- // we don't support key for lists (yet?)
- switch n.Value.(type) {
- case *ast.StructLit:
- default:
- // Caught by parser, usually.
- v.errf(n, "comprehension must be struct")
- }
- yielder.value = v.walk(n.Value)
- v.object.comprehensions = append(v.object.comprehensions, compValue{comp: sc})
-
- case *ast.Field:
- opt := n.Optional != token.NoPos
- isDef := internal.IsDefinition(n.Label) || n.Token == token.ISA
- if isDef {
- ctx := v.ctx()
- ctx.inDefinition++
- defer func() { ctx.inDefinition-- }()
- }
- attrs, err := createAttrs(v.ctx(), newNode(n), n.Attrs)
- if err != nil {
- return v.errf(n, err.format, err.args)
- }
- var leftOverDoc *docNode
- for _, c := range n.Comments() {
- if c.Position == 0 {
- leftOverDoc = v.doc
- v.doc = &docNode{n: n}
- break
- }
- }
-
- lab := n.Label
- if a, ok := lab.(*ast.Alias); ok {
- if lab, ok = a.Expr.(ast.Label); !ok {
- return v.errf(n, "alias expression is not a valid label")
- }
- }
-
- switch x := lab.(type) {
- case *ast.Interpolation:
- v.sel = "?"
- // Must be struct comprehension.
- fc := &fieldComprehension{
- baseValue: newDecl(n),
- key: v.walk(x),
- val: v.walk(n.Value),
- opt: opt,
- def: isDef,
- doc: leftOverDoc,
- attrs: attrs,
- }
- v.object.comprehensions = append(v.object.comprehensions, compValue{comp: fc})
-
- case *ast.ListLit:
- if len(x.Elts) != 1 {
- return v.errf(x, "optional label expression must have exactly one element; found %d", len(x.Elts))
- }
- var f label
- expr := x.Elts[0]
- a, ok := expr.(*ast.Alias)
- if ok {
- expr = a.Expr
- f = v.Label(v.ident(a.Ident), true)
- } else {
- f = v.Label("_", true)
- }
-
- // Parse the key filter or a bulk-optional field. The special value
- // of nil to mean "all fields".
- var key value
- if i, ok := expr.(*ast.Ident); !ok || (i.Name != "string" && i.Name != "_") {
- key = v.walk(expr)
- }
- v.sel = "*"
-
- sig := ¶ms{}
- sig.add(f, &basicType{newNode(lab), stringKind})
- template := &lambdaExpr{newNode(n), sig, nil}
-
- v.setScope(n, template)
- template.value = v.walk(n.Value)
-
- v.object.addTemplate(v.ctx(), token.NoPos, key, template)
-
- case *ast.TemplateLabel:
- if isDef {
- v.errf(x, "map element type cannot be a definition")
- }
- v.sel = "*"
- f := v.Label(v.ident(x.Ident), true)
-
- sig := ¶ms{}
- sig.add(f, &basicType{newNode(lab), stringKind})
- template := &lambdaExpr{newNode(n), sig, nil}
-
- v.setScope(n, template)
- template.value = v.walk(n.Value)
-
- v.object.addTemplate(v.ctx(), token.NoPos, nil, template)
-
- case *ast.BasicLit, *ast.Ident:
- v.sel, _, _ = ast.LabelName(x)
- if v.sel == "_" {
- if _, ok := x.(*ast.BasicLit); ok {
- v.sel = "*"
- }
- }
- f, ok := v.NodeLabel(x)
- if !ok {
- return v.errf(lab, "invalid field name: %v", lab)
- }
- val := v.walk(n.Value)
- if val == nil {
- return v.errf(lab, "invalid field value: %v",
- internal.DebugStr(n.Value))
- }
- v.object.insertValue(v.ctx(), f, opt, isDef, val, attrs, v.doc)
- v.doc = leftOverDoc
-
- default:
- panic("cue: unknown label type")
- }
-
- case *ast.Alias, *ast.LetClause:
- // parsed verbatim at reference.
-
- case *ast.ListComprehension:
- yielder := &yield{baseValue: newExpr(n.Expr)}
- lc := &listComprehension{
- newExpr(n),
- wrapClauses(v, yielder, n.Clauses),
- }
- // we don't support key for lists (yet?)
- yielder.value = v.walk(n.Expr)
- return lc
-
- // Expressions
- case *ast.Ident:
- name := v.ident(n)
-
- if name == "_" {
- ret = &top{newNode(n)}
- break
- }
-
- if n.Node == nil {
- if ret = v.resolve(n); ret != nil {
- break
- }
-
- // TODO: consider supporting GraphQL-style names:
- // String, Bytes, Boolean, Integer, Number.
- // These names will not conflict with idiomatic camel-case JSON.
- switch name {
- case "_":
- return &top{newExpr(n)}
- case "string", "__string":
- return &basicType{newExpr(n), stringKind}
- case "bytes", "__bytes":
- return &basicType{newExpr(n), bytesKind}
- case "bool", "__bool":
- return &basicType{newExpr(n), boolKind}
- case "int", "__int":
- return &basicType{newExpr(n), intKind}
- case "float", "__float":
- return &basicType{newExpr(n), floatKind}
- case "number", "__number":
- return &basicType{newExpr(n), numKind}
-
- case "len", "__len":
- return lenBuiltin
- case "close", "__close":
- return closeBuiltin
- case "and", "__and":
- return andBuiltin
- case "or", "__or":
- return orBuiltin
- }
- if r, ok := predefinedRanges[name]; ok {
- return r
- }
-
- ret = v.errf(n, "reference %q not found", name)
- break
- }
-
- // Type of reference Scope Node
- // Let Clause File/Struct LetClause
- // Alias declaration File/Struct Alias (deprecated)
- // Illegal Reference File/Struct
- // Fields
- // Label File/Struct ParenExpr, Ident, BasicLit
- // Value File/Struct Field
- // Template Field Template
- // Fields inside lambda
- // Label Field Expr
- // Value Field Field
- // Pkg nil ImportSpec
- var expr ast.Expr
- switch x := n.Node.(type) {
- case *ast.Alias:
- expr = x.Expr
- case *ast.LetClause:
- expr = x.Expr
- }
-
- if expr != nil {
- // TODO(lang): should we exempt definitions? The substitution
- // principle says we should not.
- if ret = v.aliasMap[expr]; ret != nil {
- break
- }
- old := v.ctx().inDefinition
- v.ctx().inDefinition = 0
- ret = v.walk(expr)
- v.aliasMap[expr] = ret
- v.ctx().inDefinition = old
- break
- }
-
- f := v.Label(name, true)
- if _, ok := n.Node.(*ast.ImportSpec); ok {
- n2 := v.mapScope(n.Node)
- ref := &nodeRef{baseValue: newExpr(n), node: n2, label: f}
- ret = ref
- break
- }
-
- // TODO: probably unused. Verify and remove.
- if n.Scope == nil {
- // Package or direct ancestor node.
- n2 := v.mapScope(n.Node)
- ref := &nodeRef{baseValue: newExpr(n), node: n2, label: f}
- ret = ref
- break
- }
-
- n2 := v.mapScope(n.Scope)
- ret = &nodeRef{baseValue: newExpr(n), node: n2}
-
- // Allow different names to refer to the same field in unification. We
- // do this by anonymizing the the reference. This then has to be
- // resolved again when referring to lambdas.
- l, lambda := n2.(*lambdaExpr)
- if lambda && len(l.params.arcs) == 1 {
- f = 0
- }
-
- if field, ok := n.Node.(*ast.Field); ok {
- if lambda {
- // inside bulk optional.
- ret = v.errf(n, "referencing field (%q) within lambda not yet unsupported", name)
- break
- }
- name, _, err := ast.LabelName(field.Label)
- switch {
- case xerrors.Is(err, ast.ErrIsExpression):
- a := field.Label.(*ast.Alias)
- ret = &indexExpr{newExpr(n), ret, v.walk(a.Expr)}
-
- case err != nil:
- ret = v.errf(n, "invalid label: %v", err)
-
- case name != "":
- f = v.Label(name, true)
- ret = &selectorExpr{newExpr(n), ret, f}
-
- default:
- // TODO: support dynamically computed label lookup.
- // Should that also support lookup of definitions?
- ret = v.errf(n, "unsupported field alias %q", name)
- }
- break
- }
-
- ret = &selectorExpr{newExpr(n), ret, f}
-
- case *ast.BottomLit:
- // TODO: record inline comment.
- ret = &bottom{baseValue: newExpr(n), Code: codeUser, format: "from source"}
-
- case *ast.BadDecl:
- // nothing to do
-
- case *ast.BadExpr:
- ret = v.errf(n, "invalid expression")
-
- case *ast.BasicLit:
- ret = v.litParser.parse(n)
-
- case *ast.Interpolation:
- if len(n.Elts) == 0 {
- return v.errf(n, "invalid interpolation")
- }
- first, ok1 := n.Elts[0].(*ast.BasicLit)
- last, ok2 := n.Elts[len(n.Elts)-1].(*ast.BasicLit)
- if !ok1 || !ok2 {
- return v.errf(n, "invalid interpolation")
- }
- if len(n.Elts) == 1 {
- ret = v.walk(n.Elts[0])
- break
- }
- lit := &interpolation{baseValue: newExpr(n), K: stringKind}
- ret = lit
- info, prefixLen, _, err := literal.ParseQuotes(first.Value, last.Value)
- if err != nil {
- return v.errf(n, "invalid interpolation: %v", err)
- }
- prefix := ""
- for i := 0; i < len(n.Elts); i += 2 {
- l, ok := n.Elts[i].(*ast.BasicLit)
- if !ok {
- return v.errf(n, "invalid interpolation")
- }
- s := l.Value
- if !strings.HasPrefix(s, prefix) {
- return v.errf(l, "invalid interpolation: unmatched ')'")
- }
- s = l.Value[prefixLen:]
- x := parseString(v.ctx(), l, info, s)
- lit.Parts = append(lit.Parts, x)
- if i+1 < len(n.Elts) {
- lit.Parts = append(lit.Parts, v.walk(n.Elts[i+1]))
- }
- prefix = ")"
- prefixLen = 1
- }
-
- case *ast.ParenExpr:
- ret = v.walk(n.X)
-
- case *ast.SelectorExpr:
- v.inSelector++
- ret = &selectorExpr{
- newExpr(n),
- v.walk(n.X),
- v.Label(v.ident(n.Sel), true),
- }
- v.inSelector--
-
- case *ast.IndexExpr:
- ret = &indexExpr{newExpr(n), v.walk(n.X), v.walk(n.Index)}
-
- case *ast.SliceExpr:
- slice := &sliceExpr{baseValue: newExpr(n), X: v.walk(n.X)}
- if n.Low != nil {
- slice.Lo = v.walk(n.Low)
- }
- if n.High != nil {
- slice.Hi = v.walk(n.High)
- }
- ret = slice
-
- case *ast.CallExpr:
- call := &callExpr{baseValue: newExpr(n), Fun: v.walk(n.Fun)}
- for _, a := range n.Args {
- call.Args = append(call.Args, v.walk(a))
- }
- ret = call
-
- case *ast.UnaryExpr:
- switch n.Op {
- case token.NOT, token.ADD, token.SUB:
- ret = &unaryExpr{
- newExpr(n),
- tokenMap[n.Op],
- v.walk(n.X),
- }
- case token.GEQ, token.GTR, token.LSS, token.LEQ,
- token.NEQ, token.MAT, token.NMAT:
- ret = newBound(
- v.ctx(),
- newExpr(n),
- tokenMap[n.Op],
- topKind|nonGround,
- v.walk(n.X),
- )
-
- case token.MUL:
- return v.errf(n, "preference mark not allowed at this position")
- default:
- return v.errf(n, "unsupported unary operator %q", n.Op)
- }
-
- case *ast.BinaryExpr:
- switch n.Op {
- case token.OR:
- d := &disjunction{baseValue: newExpr(n)}
- v.addDisjunctionElem(d, n.X, false)
- v.addDisjunctionElem(d, n.Y, false)
- ret = d
-
- default:
- ret = updateBin(v.ctx(), &binaryExpr{
- newExpr(n),
- tokenMap[n.Op], // op
- v.walk(n.X), // left
- v.walk(n.Y), // right
- })
- }
-
- case *ast.CommentGroup:
- // Nothing to do for a free-floating comment group.
-
- case *ast.Attribute:
- // Nothing to do for now.
-
- // nothing to do
- // case *syntax.EmbedDecl:
- default:
- // TODO: unhandled node.
- // value = ctx.mkErr(n, "unknown node type %T", n)
- panic(fmt.Sprintf("unimplemented %T", n))
-
- }
- return ret
-}
-
-func (v *astVisitor) addDisjunctionElem(d *disjunction, n ast.Node, mark bool) {
- switch x := n.(type) {
- case *ast.BinaryExpr:
- if x.Op == token.OR {
- v.addDisjunctionElem(d, x.X, mark)
- v.addDisjunctionElem(d, x.Y, mark)
- return
- }
- case *ast.UnaryExpr:
- if x.Op == token.MUL {
- mark = true
- n = x.X
- }
- d.HasDefaults = true
- }
- d.Values = append(d.Values, dValue{v.walk(n), mark})
-}
-
-func wrapClauses(v *astVisitor, y yielder, clauses []ast.Clause) yielder {
- for _, c := range clauses {
- if n, ok := c.(*ast.ForClause); ok {
- params := ¶ms{}
- fn := &lambdaExpr{newExpr(n.Source), params, nil}
- v.setScope(n, fn)
- }
- }
- for i := len(clauses) - 1; i >= 0; i-- {
- switch n := clauses[i].(type) {
- case *ast.ForClause:
- fn := v.mapScope(n).(*lambdaExpr)
- fn.value = y
-
- key := "_"
- if n.Key != nil {
- key = v.ident(n.Key)
- }
- f := v.Label(key, true)
- fn.add(f, &basicType{newExpr(n.Key), stringKind | intKind})
-
- f = v.Label(v.ident(n.Value), true)
- fn.add(f, &top{})
-
- y = &feed{newExpr(n.Source), v.walk(n.Source), fn}
-
- case *ast.IfClause:
- y = &guard{newExpr(n.Condition), v.walk(n.Condition), y}
- }
- }
- return y
-}
diff --git a/cue/ast/astutil/file_test.go b/cue/ast/astutil/file_test.go
index 27861c5..15f0636 100644
--- a/cue/ast/astutil/file_test.go
+++ b/cue/ast/astutil/file_test.go
@@ -18,11 +18,11 @@
"strings"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/token"
- "cuelang.org/go/internal/legacy/cue"
"github.com/google/go-cmp/cmp"
)
diff --git a/cue/ast_test.go b/cue/ast_test.go
deleted file mode 100644
index 1b37205..0000000
--- a/cue/ast_test.go
+++ /dev/null
@@ -1,645 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
- "strings"
- "testing"
-
- "github.com/stretchr/testify/assert"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/parser"
- "cuelang.org/go/internal"
-)
-
-func TestCompile(t *testing.T) {
- testCases := []struct {
- in string
- out string
- }{{
- in: `{
- foo: 1,
- }`,
- out: "<0>{<1>{foo: 1}, }", // emitted value, but no top-level fields
- }, {
- in: `
- foo: 1
- `,
- out: "<0>{foo: 1}",
- }, {
- in: `
- a: true
- b: 2K
- c: 4_5
- d: "abc"
- e: 3e2 // 3h1m2ss
- "": 8
- `,
- out: `<0>{"": 8, a: true, b: 2000, c: 45, d: "abc", e: 3e+2}`,
- }, {
- in: `
- a: null
- b: true
- c: false
- `,
- out: "<0>{a: null, b: true, c: false}",
- }, {
- in: `
- a: <1
- b: >= 0 & <= 10
- c: != null
- d: >100
- `,
- out: `<0>{a: <1, b: (>=0 & <=10), c: !=null, d: >100}`,
- }, {
- in: "" +
- `a: "\(4)",
- b: "one \(a) two \( a + c )",
- c: "one"`,
- out: `<0>{a: ""+4+"", b: "one "+<0>.a+" two "+(<0>.a + <0>.c)+"", c: "one"}`,
- }, {
- in: "" +
- `a: """
- multi
- """,
- b: '''
- hello world
- goodbye globe
- welcome back planet
- '''`,
- out: `<0>{a: "multi", b: 'hello world\ngoodbye globe\nwelcome back planet'}`,
- }, {
- in: "" +
- `a: """
- multi \(4)
- """,
- b: """
- hello \("world")
- goodbye \("globe")
- welcome back \("planet")
- """`,
- out: `<0>{a: "multi "+4+"", b: "hello "+"world"+"\ngoodbye "+"globe"+"\nwelcome back "+"planet"+""}`,
- }, {
- in: `
- a: _
- b: int
- c: float
- d: bool
- e: string
- `,
- out: "<0>{a: _, b: int, c: float, d: bool, e: string}",
- }, {
- in: `
- a: null
- b: true
- c: false
- `,
- out: "<0>{a: null, b: true, c: false}",
- }, {
- in: `
- null: null
- true: true
- false: false
- `,
- out: "<0>{null: null, true: true, false: false}",
- }, {
- in: `
- a: 1 + 2
- b: -2 - 3
- c: !d
- d: true
- `,
- out: "<0>{a: (1 + 2), b: (-2 - 3), c: !<0>.d, d: true}",
- }, {
- in: `
- l0: 3*[int]
- l0: [1, 2, 3]
- l1: <=5*[string]
- l1: ["a", "b"]
- l2: (<=5)*[{ a: int }]
- l2: [{a: 1}, {a: 2, b: 3}]
- l3: (<=10)*[int]
- l3: [1, 2, 3, ...]
- l4: [1, 2, ...]
- l4: [...int]
- l5: [1, ...int]
-
- s1: ((<=6)*[int])[2:3]
- s2: [0,2,3][1:2]
-
- e0: (>=2 & <=5)*[{}]
- e0: [{}]
- `,
- out: `<0>{l0: ((3 * [int]) & [1,2,3]), l1: ((<=5 * [string]) & ["a","b"]), l2: ((<=5 * [<1>{a: int}]) & [<2>{a: 1},<3>{a: 2, b: 3}]), l3: ((<=10 * [int]) & [1,2,3, ...]), l4: ([1,2, ...] & [, ...int]), l5: [1, ...int], s1: (<=6 * [int])[2:3], s2: [0,2,3][1:2], e0: (((>=2 & <=5) * [<4>{}]) & [<5>{}])}`,
- }, {
- in: `
- a: 5 | "a" | true
- aa: 5 | *"a" | true
- b: c: {
- cc: { ccc: 3 }
- }
- d: true
- `,
- out: "<0>{a: (5 | \"a\" | true), aa: (5 | *\"a\" | true), b: <1>{c: <2>{cc: <3>{ccc: 3}}}, d: true}",
- }, {
- in: `
- a: a: { b: a } // referencing ancestor nodes is legal.
- a: b: a.a // do lookup before merging of nodes
- b: a.a // different node as a.a.b, as first node counts
- c: a // same node as b, as first node counts
- d: a["a"]
- `,
- out: `<0>{a: (<1>{a: <2>{b: <1>.a}} & <3>{b: <0>.a.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
- // TODO(#152): should be
- // out: `<0>{a: (<1>{a: <2>{b: <2>}} & <3>{b: <3>.a}), b: <0>.a.a, c: <0>.a, d: <0>.a["a"]}`,
- }, {
- // bunch of aliases
- in: `
- let a1 = a2
- let a2 = 5
- b: a1
- let a3 = d
- c: {
- d: {
- r: a3
- }
- r: a3
- }
- d: { e: 4 }
- `,
- out: `<0>{b: 5, c: <1>{d: <2>{r: <0>.d}, r: <0>.d}, d: <3>{e: 4}}`,
- }, {
- // aliases with errors
- in: `
- let e1 = 1
- let e1 = 2
- e1v: e1
- e2: "a"
- let e2 = "a"
- `,
- out: `alias "e1" redeclared in same scope:` + "\n" +
- " test:3:3\n" +
- `cannot have both alias and field with name "e2" in same scope:` + "\n" +
- " test:6:3\n" +
- "<0>{}",
- }, {
- in: `
- let a = b
- b: {
- c: a // reference to own root.
- }
- `,
- out: `<0>{b: <1>{c: <0>.b}}`,
- // }, {
- // // TODO: Support this:
- // // optional fields
- // in: `
- // X=[string]: { chain: X | null }
- // `,
- // out: `
- // `,
- }, {
- // optional fields
- in: `
- [ID=string]: { name: ID }
- A="foo=bar": 3
- a: A
- B=bb: 4
- b1: B
- b1: bb
- C="\(a)": 5
- c: C
- `,
- out: `<0>{[]: <1>(ID: string)-><2>{name: <1>.ID}, "foo=bar": 3, a: <0>."foo=bar", bb: 4, b1: (<0>.bb & <0>.bb), c: <0>[""+<0>.a+""]""+<0>.a+"": 5}`,
- }, {
- // optional fields with key filters
- in: `
- JobID: =~"foo"
- a: [JobID]: { name: string }
-
- [<"s"]: { other: string }
- `,
- out: `<0>{` +
- `[<"s"]: <1>(_: string)-><2>{other: string}, ` +
- `JobID: =~"foo", a: <3>{` +
- `[<0>.JobID]: <4>(_: string)-><5>{name: string}, ` +
- `}}`,
- }, {
- // Issue #251
- // TODO: the is one of the cases where it is relatively easy to catch
- // a structural cycle. We should be able, however, to break the cycle
- // with a post-validation constraint. Clean this up with the evaluator
- // update.
- in: `
- {
- [x]: 3
- }
- x: "x"
- `,
- out: "reference \"x\" in label expression refers to field against which it would be matched:\n test:3:5\n<0>{}",
- }, {
- // illegal alias usage
- in: `
- [X=string]: { chain: X | null }
- a: X
- Y=[string]: 3
- a: X
- `,
- out: `a: invalid label: cannot reference fields with square brackets labels outside the field value:
- test:3:7
-a: invalid label: cannot reference fields with square brackets labels outside the field value:
- test:5:7
-<0>{}`,
- }, {
- // detect duplicate aliases, even if illegal
- in: `
- [X=string]: int
- X=[string]: int
- Y=foo: int
- let Y=3
- Z=[string]: { Z=3, a: int } // allowed
- `,
- out: `alias "X" redeclared in same scope:
- test:3:3
-alias "Y" redeclared in same scope:
- test:5:3
-<0>{}`,
- }, {
- in: `
- a: {
- [name=_]: { n: name }
- k: 1
- }
- b: {
- [X=_]: { x: 0, y: 1 }
- v: {}
- }
- `,
- out: `<0>{a: <1>{[]: <2>(name: string)-><3>{n: <2>.name}, k: 1}, b: <4>{[]: <5>(X: string)-><6>{x: 0, y: 1}, v: <7>{}}}`,
- }, {
- in: `
- a: {
- for k, v in b if b.a < k {
- "\(k)": v
- }
- }
- b: {
- a: 1
- b: 2
- c: 3
- }
- `,
- out: `<0>{a: <1>{ <2>for k, v in <0>.b if (<0>.b.a < <2>.k) yield <3>{""+<2>.k+"": <2>.v}}, b: <4>{a: 1, b: 2, c: 3}}`,
- }, {
- in: `
- a: { for k, v in b {"\(v)": v} }
- b: { a: "aa", b: "bb", c: "cc" }
- `,
- out: `<0>{a: <1>{ <2>for k, v in <0>.b yield <3>{""+<2>.v+"": <2>.v}}, b: <4>{a: "aa", b: "bb", c: "cc"}}`,
- }, {
- in: `
- a: [ for _, v in b { v } ]
- b: { a: 1, b: 2, c: 3 }
- `,
- out: `<0>{a: [ <1>for _, v in <0>.b yield <1>.v ], b: <2>{a: 1, b: 2, c: 3}}`,
- }, {
- in: `
- a: >=1 & <=2
- b: >=1 & >=2 & <=3
- c: >="a" & <"b"
- d: >(2+3) & <(4+5)
- `,
- out: `<0>{a: (>=1 & <=2), b: ((>=1 & >=2) & <=3), c: (>="a" & <"b"), d: (>(2 + 3) & <(4 + 5))}`,
- }, {
- in: `
- a: *1,
- b: **1 | 2
- `,
- out: `a: preference mark not allowed at this position:
- test:2:7
-b: preference mark not allowed at this position:
- test:3:8
-<0>{}`,
- }, {
- in: `
- a: int @foo(1,"str")
- `,
- out: "<0>{a: int @foo(1,\"str\")}",
- }, {
- in: `
- a: int @b([,b) // invalid
- `,
- out: "unexpected ')':\n test:2:18\nattribute missing ')':\n test:3:3\n<0>{}",
- }, {
- in: `
- a: d: {
- #base
- #info: {
- ...
- }
- Y: #info.X
- }
-
- #base: {
- #info: {...}
- }
-
- a: [Name=string]: { #info: {
- X: "foo"
- }}
- `,
- out: `<0>{` +
- `a: (<1>{d: <2>{#info: <3>{...}, Y: <2>.#info.X}, <0>.#base} & <4>{[]: <5>(Name: string)-><6>{#info: <7>C{X: "foo"}}, }), ` +
- `#base: <8>C{#info: <9>{...}}}`,
- }, {
- in: `
- #def: {
- Type: string
- Text: string
- Size: int
- }
-
- #def: {
- Type: "B"
- Size: 0
- } | {
- Type: "A"
- Size: 1
- }
- `,
- out: `<0>{` +
- `#def: (<1>C{Size: int, Type: string, Text: string} & (<2>C{Size: 0, Type: "B"} | <3>C{Size: 1, Type: "A"}))` +
- `}`,
- }, {
- // Issue #172
- in: `
- package testenv
- #env_: [NAME=_]: [VALUE=_]
- #env_: foo: "bar"
- `,
- out: "#env_.*: alias not allowed in list:\n test:3:20\n<0>{}",
- }, {
- // Issue #276
- in: `
- a: int=<100
- `,
- out: "alias \"int\" not allowed as value:\n test:2:11\n<0>{}",
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- ctx, root, err := compileFileWithErrors(t, tc.in)
- buf := &bytes.Buffer{}
- if err != nil {
- errors.Print(buf, err, nil)
- }
- buf.WriteString(debugStr(ctx, root))
- got := buf.String()
- if got != tc.out {
- t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
- }
- })
- }
-}
-
-func TestEmit(t *testing.T) {
- testCases := []struct {
- in string
- out string
- rw rewriteMode
- }{{
- in: `"\(hello), \(world)!"` + `
- hello: "Hello"
- world: "World"
- `,
- out: `""+<0>.hello+", "+<0>.world+"!"`,
- rw: evalRaw,
- }, {
- in: `"\(hello), \(world)!"` + `
- hello: "Hello"
- world: "World"
- `,
- out: `"Hello, World!"`,
- rw: evalPartial,
- }, {
- // Ambiguous disjunction must cary over to emit value.
- in: `baz
-
- baz: {
- a: 8000 | 7080
- a: 7080 | int
- }`,
- out: `<0>{a: (8000 | 7080)}`,
- rw: evalFull,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- ctx, root := compileFile(t, tc.in)
- v := testResolve(ctx, root.emit, tc.rw)
- if got := debugStr(ctx, v); got != tc.out {
- t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
- }
- })
- }
-}
-
-func TestEval(t *testing.T) {
- testCases := []struct {
- in string
- expr string
- out string
- }{{
- in: `
- hello: "Hello"
- world: "World"
- `,
- expr: `"\(hello), \(world)!"`,
- out: `"Hello, World!"`,
- }, {
- in: `
- a: { b: 2, c: 3 }
- z: 1
- `,
- expr: `a.b + a.c + z`,
- out: `6`,
- }, {
- in: `
- a: { b: 2, c: 3 }
- `,
- expr: `{ d: a.b + a.c }`,
- out: `<0>{d: 5}`,
- }, {
- in: `
- a: "Hello World!"
- `,
- expr: `strings.ToUpper(a)`,
- out: `"HELLO WORLD!"`,
- }, {
- in: `
- a: 0x8
- b: 0x1`,
- expr: `bits.Or(a, b)`, // package shorthand
- out: `9`,
- }, {
- in: `
- a: 0x8
- b: 0x1`,
- expr: `math.Or(a, b)`,
- out: `_|_(<0>.Or:undefined field "Or")`,
- }, {
- in: `a: 0x8`,
- expr: `mathematics.Abs(a)`,
- out: `_|_(reference "mathematics" not found)`,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- ctx, inst, errs := compileInstance(t, tc.in)
- if errs != nil {
- t.Fatal(errs)
- }
- expr, err := parser.ParseExpr("<test>", tc.expr)
- if err != nil {
- t.Fatal(err)
- }
- evaluated := evalExpr(ctx, inst.eval(ctx), expr)
- v := testResolve(ctx, evaluated, evalFull)
- if got := debugStr(ctx, v); got != tc.out {
- t.Errorf("output differs:\ngot %q\nwant %q", got, tc.out)
- }
- })
- }
-}
-
-func TestResolution(t *testing.T) {
- testCases := []struct {
- name string
- in string
- err string
- }{{
- name: "package name identifier should not resolve to anything",
- in: `package time
-
- import "time"
-
- a: time.Time
- `,
- }, {
- name: "duplicate_imports.cue",
- in: `
- import "time"
- import time "math"
-
- t: time.Time
- `,
- err: "time redeclared as imported package name",
- }, {
- name: "unused_import",
- in: `
- import "time"
- `,
- err: `imported and not used: "time"`,
- }, {
- name: "nonexisting import package",
- in: `import "doesnotexist"`,
- err: `package "doesnotexist" not found`,
- }, {
- name: "duplicate with different name okay",
- in: `
- import "time"
- import time2 "time"
-
- a: time.Time
- b: time2.Time
- `,
- }}
- for _, tc := range testCases {
- t.Run(tc.name, func(t *testing.T) {
- var r Runtime
- _, err := r.Compile(tc.name, tc.in)
- got := err == nil
- want := tc.err == ""
- if got != want {
- t.Fatalf("got %v; want %v", err, tc.err)
- }
- if err != nil {
- if s := err.Error(); !strings.Contains(s, tc.err) {
- t.Errorf("got %v; want %v", err, tc.err)
- }
- }
- })
- }
-}
-
-func TestShadowing(t *testing.T) {
- spec := ast.NewImport(nil, "list")
- testCases := []struct {
- file *ast.File
- want string
- }{{
- file: &ast.File{Decls: []ast.Decl{
- &ast.ImportDecl{Specs: []*ast.ImportSpec{spec}},
- &ast.EmbedDecl{
- Expr: ast.NewStruct(
- &ast.Field{
- Label: mustParseExpr(`list`).(*ast.Ident),
- Value: ast.NewCall(
- ast.NewSel(
- &ast.Ident{Name: "list", Node: spec},
- "Min")),
- })},
- }},
- want: "import \"list\", let LIST=list, {list: LIST.Min()}",
- }, {
- file: &ast.File{Decls: []ast.Decl{
- &ast.ImportDecl{Specs: []*ast.ImportSpec{spec}},
- &ast.Field{
- Label: ast.NewIdent("a"),
- Value: ast.NewStruct(&ast.Field{
- Label: mustParseExpr(`list`).(*ast.Ident),
- Value: ast.NewCall(
- ast.NewSel(&ast.Ident{Name: "list", Node: spec}, "Min")),
- }),
- },
- }},
- want: "import \"list\", let LIST=list, a: {list: LIST.Min()}",
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- var r Runtime
- inst, err := r.CompileFile(tc.file)
- if err != nil {
- t.Fatal(err)
- }
-
- ctx := r.index().newContext()
-
- n, _ := export(ctx, inst, inst.rootStruct, options{
- raw: true,
- })
- got := internal.DebugStr(n)
- assert.Equal(t, got, tc.want)
- })
- }
-}
-
-func mustParseExpr(expr string) ast.Expr {
- ex, err := parser.ParseExpr("cue", expr)
- if err != nil {
- panic(err)
- }
- return ex
-}
diff --git a/cue/attr.go b/cue/attr.go
deleted file mode 100644
index 235bed7..0000000
--- a/cue/attr.go
+++ /dev/null
@@ -1,132 +0,0 @@
-// Copyright 2019 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 cue
-
-import (
- "sort"
- "strings"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/internal"
-)
-
-// This file includes functionality for parsing attributes.
-// These functions are slightly more permissive than the spec. Together with the
-// scanner and parser the full spec is implemented, though.
-
-// attributes is used to store per-key attribute text for a fields.
-// It deliberately does not implement the value interface, as it should
-// never act as a value in any way.
-type attributes struct {
- attr []attr
-}
-type attr struct {
- text string
- offset int
-}
-
-func (a *attr) key() string {
- return a.text[1:a.offset]
-}
-
-func (a *attr) body() string {
- return a.text[a.offset+1 : len(a.text)-1]
-}
-
-func createAttrs(ctx *context, src source, attrs []*ast.Attribute) (a *attributes, err *bottom) {
- if len(attrs) == 0 {
- return nil, nil
- }
- as := []attr{}
- for _, a := range attrs {
- index := strings.IndexByte(a.Text, '(')
- n := len(a.Text)
- if index < 2 || a.Text[0] != '@' || a.Text[n-1] != ')' {
- return nil, ctx.mkErr(newNode(a), "invalid attribute %q", a.Text)
- }
- as = append(as, attr{a.Text[:n], index})
-
- if err := internal.ParseAttrBody(src.Pos(), a.Text[index+1:n-1]).Err; err != nil {
- return nil, ctx.mkErr(newNode(a), err)
- }
- }
-
- sort.SliceStable(as, func(i, j int) bool { return as[i].text < as[j].text })
- // TODO: remove these restrictions.
- for i := 1; i < len(as); i++ {
- if ai, aj := as[i-1], as[i]; ai.key() == aj.key() {
- n := newNode(attrs[0])
- return nil, ctx.mkErr(n, "multiple attributes for key %q", ai.key())
- }
- }
-
- return &attributes{as}, nil
-}
-
-// unifyAttrs merges the attributes from a and b. It may return either a or b
-// if a and b are identical.
-func unifyAttrs(ctx *context, src source, a, b *attributes) (atrs *attributes, err evaluated) {
- if a == b {
- return a, nil
- }
- if a == nil {
- return b, nil
- }
- if b == nil {
- return a, nil
- }
-
- if len(a.attr) == len(b.attr) {
- for i, x := range a.attr {
- if x != b.attr[i] {
- goto notSame
- }
- }
- return a, nil
- }
-
-notSame:
- as := append(a.attr, b.attr...)
-
- // remove duplicates and error on conflicts
- sort.Slice(as, func(i, j int) bool { return as[i].text < as[j].text })
- k := 0
- for i := 1; i < len(as); i++ {
- if ak, ai := as[k], as[i]; ak.key() == ai.key() {
- if ak.body() == ai.body() {
- continue
- }
- return nil, ctx.mkErr(src, "conflicting attributes for key %q", ai.key())
- }
- k++
- as[k] = as[i]
- }
-
- return &attributes{as[:k+1]}, nil
-}
-
-// parsedAttr holds positional information for a single parsedAttr.
-type parsedAttr struct {
- fields []keyValue
-}
-
-type keyValue struct {
- data string
- equal int // index of equal sign or 0 if non-existing
-}
-
-func (kv *keyValue) text() string { return kv.data }
-func (kv *keyValue) key() string { return kv.data[:kv.equal] }
-func (kv *keyValue) value() string { return kv.data[kv.equal+1:] }
diff --git a/cue/attr_test.go b/cue/attr_test.go
deleted file mode 100644
index 3a929c4..0000000
--- a/cue/attr_test.go
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright 2019 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 cue
-
-import (
- "reflect"
- "strings"
- "testing"
-
- "cuelang.org/go/cue/ast"
-)
-
-func TestCreateAttrs(t *testing.T) {
- testdata := []struct {
- // space-separated lists of attributes
- in, out string
- err string
- }{{
- in: "@foo()",
- out: "foo:",
- }, {
- in: "@b(bb) @aaa(aa,)",
- out: "aaa:aa, b:bb",
- }, {
- in: "@b(a,",
- err: "invalid attribute",
- }, {
- in: "@b(foo) @b(foo)",
- err: "attributes",
- }, {
- in: "@b('' ,b)",
- err: "invalid attribute",
- }, {
- in: `@foo(,"bar")`,
- out: `foo:,"bar"`,
- }, {
- in: `@foo("bar",1)`,
- out: `foo:"bar",1`,
- }, {
- in: `@foo("bar")`,
- out: `foo:"bar"`,
- }, {
- in: `@foo(,"bar",1)`,
- out: `foo:,"bar",1`,
- }}
- for _, tc := range testdata {
- t.Run(tc.in, func(t *testing.T) {
- a := []*ast.Attribute{}
- for _, s := range strings.Split(tc.in, " ") {
- a = append(a, &ast.Attribute{Text: s})
- }
- attrs, err := createAttrs(&context{}, baseValue{}, a)
-
- if tc.err != "" {
- if err == nil || !strings.Contains(debugStr(&context{}, err), tc.err) {
- t.Errorf("error was %v; want %v", err, tc.err)
- }
- return
- }
- if err != nil {
- t.Fatal(err)
- }
- sa := []string{}
- for _, a := range attrs.attr {
- sa = append(sa, a.key()+":"+a.body())
- }
- if got := strings.Join(sa, " "); got != tc.out {
- t.Errorf("got %v; want %v", got, tc.out)
- }
- })
- }
-}
-
-func TestUnifyAttrs(t *testing.T) {
- parse := func(s string) *attributes {
- a := []*ast.Attribute{}
- for _, s := range strings.Split(s, " ") {
- a = append(a, &ast.Attribute{Text: s})
- }
- attrs, _ := createAttrs(&context{}, baseValue{}, a)
- return attrs
- }
- foo := parse("@foo()")
-
- testdata := []struct {
- // space-separated lists of attributes
- a, b, out *attributes
- err string
- }{{
- a: nil,
- b: nil,
- out: nil,
- }, {
- a: nil,
- b: foo,
- out: foo,
- }, {
- a: foo,
- b: nil,
- out: foo,
- }, {
- a: foo,
- b: foo,
- out: foo,
- }, {
- a: foo,
- b: parse("@bar()"),
- out: parse("@bar() @foo()"),
- }, {
- a: foo,
- b: parse("@bar() @foo()"),
- out: parse("@bar() @foo()"),
- }, {
- a: parse("@bar() @foo()"),
- b: parse("@foo() @bar()"),
- out: parse("@bar() @foo()"),
- }, {
- a: parse("@bar() @foo()"),
- b: parse("@foo() @baz()"),
- out: parse("@bar() @baz() @foo()"),
- }, {
- a: parse("@foo(ab)"),
- b: parse("@foo(cd)"),
- err: `conflicting attributes for key "foo"`,
- }}
- for _, tc := range testdata {
- t.Run("", func(t *testing.T) {
- attrs, err := unifyAttrs(&context{}, baseValue{}, tc.a, tc.b)
- if tc.err != "" {
- if !strings.Contains(debugStr(&context{}, err), tc.err) {
- t.Errorf("error was %v; want %v", err, tc.err)
- }
- return
- }
- if err != nil {
- t.Fatal(err)
- }
- if !reflect.DeepEqual(attrs, tc.out) {
- t.Errorf("\ngot: %v;\nwant: %v", attrs, tc.out)
- }
- })
- }
-}
diff --git a/cue/binop.go b/cue/binop.go
deleted file mode 100644
index a310bde..0000000
--- a/cue/binop.go
+++ /dev/null
@@ -1,1333 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
- "fmt"
- "math/big"
- "regexp"
- "sort"
- "strings"
- "time"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/token"
-)
-
-// binSrc returns a baseValue representing a binary expression of the given
-// values.
-func binSrc(pos token.Pos, op op, a, b value) baseValue {
- return baseValue{&computedSource{pos, op, a, b}}
-}
-
-func binOp(ctx *context, src source, op op, left, right evaluated) (result evaluated) {
- _, isUnify := op.unifyType()
- if b, ok := left.(*bottom); ok {
- if isUnify && b.exprDepth == 0 && cycleError(b) != nil {
- ctx.cycleErr = true
- return right
- }
- return left
- }
- if b, ok := right.(*bottom); ok {
- if isUnify && b.exprDepth == 0 && cycleError(b) != nil {
- ctx.cycleErr = true
- return left
- }
- return right
- }
-
- left = convertBuiltin(left)
- right = convertBuiltin(right)
-
- leftKind := left.Kind()
- rightKind := right.Kind()
- kind, invert, msg := matchBinOpKind(op, leftKind, rightKind)
- if kind == bottomKind {
- simplify := func(v, orig value) value {
- switch x := v.(type) {
- case *disjunction:
- return orig
- case *binaryExpr:
- if x.Op == opDisjunction {
- return orig
- }
- default:
- return x
- }
- return v
- }
- var l, r value = left, right
- if x, ok := src.(*binaryExpr); ok {
- l = simplify(x.X, left)
- r = simplify(x.Y, right)
- }
- return ctx.mkErr(src, msg, op, ctx.str(l), ctx.str(r), leftKind, rightKind)
- }
- if kind.hasReferences() {
- panic("unexpected references in expression")
- }
- if invert {
- left, right = right, left
- }
- if !isUnify {
- // Any operation other than unification or disjunction must be on
- // concrete types. Disjunction is handled separately.
- if !leftKind.isGround() || !rightKind.isGround() {
- return ctx.mkErr(src, codeIncomplete, "incomplete error")
- }
- ctx.incEvalDepth()
- v := left.binOp(ctx, src, op, right) // may return incomplete
- ctx.decEvalDepth()
- return v
- }
-
- // isUnify
-
- // TODO: unify type masks.
- if left == right {
- return left
- }
- if isTop(left) {
- return right
- }
- if isTop(right) {
- return left
- }
-
- if dl, ok := left.(*disjunction); ok {
- return distribute(ctx, src, op, dl, right)
- } else if dr, ok := right.(*disjunction); ok {
- return distribute(ctx, src, op, dr, left)
- }
-
- if _, ok := right.(*unification); ok {
- return right.binOp(ctx, src, op, left)
- }
-
- // TODO: value may be incomplete if there is a cycle. Instead of an error
- // schedule an assert and return the atomic value, if applicable.
- v := left.binOp(ctx, src, op, right)
- if isBottom(v) {
- v := right.binOp(ctx, src, op, left)
- // Return the original failure if both fail, as this will result in
- // better error messages.
- if !isBottom(v) || isCustom(v) {
- return v
- }
- }
- return v
-}
-
-type mVal struct {
- val evaluated
- mark bool
-}
-
-// distribute distributes a value over the element of a disjunction in a
-// unification operation.
-// TODO: this is an exponential algorithm. There is no reason to have to
-// resolve this early. Revise this to only do early pruning but not a full
-// evaluation.
-func distribute(ctx *context, src source, op op, x, y evaluated) evaluated {
- dn := &disjunction{baseValue: src.base()}
- dist(ctx, dn, false, op, mVal{x, true}, mVal{y, true})
- return dn.normalize(ctx, src).val
-}
-
-func dist(ctx *context, d *disjunction, mark bool, op op, x, y mVal) {
- if dx, ok := x.val.(*disjunction); ok {
- if dx.HasDefaults {
- mark = true
- d.HasDefaults = true
- }
- for _, dxv := range dx.Values {
- m := dxv.Default || !dx.HasDefaults
- dist(ctx, d, mark, op, mVal{dxv.Val.evalPartial(ctx), m}, y)
- }
- return
- }
- if dy, ok := y.val.(*disjunction); ok {
- if dy.HasDefaults {
- mark = true
- d.HasDefaults = true
- }
- for _, dxy := range dy.Values {
- m := dxy.Default || !dy.HasDefaults
- dist(ctx, d, mark, op, x, mVal{dxy.Val.evalPartial(ctx), m})
- }
- return
- }
- src := binSrc(token.NoPos, op, x.val, y.val)
- d.add(ctx, binOp(ctx, src, op, x.val, y.val), mark && x.mark && y.mark)
-}
-
-func (x *disjunction) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- panic("unreachable: special-cased")
-}
-
-func (x *bottom) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- panic("unreachable: special-cased")
-}
-
-// add adds to a unification. Note that the value cannot be a struct and thus
-// there is no need to distinguish between checked and unchecked unification.
-func (x *unification) add(ctx *context, src source, v evaluated) evaluated {
- for progress := true; progress; {
- progress = false
- k := 0
-
- for i, vx := range x.Values {
- a := binOp(ctx, src, opUnify, vx, v)
- switch _, isUnify := a.(*unification); {
- case isBottom(a):
- if !isIncomplete(a) {
- return a
- }
- fallthrough
- case isUnify:
- x.Values[k] = x.Values[i]
- k++
- continue
- }
- // k will not be raised in this iteration. So the outer loop
- // will ultimately terminate as k reaches 0.
- // In practice it is seems unlikely that there will be more than
- // two iterations for any addition.
- // progress = true
- v = a
- }
- if k == 0 {
- return v
- }
- x.Values = x.Values[:k]
- }
- x.Values = append(x.Values, v)
- return nil
-}
-
-func (x *unification) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- if _, isUnify := op.unifyType(); isUnify {
- // Cannot be checked unification.
- u := &unification{baseValue: baseValue{src}}
- u.Values = append(u.Values, x.Values...)
- if y, ok := other.(*unification); ok {
- for _, vy := range y.Values {
- if v := u.add(ctx, src, vy); v != nil {
- return v
- }
- }
- } else if v := u.add(ctx, src, other); v != nil {
- return v
- }
- return u
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *top) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch op {
- case opUnify, opUnifyUnchecked:
- return other
- }
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, codeIncomplete, "binary operation on (incomplete) top value")
-}
-
-func (x *basicType) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- k := unifyType(x.Kind(), other.Kind())
- switch y := other.(type) {
- case *basicType:
- switch op {
- // TODO: other types.
- case opUnify, opUnifyUnchecked:
- if k&typeKinds != bottomKind {
- return &basicType{binSrc(src.Pos(), op, x, other), k & typeKinds}
- }
- }
-
- case *bound:
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, codeIncomplete, "%s with incomplete values", op)
-
- case *numLit:
- if op == opUnify || op == opUnifyUnchecked {
- if k == y.K {
- return y
- }
- return y.specialize(k)
- }
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, codeIncomplete, "%s with incomplete values", op)
-
- default:
- if k&typeKinds != bottomKind {
- return other
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func checkBounds(ctx *context, src source, r *bound, op op, a, b evaluated) evaluated {
- v := binOp(ctx, src, op, a, b)
- if isBottom(v) || !v.(*boolLit).B {
- return errOutOfBounds(ctx, src.Pos(), r, a)
- }
- return nil
-}
-
-func errOutOfBounds(ctx *context, pos token.Pos, r *bound, v evaluated) *bottom {
- if pos == token.NoPos {
- pos = r.Pos()
- }
- e := mkBin(ctx, pos, opUnify, r, v)
- msg := "invalid value %v (out of bound %v)"
- switch r.Op {
- case opNeq, opNMat:
- msg = "invalid value %v (excluded by %v)"
- case opMat:
- msg = "invalid value %v (does not match %v)"
- }
- return ctx.mkErr(e, msg, ctx.str(v), ctx.str(r))
-}
-
-func opInfo(op op) (cmp op, norm int) {
- switch op {
- case opGtr:
- return opGeq, 1
- case opGeq:
- return opGtr, 1
- case opLss:
- return opLeq, -1
- case opLeq:
- return opLss, -1
- case opNeq:
- return opNeq, 0
- case opMat:
- return opMat, 2
- case opNMat:
- return opNMat, 3
- }
- panic("cue: unreachable")
-}
-
-func (x *bound) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- xv := x.Expr.(evaluated)
-
- newSrc := binSrc(src.Pos(), op, x, other)
- switch op {
- case opUnify, opUnifyUnchecked:
- k, _, msg := matchBinOpKind(opUnify, x.Kind(), other.Kind())
- if k == bottomKind {
- return ctx.mkErr(src, msg, opUnify, ctx.str(x), ctx.str(other), x.Kind(), other.Kind())
- }
- switch y := other.(type) {
- case *basicType:
- k := unifyType(x.k, y.Kind())
- if k == x.k {
- return x
- }
- return newBound(ctx, newSrc.base(), x.Op, k, xv)
-
- case *bound:
- yv := y.Expr.(evaluated)
- if !xv.Kind().isGround() || !yv.Kind().isGround() {
- return ctx.mkErr(newSrc, codeIncomplete, "cannot add incomplete values")
- }
-
- cmp, xCat := opInfo(x.Op)
- _, yCat := opInfo(y.Op)
-
- switch {
- case xCat == yCat:
- if x.Op == opNeq || x.Op == opMat || x.Op == opNMat {
- if test(ctx, x, opEql, xv, yv) {
- return x
- }
- break // unify the two bounds
- }
-
- // xCat == yCat && x.op != opNeq
- // > a & >= b
- // > a if a >= b
- // >= b if a < b
- // > a & > b
- // > a if a >= b
- // > b if a < b
- // >= a & > b
- // >= a if a > b
- // > b if a <= b
- // >= a & >= b
- // >= a if a > b
- // >= b if a <= b
- // inverse is true as well.
-
- // Tighten bound.
- if test(ctx, x, cmp, xv, yv) {
- return x
- }
- return y
-
- case xCat == -yCat:
- if xCat == -1 {
- x, y = y, x
- }
- a, aOK := x.Expr.(evaluated).(*numLit)
- b, bOK := y.Expr.(evaluated).(*numLit)
-
- if !aOK || !bOK {
- break
- }
-
- var d, lo, hi apd.Decimal
- lo.Set(&a.X)
- hi.Set(&b.X)
- if k&floatKind == 0 {
- // Readjust bounds for integers.
- if x.Op == opGeq {
- // >=3.4 ==> >=4
- _, _ = apd.BaseContext.Ceil(&lo, &a.X)
- } else {
- // >3.4 ==> >3
- _, _ = apd.BaseContext.Floor(&lo, &a.X)
- }
- if y.Op == opLeq {
- // <=2.3 ==> <= 2
- _, _ = apd.BaseContext.Floor(&hi, &b.X)
- } else {
- // <2.3 ==> < 3
- _, _ = apd.BaseContext.Ceil(&hi, &b.X)
- }
- }
-
- cond, err := apd.BaseContext.Sub(&d, &hi, &lo)
- if cond.Inexact() || err != nil {
- break
- }
-
- // attempt simplification
- // numbers
- // >=a & <=b
- // a if a == b
- // _|_ if a < b
- // >=a & <b
- // _|_ if b <= a
- // >a & <=b
- // _|_ if b <= a
- // >a & <b
- // _|_ if b <= a
-
- // integers
- // >=a & <=b
- // a if b-a == 0
- // _|_ if a < b
- // >=a & <b
- // a if b-a == 1
- // _|_ if b <= a
- // >a & <=b
- // b if b-a == 1
- // _|_ if b <= a
- // >a & <b
- // a+1 if b-a == 2
- // _|_ if b <= a
-
- n := newNum(src, k&numKind, a.rep|b.rep)
- switch diff, err := d.Int64(); {
- case err != nil:
-
- case diff == 1:
- if k&floatKind == 0 {
- if x.Op == opGeq && y.Op == opLss {
- return n.set(&lo)
- }
- if x.Op == opGtr && y.Op == opLeq {
- return n.set(&hi)
- }
- }
-
- case diff == 2:
- if k&floatKind == 0 && x.Op == opGtr && y.Op == opLss {
- _, _ = apd.BaseContext.Add(&d, d.SetInt64(1), &lo)
- return n.set(&d)
-
- }
-
- case diff == 0:
- if x.Op == opGeq && y.Op == opLeq {
- return n.set(&lo)
- }
- fallthrough
-
- case d.Negative:
- return ctx.mkErr(newSrc, "conflicting bounds %v and %v",
- ctx.str(x), ctx.str(y))
- }
-
- case x.Op == opNeq:
- if !test(ctx, x, y.Op, xv, yv) {
- return y
- }
-
- case y.Op == opNeq:
- if !test(ctx, x, x.Op, yv, xv) {
- return x
- }
- }
- return &unification{newSrc, []evaluated{x, y}}
-
- case *numLit:
- if err := checkBounds(ctx, src, x, x.Op, y, xv); err != nil {
- return err
- }
- // Narrow down number type.
- if y.K != k {
- return y.specialize(k)
- }
- return other
-
- case *nullLit, *boolLit, *durationLit, *list, *structLit, *stringLit, *bytesLit:
- // All remaining concrete types. This includes non-comparable types
- // for comparison to null.
- if err := checkBounds(ctx, src, x, x.Op, y, xv); err != nil {
- return err
- }
- return y
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *customValidator) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- newSrc := binSrc(src.Pos(), op, x, other)
- switch op {
- case opUnify, opUnifyUnchecked:
- k, _, msg := matchBinOpKind(opUnify, x.Kind(), other.Kind())
- if k == bottomKind {
- return ctx.mkErr(src, msg, op, ctx.str(x), ctx.str(other), x.Kind(), other.Kind())
- }
- switch y := other.(type) {
- case *basicType:
- k := unifyType(x.Kind(), y.Kind())
- if k == x.Kind() {
- return x
- }
- return &unification{newSrc, []evaluated{x, y}}
-
- case *customValidator:
- return &unification{newSrc, []evaluated{x, y}}
-
- case *bound:
- return &unification{newSrc, []evaluated{x, y}}
-
- case *numLit:
- if err := x.check(ctx, y); err != nil {
- return err
- }
- // Narrow down number type.
- if y.K != k {
- return y.specialize(k)
- }
- return other
-
- case *nullLit, *boolLit, *durationLit, *list, *structLit, *stringLit, *bytesLit:
- // All remaining concrete types. This includes non-comparable types
- // for comparison to null.
- if err := x.check(ctx, y); err != nil {
- return err
- }
- return y
- }
- }
- return ctx.mkErr(src, "invalid operation %v and %v (operator not defined for custom validator)", ctx.str(x), ctx.str(other))
-}
-
-func (x *customValidator) check(ctx *context, v evaluated) evaluated {
- args := make([]evaluated, 1+len(x.Args))
- args[0] = v
- for i, v := range x.Args {
- args[1+i] = v.(evaluated)
- }
- res := x.Builtin.call(ctx, x, args...)
- if isBottom(res) {
- return res.(evaluated)
- }
- if b, ok := res.(*boolLit); !ok {
- // should never reach here
- return ctx.mkErr(x, "invalid custom validator")
- } else if !b.B {
- var buf bytes.Buffer
- fmt.Fprintf(&buf, "%s.%s", ctx.LabelStr(x.Builtin.pkg), x.Builtin.Name)
- buf.WriteString("(")
- for _, a := range x.Args {
- buf.WriteString(ctx.str(a))
- }
- buf.WriteString(")")
- return ctx.mkErr(x, "invalid value %s (does not satisfy %s)", ctx.str(v), buf.String())
- }
- return nil
-}
-
-func evalLambda(ctx *context, a value, finalize bool) (l *lambdaExpr, err evaluated) {
- if a == nil {
- return nil, nil
- }
- // NOTE: the values of a lambda might still be a disjunction
- e := ctx.manifest(a)
- if isBottom(e) {
- return nil, e
- }
- l, ok := e.(*lambdaExpr)
- if !ok {
- return nil, ctx.mkErr(a, "value must be lambda")
- }
- lambda := ctx.deref(l).(*lambdaExpr)
- if finalize {
- lambda.value = wrapFinalize(ctx, lambda.value)
- }
- return lambda, nil
-}
-
-func (x *structLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- y, ok := other.(*structLit)
- unchecked, isUnify := op.unifyType()
- if !ok || !isUnify {
- return ctx.mkIncompatible(src, op, x, other)
- }
-
- // TODO: unify emit
-
- x = ctx.deref(x).(*structLit)
- y = ctx.deref(y).(*structLit)
- if x == y {
- return x
- }
- arcs := make(arcs, 0, len(x.Arcs)+len(y.Arcs))
- var base baseValue
- if src.computed() != nil {
- base = baseValue{src.computed()}
- } else {
- base = binSrc(src.Pos(), op, x, other)
- }
- obj := &structLit{
- base, // baseValue
- x.emit, // emit
- nil, // template
- x.closeStatus | y.closeStatus, // closeStatus
- nil, // comprehensions
- arcs, // arcs
- nil, // attributes
- }
- defer ctx.pushForwards(x, obj, y, obj).popForwards()
-
- optionals, err := unifyOptionals(ctx, src, op, x, y)
- if err != nil {
- return err
- }
- obj.optionals = optionals
-
- // If unifying with a closed struct that does not have a template,
- // we need to apply the template to all elements.
-
- sz := len(x.comprehensions) + len(y.comprehensions)
- obj.comprehensions = make([]compValue, sz)
- for i, c := range x.comprehensions {
- obj.comprehensions[i] = compValue{
- checked: c.checked || (!unchecked && y.isClosed()),
- comp: ctx.copy(c.comp),
- }
- }
- for i, c := range y.comprehensions {
- obj.comprehensions[i+len(x.comprehensions)] = compValue{
- checked: c.checked || (!unchecked && x.isClosed()),
- comp: ctx.copy(c.comp),
- }
- }
-
- for _, a := range x.Arcs {
- found := false
- for _, b := range y.Arcs {
- if a.Label == b.Label {
- found = true
- break
- }
- }
- if !unchecked && !found && !y.allows(ctx, a.Label) && !a.definition {
- if a.optional {
- continue
- }
- // TODO: pass position of key, not value. Currently does not have
- // a position.
- return ctx.mkErr(a.v, a.v, "field %q not allowed in closed struct",
- ctx.LabelStr(a.Label))
- }
- cp := ctx.copy(a.v)
- obj.Arcs = append(obj.Arcs,
- arc{a.Label, a.optional, a.definition, cp, nil, a.attrs, a.docs})
- }
-outer:
- for _, a := range y.Arcs {
- v := ctx.copy(a.v)
- found := false
- for i, b := range obj.Arcs {
- if a.Label == b.Label {
- found = true
- if a.definition != b.definition {
- src := binSrc(x.Pos(), op, a.v, b.v)
- return ctx.mkErr(src, "field %q declared as definition and regular field",
- ctx.LabelStr(a.Label))
- }
- w := b.v
- if x.closeStatus.shouldFinalize() {
- w = wrapFinalize(ctx, w)
- }
- if y.closeStatus.shouldFinalize() {
- v = wrapFinalize(ctx, v)
- }
- v = mkBin(ctx, src.Pos(), op, w, v)
- obj.Arcs[i].v = v
- obj.Arcs[i].Value = nil
- obj.Arcs[i].optional = a.optional && b.optional
- obj.Arcs[i].docs = mergeDocs(a.docs, b.docs)
- attrs, err := unifyAttrs(ctx, src, a.attrs, b.attrs)
- if err != nil {
- return err
- }
- obj.Arcs[i].attrs = attrs
- continue outer
- }
- }
- if !unchecked && !found && !x.allows(ctx, a.Label) && !a.definition {
- if a.optional {
- continue
- }
- // TODO: pass position of key, not value. Currently does not have a
- // position.
- return ctx.mkErr(a.v, x, "field %q not allowed in closed struct",
- ctx.LabelStr(a.Label))
- }
- a.setValue(v)
- obj.Arcs = append(obj.Arcs, a)
- }
- sort.Stable(obj)
-
- if unchecked && obj.optionals.isFull() {
- obj.closeStatus.unclose()
- }
-
- return obj
-}
-
-func (x *structLit) rewriteOpt(ctx *context) (*optionals, evaluated) {
- fn := func(v value) value {
- if l, ok := v.(*lambdaExpr); ok {
- l, err := evalLambda(ctx, l, x.closeStatus.shouldFinalize())
- if err != nil {
- return err
- }
- v = l
- }
- return ctx.copy(v)
- }
- c, err := x.optionals.rewrite(fn)
- if err != nil {
- return c, err
- }
- return c, nil
-}
-
-func unifyOptionals(ctx *context, src source, op op, x, y *structLit) (o *optionals, err evaluated) {
- if x.optionals == nil && y.optionals == nil {
- return nil, nil
- }
- left, err := x.rewriteOpt(ctx)
- if err != nil {
- return left, err
- }
- right, err := y.rewriteOpt(ctx)
- if err != nil {
- return right, err
- }
-
- closeStatus := x.closeStatus | y.closeStatus
- switch {
- case left.isDotDotDot() && right.isDotDotDot():
-
- case left == nil && (!x.closeStatus.isClosed() || op == opUnifyUnchecked):
- return right, nil
-
- case right == nil && (!y.closeStatus.isClosed() || op == opUnifyUnchecked):
- return left, nil
-
- case op == opUnify && closeStatus.isClosed(),
- left != nil && (left.left != nil || left.right != nil),
- right != nil && (right.left != nil || right.right != nil):
- return &optionals{closeStatus, op, left, right, nil}, nil
- }
-
- // opUnify where both structs are open or opUnifyUnchecked
- for _, f := range right.fields {
- left.add(ctx, f.key, f.value)
- }
- return left, nil
-}
-
-func (x *nullLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- // TODO: consider using binSrc instead of src.base() for better traceability.
- switch other.(type) {
- case *nullLit:
- switch op {
- case opEql:
- return &boolLit{baseValue: src.base(), B: true}
- case opNeq:
- return &boolLit{baseValue: src.base(), B: false}
- case opUnify, opUnifyUnchecked:
- return x
- }
-
- case *bound:
- // Not strictly necessary, but handling this results in better error
- // messages.
- if op == opUnify || op == opUnifyUnchecked {
- return other.binOp(ctx, src, opUnify, x)
- }
-
- default:
- switch op {
- case opEql:
- return &boolLit{baseValue: src.base(), B: false}
- case opNeq:
- return &boolLit{baseValue: src.base(), B: true}
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *boolLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch y := other.(type) {
- case *basicType:
- // range math
- return x
-
- case *boolLit:
- switch op {
- case opUnify, opUnifyUnchecked:
- if x.B != y.B {
- return ctx.mkErr(x, "conflicting values %v and %v", x.B, y.B)
- }
- return x
- case opLand:
- return boolTonode(src, x.B && y.B)
- case opLor:
- return boolTonode(src, x.B || y.B)
- case opEql:
- return boolTonode(src, x.B == y.B)
- case opNeq:
- return boolTonode(src, x.B != y.B)
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *stringLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch y := other.(type) {
- // case *basicType:
- // return x
-
- // TODO: rangelit
-
- case *stringLit:
- str := other.strValue()
- switch op {
- case opUnify, opUnifyUnchecked:
- str := other.strValue()
- if x.Str != str {
- src := mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "conflicting values %v and %v",
- ctx.str(x), ctx.str(y))
- }
- return x
- case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
- return cmpTonode(src, op, strings.Compare(x.Str, str))
- case opAdd:
- src := binSrc(src.Pos(), op, x, other)
- return &stringLit{src, x.Str + str, nil}
- case opMat:
- if y.RE == nil {
- // This really should not happen, but leave in for safety.
- b, err := regexp.MatchString(str, x.Str)
- if err != nil {
- return ctx.mkErr(src, "error parsing regexp: %v", err)
- }
- return boolTonode(src, b)
- }
- return boolTonode(src, y.RE.MatchString(x.Str))
- case opNMat:
- if y.RE == nil {
- // This really should not happen, but leave in for safety.
- b, err := regexp.MatchString(str, x.Str)
- if err != nil {
- return ctx.mkErr(src, "error parsing regexp: %v", err)
- }
- return boolTonode(src, !b)
- }
- return boolTonode(src, !y.RE.MatchString(x.Str))
- }
- case *numLit:
- switch op {
- case opMul:
- src := binSrc(src.Pos(), op, x, other)
- return &stringLit{src, strings.Repeat(x.Str, y.intValue(ctx)), nil}
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *bytesLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch y := other.(type) {
- // case *basicType:
- // return x
-
- // TODO: rangelit
-
- case *bytesLit:
- b := y.B
- switch op {
- case opUnify, opUnifyUnchecked:
- if !bytes.Equal(x.B, b) {
- return ctx.mkErr(x, "conflicting values %v and %v",
- ctx.str(x), ctx.str(y))
- }
- return x
- case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
- return cmpTonode(src, op, bytes.Compare(x.B, b))
- case opAdd:
- copy := append([]byte(nil), x.B...)
- copy = append(copy, b...)
- return &bytesLit{binSrc(src.Pos(), op, x, other), copy, nil}
- }
-
- case *numLit:
- switch op {
- case opMul:
- src := binSrc(src.Pos(), op, x, other)
- return &bytesLit{src, bytes.Repeat(x.B, y.intValue(ctx)), nil}
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func test(ctx *context, src source, op op, a, b evaluated) bool {
- v := binOp(ctx, src, op, a, b)
- if isBottom(v) {
- return false
- }
- return v.(*boolLit).B
-}
-
-func leq(ctx *context, src source, a, b evaluated) bool {
- if isTop(a) || isTop(b) {
- return true
- }
- v := binOp(ctx, src, opLeq, a, b)
- if isBottom(v) {
- return false
- }
- return v.(*boolLit).B
-}
-
-// TODO: should these go?
-func maxNum(v value) value {
- switch x := v.(type) {
- case *numLit:
- return x
- case *bound:
- switch x.Op {
- case opLeq:
- return x.Expr
- case opLss:
- return &binaryExpr{x.baseValue, opSub, x.Expr, one}
- }
- return &basicType{x.baseValue, intKind}
- }
- return v
-}
-
-func minNum(v value) value {
- switch x := v.(type) {
- case *numLit:
- return x
- case *bound:
- switch x.Op {
- case opGeq:
- return x.Expr
- case opGtr:
- return &binaryExpr{x.baseValue, opAdd, x.Expr, one}
- }
- return &basicType{x.baseValue, intKind}
- }
- return v
-}
-
-func cmpTonode(src source, op op, r int) evaluated {
- result := false
- switch op {
- case opLss:
- result = r == -1
- case opLeq:
- result = r != 1
- case opEql, opUnify, opUnifyUnchecked:
- result = r == 0
- case opNeq:
- result = r != 0
- case opGeq:
- result = r != -1
- case opGtr:
- result = r == 1
- }
- return boolTonode(src, result)
-}
-
-func (x *numLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch y := other.(type) {
- case *basicType, *bound, *customValidator: // for better error reporting
- if op == opUnify || op == opUnifyUnchecked {
- return y.binOp(ctx, src, op, x)
- }
- case *numLit:
- k, _, _ := matchBinOpKind(op, x.Kind(), y.Kind())
- if k == bottomKind {
- break
- }
- switch op {
- case opLss, opLeq, opEql, opNeq, opGeq, opGtr:
- return cmpTonode(src, op, x.X.Cmp(&y.X))
- }
- n := newNum(src.base(), k, x.rep|y.rep)
- switch op {
- case opUnify, opUnifyUnchecked:
- if x.X.Cmp(&y.X) != 0 {
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "conflicting values %v and %v",
- ctx.str(x), ctx.str(y))
- }
- if k != x.K {
- n.X = x.X
- return n
- }
- return x
- case opAdd:
- _, _ = ctx.Add(&n.X, &x.X, &y.X)
- case opSub:
- _, _ = ctx.Sub(&n.X, &x.X, &y.X)
- case opMul:
- _, _ = ctx.Mul(&n.X, &x.X, &y.X)
- case opQuo:
- cond, err := ctx.Quo(&n.X, &x.X, &y.X)
- if err != nil {
- return ctx.mkErr(src, err.Error())
- }
- if cond.DivisionByZero() {
- return ctx.mkErr(src, "division by zero")
- }
- n.K = floatKind
- case opIDiv:
- if y.X.IsZero() {
- return ctx.mkErr(src, "division by zero")
- }
- intOp(ctx, n, (*big.Int).Div, x, y)
- case opIMod:
- if y.X.IsZero() {
- return ctx.mkErr(src, "division by zero")
- }
- intOp(ctx, n, (*big.Int).Mod, x, y)
- case opIQuo:
- if y.X.IsZero() {
- return ctx.mkErr(src, "division by zero")
- }
- intOp(ctx, n, (*big.Int).Quo, x, y)
- case opIRem:
- if y.X.IsZero() {
- return ctx.mkErr(src, "division by zero")
- }
- intOp(ctx, n, (*big.Int).Rem, x, y)
- }
- return n
-
- case *durationLit:
- if op == opMul {
- fd := float64(y.d)
- // TODO: check range
- f, _ := x.X.Float64()
- d := time.Duration(f * fd)
- return &durationLit{binSrc(src.Pos(), op, x, other), d}
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-type intFunc func(z, x, y *big.Int) *big.Int
-
-func intOp(ctx *context, n *numLit, fn intFunc, a, b *numLit) {
- var x, y apd.Decimal
- _, _ = ctx.RoundToIntegralValue(&x, &a.X)
- if x.Negative {
- x.Coeff.Neg(&x.Coeff)
- }
- _, _ = ctx.RoundToIntegralValue(&y, &b.X)
- if y.Negative {
- y.Coeff.Neg(&y.Coeff)
- }
- fn(&n.X.Coeff, &x.Coeff, &y.Coeff)
- if n.X.Coeff.Sign() < 0 {
- n.X.Coeff.Neg(&n.X.Coeff)
- n.X.Negative = true
- }
- n.K = intKind
-}
-
-// TODO: check overflow
-
-func (x *durationLit) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch y := other.(type) {
- case *basicType:
- // infinity math
-
- case *durationLit:
- switch op {
- case opUnify, opUnifyUnchecked:
- if x.d != y.d {
- return ctx.mkIncompatible(src, op, x, other)
- }
- return other
- case opLss:
- return boolTonode(src, x.d < y.d)
- case opLeq:
- return boolTonode(src, x.d <= y.d)
- case opEql:
- return boolTonode(src, x.d == y.d)
- case opNeq:
- return boolTonode(src, x.d != y.d)
- case opGeq:
- return boolTonode(src, x.d >= y.d)
- case opGtr:
- return boolTonode(src, x.d > y.d)
- case opAdd:
- return &durationLit{binSrc(src.Pos(), op, x, other), x.d + y.d}
- case opSub:
- return &durationLit{binSrc(src.Pos(), op, x, other), x.d - y.d}
- case opQuo:
- n := newFloat(src.base(), base10).setInt64(int64(x.d))
- d := apd.New(int64(y.d), 0)
- // TODO: check result if this code becomes undead.
- _, _ = ctx.Quo(&n.X, &n.X, d)
- return n
- case opIRem:
- n := newInt(src.base(), base10).setInt64(int64(x.d % y.d))
- n.X.Exponent = -9
- return n
- }
-
- case *numLit:
- switch op {
- case opMul:
- // TODO: check range
- f, _ := y.X.Float64()
- d := time.Duration(float64(x.d) * f)
- return &durationLit{binSrc(src.Pos(), op, x, other), d}
- case opQuo:
- // TODO: check range
- f, _ := y.X.Float64()
- d := time.Duration(float64(x.d) * f)
- return &durationLit{binSrc(src.Pos(), op, x, other), d}
- case opIRem:
- d := x.d % time.Duration(y.intValue(ctx))
- return &durationLit{binSrc(src.Pos(), op, x, other), d}
- }
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *list) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- switch op {
- case opUnify, opUnifyUnchecked:
- y, ok := other.(*list)
- if !ok {
- break
- }
-
- n := binOp(ctx, src, op, x.len.(evaluated), y.len.(evaluated))
- if isBottom(n) {
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "conflicting list lengths: %v", n)
- }
- sx := x.elem.Arcs
- xa := sx
- sy := y.elem.Arcs
- ya := sy
- for len(xa) < len(ya) {
- xa = append(xa, arc{Label: label(len(xa)), v: x.typ})
- }
- for len(ya) < len(xa) {
- ya = append(ya, arc{Label: label(len(ya)), v: y.typ})
- }
-
- typ := x.typ
- max, ok := n.(*numLit)
- if !ok || len(xa) < max.intValue(ctx) {
- typ = mkBin(ctx, src.Pos(), op, x.typ, y.typ)
- }
-
- // TODO: use forwarding instead of this mild hack.
- x.elem.Arcs = xa
- y.elem.Arcs = ya
- s := binOp(ctx, src, op, x.elem, y.elem).(*structLit)
- x.elem.Arcs = sx
- y.elem.Arcs = sy
-
- base := binSrc(src.Pos(), op, x, other)
- return &list{baseValue: base, elem: s, typ: typ, len: n}
-
- case opEql, opNeq:
- y, ok := other.(*list)
- if !ok {
- break
- }
- if len(x.elem.Arcs) != len(y.elem.Arcs) {
- return boolTonode(src, false)
- }
- for i := range x.elem.Arcs {
- if !test(ctx, src, op, x.at(ctx, i), y.at(ctx, i)) {
- return boolTonode(src, false)
- }
- }
- return boolTonode(src, true)
-
- case opAdd:
- y, ok := other.(*list)
- if !ok {
- break
- }
- n := &list{baseValue: binSrc(src.Pos(), op, x, other), typ: y.typ}
- arcs := []arc{}
- for _, v := range x.elem.Arcs {
- arcs = append(arcs, arc{Label: label(len(arcs)), v: v.v})
- }
- for _, v := range y.elem.Arcs {
- arcs = append(arcs, arc{Label: label(len(arcs)), v: v.v})
- }
- switch v := y.len.(type) {
- case *numLit:
- // Closed list
- n.len = newInt(v.base(), v.rep).setInt(len(arcs))
- default:
- // Open list
- n.len = y.len // TODO: add length of x?
- }
- n.elem = &structLit{baseValue: n.baseValue, Arcs: arcs}
- return n
-
- case opMul:
- k := other.Kind()
- if !k.isAnyOf(intKind) {
- panic("multiplication must be int type")
- }
- n := &list{baseValue: binSrc(src.Pos(), op, x, other), typ: x.typ}
- arcs := []arc{}
- if len(x.elem.Arcs) > 0 {
- if !k.isGround() {
- // should never reach here.
- break
- }
- if ln := other.(*numLit).intValue(ctx); ln > 0 {
- for i := 0; i < ln; i++ {
- // TODO: copy values
- for _, a := range x.elem.Arcs {
- arcs = append(arcs, arc{Label: label(len(arcs)), v: a.v})
- }
- }
- } else if ln < 0 {
- return ctx.mkErr(src, "negative number %d multiplies list", ln)
- }
- }
- switch v := x.len.(type) {
- case *numLit:
- // Closed list
- n.len = newInt(v.base(), v.rep).setInt(len(arcs))
- default:
- // Open list
- n.len = x.len // TODO: multiply length?
- }
- n.elem = &structLit{baseValue: n.baseValue, Arcs: arcs}
- return n
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *lambdaExpr) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- if y, ok := other.(*lambdaExpr); ok && op == opUnify {
- x = ctx.deref(x).(*lambdaExpr)
- y = ctx.deref(y).(*lambdaExpr)
- n, m := len(x.params.arcs), len(y.params.arcs)
- if n != m {
- src = mkBin(ctx, src.Pos(), op, x, other)
- return ctx.mkErr(src, "number of params should match (%d != %d)", n, m)
- }
- arcs := make([]arc, len(x.arcs))
- lambda := &lambdaExpr{binSrc(src.Pos(), op, x, other), ¶ms{arcs}, nil}
- defer ctx.pushForwards(x, lambda, y, lambda).popForwards()
-
- xVal := ctx.copy(x.value)
- yVal := ctx.copy(y.value)
- lambda.value = mkBin(ctx, src.Pos(), opUnify, xVal, yVal)
-
- for i := range arcs {
- xArg := ctx.copy(x.at(ctx, i)).(evaluated)
- yArg := ctx.copy(y.at(ctx, i)).(evaluated)
- v := binOp(ctx, src, op, xArg, yArg)
- if isBottom(v) {
- return v
- }
- arcs[i] = arc{Label: x.arcs[i].Label, v: v}
- }
-
- return lambda
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *builtin) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- if _, isUnify := op.unifyType(); isUnify && evaluated(x) == other {
- return x
- }
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *feed) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *guard) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *yield) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- return ctx.mkIncompatible(src, op, x, other)
-}
-
-func (x *fieldComprehension) binOp(ctx *context, src source, op op, other evaluated) evaluated {
- return ctx.mkIncompatible(src, op, x, other)
-}
diff --git a/cue/build.go b/cue/build.go
index d7849c9..65f0932 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -15,6 +15,7 @@
package cue
import (
+ "strings"
"sync"
"cuelang.org/go/cue/ast"
@@ -23,6 +24,8 @@
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/compile"
"cuelang.org/go/internal/core/runtime"
)
@@ -38,7 +41,7 @@
}
func init() {
- internal.GetRuntimeOld = func(instance interface{}) interface{} {
+ internal.GetRuntimeNew = func(instance interface{}) interface{} {
switch x := instance.(type) {
case Value:
return &Runtime{idx: x.idx}
@@ -51,7 +54,7 @@
}
}
- internal.CheckAndForkRuntimeOld = func(runtime, value interface{}) interface{} {
+ internal.CheckAndForkRuntimeNew = func(runtime, value interface{}) interface{} {
r := runtime.(*Runtime)
idx := value.(Value).ctx().index
if idx != r.idx {
@@ -59,6 +62,13 @@
}
return &Runtime{idx: newIndex(idx)}
}
+
+ internal.CoreValue = func(value interface{}) (runtime, vertex interface{}) {
+ if v, ok := value.(Value); ok && v.v != nil {
+ return v.idx.Index, v.v
+ }
+ return nil, nil
+ }
}
func dummyLoad(token.Pos, string) *build.Instance { return nil }
@@ -192,6 +202,7 @@
//
// All instances belonging to the same package should share this index.
type index struct {
+ adt.Runtime
*runtime.Index
loaded map[*build.Instance]*Instance
@@ -201,16 +212,35 @@
// sharedIndex is used for indexing builtins and any other labels common to
// all instances.
var sharedIndex = &index{
- Index: runtime.SharedIndex,
- loaded: map[*build.Instance]*Instance{},
+ Runtime: runtime.SharedRuntimeNew,
+ Index: runtime.SharedIndexNew,
+ loaded: map[*build.Instance]*Instance{},
+}
+
+// NewRuntime creates a *runtime.Runtime with builtins preloaded.
+func NewRuntime() *runtime.Runtime {
+ idx := runtime.NewIndex(sharedIndex.Index)
+ r := runtime.NewWithIndex(idx)
+ i := &index{
+ Runtime: r,
+ Index: idx,
+ loaded: map[*build.Instance]*Instance{},
+ }
+ r.Data = i
+ return r
}
// newIndex creates a new index.
func newIndex(parent *index) *index {
- return &index{
- Index: runtime.NewIndex(parent.Index),
- loaded: map[*build.Instance]*Instance{},
+ idx := runtime.NewIndex(parent.Index)
+ r := runtime.NewWithIndex(idx)
+ i := &index{
+ Runtime: r,
+ Index: idx,
+ loaded: map[*build.Instance]*Instance{},
}
+ r.Data = i
+ return i
}
func isBuiltin(s string) bool {
@@ -219,30 +249,93 @@
}
func (idx *index) loadInstance(p *build.Instance) *Instance {
- if inst := idx.loaded[p]; inst != nil {
- if !inst.complete {
- // cycles should be detected by the builder and it should not be
- // possible to construct a build.Instance that has them.
- panic("cue: cycle")
+ _ = visitInstances(p, func(p *build.Instance, errs errors.Error) errors.Error {
+ if inst := idx.loaded[p]; inst != nil {
+ if !inst.complete {
+ // cycles should be detected by the builder and it should not be
+ // possible to construct a build.Instance that has them.
+ panic("cue: cycle")
+ }
+ return inst.Err
}
- return inst
- }
- errs := runtime.ResolveFiles(idx.Index, p, isBuiltin)
- files := p.Files
- inst := newInstance(idx, p)
- idx.loaded[p] = inst
- if inst.Err == nil {
- // inst.instance.index.state = s
- // inst.instance.inst = p
+ err := runtime.ResolveFiles(idx.Index, p, isBuiltin)
+ errs = errors.Append(errs, err)
+
+ v, err := compile.Files(nil, idx.Runtime, p.Files...)
+ errs = errors.Append(errs, err)
+
+ inst := newInstance(idx, p, v)
+ idx.loaded[p] = inst
inst.Err = errs
- for _, f := range files {
- err := inst.insertFile(f)
- inst.Err = errors.Append(inst.Err, err)
+
+ inst.ImportPath = p.ImportPath
+ inst.complete = true
+
+ return inst.Err
+ })
+
+ return idx.loaded[p]
+}
+
+// TODO: runtime.Runtime has a similar, much simpler, implementation. This
+// code should go.
+
+type visitFunc func(b *build.Instance, err errors.Error) (errs errors.Error)
+
+// visitInstances calls f for each transitive dependency.
+//
+// It passes any errors that occur in transitive dependencies to the visitFunc.
+// visitFunc must return the errors it is passed or return nil to ignore it.
+func visitInstances(b *build.Instance, f visitFunc) (errs errors.Error) {
+ v := visitor{b: b, f: f, errs: b.Err}
+ for _, file := range b.Files {
+ v.file(file)
+ }
+ return v.f(b, v.errs)
+}
+
+type visitor struct {
+ b *build.Instance
+ f visitFunc
+ errs errors.Error
+}
+
+func (v *visitor) addErr(e errors.Error) {
+ v.errs = errors.Append(v.errs, e)
+}
+
+func (v *visitor) file(file *ast.File) {
+ for _, d := range file.Decls {
+ switch x := d.(type) {
+ case *ast.Package:
+ case *ast.ImportDecl:
+ for _, s := range x.Specs {
+ v.spec(s)
+ }
+ case *ast.CommentGroup:
+ default:
+ return
}
}
- inst.ImportPath = p.ImportPath
+}
- inst.complete = true
- return inst
+func (v *visitor) spec(spec *ast.ImportSpec) {
+ info, err := astutil.ParseImportSpec(spec)
+ if err != nil {
+ v.addErr(errors.Promote(err, "invalid import path"))
+ return
+ }
+
+ pkg := v.b.LookupImport(info.ID)
+ if pkg == nil {
+ if strings.Contains(info.ID, ".") {
+ v.addErr(errors.Newf(spec.Pos(),
+ "package %q imported but not defined in %s",
+ info.ID, v.b.ImportPath))
+ }
+ return
+ }
+
+ v.addErr(visitInstances(pkg, v.f))
}
diff --git a/cue/build_test.go b/cue/build_test.go
index 7817ec7..4cf0ccb 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -16,12 +16,12 @@
import (
"fmt"
- "strings"
"testing"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/token"
+ "cuelang.org/go/internal/core/debug"
)
func TestFromExpr(t *testing.T) {
@@ -36,7 +36,7 @@
ast.NewString("Hello"),
ast.NewString("World"),
),
- out: `["Hello","World"]`,
+ out: `["Hello", "World"]`,
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
@@ -45,8 +45,7 @@
if err != nil {
t.Fatal(err)
}
- ctx := inst.newContext()
- if got := debugStr(ctx, inst.eval(ctx)); got != tc.out {
+ if got := fmt.Sprint(inst.Value()); got != tc.out {
t.Errorf("\n got: %v; want %v", got, tc.out)
}
})
@@ -86,7 +85,7 @@
emit string
}{{
insts(&bimport{"", files(`test: "ok"`)}),
- `{test: "ok"}`,
+ `{test:"ok"}`,
}, {
insts(&bimport{"",
files(
@@ -200,7 +199,8 @@
if err := insts[0].Err; err != nil {
got = err.Error()
} else {
- got = strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value()))
+ cfg := &debug.Config{Compact: true}
+ got = debug.NodeString(insts[0].Index, insts[0].Value().v, cfg)
}
if got != tc.emit {
t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
diff --git a/cue/builtin.go b/cue/builtin.go
index 2da6821..9fa9fa1 100644
--- a/cue/builtin.go
+++ b/cue/builtin.go
@@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+//go:generate go run golang.org/x/tools/cmd/goimports -w -local cuelang.org/go builtins.go
+//go:generate gofmt -s -w builtins.go
+
package cue
import (
@@ -21,6 +24,7 @@
"math/big"
"path"
"sort"
+ "strings"
"github.com/cockroachdb/apd/v2"
@@ -28,6 +32,10 @@
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/compile"
+ "cuelang.org/go/internal/core/convert"
+ "cuelang.org/go/internal/core/runtime"
)
// A builtin is a builtin function or constant.
@@ -49,7 +57,6 @@
// map[string]T
//
type builtin struct {
- baseValue
Name string
pkg label
Params []kind
@@ -64,21 +71,27 @@
cue string
}
-func mustCompileBuiltins(ctx *context, p *builtinPkg, pkgName string) *structLit {
- obj := &structLit{}
+func mustCompileBuiltins(ctx *context, p *builtinPkg, pkgName string) *adt.Vertex {
+ obj := &adt.Vertex{}
pkgLabel := ctx.Label(pkgName, false)
+ st := &adt.StructLit{}
+ if len(p.native) > 0 {
+ obj.AddConjunct(adt.MakeConjunct(nil, st))
+ }
for _, b := range p.native {
b.pkg = pkgLabel
f := ctx.Label(b.Name, false) // never starts with _
// n := &node{baseValue: newBase(imp.Path)}
- var v evaluated = b
+ var v adt.Expr = toBuiltin(ctx, b)
if b.Const != "" {
v = mustParseConstBuiltin(ctx, b.Name, b.Const)
}
- obj.Arcs = append(obj.Arcs, arc{Label: f, v: v})
+ st.Decls = append(st.Decls, &adt.Field{
+ Label: f,
+ Value: v,
+ })
}
- sort.Sort(obj)
// Parse builtin CUE
if p.cue != "" {
@@ -86,32 +99,77 @@
if err != nil {
panic(fmt.Errorf("could not parse %v: %v", p.cue, err))
}
- v := newVisitor(ctx.index, nil, nil, nil, false)
- value := v.walk(expr)
- pkg := value.evalPartial(ctx).(*structLit)
- for _, a := range pkg.Arcs {
- // Discard option status and attributes at top level.
- // TODO: filter on capitalized fields?
- obj.insertValue(ctx, a.Label, false, false, a.v, nil, a.docs)
+ c, err := compile.Expr(nil, ctx.opCtx.Runtime, expr)
+ if err != nil {
+ panic(fmt.Errorf("could compile parse %v: %v", p.cue, err))
}
+ obj.AddConjunct(c)
+ }
+
+ // We could compile lazily, but this is easier for debugging.
+ obj.Finalize(ctx.opCtx)
+ if err := obj.Err(ctx.opCtx, adt.Finalized); err != nil {
+ panic(err.Err)
}
return obj
}
+func toBuiltin(ctx *context, b *builtin) *adt.Builtin {
+ x := &adt.Builtin{
+ Params: b.Params,
+ Result: b.Result,
+ Package: b.pkg,
+ Name: b.Name,
+ }
+ x.Func = func(ctx *adt.OpContext, args []adt.Value) (ret adt.Expr) {
+ runtime := ctx.Impl().(*runtime.Runtime)
+ index := runtime.Data.(*index)
+
+ // call, _ := ctx.Source().(*ast.CallExpr)
+ c := &callCtxt{
+ idx: index,
+ // src: call,
+ ctx: index.newContext(),
+ args: args,
+ builtin: b,
+ }
+ defer func() {
+ var errVal interface{} = c.err
+ if err := recover(); err != nil {
+ errVal = err
+ }
+ ret = processErr(c, errVal, ret)
+ }()
+ b.Func(c)
+ switch v := c.ret.(type) {
+ case adt.Value:
+ return v
+ case *valueError:
+ return v.err
+ }
+ if c.err != nil {
+ return nil
+ }
+ return convert.GoValueToValue(ctx, c.ret, true)
+ }
+ return x
+}
+
// newConstBuiltin parses and creates any CUE expression that does not have
// fields.
-func mustParseConstBuiltin(ctx *context, name, val string) evaluated {
+func mustParseConstBuiltin(ctx *context, name, val string) adt.Expr {
expr, err := parser.ParseExpr("<builtin:"+name+">", val)
if err != nil {
panic(err)
}
- v := newVisitor(ctx.index, nil, nil, nil, false)
- value := v.walk(expr)
- return value.evalPartial(ctx)
-}
+ c, err := compile.Expr(nil, ctx.Runtime, expr)
+ if err != nil {
+ panic(err)
+ }
+ return c.Expr()
-var _ caller = &builtin{}
+}
var lenBuiltin = &builtin{
Name: "len",
@@ -152,23 +210,39 @@
}
c.ret = len(s)
default:
- c.ret = errors.Newf(token.NoPos,
- "invalid argument type %v", k)
+ c.ret = c.ctx.opCtx.Newf("invalid argument type %v", k)
}
},
}
+func pos(n adt.Node) (p token.Pos) {
+ if n == nil {
+ return
+ }
+ src := n.Source()
+ if src == nil {
+ return
+ }
+ return src.Pos()
+}
+
var closeBuiltin = &builtin{
Name: "close",
Params: []kind{structKind},
Result: structKind,
Func: func(c *callCtxt) {
- s, ok := c.args[0].(*structLit)
+ s, ok := c.args[0].(*adt.Vertex)
if !ok {
- c.ret = errors.Newf(c.args[0].Pos(), "struct argument must be concrete")
+ c.ret = errors.Newf(pos(c.args[0]), "struct argument must be concrete")
return
}
- c.ret = s.close()
+ if s.IsClosed(c.ctx.opCtx) {
+ c.ret = s
+ } else {
+ v := *s
+ v.Closed = nil // TODO: set dedicated Closer.
+ c.ret = &v
+ }
},
}
@@ -179,12 +253,12 @@
Func: func(c *callCtxt) {
iter := c.iter(0)
if !iter.Next() {
- c.ret = &top{baseValue{c.src}}
+ c.ret = &top{}
return
}
- u := iter.Value().v.v
+ var u adt.Expr = iter.Value().v
for iter.Next() {
- u = mkBin(c.ctx, c.src.Pos(), opUnify, u, iter.Value().v.v)
+ u = &adt.BinaryExpr{Op: adt.AndOp, X: u, Y: iter.Value().v}
}
c.ret = u
},
@@ -196,11 +270,11 @@
Result: intKind,
Func: func(c *callCtxt) {
iter := c.iter(0)
- d := []dValue{}
+ d := []adt.Disjunct{}
for iter.Next() {
- d = append(d, dValue{iter.Value().v.v, false})
+ d = append(d, adt.Disjunct{iter.Value().v, false})
}
- c.ret = &disjunction{baseValue{c.src}, d, nil, false}
+ c.ret = &adt.DisjunctionExpr{nil, d, false}
if len(d) == 0 {
// TODO(manifest): This should not be unconditionally incomplete,
// but it requires results from comprehensions and all to have
@@ -209,33 +283,14 @@
// an open list or struct. This would actually be exactly what
// that means. The error here could then only add an incomplete
// status if the source is open.
- c.ret = c.ctx.mkErr(c.src, codeIncomplete, "empty list in call to or")
+ c.ret = &adt.Bottom{
+ Code: adt.IncompleteError,
+ Err: errors.Newf(c.Pos(), "empty list in call to or"),
+ }
}
},
}
-func (x *builtin) representedKind() kind {
- if x.isValidator() {
- return x.Params[0]
- }
- return x.Kind()
-}
-
-func (x *builtin) Kind() kind {
- return lambdaKind
-}
-
-func (x *builtin) evalPartial(ctx *context) evaluated {
- return x
-}
-
-func (x *builtin) subsumesImpl(s *subsumer, v value) bool {
- if y, ok := v.(*builtin); ok {
- return x == y
- }
- return false
-}
-
func (x *builtin) name(ctx *context) string {
if x.pkg == 0 {
return x.Name
@@ -247,61 +302,9 @@
return len(x.Params) == 1 && x.Result == boolKind
}
-func convertBuiltin(v evaluated) evaluated {
- x, ok := v.(*builtin)
- if ok && x.isValidator() {
- return &customValidator{v.base(), x, []evaluated{}}
- }
- return v
-}
-
-func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) {
- if x.Func == nil {
- return ctx.mkErr(x, "builtin %s is not a function", x.name(ctx))
- }
- if len(x.Params)-1 == len(args) && x.Result == boolKind {
- // We have a custom builtin
- return &customValidator{src.base(), x, args}
- }
- switch {
- case len(x.Params) < len(args):
- return ctx.mkErr(src, x, "too many arguments in call to %s (have %d, want %d)",
- x.name(ctx), len(args), len(x.Params))
- case len(x.Params) > len(args):
- return ctx.mkErr(src, x, "not enough arguments in call to %s (have %d, want %d)",
- x.name(ctx), len(args), len(x.Params))
- }
- for i, a := range args {
- if x.Params[i] != bottomKind {
- if unifyType(x.Params[i], a.Kind()) == bottomKind {
- const msg = "cannot use %s (type %s) as %s in argument %d to %s"
- return ctx.mkErr(src, x, msg, ctx.str(a), a.Kind(), x.Params[i], i+1, x.name(ctx))
- }
- }
- }
- call := callCtxt{src: src, ctx: ctx, builtin: x, args: args}
- defer func() {
- var errVal interface{} = call.err
- if err := recover(); err != nil {
- errVal = err
- }
- ret = processErr(&call, errVal, ret)
- }()
- x.Func(&call)
- switch v := call.ret.(type) {
- case value:
- return v
- case *valueError:
- return v.err
- }
- return convertVal(ctx, x, true, call.ret)
-}
-
-func processErr(call *callCtxt, errVal interface{}, ret value) value {
+func processErr(call *callCtxt, errVal interface{}, ret adt.Expr) adt.Expr {
ctx := call.ctx
- x := call.builtin
src := call.src
- const msg = "error in call to %s: %v"
switch err := errVal.(type) {
case nil:
case *callError:
@@ -311,30 +314,92 @@
ret = err.b
}
case *marshalError:
- ret = err.b
- ret = ctx.mkErr(src, x, ret, msg, x.name(ctx), err)
+ ret = wrapCallErr(call, err.b)
case *valueError:
- ret = err.err
- ret = ctx.mkErr(src, x, ret, msg, x.name(ctx), err)
- default:
+ ret = wrapCallErr(call, err.err)
+ case errors.Error:
+ ret = wrapCallErr(call, &adt.Bottom{Err: err})
+ case error:
if call.err == internal.ErrIncomplete {
ret = ctx.mkErr(src, codeIncomplete, "incomplete value")
} else {
// TODO: store the underlying error explicitly
- ret = ctx.mkErr(src, x, msg, x.name(ctx), err)
+ ret = wrapCallErr(call, &adt.Bottom{Err: errors.Promote(err, "")})
}
+ default:
+ // Likely a string passed to panic.
+ ret = wrapCallErr(call, &adt.Bottom{
+ Err: errors.Newf(call.Pos(), "%s", err),
+ })
}
return ret
}
+func wrapCallErr(c *callCtxt, b *adt.Bottom) *adt.Bottom {
+ pos := token.NoPos
+ if c.src != nil {
+ if src := c.src.Source(); src != nil {
+ pos = src.Pos()
+ }
+ }
+ const msg = "error in call to %s"
+ return &adt.Bottom{
+ Code: b.Code,
+ Err: errors.Wrapf(b.Err, pos, msg, c.builtin.name(c.ctx)),
+ }
+}
+
+func (c *callCtxt) convertError(x interface{}, name string) *adt.Bottom {
+ var err errors.Error
+ switch v := x.(type) {
+ case nil:
+ return nil
+
+ case *adt.Bottom:
+ return v
+
+ case *json.MarshalerError:
+ err = errors.Promote(v, "marshal error")
+
+ case errors.Error:
+ err = v
+
+ case error:
+ if name != "" {
+ err = errors.Newf(c.Pos(), "%s: %v", name, v)
+ } else {
+ err = errors.Newf(c.Pos(), "error in call to %s: %v", c.name(), v)
+ }
+
+ default:
+ err = errors.Newf(token.NoPos, "%s", name)
+ }
+ if err != internal.ErrIncomplete {
+ return &adt.Bottom{
+ // Wrap to preserve position information.
+ Err: errors.Wrapf(err, c.Pos(), "error in call to %s", c.name()),
+ }
+ }
+ return &adt.Bottom{
+ Code: adt.IncompleteError,
+ Err: errors.Newf(c.Pos(), "incomplete values in call to %s", c.name()),
+ }
+}
+
// callCtxt is passed to builtin implementations.
type callCtxt struct {
- src source
+ idx *index
+ src adt.Expr // *adt.CallExpr
ctx *context
builtin *builtin
- args []evaluated
- err error
+ err interface{}
ret interface{}
+
+ args []adt.Value
+}
+
+func (c *callCtxt) Pos() token.Pos {
+ return c.ctx.opCtx.Pos()
}
func (c *callCtxt) name() string {
@@ -357,8 +422,7 @@
i := sharedIndex.addInst(&Instance{
ImportPath: k,
PkgName: path.Base(k),
- rootStruct: e,
- rootValue: e,
+ root: e,
})
builtins[k] = i
@@ -375,7 +439,27 @@
if !ok {
return nil
}
- return p.rootStruct
+ return p.root
+}
+
+func init() {
+ internal.UnifyBuiltin = func(val interface{}, kind string) interface{} {
+ v := val.(Value)
+ ctx := v.ctx()
+
+ p := strings.Split(kind, ".")
+ pkg, name := p[0], p[1]
+ s := getBuiltinPkg(ctx, pkg)
+ if s == nil {
+ return v
+ }
+ a := s.Lookup(ctx.Label(name, false))
+ if a == nil {
+ return v
+ }
+
+ return v.Unify(makeValue(v.idx, a))
+ }
}
// do returns whether the call should be done.
@@ -402,12 +486,21 @@
c.err = &callError{err}
}
+func (c *callCtxt) errcf(src source, code adt.ErrorCode, format string, args ...interface{}) {
+ a := make([]interface{}, 0, 2+len(args))
+ a = append(a, code)
+ a = append(a, format)
+ a = append(a, args...)
+ err := c.ctx.mkErr(src, a...)
+ c.err = &callError{err}
+}
+
func (c *callCtxt) value(i int) Value {
v := newValueRoot(c.ctx, c.args[i])
- v, _ = v.Default()
+ // TODO: remove default
+ // v, _ = v.Default()
if !v.IsConcrete() {
- c.errf(c.src, v.toErr(c.ctx.mkErr(c.src, codeIncomplete,
- "non-concrete value")), "incomplete")
+ c.errcf(c.src, adt.IncompleteError, "non-concrete argument %d", i)
}
return v
}
@@ -423,12 +516,26 @@
}
func (c *callCtxt) invalidArgType(arg value, i int, typ string, err error) {
+ if ve, ok := err.(*valueError); ok && ve.err.IsIncomplete() {
+ c.err = ve
+ return
+ }
+ v, ok := arg.(adt.Value)
+ // TODO: make these permanent errors if the value did not originate from
+ // a reference.
+ if !ok {
+ c.errf(c.src, nil,
+ "cannot use incomplete value %s as %s in argument %d to %s: %v",
+ c.ctx.str(arg), typ, i, c.name(), err)
+ }
if err != nil {
- c.errf(c.src, err, "cannot use %s (type %s) as %s in argument %d to %s: %v",
- c.ctx.str(arg), arg.Kind(), typ, i, c.name(), err)
+ c.errf(c.src, err,
+ "cannot use %s (type %s) as %s in argument %d to %s: %v",
+ c.ctx.str(arg), v.Kind(), typ, i, c.name(), err)
} else {
- c.errf(c.src, nil, "cannot use %s (type %s) as %s in argument %d to %s",
- c.ctx.str(arg), arg.Kind(), typ, i, c.name())
+ c.errf(c.src, err,
+ "cannot use %s (type %s) as %s in argument %d to %s",
+ c.ctx.str(arg), v.Kind(), typ, i, c.name())
}
}
@@ -506,6 +613,8 @@
return n
}
+var ten = big.NewInt(10)
+
func (c *callCtxt) bigFloat(i int) *big.Float {
x := newValueRoot(c.ctx, c.args[i])
var mant big.Int
@@ -621,8 +730,8 @@
for j := 0; v.Next(); j++ {
str, err := v.Value().String()
if err != nil {
- c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
- j, i, c.name(), err)
+ c.err = errors.Wrapf(err, c.Pos(),
+ "element %d of list argument %d", j, i)
break
}
a = append(a, str)
diff --git a/cue/builtin_test.go b/cue/builtin_test.go
index 1901d34..617235e 100644
--- a/cue/builtin_test.go
+++ b/cue/builtin_test.go
@@ -49,7 +49,7 @@
`3`,
}, {
test("math", "math.Pi(3)"),
- `_|_(cannot call non-function Pi (type float))`,
+ `_|_(cannot call non-function math.Pi (type float))`,
}, {
test("math", "math.Floor(3, 5)"),
`_|_(too many arguments in call to math.Floor (have 2, want 1))`,
@@ -91,10 +91,10 @@
`'foo'`,
}, {
test("encoding/base64", `base64.Decode(null, "foo")`),
- `_|_(error in call to encoding/base64.Decode: illegal base64 data at input byte 0)`,
+ `_|_(error in call to encoding/base64.Decode: illegal base64 data at input byte 0 (and 1 more errors))`,
}, {
test("encoding/base64", `base64.Decode({}, "foo")`),
- `_|_(error in call to encoding/base64.Decode: base64: unsupported encoding: cannot use value {} (type struct) as null)`,
+ `_|_(error in call to encoding/base64.Decode: base64: unsupported encoding: cannot use value {} (type struct) as null (and 1 more errors))`,
}, {
test("encoding/hex", `hex.Encode("foo")`),
`"666f6f"`,
@@ -103,7 +103,7 @@
`'foo'`,
}, {
test("encoding/hex", `hex.Decode("foo")`),
- `_|_(error in call to encoding/hex.Decode: encoding/hex: invalid byte: U+006F 'o')`,
+ `_|_(error in call to encoding/hex.Decode: encoding/hex: invalid byte: U+006F 'o' (and 1 more errors))`,
}, {
test("encoding/hex", `hex.Dump('foo')`),
`"00000000 66 6f 6f |foo|\n"`,
@@ -112,19 +112,19 @@
`true`,
}, {
test("encoding/json", `json.Validate("{\"a\":10}", {a:<3})`),
- `_|_(error in call to encoding/json.Validate: a: invalid value 10 (out of bound <3))`,
+ `_|_(error in call to encoding/json.Validate: a: invalid value 10 (out of bound <3) (and 1 more errors))`,
}, {
test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<3})`),
- `_|_(error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3))`,
+ `_|_(error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3) (and 1 more errors))`,
}, {
test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<5})`),
`true`,
}, {
test("encoding/yaml", `yaml.Validate("a: 2\n", {a:<5, b:int})`),
- `_|_(error in call to encoding/yaml.Validate: b: incomplete value (int))`,
+ `_|_(error in call to encoding/yaml.Validate: b: incomplete value int (and 1 more errors))`,
}, {
test("encoding/yaml", `yaml.ValidatePartial("a: 2\n---\na: 4", {a:<3})`),
- `_|_(error in call to encoding/yaml.ValidatePartial: a: invalid value 4 (out of bound <3))`,
+ `_|_(error in call to encoding/yaml.ValidatePartial: a: invalid value 4 (out of bound <3) (and 1 more errors))`,
}, {
test("encoding/yaml", `yaml.ValidatePartial("a: 2\n---\na: 4", {a:<5})`),
`true`,
@@ -137,11 +137,11 @@
}, {
// Find a better alternative, as this call should go.
test("strconv", `strconv.FormatFloat(3.02, 300, 4, 64)`),
- `_|_(int 300 overflows byte in argument 1 in call to strconv.FormatFloat)`,
+ `_|_(int 300 overflows byte in argument 1 in call to strconv.FormatFloat (and 1 more errors))`,
}, {
// Find a better alternative, as this call should go.
test("strconv", `strconv.FormatFloat(3.02, -1, 4, 64)`),
- `_|_(cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat)`,
+ `_|_(cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat (and 1 more errors))`,
}, {
// Find a better alternative, as this call should go.
test("strconv", `strconv.FormatFloat(3.02, 1.0, 4, 64)`),
@@ -151,7 +151,7 @@
`2.5`,
}, {
test("list", `list.Avg([])`),
- `_|_(error in call to list.Avg: empty list)`,
+ `_|_(error in call to list.Avg: empty list (and 1 more errors))`,
}, {
test("list", `list.Avg("foo")`),
`_|_(cannot use "foo" (type string) as list in argument 1 to list.Avg)`,
@@ -166,7 +166,7 @@
`[]`,
}, {
test("list", `list.Drop([1, 2, 3, 4], -1)`),
- `_|_(error in call to list.Drop: negative index)`,
+ `_|_(error in call to list.Drop: negative index (and 1 more errors))`,
}, {
test("list", `list.FlattenN([1, [[2, 3], []], [4]], -1)`),
`[1,2,3,4]`,
@@ -184,7 +184,7 @@
`[]`,
}, {
test("list", `list.FlattenN("foo", 1)`),
- `_|_(error in call to list.FlattenN: cannot use value "foo" (type string) as list)`,
+ `_|_(error in call to list.FlattenN: cannot use value "foo" (type string) as list (and 1 more errors))`,
}, {
test("list", `list.FlattenN([], "foo")`),
`_|_(cannot use "foo" (type string) as int in argument 2 to list.FlattenN)`,
@@ -193,7 +193,7 @@
`4`,
}, {
test("list", `list.Max([])`),
- `_|_(error in call to list.Max: empty list)`,
+ `_|_(error in call to list.Max: empty list (and 1 more errors))`,
}, {
test("list", `list.Max("foo")`),
`_|_(cannot use "foo" (type string) as list in argument 1 to list.Max)`,
@@ -202,7 +202,7 @@
`1`,
}, {
test("list", `list.Min([])`),
- `_|_(error in call to list.Min: empty list)`,
+ `_|_(error in call to list.Min: empty list (and 1 more errors))`,
}, {
test("list", `list.Min("foo")`),
`_|_(cannot use "foo" (type string) as list in argument 1 to list.Min)`,
@@ -217,13 +217,13 @@
`_|_(cannot use "foo" (type string) as list in argument 1 to list.Product)`,
}, {
test("list", `list.Range(0, 5, 0)`),
- `_|_(error in call to list.Range: step must be non zero)`,
+ `_|_(error in call to list.Range: step must be non zero (and 1 more errors))`,
}, {
test("list", `list.Range(5, 0, 1)`),
- `_|_(error in call to list.Range: end must be greater than start when step is positive)`,
+ `_|_(error in call to list.Range: end must be greater than start when step is positive (and 1 more errors))`,
}, {
test("list", `list.Range(0, 5, -1)`),
- `_|_(error in call to list.Range: end must be less than start when step is negative)`,
+ `_|_(error in call to list.Range: end must be less than start when step is negative (and 1 more errors))`,
}, {
test("list", `list.Range(0, 5, 1)`),
`[0,1,2,3,4]`,
@@ -244,16 +244,16 @@
`[2,3]`,
}, {
test("list", `list.Slice([1, 2, 3, 4], -1, 3)`),
- `_|_(error in call to list.Slice: negative index)`,
+ `_|_(error in call to list.Slice: negative index (and 1 more errors))`,
}, {
test("list", `list.Slice([1, 2, 3, 4], 3, 1)`),
- `_|_(error in call to list.Slice: invalid index: 3 > 1)`,
+ `_|_(error in call to list.Slice: invalid index: 3 > 1 (and 1 more errors))`,
}, {
test("list", `list.Slice([1, 2, 3, 4], 5, 5)`),
- `_|_(error in call to list.Slice: slice bounds out of range)`,
+ `_|_(error in call to list.Slice: slice bounds out of range (and 1 more errors))`,
}, {
test("list", `list.Slice([1, 2, 3, 4], 1, 5)`),
- `_|_(error in call to list.Slice: slice bounds out of range)`,
+ `_|_(error in call to list.Slice: slice bounds out of range (and 1 more errors))`,
}, {
test("list", `list.Sort([], list.Ascending)`),
`[]`,
@@ -266,16 +266,17 @@
y:_,
less: (x.a < y.a)
})`),
- `[{a: 1, v: 2},{a: 1, v: 3},{a: 2, v: 1}]`,
+ `[{a:1,v:2},{a:1,v:3},{a:2,v:1}]`,
}, {
test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
- `_|_(error in call to list.Sort: less: conflicting values close(T, close(T)) and {b: 2} (mismatched types number|string and struct))`,
+ `_|_(error in call to list.Sort: x: conflicting values string and {b:2} (mismatched types string and struct) (and 1 more errors) (and 1 more errors))`,
}, {
test("list", `list.SortStrings(["b", "a"])`),
`["a","b"]`,
}, {
+ // TODO: path error. This should be done as part of builtin refactoring.
test("list", `list.SortStrings([1, 2])`),
- `_|_(invalid list element 0 in argument 0 to list.SortStrings: 0: cannot use value 1 (type int) as string)`,
+ `_|_(error in call to list.SortStrings: element 0 of list argument 0: 0: cannot use value 1 (type int) as string (and 1 more errors))`,
}, {
test("list", `list.Sum([1, 2, 3, 4])`),
`10`,
@@ -296,7 +297,7 @@
`[1,2,3,4]`,
}, {
test("list", `list.Take([1, 2, 3, 4], -1)`),
- `_|_(error in call to list.Take: negative index)`,
+ `_|_(error in call to list.Take: negative index (and 1 more errors))`,
}, {
test("list", `list.MinItems([1, 2, 3, 4], 2)`),
`true`,
@@ -312,20 +313,20 @@
}, {
// Panics
test("math", `math.Jacobi(1000, 2000)`),
- `_|_(error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000)`,
+ `_|_(error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000 (and 1 more errors))`,
}, {
test("math", `math.Jacobi(1000, 201)`),
`1`,
}, {
test("math", `math.Asin(2.0e400)`),
- `_|_(cannot use 2.0e+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up)`,
+ `_|_(cannot use 2.0E+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up (and 1 more errors))`,
}, {
test("math", `math.MultipleOf(4, 2)`), `true`,
}, {
test("math", `math.MultipleOf(5, 2)`), `false`,
}, {
test("math", `math.MultipleOf(5, 0)`),
- `_|_(error in call to math.MultipleOf: division by zero)`,
+ `_|_(error in call to math.MultipleOf: division by zero (and 1 more errors))`,
}, {
test("math", `math.MultipleOf(100, 1.00001)`), `false`,
}, {
@@ -342,7 +343,7 @@
`"foo"`,
}, {
test("regexp", `regexp.Find(#"f\w\w"#, "bar")`),
- `_|_(error in call to regexp.Find: no match)`,
+ `_|_(error in call to regexp.Find: no match (and 1 more errors))`,
}, {
test("regexp", `regexp.FindAll(#"f\w\w"#, "afoot afloat from", 2)`),
`["foo","flo"]`,
@@ -351,7 +352,7 @@
`["foo","flo"]`,
}, {
test("regexp", `regexp.FindAll(#"f\w\w"#, "bla bla", -1)`),
- `_|_(error in call to regexp.FindAll: no match)`,
+ `_|_(error in call to regexp.FindAll: no match (and 1 more errors))`,
}, {
test("regexp", `regexp.FindSubmatch(#"f(\w)(\w)"#, "afloat afoot from")`),
`["flo","l","o"]`,
@@ -360,19 +361,19 @@
`[["flo","l","o"],["foo","o","o"],["fro","r","o"]]`,
}, {
test("regexp", `regexp.FindAllSubmatch(#"f(\w)(\w)"#, "aglom", -1)`),
- `_|_(error in call to regexp.FindAllSubmatch: no match)`,
+ `_|_(error in call to regexp.FindAllSubmatch: no match (and 1 more errors))`,
}, {
test("regexp", `regexp.FindNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "afloat afoot from")`),
- `{A: "l", B: "o"}`,
+ `{A:"l",B:"o"}`,
}, {
test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "afloat afoot from", -1)`),
- `[{A: "l", B: "o"},{A: "o", B: "o"},{A: "r", B: "o"}]`,
+ `[{A:"l",B:"o"},{A:"o",B:"o"},{A:"r",B:"o"}]`,
}, {
test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>optional)?"#, "fbla", -1)`),
- `[{A: ""}]`,
+ `[{A:""}]`,
}, {
test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "aglom", -1)`),
- `_|_(error in call to regexp.FindAllNamedSubmatch: no match)`,
+ `_|_(error in call to regexp.FindAllNamedSubmatch: no match (and 1 more errors))`,
}, {
test("regexp", `regexp.Valid & "valid"`),
`"valid"`,
@@ -387,7 +388,7 @@
`"Hello World!"`,
}, {
test("strings", `strings.Join([1, 2], " ")`),
- `_|_(invalid list element 0 in argument 0 to strings.Join: 0: cannot use value 1 (type int) as string)`,
+ `_|_(error in call to strings.Join: element 0 of list argument 0: 0: cannot use value 1 (type int) as string (and 1 more errors))`,
}, {
test("strings", `strings.ByteAt("a", 0)`),
strconv.Itoa('a'),
@@ -432,7 +433,7 @@
`2`,
}, {
testExpr(`or([])`),
- `_|_(empty list in call to or)`,
+ `_|_(empty list in call to or (and 1 more errors))`,
}, {
test("encoding/csv", `csv.Encode([[1,2,3],[4,5],[7,8,9]])`),
`"1,2,3\n4,5\n7,8,9\n"`,
@@ -459,7 +460,7 @@
x: int
y: json.Marshal({a: x})
}`),
- `{x: int, y: Marshal ({a: x})}`,
+ `{x:int,y:_|_(cannot convert incomplete value "int" to JSON (and 1 more errors))}`,
}, {
test("encoding/yaml", `yaml.MarshalStream([{a: 1}, {b: 2}])`),
`"a: 1\n---\nb: 2\n"`,
@@ -482,14 +483,15 @@
test("net", `net.JoinHostPort([192,30,4,2], 80)`),
`"192.30.4.2:80"`,
}, {
- test("net", `net.JoinHostPort([192,30,4], 80)`),
- `_|_(error in call to net.JoinHostPort: invalid host [192,30,4])`,
+ // TODO: why is this not printing compactly?
+ test("net", `net.JoinHostPort([192, 30, 4], 80)`),
+ `_|_(error in call to net.JoinHostPort: invalid host [192, 30, 4] (and 1 more errors))`,
}, {
test("net", `net.IP("23.23.23.23")`),
`true`,
}, {
test("net", `net.IPv4 & "23.23.23.2333"`),
- `_|_(invalid value "23.23.23.2333" (does not satisfy net.IPv4()))`,
+ `_|_(invalid value "23.23.23.2333" (does not satisfy net.IPv4))`,
}, {
test("net", `net.IP("23.23.23.23")`),
`true`,
@@ -501,7 +503,7 @@
`false`,
}, {
test("net", `net.IPv4() & "ff02::1:3"`),
- `_|_(invalid value "ff02::1:3" (does not satisfy net.IPv4()))`,
+ `_|_(invalid value "ff02::1:3" (does not satisfy net.IPv4))`,
}, {
test("net", `net.LoopbackIP([127, 0, 0, 1])`),
`true`,
@@ -549,23 +551,23 @@
`_|_(invalid value "hello" (does not satisfy strings.MinRunes(10)))`,
}, {
test("struct", `struct.MinFields(0) & ""`),
- `_|_(conflicting values MinFields (0) and "" (mismatched types struct and string))`,
+ `_|_(conflicting values struct.MinFields(0) and "" (mismatched types struct and string))`,
}, {
test("struct", `struct.MinFields(0) & {a: 1}`),
- `{a: 1}`,
+ `{a:1}`,
}, {
test("struct", `struct.MinFields(2) & {a: 1}`),
- `_|_(invalid value {a: 1} (does not satisfy struct.MinFields(2)))`,
+ `_|_(invalid value {a:1} (does not satisfy struct.MinFields(2)))`,
}, {
test("struct", `struct.MaxFields(0) & {a: 1}`),
- `_|_(invalid value {a: 1} (does not satisfy struct.MaxFields(0)))`,
+ `_|_(invalid value {a:1} (does not satisfy struct.MaxFields(0)))`,
}, {
test("struct", `struct.MaxFields(2) & {a: 1}`),
- `{a: 1}`,
+ `{a:1}`,
}, {
test("math", `math.Pow(8, 4)`), `4096`,
}, {
- test("math", `math.Pow10(4)`), `10000`,
+ test("math", `math.Pow10(4)`), `1E+4`,
}, {
test("math", `math.Signbit(-4)`), `true`,
}, {
@@ -669,7 +671,41 @@
if err := insts[0].Err; err != nil {
t.Fatal(err)
}
- got := strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value()))
+ v := insts[0].Value()
+ ctx := v.ctx()
+ got := ctx.opCtx.Str(v.v)
+ if got != tc.emit {
+ t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
+ }
+ })
+ }
+}
+
+// For debugging purposes. Do not remove.
+func TestSingleBuiltin(t *testing.T) {
+ t.Skip("error message")
+
+ test := func(pkg, expr string) []*bimport {
+ return []*bimport{{"",
+ []string{fmt.Sprintf("import %q\n(%s)", pkg, expr)},
+ }}
+ }
+ testCases := []struct {
+ instances []*bimport
+ emit string
+ }{{
+ test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
+ `_|_(error in call to list.Sort: less: invalid operands {b:2} and {a:1} to '<' (type struct and struct) (and 1 more errors))`,
+ }}
+ for i, tc := range testCases {
+ t.Run(fmt.Sprint(i), func(t *testing.T) {
+ insts := Build(makeInstances(tc.instances))
+ if err := insts[0].Err; err != nil {
+ t.Fatal(err)
+ }
+ v := insts[0].Value()
+ ctx := v.ctx()
+ got := ctx.opCtx.Str(v.v)
if got != tc.emit {
t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
}
diff --git a/cue/builtins.go b/cue/builtins.go
index 7581fda..87ad902 100644
--- a/cue/builtins.go
+++ b/cue/builtins.go
@@ -148,9 +148,9 @@
}
var builtinPackages = map[string]*builtinPkg{
- "": {
- native: []*builtin{},
- },
+ // "": {
+ // native: []*builtin{},
+ // },
"crypto/md5": {
native: []*builtin{{
Name: "Size",
@@ -612,13 +612,15 @@
if !json.Valid(b) {
return false, fmt.Errorf("json: invalid JSON")
}
- r := internal.GetRuntimeOld(v).(*Runtime)
+ r := internal.GetRuntimeNew(v).(*Runtime)
inst, err := r.Compile("json.Validate", b)
if err != nil {
return false, err
}
- v = v.Unify(inst.Value())
+ t := inst.Value()
+
+ v = v.Unify(t)
if v.Err() != nil {
return false, v.Err()
}
@@ -709,7 +711,7 @@
if err != nil {
return false, err
}
- r := internal.GetRuntimeOld(v).(*Runtime)
+ r := internal.GetRuntimeNew(v).(*Runtime)
for {
expr, err := d.Decode()
if err != nil {
@@ -748,7 +750,7 @@
if err != nil {
return false, err
}
- r := internal.GetRuntimeOld(v).(*Runtime)
+ r := internal.GetRuntimeNew(v).(*Runtime)
for {
expr, err := d.Decode()
if err != nil {
diff --git a/cue/builtinutil.go b/cue/builtinutil.go
index df22d71..e226cfa 100644
--- a/cue/builtinutil.go
+++ b/cue/builtinutil.go
@@ -14,6 +14,11 @@
package cue
+import (
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/convert"
+)
+
// TODO: this code could be generated, but currently isn't.
type valueSorter struct {
@@ -33,9 +38,12 @@
func (s *valueSorter) Len() int { return len(s.a) }
func (s *valueSorter) Swap(i, j int) { s.a[i], s.a[j] = s.a[j], s.a[i] }
func (s *valueSorter) Less(i, j int) bool {
- x := fill(s.cmp, s.a[i], "x")
- x = fill(x, s.a[j], "y")
- isLess, err := x.Lookup("less").Bool()
+ ctx := s.cmp.ctx()
+ x := fill(ctx, s.cmp.v, s.a[i], "x")
+ x = fill(ctx, x, s.a[j], "y")
+ ctx.opCtx.Unify(ctx.opCtx, x, adt.Finalized) // TODO: remove.
+ v := Value{s.cmp.idx, x}
+ isLess, err := v.Lookup("less").Bool()
if err != nil && s.err == nil {
s.err = err
return true
@@ -45,13 +53,22 @@
// fill creates a new value with the old value unified with the given value.
// TODO: consider making this a method on Value.
-func fill(v Value, x interface{}, path ...string) Value {
- ctx := v.ctx()
- root := v.v.val()
+func fill(ctx *context, v *adt.Vertex, x interface{}, path ...string) *adt.Vertex {
for i := len(path) - 1; i >= 0; i-- {
x = map[string]interface{}{path[i]: x}
}
- value := convertVal(ctx, root, false, x)
- eval := binOp(ctx, baseValue{}, opUnify, root, value)
- return newValueRoot(ctx, eval)
+ value := convertVal(ctx, v, false, x)
+
+ w := adt.ToVertex(value)
+ n := &adt.Vertex{Label: v.Label}
+ n.AddConjunct(adt.MakeConjunct(nil, v))
+ n.AddConjunct(adt.MakeConjunct(nil, w))
+
+ // n.Add(v)
+ // n.Add(w)
+ return n
+}
+
+func convertVal(ctx *context, src source, nullIsTop bool, x interface{}) adt.Value {
+ return convert.GoValueToValue(ctx.opCtx, x, nullIsTop)
}
diff --git a/cue/context.go b/cue/context.go
index a63e3e9..3f00329 100644
--- a/cue/context.go
+++ b/cue/context.go
@@ -15,48 +15,17 @@
package cue
import (
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/debug"
+ "cuelang.org/go/internal/core/eval"
"github.com/cockroachdb/apd/v2"
)
// context manages evaluation state.
type context struct {
+ opCtx *adt.OpContext
*apd.Context
-
*index
-
- forwardMap []scope // pairs
- oldSize []int
-
- // constraints are to be evaluated at the end values to be evaluated later.
- constraints []*binaryExpr
- evalStack []bottom
-
- inDefinition int
- inSum int
- cycleErr bool
-
- // for debug strings
- nodeRefs map[scope]string
-
- // tracing
- trace bool
- level int
-
- // TODO: replace with proper structural cycle detection/ occurs check.
- // See Issue #29.
- maxDepth int
-}
-
-func (c *context) incEvalDepth() {
- if len(c.evalStack) > 0 {
- c.evalStack[len(c.evalStack)-1].exprDepth++
- }
-}
-
-func (c *context) decEvalDepth() {
- if len(c.evalStack) > 0 {
- c.evalStack[len(c.evalStack)-1].exprDepth--
- }
}
var baseContext apd.Context
@@ -72,50 +41,56 @@
Context: &baseContext,
index: idx,
}
+ if idx != nil {
+ c.opCtx = eval.NewContext(idx.Runtime, nil)
+ }
return c
}
-// delayConstraint schedules constraint to be evaluated and returns ret. If
-// delaying constraints is currently not allowed, it returns an error instead.
-func (c *context) delayConstraint(ret evaluated, constraint *binaryExpr) evaluated {
- c.cycleErr = true
- c.constraints = append(c.constraints, constraint)
- return ret
+func debugStr(ctx *context, v adt.Node) string {
+ return debug.NodeString(ctx.opCtx, v, nil)
}
-func (c *context) processDelayedConstraints() evaluated {
- cons := c.constraints
- c.constraints = c.constraints[:0]
- for _, dc := range cons {
- v := binOp(c, dc, dc.Op, dc.X.evalPartial(c), dc.Y.evalPartial(c))
- if isBottom(v) {
- return v
- }
+func (c *context) str(v adt.Node) string {
+ return debugStr(c, v)
+}
+
+func (c *context) mkErr(src source, args ...interface{}) *bottom {
+ return c.index.mkErr(src, args...)
+}
+
+func (c *context) vertex(v *adt.Vertex) *adt.Vertex {
+ return v
+}
+
+// vertex returns the evaluated vertex of v.
+func (v Value) vertex(ctx *context) *adt.Vertex {
+ return ctx.vertex(v.v)
+}
+
+// eval returns the evaluated value. This may not be the vertex.
+//
+// Deprecated: use ctx.value
+func (v Value) eval(ctx *context) adt.Value {
+ if v.v == nil {
+ panic("undefined value")
}
- return nil
-}
-
-func (c *context) deref(f scope) scope {
-outer:
- for {
- for i := 0; i < len(c.forwardMap); i += 2 {
- if c.forwardMap[i] == f {
- f = c.forwardMap[i+1]
- continue outer
- }
- }
- return f
+ x := ctx.manifest(v.v)
+ switch x.Kind() {
+ case adt.StructKind, adt.ListKind:
+ return x
+ default:
+ return x.Value
}
}
-func (c *context) pushForwards(pairs ...scope) *context {
- c.oldSize = append(c.oldSize, len(c.forwardMap))
- c.forwardMap = append(c.forwardMap, pairs...)
- return c
-}
+// func (v Value) evalFull(u value) (Value, adt.Value) {
+// ctx := v.ctx()
+// x := ctx.manifest(u)
+// }
-func (c *context) popForwards() {
- last := len(c.oldSize) - 1
- c.forwardMap = c.forwardMap[:c.oldSize[last]]
- c.oldSize = c.oldSize[:last]
+// TODO: change from Vertex to Vertex.
+func (c *context) manifest(v *adt.Vertex) *adt.Vertex {
+ v.Finalize(c.opCtx)
+ return v
}
diff --git a/cue/copy.go b/cue/copy.go
deleted file mode 100644
index d60f18f..0000000
--- a/cue/copy.go
+++ /dev/null
@@ -1,75 +0,0 @@
-// 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 cue
-
-func (c *context) copy(v value) value {
- // return v.copy(c)
- return rewrite(c, v, rewriteCopy)
-}
-
-func rewriteCopy(ctx *context, v value) (value, bool) {
- switch x := v.(type) {
- case *nodeRef:
- node := ctx.deref(x.node)
- if node == x.node {
- return x, false
- }
- return &nodeRef{x.baseValue, node, x.label}, false
-
- case *structLit:
- arcs := make(arcs, len(x.Arcs))
-
- obj := &structLit{x.baseValue, nil, nil, x.closeStatus, nil, arcs, nil}
-
- defer ctx.pushForwards(x, obj).popForwards()
-
- emit := x.emit
- if emit != nil {
- emit = ctx.copy(x.emit)
- }
- obj.emit = emit
-
- fn := func(v value) value { return ctx.copy(v) }
- o, err := x.optionals.rewrite(fn)
- if err != nil {
- return err, false
- }
- obj.optionals = o
-
- for i, a := range x.Arcs {
- a.setValue(ctx.copy(a.v))
- arcs[i] = a
- }
-
- comp := make([]compValue, len(x.comprehensions))
- for i, c := range x.comprehensions {
- comp[i] = compValue{c.checked, ctx.copy(c.comp)}
- }
- obj.comprehensions = comp
- return obj, false
-
- case *lambdaExpr:
- arcs := make([]arc, len(x.arcs))
- for i, a := range x.arcs {
- arcs[i] = arc{Label: a.Label, v: ctx.copy(a.v)}
- }
- lambda := &lambdaExpr{x.baseValue, ¶ms{arcs}, nil}
- defer ctx.pushForwards(x, lambda).popForwards()
-
- lambda.value = ctx.copy(x.value)
- return lambda, false
- }
- return v, true
-}
diff --git a/internal/legacy/cue/cue.go b/cue/cue.go
similarity index 100%
rename from internal/legacy/cue/cue.go
rename to cue/cue.go
diff --git a/cue/debug.go b/cue/debug.go
deleted file mode 100644
index 12cd4d9..0000000
--- a/cue/debug.go
+++ /dev/null
@@ -1,540 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
- "fmt"
- "strconv"
- "strings"
-
- "cuelang.org/go/cue/ast"
-)
-
-func debugStr(ctx *context, v value) string {
- p := newPrinter(ctx)
- p.showNodeRef = true
- p.str(v)
- return p.w.String()
-}
-
-func (c *context) str(v value) string {
- p := newPrinter(c)
- p.str(v)
- return p.w.String()
-}
-
-func (c *context) ref(v scope) string {
- v = c.deref(v)
- if c.nodeRefs == nil {
- c.nodeRefs = map[scope]string{}
- }
- ref, ok := c.nodeRefs[v]
- if ok {
- return ref
- }
- ref = strconv.Itoa(len(c.nodeRefs))
- c.nodeRefs[v] = ref
- return ref
-}
-
-func (c *context) indent() {
- fmt.Print(strings.Repeat(" ", c.level))
-}
-
-func (c *context) debugPrint(args ...interface{}) {
- if c.trace {
- c.indent()
- c.println(args...)
- }
-}
-
-func (c *context) println(args ...interface{}) {
- for i, a := range args {
- if i != 0 {
- fmt.Print(" ")
- }
- switch x := a.(type) {
- case value:
- fmt.Print(debugStr(c, x))
- default:
- fmt.Print(x)
- }
- }
- fmt.Println()
-}
-
-// func trace(c *context, r rewriter, n *node) (*context, rewriter, *node) {
-// n = derefNode(n)
-// name := "evaluate"
-// if r != nil {
-// name = fmt.Sprintf("%T", r)
-// }
-// c.debugPrint("---", name, c.ref(n))
-// if n.obj != nil {
-// c.debugPrint("<<< node: ", debugStr(c, n.obj))
-// }
-// if n.expr != nil {
-// c.debugPrint("<<< expr: ", debugStr(c, n.expr))
-// }
-// if n.value != nil {
-// c.debugPrint("<<< value:", debugStr(c, n.value))
-// }
-// c.level++
-// return c, r, n
-// }
-
-// func un(c *context, r rewriter, n *node) {
-// n = derefNode(n)
-// c.level--
-// if n.expr != nil {
-// c.debugPrint(">>> expr:", debugStr(c, n.expr))
-// }
-// if n.value != nil {
-// c.debugPrint(">>> value:", debugStr(c, n.value))
-// }
-// if n.obj != nil {
-// c.debugPrint(">>> node: ", debugStr(c, n.obj))
-// }
-// }
-
-func indent(c *context, msg string, x value) (_ *context, m, v string) {
- str := debugStr(c, x)
- c.debugPrint("...", msg)
- c.level++
- c.debugPrint("in:", str)
- return c, msg, str
-}
-
-func uni(c *context, msg, oldValue string) {
- c.debugPrint("was: ", oldValue)
- c.level--
- c.debugPrint("...", msg)
-}
-
-func newPrinter(ctx *context) *printer {
- return &printer{
- ctx: ctx,
- w: &bytes.Buffer{},
- }
-}
-
-type printer struct {
- ctx *context
- w *bytes.Buffer
- showNodeRef bool
-}
-
-func (p *printer) label(f label) string {
- if p.ctx == nil {
- return strconv.Itoa(int(f))
- }
-
- str := p.ctx.LabelStr(f)
- if strings.HasPrefix(str, "#") && !f.IsDef() ||
- strings.HasPrefix(str, "_") && !f.IsHidden() ||
- !ast.IsValidIdent(str) {
- return strconv.Quote(str)
- }
- return str
-}
-
-func (p *printer) writef(format string, args ...interface{}) {
- fmt.Fprintf(p.w, format, args...)
-}
-
-func (p *printer) write(args ...interface{}) {
- fmt.Fprint(p.w, args...)
-}
-
-func lambdaName(f label, v value) label {
- switch x := v.(type) {
- case *nodeRef:
- return lambdaName(f, x.node)
- case *lambdaExpr:
- if f == 0 && len(x.params.arcs) == 1 {
- return x.params.arcs[0].Label
- }
- }
- return f
-}
-
-func (p *printer) str(v interface{}) {
- writef := p.writef
- write := p.write
- switch x := v.(type) {
- case nil:
- write("*nil*")
- case string:
- write(x)
- case *builtin:
- write(x.name(p.ctx))
- case *nodeRef:
- if p.showNodeRef {
- writef("<%s>", p.ctx.ref(x.node))
- }
- case *selectorExpr:
- f := lambdaName(x.Sel, x.X)
- if _, ok := x.X.(*nodeRef); ok && !p.showNodeRef {
- write(p.label(f))
- } else {
- p.str(x.X)
- writef(".%v", p.label(f))
- }
- case *indexExpr:
- p.str(x.X)
- write("[")
- p.str(x.Index)
- write("]")
- case *sliceExpr:
- p.str(x.X)
- write("[")
- if x.Lo != nil {
- p.str(x.Lo)
- }
- write(":")
- if x.Hi != nil {
- p.str(x.Hi)
- }
- write("]")
- case *callExpr:
- p.str(x.Fun)
- write(" (")
- for i, a := range x.Args {
- p.str(a)
- if i < len(x.Args)-1 {
- write(",")
- }
- }
- write(")")
- case *customValidator:
- p.str(x.Builtin)
- write(" (")
- for i, a := range x.Args {
- p.str(a)
- if i < len(x.Args)-1 {
- write(",")
- }
- }
- write(")")
- case *unaryExpr:
- write(x.Op)
- p.str(x.X)
- case *binaryExpr:
- if x.Op == opUnifyUnchecked {
- p.str(x.X)
- write(", ")
- p.str(x.Y)
- break
- }
- write("(")
- p.str(x.X)
- writef(" %v ", x.Op)
- p.str(x.Y)
- write(")")
- case *unification:
- write("(")
- for i, v := range x.Values {
- if i != 0 {
- writef(" & ")
- }
- p.str(v)
- }
- write(")")
- case *disjunction:
- write("(")
- for i, v := range x.Values {
- if i != 0 {
- writef(" | ")
- }
- if v.Default {
- writef("*")
- }
- p.str(v.Val)
- }
- write(")")
- case *lambdaExpr:
- if p.showNodeRef {
- writef("<%s>", p.ctx.ref(x))
- }
- write("(")
- p.str(x.params.arcs)
- write(")->")
- v := x.value
- // strip one layer of closeIf wrapper. Evaluation may cause one
- // layer to have not yet been evaluated. This is fine.
- if w, ok := v.(*closeIfStruct); ok {
- v = w.value
- }
- p.str(v)
-
- case *closeIfStruct:
- write("close(")
- p.str(x.value)
- write(")")
-
- case *optionals:
- if x == nil {
- break
- }
- wrap := func(v *optionals) {
- if x.closed.isClosed() {
- write("C{")
- }
- p.str(v)
- if x.closed.isClosed() {
- write("}")
- }
- }
- switch {
- case x.op == opUnify:
- write("(")
- wrap(x.left)
- write(" & ")
- wrap(x.right)
- write(")")
-
- case x.op == opUnifyUnchecked:
- wrap(x.left)
- write(", ")
- wrap(x.right)
-
- default:
- for i, t := range x.fields {
- if i > 0 {
- write(", ")
- }
- write("[")
- if t.key != nil {
- p.str(t.key)
- }
- write("]: ")
- p.str(t.value)
- }
- }
-
- case *structLit:
- if x == nil {
- write("*nil node*")
- break
- }
- if p.showNodeRef {
- p.writef("<%s>", p.ctx.ref(x))
- }
- if x.closeStatus.shouldClose() {
- write("C")
- }
- write("{")
- topDefault := x.optionals.isDotDotDot()
- if !topDefault && x.optionals != nil {
- p.str(x.optionals)
- write(", ")
- }
-
- if x.emit != nil {
- p.str(x.emit)
- write(", ")
- }
- p.str(x.Arcs)
- for i, c := range x.comprehensions {
- if c.checked {
- p.write("c:")
- }
- p.str(c.comp)
- if i < len(x.comprehensions)-1 {
- p.write(", ")
- }
- }
- if topDefault && !x.closeStatus.shouldClose() {
- if len(x.Arcs) > 0 {
- p.write(", ")
- }
- p.write("...")
- }
- write("}")
-
- case []arc:
- for i, a := range x {
- p.str(a)
-
- if i < len(x)-1 {
- p.write(", ")
- }
- }
-
- case arc:
- n := x.v
- str := p.label(x.Label)
- p.writef(str)
- if x.optional {
- p.write("?")
- }
- if x.definition && !x.Label.IsDef() {
- p.write(" :: ")
- } else {
- p.write(": ")
- }
- p.str(n)
- if x.attrs != nil {
- for _, a := range x.attrs.attr {
- p.write(" ", a.text)
- }
- }
-
- case *fieldComprehension:
- p.str(x.key)
- writef(": ")
- p.str(x.val)
-
- case *listComprehension:
- writef("[")
- p.str(x.clauses)
- write(" ]")
-
- case *structComprehension:
- p.str(x.clauses)
-
- case *yield:
- writef(" yield ")
- p.str(x.value)
-
- case *feed:
- writef(" <%s>for ", p.ctx.ref(x.fn))
- a := x.fn.params.arcs[0]
- p.writef(p.label(a.Label))
- writef(", ")
- a = x.fn.params.arcs[1]
- p.writef(p.label(a.Label))
- writef(" in ")
- p.str(x.Src)
- p.str(x.fn.value)
-
- case *guard:
- writef(" if ")
- p.str(x.Condition)
- p.str(x.Dst)
-
- case *nullLit:
- write("null")
- case *boolLit:
- writef("%v", x.B)
- case *stringLit:
- writef("%q", x.Str)
- case *bytesLit:
- str := strconv.Quote(string(x.B))
- str = str[1 : len(str)-1]
- writef("'%s'", str)
- case *numLit:
- write(x.String())
- case *durationLit:
- write(x.d.String())
- case *bound:
- switch x.k & numKind {
- case intKind:
- p.writef("int & ")
- case floatKind:
- p.writef("float & ")
- }
- p.writef("%v", x.Op)
- p.str(x.Expr)
- case *interpolation:
- for i, e := range x.Parts {
- if i != 0 {
- write("+")
- }
- p.str(e)
- }
- case *list:
- // TODO: do not evaluate
- max := maxNum(x.len).evalPartial(p.ctx)
- inCast := false
- ellipsis := false
- n, ok := max.(*numLit)
- if !ok {
- // TODO: do not evaluate
- min := minNum(x.len).evalPartial(p.ctx)
- n, _ = min.(*numLit)
- }
- ln := 0
- if n != nil {
- x, _ := n.X.Int64()
- ln = int(x)
- }
- open := false
- switch max.(type) {
- case *top, *basicType:
- open = true
- }
- if !ok || ln > len(x.elem.Arcs) {
- if !open && !isTop(x.typ) {
- p.str(x.len)
- write("*[")
- p.str(x.typ)
- write("]")
- if len(x.elem.Arcs) == 0 {
- break
- }
- write("(")
- inCast = true
- }
- ellipsis = true
- }
- write("[")
- for i, a := range x.elem.Arcs {
- p.str(a.v)
- if i < len(x.elem.Arcs)-1 {
- write(",")
- }
- }
- if ellipsis {
- write(", ...")
- if !isTop(x.typ) {
- p.str(x.typ)
- }
- }
- write("]")
- if inCast {
- write(")")
- }
-
- case *bottom:
- write("_|_")
- if x.value != nil || x.format != "" {
- write("(")
- errs := x.sub
- if errs == nil {
- errs = []*bottom{x}
- }
- for i, x := range errs {
- if i > 0 {
- p.write(";")
- }
- if x.value != nil && p.showNodeRef {
- p.str(x.value)
- p.write(":")
- }
- write(x.msg())
- }
- write(")")
- }
- case *top:
- write("_") // ⊤
- case *basicType:
- write(x.K.String())
-
- default:
- panic(fmt.Sprintf("unimplemented type %T", x))
- }
-}
diff --git a/cue/doc.go b/cue/doc.go
deleted file mode 100644
index f91a2d3..0000000
--- a/cue/doc.go
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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 cue creates, evaluates and manipulates CUE configurations.
-package cue // import "cuelang.org/go/cue"
diff --git a/cue/errors.go b/cue/errors.go
index 467254f..21ed886 100644
--- a/cue/errors.go
+++ b/cue/errors.go
@@ -18,63 +18,22 @@
"fmt"
"reflect"
- "cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal/core/adt"
)
-var _ errors.Error = &nodeError{}
-
-// A nodeError is an error associated with processing an AST node.
-type nodeError struct {
- path []string // optional
- n ast.Node
-
- errors.Message
-}
-
-func (n *nodeError) Error() string {
- return errors.String(n)
-}
-
-func nodeErrorf(n ast.Node, format string, args ...interface{}) *nodeError {
- return &nodeError{
- n: n,
- Message: errors.NewMessage(format, args),
- }
-}
-
-func (e *nodeError) Position() token.Pos {
- return e.n.Pos()
-}
-
-func (e *nodeError) InputPositions() []token.Pos { return nil }
-
-func (e *nodeError) Path() []string {
- return e.path
-}
-
func (v Value) appendErr(err errors.Error, b *bottom) errors.Error {
- switch {
- case len(b.sub) > 0:
- for _, b := range b.sub {
- err = v.appendErr(err, b)
- }
- fallthrough
- case b.err != nil:
- err = errors.Append(err, b.err)
- default:
- err = errors.Append(err, &valueError{
- v: v,
- err: b,
- })
+ return &valueError{
+ v: v,
+ err: &adt.Bottom{
+ Err: errors.Append(err, b.Err),
+ },
}
- return err
}
func (v Value) toErr(b *bottom) (err errors.Error) {
- return v.appendErr(nil, b)
+ return &valueError{v: v, err: b}
}
var _ errors.Error = &valueError{}
@@ -90,23 +49,29 @@
}
func (e *valueError) Position() token.Pos {
- return e.err.Pos()
+ src := e.err.Source()
+ if src == nil {
+ return token.NoPos
+ }
+ return src.Pos()
}
func (e *valueError) InputPositions() []token.Pos {
- return e.err.Positions(e.v.ctx())
+ if e.err.Err == nil {
+ return nil
+ }
+ return e.err.Err.InputPositions()
}
func (e *valueError) Msg() (string, []interface{}) {
- return e.err.Msg()
+ if e.err.Err == nil {
+ return "", nil
+ }
+ return e.err.Err.Msg()
}
func (e *valueError) Path() (a []string) {
- if e.v.v == nil {
- return nil
- }
- a, _ = e.v.v.appendPath(a, e.v.idx)
- return a
+ return e.v.appendPath(nil)
}
type errCode = adt.ErrorCode
@@ -135,7 +100,10 @@
return false
}
-var errNotExists = &bottom{Code: codeNotExist, format: "undefined value"}
+var errNotExists = &adt.Bottom{
+ Code: codeNotExist,
+ Err: errors.Newf(token.NoPos, "undefined value"),
+}
func exists(v value) bool {
if err, ok := v.(*bottom); ok {
@@ -144,126 +112,39 @@
return true
}
-// bottom is the bottom of the value lattice. It is subsumed by all values.
-type bottom struct {
- baseValue
-
- index *index
- Code errCode
- exprDepth int
- pos source
- format string
- args []interface{}
-
- err errors.Error // pass-through from higher-level API
- sub []*bottom // sub errors
- value value
- wrapped *bottom
-}
-
-func (x *bottom) Kind() kind { return bottomKind }
-
-func (x *bottom) Positions(ctx *context) []token.Pos {
- var a []token.Pos
- if x.index != nil { // TODO: remove check?
- a = appendPositions(ctx, nil, x.pos)
- }
- if w := x.wrapped; w != nil {
- a = append(a, w.Positions(ctx)...)
- }
- for _, sub := range x.sub {
- a = append(a, sub.Positions(ctx)...)
- }
- return a
-}
-
-func appendPositions(ctx *context, pos []token.Pos, src source) []token.Pos {
- if len(pos) > 15 {
- return pos
- }
- if src != nil {
- if p := src.Pos(); p != token.NoPos {
- pos = append(pos, src.Pos())
- }
- if c := src.computed(); c != nil {
- pos = appendPositions(ctx, pos, c.x)
- pos = appendPositions(ctx, pos, c.y)
- }
- switch x := src.(type) {
- case evaluated:
- case value:
- pos = appendPositions(ctx, pos, x.evalPartial(ctx))
- }
- }
- return pos
-}
-
-func (x *bottom) Msg() (format string, args []interface{}) {
- ctx := x.index.newContext()
- // We need to copy to avoid races.
- args = make([]interface{}, len(x.args))
- copy(args, x.args)
- preEvalArgs(ctx, args)
- return x.format, x.args
-}
-
-func (x *bottom) msg() string {
- return fmt.Sprint(x)
-}
-
-func (x *bottom) Format(s fmt.State, verb rune) {
- msg, args := x.Msg()
- fmt.Fprintf(s, msg, args...)
-}
-
-func cycleError(v evaluated) *bottom {
- if err, ok := v.(*bottom); ok && err.Code == codeCycle {
- return err
- }
- return nil
-}
-
-func (c *context) mkIncompatible(src source, op op, a, b evaluated) evaluated {
- if err := firstBottom(a, b); err != nil {
- return err
- }
- e := mkBin(c, src.Pos(), op, a, b)
- return c.mkErr(e, "invalid operation %s %s %s (mismatched types %s and %s)",
- c.str(a), op, c.str(b), a.Kind(), b.Kind())
-}
-
func (idx *index) mkErr(src source, args ...interface{}) *bottom {
- e := &bottom{index: idx, pos: src}
- if src != nil {
- e.baseValue = src.base()
- }
- if v, ok := src.(value); ok {
- e.value = v
- }
+ var e *adt.Bottom
+ var code errCode = -1
outer:
for i, a := range args {
switch x := a.(type) {
case errCode:
- e.Code = x
+ code = x
case *bottom:
- e.wrapped = x
+ e = adt.CombineErrors(nil, e, x)
case []*bottom:
- e.sub = x
+ for _, b := range x {
+ e = adt.CombineErrors(nil, e, b)
+ }
case errors.Error:
- e.err = x
+ e = adt.CombineErrors(nil, e, &adt.Bottom{Err: x})
case value:
case string:
- e.format = x
- e.args = args[i+1:]
+ args := args[i+1:]
// Do not expand message so that errors can be localized.
- for i, a := range e.args {
- e.args[i] = fixArg(idx, a)
+ pos := pos(src)
+ if code < 0 {
+ code = 0
}
+ e = adt.CombineErrors(nil, e, &adt.Bottom{
+ Code: code,
+ Err: errors.Newf(pos, x, args...),
+ })
break outer
}
}
- if e.Code == codeNone && e.wrapped != nil {
- e.Code = e.wrapped.Code
+ if code >= 0 {
+ e.Code = code
}
return e
}
@@ -283,22 +164,12 @@
return fmt.Sprint(x)
}
-// preEvalArgs is used to expand value arguments just before printing.
-func preEvalArgs(ctx *context, args []interface{}) {
- for i, a := range args {
- switch v := a.(type) {
- case *bottom:
- args[i] = v.msg()
- case value:
- // TODO: convert to Go values so that localization frameworks
- // can format values accordingly.
- args[i] = ctx.str(v)
- }
+func isBottom(x adt.Node) bool {
+ if x == nil {
+ return true
}
-}
-
-func isBottom(n value) bool {
- return n.Kind() == bottomKind
+ b, _ := x.(*adt.Bottom)
+ return b != nil
}
func firstBottom(v ...value) *bottom {
diff --git a/cue/eval.go b/cue/eval.go
deleted file mode 100644
index b995a86..0000000
--- a/cue/eval.go
+++ /dev/null
@@ -1,693 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
-)
-
-type resolver interface {
- reference(ctx *context) value
-}
-
-var _ resolver = &selectorExpr{}
-var _ resolver = &indexExpr{}
-
-// decycleRef rewrites a reference that resolves to an evaluation cycle to
-// an embedding that can be unified as is.
-func decycleRef(ctx *context, v value) (value, scope) {
- switch x := v.(type) {
- case *selectorExpr:
- v, sc := decycleRef(ctx, x.X)
- if v == nil {
- e := x.evalPartial(ctx)
- v = e
- if cycleError(e) != nil {
- sc = &structLit{baseValue: x.base()}
- return &nodeRef{x.base(), sc, x.Sel}, sc
- }
- return nil, nil
- }
- return &selectorExpr{x.baseValue, v, x.Sel}, sc
- case *indexExpr:
- v, sc := decycleRef(ctx, x.X)
- if v == x {
- return nil, nil
- }
- return &indexExpr{x.baseValue, v, x.Index}, sc
- case *nodeRef:
- return nil, nil
- }
- return v, nil
-}
-
-func resolveReference(ctx *context, v value) evaluated {
- if r, ok := v.(resolver); ok {
- e := r.reference(ctx)
- if st, ok := e.(*structLit); ok {
- return st
- }
- if b, ok := e.(*bottom); ok {
- if b := cycleError(b); b != nil {
- // This is only called if we are unifying. The value referenced
- // is either a struct or not. In case the other value is not a
- // struct, we ensure an error by returning a struct. In case the
- // value is a struct, we postpone the evaluation of this
- // reference by creating an embedding for it (which are
- // evaluated after evaluating the struct itself.)
- if y, sc := decycleRef(ctx, v); y != v {
- st := &structLit{baseValue: v.base()}
- ctx.pushForwards(sc, st)
- cp := ctx.copy(y)
- ctx.popForwards()
- st.comprehensions = []compValue{{comp: cp}}
- return st
- }
- return b
- }
- }
- }
- return v.evalPartial(ctx)
-}
-
-func eval(idx *index, v value) evaluated {
- ctx := idx.newContext()
- return v.evalPartial(ctx)
-}
-
-func (x *nodeRef) evalPartial(ctx *context) (result evaluated) {
- return x.node.evalPartial(ctx)
-}
-
-// Atoms
-
-func (x *top) evalPartial(ctx *context) evaluated { return x }
-func (x *bottom) evalPartial(ctx *context) evaluated { return x }
-
-func (x *basicType) evalPartial(ctx *context) evaluated { return x }
-func (x *nullLit) evalPartial(ctx *context) evaluated { return x }
-func (x *boolLit) evalPartial(ctx *context) evaluated { return x }
-func (x *stringLit) evalPartial(ctx *context) evaluated { return x }
-func (x *bytesLit) evalPartial(ctx *context) evaluated { return x }
-func (x *numLit) evalPartial(ctx *context) evaluated { return x }
-func (x *durationLit) evalPartial(ctx *context) evaluated { return x }
-
-func (x *lambdaExpr) evalPartial(ctx *context) evaluated {
- return ctx.deref(x).(*lambdaExpr)
-}
-
-func (x *selectorExpr) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "selectorExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- e := newEval(ctx, true)
-
- const msgType = "invalid operation: %[5]s (type %[3]s does not support selection)"
- v := e.eval(x.X, structKind|lambdaKind, msgType, x)
-
- if e.is(v, structKind|lambdaKind, "") {
- sc, ok := v.(scope)
- if !ok {
- return ctx.mkErr(x, "invalid subject to selector (found %v)", v.Kind())
- }
- n := sc.Lookup(ctx, x.Sel)
- if n.optional {
- field := ctx.LabelStr(x.Sel)
- return ctx.mkErr(x, codeIncomplete, "field %q is optional", field)
- }
- if n.val() == nil {
- field := ctx.LabelStr(x.Sel)
- if st, ok := sc.(*structLit); ok && !st.isClosed() {
- return ctx.mkErr(x, codeIncomplete, "undefined field %q", field)
- }
- // m.foo undefined (type map[string]bool has no field or method foo)
- // TODO: mention x.x in error message?
- return ctx.mkErr(x, "undefined field %q", field)
- }
- return n.Value
- }
- return e.err(&selectorExpr{x.baseValue, v, x.Sel})
-}
-
-func (x *selectorExpr) reference(ctx *context) (result value) {
- if ctx.trace {
- defer uni(indent(ctx, "selectorExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- e := newEval(ctx, true)
-
- const msgType = "invalid operation: %[5]s (type %[3]s does not support selection)"
- v := e.eval(x.X, structKind|lambdaKind, msgType, x)
-
- if e.is(v, structKind|lambdaKind, "") {
- sc, ok := v.(scope)
- if !ok {
- return ctx.mkErr(x, "invalid subject to selector (found %v)", v.Kind())
- }
- n := sc.Lookup(ctx, x.Sel)
- if n.optional {
- field := ctx.LabelStr(x.Sel)
- return ctx.mkErr(x, codeIncomplete, "field %q is optional", field)
- }
- if n.val() == nil {
- field := ctx.LabelStr(x.Sel)
- if st, ok := sc.(*structLit); ok && !st.isClosed() {
- return ctx.mkErr(x, codeIncomplete, "undefined field %q", field)
- }
- // m.foo undefined (type map[string]bool has no field or method foo)
- // TODO: mention x.x in error message?
- return ctx.mkErr(x, "undefined field %q", field)
- }
- return n.v
- }
- return e.err(&selectorExpr{x.baseValue, v, x.Sel})
-}
-
-func (x *indexExpr) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "indexExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- e := newEval(ctx, true)
-
- const msgType = "invalid operation: %[5]s (type %[3]s does not support indexing)"
- const msgIndexType = "invalid %[5]s index %[1]s (type %[3]s)"
-
- val := e.eval(x.X, listKind|structKind, msgType, x)
- k := val.Kind()
- index := e.eval(x.Index, stringKind|intKind, msgIndexType, k)
-
- switch v := val.(type) {
- case *structLit:
- if e.is(index, stringKind, msgIndexType, k) {
- s := index.strValue()
- // TODO: must lookup
- n := v.Lookup(ctx, ctx.StrLabel(s))
- if n.definition {
- return ctx.mkErr(x, index,
- "field %q is a definition", s)
- }
- if n.optional {
- return ctx.mkErr(x, index, codeIncomplete, "field %q is optional", s)
- }
- if n.val() == nil {
- if !v.isClosed() {
- return ctx.mkErr(x, index, codeIncomplete, "undefined field %q", s)
- }
- return ctx.mkErr(x, index, "undefined field %q", s)
- }
- return n.Value
- }
- case atter:
- if e.is(index, intKind, msgIndexType, k) {
- i := index.(*numLit).intValue(ctx)
- if i < 0 {
- const msg = "invalid %[4]s index %[1]s (index must be non-negative)"
- return e.mkErr(x.Index, index, 0, k, msg)
- }
- return v.at(ctx, i)
- }
- }
- return e.err(&indexExpr{x.baseValue, val, index})
-}
-
-func (x *indexExpr) reference(ctx *context) (result value) {
- if ctx.trace {
- defer uni(indent(ctx, "indexExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- e := newEval(ctx, true)
-
- const msgType = "invalid operation: %[5]s (type %[3]s does not support indexing)"
- const msgIndexType = "invalid %[5]s index %[1]s (type %[3]s)"
-
- val := e.eval(x.X, listKind|structKind, msgType, x)
- k := val.Kind()
- index := e.eval(x.Index, stringKind|intKind, msgIndexType, k)
-
- switch v := val.(type) {
- case *structLit:
- if e.is(index, stringKind, msgIndexType, k) {
- s := index.strValue()
- // TODO: must lookup
- n := v.Lookup(ctx, ctx.StrLabel(s))
- if n.definition {
- return ctx.mkErr(x, index,
- "field %q is a definition", s)
- }
- if n.optional {
- return ctx.mkErr(x, index, codeIncomplete, "field %q is optional", s)
- }
- if n.val() == nil {
- if !v.isClosed() {
- return ctx.mkErr(x, index, codeIncomplete, "undefined field %q", s)
- }
- return ctx.mkErr(x, index, "undefined field %q", s)
- }
- return n.v
- }
- case *list:
- if e.is(index, intKind, msgIndexType, k) {
- i := index.(*numLit).intValue(ctx)
- if i < 0 {
- const msg = "invalid %[4]s index %[1]s (index must be non-negative)"
- return e.mkErr(x.Index, index, 0, k, msg)
- }
- return v.iterAt(ctx, i).v
- }
-
- case atter:
- if e.is(index, intKind, msgIndexType, k) {
- i := index.(*numLit).intValue(ctx)
- if i < 0 {
- const msg = "invalid %[4]s index %[1]s (index must be non-negative)"
- return e.mkErr(x.Index, index, 0, k, msg)
- }
- return v.at(ctx, i)
- }
- }
- return e.err(&indexExpr{x.baseValue, val, index})
-}
-
-// Composit
-
-func (x *sliceExpr) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "sliceExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- e := newEval(ctx, true)
- const msgType = "cannot slice %[2]s (type %[3]s)"
- const msgInvalidIndex = "invalid slice index %[1]s (type %[3]s)"
- val := e.eval(x.X, listKind, msgType)
- lo := e.evalAllowNil(x.Lo, intKind, msgInvalidIndex)
- hi := e.evalAllowNil(x.Hi, intKind, msgInvalidIndex)
- var low, high *numLit
- if lo != nil && e.is(lo, intKind, msgInvalidIndex) {
- low = lo.(*numLit)
- }
- if hi != nil && e.is(hi, intKind, msgInvalidIndex) {
- high = hi.(*numLit)
- }
- if !e.hasErr() {
- switch x := val.(type) {
- case *list:
- return x.slice(ctx, low, high)
- case *stringLit:
- return x.slice(ctx, low, high)
- }
- }
- return e.err(&sliceExpr{x.baseValue, val, lo, hi})
-}
-
-// TODO: make a callExpr a binary expression
-func (x *callExpr) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "callExpr", x))
- defer func() {
- ctx.debugPrint("result:", result)
- }()
- }
-
- e := newEval(ctx, true)
-
- fn := e.eval(x.Fun, lambdaKind, "cannot call non-function %[2]s (type %[3]s)")
- args := make([]evaluated, len(x.Args))
- for i, a := range x.Args {
- args[i] = e.evalPartial(a, typeKinds, "never triggers")
- }
- if !e.hasErr() {
- // If we have a template expression, it is either already copied it as
- // result of a references, or it is a literal, in which case it is
- // trivially fully evaluated.
- return fn.(caller).call(ctx, x, args...).evalPartial(ctx)
- }
- // Construct a simplified call for reporting purposes.
- err := &callExpr{x.baseValue, fn, nil}
- for _, a := range args {
- err.Args = append(err.Args, a)
- }
- return e.err(err)
-}
-
-func (x *customValidator) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "custom", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
- return x
-}
-
-func (x *bound) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "bound", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
- v := x.Expr.evalPartial(ctx)
- if isBottom(v) {
- if isIncomplete(v) {
- return v
- }
- return ctx.mkErr(x, v, "error evaluating bound")
- }
- if v == x.Expr {
- return x
- }
- return newBound(ctx, x.baseValue, x.Op, x.k, v)
-}
-
-func (x *interpolation) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "interpolation", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
- buf := bytes.Buffer{}
- var incomplete value
- for _, v := range x.Parts {
- switch e := ctx.manifest(v).(type) {
- case *bottom:
- return e
- case *stringLit, *numLit, *durationLit:
- buf.WriteString(e.strValue())
- default:
- k := e.Kind()
- if k&stringableKind == bottomKind {
- return ctx.mkErr(e, "expression in interpolation must evaluate to a number kind or string (found %v)", k)
- }
- if !k.isGround() {
- incomplete = v
- }
- }
- }
- if incomplete != nil {
- return ctx.mkErr(incomplete, codeIncomplete,
- "incomplete value '%s' in interpolation", ctx.str(incomplete))
- }
- return &stringLit{x.baseValue, buf.String(), nil}
-}
-
-func (x *list) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "list", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
- n := x.len.evalPartial(ctx)
- if isBottom(n) {
- return n
- }
- s := x.elem.evalPartial(ctx).(*structLit)
- if s == x.elem && n == x.len {
- return x
- }
- return &list{x.baseValue, s, x.typ, n}
-}
-
-func (x *listComprehension) evalPartial(ctx *context) evaluated {
- s := &structLit{baseValue: x.baseValue}
- list := &list{baseValue: x.baseValue, elem: s}
- err := x.clauses.yield(ctx, func(v evaluated) *bottom {
- list.elem.Arcs = append(list.elem.Arcs, arc{
- Label: label(len(list.elem.Arcs)),
- v: v.evalPartial(ctx),
- })
- return nil
- })
- if err != nil {
- return err
- }
- list.initLit()
- return list
-}
-
-func (x *structComprehension) evalPartial(ctx *context) evaluated {
- var st evaluated = &structLit{baseValue: x.baseValue}
- err := x.clauses.yield(ctx, func(v evaluated) *bottom {
- embed := v.evalPartial(ctx)
- if st, ok := embed.(*structLit); ok {
- x, err := st.expandFields(ctx)
- if err != nil {
- return err
- }
- embed = x
- }
- res := binOp(ctx, x, opUnify, st, embed)
- if b, ok := res.(*bottom); ok {
- return b
- }
- st = res
- return nil
- })
- if err != nil {
- return err
- }
- return st
-}
-
-func (x *feed) evalPartial(ctx *context) evaluated { return x }
-func (x *guard) evalPartial(ctx *context) evaluated { return x }
-func (x *yield) evalPartial(ctx *context) evaluated { return x }
-
-func (x *fieldComprehension) evalPartial(ctx *context) evaluated {
- k := x.key.evalPartial(ctx)
- v := x.val
- if err := firstBottom(k, v); err != nil {
- return err
- }
- if !k.Kind().isAnyOf(stringKind) {
- return ctx.mkErr(k, "key must be of type string")
- }
- f := ctx.Label(k.strValue(), true)
- st := &structLit{baseValue: x.baseValue}
- st.insertValue(ctx, f, x.opt, x.def, v, x.attrs, x.doc)
- return st
-}
-
-func (x *closeIfStruct) evalPartial(ctx *context) evaluated {
- v := x.value.evalPartial(ctx)
- v = updateCloseStatus(ctx, v)
- return v
-}
-
-func (x *structLit) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "struct eval", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
- x = ctx.deref(x).(*structLit)
-
- // TODO: Handle cycle?
-
- // TODO: would be great to be able to expand fields here. But would need
- // some careful consideration regarding dereferencing.
-
- return x
-}
-
-func (x *unification) evalPartial(ctx *context) (result evaluated) {
- // By definition, all of the values in this type are already evaluated.
- return x
-}
-
-func (x *disjunction) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "disjunction", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- // decSum := false
- if len(ctx.evalStack) > 1 {
- ctx.inSum++
- }
- dn := &disjunction{
- x.baseValue,
- make([]dValue, 0, len(x.Values)),
- make([]*bottom, 0, len(x.errors)),
- x.HasDefaults,
- }
- changed := false
- for _, v := range x.Values {
- n := v.Val.evalPartial(ctx)
- changed = changed || n != v.Val
- // Including elements of disjunctions recursively makes default handling
- // associative (*a | (*b|c)) == ((*a|*b) | c).
- if d, ok := n.(*disjunction); ok {
- changed = true
- for _, dv := range d.Values {
- dn.add(ctx, dv.Val, dv.Default)
- }
- } else {
- dn.add(ctx, n, v.Default)
- }
- }
- if !changed {
- dn = x
- }
- if len(ctx.evalStack) > 1 {
- ctx.inSum--
- }
- return dn.normalize(ctx, x).val
-}
-
-func (x *disjunction) manifest(ctx *context) (result evaluated) {
- values := make([]dValue, 0, len(x.Values))
- validValue := false
- for _, dv := range x.Values {
- switch {
- case isBottom(dv.Val):
- case dv.Default:
- values = append(values, dv)
- default:
- validValue = true
- }
- }
-
- switch {
- case len(values) > 0:
- // values contains all the valid defaults
- case !validValue:
- return x
- default:
- for _, dv := range x.Values {
- dv.Default = false
- values = append(values, dv)
- }
- }
-
- switch len(values) {
- case 0:
- return x
-
- case 1:
- return values[0].Val.evalPartial(ctx)
- }
-
- x = &disjunction{x.baseValue, values, x.errors, true}
- return x.normalize(ctx, x).val
-}
-
-func (x *binaryExpr) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "binaryExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
- var left, right evaluated
-
- if _, isUnify := x.Op.unifyType(); !isUnify {
- ctx.incEvalDepth()
- left = ctx.manifest(x.X)
- right = ctx.manifest(x.Y)
- ctx.decEvalDepth()
-
- // TODO: allow comparing to a literal bottom only. Find something more
- // principled perhaps. One should especially take care that two values
- // evaluating to bottom don't evaluate to true. For now we check for
- // bottom here and require that one of the values be a bottom literal.
- if isLiteralBottom(x.X) || isLiteralBottom(x.Y) {
- if b := validate(ctx, left); b != nil {
- left = b
- }
- if b := validate(ctx, right); b != nil {
- right = b
- }
- leftBottom := isBottom(left)
- rightBottom := isBottom(right)
- switch x.Op {
- case opEql:
- return &boolLit{x.baseValue, leftBottom == rightBottom}
- case opNeq:
- return &boolLit{x.baseValue, leftBottom != rightBottom}
- }
- }
- } else {
- left = resolveReference(ctx, x.X)
- right = resolveReference(ctx, x.Y)
-
- if err := cycleError(left); err != nil && ctx.inSum == 0 && right.Kind().isAtom() {
- return ctx.delayConstraint(right, x)
- }
- if err := cycleError(right); err != nil && ctx.inSum == 0 && left.Kind().isAtom() {
- return ctx.delayConstraint(left, x)
- }
-
- // check if it is a cycle that can be unwrapped.
- // If other value is a cycle or list, return the original forwarded,
- // but ensure the value is not cached. Object/list error?
- }
- return binOp(ctx, x, x.Op, left, right)
-}
-
-func (x *unaryExpr) evalPartial(ctx *context) (result evaluated) {
- if ctx.trace {
- defer uni(indent(ctx, "unaryExpr", x))
- defer func() { ctx.debugPrint("result:", result) }()
- }
-
- return evalUnary(ctx, x, x.Op, x.X)
-}
-
-func evalUnary(ctx *context, src source, op op, x value) evaluated {
- v := ctx.manifest(x)
-
- const numeric = numKind | durationKind
- kind := v.Kind()
- switch op {
- case opSub:
- if kind&numeric == bottomKind {
- return ctx.mkErr(src, "invalid operation -%s (- %s)", ctx.str(x), kind)
- }
- switch v := v.(type) {
- case *numLit:
- f := *v
- f.X.Neg(&v.X)
- return &f
- case *durationLit:
- d := *v
- d.d = -d.d
- return &d
- }
- return ctx.mkErr(src, codeIncomplete, "operand %s of '-' not concrete (was %s)", ctx.str(x), kind)
-
- case opAdd:
- if kind&numeric == bottomKind {
- return ctx.mkErr(src, "invalid operation +%s (+ %s)", ctx.str(x), kind)
- }
- switch v := v.(type) {
- case *numLit, *durationLit:
- return v
- case *top:
- return &basicType{v.baseValue, numeric | nonGround}
- case *basicType:
- return &basicType{v.baseValue, (v.K & numeric) | nonGround}
- }
- return ctx.mkErr(src, codeIncomplete, "operand %s of '+' not concrete (was %s)", ctx.str(x), kind)
-
- case opNot:
- if kind&boolKind == bottomKind {
- return ctx.mkErr(src, "invalid operation !%s (! %s)", ctx.str(x), kind)
- }
- switch v := v.(type) {
- case *boolLit:
- return &boolLit{src.base(), !v.B}
- }
- return ctx.mkErr(src, codeIncomplete, "operand %s of '!' not concrete (was %s)", ctx.str(x), kind)
- }
- return ctx.mkErr(src, "invalid operation %s%s (%s %s)", op, ctx.str(x), op, kind)
-}
diff --git a/cue/evaluator.go b/cue/evaluator.go
deleted file mode 100644
index ccdc429..0000000
--- a/cue/evaluator.go
+++ /dev/null
@@ -1,146 +0,0 @@
-// 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 cue
-
-func (c *context) manifest(v value) evaluated {
- evaluated := v.evalPartial(c)
-outer:
- switch x := evaluated.(type) {
- case *disjunction:
- evaluated = x.manifest(c)
-
- case *list:
- return x.manifest(c)
-
- default:
- break outer
- }
- return evaluated
-}
-
-type evaluator struct {
- ctx *context
- bottom []*bottom
-}
-
-const (
- // (fmt, evaluated, orig, gotKind, wantKind)
- // "invalid [string index] -1 (index must be non-negative)"
- // "invalid operation: %[1]s (type %[3] does not support indexing)"
- // msgType = "invalid %s %s (must be type %s)"
- msgGround = "invalid non-ground value %[1]s (must be concrete %[4]s)"
-)
-
-func newEval(ctx *context, manifest bool) evaluator {
- return evaluator{ctx: ctx}
-}
-
-func (e *evaluator) hasErr() bool {
- return len(e.bottom) > 0
-}
-
-func (e *evaluator) mkErr(orig, eval value, code errCode, want kind, desc string, args ...interface{}) (err *bottom) {
- args = append([]interface{}{
- eval,
- code,
- desc, // format string
- eval, // 1
- orig, // 2
- eval.Kind(), // 3
- want}, // 4
- args...)
- for i := 3; i < len(args); i++ {
- switch v := args[i].(type) {
- case value:
- args[i] = e.ctx.str(v)
- }
- }
- err = e.ctx.mkErr(orig, args...)
- // TODO: maybe replace with more specific type error.
- for i, old := range e.bottom {
- if old == eval {
- e.bottom[i] = err
- return err
- }
- }
- e.bottom = append(e.bottom, err)
- return err
-}
-
-func (e *evaluator) eval(v value, want kind, desc string, extraArgs ...interface{}) evaluated {
- eval := e.ctx.manifest(v)
-
- if isBottom(eval) {
- e.bottom = append(e.bottom, eval.(*bottom))
- return eval
- }
- got := eval.Kind()
- if got&want == bottomKind {
- return e.mkErr(v, eval, codeTypeError, want, desc, extraArgs...)
- }
- if !got.isGround() {
- return e.mkErr(v, eval, codeIncomplete, want, msgGround, extraArgs...)
- }
- return eval
-}
-
-func (e *evaluator) evalPartial(v value, want kind, desc string, extraArgs ...interface{}) evaluated {
- eval := v.evalPartial(e.ctx)
- if isBottom(eval) {
- // handle incomplete errors separately?
- e.bottom = append(e.bottom, eval.(*bottom))
- return eval
- }
- got := eval.Kind()
- if got&want == bottomKind {
- return e.mkErr(v, eval, codeTypeError, want, desc, extraArgs...)
- }
- return eval
-}
-
-func (e *evaluator) evalAllowNil(v value, want kind, desc string, extraArgs ...interface{}) evaluated {
- if v == nil {
- return nil
- }
- return e.eval(v, want, desc, extraArgs...)
-}
-
-func (e *evaluator) is(v value, want kind, desc string, args ...interface{}) bool {
- if isBottom(v) {
- // Even though errors are ground, we treat them as not allowed.
- return false
- }
- got := v.Kind()
- if got&want == bottomKind {
- e.mkErr(v, v, codeTypeError, want, desc, args...)
- return false
- }
- // groundness must already have been checked.
- return true
-}
-
-func (e *evaluator) err(v value) evaluated {
- // if bottom is a fatal (not incomplete) error, return that.
- // otherwise, try to extract a fatal error from the given value.
- // otherwise return an incomplete error with the given value as offending.
- for _, b := range e.bottom {
- if b.Code != codeIncomplete {
- return b
- }
- }
- b := *e.bottom[0]
- b.Code = codeIncomplete
- return &b
-}
diff --git a/cue/examples_test.go b/cue/examples_test.go
deleted file mode 100644
index 889fdf1..0000000
--- a/cue/examples_test.go
+++ /dev/null
@@ -1,117 +0,0 @@
-// Copyright 2019 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 cue_test
-
-import (
- "fmt"
- "log"
-
- "cuelang.org/go/cue"
-)
-
-func ExampleRuntime_Parse() {
- const config = `
- msg: "Hello \(place)!"
- place: string | *"world"
- `
-
- var r cue.Runtime
-
- instance, err := r.Compile("test", config)
- if err != nil {
- // handle error
- }
-
- str, _ := instance.Lookup("msg").String()
- fmt.Println(str)
-
- instance, _ = instance.Fill("you", "place")
- str, _ = instance.Lookup("msg").String()
- fmt.Println(str)
-
- // Output:
- // Hello world!
- // Hello you!
-}
-
-func ExampleValue_Decode() {
- type ab struct{ A, B int }
-
- var x ab
-
- var r cue.Runtime
-
- i, _ := r.Compile("test", `{A: 2, B: 4}`)
- _ = i.Value().Decode(&x)
- fmt.Println(x)
-
- i, _ = r.Compile("test", `{B: "foo"}`)
- err := i.Value().Decode(&x)
- fmt.Println(err)
-
- // Output:
- // {2 4}
- // json: cannot unmarshal string into Go struct field ab.B of type int
-}
-
-func ExampleValue_Subsume() {
- // Check compatibility of successive APIs.
- var r cue.Runtime
-
- inst, err := r.Compile("apis", `
- // Release notes:
- // - You can now specify your age and your hobby!
- #V1: {
- age: >=0 & <=100
- hobby: string
- }
-
- // Release notes:
- // - People get to be older than 100, so we relaxed it.
- // - It seems not many people have a hobby, so we made it optional.
- #V2: {
- age: >=0 & <=150 // people get older now
- hobby?: string // some people don't have a hobby
- }
-
- // Release notes:
- // - Actually no one seems to have a hobby nowadays anymore,
- // so we dropped the field.
- #V3: {
- age: >=0 & <=150
- }`)
-
- if err != nil {
- fmt.Println(err)
- // handle error
- }
- v1, err1 := inst.Value().FieldByName("#V1", true)
- v2, err2 := inst.Value().FieldByName("#V2", true)
- v3, err3 := inst.Value().FieldByName("#V3", true)
- if err1 != nil || err2 != nil || err3 != nil {
- log.Println(err1, err2, err3)
- }
-
- // Is V2 backwards compatible with V1? In other words, does V2 subsume V1?
- err = v2.Value.Subsume(v1.Value)
- fmt.Println("V2 is backwards compatible with V1:", err)
-
- err = v3.Value.Subsume(v2.Value)
- fmt.Println("V3 is backwards compatible with V2:", err)
-
- // Output:
- // V2 is backwards compatible with V1: <nil>
- // V3 is backwards compatible with V2: #V2: conflicting values <=150 and string (mismatched types number and string)
-}
diff --git a/cue/export.go b/cue/export.go
deleted file mode 100644
index a581a27..0000000
--- a/cue/export.go
+++ /dev/null
@@ -1,1302 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "math/rand"
- "sort"
- "strconv"
- "strings"
- "unicode/utf8"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
-)
-
-func doEval(m options) bool {
- return !m.raw
-}
-
-func exportFile(ctx *context, inst *Instance, v value, m options) (f *ast.File, imports []string) {
- e := exporter{ctx, m, nil, map[label]bool{}, map[string]importInfo{}, false, nil}
- top, ok := v.evalPartial(ctx).(*structLit)
- if ok {
- top, err := top.expandFields(ctx)
- if err != nil {
- v = err
- } else {
- for _, a := range top.Arcs {
- e.top[a.Label] = true
- }
- }
- }
-
- value := e.expr(v)
- return e.toFile(inst, value, m)
-}
-
-func export(ctx *context, inst *Instance, v value, m options) (n ast.Node, imports []string) {
- e := exporter{ctx, m, nil, map[label]bool{}, map[string]importInfo{}, false, nil}
- top, ok := v.evalPartial(ctx).(*structLit)
- if ok {
- top, err := top.expandFields(ctx)
- if err != nil {
- v = err
- } else {
- for _, a := range top.Arcs {
- e.top[a.Label] = true
- }
- }
- }
-
- value := e.expr(v)
- if len(e.imports) == 0 && inst == nil {
- // TODO: unwrap structs?
- return value, nil
- }
- return e.toFile(inst, value, m)
-}
-
-func (e *exporter) toFile(inst *Instance, value ast.Expr, m options) (n *ast.File, imports []string) {
- file := &ast.File{}
- if inst != nil {
- if inst.PkgName != "" {
- p := &ast.Package{Name: ast.NewIdent(inst.PkgName)}
- file.Decls = append(file.Decls, p)
- if m.docs {
- for _, d := range inst.Doc() {
- p.AddComment(d)
- break
- }
- }
- }
- }
-
- imports = make([]string, 0, len(e.imports))
- for k := range e.imports {
- imports = append(imports, k)
- }
- sort.Strings(imports)
-
- if len(imports) > 0 {
- importDecl := &ast.ImportDecl{}
- file.Decls = append(file.Decls, importDecl)
-
- for _, k := range imports {
- info := e.imports[k]
- ident := (*ast.Ident)(nil)
- if info.name != "" {
- ident = ast.NewIdent(info.name)
- }
- if info.alias != "" {
- file.Decls = append(file.Decls, &ast.LetClause{
- Ident: ast.NewIdent(info.alias),
- Expr: ast.NewIdent(info.short),
- })
- }
- importDecl.Specs = append(importDecl.Specs, ast.NewImport(ident, k))
- }
- }
-
- if obj, ok := value.(*ast.StructLit); ok {
- file.Decls = append(file.Decls, obj.Elts...)
- } else {
- file.Decls = append(file.Decls, &ast.EmbedDecl{Expr: value})
- }
-
- // resolve the file.
- return file, imports
-}
-
-type exporter struct {
- ctx *context
- mode options
- stack []remap
- top map[label]bool // label to alias or ""
- imports map[string]importInfo // pkg path to info
- inDef bool // TODO(recclose):use count instead
-
- incomplete []source
-}
-
-func (p *exporter) addIncomplete(v value) {
- // TODO: process incomplete values
-}
-
-type importInfo struct {
- name string
- short string
- alias string
-}
-
-type remap struct {
- key scope // structLit or params
- from label
- to *ast.Ident
- syn *ast.StructLit
-}
-
-func (p *exporter) unique(s string) string {
- s = strings.ToUpper(s)
- lab := s
- for {
- if !p.ctx.HasLabel(lab) {
- p.ctx.Label(lab, true)
- break
- }
- lab = s + fmt.Sprintf("%0.6x", rand.Intn(1<<24))
- }
- return lab
-}
-
-func (p *exporter) label(f label) ast.Label {
- str := p.ctx.LabelStr(f)
- if strings.HasPrefix(str, "#") && !f.IsDef() ||
- strings.HasPrefix(str, "_") && !f.IsHidden() ||
- !ast.IsValidIdent(str) {
- return ast.NewLit(token.STRING, strconv.Quote(str))
- }
- return &ast.Ident{Name: str}
-}
-
-func (p *exporter) identifier(f label) *ast.Ident {
- str := p.ctx.LabelStr(f)
- return &ast.Ident{Name: str}
-}
-
-func (p *exporter) ident(str string) *ast.Ident {
- return &ast.Ident{Name: str}
-}
-
-func (p *exporter) clause(v value) (n ast.Clause, next yielder) {
- switch x := v.(type) {
- case *feed:
- feed := &ast.ForClause{
- Value: p.identifier(x.fn.params.arcs[1].Label),
- Source: p.expr(x.Src),
- }
- key := x.fn.params.arcs[0]
- if p.ctx.LabelStr(key.Label) != "_" {
- feed.Key = p.identifier(key.Label)
- }
- return feed, x.fn.value.(yielder)
-
- case *guard:
- return &ast.IfClause{Condition: p.expr(x.Condition)}, x.Dst
- }
- panic(fmt.Sprintf("unsupported clause type %T", v))
-}
-
-func (p *exporter) shortName(inst *Instance, preferred, pkg string) string {
- info, ok := p.imports[pkg]
- short := info.short
- if !ok {
- short = inst.PkgName
- if _, ok := p.top[p.ctx.Label(short, true)]; ok && preferred != "" {
- short = preferred
- info.name = short
- }
- for {
- if _, ok := p.top[p.ctx.Label(short, true)]; !ok {
- break
- }
- short += "x"
- info.name = short
- }
- info.short = short
- p.top[p.ctx.Label(short, true)] = true
- p.imports[pkg] = info
- }
- f := p.ctx.Label(short, true)
- for _, e := range p.stack {
- if e.from == f {
- if info.alias == "" {
- info.alias = p.unique(short)
- p.imports[pkg] = info
- }
- short = info.alias
- break
- }
- }
- return short
-}
-
-func (p *exporter) mkTemplate(v value, n *ast.Ident) ast.Label {
- var expr ast.Expr
- if v != nil {
- expr = p.expr(v)
- } else {
- expr = ast.NewIdent("string")
- }
- switch n.Name {
- case "", "_":
- default:
- expr = &ast.Alias{Ident: n, Expr: ast.NewIdent("string")}
- }
- return ast.NewList(expr)
-}
-
-func hasTemplate(s *ast.StructLit) bool {
- for _, e := range s.Elts {
- switch f := e.(type) {
- case *ast.Ellipsis:
- return true
-
- case *ast.EmbedDecl:
- if st, ok := f.Expr.(*ast.StructLit); ok && hasTemplate(st) {
- return true
- }
- case *ast.Field:
- label := f.Label
- if _, ok := label.(*ast.TemplateLabel); ok {
- return true
- }
- if a, ok := label.(*ast.Alias); ok {
- label, ok = a.Expr.(ast.Label)
- if !ok {
- return false
- }
- }
- if l, ok := label.(*ast.ListLit); ok {
- if len(l.Elts) != 1 {
- return false
- }
- expr := l.Elts[0]
- if a, ok := expr.(*ast.Alias); ok {
- expr = a.Expr
- }
- if i, ok := expr.(*ast.Ident); ok {
- if i.Name == "_" || i.Name == "string" {
- return true
- }
- }
- }
- }
- }
- return false
-}
-
-func (p *exporter) showOptional() bool {
- return !p.mode.omitOptional && !p.mode.concrete
-}
-
-func (p *exporter) closeOrOpen(s *ast.StructLit, isClosed bool) ast.Expr {
- // Note, there is no point in printing close if we are dropping optional
- // fields, as by this the meaning of close will change anyway.
- if !p.showOptional() || p.mode.final {
- return s
- }
- if isClosed && !p.inDef && !hasTemplate(s) {
- return ast.NewCall(ast.NewIdent("close"), s)
- }
- if !isClosed && p.inDef && !hasTemplate(s) {
- s.Elts = append(s.Elts, &ast.Ellipsis{})
- }
- return s
-}
-
-func (p *exporter) isComplete(v value, all bool) bool {
- switch x := v.(type) {
- case *numLit, *stringLit, *bytesLit, *nullLit, *boolLit:
- return true
- case *list:
- if p.mode.final || !all {
- return true
- }
- if x.isOpen() {
- return false
- }
- for i := range x.elem.Arcs {
- if !p.isComplete(x.at(p.ctx, i), all) {
- return false
- }
- }
- return true
- case *structLit:
- return !all && p.mode.final
- case *bottom:
- return !isIncomplete(x)
- case *closeIfStruct:
- return p.isComplete(x.value, all)
- }
- return false
-}
-
-func isDisjunction(v value) bool {
- switch x := v.(type) {
- case *disjunction:
- return true
- case *closeIfStruct:
- return isDisjunction(x.value)
- }
- return false
-}
-
-func (p *exporter) recExpr(v value, e evaluated, optional bool) ast.Expr {
- var m evaluated
- if !p.mode.final {
- m = e.evalPartial(p.ctx)
- } else {
- m = p.ctx.manifest(e)
- }
- isComplete := p.isComplete(m, false)
- if optional || (!isComplete && !p.mode.concrete) {
- if !p.mode.final {
- // Schema mode.
-
- // Print references as they are, if applicable.
- //
- // TODO: We probably should not allow resolving references in
- // schema mode, or at most allow resolving _some_ references, like
- // those defined outside of packages.
- noResolve := !p.mode.resolveReferences
- if optional {
- // Don't resolve references when a field is optional.
- // This may break some unnecessary cycles.
- noResolve = true
- }
- if isBottom(e) || (v.Kind().hasReferences() && noResolve) {
- return p.expr(v)
- }
- } else {
- // Data mode.
-
- if p.mode.concrete && !m.Kind().isGround() {
- p.addIncomplete(v)
- }
- // TODO: do something more principled than this hack.
- // This likely requires disjunctions to keep track of original
- // values (so using arcs instead of values).
- opts := options{concrete: true, raw: true}
- p := &exporter{p.ctx, opts, p.stack, p.top, p.imports, p.inDef, nil}
- if isDisjunction(v) || isBottom(e) {
- return p.expr(v)
- }
- if v.Kind()&structKind == 0 {
- return p.expr(e)
- }
- if optional || isDisjunction(e) {
- // Break cycles: final and resolveReferences really should not be
- // used with optional.
- p.mode.resolveReferences = false
- p.mode.final = false
- return p.expr(v)
- }
- }
- }
- return p.expr(e)
-}
-
-func (p *exporter) isClosed(x *structLit) bool {
- return x.closeStatus.shouldClose()
-}
-
-func (p *exporter) badf(msg string, args ...interface{}) ast.Expr {
- msg = fmt.Sprintf(msg, args...)
- bad := &ast.BadExpr{}
- bad.AddComment(&ast.CommentGroup{
- Doc: true,
- List: []*ast.Comment{{Text: "// " + msg}},
- })
- return bad
-}
-
-func (p *exporter) expr(v value) ast.Expr {
- // TODO: use the raw expression for convert incomplete errors downstream
- // as well.
- if doEval(p.mode) || p.mode.concrete {
- e := v.evalPartial(p.ctx)
- x := e
- if p.mode.final {
- x = p.ctx.manifest(e)
- }
-
- if !p.isComplete(x, true) {
- if p.mode.concrete && !x.Kind().isGround() {
- p.addIncomplete(v)
- }
- switch {
- case isBottom(e):
- if p.mode.concrete {
- p.addIncomplete(v)
- }
- p = &exporter{p.ctx, options{raw: true}, p.stack, p.top, p.imports, p.inDef, nil}
- return p.expr(v)
- case v.Kind().hasReferences() && !p.mode.resolveReferences:
- case doEval(p.mode):
- v = e
- }
- } else {
- v = x
- }
- }
-
- old := p.stack
- defer func() { p.stack = old }()
-
- // TODO: also add position information.
- switch x := v.(type) {
- case *builtin:
- if x.pkg == 0 {
- return ast.NewIdent(x.Name)
- }
- pkg := p.ctx.LabelStr(x.pkg)
- inst := builtins[pkg]
- short := p.shortName(inst, "", pkg)
- return ast.NewSel(ast.NewIdent(short), x.Name)
-
- case *nodeRef:
- if x.label == 0 {
- // NOTE: this nodeRef is used within a selector.
- return nil
- }
- short := p.ctx.LabelStr(x.label)
-
- if inst := p.ctx.getImportFromNode(x.node); inst != nil {
- return ast.NewIdent(p.shortName(inst, short, inst.ImportPath))
- }
-
- // fix shadowed label.
- return ast.NewIdent(short)
-
- case *selectorExpr:
- n := p.expr(x.X)
- if n != nil {
- return ast.NewSel(n, p.ctx.LabelStr(x.Sel))
- }
- f := x.Sel
- ident := p.identifier(f)
- node, ok := x.X.(*nodeRef)
- if !ok {
- return p.badf("selector without node")
- }
- if l, ok := node.node.(*lambdaExpr); ok && len(l.arcs) == 1 {
- f = l.params.arcs[0].Label
- // TODO: ensure it is shadowed.
- ident = p.identifier(f)
- return ident
- }
-
- // TODO: nodes may have been shadowed. Use different algorithm.
- conflict := false
- for i := len(p.stack) - 1; i >= 0; i-- {
- e := &p.stack[i]
- if e.from != f {
- continue
- }
- if e.key != node.node {
- conflict = true
- continue
- }
- if conflict {
- ident = e.to
- if e.to == nil {
- name := p.unique(p.ctx.LabelStr(f))
- e.syn.Elts = append(e.syn.Elts, &ast.Alias{
- Ident: p.ident(name),
- Expr: p.identifier(f),
- })
- ident = p.ident(name)
- e.to = ident
- }
- }
- break
- }
- return ident
-
- case *indexExpr:
- return &ast.IndexExpr{X: p.expr(x.X), Index: p.expr(x.Index)}
-
- case *sliceExpr:
- return &ast.SliceExpr{
- X: p.expr(x.X),
- Low: p.expr(x.Lo),
- High: p.expr(x.Hi),
- }
-
- case *callExpr:
- call := &ast.CallExpr{}
- b := x.Fun.evalPartial(p.ctx)
- if b, ok := b.(*builtin); ok {
- call.Fun = p.expr(b)
- } else {
- call.Fun = p.expr(x.Fun)
- }
- for _, a := range x.Args {
- call.Args = append(call.Args, p.expr(a))
- }
- return call
-
- case *customValidator:
- call := ast.NewCall(p.expr(x.Builtin))
- for _, a := range x.Args {
- call.Args = append(call.Args, p.expr(a))
- }
- return call
-
- case *unaryExpr:
- return &ast.UnaryExpr{Op: opMap[x.Op], X: p.expr(x.X)}
-
- case *binaryExpr:
- // opUnifyUnchecked: represented as embedding. The two arguments must
- // be structs.
- if x.Op == opUnifyUnchecked {
- s := ast.NewStruct()
- return p.closeOrOpen(s, p.embedding(s, x))
- }
- return ast.NewBinExpr(opMap[x.Op], p.expr(x.X), p.expr(x.Y))
-
- case *bound:
- return &ast.UnaryExpr{Op: opMap[x.Op], X: p.expr(x.Expr)}
-
- case *unification:
- b := boundSimplifier{p: p}
- vals := make([]evaluated, 0, 3)
- for _, v := range x.Values {
- if !b.add(v) {
- vals = append(vals, v)
- }
- }
- e := b.expr(p.ctx)
- for _, v := range vals {
- e = wrapBin(e, p.expr(v), opUnify)
- }
- return e
-
- case *disjunction:
- if len(x.Values) == 1 {
- return p.expr(x.Values[0].Val)
- }
- expr := func(v dValue) ast.Expr {
- e := p.expr(v.Val)
- if v.Default {
- e = &ast.UnaryExpr{Op: token.MUL, X: e}
- }
- return e
- }
- bin := expr(x.Values[0])
- for _, v := range x.Values[1:] {
- bin = ast.NewBinExpr(token.OR, bin, expr(v))
- }
- return bin
-
- case *closeIfStruct:
- return p.expr(x.value)
-
- case *structLit:
- st, err := p.structure(x, !p.isClosed(x))
- if err != nil {
- return p.expr(err)
- }
- expr := p.closeOrOpen(st, p.isClosed(x))
- switch {
- // If a template is non-nil, we only show it if printing of
- // optional fields is requested. If a struct is not closed it was
- // already generated before. Furthermore, if if we are in evaluation
- // mode, the struct is already unified, so there is no need to print it.
- case p.showOptional() && p.isClosed(x) && !doEval(p.mode):
- if x.optionals == nil {
- break
- }
- p.optionals(len(x.Arcs) > 0, st, x.optionals)
- }
- return expr
-
- case *fieldComprehension:
- panic("should be handled in structLit")
-
- case *listComprehension:
- var clauses []ast.Clause
- for y, next := p.clause(x.clauses); ; y, next = p.clause(next) {
- clauses = append(clauses, y)
- if yield, ok := next.(*yield); ok {
- return &ast.ListComprehension{
- Expr: p.expr(yield.value),
- Clauses: clauses,
- }
- }
- }
-
- case *nullLit:
- return ast.NewNull()
-
- case *boolLit:
- return ast.NewBool(x.B)
-
- case *stringLit:
- return &ast.BasicLit{
- Kind: token.STRING,
- Value: quote(x.Str, '"'),
- }
-
- case *bytesLit:
- return &ast.BasicLit{
- Kind: token.STRING,
- Value: quote(string(x.B), '\''),
- }
-
- case *numLit:
- kind := token.FLOAT
- if x.K&intKind != 0 {
- kind = token.INT
- }
- return &ast.BasicLit{Kind: kind, Value: x.String()}
-
- case *durationLit:
- panic("unimplemented")
-
- case *interpolation:
- t := &ast.Interpolation{}
- multiline := false
- // TODO: mark formatting in interpolation itself.
- for i := 0; i < len(x.Parts); i += 2 {
- str := x.Parts[i].(*stringLit).Str
- if strings.IndexByte(str, '\n') >= 0 {
- multiline = true
- break
- }
- }
- quote := `"`
- if multiline {
- quote = `"""`
- }
- prefix := quote
- suffix := `\(`
- for i, e := range x.Parts {
- if i%2 == 1 {
- t.Elts = append(t.Elts, p.expr(e))
- } else {
- buf := []byte(prefix)
- if i == len(x.Parts)-1 {
- suffix = quote
- }
- str := e.(*stringLit).Str
- if multiline {
- buf = appendEscapeMulti(buf, str, '"')
- } else {
- buf = appendEscaped(buf, str, '"', true)
- }
- buf = append(buf, suffix...)
- t.Elts = append(t.Elts, &ast.BasicLit{
- Kind: token.STRING,
- Value: string(buf),
- })
- }
- prefix = ")"
- }
- return t
-
- case *list:
- list := &ast.ListLit{}
- var expr ast.Expr = list
- for i, a := range x.elem.Arcs {
- if !doEval(p.mode) {
- list.Elts = append(list.Elts, p.expr(a.v))
- } else {
- e := x.elem.at(p.ctx, i)
- list.Elts = append(list.Elts, p.recExpr(a.v, e, false))
- }
- }
- max := maxNum(x.len)
- num, ok := max.(*numLit)
- if !ok {
- min := minNum(x.len)
- num, _ = min.(*numLit)
- }
- ln := 0
- if num != nil {
- x, _ := num.X.Int64()
- ln = int(x)
- }
- open := false
- switch max.(type) {
- case *top, *basicType:
- open = true
- }
- if !ok || ln > len(x.elem.Arcs) {
- list.Elts = append(list.Elts, &ast.Ellipsis{Type: p.expr(x.typ)})
- if !open && !isTop(x.typ) {
- expr = ast.NewBinExpr(
- token.AND,
- ast.NewBinExpr(
- token.MUL,
- p.expr(x.len),
- ast.NewList(p.expr(x.typ))),
- list,
- )
-
- }
- }
- return expr
-
- case *bottom:
- err := &ast.BottomLit{}
- if x.format != "" {
- msg := x.msg()
- if len(x.sub) > 0 {
- buf := strings.Builder{}
- for i, b := range x.sub {
- if i > 0 {
- buf.WriteString("; ")
- buf.WriteString(b.msg())
- }
- }
- msg = buf.String()
- }
- comment := &ast.Comment{Text: "// " + msg}
- err.AddComment(&ast.CommentGroup{
- Line: true,
- Position: 2,
- List: []*ast.Comment{comment},
- })
- }
- return err
-
- case *top:
- return p.ident("_")
-
- case *basicType:
- return p.ident(x.K.String())
-
- case *lambdaExpr:
- return p.ident("TODO: LAMBDA")
-
- default:
- panic(fmt.Sprintf("unimplemented type %T", x))
- }
-}
-
-func (p *exporter) optionalsExpr(x *optionals, isClosed bool) ast.Expr {
- st := ast.NewStruct()
- // An empty struct has meaning in case of closed structs, where they
- // indicate no other fields may be added. Non-closed empty structs should
- // have been optimized away. In case they are not, it is just a no-op.
- if x != nil {
- p.optionals(false, st, x)
- }
- if isClosed {
- return ast.NewCall(ast.NewIdent("close"), st)
- }
- return st
-}
-
-func (p *exporter) optionals(wrap bool, st *ast.StructLit, x *optionals) (skippedEllipsis bool) {
- wrap = wrap || len(x.fields) > 1
- switch x.op {
- default:
- for _, t := range x.fields {
- l, ok := t.value.evalPartial(p.ctx).(*lambdaExpr)
- if !ok {
- // Really should not happen.
- continue
- }
- v := l.value
- if c, ok := v.(*closeIfStruct); ok {
- v = c.value
- }
- f := &ast.Field{
- Label: p.mkTemplate(t.key, p.identifier(l.params.arcs[0].Label)),
- Value: p.expr(l.value),
- }
- if internal.IsEllipsis(f) {
- skippedEllipsis = true
- continue
- }
- if !wrap {
- st.Elts = append(st.Elts, f)
- continue
- }
- st.Elts = append(st.Elts, internal.EmbedStruct(ast.NewStruct(f)))
- }
-
- case opUnify:
- // Optional constraints added with normal unification are embedded as an
- // expression. This relies on the fact that a struct embedding a closed
- // struct will itself be closed.
- st.Elts = append(st.Elts, &ast.EmbedDecl{Expr: &ast.BinaryExpr{
- X: p.optionalsExpr(x.left, x.left.isClosed()),
- Op: token.AND,
- Y: p.optionalsExpr(x.right, x.right.isClosed()),
- }})
-
- case opUnifyUnchecked:
- // Constraints added with unchecked unification are embedded
- // individually. It doesn't matter here whether this originated from
- // regular unification of open structs or embedded closed structs.
- // The result in each case is unchecked unification.
- left := p.optionalsExpr(x.left, false)
- right := p.optionalsExpr(x.right, false)
- st.Elts = append(st.Elts, &ast.EmbedDecl{Expr: left})
- st.Elts = append(st.Elts, &ast.EmbedDecl{Expr: right})
- }
- return skippedEllipsis
-}
-
-func (p *exporter) structure(x *structLit, addTempl bool) (ret *ast.StructLit, err *bottom) {
- obj := ast.NewStruct()
- if doEval(p.mode) {
- x, err = x.expandFields(p.ctx)
- if err != nil {
- return nil, err
- }
- }
-
- for _, a := range x.Arcs {
- p.stack = append(p.stack, remap{
- key: x,
- from: a.Label,
- to: nil,
- syn: obj,
- })
- }
- if x.emit != nil {
- obj.Elts = append(obj.Elts, &ast.EmbedDecl{Expr: p.expr(x.emit)})
- }
- hasEllipsis := false
- if p.showOptional() && x.optionals != nil &&
- // Optional field constraints may be omitted if they were already
- // applied and no more new fields may be added.
- !(doEval(p.mode) && x.optionals.isEmpty() && p.isClosed(x)) {
- hasEllipsis = p.optionals(len(x.Arcs) > 0, obj, x.optionals)
- }
- for i, a := range x.Arcs {
- f := &ast.Field{
- Label: p.label(a.Label),
- }
- // TODO: allow the removal of hidden fields. However, hidden fields
- // that still used in incomplete expressions should not be removed
- // (unless RequireConcrete is requested).
- if a.optional {
- // Optional fields are almost never concrete. We omit them in
- // concrete mode to allow the user to use the -a option in eval
- // without getting many errors.
- if p.mode.omitOptional || p.mode.concrete {
- continue
- }
- f.Optional = token.NoSpace.Pos()
- }
- if a.definition {
- if p.mode.omitDefinitions || p.mode.concrete {
- continue
- }
- if !internal.IsDefinition(f.Label) {
- f.Token = token.ISA
- }
- }
- if a.Label.IsHidden() && p.mode.concrete && p.mode.omitHidden {
- continue
- }
- oldInDef := p.inDef
- p.inDef = a.definition || p.inDef
- if !doEval(p.mode) {
- f.Value = p.expr(a.v)
- } else {
- f.Value = p.recExpr(a.v, x.at(p.ctx, i), a.optional)
- }
- p.inDef = oldInDef
- if a.attrs != nil && !p.mode.omitAttrs {
- for _, at := range a.attrs.attr {
- f.Attrs = append(f.Attrs, &ast.Attribute{Text: at.text})
- }
- }
- if p.mode.docs {
- for _, d := range a.docs.appendDocs(nil) {
- ast.AddComment(f, d)
- break
- }
- }
- obj.Elts = append(obj.Elts, f)
- }
-
- if !p.mode.concrete {
- for _, v := range x.comprehensions {
- switch c := v.comp.(type) {
- case *fieldComprehension:
- l := p.expr(c.key)
- label, _ := l.(ast.Label)
- opt := token.NoPos
- if c.opt {
- opt = token.NoSpace.Pos() // anything but token.NoPos
- }
- tok := token.COLON
- if c.def && !internal.IsDefinition(label) {
- tok = token.ISA
- }
- f := &ast.Field{
- Label: label,
- Optional: opt,
- Token: tok,
- Value: p.expr(c.val),
- }
- obj.Elts = append(obj.Elts, f)
-
- case *structComprehension:
- var clauses []ast.Clause
- next := c.clauses
- for {
- if yield, ok := next.(*yield); ok {
- obj.Elts = append(obj.Elts, &ast.Comprehension{
- Clauses: clauses,
- Value: p.expr(yield.value),
- })
- break
- }
-
- var y ast.Clause
- y, next = p.clause(next)
- clauses = append(clauses, y)
- }
- }
- }
- }
-
- if hasEllipsis {
- obj.Elts = append(obj.Elts, &ast.Ellipsis{})
- }
- return obj, nil
-}
-
-func hasBulk(a []ast.Decl) bool {
- for _, d := range a {
- if internal.IsBulkField(d) {
- return true
- }
- }
- return false
-}
-
-func (p *exporter) embedding(s *ast.StructLit, n value) (closed bool) {
- switch x := n.(type) {
- case *structLit:
- st, err := p.structure(x, true)
- if err != nil {
- n = err
- break
- }
- if hasBulk(st.Elts) {
- s.Elts = append(s.Elts, internal.EmbedStruct(st))
- } else {
- s.Elts = append(s.Elts, st.Elts...)
- }
- return p.isClosed(x)
-
- case *binaryExpr:
- if x.Op != opUnifyUnchecked {
- // should not happen
- s.Elts = append(s.Elts, &ast.EmbedDecl{Expr: p.expr(x)})
- return false
- }
- leftClosed := p.embedding(s, x.X)
- rightClosed := p.embedding(s, x.Y)
- return leftClosed || rightClosed
- }
- s.Elts = append(s.Elts, &ast.EmbedDecl{Expr: p.expr(n)})
- return false
-}
-
-// quote quotes the given string.
-func quote(str string, quote byte) string {
- if strings.IndexByte(str, '\n') < 0 {
- buf := []byte{quote}
- buf = appendEscaped(buf, str, quote, true)
- buf = append(buf, quote)
- return string(buf)
- }
- buf := []byte{quote, quote, quote}
- buf = append(buf, multiSep...)
- buf = appendEscapeMulti(buf, str, quote)
- buf = append(buf, quote, quote, quote)
- return string(buf)
-}
-
-// TODO: consider the best indent strategy.
-const multiSep = "\n "
-
-func appendEscapeMulti(buf []byte, str string, quote byte) []byte {
- // TODO(perf)
- a := strings.Split(str, "\n")
- for _, s := range a {
- buf = appendEscaped(buf, s, quote, true)
- buf = append(buf, multiSep...)
- }
- return buf
-}
-
-const lowerhex = "0123456789abcdef"
-
-func appendEscaped(buf []byte, s string, quote byte, graphicOnly bool) []byte {
- for width := 0; len(s) > 0; s = s[width:] {
- r := rune(s[0])
- width = 1
- if r >= utf8.RuneSelf {
- r, width = utf8.DecodeRuneInString(s)
- }
- if width == 1 && r == utf8.RuneError {
- buf = append(buf, `\x`...)
- buf = append(buf, lowerhex[s[0]>>4])
- buf = append(buf, lowerhex[s[0]&0xF])
- continue
- }
- buf = appendEscapedRune(buf, r, quote, graphicOnly)
- }
- return buf
-}
-
-func appendEscapedRune(buf []byte, r rune, quote byte, graphicOnly bool) []byte {
- var runeTmp [utf8.UTFMax]byte
- if r == rune(quote) || r == '\\' { // always backslashed
- buf = append(buf, '\\')
- buf = append(buf, byte(r))
- return buf
- }
- // TODO(perf): IsGraphic calls IsPrint.
- if strconv.IsPrint(r) || graphicOnly && strconv.IsGraphic(r) {
- n := utf8.EncodeRune(runeTmp[:], r)
- buf = append(buf, runeTmp[:n]...)
- return buf
- }
- switch r {
- case '\a':
- buf = append(buf, `\a`...)
- case '\b':
- buf = append(buf, `\b`...)
- case '\f':
- buf = append(buf, `\f`...)
- case '\n':
- buf = append(buf, `\n`...)
- case '\r':
- buf = append(buf, `\r`...)
- case '\t':
- buf = append(buf, `\t`...)
- case '\v':
- buf = append(buf, `\v`...)
- default:
- switch {
- case r < ' ':
- // Invalid for strings, only bytes.
- buf = append(buf, `\x`...)
- buf = append(buf, lowerhex[byte(r)>>4])
- buf = append(buf, lowerhex[byte(r)&0xF])
- case r > utf8.MaxRune:
- r = 0xFFFD
- fallthrough
- case r < 0x10000:
- buf = append(buf, `\u`...)
- for s := 12; s >= 0; s -= 4 {
- buf = append(buf, lowerhex[r>>uint(s)&0xF])
- }
- default:
- buf = append(buf, `\U`...)
- for s := 28; s >= 0; s -= 4 {
- buf = append(buf, lowerhex[r>>uint(s)&0xF])
- }
- }
- }
- return buf
-}
-
-type boundSimplifier struct {
- p *exporter
-
- isInt bool
- min *bound
- minNum *numLit
- max *bound
- maxNum *numLit
-}
-
-func (s *boundSimplifier) add(v value) (used bool) {
- switch x := v.(type) {
- case *basicType:
- switch x.K & scalarKinds {
- case intKind:
- s.isInt = true
- return true
- }
-
- case *bound:
- if x.k&concreteKind == intKind {
- s.isInt = true
- }
- switch x.Op {
- case opGtr:
- if n, ok := x.Expr.(*numLit); ok {
- if s.min == nil || s.minNum.X.Cmp(&n.X) != 1 {
- s.min = x
- s.minNum = n
- }
- return true
- }
-
- case opGeq:
- if n, ok := x.Expr.(*numLit); ok {
- if s.min == nil || s.minNum.X.Cmp(&n.X) == -1 {
- s.min = x
- s.minNum = n
- }
- return true
- }
-
- case opLss:
- if n, ok := x.Expr.(*numLit); ok {
- if s.max == nil || s.maxNum.X.Cmp(&n.X) != -1 {
- s.max = x
- s.maxNum = n
- }
- return true
- }
-
- case opLeq:
- if n, ok := x.Expr.(*numLit); ok {
- if s.max == nil || s.maxNum.X.Cmp(&n.X) == 1 {
- s.max = x
- s.maxNum = n
- }
- return true
- }
- }
- }
-
- return false
-}
-
-type builtinRange struct {
- typ string
- lo *apd.Decimal
- hi *apd.Decimal
-}
-
-func makeDec(s string) *apd.Decimal {
- d, _, err := apd.NewFromString(s)
- if err != nil {
- panic(err)
- }
- return d
-}
-
-func (s *boundSimplifier) expr(ctx *context) (e ast.Expr) {
- if s.min == nil || s.max == nil {
- return nil
- }
- switch {
- case s.isInt:
- t := s.matchRange(intRanges)
- if t != "" {
- e = ast.NewIdent(t)
- break
- }
- if sign := s.minNum.X.Sign(); sign == -1 {
- e = ast.NewIdent("int")
-
- } else {
- e = ast.NewIdent("uint")
- if sign == 0 && s.min.Op == opGeq {
- s.min = nil
- break
- }
- }
- fallthrough
- default:
- t := s.matchRange(floatRanges)
- if t != "" {
- e = wrapBin(e, ast.NewIdent(t), opUnify)
- }
- }
-
- if s.min != nil {
- e = wrapBin(e, s.p.expr(s.min), opUnify)
- }
- if s.max != nil {
- e = wrapBin(e, s.p.expr(s.max), opUnify)
- }
- return e
-}
-
-func (s *boundSimplifier) matchRange(ranges []builtinRange) (t string) {
- for _, r := range ranges {
- if !s.minNum.X.IsZero() && s.min.Op == opGeq && s.minNum.X.Cmp(r.lo) == 0 {
- switch s.maxNum.X.Cmp(r.hi) {
- case 0:
- if s.max.Op == opLeq {
- s.max = nil
- }
- s.min = nil
- return r.typ
- case -1:
- if !s.minNum.X.IsZero() {
- s.min = nil
- return r.typ
- }
- case 1:
- }
- } else if s.max.Op == opLeq && s.maxNum.X.Cmp(r.hi) == 0 {
- switch s.minNum.X.Cmp(r.lo) {
- case -1:
- case 0:
- if s.min.Op == opGeq {
- s.min = nil
- }
- fallthrough
- case 1:
- s.max = nil
- return r.typ
- }
- }
- }
- return ""
-}
-
-var intRanges = []builtinRange{
- {"int8", makeDec("-128"), makeDec("127")},
- {"int16", makeDec("-32768"), makeDec("32767")},
- {"int32", makeDec("-2147483648"), makeDec("2147483647")},
- {"int64", makeDec("-9223372036854775808"), makeDec("9223372036854775807")},
- {"int128", makeDec("-170141183460469231731687303715884105728"),
- makeDec("170141183460469231731687303715884105727")},
-
- {"uint8", makeDec("0"), makeDec("255")},
- {"uint16", makeDec("0"), makeDec("65535")},
- {"uint32", makeDec("0"), makeDec("4294967295")},
- {"uint64", makeDec("0"), makeDec("18446744073709551615")},
- {"uint128", makeDec("0"), makeDec("340282366920938463463374607431768211455")},
-
- // {"rune", makeDec("0"), makeDec(strconv.Itoa(0x10FFFF))},
-}
-
-var floatRanges = []builtinRange{
- // 2**127 * (2**24 - 1) / 2**23
- {"float32",
- makeDec("-3.40282346638528859811704183484516925440e+38"),
- makeDec("+3.40282346638528859811704183484516925440e+38")},
-
- // 2**1023 * (2**53 - 1) / 2**52
- {"float64",
- makeDec("-1.797693134862315708145274237317043567981e+308"),
- makeDec("+1.797693134862315708145274237317043567981e+308")},
-}
-
-func wrapBin(a, b ast.Expr, op op) ast.Expr {
- if a == nil {
- return b
- }
- if b == nil {
- return a
- }
- return ast.NewBinExpr(opMap[op], a, b)
-}
diff --git a/cue/export_test.go b/cue/export_test.go
deleted file mode 100644
index 96771bb..0000000
--- a/cue/export_test.go
+++ /dev/null
@@ -1,1205 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "log"
- "strings"
- "testing"
-
- "cuelang.org/go/cue/format"
-)
-
-func TestExport(t *testing.T) {
- // Do not inline: the named struct is used as a marker in
- // testdata/gen.go.
- type exportTest struct {
- raw bool // skip evaluation the root, fully raw
- eval bool // evaluate the full export
- noOpt bool
- in, out string
- }
- testCases := []exportTest{{
- in: `"hello"`,
- out: `"hello"`,
- }, {
- in: `'hello'`,
- out: `'hello'`,
- }, {
- in: `'hello\nworld'`,
- out: "'''" +
- multiSep + "hello" +
- multiSep + "world" +
- multiSep + "'''",
- }, {
- in: `"hello\nworld"`,
- out: `"""` +
- multiSep + "hello" +
- multiSep + "world" +
- multiSep + `"""`,
- }, {
- in: `{
- $type: 3
- "_": int
- "_foo": int
- _bar: int
- }`,
- out: unindent(`
- {
- $type: 3
- "_": int
- "_foo": int
- _bar: int
- }`),
- }, {
- in: "{ a: 1, b: a + 2, c: null, d: true, e: _, f: string }",
- out: unindent(`
- {
- a: 1
- b: 3
- c: null
- d: true
- e: _
- f: string
- }`),
- }, {
- // Here the failed lookups are not considered permanent
- // failures, as the structs are open.
- in: `{ a: { b: 2.0, s: "abc" }, b: a.b, c: a.c, d: a["d"], e: a.t[2:3] }`,
- out: unindent(`
- {
- a: {
- b: 2.0
- s: "abc"
- }
- b: 2.0
- c: a.c
- d: a["d"]
- e: a.t[2:3]
- }`),
- }, {
- // Here the failed lookups are permanent failures as the structs are
- // closed.
- in: `{ #a: { b: 2.0, s: "abc" }, b: #a.b, c: #a.c, d: #a["d"], e: #a.t[2:3] }`,
- out: unindent(`
- {
- #a: {
- b: 2.0
- s: "abc"
- }
- b: 2.0
- c: _|_ // undefined field "c"
- d: _|_ // undefined field "d"
- e: _|_ // undefined field "t"
- }`),
- }, {
- // Insert comma between error and inserted message.
- in: `{ a: [ 3&4] }`,
- out: unindent(`
- {
- a: [_|_, // conflicting values 3 and 4
- ]
- }`),
- }, {
- in: `{
- a: 5*[int]
- a: [1, 2, ...]
- b: <=5*[int]
- b: [1, 2, ...]
- c: (>=3 & <=5)*[int]
- c: [1, 2, ...]
- d: >=2*[int]
- d: [1, 2, ...]
- e: [...int]
- e: [1, 2, ...]
- f: [1, 2, ...]
- }`,
- out: unindent(`
- {
- a: [1, 2, int, int, int]
- b: <=5*[int] & [1, 2, ...]
- c: (>=3 & <=5)*[int] & [1, 2, ...]
- d: >=2*[int] & [1, 2, ...]
- e: [1, 2]
- f: [1, 2]
- }`),
- }, {
- raw: true,
- in: `{
- a: 5*[int]
- a: [1, 2, ...]
- b: <=5*[int]
- b: [1, 2, ...]
- c: (>=3 & <=5)*[int]
- c: [1, 2, ...]
- d: >=2*[int]
- d: [1, 2, ...]
- e: [...int]
- e: [1, 2, ...]
- f: [1, 2, ...]
- }`,
- out: unindent(`
- {
- a: 5*[int] & [1, 2, ...]
- b: <=5*[int] & [1, 2, ...]
- c: (>=3 & <=5)*[int] & [1, 2, ...]
- d: >=2*[int] & [1, 2, ...]
- e: [...int] & [1, 2, ...]
- f: [1, 2, ...]
- }`),
- }, {
- raw: true,
- in: `{ a: { b: [] }, c: a.b, d: a["b"] }`,
- out: unindent(`
- {
- a: b: []
- c: a.b
- d: a["b"]
- }`),
- }, {
- raw: true,
- in: `{ a: *"foo" | *"bar" | *string | int, b: a[2:3] }`,
- out: unindent(`
- {
- a: *"foo" | *"bar" | *string | int
- b: a[2:3]
- }`),
- }, {
- in: `{
- a: >=0 & <=10 & !=1
- }`,
- out: unindent(`
- {
- a: >=0 & <=10 & !=1
- }`),
- }, {
- raw: true,
- in: `{
- a: >=0 & <=10 & !=1
- }`,
- out: unindent(`
- {
- a: >=0 & <=10 & !=1
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- a: (*1 | 2) & (1 | *2)
- b: [(*1 | 2) & (1 | *2)]
- }`,
- out: unindent(`
- {
- a: 1 | 2 | *_|_
- b: [1 | 2 | *_|_]
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- u16: int & >=0 & <=65535
- u32: uint32
- u64: uint64
- u128: uint128
- u8: uint8
- ua: uint16 & >0
- us: >=0 & <10_000 & int
- i16: >=-32768 & int & <=32767
- i32: int32 & > 0
- i64: int64
- i128: int128
- f64: float64
- fi: float64 & int
- }`,
- out: unindent(`
- {
- u16: uint16
- u32: uint32
- u64: uint64
- u128: uint128
- u8: uint8
- ua: uint16 & >0
- us: uint & <10000
- i16: int16
- i32: int32 & >0
- i64: int64
- i128: int128
- f64: float64
- fi: int & float64
- }`),
- }, {
- raw: true,
- in: `{ a: [1, 2], b: { for k, v in a if v > 1 { "\(k)": v } } }`,
- out: unindent(`
- {
- a: [1, 2]
- b: {
- for k, v in a if v > 1 {
- "\(k)": v
- }
- }
- }`),
- }, {
- raw: true,
- in: `{ a: [1, 2], b: [ for k, v in a { v }] }`,
- out: unindent(`
- {
- a: [1, 2]
- b: [ for k, v in a { v } ]
- }`),
- }, {
- raw: true,
- in: `{ a: >=0 & <=10, b: "Count: \(a) times" }`,
- out: unindent(`
- {
- a: >=0 & <=10
- b: "Count: \(a) times"
- }`),
- }, {
- raw: true,
- in: `{ a: "", b: len(a) }`,
- out: unindent(`
- {
- a: ""
- b: len(a)
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- b: {
- idx: a[str]
- str: string
- }
- b: a: b: 4
- a: b: 3
- }`,
- // reference to a must be redirected to outer a through alias
- out: unindent(`
- {
- A = a
- b: {
- idx: A[str]
- a: b: 4
- str: string
- }
- a: b: 3
- }`),
- }, {
- raw: true,
- eval: true,
- noOpt: true,
- in: `{
- job: [Name=_]: {
- name: Name
- replicas: uint | *1 @protobuf(10)
- command: string
- }
-
- job: list: command: "ls"
-
- job: nginx: {
- command: "nginx"
- replicas: 2
- }
- }`,
- out: unindent(`
- {
- job: {
- list: {
- name: "list"
- replicas: >=0 | *1 @protobuf(10)
- command: "ls"
- }
- nginx: {
- name: "nginx"
- replicas: 2 @protobuf(10)
- command: "nginx"
- }
- }
- }`),
- }, {
- // TODO: positions of embedded structs is not preserved. Use some kind
- // of topological sort to preserve order.
- raw: true,
- in: `{
- #emb: {
- a: 1
-
- sub: {
- f: 3
- }
- }
- #def: {
- #emb
-
- b: 2
- }
- #f: a: 10
- #e: {
- #f
-
- b: int
- [_]: <100
- {[_]: <300}
- }
- }`,
- out: unindent(`
- {
- #emb: {
- a: 1
- sub: f: 3
- }
- #def: {
- b: 2
- #emb
- }
- #f: {
- a: 10
- }
- #e: {
- {[string]: <100}
- b: int
- #f
- {[string]: <300}
- }
- }`),
- }, {
- raw: true,
- eval: true,
- noOpt: true,
- in: `{
- reg: { foo: 1, bar: { baz: 3 } }
- #def: {
- a: 1
-
- sub: reg
- }
- val: #def
- #def2: {
- a: { b: int }
- }
- val2: #def2
- }`,
- out: unindent(`
- {
- reg: {
- foo: 1
- bar: baz: 3
- }
- #def: {
- a: 1
- sub: reg
- }
- val: #def
- #def2: {
- a: b: int
- }
- val2: #def2
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- b: [{
- [X=_]: int
- if a > 4 {
- f: 4
- }
- }][a]
- a: int
- c: *1 | 2
- }`,
- // reference to a must be redirected to outer a through alias
- out: unindent(`
- {
- b: [{
- [X=string]: int
- if a > 4 {
- f: 4
- }
- }][a]
- a: int
- c: *1 | 2
- }`),
- }, {
- raw: true,
- in: `{
- if false {
- { a: 1 } | { b: 1 }
- }
- }`,
- // reference to a must be redirected to outer a through alias
- out: unindent(`
- {
- if false {
- {
- a: 1
- } | {
- b: 1
- }
- }
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- #Foo: {
- #Bar: #Foo | string
- }
- }`,
- out: unindent(`
- {
- #Foo: {
- #Bar: #Foo | string
- }
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- #FindInMap: {
- #: "Fn::FindInMap": [string | #FindInMap]
- }
- a: [...string]
- }`,
- out: unindent(`
- {
- #FindInMap: {
- #: {
- "Fn::FindInMap": [string | #FindInMap]
- }
- }
- a: [...string]
- }`),
- }, {
- raw: true,
- eval: true,
- noOpt: true,
- in: `{
- #And: {
- #: "Fn::And": [...(3 | #And)]
- }
- #Ands: #And & {
- #: "Fn::And" : [_]
- }
- }`,
- out: unindent(`
- {
- #And: {
- #: {
- "Fn::And": [...3 | #And]
- }
- }
- #Ands: #And & {
- #: {
- "Fn::And": [_]
- }
- }
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- #Foo: {
- sgl: #Bar
- ref: null | #Foo
- ext: #Bar | null
- ref: null | #Foo
- ref2: null | #Foo.sgl
- ...
- }
- #Foo: {
- "#Foo": 2
- ...
- }
- #Bar: string
- }`,
- out: unindent(`
- {
- #Foo: {
- "#Foo": 2
- sgl: #Bar
- ref: (null | #Foo) & (null | #Foo)
- ext: #Bar | null
- ref2: null | #Foo.sgl
- ...
- }
- #Bar: string
- }`),
- }, {
- raw: true,
- eval: true,
- in: `{
- A: [uint]
- B: A & ([10] | [192])
- }`,
- out: unindent(`
- {
- A: [>=0]
- B: A & ([10] | [192])
- }`),
- }, {
- in: `{
- [string]: _
- foo: 3
- }`,
- out: unindent(`
- {
- foo: 3
- ...
- }`),
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- body := fmt.Sprintf("Test: %s", tc.in)
- ctx, obj := compileFile(t, body)
- ctx.trace = *traceOn
- var root value = obj
- if !tc.raw {
- root = testResolve(ctx, obj, evalFull)
- }
- t.Log(debugStr(ctx, root))
-
- n := root.(*structLit).Arcs[0].v
- v := newValueRoot(ctx, n)
-
- opts := options{raw: !tc.eval, omitOptional: tc.noOpt}
- node, _ := export(ctx, nil, v.eval(ctx), opts)
- b, err := format.Node(node, format.Simplify())
- if err != nil {
- log.Fatal(err)
- }
- if got := string(b); got != tc.out {
- t.Errorf("\ngot %v;\nwant %v", got, tc.out)
- }
- })
- }
-}
-
-func TestExportFile(t *testing.T) {
- testCases := []struct {
- eval bool // evaluate the full export
- in, out string
- opts []Option
- }{{
- eval: true,
- opts: []Option{Docs(true)},
- in: `
- // Hello foo
- package foo
-
- import "strings"
-
- a: strings.ContainsAny("c")
- `,
- out: unindent(`
- // Hello foo
- package foo
-
- import "strings"
-
- a: strings.ContainsAny("c")`),
- }, {
- eval: true,
- in: `
- // Drop this comment
- package foo
-
- import "time"
-
- a: time.Time
- `,
- out: unindent(`
- package foo
-
- import "time"
-
- a: time.Time`),
- }, {
- in: `
- import "time"
-
- {
- a: time.Time
- } & {
- time: int
- } `,
- out: unindent(`
- import timex "time"
-
- time: int
- a: timex.Time`),
- }, {
- in: `
- import time2 "time"
-
- a: time2.Time`,
- out: unindent(`
- import "time"
-
- a: time.Time`),
- }, {
- in: `
- import time2 "time"
-
- time: int
- a: time2.Time`,
- out: unindent(`
- import time2 "time"
-
- time: int
- a: time2.Time`),
- }, {
- in: `
- import "strings"
-
- a: strings.TrimSpace(" c ")
- `,
- out: unindent(`
- import "strings"
-
- a: strings.TrimSpace(" c ")`),
- }, {
- in: `
- import "strings"
-
- let stringsx = strings
-
- a: {
- strings: stringsx.ContainsAny("c")
- }
- `,
- out: unindent(`
- import "strings"
-
- let STRINGS = strings
- a: strings: STRINGS.ContainsAny("c")`),
- }, {
- in: `
- a: b - 100
- b: a + 100
- `,
- out: unindent(`
- a: b - 100
- b: a + 100`),
- }, {
- in: `A: {
- [_]: B
- } @protobuf(1,"test")
-
- B: {}
- B: {a: int} | {b: int}
-
- #C: [#D]: int
- #D: string
- `,
- out: unindent(`
- A: {
- [string]: B
- } @protobuf(1,"test")
- B: {} & ({
- a: int
- } | {
- b: int
- })
- #C: {
- [#D]: int
- [#D]: int
- }
- #D: string`),
- }, {
- in: `
- a: {
- foo: 3
- [=~"foo"]: int
- }
- `,
- out: unindent(`
- a: {
- {[=~"foo"]: int}
- foo: 3
- }`),
- }, {
- in: `
- import "time"
-
- a: { b: time.Duration } | { c: time.Duration }
- `,
- out: unindent(`
- import "time"
-
- a: {
- b: time.Duration
- } | {
- c: time.Duration
- }`),
- }, {
- // a closed struct unified with a struct with a template restrictions is
- // exported as a conjunction of two structs.
- eval: true,
- opts: []Option{ResolveReferences(true)},
- in: `
- #A: { b: int }
- a: #A & { [string]: <10 }
- #B: a
- `,
- out: unindent(`
- #A: {
- b: int
- }
- a: close({
- b: <10
- })
- #B: {
- b: <10
- }`),
- }, {
- eval: true,
- opts: []Option{Final()},
- in: `{
- reg: { foo: 1, bar: { baz: 3 } }
- #def: {
- a: 1
-
- sub: reg
- }
- val: #def
- }`,
- out: unindent(`
- {
- reg: {
- foo: 1
- bar: baz: 3
- }
- val: {
- a: 1
- sub: {
- foo: 1
- bar: baz: 3
- }
- }
- }`),
- }, {
- eval: true,
- opts: []Option{ResolveReferences(true)},
- in: `
- #T: {
- [_]: int64
- }
- #X: {
- x: int
- } & #T
- x: #X
- `,
- out: unindent(`
- x: {
- {[string]: int64}
- x: int64
- }
- #T: {
- [string]: int64
- }
- #X: {
- {[string]: int64}
- x: int64
- }`),
- }, {
- eval: true,
- opts: []Option{Optional(false), ResolveReferences(true)},
- in: `
- #T: {
- [_]: int64
- }
- #X: {
- x: int
- } & #T
- x: #X
- `,
- out: unindent(`
- x: x: int64
- #T: {}
- #X: {
- x: int64
- }`),
- }, {
- eval: true,
- opts: []Option{ResolveReferences(true)},
- in: `{
- reg: { foo: 1, bar: { baz: 3 } }
- #def: {
- a: 1
-
- sub: reg
- }
- val: #def
- #def2: {
- a: { b: int }
- }
- val2: #def2
- }`,
- out: unindent(`
- {
- reg: {
- foo: 1
- bar: baz: 3
- }
- #def: {
- a: 1
- sub: {
- foo: 1
- bar: {
- baz: 3
- ...
- }
- ...
- }
- }
- val: close({
- a: 1
- sub: {
- foo: 1
- bar: baz: 3
- }
- })
- #def2: {
- a: b: int
- }
- val2: close({
- a: close({
- b: int
- })
- })
- }`),
- }, {
- eval: true,
- in: `
- a?: 1
- b?: 2
- b?: 2
- c?: 3
- c: 3`,
- out: unindent(`
- a?: 1
- b?: 2
- c: 3`),
- }, {
- eval: true,
- opts: []Option{ResolveReferences(true)},
- in: `
- #A: {
- [=~"^[a-s]*$"]: int
- }
- #B: {
- [=~"^[m-z]+"]: int
- }
- C: {#A & #B}
- #D: {#A & #B}
- `,
- // TODO: the outer close of C could be optimized away.
- out: unindent(`
- #A: {
- [=~"^[a-s]*$"]: int
- }
- #B: {
- [=~"^[m-z]+"]: int
- }
- C: close({
- close({
- [=~"^[a-s]*$"]: int
- }) & close({
- [=~"^[m-z]+"]: int
- })
- })
- #D: {
- close({
- [=~"^[a-s]*$"]: int
- }) & close({
- [=~"^[m-z]+"]: int
- })
- }`),
- }, {
- eval: true,
- opts: []Option{Docs(true)},
- in: `
- // Definition
- #A: {
- // TODO: support
- [string]: int
- }
- // Field
- a: #A
-
- // Pick first comment only.
- a: _
- `,
- out: unindent(`
- // Definition
- #A: {
- [string]: int
- }
-
- // Field
- a: #A`),
- }, {
- eval: true,
- opts: []Option{Docs(true)},
- // It is okay to allow bulk-optional fields along-side definitions.
- in: `
- "#A": {
- {[string]: int}
- "#B": 4
- }
- // Definition
- #A: {
- {[string]: int}
- #B: 4
- }
- a: #A.#B
- `,
- out: unindent(`
- "#A": {
- {[string]: int}
- "#B": 4
- }
- // Definition
- #A: {
- {[string]: int}
- #B: 4
- }
- a: 4`),
- }, {
- in: `
- #A: {
- #B: 4
- }
- a: #A.#B
- `,
- out: unindent(`
- #A: {
- #B: 4
- }
- a: #A.#B`),
- }, {
- eval: true,
- in: `
- x: [string]: int
- a: [P=string]: {
- b: x[P]
- c: P
- e: len(P)
- }
- `,
- out: unindent(`
- x: [string]: int
- a: [P=string]: {
- b: x[P]
- c: P
- e: len(P)
- }`),
- }, {
- eval: true,
- in: `
- list: [...string]
- foo: 1 | 2 | *3
- foo: int
- `,
- out: unindent(`
- list: [...string]
- foo: 1 | 2 | *3`),
- }, {
- eval: true,
- opts: []Option{Final()},
- in: `
- list: [...string]
- foo: 1 | 2 | *3
- foo: int
- `,
- out: unindent(`
- {
- list: []
- foo: 3
- }`),
- }, {
- // Expand final values, not values that may still be incomplete.
- eval: true,
- in: `
- import "math"
- import "tool/exec"
-
- #A: {
- b: 3
- }
-
- a: #A
- pi: math.Pi
- run: exec.Run
- `,
- out: unindent(`
- import "tool/exec"
-
- #A: {
- b: 3
- }
- a: #A
- pi: 3.14159265358979323846264338327950288419716939937510582097494459
- run: exec.Run`),
- }, {
- // Change mode midway to break cycle.
- eval: true,
- opts: []Option{Final(), Optional(true)},
- in: `
- Foo: {
- foo?: Foo
- bar: string
- baz: bar + "2"
- }
-
- foo: Foo & {
- foo: {
- bar: "barNested"
- }
- bar: "barParent"
- }`,
- out: unindent(`
- {
- Foo: {
- foo?: Foo
- bar: string
- baz: bar + "2"
- }
- foo: {
- foo: {
- foo?: Foo
- bar: "barNested"
- baz: "barNested2"
- }
- bar: "barParent"
- baz: "barParent2"
- }
- }`),
- }, {
- eval: true,
- opts: []Option{Final(), Definitions(true)},
- in: `
- package tst
-
- x: {
- a: #A
- b: #A
- }
- #A: string | [#A]
-
- `,
- out: unindent(`
- {
- x: {
- a: #A
- b: #A
- }
- #A: string | [#A]
- }`),
- }, {
- eval: true,
- opts: []Option{Definitions(true)},
- in: `
- body?: {
- a: int
- b?: string
- }
- `,
- out: unindent(`
- body?: {
- a: int
- b?: string
- }`),
- }, {
- // Drop comprehensions in final mode.
- eval: true,
- opts: []Option{Final(), Concrete(true)},
- in: `
- foo: _
- a: {
- if len(foo.bar) > 0 {
- command: ["envoy-lifecycle", string]
- }
- }
- `,
- out: unindent(`
- {
- foo: _
- a: {}
- }`),
- }, {
- // Don't output hidden fields in concrete and final mode.
- eval: true,
- opts: []Option{Final(), Concrete(true)},
- in: `
- _foo: _
- `,
- out: `{}`,
- }, {
- eval: true,
- in: `
- #A: {
- foo: int @tag2(2)
- } @tag2(1)
-
- #A: {
- foo: 2 @tag(2)
- } @tag(1)
-
- `,
- out: `#A: {
- foo: 2 @tag(2) @tag2(2)
-} @tag(1) @tag2(1)`,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- var r Runtime
- inst, err := r.Compile("test", tc.in)
- if err != nil {
- t.Fatal(err)
- }
- opts := tc.opts
- if !tc.eval {
- opts = []Option{Raw()}
- }
- b, err := format.Node(inst.Value().Syntax(opts...), format.Simplify())
- if err != nil {
- log.Fatal(err)
- }
- if got := strings.TrimSpace(string(b)); got != tc.out {
- t.Errorf("\ngot:\n%v\nwant:\n%v", got, tc.out)
- }
- })
- }
-}
-
-func unindent(s string) string {
- lines := strings.Split(s, "\n")[1:]
- ws := lines[0][:len(lines[0])-len(strings.TrimLeft(lines[0], " \t"))]
- for i, s := range lines {
- if s == "" {
- continue
- }
- if !strings.HasPrefix(s, ws) {
- panic("invalid indentation")
- }
- lines[i] = lines[i][len(ws):]
- }
- return strings.Join(lines, "\n")
-}
diff --git a/cue/gen.go b/cue/gen.go
deleted file mode 100644
index 0dcf198..0000000
--- a/cue/gen.go
+++ /dev/null
@@ -1,460 +0,0 @@
-// 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.
-
-// +build ignore
-
-package main
-
-import (
- "bytes"
- "flag"
- "fmt"
- "go/ast"
- "go/constant"
- "go/format"
- "go/parser"
- "go/printer"
- "go/token"
- "io"
- "io/ioutil"
- "log"
- "math/big"
- "os"
- "path"
- "path/filepath"
- "sort"
- "strconv"
- "strings"
-
- "cuelang.org/go/cue"
- "cuelang.org/go/cue/errors"
- cueformat "cuelang.org/go/cue/format"
- "cuelang.org/go/cue/load"
- "cuelang.org/go/internal"
-)
-
-const prefix = "../pkg/"
-
-const header = `// Code generated by go generate. DO NOT EDIT.
-
-package cue
-
-`
-
-const initFunc = `
-func init() {
- initBuiltins(builtinPackages)
-}
-
-var _ io.Reader
-`
-
-func main() {
- flag.Parse()
- log.SetFlags(log.Lshortfile)
- log.SetOutput(os.Stdout)
-
- g := generator{
- w: &bytes.Buffer{},
- decls: &bytes.Buffer{},
- fset: token.NewFileSet(),
- }
-
- fmt.Fprintln(g.w, "var builtinPackages = map[string]*builtinPkg{")
- filepath.Walk(prefix, func(dir string, info os.FileInfo, err error) error {
- if err != nil {
- log.Fatal(err)
- }
- if info.Name() == "testdata" {
- return filepath.SkipDir
- }
- if info.IsDir() {
- g.processDir(dir)
- }
- return nil
- })
- fmt.Fprintln(g.w, "}")
-
- w := &bytes.Buffer{}
- fmt.Fprintln(w, header)
-
- m := map[string]*ast.ImportSpec{}
- keys := []string{}
- for _, spec := range g.imports {
- if spec.Path.Value == `"cuelang.org/go/cue"` {
- // Don't add this package.
- continue
- }
- if prev, ok := m[spec.Path.Value]; ok {
- if importName(prev) != importName(spec) {
- log.Fatalf("inconsistent name for import %s: %q != %q",
- spec.Path.Value, importName(prev), importName(spec))
- }
- continue
- }
- m[spec.Path.Value] = spec
- keys = append(keys, spec.Path.Value)
- }
- fmt.Fprintln(w, "import (")
- sort.Strings(keys)
- for _, k := range keys {
- printer.Fprint(w, g.fset, m[k])
- fmt.Fprintln(w)
- }
- fmt.Fprintln(w, ")")
- fmt.Fprintln(w, initFunc)
- io.Copy(w, g.decls)
- io.Copy(w, g.w)
-
- b, err := format.Source(w.Bytes())
- if err != nil {
- b = w.Bytes() // write the unformatted source
- }
- // TODO: do this in a more principled way. The best is probably to
- // put all builtins in a separate package.
- b = bytes.Replace(b, []byte("cue."), []byte(""), -1)
- b = bytes.Replace(b, []byte("{{}}"), []byte("{}"), -1)
-
- if err := ioutil.WriteFile("builtins.go", b, 0644); err != nil {
- log.Fatal(err)
- }
- if err != nil {
- log.Fatal(err)
- }
-}
-
-type generator struct {
- w *bytes.Buffer
- decls *bytes.Buffer
- name string
- fset *token.FileSet
- defaultPkg string
- first bool
- iota int
-
- imports []*ast.ImportSpec
-}
-
-func (g *generator) processDir(dir string) {
- goFiles, err := filepath.Glob(filepath.Join(dir, "*.go"))
- if err != nil {
- log.Fatal(err)
- }
-
- cueFiles, err := filepath.Glob(filepath.Join(dir, "*.cue"))
- if err != nil {
- log.Fatal(err)
- }
-
- if len(goFiles)+len(cueFiles) == 0 {
- return
- }
-
- pkg := dir[len(prefix):]
- fmt.Fprintf(g.w, "%q: &builtinPkg{\nnative: []*builtin{{\n", pkg)
- g.first = true
- for _, filename := range goFiles {
- g.processGo(filename)
- }
- fmt.Fprintf(g.w, "}},\n")
- g.processCUE(dir)
- fmt.Fprintf(g.w, "},\n")
-}
-
-func (g *generator) sep() {
- if g.first {
- g.first = false
- return
- }
- fmt.Fprintln(g.w, "}, {")
-}
-
-func importName(s *ast.ImportSpec) string {
- if s.Name != nil {
- return s.Name.Name
- }
- pkg, err := strconv.Unquote(s.Path.Value)
- if err != nil {
- log.Fatal(err)
- }
- return path.Base(pkg)
-}
-
-// processCUE mixes in CUE definitions defined in the package directory.
-func (g *generator) processCUE(dir string) {
- instances := cue.Build(load.Instances([]string{dir}, &load.Config{
- StdRoot: "../pkg",
- }))
-
- if err := instances[0].Err; err != nil {
- if !strings.Contains(err.Error(), "no CUE files") {
- errors.Print(os.Stderr, err, nil)
- log.Fatalf("error processing %s: %v", dir, err)
- }
- return
- }
-
- n := internal.ToExpr(instances[0].Value().Syntax(cue.Raw()))
- b, err := cueformat.Node(n)
- if err != nil {
- log.Fatal(err)
- }
- b = bytes.ReplaceAll(b, []byte("\n\n"), []byte("\n"))
- // body = strings.ReplaceAll(body, "\t", "")
- // TODO: escape backtick
- fmt.Fprintf(g.w, "cue: `%s`,\n", string(b))
-}
-
-func (g *generator) processGo(filename string) {
- if strings.HasSuffix(filename, "_test.go") {
- return
- }
- f, err := parser.ParseFile(g.fset, filename, nil, parser.ParseComments)
- if err != nil {
- log.Fatal(err)
- }
- g.defaultPkg = ""
- g.name = f.Name.Name
- if g.name == "structs" {
- g.name = "struct"
- }
-
- for _, d := range f.Decls {
- switch x := d.(type) {
- case *ast.GenDecl:
- switch x.Tok {
- case token.CONST:
- for _, spec := range x.Specs {
- if !ast.IsExported(spec.(*ast.ValueSpec).Names[0].Name) {
- continue
- }
- g.genConst(spec.(*ast.ValueSpec))
- }
- case token.IMPORT:
- for _, s := range x.Specs {
- spec := s.(*ast.ImportSpec)
- g.imports = append(g.imports, spec)
- }
- if g.defaultPkg == "" {
- g.defaultPkg = importName(x.Specs[0].(*ast.ImportSpec))
- }
- case token.VAR:
- for _, spec := range x.Specs {
- if ast.IsExported(spec.(*ast.ValueSpec).Names[0].Name) {
- log.Fatal("gen %s: var declarations not supported", filename)
- }
- }
- printer.Fprint(g.decls, g.fset, x)
- fmt.Fprint(g.decls, "\n\n")
- continue
- case token.TYPE:
- // TODO: support type declarations.
- for _, spec := range x.Specs {
- if ast.IsExported(spec.(*ast.TypeSpec).Name.Name) {
- log.Fatal("gen %s: type declarations not supported", filename)
- }
- }
- continue
- default:
- log.Fatalf("gen %s: unexpected spec of type %s", filename, x.Tok)
- }
- case *ast.FuncDecl:
- g.genFun(x)
- }
- }
-}
-
-func (g *generator) genConst(spec *ast.ValueSpec) {
- name := spec.Names[0].Name
- value := ""
- switch v := g.toValue(spec.Values[0]); v.Kind() {
- case constant.Bool, constant.Int, constant.String:
- // TODO: convert octal numbers
- value = v.ExactString()
- case constant.Float:
- var rat big.Rat
- rat.SetString(v.ExactString())
- var float big.Float
- float.SetRat(&rat)
- value = float.Text('g', -1)
- default:
- fmt.Printf("Dropped entry %s.%s (%T: %v)\n", g.defaultPkg, name, v.Kind(), v.ExactString())
- return
- }
- g.sep()
- fmt.Fprintf(g.w, "Name: %q,\n Const: %q,\n", name, value)
-}
-
-func (g *generator) toValue(x ast.Expr) constant.Value {
- switch x := x.(type) {
- case *ast.BasicLit:
- return constant.MakeFromLiteral(x.Value, x.Kind, 0)
- case *ast.BinaryExpr:
- return constant.BinaryOp(g.toValue(x.X), x.Op, g.toValue(x.Y))
- case *ast.UnaryExpr:
- return constant.UnaryOp(x.Op, g.toValue(x.X), 0)
- default:
- log.Fatalf("%s: unsupported expression type %T: %#v", g.defaultPkg, x, x)
- }
- return constant.MakeUnknown()
-}
-
-func (g *generator) genFun(x *ast.FuncDecl) {
- if x.Body == nil {
- return
- }
- types := []string{}
- if x.Type.Results != nil {
- for _, f := range x.Type.Results.List {
- if len(f.Names) > 0 {
- for range f.Names {
- types = append(types, g.goKind(f.Type))
- }
- } else {
- types = append(types, g.goKind(f.Type))
- }
- }
- }
- if n := len(types); n != 1 && (n != 2 || types[1] != "error") {
- fmt.Printf("Dropped func %s.%s: must have one return value or a value and an error %v\n", g.defaultPkg, x.Name.Name, types)
- return
- }
-
- if !ast.IsExported(x.Name.Name) || x.Recv != nil {
- if strings.HasPrefix(x.Name.Name, g.name) {
- printer.Fprint(g.decls, g.fset, x)
- fmt.Fprint(g.decls, "\n\n")
- }
- return
- }
-
- g.sep()
- fmt.Fprintf(g.w, "Name: %q,\n", x.Name.Name)
-
- args := []string{}
- vals := []string{}
- kind := []string{}
- for _, f := range x.Type.Params.List {
- for _, name := range f.Names {
- typ := g.goKind(f.Type)
- argKind := g.goToCUE(f.Type)
- vals = append(vals, fmt.Sprintf("c.%s(%d)", typ, len(args)))
- args = append(args, name.Name)
- kind = append(kind, argKind)
- }
- }
-
- fmt.Fprintf(g.w, "Params: []kind{%s},\n", strings.Join(kind, ", "))
- result := g.goToCUE(x.Type.Results.List[0].Type)
- fmt.Fprintf(g.w, "Result: %s,\n", result)
- argList := strings.Join(args, ", ")
- valList := strings.Join(vals, ", ")
- init := ""
- if len(args) > 0 {
- init = fmt.Sprintf("%s := %s", argList, valList)
- }
-
- fmt.Fprintf(g.w, "Func: func(c *callCtxt) {")
- defer fmt.Fprintln(g.w, "},")
- fmt.Fprintln(g.w)
- if init != "" {
- fmt.Fprintln(g.w, init)
- }
- fmt.Fprintln(g.w, "if c.do() {")
- defer fmt.Fprintln(g.w, "}")
- if len(types) == 1 {
- fmt.Fprint(g.w, "c.ret = func() interface{} ")
- } else {
- fmt.Fprint(g.w, "c.ret, c.err = func() (interface{}, error) ")
- }
- printer.Fprint(g.w, g.fset, x.Body)
- fmt.Fprintln(g.w, "()")
-}
-
-func (g *generator) goKind(expr ast.Expr) string {
- if star, isStar := expr.(*ast.StarExpr); isStar {
- expr = star.X
- }
- w := &bytes.Buffer{}
- printer.Fprint(w, g.fset, expr)
- switch str := w.String(); str {
- case "big.Int":
- return "bigInt"
- case "big.Float":
- return "bigFloat"
- case "big.Rat":
- return "bigRat"
- case "internal.Decimal":
- return "decimal"
- case "[]*internal.Decimal":
- return "decimalList"
- case "cue.Struct":
- return "structVal"
- case "cue.Value":
- return "value"
- case "cue.List":
- return "list"
- case "[]string":
- return "strList"
- case "[]byte":
- return "bytes"
- case "[]cue.Value":
- return "list"
- case "io.Reader":
- return "reader"
- case "time.Time":
- return "string"
- default:
- return str
- }
-}
-
-func (g *generator) goToCUE(expr ast.Expr) (cueKind string) {
- // TODO: detect list and structs types for return values.
- switch k := g.goKind(expr); k {
- case "error":
- cueKind += "bottomKind"
- case "bool":
- cueKind += "boolKind"
- case "bytes", "reader":
- cueKind += "bytesKind|stringKind"
- case "string":
- cueKind += "stringKind"
- case "int", "int8", "int16", "int32", "rune", "int64",
- "uint", "byte", "uint8", "uint16", "uint32", "uint64",
- "bigInt":
- cueKind += "intKind"
- case "float64", "bigRat", "bigFloat", "decimal":
- cueKind += "numKind"
- case "list", "decimalList", "strList":
- cueKind += "listKind"
- case "structVal":
- cueKind += "structKind"
- case "value":
- // Must use callCtxt.value method for these types and resolve manually.
- cueKind += "topKind" // TODO: can be more precise
- default:
- switch {
- case strings.HasPrefix(k, "[]"):
- cueKind += "listKind"
- case strings.HasPrefix(k, "map["):
- cueKind += "structKind"
- default:
- // log.Println("Unknown type:", k)
- // Must use callCtxt.value method for these types and resolve manually.
- cueKind += "topKind" // TODO: can be more precise
- }
- }
- return cueKind
-}
diff --git a/cue/go.go b/cue/go.go
index ae41b4b..28399d9 100644
--- a/cue/go.go
+++ b/cue/go.go
@@ -1,4 +1,4 @@
-// Copyright 2019 CUE Authors
+// Copyright 2020 CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -15,684 +15,32 @@
package cue
import (
- "encoding"
- "encoding/json"
- "fmt"
- "math/big"
- "reflect"
- "sort"
- "strings"
- "unicode/utf8"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/parser"
- "cuelang.org/go/cue/token"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/convert"
+ "cuelang.org/go/internal/core/eval"
)
-// This file contains functionality for converting Go to CUE.
-//
-// The code in this file is a prototype implementation and is far from
-// optimized.
-
func init() {
internal.FromGoValue = func(runtime, x interface{}, nilIsTop bool) interface{} {
- return convertValue(runtime.(*Runtime), x, nilIsTop)
+ r := runtime.(*Runtime)
+ ctx := eval.NewContext(r.index().Runtime, nil)
+ v := convert.GoValueToValue(ctx, x, nilIsTop)
+ n := adt.ToVertex(v)
+ return Value{r.idx, n}
}
internal.FromGoType = func(runtime, x interface{}) interface{} {
- return convertType(runtime.(*Runtime), x)
- }
-}
-
-func convertValue(r *Runtime, x interface{}, nilIsTop bool) Value {
- ctx := r.index().newContext()
- v := convertVal(ctx, baseValue{}, nilIsTop, x)
- return newValueRoot(ctx, v)
-}
-
-func convertType(r *Runtime, x interface{}) Value {
- ctx := r.index().newContext()
- v := convertGoType(r, reflect.TypeOf(x))
- return newValueRoot(ctx, v)
-
-}
-
-// parseTag parses a CUE expression from a cue tag.
-func parseTag(ctx *context, obj *structLit, field label, tag string) value {
- if p := strings.Index(tag, ","); p >= 0 {
- tag = tag[:p]
- }
- if tag == "" {
- return &top{}
- }
- expr, err := parser.ParseExpr("<field:>", tag)
- if err != nil {
- field := ctx.LabelStr(field)
- return ctx.mkErr(baseValue{}, "invalid tag %q for field %q: %v", tag, field, err)
- }
- v := newVisitor(ctx.index, nil, nil, obj, true)
- return v.walk(expr)
-}
-
-// TODO: should we allow mapping names in cue tags? This only seems like a good
-// idea if we ever want to allow mapping CUE to a different name than JSON.
-var tagsWithNames = []string{"json", "yaml", "protobuf"}
-
-func getName(f *reflect.StructField) string {
- name := f.Name
- for _, s := range tagsWithNames {
- if tag, ok := f.Tag.Lookup(s); ok {
- if p := strings.Index(tag, ","); p >= 0 {
- tag = tag[:p]
- }
- if tag != "" {
- name = tag
- break
- }
- }
- }
- return name
-}
-
-// isOptional indicates whether a field should be marked as optional.
-func isOptional(f *reflect.StructField) bool {
- isOptional := false
- switch f.Type.Kind() {
- case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice:
- // Note: it may be confusing to distinguish between an empty slice and
- // a nil slice. However, it is also surprising to not be able to specify
- // a default value for a slice. So for now we will allow it.
- isOptional = true
- }
- if tag, ok := f.Tag.Lookup("cue"); ok {
- // TODO: only if first field is not empty.
- isOptional = false
- for _, f := range strings.Split(tag, ",")[1:] {
- switch f {
- case "opt":
- isOptional = true
- case "req":
- return false
- }
- }
- } else if tag, ok = f.Tag.Lookup("json"); ok {
- isOptional = false
- for _, f := range strings.Split(tag, ",")[1:] {
- if f == "omitempty" {
- return true
- }
- }
- }
- return isOptional
-}
-
-// isOmitEmpty means that the zero value is interpreted as undefined.
-func isOmitEmpty(f *reflect.StructField) bool {
- isOmitEmpty := false
- switch f.Type.Kind() {
- case reflect.Ptr, reflect.Map, reflect.Chan, reflect.Interface, reflect.Slice:
- // Note: it may be confusing to distinguish between an empty slice and
- // a nil slice. However, it is also surprising to not be able to specify
- // a default value for a slice. So for now we will allow it.
- isOmitEmpty = true
-
- default:
- // TODO: we can also infer omit empty if a type cannot be nil if there
- // is a constraint that unconditionally disallows the zero value.
- }
- tag, ok := f.Tag.Lookup("json")
- if ok {
- isOmitEmpty = false
- for _, f := range strings.Split(tag, ",")[1:] {
- if f == "omitempty" {
- return true
- }
- }
- }
- return isOmitEmpty
-}
-
-// parseJSON parses JSON into a CUE value. b must be valid JSON.
-func parseJSON(ctx *context, b []byte) evaluated {
- expr, err := parser.ParseExpr("json", b)
- if err != nil {
- panic(err) // cannot happen
- }
- v := newVisitor(ctx.index, nil, nil, nil, false)
- return v.walk(expr).evalPartial(ctx)
-}
-
-func isZero(v reflect.Value) bool {
- x := v.Interface()
- if x == nil {
- return true
- }
- switch k := v.Kind(); k {
- case reflect.Struct, reflect.Array:
- // we never allow optional values for these types.
- return false
-
- case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
- reflect.Slice:
- // Note that for maps we preserve the distinction between a nil map and
- // an empty map.
- return v.IsNil()
-
- case reflect.String:
- return v.Len() == 0
-
- default:
- return x == reflect.Zero(v.Type()).Interface()
- }
-}
-
-func convertVal(ctx *context, src source, nilIsTop bool, x interface{}) evaluated {
- v := convertRec(ctx, src, nilIsTop, x)
- if v == nil {
- return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", v)
- }
- return v
-}
-
-func isNil(x reflect.Value) bool {
- switch x.Kind() {
- // Only check for supported types; ignore func and chan.
- case reflect.Ptr, reflect.Map, reflect.Slice, reflect.Interface:
- return x.IsNil()
- }
- return false
-}
-
-func convertRec(ctx *context, src source, nilIsTop bool, x interface{}) evaluated {
- switch v := x.(type) {
- case nil:
- if nilIsTop {
- return &top{src.base()}
- }
- return &nullLit{src.base()}
-
- case Value:
- if ctx.index != v.ctx().index {
- panic("value of type Value is not created with same Runtime as Instance")
- }
- return v.eval(ctx)
-
- case *ast.File:
- x := newVisitorCtx(ctx, nil, nil, nil, false)
- return ctx.manifest(x.walk(v))
-
- case ast.Expr:
- x := newVisitorCtx(ctx, nil, nil, nil, false)
- return ctx.manifest(x.walk(v))
-
- case *big.Int:
- n := newInt(src.base(), 0)
- n.X.Coeff.Set(v)
- if v.Sign() < 0 {
- n.X.Coeff.Neg(&n.X.Coeff)
- n.X.Negative = true
- }
- return n
-
- case *big.Rat:
- // should we represent this as a binary operation?
- n := newNum(src, numKind, 0)
- _, err := ctx.Quo(&n.X, apd.NewWithBigInt(v.Num(), 0), apd.NewWithBigInt(v.Denom(), 0))
+ r := runtime.(*Runtime)
+ ctx := eval.NewContext(r.index().Runtime, nil)
+ expr, err := convert.GoTypeToExpr(ctx, x)
if err != nil {
- return ctx.mkErr(src, err)
+ expr = &adt.Bottom{Err: err}
}
- if !v.IsInt() {
- n.K = floatKind
- }
- return n
+ n := &adt.Vertex{}
+ n.AddConjunct(adt.MakeConjunct(nil, expr))
+ return Value{r.idx, n}
- case *big.Float:
- return newFloat(src, 0).setString(v.String())
-
- case *apd.Decimal:
- n := newNum(src, numKind, 0).set(v)
- if !n.isInt(ctx) {
- n.K = floatKind
- }
- return n
-
- case json.Marshaler:
- b, err := v.MarshalJSON()
- if err != nil {
- return ctx.mkErr(src, err)
- }
-
- return parseJSON(ctx, b)
-
- case encoding.TextMarshaler:
- b, err := v.MarshalText()
- if err != nil {
- return ctx.mkErr(src, err)
- }
- b, err = json.Marshal(string(b))
- if err != nil {
- return ctx.mkErr(src, err)
- }
- return parseJSON(ctx, b)
-
- case error:
- return ctx.mkErr(src, v.Error())
- case bool:
- return &boolLit{src.base(), v}
- case string:
- if !utf8.ValidString(v) {
- return ctx.mkErr(src,
- "cannot convert result to string: invalid UTF-8")
- }
- return &stringLit{src.base(), v, nil}
- case []byte:
- return &bytesLit{src.base(), v, nil}
- case int:
- return toInt(ctx, src, int64(v))
- case int8:
- return toInt(ctx, src, int64(v))
- case int16:
- return toInt(ctx, src, int64(v))
- case int32:
- return toInt(ctx, src, int64(v))
- case int64:
- return toInt(ctx, src, int64(v))
- case uint:
- return toUint(ctx, src, uint64(v))
- case uint8:
- return toUint(ctx, src, uint64(v))
- case uint16:
- return toUint(ctx, src, uint64(v))
- case uint32:
- return toUint(ctx, src, uint64(v))
- case uint64:
- return toUint(ctx, src, uint64(v))
- case uintptr:
- return toUint(ctx, src, uint64(v))
- case float64:
- return newFloat(src, 0).setString(fmt.Sprintf("%g", v))
- case float32:
- return newFloat(src, 0).setString(fmt.Sprintf("%g", v))
-
- case reflect.Value:
- if v.CanInterface() {
- return convertRec(ctx, src, nilIsTop, v.Interface())
- }
-
- default:
- value := reflect.ValueOf(v)
- switch value.Kind() {
- case reflect.Bool:
- return &boolLit{src.base(), value.Bool()}
-
- case reflect.String:
- str := value.String()
- if !utf8.ValidString(str) {
- return ctx.mkErr(src,
- "cannot convert result to string: invalid UTF-8")
- }
- return &stringLit{src.base(), str, nil}
-
- case reflect.Int, reflect.Int8, reflect.Int16,
- reflect.Int32, reflect.Int64:
- return toInt(ctx, src, value.Int())
-
- case reflect.Uint, reflect.Uint8, reflect.Uint16,
- reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- return toUint(ctx, src, value.Uint())
-
- case reflect.Float32, reflect.Float64:
- return convertRec(ctx, src, nilIsTop, value.Float())
-
- case reflect.Ptr:
- if value.IsNil() {
- if nilIsTop {
- return &top{src.base()}
- }
- return &nullLit{src.base()}
- }
- return convertRec(ctx, src, nilIsTop, value.Elem().Interface())
-
- case reflect.Struct:
- obj := newStruct(src)
- t := value.Type()
- for i := 0; i < value.NumField(); i++ {
- t := t.Field(i)
- if t.PkgPath != "" {
- continue
- }
- val := value.Field(i)
- if !nilIsTop && isNil(val) {
- continue
- }
- if tag, _ := t.Tag.Lookup("json"); tag == "-" {
- continue
- }
- if isOmitEmpty(&t) && isZero(val) {
- continue
- }
- sub := convertRec(ctx, src, nilIsTop, val.Interface())
- if sub == nil {
- // mimic behavior of encoding/json: skip fields of unsupported types
- continue
- }
- if isBottom(sub) {
- return sub
- }
-
- // leave errors like we do during normal evaluation or do we
- // want to return the error?
- name := getName(&t)
- if name == "-" {
- continue
- }
- f := ctx.StrLabel(name)
- obj.Arcs = append(obj.Arcs, arc{Label: f, v: sub})
- }
- sort.Sort(obj)
- return obj
-
- case reflect.Map:
- obj := newStruct(src)
-
- sorted := []string{}
- keys := []string{}
- t := value.Type()
- switch key := t.Key(); key.Kind() {
- case reflect.String,
- reflect.Int, reflect.Int8, reflect.Int16,
- reflect.Int32, reflect.Int64,
- reflect.Uint, reflect.Uint8, reflect.Uint16,
- reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- for _, k := range value.MapKeys() {
- val := value.MapIndex(k)
- // if isNil(val) {
- // continue
- // }
-
- sub := convertRec(ctx, src, nilIsTop, val.Interface())
- // mimic behavior of encoding/json: report error of
- // unsupported type.
- if sub == nil {
- return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", val)
- }
- if isBottom(sub) {
- return sub
- }
-
- s := fmt.Sprint(k)
- keys = append(keys, s)
- sorted = append(sorted, s)
-
- // Set feature later.
- obj.Arcs = append(obj.Arcs, arc{Label: 0, v: sub})
- }
-
- default:
- return ctx.mkErr(baseValue{}, "unsupported Go type for map key (%v)", key)
- }
-
- // Assign label in normalized order.
- sort.Strings(sorted)
- for _, k := range sorted {
- ctx.StrLabel(k)
- }
-
- // Now assign the labels to the arcs.
- for i, k := range keys {
- obj.Arcs[i].Label = ctx.StrLabel(k)
- }
- sort.Sort(obj)
- return obj
-
- case reflect.Slice, reflect.Array:
- list := &list{baseValue: src.base()}
- arcs := []arc{}
- for i := 0; i < value.Len(); i++ {
- val := value.Index(i)
- x := convertRec(ctx, src, nilIsTop, val.Interface())
- if x == nil {
- return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", val)
- }
- if isBottom(x) {
- return x
- }
- arcs = append(arcs, arc{Label: label(len(arcs)), v: x})
- }
- list.elem = &structLit{baseValue: list.baseValue, Arcs: arcs}
- list.initLit()
- // There is no need to set the type of the list, as the list will
- // be of fixed size and all elements will already have a defined
- // value.
- return list
- }
- }
- return nil
-}
-
-func toInt(ctx *context, src source, x int64) evaluated {
- return newInt(src, 0).setInt64(x)
-}
-
-func toUint(ctx *context, src source, x uint64) evaluated {
- return newInt(src, 0).setUInt64(x)
-}
-
-func convertGoType(r *Runtime, t reflect.Type) value {
- ctx := r.index().newContext()
- // TODO: this can be much more efficient.
- ctx.mutex.Lock()
- defer ctx.mutex.Unlock()
- return goTypeToValue(ctx, true, t)
-}
-
-var (
- jsonMarshaler = reflect.TypeOf(new(json.Marshaler)).Elem()
- textMarshaler = reflect.TypeOf(new(encoding.TextMarshaler)).Elem()
- topSentinel = &top{}
-)
-
-// goTypeToValue converts a Go Type to a value.
-//
-// TODO: if this value will always be unified with a concrete type in Go, then
-// many of the fields may be omitted.
-func goTypeToValue(ctx *context, allowNullDefault bool, t reflect.Type) value {
- v := goTypeToValueRec(ctx, allowNullDefault, t)
- if v == nil {
- return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", t)
- }
- return v
-}
-
-func goTypeToValueRec(ctx *context, allowNullDefault bool, t reflect.Type) (e value) {
- if e, ok := ctx.LoadType(t); ok {
- return e.(value)
- }
-
- switch reflect.Zero(t).Interface().(type) {
- case *big.Int, big.Int:
- e = &basicType{K: intKind}
- goto store
-
- case *big.Float, big.Float, *big.Rat, big.Rat:
- e = &basicType{K: numKind}
- goto store
-
- case *apd.Decimal, apd.Decimal:
- e = &basicType{K: numKind}
- goto store
- }
-
- // Even if this is for types that we know cast to a certain type, it can't
- // hurt to return top, as in these cases the concrete values will be
- // strict instances and there cannot be any tags that further constrain
- // the values.
- if t.Implements(jsonMarshaler) || t.Implements(textMarshaler) {
- return topSentinel
- }
-
- switch k := t.Kind(); k {
- case reflect.Ptr:
- elem := t.Elem()
- for elem.Kind() == reflect.Ptr {
- elem = elem.Elem()
- }
- e = goTypeToValueRec(ctx, false, elem)
- if allowNullDefault {
- e = wrapOrNull(e)
- }
-
- case reflect.Interface:
- switch t.Name() {
- case "error":
- // This is really null | _|_. There is no error if the error is null.
- e = &nullLit{} // null
- default:
- e = topSentinel // `_`
- }
-
- case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
- reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- e = predefinedRanges[t.Kind().String()]
-
- case reflect.Uint, reflect.Uintptr:
- e = predefinedRanges["uint64"]
-
- case reflect.Int:
- e = predefinedRanges["int64"]
-
- case reflect.String:
- e = &basicType{K: stringKind}
-
- case reflect.Bool:
- e = &basicType{K: boolKind}
-
- case reflect.Float32, reflect.Float64:
- e = &basicType{K: floatKind}
-
- case reflect.Struct:
- // First iterate to create struct, then iterate another time to
- // resolve field tags to allow field tags to refer to the struct fields.
- tags := map[label]string{}
- obj := newStruct(baseValue{})
- ctx.StoreType(t, obj)
-
- for i := 0; i < t.NumField(); i++ {
- f := t.Field(i)
- if f.PkgPath != "" {
- continue
- }
- _, ok := f.Tag.Lookup("cue")
- elem := goTypeToValueRec(ctx, !ok, f.Type)
- if elem == nil || isBottom(elem) {
- continue // Ignore fields for unsupported types
- }
-
- // leave errors like we do during normal evaluation or do we
- // want to return the error?
- name := getName(&f)
- if name == "-" {
- continue
- }
- l := ctx.StrLabel(name)
- obj.Arcs = append(obj.Arcs, arc{
- Label: l,
- // The GO JSON decoder always allows a value to be undefined.
- optional: isOptional(&f),
- v: elem,
- })
-
- if tag, ok := f.Tag.Lookup("cue"); ok {
- tags[l] = tag
- }
- }
- sort.Sort(obj)
-
- for label, tag := range tags {
- v := parseTag(ctx, obj, label, tag)
- if isBottom(v) {
- return v
- }
- for i, a := range obj.Arcs {
- if a.Label == label {
- // Instead of unifying with the existing type, we substitute
- // with the constraints from the tags. The type constraints
- // will be implied when unified with a concrete value.
- obj.Arcs[i].v = mkBin(ctx, token.NoPos, opUnify, a.v, v)
- }
- }
- }
-
- return obj
-
- case reflect.Array, reflect.Slice:
- if t.Elem().Kind() == reflect.Uint8 {
- e = &basicType{K: bytesKind}
- } else {
- elem := goTypeToValueRec(ctx, allowNullDefault, t.Elem())
- if elem == nil {
- return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", t.Elem())
- }
-
- var ln value = &top{}
- if t.Kind() == reflect.Array {
- ln = toInt(ctx, baseValue{}, int64(t.Len()))
- }
- e = &list{elem: &structLit{}, typ: elem, len: ln}
- }
- if k == reflect.Slice {
- e = wrapOrNull(e)
- }
-
- case reflect.Map:
- switch key := t.Key(); key.Kind() {
- case reflect.String, reflect.Int, reflect.Int8, reflect.Int16,
- reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
- reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
- default:
- return ctx.mkErr(baseValue{}, "unsupported Go type for map key (%v)", key)
- }
-
- obj := newStruct(baseValue{})
- sig := ¶ms{}
- sig.add(ctx.Label("_", true), &basicType{K: stringKind})
- v := goTypeToValueRec(ctx, allowNullDefault, t.Elem())
- if v == nil {
- return ctx.mkErr(baseValue{}, "unsupported Go type (%v)", t.Elem())
- }
- if isBottom(v) {
- return v
- }
- obj.optionals = newOptional(nil, &lambdaExpr{params: sig, value: v})
-
- e = wrapOrNull(obj)
- }
-
-store:
- // TODO: store error if not nil?
- if e != nil {
- ctx.StoreType(t, e)
- }
- return e
-}
-
-func wrapOrNull(e value) value {
- if e == nil || isBottom(e) || e.Kind().isAnyOf(nullKind) {
- return e
- }
- return makeNullable(e, true)
-}
-
-func makeNullable(e value, nullIsDefault bool) value {
- return &disjunction{
- baseValue: baseValue{e},
- Values: []dValue{
- {Val: &nullLit{}, Default: nullIsDefault},
- {Val: e}},
- errors: nil,
- HasDefaults: nullIsDefault,
+ // return convertType(runtime.(*Runtime), x)
}
}
diff --git a/cue/go_test.go b/cue/go_test.go
deleted file mode 100644
index fd5a03f..0000000
--- a/cue/go_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-// Copyright 2019 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 cue
-
-import (
- "math/big"
- "reflect"
- "testing"
- "time"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/errors"
-)
-
-func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return }
-
-func TestConvert(t *testing.T) {
- i34 := big.NewInt(34)
- d34 := mkBigInt(34)
- n34 := mkBigInt(-34)
- f34 := big.NewFloat(34.0000)
- testCases := []struct {
- goVal interface{}
- want string
- }{{
- nil, "_",
- }, {
- true, "true",
- }, {
- false, "false",
- }, {
- errors.New("oh noes"), "_|_(oh noes)",
- }, {
- "foo", `"foo"`,
- }, {
- "\x80", `_|_(cannot convert result to string: invalid UTF-8)`,
- }, {
- 3, "3",
- }, {
- uint(3), "3",
- }, {
- uint8(3), "3",
- }, {
- uint16(3), "3",
- }, {
- uint32(3), "3",
- }, {
- uint64(3), "3",
- }, {
- int8(-3), "-3",
- }, {
- int16(-3), "-3",
- }, {
- int32(-3), "-3",
- }, {
- int64(-3), "-3",
- }, {
- float64(3.1), "3.1",
- }, {
- float32(3.1), "3.1",
- }, {
- uintptr(3), "3",
- }, {
- &i34, "34",
- }, {
- &f34, "34",
- }, {
- &d34, "34",
- }, {
- &n34, "-34",
- }, {
- []int{1, 2, 3, 4}, "[1,2,3,4]",
- }, {
- struct {
- A int
- B *int
- }{3, nil},
- "<0>{A: 3}",
- }, {
- []interface{}{}, "[]",
- }, {
- []interface{}{nil}, "[_]",
- }, {
- map[string]interface{}{"a": 1, "x": nil}, "<0>{x: _, a: 1}",
- }, {
- map[string][]int{
- "a": {1},
- "b": {3, 4},
- }, "<0>{a: [1], b: [3,4]}",
- }, {
- map[bool]int{}, "_|_(unsupported Go type for map key (bool))",
- }, {
- map[struct{}]int{{}: 2}, "_|_(unsupported Go type for map key (struct {}))",
- }, {
- map[int]int{1: 2}, `<0>{"1": 2}`,
- }, {
- struct {
- a int
- b int
- }{3, 4},
- "<0>{}",
- }, {
- struct {
- A int
- B int `json:"-"`
- C int `json:",omitempty"`
- }{3, 4, 0},
- "<0>{A: 3}",
- }, {
- struct {
- A int
- B int
- }{3, 4},
- "<0>{A: 3, B: 4}",
- }, {
- struct {
- A int `json:"a"`
- B int `yaml:"b"`
- }{3, 4},
- "<0>{a: 3, b: 4}",
- }, {
- struct {
- A int `json:"" yaml:"" protobuf:"aa"`
- B int `yaml:"cc" json:"bb" protobuf:"aa"`
- }{3, 4},
- "<0>{aa: 3, bb: 4}",
- }, {
- &struct{ A int }{3}, "<0>{A: 3}",
- }, {
- (*struct{ A int })(nil), "_",
- }, {
- reflect.ValueOf(3), "3",
- }, {
- time.Date(2019, 4, 1, 0, 0, 0, 0, time.UTC), `"2019-04-01T00:00:00Z"`,
- }}
- inst := getInstance(t, "{}")
- b := ast.NewIdent("dummy")
- for _, tc := range testCases {
- ctx := inst.newContext()
- t.Run("", func(t *testing.T) {
- v := convertVal(ctx, newNode(b), true, tc.goVal)
- got := debugStr(ctx, v)
- if got != tc.want {
- t.Errorf("got %q; want %q", got, tc.want)
- }
- })
- }
-}
-
-func TestConvertType(t *testing.T) {
- testCases := []struct {
- goTyp interface{}
- want string
- }{{
- struct {
- A int `cue:">=0&<100"`
- B *big.Int `cue:">=0"`
- C *big.Int
- D big.Int
- F *big.Float
- }{},
- // TODO: indicate that B is explicitly an int only.
- `<0>{A: ((int & >=-9223372036854775808 & int & <=9223372036854775807) & (>=0 & <100)), ` +
- `B: (int & >=0), ` +
- `C?: int, ` +
- `D: int, ` +
- `F?: number}`,
- }, {
- &struct {
- A int16 `cue:">=0&<100"`
- B error `json:"b,"`
- C string
- D bool
- F float64
- L []byte
- T time.Time
- G func()
- }{},
- `(*null | <0>{T: _, ` +
- `A: ((int & >=-32768 & int & <=32767) & (>=0 & <100)), ` +
- `C: string, ` +
- `D: bool, ` +
- `F: float, ` +
- `b: null, ` +
- `L?: (*null | bytes)})`,
- }, {
- struct {
- A int `cue:"<"` // invalid
- }{},
- "_|_(invalid tag \"<\" for field \"A\": expected operand, found 'EOF')",
- }, {
- struct {
- A int `json:"-"` // skip
- D *apd.Decimal
- P ***apd.Decimal
- I interface{ Foo() }
- T string `cue:""` // allowed
- h int
- }{},
- "<0>{T: string, D?: number, P?: (*null | number), I?: _}",
- }, {
- struct {
- A int8 `cue:"C-B"`
- B int8 `cue:"C-A,opt"`
- C int8 `cue:"A+B"`
- }{},
- // TODO: should B be marked as optional?
- `<0>{A: ((int & >=-128 & int & <=127) & (<0>.C - <0>.B)), ` +
- `B?: ((int & >=-128 & int & <=127) & (<0>.C - <0>.A)), ` +
- `C: ((int & >=-128 & int & <=127) & (<0>.A + <0>.B))}`,
- }, {
- []string{},
- `(*null | [, ...string])`,
- }, {
- [4]string{},
- `4*[string]`,
- }, {
- []func(){},
- "_|_(unsupported Go type (func()))",
- }, {
- map[string]struct{ A map[string]uint }{},
- `(*null | ` +
- `<0>{[]: <1>(_: string)-><2>{` +
- `A?: (*null | ` +
- `<3>{[]: <4>(_: string)->(int & >=0 & int & <=18446744073709551615), })}, })`,
- }, {
- map[float32]int{},
- `_|_(unsupported Go type for map key (float32))`,
- }, {
- map[int]map[float32]int{},
- `_|_(unsupported Go type for map key (float32))`,
- }, {
- map[int]func(){},
- `_|_(unsupported Go type (func()))`,
- }, {
- time.Now, // a function
- "_|_(unsupported Go type (func() time.Time))",
- }}
- inst := getInstance(t, "{}")
-
- for _, tc := range testCases {
- ctx := inst.newContext()
- t.Run("", func(t *testing.T) {
- v := goTypeToValue(ctx, true, reflect.TypeOf(tc.goTyp))
- got := debugStr(ctx, v)
- if got != tc.want {
- t.Errorf("\n got %q;\nwant %q", got, tc.want)
- }
- })
- }
-}
diff --git a/cue/instance.go b/cue/instance.go
index a779041..5b954ea 100644
--- a/cue/instance.go
+++ b/cue/instance.go
@@ -18,8 +18,11 @@
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/token"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/compile"
+ "cuelang.org/go/internal/core/convert"
+ "cuelang.org/go/internal/core/eval"
"cuelang.org/go/internal/core/runtime"
)
@@ -28,12 +31,7 @@
type Instance struct {
*index
- rootStruct *structLit // the struct to insert root values into
- rootValue value // the value to evaluate: may add comprehensions
-
- // scope is used as an additional top-level scope between the package scope
- // and the predeclared identifiers.
- scope *structLit
+ root *adt.Vertex
ImportPath string
Dir string
@@ -49,12 +47,12 @@
}
func (x *index) addInst(p *Instance) *Instance {
- x.Index.AddInst(p.ImportPath, p.rootStruct, p)
+ x.Index.AddInst(p.ImportPath, p.root, p)
p.index = x
return p
}
-func (x *index) getImportFromNode(v value) *Instance {
+func (x *index) getImportFromNode(v *adt.Vertex) *Instance {
p := x.Index.GetImportFromNode(v)
if p == nil {
return nil
@@ -74,25 +72,23 @@
internal.MakeInstance = func(value interface{}) interface{} {
v := value.(Value)
x := v.eval(v.ctx())
- st, ok := x.(*structLit)
+ st, ok := x.(*adt.Vertex)
if !ok {
- st = &structLit{baseValue: x.base(), emit: x}
+ st = &adt.Vertex{}
+ st.AddConjunct(adt.MakeConjunct(nil, x))
}
return v.ctx().index.addInst(&Instance{
- rootStruct: st,
- rootValue: v.v.v,
+ root: st,
})
}
}
// newInstance creates a new instance. Use Insert to populate the instance.
-func newInstance(x *index, p *build.Instance) *Instance {
+func newInstance(x *index, p *build.Instance, v *adt.Vertex) *Instance {
// TODO: associate root source with structLit.
- st := &structLit{baseValue: baseValue{nil}}
i := &Instance{
- rootStruct: st,
- rootValue: st,
- inst: p,
+ root: v,
+ inst: p,
}
if p != nil {
i.ImportPath = p.ImportPath
@@ -118,28 +114,7 @@
func (inst *Instance) eval(ctx *context) evaluated {
// TODO: remove manifest here?
- v := ctx.manifest(inst.rootValue)
- if s, ok := v.(*structLit); ok && s.emit != nil {
- e := s.emit.evalPartial(ctx)
- src := binSrc(token.NoPos, opUnify, v, e)
- outer:
- switch e.(type) {
- case *structLit, *top:
- v = binOp(ctx, src, opUnifyUnchecked, v, e)
- if s, ok := v.(*structLit); ok {
- s.emit = nil
- }
-
- default:
- for _, a := range s.Arcs {
- if !a.definition {
- v = binOp(ctx, src, opUnify, v, e)
- break outer
- }
- }
- return e
- }
- }
+ v := ctx.manifest(inst.root)
return v
}
@@ -148,20 +123,63 @@
v := value.(Value)
e := expr.(ast.Expr)
ctx := v.idx.newContext()
- return newValueRoot(ctx, evalExpr(ctx, v.eval(ctx), e))
+ return newValueRoot(ctx, evalExpr(ctx, v.vertex(ctx), e))
}
}
-func evalExpr(ctx *context, x value, expr ast.Expr) evaluated {
- if isBottom(x) {
- return ctx.mkErr(x, "error evaluating instance: %v", x)
+// evalExpr evaluates expr within scope.
+func evalExpr(ctx *context, scope *adt.Vertex, expr ast.Expr) evaluated {
+ cfg := &compile.Config{
+ Scope: scope,
+ Imports: func(x *ast.Ident) (pkgPath string) {
+ if _, ok := builtins[x.Name]; !ok {
+ return ""
+ }
+ return x.Name
+ },
}
- obj, ok := x.(*structLit)
- if !ok {
- return ctx.mkErr(x, "instance is not a struct, found %s", x.Kind())
+
+ c, err := compile.Expr(cfg, ctx.opCtx, expr)
+ if err != nil {
+ return &adt.Bottom{Err: err}
}
- v := newVisitor(ctx.index, nil, nil, obj, true)
- return v.walk(expr).evalPartial(ctx)
+ return adt.Resolve(ctx.opCtx, c)
+
+ // scope.Finalize(ctx.opCtx) // TODO: not appropriate here.
+ // switch s := scope.Value.(type) {
+ // case *bottom:
+ // return s
+ // case *adt.StructMarker:
+ // default:
+ // return ctx.mkErr(scope, "instance is not a struct, found %s", scope.Kind())
+ // }
+
+ // c := ctx.opCtx
+
+ // x, err := compile.Expr(&compile.Config{Scope: scope}, c.Runtime, expr)
+ // if err != nil {
+ // return c.NewErrf("could not evaluate %s: %v", c.Str(x), err)
+ // }
+
+ // env := &adt.Environment{Vertex: scope}
+
+ // switch v := x.(type) {
+ // case adt.Value:
+ // return v
+ // case adt.Resolver:
+ // r, err := c.Resolve(env, v)
+ // if err != nil {
+ // return err
+ // }
+ // return r
+
+ // case adt.Evaluator:
+ // e, _ := c.Evaluate(env, x)
+ // return e
+
+ // }
+
+ // return c.NewErrf("could not evaluate %s", c.Str(x))
}
// Doc returns the package comments for this instance.
@@ -183,7 +201,8 @@
// top-level values.
func (inst *Instance) Value() Value {
ctx := inst.newContext()
- return newValueRoot(ctx, inst.eval(ctx))
+ inst.root.Finalize(ctx.opCtx)
+ return newVertexRoot(ctx, inst.root)
}
// Eval evaluates an expression within an existing instance.
@@ -191,7 +210,9 @@
// Expressions may refer to builtin packages if they can be uniquely identified.
func (inst *Instance) Eval(expr ast.Expr) Value {
ctx := inst.newContext()
- result := evalExpr(ctx, inst.eval(ctx), expr)
+ v := inst.root
+ v.Finalize(ctx.opCtx)
+ result := evalExpr(ctx, v, expr)
return newValueRoot(ctx, result)
}
@@ -201,33 +222,22 @@
// that these will only surface during manifestation. This allows
// non-conflicting parts to be used.
func Merge(inst ...*Instance) *Instance {
- switch len(inst) {
- case 0:
- return nil
- case 1:
- return inst[0]
- }
+ v := &adt.Vertex{}
- values := []value{}
+ i := inst[0]
+ ctx := i.index.newContext().opCtx
+
+ // TODO: interesting test: use actual unification and then on K8s corpus.
+
for _, i := range inst {
- if i.Err != nil {
- return i
- }
- values = append(values, i.rootValue)
+ w := i.Value()
+ v.AddConjunct(adt.MakeConjunct(nil, w.v.ToDataAll()))
}
- merged := &mergedValues{values: values}
+ v.Finalize(ctx)
- ctx := inst[0].newContext()
-
- st, ok := ctx.manifest(merged).(*structLit)
- if !ok {
- return nil
- }
-
- p := ctx.index.addInst(&Instance{
- rootStruct: st,
- rootValue: merged,
- complete: true,
+ p := i.index.addInst(&Instance{
+ root: v,
+ complete: true,
})
return p
}
@@ -239,37 +249,33 @@
p.Complete()
idx := inst.index
+ r := inst.index.Runtime
- i := newInstance(idx, p)
+ rErr := runtime.ResolveFiles(idx.Index, p, isBuiltin)
+
+ v, err := compile.Files(&compile.Config{Scope: inst.root}, r, p.Files...)
+
+ v.AddConjunct(adt.MakeConjunct(nil, inst.root))
+
+ i := newInstance(idx, p, v)
+ if rErr != nil {
+ i.setListOrError(err)
+ }
if i.Err != nil {
- return i
+ i.setListOrError(err)
}
- ctx := inst.newContext()
- val := newValueRoot(ctx, inst.rootValue)
- v, err := val.structValFull(ctx)
if err != nil {
- i.setError(val.toErr(err))
- return i
+ i.setListOrError(err)
}
- i.scope = v.obj
- if err := runtime.ResolveFiles(idx.Index, p, isBuiltin); err != nil {
- i.setError(err)
- return i
- }
- for _, f := range p.Files {
- if err := i.insertFile(f); err != nil {
- i.setListOrError(err)
- }
- }
i.complete = true
return i
}
func (inst *Instance) value() Value {
- return newValueRoot(inst.newContext(), inst.rootValue)
+ return newVertexRoot(inst.newContext(), inst.root)
}
// Lookup reports the value at a path starting from the top level struct. The
@@ -322,44 +328,36 @@
// a Value. In the latter case, it will panic if the Value is not from the same
// Runtime.
func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
- ctx := inst.newContext()
- root := ctx.manifest(inst.rootValue)
for i := len(path) - 1; i >= 0; i-- {
x = map[string]interface{}{path[i]: x}
}
- value := convertVal(ctx, root, true, x)
- eval := binOp(ctx, baseValue{}, opUnify, root, value)
- // TODO: validate recursively?
- err := inst.Err
- var st *structLit
- var stVal evaluated
- switch x := eval.(type) {
- case *structLit:
- st = x
- stVal = x
- default:
- // This should not happen.
- b := ctx.mkErr(eval, "error filling struct")
- err = inst.Value().toErr(b)
- st = &structLit{emit: b}
- stVal = b
- case *bottom:
- err = inst.Value().toErr(x)
- st = &structLit{emit: x}
- stVal = x
+ a := make([]adt.Conjunct, len(inst.root.Conjuncts))
+ copy(a, inst.root.Conjuncts)
+ u := &adt.Vertex{Conjuncts: a}
+
+ if v, ok := x.(Value); ok {
+ if inst.index != v.idx {
+ panic("value of type Value is not created with same Runtime as Instance")
+ }
+ for _, c := range v.v.Conjuncts {
+ u.AddConjunct(c)
+ }
+ } else {
+ ctx := eval.NewContext(inst.index.Runtime, nil)
+ expr := convert.GoValueToExpr(ctx, true, x)
+ u.AddConjunct(adt.MakeConjunct(nil, expr))
+ u.Finalize(ctx)
}
inst = inst.index.addInst(&Instance{
- rootStruct: st,
- rootValue: stVal,
- inst: nil,
+ root: u,
+ inst: nil,
// Omit ImportPath to indicate this is not an importable package.
Dir: inst.Dir,
PkgName: inst.PkgName,
Incomplete: inst.Incomplete,
- Err: err,
- complete: err != nil,
+ complete: true,
})
- return inst, err
+ return inst, nil
}
diff --git a/cue/instance_test.go b/cue/instance_test.go
deleted file mode 100644
index e11cbf6..0000000
--- a/cue/instance_test.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// 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 cue
-
-import (
- "strings"
- "testing"
-
- "cuelang.org/go/cue/build"
-)
-
-func toString(t *testing.T, v Value) string {
- t.Helper()
-
- b, err := v.MarshalJSON()
- if err != nil {
- t.Fatal(err)
- }
-
- return strings.Replace(string(b), `"`, "", -1)
-}
-
-func TestMerge(t *testing.T) {
- insts := func(s ...string) []string { return s }
- testCases := []struct {
- desc string
- instances []string
- out string
- isErr bool
- }{{
- desc: "single",
- instances: insts(`a: 1, b: 2`),
- out: `{a:1,b:2}`,
- }, {
- desc: "multiple",
- instances: insts(
- `a: 1`,
- `b: 2`,
- `a: int`,
- ),
- out: `{a:1,b:2}`,
- }, {
- desc: "templates",
- instances: insts(`
- obj: [string]: { a: "A" }
- obj: alpha: { b: 2 }
- `,
- `
- obj: [string]: { a: "B" }
- obj: beta: { b: 3 }
- `,
- ),
- out: `{obj:{alpha:{a:A,b:2},beta:{a:B,b:3}}}`,
- }, {
- // Structs that are shared in templates may have conflicting results.
- // However, this is not an issue as long as these value are not
- // referenced during evaluation. For generating JSON this is not an
- // issue as such fields are typically hidden.
- desc: "shared struct",
- instances: insts(`
- _shared: { a: "A" }
- obj: [string]: _shared & {}
- obj: alpha: { b: 2 }
- `,
- `
- _shared: { a: "B" }
- obj: [string]: _shared & {}
- obj: beta: { b: 3 }
- `,
- ),
- out: `{obj:{alpha:{a:A,b:2},beta:{a:B,b:3}}}`,
- }, {
- desc: "top-level comprehensions",
- instances: insts(`
- t: { for k, x in s {"\(k)": 10} }
- s: [string]: {}
- s: foo: a: 1
- `,
- `
- t: { for k, x in s {"\(k)": 10 } }
- s: [string]: {}
- s: bar: b: 2
- `,
- ),
- out: `{t:{foo:10,bar:10},s:{foo:{a:1},bar:{b:2}}}`,
- }, {
- desc: "error",
- instances: insts(`a:`),
- out: `{}`,
- isErr: true,
- }}
-
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- ctx := build.NewContext()
- in := []*build.Instance{}
- for _, str := range tc.instances {
- bi := ctx.NewInstance("dir", nil) // no packages
- _ = bi.AddFile("file", str)
- in = append(in, bi)
- }
- merged := Merge(Build(in)...)
- if err := merged.Err; err != nil {
- if !tc.isErr {
- t.Fatal(err)
- }
- }
-
- if got := toString(t, merged.Value()); got != tc.out {
- t.Errorf("\n got: %s\nwant: %s", got, tc.out)
- }
- })
- }
-}
-
-func TestInstance_Build(t *testing.T) {
- testCases := []struct {
- desc string
- instance string
- overlay string
- out string
- }{{
- desc: "single",
- instance: `a: {b: 1, c: 2}`,
- overlay: `res: a`,
- out: `{res:{b:1,c:2}}`,
- }}
-
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- ctx := build.NewContext()
-
- bi := ctx.NewInstance("main", nil) // no packages
- _ = bi.AddFile("file", tc.instance)
- main := Build([]*build.Instance{bi})
- if err := main[0].Err; err != nil {
- t.Fatal(err)
- }
-
- bi = ctx.NewInstance("overlay", nil) // no packages
- _ = bi.AddFile("file", tc.overlay)
-
- overlay := main[0].Build(bi)
-
- if got := toString(t, overlay.Value()); got != tc.out {
- t.Errorf("\n got: %s\nwant: %s", got, tc.out)
- }
- })
- }
-}
diff --git a/cue/kind.go b/cue/kind.go
deleted file mode 100644
index 1f95d00..0000000
--- a/cue/kind.go
+++ /dev/null
@@ -1,314 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
-)
-
-func unifyType(a, b kind) kind {
- const mask = topKind
- isRef := (a &^ mask) | (b &^ mask)
- return isRef | (a & b)
-}
-
-type kind uint16
-
-const (
- unknownKind kind = (1 << iota)
- nullKind
- boolKind
- intKind
- floatKind
- stringKind
- bytesKind
- durationKind
- listKind
- structKind
-
- lambdaKind
- // customKind
-
- // nonGround means that a value is not specific enough to be emitted.
- // Structs and lists are indicated as ground even when their values are not.
- nonGround
-
- // TODO: distinguish beteween nonGround and disjunctions?
-
- // a referenceKind is typically top and nonGround, but is indicated with an
- // additional bit. If reference is set and nonGround is not, it is possible
- // to move the reference to an assertion clause.
- referenceKind
-
- atomKind = (listKind - 1) &^ unknownKind
- addableKind = (structKind - 1) &^ unknownKind
- concreteKind = (lambdaKind - 1) &^ unknownKind
-
- // doneKind indicates a value can not further develop on its own (i.e. not a
- // reference). If doneKind is not set, but the result is ground, it
- // typically possible to hoist the reference out of a unification operation.
-
- // For rational numbers, typically both intKind and floatKind are set,
- // unless the range is restricted by a root type.
- numKind = intKind | floatKind
-
- comparableKind = (listKind - 1) &^ unknownKind
- stringableKind = scalarKinds | stringKind
- topKind = (referenceKind - 1) // all kinds, but not references
- typeKinds = (nonGround - 1) &^ unknownKind
- okKinds = typeKinds &^ bottomKind
- fixedKinds = okKinds &^ (structKind | lambdaKind)
- scalarKinds = numKind | durationKind
-
- bottomKind = 0
-)
-
-func isTop(v value) bool {
- _, ok := v.(*top)
- return ok
-}
-
-func isCustom(v value) bool {
- _, ok := v.(*customValidator)
- return ok
-}
-
-// isDone means that the value will not evaluate further.
-func (k kind) isDone() bool { return k&referenceKind == bottomKind }
-func (k kind) hasReferences() bool { return k&referenceKind != bottomKind }
-func (k kind) isConcrete() bool { return k&^(lambdaKind-1) == bottomKind }
-func (k kind) isGround() bool { return k&^(nonGround-1) == bottomKind }
-func (k kind) isAtom() bool { return k.isGround() && k&atomKind != bottomKind }
-func (k kind) isAnyOf(of kind) bool {
- return k&of != bottomKind
-}
-func (k kind) stringable() bool {
- return k.isGround() && k&stringKind|scalarKinds != bottomKind
-}
-
-func (k kind) String() string {
- str := ""
- if k&topKind == topKind {
- str = "_"
- goto finalize
- }
- for i := kind(1); i < referenceKind; i <<= 1 {
- t := ""
- switch k & i {
- case bottomKind:
- continue
- case nullKind:
- t = "null"
- case boolKind:
- t = "bool"
- case intKind:
- if k&floatKind != 0 {
- t = "number"
- } else {
- t = "int"
- }
- case floatKind:
- if k&intKind != 0 {
- continue
- }
- t = "float"
- case stringKind:
- t = "string"
- case bytesKind:
- t = "bytes"
- case durationKind:
- t = "duration"
- case listKind:
- t = "list"
- case structKind:
- t = "struct"
- case lambdaKind:
- t = "lambda"
- case nonGround, referenceKind:
- continue
- default:
- t = fmt.Sprintf("<unknown> %x", int(i))
- }
- if str != "" {
- str += "|"
- }
- str += t
- }
-finalize:
- if str == "" {
- return "_|_"
- }
- return str
-}
-
-// matchBinOpKind returns the result kind of applying the given op to operands with
-// the given kinds. The operation is disallowed if the return value is bottomKind. If
-// the second return value is true, the operands should be swapped before evaluation.
-//
-// Evaluating binary expressions uses this to
-// - fail incompatible operations early, even if the concrete types are
-// not known,
-// - check the result type of unification,
-//
-// Secondary goals:
-// - keep type compatibility mapped at a central place
-// - reduce the amount op type switching.
-// - simplifies testing
-func matchBinOpKind(op op, a, b kind) (k kind, swap bool, msg string) {
- if op == opDisjunction {
- return a | b, false, ""
- }
- u := unifyType(a, b)
- valBits := u & typeKinds
- catBits := u &^ typeKinds
- aGround := a&nonGround == 0
- bGround := b&nonGround == 0
- a = a & typeKinds
- b = b & typeKinds
- if valBits == bottomKind {
- msg := "invalid operation %[2]s %[1]s %[3]s (mismatched types %[4]s and %[5]s)"
- k := nullKind
- switch op {
- case opLss, opLeq, opGtr, opGeq:
- if a.isAnyOf(numKind) && b.isAnyOf(numKind) {
- return boolKind, false, ""
- }
- case opEql, opNeq:
- if a.isAnyOf(numKind) && b.isAnyOf(numKind) {
- return boolKind, false, ""
- }
- if a&nullKind != 0 {
- return k, false, ""
- }
- if b&nullKind != 0 {
- return k, true, ""
- }
- return bottomKind, false, msg
- case opUnify, opUnifyUnchecked:
- if a&nullKind != 0 {
- return k, false, ""
- }
- if b&nullKind != 0 {
- return k, true, ""
- }
- switch {
- case a.isGround() && !b.isGround():
- msg = "invalid value %[2]s (must be %[5]s)"
- case !a.isGround() && b.isGround():
- msg = "invalid value %[3]s (must be %[4]s)"
- default:
- msg = "conflicting values %[2]s and %[3]s (mismatched types %[4]s and %[5]s)"
- }
- return bottomKind, false, msg
- case opRem, opQuo, opMul, opAdd, opSub:
- if a.isAnyOf(numKind) && b.isAnyOf(numKind) {
- return floatKind, false, ""
- }
- }
- if op == opMul {
- if a.isAnyOf(listKind|stringKind|bytesKind) && b.isAnyOf(intKind) {
- return a | catBits, false, ""
- }
- if b.isAnyOf(listKind|stringKind|bytesKind) && a.isAnyOf(intKind) {
- return b | catBits, true, ""
- }
- }
- // non-overlapping types
- if a&scalarKinds == 0 || b&scalarKinds == 0 {
- return bottomKind, false, msg
- }
- // a and b have different numeric types.
- switch {
- case b.isAnyOf(durationKind):
- // a must be a numeric, non-duration type.
- if op == opMul {
- return durationKind | catBits, true, msg
- }
- case a.isAnyOf(durationKind):
- if opIn(op, opMul, opQuo, opRem) {
- return durationKind | catBits, false, msg
- }
- case op.isCmp():
- return boolKind, false, ""
- }
- return bottomKind, false, msg
- }
- switch {
- case aGround && bGround:
- // both ground values: nothing to do
-
- case op != opUnify && op != opLand && op != opLor && op != opNeq:
-
- default:
- swap = aGround && !bGround
- }
- // a and b have overlapping types.
- switch op {
- case opUnify, opUnifyUnchecked:
- // Increase likelihood of unification succeeding on first try.
- return u, swap, ""
-
- case opLand, opLor:
- if u.isAnyOf(boolKind) {
- return boolKind | catBits, swap, ""
- }
- case opMat, opNMat:
- if u.isAnyOf(stringKind | bytesKind) {
- return boolKind | catBits, false, ""
- }
- case opEql, opNeq:
- if u.isAnyOf(fixedKinds) {
- return boolKind | catBits, false, ""
- }
- case opLss, opLeq, opGeq, opGtr:
- if u.isAnyOf(fixedKinds) {
- return boolKind | catBits, false, ""
- }
- case opAdd:
- if u.isAnyOf(addableKind) {
- return u&(addableKind) | catBits, false, ""
- }
- case opSub:
- if u.isAnyOf(scalarKinds) {
- return u&scalarKinds | catBits, false, ""
- }
- case opRem:
- if u.isAnyOf(numKind) {
- return floatKind | catBits, false, ""
- }
- case opQuo:
- if u.isAnyOf(numKind) {
- return floatKind | catBits, false, ""
- }
- case opIRem, opIMod:
- if u.isAnyOf(intKind) {
- return u&(intKind) | catBits, false, ""
- }
- case opIQuo, opIDiv:
- if u.isAnyOf(intKind) {
- return intKind | catBits, false, ""
- }
- case opMul:
- if u.isAnyOf(numKind) {
- return u&numKind | catBits, false, ""
- }
- default:
- panic("unimplemented")
- }
- // TODO: localize
- msg = "invalid operation %[2]s %[1]s %[3]s"
- msg += fmt.Sprintf(" (operator '%s' not defined on %s)", op, valBits)
- return bottomKind, false, msg
-}
diff --git a/cue/kind_test.go b/cue/kind_test.go
deleted file mode 100644
index 48f48db..0000000
--- a/cue/kind_test.go
+++ /dev/null
@@ -1,63 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "testing"
-)
-
-func TestMatchBinOpKind(t *testing.T) {
- testCases := []struct {
- op op
- a kind
- b kind
- want kind
- }{{
- op: opMul,
- a: floatKind,
- b: numKind,
- want: floatKind,
- }, {
- op: opMul,
- a: intKind,
- b: numKind,
- want: intKind,
- }, {
- op: opMul,
- a: floatKind,
- b: intKind,
- want: floatKind,
- }, {
- op: opMul,
- a: listKind,
- b: intKind,
- want: listKind,
- }, {
- op: opMul,
- a: intKind,
- b: listKind,
- want: listKind,
- }}
- for _, tc := range testCases {
- key := fmt.Sprintf("%s(%v, %v)", tc.op, tc.a, tc.b)
- t.Run(key, func(t *testing.T) {
- got, _, _ := matchBinOpKind(tc.op, tc.a, tc.b)
- if got != tc.want {
- t.Errorf("got %v, want %v", got, tc.want)
- }
- })
- }
-}
diff --git a/cue/lit.go b/cue/lit.go
deleted file mode 100644
index 2d2c778..0000000
--- a/cue/lit.go
+++ /dev/null
@@ -1,82 +0,0 @@
-// 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 cue
-
-import (
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/literal"
- "cuelang.org/go/cue/token"
-)
-
-const base10 literal.Multiplier = 100
-
-type litParser struct {
- ctx *context
- num literal.NumInfo
-}
-
-func (p *litParser) parse(l *ast.BasicLit) (n value) {
- ctx := p.ctx
- s := l.Value
- if s == "" {
- return p.ctx.mkErr(newNode(l), "invalid literal %q", s)
- }
- switch l.Kind {
- case token.STRING:
- info, nStart, _, err := literal.ParseQuotes(s, s)
- if err != nil {
- return ctx.mkErr(newNode(l), err.Error())
- }
- s := s[nStart:]
- return parseString(ctx, l, info, s)
-
- case token.FLOAT, token.INT:
- err := literal.ParseNum(s, &p.num)
- if err != nil {
- return ctx.mkErr(newNode(l), err)
- }
- kind := floatKind
- if p.num.IsInt() {
- kind = intKind
- }
- n := newNum(newExpr(l), kind, 0)
- if err = p.num.Decimal(&n.X); err != nil {
- return ctx.mkErr(newNode(l), err)
- }
- return n
-
- case token.TRUE:
- return &boolLit{newExpr(l), true}
- case token.FALSE:
- return &boolLit{newExpr(l), false}
- case token.NULL:
- return &nullLit{newExpr(l)}
- default:
- return ctx.mkErr(newExpr(l), "unknown literal type")
- }
-}
-
-// parseString decodes a string without the starting and ending quotes.
-func parseString(ctx *context, node ast.Expr, q literal.QuoteInfo, s string) (n value) {
- src := newExpr(node)
- str, err := q.Unquote(s)
- if err != nil {
- return ctx.mkErr(src, "invalid string: %v", err)
- }
- if q.IsDouble() {
- return &stringLit{src, str, nil}
- }
- return &bytesLit{src, []byte(str), nil}
-}
diff --git a/cue/lit_test.go b/cue/lit_test.go
deleted file mode 100644
index 9cf25cf..0000000
--- a/cue/lit_test.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "math/big"
- "testing"
-
- "github.com/cockroachdb/apd/v2"
- "github.com/google/go-cmp/cmp"
- "github.com/google/go-cmp/cmp/cmpopts"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/literal"
- "cuelang.org/go/cue/token"
-)
-
-var testBase = newExpr(&ast.BasicLit{})
-
-func mkInt(a int64) *numLit {
- return newInt(testBase, base10).setInt64(a)
-}
-func mkIntString(a string) *numLit {
- return newInt(testBase, base10).setString(a)
-}
-func mkFloat(a string) *numLit {
- return newFloat(testBase, base10).setString(a)
-}
-
-var diffOpts = []cmp.Option{
- cmp.Comparer(func(x, y big.Rat) bool {
- return x.String() == y.String()
- }),
- cmp.Comparer(func(x, y big.Int) bool {
- return x.String() == y.String()
- }),
- cmp.AllowUnexported(
- nullLit{},
- boolLit{},
- stringLit{},
- bytesLit{},
- numLit{},
- ),
- cmpopts.IgnoreUnexported(
- bottom{},
- baseValue{},
- baseValue{},
- ),
-}
-
-var (
- nullSentinel = &nullLit{}
- trueSentinel = &boolLit{B: true}
- falseSentinel = &boolLit{B: false}
-)
-
-func TestLiterals(t *testing.T) {
- t.Skip()
- mkMul := func(x int64, m literal.Multiplier, base int) *numLit {
- return newInt(testBase, m).setInt64(x)
- }
- hk := newInt(testBase, 0).setInt64(100000)
- testCases := []struct {
- lit string
- node value
- }{
- {"0", mkInt(0)},
- {"null", nullSentinel},
- {"true", trueSentinel},
- {"false", falseSentinel},
- {"fls", &bottom{}},
- {`"foo"`, &stringLit{Str: "foo"}},
- {`#"foo"#`, &stringLit{Str: "foo"}},
- {`#""foo"#`, &stringLit{Str: `"foo`}},
- {`#" ""#`, &stringLit{Str: ` "`}},
- {`"\"foo\""`, &stringLit{Str: `"foo"`}},
- {`"foo\u0032"`, &stringLit{Str: `foo2`}},
- {`"foo\U00000033"`, &stringLit{Str: `foo3`}},
- {`"foo\U0001f499"`, &stringLit{Str: `foo💙`}},
- {`"\a\b\f\n\r\t\v"`, &stringLit{Str: "\a\b\f\n\r\t\v"}},
- {`"""
- """`, &stringLit{Str: ""}},
- {`"""
- abc
- """`, &stringLit{Str: "abc"}},
- {`"""
- abc
- def
- """`, &stringLit{Str: "abc\ndef"}},
- {`"""
- abc
- def
- """`, &stringLit{Str: "abc\n\tdef"}},
- {`'\xff'`, &bytesLit{B: []byte("\xff")}},
- {"1", mkInt(1)},
- {"100_000", hk},
- {"1.", mkFloat("1")},
- {"0.0", mkFloat("0.0")},
- {".0", mkFloat(".0")},
- {"012.34", mkFloat("012.34")},
- {".01", mkFloat(".01")},
- {".01e2", mkFloat("1")},
- {"0.", mkFloat("0.")},
- {"1K", mkMul(1000, literal.K, 10)},
- {".5K", mkMul(500, literal.K, 10)},
- {"1Mi", mkMul(1024*1024, literal.Mi, 10)},
- {"1.5Mi", mkMul((1024+512)*1024, literal.Mi, 10)},
- {"1.3Mi", &bottom{}}, // Cannot be accurately represented.
- {"1.3G", mkMul(1300000000, literal.G, 10)},
- {"1.3e+20", mkFloat("1.3e+20")},
- {"1.3e20", mkFloat("1.3e+20")},
- {"1.3e-5", mkFloat("1.3e-5")},
- {".3e-1", mkFloat("0.3e-1")},
- {"0e-5", mkFloat("0e-5")},
- {"0E-5", mkFloat("0e-5")},
- {"5e-5", mkFloat("5e-5")},
- {"5E-5", mkFloat("5e-5")},
- {"0x1234", mkMul(0x1234, 0, 16)},
- {"0xABCD", mkMul(0xABCD, 0, 16)},
- {"-0xABCD", mkMul(0xABCD, 0, 16)},
- {"0b11001000", mkMul(0xc8, 0, 2)},
- {"0b1", mkMul(1, 0, 2)},
- {"0o755", mkMul(0755, 0, 8)},
- {"0755", mkMul(0755, 0, 8)},
- }
- p := litParser{
- ctx: &context{Context: &apd.BaseContext},
- }
- for i, tc := range testCases {
- t.Run(fmt.Sprintf("%d/%+q", i, tc.lit), func(t *testing.T) {
- got := p.parse(&ast.BasicLit{Value: tc.lit})
- if !cmp.Equal(got, tc.node, diffOpts...) {
- t.Error(cmp.Diff(got, tc.node, diffOpts...))
- t.Errorf("%#v, %#v\n", got, tc.node)
- }
- })
- }
-}
-
-func TestLiteralErrors(t *testing.T) {
- testCases := []struct {
- kind token.Token
- lit string
- }{
- {token.STRING, `"foo\u"`},
- {token.STRING, `"foo\u003"`},
- {token.STRING, `"foo\U1234567"`},
- {token.STRING, `"foo\U12345678"`},
- {token.STRING, `"foo\Ug"`},
- {token.STRING, `"\xff"`},
- // not allowed in string literal, only binary
- {token.STRING, `"foo\x00"`},
- {token.INT, `0x`},
- {token.INT, `0o`},
- {token.INT, `0b`},
- {token.INT, `0_`},
- {token.INT, "0128"},
- {token.STRING, ``},
- {token.STRING, `"`},
- {token.STRING, `"a`},
- // wrong indentation
- {token.STRING, `"""
- abc
- def
- """`},
- // non-matching quotes
- {token.STRING, `"""
- abc
- '''`},
- {token.STRING, `"""
- abc
- "`},
- {token.STRING, `"abc \( foo "`},
- }
- p := litParser{
- ctx: &context{Context: &apd.BaseContext},
- }
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("%+q", tc.lit), func(t *testing.T) {
- got := p.parse(&ast.BasicLit{Kind: tc.kind, Value: tc.lit})
- if _, ok := got.(*bottom); !ok {
- t.Fatalf("expected error but found none")
- }
- })
- }
-}
diff --git a/cue/load/loader_test.go b/cue/load/loader_test.go
index a77936c..f3c4ec0 100644
--- a/cue/load/loader_test.go
+++ b/cue/load/loader_test.go
@@ -26,8 +26,8 @@
"github.com/kylelemons/godebug/diff"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/format"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/str"
)
diff --git a/cue/marshal.go b/cue/marshal.go
index fd3cbe1..c48d136 100644
--- a/cue/marshal.go
+++ b/cue/marshal.go
@@ -28,6 +28,7 @@
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/core/export"
)
// root.
@@ -141,7 +142,7 @@
return p
}
// TODO: support exporting instance
- file, _ := exportFile(ctx, nil, i.rootValue, options{raw: true})
+ file, _ := export.Def(r.idx.Runtime, i.root)
imports := []string{}
for _, i := range internal.Imports(file) {
for _, spec := range i.(*ast.ImportDecl).Specs {
@@ -151,8 +152,15 @@
}
if i.PkgName != "" {
- pkg := &ast.Package{Name: ast.NewIdent(i.PkgName)}
- file.Decls = append([]ast.Decl{pkg}, file.Decls...)
+ p, name, _ := internal.PackageInfo(file)
+ if p == nil {
+ pkg := &ast.Package{Name: ast.NewIdent(i.PkgName)}
+ file.Decls = append([]ast.Decl{pkg}, file.Decls...)
+ } else if name != i.PkgName {
+ // p is guaranteed to be generated by Def, so it is "safe" to
+ // modify.
+ p.Name = ast.NewIdent(i.PkgName)
+ }
}
b, err := format.Node(file)
diff --git a/cue/marshal_test.go b/cue/marshal_test.go
index b380b1a..64775aa 100644
--- a/cue/marshal_test.go
+++ b/cue/marshal_test.go
@@ -100,7 +100,7 @@
insts := func(i ...*instanceData) []*instanceData { return i }
pkg1 := &instanceData{
true,
- "pkg1",
+ "example.com/foo/pkg1",
files(`
package pkg1
@@ -137,7 +137,7 @@
files(
`package test
- import "pkg1"
+ import "example.com/foo/pkg1"
"Hello \(pkg1.Object)!"`),
}),
@@ -147,7 +147,7 @@
files(
`package test
- import pkg2 "pkg1"
+ import pkg2 "example.com/foo/pkg1"
pkg1: pkg2.Object
"Hello \(pkg1)!"`),
diff --git a/cue/resolve_test.go b/cue/resolve_test.go
index e7480da..b9ad808 100644
--- a/cue/resolve_test.go
+++ b/cue/resolve_test.go
@@ -15,3031 +15,12 @@
package cue
import (
- "flag"
"strings"
"testing"
+
+ "cuelang.org/go/internal/core/adt"
)
-var traceOn = flag.Bool("debug", false, "enable tracing")
-
-func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, error) {
- t.Helper()
- ctx, inst, err := compileInstance(t, body)
- return ctx, inst.rootValue.evalPartial(ctx).(*structLit), err
-}
-
-func compileFile(t *testing.T, body string) (*context, *structLit) {
- t.Helper()
- ctx, inst, errs := compileInstance(t, body)
- if errs != nil {
- t.Fatal(errs)
- }
- return ctx, inst.rootValue.evalPartial(ctx).(*structLit)
-}
-
-func compileInstance(t *testing.T, body string) (*context, *Instance, error) {
- var r Runtime
- inst, err := r.Compile("test", body)
-
- if err != nil {
- x := newInstance(newIndex(sharedIndex), nil)
- ctx := x.newContext()
- return ctx, x, err
- }
-
- return r.index().newContext(), inst, nil
-}
-
-func rewriteHelper(t *testing.T, cases []testCase, r rewriteMode) {
- for _, tc := range cases {
- t.Run(tc.desc, func(t *testing.T) {
- ctx, obj := compileFile(t, tc.in)
- ctx.trace = *traceOn
- root := testResolve(ctx, obj, r)
-
- got := debugStr(ctx, root)
-
- // Copy the result
- if got != tc.out {
- fn := t.Errorf
- if tc.skip {
- fn = t.Skipf
- }
- fn("output differs:\ngot %s\nwant %s", got, tc.out)
- }
- })
- }
-}
-
-type testCase struct {
- desc string
- in string
- out string
- skip bool
-}
-
-func TestBasicRewrite(t *testing.T) {
- testCases := []testCase{{
- desc: "errors",
- in: `
- a: _|_ & _|_
- b: null & _|_
- c: b.a == _|_
- d: _|_ != b.a
- e: _|_ == _|_
- `,
- out: `<0>{a: _|_(from source), b: _|_(from source), c: true, d: false, e: true}`,
- }, {
- desc: "regexp",
- in: `
- c1: "a" =~ "a"
- c2: "foo" =~ "[a-z]{3}"
- c3: "foo" =~ "[a-z]{4}"
- c4: "foo" !~ "[a-z]{4}"
-
- b1: =~ "a"
- b1: "a"
- b2: =~ "[a-z]{3}"
- b2: "foo"
- b3: =~ "[a-z]{4}"
- b3: "foo"
- b4: !~ "[a-z]{4}"
- b4: "foo"
-
- s1: != "b" & =~"c" // =~"c"
- s2: != "b" & =~"[a-z]" // != "b" & =~"[a-z]"
-
- e1: "foo" =~ 1
- e2: "foo" !~ true
- e3: != "a" & <5
- `,
- out: `<0>{c1: true, ` +
- `c2: true, ` +
- `c3: false, ` +
- `c4: true, ` +
-
- `b1: "a", ` +
- `b2: "foo", ` +
- `b3: _|_((=~"[a-z]{4}" & "foo"):invalid value "foo" (does not match =~"[a-z]{4}")), ` +
- `b4: "foo", ` +
-
- `s1: =~"c", ` +
- `s2: (!="b" & =~"[a-z]"), ` +
-
- `e1: _|_(("foo" =~ 1):invalid operation "foo" =~ 1 (mismatched types string and int)), ` +
- `e2: _|_(("foo" !~ true):invalid operation "foo" !~ true (mismatched types string and bool)), ` +
- `e3: _|_((!="a" & <5):conflicting values !="a" and <5 (mismatched types string and number))}`,
- }, {
- desc: "arithmetic",
- in: `
- i1: 1 & int
- i2: 2 & int
-
- sum: -1 + +2 // 1
- div1: 2.0 / 3 * 6 // 4
- div2: 2 / 3 * 6 // 4
- div3: 1.00 / 1.00
- divZero: 1.0 / 0
- div00: 0 / 0
- b: 1 != 4
- add: div1 + 1.0
-
- idiv00: 0 div 0
- imod00: 0 mod 0
- iquo00: 0 quo 0
- irem00: 0 rem 0
-
- v1: 1.0T/2.0
- v2: 2.0 == 2
- v3: 2.0/3.0
- v5: i1 div i2
-
- e0: 2 + "a"
- // these are now all alloweed
- // e1: 2.0 / i1
- // e2: i1 / 2.0
- // e3: 3.0 % i2
- // e4: i1 % 2.0
- e5: 1.0 div 2
- e6: 2 rem 2.0
- e7: 2 quo 2.0
- e8: 1.0 mod 1
- `,
- out: `<0>{i1: 1, i2: 2, ` +
- `sum: 1, ` +
- `div1: 4.00000000000000000000000, ` +
- `div2: 4.00000000000000000000000, ` +
- `div3: 1., ` +
- `divZero: _|_((1.0 / 0):division by zero), ` +
- `div00: _|_((0 / 0):division undefined), ` +
- `b: true, ` +
- `add: 5.00000000000000000000000, ` +
- `idiv00: _|_((0 div 0):division by zero), ` +
- `imod00: _|_((0 mod 0):division by zero), ` +
- `iquo00: _|_((0 quo 0):division by zero), ` +
- `irem00: _|_((0 rem 0):division by zero), ` +
- `v1: 5.0000000000e+11, ` +
- `v2: true, ` +
- `v3: 0.666666666666666666666667, ` +
- `v5: 0, ` +
-
- `e0: _|_((2 + "a"):invalid operation 2 + "a" (mismatched types int and string)), ` +
- // `e1: _|_((2.0 / 1):unsupported op /(float, int)), ` +
- // `e2: _|_((1 / 2.0):unsupported op /(int, float)), ` +
- // `e3: _|_((3.0 % 2):unsupported op %(float, int)), ` +
- // `e4: _|_((1 % 2.0):unsupported op %(int, float)), ` +
- `e5: _|_((1.0 div 2):invalid operation 1.0 div 2 (mismatched types float and int)), ` +
- `e6: _|_((2 rem 2.0):invalid operation 2 rem 2.0 (mismatched types int and float)), ` +
- `e7: _|_((2 quo 2.0):invalid operation 2 quo 2.0 (mismatched types int and float)), ` +
- `e8: _|_((1.0 mod 1):invalid operation 1.0 mod 1 (mismatched types float and int))}`,
- }, {
- desc: "integer-specific arithmetic",
- in: `
- q1: 5 quo 2 // 2
- q2: 5 quo -2 // -2
- q3: -5 quo 2 // -2
- q4: -5 quo -2 // 2
- qe1: 2.0 quo 1
- qe2: 2 quo 1.0
-
- r1: 5 rem 2 // 1
- r2: 5 rem -2 // 1
- r3: -5 rem 2 // -1
- r4: -5 rem -2 // -1
- re1: 2.0 rem 1
- re2: 2 rem 1.0
-
- d1: 5 div 2 // 2
- d2: 5 div -2 // -2
- d3: -5 div 2 // -3
- d4: -5 div -2 // 3
- de1: 2.0 div 1
- de2: 2 div 1.0
-
- m1: 5 mod 2 // 1
- m2: 5 mod -2 // 1
- m3: -5 mod 2 // 1
- m4: -5 mod -2 // 1
- me1: 2.0 mod 1
- me2: 2 mod 1.0
- `,
- out: `<0>{q1: 2, q2: -2, q3: -2, q4: 2, ` +
- `qe1: _|_((2.0 quo 1):invalid operation 2.0 quo 1 (mismatched types float and int)), ` +
- `qe2: _|_((2 quo 1.0):invalid operation 2 quo 1.0 (mismatched types int and float)), ` +
- `r1: 1, r2: 1, r3: -1, r4: -1, ` +
- `re1: _|_((2.0 rem 1):invalid operation 2.0 rem 1 (mismatched types float and int)), ` +
- `re2: _|_((2 rem 1.0):invalid operation 2 rem 1.0 (mismatched types int and float)), ` +
- `d1: 2, d2: -2, d3: -3, d4: 3, ` +
- `de1: _|_((2.0 div 1):invalid operation 2.0 div 1 (mismatched types float and int)), ` +
- `de2: _|_((2 div 1.0):invalid operation 2 div 1.0 (mismatched types int and float)), ` +
- `m1: 1, m2: 1, m3: 1, m4: 1, ` +
- `me1: _|_((2.0 mod 1):invalid operation 2.0 mod 1 (mismatched types float and int)), ` +
- `me2: _|_((2 mod 1.0):invalid operation 2 mod 1.0 (mismatched types int and float))}`,
- }, {
- desc: "booleans",
- in: `
- t: true
- t: !false
- f: false
- f: !t
- e: true
- e: !true
- `,
- out: "<0>{t: true, f: false, e: _|_(true:conflicting values true and false)}",
- }, {
- desc: "boolean arithmetic",
- in: `
- a: true && true
- b: true || false
- c: false == true
- d: false != true
- e: true & true
- f: true & false
- `,
- out: "<0>{a: true, b: true, c: false, d: true, e: true, f: _|_(true:conflicting values true and false)}",
- }, {
- desc: "basic type",
- in: `
- a: 1 & int
- b: number & 1
- c: 1.0
- c: float
- d: int & float // _|_
- e: "4" & string
- f: true
- f: bool
- `,
- out: `<0>{a: 1, b: 1, c: 1.0, d: _|_((int & float):conflicting values int and float (mismatched types int and float)), e: "4", f: true}`, // TODO: eliminate redundancy
- }, {
- desc: "strings and bytes",
- in: `
- s0: "foo" + "bar"
- s1: 3 * "abc"
- s2: "abc" * 2
-
- b0: 'foo' + 'bar'
- b1: 3 * 'abc'
- b2: 'abc' * 2
-
- // TODO: consider the semantics of this and perhaps allow this.
- e0: "a" + ''
- e1: 'b' + "c"
- `,
- out: `<0>{` +
- `s0: "foobar", ` +
- `s1: "abcabcabc", ` +
- `s2: "abcabc", ` +
- `b0: 'foobar', ` +
- `b1: 'abcabcabc', ` +
- `b2: 'abcabc', ` +
-
- `e0: _|_(("a" + ''):invalid operation "a" + '' (mismatched types string and bytes)), ` +
- `e1: _|_(('b' + "c"):invalid operation 'b' + "c" (mismatched types bytes and string))` +
- `}`,
- }, {
- desc: "escaping",
-
- in: `
- a: "foo\nbar",
- b: a,
-
- // TODO: mimic http://exploringjs.com/es6/ch_template-literals.html#sec_introduction-template-literals
- `,
- out: `<0>{a: "foo\nbar", b: "foo\nbar"}`,
- // out: `<0>{a: "foo\nbar", b: <0>.a}`,
- }, {
- desc: "reference",
- in: `
- a: b
- b: 2
- d: {
- d: 3
- e: d
- }
- e: {
- e: {
- v: 1
- }
- f: {
- v: e.v
- }
- }
- `,
- out: "<0>{a: 2, b: 2, d: <1>{d: 3, e: 3}, e: <2>{e: <3>{v: 1}, f: <4>{v: 1}}}",
- }, {
- desc: "lists",
- in: `
- list: [1,2,3]
- index: [1,2,3][1]
- unify: [1,2,3] & [_,2,3]
- e: [] & 4
- e2: [3]["d"]
- e3: [3][-1]
- e4: [1, 2, ...>=4 & <=5] & [1, 2, 4, 8]
- e5: [1, 2, 4, 8] & [1, 2, ...>=4 & <=5]
- `,
- out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):conflicting values [] and 4 (mismatched types list and int)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: [1,2,4,_|_((<=5 & 8):invalid value 8 (out of bound <=5))], e5: [1,2,4,_|_((<=5 & 8):invalid value 8 (out of bound <=5))]}`,
- }, {
- desc: "list arithmetic",
- in: `
- list: [1,2,3]
- mul0: list*0
- mul1: list*1
- mul2: 2*list
- list1: [1]
- mul1_0: list1*0
- mul1_1: 1*list1
- mul1_2: list1*2
- e: list*-1
- `,
- out: `<0>{list: [1,2,3], ` +
- `mul0: [], ` +
- `mul1: [1,2,3], ` +
- `mul2: [1,2,3,1,2,3], ` +
- `list1: [1], ` +
- `mul1_0: [], ` +
- `mul1_1: [1], ` +
- `mul1_2: [1,1], ` +
- `e: _|_((<1>.list * -1):negative number -1 multiplies list)}`,
- }, {
- desc: "selecting",
- in: `
- obj: {a: 1, b: 2}
- index: {a: 1, b: 2}["b"]
- mulidx: {a: 1, b: {a:1, b: 3}}["b"]["b"]
- e: {a: 1}[4]
- f: {a: 1}.b
- g: {a: 1}["b"]
- h: [3].b
- `,
- out: `<0>{obj: <1>{a: 1, b: 2}, index: 2, mulidx: 3, e: _|_(4:invalid struct index 4 (type int)), f: <2>{a: 1}.b, g: <3>{a: 1}["b"], h: _|_([3]:invalid operation: [3].b (type list does not support selection))}`,
- }, {
- desc: "obj unify",
- in: `
- o1: {a: 1 } & { b: 2} // {a:1,b:2}
- o2: {a: 1, b:2 } & { b: 2} // {a:1,b:2}
- o3: {a: 1 } & { a:1, b: 2} // {a:1,b:2}
- o4: {a: 1 } & { b: 2} // {a:1,b:2}
- o4: {a: 1, b:2 } & { b: 2}
- o4: {a: 1 } & { a:1, b: 2}
- e: 1 // 1 & {a:3}
- e: {a:3}
- `,
- out: "<0>{o1: <1>{a: 1, b: 2}, o2: <2>{a: 1, b: 2}, o3: <3>{a: 1, b: 2}, o4: <4>{a: 1, b: 2}, e: _|_((1 & <5>{a: 3}):conflicting values 1 and {a: 3} (mismatched types int and struct))}",
- }, {
- desc: "disjunctions",
- in: `
- o1: 1 | 2 | 3
- o2: (1 | 2 | 3) & 1
- o3: 2 & (1 | *2 | 3)
- o4: (1 | *2 | 3) & (1 | 2 | *3)
- o5: (1 | *2 | 3) & (3 | *2 | 1)
- o6: (1 | 2 | 3) & (3 | 1 | 2)
- o7: (1 | 2 | 3) & (2 | 3)
- o8: (1 | 2 | 3) & (3 | 2)
- o9: (2 | 3) & (1 | 2 | 3)
- o10: (3 | 2) & (1 | *2 | 3)
-
- m1: (*1 | (*2 | 3)) & (>=2 & <=3)
- m2: (*1 | (*2 | 3)) & (2 | 3)
- m3: (*1 | *(*2 | 3)) & (2 | 3)
- m4: (2 | 3) & (*2 | 3)
- m5: (*2 | 3) & (2 | 3)
-
- // (*2 | 3) & (2 | 3)
- // (2 | 3) & (*2 | 3)
- // 2&(*2 | 3) | 3&(*2 | 3)
- // (*1 | (*2 | 3)) & (2 | 3)
- // *1& (2 | 3) | (*2 | 3)&(2 | 3)
- // *2&(2 | 3) | 3&(2 | 3)
-
- // (2 | 3)&(*1 | (*2 | 3))
- // 2&(*1 | (*2 | 3)) | 3&(*1 | (*2 | 3))
- // *1&2 | (*2 | 3)&2 | *1&3 | (*2 | 3)&3
- // (*2 | 3)&2 | (*2 | 3)&3
- // *2 | 3
-
-
- // All errors are treated the same as per the unification model.
- i1: [1, 2][3] | "c"
- `,
- out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | 2 | 3 | *_|_), o5: (1 | *2 | 3), o6: (1 | 2 | 3), o7: (2 | 3), o8: (2 | 3), o9: (2 | 3), o10: (3 | *2), m1: (*2 | 3), m2: (*2 | 3), m3: (*2 | 3), m4: (*2 | 3), m5: (*2 | 3), i1: "c"}`,
- }, {
- desc: "types",
- in: `
- i: int
- j: int & 3
- s: string
- t: "s" & string
- e: int & string
- e2: 1 & string
- b: !int
- p: +true
- m: -false
- `,
- out: `<0>{i: int, j: 3, s: string, t: "s", e: _|_((int & string):conflicting values int and string (mismatched types int and string)), e2: _|_((1 & string):conflicting values 1 and string (mismatched types int and string)), b: _|_(!int:invalid operation !int (! int)), p: _|_(+true:invalid operation +true (+ bool)), m: _|_(-false:invalid operation -false (- bool))}`,
- }, {
- desc: "comparison",
- in: `
- lss: 1 < 2
- leq: 1 <= 1.0
- leq: 2.0 <= 3
- eql: 1 == 1.0
- neq: 1.0 == 1
- gtr: !(2 > 3)
- geq: 2.0 >= 2
- seq: "a" + "b" == "ab"
- err: 2 == "s"
- `,
- out: `<0>{lss: true, leq: true, eql: true, neq: true, gtr: true, geq: true, seq: true, err: _|_((2 == "s"):invalid operation 2 == "s" (mismatched types int and string))}`,
- }, {
- desc: "null",
- in: `
- eql: null == null
- neq: null != null
- unf: null & null
-
- // errors
- eq1: null == 1
- eq2: 1 == null
- ne1: "s" != null
- call: null()
- `,
- out: `<0>{eql: true, neq: false, unf: null, eq1: false, eq2: false, ne1: true, call: _|_(null:cannot call non-function null (type null))}`,
- }, {
- desc: "self-reference cycles",
- in: `
- a: b - 100
- b: a + 100
-
- c: [c[1], c[0]]
- `,
- out: `<0>{a: (<1>.b - 100), ` +
- `b: (<1>.a + 100), ` +
- `c: [<1>.c[1],<1>.c[0]]}`,
- }, {
- desc: "resolved self-reference cycles",
- in: `
- a: b - 100
- b: a + 100
- b: 200
-
- c: [c[1], a]
-
- s1: s2 & {a: 1}
- s2: s3 & {b: 2}
- s3: s1 & {c: 3}
- `,
- out: `<0>{a: 100, b: 200, c: [100,100], s1: <1>{a: 1, b: 2, c: 3}, s2: <2>{a: 1, b: 2, c: 3}, s3: <3>{a: 1, b: 2, c: 3}}`,
- }, {
- desc: "resolved self-reference cycles: Issue 19",
- in: `
- // CUE knows how to resolve the following:
- x: y + 100
- y: x - 100
- x: 200
-
- z1: z2 + 1
- z2: z3 + 2
- z3: z1 - 3
- z3: 8
-
- // TODO: extensive tests with disjunctions.
- `,
- out: `<0>{x: 200, y: 100, z1: 11, z2: 10, z3: 8}`,
- }, {
- desc: "delayed constraint failure",
- in: `
- a: b - 100
- b: a + 110
- b: 200
-
- x: 100
- x: x + 1
- `,
- out: `<0>{` +
- `x: _|_((100 & 101):conflicting values 100 and 101), ` +
- `a: _|_((210 & 200):conflicting values 210 and 200), ` +
- `b: _|_((210 & 200):conflicting values 210 and 200)}`,
- // TODO: find a way to mark error in data.
- }}
- rewriteHelper(t, testCases, evalPartial)
-}
-
-func TestChooseDefault(t *testing.T) {
- testCases := []testCase{{
- desc: "pick first",
- in: `
- a: *5 | "a" | true
- b: c: *{
- a: 2
- } | {
- a : 3
- }
- `,
- out: "<0>{a: 5, b: <1>{c: <2>{a: 2}}}",
- }, {
- // In this test, default results to bottom, meaning that the non-default
- // value remains.
- desc: "simple disambiguation conflict",
- in: `
- a: *"a" | "b"
- b: *"b" | "a"
- c: a & b
- `,
- out: `<0>{a: "a", b: "b", c: ("a" | "b")}`,
- }, {
- desc: "associativity of defaults",
- in: `
- a: *"a" | ("b" | "c")
- b: (*"a" | "b") | "c"
- c: *"a" | (*"b" | "c")
- x: a & b
- y: b & c
- `,
- out: `<0>{x: "a", y: (*"a" | *"b"), a: "a", b: "a", c: (*"a" | *"b")}`,
- }}
- rewriteHelper(t, testCases, evalFull)
-}
-
-func TestResolve(t *testing.T) {
- testCases := []testCase{{
- desc: "convert _ to top",
- in: `a: { [_]: _ }`,
- out: `<0>{a: <1>{...}}`,
- }, {
- in: `
- a: b.c.d
- b: c: { d: 3 }
- c: { c: d.d, }
- d: { d: 2 }
- `,
- out: "<0>{a: 3, b: <1>{c: <2>{d: 3}}, c: <3>{c: 2}, d: <4>{d: 2}}",
- }, {
- in: "`foo-bar`: 3\n x: `foo-bar`,",
- out: `<0>{x: 3, "foo-bar": 3}`,
- }, {
- desc: "resolution of quoted identifiers",
- in: `
- package foo
-
-` + "`foo-bar`" + `: 2
-"baz": ` + "`foo-bar`" + `
-
-a: {
- qux: 3
- ` + "`qux-quux`" + `: qux
- "qaz": ` + "`qux-quux`" + `
-}`,
- out: `<0>{"foo-bar": 2, baz: 2, a: <1>{qux: 3, "qux-quux": 3, qaz: 3}}`,
- }, {
- in: `
- a: _
- b: a
- a: { d: 1, d: _ }
- b: _
- `,
- out: `<0>{a: <1>{d: 1}, b: <2>{d: 1}}`,
- }, {
- desc: "JSON",
- in: `
- a="a": 3
- b: a
- o: { "a\nb": 2 } // TODO: use $ for root?
- c: o["a\nb"]
- `,
- out: `<0>{a: 3, b: 3, o: <1>{"a\nb": 2}, c: 2}`,
- }, {
- desc: "arithmetic",
- in: `
- v1: 1.0T/2.0
- v2: 2.0 == 2
- n1: 1
- v5: 2.0 / n1
- v6: 1.0 / 1.0
- e2: int & 4.0/2.0
- `,
- out: `<0>{v1: 5.0000000000e+11, v2: true, n1: 1, v5: 2.0, v6: 1., e2: _|_((int & (4.0 / 2.0)):conflicting values int and (4.0 / 2.0) (mismatched types int and float))}`,
- }, {
- desc: "inequality",
- in: `
- a: 1 != 2
- b: 1 != null
- c: true == null
- d: null != {}
- e: null == []
- f: 0 == 0.0 // types are unified first TODO: make this consistent
- `,
- out: `<0>{a: true, b: true, c: false, d: true, e: false, f: true}`,
- }, {
- desc: "attributes",
- in: `
- a: { foo: 1 @foo() @baz(1) }
- b: { foo: 1 @bar() @foo() }
- c: a & b
-
- e: a & { foo: 1 @foo(other) }
- `,
- out: `<0>{a: <1>{foo: 1 @baz(1) @foo()}, ` +
- `b: <2>{foo: 1 @bar() @foo()}, ` +
- `c: <3>{foo: 1 @bar() @baz(1) @foo()}, ` +
- `e: _|_((<4>.a & <5>{foo: 1 @foo(other)}):conflicting attributes for key "foo")}`,
- }, {
- desc: "optional field unification",
- in: `
- a: { foo?: string }
- b: { foo: "foo" }
- c: a & b
- d: a & { "foo"?: "bar" }
-
- g1: 1
- "g\(1)"?: 1
- "g\(2)"?: 2
- `,
- out: `<0>{a: <1>{foo?: string}, ` +
- `b: <2>{foo: "foo"}, ` +
- `c: <3>{foo: "foo"}, ` +
- `d: <4>{foo?: "bar"}, ` +
- `g1: 1, ` +
- `g2?: 2}`,
- }, {
- desc: "optional field resolves to incomplete",
- in: `
- r: {
- a?: 3
- b: a
- c: r["a"]
- }
- `,
- out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <3>.r["a"]}}`,
- // TODO(#152): should be
- // out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <2>["a"]}}`,
- }, {
- desc: "bounds",
- in: `
- i1: >1 & 5
- i2: (>=0 & <=10) & 5
- i3: !=null & []
- i4: !=2 & !=4
-
-
- s1: >=0 & <=10 & !=1 // no simplification
- s2: >=0 & <=10 & !=11 // >=0 & <=10
- s3: >5 & !=5 // >5
- s4: <10 & !=10 // <10
- s5: !=2 & !=2
-
- // TODO: could change inequality
- s6: !=2 & >=2
- s7: >=2 & !=2
-
- s8: !=5 & >5
-
- s10: >=0 & <=10 & <12 & >1 // >1 & <=10
- s11: >0 & >=0 & <=12 & <12 // >0 & <12
-
- s20: >=10 & <=10 // 10
-
- s22: >5 & <=6 // no simplification
- s22a: >5 & (<=6 & int) // 6
- s22b: (int & >5) & <=6 // 6
- s22c: >=5 & (<6 & int) // 5
- s22d: (int & >=5) & <6 // 5
- s22e: (>=5 & <6) & int // 5
- s22f: int & (>=5 & <6) // 5
-
- s23: >0 & <2 // no simplification
- s23a: (>0 & <2) & int // int & 1
- s23b: int & (>0 & <2) // int & 1
- s23c: (int & >0) & <2 // int & 1
- s23d: >0 & (int & <2) // int & 1
- s23e: >0.0 & <2.0 // no simplification
-
- s30: >0 & int
-
- e1: null & !=null
- e2: !=null & null
- e3: >1 & 1
- e4: <0 & 0
- e5: >1 & <0
- e6: >11 & <11
- e7: >=11 & <11
- e8: >11 & <=11
- e9: >"a" & <1
- `,
- out: `<0>{i1: 5, i2: 5, i3: [], i4: (!=2 & !=4), ` +
-
- `s1: (>=0 & <=10 & !=1), ` +
- `s2: (>=0 & <=10), ` +
- `s3: >5, ` +
- `s4: <10, ` +
- `s5: !=2, ` +
-
- `s6: (!=2 & >=2), ` +
- `s7: (>=2 & !=2), ` +
-
- `s8: >5, ` +
-
- `s10: (<=10 & >1), ` +
- `s11: (>0 & <12), ` +
-
- `s20: 10, ` +
-
- `s22: (>5 & <=6), ` +
- `s22a: 6, ` +
- `s22b: 6, ` +
- `s22c: 5, ` +
- `s22d: 5, ` +
- `s22e: 5, ` +
- `s22f: 5, ` +
-
- `s23: (>0 & <2), ` +
- `s23a: 1, ` +
- `s23b: 1, ` +
- `s23c: 1, ` +
- `s23d: 1, ` +
- `s23e: (>0.0 & <2.0), ` +
-
- `s30: int & >0, ` +
-
- `e1: _|_((!=null & null):invalid value null (excluded by !=null)), ` +
- `e2: _|_((!=null & null):invalid value null (excluded by !=null)), ` +
- `e3: _|_((>1 & 1):invalid value 1 (out of bound >1)), ` +
- `e4: _|_((<0 & 0):invalid value 0 (out of bound <0)), ` +
- `e5: _|_(conflicting bounds >1 and <0), ` +
- `e6: _|_(conflicting bounds >11 and <11), ` +
- `e7: _|_(conflicting bounds >=11 and <11), ` +
- `e8: _|_(conflicting bounds >11 and <=11), ` +
- `e9: _|_((>"a" & <1):conflicting values >"a" and <1 (mismatched types string and number))}`,
- }, {
- desc: "bound conversions",
- in: `
- r0: int & >0.1 & <=1.9
- r1: int & >0.1 & <1.9
- r2: int & >=0.1 & <1.9
- r3: int & >=-1.9 & <=-0.1
- r4: int & >-1.9 & <=-0.1
-
- r5: >=1.1 & <=1.1
- r6: r5 & 1.1
-
- c1: (1.2 & >1.3) & <2
- c2: 1.2 & (>1.3 & <2)
-
- c3: 1.2 & (>=1 & <2)
- c4: 1.2 & (>=1 & <2 & int)
- `,
- out: `<0>{` +
- `r0: 1, ` +
- `r1: 1, ` +
- `r2: 1, ` +
- `r3: -1, ` +
- `r4: -1, ` +
- `r5: 1.1, ` +
- `r6: 1.1, ` +
- `c1: _|_((>1.3 & 1.2):invalid value 1.2 (out of bound >1.3)), ` +
- `c2: _|_((>1.3 & 1.2):invalid value 1.2 (out of bound >1.3)), ` +
- `c3: 1.2, ` +
- `c4: _|_((1.2 & ((>=1 & <2) & int)):conflicting values 1.2 and ((>=1 & <2) & int) (mismatched types float and int))}`,
- }, {
- desc: "custom validators",
- in: `
- import "strings"
-
- a: strings.ContainsAny("ab")
- a: "after"
-
- b: strings.ContainsAny("c")
- b: "dog"
-
- c: strings.ContainsAny("d") & strings.ContainsAny("g")
- c: "dog"
- `,
- out: `<0>{` +
- `a: "after", ` +
- `b: _|_(strings.ContainsAny ("c"):invalid value "dog" (does not satisfy strings.ContainsAny("c"))), ` +
- `c: "dog"` +
- `}`,
- }, {
- desc: "null coalescing",
- in: `
- a: null
- b: a.x | "b"
- c: a["x"] | "c"
- `,
- out: `<0>{a: null, b: "b", c: "c"}`,
- }, {
- desc: "reference across tuples and back",
- // Tests that it is okay to partially evaluate structs.
- in: `
- a: { c: b.e, d: b.f }
- b: { e: 3, f: a.c }
- `,
- out: "<0>{a: <1>{c: 3, d: 3}, b: <2>{e: 3, f: 3}}",
- }, {
- desc: "index",
- in: `
- a: [2][0]
- b: {foo:"bar"}["foo"]
- c: (*l|{"3":3})["3"]
- d: (*[]|[1])[0]
- l: []
- e1: [2][""]
- e2: 2[2]
- e3: [][true]
- e4: [1,2,3][3]
- e5: [1,2,3][-1]
- e6: (*[]|{})[1]
- def: {
- a: 1
- #b: 3
- }
- e7: def["b"]
- `,
- out: `<0>{a: 2, b: "bar", c: _|_("3":invalid list index "3" (type string)), l: [], d: _|_([]:index 0 out of bounds), e1: _|_("":invalid list index "" (type string)), e2: _|_(2:invalid operation: 2[2] (type int does not support indexing)), e3: _|_(true:invalid list index true (type bool)), e4: _|_([1,2,3]:index 3 out of bounds), e5: _|_(-1:invalid list index -1 (index must be non-negative)), e6: _|_([]:index 1 out of bounds), def: <1>{a: 1, #b: 3}, e7: <2>.def["b"]}`,
- // }, {
- // NOTE: string indexing no longer supported.
- // Keeping it around until this is no longer an experiment.
- // desc: "string index",
- // in: `
- // a0: "abc"[0]
- // a1: "abc"[1]
- // a2: "abc"[2]
- // a3: "abc"[3]
- // a4: "abc"[-1]
-
- // b: "zoëven"[2]
- // `,
- // out: `<0>{a0: "a", a1: "b", a2: "c", a3: _|_("abc":index 3 out of bounds), a4: _|_(-1:invalid string index -1 (index must be non-negative)), b: "ë"}`,
- }, {
- desc: "disjunctions of lists",
- in: `
- l: [ int, int ] | [ string, string ]
-
- l1: [ "a", "b" ]
- l2: l & [ "c", "d" ]
- `,
- out: `<0>{l: ([int,int] | [string,string]), l1: ["a","b"], l2: ["c","d"]}`,
- }, {
- desc: "slice",
- in: `
- a: [2][0:0]
- b: [0][1:1]
- e1: [][1:1]
- e2: [0][-1:0]
- e3: [0][1:0]
- e4: [0][1:2]
- e5: 4[1:2]
- e6: [2]["":]
- e7: [2][:"9"]
-
- `,
- out: `<0>{a: [], b: [], e1: _|_(1:slice bounds out of range), e2: _|_([0]:negative slice index), e3: _|_([0]:invalid slice index: 1 > 0), e4: _|_(2:slice bounds out of range), e5: _|_(4:cannot slice 4 (type int)), e6: _|_("":invalid slice index "" (type string)), e7: _|_("9":invalid slice index "9" (type string))}`,
- // }, {
- // NOTE: string indexing no longer supported.
- // Keeping it around until this is no longer an experiment.
- // desc: "string slice",
- // in: `
- // a0: ""[0:0]
- // a1: ""[:]
- // a2: ""[0:]
- // a3: ""[:0]
- // b0: "abc"[0:0]
- // b1: "abc"[0:1]
- // b2: "abc"[0:2]
- // b3: "abc"[0:3]
- // b4: "abc"[3:3]
- // b5: "abc"[1:]
- // b6: "abc"[:2]
-
- // // TODO: supported extended graphemes, instead of just runes.
- // u: "Spaß"[3:4]
- // `,
- // out: `<0>{a0: "", a1: "", a2: "", a3: "", b0: "", b1: "a", b2: "ab", b3: "abc", b4: "", b5: "bc", b6: "ab", u: "ß"}`,
- }, {
- desc: "list types",
- in: `
- l0: 3*[int]
- l0: [1, 2, 3]
- l2: [...{ a: int }]
- l2: [{a: 1}, {a: 2, b: 3}]
-
- // TODO: work out a decent way to specify length ranges of lists.
- // l3: <=10*[int]
- // l3: [1, 2, 3, ...]
-
- s1: (6*[int])[2:3]
- s2: [0,2,3][1:2]
-
- i1: (6*[int])[2]
- i2: [0,2,3][2]
-
- t0: [...{a: 8}]
- t0: [{}]
- t1: [...]
- t1: [...int]
-
- e0: 2*[{}]
- e0: [{}]
- e1: [...int]
- e1: [...float]
- `,
- out: `<0>{` +
- `l0: [1,2,3], ` +
- `l2: [<1>{a: 1},<2>{a: 2, b: 3}], ` +
- `s1: [int], ` +
- `s2: [2], ` +
- `i1: int, ` +
- `i2: 3, ` +
- `t0: [<3>{a: 8}], ` +
- `t1: [, ...int], ` +
- `e0: _|_(([<4>{},<4>{}] & [<5>{}]):conflicting list lengths: conflicting values 2 and 1), ` +
- `e1: [, ..._|_((int & float):conflicting values int and float (mismatched types int and float))]` +
- `}`,
- }, {
- // TODO: consider removing list arithmetic altogether. It is no longer
- // needed to indicate the allowed capacity of a list and that didn't
- // work anyway.
- desc: "list arithmetic",
- in: `
- l0: 3*[1, 2, 3]
- l1: 0*[1, 2, 3]
- l2: 10*[]
- l3: <=2*[]
- l4: <=2*[int]
- l5: <=2*(int*[int])
- l6: 3*[...int]
- l7: 3*[1, ...int]
- l8: 3*[1, 2, ...int]
-
- s0: [] + []
- s1: [1] + []
- s2: [] + [2]
- s3: [1] + [2]
- s4: [1,2] + []
- s5: [] + [1,2]
- s6: [1] + [1,2]
- s7: [1,2] + [1]
- s8: [1,2] + [1,2]
- s9: [] + [...]
- s10: [1] + [...]
- s11: [] + [2, ...]
- s12: [1] + [2, ...]
- s13: [1,2] + [...]
- s14: [] + [1,2, ...]
- s15: [1] + [1,2, ...]
- s16: [1,2] + [1, ...]
- s17: [1,2] + [1,2, ...]
-
- s18: [...] + []
- s19: [1, ...] + []
- s20: [...] + [2]
- s21: [1, ...] + [2]
- s22: [1,2, ...] + []
- s23: [...] + [1,2]
- s24: [1, ...] + [1,2]
- s25: [1,2, ...] + [1]
- s26: [1,2, ...] + [1,2]
- s27: [...] + [...]
- s28: [1, ...] + [...]
- s29: [...] + [2, ...]
- s30: [1, ...] + [2, ...]
- s31: [1,2, ...] + [...]
- s32: [...] + [1,2, ...]
- s33: [1, ...] + [1,2, ...]
- s34: [1,2, ...] + [1, ...]
- s35: [1,2, ...] + [1,2, ...]
- `,
- out: `<0>{l0: [1,2,3,1,2,3,1,2,3], ` +
- `l1: [], ` +
- `l2: [], ` +
- `l3: (<=2 * []), ` +
- `l4: (<=2 * [int]), ` +
- `l5: (<=2 * (int * [int])), ` +
- `l6: [], ` +
- `l7: [1,1,1], ` +
- `l8: [1,2,1,2,1,2], ` +
-
- `s0: [], ` +
- `s1: [1], ` +
- `s2: [2], ` +
- `s3: [1,2], ` +
- `s4: [1,2], ` +
- `s5: [1,2], ` +
- `s6: [1,1,2], ` +
- `s7: [1,2,1], ` +
- `s8: [1,2,1,2], ` +
- `s9: [], ` +
- `s10: [1], ` +
- `s11: [2], ` +
- `s12: [1,2], ` +
- `s13: [1,2], ` +
- `s14: [1,2], ` +
- `s15: [1,1,2], ` +
- `s16: [1,2,1], ` +
- `s17: [1,2,1,2], ` +
-
- `s18: [], ` +
- `s19: [1], ` +
- `s20: [2], ` +
- `s21: [1,2], ` +
- `s22: [1,2], ` +
- `s23: [1,2], ` +
- `s24: [1,1,2], ` +
- `s25: [1,2,1], ` +
- `s26: [1,2,1,2], ` +
- `s27: [], ` +
- `s28: [1], ` +
- `s29: [2], ` +
- `s30: [1,2], ` +
- `s31: [1,2], ` +
- `s32: [1,2], ` +
- `s33: [1,1,2], ` +
- `s34: [1,2,1], ` +
- `s35: [1,2,1,2]` +
-
- `}`,
- }, {
- desc: "list equality",
- in: `
- eq0: [] == []
- eq1: [...] == []
- eq2: [] == [...]
- eq3: [...] == [...]
-
- eq4: [1] == [1]
- eq5: [1, ...] == [1]
- eq6: [1] == [1, ...]
- eq7: [1, ...] == [1, ...]
-
- eq8: [1, 2] == [1, 2]
- eq9: [1, 2, ...] == [1, 2]
- eq10: [1, 2] == [1, 2, ...]
- eq11: [1, 2, ...] == [1, 2, ...]
-
- ne0: [] != []
- ne1: [...] != []
- ne2: [] != [...]
- ne3: [...] != [...]
-
- ne4: [1] != [1]
- ne5: [1, ...] != [1]
- ne6: [1] != [1, ...]
- ne7: [1, ...] != [1, ...]
-
- ne8: [1, 2] != [1, 2]
- ne9: [1, 2, ...] != [1, 2]
- ne10: [1, 2] != [1, 2, ...]
- ne11: [1, 2, ...] != [1, 2, ...]
-
- feq0: [] == [1]
- feq1: [...] == [1]
- feq2: [] == [1, ...]
- feq3: [...] == [1, ...]
-
- feq4: [1] == []
- feq5: [1, ...] == []
- feq6: [1] == [...]
- feq7: [1, ...] == [...]
-
- feq8: [1, 2] == [1]
- feq9: [1, ...] == [1, 2]
- feq10: [1, 2] == [1, ...]
- feq11: [1, ...] == [1, 2, ...]
-
- fne0: [] != [1]
- fne1: [...] != [1]
- fne2: [] != [1, ...]
- fne3: [1, ...] != [1, ...]
-
- fne4: [1] != []
- fne5: [1, ...] != []
- fne6: [1] != [...]
- fne7: [1, ...] != [...]
-
- fne8: [1, 2] != [1]
- fne9: [1, ...] != [1, 2]
- fne10: [1, 2] != [1, ...]
- fne11: [1, ...] != [1, 2, ...]
- `,
- out: `<0>{` +
- `eq0: true, eq1: true, eq2: true, eq3: true, eq4: true, eq5: true, eq6: true, eq7: true, eq8: true, eq9: true, eq10: true, eq11: true, ` +
- `ne0: true, ne1: true, ne2: true, ne3: true, ne4: false, ne5: false, ne6: false, ne7: false, ne8: false, ne9: false, ne10: false, ne11: false, ` +
- `feq0: false, feq1: false, feq2: false, feq3: false, feq4: false, feq5: false, feq6: false, feq7: false, feq8: false, feq9: false, feq10: false, feq11: false, ` +
- `fne0: false, fne1: false, fne2: false, fne3: false, fne4: false, fne5: false, fne6: false, fne7: false, fne8: false, fne9: false, fne10: false, fne11: false}`,
- }, {
- desc: "list unification",
- in: `
- a: { l: ["foo", v], v: l[1] }
- b: a & { l: [_, "bar"] }
- `,
- out: `<0>{` +
- `a: <1>{l: ["foo",<2>.v], ` +
- `v: <2>.l[1]}, ` +
- `b: <3>{l: ["foo","bar"], v: "bar"}}`,
- }, {
- desc: "correct error messages",
- // Tests that it is okay to partially evaluate structs.
- in: `
- a: "a" & 1
- `,
- out: `<0>{a: _|_(("a" & 1):conflicting values "a" and 1 (mismatched types string and int))}`,
- }, {
- desc: "structs",
- in: `
- a: t & { c: 5 } // {c:5,d:15}
- b: ti & { c: 7 } // {c:7,d:21}
- t: { c: number, d: c * 3 } // {c:number,d:number*3}
- ti: t & { c: int }
- `,
- out: `<0>{a: <1>{c: 5, d: 15}, t: <2>{c: number, d: (<3>.c * 3)}, b: <4>{c: 7, d: 21}, ti: <5>{c: int, d: (<6>.c * 3)}}`,
- }, {
- desc: "definitions",
- in: `
- #Foo: {
- field: int
- recursive: {
- field: string
- }
- }
-
- // Allowed
- #Foo1: { field: int }
- #Foo1: { field2: string }
-
- foo: #Foo
- foo: { feild: 2 }
-
- foo1: #Foo
- foo1: {
- field: 2
- recursive: {
- feild: 2 // Not caught as per spec. TODO: change?
- }
- }
-
- #Bar: {
- field: int
- {[A=_]: int}
- }
- bar: #Bar
- bar: { feild: 2 }
-
- #Mixed: string
- Mixed: string
-
- mixedRec: { #Mixed: string }
- mixedRec: { Mixed: string }
- `,
- out: `<0>{` +
- `#Foo: <1>C{field: int, recursive: <2>C{field: string}}, ` +
- `#Foo1: <3>C{field: int, field2: string}, ` +
- `foo: _|_(2:field "feild" not allowed in closed struct), ` +
- `foo1: <4>C{field: 2, recursive: _|_(2:field "feild" not allowed in closed struct)}, ` +
- `#Bar: <5>{[]: <6>(A: string)->int, field: int}, ` +
- `bar: <7>{[]: <8>(A: string)->int, field: int, feild: 2}, ` +
- `#Mixed: string, ` +
- `Mixed: string, ` +
- `mixedRec: <9>{#Mixed: string, Mixed: string}}`,
- }, {
- desc: "combined definitions",
- in: `
- // Allow combining of structs within a definition
- #D1: {
- env: a: "A"
- env: b: "B"
- #def: {a: "A"}
- #def: {b: "B"}
- }
-
- d1: #D1 & { env: c: "C" }
-
- #D2: {
- a: int
- }
- #D2: {
- b: int
- }
-
- #D3: {
- env: a: "A"
- }
- #D3: {
- env: b: "B"
- }
-
- #D4: {
- env: #DC
- env: b: int
- }
-
- #DC: { a: int }
- `,
- out: `<0>{` +
- `#D1: <1>C{env: <2>C{a: "A", b: "B"}, #def: <3>C{a: "A", b: "B"}}, ` +
- `d1: <4>C{env: _|_("C":field "c" not allowed in closed struct), #def: <5>C{a: "A", b: "B"}}, ` +
- `#D2: <6>C{a: int, b: int}, ` +
- `#D3: <7>C{env: <8>C{a: "A", b: "B"}}, ` +
- `#D4: <9>C{env: _|_(int:field "b" not allowed in closed struct)}, ` +
- `#DC: <10>C{a: int}` +
- `}`,
- }, {
- desc: "new-style definitions",
- in: `
- #Foo: {
- a: 1
- b: int
- }
- "#Foo": #Foo & {b: 1}
-
- bulk: {[string]: string} & {
- #def: 4 // Different namespace, so bulk option does not apply.
- _hid: 3
- a: "foo"
- }
- `,
- out: `<0>{` +
- `"#Foo": <1>C{a: 1, b: 1}, ` +
- `#Foo: <2>C{a: 1, b: int}, ` +
- `bulk: <3>{[]: <4>(_: string)->string, a: "foo", #def: 4, _hid: 3}` +
- `}`,
- }, {
- desc: "recursive closing starting at non-definition",
- in: `
- z: a: {
- #B: {
- c: d: 1
- c: f: 1
- }
- }
- A: z & { a: { #B: { c: e: 2 } } }
- `,
- out: `<0>{z: <1>{a: <2>{#B: <3>C{c: <4>C{d: 1, f: 1}}}}, A: <5>{a: <6>{#B: <7>C{c: _|_(2:field "e" not allowed in closed struct)}}}}`,
- }, {
- desc: "non-closed definition carries over closedness to enclosed template",
- in: `
- #S: {
- [string]: { a: int }
- }
- a: #S & {
- v: { b: int }
- }
- #Q: {
- [string]: { a: int } | { b: int }
- }
- b: #Q & {
- w: { c: int }
- }
- #R: {
- [string]: [{ a: int }, { b: int }]
- }
- c: #R & {
- w: [{ d: int }, ...]
- }
- `,
- out: `<0>{` +
- `#S: <1>{[]: <2>(_: string)-><3>C{a: int}, }, ` +
- `a: <4>{[]: <5>(_: string)-><6>C{a: int}, v: _|_(int:field "b" not allowed in closed struct)}, ` +
- `b: <7>{[]: <8>(_: string)->(<9>C{a: int} | <10>C{b: int}), w: _|_(int:empty disjunction: field "c" not allowed in closed struct)}, ` +
- `#Q: <11>{[]: <12>(_: string)->(<13>C{a: int} | <14>C{b: int}), }, ` +
- `c: <15>{[]: <16>(_: string)->[<17>C{a: int},<18>C{b: int}], w: [_|_(int:field "d" not allowed in closed struct),<19>C{b: int}]}, ` +
- `#R: <20>{[]: <21>(_: string)->[<22>C{a: int},<23>C{b: int}], }}`,
- }, {
- desc: "definitions with disjunctions",
- in: `
- #Foo: {
- field: int
-
- { a: 1 } |
- { b: 2 }
- }
-
- foo: #Foo
- foo: { a: 1 }
-
- bar: #Foo
- bar: { c: 2 }
-
- baz: #Foo
- baz: { b: 2 }
- `,
- out: `<0>{` +
- `#Foo: (<1>C{field: int, a: 1} | <2>C{field: int, b: 2}), ` +
- `foo: <3>C{field: int, a: 1}, ` +
- `bar: _|_(2:empty disjunction: field "c" not allowed in closed struct), ` +
- `baz: <4>C{field: int, b: 2}}`,
- }, {
- desc: "definitions with disjunctions recurisive",
- in: `
- #Foo: {
- x: {
- field: int
-
- { a: 1 } |
- { b: 2 }
- }
- x: c: 3
- }
- `,
- out: `<0>{` +
- `#Foo: <1>C{x: (<2>C{field: int, a: 1, c: 3} | <3>C{field: int, b: 2, c: 3})}` +
- `}`,
- }, {
- desc: "definitions with embedding",
- in: `
- #E: {
- a: { b: int }
- }
-
- #S: {
- #E
- a: { c: int }
- b: 3
- }
-
- // adding a field to a nested struct that is closed.
- #e1: #S & { a: d: 4 }
- // literal struct not closed until after unification.
- #v1: #S & { a: c: 4 }
- `,
- out: `<0>{` +
- `#E: <1>C{a: <2>C{b: int}}, ` +
- `#S: <3>C{a: <4>C{b: int, c: int}, b: 3}, ` +
- `#e1: <5>C{a: _|_(4:field "d" not allowed in closed struct), b: 3}, ` +
- `#v1: <6>C{a: <7>C{b: int, c: 4}, b: 3}}`,
- }, {
- desc: "top-level definition with struct and disjunction",
- in: `
- #def: {
- Type: string
- Text: string
- Size: int
- }
-
- #def: {
- Type: "B"
- Size: 0
- } | {
- Type: "A"
- Size: 1
- }`,
- out: `<0>{` +
- `#def: (<1>C{Size: (0 & int), Type: ("B" & string), Text: string} | ` +
- `<2>C{Size: (1 & int), Type: ("A" & string), Text: string})` +
- `}`,
- }, {
- desc: "closing structs",
- in: `
- op: {x: int} // {x: int}
- ot: {x: int, ...} // {x: int, ...}
- cp: close({x: int}) // closed({x: int})
- ct: close({x: int, ...}) // {x: int, ...}
-
- opot: op & ot // {x: int, ...}
- otop: ot & op // {x: int, ...}
- opcp: op & cp // closed({x: int})
- cpop: cp & op // closed({x: int})
- opct: op & ct // {x: int, ...}
- ctop: ct & op // {x: int, ...}
- otcp: ot & cp // closed({x: int})
- cpot: cp & ot // closed({x: int})
- otct: ot & ct // {x: int, ...}
- ctot: ct & ot // {x: int, ...}
- cpct: cp & ct // closed({x: int})
- ctcp: ct & cp // closed({x: int})
- ctct: ct & ct // {x: int, ...}
- `,
- out: `<0>{` +
- `op: <1>{x: int}, ` +
- `ot: <2>{x: int, ...}, ` +
- `cp: <3>C{x: int}, ` +
- `ct: <4>{x: int, ...}, ` +
- `opot: <5>{x: int, ...}, ` +
- `otop: <6>{x: int, ...}, ` +
- `opcp: <7>C{x: int}, ` +
- `cpop: <8>C{x: int}, ` +
- `opct: <9>{x: int, ...}, ` +
- `ctop: <10>{x: int, ...}, ` +
- `otcp: <11>C{x: int}, ` +
- `cpot: <12>C{x: int}, ` +
- `otct: <13>{x: int, ...}, ` +
- `ctot: <14>{x: int, ...}, ` +
- `cpct: <15>C{x: int}, ` +
- `ctcp: <16>C{x: int}, ` +
- `ctct: <17>{x: int, ...}}`,
- }, {
- desc: "excluded embedding from closing",
- in: `
- #S: {
- a: { c: int }
- {
- c: { d: int }
- }
- B = { open: int }
- b: B
- }
- V: #S & {
- c: e: int
- b: extra: int
- }
- `,
- out: `<0>{` +
- `#S: <1>C{` +
- `a: <2>C{c: int}, ` +
- `c: <3>{d: int}, ` +
- `b: <4>{open: int}}, ` +
- `V: <5>C{` +
- `a: <6>C{c: int}, ` +
- `c: <7>{d: int, e: int}, ` +
- `b: <8>{open: int, extra: int}}}`,
- }, {
- desc: "closing with failed optional",
- in: `
- #k1: {a: int, b?: int} & #A // closed({a: int})
- #k2: #A & {a: int, b?: int} // closed({a: int})
-
- o1: {a?: 3} & {a?: 4} // {a?: _|_}
-
- // Optional fields with error values can be elimintated when closing
- #o2: {a?: 3} & {a?: 4} // close({})
-
- #d1: {a?: 2, b: 4} | {a?: 3, c: 5}
- v1: #d1 & {a?: 3, b: 4} // close({b: 4})
-
- #A: {a: int}
- `,
- out: `<0>{` +
- `#k1: <1>C{a: int}, ` +
- `#A: <2>C{a: int}, ` +
- `#k2: <3>C{a: int}, ` +
- `o1: <4>{a?: _|_((3 & 4):conflicting values 3 and 4)}, ` +
- `#o2: <5>C{a?: _|_((3 & 4):conflicting values 3 and 4)}, ` +
- `#d1: (<6>C{a?: 2, b: 4} | <7>C{a?: 3, c: 5}), ` +
- `v1: <8>C{a?: _|_((2 & 3):conflicting values 2 and 3), b: 4}` +
- `}`,
- }, {
- desc: "closing with comprehensions",
- in: `
- #A: {f1: int, f2: int}
-
- for k, v in {f3 : int} {
- a: #A & { "\(k)": v }
- }
-
- #B: {
- for k, v in {f1: int} {
- "\(k)": v
- }
- }
-
- #C: {
- f1: _
- for k, v in {f1: int} {
- "\(k)": v
- }
- }
-
- #D: {
- for k, v in {f1: int} {
- "\(k)": v
- }
- ...
- }
-
- #E: #A & {
- for k, v in { f3: int } {
- "\(k)": v
- }
- }
- `,
- out: `<0>{` +
- `#A: <1>C{f1: int, f2: int}, ` +
- `a: _|_(<2>.v:field "f3" not allowed in closed struct), ` +
- `#B: <3>C{f1: int}, ` +
- `#C: <4>C{f1: int}, ` +
- `#D: <5>{f1: int, ...}, ` +
- `#E: _|_(<6>.v:field "f3" not allowed in closed struct)` +
- `}`,
- }, {
- desc: "incomplete comprehensions",
- in: `
- A: {
- for v in src {
- "\(v)": v
- }
- src: _
- if true {
- baz: "baz"
- }
- }
- B: A & {
- src: ["foo", "bar"]
- }
- `,
- out: `<0>{` +
- `A: <1>{src: _, baz: "baz" <2>for _, v in <3>.src yield <4>{""+<2>.v+"": <2>.v}}, ` +
- `B: <5>{src: ["foo","bar"], baz: "baz", foo: "foo", bar: "bar"}}`,
- }, {
- desc: "reference to root",
- in: `
- a: { b: int }
- c: a & {
- b: 100
- d: a.b + 3 // do not resolve as c != a.
- }
- x: {
- b: int
- c: b + 5
- }
- y: x & {
- b: 100
- // c should resolve to 105
- }
- v: {
- b: int
- c: v.b + 5 // reference starting from copied node.
- }
- w: v & { b: 100 }
- wp: v & { b: 100 }
- `,
- out: `<0>{x: <1>{b: int, c: (<2>.b + 5)}, y: <3>{b: 100, c: 105}, a: <4>{b: int}, c: <5>{b: 100, d: (<6>.a.b + 3)}, v: <7>{b: int, c: (<6>.v.b + 5)}, w: <8>{b: 100, c: (<6>.v.b + 5)}, wp: <9>{b: 100, c: (<6>.v.b + 5)}}`,
- // TODO(#152): should be
- // out: `<0>{a: <1>{b: int}, c: <2>{b: 100, d: (<3>.a.b + 3)}, x: <4>{b: int, c: (<5>.b + 5)}, y: <6>{b: 100, c: 105}, v: <7>{b: int, c: (<8>.b + 5)}, w: <9>{b: 100, c: 105}, wp: <10>{b: 100, c: 105}}`,
- }, {
- desc: "references from template to concrete",
- in: `
- res: [t]
- t: [X=string]: {
- a: c + b.str
- b: str: string
- c: "X"
- }
- t: x: { b: str: "DDDD" }
- `,
- out: `<0>{res: [<1>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <5>{a: "XDDDD", c: "X", b: <6>{str: "DDDD"}}}], t: <7>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <8>{a: "XDDDD", c: "X", b: <9>{str: "DDDD"}}}}`,
- }, {
- desc: "interpolation",
- in: `
- a: "\(4)"
- b: "one \(a) two \( a + c )"
- c: "one"
- d: "\(r)"
- u: "\(_)"
- r: _
- e: "\([])"`,
- out: `<0>{a: "4", b: "one 4 two 4one", c: "one", d: ""+<1>.r+"", r: _, u: ""+_+"", e: _|_([]:expression in interpolation must evaluate to a number kind or string (found list))}`,
- }, {
- desc: "multiline interpolation",
- in: `
- a1: """
- before
- \(4)
- after
- """
- a2: """
- before
- \(4)
-
- """
- a3: """
-
- \(4)
- after
- """
- a4: """
-
- \(4)
-
- """
- m1: """
- before
- \(
- 4)
- after
- """
- m2: """
- before
- \(
- 4)
-
- """
- m3: """
-
- \(
-
- 4)
- after
- """
- m4: """
-
- \(
- 4)
-
- """
- `,
- out: `<0>{` +
- `a1: "before\n4\nafter", a2: "before\n4\n", a3: "\n4\nafter", a4: "\n4\n", ` +
- `m1: "before\n4\nafter", m2: "before\n4\n", m3: "\n4\nafter", m4: "\n4\n"` +
- `}`,
- }, {
- desc: "diamond-shaped constraints",
- in: `
- S: {
- A: {
- a: 1,
- },
- B: A & {
- b: 2,
- }
- },
- T: S & { // S == { A: { a:1 }, B: { a:1, b:2 } }
- A: {
- c: 3,
- },
- B: { // S.B & A
- d: 4, // Combines constraints S.A, S.B, T.A, and T.B
- }
- }`,
- out: "<0>{T: <1>{A: <2>{a: 1, c: 3}, B: <3>{a: 1, b: 2, c: 3, d: 4}}, S: <4>{A: <5>{a: 1}, B: <6>{a: 1, b: 2}}}",
- }, {
- desc: "field templates",
- in: `
- a: {
- {[name=_]: int}
- k: 1
- }
- b: {
- {[X=_]: { x: 0, y: *1 | int }}
- v: {}
- w: { x: 0 }
- }
- b: { [y=_]: {} }
- c: {
- {[Name=_]: { name: Name, y: 1 }}
- foo: {}
- bar: _
- }
- `,
- out: `<0>{a: <1>{[]: <2>(name: string)->int, k: 1}, b: <3>{[]: <4>(X: string)->(<5>{x: 0, y: (*1 | int)} & <6>{}), v: <7>{x: 0, y: (*1 | int)}, w: <8>{x: 0, y: (*1 | int)}}, c: <9>{[]: <10>(Name: string)-><11>{y: 1, name: <10>.Name}, foo: <12>{y: 1, name: "foo"}, bar: <13>{y: 1, name: "bar"}}}`,
- }, {
- desc: "range unification",
- in: `
- // with concrete values
- a1: >=1 & <=5 & 3
- a2: >=1 & <=5 & 1
- a3: >=1 & <=5 & 5
- a4: >=1 & <=5 & 6
- a5: >=1 & <=5 & 0
-
- a6: 3 & >=1 & <=5
- a7: 1 & >=1 & <=5
- a8: 5 & >=1 & <=5
- a9: 6 & >=1 & <=5
- a10: 0 & >=1 & <=5
-
- // with ranges
- b1: >=1 & <=5 & >=1 & <=5
- b2: >=1 & <=5 & >=1 & <=1
- b3: >=1 & <=5 & >=5 & <=5
- b4: >=1 & <=5 & >=2 & <=3
- b5: >=1 & <=5 & >=3 & <=9
- b6: >=1 & <=5 & >=5 & <=9
- b7: >=1 & <=5 & >=6 & <=9
-
- b8: >=1 & <=5 & >=1 & <=5
- b9: >=1 & <=1 & >=1 & <=5
- b10: >=5 & <=5 & >=1 & <=5
- b11: >=2 & <=3 & >=1 & <=5
- b12: >=3 & <=9 & >=1 & <=5
- b13: >=5 & <=9 & >=1 & <=5
- b14: >=6 & <=9 & >=1 & <=5
-
- // ranges with more general types
- c1: int & >=1 & <=5
- c2: >=1 & <=5 & int
- c3: string & >=1 & <=5
- c4: >=1 & <=5 & string
-
- // other types
- s1: >="d" & <="z" & "e"
- s2: >="d" & <="z" & "ee"
-
- n1: number & >=1 & <=2
- n2: int & >=1.1 & <=1.3
- n3: >=1.0 & <=3.0 & 2
- n4: >=0.0 & <=0.1 & 0.09999
- n5: >=1 & <=5 & 2.5
- `,
- out: `<0>{` +
- `a1: 3, ` +
- `a2: 1, ` +
- `a3: 5, ` +
- `a4: _|_((<=5 & 6):invalid value 6 (out of bound <=5)), ` +
- `a5: _|_((>=1 & 0):invalid value 0 (out of bound >=1)), ` +
- `a6: 3, ` +
- `a7: 1, ` +
- `a8: 5, ` +
-
- `a9: _|_((<=5 & 6):invalid value 6 (out of bound <=5)), ` +
- `a10: _|_((>=1 & 0):invalid value 0 (out of bound >=1)), ` +
-
- `b1: (>=1 & <=5), ` +
- `b2: 1, ` +
- `b3: 5, ` +
- `b4: (>=2 & <=3), ` +
- `b5: (>=3 & <=5), ` +
- `b6: 5, ` +
- `b7: _|_(conflicting bounds >=6 and <=5), ` +
- `b8: (>=1 & <=5), ` +
- `b9: 1, ` +
- `b10: 5, ` +
- `b11: (>=2 & <=3), ` +
- `b12: (>=3 & <=5), ` +
- `b13: 5, ` +
- `b14: _|_(conflicting bounds >=6 and <=5), ` +
- `c1: (int & >=1 & <=5), ` +
- `c2: (<=5 & int & >=1), ` +
- `c3: _|_((string & >=1):conflicting values string and >=1 (mismatched types string and number)), ` +
- `c4: _|_(((>=1 & <=5) & string):conflicting values (>=1 & <=5) and string (mismatched types number and string)), ` +
- `s1: "e", ` +
- `s2: "ee", ` +
- `n1: (>=1 & <=2), ` +
- `n2: _|_(conflicting bounds int & >=1.1 and <=1.3), ` +
- `n3: 2, ` +
- `n4: 0.09999, ` +
- `n5: 2.5}`,
- }, {
- desc: "predefined ranges",
- in: `
- k1: int8
- k1: 44
-
- k2: int64
- k2: -8_000_000_000
-
- e1: int16
- e1: 100_000
- `,
- out: `<0>{k1: 44, k2: -8000000000, ` +
- `e1: _|_((int & <=32767 & 100000):invalid value 100000 (out of bound int & <=32767))}`,
- }, {
- desc: "struct comprehensions",
- in: `
- obj: foo: a: "bar"
- obj: [Name=string]: {
- a: *"dummy" | string
- if true {
- sub: as: a
- }
- }
-
- for k, v in { #def: 1, opt?: 2, _hid: 3, reg: 4 } {
- "\(k)": v
- }
- `,
- out: `<0>{obj: <1>{[]: <2>(Name: string)-><3>{a: (*"dummy" | string) if true yield <4>{sub: <5>{as: <3>.a}}}, foo: <6>{a: "bar", sub: <7>{as: "bar"}}}, reg: 4}`,
- }, {
- desc: "builtins",
- in: `
- a1: {
- a: and([b, c])
- b: =~"oo"
- c: =~"fo"
- }
- a2: a1 & { a: "foo" }
- a3: a1 & { a: "bar" }
-
- o1: {
- a: or([b, c])
- b: string
- c: "bar"
- }
- o2: o1 & { a: "foo" }
- o3: o1 & { a: "foo", b: "baz" }
- `,
- out: `<0>{` +
- `a1: <1>{a: (=~"oo" & =~"fo"), b: =~"oo", c: =~"fo"}, ` +
- `a2: <2>{a: "foo", b: =~"oo", c: =~"fo"}, ` +
- `a3: <3>{a: _|_((=~"oo" & "bar"):invalid value "bar" (does not match =~"oo")), b: =~"oo", c: =~"fo"}, ` +
- `o1: <4>{a: string, b: string, c: "bar"}, ` +
- `o2: <5>{a: "foo", b: string, c: "bar"}, ` +
- `o3: <6>{a: _|_(("baz" & "foo"):empty disjunction: conflicting values "baz" and "foo";("bar" & "foo"):empty disjunction: conflicting values "bar" and "foo"), b: "baz", c: "bar"}}`,
- }, {
- desc: "self-reference cycles conflicts with strings",
- in: `
- a: {
- x: y+"?"
- y: x+"!"
- }
- a: x: "hey"
- `,
- out: `<0>{a: <1>{x: _|_(("hey!?" & "hey"):conflicting values "hey!?" and "hey"), y: "hey!"}}`,
- }, {
- desc: "resolved self-reference cycles with disjunctions",
- in: `
- a: b&{x:1} | {y:1} // {x:1,y:3,z:2} | {y:1}
- b: {x:2} | c&{z:2} // {x:2} | {x:1,y:3,z:2}
- c: a&{y:3} | {z:3} // {x:1,y:3,z:2} | {z:3}
- `,
- out: `<0>{a: (<1>{x: 1, y: 3, z: 2} | <2>{y: 1}), b: (<3>{x: 2} | <4>{x: 1, y: 3, z: 2}), c: (<5>{x: 1, y: 3, z: 2} | <6>{z: 3})}`,
- }, {
- // We take a very conservative stance on delaying arithmetic
- // expressions within disjunctions. It should remain resolvable, though,
- // once the user specifies one.
- desc: "resolved self-reference cycles with disjunction",
- in: `
- // The second disjunct in xa1 is not resolvable and can be
- // eliminated:
- // xa4 & 9
- // (xa2 + 2) & 9
- // ((xa3 + 2) + 2) & 9
- // (((6 & xa1-2) + 2) + 2) & 9
- // ((6 + 2) + 2) & 9 // 6 == xa1-2
- // 10 & 9 => _|_
- // The remaining values resolve.
- xa1: (xa2 & 8) | (xa4 & 9)
- xa2: xa3 + 2
- xa3: 6 & xa1-2
- xa4: xa2 + 2
-
- // The second disjunct in xb4 can be eliminated as both disjuncts
- // of xb3 result in an incompatible sum when substituted.
- xb1: (xb2 & 8) | (xb4 & 9)
- xb2: xb3 + 2
- xb3: (6 & (xb1-2)) | (xb4 & 9)
- xb4: xb2 + 2
-
- // Another variant with more disjunctions. xc1 remains with two
- // possibilities. Technically, only the first value is valid.
- // However, to fully determine that, all options of the remaining
- // disjunction will have to be evaluated algebraically, which is
- // not done.
- xc1: xc2 & 8 | xc4 & 9 | xc5 & 9
- xc2: xc3 + 2
- xc3: 6 & xc1-2
- xc4: xc2 + 1
- xc5: xc2 + 2
-
- // The above is resolved by setting xd1 explicitly.
- xd1: xd2 & 8 | xd4 & 9 | xd5 & 9
- xd2: xd3 + 2
- xd3: 6 & xd1-2
- xd4: xd2 + 1
- xd5: xd2 + 2
- xd1: 8
-
- // The above is resolved by setting xd1 explicitly to the wrong
- // value, resulting in an error.
- xe1: xe2 & 8 | xe4 & 9 | xe5 & 9
- xe2: xe3 + 2
- xe3: 6 & xe1-2
- xe4: xe2 + 1
- xe5: xe2 + 2
- xe1: 9
-
- // Only one solution.
- xf1: xf2 & 8 | xf4 & 9
- xf2: xf3 + 2
- xf3: 6 & xf1-2 | xf4 & 9
- xf4: xf2 + 2
-
- z1: z2 + 1 | z3 + 5
- z2: z3 + 2
- z3: z1 - 3
- z3: 8
- `,
- out: `<0>{` +
- `xa1: 8, ` +
- `xa2: 8, ` +
- `xa4: 10, ` +
- `xa3: 6, ` +
-
- `xb1: 8, ` +
- `xb2: 8, ` +
- `xb4: 10, ` +
- `xb3: 6, ` +
-
- `xc1: ((<1>.xc2 & 8) | (<1>.xc4 & 9) | (<1>.xc5 & 9)), ` +
- `xc2: (<1>.xc3 + 2), ` +
- `xc4: (<1>.xc2 + 1), ` +
- `xc5: (<1>.xc2 + 2), ` +
- `xc3: (6 & (<1>.xc1 - 2)), ` +
-
- `xd1: 8, ` +
- `xd2: 8, ` +
- `xd4: 9, ` +
- `xd5: 10, ` +
- `xd3: 6, ` +
-
- `xe1: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe2: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe4: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe5: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe3: _|_((6 & 7):conflicting values 6 and 7), ` +
-
- `xf1: 8, ` +
- `xf2: 8, ` +
- `xf4: 10, ` +
- `xf3: 6, ` +
-
- `z1: ((<1>.z2 + 1) | (<1>.z3 + 5)), ` +
- `z2: (<1>.z3 + 2), ` +
- `z3: ((<1>.z1 - 3) & 8)}`,
- }, {
- // Defaults should not alter the result of the above disjunctions.
- // The results may differ, but errors and resolution should be roughly
- // the same.
- desc: "resolved self-reference cycles with disjunction with defaults",
- in: `
- // The disjunction in xa could be resolved, but as disjunctions
- // are not resolved for expression, it remains unresolved.
- xa1: (xa2 & 8) | *(xa4 & 9)
- xa2: xa3 + 2
- xa3: 6 & xa1-2
- xa4: xa2 + 2
-
- // As xb3 is a disjunction, xb2 cannot be resolved and evaluating
- // the cycle completely is broken. However, it is not an error
- // as the user might still resolve the disjunction.
- xb1: *(xb2 & 8) | (xb4 & 9)
- xb2: xb3 + 2
- xb3: *(6 & (xb1-2)) | (xb4 & 9)
- xb4: xb2 + 2
-
- // Another variant with more disjunctions. xc1 remains with two
- // possibilities. Technically, only the first value is valid.
- // However, to fully determine that, all options of the remaining
- // disjunction will have to be evaluated algebraically, which is
- // not done.
- xc1: *(xc2 & 8) | (xc4 & 9) | (xc5 & 9)
- xc2: xc3 + 2
- xc3: 6 & xc1-2
- xc4: xc2 + 1
- xc5: xc2 + 2
-
- // The above is resolved by setting xd1 explicitly.
- xd1: *(xd2 & 8) | xd4 & 9 | xd5 & 9
- xd2: xd3 + 2
- xd3: 6 & xd1-2
- xd4: xd2 + 1
- xd5: xd2 + 2
-
- // The above is resolved by setting xd1 explicitly to the wrong
- // value, resulting in an error.
- xe1: *(xe2 & 8) | xe4 & 9 | xe5 & 9
- xe2: xe3 + 2
- xe3: 6 & xe1-2
- xe4: xe2 + 1
- xe5: xe2 + 2
- xe1: 9
-
- z1: *(z2 + 1) | z3 + 5
- z2: z3 + 2
- z3: z1 - 3
- z3: 8
- `,
- out: `<0>{` +
- `xa1: 8, ` +
- `xa2: 8, ` +
- `xa4: 10, ` +
- `xa3: 6, ` +
-
- `xb1: 8, ` +
- `xb2: 8, ` +
- `xb4: 10, ` +
- `xb3: 6, ` +
-
- `xc1: (*8 | 9), ` + // not resolved because we use evalPartial
- `xc2: 8, ` +
- `xc4: 9, ` +
- `xc5: 10, ` +
- `xc3: 6, ` +
-
- `xd1: (*8 | 9), ` + // TODO: eliminate 9?
- `xd2: 8, ` +
- `xd4: 9, ` +
- `xd5: 10, ` +
- `xd3: 6, ` +
-
- `xe1: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe2: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe4: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe5: _|_((6 & 7):conflicting values 6 and 7), ` +
- `xe3: _|_((6 & 7):conflicting values 6 and 7), ` +
-
- `z1: (*11 | 13), ` + // 13 is eliminated with evalFull
- `z2: 10, ` +
- `z3: 8}`,
- }}
- rewriteHelper(t, testCases, evalPartial)
-}
-
-func TestFullEval(t *testing.T) {
- testCases := []testCase{{
- desc: "detect conflicting value",
- in: `
- a: 8000.9
- a: 7080 | int`,
- out: `<0>{a: _|_((8000.9 & (int | int)):conflicting values 8000.9 and int (mismatched types float and int))}`, // TODO: fix repetition
- }, {
- desc: "conflicts in optional fields are okay ",
- in: `
- d: {a: 1, b?: 3} | {a: 2}
-
- // the following conjunction should not eliminate any disjuncts
- c: d & {b?:4}
- `,
- out: `<0>{d: (<1>{a: 1, b?: 3} | <2>{a: 2}), c: (<3>{a: 1, b?: (3 & 4)} | <4>{a: 2, b?: 4})}`,
- }, {
- desc: "resolve all disjunctions",
- in: `
- service: [Name=string]: {
- name: string | *Name
- port: int | *7080
- }
- service: foo: _
- service: bar: { port: 8000 }
- service: baz: { name: "foobar" }
- `,
- out: `<0>{service: <1>{[]: <2>(Name: string)-><3>{name: (string | *<2>.Name), port: (int | *7080)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`,
- }, {
- desc: "field templates",
- in: `
- a: {
- [name=_]: int
- k: 1
- }
- b: {
- [X=_]: { x: 0, y: *1 | int }
- v: {}
- w: { y: 0 }
- }
- b: { [y=_]: {} } // TODO: allow different name
- c: {
- [Name=_]: { name: Name, y: 1 }
- foo: {}
- bar: _
- }
- `,
- out: `<0>{a: <1>{[]: <2>(name: string)->int, k: 1}, b: <3>{[]: <4>(X: string)->(<5>{x: 0, y: (*1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{[]: <10>(Name: string)-><11>{y: 1, name: <10>.Name}, foo: <12>{y: 1, name: "foo"}, bar: <13>{y: 1, name: "bar"}}}`,
- }, {
- desc: "field comprehension",
- in: `
- a: {
- for k, v in b
- if k < "d"
- if v > b.a {
- "\(k)": v
- }
- }
- b: {
- a: 1
- b: 2
- c: 3
- d: 4
- }
- c: {
- for k, v in b
- if k < "d"
- if v > b.a {
- "\(k)": v
- }
- }
- `,
- out: `<0>{a: <1>{b: 2, c: 3}, b: <2>{a: 1, b: 2, c: 3, d: 4}, c: <3>{b: 2, c: 3}}`,
- }, {
- desc: "conditional field",
- in: `
- if b {
- a: "foo"
- }
- b: true
- c: {
- a: 3
- if a > 1 {
- a: 3
- }
- }
- d: {
- a: int
- if a > 1 {
- a: 3
- }
- }
- `,
- // NOTE: the node numbers are not correct here, but this is an artifact
- // of the testing code.
- out: `<0>{b: true, a: "foo", c: <1>{a: 3}, d: <2>{a: int if (<3>.a > 1) yield <4>{a: 3}}}`,
- }, {
- desc: "referencing field in field comprehension",
- in: `
- a: { b: c: 4 }
- a: {
- b: d: 5
- for k, v in b {
- "\(k)": v
- }
- }
- `,
- out: `<0>{a: <1>{b: <2>{c: 4, d: 5}, c: 4, d: 5}}`,
- }, {
- desc: "different labels for templates",
- in: `
- a: [X=string]: { name: X }
- a: [Name=string]: { name: Name }
- a: foo: {}
- `,
- out: `<0>{a: <1>{[]: <2>(X: string)->(<3>{name: <2>.X} & <4>{name: <2>.X}), foo: <5>{name: "foo"}}}`,
- }, {
- // TODO: rename EE and FF to E and F to check correct ordering.
-
- desc: "nested templates in one field",
- in: `
- a: [A=string]: b: [B=string]: {
- name: A
- kind: B
- }
- a: "A": b: "B": _
- a: "C": b: "D": _
- a: "EE": b: "FF": { c: "bar" }
- `,
- out: `<0>{a: <1>{[]: <2>(A: string)-><3>{b: <4>{[]: <5>(B: string)-><6>{name: <2>.A, kind: <5>.B}, }}, ` +
- `A: <7>{b: <8>{[]: <9>(B: string)-><10>{name: <11>.A, kind: <9>.B}, ` +
- `B: <12>{name: "A", kind: "B"}}}, ` +
- `C: <13>{b: <14>{[]: <15>(B: string)-><16>{name: <17>.A, kind: <15>.B}, ` +
- `D: <18>{name: "C", kind: "D"}}}, ` +
- `EE: <19>{b: <20>{[]: <21>(B: string)-><22>{name: <23>.A, kind: <21>.B}, ` +
- `FF: <24>{name: "EE", kind: "FF", c: "bar"}}}}}`,
- }, {
- desc: "template unification within one struct",
- in: `
- a: {
- [A=string]: { name: A }
- // TODO: allow duplicate alias here
- [X=string]: { kind: X }
- }
- a: "A": _
- a: "C": _
- a: "E": { c: "bar" }
- `,
- out: `<0>{a: <1>{[]: <2>(A: string)->(<3>{name: <2>.A} & <4>{kind: <2>.A}), ` +
- `E: <5>{name: "E", kind: "E", c: "bar"}, ` +
- `A: <6>{name: "A", kind: "A"}, ` +
- `C: <7>{name: "C", kind: "C"}}}`,
- }, {
- desc: "field comprehensions with multiple keys",
- in: `
- for x in [
- {a: "A", b: "B" },
- {a: "C", b: "D" },
- {a: "E", b: "F" },
- ] {
- a: "\(x.a)": b: "\(x.b)": x
- }
-
- for x in [
- {a: "A", b: "B" },
- {a: "C", b: "D" },
- {a: "E", b: "F" },
- ] {
- "\(x.a)": "\(x.b)": x
- }
- `,
- out: `<0>{E: <1>{F: <2>{a: "E", b: "F"}}, ` +
- `a: <3>{` +
- `E: <4>{b: <5>{F: <6>{a: "E", b: "F"}}}, ` +
- `A: <7>{b: <8>{B: <9>{a: "A", b: "B"}}}, ` +
- `C: <10>{b: <11>{D: <12>{a: "C", b: "D"}}}}, ` +
- `A: <13>{B: <14>{a: "A", b: "B"}}, ` +
- `C: <15>{D: <16>{a: "C", b: "D"}}}`,
- // TODO: this order would be desirable.
- // out: `<0>{a: <1>{` +
- // `A: <2>{b: <3>{B: <4>{a: "A", b: "B"}}}, ` +
- // `C: <5>{b: <6>{D: <7>{a: "C", b: "D"}}}, ` +
- // `E: <8>{b: <9>{F: <10>{a: "E", b: "F"}}}}, ` +
- // `A: <11>{B: <12>{a: "A", b: "B"}}, ` +
- // `C: <13>{D: <14>{a: "C", b: "D"}}, ` +
- // `E: <15>{F: <16>{a: "E", b: "F"}}}`,
- }, {
- desc: "field comprehensions with templates",
- in: `
- num: 1
- a: {
- if num < 5 {
- [A=string]: [B=string]: {
- name: A
- kind: B
- }
- }
- }
- a: b: c: d: "bar"
- `,
- out: `<0>{num: 1, a: <1>{[]: <2>(A: string)-><3>{[]: <4>(B: string)-><5>{name: <2>.A, kind: <4>.B}, }, ` +
- `b: <6>{[]: <7>(B: string)-><8>{name: <9>.A, kind: <7>.B}, ` +
- `c: <10>{name: "b", kind: "c", ` +
- `d: "bar"}}}}`,
- }, {
- desc: "disjunctions of lists",
- in: `
- l: *[ int, int ] | [ string, string ]
-
- l1: [ "a", "b" ]
- l2: l & [ "c", "d" ]
- `,
- out: `<0>{l: [int,int], l1: ["a","b"], l2: ["c","d"]}`,
- }, {
- desc: "normalization",
- in: `
- a: string | string
- b: *1 | *int
- c: *1.0 | *float
- `,
- out: `<0>{a: string, b: int, c: float}`,
- }, {
- desc: "default disambiguation and elimination",
- in: `
- a: *1 | int
- b: *3 | int
- c: a & b
- d: b & a
-
- e: *1 | *1
- `,
- out: `<0>{a: 1, b: 3, c: int, d: int, e: 1}`,
- }, {
- desc: "list comprehension",
- in: `
- a: [ for k, v in b if k < "d" if v > b.a { k }]
- b: {
- a: 1
- b: 2
- c: 3
- d: 4
- }
- c: [ for _, x in b for _, y in b if x < y { x } ]
- d: [ for x, _ in a { x } ]
- `,
- out: `<0>{a: ["b","c"], b: <1>{a: 1, b: 2, c: 3, d: 4}, c: [1,1,1,2,2,3], d: [0,1]}`,
- }, {
- desc: "struct comprehension with template",
- in: `
- result: [ for _, v in service { v } ]
-
- service: [Name=string]: {
- name: *Name | string
- type: "service"
- port: *7080 | int
- }
- service: foo: {}
- service: bar: { port: 8000 }
- service: baz: { name: "foobar" }
- `,
- out: `<0>{result: [` +
- `<1>{name: "foo", type: "service", port: 7080},` +
- `<2>{name: "bar", type: "service", port: 8000},` +
- `<3>{name: "foobar", type: "service", port: 7080}], ` +
-
- `service: <4>{` +
- `[]: <5>(Name: string)-><6>{name: (*<5>.Name | string), type: "service", port: (*7080 | int)}, ` +
- `foo: <7>{name: "foo", type: "service", port: 7080}, ` +
- `bar: <8>{name: "bar", type: "service", port: 8000}, ` +
- `baz: <9>{name: "foobar", type: "service", port: 7080}}}`,
- }, {
- desc: "resolutions in struct comprehension keys",
- in: `
- a: { for _, b in ["c"] { "\(b + ".")": "a" } }
- `,
- out: `<0>{a: <1>{"c.": "a"}}`,
- }, {
- desc: "recursive evaluation within list",
- in: `
- l: [a]
- a: b & { c: "t" }
- b: {
- d: c
- c: string
- }
- l1: [a1]
- a1: b1 & { c: "t" }
- b1: {
- d: "s" + c
- c: string
- }
- `,
- out: `<0>{` +
- `l: [<1>{c: "t", d: "t"}], ` +
- `a: <2>{c: "t", d: "t"}, ` +
- `b: <3>{c: string, d: string}, ` +
- `l1: [<4>{c: "t", d: "st"}], ` +
- `a1: <5>{c: "t", d: "st"}, ` +
- `b1: <6>{c: string, d: ("s" + <7>.c)}}`,
- }, {
- desc: "ips",
- in: `
- IP: 4*[ uint8 ]
-
- Private:
- *[ 192, 168, uint8, uint8 ] |
- [ 10, uint8, uint8, uint8] |
- [ 172, >=16 & <=32, uint8, uint8 ]
-
- Inst: Private & [ _, 10, ... ]
-
- MyIP: Inst & [_, _, 10, 10 ]
- `,
- out: `<0>{` +
- `IP: [(int & >=0 & int & <=255),(int & >=0 & int & <=255),(int & >=0 & int & <=255),(int & >=0 & int & <=255)], ` +
- `Private: [192,168,(int & >=0 & int & <=255),(int & >=0 & int & <=255)], ` +
- `Inst: [10,10,(int & >=0 & int & <=255),(int & >=0 & int & <=255)], ` +
- `MyIP: [10,10,10,10]` +
- `}`,
- }, {
- desc: "complex interaction of groundness",
- in: `
- res: [ for x in a for y in x { y & { d: "b" } }]
- res: [ a.b.c & { d: "b" } ]
-
- a: b: [C=string]: { d: string, s: "a" + d }
- a: b: c: d: string
- `,
- // TODO(perf): unification should catch shared node.
- out: `<0>{res: [<1>{d: "b", s: "ab"}], ` +
- `a: <2>{b: <3>{[]: <4>(C: string)-><5>{d: string, s: ("a" + <5>.d)}, c: <6>{d: string, s: ("a" + <7>.d)}}}}`,
- }, {
- desc: "complex groundness 2",
- in: `
- r1: f1 & { y: "c" }
-
- f1: { y: string, res: a.b.c & { d: y } }
-
- a: b: c: { d: string, s: "a" + d }
- a: b: [C=string]: { d: string, s: "a" + d }
- a: b: c: d: string
- `,
- out: `<0>{r1: <1>{y: "c", res: <2>{d: "c", s: "ac"}}, f1: <3>{y: string, res: <4>{d: string, s: (("a" + <5>.d) & ("a" + <5>.d))}}, a: <6>{b: <7>{[]: <8>(C: string)-><9>{d: string, s: ("a" + <9>.d)}, c: <10>{d: string, s: (("a" + <11>.d) & ("a" + <11>.d))}}}}`,
- }, {
- desc: "references from template to concrete",
- in: `
- res: [t]
- t: [X=string]: {
- a: c + b.str
- b: str: string
- c: "X"
- }
- t: x: { b: str: "DDDD" }
- `,
- out: `<0>{res: [<1>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <5>{a: "XDDDD", c: "X", b: <6>{str: "DDDD"}}}], ` +
- `t: <7>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <8>{a: "XDDDD", c: "X", b: <9>{str: "DDDD"}}}}`,
- }, {
- // TODO: A nice property for CUE to have would be that evaluation time
- // is proportional to the number of output nodes (note that this is
- // not the same as saying that the running time is O(n)).
- // We should probably disallow shenanigans like the one below. But until
- // this is allowed, it should at least be correct. At least we are not
- // making reentrant coding easy.
- desc: "reentrance",
- in: `
- // This indirection is needed to avoid binding references to fib
- // within fib to the instantiated version.
- fibRec: {nn: int, out: (fib & {n: nn}).out}
- fib: {
- n: int
-
- if n >= 2 {
- out: (fibRec & {nn: n - 2}).out + (fibRec & {nn: n - 1}).out
- }
- if n < 2 {
- out: n
- }
- }
- fib2: (fib & {n: 2}).out
- fib7: (fib & {n: 7}).out
- fib12: (fib & {n: 12}).out
- `,
- out: `<0>{` +
- `fibRec: <1>{` +
- `nn: int, ` +
- `out: (<2>.fib & <3>{n: <4>.nn}).out}, ` +
- // NOTE: the node numbers are not correct here, but this is an artifact
- // of the testing code.
- `fib: <5>{n: int if (<6>.n >= 2) yield <7>{out: ((<2>.fibRec & <8>{nn: (<6>.n - 2)}).out + (<2>.fibRec & <9>{nn: (<6>.n - 1)}).out)}, if (<6>.n < 2) yield <10>{out: <6>.n}}, ` +
- `fib2: 1, ` +
- `fib7: 13, ` +
- `fib12: 144}`,
- }, {
- desc: "Issue #23",
- in: `
- x: {a:1}|{a:2}
- y: x & {a:3}
- `,
- out: `<0>{x: (<1>{a: 1} | <2>{a: 2}), y: _|_((1 & 3):empty disjunction: conflicting values 1 and 3;(2 & 3):empty disjunction: conflicting values 2 and 3)}`,
- }, {
- desc: "cannot resolve references that would be ambiguous",
- in: `
- a1: *0 | 1
- a1: a3 - a2
- a2: *0 | 1
- a2: a3 - a1
- a3: 1
-
- b1: (*0 | 1) & b2
- b2: (0 | *1) & b1
-
- c1: (*{a:1} | {b:1}) & c2
- c2: (*{a:2} | {b:2}) & c1
- `,
- out: `<0>{` +
- `a1: ((*0 | 1) & (<1>.a3 - <1>.a2)), ` +
- `a3: 1, ` +
- `a2: ((*0 | 1) & (<1>.a3 - <1>.a1)), ` +
- `b1: (0 | 1), ` +
- `b2: (0 | 1), ` +
- `c1: (<2>{a: 1, b: 2} | <3>{a: 2, b: 1}), ` +
- `c2: (<4>{a: 2, b: 1} | <5>{a: 1, b: 2})}`,
- }, {
- desc: "dont convert incomplete errors to non-incomplete",
- in: `
- import "strings"
-
- n1: {min: <max, max: >min}
- n2: -num
- n3: +num
- n4: num + num
- n5: num - num
- n6: num * num
- n7: num / num
-
- b1: !is
-
- s1: "\(str)"
- s2: strings.ContainsAny("dd")
- s3: strings.ContainsAny(str, "dd")
-
- str: string
- num: <4
- is: bool
- `,
- out: `<0>{` +
- `n1: <1>{min: <<2>.max, max: ><2>.min}, ` +
- `n2: -<3>.num, num: <4, ` +
- `n3: +<3>.num, ` +
- `n4: (<3>.num + <3>.num), ` +
- `n5: (<3>.num - <3>.num), ` +
- `n6: (<3>.num * <3>.num), ` +
- `n7: (<3>.num / <3>.num), ` +
- `b1: !<3>.is, ` +
- `is: bool, ` +
- `s1: ""+<3>.str+"", ` +
- `str: string, ` +
- `s2: strings.ContainsAny ("dd"), ` +
- `s3: <4>.ContainsAny (<3>.str,"dd")}`,
- }, {
- desc: "len of incomplete types",
- in: `
- args: *[] | [...string]
- v1: len(args)
- v2: len([])
- v3: len({})
- v4: len({a: 3})
- v5: len({a: 3} | {a: 4})
- v6: len('sf' | 'dd')
- v7: len([2] | *[1, 2])
- v8: len([2] | [1, 2])
- v9: len("😂")
- v10: len("")
- `,
- out: `<0>{` +
- `args: [], ` +
- `v1: 0, ` +
- `v2: 0, ` +
- `v3: 0, ` +
- `v4: 1, ` +
- `v5: len ((<1>{a: 3} | <2>{a: 4})), ` +
- `v6: len (('sf' | 'dd')), ` +
- `v7: 2, ` +
- `v8: len (([2] | [1,2])), ` +
- `v9: 4, ` +
- `v10: 0}`,
- }, {
- desc: "slice rewrite bug",
- in: `
- fn: {
- arg: [...int] & [1]
- out: arg[1:]
- }
- fn1: fn & {arg: [1]}
- `,
- out: `<0>{fn: <1>{arg: [1], out: []}, fn1: <2>{arg: [1], out: []}}`,
- }, {
- desc: "Issue #94",
- in: `
- foo: {
- opt?: 1
- "txt": 2
- #def: 3
- regular: 4
- _hidden: 5
- }
- comp: { for k, v in foo { "\(k)": v } }
- select: {
- opt: foo.opt
- "txt": foo.txt
- #def: foo.#def
- regular: foo.regular
- _hidden: foo._hidden
- }
- index: {
- opt: foo["opt"]
- "txt": foo["txt"]
- #def: foo["#def"]
- regular: foo["regular"]
- _hidden: foo["_hidden"]
- }
- `,
- out: `<0>{` +
- `foo: <1>{opt?: 1, txt: 2, #def: 3, regular: 4, _hidden: 5}, ` +
- `comp: <2>{txt: 2, regular: 4}, ` +
- `select: <3>{opt: <4>.foo.opt, txt: 2, #def: 3, regular: 4, _hidden: 5}, ` +
- `index: <5>{opt: <4>.foo["opt"], txt: 2, #def: <4>.foo["#def"], regular: 4, _hidden: <4>.foo["_hidden"]}}`,
- }, {
- desc: "retain references with interleaved embedding",
- in: `
- a: d: {
- #base
- #info: {...}
- Y: #info.X
- }
-
- #base: {
- #info: {...}
- }
-
- a: [Name=string]: { #info: {
- X: "foo"
- }}
- `,
- out: `<0>{a: <1>{[]: <2>(Name: string)-><3>{#info: <4>C{X: "foo"}}, d: <5>C{#info: <6>C{X: "foo"}, Y: "foo"}}, #base: <7>C{#info: <8>{...}}}`,
- }, {
- desc: "comparison against bottom",
- in: `
- a: _|_ == _|_
- b: err == 1&2 // not a literal error, so not allowed
- c: err == _|_ // allowed
- d: err != _|_ // allowed
- e: err != 1&3
- // z: err == err // TODO: should infer to be true?
- f: ({a: 1} & {a: 2}) == _|_
- g: ({a: 1} & {b: 2}) == _|_
- h: _|_ == ({a: 1} & {a: 2})
- i: _|_ == ({a: 1} & {b: 2})
-
- err: 1 & 2
- `,
- out: `<0>{a: true, b: _|_((1 & 2):conflicting values 1 and 2), err: _|_((1 & 2):conflicting values 1 and 2), c: true, d: false, e: _|_((1 & 2):conflicting values 1 and 2), f: true, g: false, h: true, i: false}`,
- }, {
- desc: "or builtin should not fail on non-concrete empty list",
- in: `
- #Workflow: {
- jobs: {
- [jobID=string]: {
- }
- }
- #JobID: or([ for k, _ in jobs { k } ])
- }
-
- foo: #Workflow & {
- jobs: foo: {
- }
- }
- `,
- out: `<0>{#Workflow: <1>C{jobs: <2>{[]: <3>(jobID: string)-><4>C{}, }, #JobID: or ([ <5>for k, _ in <6>.jobs yield <5>.k ])}, foo: <7>C{jobs: <8>{[]: <9>(jobID: string)-><10>C{}, foo: <11>C{}}, #JobID: "foo"}}`,
- }, {
- desc: "Issue #153",
- in: `
- Foo: {
- listOfCloseds: [...#Closed]
- }
-
- #Closed: {
- a: int | *0
- }
-
- Junk: {
- b: 2
- }
-
- Foo & {
- listOfCloseds: [{
- for k, v in Junk {
- "\(k)": v
- }
- }]
- }
- `,
- out: `<0>{<1>{listOfCloseds: [_|_(<2>.v:field "b" not allowed in closed struct)]}, Foo: <3>{listOfCloseds: []}, #Closed: <4>C{a: 0}, Junk: <5>{b: 2}}`,
- }, {
- desc: "label and field aliases",
- in: `
- p: [ID=string]: { name: ID }
- A="foo=bar": "str"
- a: A
- B=bb: 4
- b1: B
- b1: bb
- C="\(a)": 5
- c: C
- `,
- out: `<0>{` +
- `p: <1>{[]: <2>(ID: string)-><3>{name: <2>.ID}, }, ` +
- `"foo=bar": "str", ` +
- `a: "str", ` +
- `bb: 4, ` +
- `b1: 4, ` +
- `c: 5, ` +
- `str: 5}`,
- }, {
- desc: "optionals with label filters",
- in: `
- #JobID: =~"^[a-zA-Z]*$"
- #Job: {
- name: string
- cmd: string
- }
- #Jobs: {
- [#JobID]: #Job
- [=~"Test$"]: name: =~"^test" // Must work without ...
- }
-
- jobs: foo: name: "allGood"
- jobs: foo: name: "allGood"
-
- jobs1: #Jobs
- jobs1: foo1: {} // faulty
-
- jobs2: #Jobs
- jobs2: fooTest: name: "badName" // faulty
-
- jobs3: #Jobs
- jobs3: [string]: #Job
- jobs3: fooTest1: name: "badName" // faulty
- `,
- out: `<0>{` +
- `#JobID: =~"^[a-zA-Z]*$", ` +
- `#Job: <1>C{name: string, cmd: string}, ` +
- `#Jobs: <2>C{[=~"^[a-zA-Z]*$"]: <3>(_: string)-><4>.#Job, [=~"Test$"]: <5>(_: string)-><6>C{name: =~"^test"}, }, ` +
- `jobs: <7>{foo: <8>{name: "allGood"}}, ` +
- `jobs1: _|_(<9>{}:field "foo1" not allowed in closed struct), ` +
- `jobs2: <10>C{[=~"^[a-zA-Z]*$"]: <11>(_: string)-><4>.#Job, [=~"Test$"]: <12>(_: string)-><13>C{name: =~"^test"}, fooTest: _|_(string:field "cmd" not allowed in closed struct)}, ` +
- `jobs3: _|_(<14>{name: "badName"}:field "fooTest1" not allowed in closed struct)}`,
- }, {
- desc: "optionals in open structs",
- in: `
- A: {
- [=~"^[a-s]*$"]: int
- }
- B: {
- [=~"^[m-z]*$"]: int
- }
- #C: {A & B}
- c: #C & { aaa: 3 }
- `,
- out: `<0>{A: <1>{[=~"^[a-s]*$"]: <2>(_: string)->int, }, B: <3>{[=~"^[m-z]*$"]: <4>(_: string)->int, }, #C: <5>C{[=~"^[a-s]*$"]: <6>(_: string)->int, [=~"^[m-z]*$"]: <7>(_: string)->int, }, c: <8>C{[=~"^[a-s]*$"]: <9>(_: string)->int, [=~"^[m-z]*$"]: <10>(_: string)->int, aaa: 3}}`,
- }, {
- desc: "conjunction of optional sets",
- in: `
- #A: {
- [=~"^[a-s]*$"]: int
- }
- #B: {
- [=~"^[m-z]*$"]: int
- }
-
- #C: #A & #B
- c: #C & { aaa: 3 }
-
- #D: {#A & #B}
- d: #D & { aaa: 3 }
- `,
- out: `<0>{` +
- `#A: <1>C{[=~"^[a-s]*$"]: <2>(_: string)->int, }, ` +
- `#B: <3>C{[=~"^[m-z]*$"]: <4>(_: string)->int, }, ` +
- `#C: <5>C{(C{[=~"^[a-s]*$"]: <6>(_: string)->int} & C{[=~"^[m-z]*$"]: <7>(_: string)->int}), }, ` +
- `c: _|_(3:field "aaa" not allowed in closed struct), ` +
- `#D: <8>C{(C{[=~"^[a-s]*$"]: <9>(_: string)->int} & C{[=~"^[m-z]*$"]: <10>(_: string)->int}), }, ` +
- `d: _|_(3:field "aaa" not allowed in closed struct)` +
- `}`,
- }, {
- desc: "continue recursive closing for optionals",
- in: `
- #S: {
- [string]: { a: int }
- }
- a: #S & {
- v: { b: int }
- }
- `,
- out: `<0>{#S: <1>{[]: <2>(_: string)-><3>C{a: int}, }, a: <4>{[]: <5>(_: string)-><6>C{a: int}, v: _|_(int:field "b" not allowed in closed struct)}}`,
- }, {
- desc: "augment closed optionals",
- in: `
- #A: {
- [=~"^[a-s]*$"]: int
- }
- #B: {
- [=~"^[m-z]*?"]: int
- }
- #C: {
- #A & #B
- [=~"^Q*$"]: int
- }
- c: #C & { QQ: 3 }
- #D: {
- #A
- #B
- }
- d: #D & { aaa: 4 }
- `,
- out: `<0>{` +
- `#A: <1>C{[=~"^[a-s]*$"]: <2>(_: string)->int, }, ` +
- `#B: <3>C{[=~"^[m-z]*?"]: <4>(_: string)->int, }, ` +
- `#C: <5>C{C{[=~"^Q*$"]: <6>(_: string)->int}, C{(C{[=~"^[a-s]*$"]: <7>(_: string)->int} & C{[=~"^[m-z]*?"]: <8>(_: string)->int})}, }, ` +
- `c: <9>C{C{[=~"^Q*$"]: <10>(_: string)->int}, C{(C{[=~"^[a-s]*$"]: <11>(_: string)->int} & C{[=~"^[m-z]*?"]: <12>(_: string)->int})}, QQ: 3}, ` +
- `#D: <13>C{[=~"^[a-s]*$"]: <14>(_: string)->int, [=~"^[m-z]*?"]: <15>(_: string)->int, }, ` +
- `d: <16>C{[=~"^[a-s]*$"]: <17>(_: string)->int, [=~"^[m-z]*?"]: <18>(_: string)->int, aaa: 4}}`,
- }, {
- in: `
- #Task: {
- {
- op: "pull"
- tag: *"latest" | string
- refToTag: tag
- tagExpr: tag + "dd"
- tagInString: "\(tag)"
- } | {
- op: "scratch"
- }
- }
-
- foo: #Task & {"op": "pull"}
- `,
- out: `<0>{#Task: (<1>C{op: "pull", tag: (*"latest" | string), refToTag: <1>.tag, tagExpr: (<1>.tag + "dd"), tagInString: ""+<1>.tag+""} | <2>C{op: "scratch"}), foo: <3>C{op: "pull", tag: "latest", refToTag: "latest", tagExpr: "latestdd", tagInString: "latest"}}`,
- }, {
- in: `
- t: {
- #ok: *true | bool
- if #ok {
- x: int
- }
- }
- s: t & {
- #ok: false
- }`,
- out: `<0>{t: <1>{x: int, #ok: true}, s: <2>{#ok: false}}`,
- }, {
- desc: "cross-dependent comprehension",
- // TODO(eval): fix: c should ultimately be allowed the struct. Current
- // semantics require, however, that generated fields are not available
- // for evaluation. This, however, does not have to hold, for closedness
- // checks and allowing this would be more intuitive.
- // Until that time, ensure that the behavior is at commutative.
- in: `
- #a: {
- if b {
- c: 4
- }
- b: bool
- }
- x: (#a & { b: true}) & {c: 4 }
- y: x
- `,
- // c should not be allowed, as it would break commutativiy.
- // See comments above.
- out: `<0>{x: _|_(4:field "c" not allowed in closed struct), y: _|_(4:field "c" not allowed in closed struct), #a: <1>C{b: bool if <2>.b yield <3>C{c: 4}}}`,
- }, {
- desc: "optional expanded before lookup",
- in: `
- test: [ID=_]: {
- name: ID
- }
-
- test: A: {
- field1: "1"
- field2: "2"
- }
-
- B: test.A & {}
- `,
- out: `<0>{test: <1>{[]: <2>(ID: string)-><3>{name: <2>.ID}, A: <4>{name: "A", field1: "1", field2: "2"}}, B: <5>{name: "A", field1: "1", field2: "2"}}`,
- }, {
- desc: "Issue #178",
- in: `
- import "encoding/csv"
- import "encoding/hex"
-
- foo: csv.Decode(data)
- data: bytes
-
- len: int
- bar: hex.EncodedLen(len)
- `,
- out: `<0>{foo: <1>.Decode (<2>.data), data: bytes, len: int, bar: <3>.EncodedLen (<2>.len)}`,
- }, {
- // This resulted in an issue in an older version. Prevent regression.
- desc: "comprehension and skipped field",
- in: `
- for key, value in {x: v: 1} {
- "\(key)": {
- v: *{for pod, _ in value.v {}} | {"\(value.v)": 2}
- _p: 3
- }
- }
- `,
- out: `<0>{x: <1>{v: <2>{"1": 2}, _p: 3}}`,
- }, {
- desc: "non-structural direct cycles",
- in: `
- c1: {bar: baz: 2} & c1.bar
- c2: {bar: 1} & c2.bar
- `,
- out: `<0>{` +
- `c1: <1>{bar: <2>{baz: 2}, baz: 2}, ` +
- `c2: _|_(conflicting values {bar: 1} and 1 (mismatched types struct and int))}`,
- }, {
- desc: "dont bind to string labels",
- in: `
- x: 1
- y: {
- "x": 2
- z: x
- }
- `,
- out: `<0>{x: 1, y: <1>{x: 2, z: 1}}`,
- }, {
- desc: "dont pass incomplete values to builtins",
- in: `
- import "encoding/json"
-
- input: string
- foo: json.Marshal(input)
- `,
- out: `<0>{input: string, foo: <1>.Marshal (<2>.input)}`,
- }, {
- desc: "alias reuse in nested scope",
- in: `
- #Foo: {
- let X = or([ for k, _ in {} { k } ])
- connection: [X]: X
- }
- #A: {
- foo: "key"
- let X = foo
- a: foo: [X]: X
- }
- #B: {
- foo: string
- let X = foo
- a: foo: [X]: X
- }
- b: #B & { foo: "key" }
- `,
- out: `<0>{` +
- `#Foo: <1>C{connection: <2>C{[or ([ <3>for k, _ in <4>{} yield <3>.k ])]: <5>(_: string)->or ([ <3>for k, _ in <4>{} yield <3>.k ]), }}, ` +
- `#A: <6>C{foo: "key", a: <7>C{foo: <8>C{["key"]: <9>(_: string)-><10>.foo, }}}, ` +
- `#B: <11>C{foo: string, a: <12>C{foo: <13>C{[string]: <14>(_: string)-><15>.foo, }}}, ` +
- `b: <16>C{foo: "key", a: <17>C{foo: <18>C{["key"]: <19>(_: string)-><20>.foo, }}}` +
- `}`,
- }, {
- desc: "json Marshaling detects incomplete",
- in: `
- import "encoding/json"
- a: json.Marshal({ a: string} )
-
- foo: { a: 3, b: foo.c }
- b: json.Marshal(foo)
- `,
- out: `<0>{` +
- `a: <1>.Marshal (<2>{a: string}), ` +
- `foo: <3>{a: 3, b: <4>.foo.c}, ` +
- `b: <1>.Marshal (<4>.foo)}`,
- }, {
- desc: "detectIncompleteYAML",
- in: `
- package foobar
-
- import yaml "encoding/yaml"
-
- #Spec: {
- _vars: {something: string}
- data: {
- #foo: {
- use: _vars.something
- }
- baz: yaml.Marshal(_vars.something)
- foobar: yaml.Marshal(#foo)
- }
- }
- Val: #Spec & {
- _vars: something: "var-string"
- }
- `,
- out: `<0>{#Spec: <1>C{_vars: <2>C{something: string}, data: <3>C{#foo: <4>C{use: string}, baz: <5>.Marshal (<6>._vars.something), foobar: <5>.Marshal (<7>.#foo)}}, Val: <8>C{_vars: <9>C{something: "var-string"}, data: <10>C{#foo: <11>C{use: "var-string"}, baz: "var-string\n", foobar: "use: var-string\n"}}}`,
- }, {
- desc: "detectIncompleteJSON",
- in: `
- package foobar
-
- import "encoding/json"
-
- #Spec: {
- _vars: {something: string}
- data: {
- #foo: {
- use: _vars.something
- }
- baz: json.Marshal(_vars.something)
- foobar: json.Marshal(#foo)
- }
- }
- Val: #Spec & {
- _vars: something: "var-string"
- }
- `,
- out: `<0>{#Spec: <1>C{_vars: <2>C{something: string}, data: <3>C{#foo: <4>C{use: string}, baz: <5>.Marshal (<6>._vars.something), foobar: <5>.Marshal (<7>.#foo)}}, Val: <8>C{_vars: <9>C{something: "var-string"}, data: <10>C{#foo: <11>C{use: "var-string"}, baz: "\"var-string\"", foobar: "{\"use\":\"var-string\"}"}}}`,
- }, {
- desc: "issue312",
- in: `
- for x in [1] {
- *close({}) | { [_]: null }
- }
- `,
- out: `<0>{ <1>for _, x in [1] yield <2>{}, (*close (<3>{}) | <4>{[]: <5>(_: string)->null, })}`,
- }, {
- // TODO(eval): note that this behavior is incompatible with allowing
- // non-struct as emit values. If we ever want to do this, we need to
- // do it soon.
- desc: "issue312",
- in: `
- y: *1 | {a: 2}
- for x in [1] { y }
- `,
- out: `<0>{y: 1, a: 2}`,
- }, {
- desc: "issue318",
- in: `
- #T: {
- arg: x: string
- out1: "\(arg.x) \(arg.y)"
- out2: "\(arg.y)"
- vx: arg.x
- vy: arg.y
- }
- `,
- out: `<0>{` +
- `#T: <1>C{` +
- `arg: <2>C{x: string}, ` +
- `out1: _|_(<3>.arg.y:undefined field "y"), ` +
- `out2: _|_(<3>.arg.y:undefined field "y"), ` +
- `vx: string, ` +
- `vy: _|_(<3>.arg.y:undefined field "y")}}`,
- }, {
- desc: "issue314",
- in: `
- import (
- "text/template"
- "encoding/yaml"
- "encoding/json"
- )
-
- x: {
- s: "myname"
- #T
- }
-
- #T: {
- s: string
- out: template.Execute("{{.s}}", {
- "s": s
- })
- }
-
- #V: {
- s: string
- out: json.Marshal({"s": s})
- }
-
- #U: {
- s: string
- out: yaml.Marshal({"s": s})
- }`,
- out: `<0>{` +
- `x: <1>C{s: "myname", out: "myname"}, ` +
- `#T: <2>C{s: string, out: <3>.Execute ("{{.s}}",<4>C{s: <5>.s})}, ` +
- `#V: <6>C{s: string, out: <7>.Marshal (<8>C{s: <9>.s})}, ` +
- `#U: <10>C{s: string, out: <11>.Marshal (<12>C{s: <13>.s})}}`,
- }}
- rewriteHelper(t, testCases, evalFull)
-}
-
func TestX(t *testing.T) {
// Don't remove. For debugging.
in := `
@@ -3048,5 +29,34 @@
if strings.TrimSpace(in) == "" {
t.Skip()
}
- rewriteHelper(t, []testCase{{in: in}}, evalFull)
+}
+
+// var traceOn = flag.Bool("debug", false, "enable tracing")
+
+// func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, error) {
+// t.Helper()
+// ctx, inst, err := compileInstance(t, body)
+// return ctx, inst.root, err
+// }
+
+// func compileFile(t *testing.T, body string) (*context, *structLit) {
+// t.Helper()
+// ctx, inst, errs := compileInstance(t, body)
+// if errs != nil {
+// t.Fatal(errs)
+// }
+// return ctx, inst.root
+// }
+
+func compileInstance(t *testing.T, body string) (*context, *Instance, error) {
+ var r Runtime
+ inst, err := r.Compile("test", body)
+
+ if err != nil {
+ x := newInstance(newIndex(sharedIndex), nil, &adt.Vertex{})
+ ctx := x.newContext()
+ return ctx, x, err
+ }
+
+ return r.index().newContext(), inst, nil
}
diff --git a/cue/rewrite.go b/cue/rewrite.go
deleted file mode 100644
index 7f7916f..0000000
--- a/cue/rewrite.go
+++ /dev/null
@@ -1,277 +0,0 @@
-// 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 cue
-
-// TODO: nodeRefs are currently not updated if the structs they point to are
-// updated. Handing this in uses of rewrite is tedious and hard to get correct.
-// Make this a general mechanism. This can be done using a Tomabechi-like
-// approach of associating copies with nodes in one pass, and then make a
-// complete copy in a second.
-
-type rewriteFunc func(ctx *context, v value) (value, bool)
-
-func rewrite(ctx *context, v value, fn rewriteFunc) value {
- v, descend := fn(ctx, v)
- if !descend {
- return v
- }
- return v.rewrite(ctx, fn)
-}
-
-func (x *nodeRef) rewrite(ctx *context, fn rewriteFunc) value {
- return x
-}
-
-func (x *closeIfStruct) rewrite(ctx *context, fn rewriteFunc) value {
- v := rewrite(ctx, x.value, fn)
- if v == x.value {
- return x
- }
- return wrapFinalize(ctx, v)
-}
-
-func (x *structLit) rewrite(ctx *context, fn rewriteFunc) value {
- emit := x.emit
- if emit != nil {
- emit = rewrite(ctx, x.emit, fn)
- }
- arcs := make(arcs, len(x.Arcs))
- obj := &structLit{baseValue: x.baseValue, emit: emit, Arcs: arcs}
- changed := emit == x.emit
- for i, a := range x.Arcs {
- a.setValue(rewrite(ctx, a.v, fn))
- changed = changed || arcs[i].v != a.v
- arcs[i] = a
- }
- if !changed {
- return x
- }
- return obj
-}
-
-func (x *selectorExpr) rewrite(ctx *context, fn rewriteFunc) value {
- v := rewrite(ctx, x.X, fn)
- if v == x.X {
- return x
- }
- return &selectorExpr{x.baseValue, v, x.Sel}
-}
-
-func (x *indexExpr) rewrite(ctx *context, fn rewriteFunc) value {
- v := rewrite(ctx, x.X, fn)
- index := rewrite(ctx, x.Index, fn)
- if v == x.X && index == x.Index {
- return x
- }
- return &indexExpr{x.baseValue, v, index}
-}
-
-// Even more boring stuff below.
-
-func (x *builtin) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *top) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *bottom) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *basicType) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *nullLit) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *boolLit) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *stringLit) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *bytesLit) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *numLit) rewrite(ctx *context, fn rewriteFunc) value { return x }
-func (x *durationLit) rewrite(ctx *context, fn rewriteFunc) value { return x }
-
-func (x *customValidator) rewrite(ctx *context, fn rewriteFunc) value {
- args := make([]evaluated, len(x.Args))
- changed := false
- for i, a := range x.Args {
- v := rewrite(ctx, a, fn)
- args[i] = v.(evaluated)
- changed = changed || v != a
- }
- if !changed {
- return x
- }
- return &customValidator{baseValue: x.baseValue, Args: args, Builtin: x.Builtin}
-}
-
-func (x *bound) rewrite(ctx *context, fn rewriteFunc) value {
- v := rewrite(ctx, x.Expr, fn)
- if v == x.Expr {
- return x
- }
- return newBound(ctx, x.baseValue, x.Op, x.k, v)
-}
-
-func (x *interpolation) rewrite(ctx *context, fn rewriteFunc) value {
- parts := make([]value, len(x.Parts))
- changed := false
- for i, p := range x.Parts {
- parts[i] = rewrite(ctx, p, fn)
- changed = changed || parts[i] != p
- }
- if !changed {
- return x
- }
- return &interpolation{x.baseValue, x.K, parts}
-}
-
-func (x *list) rewrite(ctx *context, fn rewriteFunc) value {
- elem := rewrite(ctx, x.elem, fn).(*structLit)
- typ := rewrite(ctx, x.typ, fn)
- len := rewrite(ctx, x.len, fn)
- if elem == x.elem && typ == x.typ && len == x.len {
- return x
- }
- return &list{x.baseValue, elem, typ, len}
-}
-
-func (x *sliceExpr) rewrite(ctx *context, fn rewriteFunc) value {
- v := rewrite(ctx, x.X, fn)
- var lo, hi value
- if x.Lo != nil {
- lo = rewrite(ctx, x.Lo, fn)
- }
- if x.Hi != nil {
- hi = rewrite(ctx, x.Hi, fn)
- }
- if v == x.X && lo == x.Lo && hi == x.Hi {
- return x
- }
- return &sliceExpr{x.baseValue, v, lo, hi}
-}
-
-func (x *callExpr) rewrite(ctx *context, fn rewriteFunc) value {
- args := make([]value, len(x.Args))
- changed := false
- for i, a := range x.Args {
- v := rewrite(ctx, a, fn)
- args[i] = v
- changed = changed || v != a
- }
- v := rewrite(ctx, x.Fun, fn)
- if !changed && v == x.Fun {
- return x
- }
- return &callExpr{baseValue: x.baseValue, Fun: v, Args: args}
-}
-
-func (x *lambdaExpr) rewrite(ctx *context, fn rewriteFunc) value {
- arcs := make([]arc, len(x.arcs))
- changed := false
- for i, a := range x.arcs {
- v := rewrite(ctx, a.v, fn)
- arcs[i] = arc{Label: a.Label, v: v}
- changed = changed || v != a.v
- }
- value := rewrite(ctx, x.value, fn)
- if !changed && value == x.value {
- return x
- }
- return &lambdaExpr{x.baseValue, ¶ms{arcs}, value}
-}
-
-func (x *unaryExpr) rewrite(ctx *context, fn rewriteFunc) value {
- v := rewrite(ctx, x.X, fn)
- if v == x.X {
- return x
- }
- return &unaryExpr{x.baseValue, x.Op, v}
-}
-
-func (x *binaryExpr) rewrite(ctx *context, fn rewriteFunc) value {
- left := rewrite(ctx, x.X, fn)
- right := rewrite(ctx, x.Y, fn)
- if left == x.X && right == x.Y {
- return x
- }
- return updateBin(ctx, &binaryExpr{x.baseValue, x.Op, left, right})
-}
-
-func (x *unification) rewrite(ctx *context, fn rewriteFunc) value {
- values := make([]evaluated, len(x.Values))
- changed := false
- for i, v := range x.Values {
- values[i] = rewrite(ctx, v, fn).(evaluated)
- changed = changed || v != values[i]
- }
- if !changed {
- return x
- }
- return &unification{x.baseValue, values}
-}
-
-func (x *disjunction) rewrite(ctx *context, fn rewriteFunc) value {
- values := make([]dValue, len(x.Values))
- changed := false
- for i, d := range x.Values {
- v := rewrite(ctx, d.Val, fn)
- values[i] = dValue{v, d.Default}
- changed = changed || v != d.Val
- }
- if !changed {
- return x
- }
- return &disjunction{x.baseValue, values, x.errors, x.HasDefaults}
-}
-
-func (x *listComprehension) rewrite(ctx *context, fn rewriteFunc) value {
- clauses := rewrite(ctx, x.clauses, fn).(yielder)
- if clauses == x.clauses {
- return x
- }
- return &listComprehension{x.baseValue, clauses}
-}
-
-func (x *structComprehension) rewrite(ctx *context, fn rewriteFunc) value {
- clauses := rewrite(ctx, x.clauses, fn).(yielder)
- if clauses == x.clauses {
- return x
- }
- return &structComprehension{x.baseValue, clauses}
-}
-
-func (x *fieldComprehension) rewrite(ctx *context, fn rewriteFunc) value {
- key := rewrite(ctx, x.key, fn)
- val := rewrite(ctx, x.val, fn)
- if key == x.key && val == x.val {
- return x
- }
- return &fieldComprehension{x.baseValue, key, val, x.opt, x.def, x.doc, x.attrs}
-}
-
-func (x *yield) rewrite(ctx *context, fn rewriteFunc) value {
- value := rewrite(ctx, x.value, fn)
- if value == x.value {
- return x
- }
- return &yield{x.baseValue, value}
-}
-
-func (x *guard) rewrite(ctx *context, fn rewriteFunc) value {
- condition := rewrite(ctx, x.Condition, fn)
- value := rewrite(ctx, x.Dst, fn).(yielder)
- if condition == x.Condition && value == x.Dst {
- return x
- }
- return &guard{x.baseValue, condition, value}
-}
-
-func (x *feed) rewrite(ctx *context, fn rewriteFunc) value {
- source := rewrite(ctx, x.Src, fn)
- lambda := rewrite(ctx, x.fn, fn).(*lambdaExpr)
- if source == x.Src && lambda == x.fn {
- return x
- }
- return &feed{x.baseValue, source, lambda}
-}
diff --git a/cue/rewrite_test.go b/cue/rewrite_test.go
deleted file mode 100644
index 4baa561..0000000
--- a/cue/rewrite_test.go
+++ /dev/null
@@ -1,87 +0,0 @@
-// 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 cue
-
-type rewriteMode int
-
-const (
- evalRaw rewriteMode = iota
- evalSimplify
- evalPartial // all but disjunctions
- evalFull
-)
-
-// testResolve recursively
-func testResolve(ctx *context, v value, m rewriteMode) (result value) {
- if m == evalRaw || v == nil {
- return v
- }
- return rewriteRec(ctx, v, v.evalPartial(ctx), m)
-}
-
-func rewriteRec(ctx *context, raw value, eval evaluated, m rewriteMode) (result value) {
- if m >= evalPartial {
- if isIncomplete(eval) {
- return raw
- }
- if m == evalFull {
- eval = ctx.manifest(eval)
- if isBottom(eval) {
- return eval
- }
- }
- }
- switch x := eval.(type) {
- case *structLit:
- if m == evalFull {
- e := ctx.manifest(x)
- if isBottom(e) {
- return e
- }
- x = e.(*structLit)
- }
- var err *bottom
- x, err = x.expandFields(ctx)
- if err != nil {
- if isIncomplete(err) {
- return raw
- }
- return err
- }
- arcs := make(arcs, len(x.Arcs))
- for i, a := range x.Arcs {
- v := x.at(ctx, i)
- a.setValue(rewriteRec(ctx, a.v, v, m))
- arcs[i] = a
- }
-
- t, e := x.optionals.rewrite(func(v value) value {
- return rewriteRec(ctx, v, v.evalPartial(ctx), m)
- })
- if e != nil {
- return err
- }
- emit := testResolve(ctx, x.emit, m)
- obj := &structLit{x.baseValue, emit, t, x.closeStatus, x.comprehensions, arcs, nil}
- return obj
- case *list:
- elm := rewriteRec(ctx, x.elem, x.elem, m).(*structLit)
- len := rewriteRec(ctx, x.len, x.len.(evaluated), m)
- typ := rewriteRec(ctx, x.typ, x.typ.evalPartial(ctx), m)
- return &list{x.baseValue, elm, typ, len}
- default:
- return eval
- }
-}
diff --git a/cue/strip.go b/cue/strip.go
deleted file mode 100644
index a806a3d..0000000
--- a/cue/strip.go
+++ /dev/null
@@ -1,129 +0,0 @@
-// 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 cue
-
-import (
- "sort"
-)
-
-// A mergedValues type merges structs without unifying their templates.
-// It evaluates structs in parallel and then creates a new mergedValues
-// for each duplicate arc. The mergedValues do not reappear once there is
-// only a single value per arc.
-//
-// This is used to merge different instances which may have incompatible
-// specializations, but have disjuncts objects that may otherwise be shared
-// in the same namespace.
-type mergedValues struct {
- baseValue
- values []value
-}
-
-func (x *mergedValues) evalPartial(ctx *context) evaluated {
- var structs []*structLit
- for _, v := range x.values {
- v = v.evalPartial(ctx)
- o, ok := v.(*structLit)
- if !ok {
- v := x.values[0]
- for _, w := range x.values[1:] {
- v = mkBin(ctx, w.Pos(), opUnify, v, w)
- }
- return v.evalPartial(ctx)
- }
- o, err := o.expandFields(ctx)
- if err != nil {
- return err
- }
- structs = append(structs, o)
- }
-
- // Pre-expand the arcs so that we can discard the templates.
- obj := &structLit{
- baseValue: structs[0].baseValue,
- }
- var arcs arcs
- for _, v := range structs {
- for i := 0; i < len(v.Arcs); i++ {
- w := v.iterAt(ctx, i)
- arcs = append(arcs, w)
- }
- }
- obj.Arcs = arcs
- sort.Stable(obj)
-
- values := []value{}
- for _, v := range structs {
- if v.emit != nil {
- values = append(values, v.emit)
- }
- }
- switch len(values) {
- case 0:
- case 1:
- obj.emit = values[0]
- default:
- obj.emit = &mergedValues{values[0].base(), values}
- }
-
- // merge arcs
- k := 0
- for i := 0; i < len(arcs); k++ {
- a := arcs[i]
- // TODO: consider storing the evaluated value. This is a performance
- // versus having more information tradeoff. It results in the same
- // value.
- values := []value{a.v}
- for i++; i < len(arcs) && a.Label == arcs[i].Label; i++ {
- values = append(values, arcs[i].v)
- a.optional = a.optional && arcs[i].optional
- var err evaluated
- a.attrs, err = unifyAttrs(ctx, a.v, a.attrs, arcs[i].attrs)
- if err != nil {
- return err
- }
- a.docs = mergeDocs(a.docs, arcs[i].docs)
- }
- if len(values) == 1 {
- arcs[k] = a
- continue
- }
- a.Value = nil
- a.v = &mergedValues{a.v.base(), values}
- arcs[k] = a
- }
- obj.Arcs = arcs[:k]
- return obj
-}
-
-func (x *mergedValues) Kind() kind {
- k := x.values[0].Kind()
- for _, v := range x.values {
- k = unifyType(k, v.Kind())
- }
- return k
-}
-
-func (x *mergedValues) rewrite(ctx *context, fn rewriteFunc) value {
- vs := make([]value, len(x.values))
- for i, v := range x.values {
- vs[i] = rewrite(ctx, v, fn)
- }
- return &mergedValues{x.baseValue, vs}
-}
-
-func (x *mergedValues) subsumesImpl(s *subsumer, v value) bool {
- return false
-}
diff --git a/cue/subsume.go b/cue/subsume.go
deleted file mode 100644
index 6eca12d..0000000
--- a/cue/subsume.go
+++ /dev/null
@@ -1,662 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
-
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
-)
-
-// TODO: it probably makes sense to have only two modes left: subsuming a schema
-// and subsuming a final value.
-
-func subsumes(v, w Value, mode subsumeMode) error {
- ctx := v.ctx()
- gt := v.eval(ctx)
- lt := w.eval(ctx)
- s := subsumer{ctx: ctx, mode: mode}
- if !s.subsumes(gt, lt) {
- var b *bottom
- src := binSrc(token.NoPos, opUnify, gt, lt)
- if s.gt != nil && s.lt != nil {
- src := binSrc(token.NoPos, opUnify, s.gt, s.lt)
- var ok bool
- if s.missing != 0 {
- b = ctx.mkErr(src, "missing field %q", ctx.LabelStr(s.missing))
- } else if b, ok = binOp(ctx, src, opUnify, s.gt, s.lt).(*bottom); !ok {
- b = ctx.mkErr(src, "value not an instance")
- }
- }
- if b == nil {
- b = ctx.mkErr(src, "value not an instance")
- } else {
- b = ctx.mkErr(src, b, "%v", b)
- }
- err := w.toErr(b)
- if s.inexact {
- err = internal.DecorateError(internal.ErrInexact, err)
- }
- return err
- }
- return nil
-}
-
-type subsumer struct {
- ctx *context
- mode subsumeMode
-
- inexact bool // If true, the result could be a false negative.
-
- // recorded values where an error occurred.
- gt, lt evaluated
- missing label
-
- // depth is used to work around undetected cycles.
- // TODO(eval): remove once cycle detection is implemented.
- depth int
-}
-
-type subsumeMode int
-
-const (
- // subChoose ensures values are elected before doing a subsumption. This
- // feature is on the conservative side and may result in false negatives.
- subChoose subsumeMode = 1 << iota
-
- // subNoOptional ignores optional fields for the purpose of subsumption.
- // This option is predominantly intended for implementing equality checks.
- // TODO: may be unnecessary now subFinal is available.
- subNoOptional
-
- // The subsumed value is final.
- subFinal
-
- // subSchema is used to compare schema. It should ignore closedness.
- subSchema
-)
-
-// TODO: improve upon this highly inefficient implementation. There should
-// be a dedicated equal function once the dust settles.
-func equals(c *context, x, y value) bool {
- s := subsumer{ctx: c, mode: subNoOptional}
- return s.subsumes(x, y) && s.subsumes(y, x)
-}
-
-// subsumes checks gt subsumes lt. If any of the values contains references or
-// unevaluated expressions, structural subsumption is performed. This means
-// subsumption is conservative; it may return false when a guarantee for
-// subsumption could be proven. For concreted values it returns the exact
-// relation. It never returns a false positive.
-func (s *subsumer) subsumes(gt, lt value) (result bool) {
- if s.depth > internal.MaxDepth {
- return true
- }
- s.depth++
- defer func() { s.depth-- }()
-
- ctx := s.ctx
- var v, w evaluated
- if s.mode&subChoose == 0 {
- v = gt.evalPartial(ctx)
- w = lt.evalPartial(ctx)
- } else {
- v = ctx.manifest(gt)
- w = ctx.manifest(lt)
- }
- if !isIncomplete(v) && !isIncomplete(w) {
- gt = v
- lt = w
- }
- a := gt.Kind()
- b := lt.Kind()
- switch {
- case b == bottomKind:
- return true
- case b&^(a&b) != 0:
- // a does not have strictly more bits. This implies any ground kind
- // subsuming a non-ground type.
- goto exit
- // TODO: consider not supporting references.
- // case (a|b)&(referenceKind) != 0:
- // // no resolution if references are in play.
- // return false, false
- }
- switch lt := lt.(type) {
- case *unification:
- if _, ok := gt.(*unification); !ok {
- for _, x := range lt.Values {
- if s.subsumes(gt, x) {
- return true
- }
- }
- goto exit
- }
-
- case *disjunction:
- if _, ok := gt.(*disjunction); !ok {
- for _, x := range lt.Values {
- if !s.subsumes(gt, x.Val) {
- return false
- }
- }
- return true
- }
- }
-
- result = gt.subsumesImpl(s, lt)
-exit:
- if !result && s.gt == nil && s.lt == nil {
- s.gt = v
- s.lt = w
- }
- return result
-}
-
-func (x *structLit) subsumesImpl(s *subsumer, v value) bool {
- ctx := s.ctx
- ignoreOptional := s.mode&subNoOptional != 0
- if o, ok := v.(*structLit); ok {
- if x.optionals != nil && !ignoreOptional {
- if s.mode&subFinal == 0 {
- // TODO: also cross-validate optional fields in the schema case.
- s.inexact = true
- return false
- }
- for _, b := range o.Arcs {
- if b.optional || b.definition {
- continue
- }
- name := ctx.LabelStr(b.Label)
- arg := &stringLit{x.baseValue, name, nil}
- u, _ := x.optionals.constraint(ctx, arg)
- if u != nil && !s.subsumes(u, b.v) {
- return false
- }
- }
- }
- if len(x.comprehensions) > 0 {
- s.inexact = true
- return false
- }
- if x.emit != nil {
- if o.emit == nil || !s.subsumes(x.emit, o.emit) {
- return false
- }
- }
-
- xClosed := x.closeStatus.shouldClose() && s.mode&subSchema == 0
- oClosed := o.closeStatus.shouldClose() && s.mode&subSchema == 0
-
- // all arcs in n must exist in v and its values must subsume.
- for _, a := range x.Arcs {
- if a.optional && ignoreOptional {
- continue
- }
- b := o.Lookup(ctx, a.Label)
- if !a.optional && b.optional {
- return false
- } else if b.val() == nil {
- if a.definition && s.mode&subFinal != 0 {
- continue
- }
- // if o is closed, the field is implicitly defined as _|_ and
- // thus subsumed. Technically, this is even true if a is not
- // optional, but in that case it means that o is invalid, so
- // return false regardless
- if a.optional && (oClosed || s.mode&subFinal != 0) {
- continue
- }
- // If field a is optional and has value top, neither the
- // omission of the field nor the field defined with any value
- // may cause unification to fail.
- if a.optional && isTop(a.v) {
- continue
- }
- s.missing = a.Label
- s.gt = a.val()
- s.lt = o
- return false
- } else if a.definition != b.definition {
- return false
- } else if !s.subsumes(a.v, b.val()) {
- return false
- }
- }
- // For closed structs, all arcs in b must exist in a.
- if xClosed {
- if !ignoreOptional && !oClosed && s.mode&subFinal == 0 {
- return false
- }
- ignoreOptional = ignoreOptional || s.mode&subFinal != 0
- for _, b := range o.Arcs {
- if ignoreOptional && b.optional {
- continue
- }
- a := x.Lookup(ctx, b.Label)
- if a.val() == nil {
- name := ctx.LabelStr(b.Label)
- arg := &stringLit{x.baseValue, name, nil}
- u, _ := x.optionals.constraint(ctx, arg)
- if u == nil { // subsumption already checked
- s.lt = b.val()
- return false
- }
- }
- }
- }
- }
- return !isBottom(v)
-}
-
-func (*top) subsumesImpl(s *subsumer, v value) bool {
- return true
-}
-
-func (x *bottom) subsumesImpl(s *subsumer, v value) bool {
- // never called.
- return v.Kind() == bottomKind
-}
-
-func (x *basicType) subsumesImpl(s *subsumer, v value) bool {
- return true
-}
-
-func (x *bound) subsumesImpl(s *subsumer, v value) bool {
- ctx := s.ctx
- if isBottom(v) {
- return true
- }
- kx := x.Expr.Kind()
- if !kx.isDone() || !kx.isGround() {
- return false
- }
-
- switch y := v.(type) {
- case *bound:
- if ky := y.Expr.Kind(); ky.isDone() && ky.isGround() {
- if (kx&ky)&^kx != 0 {
- return false
- }
- // x subsumes y if
- // x: >= a, y: >= b ==> a <= b
- // x: >= a, y: > b ==> a <= b
- // x: > a, y: > b ==> a <= b
- // x: > a, y: >= b ==> a < b
- //
- // x: <= a, y: <= b ==> a >= b
- //
- // x: != a, y: != b ==> a != b
- //
- // false if types or op direction doesn't match
-
- xv := x.Expr.(evaluated)
- yv := y.Expr.(evaluated)
- switch x.Op {
- case opGtr:
- if y.Op == opGeq {
- return test(ctx, x, opLss, xv, yv)
- }
- fallthrough
- case opGeq:
- if y.Op == opGtr || y.Op == opGeq {
- return test(ctx, x, opLeq, xv, yv)
- }
- case opLss:
- if y.Op == opLeq {
- return test(ctx, x, opGtr, xv, yv)
- }
- fallthrough
- case opLeq:
- if y.Op == opLss || y.Op == opLeq {
- return test(ctx, x, opGeq, xv, yv)
- }
- case opNeq:
- switch y.Op {
- case opNeq:
- return test(ctx, x, opEql, xv, yv)
- case opGeq:
- return test(ctx, x, opLss, xv, yv)
- case opGtr:
- return test(ctx, x, opLeq, xv, yv)
- case opLss:
- return test(ctx, x, opGeq, xv, yv)
- case opLeq:
- return test(ctx, x, opGtr, xv, yv)
- }
-
- case opMat, opNMat:
- // these are just approximations
- if y.Op == x.Op {
- return test(ctx, x, opEql, xv, yv)
- }
-
- default:
- // opNeq already handled above.
- panic("cue: undefined bound mode")
- }
- }
- // structural equivalence
- return false
-
- case *numLit, *stringLit, *durationLit, *boolLit:
- return test(ctx, x, x.Op, y.(evaluated), x.Expr.(evaluated))
- }
- return false
-}
-
-func (x *nullLit) subsumesImpl(s *subsumer, v value) bool {
- return true
-}
-
-func (x *boolLit) subsumesImpl(s *subsumer, v value) bool {
- return x.B == v.(*boolLit).B
-}
-
-func (x *stringLit) subsumesImpl(s *subsumer, v value) bool {
- return x.Str == v.(*stringLit).Str
-}
-
-func (x *bytesLit) subsumesImpl(s *subsumer, v value) bool {
- return bytes.Equal(x.B, v.(*bytesLit).B)
-}
-
-func (x *numLit) subsumesImpl(s *subsumer, v value) bool {
- b := v.(*numLit)
- return x.X.Cmp(&b.X) == 0
-}
-
-func (x *durationLit) subsumesImpl(s *subsumer, v value) bool {
- return x.d == v.(*durationLit).d
-}
-
-func (x *list) subsumesImpl(s *subsumer, v value) bool {
- switch y := v.(type) {
- case *list:
- if !s.subsumes(x.len, y.len) {
- return false
- }
- // TODO: need to handle case where len(x.elem) > len(y.elem) explicitly
- // if we introduce cap().
- if !s.subsumes(x.elem, y.elem) {
- return false
- }
- // TODO: assuming continuous indices, use merge sort if we allow
- // sparse arrays.
- for _, a := range y.elem.Arcs[len(x.elem.Arcs):] {
- if !s.subsumes(x.typ, a.v) {
- return false
- }
- }
- if y.isOpen() { // implies from first check that x.IsOpen.
- return s.subsumes(x.typ, y.typ)
- }
- return true
- }
- return isBottom(v)
-}
-
-func (x *params) subsumes(s *subsumer, y *params) bool {
- // structural equivalence
- // TODO: make agnostic to argument names.
- if len(y.arcs) != len(x.arcs) {
- return false
- }
- for i, a := range x.arcs {
- if !s.subsumes(a.v, y.arcs[i].v) {
- return false
- }
- }
- return true
-}
-
-func (x *lambdaExpr) subsumesImpl(s *subsumer, v value) bool {
- // structural equivalence
- if y, ok := v.(*lambdaExpr); ok {
- return x.params.subsumes(s, y.params) &&
- s.subsumes(x.value, y.value)
- }
- return isBottom(v)
-}
-
-func (x *unification) subsumesImpl(s *subsumer, v value) bool {
- if y, ok := v.(*unification); ok {
- // A unification subsumes another unification if for all values a in x
- // there is a value b in y such that a subsumes b.
- //
- // This assumes overlapping ranges in disjunctions are merged.If this is
- // not the case, subsumes will return a false negative, which is
- // allowed.
- outer:
- for _, vx := range x.Values {
- for _, vy := range y.Values {
- if s.subsumes(vx, vy) {
- continue outer
- }
- }
- // TODO: should this be marked as inexact?
- return false
- }
- return true
- }
- subsumed := true
- for _, vx := range x.Values {
- subsumed = subsumed && s.subsumes(vx, v)
- }
- return subsumed
-}
-
-// subsumes for disjunction is logically precise. However, just like with
-// structural subsumption, it should not have to be called after evaluation.
-func (x *disjunction) subsumesImpl(s *subsumer, v value) bool {
- // A disjunction subsumes another disjunction if all values of v are
- // subsumed by any of the values of x, and default values in v are subsumed
- // by the default values of x.
- //
- // This assumes that overlapping ranges in x are merged. If this is not the
- // case, subsumes will return a false negative, which is allowed.
- if d, ok := v.(*disjunction); ok {
- // at least one value in x should subsume each value in d.
- outer:
- for _, vd := range d.Values {
- // v is subsumed if any value in x subsumes v.
- for _, vx := range x.Values {
- if (vx.Default || !vd.Default) && s.subsumes(vx.Val, vd.Val) {
- continue outer
- }
- }
- return false
- }
- return true
- }
- // v is subsumed if any value in x subsumes v.
- for _, vx := range x.Values {
- if s.subsumes(vx.Val, v) {
- return true
- }
- }
- // TODO: should this be marked as inexact?
- return false
-}
-
-// Structural subsumption operations. Should never have to be called after
-// evaluation.
-
-// structural equivalence
-func (x *nodeRef) subsumesImpl(s *subsumer, v value) bool {
- if r, ok := v.(*nodeRef); ok {
- return x.node == r.node
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *selectorExpr) subsumesImpl(s *subsumer, v value) bool {
- if r, ok := v.(*selectorExpr); ok {
- return x.Sel == r.Sel && s.subsumes(x.X, r.X) // subChoose
- }
- return isBottom(v)
-}
-
-func (x *interpolation) subsumesImpl(s *subsumer, v value) bool {
- switch v := v.(type) {
- case *stringLit:
- // Be conservative if not ground.
- s.inexact = true
- return false
-
- case *interpolation:
- // structural equivalence
- if len(x.Parts) != len(v.Parts) {
- return false
- }
- for i, p := range x.Parts {
- if !s.subsumes(p, v.Parts[i]) {
- return false
- }
- }
- return true
- }
- return false
-}
-
-// structural equivalence
-func (x *indexExpr) subsumesImpl(s *subsumer, v value) bool {
- // TODO: what does it mean to subsume if the index value is not known?
- if r, ok := v.(*indexExpr); ok {
- // TODO: could be narrowed down if we know the exact value of the index
- // and referenced value.
- return s.subsumes(x.X, r.X) && s.subsumes(x.Index, r.Index)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *sliceExpr) subsumesImpl(s *subsumer, v value) bool {
- // TODO: what does it mean to subsume if the index value is not known?
- if r, ok := v.(*sliceExpr); ok {
- // TODO: could be narrowed down if we know the exact value of the index
- // and referenced value.
- return s.subsumes(x.X, r.X) &&
- s.subsumes(x.Lo, r.Lo) &&
- s.subsumes(x.Hi, r.Hi)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *customValidator) subsumesImpl(s *subsumer, v value) bool {
- y, ok := v.(*customValidator)
- if !ok {
- return isBottom(v)
- }
- if x.Builtin != y.Builtin {
- return false
- }
- for i, v := range x.Args {
- if !s.subsumes(v, y.Args[i]) {
- return false
- }
- }
- return true
-}
-
-// structural equivalence
-func (x *callExpr) subsumesImpl(s *subsumer, v value) bool {
- if c, ok := v.(*callExpr); ok {
- if len(x.Args) != len(c.Args) {
- return false
- }
- for i, a := range x.Args {
- if !s.subsumes(a, c.Args[i]) {
- return false
- }
- }
- return s.subsumes(x.Fun, c.Fun)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *unaryExpr) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*unaryExpr); ok {
- return x.Op == b.Op && s.subsumes(x.X, b.X)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *binaryExpr) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*binaryExpr); ok {
- return x.Op == b.Op &&
- s.subsumes(x.X, b.X) &&
- s.subsumes(x.Y, b.Y)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *listComprehension) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*listComprehension); ok {
- return s.subsumes(x.clauses, b.clauses)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *structComprehension) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*structComprehension); ok {
- return s.subsumes(x.clauses, b.clauses)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *fieldComprehension) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*fieldComprehension); ok {
- return s.subsumes(x.key, b.key) &&
- s.subsumes(x.val, b.val) &&
- !x.opt && b.opt &&
- x.def == b.def
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *yield) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*yield); ok {
- return s.subsumes(x.value, b.value)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *feed) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*feed); ok {
- return s.subsumes(x.Src, b.Src) &&
- s.subsumes(x.fn, b.fn)
- }
- return isBottom(v)
-}
-
-// structural equivalence
-func (x *guard) subsumesImpl(s *subsumer, v value) bool {
- if b, ok := v.(*guard); ok {
- return s.subsumes(x.Condition, b.Condition) &&
- s.subsumes(x.Dst, b.Dst)
- }
- return isBottom(v)
-}
diff --git a/cue/subsume_test.go b/cue/subsume_test.go
deleted file mode 100644
index 0f6cd01..0000000
--- a/cue/subsume_test.go
+++ /dev/null
@@ -1,476 +0,0 @@
-// 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 cue
-
-import (
- "regexp"
- "strconv"
- "strings"
- "testing"
-)
-
-func TestSubsume(t *testing.T) {
- // Do not inline: the named struct is used as a marker in
- // testdata/gen.go.
- type subsumeTest struct {
- // the result of b ⊑ a, where a and b are defined in "in"
- subsumes bool
- in string
- mode subsumeMode
- }
- testCases := []subsumeTest{
- // Top subsumes everything
- 0: {subsumes: true, in: `a: _, b: _ `},
- 1: {subsumes: true, in: `a: _, b: null `},
- 2: {subsumes: true, in: `a: _, b: int `},
- 3: {subsumes: true, in: `a: _, b: 1 `},
- 4: {subsumes: true, in: `a: _, b: float `},
- 5: {subsumes: true, in: `a: _, b: "s" `},
- 6: {subsumes: true, in: `a: _, b: {} `},
- 7: {subsumes: true, in: `a: _, b: []`},
- 8: {subsumes: true, in: `a: _, b: _|_ `},
-
- // Nothing besides top subsumed top
- 9: {subsumes: false, in: `a: null, b: _`},
- 10: {subsumes: false, in: `a: int, b: _`},
- 11: {subsumes: false, in: `a: 1, b: _`},
- 12: {subsumes: false, in: `a: float, b: _`},
- 13: {subsumes: false, in: `a: "s", b: _`},
- 14: {subsumes: false, in: `a: {}, b: _`},
- 15: {subsumes: false, in: `a: [], b: _`},
- 16: {subsumes: false, in: `a: _|_ , b: _`},
-
- // Bottom subsumes nothing except bottom itself.
- 17: {subsumes: false, in: `a: _|_, b: null `},
- 18: {subsumes: false, in: `a: _|_, b: int `},
- 19: {subsumes: false, in: `a: _|_, b: 1 `},
- 20: {subsumes: false, in: `a: _|_, b: float `},
- 21: {subsumes: false, in: `a: _|_, b: "s" `},
- 22: {subsumes: false, in: `a: _|_, b: {} `},
- 23: {subsumes: false, in: `a: _|_, b: [] `},
- 24: {subsumes: true, in: ` a: _|_, b: _|_ `},
-
- // All values subsume bottom
- 25: {subsumes: true, in: `a: null, b: _|_`},
- 26: {subsumes: true, in: `a: int, b: _|_`},
- 27: {subsumes: true, in: `a: 1, b: _|_`},
- 28: {subsumes: true, in: `a: float, b: _|_`},
- 29: {subsumes: true, in: `a: "s", b: _|_`},
- 30: {subsumes: true, in: `a: {}, b: _|_`},
- 31: {subsumes: true, in: `a: [], b: _|_`},
- 32: {subsumes: true, in: `a: true, b: _|_`},
- 33: {subsumes: true, in: `a: _|_, b: _|_`},
-
- // null subsumes only null
- 34: {subsumes: true, in: ` a: null, b: null `},
- 35: {subsumes: false, in: `a: null, b: 1 `},
- 36: {subsumes: false, in: `a: 1, b: null `},
-
- 37: {subsumes: true, in: ` a: true, b: true `},
- 38: {subsumes: false, in: `a: true, b: false `},
-
- 39: {subsumes: true, in: ` a: "a", b: "a" `},
- 40: {subsumes: false, in: `a: "a", b: "b" `},
- 41: {subsumes: true, in: ` a: string, b: "a" `},
- 42: {subsumes: false, in: `a: "a", b: string `},
-
- // Number typing (TODO)
- //
- // In principle, an "int" cannot assume an untyped "1", as "1" may
- // still by typed as a float. They are two different type aspects. When
- // considering, keep in mind that:
- // Key requirement: if A subsumes B, it must not be possible to
- // specialize B further such that A does not subsume B. HOWEVER,
- // The type conversion rules for conversion are INDEPENDENT of the
- // rules for subsumption!
- // Consider:
- // - only having number, but allowing user-defined types.
- // Subsumption would still work the same, but it may be somewhat
- // less weird.
- // - making 1 always an int and 1.0 always a float.
- // - the int type would subsume any derived type from int.
- // - arithmetic would allow implicit conversions, but maybe not for
- // types.
- //
- // TODO: irrational numbers: allow untyped, but require explicit
- // trunking when assigning to float.
- //
- // a: number; cue.IsInteger(a) && a > 0
- // t: (x) -> number; cue.IsInteger(a) && a > 0
- // type x number: cue.IsInteger(x) && x > 0
- // x: typeOf(number); cue.IsInteger(x) && x > 0
- 43: {subsumes: true, in: `a: 1, b: 1 `},
- 44: {subsumes: true, in: `a: 1.0, b: 1.0 `},
- 45: {subsumes: true, in: `a: 3.0, b: 3.0 `},
- 46: {subsumes: false, in: `a: 1.0, b: 1 `},
- 47: {subsumes: false, in: `a: 1, b: 1.0 `},
- 48: {subsumes: false, in: `a: 3, b: 3.0`},
- 49: {subsumes: true, in: `a: int, b: 1`},
- 50: {subsumes: true, in: `a: int, b: int & 1`},
- 51: {subsumes: true, in: `a: float, b: 1.0`},
- 52: {subsumes: false, in: `a: float, b: 1`},
- 53: {subsumes: false, in: `a: int, b: 1.0`},
- 54: {subsumes: true, in: `a: int, b: int`},
- 55: {subsumes: true, in: `a: number, b: int`},
-
- // Structs
- 64: {subsumes: true, in: `a: {}, b: {}`},
- 65: {subsumes: true, in: `a: {}, b: {a: 1}`},
- 66: {subsumes: true, in: `a: {a:1}, b: {a:1, b:1}`},
- 67: {subsumes: true, in: `a: {s: { a:1} }, b: { s: { a:1, b:2 }}`},
- 68: {subsumes: true, in: `a: {}, b: {}`},
- // TODO: allow subsumption of unevaluated values?
- // ref not yet evaluated and not structurally equivalent
- 69: {subsumes: true, in: `a: {}, b: {} & c, c: {}`},
-
- 70: {subsumes: false, in: `a: {a:1}, b: {}`},
- 71: {subsumes: false, in: `a: {a:1, b:1}, b: {a:1}`},
- 72: {subsumes: false, in: `a: {s: { a:1} }, b: { s: {}}`},
-
- 84: {subsumes: true, in: `a: 1 | 2, b: 2 | 1`},
- 85: {subsumes: true, in: `a: 1 | 2, b: 1 | 2`},
-
- 86: {subsumes: true, in: `a: number, b: 2 | 1`},
- 87: {subsumes: true, in: `a: number, b: 2 | 1`},
- 88: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
-
- 89: {subsumes: true, in: `a: float | number, b: 1 | 2 | 3.1`},
-
- 90: {subsumes: false, in: `a: int, b: 1 | 2 | 3.1`},
- 91: {subsumes: true, in: `a: 1 | 2, b: 1`},
- 92: {subsumes: true, in: `a: 1 | 2, b: 2`},
- 93: {subsumes: false, in: `a: 1 | 2, b: 3`},
-
- // Structural
- 94: {subsumes: false, in: `a: int + int, b: int`},
- 95: {subsumes: true, in: `a: int + int, b: int + int`},
- 96: {subsumes: true, in: `a: int + number, b: int + int`},
- 97: {subsumes: true, in: `a: number + number, b: int + int`},
- // TODO: allow subsumption of unevaluated values?
- // TODO: may be false if we allow arithmetic on incomplete values.
- 98: {subsumes: false, in: `a: int + int, b: int * int`},
-
- // TODO: allow subsumption of unevaluated values?
- // true because both evaluate to bottom
- 99: {subsumes: true, in: `a: !int, b: !int`},
- 100: {subsumes: true, in: `a: !number, b: !int`},
- 101: {subsumes: true, in: `a: !int, b: !number`},
-
- // TODO: allow subsumption of unevaluated values?
- 102: {subsumes: false, in: `a: int + int, b: !number`},
- // TODO: allow subsumption of unevaluated values?
- 103: {subsumes: false, in: `a: !bool, b: bool`},
- 104: {subsumes: true, in: `a: !bool, b: !bool`},
-
- // Call
- 113: {subsumes: true, in: `
- a: fn()
- b: fn()
- fn: _`,
- },
- 114: {subsumes: false, in: `
- a: fn(),
- b: fn(1)
- fn: _`,
- },
- 115: {subsumes: true, in: `
- a: fn(2)
- b: fn(2)
- fn: _`,
- },
- 116: {subsumes: true, in: `
- a: fn(number)
- b: fn(2)
- fn: _`,
- },
- 117: {subsumes: false, in: `
- a: fn(2)
- b: fn(number)
- fn: _`,
- },
-
- // TODO: allow subsumption of unevaluated values?
- // TODO: okay, but why false?
- 121: {subsumes: false, in: `a: c + d, b: int, c: int, d: int`},
- // TODO: allow subsumption of unevaluated values?
- 122: {subsumes: true, in: `a: {}, b: c & {}, c: {}`},
-
- // references
- 123: {subsumes: true, in: `a: c, b: c, c: {}`},
- // TODO: allow subsumption of unevaluated values?
- 124: {subsumes: true, in: `a: c, b: d, c: {}, d: {}`},
- 125: {subsumes: false, in: `a: c, b: d, c: {a:1}, d: {}`},
- // TODO: allow subsumption of unevaluated values?
- 126: {subsumes: true, in: `a: c, b: d, c: {a:1}, d: c & {b:1}`},
- 127: {subsumes: false, in: `a: d, b: c, c: {a:1}, d: c & {b:1}`},
- 128: {subsumes: false, in: `a: c.c, b: c, c: { d: number}`},
-
- // type unification catches a reference error.
- 129: {subsumes: false, in: `a: c, b: d, c: 1, d: 2`},
-
- 130: {subsumes: true, in: ` a: [1][1], b: [1][1]`},
- 131: {subsumes: true, in: ` a: [1][number], b: [1][1]`},
- 132: {subsumes: true, in: ` a: [number][1], b: [1][1]`},
- 133: {subsumes: true, in: ` a: [number][number], b: [1][1]`},
- 134: {subsumes: false, in: ` a: [1][0], b: [1][number]`},
- 135: {subsumes: false, in: ` a: [1][0], b: [number][0]`},
- 136: {subsumes: true, in: ` a: [number][number], b: [1][number]`},
- 137: {subsumes: true, in: ` a: [number][number], b: [number][1]`},
- // purely structural:
- 138: {subsumes: false, in: ` a: [number][number], b: number`},
-
- // interpolations
- 139: {subsumes: true, in: ` a: "\(d)", b: "\(d)", d: _`},
- // TODO: allow subsumption of unevaluated values?
- 140: {subsumes: true, in: ` a: "\(d)", b: "\(e)", d: _, e: _`},
-
- 141: {subsumes: true, in: ` a: "\(string)", b: "\("foo")"`},
- // TODO: allow subsumption of unevaluated values?
- 142: {subsumes: true, in: ` a: "\(string)", b: "\(d)", d: "foo"`},
- 143: {subsumes: true, in: ` a: "\("foo")", b: "\("foo")"`},
- 144: {subsumes: false, in: ` a: "\("foo")", b: "\(1) \(2)"`},
-
- 145: {subsumes: false, in: ` a: "s \(d) e", b: "s a e", d: _`},
- 146: {subsumes: false, in: ` a: "s \(d)m\(d) e", b: "s a e", d: _`},
-
- 147: {subsumes: true, in: ` a: 7080, b: *7080 | int`, mode: subChoose},
-
- // Defaults
- 150: {subsumes: false, in: `a: number | *1, b: number | *2`},
- 151: {subsumes: true, in: `a: number | *2, b: number | *2`},
- 152: {subsumes: true, in: `a: int | *float, b: int | *2.0`},
- 153: {subsumes: false, in: `a: int | *2, b: int | *2.0`},
- 154: {subsumes: true, in: `a: number | *2 | *3, b: number | *2`},
- 155: {subsumes: true, in: `a: number, b: number | *2`},
-
- // Bounds
- 170: {subsumes: true, in: `a: >=2, b: >=2`},
- 171: {subsumes: true, in: `a: >=1, b: >=2`},
- 172: {subsumes: true, in: `a: >0, b: >=2`},
- 173: {subsumes: true, in: `a: >1, b: >1`},
- 174: {subsumes: true, in: `a: >=1, b: >1`},
- 175: {subsumes: false, in: `a: >1, b: >=1`},
- 176: {subsumes: true, in: `a: >=1, b: >=1`},
- 177: {subsumes: true, in: `a: <1, b: <1`},
- 178: {subsumes: true, in: `a: <=1, b: <1`},
- 179: {subsumes: false, in: `a: <1, b: <=1`},
- 180: {subsumes: true, in: `a: <=1, b: <=1`},
-
- 181: {subsumes: true, in: `a: !=1, b: !=1`},
- 182: {subsumes: false, in: `a: !=1, b: !=2`},
-
- 183: {subsumes: false, in: `a: !=1, b: <=1`},
- 184: {subsumes: true, in: `a: !=1, b: <1`},
- 185: {subsumes: false, in: `a: !=1, b: >=1`},
- 186: {subsumes: true, in: `a: !=1, b: <1`},
-
- 187: {subsumes: true, in: `a: !=1, b: <=0`},
- 188: {subsumes: true, in: `a: !=1, b: >=2`},
- 189: {subsumes: true, in: `a: !=1, b: >1`},
-
- 195: {subsumes: false, in: `a: >=2, b: !=2`},
- 196: {subsumes: false, in: `a: >2, b: !=2`},
- 197: {subsumes: false, in: `a: <2, b: !=2`},
- 198: {subsumes: false, in: `a: <=2, b: !=2`},
-
- 200: {subsumes: true, in: `a: =~"foo", b: =~"foo"`},
- 201: {subsumes: false, in: `a: =~"foo", b: =~"bar"`},
- 202: {subsumes: false, in: `a: =~"foo1", b: =~"foo"`},
-
- 203: {subsumes: true, in: `a: !~"foo", b: !~"foo"`},
- 204: {subsumes: false, in: `a: !~"foo", b: !~"bar"`},
- 205: {subsumes: false, in: `a: !~"foo", b: !~"foo1"`},
-
- // The following is could be true, but we will not go down the rabbit
- // hold of trying to prove subsumption of regular expressions.
- 210: {subsumes: false, in: `a: =~"foo", b: =~"foo1"`},
- 211: {subsumes: false, in: `a: !~"foo1", b: !~"foo"`},
-
- 220: {subsumes: true, in: `a: <5, b: 4`},
- 221: {subsumes: false, in: `a: <5, b: 5`},
- 222: {subsumes: true, in: `a: <=5, b: 5`},
- 223: {subsumes: false, in: `a: <=5.0, b: 5.00000001`},
- 224: {subsumes: true, in: `a: >5, b: 6`},
- 225: {subsumes: false, in: `a: >5, b: 5`},
- 226: {subsumes: true, in: `a: >=5, b: 5`},
- 227: {subsumes: false, in: `a: >=5, b: 4`},
- 228: {subsumes: true, in: `a: !=5, b: 6`},
- 229: {subsumes: false, in: `a: !=5, b: 5`},
- 230: {subsumes: false, in: `a: !=5.0, b: 5.0`},
- 231: {subsumes: false, in: `a: !=5.0, b: 5`},
-
- 250: {subsumes: true, in: `a: =~ #"^\d{3}$"#, b: "123"`},
- 251: {subsumes: false, in: `a: =~ #"^\d{3}$"#, b: "1234"`},
- 252: {subsumes: true, in: `a: !~ #"^\d{3}$"#, b: "1234"`},
- 253: {subsumes: false, in: `a: !~ #"^\d{3}$"#, b: "123"`},
-
- // Conjunctions
- 300: {subsumes: true, in: `a: >0, b: >=2 & <=100`},
- 301: {subsumes: false, in: `a: >0, b: >=0 & <=100`},
-
- 310: {subsumes: true, in: `a: >=0 & <=100, b: 10`},
- 311: {subsumes: true, in: `a: >=0 & <=100, b: >=0 & <=100`},
- 312: {subsumes: false, in: `a: !=2 & !=4, b: >3`},
- 313: {subsumes: true, in: `a: !=2 & !=4, b: >5`},
-
- 314: {subsumes: false, in: `a: >=0 & <=100, b: >=0 & <=150`},
- 315: {subsumes: true, in: `a: >=0 & <=150, b: >=0 & <=100`},
-
- // Disjunctions
- 330: {subsumes: true, in: `a: >5, b: >10 | 8`},
- 331: {subsumes: false, in: `a: >8, b: >10 | 8`},
-
- // Optional fields
- // Optional fields defined constraints on fields that are not yet
- // defined. So even if such a field is not part of the output, it
- // influences the lattice structure.
- // For a given A and B, where A and B unify and where A has an optional
- // field that is not defined in B, the addition of an incompatible
- // value of that field in B can cause A and B to no longer unify.
- //
- 400: {subsumes: false, in: `a: {foo: 1}, b: {}`},
- 401: {subsumes: false, in: `a: {foo?: 1}, b: {}`},
- 402: {subsumes: true, in: `a: {}, b: {foo: 1}`},
- 403: {subsumes: true, in: `a: {}, b: {foo?: 1}`},
-
- 404: {subsumes: true, in: `a: {foo: 1}, b: {foo: 1}`},
- 405: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`},
- 406: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`},
- 407: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`},
-
- 408: {subsumes: false, in: `a: {foo: 1}, b: {foo: 2}`},
- 409: {subsumes: false, in: `a: {foo?: 1}, b: {foo: 2}`},
- 410: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: 2}`},
- 411: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 2}`},
-
- 412: {subsumes: true, in: `a: {foo: number}, b: {foo: 2}`},
- 413: {subsumes: true, in: `a: {foo?: number}, b: {foo: 2}`},
- 414: {subsumes: true, in: `a: {foo?: number}, b: {foo?: 2}`},
- 415: {subsumes: false, in: `a: {foo: number}, b: {foo?: 2}`},
-
- 416: {subsumes: false, in: `a: {foo: 1}, b: {foo: number}`},
- 417: {subsumes: false, in: `a: {foo?: 1}, b: {foo: number}`},
- 418: {subsumes: false, in: `a: {foo?: 1}, b: {foo?: number}`},
- 419: {subsumes: false, in: `a: {foo: 1}, b: {foo?: number}`},
-
- // The one exception of the rule: there is no value of foo that can be
- // added to b which would cause the unification of a and b to fail.
- // So an optional field with a value of top is equivalent to not
- // defining one at all.
- 420: {subsumes: true, in: `a: {foo?: _}, b: {}`},
-
- 430: {subsumes: false, in: `a: {[_]: 4}, b: {[_]: int}`},
- // TODO: handle optionals.
- 431: {subsumes: false, in: `a: {[_]: int}, b: {[_]: 2}`},
-
- 440: {subsumes: true, in: `a: {foo?: 1}, b: {}`, mode: subNoOptional},
- 441: {subsumes: true, in: `a: {}, b: {foo?: 1}`, mode: subNoOptional},
- 442: {subsumes: true, in: `a: {foo?: 1}, b: {foo: 1}`, mode: subNoOptional},
- 443: {subsumes: true, in: `a: {foo?: 1}, b: {foo?: 1}`, mode: subNoOptional},
- 444: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subNoOptional},
- 445: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subNoOptional},
- 446: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subNoOptional},
- 447: {subsumes: true, in: `a: {}, b: close({})`, mode: subNoOptional},
- 448: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subNoOptional},
-
- // Lists
- 506: {subsumes: true, in: `a: [], b: [] `},
- 507: {subsumes: true, in: `a: [1], b: [1] `},
- 508: {subsumes: false, in: `a: [1], b: [2] `},
- 509: {subsumes: false, in: `a: [1], b: [2, 3] `},
- 510: {subsumes: true, in: `a: [{b: string}], b: [{b: "foo"}] `},
- 511: {subsumes: true, in: `a: [...{b: string}], b: [{b: "foo"}] `},
- 512: {subsumes: false, in: `a: [{b: "foo"}], b: [{b: string}] `},
- 513: {subsumes: false, in: `a: [{b: string}], b: [{b: "foo"}, ...{b: "foo"}] `},
- 520: {subsumes: false, in: `a: [_, int, ...], b: [int, string, ...string] `},
-
- // Closed structs.
- 600: {subsumes: false, in: `a: close({}), b: {a: 1}`},
- 601: {subsumes: false, in: `a: close({a: 1}), b: {a: 1}`},
- 602: {subsumes: false, in: `a: close({a: 1, b: 1}), b: {a: 1}`},
- 603: {subsumes: false, in: `a: {a: 1}, b: close({})`},
- 604: {subsumes: true, in: `a: {a: 1}, b: close({a: 1})`},
- 605: {subsumes: true, in: `a: {a: 1}, b: close({a: 1, b: 1})`},
- 606: {subsumes: true, in: `a: close({b?: 1}), b: close({b: 1})`},
- 607: {subsumes: false, in: `a: close({b: 1}), b: close({b?: 1})`},
- 608: {subsumes: true, in: `a: {}, b: close({})`},
- 609: {subsumes: true, in: `a: {}, b: close({foo?: 1})`},
- 610: {subsumes: true, in: `a: {foo?:1}, b: close({})`},
-
- // Definitions are not regular fields.
- 630: {subsumes: false, in: `a: {#a: 1}, b: {a: 1}`},
- 631: {subsumes: false, in: `a: {a: 1}, b: {#a: 1}`},
-
- // Subsuming final values.
- 700: {subsumes: true, in: `a: {[string]: 1}, b: {foo: 1}`, mode: subFinal},
- 701: {subsumes: true, in: `a: {[string]: int}, b: {foo: 1}`, mode: subFinal},
- 702: {subsumes: true, in: `a: {["foo"]: int}, b: {foo: 1}`, mode: subFinal},
- 703: {subsumes: false, in: `a: close({["foo"]: 1}), b: {bar: 1}`, mode: subFinal},
- 704: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subFinal},
- 705: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subFinal},
- 706: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subFinal},
- 707: {subsumes: true, in: `a: {}, b: close({})`, mode: subFinal},
- 708: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subFinal},
- 709: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subFinal},
- 710: {subsumes: false, in: `a: {foo: [...string]}, b: {}`, mode: subFinal},
-
- // Schema values
- 800: {subsumes: true, in: `a: close({}), b: {foo: 1}`, mode: subSchema},
- // TODO(eval): FIX
- // 801: {subsumes: true, in: `a: {[string]: int}, b: {foo: 1}`, mode: subSchema},
- 804: {subsumes: false, in: `a: {foo: 1}, b: {foo?: 1}`, mode: subSchema},
- 805: {subsumes: true, in: `a: close({}), b: {foo?: 1}`, mode: subSchema},
- 806: {subsumes: true, in: `a: close({}), b: close({foo?: 1})`, mode: subSchema},
- 807: {subsumes: true, in: `a: {}, b: close({})`, mode: subSchema},
- 808: {subsumes: false, in: `a: {[string]: 1}, b: {foo: 2}`, mode: subSchema},
- 809: {subsumes: true, in: `a: {}, b: close({foo?: 1})`, mode: subSchema},
- }
-
- re := regexp.MustCompile(`a: (.*).*b: ([^\n]*)`)
- for i, tc := range testCases {
- if tc.in == "" {
- continue
- }
- m := re.FindStringSubmatch(strings.Join(strings.Split(tc.in, "\n"), ""))
- const cutset = "\n ,"
- key := strings.Trim(m[1], cutset) + " ⊑ " + strings.Trim(m[2], cutset)
-
- t.Run(strconv.Itoa(i)+"/"+key, func(t *testing.T) {
- ctx, root := compileFile(t, tc.in)
-
- // Use low-level lookup to avoid evaluation.
- var a, b value
- for _, arc := range root.Arcs {
- switch arc.Label {
- case ctx.StrLabel("a"):
- a = arc.v
- case ctx.StrLabel("b"):
- b = arc.v
- }
- }
- s := subsumer{ctx: ctx, mode: tc.mode}
- got := s.subsumes(a, b)
- if got != tc.subsumes {
- t.Errorf("got %v; want %v (%v vs %v)", got, tc.subsumes, a.Kind(), b.Kind())
- }
- })
- }
-}
-
-func TestTouchBottom(t *testing.T) {
- // Just call this function to mark coverage. It is otherwise never called.
- var x bottom
- x.subsumesImpl(nil, &bottom{})
-}
diff --git a/cue/types.go b/cue/types.go
index a5deb07..3e5d0c2 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -27,10 +27,17 @@
"github.com/cockroachdb/apd/v2"
"cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/errors"
+ "cuelang.org/go/cue/format"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
"cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/convert"
+ "cuelang.org/go/internal/core/eval"
+ "cuelang.org/go/internal/core/export"
+ "cuelang.org/go/internal/core/subsume"
+ "cuelang.org/go/internal/core/validate"
)
// Kind determines the underlying type of a Value.
@@ -71,17 +78,17 @@
// NumberKind represents any kind of number.
NumberKind = IntKind | FloatKind
- TopKind = Kind(adt.TopKind)
+ TopKind = adt.TopKind
)
// An structValue represents a JSON object.
//
// TODO: remove
type structValue struct {
- ctx *context
- path *valueData
- obj *structLit
- Arcs arcs
+ ctx *context
+ v Value
+ obj *adt.Vertex
+ features []adt.Feature
}
// Len reports the number of fields in this struct.
@@ -89,14 +96,28 @@
if o.obj == nil {
return 0
}
- return len(o.Arcs)
+ return len(o.features)
}
// At reports the key and value of the ith field, i < o.Len().
func (o *structValue) At(i int) (key string, v Value) {
- a := o.Arcs[i]
- v = newChildValue(o, i)
- return o.ctx.LabelStr(a.Label), v
+ f := o.features[i]
+ return o.ctx.LabelStr(f), newChildValue(o, i)
+}
+
+func (o *structValue) at(i int) (v *adt.Vertex, isOpt bool) {
+ f := o.features[i]
+ arc := o.obj.Lookup(f)
+ if arc == nil {
+ arc = &adt.Vertex{
+ Parent: o.v.v,
+ Label: f,
+ }
+ o.obj.MatchAndInsert(o.ctx.opCtx, arc)
+ arc.Finalize(o.ctx.opCtx)
+ isOpt = true
+ }
+ return arc, isOpt
}
// Lookup reports the field for the given key. The returned Value is invalid
@@ -106,7 +127,7 @@
i := 0
len := o.Len()
for ; i < len; i++ {
- if o.Arcs[i].Label == f {
+ if o.features[i] == f {
break
}
}
@@ -114,8 +135,7 @@
// TODO: better message.
ctx := o.ctx
x := ctx.mkErr(o.obj, codeNotExist, "value %q not found", key)
- v := x.evalPartial(ctx)
- return Value{ctx.index, &valueData{o.path.parent, 0, arc{Label: o.path.Label, Value: v, v: x}}}
+ return newErrValue(o.v, x)
}
return newChildValue(o, i)
}
@@ -190,25 +210,32 @@
// An Iterator iterates over values.
//
type Iterator struct {
- val Value
- ctx *context
- iter iterAtter
- len int
- p int
- cur Value
- f label
+ val Value
+ ctx *context
+ arcs []field
+ p int
+ cur Value
+ f label
+ isOpt bool
+}
+
+type field struct {
+ arc *adt.Vertex
+ isOptional bool
}
// Next advances the iterator to the next value and reports whether there was
// any. It must be called before the first call to Value or Key.
func (i *Iterator) Next() bool {
- if i.p >= i.len {
+ if i.p >= len(i.arcs) {
i.cur = Value{}
return false
}
- arc := i.iter.iterAt(i.ctx, i.p)
- i.cur = i.val.makeChild(i.ctx, uint32(i.p), arc)
- i.f = arc.Label
+ f := i.arcs[i.p]
+ f.arc.Finalize(i.ctx.opCtx)
+ i.cur = makeValue(i.val.idx, f.arc)
+ i.f = f.arc.Label
+ i.isOpt = f.isOptional
i.p++
return true
}
@@ -219,6 +246,10 @@
return i.cur
}
+func (i *Iterator) Feature() adt.Feature {
+ return i.f
+}
+
// Label reports the label of the value if i iterates over struct fields and
// "" otherwise.
func (i *Iterator) Label() string {
@@ -235,12 +266,12 @@
// IsOptional reports if a field is optional.
func (i *Iterator) IsOptional() bool {
- return i.cur.v.arc.optional
+ return i.isOpt
}
// IsDefinition reports if a field is a definition.
func (i *Iterator) IsDefinition() bool {
- return i.cur.v.arc.definition
+ return i.f.IsDef()
}
// marshalJSON iterates over the list and generates JSON output. HasNext
@@ -266,10 +297,11 @@
func (v Value) getNum(k kind) (*numLit, errors.Error) {
v, _ = v.Default()
- if err := v.checkKind(v.ctx(), k); err != nil {
+ ctx := v.ctx()
+ if err := v.checkKind(ctx, k); err != nil {
return nil, v.toErr(err)
}
- n, _ := v.v.Value.(*numLit)
+ n, _ := v.eval(ctx).(*numLit)
return n, nil
}
@@ -491,183 +523,116 @@
return f, nil
}
-type valueData struct {
- parent *valueData
- index uint32
- arc
-}
+func (v Value) appendPath(a []string) []string {
+ for _, f := range v.v.Path() {
+ switch f.Typ() {
+ case adt.IntLabel:
+ a = append(a, strconv.FormatInt(int64(f.Index()), 10))
-// path returns the path of the value.
-func (v *valueData) appendPath(a []string, idx *index) ([]string, kind) {
- var k kind
- if v.parent != nil {
- a, k = v.parent.appendPath(a, idx)
- }
- switch k {
- case listKind:
- a = append(a, strconv.FormatInt(int64(v.index), 10))
- case structKind:
- label := idx.LabelStr(v.arc.Label)
- if f := v.arc.Label; !f.IsDef() && !f.IsHidden() {
- if !ast.IsValidIdent(label) {
- label = strconv.Quote(label)
+ case adt.StringLabel:
+ label := v.idx.LabelStr(f)
+ if !f.IsDef() && !f.IsHidden() {
+ if !ast.IsValidIdent(label) {
+ label = strconv.Quote(label)
+ }
}
+ a = append(a, label)
+ default:
+ a = append(a, f.SelectorString(v.idx.Index))
}
- a = append(a, label)
}
- return a, v.arc.Value.Kind()
+ return a
}
// Value holds any value, which may be a Boolean, Error, List, Null, Number,
// Struct, or String.
type Value struct {
idx *index
- v *valueData
+ v *adt.Vertex
}
func newErrValue(v Value, b *bottom) Value {
- ctx := v.ctx()
- p := v.v
- if p == nil {
- return newValueRoot(ctx, b)
+ node := &adt.Vertex{Value: b}
+ if v.v != nil {
+ node.Label = v.v.Label
+ node.Parent = v.v.Parent
}
- return Value{
- ctx.index,
- &valueData{p.parent, p.index, arc{
- Label: p.arc.Label,
- Value: b,
- v: b,
- }},
+ node.UpdateStatus(adt.Finalized)
+ node.AddConjunct(adt.MakeConjunct(nil, b))
+ return makeValue(v.idx, node)
+}
+
+func newVertexRoot(ctx *context, x *adt.Vertex) Value {
+ if ctx.opCtx != nil {
+ // This is indicative of an zero Value. In some cases this is called
+ // with an error value.
+ x.Finalize(ctx.opCtx)
+ } else {
+ x.UpdateStatus(adt.Finalized)
}
+ return makeValue(ctx.index, x)
}
func newValueRoot(ctx *context, x value) Value {
- v := x.evalPartial(ctx)
- return Value{ctx.index, &valueData{nil, 0, arc{Value: v, v: x}}}
+ if n, ok := x.(*adt.Vertex); ok {
+ return newVertexRoot(ctx, n)
+ }
+ node := &adt.Vertex{}
+ node.AddConjunct(adt.MakeConjunct(nil, x))
+ return newVertexRoot(ctx, node)
}
-func newChildValue(obj *structValue, i int) Value {
- a := obj.Arcs[i]
- for j, b := range obj.obj.Arcs {
- if b.Label == a.Label {
- a = obj.obj.iterAt(obj.ctx, j)
- // TODO: adding more technical debt here. The evaluator should be
- // rewritten.
- x := obj.obj
- ctx := obj.ctx
- if x.optionals != nil {
- name := ctx.LabelStr(x.Arcs[i].Label)
- arg := &stringLit{x.baseValue, name, nil}
-
- val, _ := x.optionals.constraint(ctx, arg)
- if val != nil {
- a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
- }
- }
- break
- }
- }
-
- return Value{obj.ctx.index, &valueData{obj.path, uint32(i), a}}
+func newChildValue(o *structValue, i int) Value {
+ arc, _ := o.at(i)
+ return makeValue(o.v.idx, arc)
}
// Dereference reports the value v refers to if v is a reference or v itself
// otherwise.
func Dereference(v Value) Value {
- if v.v == nil {
+ n := v.v
+ if n == nil || len(n.Conjuncts) != 1 {
+ return v
+ }
+
+ c := n.Conjuncts[0]
+ r, _ := c.Expr().(adt.Resolver)
+ if r == nil {
return v
}
ctx := v.ctx()
- a, n := appendPath(ctx, make([]label, 0, 3), v.v.v)
-
- if n == nil {
- return v
-
+ n, b := ctx.opCtx.Resolve(c.Env, r)
+ if b != nil {
+ return newErrValue(v, b)
}
-
- p := locateNode(v.v, n)
-
- if p == nil {
-
- imp := ctx.getImportFromNode(n.node)
- if imp == nil {
- // TODO(eval): embedded structs are currently represented at the
- // same level as the enclosing struct. This means that the parent
- // of an embedded struct skips the struct in which it is embedded.
- // Treat embedded structs as "anonymous" fields.
- // See TestPathCorrection.
- return v
- }
- p = &valueData{arc: arc{v: imp.rootValue, Value: imp.rootStruct}}
- }
-
- cached := p.Value
- if cached == nil {
- cached = p.v.evalPartial(ctx)
- }
- s := cached.(*structLit)
- for _, f := range a {
- a := s.Lookup(ctx, f)
- if a.v == nil {
- return Value{}
- }
- p = &valueData{parent: p, arc: a} // index
- s, _ = a.Value.(*structLit)
- }
-
- v = Value{v.idx, p}
- return v
+ return makeValue(v.idx, n)
}
-func appendPath(ctx *context, a []label, v value) (path []label, n *nodeRef) {
- switch x := v.(type) {
- case *selectorExpr:
- a, n = appendPath(ctx, a, x.X)
- if n == nil {
- return nil, nil
- }
-
- a = append(a, x.Sel)
-
- case *indexExpr:
- e := x.Index.evalPartial(ctx)
- s, ok := e.(*stringLit)
- if !ok {
- return nil, nil
- }
-
- a, n = appendPath(ctx, a, x.X)
- if n == nil {
- return nil, nil
- }
-
- a = append(a, ctx.Label(s.Str, false))
-
- case *nodeRef:
- n = x
+func makeValue(idx *index, v *adt.Vertex) Value {
+ if v.Status() == 0 || v.Value == nil {
+ panic(fmt.Sprintf("not properly initialized (state: %v, value: %T)",
+ v.Status(), v.Value))
}
- return a, n
+ return Value{idx, v}
}
-func remakeValue(base Value, v value) Value {
- p := base.v
- if n, ok := v.(*nodeRef); ok {
- if q := locateNode(p, n); q != nil {
- p = q
- }
+func remakeValue(base Value, env *adt.Environment, v value) Value {
+ // TODO: right now this is necessary because disjunctions do not have
+ // populated conjuncts.
+ if v, ok := v.(*adt.Vertex); ok && v.Status() >= adt.Partial {
+ return Value{base.idx, v}
}
- path := *p
- path.v = v
- path.Value = v.evalPartial(base.ctx())
- return Value{base.idx, &path}
+ n := &adt.Vertex{Parent: base.v.Parent, Label: base.v.Label}
+ n.AddConjunct(adt.MakeConjunct(env, v))
+ n = base.ctx().manifest(n)
+ return makeValue(base.idx, n)
}
-func locateNode(p *valueData, n *nodeRef) *valueData {
- // the parent must exist.
- for ; p != nil && p.Value != n.node.(value); p = p.parent {
- }
- return p
+func remakeFinal(base Value, env *adt.Environment, v adt.Value) Value {
+ n := &adt.Vertex{Parent: base.v.Parent, Label: base.v.Label, Value: v}
+ n.UpdateStatus(adt.Finalized)
+ return makeValue(base.idx, n)
}
func (v Value) ctx() *context {
@@ -675,41 +640,8 @@
}
func (v Value) makeChild(ctx *context, i uint32, a arc) Value {
- return Value{v.idx, &valueData{v.v, i, a}}
-}
-
-func (v Value) makeElem(x value) Value {
- v, e := v.evalFull(x)
- return Value{v.idx, &valueData{v.v, 0, arc{
- optional: true,
- v: x,
- Value: e,
- }}}
-}
-
-func (v Value) eval(ctx *context) evaluated {
- if v.v == nil || v.v.Value == nil {
- panic("undefined value")
- }
- return ctx.manifest(v.v.Value)
-}
-
-func (v Value) evalFull(u value) (Value, evaluated) {
- ctx := v.ctx()
- x := u.evalPartial(ctx)
- if st, ok := x.(*structLit); ok {
- var err *bottom
- x, err = st.expandFields(ctx)
- if err != nil {
- x = err
- }
- if x != st {
- p := *v.v
- p.Value = x
- v.v = &p
- }
- }
- return v, x
+ a.Parent = v.v
+ return makeValue(v.idx, a)
}
// Eval resolves the references of a value and returns the result.
@@ -718,7 +650,12 @@
if v.v == nil {
return v
}
- return remakeValue(v.evalFull(v.v.v))
+ x := v.v
+ // x = eval.FinalizeValue(v.idx.Runtime, v.v)
+ // x.Finalize(v.ctx().opCtx)
+ x = x.ToDataSingle()
+ return makeValue(v.idx, x)
+ // return remakeValue(v, nil, ctx.value(x))
}
// Default reports the default value and whether it existed. It returns the
@@ -727,12 +664,139 @@
if v.v == nil {
return v, false
}
- v, u := v.evalFull(v.v.v)
- x := v.ctx().manifest(u)
- if x != u {
- return remakeValue(v, x), true
+
+ d := v.v.Default()
+ if d == v.v {
+ return v, false
}
- return v, false
+ return makeValue(v.idx, d), true
+
+ // d, ok := v.v.Value.(*adt.Disjunction)
+ // if !ok {
+ // return v, false
+ // }
+
+ // var w *adt.Vertex
+
+ // switch d.NumDefaults {
+ // case 0:
+ // return v, false
+
+ // case 1:
+ // w = d.Values[0]
+
+ // default:
+ // x := *v.v
+ // x.Value = &adt.Disjunction{
+ // Src: d.Src,
+ // Values: d.Values[:d.NumDefaults],
+ // NumDefaults: 0,
+ // }
+ // w = &x
+ // }
+
+ // w.Conjuncts = nil
+ // for _, c := range v.v.Conjuncts {
+ // // TODO: preserve field information.
+ // expr, _ := stripNonDefaults(c.Expr())
+ // w.AddConjunct(adt.MakeConjunct(c.Env, expr))
+ // }
+
+ // return makeValue(v.idx, w), true
+
+ // if !stripped {
+ // return v, false
+ // }
+
+ // n := *v.v
+ // n.Conjuncts = conjuncts
+ // return Value{v.idx, &n}, true
+
+ // isDefault := false
+ // for _, c := range v.v.Conjuncts {
+ // if hasDisjunction(c.Expr()) {
+ // isDefault = true
+ // break
+ // }
+ // }
+
+ // if !isDefault {
+ // return v, false
+ // }
+
+ // TODO: record expanded disjunctions in output.
+ // - Rename Disjunction to DisjunctionExpr
+ // - Introduce Disjuncts with Values.
+ // - In Expr introduce Star
+ // - Don't pick default by default?
+
+ // Evaluate the value.
+ // x := eval.FinalizeValue(v.idx.Runtime, v.v)
+ // if b, _ := x.Value.(*adt.Bottom); b != nil { // && b.IsIncomplete() {
+ // return v, false
+ // }
+ // // Finalize and return here.
+ // return Value{v.idx, x}, isDefault
+}
+
+// TODO: this should go: record preexpanded disjunctions in Vertex.
+func hasDisjunction(expr adt.Expr) bool {
+ switch x := expr.(type) {
+ case *adt.DisjunctionExpr:
+ return true
+ case *adt.Conjunction:
+ for _, v := range x.Values {
+ if hasDisjunction(v) {
+ return true
+ }
+ }
+ case *adt.BinaryExpr:
+ switch x.Op {
+ case adt.OrOp:
+ return true
+ case adt.AndOp:
+ return hasDisjunction(x.X) || hasDisjunction(x.Y)
+ }
+ }
+ return false
+}
+
+// TODO: this should go: record preexpanded disjunctions in Vertex.
+func stripNonDefaults(expr adt.Expr) (r adt.Expr, stripped bool) {
+ switch x := expr.(type) {
+ case *adt.DisjunctionExpr:
+ if !x.HasDefaults {
+ return x, false
+ }
+ d := *x
+ d.Values = []adt.Disjunct{}
+ for _, v := range x.Values {
+ if v.Default {
+ d.Values = append(d.Values, v)
+ }
+ }
+ if len(d.Values) == 1 {
+ return d.Values[0].Val, true
+ }
+ return &d, true
+
+ case *adt.BinaryExpr:
+ if x.Op != adt.AndOp {
+ return x, false
+ }
+ a, sa := stripNonDefaults(x.X)
+ b, sb := stripNonDefaults(x.Y)
+ if sa || sb {
+ bin := *x
+ bin.X = a
+ bin.Y = b
+ return &bin, true
+ }
+ return x, false
+
+ default:
+ return x, false
+ }
}
// Label reports he label used to obtain this value from the enclosing struct.
@@ -740,7 +804,7 @@
// TODO: get rid of this somehow. Probably by including a FieldInfo struct
// or the like.
func (v Value) Label() (string, bool) {
- if v.v.Label == 0 {
+ if v.v == nil || v.v.Label == 0 {
return "", false
}
return v.idx.LabelStr(v.v.Label), true
@@ -754,33 +818,13 @@
return BottomKind
}
c := v.v.Value
- if c == nil {
- c = v.v.v.evalPartial(v.ctx())
+ if !adt.IsConcrete(c) {
+ return BottomKind
}
- k := c.Kind()
- if k.isGround() {
- switch {
- case k.isAnyOf(nullKind):
- return NullKind
- case k.isAnyOf(boolKind):
- return BoolKind
- case k&numKind == (intKind):
- return IntKind
- case k&numKind == (floatKind):
- return FloatKind
- case k.isAnyOf(numKind):
- return NumberKind
- case k.isAnyOf(bytesKind):
- return BytesKind
- case k.isAnyOf(stringKind):
- return StringKind
- case k.isAnyOf(structKind):
- return StructKind
- case k.isAnyOf(listKind):
- return ListKind
- }
+ if v.IncompleteKind() == adt.ListKind && !v.IsClosed() {
+ return BottomKind
}
- return BottomKind
+ return c.Kind()
}
// IncompleteKind returns a mask of all kinds that this value may be.
@@ -788,40 +832,7 @@
if v.v == nil {
return BottomKind
}
- var k kind
- x := v.v.v.evalPartial(v.ctx())
- switch x := convertBuiltin(x).(type) {
- case *builtin:
- k = x.representedKind()
- case *customValidator:
- k = x.Builtin.Params[0]
- default:
- k = x.Kind()
- }
- vk := BottomKind // Everything is a bottom kind.
- for i := kind(1); i < nonGround; i <<= 1 {
- if k&i != 0 {
- switch i {
- case nullKind:
- vk |= NullKind
- case boolKind:
- vk |= BoolKind
- case intKind:
- vk |= IntKind
- case floatKind:
- vk |= FloatKind
- case stringKind:
- vk |= StringKind
- case bytesKind:
- vk |= BytesKind
- case structKind:
- vk |= StructKind
- case listKind:
- vk |= ListKind
- }
- }
- }
- return vk
+ return v.v.Kind()
}
// MarshalJSON marshalls this value into valid JSON.
@@ -840,6 +851,14 @@
}
ctx := v.idx.newContext()
x := v.eval(ctx)
+
+ if _, ok := x.(adt.Resolver); ok {
+ return nil, marshalErrf(v, x, codeIncomplete, "value %q contains unresolved references", ctx.str(x))
+ }
+ if !adt.IsConcrete(x) {
+ return nil, marshalErrf(v, x, codeIncomplete, "cannot convert incomplete value %q to JSON", ctx.str(x))
+ }
+
// TODO: implement marshalles in value.
switch k := x.Kind(); k {
case nullKind:
@@ -847,7 +866,9 @@
case boolKind:
return json.Marshal(x.(*boolLit).B)
case intKind, floatKind, numKind:
- return x.(*numLit).X.MarshalText()
+ b, err := x.(*numLit).X.MarshalText()
+ b = bytes.TrimLeft(b, "+")
+ return b, err
case stringKind:
return json.Marshal(x.(*stringLit).Str)
case bytesKind:
@@ -857,15 +878,6 @@
return marshalList(&i)
case structKind:
obj, err := v.structValData(ctx)
- st := obj.obj
- if len(st.comprehensions) > 0 {
- // This should always evaluate to incomplete. However, fall back
- // to a bad error message, rather than crashing, in case it doesn't.
- if err, ok := st.comprehensions[0].comp.evalPartial(ctx).(*bottom); ok {
- return nil, toMarshalErr(v, err)
- }
- }
-
if err != nil {
return nil, toMarshalErr(v, err)
}
@@ -873,12 +885,6 @@
case bottomKind:
return nil, toMarshalErr(v, x.(*bottom))
default:
- if k.hasReferences() {
- return nil, marshalErrf(v, x, codeIncomplete, "value %q contains unresolved references", ctx.str(x))
- }
- if !k.isGround() {
- return nil, marshalErrf(v, x, codeIncomplete, "cannot convert incomplete value %q to JSON", ctx.str(x))
- }
return nil, marshalErrf(v, x, 0, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
}
}
@@ -889,21 +895,57 @@
// TODO: the default should ideally be simplified representation that
// exactly represents the value. The latter can currently only be
// ensured with Raw().
- if v.v == nil || v.v.Value == nil {
+ if v.v == nil {
return nil
}
- ctx := v.ctx()
- o := getOptions(opts)
- var inst *Instance
- if !o.final && !o.concrete {
- inst = v.instance()
+ var o options = getOptions(opts)
+ // var inst *Instance
+
+ p := export.Profile{
+ Simplify: !o.raw,
+ ShowOptional: !o.omitOptional && !o.concrete,
+ ShowDefinitions: !o.omitDefinitions && !o.concrete,
+ ShowHidden: !o.omitHidden && !o.concrete,
+ ShowAttributes: !o.omitAttrs,
+ ShowDocs: o.docs,
}
- if o.raw {
- n, _ := export(ctx, inst, v.v.v, o)
- return n
+
+ // var expr ast.Expr
+ var err error
+ var f *ast.File
+ if o.concrete || o.final {
+ // inst = v.instance()
+ var expr ast.Expr
+ expr, err = p.Value(v.idx.Runtime, v.v)
+ if err != nil {
+ return nil
+ }
+
+ // This introduces gratuitous unshadowing!
+ f, err = astutil.ToFile(expr)
+ if err != nil {
+ return nil
+ }
+ // return expr
+ } else {
+ f, err = p.Def(v.idx.Runtime, v.v)
+ if err != nil {
+ panic(err)
+ }
}
- n, _ := export(ctx, inst, v.v.Value, o)
- return n
+
+ if d := internal.Imports(f); len(d) == 0 {
+ if len(f.Decls) == 1 {
+ if e, ok := f.Decls[0].(*ast.EmbedDecl); ok {
+ return e.Expr
+ }
+ }
+ return &ast.StructLit{
+ Elts: f.Decls,
+ }
+ }
+
+ return f
}
// Decode initializes x with Value v. If x is a struct, it will validate the
@@ -928,7 +970,7 @@
if v.v == nil {
return nil
}
- return v.v.docs.appendDocs(nil)
+ return export.ExtractDoc(v.v)
}
// Split returns a list of values from which v originated such that
@@ -942,27 +984,9 @@
if v.v == nil {
return nil
}
- ctx := v.ctx()
a := []Value{}
- for _, x := range separate(v.v.v) {
- path := *v.v
- path.Value = x.evalPartial(ctx)
- path.v = x
- a = append(a, Value{v.idx, &path})
- }
- return a
-}
-
-func separate(v value) (a []value) {
- c := v.computed()
- if c == nil || (c.op != opUnify && c.op != opUnifyUnchecked) {
- return []value{v}
- }
- if c.x != nil {
- a = append(a, separate(c.x)...)
- }
- if c.y != nil {
- a = append(a, separate(c.y)...)
+ for _, x := range v.v.Conjuncts {
+ a = append(a, remakeValue(v, x.Env, x.Expr()))
}
return a
}
@@ -975,7 +999,10 @@
if v.v == nil {
return nil
}
- return v.v.v.Source()
+ if len(v.v.Conjuncts) == 1 {
+ return v.v.Conjuncts[0].Source()
+ }
+ return v.v.Value.Source()
}
// Err returns the error represented by v or nil v is not an error.
@@ -1000,19 +1027,10 @@
// IsClosed reports whether a list of struct is closed. It reports false when
// when the value is not a list or struct.
func (v Value) IsClosed() bool {
- switch v.Kind() {
- case StructKind:
- if st, ok := v.v.val().(*structLit); ok {
- return st.closeStatus.shouldClose()
- }
- case ListKind:
- if l, ok := v.v.val().(*list); ok {
- if n, ok := l.len.(*numLit); ok {
- return n.intValue(v.ctx()) == len(l.elem.Arcs)
- }
- }
+ if v.v == nil {
+ return false
}
- return false
+ return v.v.IsClosed(v.ctx().opCtx)
}
// IsConcrete reports whether the current value is a concrete scalar value
@@ -1023,35 +1041,32 @@
if v.v == nil {
return false // any is neither concrete, not a list or struct.
}
- x := v.v.v.evalPartial(v.ctx())
-
- // Errors marked as incomplete are treated as not complete.
- if isIncomplete(x) {
+ if b, ok := v.v.Value.(*adt.Bottom); ok {
+ return !b.IsIncomplete()
+ }
+ if !adt.IsConcrete(v.v) {
return false
}
- // Other errors are considered ground.
- return x.Kind().isConcrete()
+ if v.IncompleteKind() == adt.ListKind && !v.IsClosed() {
+ return false
+ }
+ return true
}
-// Deprecated: IsIncomplete
-//
-// It indicates that the value cannot be fully evaluated due to
-// insufficient information.
-func (v Value) IsIncomplete() bool {
- // TODO: remove
- x := v.eval(v.ctx())
- if !x.Kind().isConcrete() {
- return true
- }
- return isIncomplete(x)
-}
+// // Deprecated: IsIncomplete
+// //
+// // It indicates that the value cannot be fully evaluated due to
+// // insufficient information.
+// func (v Value) IsIncomplete() bool {
+// panic("deprecated")
+// }
// Exists reports whether this value existed in the configuration.
func (v Value) Exists() bool {
if v.v == nil {
return false
}
- return exists(v.eval(v.ctx()))
+ return exists(v.v.Value)
}
func (v Value) checkKind(ctx *context, want kind) *bottom {
@@ -1063,22 +1078,23 @@
if b, ok := x.(*bottom); ok {
return b
}
- got := x.Kind()
+ k := x.Kind()
if want != bottomKind {
- if got&want&concreteKind == bottomKind {
+ if k&want == bottomKind {
return ctx.mkErr(x, "cannot use value %v (type %s) as %s",
- v.ctx().str(x), got, want)
+ ctx.opCtx.Str(x), k, want)
}
- if !got.isGround() {
- return ctx.mkErr(x, codeIncomplete,
- "non-concrete value %v", got)
+ if !adt.IsConcrete(x) {
+ return ctx.mkErr(x, codeIncomplete, "non-concrete value %v", k)
}
}
return nil
}
func makeInt(v Value, x int64) Value {
- return remakeValue(v, newInt(v.v.v.base(), base10).setInt64(x))
+ n := &adt.Num{K: intKind}
+ n.X.SetInt64(int64(x))
+ return remakeFinal(v, nil, n)
}
// Len returns the number of items of the underlying value.
@@ -1086,17 +1102,35 @@
// number of fields, for bytes the number of bytes.
func (v Value) Len() Value {
if v.v != nil {
- switch x := v.v.v.evalPartial(v.ctx()).(type) {
- case *list:
- return remakeValue(v, x.len.evalPartial(v.ctx()))
+ switch x := v.eval(v.ctx()).(type) {
+ case *adt.Vertex:
+ if x.IsList() {
+ ctx := v.ctx()
+ n := &adt.Num{K: intKind}
+ n.X.SetInt64(int64(len(x.Elems())))
+ if x.IsClosed(ctx.opCtx) {
+ return remakeFinal(v, nil, n)
+ }
+ // Note: this HAS to be a Conjunction value and cannot be
+ // an adt.BinaryExpr, as the expressions would be considered
+ // to be self-contained and unresolvable when evaluated
+ // (can never become concrete).
+ c := &adt.Conjunction{Values: []adt.Value{
+ &adt.BasicType{K: adt.IntKind},
+ &adt.BoundValue{Op: adt.GreaterEqualOp, Value: n},
+ }}
+ return remakeFinal(v, nil, c)
+
+ }
case *bytesLit:
- return makeInt(v, int64(x.len()))
+ return makeInt(v, int64(len(x.B)))
case *stringLit:
- return makeInt(v, int64(x.len()))
+ return makeInt(v, int64(len([]rune(x.Str))))
}
}
const msg = "len not supported for type %v"
- return remakeValue(v, v.ctx().mkErr(v.v.v, msg, v.Kind()))
+ return remakeValue(v, nil, v.ctx().mkErr(v.v, msg, v.Kind()))
+
}
// Elem returns the value of undefined element types of lists and structs.
@@ -1104,51 +1138,51 @@
if v.v == nil {
return Value{}, false
}
- ctx := v.ctx()
- switch x := v.v.Value.(type) {
- case *structLit:
- t, _ := x.optionals.constraint(ctx, nil)
- if t == nil {
- break
- }
- return v.makeElem(t), true
- case *list:
- return v.makeElem(x.typ), true
+ ctx := v.ctx().opCtx
+ x := &adt.Vertex{
+ Parent: v.v,
+ Label: 0,
}
- return Value{}, false
+ v.v.Finalize(ctx)
+ v.v.MatchAndInsert(ctx, x)
+ if len(x.Conjuncts) == 0 {
+ return Value{}, false
+ }
+ x.Finalize(ctx)
+ return makeValue(v.idx, x), true
}
-// BulkOptionals returns all bulk optional fields as key-value pairs.
-// See also Elem and Template.
-func (v Value) BulkOptionals() [][2]Value {
- x, ok := v.v.Value.(*structLit)
- if !ok {
- return nil
- }
- return v.appendBulk(nil, x.optionals)
-}
+// // BulkOptionals returns all bulk optional fields as key-value pairs.
+// // See also Elem and Template.
+// func (v Value) BulkOptionals() [][2]Value {
+// x, ok := v.path.cache.(*structLit)
+// if !ok {
+// return nil
+// }
+// return v.appendBulk(nil, x.optionals)
+// }
-func (v Value) appendBulk(a [][2]Value, x *optionals) [][2]Value {
- if x == nil {
- return a
- }
- a = v.appendBulk(a, x.left)
- a = v.appendBulk(a, x.right)
- for _, set := range x.fields {
- if set.key != nil {
- ctx := v.ctx()
- fn, ok := ctx.manifest(set.value).(*lambdaExpr)
- if !ok {
- // create error
- continue
- }
- x := fn.call(ctx, set.value, &basicType{K: stringKind})
+// func (v Value) appendBulk(a [][2]Value, x *optionals) [][2]Value {
+// if x == nil {
+// return a
+// }
+// a = v.appendBulk(a, x.left)
+// a = v.appendBulk(a, x.right)
+// for _, set := range x.fields {
+// if set.key != nil {
+// ctx := v.ctx()
+// fn, ok := ctx.manifest(set.value).(*lambdaExpr)
+// if !ok {
+// // create error
+// continue
+// }
+// x := fn.call(ctx, set.value, &basicType{K: stringKind})
- a = append(a, [2]Value{v.makeElem(set.key), v.makeElem(x)})
- }
- }
- return a
-}
+// a = append(a, [2]Value{v.makeElem(set.key), v.makeElem(x)})
+// }
+// }
+// return a
+// }
// List creates an iterator over the values of a list or reports an error if
// v is not a list.
@@ -1158,8 +1192,13 @@
if err := v.checkKind(ctx, listKind); err != nil {
return Iterator{ctx: ctx}, v.toErr(err)
}
- l := v.eval(ctx).(*list)
- return Iterator{ctx: ctx, val: v, iter: l, len: len(l.elem.Arcs)}, nil
+ arcs := []field{}
+ for _, a := range v.v.Elems() {
+ if a.Label.IsInt() {
+ arcs = append(arcs, field{arc: a})
+ }
+ }
+ return Iterator{ctx: ctx, val: v, arcs: arcs}, nil
}
// Null reports an error if v is not null.
@@ -1243,97 +1282,76 @@
// structVal returns an structVal or an error if v is not a struct.
func (v Value) structValOpts(ctx *context, o options) (structValue, *bottom) {
- v, _ = v.Default() // TODO: remove?
+ v, _ = v.Default()
- obj, path, err := v.getStruct()
+ obj, err := v.getStruct()
if err != nil {
return structValue{}, err
}
- // check if any fields can be omitted
- needFilter := false
- if o.omitHidden || o.omitOptional || o.omitDefinitions {
- f := label(0)
- for _, a := range obj.Arcs {
- f |= a.Label
- if a.optional && o.omitOptional {
- needFilter = true
- break
- }
- if a.definition && (o.omitDefinitions || o.concrete) {
- needFilter = true
- break
- }
- }
- needFilter = needFilter || f.IsHidden()
- }
+ features := export.VertexFeatures(obj)
- if needFilter {
- arcs := make([]arc, len(obj.Arcs))
- k := 0
- for _, a := range obj.Arcs {
- if a.definition && (o.omitDefinitions || o.concrete) {
- continue
- }
- if a.Label.IsHidden() && o.omitHidden {
- continue
- }
- if o.omitOptional && a.optional {
- continue
- }
- arcs[k] = a
- k++
+ k := 0
+ for _, f := range features {
+ if f.IsDef() && (o.omitDefinitions || o.concrete) {
+ continue
}
- arcs = arcs[:k]
- return structValue{ctx, path, obj, arcs}, nil
+ if f.IsHidden() && o.omitHidden {
+ continue
+ }
+ if arc := obj.Lookup(f); arc == nil {
+ if o.omitOptional {
+ continue
+ }
+ // ensure it really exists.
+ v := adt.Vertex{
+ Parent: obj,
+ Label: f,
+ }
+ obj.MatchAndInsert(ctx.opCtx, &v)
+ if len(v.Conjuncts) == 0 {
+ continue
+ }
+ }
+ features[k] = f
+ k++
}
- return structValue{ctx, path, obj, obj.Arcs}, nil
+ features = features[:k]
+ return structValue{ctx, v, obj, features}, nil
}
// Struct returns the underlying struct of a value or an error if the value
// is not a struct.
func (v Value) Struct() (*Struct, error) {
- obj, path, err := v.getStruct()
+ ctx := v.ctx()
+ obj, err := v.structValOpts(ctx, options{})
if err != nil {
return nil, v.toErr(err)
}
- return &Struct{Value{v.idx, path}, obj}, nil
+ return &Struct{obj}, nil
}
-func (v Value) getStruct() (*structLit, *valueData, *bottom) {
+func (v Value) getStruct() (*structLit, *bottom) {
ctx := v.ctx()
if err := v.checkKind(ctx, structKind); err != nil {
- return nil, nil, err
+ if !err.ChildError {
+ return nil, err
+ }
}
- orig := v.eval(ctx).(*structLit)
-
- // TODO: This is expansion appropriate?
- obj, err := orig.expandFields(ctx)
- if err != nil {
- return nil, nil, err
- }
-
- path := v.v
- if obj != orig {
- p := *path
- p.arc.Value = obj
- path = &p
- }
-
- return obj, path, nil
+ return v.v, nil
}
// Struct represents a CUE struct value.
type Struct struct {
- v Value
- s *structLit
+ structValue
}
// FieldInfo contains information about a struct field.
type FieldInfo struct {
- Name string
- Pos int
- Value Value
+ Selector string
+ Name string // Deprecated: use Selector
+ Pos int
+ Value Value
IsDefinition bool
IsOptional bool
@@ -1341,31 +1359,18 @@
}
func (s *Struct) Len() int {
- return len(s.s.Arcs)
+ return s.structValue.Len()
}
// field reports information about the ith field, i < o.Len().
func (s *Struct) Field(i int) FieldInfo {
+ a, opt := s.at(i)
ctx := s.v.ctx()
- a := s.s.Arcs[i]
- a.Value = s.s.at(ctx, i)
- // TODO: adding more technical debt here. The evaluator should be
- // rewritten.
- x := s.s
- if x.optionals != nil {
- name := ctx.LabelStr(x.Arcs[i].Label)
- arg := &stringLit{x.baseValue, name, nil}
-
- val, _ := x.optionals.constraint(ctx, arg)
- if val != nil {
- a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
- }
- }
-
- v := Value{ctx.index, &valueData{s.v.v, uint32(i), a}}
- str := ctx.LabelStr(a.Label)
- return FieldInfo{str, i, v, a.definition, a.optional, a.Label.IsHidden()}
+ v := makeValue(s.v.idx, a)
+ name := ctx.LabelStr(a.Label)
+ str := a.Label.SelectorString(ctx.opCtx)
+ return FieldInfo{str, name, i, v, a.Label.IsDef(), opt, a.Label.IsHidden()}
}
// FieldByName looks up a field for the given name. If isIdent is true, it will
@@ -1373,8 +1378,8 @@
// it interprets name as an arbitrary string for a regular field.
func (s *Struct) FieldByName(name string, isIdent bool) (FieldInfo, error) {
f := s.v.ctx().Label(name, isIdent)
- for i, a := range s.s.Arcs {
- if a.Label == f {
+ for i, a := range s.features {
+ if a == f {
return s.Field(i), nil
}
}
@@ -1397,16 +1402,13 @@
if err != nil {
return &Iterator{ctx: ctx}, v.toErr(err)
}
- n := &structLit{
- obj.obj.baseValue, // baseValue
- obj.obj.emit, // emit
- obj.obj.optionals, // template
- obj.obj.closeStatus, // closeStatus
- nil, // comprehensions
- obj.Arcs, // arcs
- nil, // attributes
+
+ arcs := []field{}
+ for i := range obj.features {
+ arc, isOpt := obj.at(i)
+ arcs = append(arcs, field{arc: arc, isOptional: isOpt})
}
- return &Iterator{ctx: ctx, val: v, iter: n, len: len(n.Arcs)}, nil
+ return &Iterator{ctx: ctx, val: v, arcs: arcs}, nil
}
// Lookup reports the value at a path starting from v. The empty path returns v
@@ -1440,9 +1442,9 @@
}
f := v.ctx().Label(name, true)
- for i, a := range o.Arcs {
- if a.Label == f {
- if f.IsHidden() || !a.definition || a.optional {
+ for i, a := range o.features {
+ if a == f {
+ if f.IsHidden() || !f.IsDef() { // optional not possible for now
break
}
return newChildValue(&o, i)
@@ -1455,8 +1457,7 @@
return alt
}
}
- return newErrValue(v, ctx.mkErr(v.v.v,
- "definition %q not found", name))
+ return newErrValue(v, ctx.mkErr(v.v, "definition %q not found", name))
}
var errNotFound = errors.Newf(token.NoPos, "field not found")
@@ -1516,16 +1517,15 @@
return v
}
ctx := v.ctx()
- root := v.v.val()
for i := len(path) - 1; i >= 0; i-- {
x = map[string]interface{}{path[i]: x}
}
- value := convertVal(ctx, root, true, x)
- a := v.v.arc
- a.v = mkBin(ctx, v.Pos(), opUnify, root, value)
- a.Value = a.v.evalPartial(ctx)
- // TODO: validate recursively?
- return Value{v.idx, &valueData{v.v.parent, v.v.index, a}}
+ var value = convert.GoValueToExpr(ctx.opCtx, true, x)
+ n := &adt.Vertex{Parent: v.v.Parent}
+ n.AddConjunct(adt.MakeConjunct(nil, value))
+ n.Finalize(ctx.opCtx)
+ w := makeValue(v.idx, n)
+ return v.Unify(w)
}
// Template returns a function that represents the template definition for a
@@ -1540,19 +1540,22 @@
return nil
}
- ctx := v.ctx()
- x, ok := v.v.Value.(*structLit)
- if !ok || x.optionals.isEmpty() {
+ types := v.v.OptionalTypes()
+ if types&(adt.HasAdditional|adt.HasPattern) == 0 {
return nil
}
+ parent := v.v
+ ctx := v.ctx().opCtx
return func(label string) Value {
- arg := &stringLit{x.baseValue, label, nil}
-
- if val, _ := x.optionals.constraint(ctx, arg); val != nil {
- return remakeValue(v, val)
+ f := ctx.StringLabel(label)
+ arc := &adt.Vertex{Parent: parent, Label: f}
+ v.v.MatchAndInsert(ctx, arc)
+ if len(arc.Conjuncts) == 0 {
+ return Value{}
}
- return Value{}
+ arc.Finalize(ctx)
+ return makeValue(v.idx, arc)
}
}
@@ -1570,15 +1573,19 @@
// Value v and w must be obtained from the same build.
// TODO: remove this requirement.
func (v Value) Subsume(w Value, opts ...Option) error {
- var mode subsumeMode
o := getOptions(opts)
- if o.final {
- mode |= subFinal | subChoose
+ p := subsume.CUE
+ switch {
+ case o.final && o.ignoreClosedness:
+ p = subsume.FinalOpen
+ case o.final:
+ p = subsume.Final
+ case o.ignoreClosedness:
+ p = subsume.API
}
- if o.ignoreClosedness {
- mode |= subSchema
- }
- return subsumes(v, w, mode)
+ p.Defaults = true
+ ctx := v.ctx().opCtx
+ return p.Value(ctx, v.v, w.v)
}
// Deprecated: use Subsume.
@@ -1588,11 +1595,12 @@
// Without options, Subsumes checks whether v is a backwards compatbile schema
// of w.
//
-// By default, Subsumes tests whether two values are compatib
+// By default, Subsumes tests whether two values are compatible
// Value v and w must be obtained from the same build.
// TODO: remove this requirement.
func (v Value) Subsumes(w Value) bool {
- return subsumes(v, w, subChoose) == nil
+ p := subsume.Profile{Defaults: true}
+ return p.Check(v.ctx().opCtx, v.v, w.v)
}
// Unify reports the greatest lower bound of v and w.
@@ -1600,29 +1608,47 @@
// Value v and w must be obtained from the same build.
// TODO: remove this requirement.
func (v Value) Unify(w Value) Value {
- ctx := v.ctx()
+ // ctx := v.ctx()
if v.v == nil {
return w
}
if w.v == nil {
return v
}
- if v.Err() != nil {
- // TODO: perhaps keep both errors.
- return v
- }
- if w.Err() != nil {
+ n := &adt.Vertex{Parent: v.v.Parent, Label: v.v.Label}
+ n.AddConjunct(adt.MakeConjunct(nil, v.v))
+ n.AddConjunct(adt.MakeConjunct(nil, w.v))
+
+ ctx := v.idx.newContext()
+ n.Finalize(ctx.opCtx)
+ return makeValue(v.idx, n)
+}
+
+// UnifyAccept is as v.Unify(w), but will disregard any field that is allowed
+// in the Value accept.
+func (v Value) UnifyAccept(w Value, accept Value) Value {
+ if v.v == nil {
return w
}
- a := v.v.v
- b := w.v.v
- src := binSrc(token.NoPos, opUnify, a, b)
- val := mkBin(ctx, src.Pos(), opUnify, a, b)
- u := remakeValue(v, val)
- if err := u.Validate(); err != nil {
- u = newValueRoot(ctx, ctx.mkErr(src, err))
+ if w.v == nil {
+ return v
}
- return u
+ if accept.v == nil {
+ panic("accept must exist")
+ }
+
+ n := &adt.Vertex{Parent: v.v.Parent, Label: v.v.Label}
+ n.AddConjunct(adt.MakeConjunct(nil, v.v))
+ n.AddConjunct(adt.MakeConjunct(nil, w.v))
+
+ e := eval.New(v.idx.Runtime)
+ ctx := e.NewContext(n)
+ e.UnifyAccept(ctx, n, adt.Finalized, accept.v.Closed)
+
+ // ctx := v.idx.newContext()
+ n.Closed = accept.v.Closed
+ n.Finalize(ctx)
+ return makeValue(v.idx, n)
}
// Equals reports whether two values are equal, ignoring optional fields.
@@ -1631,9 +1657,8 @@
if v.v == nil || other.v == nil {
return false
}
- x := v.v.val()
- y := other.v.val()
- return equals(v.ctx(), x, y)
+ return eval.Equal(v.ctx().opCtx, v.v, other.v)
+
}
// Format prints a debug version of a value.
@@ -1645,11 +1670,13 @@
}
switch {
case state.Flag('#'):
- _, _ = io.WriteString(state, ctx.str(v.v.v))
+ _, _ = io.WriteString(state, ctx.str(v.v))
case state.Flag('+'):
- _, _ = io.WriteString(state, debugStr(ctx, v.v.v))
+ _, _ = io.WriteString(state, debugStr(ctx, v.v))
default:
- _, _ = io.WriteString(state, ctx.str(v.v.Value))
+ n, _ := export.Raw.Expr(v.idx.Runtime, v.v)
+ b, _ := format.Node(n)
+ _, _ = state.Write(b)
}
}
@@ -1657,7 +1684,7 @@
if v.v == nil {
return nil
}
- return v.ctx().getImportFromNode(v.v.v)
+ return v.ctx().getImportFromNode(v.v)
}
// Reference returns the instance and path referred to by this value such that
@@ -1666,154 +1693,72 @@
// only return a reference if the index resolves to a concrete value.
func (v Value) Reference() (inst *Instance, path []string) {
// TODO: don't include references to hidden fields.
- if v.v == nil {
+ if v.v == nil || len(v.v.Conjuncts) != 1 {
return nil, nil
}
ctx := v.ctx()
- var x value
- var feature string
- switch sel := v.v.v.(type) {
- case *selectorExpr:
- x = sel.X
- feature = ctx.LabelStr(sel.Sel)
+ c := v.v.Conjuncts[0]
- case *indexExpr:
- e := sel.Index.evalPartial(ctx)
- s, ok := e.(*stringLit)
- if !ok {
- return nil, nil
- }
- x = sel.X
- feature = s.Str
+ return reference(ctx, c.Env, c.Expr())
+}
- default:
+func reference(c *context, env *adt.Environment, r adt.Expr) (inst *Instance, path []string) {
+ ctx := c.opCtx
+ defer ctx.PopState(ctx.PushState(env, r.Source()))
+
+ switch x := r.(type) {
+ case *adt.FieldReference:
+ env := ctx.Env(x.UpCount)
+ inst, path = mkPath(c, nil, env.Vertex)
+ path = append(path, x.Label.SelectorString(c.Index))
+
+ case *adt.LabelReference:
+ env := ctx.Env(x.UpCount)
+ return mkPath(c, nil, env.Vertex)
+
+ case *adt.DynamicReference:
+ env := ctx.Env(x.UpCount)
+ inst, path = mkPath(c, nil, env.Vertex)
+ v, _ := ctx.Evaluate(env, x.Label)
+ str := ctx.StringValue(v)
+ path = append(path, str)
+
+ case *adt.ImportReference:
+ imp := x.ImportPath.StringValue(ctx)
+ inst = c.index.getImportFromPath(imp)
+
+ case *adt.SelectorExpr:
+ inst, path = reference(c, env, x.X)
+ path = append(path, x.Sel.SelectorString(ctx))
+
+ case *adt.IndexExpr:
+ inst, path = reference(c, env, x.X)
+ v, _ := ctx.Evaluate(env, x.Index)
+ str := ctx.StringValue(v)
+ path = append(path, str)
+ }
+ if inst == nil {
return nil, nil
}
- imp, a := mkPath(ctx, v.v, x, feature, 0)
- return imp, a
+ return inst, path
}
-func mkPath(c *context, up *valueData, x value, feature string, d int) (imp *Instance, a []string) {
- switch x := x.(type) {
- case *selectorExpr:
- imp, a = mkPath(c, up, x.X, c.LabelStr(x.Sel), d+1)
- if imp == nil {
- return nil, nil
- }
-
- case *indexExpr:
- e := x.Index.evalPartial(c)
- s, ok := e.(*stringLit)
- if !ok {
- return nil, nil
- }
- imp, a = mkPath(c, up, x.X, s.Str, d+1)
- if imp == nil {
- return nil, nil
- }
-
- case *nodeRef:
- // the parent must exist.
- var v value
- if p := locateNode(up, x); p != nil {
- v, a = mkFromRoot(c, p, d+2)
- } else {
- // Either this references another parent, or it is an embedding.
- imp = c.getImportFromNode(x.node)
- if imp != nil {
- break
- }
- // This must be an embedding, go one up.
- v, a = mkFromRoot(c, up.parent, d+2)
- }
- if v == nil {
- v = x.node
- }
- imp = c.getImportFromNode(v)
- default:
- return nil, nil
+func mkPath(ctx *context, a []string, v *adt.Vertex) (inst *Instance, path []string) {
+ if v.Parent == nil {
+ return ctx.index.getImportFromNode(v), a
}
- return imp, append(a, feature)
+ inst, path = mkPath(ctx, a, v.Parent)
+ path = append(path, v.Label.SelectorString(ctx.opCtx))
+ return inst, path
}
-func mkFromRoot(c *context, up *valueData, d int) (root value, a []string) {
- if up == nil {
- return nil, make([]string, 0, d)
- }
- root, a = mkFromRoot(c, up.parent, d+1)
- if up.parent != nil {
- a = append(a, c.LabelStr(up.Label))
- } else {
- root = up.v
- }
- return root, a
-}
-
-// References reports all references used to evaluate this value. It does not
-// report references for sub fields if v is a struct.
-//
-// Deprecated: can be implemented in terms of Reference and Expr.
-func (v Value) References() [][]string {
- // TODO: the pathFinder algorithm is quite broken. Using Reference and Expr
- // will cast a much more accurate net on referenced values.
- ctx := v.ctx()
- pf := pathFinder{up: v.v}
- raw := v.v.v
- if raw == nil {
- return nil
- }
- rewrite(ctx, raw, pf.find)
- return pf.paths
-}
-
-type pathFinder struct {
- paths [][]string
- stack []label
- up *valueData
-}
-
-func (p *pathFinder) find(ctx *context, v value) (value, bool) {
- switch x := v.(type) {
- case *selectorExpr:
- i := len(p.stack)
- p.stack = append(p.stack, x.Sel)
- rewrite(ctx, x.X, p.find)
- p.stack = p.stack[:i]
- return v, false
-
- case *nodeRef:
- i := len(p.stack)
- up := p.up
- for ; up != nil && up.Value != x.node.(value); up = up.parent {
- }
- for ; up != nil && up.Label > 0; up = up.parent {
- p.stack = append(p.stack, up.Label)
- }
- path := make([]string, len(p.stack))
- for i, v := range p.stack {
- path[len(path)-1-i] = ctx.LabelStr(v)
- }
- p.paths = append(p.paths, path)
- p.stack = p.stack[:i]
- return v, false
-
- case *structLit:
- // If the stack is empty, we do not descend, as we are not evaluating
- // sub fields.
- if len(p.stack) == 0 {
- return v, false
- }
-
- stack := p.stack
- p.stack = nil
- for _, a := range x.Arcs {
- rewrite(ctx, a.v, p.find)
- }
- p.stack = stack
- return v, false
- }
- return v, true
-}
+// // References reports all references used to evaluate this value. It does not
+// // report references for sub fields if v is a struct.
+// //
+// // Deprecated: can be implemented in terms of Reference and Expr.
+// func (v Value) References() [][]string {
+// panic("deprecated")
+// }
type options struct {
concrete bool // enforce that values are concrete
@@ -1950,108 +1895,18 @@
// more than one error, retrievable with errors.Errors, if more than one
// exists.
func (v Value) Validate(opts ...Option) error {
- x := validator{}
o := options{}
o.updateOptions(opts)
- // Logically, errors are always permitted in logical fields, so we
- // force-disable them.
- // TODO: consider whether we should honor the option to allow checking
- // optional fields.
- o.omitOptional = true
- x.walk(v, o)
- return errors.Sanitize(x.errs)
-}
-type validator struct {
- errs errors.Error
- depth int
-}
-
-func (x *validator) before(v Value, o options) bool {
- if err := v.checkKind(v.ctx(), bottomKind); err != nil {
- if !o.concrete && isIncomplete(err) {
- if o.disallowCycles && err.Code == codeCycle {
- x.errs = errors.Append(x.errs, v.toErr(err))
- }
- return false
- }
- x.errs = errors.Append(x.errs, v.toErr(err))
- if len(errors.Errors(x.errs)) > 50 {
- return false // mostly to avoid some hypothetical cycle issue
- }
+ cfg := &validate.Config{
+ Concrete: o.concrete,
+ DisallowCycles: o.disallowCycles,
+ AllErrors: true,
}
- if o.concrete {
- ctx := v.ctx()
- if err := isGroundRecursive(ctx, v.eval(ctx)); err != nil {
- x.errs = errors.Append(x.errs, v.toErr(err))
- }
- }
- return true
-}
-func (x *validator) walk(v Value, opts options) {
- // TODO(#42): we can get rid of the arbitrary evaluation depth once CUE has
- // proper structural cycle detection. See Issue #42. Currently errors
- // occurring at a depth > internal.MaxDepth will not be detected.
- if x.depth > internal.MaxDepth {
- return
- }
- ctx := v.ctx()
- switch v.Kind() {
- case StructKind:
- if !x.before(v, opts) {
- return
- }
- x.depth++
- obj, err := v.structValOpts(ctx, opts)
- if err != nil {
- if !isIncomplete(err) && opts.concrete {
- x.errs = errors.Append(x.errs, v.toErr(err))
- }
- }
- for i := 0; i < obj.Len(); i++ {
- _, v := obj.At(i)
- opts := opts
- if obj.Arcs[i].definition {
- opts.concrete = false
- }
- x.walk(v, opts)
- }
- x.depth--
-
- case ListKind:
- if !x.before(v, opts) {
- return
- }
- x.depth++
- list, _ := v.List()
- for list.Next() {
- x.walk(list.Value(), opts)
- }
- x.depth--
-
- default:
- x.before(v, opts)
- }
-}
-
-func isGroundRecursive(ctx *context, v value) *bottom {
- switch x := v.(type) {
- case *bottom:
- if isIncomplete(x) {
- return x
- }
- case *list:
- for i := 0; i < len(x.elem.Arcs); i++ {
- v := ctx.manifest(x.at(ctx, i))
- if err := isGroundRecursive(ctx, v); err != nil {
- return err
- }
- }
- default:
- if !x.Kind().isGround() {
- return ctx.mkErr(v, "incomplete value (%v)", ctx.str(v))
- }
+ b := validate.Validate(v.ctx().opCtx, v.v, cfg)
+ if b != nil {
+ return b.Err
}
return nil
}
@@ -2094,15 +1949,18 @@
// is no attribute for the requested key.
func (v Value) Attribute(key string) Attribute {
// look up the attributes
- if v.v == nil || v.v.attrs == nil {
+ if v.v == nil {
return Attribute{internal.NewNonExisting(key)}
}
- for _, a := range v.v.attrs.attr {
- if a.key() != key {
+ // look up the attributes
+ for _, a := range export.ExtractFieldAttrs(v.v.Conjuncts) {
+ k, body := a.Split()
+ if key != k {
continue
}
- return Attribute{internal.ParseAttrBody(token.NoPos, a.body())}
+ return Attribute{internal.ParseAttrBody(token.NoPos, body)}
}
+
return Attribute{internal.NewNonExisting(key)}
}
@@ -2167,79 +2025,219 @@
if v.v == nil {
return NoOp, nil
}
+
+ var expr adt.Expr
+ var env *adt.Environment
+
+ if v.v.IsData() {
+ switch x := v.v.Value.(type) {
+ case *adt.ListMarker, *adt.StructMarker:
+ expr = v.v
+ default:
+ expr = x
+ }
+
+ } else {
+ switch len(v.v.Conjuncts) {
+ case 0:
+ if v.v.Value == nil {
+ return NoOp, []Value{makeValue(v.idx, v.v)}
+ }
+ switch x := v.v.Value.(type) {
+ case *adt.ListMarker, *adt.StructMarker:
+ expr = v.v
+ default:
+ expr = x
+ }
+
+ case 1:
+ // the default case, processed below.
+ c := v.v.Conjuncts[0]
+ env = c.Env
+ expr = c.Expr()
+ if w, ok := expr.(*adt.Vertex); ok {
+ return Value{v.idx, w}.Expr()
+ }
+
+ default:
+ a := []Value{}
+ ctx := v.ctx().opCtx
+ for _, c := range v.v.Conjuncts {
+ n := &adt.Vertex{
+ Parent: v.v.Parent,
+ Label: v.v.Label,
+ }
+ n.AddConjunct(c)
+ n.Finalize(ctx)
+ a = append(a, makeValue(v.idx, n))
+ }
+ return adt.AndOp, a
+ }
+ }
+
// TODO: replace appends with []Value{}. For not leave.
a := []Value{}
op := NoOp
- switch x := v.v.v.(type) {
+ switch x := expr.(type) {
case *binaryExpr:
- a = append(a, remakeValue(v, x.X))
- a = append(a, remakeValue(v, x.Y))
- op = opToOp[x.Op]
+ a = append(a, remakeValue(v, env, x.X))
+ a = append(a, remakeValue(v, env, x.Y))
+ op = x.Op
case *unaryExpr:
- a = append(a, remakeValue(v, x.X))
- op = opToOp[x.Op]
- case *bound:
- a = append(a, remakeValue(v, x.Expr))
- op = opToOp[x.Op]
- case *unification:
+ a = append(a, remakeValue(v, env, x.X))
+ op = x.Op
+ case *boundExpr:
+ a = append(a, remakeValue(v, env, x.Expr))
+ op = x.Op
+ case *boundValue:
+ a = append(a, remakeValue(v, env, x.Value))
+ op = x.Op
+ case *adt.Conjunction:
// pre-expanded unification
for _, conjunct := range x.Values {
- a = append(a, remakeValue(v, conjunct))
+ a = append(a, remakeValue(v, env, conjunct))
}
op = AndOp
- case *disjunction:
- // Filter defaults that are subsumed by another value.
+ case *adt.Disjunction:
count := 0
outer:
- for _, disjunct := range x.Values {
- if disjunct.Default {
- for _, n := range x.Values {
- s := subsumer{ctx: v.ctx()}
- if !n.Default && s.subsumes(n.Val, disjunct.Val) {
+ for i, disjunct := range x.Values {
+ if i < x.NumDefaults {
+ for _, n := range x.Values[x.NumDefaults:] {
+ if subsume.Value(v.ctx().opCtx, n, disjunct) == nil {
continue outer
}
}
}
count++
- a = append(a, remakeValue(v, disjunct.Val))
+ a = append(a, remakeValue(v, env, disjunct))
}
if count > 1 {
op = OrOp
}
+
+ case *adt.DisjunctionExpr:
+ // Filter defaults that are subsumed by another value.
+ count := 0
+ outerExpr:
+ for _, disjunct := range x.Values {
+ if disjunct.Default {
+ for _, n := range x.Values {
+ a := adt.Vertex{
+ Parent: v.v.Parent,
+ Label: v.v.Label,
+ Closed: v.v.Closed,
+ }
+ b := a
+ a.AddConjunct(adt.MakeConjunct(env, n.Val))
+ b.AddConjunct(adt.MakeConjunct(env, disjunct.Val))
+
+ e := eval.New(v.idx.Runtime)
+ ctx := e.NewContext(nil)
+ e.UnifyAccept(ctx, &a, adt.Finalized, v.v.Closed)
+ e.UnifyAccept(ctx, &b, adt.Finalized, v.v.Closed)
+ if !n.Default && subsume.Value(ctx, &a, &b) == nil {
+ continue outerExpr
+ }
+ }
+ }
+ count++
+ a = append(a, remakeValue(v, env, disjunct.Val))
+ }
+ if count > 1 {
+ op = adt.OrOp
+ }
+
case *interpolation:
for _, p := range x.Parts {
- a = append(a, remakeValue(v, p))
+ a = append(a, remakeValue(v, env, p))
}
op = InterpolationOp
+
+ case *adt.FieldReference:
+ // TODO: allow hard link
+ ctx := v.ctx().opCtx
+ f := ctx.PushState(env, x.Src)
+ env := ctx.Env(x.UpCount)
+ a = append(a, remakeValue(v, nil, &adt.NodeLink{Node: env.Vertex}))
+ a = append(a, remakeValue(v, nil, ctx.NewString(x.Label.SelectorString(ctx))))
+ _ = ctx.PopState(f)
+ op = SelectorOp
+
case *selectorExpr:
- a = append(a, remakeValue(v, x.X))
- a = append(a, remakeValue(v, &stringLit{
- x.baseValue,
- v.ctx().LabelStr(x.Sel),
- nil,
+ a = append(a, remakeValue(v, env, x.X))
+ // A string selector is quoted.
+ a = append(a, remakeValue(v, env, &adt.String{
+ Str: x.Sel.SelectorString(v.idx.Index),
}))
op = SelectorOp
+
case *indexExpr:
- a = append(a, remakeValue(v, x.X))
- a = append(a, remakeValue(v, x.Index))
+ a = append(a, remakeValue(v, env, x.X))
+ a = append(a, remakeValue(v, env, x.Index))
op = IndexOp
case *sliceExpr:
- a = append(a, remakeValue(v, x.X))
- a = append(a, remakeValue(v, x.Lo))
- a = append(a, remakeValue(v, x.Hi))
+ a = append(a, remakeValue(v, env, x.X))
+ a = append(a, remakeValue(v, env, x.Lo))
+ a = append(a, remakeValue(v, env, x.Hi))
op = SliceOp
case *callExpr:
- a = append(a, remakeValue(v, x.Fun))
+ a = append(a, remakeValue(v, env, x.Fun))
for _, arg := range x.Args {
- a = append(a, remakeValue(v, arg))
+ a = append(a, remakeValue(v, env, arg))
}
op = CallOp
case *customValidator:
- a = append(a, remakeValue(v, x.Builtin))
+ a = append(a, remakeValue(v, env, x.Builtin))
for _, arg := range x.Args {
- a = append(a, remakeValue(v, arg))
+ a = append(a, remakeValue(v, env, arg))
}
op = CallOp
+
+ case *adt.StructLit:
+ // Simulate old embeddings.
+ envEmbed := &adt.Environment{
+ Up: env,
+ Vertex: v.v,
+ }
+ fields := []adt.Decl{}
+ ctx := v.ctx().opCtx
+ for _, d := range x.Decls {
+ switch x := d.(type) {
+ case adt.Expr:
+ // embedding
+ n := &adt.Vertex{
+ Parent: v.v.Parent,
+ Label: v.v.Label}
+ c := adt.MakeConjunct(envEmbed, x)
+ n.AddConjunct(c)
+ n.Finalize(ctx)
+ a = append(a, makeValue(v.idx, n))
+
+ default:
+ fields = append(fields, d)
+ }
+ }
+ if len(a) == 0 {
+ a = append(a, v)
+ break
+ }
+
+ if len(fields) > 0 {
+ n := &adt.Vertex{
+ Parent: v.v.Parent,
+ Label: v.v.Label,
+ }
+ c := adt.MakeConjunct(env, &adt.StructLit{
+ Decls: fields,
+ })
+ n.AddConjunct(c)
+ n.Finalize(ctx)
+ a = append(a, makeValue(v.idx, n))
+ }
+
+ op = adt.AndOp
+
default:
a = append(a, v)
}
diff --git a/cue/types_test.go b/cue/types_test.go
index cea8a14..b36c6c5 100644
--- a/cue/types_test.go
+++ b/cue/types_test.go
@@ -30,6 +30,8 @@
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal"
+ "cuelang.org/go/internal/core/adt"
+ "cuelang.org/go/internal/core/debug"
)
func getInstance(t *testing.T, body ...string) *Instance {
@@ -175,6 +177,12 @@
concrete: false,
}, {
value: `import "time"
+ v: time.Time`,
+ kind: BottomKind,
+ incompleteKind: StringKind,
+ concrete: false,
+ }, {
+ value: `import "time"
v: {a: time.Time}.a`,
kind: BottomKind,
incompleteKind: StringKind,
@@ -567,7 +575,7 @@
res: "[1,2,3,]",
}, {
value: `>=5*[1,2,3, ...int]`,
- err: "incomplete",
+ err: "non-concrete value >=5 in operand to *",
}, {
value: `[for x in #y if x > 1 { x }]
#y: [1,2,3]`,
@@ -602,6 +610,9 @@
res string
err string
}{{
+ value: `{ #def: 1, _hidden: 2, opt?: 3, reg: 4 }`,
+ res: "{reg:4,}",
+ }, {
value: `_|_`,
err: "from source",
}, {
@@ -680,6 +691,9 @@
}, {
value: `{_a:"a"}`,
res: `{_a:"a",}`,
+ }, {
+ value: `{_a:"a", b?: "b", #c: 3}`,
+ res: `{_a:"a",b?:"b",#c:3,}`,
}}
for _, tc := range testCases {
t.Run(tc.value, func(t *testing.T) {
@@ -692,6 +706,9 @@
buf := []byte{'{'}
for iter.Next() {
buf = append(buf, iter.Label()...)
+ if iter.IsOptional() {
+ buf = append(buf, '?')
+ }
buf = append(buf, ':')
b, err := iter.Value().MarshalJSON()
checkFatal(t, err, tc.err, "Obj.At")
@@ -731,7 +748,7 @@
eval string
}{{
ref: []string{"v", "x"},
- raw: "(int & <=9223372036854775807 & int & >=-9223372036854775808)",
+ raw: ">=-9223372036854775808 & <=9223372036854775807 & int",
eval: "int64",
}}
for _, tc := range testCases {
@@ -788,6 +805,7 @@
return x
}
+// TODO: Exporting of Vertex as Conjunct
func TestFill(t *testing.T) {
r := &Runtime{}
@@ -838,10 +856,13 @@
path = strings.Split(tc.path, ",")
}
- v := compileT(t, r, tc.in).Value().Fill(tc.x, path...)
+ v := compileT(t, r, tc.in).Value()
+ v = v.Fill(tc.x, path...)
+
w := compileT(t, r, tc.out).Value()
- if !reflect.DeepEqual(goValue(v), goValue(w)) {
+ if !cmp.Equal(goValue(v), goValue(w)) {
+ t.Error(cmp.Diff(goValue(v), goValue(w)))
t.Errorf("\ngot: %s\nwant: %s", v, w)
}
}
@@ -854,6 +875,8 @@
#Provider: {
ID: string
notConcrete: bool
+ a: int
+ b: int
}
`)
@@ -869,9 +892,24 @@
}
got := fmt.Sprint(root.Value())
-
- if got != `{#Provider: C{ID: string, notConcrete: bool}, providers: {myprovider: C{ID: (string & "12345"), notConcrete: bool}}}` {
- t.Error(got)
+ want := `{
+ #Provider: {
+ ID: string
+ notConcrete: bool
+ a: int
+ b: int
+ }
+ providers: {
+ myprovider: {
+ ID: "12345"
+ notConcrete: bool
+ a: int
+ b: int
+ }
+ }
+}`
+ if got != want {
+ t.Errorf("got: %s\nwant: %s", got, want)
}
}
@@ -890,11 +928,11 @@
}, {
in: `_foo: 3`,
def: "_foo",
- out: `_|_(definition "_foo" not found)`,
+ out: `_|_ // definition "_foo" not found`,
}, {
in: `_#foo: 3`,
def: "_#foo",
- out: `_|_(definition "_#foo" not found)`,
+ out: `_|_ // definition "_#foo" not found`,
}}
for _, tc := range testCases {
@@ -910,6 +948,7 @@
}
}
+// TODO: trim down to individual defaults?
func TestDefaults(t *testing.T) {
testCases := []struct {
value string
@@ -928,12 +967,12 @@
ok: true,
}, {
value: `*{a:1,b:2}|{a:1}|{b:2}`,
- def: "{a: 1, b: 2}",
+ def: "{a:1,b:2}",
val: "{a: 1}|{b: 2}",
ok: true,
}, {
value: `{a:1}&{b:2}`,
- def: `{a: 1, b: 2}`,
+ def: `({a:1} & {b:2})`,
val: ``,
ok: false,
}}
@@ -946,11 +985,11 @@
t.Errorf("hasDefault: got %v; want %v", ok, tc.ok)
}
- if got := fmt.Sprint(d); got != tc.def {
+ if got := compactRawStr(d); got != tc.def {
t.Errorf("default: got %v; want %v", got, tc.def)
}
- op, val := v.Expr()
+ op, val := d.Expr()
if op != OrOp {
return
}
@@ -987,7 +1026,7 @@
// length: "2",
}, {
input: "3",
- length: "_|_(len not supported for type int)",
+ length: "_|_ // len not supported for type int",
}}
for _, tc := range testCases {
t.Run(tc.input, func(t *testing.T) {
@@ -1036,7 +1075,7 @@
a: foo: b: [Bar=string]: { d: Bar }
`,
path: []string{"a", "foo", "b", ""},
- want: `{"c":"foolabel","d":"label"}`,
+ want: `{"d":"label","c":"foolabel"}`,
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
@@ -1059,6 +1098,67 @@
}
}
+func TestElem(t *testing.T) {
+ testCases := []struct {
+ value string
+ path []string
+ want string
+ }{{
+ value: `
+ a: [...int]
+ `,
+ path: []string{"a", ""},
+ want: `int`,
+ }, {
+ value: `
+ [Name=string]: { a: Name }
+ `,
+ path: []string{"", "a"},
+ want: `string`,
+ }, {
+ value: `
+ [Name=string]: { a: Name }
+ `,
+ path: []string{""},
+ want: "{\n\ta: string\n}",
+ }, {
+ value: `
+ a: [Foo=string]: [Bar=string]: { b: Foo+Bar }
+ `,
+ path: []string{"a", "", ""},
+ want: "{\n\tb: string + string\n}",
+ }, {
+ value: `
+ a: [Foo=string]: b: [Bar=string]: { c: Foo+Bar }
+ a: foo: b: [Bar=string]: { d: Bar }
+ `,
+ path: []string{"a", "foo", "b", ""},
+ want: "{\n\td: string\n\tc: string + string\n}",
+ }}
+ for _, tc := range testCases {
+ t.Run("", func(t *testing.T) {
+ v := getInstance(t, tc.value).Value()
+ v.v.Finalize(v.ctx().opCtx) // TODO: do in instance.
+ for _, p := range tc.path {
+ if p == "" {
+ var ok bool
+ v, ok = v.Elem()
+ if !ok {
+ t.Fatal("expected element")
+ }
+ } else {
+ v = v.Lookup(p)
+ }
+ }
+ got := fmt.Sprint(v)
+ // got := debug.NodeString(v.ctx().opCtx, v.v, &debug.Config{Compact: true})
+ if got != tc.want {
+ t.Errorf("\n got: %q\nwant: %q", got, tc.want)
+ }
+ })
+ }
+}
+
func TestSubsumes(t *testing.T) {
a := []string{"a"}
b := []string{"b"}
@@ -1281,6 +1381,7 @@
}
}
+// TODO: options: disallow cycles.
func TestValidate(t *testing.T) {
testCases := []struct {
desc string
@@ -1475,7 +1576,7 @@
v = v.Lookup(e)
}
}
- got, _ := v.v.appendPath(nil, v.idx)
+ got := v.appendPath(nil)
if !reflect.DeepEqual(got, tc) {
t.Errorf("got %v; want %v", got, tc)
}
@@ -1514,7 +1615,7 @@
}, {
config: config,
path: strList(),
- str: "{a: {a: 0, b: 1, c: 2}, b: {d: a.a, e: int}",
+ str: "{a:{a:0,b:1,c:2},b:{d:0,e:int}",
}, {
config: config,
path: strList("a", "a"),
@@ -1522,7 +1623,7 @@
}, {
config: config,
path: strList("a"),
- str: "{a: 0, b: 1, c: 2}",
+ str: "{a:0,b:1,c:2}",
}, {
config: config,
path: strList("b", "d"),
@@ -1544,7 +1645,7 @@
t.Errorf("exists: got %v; want %v", got, tc.notExists)
}
- got := fmt.Sprint(v)
+ got := v.ctx().opCtx.Str(v.v)
if tc.str == "" {
t.Fatalf("str empty, got %q", got)
}
@@ -1843,6 +1944,7 @@
}
}
+// TODO: duplicate docs.
func TestValueDoc(t *testing.T) {
const config = `
// foobar defines at least foo.
@@ -1923,21 +2025,19 @@
}, {
val: v1,
path: "foos MyFoo field1",
- doc: `field1 is an int.
+ doc: `local field comment.
-local field comment.
+field1 is an int.
`,
}, {
val: v1,
path: "foos MyFoo field2",
doc: "other field comment.\n",
}, {
+ // Duplicates are now removed.
val: v1,
path: "foos MyFoo dup3",
- doc: `duplicate field comment
-
-duplicate field comment
-`,
+ doc: "duplicate field comment\n",
}, {
val: v1,
path: "bar field1",
@@ -1945,9 +2045,9 @@
}, {
val: v1,
path: "baz field1",
- doc: `comment from baz on field 1
+ doc: `comment from bar on field 1
-comment from bar on field 1
+comment from baz on field 1
`,
}, {
val: v1,
@@ -1961,9 +2061,9 @@
}, {
val: both,
path: "Foo",
- doc: `Another Foo.
+ doc: `A Foo fooses stuff.
-A Foo fooses stuff.
+Another Foo.
`,
}}
for _, tc := range testCases {
@@ -1992,6 +2092,8 @@
return doc
}
+// TODO: unwrap marshal error
+// TODO: improve error messages
func TestMarshalJSON(t *testing.T) {
testCases := []struct {
value string
@@ -2048,7 +2150,7 @@
err: `0: cannot convert incomplete value`,
}, {
value: `(>=3 * [1, 2])`,
- err: "incomplete error", // TODO: improve error
+ err: "cue: marshal error: non-concrete value >=3 in operand to *",
}, {
value: `{}`,
json: `{}`,
@@ -2117,11 +2219,11 @@
}, {
// Issue #326
value: `x: "\(string)": "v"`,
- err: `x: incomplete value 'string' in interpolation`,
+ err: `x: invalid interpolation`,
}, {
// Issue #326
value: `x: "\(bool)": "v"`,
- err: `x: expression in interpolation must evaluate to a number kind or string (found bool)`,
+ err: `invalid interpolation`,
}, {
// Issue #326
value: `
@@ -2173,8 +2275,8 @@
out: "_|_(from source)",
}, {
value: `(a.b)
- a: {}`,
- out: `_|_(undefined field "b")`,
+ a: {}`,
+ out: `_|_(undefined field b)`,
}, {
value: `true`,
out: `true`,
@@ -2190,15 +2292,18 @@
}, {
value: `12_000`,
out: `12000`,
+ // out: `12_000`,
}, {
value: `12.000`,
out: `12.000`,
}, {
value: `12M`,
out: `12000000`,
+ // out: `12M`,
}, {
value: `3.0e100`,
out: `3.0e+100`,
+ // out: `3.0e100`,
}, {
value: `[]`,
out: `[]`,
@@ -2228,8 +2333,11 @@
}
}
inst.Value().Walk(func(v Value) bool {
- if k, ok := v.Label(); ok {
- buf = append(buf, k+":"...)
+ v = v.Eval()
+ if !v.v.Label.IsInt() {
+ if k, ok := v.Label(); ok {
+ buf = append(buf, k+":"...)
+ }
}
switch v.Kind() {
case StructKind:
@@ -2237,6 +2345,11 @@
case ListKind:
buf = append(buf, '[')
default:
+ if b, _ := v.v.Value.(*adt.Bottom); b != nil {
+ s := debugStr(v.ctx(), b)
+ buf = append(buf, fmt.Sprint(s, ",")...)
+ return true
+ }
buf = append(buf, fmt.Sprint(v, ",")...)
}
return true
@@ -2283,6 +2396,7 @@
testCases := []struct {
input string
want string
+ alt string
}{{
input: "v: w: x: _|_",
want: "",
@@ -2327,6 +2441,14 @@
a: 1
`,
want: "a",
+ }, {
+ input: `
+ import "math"
+
+ v: w: x: math.Pi
+ `,
+ want: "Pi",
+ alt: "3.14159265358979323846264338327950288419716939937510582097494459",
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
@@ -2339,15 +2461,20 @@
}
if tc.want != "" {
- v := inst.Lookup(a...)
- if x, _ := v.Int64(); x != 1 {
- t.Errorf("path resolved to %s; want 1", v)
+ want := "1"
+ if tc.alt != "" {
+ want = tc.alt
+ }
+ v := fmt.Sprint(inst.Lookup(a...))
+ if v != want {
+ t.Errorf("path resolved to %s; want %s", v, want)
}
}
})
}
}
+// TODO: stack overflow
func TestPathCorrection(t *testing.T) {
testCases := []struct {
input string
@@ -2355,34 +2482,39 @@
want string
skip bool
}{{
+ // // TODO: structural cycle.
+ // input: `
+ // a: b: {
+ // c: d: b
+ // }
+ // `,
+ // lookup: func(i *Instance) Value {
+ // _, a := i.Lookup("a", "b", "c", "d").Expr()
+ // return a[0].Lookup("b", "c", "d")
+ // },
+ // want: "a.b",
+ // }, {
+
+ // TODO: embedding: have field operators.
+ // input: `
+ // a: {
+ // c: 3
+ // {x: c}
+ // }
+ // `,
+ // lookup: func(i *Instance) Value {
+ // _, a := i.Lookup("a").Expr()
+ // return a[1].Lookup("x")
+ // },
+ // want: "a.c",
+ // }, {
+
+ // TODO: implement proper Elem()
input: `
- a: b: {
- c: d: b
- }
- `,
- lookup: func(i *Instance) Value {
- _, a := i.Lookup("a", "b", "c", "d").Expr()
- return a[0].Lookup("b", "c", "d")
- },
- want: "a.b",
- }, {
- input: `
- a: {
- c: 3
- {x: c}
- }
- `,
- lookup: func(i *Instance) Value {
- _, a := i.Lookup("a").Expr()
- return a[1].Lookup("x")
- },
- want: "a.c",
- }, {
- input: `
- a: b: [...T]
- a: b: [...T]
- T: 1
- `,
+ a: b: [...T]
+ a: b: [...T]
+ T: int
+ `,
lookup: func(i *Instance) Value {
v, _ := i.Lookup("a", "b").Elem()
_, a := v.Expr()
@@ -2391,12 +2523,12 @@
want: "T",
}, {
input: `
- #S: {
- b?: [...#T]
- b?: [...#T]
- }
- #T: int
- `,
+ #S: {
+ b?: [...#T]
+ b?: [...#T]
+ }
+ #T: int
+ `,
lookup: func(i *Instance) Value {
v := i.LookupDef("#S")
f, _ := v.LookupField("b")
@@ -2407,43 +2539,65 @@
want: "#T",
}, {
input: `
- #a: {
- #T: {b: 3}
- close({}) | close({c: #T}) | close({d: string})
- }
- `,
+ #S: {
+ a?: [...#T]
+ b?: [...#T]
+ }
+ #T: int
+ `,
lookup: func(i *Instance) Value {
- f, _ := i.LookupField("#a")
- _, a := f.Value.Expr() // &
- _, a = a[1].Expr() // |
- return a[1].Lookup("c")
+ v := i.LookupDef("#S")
+ f, _ := v.LookupField("a")
+ x := f.Value
+ f, _ = v.LookupField("b")
+ y := f.Value
+ u := x.Unify(y)
+ v, _ = u.Elem()
+ _, a := v.Expr()
+ return a[0]
},
- want: "#a.#T",
+ want: "#T",
+ // }, {
+ // input: `
+ // #a: {
+ // #T: {b: 3}
+ // close({}) | close({c: #T}) | close({d: string})
+ // }
+ // `,
+ // lookup: func(i *Instance) Value {
+ // f, _ := i.LookupField("#a")
+ // _, a := f.Value.Expr() // &
+ // _, a = a[1].Expr() // |
+ // return a[1].Lookup("c")
+ // },
+ // want: "#a.#T",
}, {
- input: `
- package foo
+ // TODO: iterate over Definitions
+ // input: `
+ // package foo
- #Struct: {
- #T: int
+ // #Struct: {
+ // #T: int
- {b?: #T}
- }`,
- want: "#Struct.#T",
- lookup: func(inst *Instance) Value {
- // Locate Struct
- i, _ := inst.Value().Fields(Definitions(true))
- if !i.Next() {
- t.Fatal("no fields")
- }
- // Locate b
- i, _ = i.Value().Fields(Definitions(true), Optional(true))
- if !(i.Next() && i.Next()) {
- t.Fatal("no fields")
- }
- v := i.Value()
- return v
- },
- }, {
+ // {b?: #T}
+ // }`,
+ // want: "#Struct.#T",
+ // lookup: func(inst *Instance) Value {
+ // // Locate Struct
+ // i, _ := inst.Value().Fields(Definitions(true))
+ // if !i.Next() {
+ // t.Fatal("no fields")
+ // }
+ // // Locate b
+ // i, _ = i.Value().Fields(Definitions(true), Optional(true))
+ // if !(i.Next() && i.Next()) {
+ // t.Fatal("no fields")
+ // }
+ // v := i.Value()
+ // return v
+ // },
+ // }, {
+
input: `
package foo
@@ -2463,75 +2617,80 @@
v = v.Lookup("a")
return v
},
- }, {
- input: `
- package foo
+ // }, {
- #A: #B: #T
+ // TODO: record additionalItems in list
+ // input: `
+ // package foo
- #T: {
- a: [...#S]
- #S: {}
- }
- `,
- want: "#T.#S",
- lookup: func(inst *Instance) Value {
- f, _ := inst.Value().LookupField("#A")
- f, _ = f.Value.LookupField("#B")
- v := f.Value
- v = Dereference(v)
- v, _ = v.Lookup("a").Elem()
- return v
- },
- }, {
- input: `
- #A: {
- b: #T
- }
+ // #A: #B: #T
- #T: {
- a: #S
- #S: {}
- }
- `,
- want: "#T.#S",
- lookup: func(inst *Instance) Value {
- f, _ := inst.Value().LookupField("#A")
- v := f.Value.Lookup("b")
- v = Dereference(v)
- v = v.Lookup("a")
- return v
- },
- }, {
- // TODO(eval): embedded structs are currently represented at the same
- // level as the enclosing struct. This means that the parent of an
- // embedded struct skips the struct in which it is embedded. Treat
- // embedded structs as "anonymous" fields.
- // This could perhaps be made fixed with dereferencing as well.
- skip: true,
- input: `
- #Tracing: {
- #T: { address?: string }
- #S: { ip?: string }
+ // #T: {
+ // a: [...#S]
+ // #S: {}
+ // }
+ // `,
+ // want: "#T.#S",
+ // lookup: func(inst *Instance) Value {
+ // f, _ := inst.Value().LookupField("#A")
+ // f, _ = f.Value.LookupField("#B")
+ // v := f.Value
+ // v = Dereference(v)
+ // v, _ = v.Lookup("a").Elem()
+ // return v
+ // },
+ // }, {
- close({}) | close({
- t: #T
- }) | close({
- s: S
- })
- }
- #X: {}
- #X // Disconnect top-level struct from the one visible by close.
- `,
- want: "",
- lookup: func(inst *Instance) Value {
- f, _ := inst.Value().LookupField("#Tracing")
- v := f.Value.Eval()
- _, args := v.Expr()
- v = args[1].Lookup("t")
- v = Dereference(v)
- return v
- },
+ // YAY: works.
+ // input: `
+ // #A: {
+ // b: #T
+ // }
+
+ // #T: {
+ // a: #S
+ // #S: {}
+ // }
+ // `,
+ // want: "#T.#S",
+ // lookup: func(inst *Instance) Value {
+ // f, _ := inst.Value().LookupField("#A")
+ // v := f.Value.Lookup("b")
+ // v = Dereference(v)
+ // v = v.Lookup("a")
+ // return v
+ // },
+ // }, {
+
+ // // TODO(eval): embedded structs are currently represented at the same
+ // // level as the enclosing struct. This means that the parent of an
+ // // embedded struct skips the struct in which it is embedded. Treat
+ // // embedded structs as "anonymous" fields.
+ // // This could perhaps be made fixed with dereferencing as well.
+ // skip: true,
+ // input: `
+ // #Tracing: {
+ // #T: { address?: string }
+ // #S: { ip?: string }
+
+ // close({}) | close({
+ // t: #T
+ // }) | close({
+ // s: #S
+ // })
+ // }
+ // #X: {}
+ // #X // Disconnect top-level struct from the one visible by close.
+ // `,
+ // want: "",
+ // lookup: func(inst *Instance) Value {
+ // f, _ := inst.Value().LookupField("#Tracing")
+ // v := f.Value.Eval()
+ // _, args := v.Expr()
+ // v = args[1].Lookup("t")
+ // v = Dereference(v)
+ // return v
+ // },
}}
for _, tc := range testCases {
if tc.skip {
@@ -2556,64 +2715,64 @@
}
}
-func TestReferences(t *testing.T) {
- config1 := `
- a: {
- b: 3
- }
- c: {
- d: a.b
- e: c.d
- f: a
- }
- `
- config2 := `
- a: { c: 3 }
- b: { c: int, d: 4 }
- r: (a & b).c
- c: {args: s1 + s2}.args
- s1: string
- s2: string
- d: ({arg: b}).arg.c
- e: f.arg.c
- f: {arg: b}
- `
- testCases := []struct {
- config string
- in string
- out string
- }{
- {config1, "c.d", "a.b"},
- {config1, "c.e", "c.d"},
- {config1, "c.f", "a"},
+// func TestReferences(t *testing.T) {
+// config1 := `
+// a: {
+// b: 3
+// }
+// c: {
+// d: a.b
+// e: c.d
+// f: a
+// }
+// `
+// config2 := `
+// a: { c: 3 }
+// b: { c: int, d: 4 }
+// r: (a & b).c
+// c: {args: s1 + s2}.args
+// s1: string
+// s2: string
+// d: ({arg: b}).arg.c
+// e: f.arg.c
+// f: {arg: b}
+// `
+// testCases := []struct {
+// config string
+// in string
+// out string
+// }{
+// {config1, "c.d", "a.b"},
+// {config1, "c.e", "c.d"},
+// {config1, "c.f", "a"},
- {config2, "r", "a.c b.c"},
- {config2, "c", "s1 s2"},
- // {config2, "d", "b.c"}, // TODO: make this work as well.
- {config2, "e", "f.arg.c"}, // TODO: should also report b.c.
- }
- for _, tc := range testCases {
- t.Run(tc.in, func(t *testing.T) {
- ctx, st := compileFile(t, tc.config)
- v := newValueRoot(ctx, st)
- for _, k := range strings.Split(tc.in, ".") {
- obj, err := v.structValFull(ctx)
- if err != nil {
- t.Fatal(err)
- }
- v = obj.Lookup(k)
- }
- got := []string{}
- for _, r := range v.References() {
- got = append(got, strings.Join(r, "."))
- }
- want := strings.Split(tc.out, " ")
- if !reflect.DeepEqual(got, want) {
- t.Errorf("got %v; want %v", got, want)
- }
- })
- }
-}
+// {config2, "r", "a.c b.c"},
+// {config2, "c", "s1 s2"},
+// // {config2, "d", "b.c"}, // TODO: make this work as well.
+// {config2, "e", "f.arg.c"}, // TODO: should also report b.c.
+// }
+// for _, tc := range testCases {
+// t.Run(tc.in, func(t *testing.T) {
+// ctx, st := compileFile(t, tc.config)
+// v := newValueRoot(ctx, st)
+// for _, k := range strings.Split(tc.in, ".") {
+// obj, err := v.structValFull(ctx)
+// if err != nil {
+// t.Fatal(err)
+// }
+// v = obj.Lookup(k)
+// }
+// got := []string{}
+// for _, r := range v.References() {
+// got = append(got, strings.Join(r, "."))
+// }
+// want := strings.Split(tc.out, " ")
+// if !reflect.DeepEqual(got, want) {
+// t.Errorf("got %v; want %v", got, want)
+// }
+// })
+// }
+// }
func checkErr(t *testing.T, err error, str, name string) bool {
t.Helper()
@@ -2659,13 +2818,16 @@
input: "v: 3 + 4",
want: "+(3 4)",
}, {
- input: "v: !a, a: 3",
- want: `!(.(<0> "a"))`,
+ input: "v: !a, a: bool",
+ want: `!(.(〈〉 "a"))`,
+ }, {
+ input: "v: !a, a: 3", // TODO: Should still look up.
+ want: `!(.(〈〉 "a"))`,
}, {
input: "v: 1 | 2 | 3 | *4",
want: "|(1 2 3 4)",
}, {
- input: "v: 2 & 5",
+ input: "v: 2 & 5", // Allow even with error.
want: "&(2 5)",
}, {
input: "v: 2 | 5",
@@ -2681,7 +2843,7 @@
want: "==(2 5)",
}, {
input: "v: !b, b: true",
- want: `!(.(<0> "b"))`,
+ want: `!(.(〈〉 "b"))`,
}, {
input: "v: 2 != 5",
want: "!=(2 5)",
@@ -2729,31 +2891,31 @@
want: "mod(2 5)",
}, {
input: "v: a.b, a: b: 4",
- want: `.(.(<0> "a") "b")`,
+ want: `.(.(〈〉 "a") "b")`,
}, {
input: `v: a["b"], a: b: 3 `,
- want: `[](.(<0> "a") "b")`,
+ want: `[](.(〈〉 "a") "b")`,
}, {
input: "v: a[2:5], a: [1, 2, 3, 4, 5]",
- want: `[:](.(<0> "a") 2 5)`,
+ want: `[:](.(〈〉 "a") 2 5)`,
}, {
input: "v: len([])",
want: "()(len [])",
}, {
input: "v: a.b, a: { b: string }",
- want: `.(.(<0> "a") "b")`,
+ want: `.(.(〈〉 "a") "b")`,
}, {
input: `v: "Hello, \(x)! Welcome to \(place)", place: string, x: string`,
- want: `\()("Hello, " .(<0> "x") "! Welcome to " .(<0> "place") "")`,
+ want: `\()("Hello, " .(〈〉 "x") "! Welcome to " .(〈〉 "place") "")`,
}, {
input: `v: { a, b: 1 }, a: 2`,
- want: `&(<0>{b: 1} .(<0> "a"))`,
+ want: `&(.(〈〉 "a") {b:1})`,
}, {
input: `v: { {c: a}, b: a }, a: int`,
- want: `&(<0>{b: <1>.a} <0>{c: <1>.a})`,
+ want: `&({c:a} {b:a})`,
}, {
input: `v: [...number] | *[1, 2, 3]`,
- want: `([, ...number] | *[1,2,3])`,
+ want: `([...number]|*[1,2,3])`,
}}
for _, tc := range testCases {
t.Run(tc.input, func(t *testing.T) {
@@ -2765,10 +2927,11 @@
})
}
}
+
func exprStr(v Value) string {
op, operands := v.Expr()
if op == NoOp {
- return debugStr(v.ctx(), v.v.v)
+ return compactRawStr(v)
}
s := op.String()
s += "("
@@ -2781,3 +2944,15 @@
s += ")"
return s
}
+
+func compactRawStr(v Value) string {
+ ctx := v.ctx()
+ cfg := &debug.Config{Compact: true, Raw: true}
+ return debug.NodeString(ctx.opCtx, v.v, cfg)
+}
+
+func compactValueStr(v Value) string {
+ ctx := v.ctx()
+ cfg := &debug.Config{Compact: true}
+ return debug.NodeString(ctx.opCtx, v.v, cfg)
+}
diff --git a/cue/validate.go b/cue/validate.go
deleted file mode 100644
index f6890d4..0000000
--- a/cue/validate.go
+++ /dev/null
@@ -1,49 +0,0 @@
-// 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 cue
-
-import "cuelang.org/go/internal"
-
-// validate returns whether there is any error, recursively.
-func validate(ctx *context, v value) (err *bottom) {
- eval := v.evalPartial(ctx)
- if err, ok := eval.(*bottom); ok && err.Code != codeIncomplete && err.Code != codeCycle {
- return eval.(*bottom)
- }
- switch x := eval.(type) {
- case *structLit:
- x, err = x.expandFields(ctx)
- if err != nil {
- return err
- }
- if ctx.maxDepth++; ctx.maxDepth > internal.MaxDepth {
- return nil
- }
- for i, a := range x.Arcs {
- if a.optional {
- continue
- }
- if err := validate(ctx, x.at(ctx, i)); err != nil {
- ctx.maxDepth--
- return err
- }
- }
- ctx.maxDepth--
- case *list:
- // TODO: also validate types for open lists?
- return validate(ctx, x.elem)
- }
- return nil
-}
diff --git a/cue/value.go b/cue/value.go
deleted file mode 100644
index 29e81ff..0000000
--- a/cue/value.go
+++ /dev/null
@@ -1,1948 +0,0 @@
-// 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 cue
-
-import (
- "math/big"
- "regexp"
- "sort"
- "strconv"
- "time"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/literal"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal/core/adt"
-)
-
-type value interface {
- source
-
- rewrite(*context, rewriteFunc) value
-
- // evalPartial evaluates a value without choosing default values.
- evalPartial(*context) evaluated
-
- Kind() kind
-
- // subsumesImpl is only defined for non-reference types.
- // It should only be called by the subsumes function.
- subsumesImpl(*subsumer, value) bool
-}
-
-type evaluated interface {
- value
- binOp(*context, source, op, evaluated) evaluated
- strValue() string
-}
-
-type scope interface {
- value
- Lookup(*context, label) arc
-}
-
-type atter interface {
- // at returns the evaluated and its original value at the given position.
- // If the original could not be found, it returns an error and nil.
- at(*context, int) evaluated
-}
-
-type iterAtter interface {
- // at returns the evaluated and its original value at the given position.
- // If the original could not be found, it returns an error and nil.
- iterAt(*context, int) arc
-}
-
-// caller must be implemented by any concrete lambdaKind
-type caller interface {
- call(ctx *context, src source, args ...evaluated) value
- returnKind() kind
-}
-
-func checkKind(ctx *context, x value, want kind) *bottom {
- if b, ok := x.(*bottom); ok {
- return b
- }
- got := x.Kind()
- if got&want&concreteKind == bottomKind && want != bottomKind {
- return ctx.mkErr(x, "cannot use value %v (type %s) as %s", ctx.str(x), got, want)
- }
- if !got.isGround() {
- return ctx.mkErr(x, codeIncomplete,
- "non-concrete value %v", got)
- }
- return nil
-}
-
-func newDecl(n ast.Decl) baseValue {
- if n == nil {
- panic("empty node")
- }
- return baseValue{n}
-}
-
-func newExpr(n ast.Expr) baseValue {
- if n == nil {
- panic("empty node")
- }
- return baseValue{n}
-}
-
-func newNode(n ast.Node) baseValue {
- if n == nil {
- panic("empty node")
- }
- return baseValue{n}
-}
-
-type source interface {
- // syntax returns the parsed file of the underlying node or a computed
- // node indicating that it is a computed binary expression.
- Source() ast.Node
- computed() *computedSource
- Pos() token.Pos
- base() baseValue
-}
-
-type computedSource struct {
- pos token.Pos
- op op
- x value
- y value
-}
-
-func (s *computedSource) Pos() token.Pos {
- return s.pos
-}
-
-type posser interface {
- Pos() token.Pos
-}
-
-type baseValue struct {
- pos posser
-}
-
-func (b baseValue) Pos() token.Pos {
- if b.pos == nil {
- return token.NoPos
- }
- return b.pos.Pos()
-}
-
-func (b baseValue) computed() *computedSource {
- switch x := b.pos.(type) {
- case *computedSource:
- return x
- }
- return nil
-}
-
-func (b baseValue) Source() ast.Node {
- switch x := b.pos.(type) {
- case ast.Node:
- return x
- }
- return nil
-}
-
-func (b baseValue) base() baseValue {
- return b
-}
-
-func (b baseValue) strValue() string { panic("unimplemented") }
-func (b baseValue) returnKind() kind { panic("unimplemented") }
-
-// top is the top of the value lattice. It subsumes all possible values.
-type top struct{ baseValue }
-
-func (x *top) Kind() kind { return topKind }
-
-// basicType represents the root class of any specific type.
-type basicType struct {
- baseValue
- K kind
-}
-
-func (x *basicType) Kind() kind { return x.K | nonGround }
-
-// Literals
-
-type nullLit struct{ baseValue }
-
-func (x *nullLit) Kind() kind { return nullKind }
-
-type boolLit struct {
- baseValue
- B bool
-}
-
-func (x *boolLit) Kind() kind { return boolKind }
-
-func boolTonode(src source, b bool) evaluated {
- return &boolLit{src.base(), b}
-}
-
-type bytesLit struct {
- baseValue
- B []byte
- // Also support https://github.com/dlclark/regexp2 to
- // accommodate JSON Schema?
- RE *regexp.Regexp // only set if needed
-}
-
-func (x *bytesLit) Kind() kind { return bytesKind }
-func (x *bytesLit) strValue() string { return string(x.B) }
-
-func (x *bytesLit) iterAt(ctx *context, i int) arc {
- if i >= len(x.B) {
- return arc{}
- }
- v := x.at(ctx, i)
- return arc{v: v, Value: v}
-}
-
-func (x *bytesLit) at(ctx *context, i int) evaluated {
- if i < 0 || i >= len(x.B) {
- return ctx.mkErr(x, "index %d out of bounds", i)
- }
- // TODO: this is incorrect.
- return newInt(x, 0).setUInt64(uint64(x.B[i]))
-}
-
-func (x *bytesLit) len() int { return len(x.B) }
-
-func (x *bytesLit) slice(ctx *context, lo, hi *numLit) evaluated {
- lox := 0
- hix := len(x.B)
- if lo != nil {
- lox = lo.intValue(ctx)
- }
- if hi != nil {
- hix = hi.intValue(ctx)
- }
- if lox < 0 {
- return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", lox)
- }
- if hix < 0 {
- return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", hix)
- }
- if hix < lox {
- return ctx.mkErr(x, "invalid slice index: %d > %d", lox, hix)
- }
- if len(x.B) < hix {
- return ctx.mkErr(hi, "slice bounds out of range")
- }
- return &bytesLit{x.baseValue, x.B[lox:hix], nil}
-}
-
-type stringLit struct {
- baseValue
- Str string
- RE *regexp.Regexp // only set if needed
-
- // TODO: maintain extended grapheme index cache.
-}
-
-func (x *stringLit) Kind() kind { return stringKind }
-func (x *stringLit) strValue() string { return x.Str }
-
-func (x *stringLit) iterAt(ctx *context, i int) arc {
- runes := []rune(x.Str)
- if i >= len(runes) {
- return arc{}
- }
- v := x.at(ctx, i)
- return arc{v: v, Value: v}
-}
-
-func (x *stringLit) at(ctx *context, i int) evaluated {
- runes := []rune(x.Str)
- if i < 0 || i >= len(runes) {
- return ctx.mkErr(x, "index %d out of bounds", i)
- }
- // TODO: this is incorrect.
- return &stringLit{x.baseValue, string(runes[i : i+1]), nil}
-}
-func (x *stringLit) len() int { return len([]rune(x.Str)) }
-
-func (x *stringLit) slice(ctx *context, lo, hi *numLit) evaluated {
- runes := []rune(x.Str)
- lox := 0
- hix := len(runes)
- if lo != nil {
- lox = lo.intValue(ctx)
- }
- if hi != nil {
- hix = hi.intValue(ctx)
- }
- if lox < 0 {
- return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", lox)
- }
- if hix < 0 {
- return ctx.mkErr(x, "invalid slice index %d (must be non-negative)", hix)
- }
- if hix < lox {
- return ctx.mkErr(x, "invalid slice index: %d > %d", lox, hix)
- }
- if len(runes) < hix {
- return ctx.mkErr(hi, "slice bounds out of range")
- }
- return &stringLit{x.baseValue, string(runes[lox:hix]), nil}
-}
-
-type numLit struct {
- baseValue
- rep literal.Multiplier
- K kind
- X apd.Decimal
-}
-
-func newNum(src source, k kind, rep literal.Multiplier) *numLit {
- if k&numKind == 0 {
- panic("not a number")
- }
- return &numLit{baseValue: src.base(), rep: rep, K: k}
-}
-
-func newInt(src source, rep literal.Multiplier) *numLit {
- return newNum(src, intKind, rep)
-}
-
-func newFloat(src source, rep literal.Multiplier) *numLit {
- return newNum(src, floatKind, rep)
-}
-
-func (n numLit) specialize(k kind) *numLit {
- n.K = k
- return &n
-}
-
-func (n *numLit) set(d *apd.Decimal) *numLit {
- n.X.Set(d)
- return n
-}
-
-func (n *numLit) setInt(x int) *numLit {
- n.X.SetInt64(int64(x))
- return n
-}
-
-func (n *numLit) setInt64(x int64) *numLit {
- n.X.SetInt64(x)
- return n
-}
-
-func (n *numLit) setUInt64(x uint64) *numLit {
- n.X.Coeff.SetUint64(x)
- return n
-}
-
-func (n *numLit) setString(s string) *numLit {
- _, _, _ = n.X.SetString(s)
- return n
-}
-
-func (n *numLit) String() string {
- if n.K&intKind != 0 {
- return n.X.Text('f') // also render info
- }
- s := n.X.Text('g')
- if len(s) == 1 {
- s += "."
- }
- return s // also render info
-}
-
-func parseInt(k kind, s string) *numLit {
- num := newInt(newExpr(ast.NewLit(token.INT, s)), 0)
- _, _, err := num.X.SetString(s)
- if err != nil {
- panic(err)
- }
- return num
-}
-
-func parseFloat(s string) *numLit {
- num := newFloat(newExpr(ast.NewLit(token.FLOAT, s)), 0)
- _, _, err := num.X.SetString(s)
- if err != nil {
- panic(err)
- }
- return num
-}
-
-var ten = big.NewInt(10)
-
-var one = parseInt(intKind, "1")
-
-func (x *numLit) Kind() kind { return x.K }
-func (x *numLit) strValue() string { return x.X.String() }
-
-func (x *numLit) isInt(ctx *context) bool {
- return x.Kind()&intKind != 0
-}
-
-func (x *numLit) intValue(ctx *context) int {
- v, err := x.X.Int64()
- if err != nil {
- return 0
- }
- return int(v)
-}
-
-type durationLit struct {
- baseValue
- d time.Duration
-}
-
-func (x *durationLit) Kind() kind { return durationKind }
-func (x *durationLit) strValue() string { return x.d.String() }
-
-type bound struct {
- baseValue
- Op op // opNeq, opLss, opLeq, opGeq, or opGtr
- k kind // mostly used for number kind
- Expr value
-}
-
-func newBound(ctx *context, base baseValue, op op, k kind, v value) evaluated {
- kv := v.Kind()
- if kv.isAnyOf(numKind) {
- kv |= numKind
- } else if op == opNeq && kv&atomKind == nullKind {
- kv = typeKinds &^ nullKind
- }
- if op == opMat || op == opNMat {
- v = compileRegexp(ctx, v)
- if isBottom(v) {
- return v.(*bottom)
- }
- }
- return &bound{base, op, unifyType(k&topKind, kv) | nonGround, v}
-}
-
-func (x *bound) Kind() kind {
- return x.k
-}
-
-func mkIntRange(a, b string) evaluated {
- from := newBound(nil, baseValue{}, opGeq, intKind, parseInt(intKind, a))
- to := newBound(nil, baseValue{}, opLeq, intKind, parseInt(intKind, b))
- e := &unification{
- binSrc(token.NoPos, opUnify, from, to),
- []evaluated{from, to},
- }
- // TODO: make this an integer
- // int := &basicType{k: intKind}
- // e = &unification{
- // binSrc(token.NoPos, opUnify, int, e),
- // []evaluated{int, e},
- // }
- return e
-}
-
-func mkFloatRange(a, b string) evaluated {
- from := newBound(nil, baseValue{}, opGeq, numKind, parseFloat(a))
- to := newBound(nil, baseValue{}, opLeq, numKind, parseFloat(b))
- e := &unification{
- binSrc(token.NoPos, opUnify, from, to),
- []evaluated{from, to},
- }
- // TODO: make this an integer
- // int := &basicType{k: intKind}
- // e = &unification{
- // binSrc(token.NoPos, opUnify, int, e),
- // []evaluated{int, e},
- // }
- return e
-}
-
-var predefinedRanges = map[string]evaluated{
- "rune": mkIntRange("0", strconv.Itoa(0x10FFFF)),
- "int8": mkIntRange("-128", "127"),
- "int16": mkIntRange("-32768", "32767"),
- "int32": mkIntRange("-2147483648", "2147483647"),
- "int64": mkIntRange("-9223372036854775808", "9223372036854775807"),
- "int128": mkIntRange(
- "-170141183460469231731687303715884105728",
- "170141183460469231731687303715884105727"),
-
- // Do not include an alias for "byte", as it would be too easily confused
- // with the builtin "bytes".
- "uint": newBound(nil, baseValue{}, opGeq, intKind, parseInt(intKind, "0")),
- "uint8": mkIntRange("0", "255"),
- "uint16": mkIntRange("0", "65535"),
- "uint32": mkIntRange("0", "4294967295"),
- "uint64": mkIntRange("0", "18446744073709551615"),
- "uint128": mkIntRange("0", "340282366920938463463374607431768211455"),
-
- // 2**127 * (2**24 - 1) / 2**23
- "float32": mkFloatRange(
- "-3.40282346638528859811704183484516925440e+38",
- "+3.40282346638528859811704183484516925440e+38",
- ),
- // 2**1023 * (2**53 - 1) / 2**52
- "float64": mkFloatRange(
- "-1.797693134862315708145274237317043567981e+308",
- "+1.797693134862315708145274237317043567981e+308",
- ),
-}
-
-type interpolation struct {
- baseValue
- K kind // string or bytes
- Parts []value // odd: strings, even expressions
-}
-
-func (x *interpolation) Kind() kind { return x.K | nonGround }
-
-type list struct {
- baseValue
- elem *structLit
-
- typ value
-
- // TODO: consider removing len. Currently can only be len(a) or >= len(a)
- // and could be replaced with a bool.
- len value
-}
-
-// initLit initializes a literal list.
-func (x *list) initLit() {
- x.len = newInt(x, 0).setInt(len(x.elem.Arcs))
- x.typ = &top{x.baseValue}
-}
-
-func (x *list) manifest(ctx *context) evaluated {
- if x.Kind().isGround() {
- return x
- }
- // A list is ground if its length is ground, or if the current length
- // meets matches the cap.
- n := newInt(x, 0).setInt(len(x.elem.Arcs))
- if n := binOp(ctx, x, opUnify, n, x.len.evalPartial(ctx)); !isBottom(n) {
- return &list{
- baseValue: x.baseValue,
- elem: x.elem,
- len: n,
- typ: &top{x.baseValue},
- }
- }
- return x
-}
-
-func (x *list) Kind() kind {
- k := listKind
- if _, ok := x.len.(*numLit); ok {
- return k
- }
- return k | nonGround
-}
-
-// at returns the evaluated and original value of position i. List x must
-// already have been evaluated. It returns an error and nil if there was an
-// issue evaluating the list itself.
-func (x *list) at(ctx *context, i int) evaluated {
- arc := x.iterAt(ctx, i)
- if arc.Value == nil {
- return ctx.mkErr(x, "index %d out of bounds", i)
- }
- return arc.Value
-}
-
-// iterAt returns the evaluated and original value of position i. List x must
-// already have been evaluated. It returns an error and nil if there was an
-// issue evaluating the list itself.
-func (x *list) iterAt(ctx *context, i int) arc {
- if i < 0 {
- v := ctx.mkErr(x, "index %d out of bounds", i)
- return arc{Value: v}
- }
- if i < len(x.elem.Arcs) {
- a := x.elem.iterAt(ctx, i)
- a.Label = 0
- return a
- }
- max := maxNum(x.len.(evaluated))
- if max.Kind().isGround() {
- if max.Kind()&intKind == bottomKind {
- v := ctx.mkErr(max, "length indicator of list not of type int")
- return arc{Value: v}
- }
- n := max.(*numLit).intValue(ctx)
- if i >= n {
- return arc{}
- }
- }
- return arc{Value: x.typ.evalPartial(ctx), v: x.typ}
-}
-
-func (x *list) isOpen() bool {
- return !x.len.Kind().isGround()
-}
-
-// lo and hi must be nil or a ground integer.
-func (x *list) slice(ctx *context, lo, hi *numLit) evaluated {
- a := x.elem.Arcs
- max := maxNum(x.len).evalPartial(ctx)
- if hi != nil {
- n := hi.intValue(ctx)
- if n < 0 {
- return ctx.mkErr(x, "negative slice index")
- }
- if max.Kind().isGround() && !leq(ctx, hi, hi, max) {
- return ctx.mkErr(hi, "slice bounds out of range")
- }
- max = hi
- if n < len(a) {
- a = a[:n]
- }
- }
-
- if lo != nil {
- n := lo.intValue(ctx)
- if n < 0 {
- return ctx.mkErr(x, "negative slice index")
- }
- if n > 0 && max.Kind().isGround() {
- if !leq(ctx, lo, lo, max) {
- max := max.(*numLit).intValue(ctx)
- return ctx.mkErr(x, "invalid slice index: %v > %v", n, max)
- }
- max = binOp(ctx, lo, opSub, max, lo)
- }
- if n < len(a) {
- a = a[n:]
- } else {
- a = []arc{}
- }
- }
- arcs := make([]arc, len(a))
- for i, a := range a {
- arcs[i] = arc{Label: label(i), v: a.v, docs: a.docs}
- }
- s := &structLit{baseValue: x.baseValue, Arcs: arcs}
- return &list{baseValue: x.baseValue, elem: s, typ: x.typ, len: max}
-}
-
-// An structLit is a single structLit in the configuration tree.
-//
-// An structLit may have multiple arcs. There may be only one arc per label. Use
-// insertRaw to insert arcs to ensure this invariant holds.
-type structLit struct {
- baseValue
-
- // TODO(perf): separate out these infrequent values to save space.
- emit value // currently only supported at top level.
- // TODO: make this a list of templates and don't unify until templates are
- // applied. This allows generalization of having different constraints
- // for different field sets. This could also be used to mark closedness:
- // use [string]: _ for fully open. This could be a sentinel value.
- // For now we use a boolean for closedness.
-
- // NOTE: must be conjunction of lists.
- // For lists originating from closed structs,
- // there must be at least one match.
- // templates [][]value
- // catch_all: value
-
- // optionals holds pattern-constraint pairs that
- // are applied to all concrete values in this struct.
- optionals *optionals
- closeStatus closeMode
-
- comprehensions []compValue
-
- // TODO: consider hoisting the template arc to its own value.
- Arcs []arc
- expanded evaluated
-}
-
-// compValue is a temporary stop-gap until the correct unification algorithm is
-// implemented. This implementation is more strict than should be. When two
-// structs, of which at least one is closed, are unified, the fields resolving
-// later from unresolved comprehensions should match the closedness constraints.
-// To relax this constraint, unification could follow the lines of
-// traditional unification with bookkeeping of which fields are
-// allowed, to be applied as constraints after full unification.
-
-type compValue struct {
- checked bool
- comp value
-}
-
-// optionals holds a set of key pattern-constraint pairs, where constraints are
-// to be applied to concrete fields of which the label matches the key pattern.
-//
-// optionals will either hold concrete fields or a couple of nested optional
-// structs combined based on the op type, but not both.
-type optionals struct {
- closed closeMode
- op op
- left *optionals // nil means empty closed struct
- right *optionals // nil means empty closed struct
- fields []optionalSet
-}
-
-type optionalSet struct {
- // A key filter may be nil, in which case it means all strings, or _.
- key value
-
- // constraint must evaluate to a lambda and is applied to any concrete
- // value for which the key matches key.
- value value
-}
-
-func newOptional(key, value value) *optionals {
- return &optionals{
- fields: []optionalSet{{key, value}},
- }
-}
-
-// isClosed mirrors the closed status of the struct to which
-// this optionals belongs.
-func (o *optionals) isClosed() bool {
- if o == nil {
- return true
- }
- return o.closed.isClosed()
-}
-
-func (o *optionals) close() *optionals {
- if o == nil {
- return nil
- }
- o.closed |= isClosed
- return o
-}
-
-// isEmpty reports whether this optionals may report true for match. Even if an
-// optionals is empty, it may still hold constraints to be applied to already
-// existing concrete fields.
-func (o *optionals) isEmpty() bool {
- if o == nil {
- return true
- }
- le := o.left.isEmpty()
- re := o.right.isEmpty()
-
- if o.op == opUnify {
- if le && o.left.isClosed() {
- return true
- }
- if re && o.right.isClosed() {
- return true
- }
- }
- return le && re && len(o.fields) == 0
-}
-
-// isFull reports whether match reports true for all fields.
-func (o *optionals) isFull() bool {
- found, _ := o.match(nil, nil)
- return found
-}
-
-// match reports whether a field with the given name may be added in the
-// associated struct as a new field. ok is false if there was any closed
-// struct that failed to match. Even if match returns false, there may still be
-// constraints represented by optionals that are to be applied to existing
-// concrete fields.
-func (o *optionals) match(ctx *context, str *stringLit) (found, ok bool) {
- if o == nil {
- return false, true
- }
-
- found1, ok := o.left.match(ctx, str)
- if !ok && o.op == opUnify {
- return false, false
- }
-
- found2, ok := o.right.match(ctx, str)
- if !ok && o.op == opUnify {
- return false, false
- }
-
- if found1 || found2 {
- return true, true
- }
-
- for _, f := range o.fields {
- if f.key == nil {
- return true, true
- }
- if str != nil {
- v := binOp(ctx, f.value, opUnify, f.key.evalPartial(ctx), str)
- if !isBottom(v) {
- return true, true
- }
- }
- }
-
- return false, !o.closed.isClosed()
-}
-
-func (o *optionals) allows(ctx *context, f label) bool {
- if o == nil {
- return false
- }
- if f.IsDef() || f.IsHidden() {
- return false
- }
-
- str := ctx.LabelStr(f)
- arg := &stringLit{Str: str}
-
- found, ok := o.match(ctx, arg)
- return found && ok
-}
-
-func (o *optionals) add(ctx *context, key, value value) {
- for i, b := range o.fields {
- if b.key == key {
- o.fields[i].value = mkBin(ctx, token.NoPos, opUnify, b.value, value)
- return
- }
- }
- o.fields = append(o.fields, optionalSet{key, value})
-}
-
-// isDotDotDot reports whether optionals only contains fully-qualified
-// constraints. This is useful for some optimizations.
-func (o *optionals) isDotDotDot() bool {
- if o == nil {
- return false
- }
- if len(o.fields) > 1 {
- return false
- }
- if len(o.fields) == 1 {
- f := o.fields[0]
- if f.key != nil {
- return false
- }
- lambda, ok := f.value.(*lambdaExpr)
- if ok {
- if _, ok = lambda.value.(*top); ok {
- return true
- }
- }
- return false
- }
- if o.left == nil {
- return o.right.isDotDotDot()
- }
- if o.right == nil {
- return o.left.isDotDotDot()
- }
- return o.left.isDotDotDot() && o.right.isDotDotDot()
-}
-
-// constraint returns the unification of all constraints for which arg matches
-// the key filter. doc contains the documentation of all applicable fields.
-func (o *optionals) constraint(ctx *context, label evaluated) (u value, doc *docNode) {
- if o == nil {
- return nil, nil
- }
- add := func(v value) {
- if v != nil {
- if u == nil {
- u = v
- } else {
- u = mkBin(ctx, token.NoPos, opUnify, u, v)
- }
- }
- }
- v, doc1 := o.left.constraint(ctx, label)
- add(v)
- v, doc2 := o.right.constraint(ctx, label)
- add(v)
-
- if doc1 != nil || doc2 != nil {
- doc = &docNode{left: doc1, right: doc2}
- }
-
- arg := label
- if arg == nil {
- arg = &basicType{K: stringKind}
- }
-
- for _, s := range o.fields {
- if s.key != nil {
- if label == nil {
- continue
- }
- key := s.key.evalPartial(ctx)
- if v := binOp(ctx, label, opUnify, key, label); isBottom(v) {
- continue
- }
- }
- fn, ok := ctx.manifest(s.value).(*lambdaExpr)
- if !ok {
- // create error
- continue
- }
- add(fn.call(ctx, s.value, arg))
- if f, _ := s.value.base().Source().(*ast.Field); f != nil {
- doc = &docNode{n: f, left: doc}
- }
- }
- return u, doc
-}
-
-func (o *optionals) rewrite(fn func(value) value) (c *optionals, err evaluated) {
- if o == nil {
- return nil, nil
- }
-
- left, err := o.left.rewrite(fn)
- if err != nil {
- return nil, err
- }
- right, err := o.right.rewrite(fn)
- if err != nil {
- return nil, err
- }
-
- fields := make([]optionalSet, len(o.fields))
- for i, s := range o.fields {
- if s.key != nil {
- s.key = fn(s.key)
- if b, ok := s.key.(*bottom); ok {
- return nil, b
- }
- }
- s.value = fn(s.value)
- if b, ok := s.value.(*bottom); ok {
- return nil, b
- }
- fields[i] = s
- }
-
- return &optionals{o.closed, o.op, left, right, fields}, nil
-}
-
-type closeMode byte
-
-const (
- shouldFinalize closeMode = 1 << iota
- toClose
- isClosed
-)
-
-func (m closeMode) shouldFinalize() bool {
- return m&shouldFinalize != 0
-}
-
-func (m *closeMode) unclose() {
- *m &^= (toClose | isClosed)
-}
-
-func (m closeMode) isClosed() bool {
- return m&isClosed != 0
-}
-
-func (m closeMode) shouldClose() bool {
- return m >= toClose
-}
-
-func (x *structLit) isClosed() bool {
- return x.closeStatus.isClosed()
-}
-
-func (x *structLit) addTemplate(ctx *context, pos token.Pos, key, value value) {
- if x.optionals == nil {
- x.optionals = &optionals{}
- }
- x.optionals.add(ctx, key, value)
-}
-
-func (x *structLit) allows(ctx *context, f label) bool {
- return !x.closeStatus.isClosed() ||
- f.IsHidden() ||
- x.optionals.allows(ctx, f)
-}
-
-func newStruct(src source) *structLit {
- return &structLit{baseValue: src.base()}
-}
-
-func (x *structLit) Kind() kind { return structKind }
-
-type arcs []arc
-
-func (x *structLit) Len() int { return len(x.Arcs) }
-func (x *structLit) Less(i, j int) bool { return x.Arcs[i].Label < x.Arcs[j].Label }
-func (x *structLit) Swap(i, j int) { x.Arcs[i], x.Arcs[j] = x.Arcs[j], x.Arcs[i] }
-
-func (x *structLit) close() *structLit {
- if x.optionals.isFull() {
- return x
- }
-
- newS := *x
- newS.closeStatus = isClosed
- return &newS
-}
-
-// lookup returns the node for the given label f, if present, or nil otherwise.
-func (x *structLit) Lookup(ctx *context, f label) arc {
- x, err := x.expandFields(ctx)
- if err != nil {
- return arc{}
- }
- // Lookup is done by selector or index references. Either this is done on
- // literal nodes or nodes obtained from references. In the later case,
- // noderef will have ensured that the ancestors were evaluated.
- for i, a := range x.Arcs {
- if a.Label == f {
- a := x.iterAt(ctx, i)
- // TODO: adding more technical debt here. The evaluator should be
- // rewritten.
- if x.optionals != nil {
- name := ctx.LabelStr(x.Arcs[i].Label)
- arg := &stringLit{x.baseValue, name, nil}
-
- val, _ := x.optionals.constraint(ctx, arg)
- if val != nil {
- a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
- }
- }
- return a
- }
- }
- return arc{}
-}
-
-func (x *structLit) iterAt(ctx *context, i int) arc {
- x, err := x.expandFields(ctx)
- if err != nil || i >= len(x.Arcs) {
- return arc{}
- }
- a := x.Arcs[i]
- a.Value = x.at(ctx, i) // TODO: return template & v for original?
- return a
-}
-
-func (x *structLit) at(ctx *context, i int) evaluated {
- // TODO: limit visibility of definitions:
- // Approach:
- // - add package identifier to arc (label)
- // - assume ctx is unique for a package
- // - record package identifier in context
- // - if arc is a definition, check IsExported and verify the package if not.
- //
- // The same approach could be valid for looking up package-level identifiers.
- // - detect somehow aht root nodes are.
- //
- // Allow import of CUE files. These cannot have a package clause.
-
- var err *bottom
-
- // Lookup is done by selector or index references. Either this is done on
- // literal nodes or nodes obtained from references. In the later case,
- // noderef will have ensured that the ancestors were evaluated.
- if v := x.Arcs[i].Value; v == nil {
-
- // cycle detection
-
- popped := ctx.evalStack
- ctx.evalStack = append(ctx.evalStack, bottom{
- baseValue: x.base(),
- index: ctx.index,
- Code: codeCycle,
- value: x.Arcs[i].v,
- format: "cycle detected",
- })
- x.Arcs[i].Value = &(ctx.evalStack[len(ctx.evalStack)-1])
-
- v := x.Arcs[i].v.evalPartial(ctx)
- ctx.evalStack = popped
-
- var doc *docNode
- v, doc = x.applyTemplate(ctx, i, v)
- // only place to apply template?
-
- if (len(ctx.evalStack) > 0 && ctx.cycleErr) || cycleError(v) != nil {
- // Don't cache while we're in a evaluation cycle as it will cache
- // partial results. Each field involved in the cycle will have to
- // reevaluated the values from scratch. As the result will be
- // cached after one cycle, it will evaluate the cycle at most twice.
- x.Arcs[i].Value = nil
- return v
- }
-
- // If there as a cycle error, we have by now evaluated a full cycle and
- // it is safe to cache the result.
- ctx.cycleErr = false
-
- v = updateCloseStatus(ctx, v)
- if st, ok := v.(*structLit); ok {
- v, err = st.expandFields(ctx)
- if err != nil {
- v = err
- }
- }
- x.Arcs[i].Value = v
- if doc != nil {
- x.Arcs[i].docs = &docNode{left: doc, right: x.Arcs[i].docs}
- }
- if len(ctx.evalStack) == 0 {
- if err := ctx.processDelayedConstraints(); err != nil {
- x.Arcs[i].Value = err
- }
- }
- } else if b := cycleError(v); b != nil {
- copy := *b
- return ©
- }
- return x.Arcs[i].Value
-}
-
-// expandFields merges in embedded and interpolated fields.
-// Such fields are semantically equivalent to child values, and thus
-// should not be evaluated until the other fields of a struct are
-// fully evaluated.
-func (x *structLit) expandFields(ctx *context) (st *structLit, err *bottom) {
- switch v := x.expanded.(type) {
- case nil:
- case *structLit:
- return v, nil
- default:
- return nil, x.expanded.(*bottom)
- }
- if len(x.comprehensions) == 0 {
- x.expanded = x
- return x, nil
- }
-
- x.expanded = x
-
- comprehensions := x.comprehensions
-
- var incomplete []compValue
-
- var n evaluated = &top{x.baseValue}
- if x.emit != nil {
- n = x.emit.evalPartial(ctx)
- }
-
- var checked evaluated = &top{x.baseValue}
-
- for _, x := range comprehensions {
- v := x.comp.evalPartial(ctx)
- if v, ok := v.(*bottom); ok {
- if isIncomplete(v) {
- incomplete = append(incomplete, x)
- continue
- }
-
- return nil, v
- }
- src := binSrc(x.comp.Pos(), opUnify, x.comp, v)
- _ = checked
- if x.checked {
- checked = binOp(ctx, src, opUnifyUnchecked, checked, v)
- } else {
- n = binOp(ctx, src, opUnifyUnchecked, n, v)
- }
- }
- if len(comprehensions) == len(incomplete) {
- return x, nil
- }
-
- switch n.(type) {
- case *bottom, *top:
- default:
- orig := x.comprehensions
- x.comprehensions = incomplete
- src := binSrc(x.Pos(), opUnify, x, n)
- n = binOp(ctx, src, opUnifyUnchecked, x, n)
- x.comprehensions = orig
- }
-
- switch checked.(type) {
- case *bottom, *top:
- default:
- orig := x.comprehensions
- x.comprehensions = incomplete
- src := binSrc(x.Pos(), opUnify, n, checked)
- n = binOp(ctx, src, opUnify, x, checked)
- x.comprehensions = orig
- }
-
- switch v := n.(type) {
- case *bottom:
- x.expanded = n
- return nil, v
- case *structLit:
- x.expanded = n
- return v, nil
-
- default:
- x.expanded = x
- return x, nil
- }
-}
-
-func (x *structLit) applyTemplate(ctx *context, i int, v evaluated) (e evaluated, doc *docNode) {
- if x.optionals == nil {
- return v, nil
- }
-
- if f := x.Arcs[i].Label; !f.IsHidden() && !f.IsDef() {
- name := ctx.LabelStr(x.Arcs[i].Label)
- arg := &stringLit{x.baseValue, name, nil}
-
- var val value
- val, doc = x.optionals.constraint(ctx, arg)
- if val != nil {
- v = binOp(ctx, x, opUnify, v, val.evalPartial(ctx))
- }
- }
-
- if x.closeStatus != 0 {
- v = updateCloseStatus(ctx, v)
- }
- return v, doc
-}
-
-// A label is a canonicalized feature name.
-type label = adt.Feature
-
-// An arc holds the label-value pair.
-//
-// A fully evaluated arc has either a node or a value. An unevaluated arc,
-// however, may have both. In this case, the value must ultimately evaluate
-// to a node, which will then be merged with the existing one.
-type arc struct {
- Label label
- optional bool
- definition bool // field is a definition
-
- // TODO: add index to preserve approximate order within a struct and use
- // topological sort to compute new struct order when unifying. This could
- // also be achieved by not sorting labels on features and doing
- // a linear search in fields.
-
- v value
- Value evaluated // also used as newValue during unification.
- attrs *attributes
- docs *docNode
-}
-
-type docNode struct {
- n *ast.Field
- left *docNode
- right *docNode
-}
-
-func (d *docNode) appendDocs(docs []*ast.CommentGroup) []*ast.CommentGroup {
- if d == nil {
- return docs
- }
- docs = d.left.appendDocs(docs)
- if d.n != nil {
- docs = appendDocComments(docs, d.n)
- docs = appendDocComments(docs, d.n.Label)
- }
- docs = d.right.appendDocs(docs)
- return docs
-}
-
-func appendDocComments(docs []*ast.CommentGroup, n ast.Node) []*ast.CommentGroup {
- for _, c := range n.Comments() {
- if c.Doc {
- docs = append(docs, c)
- }
- }
- return docs
-}
-
-func mergeDocs(a, b *docNode) *docNode {
- if a == b || a == nil {
- return b
- }
- if b == nil {
- return a
- }
- // TODO: filter out duplicates?
- return &docNode{nil, a, b}
-}
-
-func (a *arc) val() evaluated {
- return a.Value
-}
-
-func (a *arc) setValue(v value) {
- a.v = v
- a.Value = nil
-}
-
-type closeIfStruct struct {
- value
-}
-
-func wrapFinalize(ctx *context, v value) value {
- if v.Kind().isAnyOf(structKind | listKind) {
- switch x := v.(type) {
- case *top:
- return v
- case *structLit:
- v = updateCloseStatus(ctx, x)
- case *list:
- v = updateCloseStatus(ctx, x)
- case *disjunction:
- v = updateCloseStatus(ctx, x)
- case *closeIfStruct:
- return x
- }
- return &closeIfStruct{v}
- }
- return v
-}
-
-func updateCloseStatus(ctx *context, v evaluated) evaluated {
- switch x := v.(type) {
- case *structLit:
- if x.closeStatus.shouldClose() {
- x.closeStatus = isClosed
- x.optionals = x.optionals.close()
- }
- x.closeStatus |= shouldFinalize
- return x
-
- case *disjunction:
- for _, d := range x.Values {
- d.Val = wrapFinalize(ctx, d.Val)
- }
-
- case *list:
- wrapFinalize(ctx, x.elem)
- if x.typ != nil {
- wrapFinalize(ctx, x.typ)
- }
- }
- return v
-}
-
-// insertValue is used during initialization but never during evaluation.
-func (x *structLit) insertValue(ctx *context, f label, optional, isDef bool, value value, a *attributes, docs *docNode) {
- for i, p := range x.Arcs {
- if f != p.Label {
- continue
- }
- x.Arcs[i].optional = x.Arcs[i].optional && optional
- x.Arcs[i].docs = mergeDocs(x.Arcs[i].docs, docs)
- x.Arcs[i].v = mkBin(ctx, token.NoPos, opUnify, p.v, value)
- if isDef != p.definition {
- src := binSrc(token.NoPos, opUnify, p.v, value)
- x.Arcs[i].v = ctx.mkErr(src,
- "field %q declared as definition and regular field",
- ctx.LabelStr(f))
- isDef = false
- }
- x.Arcs[i].definition = isDef
- attrs, err := unifyAttrs(ctx, x, x.Arcs[i].attrs, a)
- if err != nil {
- x.Arcs[i].v = err
- }
- x.Arcs[i].attrs = attrs
- // TODO: should we warn if there is a mixed mode of optional and non
- // optional fields at this point?
- return
- }
- x.Arcs = append(x.Arcs, arc{f, optional, isDef, value, nil, a, docs})
- sort.Stable(x)
-}
-
-// A nodeRef is a reference to a node.
-type nodeRef struct {
- baseValue
- node scope
- label label // for direct ancestor nodes
-}
-
-func (x *nodeRef) Kind() kind {
- // TODO(REWORK): no context available
- // n := x.node.deref(nil)
- n := x.node
- return n.Kind() | nonGround | referenceKind
-}
-
-type selectorExpr struct {
- baseValue
- X value
- Sel label
-}
-
-// TODO: could this be narrowed down?
-func (x *selectorExpr) Kind() kind {
- isRef := x.X.Kind() & referenceKind
- return topKind | isRef
-}
-
-type indexExpr struct {
- baseValue
- X value
- Index value
-}
-
-// TODO: narrow this down when we have list types.
-func (x *indexExpr) Kind() kind { return topKind | referenceKind }
-
-type sliceExpr struct {
- baseValue
- X value
- Lo value
- Hi value
-}
-
-// TODO: narrow this down when we have list types.
-func (x *sliceExpr) Kind() kind { return topKind | referenceKind }
-
-type callExpr struct {
- baseValue
- Fun value
- Args []value
-}
-
-func (x *callExpr) Kind() kind {
- // TODO: could this be narrowed down?
- switch c := x.Fun.(type) {
- case *lambdaExpr:
- return c.returnKind() | nonGround
- case *builtin:
- switch len(x.Args) {
- case len(c.Params):
- return c.Result
- case len(c.Params) - 1:
- if len(c.Params) == 0 || c.Result&boolKind == 0 {
- return bottomKind
- }
- return c.Params[0]
- }
- }
- return topKind | referenceKind
-}
-
-type customValidator struct {
- baseValue
-
- Builtin *builtin // function must return a bool
- Args []evaluated // any but the first value
-}
-
-func (x *customValidator) Kind() kind {
- if len(x.Builtin.Params) == 0 {
- return bottomKind
- }
- return x.Builtin.Params[0] | nonGround
-}
-
-type params struct {
- arcs []arc
-}
-
-func (x *params) add(f label, v value) {
- if v == nil {
- panic("nil node")
- }
- x.arcs = append(x.arcs, arc{Label: f, v: v})
-}
-
-func (x *params) iterAt(ctx *context, i int) (evaluated, value) {
- if i >= len(x.arcs) {
- return nil, nil
- }
- return x.at(ctx, i), x.arcs[i].v
-}
-
-// lookup returns the node for the given label f, if present, or nil otherwise.
-func (x *params) at(ctx *context, i int) evaluated {
- // Lookup is done by selector or index references. Either this is done on
- // literal nodes or nodes obtained from references. In the later case,
- // noderef will have ensured that the ancestors were evaluated.
- if x.arcs[i].Value == nil {
- x.arcs[i].Value = x.arcs[i].v.evalPartial(ctx)
- }
- return x.arcs[i].Value
-}
-
-// lookup returns the node for the given label f, if present, or nil otherwise.
-func (x *params) Lookup(ctx *context, f label) arc {
- if f == 0 && len(x.arcs) == 1 {
- // A template binding.
- a := x.arcs[0]
- a.Value = x.at(ctx, 0)
- return a
- }
- // Lookup is done by selector or index references. Either this is done on
- // literal nodes or nodes obtained from references. In the later case,
- // noderef will have ensured that the ancestors were evaluated.
- for i, a := range x.arcs {
- if a.Label == f {
- a.Value = x.at(ctx, i)
- return a
- }
- }
- return arc{}
-}
-
-type lambdaExpr struct {
- baseValue
- *params
- value value
-}
-
-// TODO: could this be narrowed down?
-func (x *lambdaExpr) Kind() kind { return lambdaKind }
-func (x *lambdaExpr) returnKind() kind { return x.value.Kind() }
-
-// call calls and evaluates a lambda expression. It is assumed that x may be
-// destroyed, either because it is copied as a result of a reference or because
-// it is invoked as a literal.
-func (x *lambdaExpr) call(ctx *context, p source, args ...evaluated) value {
- // fully evaluated.
- if len(x.params.arcs) != len(args) {
- return ctx.mkErr(p, x, "number of arguments does not match (%d vs %d)",
- len(x.params.arcs), len(args))
- }
-
- // force parameter substitution. It is important that the result stands on
- // its own and does not depend on its input parameters.
- arcs := make(arcs, len(x.arcs))
- for i, a := range x.arcs {
- v := binOp(ctx, p, opUnify, a.v.evalPartial(ctx), args[i])
- if isBottom(v) {
- return v
- }
- arcs[i] = arc{Label: a.Label, v: v, Value: v}
- }
- lambda := &lambdaExpr{x.baseValue, ¶ms{arcs}, nil}
- defer ctx.pushForwards(x, lambda).popForwards()
- obj := ctx.copy(x.value)
- return obj
-}
-
-// Operations
-
-type unaryExpr struct {
- baseValue
- Op op
- X value
-}
-
-func (x *unaryExpr) Kind() kind { return x.X.Kind() }
-
-func compileRegexp(ctx *context, v value) value {
- var err error
- switch x := v.(type) {
- case *stringLit:
- if x.RE == nil {
- x.RE, err = regexp.Compile(x.Str)
- if err != nil {
- return ctx.mkErr(v, "could not compile regular expression %q: %v", x.Str, err)
- }
- }
- case *bytesLit:
- if x.RE == nil {
- x.RE, err = regexp.Compile(string(x.B))
- if err != nil {
- return ctx.mkErr(v, "could not compile regular expression %q: %v", x.B, err)
- }
- }
- }
- return v
-}
-
-type binaryExpr struct {
- baseValue
- Op op
- X value
- Y value
-}
-
-func mkBin(ctx *context, pos token.Pos, op op, left, right value) value {
- if left == nil || right == nil {
- panic("operands may not be nil")
- }
- if op == opUnify {
- if left == right {
- return left
- }
- if _, ok := left.(*top); ok {
- return right
- }
- if _, ok := right.(*top); ok {
- return left
- }
- // TODO(perf): consider adding a subsumption filter.
- // if subsumes(ctx, left, right) {
- // return right
- // }
- // if subsumes(ctx, right, left) {
- // return left
- // }
- }
- bin := &binaryExpr{binSrc(pos, op, left, right), op, left, right}
- return updateBin(ctx, bin)
-}
-
-func updateBin(ctx *context, bin *binaryExpr) value {
- switch bin.Op {
- case opMat, opNMat:
- bin.Y = compileRegexp(ctx, bin.Y)
- if isBottom(bin.Y) {
- return bin.Y
- }
- }
- return bin
-}
-
-func (x *binaryExpr) Kind() kind {
- // TODO: cache results
- kind, _, _ := matchBinOpKind(x.Op, x.X.Kind(), x.Y.Kind())
- return kind | nonGround
-}
-
-// unification collects evaluated values that are not mutually exclusive
-// but cannot be represented as a single value. It allows doing the bookkeeping
-// on accumulating conjunctions, simplifying them along the way, until they do
-// resolve into a single value.
-type unification struct {
- baseValue
- Values []evaluated
-}
-
-func (x *unification) Kind() kind {
- k := topKind
- for _, v := range x.Values {
- k &= v.Kind()
- }
- return k | nonGround
-}
-
-type disjunction struct {
- baseValue
-
- Values []dValue
-
- // errors is used to keep track of all errors that occurred in
- // a disjunction for better error reporting down the road.
- // TODO: consider storing the errors in values.
- errors []*bottom
-
- HasDefaults bool // also true if it had elminated defaults.
-
- // bind is the node that a successful disjunction will bind to. This
- // allows other arcs to point to this node before the disjunction is
- // completed. For early failure, this node can be set to the glb of all
- // disjunctions. Otherwise top will suffice.
- // bind node
-}
-
-type dValue struct {
- Val value
- Default bool
-}
-
-func (x *disjunction) Kind() kind {
- k := kind(0)
- for _, v := range x.Values {
- k |= v.Val.Kind()
- }
- if k != bottomKind {
- k |= nonGround
- }
- return k
-}
-
-func (x *disjunction) Pos() token.Pos { return x.Values[0].Val.Pos() }
-
-// add add a value to the disjunction. It is assumed not to be a disjunction.
-func (x *disjunction) add(ctx *context, v value, marked bool) {
- x.Values = append(x.Values, dValue{v, marked})
- if b, ok := v.(*bottom); ok {
- x.errors = append(x.errors, b)
- }
-}
-
-// normalize removes redundant element from unification.
-// x must already have been evaluated.
-func (x *disjunction) normalize(ctx *context, src source) mVal {
- leq := func(ctx *context, lt, gt dValue) bool {
- if isBottom(lt.Val) {
- return true
- }
- s := subsumer{ctx: ctx}
- return (!lt.Default || gt.Default) && s.subsumes(gt.Val, lt.Val)
- }
- k := 0
-
- hasMarked := false
- var markedErr *bottom
-outer:
- for i, v := range x.Values {
- // TODO: this is pre-evaluation is quite aggressive. Verify whether
- // this does not trigger structural cycles (it does). If so, this can check for
- // bottom and the validation can be delayed to as late as picking
- // defaults. The drawback of this approach is that printed intermediate
- // results will not look great.
- if err := validate(ctx, v.Val); err != nil {
- x.errors = append(x.errors, err)
- if v.Default {
- markedErr = err
- }
- continue
- }
- if v.Default {
- hasMarked = true
- }
- for j, w := range x.Values {
- if i == j {
- continue
- }
- if leq(ctx, v, w) && (!leq(ctx, w, v) || j < i) {
- // strictly subsumed, or equal and and the equal element was
- // processed earlier.
- continue outer
- }
- }
- // If there was a three-way equality, an element w, where w == v could
- // already have been added.
- for j := 0; j < k; j++ {
- if leq(ctx, v, x.Values[j]) {
- continue outer
- }
- }
- // TODO: do not modify value, but create a new disjunction.
- x.Values[k] = v
- k++
- }
- if !hasMarked && markedErr != nil && (k > 1 || !x.Values[0].Val.Kind().isGround()) {
- x.Values[k] = dValue{&bottom{}, true}
- k++
- }
-
- switch k {
- case 0:
- // Empty disjunction. All elements must be errors.
- // Take the first error as an example.
- err := x.Values[0].Val
- if !isBottom(err) {
- // TODO: use format instead of debugStr.
- err = ctx.mkErr(src, ctx.str(err))
- }
- return mVal{x.computeError(ctx, src), false}
- case 1:
- v := x.Values[0]
- return mVal{v.Val.(evaluated), v.Default}
- }
- // TODO: do not modify value, but create a new disjunction.
- x.Values = x.Values[:k]
- return mVal{x, false}
-}
-
-func (x *disjunction) computeError(ctx *context, src source) evaluated {
- var errors []*bottom
-
- // Ensure every position is visited at least once.
- // This prevents discriminators fields from showing up too much. A special
- // "all errors" flag could be used to expand all errors.
- visited := map[token.Pos]bool{}
-
- for _, b := range x.errors {
- positions := b.Positions(ctx)
- if len(positions) == 0 {
- positions = append(positions, token.NoPos)
- }
- // Include the error if at least one of its positions wasn't covered
- // before.
- done := true
- for _, p := range positions {
- if !visited[p] {
- done = false
- }
- visited[p] = true
- }
- if !done {
- b := *b
- b.format = "empty disjunction: " + b.format
- errors = append(errors, &b)
- }
- }
- switch len(errors) {
- case 0:
- // Should never happen.
- return ctx.mkErr(src, errors, "empty disjunction")
- case 1:
- return ctx.mkErr(src, errors, "empty disjunction: %v", errors[0])
- default:
- return ctx.mkErr(src, errors, "empty disjunction: %v (and %d other errors)", errors[0], len(errors)-1)
- }
-}
-
-type listComprehension struct {
- baseValue
- clauses yielder
-}
-
-func (x *listComprehension) Kind() kind {
- return listKind | nonGround | referenceKind
-}
-
-type structComprehension struct {
- baseValue
- clauses yielder
-}
-
-func (x *structComprehension) Kind() kind {
- return structKind | nonGround | referenceKind
-}
-
-// TODO: rename to something better. No longer a comprehension.
-// Generated field, perhaps.
-type fieldComprehension struct {
- baseValue
- key value
- val value
- opt bool
- def bool
- doc *docNode
- attrs *attributes
-}
-
-func (x *fieldComprehension) Kind() kind {
- return structKind | nonGround
-}
-
-type yieldFunc func(v evaluated) *bottom
-
-type yielder interface {
- value
- yield(*context, yieldFunc) *bottom
-}
-
-type yield struct {
- baseValue
- value value
-}
-
-func (x *yield) Kind() kind { return topKind | referenceKind }
-
-func (x *yield) yield(ctx *context, fn yieldFunc) *bottom {
- v := x.value.evalPartial(ctx)
- if err, ok := v.(*bottom); ok {
- return err
- }
- if err := fn(v); err != nil {
- return err
- }
- return nil
-}
-
-type guard struct { // rename to guard
- baseValue
- Condition value
- Dst yielder
-}
-
-func (x *guard) Kind() kind { return topKind | referenceKind }
-
-func (x *guard) yield(ctx *context, fn yieldFunc) *bottom {
- filter := ctx.manifest(x.Condition)
- if err, ok := filter.(*bottom); ok {
- return err
- }
- if err := checkKind(ctx, filter, boolKind); err != nil {
- return err
- }
- if filter.(*boolLit).B {
- if err := x.Dst.yield(ctx, fn); err != nil {
- return err
- }
- }
- return nil
-}
-
-type feed struct {
- baseValue
- Src value
- fn *lambdaExpr
-}
-
-func (x *feed) Kind() kind { return topKind | referenceKind }
-
-func (x *feed) yield(ctx *context, yfn yieldFunc) (result *bottom) {
- if ctx.trace {
- defer uni(indent(ctx, "feed", x))
- }
- source := ctx.manifest(x.Src)
- fn := x.fn // no need to evaluate eval
-
- switch src := source.(type) {
- case *structLit:
- var err *bottom
- src, err = src.expandFields(ctx)
- if err != nil {
- return err
- }
- for i, a := range src.Arcs {
- key := &stringLit{
- x.baseValue,
- ctx.LabelStr(a.Label),
- nil,
- }
- if a.definition || a.optional || a.Label.IsHidden() {
- continue
- }
- val := src.at(ctx, i)
- v := fn.call(ctx, x, key, val)
- if err, ok := v.(*bottom); ok {
- return err
- }
- if err := v.(yielder).yield(ctx, yfn); err != nil {
- return err
- }
- }
- return nil
-
- case *list:
- for i := range src.elem.Arcs {
- idx := newInt(x, 0).setInt(i)
- v := fn.call(ctx, x, idx, src.at(ctx, i))
- if err, ok := v.(*bottom); ok {
- return err
- }
- if err := v.(yielder).yield(ctx, yfn); err != nil {
- return err
- }
- }
- return nil
-
- default:
- if err, ok := source.(*bottom); ok {
- return err
- }
- if k := source.Kind(); k&(structKind|listKind) == bottomKind {
- return ctx.mkErr(x, x.Src, "feed source must be list or struct, found %s", k)
- }
- return ctx.mkErr(x, x.Src, codeIncomplete, "incomplete feed source")
- }
-}
diff --git a/cuego/cuego.go b/cuego/cuego.go
index d666fe7..f2460a3 100644
--- a/cuego/cuego.go
+++ b/cuego/cuego.go
@@ -19,9 +19,9 @@
"reflect"
"sync"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// DefaultContext is the shared context used with top-level functions.
diff --git a/encoding/gocode/generator.go b/encoding/gocode/generator.go
index eef0216..451fa8f 100644
--- a/encoding/gocode/generator.go
+++ b/encoding/gocode/generator.go
@@ -24,9 +24,9 @@
"golang.org/x/tools/go/packages"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// Config defines options for generation Go code.
diff --git a/encoding/gocode/generator_test.go b/encoding/gocode/generator_test.go
index e639db0..85780cc 100644
--- a/encoding/gocode/generator_test.go
+++ b/encoding/gocode/generator_test.go
@@ -25,9 +25,9 @@
"github.com/kylelemons/godebug/diff"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/load"
- "cuelang.org/go/internal/legacy/cue"
)
var update = flag.Bool("update", false, "update test files")
diff --git a/encoding/gocode/gocodec/codec.go b/encoding/gocode/gocodec/codec.go
index 268b509..6631683 100644
--- a/encoding/gocode/gocodec/codec.go
+++ b/encoding/gocode/gocodec/codec.go
@@ -23,8 +23,8 @@
import (
"sync"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// Config has no options yet, but is defined for future extensibility.
diff --git a/encoding/gocode/gocodec/codec_test.go b/encoding/gocode/gocodec/codec_test.go
index 9b81384..9114f92 100644
--- a/encoding/gocode/gocodec/codec_test.go
+++ b/encoding/gocode/gocodec/codec_test.go
@@ -22,7 +22,7 @@
"github.com/google/go-cmp/cmp"
"github.com/kr/pretty"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
type Sum struct {
diff --git a/encoding/gocode/templates.go b/encoding/gocode/templates.go
index 7a766c8..22fb940 100644
--- a/encoding/gocode/templates.go
+++ b/encoding/gocode/templates.go
@@ -26,7 +26,7 @@
import (
"fmt"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
"cuelang.org/go/encoding/gocode/gocodec"
)
diff --git a/encoding/gocode/testdata/pkg1/cue_gen.go b/encoding/gocode/testdata/pkg1/cue_gen.go
index b585545..c16cab9 100644
--- a/encoding/gocode/testdata/pkg1/cue_gen.go
+++ b/encoding/gocode/testdata/pkg1/cue_gen.go
@@ -5,8 +5,8 @@
import (
"fmt"
+ "cuelang.org/go/cue"
"cuelang.org/go/encoding/gocode/gocodec"
- "cuelang.org/go/internal/legacy/cue"
)
var cuegenvalMyStruct = cuegenMake("MyStruct", &MyStruct{})
diff --git a/encoding/gocode/testdata/pkg2/cue_gen.go b/encoding/gocode/testdata/pkg2/cue_gen.go
index 6cd4325..927bf6e 100644
--- a/encoding/gocode/testdata/pkg2/cue_gen.go
+++ b/encoding/gocode/testdata/pkg2/cue_gen.go
@@ -5,8 +5,8 @@
import (
"fmt"
+ "cuelang.org/go/cue"
"cuelang.org/go/encoding/gocode/gocodec"
- "cuelang.org/go/internal/legacy/cue"
)
var cuegenvalImportMe = cuegenMake("ImportMe", &ImportMe{})
diff --git a/encoding/json/json.go b/encoding/json/json.go
index f795802..50b9726 100644
--- a/encoding/json/json.go
+++ b/encoding/json/json.go
@@ -21,13 +21,13 @@
"strconv"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/literal"
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/pkg/encoding/json"
)
diff --git a/encoding/jsonschema/constraints.go b/encoding/jsonschema/constraints.go
index 7eeaf63..ef941e2 100644
--- a/encoding/jsonschema/constraints.go
+++ b/encoding/jsonschema/constraints.go
@@ -20,11 +20,11 @@
"path"
"regexp"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// TODO: skip invalid regexps containing ?! and foes.
diff --git a/encoding/jsonschema/decode.go b/encoding/jsonschema/decode.go
index a1d1144..54f8fd6 100644
--- a/encoding/jsonschema/decode.go
+++ b/encoding/jsonschema/decode.go
@@ -24,12 +24,12 @@
"sort"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/ast/astutil"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// rootDefs defines the top-level name of the map of definitions that do not
diff --git a/encoding/jsonschema/decode_test.go b/encoding/jsonschema/decode_test.go
index 08ed07f..adc5bd6 100644
--- a/encoding/jsonschema/decode_test.go
+++ b/encoding/jsonschema/decode_test.go
@@ -24,13 +24,13 @@
"strings"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/token"
"cuelang.org/go/encoding/json"
"cuelang.org/go/encoding/yaml"
- "cuelang.org/go/internal/legacy/cue"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"golang.org/x/tools/txtar"
diff --git a/encoding/jsonschema/jsonschema.go b/encoding/jsonschema/jsonschema.go
index 6451157..9e81b73 100644
--- a/encoding/jsonschema/jsonschema.go
+++ b/encoding/jsonschema/jsonschema.go
@@ -31,9 +31,9 @@
package jsonschema
import (
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/token"
- "cuelang.org/go/internal/legacy/cue"
)
// Extract converts JSON Schema data into an equivalent CUE representation.
diff --git a/encoding/jsonschema/ref.go b/encoding/jsonschema/ref.go
index c57253c..26443fe 100644
--- a/encoding/jsonschema/ref.go
+++ b/encoding/jsonschema/ref.go
@@ -20,11 +20,11 @@
"strconv"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
func (d *decoder) parseRef(p token.Pos, str string) []string {
diff --git a/encoding/openapi/build.go b/encoding/openapi/build.go
index 5bcd485..a6d8a21 100644
--- a/encoding/openapi/build.go
+++ b/encoding/openapi/build.go
@@ -23,11 +23,11 @@
"strconv"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
"golang.org/x/xerrors"
)
diff --git a/encoding/openapi/crd.go b/encoding/openapi/crd.go
index 7e691fa..7251b43 100644
--- a/encoding/openapi/crd.go
+++ b/encoding/openapi/crd.go
@@ -44,8 +44,8 @@
//
import (
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
- "cuelang.org/go/internal/legacy/cue"
)
// newCoreBuilder returns a builder that represents a structural schema.
diff --git a/encoding/openapi/decode.go b/encoding/openapi/decode.go
index b45100a..a8a61b8 100644
--- a/encoding/openapi/decode.go
+++ b/encoding/openapi/decode.go
@@ -17,12 +17,12 @@
import (
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
"cuelang.org/go/encoding/jsonschema"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// Extract converts OpenAPI definitions to an equivalent CUE representation.
diff --git a/encoding/openapi/decode_test.go b/encoding/openapi/decode_test.go
index b3d417d..8a6d8a8 100644
--- a/encoding/openapi/decode_test.go
+++ b/encoding/openapi/decode_test.go
@@ -27,12 +27,12 @@
"github.com/stretchr/testify/assert"
"golang.org/x/tools/txtar"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
"cuelang.org/go/encoding/json"
"cuelang.org/go/encoding/openapi"
"cuelang.org/go/encoding/yaml"
- "cuelang.org/go/internal/legacy/cue"
)
// TestDecode reads the testdata/*.txtar files, converts the contained
diff --git a/encoding/openapi/openapi.go b/encoding/openapi/openapi.go
index 463d758..f26cff6 100644
--- a/encoding/openapi/openapi.go
+++ b/encoding/openapi/openapi.go
@@ -19,11 +19,11 @@
"fmt"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
cuejson "cuelang.org/go/encoding/json"
- "cuelang.org/go/internal/legacy/cue"
)
// A Config defines options for converting CUE to and from OpenAPI.
diff --git a/encoding/openapi/openapi_test.go b/encoding/openapi/openapi_test.go
index c8134f2..e8c3705 100644
--- a/encoding/openapi/openapi_test.go
+++ b/encoding/openapi/openapi_test.go
@@ -25,11 +25,11 @@
"github.com/kylelemons/godebug/diff"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/load"
"cuelang.org/go/encoding/openapi"
- "cuelang.org/go/internal/legacy/cue"
)
var update *bool = flag.Bool("update", false, "update the test output")
diff --git a/encoding/openapi/types.go b/encoding/openapi/types.go
index a216a08..ccc237a 100644
--- a/encoding/openapi/types.go
+++ b/encoding/openapi/types.go
@@ -20,10 +20,10 @@
"github.com/cockroachdb/apd/v2"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/literal"
"cuelang.org/go/cue/token"
- "cuelang.org/go/internal/legacy/cue"
)
// See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#data-types
diff --git a/encoding/yaml/yaml.go b/encoding/yaml/yaml.go
index e038776..0787077 100644
--- a/encoding/yaml/yaml.go
+++ b/encoding/yaml/yaml.go
@@ -20,9 +20,9 @@
"bytes"
"io"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
cueyaml "cuelang.org/go/internal/encoding/yaml"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/third_party/yaml"
pkgyaml "cuelang.org/go/pkg/encoding/yaml"
)
diff --git a/encoding/yaml/yaml_test.go b/encoding/yaml/yaml_test.go
index b9e4e38..304b488 100644
--- a/encoding/yaml/yaml_test.go
+++ b/encoding/yaml/yaml_test.go
@@ -18,9 +18,9 @@
"strings"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/format"
- "cuelang.org/go/internal/legacy/cue"
)
func TestYAML(t *testing.T) {
diff --git a/internal/cli/cli.go b/internal/cli/cli.go
index 404b43d..f6ffd25 100644
--- a/internal/cli/cli.go
+++ b/internal/cli/cli.go
@@ -17,11 +17,11 @@
import (
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
- "cuelang.org/go/internal/legacy/cue"
)
func ParseValue(pos token.Pos, name, str string, k cue.Kind) (x ast.Expr, errs errors.Error) {
diff --git a/internal/core/convert/go_test.go b/internal/core/convert/go_test.go
index 88db590..4a2c93e 100644
--- a/internal/core/convert/go_test.go
+++ b/internal/core/convert/go_test.go
@@ -23,13 +23,13 @@
"github.com/cockroachdb/apd/v2"
"github.com/google/go-cmp/cmp"
+ _ "cuelang.org/go/cue" // set internal.CoreValue
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/convert"
"cuelang.org/go/internal/core/debug"
"cuelang.org/go/internal/core/eval"
"cuelang.org/go/internal/core/runtime"
- _ "cuelang.org/go/internal/legacy/cue" // set internal.CoreValue
)
func mkBigInt(a int64) (v apd.Decimal) { v.SetInt64(a); return }
diff --git a/internal/core/eval/eval_test.go b/internal/core/eval/eval_test.go
index 68eb20f..af4e8c9 100644
--- a/internal/core/eval/eval_test.go
+++ b/internal/core/eval/eval_test.go
@@ -20,11 +20,11 @@
"strings"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal/core/debug"
"cuelang.org/go/internal/core/eval"
"cuelang.org/go/internal/core/validate"
"cuelang.org/go/internal/cuetxtar"
- "cuelang.org/go/internal/legacy/cue"
"github.com/rogpeppe/go-internal/txtar"
)
diff --git a/internal/core/export/export_test.go b/internal/core/export/export_test.go
index 804122e..1b415d2 100644
--- a/internal/core/export/export_test.go
+++ b/internal/core/export/export_test.go
@@ -18,6 +18,7 @@
"flag"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
@@ -26,7 +27,6 @@
"cuelang.org/go/internal/core/export"
"cuelang.org/go/internal/core/runtime"
"cuelang.org/go/internal/cuetxtar"
- "cuelang.org/go/internal/legacy/cue"
"github.com/rogpeppe/go-internal/txtar"
)
diff --git a/internal/core/export/extract_test.go b/internal/core/export/extract_test.go
index 2d5153d..02a098c 100644
--- a/internal/core/export/extract_test.go
+++ b/internal/core/export/extract_test.go
@@ -18,12 +18,12 @@
"fmt"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal/core/adt"
"cuelang.org/go/internal/core/compile"
"cuelang.org/go/internal/core/eval"
"cuelang.org/go/internal/core/export"
"cuelang.org/go/internal/cuetxtar"
- "cuelang.org/go/internal/legacy/cue"
)
func TestExtract(t *testing.T) {
diff --git a/internal/core/export/value_test.go b/internal/core/export/value_test.go
index ac506cd..193555c 100644
--- a/internal/core/export/value_test.go
+++ b/internal/core/export/value_test.go
@@ -18,6 +18,7 @@
"fmt"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal/core/adt"
@@ -26,7 +27,6 @@
"cuelang.org/go/internal/core/export"
"cuelang.org/go/internal/core/runtime"
"cuelang.org/go/internal/cuetxtar"
- "cuelang.org/go/internal/legacy/cue"
"github.com/rogpeppe/go-internal/txtar"
)
diff --git a/internal/diff/diff.go b/internal/diff/diff.go
index 538d662..d9c29ed 100644
--- a/internal/diff/diff.go
+++ b/internal/diff/diff.go
@@ -15,8 +15,8 @@
package diff
import (
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/internal/legacy/cue"
)
// Profile configures a diff operation.
diff --git a/internal/diff/diff_test.go b/internal/diff/diff_test.go
index 02c3acd..83aa395 100644
--- a/internal/diff/diff_test.go
+++ b/internal/diff/diff_test.go
@@ -18,7 +18,7 @@
"bytes"
"testing"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
func TestDiff(t *testing.T) {
diff --git a/internal/diff/print.go b/internal/diff/print.go
index 51f45bc..a87832e 100644
--- a/internal/diff/print.go
+++ b/internal/diff/print.go
@@ -18,8 +18,8 @@
"fmt"
"io"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/internal/legacy/cue"
)
// Print the differences between two structs represented by an edit script.
diff --git a/internal/encoding/detect.go b/internal/encoding/detect.go
index 25537db..b125a3f 100644
--- a/internal/encoding/detect.go
+++ b/internal/encoding/detect.go
@@ -19,8 +19,8 @@
"path"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/build"
- "cuelang.org/go/internal/legacy/cue"
)
// Detect detects the interpretation.
diff --git a/internal/encoding/detect_test.go b/internal/encoding/detect_test.go
index 5850761..bdb1b9b 100644
--- a/internal/encoding/detect_test.go
+++ b/internal/encoding/detect_test.go
@@ -17,8 +17,8 @@
import (
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/build"
- "cuelang.org/go/internal/legacy/cue"
)
func TestDetect(t *testing.T) {
diff --git a/internal/encoding/encoder.go b/internal/encoding/encoder.go
index 31f0b0b..fbb019f 100644
--- a/internal/encoding/encoder.go
+++ b/internal/encoding/encoder.go
@@ -21,6 +21,7 @@
"os"
"path/filepath"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
@@ -29,7 +30,6 @@
"cuelang.org/go/encoding/openapi"
"cuelang.org/go/internal"
"cuelang.org/go/internal/filetypes"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/pkg/encoding/yaml"
)
diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go
index 875ac21..8606bcb 100644
--- a/internal/encoding/encoding.go
+++ b/internal/encoding/encoding.go
@@ -26,6 +26,7 @@
"os"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
@@ -38,7 +39,6 @@
"cuelang.org/go/encoding/protobuf"
"cuelang.org/go/internal"
"cuelang.org/go/internal/filetypes"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/third_party/yaml"
"golang.org/x/text/encoding/unicode"
"golang.org/x/text/transform"
diff --git a/internal/filetypes/filetypes.go b/internal/filetypes/filetypes.go
index c03c1a8..4e291af 100644
--- a/internal/filetypes/filetypes.go
+++ b/internal/filetypes/filetypes.go
@@ -20,10 +20,10 @@
"path/filepath"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/token"
- "cuelang.org/go/internal/legacy/cue"
)
// Mode indicate the base mode of operation and indicates a different set of
diff --git a/internal/filetypes/gen.go b/internal/filetypes/gen.go
index 6430ff9..f0b9f03 100644
--- a/internal/filetypes/gen.go
+++ b/internal/filetypes/gen.go
@@ -21,9 +21,9 @@
"log"
"os"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/load"
"cuelang.org/go/encoding/gocode"
- "cuelang.org/go/internal/legacy/cue"
)
func main() {
diff --git a/internal/filetypes/types.go b/internal/filetypes/types.go
index ba3c586..4c47c9b 100644
--- a/internal/filetypes/types.go
+++ b/internal/filetypes/types.go
@@ -5,8 +5,8 @@
import (
"fmt"
+ "cuelang.org/go/cue"
"cuelang.org/go/encoding/gocode/gocodec"
- "cuelang.org/go/internal/legacy/cue"
)
var cuegenCodec, cuegenInstance = func() (*gocodec.Codec, *cue.Instance) {
diff --git a/internal/legacy/cue/build.go b/internal/legacy/cue/build.go
deleted file mode 100644
index 65f0932..0000000
--- a/internal/legacy/cue/build.go
+++ /dev/null
@@ -1,341 +0,0 @@
-// 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 cue
-
-import (
- "strings"
- "sync"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/ast/astutil"
- "cuelang.org/go/cue/build"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/compile"
- "cuelang.org/go/internal/core/runtime"
-)
-
-// A Runtime is used for creating CUE interpretations.
-//
-// Any operation that involves two Values or Instances should originate from
-// the same Runtime.
-//
-// The zero value of a Runtime is ready to use.
-type Runtime struct {
- ctx *build.Context // TODO: remove
- idx *index
-}
-
-func init() {
- internal.GetRuntimeNew = func(instance interface{}) interface{} {
- switch x := instance.(type) {
- case Value:
- return &Runtime{idx: x.idx}
-
- case *Instance:
- return &Runtime{idx: x.index}
-
- default:
- panic("argument must be Value or *Instance")
- }
- }
-
- internal.CheckAndForkRuntimeNew = func(runtime, value interface{}) interface{} {
- r := runtime.(*Runtime)
- idx := value.(Value).ctx().index
- if idx != r.idx {
- panic("value not from same runtime")
- }
- return &Runtime{idx: newIndex(idx)}
- }
-
- internal.CoreValue = func(value interface{}) (runtime, vertex interface{}) {
- if v, ok := value.(Value); ok && v.v != nil {
- return v.idx.Index, v.v
- }
- return nil, nil
- }
-}
-
-func dummyLoad(token.Pos, string) *build.Instance { return nil }
-
-func (r *Runtime) index() *index {
- if r.idx == nil {
- r.idx = newIndex(sharedIndex)
- }
- return r.idx
-}
-
-func (r *Runtime) buildContext() *build.Context {
- ctx := r.ctx
- if r.ctx == nil {
- ctx = build.NewContext()
- }
- return ctx
-}
-
-func (r *Runtime) complete(p *build.Instance) (*Instance, error) {
- idx := r.index()
- if err := p.Complete(); err != nil {
- return nil, err
- }
- inst := idx.loadInstance(p)
- inst.ImportPath = p.ImportPath
- if inst.Err != nil {
- return nil, inst.Err
- }
- return inst, nil
-}
-
-// Compile compiles the given source into an Instance. The source code may be
-// provided as a string, byte slice, io.Reader. The name is used as the file
-// name in position information. The source may import builtin packages. Use
-// Build to allow importing non-builtin packages.
-func (r *Runtime) Compile(filename string, source interface{}) (*Instance, error) {
- ctx := r.buildContext()
- p := ctx.NewInstance(filename, dummyLoad)
- if err := p.AddFile(filename, source); err != nil {
- return nil, p.Err
- }
- return r.complete(p)
-}
-
-// CompileFile compiles the given source file into an Instance. The source may
-// import builtin packages. Use Build to allow importing non-builtin packages.
-func (r *Runtime) CompileFile(file *ast.File) (*Instance, error) {
- ctx := r.buildContext()
- p := ctx.NewInstance(file.Filename, dummyLoad)
- err := p.AddSyntax(file)
- if err != nil {
- return nil, err
- }
- _, p.PkgName, _ = internal.PackageInfo(file)
- return r.complete(p)
-}
-
-// CompileExpr compiles the given source expression into an Instance. The source
-// may import builtin packages. Use Build to allow importing non-builtin
-// packages.
-func (r *Runtime) CompileExpr(expr ast.Expr) (*Instance, error) {
- f, err := astutil.ToFile(expr)
- if err != nil {
- return nil, err
- }
- return r.CompileFile(f)
-}
-
-// Parse parses a CUE source value into a CUE Instance. The source code may
-// be provided as a string, byte slice, or io.Reader. The name is used as the
-// file name in position information. The source may import builtin packages.
-//
-// Deprecated: use Compile
-func (r *Runtime) Parse(name string, source interface{}) (*Instance, error) {
- return r.Compile(name, source)
-}
-
-// Build creates an Instance from the given build.Instance. A returned Instance
-// may be incomplete, in which case its Err field is set.
-func (r *Runtime) Build(instance *build.Instance) (*Instance, error) {
- return r.complete(instance)
-}
-
-// Build creates one Instance for each build.Instance. A returned Instance
-// may be incomplete, in which case its Err field is set.
-//
-// Example:
-// inst := cue.Build(load.Instances(args))
-//
-func Build(instances []*build.Instance) []*Instance {
- if len(instances) == 0 {
- panic("cue: list of instances must not be empty")
- }
- var r Runtime
- a, _ := r.build(instances)
- return a
-}
-
-func (r *Runtime) build(instances []*build.Instance) ([]*Instance, error) {
- index := r.index()
-
- loaded := []*Instance{}
-
- var errs errors.Error
-
- for _, p := range instances {
- _ = p.Complete()
- errs = errors.Append(errs, p.Err)
-
- i := index.loadInstance(p)
- errs = errors.Append(errs, i.Err)
- loaded = append(loaded, i)
- }
-
- // TODO: insert imports
- return loaded, errs
-}
-
-// FromExpr creates an instance from an expression.
-// Any references must be resolved beforehand.
-//
-// Deprecated: use CompileExpr
-func (r *Runtime) FromExpr(expr ast.Expr) (*Instance, error) {
- return r.CompileFile(&ast.File{
- Decls: []ast.Decl{&ast.EmbedDecl{Expr: expr}},
- })
-}
-
-// index maps conversions from label names to internal codes.
-//
-// All instances belonging to the same package should share this index.
-type index struct {
- adt.Runtime
- *runtime.Index
-
- loaded map[*build.Instance]*Instance
- mutex sync.Mutex
-}
-
-// sharedIndex is used for indexing builtins and any other labels common to
-// all instances.
-var sharedIndex = &index{
- Runtime: runtime.SharedRuntimeNew,
- Index: runtime.SharedIndexNew,
- loaded: map[*build.Instance]*Instance{},
-}
-
-// NewRuntime creates a *runtime.Runtime with builtins preloaded.
-func NewRuntime() *runtime.Runtime {
- idx := runtime.NewIndex(sharedIndex.Index)
- r := runtime.NewWithIndex(idx)
- i := &index{
- Runtime: r,
- Index: idx,
- loaded: map[*build.Instance]*Instance{},
- }
- r.Data = i
- return r
-}
-
-// newIndex creates a new index.
-func newIndex(parent *index) *index {
- idx := runtime.NewIndex(parent.Index)
- r := runtime.NewWithIndex(idx)
- i := &index{
- Runtime: r,
- Index: idx,
- loaded: map[*build.Instance]*Instance{},
- }
- r.Data = i
- return i
-}
-
-func isBuiltin(s string) bool {
- _, ok := builtins[s]
- return ok
-}
-
-func (idx *index) loadInstance(p *build.Instance) *Instance {
- _ = visitInstances(p, func(p *build.Instance, errs errors.Error) errors.Error {
- if inst := idx.loaded[p]; inst != nil {
- if !inst.complete {
- // cycles should be detected by the builder and it should not be
- // possible to construct a build.Instance that has them.
- panic("cue: cycle")
- }
- return inst.Err
- }
-
- err := runtime.ResolveFiles(idx.Index, p, isBuiltin)
- errs = errors.Append(errs, err)
-
- v, err := compile.Files(nil, idx.Runtime, p.Files...)
- errs = errors.Append(errs, err)
-
- inst := newInstance(idx, p, v)
- idx.loaded[p] = inst
- inst.Err = errs
-
- inst.ImportPath = p.ImportPath
- inst.complete = true
-
- return inst.Err
- })
-
- return idx.loaded[p]
-}
-
-// TODO: runtime.Runtime has a similar, much simpler, implementation. This
-// code should go.
-
-type visitFunc func(b *build.Instance, err errors.Error) (errs errors.Error)
-
-// visitInstances calls f for each transitive dependency.
-//
-// It passes any errors that occur in transitive dependencies to the visitFunc.
-// visitFunc must return the errors it is passed or return nil to ignore it.
-func visitInstances(b *build.Instance, f visitFunc) (errs errors.Error) {
- v := visitor{b: b, f: f, errs: b.Err}
- for _, file := range b.Files {
- v.file(file)
- }
- return v.f(b, v.errs)
-}
-
-type visitor struct {
- b *build.Instance
- f visitFunc
- errs errors.Error
-}
-
-func (v *visitor) addErr(e errors.Error) {
- v.errs = errors.Append(v.errs, e)
-}
-
-func (v *visitor) file(file *ast.File) {
- for _, d := range file.Decls {
- switch x := d.(type) {
- case *ast.Package:
- case *ast.ImportDecl:
- for _, s := range x.Specs {
- v.spec(s)
- }
- case *ast.CommentGroup:
- default:
- return
- }
- }
-}
-
-func (v *visitor) spec(spec *ast.ImportSpec) {
- info, err := astutil.ParseImportSpec(spec)
- if err != nil {
- v.addErr(errors.Promote(err, "invalid import path"))
- return
- }
-
- pkg := v.b.LookupImport(info.ID)
- if pkg == nil {
- if strings.Contains(info.ID, ".") {
- v.addErr(errors.Newf(spec.Pos(),
- "package %q imported but not defined in %s",
- info.ID, v.b.ImportPath))
- }
- return
- }
-
- v.addErr(visitInstances(pkg, v.f))
-}
diff --git a/internal/legacy/cue/build_test.go b/internal/legacy/cue/build_test.go
deleted file mode 100644
index 4cf0ccb..0000000
--- a/internal/legacy/cue/build_test.go
+++ /dev/null
@@ -1,259 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "testing"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/build"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal/core/debug"
-)
-
-func TestFromExpr(t *testing.T) {
- testCases := []struct {
- expr ast.Expr
- out string
- }{{
- expr: ast.NewString("Hello"),
- out: `"Hello"`,
- }, {
- expr: ast.NewList(
- ast.NewString("Hello"),
- ast.NewString("World"),
- ),
- out: `["Hello", "World"]`,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- r := &Runtime{}
- inst, err := r.CompileExpr(tc.expr)
- if err != nil {
- t.Fatal(err)
- }
- if got := fmt.Sprint(inst.Value()); got != tc.out {
- t.Errorf("\n got: %v; want %v", got, tc.out)
- }
- })
- }
-}
-
-func TestBuild(t *testing.T) {
- files := func(s ...string) []string { return s }
- insts := func(i ...*bimport) []*bimport { return i }
- pkg1 := &bimport{
- "pkg1",
- files(`
- package pkg1
-
- Object: "World"
- `),
- }
- pkg2 := &bimport{
- "example.com/foo/pkg2:pkg",
- files(`
- package pkg
-
- Number: 12
- `),
- }
- pkg3 := &bimport{
- "example.com/foo/v1:pkg3",
- files(`
- package pkg3
-
- List: [1,2,3]
- `),
- }
-
- testCases := []struct {
- instances []*bimport
- emit string
- }{{
- insts(&bimport{"", files(`test: "ok"`)}),
- `{test:"ok"}`,
- }, {
- insts(&bimport{"",
- files(
- `package test
-
- import "math"
-
- "Pi: \(math.Pi)!"`)}),
- `"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
- }, {
- insts(&bimport{"",
- files(
- `package test
-
- import math2 "math"
-
- "Pi: \(math2.Pi)!"`)}),
- `"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
- }, {
- insts(pkg1, &bimport{"",
- files(
- `package test
-
- import "pkg1"
-
- "Hello \(pkg1.Object)!"`),
- }),
- `"Hello World!"`,
- }, {
- insts(pkg1, &bimport{"",
- files(
- `package test
-
- import "pkg1"
-
- "Hello \(pkg1.Object)!"`),
- }),
- `"Hello World!"`,
- }, {
- insts(pkg1, &bimport{"",
- files(
- `package test
-
- import pkg2 "pkg1"
- #pkg1: pkg2.Object
-
- "Hello \(#pkg1)!"`),
- }),
- `"Hello World!"`,
- }, {
- insts(pkg1, pkg2, &bimport{"",
- files(
- `package test
-
- import bar "pkg1"
- import baz "example.com/foo/pkg2:pkg"
-
- pkg1: Object: 3
- "Hello \(pkg1.Object)!"`),
- }),
- `imported and not used: "pkg1" as bar (and 1 more errors)`,
- }, {
- insts(pkg2, &bimport{"",
- files(
- `package test
-
- import "example.com/foo/pkg2:pkg"
-
- "Hello \(pkg2.Number)!"`),
- }),
- `imported and not used: "example.com/foo/pkg2:pkg" (and 1 more errors)`,
- // `file0.cue:5:14: unresolved reference pkg2`,
- }, {
- insts(pkg2, &bimport{"",
- files(
- `package test
-
- import "example.com/foo/pkg2:pkg"
-
- "Hello \(pkg.Number)!"`),
- }),
- `"Hello 12!"`,
- }, {
- insts(pkg3, &bimport{"",
- files(
- `package test
-
- import "example.com/foo/v1:pkg3"
-
- "Hello \(pkg3.List[1])!"`),
- }),
- `"Hello 2!"`,
- }, {
- insts(pkg3, &bimport{"",
- files(
- `package test
-
- import "example.com/foo/v1:pkg3"
-
- pkg3: 3
-
- "Hello \(pkg3.List[1])!"`),
- }),
- `pkg3 redeclared as imported package name
- previous declaration at file0.cue:5:5`,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- insts := Build(makeInstances(tc.instances))
- var got string
- if err := insts[0].Err; err != nil {
- got = err.Error()
- } else {
- cfg := &debug.Config{Compact: true}
- got = debug.NodeString(insts[0].Index, insts[0].Value().v, cfg)
- }
- if got != tc.emit {
- t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
- }
- })
- }
-}
-
-type builder struct {
- ctxt *build.Context
- imports map[string]*bimport
-}
-
-func (b *builder) load(pos token.Pos, path string) *build.Instance {
- bi := b.imports[path]
- if bi == nil {
- return nil
- }
- return b.build(bi)
-}
-
-type bimport struct {
- path string // "" means top-level
- files []string
-}
-
-func makeInstances(insts []*bimport) (instances []*build.Instance) {
- b := builder{
- ctxt: build.NewContext(),
- imports: map[string]*bimport{},
- }
- for _, bi := range insts {
- if bi.path != "" {
- b.imports[bi.path] = bi
- }
- }
- for _, bi := range insts {
- if bi.path == "" {
- instances = append(instances, b.build(bi))
- }
- }
- return
-}
-
-func (b *builder) build(bi *bimport) *build.Instance {
- path := bi.path
- if path == "" {
- path = "dir"
- }
- p := b.ctxt.NewInstance(path, b.load)
- for i, f := range bi.files {
- _ = p.AddFile(fmt.Sprintf("file%d.cue", i), f)
- }
- _ = p.Complete()
- return p
-}
diff --git a/internal/legacy/cue/builtin.go b/internal/legacy/cue/builtin.go
deleted file mode 100644
index 9fa9fa1..0000000
--- a/internal/legacy/cue/builtin.go
+++ /dev/null
@@ -1,740 +0,0 @@
-// 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.
-
-//go:generate go run golang.org/x/tools/cmd/goimports -w -local cuelang.org/go builtins.go
-//go:generate gofmt -s -w builtins.go
-
-package cue
-
-import (
- "encoding/json"
- "fmt"
- "io"
- "math/big"
- "path"
- "sort"
- "strings"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/parser"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/compile"
- "cuelang.org/go/internal/core/convert"
- "cuelang.org/go/internal/core/runtime"
-)
-
-// A builtin is a builtin function or constant.
-//
-// A function may return and a constant may be any of the following types:
-//
-// error (translates to bottom)
-// nil (translates to null)
-// bool
-// int*
-// uint*
-// float64
-// string
-// *big.Float
-// *big.Int
-//
-// For any of the above, including interface{} and these types recursively:
-// []T
-// map[string]T
-//
-type builtin struct {
- Name string
- pkg label
- Params []kind
- Result kind
- Func func(c *callCtxt)
- // Const interface{}
- Const string
-}
-
-type builtinPkg struct {
- native []*builtin
- cue string
-}
-
-func mustCompileBuiltins(ctx *context, p *builtinPkg, pkgName string) *adt.Vertex {
- obj := &adt.Vertex{}
- pkgLabel := ctx.Label(pkgName, false)
- st := &adt.StructLit{}
- if len(p.native) > 0 {
- obj.AddConjunct(adt.MakeConjunct(nil, st))
- }
- for _, b := range p.native {
- b.pkg = pkgLabel
-
- f := ctx.Label(b.Name, false) // never starts with _
- // n := &node{baseValue: newBase(imp.Path)}
- var v adt.Expr = toBuiltin(ctx, b)
- if b.Const != "" {
- v = mustParseConstBuiltin(ctx, b.Name, b.Const)
- }
- st.Decls = append(st.Decls, &adt.Field{
- Label: f,
- Value: v,
- })
- }
-
- // Parse builtin CUE
- if p.cue != "" {
- expr, err := parser.ParseExpr(pkgName, p.cue)
- if err != nil {
- panic(fmt.Errorf("could not parse %v: %v", p.cue, err))
- }
- c, err := compile.Expr(nil, ctx.opCtx.Runtime, expr)
- if err != nil {
- panic(fmt.Errorf("could compile parse %v: %v", p.cue, err))
- }
- obj.AddConjunct(c)
- }
-
- // We could compile lazily, but this is easier for debugging.
- obj.Finalize(ctx.opCtx)
- if err := obj.Err(ctx.opCtx, adt.Finalized); err != nil {
- panic(err.Err)
- }
-
- return obj
-}
-
-func toBuiltin(ctx *context, b *builtin) *adt.Builtin {
- x := &adt.Builtin{
- Params: b.Params,
- Result: b.Result,
- Package: b.pkg,
- Name: b.Name,
- }
- x.Func = func(ctx *adt.OpContext, args []adt.Value) (ret adt.Expr) {
- runtime := ctx.Impl().(*runtime.Runtime)
- index := runtime.Data.(*index)
-
- // call, _ := ctx.Source().(*ast.CallExpr)
- c := &callCtxt{
- idx: index,
- // src: call,
- ctx: index.newContext(),
- args: args,
- builtin: b,
- }
- defer func() {
- var errVal interface{} = c.err
- if err := recover(); err != nil {
- errVal = err
- }
- ret = processErr(c, errVal, ret)
- }()
- b.Func(c)
- switch v := c.ret.(type) {
- case adt.Value:
- return v
- case *valueError:
- return v.err
- }
- if c.err != nil {
- return nil
- }
- return convert.GoValueToValue(ctx, c.ret, true)
- }
- return x
-}
-
-// newConstBuiltin parses and creates any CUE expression that does not have
-// fields.
-func mustParseConstBuiltin(ctx *context, name, val string) adt.Expr {
- expr, err := parser.ParseExpr("<builtin:"+name+">", val)
- if err != nil {
- panic(err)
- }
- c, err := compile.Expr(nil, ctx.Runtime, expr)
- if err != nil {
- panic(err)
- }
- return c.Expr()
-
-}
-
-var lenBuiltin = &builtin{
- Name: "len",
- Params: []kind{stringKind | bytesKind | listKind | structKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- v := c.value(0)
- switch k := v.IncompleteKind(); k {
- case StructKind:
- s, err := v.structValData(c.ctx)
- if err != nil {
- c.ret = err
- break
- }
- c.ret = s.Len()
- case ListKind:
- i := 0
- iter, err := v.List()
- if err != nil {
- c.ret = err
- break
- }
- for ; iter.Next(); i++ {
- }
- c.ret = i
- case BytesKind:
- b, err := v.Bytes()
- if err != nil {
- c.ret = err
- break
- }
- c.ret = len(b)
- case StringKind:
- s, err := v.String()
- if err != nil {
- c.ret = err
- break
- }
- c.ret = len(s)
- default:
- c.ret = c.ctx.opCtx.Newf("invalid argument type %v", k)
- }
- },
-}
-
-func pos(n adt.Node) (p token.Pos) {
- if n == nil {
- return
- }
- src := n.Source()
- if src == nil {
- return
- }
- return src.Pos()
-}
-
-var closeBuiltin = &builtin{
- Name: "close",
- Params: []kind{structKind},
- Result: structKind,
- Func: func(c *callCtxt) {
- s, ok := c.args[0].(*adt.Vertex)
- if !ok {
- c.ret = errors.Newf(pos(c.args[0]), "struct argument must be concrete")
- return
- }
- if s.IsClosed(c.ctx.opCtx) {
- c.ret = s
- } else {
- v := *s
- v.Closed = nil // TODO: set dedicated Closer.
- c.ret = &v
- }
- },
-}
-
-var andBuiltin = &builtin{
- Name: "and",
- Params: []kind{listKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- iter := c.iter(0)
- if !iter.Next() {
- c.ret = &top{}
- return
- }
- var u adt.Expr = iter.Value().v
- for iter.Next() {
- u = &adt.BinaryExpr{Op: adt.AndOp, X: u, Y: iter.Value().v}
- }
- c.ret = u
- },
-}
-
-var orBuiltin = &builtin{
- Name: "or",
- Params: []kind{listKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- iter := c.iter(0)
- d := []adt.Disjunct{}
- for iter.Next() {
- d = append(d, adt.Disjunct{iter.Value().v, false})
- }
- c.ret = &adt.DisjunctionExpr{nil, d, false}
- if len(d) == 0 {
- // TODO(manifest): This should not be unconditionally incomplete,
- // but it requires results from comprehensions and all to have
- // some special status. Maybe this can be solved by having results
- // of list comprehensions be open if they result from iterating over
- // an open list or struct. This would actually be exactly what
- // that means. The error here could then only add an incomplete
- // status if the source is open.
- c.ret = &adt.Bottom{
- Code: adt.IncompleteError,
- Err: errors.Newf(c.Pos(), "empty list in call to or"),
- }
- }
- },
-}
-
-func (x *builtin) name(ctx *context) string {
- if x.pkg == 0 {
- return x.Name
- }
- return fmt.Sprintf("%s.%s", ctx.LabelStr(x.pkg), x.Name)
-}
-
-func (x *builtin) isValidator() bool {
- return len(x.Params) == 1 && x.Result == boolKind
-}
-
-func processErr(call *callCtxt, errVal interface{}, ret adt.Expr) adt.Expr {
- ctx := call.ctx
- src := call.src
- switch err := errVal.(type) {
- case nil:
- case *callError:
- ret = err.b
- case *json.MarshalerError:
- if err, ok := err.Err.(*marshalError); ok && err.b != nil {
- ret = err.b
- }
- case *marshalError:
- ret = wrapCallErr(call, err.b)
- case *valueError:
- ret = wrapCallErr(call, err.err)
- case errors.Error:
- ret = wrapCallErr(call, &adt.Bottom{Err: err})
- case error:
- if call.err == internal.ErrIncomplete {
- ret = ctx.mkErr(src, codeIncomplete, "incomplete value")
- } else {
- // TODO: store the underlying error explicitly
- ret = wrapCallErr(call, &adt.Bottom{Err: errors.Promote(err, "")})
- }
- default:
- // Likely a string passed to panic.
- ret = wrapCallErr(call, &adt.Bottom{
- Err: errors.Newf(call.Pos(), "%s", err),
- })
- }
- return ret
-}
-
-func wrapCallErr(c *callCtxt, b *adt.Bottom) *adt.Bottom {
- pos := token.NoPos
- if c.src != nil {
- if src := c.src.Source(); src != nil {
- pos = src.Pos()
- }
- }
- const msg = "error in call to %s"
- return &adt.Bottom{
- Code: b.Code,
- Err: errors.Wrapf(b.Err, pos, msg, c.builtin.name(c.ctx)),
- }
-}
-
-func (c *callCtxt) convertError(x interface{}, name string) *adt.Bottom {
- var err errors.Error
- switch v := x.(type) {
- case nil:
- return nil
-
- case *adt.Bottom:
- return v
-
- case *json.MarshalerError:
- err = errors.Promote(v, "marshal error")
-
- case errors.Error:
- err = v
-
- case error:
- if name != "" {
- err = errors.Newf(c.Pos(), "%s: %v", name, v)
- } else {
- err = errors.Newf(c.Pos(), "error in call to %s: %v", c.name(), v)
- }
-
- default:
- err = errors.Newf(token.NoPos, "%s", name)
- }
- if err != internal.ErrIncomplete {
- return &adt.Bottom{
- // Wrap to preserve position information.
- Err: errors.Wrapf(err, c.Pos(), "error in call to %s", c.name()),
- }
- }
- return &adt.Bottom{
- Code: adt.IncompleteError,
- Err: errors.Newf(c.Pos(), "incomplete values in call to %s", c.name()),
- }
-}
-
-// callCtxt is passed to builtin implementations.
-type callCtxt struct {
- idx *index
- src adt.Expr // *adt.CallExpr
- ctx *context
- builtin *builtin
- err interface{}
- ret interface{}
-
- args []adt.Value
-}
-
-func (c *callCtxt) Pos() token.Pos {
- return c.ctx.opCtx.Pos()
-}
-
-func (c *callCtxt) name() string {
- return c.builtin.name(c.ctx)
-}
-
-var builtins = map[string]*Instance{}
-
-func initBuiltins(pkgs map[string]*builtinPkg) {
- ctx := sharedIndex.newContext()
- keys := []string{}
- for k := range pkgs {
- keys = append(keys, k)
- }
- sort.Strings(keys)
- for _, k := range keys {
- b := pkgs[k]
- e := mustCompileBuiltins(ctx, b, k)
-
- i := sharedIndex.addInst(&Instance{
- ImportPath: k,
- PkgName: path.Base(k),
- root: e,
- })
-
- builtins[k] = i
- builtins["-/"+path.Base(k)] = i
- }
-}
-
-func getBuiltinShorthandPkg(ctx *context, shorthand string) *structLit {
- return getBuiltinPkg(ctx, "-/"+shorthand)
-}
-
-func getBuiltinPkg(ctx *context, path string) *structLit {
- p, ok := builtins[path]
- if !ok {
- return nil
- }
- return p.root
-}
-
-func init() {
- internal.UnifyBuiltin = func(val interface{}, kind string) interface{} {
- v := val.(Value)
- ctx := v.ctx()
-
- p := strings.Split(kind, ".")
- pkg, name := p[0], p[1]
- s := getBuiltinPkg(ctx, pkg)
- if s == nil {
- return v
- }
- a := s.Lookup(ctx.Label(name, false))
- if a == nil {
- return v
- }
-
- return v.Unify(makeValue(v.idx, a))
- }
-}
-
-// do returns whether the call should be done.
-func (c *callCtxt) do() bool {
- return c.err == nil
-}
-
-type callError struct {
- b *bottom
-}
-
-func (e *callError) Error() string {
- return fmt.Sprint(e.b)
-}
-
-func (c *callCtxt) errf(src source, underlying error, format string, args ...interface{}) {
- a := make([]interface{}, 0, 2+len(args))
- if err, ok := underlying.(*valueError); ok {
- a = append(a, err.err)
- }
- a = append(a, format)
- a = append(a, args...)
- err := c.ctx.mkErr(src, a...)
- c.err = &callError{err}
-}
-
-func (c *callCtxt) errcf(src source, code adt.ErrorCode, format string, args ...interface{}) {
- a := make([]interface{}, 0, 2+len(args))
- a = append(a, code)
- a = append(a, format)
- a = append(a, args...)
- err := c.ctx.mkErr(src, a...)
- c.err = &callError{err}
-}
-
-func (c *callCtxt) value(i int) Value {
- v := newValueRoot(c.ctx, c.args[i])
- // TODO: remove default
- // v, _ = v.Default()
- if !v.IsConcrete() {
- c.errcf(c.src, adt.IncompleteError, "non-concrete argument %d", i)
- }
- return v
-}
-
-func (c *callCtxt) structVal(i int) *Struct {
- v := newValueRoot(c.ctx, c.args[i])
- s, err := v.Struct()
- if err != nil {
- c.invalidArgType(c.args[i], i, "struct", err)
- return nil
- }
- return s
-}
-
-func (c *callCtxt) invalidArgType(arg value, i int, typ string, err error) {
- if ve, ok := err.(*valueError); ok && ve.err.IsIncomplete() {
- c.err = ve
- return
- }
- v, ok := arg.(adt.Value)
- // TODO: make these permanent errors if the value did not originate from
- // a reference.
- if !ok {
- c.errf(c.src, nil,
- "cannot use incomplete value %s as %s in argument %d to %s: %v",
- c.ctx.str(arg), typ, i, c.name(), err)
- }
- if err != nil {
- c.errf(c.src, err,
- "cannot use %s (type %s) as %s in argument %d to %s: %v",
- c.ctx.str(arg), v.Kind(), typ, i, c.name(), err)
- } else {
- c.errf(c.src, err,
- "cannot use %s (type %s) as %s in argument %d to %s",
- c.ctx.str(arg), v.Kind(), typ, i, c.name())
- }
-}
-
-func (c *callCtxt) int(i int) int { return int(c.intValue(i, 64, "int64")) }
-func (c *callCtxt) int8(i int) int8 { return int8(c.intValue(i, 8, "int8")) }
-func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16, "int16")) }
-func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32, "int32")) }
-func (c *callCtxt) rune(i int) rune { return rune(c.intValue(i, 32, "rune")) }
-func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64, "int64")) }
-
-func (c *callCtxt) intValue(i, bits int, typ string) int64 {
- arg := c.args[i]
- x := newValueRoot(c.ctx, arg)
- n, err := x.Int(nil)
- if err != nil {
- c.invalidArgType(arg, i, typ, err)
- return 0
- }
- if n.BitLen() > bits {
- c.errf(c.src, err, "int %s overflows %s in argument %d in call to %s",
- n, typ, i, c.name())
- }
- res, _ := x.Int64()
- return res
-}
-
-func (c *callCtxt) uint(i int) uint { return uint(c.uintValue(i, 64, "uint64")) }
-func (c *callCtxt) uint8(i int) uint8 { return uint8(c.uintValue(i, 8, "uint8")) }
-func (c *callCtxt) byte(i int) uint8 { return byte(c.uintValue(i, 8, "byte")) }
-func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16, "uint16")) }
-func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32, "uint32")) }
-func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64, "uint64")) }
-
-func (c *callCtxt) uintValue(i, bits int, typ string) uint64 {
- x := newValueRoot(c.ctx, c.args[i])
- n, err := x.Int(nil)
- if err != nil || n.Sign() < 0 {
- c.invalidArgType(c.args[i], i, typ, err)
- return 0
- }
- if n.BitLen() > bits {
- c.errf(c.src, err, "int %s overflows %s in argument %d in call to %s",
- n, typ, i, c.name())
- }
- res, _ := x.Uint64()
- return res
-}
-
-func (c *callCtxt) decimal(i int) *apd.Decimal {
- x := newValueRoot(c.ctx, c.args[i])
- if _, err := x.MantExp(nil); err != nil {
- c.invalidArgType(c.args[i], i, "Decimal", err)
- return nil
- }
- return &c.args[i].(*numLit).X
-}
-
-func (c *callCtxt) float64(i int) float64 {
- x := newValueRoot(c.ctx, c.args[i])
- res, err := x.Float64()
- if err != nil {
- c.invalidArgType(c.args[i], i, "float64", err)
- return 0
- }
- return res
-}
-
-func (c *callCtxt) bigInt(i int) *big.Int {
- x := newValueRoot(c.ctx, c.args[i])
- n, err := x.Int(nil)
- if err != nil {
- c.invalidArgType(c.args[i], i, "int", err)
- return nil
- }
- return n
-}
-
-var ten = big.NewInt(10)
-
-func (c *callCtxt) bigFloat(i int) *big.Float {
- x := newValueRoot(c.ctx, c.args[i])
- var mant big.Int
- exp, err := x.MantExp(&mant)
- if err != nil {
- c.invalidArgType(c.args[i], i, "float", err)
- return nil
- }
- f := &big.Float{}
- f.SetInt(&mant)
- if exp != 0 {
- var g big.Float
- e := big.NewInt(int64(exp))
- f.Mul(f, g.SetInt(e.Exp(ten, e, nil)))
- }
- return f
-}
-
-func (c *callCtxt) string(i int) string {
- x := newValueRoot(c.ctx, c.args[i])
- v, err := x.String()
- if err != nil {
- c.invalidArgType(c.args[i], i, "string", err)
- return ""
- }
- return v
-}
-
-func (c *callCtxt) bytes(i int) []byte {
- x := newValueRoot(c.ctx, c.args[i])
- v, err := x.Bytes()
- if err != nil {
- c.invalidArgType(c.args[i], i, "bytes", err)
- return nil
- }
- return v
-}
-
-func (c *callCtxt) reader(i int) io.Reader {
- x := newValueRoot(c.ctx, c.args[i])
- // TODO: optimize for string and bytes cases
- r, err := x.Reader()
- if err != nil {
- c.invalidArgType(c.args[i], i, "bytes|string", err)
- return nil
- }
- return r
-}
-
-func (c *callCtxt) bool(i int) bool {
- x := newValueRoot(c.ctx, c.args[i])
- b, err := x.Bool()
- if err != nil {
- c.invalidArgType(c.args[i], i, "bool", err)
- return false
- }
- return b
-}
-
-func (c *callCtxt) list(i int) (a []Value) {
- arg := c.args[i]
- x := newValueRoot(c.ctx, arg)
- v, err := x.List()
- if err != nil {
- c.invalidArgType(c.args[i], i, "list", err)
- return a
- }
- for v.Next() {
- a = append(a, v.Value())
- }
- return a
-}
-
-func (c *callCtxt) iter(i int) (a Iterator) {
- arg := c.args[i]
- x := newValueRoot(c.ctx, arg)
- v, err := x.List()
- if err != nil {
- c.invalidArgType(c.args[i], i, "list", err)
- return Iterator{ctx: c.ctx}
- }
- return v
-}
-
-func (c *callCtxt) decimalList(i int) (a []*apd.Decimal) {
- arg := c.args[i]
- x := newValueRoot(c.ctx, arg)
- v, err := x.List()
- if err != nil {
- c.invalidArgType(c.args[i], i, "list", err)
- return nil
- }
- for j := 0; v.Next(); j++ {
- num, err := v.Value().getNum(numKind)
- if err != nil {
- c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
- j, i, c.name(), err)
- break
- }
- a = append(a, &num.X)
- }
- return a
-}
-
-func (c *callCtxt) strList(i int) (a []string) {
- arg := c.args[i]
- x := newValueRoot(c.ctx, arg)
- v, err := x.List()
- if err != nil {
- c.invalidArgType(c.args[i], i, "list", err)
- return nil
- }
- for j := 0; v.Next(); j++ {
- str, err := v.Value().String()
- if err != nil {
- c.err = errors.Wrapf(err, c.Pos(),
- "element %d of list argument %d", j, i)
- break
- }
- a = append(a, str)
- }
- return a
-}
diff --git a/internal/legacy/cue/builtin_test.go b/internal/legacy/cue/builtin_test.go
deleted file mode 100644
index 617235e..0000000
--- a/internal/legacy/cue/builtin_test.go
+++ /dev/null
@@ -1,714 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "math/big"
- "strconv"
- "strings"
- "testing"
-)
-
-func TestBuiltins(t *testing.T) {
- test := func(pkg, expr string) []*bimport {
- return []*bimport{{"",
- []string{fmt.Sprintf("import %q\n(%s)", pkg, expr)},
- }}
- }
- testExpr := func(expr string) []*bimport {
- return []*bimport{{"",
- []string{fmt.Sprintf("(%s)", expr)},
- }}
- }
- hexToDec := func(s string) string {
- var x big.Int
- x.SetString(s, 16)
- return x.String()
- }
- testCases := []struct {
- instances []*bimport
- emit string
- }{{
- test("math", "math.Pi"),
- `3.14159265358979323846264338327950288419716939937510582097494459`,
- }, {
- test("math", "math.Floor(math.Pi)"),
- `3`,
- }, {
- test("math", "math.Pi(3)"),
- `_|_(cannot call non-function math.Pi (type float))`,
- }, {
- test("math", "math.Floor(3, 5)"),
- `_|_(too many arguments in call to math.Floor (have 2, want 1))`,
- }, {
- test("math", `math.Floor("foo")`),
- `_|_(cannot use "foo" (type string) as number in argument 1 to math.Floor)`,
- }, {
- test("crypto/sha256", `sha256.Sum256("hash me")`),
- `'\xeb \x1a\xf5\xaa\xf0\xd6\x06)\xd3Ҧ\x1eFl\xfc\x0f\xed\xb5\x17\xad\xd81\xec\xacR5\xe1کc\xd6'`,
- }, {
- test("crypto/md5", `len(md5.Sum("hash me"))`),
- `16`,
- }, {
- test("crypto/sha1", `len(sha1.Sum("hash me"))`),
- `20`,
- }, {
- test("crypto/sha256", `len(sha256.Sum256("hash me"))`),
- `32`,
- }, {
- test("crypto/sha256", `len(sha256.Sum224("hash me"))`),
- `28`,
- }, {
- test("crypto/sha512", `len(sha512.Sum512("hash me"))`),
- `64`,
- }, {
- test("crypto/sha512", `len(sha512.Sum384("hash me"))`),
- `48`,
- }, {
- test("crypto/sha512", `len(sha512.Sum512_224("hash me"))`),
- `28`,
- }, {
- test("crypto/sha512", `len(sha512.Sum512_256("hash me"))`),
- `32`,
- }, {
- test("encoding/base64", `base64.Encode(null, "foo")`),
- `"Zm9v"`,
- }, {
- test("encoding/base64", `base64.Decode(null, base64.Encode(null, "foo"))`),
- `'foo'`,
- }, {
- test("encoding/base64", `base64.Decode(null, "foo")`),
- `_|_(error in call to encoding/base64.Decode: illegal base64 data at input byte 0 (and 1 more errors))`,
- }, {
- test("encoding/base64", `base64.Decode({}, "foo")`),
- `_|_(error in call to encoding/base64.Decode: base64: unsupported encoding: cannot use value {} (type struct) as null (and 1 more errors))`,
- }, {
- test("encoding/hex", `hex.Encode("foo")`),
- `"666f6f"`,
- }, {
- test("encoding/hex", `hex.Decode(hex.Encode("foo"))`),
- `'foo'`,
- }, {
- test("encoding/hex", `hex.Decode("foo")`),
- `_|_(error in call to encoding/hex.Decode: encoding/hex: invalid byte: U+006F 'o' (and 1 more errors))`,
- }, {
- test("encoding/hex", `hex.Dump('foo')`),
- `"00000000 66 6f 6f |foo|\n"`,
- }, {
- test("encoding/json", `json.Validate("{\"a\":10}", {b:string})`),
- `true`,
- }, {
- test("encoding/json", `json.Validate("{\"a\":10}", {a:<3})`),
- `_|_(error in call to encoding/json.Validate: a: invalid value 10 (out of bound <3) (and 1 more errors))`,
- }, {
- test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<3})`),
- `_|_(error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3) (and 1 more errors))`,
- }, {
- test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<5})`),
- `true`,
- }, {
- test("encoding/yaml", `yaml.Validate("a: 2\n", {a:<5, b:int})`),
- `_|_(error in call to encoding/yaml.Validate: b: incomplete value int (and 1 more errors))`,
- }, {
- test("encoding/yaml", `yaml.ValidatePartial("a: 2\n---\na: 4", {a:<3})`),
- `_|_(error in call to encoding/yaml.ValidatePartial: a: invalid value 4 (out of bound <3) (and 1 more errors))`,
- }, {
- test("encoding/yaml", `yaml.ValidatePartial("a: 2\n---\na: 4", {a:<5})`),
- `true`,
- }, {
- test("encoding/yaml", `yaml.ValidatePartial("a: 2\n", {a:<5, b:int})`),
- `true`,
- }, {
- test("strconv", `strconv.FormatUint(64, 16)`),
- `"40"`,
- }, {
- // Find a better alternative, as this call should go.
- test("strconv", `strconv.FormatFloat(3.02, 300, 4, 64)`),
- `_|_(int 300 overflows byte in argument 1 in call to strconv.FormatFloat (and 1 more errors))`,
- }, {
- // Find a better alternative, as this call should go.
- test("strconv", `strconv.FormatFloat(3.02, -1, 4, 64)`),
- `_|_(cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat (and 1 more errors))`,
- }, {
- // Find a better alternative, as this call should go.
- test("strconv", `strconv.FormatFloat(3.02, 1.0, 4, 64)`),
- `_|_(cannot use 1.0 (type float) as int in argument 2 to strconv.FormatFloat)`,
- }, {
- test("list", `list.Avg([1, 2, 3, 4])`),
- `2.5`,
- }, {
- test("list", `list.Avg([])`),
- `_|_(error in call to list.Avg: empty list (and 1 more errors))`,
- }, {
- test("list", `list.Avg("foo")`),
- `_|_(cannot use "foo" (type string) as list in argument 1 to list.Avg)`,
- }, {
- test("list", `list.Drop([1, 2, 3, 4], 0)`),
- `[1,2,3,4]`,
- }, {
- test("list", `list.Drop([1, 2, 3, 4], 2)`),
- `[3,4]`,
- }, {
- test("list", `list.Drop([1, 2, 3, 4], 10)`),
- `[]`,
- }, {
- test("list", `list.Drop([1, 2, 3, 4], -1)`),
- `_|_(error in call to list.Drop: negative index (and 1 more errors))`,
- }, {
- test("list", `list.FlattenN([1, [[2, 3], []], [4]], -1)`),
- `[1,2,3,4]`,
- }, {
- test("list", `list.FlattenN([1, [[2, 3], []], [4]], 0)`),
- `[1,[[2,3],[]],[4]]`,
- }, {
- test("list", `list.FlattenN([1, [[2, 3], []], [4]], 1)`),
- `[1,[2,3],[],4]`,
- }, {
- test("list", `list.FlattenN([1, [[2, 3], []], [4]], 2)`),
- `[1,2,3,4]`,
- }, {
- test("list", `list.FlattenN([[1, 2] | *[]], -1)`),
- `[]`,
- }, {
- test("list", `list.FlattenN("foo", 1)`),
- `_|_(error in call to list.FlattenN: cannot use value "foo" (type string) as list (and 1 more errors))`,
- }, {
- test("list", `list.FlattenN([], "foo")`),
- `_|_(cannot use "foo" (type string) as int in argument 2 to list.FlattenN)`,
- }, {
- test("list", `list.Max([1, 2, 3, 4])`),
- `4`,
- }, {
- test("list", `list.Max([])`),
- `_|_(error in call to list.Max: empty list (and 1 more errors))`,
- }, {
- test("list", `list.Max("foo")`),
- `_|_(cannot use "foo" (type string) as list in argument 1 to list.Max)`,
- }, {
- test("list", `list.Min([1, 2, 3, 4])`),
- `1`,
- }, {
- test("list", `list.Min([])`),
- `_|_(error in call to list.Min: empty list (and 1 more errors))`,
- }, {
- test("list", `list.Min("foo")`),
- `_|_(cannot use "foo" (type string) as list in argument 1 to list.Min)`,
- }, {
- test("list", `list.Product([1, 2, 3, 4])`),
- `24`,
- }, {
- test("list", `list.Product([])`),
- `1`,
- }, {
- test("list", `list.Product("foo")`),
- `_|_(cannot use "foo" (type string) as list in argument 1 to list.Product)`,
- }, {
- test("list", `list.Range(0, 5, 0)`),
- `_|_(error in call to list.Range: step must be non zero (and 1 more errors))`,
- }, {
- test("list", `list.Range(5, 0, 1)`),
- `_|_(error in call to list.Range: end must be greater than start when step is positive (and 1 more errors))`,
- }, {
- test("list", `list.Range(0, 5, -1)`),
- `_|_(error in call to list.Range: end must be less than start when step is negative (and 1 more errors))`,
- }, {
- test("list", `list.Range(0, 5, 1)`),
- `[0,1,2,3,4]`,
- }, {
- test("list", `list.Range(0, 1, 1)`),
- `[0]`,
- }, {
- test("list", `list.Range(0, 5, 2)`),
- `[0,2,4]`,
- }, {
- test("list", `list.Range(5, 0, -1)`),
- `[5,4,3,2,1]`,
- }, {
- test("list", `list.Range(0, 5, 0.5)`),
- `[0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5]`,
- }, {
- test("list", `list.Slice([1, 2, 3, 4], 1, 3)`),
- `[2,3]`,
- }, {
- test("list", `list.Slice([1, 2, 3, 4], -1, 3)`),
- `_|_(error in call to list.Slice: negative index (and 1 more errors))`,
- }, {
- test("list", `list.Slice([1, 2, 3, 4], 3, 1)`),
- `_|_(error in call to list.Slice: invalid index: 3 > 1 (and 1 more errors))`,
- }, {
- test("list", `list.Slice([1, 2, 3, 4], 5, 5)`),
- `_|_(error in call to list.Slice: slice bounds out of range (and 1 more errors))`,
- }, {
- test("list", `list.Slice([1, 2, 3, 4], 1, 5)`),
- `_|_(error in call to list.Slice: slice bounds out of range (and 1 more errors))`,
- }, {
- test("list", `list.Sort([], list.Ascending)`),
- `[]`,
- }, {
- test("list", `list.Sort([2, 3, 1, 4], {x:_, y:_, less: x<y})`),
- `[1,2,3,4]`,
- }, {
- test("list", `list.SortStable([{a:2,v:1}, {a:1,v:2}, {a:1,v:3}], {
- x:_,
- y:_,
- less: (x.a < y.a)
- })`),
- `[{a:1,v:2},{a:1,v:3},{a:2,v:1}]`,
- }, {
- test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
- `_|_(error in call to list.Sort: x: conflicting values string and {b:2} (mismatched types string and struct) (and 1 more errors) (and 1 more errors))`,
- }, {
- test("list", `list.SortStrings(["b", "a"])`),
- `["a","b"]`,
- }, {
- // TODO: path error. This should be done as part of builtin refactoring.
- test("list", `list.SortStrings([1, 2])`),
- `_|_(error in call to list.SortStrings: element 0 of list argument 0: 0: cannot use value 1 (type int) as string (and 1 more errors))`,
- }, {
- test("list", `list.Sum([1, 2, 3, 4])`),
- `10`,
- }, {
- test("list", `list.Sum([])`),
- `0`,
- }, {
- test("list", `list.Sum("foo")`),
- `_|_(cannot use "foo" (type string) as list in argument 1 to list.Sum)`,
- }, {
- test("list", `list.Take([1, 2, 3, 4], 0)`),
- `[]`,
- }, {
- test("list", `list.Take([1, 2, 3, 4], 2)`),
- `[1,2]`,
- }, {
- test("list", `list.Take([1, 2, 3, 4], 10)`),
- `[1,2,3,4]`,
- }, {
- test("list", `list.Take([1, 2, 3, 4], -1)`),
- `_|_(error in call to list.Take: negative index (and 1 more errors))`,
- }, {
- test("list", `list.MinItems([1, 2, 3, 4], 2)`),
- `true`,
- }, {
- test("list", `list.MinItems([1, 2, 3, 4], 5)`),
- `false`,
- }, {
- test("list", `list.MaxItems([1, 2, 3, 4], 5)`),
- `true`,
- }, {
- test("list", `list.MaxItems([1, 2, 3, 4], 2)`),
- `false`,
- }, {
- // Panics
- test("math", `math.Jacobi(1000, 2000)`),
- `_|_(error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000 (and 1 more errors))`,
- }, {
- test("math", `math.Jacobi(1000, 201)`),
- `1`,
- }, {
- test("math", `math.Asin(2.0e400)`),
- `_|_(cannot use 2.0E+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up (and 1 more errors))`,
- }, {
- test("math", `math.MultipleOf(4, 2)`), `true`,
- }, {
- test("math", `math.MultipleOf(5, 2)`), `false`,
- }, {
- test("math", `math.MultipleOf(5, 0)`),
- `_|_(error in call to math.MultipleOf: division by zero (and 1 more errors))`,
- }, {
- test("math", `math.MultipleOf(100, 1.00001)`), `false`,
- }, {
- test("math", `math.MultipleOf(1, 1)`), `true`,
- }, {
- test("math", `math.MultipleOf(5, 2.5)`), `true`,
- }, {
- test("math", `math.MultipleOf(100e100, 10)`), `true`,
- }, {
- test("encoding/csv", `csv.Decode("1,2,3\n4,5,6")`),
- `[["1","2","3"],["4","5","6"]]`,
- }, {
- test("regexp", `regexp.Find(#"f\w\w"#, "afoot")`),
- `"foo"`,
- }, {
- test("regexp", `regexp.Find(#"f\w\w"#, "bar")`),
- `_|_(error in call to regexp.Find: no match (and 1 more errors))`,
- }, {
- test("regexp", `regexp.FindAll(#"f\w\w"#, "afoot afloat from", 2)`),
- `["foo","flo"]`,
- }, {
- test("regexp", `regexp.FindAll(#"f\w\w"#, "afoot afloat from", 2)`),
- `["foo","flo"]`,
- }, {
- test("regexp", `regexp.FindAll(#"f\w\w"#, "bla bla", -1)`),
- `_|_(error in call to regexp.FindAll: no match (and 1 more errors))`,
- }, {
- test("regexp", `regexp.FindSubmatch(#"f(\w)(\w)"#, "afloat afoot from")`),
- `["flo","l","o"]`,
- }, {
- test("regexp", `regexp.FindAllSubmatch(#"f(\w)(\w)"#, "afloat afoot from", -1)`),
- `[["flo","l","o"],["foo","o","o"],["fro","r","o"]]`,
- }, {
- test("regexp", `regexp.FindAllSubmatch(#"f(\w)(\w)"#, "aglom", -1)`),
- `_|_(error in call to regexp.FindAllSubmatch: no match (and 1 more errors))`,
- }, {
- test("regexp", `regexp.FindNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "afloat afoot from")`),
- `{A:"l",B:"o"}`,
- }, {
- test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "afloat afoot from", -1)`),
- `[{A:"l",B:"o"},{A:"o",B:"o"},{A:"r",B:"o"}]`,
- }, {
- test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>optional)?"#, "fbla", -1)`),
- `[{A:""}]`,
- }, {
- test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "aglom", -1)`),
- `_|_(error in call to regexp.FindAllNamedSubmatch: no match (and 1 more errors))`,
- }, {
- test("regexp", `regexp.Valid & "valid"`),
- `"valid"`,
- }, {
- test("regexp", `regexp.Valid & "invalid)"`),
- "_|_(error in call to regexp.Valid: error parsing regexp: unexpected ): `invalid)`)",
- }, {
- test("strconv", `strconv.FormatBool(true)`),
- `"true"`,
- }, {
- test("strings", `strings.Join(["Hello", "World!"], " ")`),
- `"Hello World!"`,
- }, {
- test("strings", `strings.Join([1, 2], " ")`),
- `_|_(error in call to strings.Join: element 0 of list argument 0: 0: cannot use value 1 (type int) as string (and 1 more errors))`,
- }, {
- test("strings", `strings.ByteAt("a", 0)`),
- strconv.Itoa('a'),
- }, {
- test("strings", `strings.ByteSlice("Hello", 2, 5)`),
- `'llo'`,
- }, {
- test("strings", `strings.SliceRunes("✓ Hello", 0, 3)`),
- `"✓ H"`,
- }, {
- test("strings", `strings.Runes("Café")`),
- strings.Replace(fmt.Sprint([]rune{'C', 'a', 'f', 'é'}), " ", ",", -1),
- }, {
- test("math/bits", `bits.Or(0x8, 0x1)`),
- `9`,
- }, {
- testExpr(`len({})`),
- `0`,
- }, {
- testExpr(`len({a: 1, b: 2, {[foo=_]: int}, _c: 3})`),
- `2`,
- }, {
- testExpr(`len([1, 2, 3])`),
- `3`,
- }, {
- testExpr(`len("foo")`),
- `3`,
- }, {
- testExpr(`len('f\x20\x20')`),
- `3`,
- }, {
- testExpr(`and([string, "foo"])`),
- `"foo"`,
- }, {
- testExpr(`and([string, =~"fo"]) & "foo"`),
- `"foo"`,
- }, {
- testExpr(`and([])`),
- `{}`, // _ & top scope
- }, {
- testExpr(`or([1, 2, 3]) & 2`),
- `2`,
- }, {
- testExpr(`or([])`),
- `_|_(empty list in call to or (and 1 more errors))`,
- }, {
- test("encoding/csv", `csv.Encode([[1,2,3],[4,5],[7,8,9]])`),
- `"1,2,3\n4,5\n7,8,9\n"`,
- }, {
- test("encoding/csv", `csv.Encode([["a", "b"], ["c"]])`),
- `"a,b\nc\n"`,
- }, {
- test("encoding/json", `json.Valid("1")`),
- `true`,
- }, {
- test("encoding/json", `json.Compact("[1, 2]")`),
- `"[1,2]"`,
- }, {
- test("encoding/json", `json.Indent(#"{"a": 1, "b": 2}"#, "", " ")`),
- `"{\n \"a\": 1,\n \"b\": 2\n}"`,
- }, {
- test("encoding/json", `json.Unmarshal("1")`),
- `1`,
- }, {
- test("encoding/json", `json.MarshalStream([{a: 1}, {b: 2}])`),
- `"{\"a\":1}\n{\"b\":2}\n"`,
- }, {
- test("encoding/json", `{
- x: int
- y: json.Marshal({a: x})
- }`),
- `{x:int,y:_|_(cannot convert incomplete value "int" to JSON (and 1 more errors))}`,
- }, {
- test("encoding/yaml", `yaml.MarshalStream([{a: 1}, {b: 2}])`),
- `"a: 1\n---\nb: 2\n"`,
- }, {
- test("net", `net.FQDN & "foo.bar."`),
- `"foo.bar."`,
- }, {
- test("net", `net.FQDN("foo.bararararararararararararararararararararararararararararararararara")`),
- `false`,
- }, {
- test("net", `net.SplitHostPort("[::%lo0]:80")`),
- `["::%lo0","80"]`,
- }, {
- test("net", `net.JoinHostPort("example.com", "80")`),
- `"example.com:80"`,
- }, {
- test("net", `net.JoinHostPort("2001:db8::1", 80)`),
- `"[2001:db8::1]:80"`,
- }, {
- test("net", `net.JoinHostPort([192,30,4,2], 80)`),
- `"192.30.4.2:80"`,
- }, {
- // TODO: why is this not printing compactly?
- test("net", `net.JoinHostPort([192, 30, 4], 80)`),
- `_|_(error in call to net.JoinHostPort: invalid host [192, 30, 4] (and 1 more errors))`,
- }, {
- test("net", `net.IP("23.23.23.23")`),
- `true`,
- }, {
- test("net", `net.IPv4 & "23.23.23.2333"`),
- `_|_(invalid value "23.23.23.2333" (does not satisfy net.IPv4))`,
- }, {
- test("net", `net.IP("23.23.23.23")`),
- `true`,
- }, {
- test("net", `net.IP("2001:db8::1")`),
- `true`,
- }, {
- test("net", `net.IPv4("2001:db8::1")`),
- `false`,
- }, {
- test("net", `net.IPv4() & "ff02::1:3"`),
- `_|_(invalid value "ff02::1:3" (does not satisfy net.IPv4))`,
- }, {
- test("net", `net.LoopbackIP([127, 0, 0, 1])`),
- `true`,
- }, {
- test("net", `net.LoopbackIP("127.0.0.1")`),
- `true`,
- }, {
- test("net", `net.ToIP4("127.0.0.1")`),
- `[127,0,0,1]`,
- }, {
- test("net", `net.ToIP16("127.0.0.1")`),
- `[0,0,0,0,0,0,0,0,0,0,255,255,127,0,0,1]`,
- }, {
- test("strings", `strings.ToCamel("AlphaBeta")`),
- `"alphaBeta"`,
- }, {
- test("strings", `strings.ToTitle("alpha")`),
- `"Alpha"`,
- }, {
- test("strings", `strings.MaxRunes(3) & "foo"`),
- `"foo"`,
- }, {
- test("strings", `strings.MaxRunes(3) & "quux"`),
- `_|_(invalid value "quux" (does not satisfy strings.MaxRunes(3)))`,
- }, {
- test("strings", `strings.MinRunes(1) & "e"`),
- `"e"`,
- }, {
- test("strings", `strings.MaxRunes(0) & "e"`),
- `_|_(invalid value "e" (does not satisfy strings.MaxRunes(0)))`,
- }, {
- test("strings", `strings.MaxRunes(0) & ""`),
- `""`,
- }, {
- test("strings", `strings.MinRunes(3) & "hello"`),
- `"hello"`,
- }, {
- test("strings", `strings.MaxRunes(10) & "hello"`),
- `"hello"`,
- }, {
- test("strings", `strings.MaxRunes(3) & "hello"`),
- `_|_(invalid value "hello" (does not satisfy strings.MaxRunes(3)))`,
- }, {
- test("strings", `strings.MinRunes(10) & "hello"`),
- `_|_(invalid value "hello" (does not satisfy strings.MinRunes(10)))`,
- }, {
- test("struct", `struct.MinFields(0) & ""`),
- `_|_(conflicting values struct.MinFields(0) and "" (mismatched types struct and string))`,
- }, {
- test("struct", `struct.MinFields(0) & {a: 1}`),
- `{a:1}`,
- }, {
- test("struct", `struct.MinFields(2) & {a: 1}`),
- `_|_(invalid value {a:1} (does not satisfy struct.MinFields(2)))`,
- }, {
- test("struct", `struct.MaxFields(0) & {a: 1}`),
- `_|_(invalid value {a:1} (does not satisfy struct.MaxFields(0)))`,
- }, {
- test("struct", `struct.MaxFields(2) & {a: 1}`),
- `{a:1}`,
- }, {
- test("math", `math.Pow(8, 4)`), `4096`,
- }, {
- test("math", `math.Pow10(4)`), `1E+4`,
- }, {
- test("math", `math.Signbit(-4)`), `true`,
- }, {
- test("math", `math.Round(2.5)`), `3`,
- }, {
- test("math", `math.Round(-2.5)`), `-3`,
- }, {
- test("math", `math.RoundToEven(2.5)`), `2`,
- }, {
- test("math", `math.RoundToEven(-2.5)`), `-2`,
- }, {
- test("math", `math.Abs(2.5)`), `2.5`,
- }, {
- test("math", `math.Abs(-2.2)`), `2.2`,
- }, {
- test("math", `math.Cbrt(2)`), `1.25992104989487316476721`,
- }, {
- test("math", `math.Copysign(5, -2.2)`), `-5`,
- }, {
- test("math", `math.Exp(3)`), `20.0855369231876677409285`,
- }, {
- test("math", `math.Exp2(3.5)`), `11.3137084989847603904135`,
- }, {
- test("math", `math.Log(4)`), `1.38629436111989061883446`,
- }, {
- test("math", `math.Log10(4)`), `0.602059991327962390427478`,
- }, {
- test("math", `math.Log2(5)`),
- `2.32192809488736234787032`,
- }, {
- test("math", `math.Dim(3, 2.5)`), `0.5`,
- }, {
- test("math", `math.Dim(5, 7.2)`), `0`,
- }, {
- test("math", `math.Ceil(2.5)`), `3`,
- }, {
- test("math", `math.Ceil(-2.2)`), `-2`,
- }, {
- test("math", `math.Floor(2.9)`), `2`,
- }, {
- test("math", `math.Floor(-2.2)`), `-3`,
- }, {
- test("math", `math.Trunc(2.5)`), `2`,
- }, {
- test("math", `math.Trunc(-2.9)`), `-2`,
- }, {
- test("math/bits", `bits.Lsh(0x8, 4)`), `128`,
- }, {
- test("math/bits", `bits.Rsh(0x100, 4)`), `16`,
- }, {
- test("math/bits", `bits.At(0x100, 8)`), `1`,
- }, {
- test("math/bits", `bits.At(0x100, 9)`), `0`,
- }, {
- test("math/bits", `bits.Set(0x100, 7, 1)`), `384`,
- }, {
- test("math/bits", `bits.Set(0x100, 8, 0)`), `0`,
- }, {
- test("math/bits", `bits.And(0x10000000000000F0E, 0xF0F7)`), `6`,
- }, {
- test("math/bits", `bits.Or(0x100000000000000F0, 0x0F)`),
- hexToDec("100000000000000FF"),
- }, {
- test("math/bits", `bits.Xor(0x10000000000000F0F, 0xFF0)`),
- hexToDec("100000000000000FF"),
- }, {
- test("math/bits", `bits.Xor(0xFF0, 0x10000000000000F0F)`),
- hexToDec("100000000000000FF"),
- }, {
- test("math/bits", `bits.Clear(0xF, 0x100000000000008)`), `7`,
- }, {
- test("math/bits", `bits.Clear(0x1000000000000000008, 0xF)`),
- hexToDec("1000000000000000000"),
- }, {
- test("text/tabwriter", `tabwriter.Write("""
- a\tb\tc
- aaa\tbb\tvv
- """)`),
- `"a b c\naaa bb vv"`,
- }, {
- test("text/tabwriter", `tabwriter.Write([
- "a\tb\tc",
- "aaa\tbb\tvv"])`),
- `"a b c\naaa bb vv\n"`,
- }, {
- test("text/template", `template.Execute("{{.}}-{{.}}", "foo")`),
- `"foo-foo"`,
- }, {
- test("time", `time.Time & "1937-01-01T12:00:27.87+00:20"`),
- `"1937-01-01T12:00:27.87+00:20"`,
- }, {
- test("time", `time.Time & "no time"`),
- `_|_(error in call to time.Time: invalid time "no time")`,
- }, {
- test("time", `time.Unix(1500000000, 123456)`),
- `"2017-07-14T02:40:00.000123456Z"`,
- }}
- for i, tc := range testCases {
- t.Run(fmt.Sprint(i), func(t *testing.T) {
- insts := Build(makeInstances(tc.instances))
- if err := insts[0].Err; err != nil {
- t.Fatal(err)
- }
- v := insts[0].Value()
- ctx := v.ctx()
- got := ctx.opCtx.Str(v.v)
- if got != tc.emit {
- t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
- }
- })
- }
-}
-
-// For debugging purposes. Do not remove.
-func TestSingleBuiltin(t *testing.T) {
- t.Skip("error message")
-
- test := func(pkg, expr string) []*bimport {
- return []*bimport{{"",
- []string{fmt.Sprintf("import %q\n(%s)", pkg, expr)},
- }}
- }
- testCases := []struct {
- instances []*bimport
- emit string
- }{{
- test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
- `_|_(error in call to list.Sort: less: invalid operands {b:2} and {a:1} to '<' (type struct and struct) (and 1 more errors))`,
- }}
- for i, tc := range testCases {
- t.Run(fmt.Sprint(i), func(t *testing.T) {
- insts := Build(makeInstances(tc.instances))
- if err := insts[0].Err; err != nil {
- t.Fatal(err)
- }
- v := insts[0].Value()
- ctx := v.ctx()
- got := ctx.opCtx.Str(v.v)
- if got != tc.emit {
- t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
- }
- })
- }
-}
diff --git a/internal/legacy/cue/builtins.go b/internal/legacy/cue/builtins.go
deleted file mode 100644
index 87ad902..0000000
--- a/internal/legacy/cue/builtins.go
+++ /dev/null
@@ -1,3784 +0,0 @@
-// Code generated by go generate. DO NOT EDIT.
-
-package cue
-
-import (
- "bytes"
- "crypto/md5"
- "crypto/sha1"
- "crypto/sha256"
- "crypto/sha512"
- "encoding/base64"
- "encoding/csv"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "html"
- "io"
- "math"
- "math/big"
- "math/bits"
- "net"
- "path"
- "regexp"
- "sort"
- "strconv"
- "strings"
- "text/tabwriter"
- "text/template"
- "time"
- "unicode"
- "unicode/utf8"
-
- "github.com/cockroachdb/apd/v2"
- "golang.org/x/net/idna"
-
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/literal"
- "cuelang.org/go/cue/parser"
- "cuelang.org/go/internal"
- cueyaml "cuelang.org/go/internal/encoding/yaml"
- "cuelang.org/go/internal/third_party/yaml"
-)
-
-func init() {
- initBuiltins(builtinPackages)
-}
-
-var _ io.Reader
-
-var roundTruncContext = apd.Context{Rounding: apd.RoundDown}
-
-var roundUpContext = apd.Context{Rounding: apd.RoundHalfUp}
-
-var roundEvenContext = apd.Context{Rounding: apd.RoundHalfEven}
-
-var mulContext = apd.BaseContext.WithPrecision(1)
-
-var apdContext = apd.BaseContext.WithPrecision(24)
-
-var zero = apd.New(0, 0)
-
-var two = apd.New(2, 0)
-
-var idnaProfile = idna.New(
- idna.ValidateLabels(true),
- idna.VerifyDNSLength(true),
- idna.StrictDomainName(true),
-)
-
-func netGetIP(ip Value) (goip net.IP) {
- switch ip.Kind() {
- case StringKind:
- s, err := ip.String()
- if err != nil {
- return nil
- }
- goip := net.ParseIP(s)
- if goip == nil {
- return nil
- }
- return goip
-
- case BytesKind:
- b, err := ip.Bytes()
- if err != nil {
- return nil
- }
- goip := net.ParseIP(string(b))
- if goip == nil {
- return nil
- }
- return goip
-
- case ListKind:
- iter, err := ip.List()
- if err != nil {
- return nil
- }
- for iter.Next() {
- v, err := iter.Value().Int64()
- if err != nil {
- return nil
- }
- if v < 0 || 255 < v {
- return nil
- }
- goip = append(goip, byte(v))
- }
- return goip
-
- default:
-
- return nil
- }
-}
-
-func netToList(ip net.IP) []uint {
- a := make([]uint, len(ip))
- for i, p := range ip {
- a[i] = uint(p)
- }
- return a
-}
-
-var split = path.Split
-
-var pathClean = path.Clean
-
-var pathExt = path.Ext
-
-var pathBase = path.Base
-
-var pathIsAbs = path.IsAbs
-
-var pathDir = path.Dir
-
-var errNoMatch = errors.New("no match")
-
-var errNoNamedGroup = errors.New("no named groups")
-
-func timeFormat(value, layout string) (bool, error) {
- _, err := time.Parse(layout, value)
- if err != nil {
-
- return false, fmt.Errorf("invalid time %q", value)
- }
- return true, nil
-}
-
-var builtinPackages = map[string]*builtinPkg{
- // "": {
- // native: []*builtin{},
- // },
- "crypto/md5": {
- native: []*builtin{{
- Name: "Size",
- Const: "16",
- }, {
- Name: "BlockSize",
- Const: "64",
- }, {
- Name: "Sum",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := md5.Sum(data)
- return a[:]
- }()
- }
- },
- }},
- },
- "crypto/sha1": {
- native: []*builtin{{
- Name: "Size",
- Const: "20",
- }, {
- Name: "BlockSize",
- Const: "64",
- }, {
- Name: "Sum",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha1.Sum(data)
- return a[:]
- }()
- }
- },
- }},
- },
- "crypto/sha256": {
- native: []*builtin{{
- Name: "Size",
- Const: "32",
- }, {
- Name: "Size224",
- Const: "28",
- }, {
- Name: "BlockSize",
- Const: "64",
- }, {
- Name: "Sum256",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha256.Sum256(data)
- return a[:]
- }()
- }
- },
- }, {
- Name: "Sum224",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha256.Sum224(data)
- return a[:]
- }()
- }
- },
- }},
- },
- "crypto/sha512": {
- native: []*builtin{{
- Name: "Size",
- Const: "64",
- }, {
- Name: "Size224",
- Const: "28",
- }, {
- Name: "Size256",
- Const: "32",
- }, {
- Name: "Size384",
- Const: "48",
- }, {
- Name: "BlockSize",
- Const: "128",
- }, {
- Name: "Sum512",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha512.Sum512(data)
- return a[:]
- }()
- }
- },
- }, {
- Name: "Sum384",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha512.Sum384(data)
- return a[:]
- }()
- }
- },
- }, {
- Name: "Sum512_224",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha512.Sum512_224(data)
- return a[:]
- }()
- }
- },
- }, {
- Name: "Sum512_256",
- Params: []kind{bytesKind | stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- a := sha512.Sum512_256(data)
- return a[:]
- }()
- }
- },
- }},
- },
- "encoding/base64": {
- native: []*builtin{{
- Name: "EncodedLen",
- Params: []kind{topKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- encoding, n := c.value(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if err := encoding.Null(); err != nil {
- return 0, fmt.Errorf("base64: unsupported encoding: %v", err)
- }
- return base64.StdEncoding.EncodedLen(n), nil
- }()
- }
- },
- }, {
- Name: "DecodedLen",
- Params: []kind{topKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- encoding, x := c.value(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if err := encoding.Null(); err != nil {
- return 0, fmt.Errorf("base64: unsupported encoding: %v", err)
- }
- return base64.StdEncoding.DecodedLen(x), nil
- }()
- }
- },
- }, {
- Name: "Encode",
- Params: []kind{topKind, bytesKind | stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- encoding, src := c.value(0), c.bytes(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if err := encoding.Null(); err != nil {
- return "", fmt.Errorf("base64: unsupported encoding: %v", err)
- }
- return base64.StdEncoding.EncodeToString(src), nil
- }()
- }
- },
- }, {
- Name: "Decode",
- Params: []kind{topKind, stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- encoding, s := c.value(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if err := encoding.Null(); err != nil {
- return nil, fmt.Errorf("base64: unsupported encoding: %v", err)
- }
- return base64.StdEncoding.DecodeString(s)
- }()
- }
- },
- }},
- },
- "encoding/csv": {
- native: []*builtin{{
- Name: "Encode",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- x := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- buf := &bytes.Buffer{}
- w := csv.NewWriter(buf)
- iter, err := x.List()
- if err != nil {
- return "", err
- }
- for iter.Next() {
- row, err := iter.Value().List()
- if err != nil {
- return "", err
- }
- a := []string{}
- for row.Next() {
- col := row.Value()
- if str, err := col.String(); err == nil {
- a = append(a, str)
- } else {
- b, err := col.MarshalJSON()
- if err != nil {
- return "", err
- }
- a = append(a, string(b))
- }
- }
- _ = w.Write(a)
- }
- w.Flush()
- return buf.String(), nil
- }()
- }
- },
- }, {
- Name: "Decode",
- Params: []kind{bytesKind | stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- r := c.reader(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return csv.NewReader(r).ReadAll()
- }()
- }
- },
- }},
- },
- "encoding/hex": {
- native: []*builtin{{
- Name: "EncodedLen",
- Params: []kind{intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- n := c.int(0)
- if c.do() {
- c.ret = func() interface{} {
- return hex.EncodedLen(n)
- }()
- }
- },
- }, {
- Name: "DecodedLen",
- Params: []kind{intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x := c.int(0)
- if c.do() {
- c.ret = func() interface{} {
- return hex.DecodedLen(x)
- }()
- }
- },
- }, {
- Name: "Decode",
- Params: []kind{stringKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return hex.DecodeString(s)
- }()
- }
- },
- }, {
- Name: "Dump",
- Params: []kind{bytesKind | stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- return hex.Dump(data)
- }()
- }
- },
- }, {
- Name: "Encode",
- Params: []kind{bytesKind | stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- src := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- return hex.EncodeToString(src)
- }()
- }
- },
- }},
- },
- "encoding/json": {
- native: []*builtin{{
- Name: "Valid",
- Params: []kind{bytesKind | stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- return json.Valid(data)
- }()
- }
- },
- }, {
- Name: "Compact",
- Params: []kind{bytesKind | stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- src := c.bytes(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- dst := bytes.Buffer{}
- if err := json.Compact(&dst, src); err != nil {
- return "", err
- }
- return dst.String(), nil
- }()
- }
- },
- }, {
- Name: "Indent",
- Params: []kind{bytesKind | stringKind, stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- src, prefix, indent := c.bytes(0), c.string(1), c.string(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- dst := bytes.Buffer{}
- if err := json.Indent(&dst, src, prefix, indent); err != nil {
- return "", err
- }
- return dst.String(), nil
- }()
- }
- },
- }, {
- Name: "HTMLEscape",
- Params: []kind{bytesKind | stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- src := c.bytes(0)
- if c.do() {
- c.ret = func() interface{} {
- dst := &bytes.Buffer{}
- json.HTMLEscape(dst, src)
- return dst.String()
- }()
- }
- },
- }, {
- Name: "Marshal",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- v := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- b, err := json.Marshal(v)
- return string(b), err
- }()
- }
- },
- }, {
- Name: "MarshalStream",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- v := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
-
- iter, err := v.List()
- if err != nil {
- return "", err
- }
- buf := &bytes.Buffer{}
- for iter.Next() {
- b, err := json.Marshal(iter.Value())
- if err != nil {
- return "", err
- }
- buf.Write(b)
- buf.WriteByte('\n')
- }
- return buf.String(), nil
- }()
- }
- },
- }, {
- Name: "Unmarshal",
- Params: []kind{bytesKind | stringKind},
- Result: topKind,
- Func: func(c *callCtxt) {
- b := c.bytes(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if !json.Valid(b) {
- return nil, fmt.Errorf("json: invalid JSON")
- }
- expr, err := parser.ParseExpr("json", b)
- if err != nil {
-
- return nil, fmt.Errorf("json: could not parse JSON: %v", err)
- }
- return expr, nil
- }()
- }
- },
- }, {
- Name: "Validate",
- Params: []kind{bytesKind | stringKind, topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- b, v := c.bytes(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if !json.Valid(b) {
- return false, fmt.Errorf("json: invalid JSON")
- }
- r := internal.GetRuntimeNew(v).(*Runtime)
- inst, err := r.Compile("json.Validate", b)
- if err != nil {
- return false, err
- }
-
- t := inst.Value()
-
- v = v.Unify(t)
- if v.Err() != nil {
- return false, v.Err()
- }
- return true, nil
- }()
- }
- },
- }},
- },
- "encoding/yaml": {
- native: []*builtin{{
- Name: "Marshal",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- v := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if err := v.Validate(Concrete(true)); err != nil {
- if err := v.Validate(); err != nil {
- return "", err
- }
- return "", internal.ErrIncomplete
- }
- n := v.Syntax(Final(), Concrete(true))
- b, err := cueyaml.Encode(n)
- return string(b), err
- }()
- }
- },
- }, {
- Name: "MarshalStream",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- v := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
-
- iter, err := v.List()
- if err != nil {
- return "", err
- }
- buf := &bytes.Buffer{}
- for i := 0; iter.Next(); i++ {
- if i > 0 {
- buf.WriteString("---\n")
- }
- v := iter.Value()
- if err := v.Validate(Concrete(true)); err != nil {
- if err := v.Validate(); err != nil {
- return "", err
- }
- return "", internal.ErrIncomplete
- }
- n := v.Syntax(Final(), Concrete(true))
- b, err := cueyaml.Encode(n)
- if err != nil {
- return "", err
- }
- buf.Write(b)
- }
- return buf.String(), nil
- }()
- }
- },
- }, {
- Name: "Unmarshal",
- Params: []kind{bytesKind | stringKind},
- Result: topKind,
- Func: func(c *callCtxt) {
- data := c.bytes(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return yaml.Unmarshal("", data)
- }()
- }
- },
- }, {
- Name: "Validate",
- Params: []kind{bytesKind | stringKind, topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- b, v := c.bytes(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- d, err := yaml.NewDecoder("yaml.Validate", b)
- if err != nil {
- return false, err
- }
- r := internal.GetRuntimeNew(v).(*Runtime)
- for {
- expr, err := d.Decode()
- if err != nil {
- if err == io.EOF {
- return true, nil
- }
- return false, err
- }
-
- inst, err := r.CompileExpr(expr)
- if err != nil {
- return false, err
- }
-
- x := v.Unify(inst.Value())
- if err := x.Err(); err != nil {
- return false, err
- }
- if err := x.Validate(Concrete(true)); err != nil {
- return false, err
- }
-
- }
- }()
- }
- },
- }, {
- Name: "ValidatePartial",
- Params: []kind{bytesKind | stringKind, topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- b, v := c.bytes(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- d, err := yaml.NewDecoder("yaml.ValidatePartial", b)
- if err != nil {
- return false, err
- }
- r := internal.GetRuntimeNew(v).(*Runtime)
- for {
- expr, err := d.Decode()
- if err != nil {
- if err == io.EOF {
- return true, nil
- }
- return false, err
- }
-
- inst, err := r.CompileExpr(expr)
- if err != nil {
- return false, err
- }
-
- if x := v.Unify(inst.Value()); x.Err() != nil {
- return false, x.Err()
- }
- }
- }()
- }
- },
- }},
- },
- "html": {
- native: []*builtin{{
- Name: "Escape",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return html.EscapeString(s)
- }()
- }
- },
- }, {
- Name: "Unescape",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return html.UnescapeString(s)
- }()
- }
- },
- }},
- },
- "list": {
- native: []*builtin{{
- Name: "Drop",
- Params: []kind{listKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- x, n := c.list(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if n < 0 {
- return nil, fmt.Errorf("negative index")
- }
-
- if n > len(x) {
- return []Value{}, nil
- }
-
- return x[n:], nil
- }()
- }
- },
- }, {
- Name: "FlattenN",
- Params: []kind{topKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- xs, depth := c.value(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var flattenN func(Value, int) ([]Value, error)
- flattenN = func(xs Value, depth int) ([]Value, error) {
- var res []Value
- iter, err := xs.List()
- if err != nil {
- return nil, err
- }
- for iter.Next() {
- val, _ := iter.Value().Default()
- if val.Kind() == ListKind && depth != 0 {
- d := depth - 1
- values, err := flattenN(val, d)
- if err != nil {
- return nil, err
- }
- res = append(res, values...)
- } else {
- res = append(res, val)
- }
- }
- return res, nil
- }
- return flattenN(xs, depth)
- }()
- }
- },
- }, {
- Name: "Take",
- Params: []kind{listKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- x, n := c.list(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if n < 0 {
- return nil, fmt.Errorf("negative index")
- }
-
- if n > len(x) {
- return x, nil
- }
-
- return x[:n], nil
- }()
- }
- },
- }, {
- Name: "Slice",
- Params: []kind{listKind, intKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- x, i, j := c.list(0), c.int(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if i < 0 {
- return nil, fmt.Errorf("negative index")
- }
-
- if i > j {
- return nil, fmt.Errorf("invalid index: %v > %v", i, j)
- }
-
- if i > len(x) {
- return nil, fmt.Errorf("slice bounds out of range")
- }
-
- if j > len(x) {
- return nil, fmt.Errorf("slice bounds out of range")
- }
-
- return x[i:j], nil
- }()
- }
- },
- }, {
- Name: "MinItems",
- Params: []kind{listKind, intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- a, n := c.list(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
- return len(a) >= n
- }()
- }
- },
- }, {
- Name: "MaxItems",
- Params: []kind{listKind, intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- a, n := c.list(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
- return len(a) <= n
- }()
- }
- },
- }, {
- Name: "UniqueItems",
- Params: []kind{listKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- a := c.list(0)
- if c.do() {
- c.ret = func() interface{} {
- b := []string{}
- for _, v := range a {
- b = append(b, fmt.Sprint(v))
- }
- sort.Strings(b)
- for i := 1; i < len(b); i++ {
- if b[i-1] == b[i] {
- return false
- }
- }
- return true
- }()
- }
- },
- }, {
- Name: "Contains",
- Params: []kind{listKind, topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- a, v := c.list(0), c.value(1)
- if c.do() {
- c.ret = func() interface{} {
- for _, w := range a {
- if v.Equals(w) {
- return true
- }
- }
- return false
- }()
- }
- },
- }, {
- Name: "Avg",
- Params: []kind{listKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- xs := c.decimalList(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if 0 == len(xs) {
- return nil, fmt.Errorf("empty list")
- }
-
- s := apd.New(0, 0)
- for _, x := range xs {
- _, err := internal.BaseContext.Add(s, x, s)
- if err != nil {
- return nil, err
- }
- }
-
- var d apd.Decimal
- l := apd.New(int64(len(xs)), 0)
- _, err := internal.BaseContext.Quo(&d, s, l)
- if err != nil {
- return nil, err
- }
- return &d, nil
- }()
- }
- },
- }, {
- Name: "Max",
- Params: []kind{listKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- xs := c.decimalList(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if 0 == len(xs) {
- return nil, fmt.Errorf("empty list")
- }
-
- max := xs[0]
- for _, x := range xs[1:] {
- if -1 == max.Cmp(x) {
- max = x
- }
- }
- return max, nil
- }()
- }
- },
- }, {
- Name: "Min",
- Params: []kind{listKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- xs := c.decimalList(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if 0 == len(xs) {
- return nil, fmt.Errorf("empty list")
- }
-
- min := xs[0]
- for _, x := range xs[1:] {
- if +1 == min.Cmp(x) {
- min = x
- }
- }
- return min, nil
- }()
- }
- },
- }, {
- Name: "Product",
- Params: []kind{listKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- xs := c.decimalList(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- d := apd.New(1, 0)
- for _, x := range xs {
- _, err := internal.BaseContext.Mul(d, x, d)
- if err != nil {
- return nil, err
- }
- }
- return d, nil
- }()
- }
- },
- }, {
- Name: "Range",
- Params: []kind{numKind, numKind, numKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- start, limit, step := c.decimal(0), c.decimal(1), c.decimal(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if step.IsZero() {
- return nil, fmt.Errorf("step must be non zero")
- }
-
- if !step.Negative && +1 == start.Cmp(limit) {
- return nil, fmt.Errorf("end must be greater than start when step is positive")
- }
-
- if step.Negative && -1 == start.Cmp(limit) {
- return nil, fmt.Errorf("end must be less than start when step is negative")
- }
-
- var vals []*internal.Decimal
- num := start
- for {
- if !step.Negative && -1 != num.Cmp(limit) {
- break
- }
-
- if step.Negative && +1 != num.Cmp(limit) {
- break
- }
-
- vals = append(vals, num)
- d := apd.New(0, 0)
- _, err := internal.BaseContext.Add(d, step, num)
- if err != nil {
- return nil, err
- }
- num = d
- }
- return vals, nil
- }()
- }
- },
- }, {
- Name: "Sum",
- Params: []kind{listKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- xs := c.decimalList(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- d := apd.New(0, 0)
- for _, x := range xs {
- _, err := internal.BaseContext.Add(d, x, d)
- if err != nil {
- return nil, err
- }
- }
- return d, nil
- }()
- }
- },
- }, {
- Name: "Sort",
- Params: []kind{listKind, topKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- list, cmp := c.list(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- s := valueSorter{list, cmp, nil}
-
- sort.Sort(&s)
- return s.ret()
- }()
- }
- },
- }, {
- Name: "SortStable",
- Params: []kind{listKind, topKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- list, cmp := c.list(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- s := valueSorter{list, cmp, nil}
- sort.Stable(&s)
- return s.ret()
- }()
- }
- },
- }, {
- Name: "SortStrings",
- Params: []kind{listKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- a := c.strList(0)
- if c.do() {
- c.ret = func() interface{} {
- sort.Strings(a)
- return a
- }()
- }
- },
- }, {
- Name: "IsSorted",
- Params: []kind{listKind, topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- list, cmp := c.list(0), c.value(1)
- if c.do() {
- c.ret = func() interface{} {
- s := valueSorter{list, cmp, nil}
- return sort.IsSorted(&s)
- }()
- }
- },
- }, {
- Name: "IsSortedStrings",
- Params: []kind{listKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- a := c.strList(0)
- if c.do() {
- c.ret = func() interface{} {
- return sort.StringsAreSorted(a)
- }()
- }
- },
- }},
- cue: `{
- Comparer: {
- T: _
- less: bool
- x: T
- y: T
- }
- Ascending: {
- T: number | string
- less: true && x < y
- x: T
- y: T
- Comparer
- }
- Descending: {
- T: number | string
- less: x > y
- x: T
- y: T
- Comparer
- }
-}`,
- },
- "math": {
- native: []*builtin{{
- Name: "MaxExp",
- Const: "2147483647",
- }, {
- Name: "MinExp",
- Const: "-2147483648",
- }, {
- Name: "MaxPrec",
- Const: "4294967295",
- }, {
- Name: "ToNearestEven",
- Const: "0",
- }, {
- Name: "ToNearestAway",
- Const: "1",
- }, {
- Name: "ToZero",
- Const: "2",
- }, {
- Name: "AwayFromZero",
- Const: "3",
- }, {
- Name: "ToNegativeInf",
- Const: "4",
- }, {
- Name: "ToPositiveInf",
- Const: "5",
- }, {
- Name: "Below",
- Const: "-1",
- }, {
- Name: "Exact",
- Const: "0",
- }, {
- Name: "Above",
- Const: "1",
- }, {
- Name: "Jacobi",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x, y := c.bigInt(0), c.bigInt(1)
- if c.do() {
- c.ret = func() interface{} {
- return big.Jacobi(x, y)
- }()
- }
- },
- }, {
- Name: "MaxBase",
- Const: "62",
- }, {
- Name: "Floor",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Floor(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Ceil",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Ceil(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Trunc",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := roundTruncContext.RoundToIntegralExact(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Round",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := roundUpContext.RoundToIntegralExact(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "RoundToEven",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := roundEvenContext.RoundToIntegralExact(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "MultipleOf",
- Params: []kind{numKind, numKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- x, y := c.decimal(0), c.decimal(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d apd.Decimal
- cond, err := mulContext.Quo(&d, x, y)
- return !cond.Inexact(), err
- }()
- }
- },
- }, {
- Name: "Abs",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Abs(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Acosh",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Acosh(x)
- }()
- }
- },
- }, {
- Name: "Asin",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Asin(x)
- }()
- }
- },
- }, {
- Name: "Acos",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Acos(x)
- }()
- }
- },
- }, {
- Name: "Asinh",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Asinh(x)
- }()
- }
- },
- }, {
- Name: "Atan",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Atan(x)
- }()
- }
- },
- }, {
- Name: "Atan2",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- y, x := c.float64(0), c.float64(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Atan2(y, x)
- }()
- }
- },
- }, {
- Name: "Atanh",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Atanh(x)
- }()
- }
- },
- }, {
- Name: "Cbrt",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Cbrt(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "E",
- Const: "2.71828182845904523536028747135266249775724709369995957496696763",
- }, {
- Name: "Pi",
- Const: "3.14159265358979323846264338327950288419716939937510582097494459",
- }, {
- Name: "Phi",
- Const: "1.61803398874989484820458683436563811772030917980576286213544861",
- }, {
- Name: "Sqrt2",
- Const: "1.41421356237309504880168872420969807856967187537694807317667974",
- }, {
- Name: "SqrtE",
- Const: "1.64872127070012814684865078781416357165377610071014801157507931",
- }, {
- Name: "SqrtPi",
- Const: "1.77245385090551602729816748334114518279754945612238712821380779",
- }, {
- Name: "SqrtPhi",
- Const: "1.27201964951406896425242246173749149171560804184009624861664038",
- }, {
- Name: "Ln2",
- Const: "0.693147180559945309417232121458176568075500134360255254120680009",
- }, {
- Name: "Log2E",
- Const: "1.442695040888963407359924681001892137426645954152985934135449408",
- }, {
- Name: "Ln10",
- Const: "2.3025850929940456840179914546843642076011014886287729760333278",
- }, {
- Name: "Log10E",
- Const: "0.43429448190325182765112891891660508229439700580366656611445378",
- }, {
- Name: "Copysign",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x, y := c.decimal(0), c.decimal(1)
- if c.do() {
- c.ret = func() interface{} {
- var d internal.Decimal
- d.Set(x)
- d.Negative = y.Negative
- return &d
- }()
- }
- },
- }, {
- Name: "Dim",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x, y := c.decimal(0), c.decimal(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Sub(&d, x, y)
- if err != nil {
- return nil, err
- }
- if d.Negative {
- return zero, nil
- }
- return &d, nil
- }()
- }
- },
- }, {
- Name: "Erf",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Erf(x)
- }()
- }
- },
- }, {
- Name: "Erfc",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Erfc(x)
- }()
- }
- },
- }, {
- Name: "Erfinv",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Erfinv(x)
- }()
- }
- },
- }, {
- Name: "Erfcinv",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Erfcinv(x)
- }()
- }
- },
- }, {
- Name: "Exp",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Exp(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Exp2",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Pow(&d, two, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Expm1",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Expm1(x)
- }()
- }
- },
- }, {
- Name: "Gamma",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Gamma(x)
- }()
- }
- },
- }, {
- Name: "Hypot",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- p, q := c.float64(0), c.float64(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Hypot(p, q)
- }()
- }
- },
- }, {
- Name: "J0",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.J0(x)
- }()
- }
- },
- }, {
- Name: "Y0",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Y0(x)
- }()
- }
- },
- }, {
- Name: "J1",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.J1(x)
- }()
- }
- },
- }, {
- Name: "Y1",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Y1(x)
- }()
- }
- },
- }, {
- Name: "Jn",
- Params: []kind{intKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- n, x := c.int(0), c.float64(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Jn(n, x)
- }()
- }
- },
- }, {
- Name: "Yn",
- Params: []kind{intKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- n, x := c.int(0), c.float64(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Yn(n, x)
- }()
- }
- },
- }, {
- Name: "Ldexp",
- Params: []kind{numKind, intKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- frac, exp := c.float64(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Ldexp(frac, exp)
- }()
- }
- },
- }, {
- Name: "Log",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Ln(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Log10",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Log10(&d, x)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Log2",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d, ln2 internal.Decimal
- _, _ = apdContext.Ln(&ln2, two)
- _, err := apdContext.Ln(&d, x)
- if err != nil {
- return &d, err
- }
- _, err = apdContext.Quo(&d, &d, &ln2)
- return &d, nil
- }()
- }
- },
- }, {
- Name: "Log1p",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Log1p(x)
- }()
- }
- },
- }, {
- Name: "Logb",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Logb(x)
- }()
- }
- },
- }, {
- Name: "Ilogb",
- Params: []kind{numKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Ilogb(x)
- }()
- }
- },
- }, {
- Name: "Mod",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x, y := c.float64(0), c.float64(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Mod(x, y)
- }()
- }
- },
- }, {
- Name: "Pow",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x, y := c.decimal(0), c.decimal(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var d internal.Decimal
- _, err := apdContext.Pow(&d, x, y)
- return &d, err
- }()
- }
- },
- }, {
- Name: "Pow10",
- Params: []kind{intKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- n := c.int32(0)
- if c.do() {
- c.ret = func() interface{} {
- return apd.New(1, n)
- }()
- }
- },
- }, {
- Name: "Remainder",
- Params: []kind{numKind, numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x, y := c.float64(0), c.float64(1)
- if c.do() {
- c.ret = func() interface{} {
- return math.Remainder(x, y)
- }()
- }
- },
- }, {
- Name: "Signbit",
- Params: []kind{numKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- x := c.decimal(0)
- if c.do() {
- c.ret = func() interface{} {
- return x.Negative
- }()
- }
- },
- }, {
- Name: "Cos",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Cos(x)
- }()
- }
- },
- }, {
- Name: "Sin",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Sin(x)
- }()
- }
- },
- }, {
- Name: "Sinh",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Sinh(x)
- }()
- }
- },
- }, {
- Name: "Cosh",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Cosh(x)
- }()
- }
- },
- }, {
- Name: "Sqrt",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Sqrt(x)
- }()
- }
- },
- }, {
- Name: "Tan",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Tan(x)
- }()
- }
- },
- }, {
- Name: "Tanh",
- Params: []kind{numKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- x := c.float64(0)
- if c.do() {
- c.ret = func() interface{} {
- return math.Tanh(x)
- }()
- }
- },
- }},
- },
- "math/bits": {
- native: []*builtin{{
- Name: "Lsh",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x, n := c.bigInt(0), c.uint(1)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.Lsh(x, n)
- return &z
- }()
- }
- },
- }, {
- Name: "Rsh",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x, n := c.bigInt(0), c.uint(1)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.Rsh(x, n)
- return &z
- }()
- }
- },
- }, {
- Name: "At",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x, i := c.bigInt(0), c.uint(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if i > math.MaxInt32 {
- return 0, fmt.Errorf("bit index too large")
- }
- return x.Bit(int(i)), nil
- }()
- }
- },
- }, {
- Name: "Set",
- Params: []kind{intKind, intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x, i, bit := c.bigInt(0), c.int(1), c.uint(2)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.SetBit(x, i, bit)
- return &z
- }()
- }
- },
- }, {
- Name: "And",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- a, b := c.bigInt(0), c.bigInt(1)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.And(a, b)
- return &z
- }()
- }
- },
- }, {
- Name: "Or",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- a, b := c.bigInt(0), c.bigInt(1)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.Or(a, b)
- return &z
- }()
- }
- },
- }, {
- Name: "Xor",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- a, b := c.bigInt(0), c.bigInt(1)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.Xor(a, b)
- return &z
- }()
- }
- },
- }, {
- Name: "Clear",
- Params: []kind{intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- a, b := c.bigInt(0), c.bigInt(1)
- if c.do() {
- c.ret = func() interface{} {
- var z big.Int
- z.AndNot(a, b)
- return &z
- }()
- }
- },
- }, {
- Name: "OnesCount",
- Params: []kind{intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x := c.bigInt(0)
- if c.do() {
- c.ret = func() interface{} {
- var count int
- for _, w := range x.Bits() {
- count += bits.OnesCount64(uint64(w))
- }
- return count
- }()
- }
- },
- }, {
- Name: "Len",
- Params: []kind{intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- x := c.bigInt(0)
- if c.do() {
- c.ret = func() interface{} {
- return x.BitLen()
- }()
- }
- },
- }},
- },
- "net": {
- native: []*builtin{{
- Name: "SplitHostPort",
- Params: []kind{stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- host, port, err := net.SplitHostPort(s)
- if err != nil {
- return nil, err
- }
- return []string{host, port}, nil
- }()
- }
- },
- }, {
- Name: "JoinHostPort",
- Params: []kind{topKind, topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- host, port := c.value(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- var err error
- hostStr := ""
- switch host.Kind() {
- case ListKind:
- ipdata := netGetIP(host)
- if len(ipdata) != 4 && len(ipdata) != 16 {
- err = fmt.Errorf("invalid host %q", host)
- }
- hostStr = ipdata.String()
- case BytesKind:
- var b []byte
- b, err = host.Bytes()
- hostStr = string(b)
- default:
- hostStr, err = host.String()
- }
- if err != nil {
- return "", err
- }
-
- portStr := ""
- switch port.Kind() {
- case StringKind:
- portStr, err = port.String()
- case BytesKind:
- var b []byte
- b, err = port.Bytes()
- portStr = string(b)
- default:
- var i int64
- i, err = port.Int64()
- portStr = strconv.Itoa(int(i))
- }
- if err != nil {
- return "", err
- }
-
- return net.JoinHostPort(hostStr, portStr), nil
- }()
- }
- },
- }, {
- Name: "FQDN",
- Params: []kind{stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- for i := 0; i < len(s); i++ {
- if s[i] >= utf8.RuneSelf {
- return false
- }
- }
- _, err := idnaProfile.ToASCII(s)
- return err == nil
- }()
- }
- },
- }, {
- Name: "IPv4len",
- Const: "4",
- }, {
- Name: "IPv6len",
- Const: "16",
- }, {
- Name: "ParseIP",
- Params: []kind{stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- goip := net.ParseIP(s)
- if goip == nil {
- return nil, fmt.Errorf("invalid IP address %q", s)
- }
- return netToList(goip), nil
- }()
- }
- },
- }, {
- Name: "IPv4",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
-
- return netGetIP(ip).To4() != nil
- }()
- }
- },
- }, {
- Name: "IP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
-
- return netGetIP(ip) != nil
- }()
- }
- },
- }, {
- Name: "LoopbackIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsLoopback()
- }()
- }
- },
- }, {
- Name: "MulticastIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsMulticast()
- }()
- }
- },
- }, {
- Name: "InterfaceLocalMulticastIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsInterfaceLocalMulticast()
- }()
- }
- },
- }, {
- Name: "LinkLocalMulticastIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsLinkLocalMulticast()
- }()
- }
- },
- }, {
- Name: "LinkLocalUnicastIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsLinkLocalUnicast()
- }()
- }
- },
- }, {
- Name: "GlobalUnicastIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsGlobalUnicast()
- }()
- }
- },
- }, {
- Name: "UnspecifiedIP",
- Params: []kind{topKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret = func() interface{} {
- return netGetIP(ip).IsUnspecified()
- }()
- }
- },
- }, {
- Name: "ToIP4",
- Params: []kind{topKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- ipdata := netGetIP(ip)
- if ipdata == nil {
- return nil, fmt.Errorf("invalid IP %q", ip)
- }
- ipv4 := ipdata.To4()
- if ipv4 == nil {
- return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata)
- }
- return netToList(ipv4), nil
- }()
- }
- },
- }, {
- Name: "ToIP16",
- Params: []kind{topKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- ipdata := netGetIP(ip)
- if ipdata == nil {
- return nil, fmt.Errorf("invalid IP %q", ip)
- }
- return netToList(ipdata), nil
- }()
- }
- },
- }, {
- Name: "IPString",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- ip := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- ipdata := netGetIP(ip)
- if ipdata == nil {
- return "", fmt.Errorf("invalid IP %q", ip)
- }
- return ipdata.String(), nil
- }()
- }
- },
- }},
- },
- "path": {
- native: []*builtin{{
- Name: "Split",
- Params: []kind{stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- path := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- file, dir := split(path)
- return []string{file, dir}
- }()
- }
- },
- }, {
- Name: "Match",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- pattern, name := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return path.Match(pattern, name)
- }()
- }
- },
- }, {
- Name: "Clean",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- path := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return pathClean(path)
- }()
- }
- },
- }, {
- Name: "Ext",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- path := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return pathExt(path)
- }()
- }
- },
- }, {
- Name: "Base",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- path := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return pathBase(path)
- }()
- }
- },
- }, {
- Name: "IsAbs",
- Params: []kind{stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- path := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return pathIsAbs(path)
- }()
- }
- },
- }, {
- Name: "Dir",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- path := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return pathDir(path)
- }()
- }
- },
- }},
- },
- "regexp": {
- native: []*builtin{{
- Name: "Valid",
- Params: []kind{stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- pattern := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- _, err := regexp.Compile(pattern)
- return err == nil, err
- }()
- }
- },
- }, {
- Name: "Find",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- pattern, s := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return "", err
- }
- m := re.FindStringIndex(s)
- if m == nil {
- return "", errNoMatch
- }
- return s[m[0]:m[1]], nil
- }()
- }
- },
- }, {
- Name: "FindAll",
- Params: []kind{stringKind, stringKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- pattern, s, n := c.string(0), c.string(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return nil, err
- }
- m := re.FindAllString(s, n)
- if m == nil {
- return nil, errNoMatch
- }
- return m, nil
- }()
- }
- },
- }, {
- Name: "FindSubmatch",
- Params: []kind{stringKind, stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- pattern, s := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return nil, err
- }
- m := re.FindStringSubmatch(s)
- if m == nil {
- return nil, errNoMatch
- }
- return m, nil
- }()
- }
- },
- }, {
- Name: "FindAllSubmatch",
- Params: []kind{stringKind, stringKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- pattern, s, n := c.string(0), c.string(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return nil, err
- }
- m := re.FindAllStringSubmatch(s, n)
- if m == nil {
- return nil, errNoMatch
- }
- return m, nil
- }()
- }
- },
- }, {
- Name: "FindNamedSubmatch",
- Params: []kind{stringKind, stringKind},
- Result: structKind,
- Func: func(c *callCtxt) {
- pattern, s := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return nil, err
- }
- names := re.SubexpNames()
- if len(names) == 0 {
- return nil, errNoNamedGroup
- }
- m := re.FindStringSubmatch(s)
- if m == nil {
- return nil, errNoMatch
- }
- r := make(map[string]string, len(names)-1)
- for k, name := range names {
- if name != "" {
- r[name] = m[k]
- }
- }
- return r, nil
- }()
- }
- },
- }, {
- Name: "FindAllNamedSubmatch",
- Params: []kind{stringKind, stringKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- pattern, s, n := c.string(0), c.string(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- re, err := regexp.Compile(pattern)
- if err != nil {
- return nil, err
- }
- names := re.SubexpNames()
- if len(names) == 0 {
- return nil, errNoNamedGroup
- }
- m := re.FindAllStringSubmatch(s, n)
- if m == nil {
- return nil, errNoMatch
- }
- result := make([]map[string]string, len(m))
- for i, m := range m {
- r := make(map[string]string, len(names)-1)
- for k, name := range names {
- if name != "" {
- r[name] = m[k]
- }
- }
- result[i] = r
- }
- return result, nil
- }()
- }
- },
- }, {
- Name: "Match",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- pattern, s := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return regexp.MatchString(pattern, s)
- }()
- }
- },
- }, {
- Name: "QuoteMeta",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return regexp.QuoteMeta(s)
- }()
- }
- },
- }},
- },
- "strconv": {
- native: []*builtin{{
- Name: "Unquote",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return literal.Unquote(s)
- }()
- }
- },
- }, {
- Name: "ParseBool",
- Params: []kind{stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- str := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return strconv.ParseBool(str)
- }()
- }
- },
- }, {
- Name: "FormatBool",
- Params: []kind{boolKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- b := c.bool(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.FormatBool(b)
- }()
- }
- },
- }, {
- Name: "ParseFloat",
- Params: []kind{stringKind, intKind},
- Result: numKind,
- Func: func(c *callCtxt) {
- s, bitSize := c.string(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return strconv.ParseFloat(s, bitSize)
- }()
- }
- },
- }, {
- Name: "IntSize",
- Const: "64",
- }, {
- Name: "ParseUint",
- Params: []kind{stringKind, intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, base, bitSize := c.string(0), c.int(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return strconv.ParseUint(s, base, bitSize)
- }()
- }
- },
- }, {
- Name: "ParseInt",
- Params: []kind{stringKind, intKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, base, bitSize := c.string(0), c.int(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return strconv.ParseInt(s, base, bitSize)
- }()
- }
- },
- }, {
- Name: "Atoi",
- Params: []kind{stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return strconv.Atoi(s)
- }()
- }
- },
- }, {
- Name: "FormatFloat",
- Params: []kind{numKind, intKind, intKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- f, fmt, prec, bitSize := c.float64(0), c.byte(1), c.int(2), c.int(3)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.FormatFloat(f, fmt, prec, bitSize)
- }()
- }
- },
- }, {
- Name: "FormatUint",
- Params: []kind{intKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- i, base := c.uint64(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.FormatUint(i, base)
- }()
- }
- },
- }, {
- Name: "FormatInt",
- Params: []kind{intKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- i, base := c.int64(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.FormatInt(i, base)
- }()
- }
- },
- }, {
- Name: "Quote",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.Quote(s)
- }()
- }
- },
- }, {
- Name: "QuoteToASCII",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.QuoteToASCII(s)
- }()
- }
- },
- }, {
- Name: "QuoteToGraphic",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.QuoteToGraphic(s)
- }()
- }
- },
- }, {
- Name: "QuoteRune",
- Params: []kind{intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- r := c.rune(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.QuoteRune(r)
- }()
- }
- },
- }, {
- Name: "QuoteRuneToASCII",
- Params: []kind{intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- r := c.rune(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.QuoteRuneToASCII(r)
- }()
- }
- },
- }, {
- Name: "QuoteRuneToGraphic",
- Params: []kind{intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- r := c.rune(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.QuoteRuneToGraphic(r)
- }()
- }
- },
- }, {
- Name: "IsPrint",
- Params: []kind{intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- r := c.rune(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.IsPrint(r)
- }()
- }
- },
- }, {
- Name: "IsGraphic",
- Params: []kind{intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- r := c.rune(0)
- if c.do() {
- c.ret = func() interface{} {
- return strconv.IsGraphic(r)
- }()
- }
- },
- }},
- },
- "strings": {
- native: []*builtin{{
- Name: "ByteAt",
- Params: []kind{bytesKind | stringKind, intKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- b, i := c.bytes(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if i < 0 || i >= len(b) {
- return 0, fmt.Errorf("index out of range")
- }
- return b[i], nil
- }()
- }
- },
- }, {
- Name: "ByteSlice",
- Params: []kind{bytesKind | stringKind, intKind, intKind},
- Result: bytesKind | stringKind,
- Func: func(c *callCtxt) {
- b, start, end := c.bytes(0), c.int(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if start < 0 || start > end || end > len(b) {
- return nil, fmt.Errorf("index out of range")
- }
- return b[start:end], nil
- }()
- }
- },
- }, {
- Name: "Runes",
- Params: []kind{stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return []rune(s)
- }()
- }
- },
- }, {
- Name: "MinRunes",
- Params: []kind{stringKind, intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s, min := c.string(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
-
- return len([]rune(s)) >= min
- }()
- }
- },
- }, {
- Name: "MaxRunes",
- Params: []kind{stringKind, intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s, max := c.string(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
-
- return len([]rune(s)) <= max
- }()
- }
- },
- }, {
- Name: "ToTitle",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
-
- prev := ' '
- return strings.Map(
- func(r rune) rune {
- if unicode.IsSpace(prev) {
- prev = r
- return unicode.ToTitle(r)
- }
- prev = r
- return r
- },
- s)
- }()
- }
- },
- }, {
- Name: "ToCamel",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
-
- prev := ' '
- return strings.Map(
- func(r rune) rune {
- if unicode.IsSpace(prev) {
- prev = r
- return unicode.ToLower(r)
- }
- prev = r
- return r
- },
- s)
- }()
- }
- },
- }, {
- Name: "SliceRunes",
- Params: []kind{stringKind, intKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, start, end := c.string(0), c.int(1), c.int(2)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- runes := []rune(s)
- if start < 0 || start > end || end > len(runes) {
- return "", fmt.Errorf("index out of range")
- }
- return string(runes[start:end]), nil
- }()
- }
- },
- }, {
- Name: "Compare",
- Params: []kind{stringKind, stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- a, b := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Compare(a, b)
- }()
- }
- },
- }, {
- Name: "Count",
- Params: []kind{stringKind, stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, substr := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Count(s, substr)
- }()
- }
- },
- }, {
- Name: "Contains",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s, substr := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Contains(s, substr)
- }()
- }
- },
- }, {
- Name: "ContainsAny",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s, chars := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.ContainsAny(s, chars)
- }()
- }
- },
- }, {
- Name: "LastIndex",
- Params: []kind{stringKind, stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, substr := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.LastIndex(s, substr)
- }()
- }
- },
- }, {
- Name: "IndexAny",
- Params: []kind{stringKind, stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, chars := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.IndexAny(s, chars)
- }()
- }
- },
- }, {
- Name: "LastIndexAny",
- Params: []kind{stringKind, stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, chars := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.LastIndexAny(s, chars)
- }()
- }
- },
- }, {
- Name: "SplitN",
- Params: []kind{stringKind, stringKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s, sep, n := c.string(0), c.string(1), c.int(2)
- if c.do() {
- c.ret = func() interface{} {
- return strings.SplitN(s, sep, n)
- }()
- }
- },
- }, {
- Name: "SplitAfterN",
- Params: []kind{stringKind, stringKind, intKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s, sep, n := c.string(0), c.string(1), c.int(2)
- if c.do() {
- c.ret = func() interface{} {
- return strings.SplitAfterN(s, sep, n)
- }()
- }
- },
- }, {
- Name: "Split",
- Params: []kind{stringKind, stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s, sep := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Split(s, sep)
- }()
- }
- },
- }, {
- Name: "SplitAfter",
- Params: []kind{stringKind, stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s, sep := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.SplitAfter(s, sep)
- }()
- }
- },
- }, {
- Name: "Fields",
- Params: []kind{stringKind},
- Result: listKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Fields(s)
- }()
- }
- },
- }, {
- Name: "Join",
- Params: []kind{listKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- elems, sep := c.strList(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Join(elems, sep)
- }()
- }
- },
- }, {
- Name: "HasPrefix",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s, prefix := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.HasPrefix(s, prefix)
- }()
- }
- },
- }, {
- Name: "HasSuffix",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s, suffix := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.HasSuffix(s, suffix)
- }()
- }
- },
- }, {
- Name: "Repeat",
- Params: []kind{stringKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, count := c.string(0), c.int(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Repeat(s, count)
- }()
- }
- },
- }, {
- Name: "ToUpper",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strings.ToUpper(s)
- }()
- }
- },
- }, {
- Name: "ToLower",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strings.ToLower(s)
- }()
- }
- },
- }, {
- Name: "Trim",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, cutset := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Trim(s, cutset)
- }()
- }
- },
- }, {
- Name: "TrimLeft",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, cutset := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.TrimLeft(s, cutset)
- }()
- }
- },
- }, {
- Name: "TrimRight",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, cutset := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.TrimRight(s, cutset)
- }()
- }
- },
- }, {
- Name: "TrimSpace",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return strings.TrimSpace(s)
- }()
- }
- },
- }, {
- Name: "TrimPrefix",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, prefix := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.TrimPrefix(s, prefix)
- }()
- }
- },
- }, {
- Name: "TrimSuffix",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, suffix := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.TrimSuffix(s, suffix)
- }()
- }
- },
- }, {
- Name: "Replace",
- Params: []kind{stringKind, stringKind, stringKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s, old, new, n := c.string(0), c.string(1), c.string(2), c.int(3)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Replace(s, old, new, n)
- }()
- }
- },
- }, {
- Name: "Index",
- Params: []kind{stringKind, stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s, substr := c.string(0), c.string(1)
- if c.do() {
- c.ret = func() interface{} {
- return strings.Index(s, substr)
- }()
- }
- },
- }},
- },
- "struct": {
- native: []*builtin{{
- Name: "MinFields",
- Params: []kind{structKind, intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- object, n := c.structVal(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- iter := object.Fields(Hidden(false), Optional(false))
- count := 0
- for iter.Next() {
- count++
- }
- return count >= n, nil
- }()
- }
- },
- }, {
- Name: "MaxFields",
- Params: []kind{structKind, intKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- object, n := c.structVal(0), c.int(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- iter := object.Fields(Hidden(false), Optional(false))
- count := 0
- for iter.Next() {
- count++
- }
- return count <= n, nil
- }()
- }
- },
- }},
- },
- "text/tabwriter": {
- native: []*builtin{{
- Name: "Write",
- Params: []kind{topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- data := c.value(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- buf := &bytes.Buffer{}
- tw := tabwriter.NewWriter(buf, 0, 4, 1, ' ', 0)
-
- write := func(v Value) error {
- b, err := v.Bytes()
- if err != nil {
- return err
- }
- _, err = tw.Write(b)
- if err != nil {
- return err
- }
- return nil
- }
-
- switch data.Kind() {
- case BytesKind, StringKind:
- if err := write(data); err != nil {
- return "", err
- }
- case ListKind:
- for i, _ := data.List(); i.Next(); {
- if err := write(i.Value()); err != nil {
- return "", err
- }
- _, _ = tw.Write([]byte{'\n'})
- }
- default:
- return "", fmt.Errorf("tabwriter.Write: unsupported type %v", data.Kind())
- }
-
- err := tw.Flush()
- return buf.String(), err
- }()
- }
- },
- }},
- },
- "text/template": {
- native: []*builtin{{
- Name: "Execute",
- Params: []kind{stringKind, topKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- templ, data := c.string(0), c.value(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- t, err := template.New("").Parse(templ)
- if err != nil {
- return "", err
- }
- var x interface{}
- if err := data.Decode(&x); err != nil {
- return "", err
- }
- buf := &bytes.Buffer{}
- if err := t.Execute(buf, x); err != nil {
- return "", err
- }
- return buf.String(), nil
- }()
- }
- },
- }, {
- Name: "HTMLEscape",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return template.HTMLEscapeString(s)
- }()
- }
- },
- }, {
- Name: "JSEscape",
- Params: []kind{stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret = func() interface{} {
- return template.JSEscapeString(s)
- }()
- }
- },
- }},
- },
- "time": {
- native: []*builtin{{
- Name: "Nanosecond",
- Const: "1",
- }, {
- Name: "Microsecond",
- Const: "1000",
- }, {
- Name: "Millisecond",
- Const: "1000000",
- }, {
- Name: "Second",
- Const: "1000000000",
- }, {
- Name: "Minute",
- Const: "60000000000",
- }, {
- Name: "Hour",
- Const: "3600000000000",
- }, {
- Name: "Duration",
- Params: []kind{stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- if _, err := time.ParseDuration(s); err != nil {
- return false, err
- }
- return true, nil
- }()
- }
- },
- }, {
- Name: "ParseDuration",
- Params: []kind{stringKind},
- Result: intKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- d, err := time.ParseDuration(s)
- if err != nil {
- return 0, err
- }
- return int64(d), nil
- }()
- }
- },
- }, {
- Name: "ANSIC",
- Const: "\"Mon Jan _2 15:04:05 2006\"",
- }, {
- Name: "UnixDate",
- Const: "\"Mon Jan _2 15:04:05 MST 2006\"",
- }, {
- Name: "RubyDate",
- Const: "\"Mon Jan 02 15:04:05 -0700 2006\"",
- }, {
- Name: "RFC822",
- Const: "\"02 Jan 06 15:04 MST\"",
- }, {
- Name: "RFC822Z",
- Const: "\"02 Jan 06 15:04 -0700\"",
- }, {
- Name: "RFC850",
- Const: "\"Monday, 02-Jan-06 15:04:05 MST\"",
- }, {
- Name: "RFC1123",
- Const: "\"Mon, 02 Jan 2006 15:04:05 MST\"",
- }, {
- Name: "RFC1123Z",
- Const: "\"Mon, 02 Jan 2006 15:04:05 -0700\"",
- }, {
- Name: "RFC3339",
- Const: "\"2006-01-02T15:04:05Z07:00\"",
- }, {
- Name: "RFC3339Nano",
- Const: "\"2006-01-02T15:04:05.999999999Z07:00\"",
- }, {
- Name: "RFC3339Date",
- Const: "\"2006-01-02\"",
- }, {
- Name: "Kitchen",
- Const: "\"3:04PM\"",
- }, {
- Name: "Kitchen24",
- Const: "\"15:04\"",
- }, {
- Name: "January",
- Const: "1",
- }, {
- Name: "February",
- Const: "2",
- }, {
- Name: "March",
- Const: "3",
- }, {
- Name: "April",
- Const: "4",
- }, {
- Name: "May",
- Const: "5",
- }, {
- Name: "June",
- Const: "6",
- }, {
- Name: "July",
- Const: "7",
- }, {
- Name: "August",
- Const: "8",
- }, {
- Name: "September",
- Const: "9",
- }, {
- Name: "October",
- Const: "10",
- }, {
- Name: "November",
- Const: "11",
- }, {
- Name: "December",
- Const: "12",
- }, {
- Name: "Sunday",
- Const: "0",
- }, {
- Name: "Monday",
- Const: "1",
- }, {
- Name: "Tuesday",
- Const: "2",
- }, {
- Name: "Wednesday",
- Const: "3",
- }, {
- Name: "Thursday",
- Const: "4",
- }, {
- Name: "Friday",
- Const: "5",
- }, {
- Name: "Saturday",
- Const: "6",
- }, {
- Name: "Time",
- Params: []kind{stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- s := c.string(0)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return timeFormat(s, time.RFC3339Nano)
- }()
- }
- },
- }, {
- Name: "Format",
- Params: []kind{stringKind, stringKind},
- Result: boolKind,
- Func: func(c *callCtxt) {
- value, layout := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- return timeFormat(value, layout)
- }()
- }
- },
- }, {
- Name: "Parse",
- Params: []kind{stringKind, stringKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- layout, value := c.string(0), c.string(1)
- if c.do() {
- c.ret, c.err = func() (interface{}, error) {
- t, err := time.Parse(layout, value)
- if err != nil {
- return "", err
- }
- return t.UTC().Format(time.RFC3339Nano), nil
- }()
- }
- },
- }, {
- Name: "Unix",
- Params: []kind{intKind, intKind},
- Result: stringKind,
- Func: func(c *callCtxt) {
- sec, nsec := c.int64(0), c.int64(1)
- if c.do() {
- c.ret = func() interface{} {
- t := time.Unix(sec, nsec)
- return t.UTC().Format(time.RFC3339Nano)
- }()
- }
- },
- }},
- },
- "tool": {
- native: []*builtin{},
- cue: `{
- Command: {
- $usage?: string
- $short?: string
- $long?: string
- Tasks
- }
- Tasks: Task | {
- [name=string]: Tasks
- }
- Task: {
- $type: "tool.Task"
- $id: =~"\\."
- $after?: Task | [...Task]
- }
- Name: =~"^\\PL([-](\\PL|\\PN))*$"
-}`,
- },
- "tool/cli": {
- native: []*builtin{},
- cue: `{
- Print: {
- $id: *"tool/cli.Print" | "print"
- text: string
- }
-}`,
- },
- "tool/exec": {
- native: []*builtin{},
- cue: `{
- Run: {
- $id: *"tool/exec.Run" | "exec"
- cmd: string | [string, ...string]
- env: {
- [string]: string | [...=~"="]
- }
- stdout: *null | string | bytes
- stderr: *null | string | bytes
- stdin: *null | string | bytes
- success: bool
- }
-}`,
- },
- "tool/file": {
- native: []*builtin{},
- cue: `{
- Read: {
- $id: "tool/file.Read"
- filename: !=""
- contents: *bytes | string
- }
- Create: {
- $id: "tool/file.Create"
- filename: !=""
- contents: bytes | string
- permissions: int | *420
- }
- Append: {
- $id: "tool/file.Append"
- filename: !=""
- contents: bytes | string
- permissions: int | *420
- }
- Glob: {
- $id: "tool/file.Glob"
- glob: !=""
- files: [...string]
- }
-}`,
- },
- "tool/http": {
- native: []*builtin{},
- cue: `{
- Get: Do & {
- method: "GET"
- }
- Do: {
- $id: *"tool/http.Do" | "http"
- method: string
- response: {
- body: *bytes | string
- header: {
- [string]: string | [...string]
- }
- trailer: {
- [string]: string | [...string]
- }
- status: string
- statusCode: int
- }
- url: string
- request: {
- body: *bytes | string
- header: {
- [string]: string | [...string]
- }
- trailer: {
- [string]: string | [...string]
- }
- }
- }
- Post: Do & {
- method: "POST"
- }
- Put: Do & {
- method: "PUT"
- }
- Delete: Do & {
- method: "DELETE"
- }
-}`,
- },
- "tool/os": {
- native: []*builtin{},
- cue: `{
- Name: !="" & !~"^[$]"
- Value: bool | number | *string | null
- Setenv: {
- $id: "tool/os.Setenv"
- {[Name]: Value}
- }
- Getenv: {
- $id: "tool/os.Getenv"
- {[Name]: Value}
- }
- Environ: {
- $id: "tool/os.Environ"
- {[Name]: Value}
- }
- Clearenv: {
- $id: "tool/os.Clearenv"
- }
-}`,
- },
-}
diff --git a/internal/legacy/cue/builtinutil.go b/internal/legacy/cue/builtinutil.go
deleted file mode 100644
index e226cfa..0000000
--- a/internal/legacy/cue/builtinutil.go
+++ /dev/null
@@ -1,74 +0,0 @@
-// Copyright 2019 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 cue
-
-import (
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/convert"
-)
-
-// TODO: this code could be generated, but currently isn't.
-
-type valueSorter struct {
- a []Value
- cmp Value
- err error
-}
-
-func (s *valueSorter) ret() ([]Value, error) {
- if s.err != nil {
- return nil, s.err
- }
- // The input slice is already a copy and that we can modify it safely.
- return s.a, nil
-}
-
-func (s *valueSorter) Len() int { return len(s.a) }
-func (s *valueSorter) Swap(i, j int) { s.a[i], s.a[j] = s.a[j], s.a[i] }
-func (s *valueSorter) Less(i, j int) bool {
- ctx := s.cmp.ctx()
- x := fill(ctx, s.cmp.v, s.a[i], "x")
- x = fill(ctx, x, s.a[j], "y")
- ctx.opCtx.Unify(ctx.opCtx, x, adt.Finalized) // TODO: remove.
- v := Value{s.cmp.idx, x}
- isLess, err := v.Lookup("less").Bool()
- if err != nil && s.err == nil {
- s.err = err
- return true
- }
- return isLess
-}
-
-// fill creates a new value with the old value unified with the given value.
-// TODO: consider making this a method on Value.
-func fill(ctx *context, v *adt.Vertex, x interface{}, path ...string) *adt.Vertex {
- for i := len(path) - 1; i >= 0; i-- {
- x = map[string]interface{}{path[i]: x}
- }
- value := convertVal(ctx, v, false, x)
-
- w := adt.ToVertex(value)
- n := &adt.Vertex{Label: v.Label}
- n.AddConjunct(adt.MakeConjunct(nil, v))
- n.AddConjunct(adt.MakeConjunct(nil, w))
-
- // n.Add(v)
- // n.Add(w)
- return n
-}
-
-func convertVal(ctx *context, src source, nullIsTop bool, x interface{}) adt.Value {
- return convert.GoValueToValue(ctx.opCtx, x, nullIsTop)
-}
diff --git a/internal/legacy/cue/context.go b/internal/legacy/cue/context.go
deleted file mode 100644
index 3f00329..0000000
--- a/internal/legacy/cue/context.go
+++ /dev/null
@@ -1,96 +0,0 @@
-// 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 cue
-
-import (
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/debug"
- "cuelang.org/go/internal/core/eval"
- "github.com/cockroachdb/apd/v2"
-)
-
-// context manages evaluation state.
-type context struct {
- opCtx *adt.OpContext
- *apd.Context
- *index
-}
-
-var baseContext apd.Context
-
-func init() {
- baseContext = apd.BaseContext
- baseContext.Precision = 24
-}
-
-// newContext returns a new evaluation context.
-func (idx *index) newContext() *context {
- c := &context{
- Context: &baseContext,
- index: idx,
- }
- if idx != nil {
- c.opCtx = eval.NewContext(idx.Runtime, nil)
- }
- return c
-}
-
-func debugStr(ctx *context, v adt.Node) string {
- return debug.NodeString(ctx.opCtx, v, nil)
-}
-
-func (c *context) str(v adt.Node) string {
- return debugStr(c, v)
-}
-
-func (c *context) mkErr(src source, args ...interface{}) *bottom {
- return c.index.mkErr(src, args...)
-}
-
-func (c *context) vertex(v *adt.Vertex) *adt.Vertex {
- return v
-}
-
-// vertex returns the evaluated vertex of v.
-func (v Value) vertex(ctx *context) *adt.Vertex {
- return ctx.vertex(v.v)
-}
-
-// eval returns the evaluated value. This may not be the vertex.
-//
-// Deprecated: use ctx.value
-func (v Value) eval(ctx *context) adt.Value {
- if v.v == nil {
- panic("undefined value")
- }
- x := ctx.manifest(v.v)
- switch x.Kind() {
- case adt.StructKind, adt.ListKind:
- return x
- default:
- return x.Value
- }
-}
-
-// func (v Value) evalFull(u value) (Value, adt.Value) {
-// ctx := v.ctx()
-// x := ctx.manifest(u)
-// }
-
-// TODO: change from Vertex to Vertex.
-func (c *context) manifest(v *adt.Vertex) *adt.Vertex {
- v.Finalize(c.opCtx)
- return v
-}
diff --git a/internal/legacy/cue/errors.go b/internal/legacy/cue/errors.go
deleted file mode 100644
index 21ed886..0000000
--- a/internal/legacy/cue/errors.go
+++ /dev/null
@@ -1,182 +0,0 @@
-// 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 cue
-
-import (
- "fmt"
- "reflect"
-
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal/core/adt"
-)
-
-func (v Value) appendErr(err errors.Error, b *bottom) errors.Error {
- return &valueError{
- v: v,
- err: &adt.Bottom{
- Err: errors.Append(err, b.Err),
- },
- }
-}
-
-func (v Value) toErr(b *bottom) (err errors.Error) {
- return &valueError{v: v, err: b}
-}
-
-var _ errors.Error = &valueError{}
-
-// A valueError is returned as a result of evaluating a value.
-type valueError struct {
- v Value
- err *bottom
-}
-
-func (e *valueError) Error() string {
- return errors.String(e)
-}
-
-func (e *valueError) Position() token.Pos {
- src := e.err.Source()
- if src == nil {
- return token.NoPos
- }
- return src.Pos()
-}
-
-func (e *valueError) InputPositions() []token.Pos {
- if e.err.Err == nil {
- return nil
- }
- return e.err.Err.InputPositions()
-}
-
-func (e *valueError) Msg() (string, []interface{}) {
- if e.err.Err == nil {
- return "", nil
- }
- return e.err.Err.Msg()
-}
-
-func (e *valueError) Path() (a []string) {
- return e.v.appendPath(nil)
-}
-
-type errCode = adt.ErrorCode
-
-const (
- codeNone errCode = 0
- codeFatal = adt.EvalError
- codeNotExist = adt.NotExistError
- codeTypeError = adt.EvalError
- codeIncomplete = adt.IncompleteError
- codeUser = adt.UserError
- codeCycle = adt.CycleError
-)
-
-func isIncomplete(v value) bool {
- if err, ok := v.(*bottom); ok {
- return err.Code == codeIncomplete || err.Code == codeCycle
- }
- return false
-}
-
-func isLiteralBottom(v value) bool {
- if err, ok := v.(*bottom); ok {
- return err.Code == codeUser
- }
- return false
-}
-
-var errNotExists = &adt.Bottom{
- Code: codeNotExist,
- Err: errors.Newf(token.NoPos, "undefined value"),
-}
-
-func exists(v value) bool {
- if err, ok := v.(*bottom); ok {
- return err.Code != codeNotExist
- }
- return true
-}
-
-func (idx *index) mkErr(src source, args ...interface{}) *bottom {
- var e *adt.Bottom
- var code errCode = -1
-outer:
- for i, a := range args {
- switch x := a.(type) {
- case errCode:
- code = x
- case *bottom:
- e = adt.CombineErrors(nil, e, x)
- case []*bottom:
- for _, b := range x {
- e = adt.CombineErrors(nil, e, b)
- }
- case errors.Error:
- e = adt.CombineErrors(nil, e, &adt.Bottom{Err: x})
- case value:
- case string:
- args := args[i+1:]
- // Do not expand message so that errors can be localized.
- pos := pos(src)
- if code < 0 {
- code = 0
- }
- e = adt.CombineErrors(nil, e, &adt.Bottom{
- Code: code,
- Err: errors.Newf(pos, x, args...),
- })
- break outer
- }
- }
- if code >= 0 {
- e.Code = code
- }
- return e
-}
-
-func fixArg(idx *index, x interface{}) interface{} {
- switch x.(type) {
- case uint, int, string:
- return x
- case value:
- return x
- }
- t := reflect.TypeOf(x)
- // Store all non-ptr types as is, as they cannot change.
- if k := t.Kind(); k == reflect.String || k <= reflect.Complex128 {
- return x
- }
- return fmt.Sprint(x)
-}
-
-func isBottom(x adt.Node) bool {
- if x == nil {
- return true
- }
- b, _ := x.(*adt.Bottom)
- return b != nil
-}
-
-func firstBottom(v ...value) *bottom {
- for _, b := range v {
- if isBottom(b) {
- return b.(*bottom)
- }
- }
- return nil
-}
diff --git a/internal/legacy/cue/go.go b/internal/legacy/cue/go.go
deleted file mode 100644
index 28399d9..0000000
--- a/internal/legacy/cue/go.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2020 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 cue
-
-import (
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/convert"
- "cuelang.org/go/internal/core/eval"
-)
-
-func init() {
- internal.FromGoValue = func(runtime, x interface{}, nilIsTop bool) interface{} {
- r := runtime.(*Runtime)
- ctx := eval.NewContext(r.index().Runtime, nil)
- v := convert.GoValueToValue(ctx, x, nilIsTop)
- n := adt.ToVertex(v)
- return Value{r.idx, n}
- }
-
- internal.FromGoType = func(runtime, x interface{}) interface{} {
- r := runtime.(*Runtime)
- ctx := eval.NewContext(r.index().Runtime, nil)
- expr, err := convert.GoTypeToExpr(ctx, x)
- if err != nil {
- expr = &adt.Bottom{Err: err}
- }
- n := &adt.Vertex{}
- n.AddConjunct(adt.MakeConjunct(nil, expr))
- return Value{r.idx, n}
-
- // return convertType(runtime.(*Runtime), x)
- }
-}
diff --git a/internal/legacy/cue/instance.go b/internal/legacy/cue/instance.go
deleted file mode 100644
index 5b954ea..0000000
--- a/internal/legacy/cue/instance.go
+++ /dev/null
@@ -1,363 +0,0 @@
-// 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 cue
-
-import (
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/build"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/compile"
- "cuelang.org/go/internal/core/convert"
- "cuelang.org/go/internal/core/eval"
- "cuelang.org/go/internal/core/runtime"
-)
-
-// An Instance defines a single configuration based on a collection of
-// underlying CUE files.
-type Instance struct {
- *index
-
- root *adt.Vertex
-
- ImportPath string
- Dir string
- PkgName string
- DisplayName string
-
- Incomplete bool // true if Pkg and all its dependencies are free of errors
- Err errors.Error // non-nil if the package had errors
-
- inst *build.Instance
-
- complete bool // for cycle detection
-}
-
-func (x *index) addInst(p *Instance) *Instance {
- x.Index.AddInst(p.ImportPath, p.root, p)
- p.index = x
- return p
-}
-
-func (x *index) getImportFromNode(v *adt.Vertex) *Instance {
- p := x.Index.GetImportFromNode(v)
- if p == nil {
- return nil
- }
- return p.(*Instance)
-}
-
-func (x *index) getImportFromPath(id string) *Instance {
- node := x.Index.GetImportFromPath(id)
- if node == nil {
- return nil
- }
- return x.Index.GetImportFromNode(node).(*Instance)
-}
-
-func init() {
- internal.MakeInstance = func(value interface{}) interface{} {
- v := value.(Value)
- x := v.eval(v.ctx())
- st, ok := x.(*adt.Vertex)
- if !ok {
- st = &adt.Vertex{}
- st.AddConjunct(adt.MakeConjunct(nil, x))
- }
- return v.ctx().index.addInst(&Instance{
- root: st,
- })
- }
-}
-
-// newInstance creates a new instance. Use Insert to populate the instance.
-func newInstance(x *index, p *build.Instance, v *adt.Vertex) *Instance {
- // TODO: associate root source with structLit.
- i := &Instance{
- root: v,
- inst: p,
- }
- if p != nil {
- i.ImportPath = p.ImportPath
- i.Dir = p.Dir
- i.PkgName = p.PkgName
- i.DisplayName = p.ImportPath
- if p.Err != nil {
- i.setListOrError(p.Err)
- }
- }
- return x.addInst(i)
-}
-
-func (inst *Instance) setListOrError(err errors.Error) {
- inst.Incomplete = true
- inst.Err = errors.Append(inst.Err, err)
-}
-
-func (inst *Instance) setError(err errors.Error) {
- inst.Incomplete = true
- inst.Err = errors.Append(inst.Err, err)
-}
-
-func (inst *Instance) eval(ctx *context) evaluated {
- // TODO: remove manifest here?
- v := ctx.manifest(inst.root)
- return v
-}
-
-func init() {
- internal.EvalExpr = func(value, expr interface{}) interface{} {
- v := value.(Value)
- e := expr.(ast.Expr)
- ctx := v.idx.newContext()
- return newValueRoot(ctx, evalExpr(ctx, v.vertex(ctx), e))
- }
-}
-
-// evalExpr evaluates expr within scope.
-func evalExpr(ctx *context, scope *adt.Vertex, expr ast.Expr) evaluated {
- cfg := &compile.Config{
- Scope: scope,
- Imports: func(x *ast.Ident) (pkgPath string) {
- if _, ok := builtins[x.Name]; !ok {
- return ""
- }
- return x.Name
- },
- }
-
- c, err := compile.Expr(cfg, ctx.opCtx, expr)
- if err != nil {
- return &adt.Bottom{Err: err}
- }
- return adt.Resolve(ctx.opCtx, c)
-
- // scope.Finalize(ctx.opCtx) // TODO: not appropriate here.
- // switch s := scope.Value.(type) {
- // case *bottom:
- // return s
- // case *adt.StructMarker:
- // default:
- // return ctx.mkErr(scope, "instance is not a struct, found %s", scope.Kind())
- // }
-
- // c := ctx.opCtx
-
- // x, err := compile.Expr(&compile.Config{Scope: scope}, c.Runtime, expr)
- // if err != nil {
- // return c.NewErrf("could not evaluate %s: %v", c.Str(x), err)
- // }
-
- // env := &adt.Environment{Vertex: scope}
-
- // switch v := x.(type) {
- // case adt.Value:
- // return v
- // case adt.Resolver:
- // r, err := c.Resolve(env, v)
- // if err != nil {
- // return err
- // }
- // return r
-
- // case adt.Evaluator:
- // e, _ := c.Evaluate(env, x)
- // return e
-
- // }
-
- // return c.NewErrf("could not evaluate %s", c.Str(x))
-}
-
-// Doc returns the package comments for this instance.
-func (inst *Instance) Doc() []*ast.CommentGroup {
- var docs []*ast.CommentGroup
- if inst.inst == nil {
- return nil
- }
- for _, f := range inst.inst.Files {
- if c := internal.FileComment(f); c != nil {
- docs = append(docs, c)
- }
- }
- return docs
-}
-
-// Value returns the root value of the configuration. If the configuration
-// defines in emit value, it will be that value. Otherwise it will be all
-// top-level values.
-func (inst *Instance) Value() Value {
- ctx := inst.newContext()
- inst.root.Finalize(ctx.opCtx)
- return newVertexRoot(ctx, inst.root)
-}
-
-// Eval evaluates an expression within an existing instance.
-//
-// Expressions may refer to builtin packages if they can be uniquely identified.
-func (inst *Instance) Eval(expr ast.Expr) Value {
- ctx := inst.newContext()
- v := inst.root
- v.Finalize(ctx.opCtx)
- result := evalExpr(ctx, v, expr)
- return newValueRoot(ctx, result)
-}
-
-// Merge unifies the given instances into a single one.
-//
-// Errors regarding conflicts are included in the result, but not reported, so
-// that these will only surface during manifestation. This allows
-// non-conflicting parts to be used.
-func Merge(inst ...*Instance) *Instance {
- v := &adt.Vertex{}
-
- i := inst[0]
- ctx := i.index.newContext().opCtx
-
- // TODO: interesting test: use actual unification and then on K8s corpus.
-
- for _, i := range inst {
- w := i.Value()
- v.AddConjunct(adt.MakeConjunct(nil, w.v.ToDataAll()))
- }
- v.Finalize(ctx)
-
- p := i.index.addInst(&Instance{
- root: v,
- complete: true,
- })
- return p
-}
-
-// Build creates a new instance from the build instances, allowing unbound
-// identifier to bind to the top-level field in inst. The top-level fields in
-// inst take precedence over predeclared identifier and builtin functions.
-func (inst *Instance) Build(p *build.Instance) *Instance {
- p.Complete()
-
- idx := inst.index
- r := inst.index.Runtime
-
- rErr := runtime.ResolveFiles(idx.Index, p, isBuiltin)
-
- v, err := compile.Files(&compile.Config{Scope: inst.root}, r, p.Files...)
-
- v.AddConjunct(adt.MakeConjunct(nil, inst.root))
-
- i := newInstance(idx, p, v)
- if rErr != nil {
- i.setListOrError(err)
- }
- if i.Err != nil {
- i.setListOrError(err)
- }
-
- if err != nil {
- i.setListOrError(err)
- }
-
- i.complete = true
-
- return i
-}
-
-func (inst *Instance) value() Value {
- return newVertexRoot(inst.newContext(), inst.root)
-}
-
-// Lookup reports the value at a path starting from the top level struct. The
-// Exists method of the returned value will report false if the path did not
-// exist. The Err method reports if any error occurred during evaluation. The
-// empty path returns the top-level configuration struct. Use LookupDef for definitions or LookupField for
-// any kind of field.
-func (inst *Instance) Lookup(path ...string) Value {
- return inst.value().Lookup(path...)
-}
-
-// LookupDef reports the definition with the given name within struct v. The
-// Exists method of the returned value will report false if the definition did
-// not exist. The Err method reports if any error occurred during evaluation.
-func (inst *Instance) LookupDef(path string) Value {
- return inst.value().LookupDef(path)
-}
-
-// LookupField reports a Field at a path starting from v, or an error if the
-// path is not. The empty path returns v itself.
-//
-// It cannot look up hidden or unexported fields.
-//
-// Deprecated: this API does not work with new-style definitions. Use
-// FieldByName defined on inst.Value().
-func (inst *Instance) LookupField(path ...string) (f FieldInfo, err error) {
- v := inst.value()
- for _, k := range path {
- s, err := v.Struct()
- if err != nil {
- return f, err
- }
-
- f, err = s.FieldByName(k, true)
- if err != nil {
- return f, err
- }
- if f.IsHidden {
- return f, errNotFound
- }
- v = f.Value
- }
- return f, err
-}
-
-// Fill creates a new instance with the values of the old instance unified with
-// the given value. It is not possible to update the emit value.
-//
-// Values may be any Go value that can be converted to CUE, an ast.Expr or
-// a Value. In the latter case, it will panic if the Value is not from the same
-// Runtime.
-func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
- for i := len(path) - 1; i >= 0; i-- {
- x = map[string]interface{}{path[i]: x}
- }
- a := make([]adt.Conjunct, len(inst.root.Conjuncts))
- copy(a, inst.root.Conjuncts)
- u := &adt.Vertex{Conjuncts: a}
-
- if v, ok := x.(Value); ok {
- if inst.index != v.idx {
- panic("value of type Value is not created with same Runtime as Instance")
- }
- for _, c := range v.v.Conjuncts {
- u.AddConjunct(c)
- }
- } else {
- ctx := eval.NewContext(inst.index.Runtime, nil)
- expr := convert.GoValueToExpr(ctx, true, x)
- u.AddConjunct(adt.MakeConjunct(nil, expr))
- u.Finalize(ctx)
- }
- inst = inst.index.addInst(&Instance{
- root: u,
- inst: nil,
-
- // Omit ImportPath to indicate this is not an importable package.
- Dir: inst.Dir,
- PkgName: inst.PkgName,
- Incomplete: inst.Incomplete,
-
- complete: true,
- })
- return inst, nil
-}
diff --git a/internal/legacy/cue/marshal.go b/internal/legacy/cue/marshal.go
deleted file mode 100644
index c48d136..0000000
--- a/internal/legacy/cue/marshal.go
+++ /dev/null
@@ -1,223 +0,0 @@
-// Copyright 2019 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 cue
-
-import (
- "bytes"
- "compress/gzip"
- "encoding/gob"
- "path/filepath"
- "strings"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/ast/astutil"
- "cuelang.org/go/cue/build"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/format"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/export"
-)
-
-// root.
-type instanceData struct {
- Root bool
- Path string
- Files []fileData
-}
-
-type fileData struct {
- Name string
- Data []byte
-}
-
-const version = 1
-
-type unmarshaller struct {
- ctxt *build.Context
- imports map[string]*instanceData
-}
-
-func (b *unmarshaller) load(pos token.Pos, path string) *build.Instance {
- bi := b.imports[path]
- if bi == nil {
- return nil
- }
- return b.build(bi)
-}
-
-func (b *unmarshaller) build(bi *instanceData) *build.Instance {
- p := b.ctxt.NewInstance(bi.Path, b.load)
- p.ImportPath = bi.Path
- for _, f := range bi.Files {
- _ = p.AddFile(f.Name, f.Data)
- }
- p.Complete()
- return p
-}
-
-func compileInstances(r *Runtime, data []*instanceData) (instances []*Instance, err error) {
- b := unmarshaller{
- ctxt: r.buildContext(),
- imports: map[string]*instanceData{},
- }
- for _, i := range data {
- if i.Path == "" {
- if !i.Root {
- return nil, errors.Newf(token.NoPos,
- "data contains non-root package without import path")
- }
- continue
- }
- b.imports[i.Path] = i
- }
-
- builds := []*build.Instance{}
- for _, i := range data {
- if !i.Root {
- continue
- }
- builds = append(builds, b.build(i))
- }
-
- return r.build(builds)
-}
-
-// Unmarshal creates an Instance from bytes generated by the MarshalBinary
-// method of an instance.
-func (r *Runtime) Unmarshal(b []byte) ([]*Instance, error) {
- if len(b) == 0 {
- return nil, errors.Newf(token.NoPos, "unmarshal failed: empty buffer")
- }
-
- switch b[0] {
- case version:
- default:
- return nil, errors.Newf(token.NoPos,
- "unmarshal failed: unsupported version %d, regenerate data", b[0])
- }
-
- reader, err := gzip.NewReader(bytes.NewReader(b[1:]))
- if err != nil {
- return nil, errors.Newf(token.NoPos, "unmarshal failed: %v", err)
- }
-
- data := []*instanceData{}
- err = gob.NewDecoder(reader).Decode(&data)
- if err != nil {
- return nil, errors.Newf(token.NoPos, "unmarshal failed: %v", err)
- }
-
- return compileInstances(r, data)
-}
-
-// Marshal creates bytes from a group of instances. Imported instances will
-// be included in the emission.
-//
-// The stored instances are functionally the same, but preserving of file
-// information is only done on a best-effort basis.
-func (r *Runtime) Marshal(instances ...*Instance) (b []byte, err error) {
- ctx := r.index().newContext()
-
- staged := []instanceData{}
- done := map[string]int{}
-
- var errs errors.Error
-
- var stageInstance func(i *Instance) (pos int)
- stageInstance = func(i *Instance) (pos int) {
- if p, ok := done[i.ImportPath]; ok {
- return p
- }
- // TODO: support exporting instance
- file, _ := export.Def(r.idx.Runtime, i.root)
- imports := []string{}
- for _, i := range internal.Imports(file) {
- for _, spec := range i.(*ast.ImportDecl).Specs {
- info, _ := astutil.ParseImportSpec(spec)
- imports = append(imports, info.ID)
- }
- }
-
- if i.PkgName != "" {
- p, name, _ := internal.PackageInfo(file)
- if p == nil {
- pkg := &ast.Package{Name: ast.NewIdent(i.PkgName)}
- file.Decls = append([]ast.Decl{pkg}, file.Decls...)
- } else if name != i.PkgName {
- // p is guaranteed to be generated by Def, so it is "safe" to
- // modify.
- p.Name = ast.NewIdent(i.PkgName)
- }
- }
-
- b, err := format.Node(file)
- errs = errors.Append(errs, errors.Promote(err, "marshal"))
-
- filename := "unmarshal"
- if i.inst != nil && len(i.inst.Files) == 1 {
- filename = i.inst.Files[0].Filename
-
- dir := i.Dir
- if i.inst != nil && i.inst.Root != "" {
- dir = i.inst.Root
- }
- if dir != "" {
- filename = filepath.FromSlash(filename)
- filename, _ = filepath.Rel(dir, filename)
- filename = filepath.ToSlash(filename)
- }
- }
- // TODO: this should probably be changed upstream, but as the path
- // is for reference purposes only, this is safe.
- importPath := filepath.ToSlash(i.ImportPath)
-
- staged = append(staged, instanceData{
- Path: importPath,
- Files: []fileData{{filename, b}},
- })
-
- p := len(staged) - 1
-
- for _, imp := range imports {
- i := ctx.getImportFromPath(imp)
- if i == nil || !strings.Contains(imp, ".") {
- continue // a builtin package.
- }
- stageInstance(i)
- }
-
- return p
- }
-
- for _, i := range instances {
- staged[stageInstance(i)].Root = true
- }
-
- buf := &bytes.Buffer{}
- buf.WriteByte(version)
-
- zw := gzip.NewWriter(buf)
- if err := gob.NewEncoder(zw).Encode(staged); err != nil {
- return nil, err
- }
-
- if err := zw.Close(); err != nil {
- return nil, err
- }
-
- return buf.Bytes(), nil
-
-}
diff --git a/internal/legacy/cue/marshal_test.go b/internal/legacy/cue/marshal_test.go
deleted file mode 100644
index 64775aa..0000000
--- a/internal/legacy/cue/marshal_test.go
+++ /dev/null
@@ -1,202 +0,0 @@
-// Copyright 2019 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 cue
-
-import (
- "fmt"
- "strings"
- "testing"
-
- "github.com/google/go-cmp/cmp"
-)
-
-func TestMarshalling(t *testing.T) {
- testCases := []struct {
- filename string
- input string
- pkg string
- }{{
- filename: "foo.cue",
- pkg: "foo",
- input: `package foo
-
- A: int
- B: string
- `,
- }, {
- filename: "bar.cue",
- pkg: "bar",
- input: `package bar
-
- "Hello world!"
- `,
- }, {
- filename: "qux.cue",
- input: `
- "Hello world!"
- `,
- }, {
- filename: "baz.cue",
- pkg: "baz",
- input: `package baz
-
- import "strings"
-
- a: strings.TrimSpace(" Hello world! ")
- `}}
- for _, tc := range testCases {
- t.Run(tc.filename, func(t *testing.T) {
- r := &Runtime{}
- inst, err := r.Compile(tc.filename, tc.input)
- if err != nil {
- t.Fatal(err)
- }
- inst.ImportPath = "test/pkg"
- want := fmt.Sprint(inst.Value())
-
- b, err := r.Marshal(inst)
- if err != nil {
- t.Fatal(err)
- }
-
- r2 := &Runtime{}
- instances, err := r2.Unmarshal(b)
- if err != nil {
- t.Fatal(err)
- }
- inst = instances[0]
-
- if inst.ImportPath != "test/pkg" {
- t.Error("import path was not restored")
- }
- got := fmt.Sprint(inst.Value())
-
- if got != want {
- t.Errorf("\ngot: %q;\nwant: %q", got, want)
- }
- })
- }
-}
-
-func TestMarshalMultiPackage(t *testing.T) {
- files := func(s ...string) (a []fileData) {
- for i, s := range s {
- a = append(a, fileData{fmt.Sprintf("file%d.cue", i), []byte(s)})
- }
- return a
- }
- insts := func(i ...*instanceData) []*instanceData { return i }
- pkg1 := &instanceData{
- true,
- "example.com/foo/pkg1",
- files(`
- package pkg1
-
- Object: "World"
- `),
- }
- pkg2 := &instanceData{
- true,
- "example.com/foo/pkg2",
- files(`
- package pkg
-
- Number: 12
- `),
- }
-
- testCases := []struct {
- instances []*instanceData
- emit string
- }{{
- insts(&instanceData{true, "", files(`test: "ok"`)}),
- `{test: "ok"}`,
- }, {
- insts(&instanceData{true, "",
- files(
- `package test
-
- import math2 "math"
-
- "Pi: \(math2.Pi)!"`)}),
- `"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
- }, {
- insts(pkg1, &instanceData{true, "",
- files(
- `package test
-
- import "example.com/foo/pkg1"
-
- "Hello \(pkg1.Object)!"`),
- }),
- `"Hello World!"`,
- }, {
- insts(pkg1, &instanceData{true, "",
- files(
- `package test
-
- import pkg2 "example.com/foo/pkg1"
- pkg1: pkg2.Object
-
- "Hello \(pkg1)!"`),
- }),
- `"Hello World!"`,
- }, {
- insts(pkg2, &instanceData{true, "",
- files(
- `package test
-
- import "example.com/foo/pkg2"
-
- "Hello \(pkg.Number)!"`),
- }),
- `"Hello 12!"`,
- }}
-
- strValue := func(a []*Instance) (ret []string) {
- for _, i := range a {
- ret = append(ret, strings.TrimSpace((fmt.Sprint(i.Value()))))
- }
- return ret
- }
-
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- r := &Runtime{}
-
- insts, err := compileInstances(r, tc.instances)
- if err != nil {
- t.Fatal(err)
- }
- want := strValue(insts)
-
- b, err := r.Marshal(insts...)
- if err != nil {
- t.Fatal(err)
- }
-
- r2 := &Runtime{}
- insts, err = r2.Unmarshal(b)
- if err != nil {
- t.Fatal(err)
- }
- got := strValue(insts)
-
- if !cmp.Equal(got, want) {
- t.Error(cmp.Diff(got, want))
- }
- })
- }
-}
diff --git a/internal/legacy/cue/op.go b/internal/legacy/cue/op.go
deleted file mode 100644
index b313289..0000000
--- a/internal/legacy/cue/op.go
+++ /dev/null
@@ -1,219 +0,0 @@
-// 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 cue
-
-import (
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal/core/adt"
-)
-
-// Op indicates the operation at the top of an expression tree of the expression
-// use to evaluate a value.
-type Op = adt.Op
-
-// Values of Op.
-const (
- NoOp = adt.NoOp
-
- AndOp = adt.AndOp
- OrOp = adt.OrOp
-
- SelectorOp = adt.SelectorOp
- IndexOp = adt.IndexOp
- SliceOp = adt.SliceOp
- CallOp = adt.CallOp
-
- BooleanAndOp = adt.BoolAndOp
- BooleanOrOp = adt.BoolOrOp
-
- EqualOp = adt.EqualOp
- NotOp = adt.NotOp
- NotEqualOp = adt.NotEqualOp
- LessThanOp = adt.LessThanOp
- LessThanEqualOp = adt.LessEqualOp
- GreaterThanOp = adt.GreaterThanOp
- GreaterThanEqualOp = adt.GreaterEqualOp
-
- RegexMatchOp = adt.MatchOp
- NotRegexMatchOp = adt.NotMatchOp
-
- AddOp = adt.AddOp
- SubtractOp = adt.SubtractOp
- MultiplyOp = adt.MultiplyOp
- FloatQuotientOp = adt.FloatQuotientOp
- IntQuotientOp = adt.IntQuotientOp
- IntRemainderOp = adt.IntRemainderOp
- IntDivideOp = adt.IntDivideOp
- IntModuloOp = adt.IntModuloOp
-
- InterpolationOp = adt.InterpolationOp
-)
-
-var opToOp = map[op]Op{
- opUnify: AndOp,
- // TODO(eval): opUnifyUnchecked is not the same as opUnify and should have its own
- // category, if needed. More likely opUnifyUnchecked, should be
- // represented as a separate embedding method.
- opUnifyUnchecked: AndOp,
- opDisjunction: OrOp,
- opLand: BooleanAndOp,
- opLor: BooleanOrOp,
- opEql: EqualOp,
- opNot: NotOp,
- opNeq: NotEqualOp,
- opLss: LessThanOp,
- opLeq: LessThanEqualOp,
- opGtr: GreaterThanOp,
- opGeq: GreaterThanEqualOp,
- opMat: RegexMatchOp,
- opNMat: NotRegexMatchOp,
- opAdd: AddOp,
- opSub: SubtractOp,
- opMul: MultiplyOp,
- opQuo: FloatQuotientOp,
- opIQuo: IntQuotientOp,
- opIRem: IntRemainderOp,
- opIDiv: IntDivideOp,
- opIMod: IntModuloOp,
-}
-
-func opIn(op op, anyOf ...op) bool {
- for _, o := range anyOf {
- if o == op {
- return true
- }
- }
- return false
-}
-
-// isCmp reports whether an op is a comparator.
-func (op op) isCmp() bool {
- return opEql <= op && op <= opGeq
-}
-
-func (op op) unifyType() (unchecked, ok bool) {
- if op == opUnifyUnchecked {
- return true, true
- }
- return false, op == opUnify
-}
-
-type op uint16
-
-const (
- opUnknown op = iota
-
- opUnify
- opUnifyUnchecked
- opDisjunction
-
- opLand
- opLor
- opNot
-
- opEql
- opNeq
- opMat
- opNMat
-
- opLss
- opGtr
- opLeq
- opGeq
-
- opAdd
- opSub
- opMul
- opQuo
- opRem
-
- opIDiv
- opIMod
- opIQuo
- opIRem
-)
-
-var opStrings = []string{
- opUnknown: "??",
-
- opUnify: "&",
- // opUnifyUnchecked is internal only. Syntactically this is
- // represented as embedding.
- opUnifyUnchecked: "&!",
- opDisjunction: "|",
-
- opLand: "&&",
- opLor: "||",
- opNot: "!",
-
- opEql: "==",
- opNeq: "!=",
- opMat: "=~",
- opNMat: "!~",
-
- opLss: "<",
- opGtr: ">",
- opLeq: "<=",
- opGeq: ">=",
-
- opAdd: "+",
- opSub: "-",
- opMul: "*",
- opQuo: "/",
-
- opIDiv: "div",
- opIMod: "mod",
- opIQuo: "quo",
- opIRem: "rem",
-}
-
-func (op op) String() string { return opStrings[op] }
-
-var tokenMap = map[token.Token]op{
- token.OR: opDisjunction, // |
- token.AND: opUnify, // &
-
- token.ADD: opAdd, // +
- token.SUB: opSub, // -
- token.MUL: opMul, // *
- token.QUO: opQuo, // /
-
- token.IDIV: opIDiv, // div
- token.IMOD: opIMod, // mod
- token.IQUO: opIQuo, // quo
- token.IREM: opIRem, // rem
-
- token.LAND: opLand, // &&
- token.LOR: opLor, // ||
-
- token.EQL: opEql, // ==
- token.LSS: opLss, // <
- token.GTR: opGtr, // >
- token.NOT: opNot, // !
-
- token.NEQ: opNeq, // !=
- token.LEQ: opLeq, // <=
- token.GEQ: opGeq, // >=
- token.MAT: opMat, // =~
- token.NMAT: opNMat, // !~
-}
-
-var opMap = map[op]token.Token{}
-
-func init() {
- for t, o := range tokenMap {
- opMap[o] = t
- }
-}
diff --git a/internal/legacy/cue/resolve_test.go b/internal/legacy/cue/resolve_test.go
deleted file mode 100644
index b9ad808..0000000
--- a/internal/legacy/cue/resolve_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-// 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 cue
-
-import (
- "strings"
- "testing"
-
- "cuelang.org/go/internal/core/adt"
-)
-
-func TestX(t *testing.T) {
- // Don't remove. For debugging.
- in := `
- `
-
- if strings.TrimSpace(in) == "" {
- t.Skip()
- }
-}
-
-// var traceOn = flag.Bool("debug", false, "enable tracing")
-
-// func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, error) {
-// t.Helper()
-// ctx, inst, err := compileInstance(t, body)
-// return ctx, inst.root, err
-// }
-
-// func compileFile(t *testing.T, body string) (*context, *structLit) {
-// t.Helper()
-// ctx, inst, errs := compileInstance(t, body)
-// if errs != nil {
-// t.Fatal(errs)
-// }
-// return ctx, inst.root
-// }
-
-func compileInstance(t *testing.T, body string) (*context, *Instance, error) {
- var r Runtime
- inst, err := r.Compile("test", body)
-
- if err != nil {
- x := newInstance(newIndex(sharedIndex), nil, &adt.Vertex{})
- ctx := x.newContext()
- return ctx, x, err
- }
-
- return r.index().newContext(), inst, nil
-}
diff --git a/internal/legacy/cue/types.go b/internal/legacy/cue/types.go
deleted file mode 100644
index 3e5d0c2..0000000
--- a/internal/legacy/cue/types.go
+++ /dev/null
@@ -1,2245 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "math"
- "math/big"
- "strconv"
- "strings"
-
- "github.com/cockroachdb/apd/v2"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/ast/astutil"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/cue/format"
- "cuelang.org/go/cue/token"
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/convert"
- "cuelang.org/go/internal/core/eval"
- "cuelang.org/go/internal/core/export"
- "cuelang.org/go/internal/core/subsume"
- "cuelang.org/go/internal/core/validate"
-)
-
-// Kind determines the underlying type of a Value.
-type Kind = adt.Kind
-
-const BottomKind Kind = 0
-
-const (
- // NullKind indicates a null value.
- NullKind Kind = adt.NullKind
-
- // BoolKind indicates a boolean value.
- BoolKind = adt.BoolKind
-
- // IntKind represents an integral number.
- IntKind = adt.IntKind
-
- // FloatKind represents a decimal float point number that cannot be
- // converted to an integer. The underlying number may still be integral,
- // but resulting from an operation that enforces the float type.
- FloatKind = adt.FloatKind
-
- // StringKind indicates any kind of string.
- StringKind = adt.StringKind
-
- // BytesKind is a blob of data.
- BytesKind = adt.BytesKind
-
- // StructKind is a kev-value map.
- StructKind = adt.StructKind
-
- // ListKind indicates a list of values.
- ListKind = adt.ListKind
-
- // _numberKind is used as a implementation detail inside
- // Kind.String to indicate NumberKind.
-
- // NumberKind represents any kind of number.
- NumberKind = IntKind | FloatKind
-
- TopKind = adt.TopKind
-)
-
-// An structValue represents a JSON object.
-//
-// TODO: remove
-type structValue struct {
- ctx *context
- v Value
- obj *adt.Vertex
- features []adt.Feature
-}
-
-// Len reports the number of fields in this struct.
-func (o *structValue) Len() int {
- if o.obj == nil {
- return 0
- }
- return len(o.features)
-}
-
-// At reports the key and value of the ith field, i < o.Len().
-func (o *structValue) At(i int) (key string, v Value) {
- f := o.features[i]
- return o.ctx.LabelStr(f), newChildValue(o, i)
-}
-
-func (o *structValue) at(i int) (v *adt.Vertex, isOpt bool) {
- f := o.features[i]
- arc := o.obj.Lookup(f)
- if arc == nil {
- arc = &adt.Vertex{
- Parent: o.v.v,
- Label: f,
- }
- o.obj.MatchAndInsert(o.ctx.opCtx, arc)
- arc.Finalize(o.ctx.opCtx)
- isOpt = true
- }
- return arc, isOpt
-}
-
-// Lookup reports the field for the given key. The returned Value is invalid
-// if it does not exist.
-func (o *structValue) Lookup(key string) Value {
- f := o.ctx.StrLabel(key)
- i := 0
- len := o.Len()
- for ; i < len; i++ {
- if o.features[i] == f {
- break
- }
- }
- if i == len {
- // TODO: better message.
- ctx := o.ctx
- x := ctx.mkErr(o.obj, codeNotExist, "value %q not found", key)
- return newErrValue(o.v, x)
- }
- return newChildValue(o, i)
-}
-
-// MarshalJSON returns a valid JSON encoding or reports an error if any of the
-// fields is invalid.
-func (o *structValue) marshalJSON() (b []byte, err errors.Error) {
- b = append(b, '{')
- n := o.Len()
- for i := 0; i < n; i++ {
- k, v := o.At(i)
- s, err := json.Marshal(k)
- if err != nil {
- return nil, unwrapJSONError(err)
- }
- b = append(b, s...)
- b = append(b, ':')
- bb, err := json.Marshal(v)
- if err != nil {
- return nil, unwrapJSONError(err)
- }
- b = append(b, bb...)
- if i < n-1 {
- b = append(b, ',')
- }
- }
- b = append(b, '}')
- return b, nil
-}
-
-var _ errors.Error = &marshalError{}
-
-type marshalError struct {
- err errors.Error
- b *bottom
-}
-
-func toMarshalErr(v Value, b *bottom) error {
- return &marshalError{v.toErr(b), b}
-}
-
-func marshalErrf(v Value, src source, code errCode, msg string, args ...interface{}) error {
- arguments := append([]interface{}{code, msg}, args...)
- b := v.idx.mkErr(src, arguments...)
- return toMarshalErr(v, b)
-}
-
-func (e *marshalError) Error() string {
- return fmt.Sprintf("cue: marshal error: %v", e.err)
-}
-
-func (e *marshalError) Path() []string { return e.err.Path() }
-func (e *marshalError) Msg() (string, []interface{}) { return e.err.Msg() }
-func (e *marshalError) Position() token.Pos { return e.err.Position() }
-func (e *marshalError) InputPositions() []token.Pos {
- return e.err.InputPositions()
-}
-
-func unwrapJSONError(err error) errors.Error {
- switch x := err.(type) {
- case *json.MarshalerError:
- return unwrapJSONError(x.Err)
- case *marshalError:
- return x
- case errors.Error:
- return &marshalError{x, nil}
- default:
- return &marshalError{errors.Wrapf(err, token.NoPos, "json error"), nil}
- }
-}
-
-// An Iterator iterates over values.
-//
-type Iterator struct {
- val Value
- ctx *context
- arcs []field
- p int
- cur Value
- f label
- isOpt bool
-}
-
-type field struct {
- arc *adt.Vertex
- isOptional bool
-}
-
-// Next advances the iterator to the next value and reports whether there was
-// any. It must be called before the first call to Value or Key.
-func (i *Iterator) Next() bool {
- if i.p >= len(i.arcs) {
- i.cur = Value{}
- return false
- }
- f := i.arcs[i.p]
- f.arc.Finalize(i.ctx.opCtx)
- i.cur = makeValue(i.val.idx, f.arc)
- i.f = f.arc.Label
- i.isOpt = f.isOptional
- i.p++
- return true
-}
-
-// Value returns the current value in the list. It will panic if Next advanced
-// past the last entry.
-func (i *Iterator) Value() Value {
- return i.cur
-}
-
-func (i *Iterator) Feature() adt.Feature {
- return i.f
-}
-
-// Label reports the label of the value if i iterates over struct fields and
-// "" otherwise.
-func (i *Iterator) Label() string {
- if i.f == 0 {
- return ""
- }
- return i.ctx.LabelStr(i.f)
-}
-
-// IsHidden reports if a field is hidden from the data model.
-func (i *Iterator) IsHidden() bool {
- return i.f.IsHidden()
-}
-
-// IsOptional reports if a field is optional.
-func (i *Iterator) IsOptional() bool {
- return i.isOpt
-}
-
-// IsDefinition reports if a field is a definition.
-func (i *Iterator) IsDefinition() bool {
- return i.f.IsDef()
-}
-
-// marshalJSON iterates over the list and generates JSON output. HasNext
-// will return false after this operation.
-func marshalList(l *Iterator) (b []byte, err errors.Error) {
- b = append(b, '[')
- if l.Next() {
- for i := 0; ; i++ {
- x, err := json.Marshal(l.Value())
- if err != nil {
- return nil, unwrapJSONError(err)
- }
- b = append(b, x...)
- if !l.Next() {
- break
- }
- b = append(b, ',')
- }
- }
- b = append(b, ']')
- return b, nil
-}
-
-func (v Value) getNum(k kind) (*numLit, errors.Error) {
- v, _ = v.Default()
- ctx := v.ctx()
- if err := v.checkKind(ctx, k); err != nil {
- return nil, v.toErr(err)
- }
- n, _ := v.eval(ctx).(*numLit)
- return n, nil
-}
-
-// MantExp breaks x into its mantissa and exponent components and returns the
-// exponent. If a non-nil mant argument is provided its value is set to the
-// mantissa of x. The components satisfy x == mant × 10**exp. It returns an
-// error if v is not a number.
-//
-// The components are not normalized. For instance, 2.00 is represented mant ==
-// 200 and exp == -2. Calling MantExp with a nil argument is an efficient way to
-// get the exponent of the receiver.
-func (v Value) MantExp(mant *big.Int) (exp int, err error) {
- n, err := v.getNum(numKind)
- if err != nil {
- return 0, err
- }
- if n.X.Form != 0 {
- return 0, ErrInfinite
- }
- if mant != nil {
- mant.Set(&n.X.Coeff)
- if n.X.Negative {
- mant.Neg(mant)
- }
- }
- return int(n.X.Exponent), nil
-}
-
-// AppendInt appends the string representation of x in the given base to buf and
-// returns the extended buffer, or an error if the underlying number was not
-// an integer.
-func (v Value) AppendInt(buf []byte, base int) ([]byte, error) {
- i, err := v.Int(nil)
- if err != nil {
- return nil, err
- }
- return i.Append(buf, base), nil
-}
-
-// AppendFloat appends to buf the string form of the floating-point number x.
-// It returns an error if v is not a number.
-func (v Value) AppendFloat(buf []byte, fmt byte, prec int) ([]byte, error) {
- n, err := v.getNum(numKind)
- if err != nil {
- return nil, err
- }
- ctx := apd.BaseContext
- nd := int(apd.NumDigits(&n.X.Coeff)) + int(n.X.Exponent)
- if n.X.Form == apd.Infinite {
- if n.X.Negative {
- buf = append(buf, '-')
- }
- return append(buf, string('∞')...), nil
- }
- if fmt == 'f' && nd > 0 {
- ctx.Precision = uint32(nd + prec)
- } else {
- ctx.Precision = uint32(prec)
- }
- var d apd.Decimal
- ctx.Round(&d, &n.X)
- return d.Append(buf, fmt), nil
-}
-
-var (
- // ErrBelow indicates that a value was rounded down in a conversion.
- ErrBelow = errors.New("value was rounded down")
-
- // ErrAbove indicates that a value was rounded up in a conversion.
- ErrAbove = errors.New("value was rounded up")
-
- // ErrInfinite indicates that a value is infinite.
- ErrInfinite = errors.New("infinite")
-)
-
-// Int converts the underlying integral number to an big.Int. It reports an
-// error if the underlying value is not an integer type. If a non-nil *Int
-// argument z is provided, Int stores the result in z instead of allocating a
-// new Int.
-func (v Value) Int(z *big.Int) (*big.Int, error) {
- n, err := v.getNum(intKind)
- if err != nil {
- return nil, err
- }
- if z == nil {
- z = &big.Int{}
- }
- if n.X.Exponent != 0 {
- panic("cue: exponent should always be nil for integer types")
- }
- z.Set(&n.X.Coeff)
- if n.X.Negative {
- z.Neg(z)
- }
- return z, nil
-}
-
-// Int64 converts the underlying integral number to int64. It reports an
-// error if the underlying value is not an integer type or cannot be represented
-// as an int64. The result is (math.MinInt64, ErrAbove) for x < math.MinInt64,
-// and (math.MaxInt64, ErrBelow) for x > math.MaxInt64.
-func (v Value) Int64() (int64, error) {
- n, err := v.getNum(intKind)
- if err != nil {
- return 0, err
- }
- if !n.X.Coeff.IsInt64() {
- if n.X.Negative {
- return math.MinInt64, ErrAbove
- }
- return math.MaxInt64, ErrBelow
- }
- i := n.X.Coeff.Int64()
- if n.X.Negative {
- i = -i
- }
- return i, nil
-}
-
-// Uint64 converts the underlying integral number to uint64. It reports an
-// error if the underlying value is not an integer type or cannot be represented
-// as a uint64. The result is (0, ErrAbove) for x < 0, and
-// (math.MaxUint64, ErrBelow) for x > math.MaxUint64.
-func (v Value) Uint64() (uint64, error) {
- n, err := v.getNum(intKind)
- if err != nil {
- return 0, err
- }
- if n.X.Negative {
- return 0, ErrAbove
- }
- if !n.X.Coeff.IsUint64() {
- return math.MaxUint64, ErrBelow
- }
- i := n.X.Coeff.Uint64()
- return i, nil
-}
-
-// trimZeros trims 0's for better JSON respresentations.
-func trimZeros(s string) string {
- n1 := len(s)
- s2 := strings.TrimRight(s, "0")
- n2 := len(s2)
- if p := strings.IndexByte(s2, '.'); p != -1 {
- if p == n2-1 {
- return s[:len(s2)+1]
- }
- return s2
- }
- if n1-n2 <= 4 {
- return s
- }
- return fmt.Sprint(s2, "e+", n1-n2)
-}
-
-var (
- smallestPosFloat64 *apd.Decimal
- smallestNegFloat64 *apd.Decimal
- maxPosFloat64 *apd.Decimal
- maxNegFloat64 *apd.Decimal
-)
-
-func init() {
- const (
- // math.SmallestNonzeroFloat64: 1 / 2**(1023 - 1 + 52)
- smallest = "4.940656458412465441765687928682213723651e-324"
- // math.MaxFloat64: 2**1023 * (2**53 - 1) / 2**52
- max = "1.797693134862315708145274237317043567981e+308"
- )
- ctx := apd.BaseContext
- ctx.Precision = 40
-
- var err error
- smallestPosFloat64, _, err = ctx.NewFromString(smallest)
- if err != nil {
- panic(err)
- }
- smallestNegFloat64, _, err = ctx.NewFromString("-" + smallest)
- if err != nil {
- panic(err)
- }
- maxPosFloat64, _, err = ctx.NewFromString(max)
- if err != nil {
- panic(err)
- }
- maxNegFloat64, _, err = ctx.NewFromString("-" + max)
- if err != nil {
- panic(err)
- }
-}
-
-// Float64 returns the float64 value nearest to x. It reports an error if v is
-// not a number. If x is too small to be represented by a float64 (|x| <
-// math.SmallestNonzeroFloat64), the result is (0, ErrBelow) or (-0, ErrAbove),
-// respectively, depending on the sign of x. If x is too large to be represented
-// by a float64 (|x| > math.MaxFloat64), the result is (+Inf, ErrAbove) or
-// (-Inf, ErrBelow), depending on the sign of x.
-func (v Value) Float64() (float64, error) {
- n, err := v.getNum(numKind)
- if err != nil {
- return 0, err
- }
- if n.X.Negative {
- if n.X.Cmp(smallestNegFloat64) == 1 {
- return -0, ErrAbove
- }
- if n.X.Cmp(maxNegFloat64) == -1 {
- return math.Inf(-1), ErrBelow
- }
- } else {
- if n.X.Cmp(smallestPosFloat64) == -1 {
- return 0, ErrBelow
- }
- if n.X.Cmp(maxPosFloat64) == 1 {
- return math.Inf(1), ErrAbove
- }
- }
- f, _ := n.X.Float64()
- return f, nil
-}
-
-func (v Value) appendPath(a []string) []string {
- for _, f := range v.v.Path() {
- switch f.Typ() {
- case adt.IntLabel:
- a = append(a, strconv.FormatInt(int64(f.Index()), 10))
-
- case adt.StringLabel:
- label := v.idx.LabelStr(f)
- if !f.IsDef() && !f.IsHidden() {
- if !ast.IsValidIdent(label) {
- label = strconv.Quote(label)
- }
- }
- a = append(a, label)
- default:
- a = append(a, f.SelectorString(v.idx.Index))
- }
- }
- return a
-}
-
-// Value holds any value, which may be a Boolean, Error, List, Null, Number,
-// Struct, or String.
-type Value struct {
- idx *index
- v *adt.Vertex
-}
-
-func newErrValue(v Value, b *bottom) Value {
- node := &adt.Vertex{Value: b}
- if v.v != nil {
- node.Label = v.v.Label
- node.Parent = v.v.Parent
- }
- node.UpdateStatus(adt.Finalized)
- node.AddConjunct(adt.MakeConjunct(nil, b))
- return makeValue(v.idx, node)
-}
-
-func newVertexRoot(ctx *context, x *adt.Vertex) Value {
- if ctx.opCtx != nil {
- // This is indicative of an zero Value. In some cases this is called
- // with an error value.
- x.Finalize(ctx.opCtx)
- } else {
- x.UpdateStatus(adt.Finalized)
- }
- return makeValue(ctx.index, x)
-}
-
-func newValueRoot(ctx *context, x value) Value {
- if n, ok := x.(*adt.Vertex); ok {
- return newVertexRoot(ctx, n)
- }
- node := &adt.Vertex{}
- node.AddConjunct(adt.MakeConjunct(nil, x))
- return newVertexRoot(ctx, node)
-}
-
-func newChildValue(o *structValue, i int) Value {
- arc, _ := o.at(i)
- return makeValue(o.v.idx, arc)
-}
-
-// Dereference reports the value v refers to if v is a reference or v itself
-// otherwise.
-func Dereference(v Value) Value {
- n := v.v
- if n == nil || len(n.Conjuncts) != 1 {
- return v
- }
-
- c := n.Conjuncts[0]
- r, _ := c.Expr().(adt.Resolver)
- if r == nil {
- return v
- }
-
- ctx := v.ctx()
- n, b := ctx.opCtx.Resolve(c.Env, r)
- if b != nil {
- return newErrValue(v, b)
- }
- return makeValue(v.idx, n)
-}
-
-func makeValue(idx *index, v *adt.Vertex) Value {
- if v.Status() == 0 || v.Value == nil {
- panic(fmt.Sprintf("not properly initialized (state: %v, value: %T)",
- v.Status(), v.Value))
- }
- return Value{idx, v}
-}
-
-func remakeValue(base Value, env *adt.Environment, v value) Value {
- // TODO: right now this is necessary because disjunctions do not have
- // populated conjuncts.
- if v, ok := v.(*adt.Vertex); ok && v.Status() >= adt.Partial {
- return Value{base.idx, v}
- }
- n := &adt.Vertex{Parent: base.v.Parent, Label: base.v.Label}
- n.AddConjunct(adt.MakeConjunct(env, v))
- n = base.ctx().manifest(n)
- return makeValue(base.idx, n)
-}
-
-func remakeFinal(base Value, env *adt.Environment, v adt.Value) Value {
- n := &adt.Vertex{Parent: base.v.Parent, Label: base.v.Label, Value: v}
- n.UpdateStatus(adt.Finalized)
- return makeValue(base.idx, n)
-}
-
-func (v Value) ctx() *context {
- return v.idx.newContext()
-}
-
-func (v Value) makeChild(ctx *context, i uint32, a arc) Value {
- a.Parent = v.v
- return makeValue(v.idx, a)
-}
-
-// Eval resolves the references of a value and returns the result.
-// This method is not necessary to obtain concrete values.
-func (v Value) Eval() Value {
- if v.v == nil {
- return v
- }
- x := v.v
- // x = eval.FinalizeValue(v.idx.Runtime, v.v)
- // x.Finalize(v.ctx().opCtx)
- x = x.ToDataSingle()
- return makeValue(v.idx, x)
- // return remakeValue(v, nil, ctx.value(x))
-}
-
-// Default reports the default value and whether it existed. It returns the
-// normal value if there is no default.
-func (v Value) Default() (Value, bool) {
- if v.v == nil {
- return v, false
- }
-
- d := v.v.Default()
- if d == v.v {
- return v, false
- }
- return makeValue(v.idx, d), true
-
- // d, ok := v.v.Value.(*adt.Disjunction)
- // if !ok {
- // return v, false
- // }
-
- // var w *adt.Vertex
-
- // switch d.NumDefaults {
- // case 0:
- // return v, false
-
- // case 1:
- // w = d.Values[0]
-
- // default:
- // x := *v.v
- // x.Value = &adt.Disjunction{
- // Src: d.Src,
- // Values: d.Values[:d.NumDefaults],
- // NumDefaults: 0,
- // }
- // w = &x
- // }
-
- // w.Conjuncts = nil
- // for _, c := range v.v.Conjuncts {
- // // TODO: preserve field information.
- // expr, _ := stripNonDefaults(c.Expr())
- // w.AddConjunct(adt.MakeConjunct(c.Env, expr))
- // }
-
- // return makeValue(v.idx, w), true
-
- // if !stripped {
- // return v, false
- // }
-
- // n := *v.v
- // n.Conjuncts = conjuncts
- // return Value{v.idx, &n}, true
-
- // isDefault := false
- // for _, c := range v.v.Conjuncts {
- // if hasDisjunction(c.Expr()) {
- // isDefault = true
- // break
- // }
- // }
-
- // if !isDefault {
- // return v, false
- // }
-
- // TODO: record expanded disjunctions in output.
- // - Rename Disjunction to DisjunctionExpr
- // - Introduce Disjuncts with Values.
- // - In Expr introduce Star
- // - Don't pick default by default?
-
- // Evaluate the value.
- // x := eval.FinalizeValue(v.idx.Runtime, v.v)
- // if b, _ := x.Value.(*adt.Bottom); b != nil { // && b.IsIncomplete() {
- // return v, false
- // }
- // // Finalize and return here.
- // return Value{v.idx, x}, isDefault
-}
-
-// TODO: this should go: record preexpanded disjunctions in Vertex.
-func hasDisjunction(expr adt.Expr) bool {
- switch x := expr.(type) {
- case *adt.DisjunctionExpr:
- return true
- case *adt.Conjunction:
- for _, v := range x.Values {
- if hasDisjunction(v) {
- return true
- }
- }
- case *adt.BinaryExpr:
- switch x.Op {
- case adt.OrOp:
- return true
- case adt.AndOp:
- return hasDisjunction(x.X) || hasDisjunction(x.Y)
- }
- }
- return false
-}
-
-// TODO: this should go: record preexpanded disjunctions in Vertex.
-func stripNonDefaults(expr adt.Expr) (r adt.Expr, stripped bool) {
- switch x := expr.(type) {
- case *adt.DisjunctionExpr:
- if !x.HasDefaults {
- return x, false
- }
- d := *x
- d.Values = []adt.Disjunct{}
- for _, v := range x.Values {
- if v.Default {
- d.Values = append(d.Values, v)
- }
- }
- if len(d.Values) == 1 {
- return d.Values[0].Val, true
- }
- return &d, true
-
- case *adt.BinaryExpr:
- if x.Op != adt.AndOp {
- return x, false
- }
- a, sa := stripNonDefaults(x.X)
- b, sb := stripNonDefaults(x.Y)
- if sa || sb {
- bin := *x
- bin.X = a
- bin.Y = b
- return &bin, true
- }
- return x, false
-
- default:
- return x, false
- }
-}
-
-// Label reports he label used to obtain this value from the enclosing struct.
-//
-// TODO: get rid of this somehow. Probably by including a FieldInfo struct
-// or the like.
-func (v Value) Label() (string, bool) {
- if v.v == nil || v.v.Label == 0 {
- return "", false
- }
- return v.idx.LabelStr(v.v.Label), true
-}
-
-// Kind returns the kind of value. It returns BottomKind for atomic values that
-// are not concrete. For instance, it will return BottomKind for the bounds
-// >=0.
-func (v Value) Kind() Kind {
- if v.v == nil {
- return BottomKind
- }
- c := v.v.Value
- if !adt.IsConcrete(c) {
- return BottomKind
- }
- if v.IncompleteKind() == adt.ListKind && !v.IsClosed() {
- return BottomKind
- }
- return c.Kind()
-}
-
-// IncompleteKind returns a mask of all kinds that this value may be.
-func (v Value) IncompleteKind() Kind {
- if v.v == nil {
- return BottomKind
- }
- return v.v.Kind()
-}
-
-// MarshalJSON marshalls this value into valid JSON.
-func (v Value) MarshalJSON() (b []byte, err error) {
- b, err = v.marshalJSON()
- if err != nil {
- return nil, unwrapJSONError(err)
- }
- return b, nil
-}
-
-func (v Value) marshalJSON() (b []byte, err error) {
- v, _ = v.Default()
- if v.v == nil {
- return json.Marshal(nil)
- }
- ctx := v.idx.newContext()
- x := v.eval(ctx)
-
- if _, ok := x.(adt.Resolver); ok {
- return nil, marshalErrf(v, x, codeIncomplete, "value %q contains unresolved references", ctx.str(x))
- }
- if !adt.IsConcrete(x) {
- return nil, marshalErrf(v, x, codeIncomplete, "cannot convert incomplete value %q to JSON", ctx.str(x))
- }
-
- // TODO: implement marshalles in value.
- switch k := x.Kind(); k {
- case nullKind:
- return json.Marshal(nil)
- case boolKind:
- return json.Marshal(x.(*boolLit).B)
- case intKind, floatKind, numKind:
- b, err := x.(*numLit).X.MarshalText()
- b = bytes.TrimLeft(b, "+")
- return b, err
- case stringKind:
- return json.Marshal(x.(*stringLit).Str)
- case bytesKind:
- return json.Marshal(x.(*bytesLit).B)
- case listKind:
- i, _ := v.List()
- return marshalList(&i)
- case structKind:
- obj, err := v.structValData(ctx)
- if err != nil {
- return nil, toMarshalErr(v, err)
- }
- return obj.marshalJSON()
- case bottomKind:
- return nil, toMarshalErr(v, x.(*bottom))
- default:
- return nil, marshalErrf(v, x, 0, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
- }
-}
-
-// Syntax converts the possibly partially evaluated value into syntax. This
-// can use used to print the value with package format.
-func (v Value) Syntax(opts ...Option) ast.Node {
- // TODO: the default should ideally be simplified representation that
- // exactly represents the value. The latter can currently only be
- // ensured with Raw().
- if v.v == nil {
- return nil
- }
- var o options = getOptions(opts)
- // var inst *Instance
-
- p := export.Profile{
- Simplify: !o.raw,
- ShowOptional: !o.omitOptional && !o.concrete,
- ShowDefinitions: !o.omitDefinitions && !o.concrete,
- ShowHidden: !o.omitHidden && !o.concrete,
- ShowAttributes: !o.omitAttrs,
- ShowDocs: o.docs,
- }
-
- // var expr ast.Expr
- var err error
- var f *ast.File
- if o.concrete || o.final {
- // inst = v.instance()
- var expr ast.Expr
- expr, err = p.Value(v.idx.Runtime, v.v)
- if err != nil {
- return nil
- }
-
- // This introduces gratuitous unshadowing!
- f, err = astutil.ToFile(expr)
- if err != nil {
- return nil
- }
- // return expr
- } else {
- f, err = p.Def(v.idx.Runtime, v.v)
- if err != nil {
- panic(err)
- }
- }
-
- if d := internal.Imports(f); len(d) == 0 {
- if len(f.Decls) == 1 {
- if e, ok := f.Decls[0].(*ast.EmbedDecl); ok {
- return e.Expr
- }
- }
- return &ast.StructLit{
- Elts: f.Decls,
- }
- }
-
- return f
-}
-
-// Decode initializes x with Value v. If x is a struct, it will validate the
-// constraints specified in the field tags.
-func (v Value) Decode(x interface{}) error {
- // TODO: optimize
- b, err := v.MarshalJSON()
- if err != nil {
- return err
- }
- return json.Unmarshal(b, x)
-}
-
-// // EncodeJSON generates JSON for the given value.
-// func (v Value) EncodeJSON(w io.Writer, v Value) error {
-// return nil
-// }
-
-// Doc returns all documentation comments associated with the field from which
-// the current value originates.
-func (v Value) Doc() []*ast.CommentGroup {
- if v.v == nil {
- return nil
- }
- return export.ExtractDoc(v.v)
-}
-
-// Split returns a list of values from which v originated such that
-// the unification of all these values equals v and for all returned values.
-// It will also split unchecked unifications (embeddings), so unifying the
-// split values may fail if actually unified.
-// Source returns a non-nil value.
-//
-// Deprecated: use Expr.
-func (v Value) Split() []Value {
- if v.v == nil {
- return nil
- }
- a := []Value{}
- for _, x := range v.v.Conjuncts {
- a = append(a, remakeValue(v, x.Env, x.Expr()))
- }
- return a
-}
-
-// Source returns the original node for this value. The return value may not
-// be a syntax.Expr. For instance, a struct kind may be represented by a
-// struct literal, a field comprehension, or a file. It returns nil for
-// computed nodes. Use Split to get all source values that apply to a field.
-func (v Value) Source() ast.Node {
- if v.v == nil {
- return nil
- }
- if len(v.v.Conjuncts) == 1 {
- return v.v.Conjuncts[0].Source()
- }
- return v.v.Value.Source()
-}
-
-// Err returns the error represented by v or nil v is not an error.
-func (v Value) Err() error {
- if err := v.checkKind(v.ctx(), bottomKind); err != nil {
- return v.toErr(err)
- }
- return nil
-}
-
-// Pos returns position information.
-func (v Value) Pos() token.Pos {
- if v.v == nil || v.Source() == nil {
- return token.NoPos
- }
- pos := v.Source().Pos()
- return pos
-}
-
-// TODO: IsFinal: this value can never be changed.
-
-// IsClosed reports whether a list of struct is closed. It reports false when
-// when the value is not a list or struct.
-func (v Value) IsClosed() bool {
- if v.v == nil {
- return false
- }
- return v.v.IsClosed(v.ctx().opCtx)
-}
-
-// IsConcrete reports whether the current value is a concrete scalar value
-// (not relying on default values), a terminal error, a list, or a struct.
-// It does not verify that values of lists or structs are concrete themselves.
-// To check whether there is a concrete default, use v.Default().IsConcrete().
-func (v Value) IsConcrete() bool {
- if v.v == nil {
- return false // any is neither concrete, not a list or struct.
- }
- if b, ok := v.v.Value.(*adt.Bottom); ok {
- return !b.IsIncomplete()
- }
- if !adt.IsConcrete(v.v) {
- return false
- }
- if v.IncompleteKind() == adt.ListKind && !v.IsClosed() {
- return false
- }
- return true
-}
-
-// // Deprecated: IsIncomplete
-// //
-// // It indicates that the value cannot be fully evaluated due to
-// // insufficient information.
-// func (v Value) IsIncomplete() bool {
-// panic("deprecated")
-// }
-
-// Exists reports whether this value existed in the configuration.
-func (v Value) Exists() bool {
- if v.v == nil {
- return false
- }
- return exists(v.v.Value)
-}
-
-func (v Value) checkKind(ctx *context, want kind) *bottom {
- if v.v == nil {
- return errNotExists
- }
- // TODO: use checkKind
- x := v.eval(ctx)
- if b, ok := x.(*bottom); ok {
- return b
- }
- k := x.Kind()
- if want != bottomKind {
- if k&want == bottomKind {
- return ctx.mkErr(x, "cannot use value %v (type %s) as %s",
- ctx.opCtx.Str(x), k, want)
- }
- if !adt.IsConcrete(x) {
- return ctx.mkErr(x, codeIncomplete, "non-concrete value %v", k)
- }
- }
- return nil
-}
-
-func makeInt(v Value, x int64) Value {
- n := &adt.Num{K: intKind}
- n.X.SetInt64(int64(x))
- return remakeFinal(v, nil, n)
-}
-
-// Len returns the number of items of the underlying value.
-// For lists it reports the capacity of the list. For structs it indicates the
-// number of fields, for bytes the number of bytes.
-func (v Value) Len() Value {
- if v.v != nil {
- switch x := v.eval(v.ctx()).(type) {
- case *adt.Vertex:
- if x.IsList() {
- ctx := v.ctx()
- n := &adt.Num{K: intKind}
- n.X.SetInt64(int64(len(x.Elems())))
- if x.IsClosed(ctx.opCtx) {
- return remakeFinal(v, nil, n)
- }
- // Note: this HAS to be a Conjunction value and cannot be
- // an adt.BinaryExpr, as the expressions would be considered
- // to be self-contained and unresolvable when evaluated
- // (can never become concrete).
- c := &adt.Conjunction{Values: []adt.Value{
- &adt.BasicType{K: adt.IntKind},
- &adt.BoundValue{Op: adt.GreaterEqualOp, Value: n},
- }}
- return remakeFinal(v, nil, c)
-
- }
- case *bytesLit:
- return makeInt(v, int64(len(x.B)))
- case *stringLit:
- return makeInt(v, int64(len([]rune(x.Str))))
- }
- }
- const msg = "len not supported for type %v"
- return remakeValue(v, nil, v.ctx().mkErr(v.v, msg, v.Kind()))
-
-}
-
-// Elem returns the value of undefined element types of lists and structs.
-func (v Value) Elem() (Value, bool) {
- if v.v == nil {
- return Value{}, false
- }
- ctx := v.ctx().opCtx
- x := &adt.Vertex{
- Parent: v.v,
- Label: 0,
- }
- v.v.Finalize(ctx)
- v.v.MatchAndInsert(ctx, x)
- if len(x.Conjuncts) == 0 {
- return Value{}, false
- }
- x.Finalize(ctx)
- return makeValue(v.idx, x), true
-}
-
-// // BulkOptionals returns all bulk optional fields as key-value pairs.
-// // See also Elem and Template.
-// func (v Value) BulkOptionals() [][2]Value {
-// x, ok := v.path.cache.(*structLit)
-// if !ok {
-// return nil
-// }
-// return v.appendBulk(nil, x.optionals)
-// }
-
-// func (v Value) appendBulk(a [][2]Value, x *optionals) [][2]Value {
-// if x == nil {
-// return a
-// }
-// a = v.appendBulk(a, x.left)
-// a = v.appendBulk(a, x.right)
-// for _, set := range x.fields {
-// if set.key != nil {
-// ctx := v.ctx()
-// fn, ok := ctx.manifest(set.value).(*lambdaExpr)
-// if !ok {
-// // create error
-// continue
-// }
-// x := fn.call(ctx, set.value, &basicType{K: stringKind})
-
-// a = append(a, [2]Value{v.makeElem(set.key), v.makeElem(x)})
-// }
-// }
-// return a
-// }
-
-// List creates an iterator over the values of a list or reports an error if
-// v is not a list.
-func (v Value) List() (Iterator, error) {
- v, _ = v.Default()
- ctx := v.ctx()
- if err := v.checkKind(ctx, listKind); err != nil {
- return Iterator{ctx: ctx}, v.toErr(err)
- }
- arcs := []field{}
- for _, a := range v.v.Elems() {
- if a.Label.IsInt() {
- arcs = append(arcs, field{arc: a})
- }
- }
- return Iterator{ctx: ctx, val: v, arcs: arcs}, nil
-}
-
-// Null reports an error if v is not null.
-func (v Value) Null() error {
- v, _ = v.Default()
- if err := v.checkKind(v.ctx(), nullKind); err != nil {
- return v.toErr(err)
- }
- return nil
-}
-
-// // IsNull reports whether v is null.
-// func (v Value) IsNull() bool {
-// return v.Null() == nil
-// }
-
-// Bool returns the bool value of v or false and an error if v is not a boolean.
-func (v Value) Bool() (bool, error) {
- v, _ = v.Default()
- ctx := v.ctx()
- if err := v.checkKind(ctx, boolKind); err != nil {
- return false, v.toErr(err)
- }
- return v.eval(ctx).(*boolLit).B, nil
-}
-
-// String returns the string value if v is a string or an error otherwise.
-func (v Value) String() (string, error) {
- v, _ = v.Default()
- ctx := v.ctx()
- if err := v.checkKind(ctx, stringKind); err != nil {
- return "", v.toErr(err)
- }
- return v.eval(ctx).(*stringLit).Str, nil
-}
-
-// Bytes returns a byte slice if v represents a list of bytes or an error
-// otherwise.
-func (v Value) Bytes() ([]byte, error) {
- v, _ = v.Default()
- ctx := v.ctx()
- switch x := v.eval(ctx).(type) {
- case *bytesLit:
- return append([]byte(nil), x.B...), nil
- case *stringLit:
- return []byte(x.Str), nil
- }
- return nil, v.toErr(v.checkKind(ctx, bytesKind|stringKind))
-}
-
-// Reader returns a new Reader if v is a string or bytes type and an error
-// otherwise.
-func (v Value) Reader() (io.Reader, error) {
- v, _ = v.Default()
- ctx := v.ctx()
- switch x := v.eval(ctx).(type) {
- case *bytesLit:
- return bytes.NewReader(x.B), nil
- case *stringLit:
- return strings.NewReader(x.Str), nil
- }
- return nil, v.toErr(v.checkKind(ctx, stringKind|bytesKind))
-}
-
-// TODO: distinguish between optional, hidden, etc. Probably the best approach
-// is to mark options in context and have a single function for creating
-// a structVal.
-
-// structVal returns an structVal or an error if v is not a struct.
-func (v Value) structValData(ctx *context) (structValue, *bottom) {
- return v.structValOpts(ctx, options{
- omitHidden: true,
- omitDefinitions: true,
- omitOptional: true,
- })
-}
-
-func (v Value) structValFull(ctx *context) (structValue, *bottom) {
- return v.structValOpts(ctx, options{})
-}
-
-// structVal returns an structVal or an error if v is not a struct.
-func (v Value) structValOpts(ctx *context, o options) (structValue, *bottom) {
- v, _ = v.Default()
-
- obj, err := v.getStruct()
- if err != nil {
- return structValue{}, err
- }
-
- features := export.VertexFeatures(obj)
-
- k := 0
- for _, f := range features {
- if f.IsDef() && (o.omitDefinitions || o.concrete) {
- continue
- }
- if f.IsHidden() && o.omitHidden {
- continue
- }
- if arc := obj.Lookup(f); arc == nil {
- if o.omitOptional {
- continue
- }
- // ensure it really exists.
- v := adt.Vertex{
- Parent: obj,
- Label: f,
- }
- obj.MatchAndInsert(ctx.opCtx, &v)
- if len(v.Conjuncts) == 0 {
- continue
- }
- }
- features[k] = f
- k++
- }
- features = features[:k]
- return structValue{ctx, v, obj, features}, nil
-}
-
-// Struct returns the underlying struct of a value or an error if the value
-// is not a struct.
-func (v Value) Struct() (*Struct, error) {
- ctx := v.ctx()
- obj, err := v.structValOpts(ctx, options{})
- if err != nil {
- return nil, v.toErr(err)
- }
- return &Struct{obj}, nil
-}
-
-func (v Value) getStruct() (*structLit, *bottom) {
- ctx := v.ctx()
- if err := v.checkKind(ctx, structKind); err != nil {
- if !err.ChildError {
- return nil, err
- }
- }
- return v.v, nil
-}
-
-// Struct represents a CUE struct value.
-type Struct struct {
- structValue
-}
-
-// FieldInfo contains information about a struct field.
-type FieldInfo struct {
- Selector string
- Name string // Deprecated: use Selector
- Pos int
- Value Value
-
- IsDefinition bool
- IsOptional bool
- IsHidden bool
-}
-
-func (s *Struct) Len() int {
- return s.structValue.Len()
-}
-
-// field reports information about the ith field, i < o.Len().
-func (s *Struct) Field(i int) FieldInfo {
- a, opt := s.at(i)
- ctx := s.v.ctx()
-
- v := makeValue(s.v.idx, a)
- name := ctx.LabelStr(a.Label)
- str := a.Label.SelectorString(ctx.opCtx)
- return FieldInfo{str, name, i, v, a.Label.IsDef(), opt, a.Label.IsHidden()}
-}
-
-// FieldByName looks up a field for the given name. If isIdent is true, it will
-// look up a definition or hidden field (starting with `_` or `_#`). Otherwise
-// it interprets name as an arbitrary string for a regular field.
-func (s *Struct) FieldByName(name string, isIdent bool) (FieldInfo, error) {
- f := s.v.ctx().Label(name, isIdent)
- for i, a := range s.features {
- if a == f {
- return s.Field(i), nil
- }
- }
- return FieldInfo{}, errNotFound
-}
-
-// Fields creates an iterator over the Struct's fields.
-func (s *Struct) Fields(opts ...Option) *Iterator {
- iter, _ := s.v.Fields(opts...)
- return iter
-}
-
-// Fields creates an iterator over v's fields if v is a struct or an error
-// otherwise.
-func (v Value) Fields(opts ...Option) (*Iterator, error) {
- o := options{omitDefinitions: true, omitHidden: true, omitOptional: true}
- o.updateOptions(opts)
- ctx := v.ctx()
- obj, err := v.structValOpts(ctx, o)
- if err != nil {
- return &Iterator{ctx: ctx}, v.toErr(err)
- }
-
- arcs := []field{}
- for i := range obj.features {
- arc, isOpt := obj.at(i)
- arcs = append(arcs, field{arc: arc, isOptional: isOpt})
- }
- return &Iterator{ctx: ctx, val: v, arcs: arcs}, nil
-}
-
-// Lookup reports the value at a path starting from v. The empty path returns v
-// itself. Use LookupDef for definitions or LookupField for any kind of field.
-//
-// The Exists() method can be used to verify if the returned value existed.
-// Lookup cannot be used to look up hidden or optional fields or definitions.
-func (v Value) Lookup(path ...string) Value {
- ctx := v.ctx()
- for _, k := range path {
- // TODO(eval) TODO(error): always search in full data and change error
- // message if a field is found but is of the incorrect type.
- obj, err := v.structValData(ctx)
- if err != nil {
- // TODO: return a Value at the same location and a new error?
- return newErrValue(v, err)
- }
- v = obj.Lookup(k)
- }
- return v
-}
-
-// LookupDef reports the definition with the given name within struct v. The
-// Exists method of the returned value will report false if the definition did
-// not exist. The Err method reports if any error occurred during evaluation.
-func (v Value) LookupDef(name string) Value {
- ctx := v.ctx()
- o, err := v.structValFull(ctx)
- if err != nil {
- return newErrValue(v, err)
- }
-
- f := v.ctx().Label(name, true)
- for i, a := range o.features {
- if a == f {
- if f.IsHidden() || !f.IsDef() { // optional not possible for now
- break
- }
- return newChildValue(&o, i)
- }
- }
- if !strings.HasPrefix(name, "#") {
- alt := v.LookupDef("#" + name)
- // Use the original error message if this resulted in an error as well.
- if alt.Err() == nil {
- return alt
- }
- }
- return newErrValue(v, ctx.mkErr(v.v, "definition %q not found", name))
-}
-
-var errNotFound = errors.Newf(token.NoPos, "field not found")
-
-// FieldByName looks up a field for the given name. If isIdent is true, it will
-// look up a definition or hidden field (starting with `_` or `_#`). Otherwise
-// it interprets name as an arbitrary string for a regular field.
-func (v Value) FieldByName(name string, isIdent bool) (f FieldInfo, err error) {
- s, err := v.Struct()
- if err != nil {
- return f, err
- }
- return s.FieldByName(name, isIdent)
-}
-
-// LookupField reports information about a field of v.
-//
-// Deprecated: this API does not work with new-style definitions. Use FieldByName.
-func (v Value) LookupField(name string) (FieldInfo, error) {
- s, err := v.Struct()
- if err != nil {
- // TODO: return a Value at the same location and a new error?
- return FieldInfo{}, err
- }
- f, err := s.FieldByName(name, true)
- if err != nil {
- return f, err
- }
- if f.IsHidden {
- return f, errNotFound
- }
- return f, err
-}
-
-// TODO: expose this API?
-//
-// // EvalExpr evaluates an expression within the scope of v, which must be
-// // a struct.
-// //
-// // Expressions may refer to builtin packages if they can be uniquely identified.
-// func (v Value) EvalExpr(expr ast.Expr) Value {
-// ctx := v.ctx()
-// result := evalExpr(ctx, v.eval(ctx), expr)
-// return newValueRoot(ctx, result)
-// }
-
-// Fill creates a new value by unifying v with the value of x at the given path.
-//
-// Values may be any Go value that can be converted to CUE, an ast.Expr or
-// a Value. In the latter case, it will panic if the Value is not from the same
-// Runtime.
-//
-// Any reference in v referring to the value at the given path will resolve
-// to x in the newly created value. The resulting value is not validated.
-func (v Value) Fill(x interface{}, path ...string) Value {
- if v.v == nil {
- return v
- }
- ctx := v.ctx()
- for i := len(path) - 1; i >= 0; i-- {
- x = map[string]interface{}{path[i]: x}
- }
- var value = convert.GoValueToExpr(ctx.opCtx, true, x)
- n := &adt.Vertex{Parent: v.v.Parent}
- n.AddConjunct(adt.MakeConjunct(nil, value))
- n.Finalize(ctx.opCtx)
- w := makeValue(v.idx, n)
- return v.Unify(w)
-}
-
-// Template returns a function that represents the template definition for a
-// struct in a configuration file. It returns nil if v is not a struct kind or
-// if there is no template associated with the struct.
-//
-// The returned function returns the value that would be unified with field
-// given its name.
-func (v Value) Template() func(label string) Value {
- // TODO: rename to optional.
- if v.v == nil {
- return nil
- }
-
- types := v.v.OptionalTypes()
- if types&(adt.HasAdditional|adt.HasPattern) == 0 {
- return nil
- }
-
- parent := v.v
- ctx := v.ctx().opCtx
- return func(label string) Value {
- f := ctx.StringLabel(label)
- arc := &adt.Vertex{Parent: parent, Label: f}
- v.v.MatchAndInsert(ctx, arc)
- if len(arc.Conjuncts) == 0 {
- return Value{}
- }
- arc.Finalize(ctx)
- return makeValue(v.idx, arc)
- }
-}
-
-// Subsume reports nil when w is an instance of v or an error otherwise.
-//
-// Without options, the entire value is considered for assumption, which means
-// Subsume tests whether v is a backwards compatible (newer) API version of w.
-// Use the Final() to indicate that the subsumed value is data, and that
-//
-// Use the Final option to check subsumption if a w is known to be final,
-// and should assumed to be closed.
-//
-// Options are currently ignored and the function will panic if any are passed.
-//
-// Value v and w must be obtained from the same build.
-// TODO: remove this requirement.
-func (v Value) Subsume(w Value, opts ...Option) error {
- o := getOptions(opts)
- p := subsume.CUE
- switch {
- case o.final && o.ignoreClosedness:
- p = subsume.FinalOpen
- case o.final:
- p = subsume.Final
- case o.ignoreClosedness:
- p = subsume.API
- }
- p.Defaults = true
- ctx := v.ctx().opCtx
- return p.Value(ctx, v.v, w.v)
-}
-
-// Deprecated: use Subsume.
-//
-// Subsumes reports whether w is an instance of v.
-//
-// Without options, Subsumes checks whether v is a backwards compatbile schema
-// of w.
-//
-// By default, Subsumes tests whether two values are compatible
-// Value v and w must be obtained from the same build.
-// TODO: remove this requirement.
-func (v Value) Subsumes(w Value) bool {
- p := subsume.Profile{Defaults: true}
- return p.Check(v.ctx().opCtx, v.v, w.v)
-}
-
-// Unify reports the greatest lower bound of v and w.
-//
-// Value v and w must be obtained from the same build.
-// TODO: remove this requirement.
-func (v Value) Unify(w Value) Value {
- // ctx := v.ctx()
- if v.v == nil {
- return w
- }
- if w.v == nil {
- return v
- }
- n := &adt.Vertex{Parent: v.v.Parent, Label: v.v.Label}
- n.AddConjunct(adt.MakeConjunct(nil, v.v))
- n.AddConjunct(adt.MakeConjunct(nil, w.v))
-
- ctx := v.idx.newContext()
- n.Finalize(ctx.opCtx)
- return makeValue(v.idx, n)
-}
-
-// UnifyAccept is as v.Unify(w), but will disregard any field that is allowed
-// in the Value accept.
-func (v Value) UnifyAccept(w Value, accept Value) Value {
- if v.v == nil {
- return w
- }
- if w.v == nil {
- return v
- }
- if accept.v == nil {
- panic("accept must exist")
- }
-
- n := &adt.Vertex{Parent: v.v.Parent, Label: v.v.Label}
- n.AddConjunct(adt.MakeConjunct(nil, v.v))
- n.AddConjunct(adt.MakeConjunct(nil, w.v))
-
- e := eval.New(v.idx.Runtime)
- ctx := e.NewContext(n)
- e.UnifyAccept(ctx, n, adt.Finalized, accept.v.Closed)
-
- // ctx := v.idx.newContext()
- n.Closed = accept.v.Closed
- n.Finalize(ctx)
- return makeValue(v.idx, n)
-}
-
-// Equals reports whether two values are equal, ignoring optional fields.
-// The result is undefined for incomplete values.
-func (v Value) Equals(other Value) bool {
- if v.v == nil || other.v == nil {
- return false
- }
- return eval.Equal(v.ctx().opCtx, v.v, other.v)
-
-}
-
-// Format prints a debug version of a value.
-func (v Value) Format(state fmt.State, verb rune) {
- ctx := v.ctx()
- if v.v == nil {
- fmt.Fprint(state, "<nil>")
- return
- }
- switch {
- case state.Flag('#'):
- _, _ = io.WriteString(state, ctx.str(v.v))
- case state.Flag('+'):
- _, _ = io.WriteString(state, debugStr(ctx, v.v))
- default:
- n, _ := export.Raw.Expr(v.idx.Runtime, v.v)
- b, _ := format.Node(n)
- _, _ = state.Write(b)
- }
-}
-
-func (v Value) instance() *Instance {
- if v.v == nil {
- return nil
- }
- return v.ctx().getImportFromNode(v.v)
-}
-
-// Reference returns the instance and path referred to by this value such that
-// inst.Lookup(path) resolves to the same value, or no path if this value is not
-// a reference. If a reference contains index selection (foo[bar]), it will
-// only return a reference if the index resolves to a concrete value.
-func (v Value) Reference() (inst *Instance, path []string) {
- // TODO: don't include references to hidden fields.
- if v.v == nil || len(v.v.Conjuncts) != 1 {
- return nil, nil
- }
- ctx := v.ctx()
- c := v.v.Conjuncts[0]
-
- return reference(ctx, c.Env, c.Expr())
-}
-
-func reference(c *context, env *adt.Environment, r adt.Expr) (inst *Instance, path []string) {
- ctx := c.opCtx
- defer ctx.PopState(ctx.PushState(env, r.Source()))
-
- switch x := r.(type) {
- case *adt.FieldReference:
- env := ctx.Env(x.UpCount)
- inst, path = mkPath(c, nil, env.Vertex)
- path = append(path, x.Label.SelectorString(c.Index))
-
- case *adt.LabelReference:
- env := ctx.Env(x.UpCount)
- return mkPath(c, nil, env.Vertex)
-
- case *adt.DynamicReference:
- env := ctx.Env(x.UpCount)
- inst, path = mkPath(c, nil, env.Vertex)
- v, _ := ctx.Evaluate(env, x.Label)
- str := ctx.StringValue(v)
- path = append(path, str)
-
- case *adt.ImportReference:
- imp := x.ImportPath.StringValue(ctx)
- inst = c.index.getImportFromPath(imp)
-
- case *adt.SelectorExpr:
- inst, path = reference(c, env, x.X)
- path = append(path, x.Sel.SelectorString(ctx))
-
- case *adt.IndexExpr:
- inst, path = reference(c, env, x.X)
- v, _ := ctx.Evaluate(env, x.Index)
- str := ctx.StringValue(v)
- path = append(path, str)
- }
- if inst == nil {
- return nil, nil
- }
- return inst, path
-}
-
-func mkPath(ctx *context, a []string, v *adt.Vertex) (inst *Instance, path []string) {
- if v.Parent == nil {
- return ctx.index.getImportFromNode(v), a
- }
- inst, path = mkPath(ctx, a, v.Parent)
- path = append(path, v.Label.SelectorString(ctx.opCtx))
- return inst, path
-}
-
-// // References reports all references used to evaluate this value. It does not
-// // report references for sub fields if v is a struct.
-// //
-// // Deprecated: can be implemented in terms of Reference and Expr.
-// func (v Value) References() [][]string {
-// panic("deprecated")
-// }
-
-type options struct {
- concrete bool // enforce that values are concrete
- raw bool // show original values
- hasHidden bool
- omitHidden bool
- omitDefinitions bool
- omitOptional bool
- omitAttrs bool
- resolveReferences bool
- final bool
- ignoreClosedness bool // used for comparing APIs
- docs bool
- disallowCycles bool // implied by concrete
-}
-
-// An Option defines modes of evaluation.
-type Option option
-
-type option func(p *options)
-
-// Final indicates a value is final. It implicitly closes all structs and lists
-// in a value and selects defaults.
-func Final() Option {
- return func(o *options) {
- o.final = true
- o.omitDefinitions = true
- o.omitOptional = true
- o.omitHidden = true
- }
-}
-
-// Schema specifies the input is a Schema. Used by Subsume.
-func Schema() Option {
- return func(o *options) {
- o.ignoreClosedness = true
- }
-}
-
-// Concrete ensures that all values are concrete.
-//
-// For Validate this means it returns an error if this is not the case.
-// In other cases a non-concrete value will be replaced with an error.
-func Concrete(concrete bool) Option {
- return func(p *options) {
- if concrete {
- p.concrete = true
- p.final = true
- if !p.hasHidden {
- p.omitHidden = true
- p.omitDefinitions = true
- }
- }
- }
-}
-
-// DisallowCycles forces validation in the precense of cycles, even if
-// non-concrete values are allowed. This is implied by Concrete(true).
-func DisallowCycles(disallow bool) Option {
- return func(p *options) { p.disallowCycles = disallow }
-}
-
-// ResolveReferences forces the evaluation of references when outputting.
-// This implies the input cannot have cycles.
-func ResolveReferences(resolve bool) Option {
- return func(p *options) { p.resolveReferences = resolve }
-}
-
-// Raw tells Syntax to generate the value as is without any simplifications.
-func Raw() Option {
- return func(p *options) { p.raw = true }
-}
-
-// All indicates that all fields and values should be included in processing
-// even if they can be elided or omitted.
-func All() Option {
- return func(p *options) {
- p.omitAttrs = false
- p.omitHidden = false
- p.omitDefinitions = false
- p.omitOptional = false
- }
-}
-
-// Docs indicates whether docs should be included.
-func Docs(include bool) Option {
- return func(p *options) { p.docs = true }
-}
-
-// Definitions indicates whether definitions should be included.
-//
-// Definitions may still be included for certain functions if they are referred
-// to by other other values.
-func Definitions(include bool) Option {
- return func(p *options) {
- p.hasHidden = true
- p.omitDefinitions = !include
- }
-}
-
-// Hidden indicates that definitions and hidden fields should be included.
-//
-// Deprecated: Hidden fields are deprecated.
-func Hidden(include bool) Option {
- return func(p *options) {
- p.hasHidden = true
- p.omitHidden = !include
- p.omitDefinitions = !include
- }
-}
-
-// Optional indicates that optional fields should be included.
-func Optional(include bool) Option {
- return func(p *options) { p.omitOptional = !include }
-}
-
-// Attributes indicates that attributes should be included.
-func Attributes(include bool) Option {
- return func(p *options) { p.omitAttrs = !include }
-}
-
-func getOptions(opts []Option) (o options) {
- o.updateOptions(opts)
- return
-}
-
-func (o *options) updateOptions(opts []Option) {
- for _, fn := range opts {
- fn(o)
- }
-}
-
-// Validate reports any errors, recursively. The returned error may represent
-// more than one error, retrievable with errors.Errors, if more than one
-// exists.
-func (v Value) Validate(opts ...Option) error {
- o := options{}
- o.updateOptions(opts)
-
- cfg := &validate.Config{
- Concrete: o.concrete,
- DisallowCycles: o.disallowCycles,
- AllErrors: true,
- }
-
- b := validate.Validate(v.ctx().opCtx, v.v, cfg)
- if b != nil {
- return b.Err
- }
- return nil
-}
-
-// Walk descends into all values of v, calling f. If f returns false, Walk
-// will not descent further. It only visits values that are part of the data
-// model, so this excludes optional fields, hidden fields, and definitions.
-func (v Value) Walk(before func(Value) bool, after func(Value)) {
- ctx := v.ctx()
- switch v.Kind() {
- case StructKind:
- if before != nil && !before(v) {
- return
- }
- obj, _ := v.structValData(ctx)
- for i := 0; i < obj.Len(); i++ {
- _, v := obj.At(i)
- v.Walk(before, after)
- }
- case ListKind:
- if before != nil && !before(v) {
- return
- }
- list, _ := v.List()
- for list.Next() {
- list.Value().Walk(before, after)
- }
- default:
- if before != nil {
- before(v)
- }
- }
- if after != nil {
- after(v)
- }
-}
-
-// Attribute returns the attribute data for the given key.
-// The returned attribute will return an error for any of its methods if there
-// is no attribute for the requested key.
-func (v Value) Attribute(key string) Attribute {
- // look up the attributes
- if v.v == nil {
- return Attribute{internal.NewNonExisting(key)}
- }
- // look up the attributes
- for _, a := range export.ExtractFieldAttrs(v.v.Conjuncts) {
- k, body := a.Split()
- if key != k {
- continue
- }
- return Attribute{internal.ParseAttrBody(token.NoPos, body)}
- }
-
- return Attribute{internal.NewNonExisting(key)}
-}
-
-// An Attribute contains meta data about a field.
-type Attribute struct {
- attr internal.Attr
-}
-
-// Err returns the error associated with this Attribute or nil if this
-// attribute is valid.
-func (a *Attribute) Err() error {
- return a.attr.Err
-}
-
-// String reports the possibly empty string value at the given position or
-// an error the attribute is invalid or if the position does not exist.
-func (a *Attribute) String(pos int) (string, error) {
- return a.attr.String(pos)
-}
-
-// Int reports the integer at the given position or an error if the attribute is
-// invalid, the position does not exist, or the value at the given position is
-// not an integer.
-func (a *Attribute) Int(pos int) (int64, error) {
- return a.attr.Int(pos)
-}
-
-// Flag reports whether an entry with the given name exists at position pos or
-// onwards or an error if the attribute is invalid or if the first pos-1 entries
-// are not defined.
-func (a *Attribute) Flag(pos int, key string) (bool, error) {
- return a.attr.Flag(pos, key)
-}
-
-// Lookup searches for an entry of the form key=value from position pos onwards
-// and reports the value if found. It reports an error if the attribute is
-// invalid or if the first pos-1 entries are not defined.
-func (a *Attribute) Lookup(pos int, key string) (val string, found bool, err error) {
- return a.attr.Lookup(pos, key)
-}
-
-// Expr reports the operation of the underlying expression and the values it
-// operates on.
-//
-// For unary expressions, it returns the single value of the expression.
-//
-// For binary expressions it returns first the left and right value, in that
-// order. For associative operations however, (for instance '&' and '|'), it may
-// return more than two values, where the operation is to be applied in
-// sequence.
-//
-// For selector and index expressions it returns the subject and then the index.
-// For selectors, the index is the string value of the identifier.
-//
-// For interpolations it returns a sequence of values to be concatenated, some
-// of which will be literal strings and some unevaluated expressions.
-//
-// A builtin call expression returns the value of the builtin followed by the
-// args of the call.
-func (v Value) Expr() (Op, []Value) {
- // TODO: return v if this is complete? Yes for now
- if v.v == nil {
- return NoOp, nil
- }
-
- var expr adt.Expr
- var env *adt.Environment
-
- if v.v.IsData() {
- switch x := v.v.Value.(type) {
- case *adt.ListMarker, *adt.StructMarker:
- expr = v.v
- default:
- expr = x
- }
-
- } else {
- switch len(v.v.Conjuncts) {
- case 0:
- if v.v.Value == nil {
- return NoOp, []Value{makeValue(v.idx, v.v)}
- }
- switch x := v.v.Value.(type) {
- case *adt.ListMarker, *adt.StructMarker:
- expr = v.v
- default:
- expr = x
- }
-
- case 1:
- // the default case, processed below.
- c := v.v.Conjuncts[0]
- env = c.Env
- expr = c.Expr()
- if w, ok := expr.(*adt.Vertex); ok {
- return Value{v.idx, w}.Expr()
- }
-
- default:
- a := []Value{}
- ctx := v.ctx().opCtx
- for _, c := range v.v.Conjuncts {
- n := &adt.Vertex{
- Parent: v.v.Parent,
- Label: v.v.Label,
- }
- n.AddConjunct(c)
- n.Finalize(ctx)
- a = append(a, makeValue(v.idx, n))
- }
- return adt.AndOp, a
- }
- }
-
- // TODO: replace appends with []Value{}. For not leave.
- a := []Value{}
- op := NoOp
- switch x := expr.(type) {
- case *binaryExpr:
- a = append(a, remakeValue(v, env, x.X))
- a = append(a, remakeValue(v, env, x.Y))
- op = x.Op
- case *unaryExpr:
- a = append(a, remakeValue(v, env, x.X))
- op = x.Op
- case *boundExpr:
- a = append(a, remakeValue(v, env, x.Expr))
- op = x.Op
- case *boundValue:
- a = append(a, remakeValue(v, env, x.Value))
- op = x.Op
- case *adt.Conjunction:
- // pre-expanded unification
- for _, conjunct := range x.Values {
- a = append(a, remakeValue(v, env, conjunct))
- }
- op = AndOp
- case *adt.Disjunction:
- count := 0
- outer:
- for i, disjunct := range x.Values {
- if i < x.NumDefaults {
- for _, n := range x.Values[x.NumDefaults:] {
- if subsume.Value(v.ctx().opCtx, n, disjunct) == nil {
- continue outer
- }
- }
- }
- count++
- a = append(a, remakeValue(v, env, disjunct))
- }
- if count > 1 {
- op = OrOp
- }
-
- case *adt.DisjunctionExpr:
- // Filter defaults that are subsumed by another value.
- count := 0
- outerExpr:
- for _, disjunct := range x.Values {
- if disjunct.Default {
- for _, n := range x.Values {
- a := adt.Vertex{
- Parent: v.v.Parent,
- Label: v.v.Label,
- Closed: v.v.Closed,
- }
- b := a
- a.AddConjunct(adt.MakeConjunct(env, n.Val))
- b.AddConjunct(adt.MakeConjunct(env, disjunct.Val))
-
- e := eval.New(v.idx.Runtime)
- ctx := e.NewContext(nil)
- e.UnifyAccept(ctx, &a, adt.Finalized, v.v.Closed)
- e.UnifyAccept(ctx, &b, adt.Finalized, v.v.Closed)
- if !n.Default && subsume.Value(ctx, &a, &b) == nil {
- continue outerExpr
- }
- }
- }
- count++
- a = append(a, remakeValue(v, env, disjunct.Val))
- }
- if count > 1 {
- op = adt.OrOp
- }
-
- case *interpolation:
- for _, p := range x.Parts {
- a = append(a, remakeValue(v, env, p))
- }
- op = InterpolationOp
-
- case *adt.FieldReference:
- // TODO: allow hard link
- ctx := v.ctx().opCtx
- f := ctx.PushState(env, x.Src)
- env := ctx.Env(x.UpCount)
- a = append(a, remakeValue(v, nil, &adt.NodeLink{Node: env.Vertex}))
- a = append(a, remakeValue(v, nil, ctx.NewString(x.Label.SelectorString(ctx))))
- _ = ctx.PopState(f)
- op = SelectorOp
-
- case *selectorExpr:
- a = append(a, remakeValue(v, env, x.X))
- // A string selector is quoted.
- a = append(a, remakeValue(v, env, &adt.String{
- Str: x.Sel.SelectorString(v.idx.Index),
- }))
- op = SelectorOp
-
- case *indexExpr:
- a = append(a, remakeValue(v, env, x.X))
- a = append(a, remakeValue(v, env, x.Index))
- op = IndexOp
- case *sliceExpr:
- a = append(a, remakeValue(v, env, x.X))
- a = append(a, remakeValue(v, env, x.Lo))
- a = append(a, remakeValue(v, env, x.Hi))
- op = SliceOp
- case *callExpr:
- a = append(a, remakeValue(v, env, x.Fun))
- for _, arg := range x.Args {
- a = append(a, remakeValue(v, env, arg))
- }
- op = CallOp
- case *customValidator:
- a = append(a, remakeValue(v, env, x.Builtin))
- for _, arg := range x.Args {
- a = append(a, remakeValue(v, env, arg))
- }
- op = CallOp
-
- case *adt.StructLit:
- // Simulate old embeddings.
- envEmbed := &adt.Environment{
- Up: env,
- Vertex: v.v,
- }
- fields := []adt.Decl{}
- ctx := v.ctx().opCtx
- for _, d := range x.Decls {
- switch x := d.(type) {
- case adt.Expr:
- // embedding
- n := &adt.Vertex{
- Parent: v.v.Parent,
- Label: v.v.Label}
- c := adt.MakeConjunct(envEmbed, x)
- n.AddConjunct(c)
- n.Finalize(ctx)
- a = append(a, makeValue(v.idx, n))
-
- default:
- fields = append(fields, d)
- }
- }
- if len(a) == 0 {
- a = append(a, v)
- break
- }
-
- if len(fields) > 0 {
- n := &adt.Vertex{
- Parent: v.v.Parent,
- Label: v.v.Label,
- }
- c := adt.MakeConjunct(env, &adt.StructLit{
- Decls: fields,
- })
- n.AddConjunct(c)
- n.Finalize(ctx)
- a = append(a, makeValue(v.idx, n))
- }
-
- op = adt.AndOp
-
- default:
- a = append(a, v)
- }
- return op, a
-}
diff --git a/internal/legacy/cue/types_test.go b/internal/legacy/cue/types_test.go
deleted file mode 100644
index b36c6c5..0000000
--- a/internal/legacy/cue/types_test.go
+++ /dev/null
@@ -1,2958 +0,0 @@
-// 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 cue
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "math"
- "math/big"
- "reflect"
- "strconv"
- "strings"
- "testing"
-
- "github.com/google/go-cmp/cmp"
-
- "cuelang.org/go/cue/ast"
- "cuelang.org/go/cue/errors"
- "cuelang.org/go/internal"
- "cuelang.org/go/internal/core/adt"
- "cuelang.org/go/internal/core/debug"
-)
-
-func getInstance(t *testing.T, body ...string) *Instance {
- t.Helper()
-
- insts := Build(makeInstances([]*bimport{{files: body}}))
- if insts[0].Err != nil {
- t.Fatalf("unexpected parse error: %v", insts[0].Err)
- }
- return insts[0]
-}
-
-func TestValueType(t *testing.T) {
- testCases := []struct {
- value string
- kind Kind
- incompleteKind Kind
- json string
- valid bool
- concrete bool
- closed bool
- // pos token.Pos
- }{{ // Not a concrete value.
- value: `v: _`,
- kind: BottomKind,
- incompleteKind: TopKind,
- }, {
- value: `v: _|_`,
- kind: BottomKind,
- incompleteKind: BottomKind,
- concrete: true,
- }, {
- value: `v: 1&2`,
- kind: BottomKind,
- incompleteKind: BottomKind,
- concrete: true,
- }, {
- value: `v: b, b: 1&2`,
- kind: BottomKind,
- incompleteKind: BottomKind,
- concrete: true,
- }, {
- value: `v: (b[a]), b: 1, a: 1`,
- kind: BottomKind,
- incompleteKind: BottomKind,
- concrete: true,
- }, { // TODO: should be error{
- value: `v: (b)
- b: bool`,
- kind: BottomKind,
- incompleteKind: BoolKind,
- }, {
- value: `v: ([][b]), b: "d"`,
- kind: BottomKind,
- incompleteKind: BottomKind,
- concrete: true,
- }, {
- value: `v: null`,
- kind: NullKind,
- incompleteKind: NullKind,
- concrete: true,
- }, {
- value: `v: true`,
- kind: BoolKind,
- incompleteKind: BoolKind,
- concrete: true,
- }, {
- value: `v: false`,
- kind: BoolKind,
- incompleteKind: BoolKind,
- concrete: true,
- }, {
- value: `v: bool`,
- kind: BottomKind,
- incompleteKind: BoolKind,
- }, {
- value: `v: 2`,
- kind: IntKind,
- incompleteKind: IntKind,
- concrete: true,
- }, {
- value: `v: 2.0`,
- kind: FloatKind,
- incompleteKind: FloatKind,
- concrete: true,
- }, {
- value: `v: 2.0Mi`,
- kind: IntKind,
- incompleteKind: IntKind,
- concrete: true,
- }, {
- value: `v: 14_000`,
- kind: IntKind,
- incompleteKind: IntKind,
- concrete: true,
- }, {
- value: `v: >=0 & <5`,
- kind: BottomKind,
- incompleteKind: NumberKind,
- }, {
- value: `v: float`,
- kind: BottomKind,
- incompleteKind: FloatKind,
- }, {
- value: `v: "str"`,
- kind: StringKind,
- incompleteKind: StringKind,
- concrete: true,
- }, {
- value: "v: '''\n'''",
- kind: BytesKind,
- incompleteKind: BytesKind,
- concrete: true,
- }, {
- value: "v: string",
- kind: BottomKind,
- incompleteKind: StringKind,
- }, {
- value: `v: {}`,
- kind: StructKind,
- incompleteKind: StructKind,
- concrete: true,
- }, {
- value: `v: close({})`,
- kind: StructKind,
- incompleteKind: StructKind,
- concrete: true,
- closed: true,
- }, {
- value: `v: []`,
- kind: ListKind,
- incompleteKind: ListKind,
- concrete: true,
- closed: true,
- }, {
- value: `v: [...int]`,
- kind: BottomKind,
- incompleteKind: ListKind,
- concrete: false,
- }, {
- value: `v: {a: int, b: [1][a]}.b`,
- kind: BottomKind,
- concrete: false,
- }, {
- value: `import "time"
- v: time.Time`,
- kind: BottomKind,
- incompleteKind: StringKind,
- concrete: false,
- }, {
- value: `import "time"
- v: {a: time.Time}.a`,
- kind: BottomKind,
- incompleteKind: StringKind,
- concrete: false,
- }, {
- value: `import "time"
- v: {a: time.Time & string}.a`,
- kind: BottomKind,
- incompleteKind: StringKind,
- concrete: false,
- }, {
- value: `import "strings"
- v: {a: strings.ContainsAny("D")}.a`,
- kind: BottomKind,
- incompleteKind: StringKind,
- concrete: false,
- }, {
- value: `import "struct"
- v: {a: struct.MaxFields(2) & {}}.a`,
- kind: StructKind, // Can determine a valid struct already.
- incompleteKind: StructKind,
- concrete: true,
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- inst := getInstance(t, tc.value)
- v := inst.Lookup("v")
- if got := v.Kind(); got != tc.kind {
- t.Errorf("Kind: got %x; want %x", int(got), int(tc.kind))
- }
- want := tc.incompleteKind | BottomKind
- if got := v.IncompleteKind(); got != want {
- t.Errorf("IncompleteKind: got %x; want %x", int(got), int(want))
- }
- if got := v.IsConcrete(); got != tc.concrete {
- t.Errorf("IsConcrete: got %v; want %v", got, tc.concrete)
- }
- if got := v.IsClosed(); got != tc.closed {
- t.Errorf("IsClosed: got %v; want %v", got, tc.closed)
- }
- })
- }
-}
-
-func TestInt(t *testing.T) {
- testCases := []struct {
- value string
- int int64
- uint uint64
- base int
- err string
- errU string
- notInt bool
- }{{
- value: "1",
- int: 1,
- uint: 1,
- }, {
- value: "-1",
- int: -1,
- uint: 0,
- errU: ErrAbove.Error(),
- }, {
- value: "-111222333444555666777888999000",
- int: math.MinInt64,
- uint: 0,
- err: ErrAbove.Error(),
- errU: ErrAbove.Error(),
- }, {
- value: "111222333444555666777888999000",
- int: math.MaxInt64,
- uint: math.MaxUint64,
- err: ErrBelow.Error(),
- errU: ErrBelow.Error(),
- }, {
- value: "1.0",
- err: "cannot use value 1.0 (type float) as int",
- errU: "cannot use value 1.0 (type float) as int",
- notInt: true,
- }, {
- value: "int",
- err: "non-concrete value int",
- errU: "non-concrete value int",
- notInt: true,
- }, {
- value: "_|_",
- err: "from source",
- errU: "from source",
- notInt: true,
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- n := getInstance(t, tc.value).Value()
- base := 10
- if tc.base > 0 {
- base = tc.base
- }
- b, err := n.AppendInt(nil, base)
- if checkFailed(t, err, tc.err, "append") {
- want := tc.value
- if got := string(b); got != want {
- t.Errorf("append: got %v; want %v", got, want)
- }
- }
-
- vi, err := n.Int64()
- checkErr(t, err, tc.err, "Int64")
- if vi != tc.int {
- t.Errorf("Int64: got %v; want %v", vi, tc.int)
- }
-
- vu, err := n.Uint64()
- checkErr(t, err, tc.errU, "Uint64")
- if vu != uint64(tc.uint) {
- t.Errorf("Uint64: got %v; want %v", vu, tc.uint)
- }
- })
- }
-}
-
-func TestFloat(t *testing.T) {
- testCases := []struct {
- value string
- float string
- float64 float64
- mant string
- exp int
- fmt byte
- prec int
- kind Kind
- err string
- }{{
- value: "1",
- float: "1",
- mant: "1",
- exp: 0,
- float64: 1,
- fmt: 'g',
- kind: IntKind,
- }, {
- value: "-1",
- float: "-1",
- mant: "-1",
- exp: 0,
- float64: -1,
- fmt: 'g',
- kind: IntKind,
- }, {
- value: "1.0",
- float: "1.0",
- mant: "10",
- exp: -1,
- float64: 1.0,
- fmt: 'g',
- kind: FloatKind,
- }, {
- value: "2.6",
- float: "2.6",
- mant: "26",
- exp: -1,
- float64: 2.6,
- fmt: 'g',
- kind: FloatKind,
- }, {
- value: "20.600",
- float: "20.60",
- mant: "20600",
- exp: -3,
- float64: 20.60,
- prec: 2,
- fmt: 'f',
- kind: FloatKind,
- }, {
- value: "1/0",
- float: "",
- float64: 0,
- prec: 2,
- fmt: 'f',
- err: "division by zero",
- kind: BottomKind,
- }, {
- value: "1.797693134862315708145274237317043567982e+308",
- float: "1.8e+308",
- mant: "1797693134862315708145274237317043567982",
- exp: 269,
- float64: math.Inf(1),
- prec: 2,
- fmt: 'g',
- err: ErrAbove.Error(),
- kind: FloatKind,
- }, {
- value: "-1.797693134862315708145274237317043567982e+308",
- float: "-1.8e+308",
- mant: "-1797693134862315708145274237317043567982",
- exp: 269,
- float64: math.Inf(-1),
- prec: 2,
- fmt: 'g',
- kind: FloatKind,
- err: ErrBelow.Error(),
- }, {
- value: "4.940656458412465441765687928682213723650e-324",
- float: "4.941e-324",
- mant: "4940656458412465441765687928682213723650",
- exp: -363,
- float64: 0,
- prec: 4,
- fmt: 'g',
- kind: FloatKind,
- err: ErrBelow.Error(),
- }, {
- value: "-4.940656458412465441765687928682213723650e-324",
- float: "-4.940656458412465441765687928682213723650e-324",
- mant: "-4940656458412465441765687928682213723650",
- exp: -363,
- float64: 0,
- prec: -1,
- fmt: 'g',
- kind: FloatKind,
- err: ErrAbove.Error(),
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- n := getInstance(t, tc.value).Value()
- if n.Kind() != tc.kind {
- t.Fatal("Not a number")
- }
-
- var mant big.Int
- exp, err := n.MantExp(&mant)
- mstr := ""
- if err == nil {
- mstr = mant.String()
- }
- if exp != tc.exp || mstr != tc.mant {
- t.Errorf("mantExp: got %s %d; want %s %d", mstr, exp, tc.mant, tc.exp)
- }
-
- b, _ := n.AppendFloat(nil, tc.fmt, tc.prec)
- want := tc.float
- if got := string(b); got != want {
- t.Errorf("append: got %v; want %v", got, want)
- }
-
- f, err := n.Float64()
- checkErr(t, err, tc.err, "Float64")
- if f != tc.float64 {
- t.Errorf("Float64: got %v; want %v", f, tc.float64)
- }
- })
- }
-}
-
-func TestString(t *testing.T) {
- testCases := []struct {
- value string
- str string
- err string
- }{{
- value: `""`,
- str: ``,
- }, {
- value: `"Hello world!"`,
- str: `Hello world!`,
- }, {
- value: `"Hello \(#world)!"
- #world: "world"`,
- str: `Hello world!`,
- }, {
- value: `string`,
- err: "non-concrete value string",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- str, err := getInstance(t, tc.value).Value().String()
- checkFatal(t, err, tc.err, "init")
- if str != tc.str {
- t.Errorf("String: got %q; want %q", str, tc.str)
- }
-
- b, err := getInstance(t, tc.value).Value().Bytes()
- checkFatal(t, err, tc.err, "init")
- if got := string(b); got != tc.str {
- t.Errorf("Bytes: got %q; want %q", got, tc.str)
- }
-
- r, err := getInstance(t, tc.value).Value().Reader()
- checkFatal(t, err, tc.err, "init")
- b, _ = ioutil.ReadAll(r)
- if got := string(b); got != tc.str {
- t.Errorf("Reader: got %q; want %q", got, tc.str)
- }
- })
- }
-}
-
-func TestError(t *testing.T) {
- testCases := []struct {
- value string
- err string
- }{{
- value: `_|_`,
- err: "from source",
- }, {
- value: `"Hello world!"`,
- }, {
- value: `string`,
- err: "",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- err := getInstance(t, tc.value).Value().Err()
- checkErr(t, err, tc.err, "init")
- })
- }
-}
-
-func TestNull(t *testing.T) {
- testCases := []struct {
- value string
- err string
- }{{
- value: `v: _|_`,
- err: "from source",
- }, {
- value: `v: "str"`,
- err: "cannot use value \"str\" (type string) as null",
- }, {
- value: `v: null`,
- }, {
- value: `v: _`,
- err: "non-concrete value _",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- err := getInstance(t, tc.value).Lookup("v").Null()
- checkErr(t, err, tc.err, "init")
- })
- }
-}
-
-func TestBool(t *testing.T) {
- testCases := []struct {
- value string
- bool bool
- err string
- }{{
- value: `_|_`,
- err: "from source",
- }, {
- value: `"str"`,
- err: "cannot use value \"str\" (type string) as bool",
- }, {
- value: `true`,
- bool: true,
- }, {
- value: `false`,
- }, {
- value: `bool`,
- err: "non-concrete value bool",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- got, err := getInstance(t, tc.value).Value().Bool()
- if checkErr(t, err, tc.err, "init") {
- if got != tc.bool {
- t.Errorf("got %v; want %v", got, tc.bool)
- }
- }
- })
- }
-}
-
-func TestList(t *testing.T) {
- testCases := []struct {
- value string
- res string
- err string
- }{{
- value: `_|_`,
- err: "from source",
- }, {
- value: `"str"`,
- err: "cannot use value \"str\" (type string) as list",
- }, {
- value: `[]`,
- res: "[]",
- }, {
- value: `[1,2,3]`,
- res: "[1,2,3,]",
- }, {
- value: `>=5*[1,2,3, ...int]`,
- err: "non-concrete value >=5 in operand to *",
- }, {
- value: `[for x in #y if x > 1 { x }]
- #y: [1,2,3]`,
- res: "[2,3,]",
- }, {
- value: `[int]`,
- err: "cannot convert incomplete value",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- l, err := getInstance(t, tc.value).Value().List()
- checkFatal(t, err, tc.err, "init")
-
- buf := []byte{'['}
- for l.Next() {
- b, err := l.Value().MarshalJSON()
- checkFatal(t, err, tc.err, "list.Value")
- buf = append(buf, b...)
- buf = append(buf, ',')
- }
- buf = append(buf, ']')
- if got := string(buf); got != tc.res {
- t.Errorf("got %v; want %v", got, tc.res)
- }
- })
- }
-}
-
-func TestFields(t *testing.T) {
- testCases := []struct {
- value string
- res string
- err string
- }{{
- value: `{ #def: 1, _hidden: 2, opt?: 3, reg: 4 }`,
- res: "{reg:4,}",
- }, {
- value: `_|_`,
- err: "from source",
- }, {
- value: `"str"`,
- err: "cannot use value \"str\" (type string) as struct",
- }, {
- value: `{}`,
- res: "{}",
- }, {
- value: `{a:1,b:2,c:3}`,
- res: "{a:1,b:2,c:3,}",
- }, {
- value: `{a:1,"_b":2,c:3,_d:4}`,
- res: "{a:1,_b:2,c:3,}",
- }, {
- value: `{_a:"a"}`,
- res: "{}",
- }, {
- value: `{ for k, v in #y if v > 1 {"\(k)": v} }
- #y: {a:1,b:2,c:3}`,
- res: "{b:2,c:3,}",
- }, {
- value: `{ #def: 1, _hidden: 2, opt?: 3, reg: 4 }`,
- res: "{reg:4,}",
- }, {
- value: `{a:1,b:2,c:int}`,
- err: "cannot convert incomplete value",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- obj := getInstance(t, tc.value).Value()
-
- iter, err := obj.Fields()
- checkFatal(t, err, tc.err, "init")
-
- buf := []byte{'{'}
- for iter.Next() {
- buf = append(buf, iter.Label()...)
- buf = append(buf, ':')
- b, err := iter.Value().MarshalJSON()
- checkFatal(t, err, tc.err, "Obj.At")
- buf = append(buf, b...)
- buf = append(buf, ',')
- }
- buf = append(buf, '}')
- if got := string(buf); got != tc.res {
- t.Errorf("got %v; want %v", got, tc.res)
- }
-
- iter, _ = obj.Fields()
- for iter.Next() {
- want, err := iter.Value().MarshalJSON()
- checkFatal(t, err, tc.err, "Obj.At2")
-
- got, err := obj.Lookup(iter.Label()).MarshalJSON()
- checkFatal(t, err, tc.err, "Obj.At2")
-
- if !bytes.Equal(got, want) {
- t.Errorf("Lookup: got %q; want %q", got, want)
- }
- }
- v := obj.Lookup("non-existing")
- checkErr(t, v.Err(), "not found", "non-existing")
- })
- }
-}
-
-func TestAllFields(t *testing.T) {
- testCases := []struct {
- value string
- res string
- err string
- }{{
- value: `{a:1,"_b":2,c:3,_d:4}`,
- res: "{a:1,_b:2,c:3,_d:4,}",
- }, {
- value: `{_a:"a"}`,
- res: `{_a:"a",}`,
- }, {
- value: `{_a:"a", b?: "b", #c: 3}`,
- res: `{_a:"a",b?:"b",#c:3,}`,
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- obj := getInstance(t, tc.value).Value()
-
- var iter *Iterator // Verify that the returned iterator is a pointer.
- iter, err := obj.Fields(All())
- checkFatal(t, err, tc.err, "init")
-
- buf := []byte{'{'}
- for iter.Next() {
- buf = append(buf, iter.Label()...)
- if iter.IsOptional() {
- buf = append(buf, '?')
- }
- buf = append(buf, ':')
- b, err := iter.Value().MarshalJSON()
- checkFatal(t, err, tc.err, "Obj.At")
- buf = append(buf, b...)
- buf = append(buf, ',')
- }
- buf = append(buf, '}')
- if got := string(buf); got != tc.res {
- t.Errorf("got %v; want %v", got, tc.res)
- }
- })
- }
-}
-
-func TestLookup(t *testing.T) {
- var runtime = new(Runtime)
- inst, err := runtime.Compile("x.cue", `
-#V: {
- x: int
-}
-#X: {
- [string]: int64
-} & #V
-v: #X
-`)
- if err != nil {
- t.Fatalf("compile: %v", err)
- }
- // expr, err := parser.ParseExpr("lookup.cue", `v`, parser.DeclarationErrors, parser.AllErrors)
- // if err != nil {
- // log.Fatalf("parseExpr: %v", err)
- // }
- // v := inst.Eval(expr)
- testCases := []struct {
- ref []string
- raw string
- eval string
- }{{
- ref: []string{"v", "x"},
- raw: ">=-9223372036854775808 & <=9223372036854775807 & int",
- eval: "int64",
- }}
- for _, tc := range testCases {
- v := inst.Lookup(tc.ref...)
-
- if got := fmt.Sprint(v); got != tc.raw {
- t.Errorf("got %v; want %v", got, tc.raw)
- }
-
- got := fmt.Sprint(internal.DebugStr(v.Eval().Syntax()))
- if got != tc.eval {
- t.Errorf("got %v; want %v", got, tc.eval)
- }
-
- v = inst.Lookup()
- for _, ref := range tc.ref {
- s, err := v.Struct()
- if err != nil {
- t.Fatal(err)
- }
- fi, err := s.FieldByName(ref, false)
- if err != nil {
- t.Fatal(err)
- }
- v = fi.Value
- }
-
- if got := fmt.Sprint(v); got != tc.raw {
- t.Errorf("got %v; want %v", got, tc.raw)
- }
-
- got = fmt.Sprint(internal.DebugStr(v.Eval().Syntax()))
- if got != tc.eval {
- t.Errorf("got %v; want %v", got, tc.eval)
- }
- }
-}
-
-func compileT(t *testing.T, r *Runtime, s string) *Instance {
- t.Helper()
- inst, err := r.Compile("", s)
- if err != nil {
- t.Fatal(err)
- }
- return inst
-}
-
-func goValue(v Value) interface{} {
- var x interface{}
- err := v.Decode(&x)
- if err != nil {
- return err
- }
- return x
-}
-
-// TODO: Exporting of Vertex as Conjunct
-func TestFill(t *testing.T) {
- r := &Runtime{}
-
- inst, err := r.CompileExpr(ast.NewStruct("bar", ast.NewString("baz")))
- if err != nil {
- t.Fatal(err)
- }
-
- testCases := []struct {
- in string
- x interface{}
- path string // comma-separated path
- out string
- }{{
- in: `
- foo: int
- bar: foo
- `,
- x: 3,
- path: "foo",
- out: `
- foo: 3
- bar: 3
- `,
- }, {
- in: `
- string
- `,
- x: "foo",
- path: "",
- out: `
- "foo"
- `,
- }, {
- in: `
- foo: _
- `,
- x: inst.Value(),
- path: "foo",
- out: `
- {foo: {bar: "baz"}}
- `,
- }}
-
- for _, tc := range testCases {
- var path []string
- if tc.path != "" {
- path = strings.Split(tc.path, ",")
- }
-
- v := compileT(t, r, tc.in).Value()
- v = v.Fill(tc.x, path...)
-
- w := compileT(t, r, tc.out).Value()
-
- if !cmp.Equal(goValue(v), goValue(w)) {
- t.Error(cmp.Diff(goValue(v), goValue(w)))
- t.Errorf("\ngot: %s\nwant: %s", v, w)
- }
- }
-}
-
-func TestFill2(t *testing.T) {
- r := &Runtime{}
-
- root, err := r.Compile("test", `
- #Provider: {
- ID: string
- notConcrete: bool
- a: int
- b: int
- }
- `)
-
- if err != nil {
- t.Fatal(err)
- }
-
- spec := root.LookupDef("#Provider")
- providerInstance := spec.Fill("12345", "ID")
- root, err = root.Fill(providerInstance, "providers", "myprovider")
- if err != nil {
- t.Fatal(err)
- }
-
- got := fmt.Sprint(root.Value())
- want := `{
- #Provider: {
- ID: string
- notConcrete: bool
- a: int
- b: int
- }
- providers: {
- myprovider: {
- ID: "12345"
- notConcrete: bool
- a: int
- b: int
- }
- }
-}`
- if got != want {
- t.Errorf("got: %s\nwant: %s", got, want)
- }
-}
-
-func TestValue_LookupDef(t *testing.T) {
- r := &Runtime{}
-
- testCases := []struct {
- in string
- def string // comma-separated path
- exists bool
- out string
- }{{
- in: `#foo: 3`,
- def: "#foo",
- out: `3`,
- }, {
- in: `_foo: 3`,
- def: "_foo",
- out: `_|_ // definition "_foo" not found`,
- }, {
- in: `_#foo: 3`,
- def: "_#foo",
- out: `_|_ // definition "_#foo" not found`,
- }}
-
- for _, tc := range testCases {
- t.Run(tc.def, func(t *testing.T) {
- v := compileT(t, r, tc.in).Value()
- v = v.LookupDef(tc.def)
- got := fmt.Sprint(v)
-
- if got != tc.out {
- t.Errorf("\ngot: %s\nwant: %s", got, tc.out)
- }
- })
- }
-}
-
-// TODO: trim down to individual defaults?
-func TestDefaults(t *testing.T) {
- testCases := []struct {
- value string
- def string
- val string
- ok bool
- }{{
- value: `number | *1`,
- def: "1",
- val: "number",
- ok: true,
- }, {
- value: `1 | 2 | *3`,
- def: "3",
- val: "1|2|3",
- ok: true,
- }, {
- value: `*{a:1,b:2}|{a:1}|{b:2}`,
- def: "{a:1,b:2}",
- val: "{a: 1}|{b: 2}",
- ok: true,
- }, {
- value: `{a:1}&{b:2}`,
- def: `({a:1} & {b:2})`,
- val: ``,
- ok: false,
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- v := getInstance(t, "a: "+tc.value).Lookup("a")
-
- d, ok := v.Default()
- if ok != tc.ok {
- t.Errorf("hasDefault: got %v; want %v", ok, tc.ok)
- }
-
- if got := compactRawStr(d); got != tc.def {
- t.Errorf("default: got %v; want %v", got, tc.def)
- }
-
- op, val := d.Expr()
- if op != OrOp {
- return
- }
- vars := []string{}
- for _, v := range val {
- vars = append(vars, fmt.Sprint(v))
- }
- if got := strings.Join(vars, "|"); got != tc.val {
- t.Errorf("value: got %v; want %v", got, tc.val)
- }
- })
- }
-}
-
-func TestLen(t *testing.T) {
- testCases := []struct {
- input string
- length string
- }{{
- input: "[1, 3]",
- length: "2",
- }, {
- input: "[1, 3, ...]",
- length: "int & >=2",
- }, {
- input: `"foo"`,
- length: "3",
- }, {
- input: `'foo'`,
- length: "3",
- // TODO: Currently not supported.
- // }, {
- // input: "{a:1, b:3, a:1, c?: 3, _hidden: 4}",
- // length: "2",
- }, {
- input: "3",
- length: "_|_ // len not supported for type int",
- }}
- for _, tc := range testCases {
- t.Run(tc.input, func(t *testing.T) {
- v := getInstance(t, "a: "+tc.input).Lookup("a")
-
- length := v.Len()
- if got := fmt.Sprint(length); got != tc.length {
- t.Errorf("length: got %v; want %v", got, tc.length)
- }
- })
- }
-}
-
-func TestTemplate(t *testing.T) {
- testCases := []struct {
- value string
- path []string
- want string
- }{{
- value: `
- a: [Name=string]: Name
- `,
- path: []string{"a", ""},
- want: `"label"`,
- }, {
- value: `
- [Name=string]: { a: Name }
- `,
- path: []string{"", "a"},
- want: `"label"`,
- }, {
- value: `
- [Name=string]: { a: Name }
- `,
- path: []string{""},
- want: `{"a":"label"}`,
- }, {
- value: `
- a: [Foo=string]: [Bar=string]: { b: Foo+Bar }
- `,
- path: []string{"a", "", ""},
- want: `{"b":"labellabel"}`,
- }, {
- value: `
- a: [Foo=string]: b: [Bar=string]: { c: Foo+Bar }
- a: foo: b: [Bar=string]: { d: Bar }
- `,
- path: []string{"a", "foo", "b", ""},
- want: `{"d":"label","c":"foolabel"}`,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- v := getInstance(t, tc.value).Value()
- for _, p := range tc.path {
- if p == "" {
- v = v.Template()("label")
- } else {
- v = v.Lookup(p)
- }
- }
- b, err := v.MarshalJSON()
- if err != nil {
- t.Fatal(err)
- }
- if got := string(b); got != tc.want {
- t.Errorf("\n got: %q\nwant: %q", got, tc.want)
- }
- })
- }
-}
-
-func TestElem(t *testing.T) {
- testCases := []struct {
- value string
- path []string
- want string
- }{{
- value: `
- a: [...int]
- `,
- path: []string{"a", ""},
- want: `int`,
- }, {
- value: `
- [Name=string]: { a: Name }
- `,
- path: []string{"", "a"},
- want: `string`,
- }, {
- value: `
- [Name=string]: { a: Name }
- `,
- path: []string{""},
- want: "{\n\ta: string\n}",
- }, {
- value: `
- a: [Foo=string]: [Bar=string]: { b: Foo+Bar }
- `,
- path: []string{"a", "", ""},
- want: "{\n\tb: string + string\n}",
- }, {
- value: `
- a: [Foo=string]: b: [Bar=string]: { c: Foo+Bar }
- a: foo: b: [Bar=string]: { d: Bar }
- `,
- path: []string{"a", "foo", "b", ""},
- want: "{\n\td: string\n\tc: string + string\n}",
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- v := getInstance(t, tc.value).Value()
- v.v.Finalize(v.ctx().opCtx) // TODO: do in instance.
- for _, p := range tc.path {
- if p == "" {
- var ok bool
- v, ok = v.Elem()
- if !ok {
- t.Fatal("expected element")
- }
- } else {
- v = v.Lookup(p)
- }
- }
- got := fmt.Sprint(v)
- // got := debug.NodeString(v.ctx().opCtx, v.v, &debug.Config{Compact: true})
- if got != tc.want {
- t.Errorf("\n got: %q\nwant: %q", got, tc.want)
- }
- })
- }
-}
-
-func TestSubsumes(t *testing.T) {
- a := []string{"a"}
- b := []string{"b"}
- testCases := []struct {
- value string
- pathA []string
- pathB []string
- want bool
- }{{
- value: `4`,
- want: true,
- }, {
- value: `a: string, b: "foo"`,
- pathA: a,
- pathB: b,
- want: true,
- }, {
- value: `a: string, b: "foo"`,
- pathA: b,
- pathB: a,
- want: false,
- }, {
- value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
- pathA: a,
- pathB: b,
- want: true,
- }, {
- value: `a: [string, 4], b: ["foo", 4]`,
- pathA: a,
- pathB: b,
- want: true,
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- v := getInstance(t, tc.value)
- a := v.Lookup(tc.pathA...)
- b := v.Lookup(tc.pathB...)
- got := a.Subsumes(b)
- if got != tc.want {
- t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b)
- }
- })
- }
-}
-
-func TestUnify(t *testing.T) {
- a := []string{"a"}
- b := []string{"b"}
- testCases := []struct {
- value string
- pathA []string
- pathB []string
- want string
- }{{
- value: `4`,
- want: `4`,
- }, {
- value: `a: string, b: "foo"`,
- pathA: a,
- pathB: b,
- want: `"foo"`,
- }, {
- value: `a: string, b: "foo"`,
- pathA: b,
- pathB: a,
- want: `"foo"`,
- }, {
- value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
- pathA: a,
- pathB: b,
- want: `{"a":"foo","b":4}`,
- }, {
- value: `a: [string, 4], b: ["foo", 4]`,
- pathA: a,
- pathB: b,
- want: `["foo",4]`,
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- v := getInstance(t, tc.value).Value()
- x := v.Lookup(tc.pathA...)
- y := v.Lookup(tc.pathB...)
- b, err := x.Unify(y).MarshalJSON()
- if err != nil {
- t.Fatal(err)
- }
- if got := string(b); got != tc.want {
- t.Errorf("got %v; want %v", got, tc.want)
- }
- })
- }
-}
-
-func TestEquals(t *testing.T) {
- testCases := []struct {
- a, b string
- want bool
- }{{
- `4`, `4`, true,
- }, {
- `"str"`, `2`, false,
- }, {
- `2`, `3`, false,
- }, {
- `[1]`, `[3]`, false,
- }, {
- `[{a: 1,...}]`, `[{a: 1,...}]`, true,
- }, {
- `[]`, `[]`, true,
- }, {
- `{
- a: b,
- b: a,
- }`,
- `{
- a: b,
- b: a,
- }`,
- true,
- }, {
- `{
- a: "foo",
- b: "bar",
- }`,
- `{
- a: "foo",
- }`,
- false,
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- var r Runtime
- a, err := r.Compile("a", tc.a)
- if err != nil {
- t.Fatal(err)
- }
- b, err := r.Compile("b", tc.b)
- if err != nil {
- t.Fatal(err)
- }
- got := a.Value().Equals(b.Value())
- if got != tc.want {
- t.Errorf("got %v; want %v", got, tc.want)
- }
- })
- }
-}
-
-func TestDecode(t *testing.T) {
- type fields struct {
- A int `json:"A"`
- B int `json:"B"`
- C int `json:"C"`
- }
- intList := func(ints ...int) *[]int {
- ints = append([]int{}, ints...)
- return &ints
- }
- testCases := []struct {
- value string
- dst interface{}
- want interface{}
- err string
- }{{
- value: `_|_`,
- err: "from source",
- }, {
- value: `"str"`,
- dst: new(string),
- want: "str",
- }, {
- value: `"str"`,
- dst: new(int),
- err: "cannot unmarshal string into Go value of type int",
- }, {
- value: `{}`,
- dst: &fields{},
- want: fields{},
- }, {
- value: `{a:1,b:2,c:3}`,
- dst: &fields{},
- want: fields{A: 1, B: 2, C: 3},
- }, {
- value: `{for k, v in y if v > 1 {"\(k)": v} }
- y: {a:1,b:2,c:3}`,
- dst: &fields{},
- want: fields{B: 2, C: 3},
- }, {
- value: `{a:1,b:2,c:int}`,
- dst: new(fields),
- err: "cannot convert incomplete value",
- }, {
- value: `[]`,
- dst: intList(),
- want: *intList(),
- }, {
- value: `[1,2,3]`,
- dst: intList(),
- want: *intList(1, 2, 3),
- }, {
- value: `[for x in #y if x > 1 { x }]
- #y: [1,2,3]`,
- dst: intList(),
- want: *intList(2, 3),
- }, {
- value: `[int]`,
- err: "cannot convert incomplete value",
- }}
- for _, tc := range testCases {
- t.Run(tc.value, func(t *testing.T) {
- err := getInstance(t, tc.value).Value().Decode(tc.dst)
- checkFatal(t, err, tc.err, "init")
-
- got := reflect.ValueOf(tc.dst).Elem().Interface()
- if !cmp.Equal(got, tc.want) {
- t.Error(cmp.Diff(got, tc.want))
- t.Errorf("\n%#v\n%#v", got, tc.want)
- }
- })
- }
-}
-
-// TODO: options: disallow cycles.
-func TestValidate(t *testing.T) {
- testCases := []struct {
- desc string
- in string
- err bool
- opts []Option
- }{{
- desc: "issue #51",
- in: `
- a: [string]: foo
- a: b: {}
- `,
- err: true,
- }, {
- desc: "concrete",
- in: `
- a: 1
- b: { c: 2, d: 3 }
- c: d: e: f: 5
- g?: int
- `,
- opts: []Option{Concrete(true)},
- }, {
- desc: "definition error",
- in: `
- #b: 1 & 2
- `,
- opts: []Option{},
- err: true,
- }, {
- desc: "definition error okay if optional",
- in: `
- #b?: 1 & 2
- `,
- opts: []Option{},
- }, {
- desc: "definition with optional",
- in: `
- #b: {
- a: int
- b?: >=0
- }
- `,
- opts: []Option{Concrete(true)},
- }, {
- desc: "disjunction",
- in: `a: 1 | 2`,
- }, {
- desc: "disjunction concrete",
- in: `a: 1 | 2`,
- opts: []Option{Concrete(true)},
- err: true,
- }, {
- desc: "incomplete concrete",
- in: `a: string`,
- }, {
- desc: "incomplete",
- in: `a: string`,
- opts: []Option{Concrete(true)},
- err: true,
- }, {
- desc: "list",
- in: `a: [{b: string}, 3]`,
- }, {
- desc: "list concrete",
- in: `a: [{b: string}, 3]`,
- opts: []Option{Concrete(true)},
- err: true,
- }, {
- desc: "allow cycles",
- in: `
- a: b - 100
- b: a + 100
- c: [c[1], c[0]]
- `,
- }, {
- desc: "disallow cycles",
- in: `
- a: b - 100
- b: a + 100
- c: [c[1], c[0]]
- `,
- opts: []Option{DisallowCycles(true)},
- err: true,
- }, {
- desc: "builtins are okay",
- in: `
- import "time"
-
- a: { b: time.Duration } | { c: time.Duration }
- `,
- }, {
- desc: "comprehension error",
- in: `
- a: { if b == "foo" { field: 2 } }
- `,
- err: true,
- }, {
- desc: "ignore optional in schema",
- in: `
- #Schema1: {
- a?: int
- }
- instance1: #Schema1
- `,
- opts: []Option{Concrete(true)},
- }, {
- desc: "issue324",
- in: `
- import "encoding/yaml"
-
- x: string
- a: b: c: *["\(x)"] | _
- d: yaml.Marshal(a.b)
- `,
- }, {
- desc: "allow non-concrete values for definitions",
- in: `
- variables: #variables
-
- {[!~"^[.]"]: #job}
-
- #variables: [string]: int | string
-
- #job: ({a: int} | {b: int}) & {
- "variables"?: #variables
- }
- `,
- }}
- for _, tc := range testCases {
- t.Run(tc.desc, func(t *testing.T) {
- r := Runtime{}
- inst, err := r.Parse("validate", tc.in)
- if err == nil {
- err = inst.Value().Validate(tc.opts...)
- }
- if gotErr := err != nil; gotErr != tc.err {
- t.Errorf("got %v; want %v", err, tc.err)
- }
- })
- }
-}
-
-func TestPath(t *testing.T) {
- config := `
- a: b: c: 5
- b: {
- b1: 3
- b2: 4
- "b 3": 5
- "4b": 6
- l: [
- {a: 2},
- {c: 2},
- ]
- }
- `
- mkpath := func(p ...string) []string { return p }
- testCases := [][]string{
- mkpath("a", "b", "c"),
- mkpath("b", "l", "1", "c"),
- mkpath("b", `"b 3"`),
- mkpath("b", `"4b"`),
- }
- for _, tc := range testCases {
- r := Runtime{}
- inst, err := r.Parse("config", config)
- if err != nil {
- t.Fatal(err)
- }
- t.Run(strings.Join(tc, "."), func(t *testing.T) {
- v := inst.Lookup(tc[0])
- for _, e := range tc[1:] {
- if '0' <= e[0] && e[0] <= '9' {
- i, err := strconv.Atoi(e)
- if err != nil {
- t.Fatal(err)
- }
- iter, err := v.List()
- if err != nil {
- t.Fatal(err)
- }
- for c := 0; iter.Next(); c++ {
- if c == i {
- v = iter.Value()
- break
- }
- }
- } else if e[0] == '"' {
- v = v.Lookup(e[1 : len(e)-1])
- } else {
- v = v.Lookup(e)
- }
- }
- got := v.appendPath(nil)
- if !reflect.DeepEqual(got, tc) {
- t.Errorf("got %v; want %v", got, tc)
- }
- })
- }
-}
-
-func TestValueLookup(t *testing.T) {
- config := `
- a: {
- a: 0
- b: 1
- c: 2
- }
- b: {
- d: a.a
- e: int
- }
- `
-
- strList := func(s ...string) []string { return s }
-
- testCases := []struct {
- config string
- path []string
- str string
- notExists bool
- }{{
- config: "_|_",
- path: strList(""),
- str: "from source",
- }, {
- config: "_|_",
- path: strList("a"),
- str: "from source",
- }, {
- config: config,
- path: strList(),
- str: "{a:{a:0,b:1,c:2},b:{d:0,e:int}",
- }, {
- config: config,
- path: strList("a", "a"),
- str: "0",
- }, {
- config: config,
- path: strList("a"),
- str: "{a:0,b:1,c:2}",
- }, {
- config: config,
- path: strList("b", "d"),
- str: "0",
- }, {
- config: config,
- path: strList("c", "non-existing"),
- str: "not found",
- notExists: true,
- }, {
- config: config,
- path: strList("b", "d", "lookup in non-struct"),
- str: "cannot use value 0 (type int) as struct",
- }}
- for _, tc := range testCases {
- t.Run(tc.str, func(t *testing.T) {
- v := getInstance(t, tc.config).Value().Lookup(tc.path...)
- if got := !v.Exists(); got != tc.notExists {
- t.Errorf("exists: got %v; want %v", got, tc.notExists)
- }
-
- got := v.ctx().opCtx.Str(v.v)
- if tc.str == "" {
- t.Fatalf("str empty, got %q", got)
- }
- if !strings.Contains(got, tc.str) {
- t.Errorf("\n got %v\nwant %v", got, tc.str)
- }
- })
- }
-}
-
-func cmpError(a, b error) bool {
- if a == nil {
- return b == nil
- }
- if b == nil {
- return a == nil
- }
- return a.Error() == b.Error()
-}
-
-func TestAttributeErr(t *testing.T) {
- const config = `
- a: {
- a: 0 @foo(a,b,c=1)
- b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
- }
- `
- testCases := []struct {
- path string
- attr string
- err error
- }{{
- path: "a",
- attr: "foo",
- err: nil,
- }, {
- path: "a",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }, {
- path: "xx",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }, {
- path: "e",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }}
- for _, tc := range testCases {
- t.Run(tc.path+"-"+tc.attr, func(t *testing.T) {
- v := getInstance(t, config).Value().Lookup("a", tc.path)
- a := v.Attribute(tc.attr)
- err := a.Err()
- if !cmpError(err, tc.err) {
- t.Errorf("got %v; want %v", err, tc.err)
- }
- })
- }
-}
-
-func TestAttributeString(t *testing.T) {
- const config = `
- a: {
- a: 0 @foo(a,b,c=1)
- b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
- }
- `
- testCases := []struct {
- path string
- attr string
- pos int
- str string
- err error
- }{{
- path: "a",
- attr: "foo",
- pos: 0,
- str: "a",
- }, {
- path: "a",
- attr: "foo",
- pos: 2,
- str: "c=1",
- }, {
- path: "b",
- attr: "bar",
- pos: 3,
- str: "d=1",
- }, {
- path: "e",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }, {
- path: "b",
- attr: "foo",
- pos: 4,
- err: errors.New("field does not exist"),
- }}
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
- v := getInstance(t, config).Value().Lookup("a", tc.path)
- a := v.Attribute(tc.attr)
- got, err := a.String(tc.pos)
- if !cmpError(err, tc.err) {
- t.Errorf("err: got %v; want %v", err, tc.err)
- }
- if got != tc.str {
- t.Errorf("str: got %v; want %v", got, tc.str)
- }
- })
- }
-}
-
-func TestAttributeInt(t *testing.T) {
- const config = `
- a: {
- a: 0 @foo(1,3,c=1)
- b: 1 @bar(a,-4,c,d=1) @foo(a,,d=1)
- }
- `
- testCases := []struct {
- path string
- attr string
- pos int
- val int64
- err error
- }{{
- path: "a",
- attr: "foo",
- pos: 0,
- val: 1,
- }, {
- path: "b",
- attr: "bar",
- pos: 1,
- val: -4,
- }, {
- path: "e",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }, {
- path: "b",
- attr: "foo",
- pos: 4,
- err: errors.New("field does not exist"),
- }, {
- path: "a",
- attr: "foo",
- pos: 2,
- err: errors.New(`strconv.ParseInt: parsing "c=1": invalid syntax`),
- }}
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
- v := getInstance(t, config).Value().Lookup("a", tc.path)
- a := v.Attribute(tc.attr)
- got, err := a.Int(tc.pos)
- if !cmpError(err, tc.err) {
- t.Errorf("err: got %v; want %v", err, tc.err)
- }
- if got != tc.val {
- t.Errorf("val: got %v; want %v", got, tc.val)
- }
- })
- }
-}
-
-func TestAttributeFlag(t *testing.T) {
- const config = `
- a: {
- a: 0 @foo(a,b,c=1)
- b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
- }
- `
- testCases := []struct {
- path string
- attr string
- pos int
- flag string
- val bool
- err error
- }{{
- path: "a",
- attr: "foo",
- pos: 0,
- flag: "a",
- val: true,
- }, {
- path: "b",
- attr: "bar",
- pos: 1,
- flag: "a",
- val: false,
- }, {
- path: "b",
- attr: "bar",
- pos: 0,
- flag: "c",
- val: true,
- }, {
- path: "e",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }, {
- path: "b",
- attr: "foo",
- pos: 4,
- err: errors.New("field does not exist"),
- }}
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
- v := getInstance(t, config).Value().Lookup("a", tc.path)
- a := v.Attribute(tc.attr)
- got, err := a.Flag(tc.pos, tc.flag)
- if !cmpError(err, tc.err) {
- t.Errorf("err: got %v; want %v", err, tc.err)
- }
- if got != tc.val {
- t.Errorf("val: got %v; want %v", got, tc.val)
- }
- })
- }
-}
-
-func TestAttributeLookup(t *testing.T) {
- const config = `
- a: {
- a: 0 @foo(a,b,c=1)
- b: 1 @bar(a,b,e=-5,d=1) @foo(a,,d=1)
- }
- `
- testCases := []struct {
- path string
- attr string
- pos int
- key string
- val string
- err error
- }{{
- path: "a",
- attr: "foo",
- pos: 0,
- key: "c",
- val: "1",
- }, {
- path: "b",
- attr: "bar",
- pos: 1,
- key: "a",
- val: "",
- }, {
- path: "b",
- attr: "bar",
- pos: 0,
- key: "e",
- val: "-5",
- }, {
- path: "b",
- attr: "bar",
- pos: 0,
- key: "d",
- val: "1",
- }, {
- path: "b",
- attr: "foo",
- pos: 2,
- key: "d",
- val: "1",
- }, {
- path: "b",
- attr: "foo",
- pos: 2,
- key: "f",
- val: "",
- }, {
- path: "e",
- attr: "bar",
- err: errors.New(`attribute "bar" does not exist`),
- }, {
- path: "b",
- attr: "foo",
- pos: 4,
- err: errors.New("field does not exist"),
- }}
- for _, tc := range testCases {
- t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
- v := getInstance(t, config).Value().Lookup("a", tc.path)
- a := v.Attribute(tc.attr)
- got, _, err := a.Lookup(tc.pos, tc.key)
- if !cmpError(err, tc.err) {
- t.Errorf("err: got %v; want %v", err, tc.err)
- }
- if got != tc.val {
- t.Errorf("val: got %v; want %v", got, tc.val)
- }
- })
- }
-}
-
-// TODO: duplicate docs.
-func TestValueDoc(t *testing.T) {
- const config = `
- // foobar defines at least foo.
- package foobar
-
- // A Foo fooses stuff.
- Foo: {
- // field1 is an int.
- field1: int
-
- field2: int
-
- // duplicate field comment
- dup3: int
- }
-
- // foos are instances of Foo.
- foos: [string]: Foo
-
- // My first little foo.
- foos: MyFoo: {
- // local field comment.
- field1: 0
-
- // Dangling comment.
-
- // other field comment.
- field2: 1
-
- // duplicate field comment
- dup3: int
- }
-
- bar: {
- // comment from bar on field 1
- field1: int
- // comment from bar on field 2
- field2: int // don't include this
- }
-
- baz: bar & {
- // comment from baz on field 1
- field1: int
- field2: int
- }
- `
- config2 := `
- // Another Foo.
- Foo: {}
- `
- var r Runtime
- getInst := func(name, body string) *Instance {
- inst, err := r.Compile("dir/file1.cue", body)
- if err != nil {
- t.Fatal(err)
- }
- return inst
- }
-
- inst := getInst("config", config)
-
- v1 := inst.Value()
- v2 := getInst("config2", config2).Value()
- both := v1.Unify(v2)
-
- testCases := []struct {
- val Value
- path string
- doc string
- }{{
- val: v1,
- path: "foos",
- doc: "foos are instances of Foo.\n",
- }, {
- val: v1,
- path: "foos MyFoo",
- doc: "My first little foo.\n",
- }, {
- val: v1,
- path: "foos MyFoo field1",
- doc: `local field comment.
-
-field1 is an int.
-`,
- }, {
- val: v1,
- path: "foos MyFoo field2",
- doc: "other field comment.\n",
- }, {
- // Duplicates are now removed.
- val: v1,
- path: "foos MyFoo dup3",
- doc: "duplicate field comment\n",
- }, {
- val: v1,
- path: "bar field1",
- doc: "comment from bar on field 1\n",
- }, {
- val: v1,
- path: "baz field1",
- doc: `comment from bar on field 1
-
-comment from baz on field 1
-`,
- }, {
- val: v1,
- path: "baz field2",
- doc: "comment from bar on field 2\n",
- }, {
- val: v2,
- path: "Foo",
- doc: `Another Foo.
-`,
- }, {
- val: both,
- path: "Foo",
- doc: `A Foo fooses stuff.
-
-Another Foo.
-`,
- }}
- for _, tc := range testCases {
- t.Run("field:"+tc.path, func(t *testing.T) {
- v := tc.val.Lookup(strings.Split(tc.path, " ")...)
- doc := docStr(v.Doc())
- if doc != tc.doc {
- t.Errorf("doc: got:\n%vwant:\n%v", doc, tc.doc)
- }
- })
- }
- want := "foobar defines at least foo.\n"
- if got := docStr(inst.Doc()); got != want {
- t.Errorf("pkg: got:\n%vwant:\n%v", got, want)
- }
-}
-
-func docStr(docs []*ast.CommentGroup) string {
- doc := ""
- for _, d := range docs {
- if doc != "" {
- doc += "\n"
- }
- doc += d.Text()
- }
- return doc
-}
-
-// TODO: unwrap marshal error
-// TODO: improve error messages
-func TestMarshalJSON(t *testing.T) {
- testCases := []struct {
- value string
- json string
- err string
- }{{
- value: `""`,
- json: `""`,
- }, {
- value: `null`,
- json: `null`,
- }, {
- value: `_|_`,
- err: "from source",
- }, {
- value: `(a.b)
- a: {}`,
- err: "undefined field",
- }, {
- value: `true`,
- json: `true`,
- }, {
- value: `false`,
- json: `false`,
- }, {
- value: `bool`,
- err: "cannot convert incomplete value",
- }, {
- value: `"str"`,
- json: `"str"`,
- }, {
- value: `12_000`,
- json: `12000`,
- }, {
- value: `12.000`,
- json: `12.000`,
- }, {
- value: `12M`,
- json: `12000000`,
- }, {
- value: `3.0e100`,
- json: `3.0E+100`,
- }, {
- value: `0/0`,
- err: "division undefined",
- }, {
- value: `[]`,
- json: `[]`,
- }, {
- value: `[1, 2, 3]`,
- json: `[1,2,3]`,
- }, {
- value: `[int]`,
- err: `0: cannot convert incomplete value`,
- }, {
- value: `(>=3 * [1, 2])`,
- err: "cue: marshal error: non-concrete value >=3 in operand to *",
- }, {
- value: `{}`,
- json: `{}`,
- }, {
- value: `{a: 2, b: 3, c: ["A", "B"]}`,
- json: `{"a":2,"b":3,"c":["A","B"]}`,
- }, {
- value: `{a: 2, b: 3, c: [string, "B"]}`,
- err: `c.0: cannot convert incomplete value`,
- }, {
- value: `{a: [{b: [0, {c: string}] }] }`,
- err: `a.0.b.1.c: cannot convert incomplete value`,
- }, {
- value: `{foo?: 1, bar?: 2, baz: 3}`,
- json: `{"baz":3}`,
- }, {
- // Has an unresolved cycle, but should not matter as all fields involved
- // are optional
- value: `{foo?: bar, bar?: foo, baz: 3}`,
- json: `{"baz":3}`,
- }, {
- // Issue #107
- value: `a: 1.0/1`,
- json: `{"a":1.0}`,
- }, {
- // Issue #108
- value: `
- a: int
- a: >0
- a: <2
-
- b: int
- b: >=0.9
- b: <1.1
-
- c: int
- c: >1
- c: <=2
-
- d: int
- d: >=1
- d: <=1.5
-
- e: int
- e: >=1
- e: <=1.32
-
- f: >=1.1 & <=1.1
- `,
- json: `{"a":1,"b":1,"c":2,"d":1,"e":1,"f":1.1}`,
- }, {
- value: `
- #Task: {
- {
- op: "pull"
- tag: *"latest" | string
- tagInString: tag + "dd"
- } | {
- op: "scratch"
- }
- }
-
- foo: #Task & {"op": "pull"}
- `,
- json: `{"foo":{"op":"pull","tag":"latest","tagInString":"latestdd"}}`,
- }, {
- // Issue #326
- value: `x: "\(string)": "v"`,
- err: `x: invalid interpolation`,
- }, {
- // Issue #326
- value: `x: "\(bool)": "v"`,
- err: `invalid interpolation`,
- }, {
- // Issue #326
- value: `
- x: {
- for k, v in y {
- "\(k)": v
- }
- }
- y: {}
- `,
- json: `{"x":{},"y":{}}`,
- }, {
- // Issue #326
- value: `
- x: {
- for k, v in y {
- "\(k)": v
- }
- }
- y: _
- `,
- err: `x: incomplete feed source`,
- }}
- for i, tc := range testCases {
- t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
- inst := getInstance(t, tc.value)
- b, err := inst.Value().MarshalJSON()
- checkFatal(t, err, tc.err, "init")
-
- if got := string(b); got != tc.json {
- t.Errorf("\n got %v;\nwant %v", got, tc.json)
- }
- })
- }
-}
-
-func TestWalk(t *testing.T) {
- testCases := []struct {
- value string
- out string
- }{{
- value: `""`,
- out: `""`,
- }, {
- value: `null`,
- out: `null`,
- }, {
- value: `_|_`,
- out: "_|_(from source)",
- }, {
- value: `(a.b)
- a: {}`,
- out: `_|_(undefined field b)`,
- }, {
- value: `true`,
- out: `true`,
- }, {
- value: `false`,
- out: `false`,
- }, {
- value: `bool`,
- out: "bool",
- }, {
- value: `"str"`,
- out: `"str"`,
- }, {
- value: `12_000`,
- out: `12000`,
- // out: `12_000`,
- }, {
- value: `12.000`,
- out: `12.000`,
- }, {
- value: `12M`,
- out: `12000000`,
- // out: `12M`,
- }, {
- value: `3.0e100`,
- out: `3.0e+100`,
- // out: `3.0e100`,
- }, {
- value: `[]`,
- out: `[]`,
- }, {
- value: `[1, 2, 3]`,
- out: `[1,2,3]`,
- }, {
- value: `[int]`,
- out: `[int]`,
- }, {
- value: `3 * [1, 2]`,
- out: `[1,2,1,2,1,2]`,
- }, {
- value: `{}`,
- out: `{}`,
- }, {
- value: `{a: 2, b: 3, c: ["A", "B"]}`,
- out: `{a:2,b:3,c:["A","B"]}`,
- }}
- for i, tc := range testCases {
- t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
- inst := getInstance(t, tc.value)
- buf := []byte{}
- stripComma := func() {
- if n := len(buf) - 1; buf[n] == ',' {
- buf = buf[:n]
- }
- }
- inst.Value().Walk(func(v Value) bool {
- v = v.Eval()
- if !v.v.Label.IsInt() {
- if k, ok := v.Label(); ok {
- buf = append(buf, k+":"...)
- }
- }
- switch v.Kind() {
- case StructKind:
- buf = append(buf, '{')
- case ListKind:
- buf = append(buf, '[')
- default:
- if b, _ := v.v.Value.(*adt.Bottom); b != nil {
- s := debugStr(v.ctx(), b)
- buf = append(buf, fmt.Sprint(s, ",")...)
- return true
- }
- buf = append(buf, fmt.Sprint(v, ",")...)
- }
- return true
- }, func(v Value) {
- switch v.Kind() {
- case StructKind:
- stripComma()
- buf = append(buf, "},"...)
- case ListKind:
- stripComma()
- buf = append(buf, "],"...)
- }
- })
- stripComma()
- if got := string(buf); got != tc.out {
- t.Errorf("\n got %v;\nwant %v", got, tc.out)
- }
- })
- }
-}
-
-func TestTrimZeros(t *testing.T) {
- testCases := []struct {
- in string
- out string
- }{
- {"", ""},
- {"2", "2"},
- {"2.0", "2.0"},
- {"2.000000000000", "2.0"},
- {"2000000000000", "2e+12"},
- {"2000000", "2e+6"},
- }
- for _, tc := range testCases {
- t.Run(tc.in, func(t *testing.T) {
- if got := trimZeros(tc.in); got != tc.out {
- t.Errorf("got %q; want %q", got, tc.out)
- }
- })
- }
-}
-
-func TestReference(t *testing.T) {
- testCases := []struct {
- input string
- want string
- alt string
- }{{
- input: "v: w: x: _|_",
- want: "",
- }, {
- input: "v: w: x: 2",
- want: "",
- }, {
- input: "v: w: x: a, a: 1",
- want: "a",
- }, {
- input: "v: w: x: a.b.c, a: b: c: 1",
- want: "a b c",
- }, {
- input: "v: w: x: w.a.b.c, v: w: a: b: c: 1",
- want: "v w a b c",
- }, {
- input: `v: w: x: w.a.b.c, v: w: a: b: c: 1, #D: 3, opt?: 3, "v\(#D)": 3, X: {a: 3}, X`,
- want: "v w a b c",
- }, {
- input: `v: w: x: w.a[bb]["c"], v: w: a: b: c: 1, bb: "b"`,
- want: "v w a b c",
- }, {
- input: `v: {
- for t in src {
- w: "t\(t)": 1
- w: "\(t)": w["t\(t)"]
- }
- },
- src: ["x", "y"]`,
- want: "v w tx",
- }, {
- input: `
- v: w: x: a
- a: 1
- for i in [] {
- }
- `,
- want: "a",
- }, {
- input: `
- v: w: close({x: a})
- a: 1
- `,
- want: "a",
- }, {
- input: `
- import "math"
-
- v: w: x: math.Pi
- `,
- want: "Pi",
- alt: "3.14159265358979323846264338327950288419716939937510582097494459",
- }}
- for _, tc := range testCases {
- t.Run("", func(t *testing.T) {
- var r Runtime
- inst, _ := r.Compile("in", tc.input) // getInstance(t, tc.input)
- v := inst.Lookup("v", "w", "x")
- inst, a := v.Reference()
- if got := strings.Join(a, " "); got != tc.want {
- t.Errorf("\n got %v;\nwant %v", got, tc.want)
- }
-
- if tc.want != "" {
- want := "1"
- if tc.alt != "" {
- want = tc.alt
- }
- v := fmt.Sprint(inst.Lookup(a...))
- if v != want {
- t.Errorf("path resolved to %s; want %s", v, want)
- }
- }
- })
- }
-}
-
-// TODO: stack overflow
-func TestPathCorrection(t *testing.T) {
- testCases := []struct {
- input string
- lookup func(i *Instance) Value
- want string
- skip bool
- }{{
- // // TODO: structural cycle.
- // input: `
- // a: b: {
- // c: d: b
- // }
- // `,
- // lookup: func(i *Instance) Value {
- // _, a := i.Lookup("a", "b", "c", "d").Expr()
- // return a[0].Lookup("b", "c", "d")
- // },
- // want: "a.b",
- // }, {
-
- // TODO: embedding: have field operators.
- // input: `
- // a: {
- // c: 3
- // {x: c}
- // }
- // `,
- // lookup: func(i *Instance) Value {
- // _, a := i.Lookup("a").Expr()
- // return a[1].Lookup("x")
- // },
- // want: "a.c",
- // }, {
-
- // TODO: implement proper Elem()
- input: `
- a: b: [...T]
- a: b: [...T]
- T: int
- `,
- lookup: func(i *Instance) Value {
- v, _ := i.Lookup("a", "b").Elem()
- _, a := v.Expr()
- return a[0]
- },
- want: "T",
- }, {
- input: `
- #S: {
- b?: [...#T]
- b?: [...#T]
- }
- #T: int
- `,
- lookup: func(i *Instance) Value {
- v := i.LookupDef("#S")
- f, _ := v.LookupField("b")
- v, _ = f.Value.Elem()
- _, a := v.Expr()
- return a[0]
- },
- want: "#T",
- }, {
- input: `
- #S: {
- a?: [...#T]
- b?: [...#T]
- }
- #T: int
- `,
- lookup: func(i *Instance) Value {
- v := i.LookupDef("#S")
- f, _ := v.LookupField("a")
- x := f.Value
- f, _ = v.LookupField("b")
- y := f.Value
- u := x.Unify(y)
- v, _ = u.Elem()
- _, a := v.Expr()
- return a[0]
- },
- want: "#T",
- // }, {
- // input: `
- // #a: {
- // #T: {b: 3}
- // close({}) | close({c: #T}) | close({d: string})
- // }
- // `,
- // lookup: func(i *Instance) Value {
- // f, _ := i.LookupField("#a")
- // _, a := f.Value.Expr() // &
- // _, a = a[1].Expr() // |
- // return a[1].Lookup("c")
- // },
- // want: "#a.#T",
- }, {
- // TODO: iterate over Definitions
- // input: `
- // package foo
-
- // #Struct: {
- // #T: int
-
- // {b?: #T}
- // }`,
- // want: "#Struct.#T",
- // lookup: func(inst *Instance) Value {
- // // Locate Struct
- // i, _ := inst.Value().Fields(Definitions(true))
- // if !i.Next() {
- // t.Fatal("no fields")
- // }
- // // Locate b
- // i, _ = i.Value().Fields(Definitions(true), Optional(true))
- // if !(i.Next() && i.Next()) {
- // t.Fatal("no fields")
- // }
- // v := i.Value()
- // return v
- // },
- // }, {
-
- input: `
- package foo
-
- #A: #B: #T
-
- #T: {
- a: #S.#U
- #S: #U: {}
- }
- `,
- want: "#T.#S.#U",
- lookup: func(inst *Instance) Value {
- f, _ := inst.Value().LookupField("#A")
- f, _ = f.Value.LookupField("#B")
- v := f.Value
- v = Dereference(v)
- v = v.Lookup("a")
- return v
- },
- // }, {
-
- // TODO: record additionalItems in list
- // input: `
- // package foo
-
- // #A: #B: #T
-
- // #T: {
- // a: [...#S]
- // #S: {}
- // }
- // `,
- // want: "#T.#S",
- // lookup: func(inst *Instance) Value {
- // f, _ := inst.Value().LookupField("#A")
- // f, _ = f.Value.LookupField("#B")
- // v := f.Value
- // v = Dereference(v)
- // v, _ = v.Lookup("a").Elem()
- // return v
- // },
- // }, {
-
- // YAY: works.
- // input: `
- // #A: {
- // b: #T
- // }
-
- // #T: {
- // a: #S
- // #S: {}
- // }
- // `,
- // want: "#T.#S",
- // lookup: func(inst *Instance) Value {
- // f, _ := inst.Value().LookupField("#A")
- // v := f.Value.Lookup("b")
- // v = Dereference(v)
- // v = v.Lookup("a")
- // return v
- // },
- // }, {
-
- // // TODO(eval): embedded structs are currently represented at the same
- // // level as the enclosing struct. This means that the parent of an
- // // embedded struct skips the struct in which it is embedded. Treat
- // // embedded structs as "anonymous" fields.
- // // This could perhaps be made fixed with dereferencing as well.
- // skip: true,
- // input: `
- // #Tracing: {
- // #T: { address?: string }
- // #S: { ip?: string }
-
- // close({}) | close({
- // t: #T
- // }) | close({
- // s: #S
- // })
- // }
- // #X: {}
- // #X // Disconnect top-level struct from the one visible by close.
- // `,
- // want: "",
- // lookup: func(inst *Instance) Value {
- // f, _ := inst.Value().LookupField("#Tracing")
- // v := f.Value.Eval()
- // _, args := v.Expr()
- // v = args[1].Lookup("t")
- // v = Dereference(v)
- // return v
- // },
- }}
- for _, tc := range testCases {
- if tc.skip {
- continue
- }
- t.Run("", func(t *testing.T) {
- var r Runtime
- inst, err := r.Compile("in", tc.input)
- if err != nil {
- t.Fatal(err)
- }
- v := tc.lookup(inst)
- gotInst, ref := v.Reference()
- if gotInst != inst {
- t.Error("reference not in original instance")
- }
- gotPath := strings.Join(ref, ".")
- if gotPath != tc.want {
- t.Errorf("got path %s; want %s", gotPath, tc.want)
- }
- })
- }
-}
-
-// func TestReferences(t *testing.T) {
-// config1 := `
-// a: {
-// b: 3
-// }
-// c: {
-// d: a.b
-// e: c.d
-// f: a
-// }
-// `
-// config2 := `
-// a: { c: 3 }
-// b: { c: int, d: 4 }
-// r: (a & b).c
-// c: {args: s1 + s2}.args
-// s1: string
-// s2: string
-// d: ({arg: b}).arg.c
-// e: f.arg.c
-// f: {arg: b}
-// `
-// testCases := []struct {
-// config string
-// in string
-// out string
-// }{
-// {config1, "c.d", "a.b"},
-// {config1, "c.e", "c.d"},
-// {config1, "c.f", "a"},
-
-// {config2, "r", "a.c b.c"},
-// {config2, "c", "s1 s2"},
-// // {config2, "d", "b.c"}, // TODO: make this work as well.
-// {config2, "e", "f.arg.c"}, // TODO: should also report b.c.
-// }
-// for _, tc := range testCases {
-// t.Run(tc.in, func(t *testing.T) {
-// ctx, st := compileFile(t, tc.config)
-// v := newValueRoot(ctx, st)
-// for _, k := range strings.Split(tc.in, ".") {
-// obj, err := v.structValFull(ctx)
-// if err != nil {
-// t.Fatal(err)
-// }
-// v = obj.Lookup(k)
-// }
-// got := []string{}
-// for _, r := range v.References() {
-// got = append(got, strings.Join(r, "."))
-// }
-// want := strings.Split(tc.out, " ")
-// if !reflect.DeepEqual(got, want) {
-// t.Errorf("got %v; want %v", got, want)
-// }
-// })
-// }
-// }
-
-func checkErr(t *testing.T, err error, str, name string) bool {
- t.Helper()
- if err == nil {
- if str != "" {
- t.Errorf(`err:%s: got ""; want %q`, name, str)
- }
- return true
- }
- return checkFailed(t, err, str, name)
-}
-
-func checkFatal(t *testing.T, err error, str, name string) {
- t.Helper()
- if !checkFailed(t, err, str, name) {
- t.SkipNow()
- }
-}
-
-func checkFailed(t *testing.T, err error, str, name string) bool {
- t.Helper()
- if err != nil {
- got := err.Error()
- if str == "" {
- t.Fatalf(`err:%s: got %q; want ""`, name, got)
- }
- if !strings.Contains(got, str) {
- t.Errorf(`err:%s: got %q; want %q`, name, got, str)
- }
- return false
- }
- return true
-}
-
-func TestExpr(t *testing.T) {
- testCases := []struct {
- input string
- want string
- }{{
- input: "v: 3",
- want: "3",
- }, {
- input: "v: 3 + 4",
- want: "+(3 4)",
- }, {
- input: "v: !a, a: bool",
- want: `!(.(〈〉 "a"))`,
- }, {
- input: "v: !a, a: 3", // TODO: Should still look up.
- want: `!(.(〈〉 "a"))`,
- }, {
- input: "v: 1 | 2 | 3 | *4",
- want: "|(1 2 3 4)",
- }, {
- input: "v: 2 & 5", // Allow even with error.
- want: "&(2 5)",
- }, {
- input: "v: 2 | 5",
- want: "|(2 5)",
- }, {
- input: "v: 2 && 5",
- want: "&&(2 5)",
- }, {
- input: "v: 2 || 5",
- want: "||(2 5)",
- }, {
- input: "v: 2 == 5",
- want: "==(2 5)",
- }, {
- input: "v: !b, b: true",
- want: `!(.(〈〉 "b"))`,
- }, {
- input: "v: 2 != 5",
- want: "!=(2 5)",
- }, {
- input: "v: <5",
- want: "<(5)",
- }, {
- input: "v: 2 <= 5",
- want: "<=(2 5)",
- }, {
- input: "v: 2 > 5",
- want: ">(2 5)",
- }, {
- input: "v: 2 >= 5",
- want: ">=(2 5)",
- }, {
- input: "v: 2 =~ 5",
- want: "=~(2 5)",
- }, {
- input: "v: 2 !~ 5",
- want: "!~(2 5)",
- }, {
- input: "v: 2 + 5",
- want: "+(2 5)",
- }, {
- input: "v: 2 - 5",
- want: "-(2 5)",
- }, {
- input: "v: 2 * 5",
- want: "*(2 5)",
- }, {
- input: "v: 2 / 5",
- want: "/(2 5)",
- }, {
- input: "v: 2 quo 5",
- want: "quo(2 5)",
- }, {
- input: "v: 2 rem 5",
- want: "rem(2 5)",
- }, {
- input: "v: 2 div 5",
- want: "div(2 5)",
- }, {
- input: "v: 2 mod 5",
- want: "mod(2 5)",
- }, {
- input: "v: a.b, a: b: 4",
- want: `.(.(〈〉 "a") "b")`,
- }, {
- input: `v: a["b"], a: b: 3 `,
- want: `[](.(〈〉 "a") "b")`,
- }, {
- input: "v: a[2:5], a: [1, 2, 3, 4, 5]",
- want: `[:](.(〈〉 "a") 2 5)`,
- }, {
- input: "v: len([])",
- want: "()(len [])",
- }, {
- input: "v: a.b, a: { b: string }",
- want: `.(.(〈〉 "a") "b")`,
- }, {
- input: `v: "Hello, \(x)! Welcome to \(place)", place: string, x: string`,
- want: `\()("Hello, " .(〈〉 "x") "! Welcome to " .(〈〉 "place") "")`,
- }, {
- input: `v: { a, b: 1 }, a: 2`,
- want: `&(.(〈〉 "a") {b:1})`,
- }, {
- input: `v: { {c: a}, b: a }, a: int`,
- want: `&({c:a} {b:a})`,
- }, {
- input: `v: [...number] | *[1, 2, 3]`,
- want: `([...number]|*[1,2,3])`,
- }}
- for _, tc := range testCases {
- t.Run(tc.input, func(t *testing.T) {
- v := getInstance(t, tc.input).Lookup("v")
- got := exprStr(v)
- if got != tc.want {
- t.Errorf("\n got %v;\nwant %v", got, tc.want)
- }
- })
- }
-}
-
-func exprStr(v Value) string {
- op, operands := v.Expr()
- if op == NoOp {
- return compactRawStr(v)
- }
- s := op.String()
- s += "("
- for i, v := range operands {
- if i > 0 {
- s += " "
- }
- s += exprStr(v)
- }
- s += ")"
- return s
-}
-
-func compactRawStr(v Value) string {
- ctx := v.ctx()
- cfg := &debug.Config{Compact: true, Raw: true}
- return debug.NodeString(ctx.opCtx, v.v, cfg)
-}
-
-func compactValueStr(v Value) string {
- ctx := v.ctx()
- cfg := &debug.Config{Compact: true}
- return debug.NodeString(ctx.opCtx, v.v, cfg)
-}
diff --git a/internal/task/task.go b/internal/task/task.go
index 5322a47..a6a54d2 100644
--- a/internal/task/task.go
+++ b/internal/task/task.go
@@ -20,8 +20,8 @@
"io"
"sync"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/internal/legacy/cue"
)
// A Context provides context for running a task.
diff --git a/internal/walk/walk.go b/internal/walk/walk.go
index b74f56e..8e3c9f9 100644
--- a/internal/walk/walk.go
+++ b/internal/walk/walk.go
@@ -21,7 +21,7 @@
// satisfactory API has been established, it can be made public.
package walk
-import "cuelang.org/go/internal/legacy/cue"
+import "cuelang.org/go/cue"
// TODO:
// - allow overriding options for descendants.
diff --git a/pkg/encoding/base64/manual.go b/pkg/encoding/base64/manual.go
index 178f105..b292424 100644
--- a/pkg/encoding/base64/manual.go
+++ b/pkg/encoding/base64/manual.go
@@ -19,7 +19,7 @@
"encoding/base64"
"fmt"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// EncodedLen returns the length in bytes of the base64 encoding
diff --git a/pkg/encoding/csv/manual.go b/pkg/encoding/csv/manual.go
index f8b31d0..99e2cf1 100644
--- a/pkg/encoding/csv/manual.go
+++ b/pkg/encoding/csv/manual.go
@@ -19,7 +19,7 @@
"encoding/csv"
"io"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// Encode encode the given list of lists to CSV.
diff --git a/pkg/encoding/json/manual.go b/pkg/encoding/json/manual.go
index 591724b..96a9721 100644
--- a/pkg/encoding/json/manual.go
+++ b/pkg/encoding/json/manual.go
@@ -19,10 +19,10 @@
"encoding/json"
"fmt"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
)
// Compact generates the JSON-encoded src with insignificant space characters
diff --git a/pkg/encoding/yaml/manual.go b/pkg/encoding/yaml/manual.go
index 16d6d38..60b7318 100644
--- a/pkg/encoding/yaml/manual.go
+++ b/pkg/encoding/yaml/manual.go
@@ -18,10 +18,10 @@
"bytes"
"io"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/internal"
cueyaml "cuelang.org/go/internal/encoding/yaml"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/third_party/yaml"
)
diff --git a/pkg/list/list.go b/pkg/list/list.go
index db6c2d3..be8493b 100644
--- a/pkg/list/list.go
+++ b/pkg/list/list.go
@@ -19,7 +19,7 @@
"fmt"
"sort"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// Drop reports the suffix of list x after the first n elements,
diff --git a/pkg/list/sort.go b/pkg/list/sort.go
index f39a407..ac33f4f 100644
--- a/pkg/list/sort.go
+++ b/pkg/list/sort.go
@@ -21,7 +21,7 @@
import (
"sort"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// valueSorter defines a sort.Interface; implemented in cue/builtinutil.go.
diff --git a/pkg/net/host.go b/pkg/net/host.go
index 9a357c7..6018da4 100644
--- a/pkg/net/host.go
+++ b/pkg/net/host.go
@@ -22,7 +22,7 @@
"golang.org/x/net/idna"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
var idnaProfile = idna.New(
diff --git a/pkg/net/ip.go b/pkg/net/ip.go
index 5f9aeef..6b658e3 100644
--- a/pkg/net/ip.go
+++ b/pkg/net/ip.go
@@ -19,7 +19,7 @@
"fmt"
"net"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// IP address lengths (bytes).
diff --git a/pkg/struct/struct.go b/pkg/struct/struct.go
index 5b51254..00094e1 100644
--- a/pkg/struct/struct.go
+++ b/pkg/struct/struct.go
@@ -16,7 +16,7 @@
package structs
import (
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// MinFields validates the minimum number of fields that are part of a struct.
diff --git a/pkg/text/tabwriter/manual.go b/pkg/text/tabwriter/manual.go
index 5512565..42f90aa 100644
--- a/pkg/text/tabwriter/manual.go
+++ b/pkg/text/tabwriter/manual.go
@@ -19,7 +19,7 @@
"fmt"
"text/tabwriter"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// Write formats text in columns. See golang.org/pkg/text/tabwriter for more
diff --git a/pkg/text/template/manual.go b/pkg/text/template/manual.go
index fd8116c..646f5e8 100644
--- a/pkg/text/template/manual.go
+++ b/pkg/text/template/manual.go
@@ -18,7 +18,7 @@
"bytes"
"text/template"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
// Execute executes a Go-style template.
diff --git a/pkg/tool/cli/cli.go b/pkg/tool/cli/cli.go
index 01d9e28..8ba4878 100644
--- a/pkg/tool/cli/cli.go
+++ b/pkg/tool/cli/cli.go
@@ -20,7 +20,7 @@
import (
"fmt"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/exec/exec.go b/pkg/tool/exec/exec.go
index c29e754..62c2cec 100644
--- a/pkg/tool/exec/exec.go
+++ b/pkg/tool/exec/exec.go
@@ -24,8 +24,8 @@
"golang.org/x/xerrors"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/errors"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/exec/exec_test.go b/pkg/tool/exec/exec_test.go
index eaf21c6..69e9484 100644
--- a/pkg/tool/exec/exec_test.go
+++ b/pkg/tool/exec/exec_test.go
@@ -20,7 +20,7 @@
"github.com/google/go-cmp/cmp"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/file/file.go b/pkg/tool/file/file.go
index bfdb8f0..7c206ca 100644
--- a/pkg/tool/file/file.go
+++ b/pkg/tool/file/file.go
@@ -22,7 +22,7 @@
"os"
"path/filepath"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/file/file_test.go b/pkg/tool/file/file_test.go
index aa04bdd..c9bc38f 100644
--- a/pkg/tool/file/file_test.go
+++ b/pkg/tool/file/file_test.go
@@ -22,9 +22,9 @@
"reflect"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/http/http.go b/pkg/tool/http/http.go
index b7c5f23..4c6f9e6 100644
--- a/pkg/tool/http/http.go
+++ b/pkg/tool/http/http.go
@@ -22,7 +22,7 @@
"io/ioutil"
"net/http"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/os/env.go b/pkg/tool/os/env.go
index 137a08c..0d0a535 100644
--- a/pkg/tool/os/env.go
+++ b/pkg/tool/os/env.go
@@ -21,10 +21,10 @@
"os"
"strings"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/internal/cli"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/task"
)
diff --git a/pkg/tool/os/env_test.go b/pkg/tool/os/env_test.go
index 1a1d636..8a6d9fb 100644
--- a/pkg/tool/os/env_test.go
+++ b/pkg/tool/os/env_test.go
@@ -21,12 +21,12 @@
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/parser"
"cuelang.org/go/cue/token"
"cuelang.org/go/internal"
- "cuelang.org/go/internal/legacy/cue"
"cuelang.org/go/internal/task"
)
diff --git a/tools/trim/trim.go b/tools/trim/trim.go
index 4ac7cc9..4af47a4 100644
--- a/tools/trim/trim.go
+++ b/tools/trim/trim.go
@@ -67,7 +67,7 @@
"cuelang.org/go/internal"
// "cuelang.org/go/cue"
- "cuelang.org/go/internal/legacy/cue"
+ "cuelang.org/go/cue"
)
type Runtime = cue.Runtime
diff --git a/tools/trim/trim_test.go b/tools/trim/trim_test.go
index e3ede7f..28010bc 100644
--- a/tools/trim/trim_test.go
+++ b/tools/trim/trim_test.go
@@ -18,12 +18,12 @@
"flag"
"testing"
+ "cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/errors"
"cuelang.org/go/cue/format"
"cuelang.org/go/cue/parser"
"cuelang.org/go/internal/cuetxtar"
- "cuelang.org/go/internal/legacy/cue"
"github.com/rogpeppe/go-internal/txtar"
)