blob: ccdc4296d558cd78e59b4d448f0087cea5c6dd0a [file] [log] [blame]
// Copyright 2018 The CUE Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package 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
}