blob: 1db9eff0ed08e349e18ab77ce29e516349ab0998 [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
import (
"bytes"
"fmt"
"strconv"
"strings"
)
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))
}
return p.ctx.labelStr(f)
}
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].feature
}
}
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.feature, 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.x)
write(" (")
for i, a := range x.args {
p.str(a)
if i < len(x.args)-1 {
write(",")
}
}
write(")")
case *customValidator:
p.str(x.call)
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:
write("(")
p.str(x.left)
writef(" %v ", x.op)
p.str(x.right)
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.marked {
writef("*")
}
p.str(v.val)
}
write(")")
case *lambdaExpr:
if p.showNodeRef {
writef("<%s>", p.ctx.ref(x))
}
write("(")
p.str(x.params.arcs)
write(")->")
p.str(x.value)
case *structLit:
if x == nil {
write("*nil node*")
break
}
if p.showNodeRef {
p.writef("<%s>", p.ctx.ref(x))
}
if x.isClosed {
write("C")
}
write("{")
topDefault := false
switch {
case x.template != nil:
lambda, ok := x.template.(*lambdaExpr)
if ok {
if _, topDefault = lambda.value.(*top); topDefault {
break
}
}
write("<>: ")
p.str(x.template)
write(", ")
}
p.str(x.arcs)
for i, c := range x.comprehensions {
p.str(c)
if i < len(x.comprehensions)-1 {
p.write(", ")
}
}
if topDefault && !x.isClosed {
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
orig := p.label(x.feature)
str := strconv.Quote(orig)
if len(orig)+2 == len(str) {
str = str[1 : len(str)-1]
}
p.writef(str)
if x.optional {
p.write("?")
}
if x.definition {
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.clauses)
case *listComprehension:
writef("[")
p.str(x.clauses)
write(" ]")
case *yield:
writef(" yield ")
writef("(")
p.str(x.key)
if x.opt {
writef("?")
}
writef("): ")
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.feature))
writef(", ")
a = x.fn.params.arcs[1]
p.writef(p.label(a.feature))
writef(" in ")
p.str(x.source)
p.str(x.fn.value)
case *guard:
writef(" if ")
p.str(x.condition)
p.str(x.value)
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:
if x.k&intKind != 0 {
write(x.v.Text('f')) // also render info
} else {
write(x.v.Text('g')) // also render info
}
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.value)
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.v.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("(")
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))
}
}