github.com/solo-io/cue@v0.4.7/internal/core/debug/debug.go (about)

     1  // Copyright 2020 CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package debug prints a given ADT node.
    16  //
    17  // Note that the result is not valid CUE, but instead prints the internals
    18  // of an ADT node in human-readable form. It uses a simple indentation algorithm
    19  // for improved readability and diffing.
    20  //
    21  package debug
    22  
    23  import (
    24  	"fmt"
    25  	"io"
    26  	"strconv"
    27  	"strings"
    28  
    29  	"github.com/solo-io/cue/cue/errors"
    30  	"github.com/solo-io/cue/cue/literal"
    31  	"github.com/solo-io/cue/internal"
    32  	"github.com/solo-io/cue/internal/core/adt"
    33  )
    34  
    35  const (
    36  	openTuple  = "\u3008"
    37  	closeTuple = "\u3009"
    38  )
    39  
    40  type Config struct {
    41  	Cwd     string
    42  	Compact bool
    43  	Raw     bool
    44  }
    45  
    46  func WriteNode(w io.Writer, i adt.StringIndexer, n adt.Node, config *Config) {
    47  	if config == nil {
    48  		config = &Config{}
    49  	}
    50  	p := printer{Writer: w, index: i, cfg: config}
    51  	if config.Compact {
    52  		p := compactPrinter{p}
    53  		p.node(n)
    54  	} else {
    55  		p.node(n)
    56  	}
    57  }
    58  
    59  func NodeString(i adt.StringIndexer, n adt.Node, config *Config) string {
    60  	b := &strings.Builder{}
    61  	WriteNode(b, i, n, config)
    62  	return b.String()
    63  }
    64  
    65  type printer struct {
    66  	io.Writer
    67  	index  adt.StringIndexer
    68  	indent string
    69  	cfg    *Config
    70  
    71  	// modes:
    72  	// - show vertex
    73  	// - show original conjuncts
    74  	// - show unevaluated
    75  	// - auto
    76  }
    77  
    78  func (w *printer) string(s string) {
    79  	s = strings.Replace(s, "\n", "\n"+w.indent, -1)
    80  	_, _ = io.WriteString(w, s)
    81  }
    82  
    83  func (w *printer) label(f adt.Feature) {
    84  	w.string(w.labelString(f))
    85  }
    86  
    87  func (w *printer) ident(f adt.Feature) {
    88  	w.string(f.IdentString(w.index))
    89  }
    90  
    91  // TODO: fold into label once :: is no longer supported.
    92  func (w *printer) labelString(f adt.Feature) string {
    93  	if f.IsHidden() {
    94  		ident := f.IdentString(w.index)
    95  		if pkgName := f.PkgID(w.index); pkgName != "_" {
    96  			ident = fmt.Sprintf("%s(%s)", ident, pkgName)
    97  		}
    98  		return ident
    99  	}
   100  	return f.SelectorString(w.index)
   101  }
   102  
   103  func (w *printer) shortError(errs errors.Error) {
   104  	for {
   105  		msg, args := errs.Msg()
   106  		fmt.Fprintf(w, msg, args...)
   107  
   108  		err := errors.Unwrap(errs)
   109  		if err == nil {
   110  			break
   111  		}
   112  
   113  		if errs, _ = err.(errors.Error); errs != nil {
   114  			w.string(err.Error())
   115  			break
   116  		}
   117  	}
   118  }
   119  
   120  func (w *printer) interpolation(x *adt.Interpolation) {
   121  	quote := `"`
   122  	if x.K == adt.BytesKind {
   123  		quote = `'`
   124  	}
   125  	w.string(quote)
   126  	for i := 0; i < len(x.Parts); i += 2 {
   127  		switch x.K {
   128  		case adt.StringKind:
   129  			if s, ok := x.Parts[i].(*adt.String); ok {
   130  				w.string(s.Str)
   131  			} else {
   132  				w.string("<bad string>")
   133  			}
   134  		case adt.BytesKind:
   135  			if s, ok := x.Parts[i].(*adt.Bytes); ok {
   136  				_, _ = w.Write(s.B)
   137  			} else {
   138  				w.string("<bad bytes>")
   139  			}
   140  		}
   141  		if i+1 < len(x.Parts) {
   142  			w.string(`\(`)
   143  			w.node(x.Parts[i+1])
   144  			w.string(`)`)
   145  		}
   146  	}
   147  	w.string(quote)
   148  }
   149  
   150  func (w *printer) node(n adt.Node) {
   151  	switch x := n.(type) {
   152  	case *adt.Vertex:
   153  		var kind adt.Kind
   154  		if x.BaseValue != nil {
   155  			kind = x.BaseValue.Kind()
   156  		}
   157  
   158  		kindStr := kind.String()
   159  
   160  		// TODO: replace with showing full closedness data.
   161  		if x.IsClosedList() || x.IsClosedStruct() {
   162  			if kind == adt.ListKind || kind == adt.StructKind {
   163  				kindStr = "#" + kindStr
   164  			}
   165  		}
   166  
   167  		fmt.Fprintf(w, "(%s){", kindStr)
   168  
   169  		saved := w.indent
   170  		w.indent += "  "
   171  		defer func() { w.indent = saved }()
   172  
   173  		switch v := x.BaseValue.(type) {
   174  		case nil:
   175  		case *adt.Bottom:
   176  			// TODO: reuse bottom.
   177  			saved := w.indent
   178  			w.indent += "// "
   179  			w.string("\n")
   180  			fmt.Fprintf(w, "[%v]", v.Code)
   181  			if !v.ChildError {
   182  				msg := errors.Details(v.Err, &errors.Config{
   183  					Cwd:     w.cfg.Cwd,
   184  					ToSlash: true,
   185  				})
   186  				msg = strings.TrimSpace(msg)
   187  				if msg != "" {
   188  					w.string(" ")
   189  					w.string(msg)
   190  				}
   191  			}
   192  			w.indent = saved
   193  
   194  		case *adt.StructMarker, *adt.ListMarker:
   195  			// if len(x.Arcs) == 0 {
   196  			// 	// w.string("}")
   197  			// 	// return
   198  			// }
   199  
   200  		case adt.Value:
   201  			if len(x.Arcs) == 0 {
   202  				w.string(" ")
   203  				w.node(v)
   204  				w.string(" }")
   205  				return
   206  			}
   207  			w.string("\n")
   208  			w.node(v)
   209  		}
   210  
   211  		for _, a := range x.Arcs {
   212  			w.string("\n")
   213  			w.label(a.Label)
   214  			w.string(": ")
   215  			w.node(a)
   216  		}
   217  
   218  		if x.BaseValue == nil {
   219  			w.indent += "// "
   220  			w.string("// ")
   221  			for i, c := range x.Conjuncts {
   222  				if i > 0 {
   223  					w.string(" & ")
   224  				}
   225  				w.node(c.Expr()) // TODO: also include env?
   226  			}
   227  		}
   228  
   229  		w.indent = saved
   230  		w.string("\n")
   231  		w.string("}")
   232  
   233  	case *adt.StructMarker:
   234  		w.string("struct")
   235  
   236  	case *adt.ListMarker:
   237  		w.string("list")
   238  
   239  	case *adt.StructLit:
   240  		if len(x.Decls) == 0 {
   241  			w.string("{}")
   242  			break
   243  		}
   244  		w.string("{")
   245  		w.indent += "  "
   246  		for _, d := range x.Decls {
   247  			w.string("\n")
   248  			w.node(d)
   249  		}
   250  		w.indent = w.indent[:len(w.indent)-2]
   251  		w.string("\n}")
   252  
   253  	case *adt.ListLit:
   254  		if len(x.Elems) == 0 {
   255  			w.string("[]")
   256  			break
   257  		}
   258  		w.string("[")
   259  		w.indent += "  "
   260  		for _, d := range x.Elems {
   261  			w.string("\n")
   262  			w.node(d)
   263  			w.string(",")
   264  		}
   265  		w.indent = w.indent[:len(w.indent)-2]
   266  		w.string("\n]")
   267  
   268  	case *adt.Field:
   269  		s := w.labelString(x.Label)
   270  		w.string(s)
   271  		w.string(":")
   272  		if x.Label.IsDef() && !internal.IsDef(s) {
   273  			w.string(":")
   274  		}
   275  		w.string(" ")
   276  		w.node(x.Value)
   277  
   278  	case *adt.OptionalField:
   279  		s := w.labelString(x.Label)
   280  		w.string(s)
   281  		w.string("?:")
   282  		if x.Label.IsDef() && !internal.IsDef(s) {
   283  			w.string(":")
   284  		}
   285  		w.string(" ")
   286  		w.node(x.Value)
   287  
   288  	case *adt.BulkOptionalField:
   289  		w.string("[")
   290  		w.node(x.Filter)
   291  		w.string("]: ")
   292  		w.node(x.Value)
   293  
   294  	case *adt.DynamicField:
   295  		w.node(x.Key)
   296  		if x.IsOptional() {
   297  			w.string("?")
   298  		}
   299  		w.string(": ")
   300  		w.node(x.Value)
   301  
   302  	case *adt.Ellipsis:
   303  		w.string("...")
   304  		if x.Value != nil {
   305  			w.node(x.Value)
   306  		}
   307  
   308  	case *adt.Bottom:
   309  		w.string(`_|_`)
   310  		if x.Err != nil {
   311  			w.string("(")
   312  			w.shortError(x.Err)
   313  			w.string(")")
   314  		}
   315  
   316  	case *adt.Null:
   317  		w.string("null")
   318  
   319  	case *adt.Bool:
   320  		fmt.Fprint(w, x.B)
   321  
   322  	case *adt.Num:
   323  		fmt.Fprint(w, &x.X)
   324  
   325  	case *adt.String:
   326  		w.string(literal.String.Quote(x.Str))
   327  
   328  	case *adt.Bytes:
   329  		w.string(literal.Bytes.Quote(string(x.B)))
   330  
   331  	case *adt.Top:
   332  		w.string("_")
   333  
   334  	case *adt.BasicType:
   335  		fmt.Fprint(w, x.K)
   336  
   337  	case *adt.BoundExpr:
   338  		fmt.Fprint(w, x.Op)
   339  		w.node(x.Expr)
   340  
   341  	case *adt.BoundValue:
   342  		fmt.Fprint(w, x.Op)
   343  		w.node(x.Value)
   344  
   345  	case *adt.NodeLink:
   346  		w.string(openTuple)
   347  		for i, f := range x.Node.Path() {
   348  			if i > 0 {
   349  				w.string(".")
   350  			}
   351  			w.label(f)
   352  		}
   353  		w.string(closeTuple)
   354  
   355  	case *adt.FieldReference:
   356  		w.string(openTuple)
   357  		w.string(strconv.Itoa(int(x.UpCount)))
   358  		w.string(";")
   359  		w.label(x.Label)
   360  		w.string(closeTuple)
   361  
   362  	case *adt.ValueReference:
   363  		w.string(openTuple)
   364  		w.string(strconv.Itoa(int(x.UpCount)))
   365  		w.string(closeTuple)
   366  
   367  	case *adt.LabelReference:
   368  		w.string(openTuple)
   369  		w.string(strconv.Itoa(int(x.UpCount)))
   370  		w.string(";-")
   371  		w.string(closeTuple)
   372  
   373  	case *adt.DynamicReference:
   374  		w.string(openTuple)
   375  		w.string(strconv.Itoa(int(x.UpCount)))
   376  		w.string(";(")
   377  		w.node(x.Label)
   378  		w.string(")")
   379  		w.string(closeTuple)
   380  
   381  	case *adt.ImportReference:
   382  		w.string(openTuple + "import;")
   383  		w.label(x.ImportPath)
   384  		w.string(closeTuple)
   385  
   386  	case *adt.LetReference:
   387  		w.string(openTuple)
   388  		w.string(strconv.Itoa(int(x.UpCount)))
   389  		w.string(";let ")
   390  		w.ident(x.Label)
   391  		w.string(closeTuple)
   392  
   393  	case *adt.SelectorExpr:
   394  		w.node(x.X)
   395  		w.string(".")
   396  		w.label(x.Sel)
   397  
   398  	case *adt.IndexExpr:
   399  		w.node(x.X)
   400  		w.string("[")
   401  		w.node(x.Index)
   402  		w.string("]")
   403  
   404  	case *adt.SliceExpr:
   405  		w.node(x.X)
   406  		w.string("[")
   407  		if x.Lo != nil {
   408  			w.node(x.Lo)
   409  		}
   410  		w.string(":")
   411  		if x.Hi != nil {
   412  			w.node(x.Hi)
   413  		}
   414  		if x.Stride != nil {
   415  			w.string(":")
   416  			w.node(x.Stride)
   417  		}
   418  		w.string("]")
   419  
   420  	case *adt.Interpolation:
   421  		w.interpolation(x)
   422  
   423  	case *adt.UnaryExpr:
   424  		fmt.Fprint(w, x.Op)
   425  		w.node(x.X)
   426  
   427  	case *adt.BinaryExpr:
   428  		w.string("(")
   429  		w.node(x.X)
   430  		fmt.Fprint(w, " ", x.Op, " ")
   431  		w.node(x.Y)
   432  		w.string(")")
   433  
   434  	case *adt.CallExpr:
   435  		w.node(x.Fun)
   436  		w.string("(")
   437  		for i, a := range x.Args {
   438  			if i > 0 {
   439  				w.string(", ")
   440  			}
   441  			w.node(a)
   442  		}
   443  		w.string(")")
   444  
   445  	case *adt.Builtin:
   446  		if x.Package != 0 {
   447  			w.label(x.Package)
   448  			w.string(".")
   449  		}
   450  		w.string(x.Name)
   451  
   452  	case *adt.BuiltinValidator:
   453  		w.node(x.Builtin)
   454  		w.string("(")
   455  		for i, a := range x.Args {
   456  			if i > 0 {
   457  				w.string(", ")
   458  			}
   459  			w.node(a)
   460  		}
   461  		w.string(")")
   462  
   463  	case *adt.DisjunctionExpr:
   464  		w.string("(")
   465  		for i, a := range x.Values {
   466  			if i > 0 {
   467  				w.string("|")
   468  			}
   469  			// Disjunct
   470  			if a.Default {
   471  				w.string("*")
   472  			}
   473  			w.node(a.Val)
   474  		}
   475  		w.string(")")
   476  
   477  	case *adt.Conjunction:
   478  		w.string("&(")
   479  		for i, c := range x.Values {
   480  			if i > 0 {
   481  				w.string(", ")
   482  			}
   483  			w.node(c)
   484  		}
   485  		w.string(")")
   486  
   487  	case *adt.Disjunction:
   488  		w.string("|(")
   489  		for i, c := range x.Values {
   490  			if i > 0 {
   491  				w.string(", ")
   492  			}
   493  			if i < x.NumDefaults {
   494  				w.string("*")
   495  			}
   496  			w.node(c)
   497  		}
   498  		w.string(")")
   499  
   500  	case *adt.ForClause:
   501  		w.string("for ")
   502  		w.ident(x.Key)
   503  		w.string(", ")
   504  		w.ident(x.Value)
   505  		w.string(" in ")
   506  		w.node(x.Src)
   507  		w.string(" ")
   508  		w.node(x.Dst)
   509  
   510  	case *adt.IfClause:
   511  		w.string("if ")
   512  		w.node(x.Condition)
   513  		w.string(" ")
   514  		w.node(x.Dst)
   515  
   516  	case *adt.LetClause:
   517  		w.string("let ")
   518  		w.ident(x.Label)
   519  		w.string(" = ")
   520  		w.node(x.Expr)
   521  		w.string(" ")
   522  		w.node(x.Dst)
   523  
   524  	case *adt.ValueClause:
   525  		w.node(x.StructLit)
   526  
   527  	default:
   528  		panic(fmt.Sprintf("unknown type %T", x))
   529  	}
   530  }