cuelang.org/go@v0.13.0/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  package debug
    21  
    22  import (
    23  	"fmt"
    24  	"strconv"
    25  	"strings"
    26  
    27  	"cuelang.org/go/cue/errors"
    28  	"cuelang.org/go/cue/literal"
    29  	"cuelang.org/go/internal/core/adt"
    30  )
    31  
    32  const (
    33  	openTuple  = "\u3008"
    34  	closeTuple = "\u3009"
    35  )
    36  
    37  type Config struct {
    38  	Cwd     string
    39  	Compact bool
    40  	Raw     bool
    41  }
    42  
    43  // AppendNode writes a string representation of the node to w.
    44  func AppendNode(dst []byte, i adt.StringIndexer, n adt.Node, config *Config) []byte {
    45  	if config == nil {
    46  		config = &Config{}
    47  	}
    48  	p := printer{dst: dst, index: i, cfg: config}
    49  	if config.Compact {
    50  		p := compactPrinter{p}
    51  		p.node(n)
    52  		return p.dst
    53  	}
    54  	p.node(n)
    55  	return p.dst
    56  }
    57  
    58  // NodeString returns a string representation of the given node.
    59  // The StringIndexer value i is used to translate elements of n to strings.
    60  // Commonly available implementations of StringIndexer include *adt.OpContext
    61  // and *runtime.Runtime.
    62  func NodeString(i adt.StringIndexer, n adt.Node, config *Config) string {
    63  	var buf [128]byte
    64  	return string(AppendNode(buf[:0], i, n, config))
    65  }
    66  
    67  type printer struct {
    68  	dst    []byte
    69  	index  adt.StringIndexer
    70  	indent string
    71  	cfg    *Config
    72  
    73  	// keep track of vertices to avoid cycles.
    74  	stack []*adt.Vertex
    75  
    76  	// modes:
    77  	// - show vertex
    78  	// - show original conjuncts
    79  	// - show unevaluated
    80  	// - auto
    81  }
    82  
    83  func (w *printer) string(s string) {
    84  	if len(w.indent) > 0 {
    85  		s = strings.Replace(s, "\n", "\n"+w.indent, -1)
    86  	}
    87  	w.dst = append(w.dst, s...)
    88  }
    89  
    90  func (w *printer) int(i int64) {
    91  	w.dst = strconv.AppendInt(w.dst, i, 10)
    92  }
    93  
    94  func (w *printer) label(f adt.Feature) {
    95  	switch {
    96  	case f.IsHidden():
    97  		ident := f.IdentString(w.index)
    98  		if pkgName := f.PkgID(w.index); pkgName != "_" {
    99  			ident = fmt.Sprintf("%s(%s)", ident, pkgName)
   100  		}
   101  		w.string(ident)
   102  
   103  	case f.IsLet():
   104  		ident := f.RawString(w.index)
   105  		ident = strings.Replace(ident, "\x00", "#", 1)
   106  		w.string(ident)
   107  
   108  	default:
   109  		w.string(f.SelectorString(w.index))
   110  	}
   111  }
   112  
   113  func (w *printer) ident(f adt.Feature) {
   114  	w.string(f.IdentString(w.index))
   115  }
   116  
   117  func (w *printer) path(v *adt.Vertex) {
   118  	if p := v.Parent; p != nil && p.Label != 0 {
   119  		w.path(v.Parent)
   120  		w.string(".")
   121  	}
   122  	w.label(v.Label)
   123  }
   124  
   125  func (w *printer) shared(v *adt.Vertex) {
   126  	w.string("~(")
   127  	w.path(v)
   128  	w.string(")")
   129  }
   130  
   131  // printShared prints a reference to a structure-shared node that is a value
   132  // of v, if it is a shared node. It reports the dereferenced node and whether
   133  // the node was printed.
   134  func (w *printer) printShared(v0 *adt.Vertex) (x *adt.Vertex, ok bool) {
   135  	// Handle cyclic shared nodes differently.  If a shared node was part of
   136  	// a disjunction, it will still be wrapped in a disjunct Vertex.
   137  	// Similarly, a shared node should never point to a disjunct directly,
   138  	// but rather to the original arc that subsequently points to a
   139  	// disjunct.
   140  	v0 = v0.DerefDisjunct()
   141  	isCyclic := v0.IsCyclic
   142  	s, ok := v0.BaseValue.(*adt.Vertex)
   143  	v1 := v0.DerefValue()
   144  	useReference := v0.IsShared && !v1.Internal()
   145  	isCyclic = isCyclic || v1.IsCyclic
   146  	_ = isCyclic
   147  	// NOTE(debug): use this line instead of the following to expand shared
   148  	// cases where it is safe to do so.
   149  	// if useReference && isCyclic && ok && len(v.Arcs) > 0 {
   150  	if useReference && ok && len(v1.Arcs) > 0 {
   151  		w.shared(v1)
   152  		return v1, true
   153  	}
   154  	if !w.pushVertex(v1) {
   155  		if s != nil {
   156  			w.shared(s)
   157  			w.string(" =>")
   158  		}
   159  		w.shared(v1)
   160  		return v1, true
   161  	}
   162  	return v1, false
   163  }
   164  
   165  func (w *printer) pushVertex(v *adt.Vertex) bool {
   166  	for _, x := range w.stack {
   167  		if x == v {
   168  			w.string("<TODO: unmarked structural cycle>")
   169  			return false
   170  		}
   171  	}
   172  	w.stack = append(w.stack, v)
   173  	return true
   174  }
   175  
   176  func (w *printer) popVertex() {
   177  	w.stack = w.stack[:len(w.stack)-1]
   178  }
   179  
   180  func (w *printer) shortError(errs errors.Error) {
   181  	for {
   182  		msg, args := errs.Msg()
   183  		w.dst = fmt.Appendf(w.dst, msg, args...)
   184  
   185  		err := errors.Unwrap(errs)
   186  		if err == nil {
   187  			break
   188  		}
   189  
   190  		if errs, _ = err.(errors.Error); errs != nil {
   191  			w.string(err.Error())
   192  			break
   193  		}
   194  	}
   195  }
   196  
   197  func (w *printer) interpolation(x *adt.Interpolation) {
   198  	quote := `"`
   199  	if x.K == adt.BytesKind {
   200  		quote = `'`
   201  	}
   202  	w.string(quote)
   203  	for i := 0; i < len(x.Parts); i += 2 {
   204  		switch x.K {
   205  		case adt.StringKind:
   206  			if s, ok := x.Parts[i].(*adt.String); ok {
   207  				w.string(s.Str)
   208  			} else {
   209  				w.string("<bad string>")
   210  			}
   211  		case adt.BytesKind:
   212  			if s, ok := x.Parts[i].(*adt.Bytes); ok {
   213  				w.dst = append(w.dst, s.B...)
   214  			} else {
   215  				w.string("<bad bytes>")
   216  			}
   217  		}
   218  		if i+1 < len(x.Parts) {
   219  			w.string(`\(`)
   220  			w.node(x.Parts[i+1])
   221  			w.string(`)`)
   222  		}
   223  	}
   224  	w.string(quote)
   225  }
   226  
   227  func (w *printer) arg(n adt.Node) {
   228  	if x, ok := n.(*adt.Vertex); ok {
   229  		if x.Label != adt.InvalidLabel {
   230  			w.path(x)
   231  			return
   232  		}
   233  	}
   234  	w.node(n)
   235  }
   236  
   237  func (w *printer) node(n adt.Node) {
   238  	switch x := n.(type) {
   239  	case *adt.Vertex:
   240  		x, ok := w.printShared(x)
   241  		if ok {
   242  			return
   243  		}
   244  		defer w.popVertex()
   245  
   246  		var kind adt.Kind
   247  		if x.BaseValue != nil {
   248  			kind = x.BaseValue.Kind()
   249  		}
   250  
   251  		kindStr := kind.String()
   252  
   253  		// TODO: replace with showing full closedness data.
   254  		if x.IsClosedList() || x.IsClosedStruct() {
   255  			if kind == adt.ListKind || kind == adt.StructKind {
   256  				kindStr = "#" + kindStr
   257  			}
   258  		}
   259  
   260  		w.dst = fmt.Appendf(w.dst, "(%s){", kindStr)
   261  
   262  		saved := w.indent
   263  		w.indent += "  "
   264  		defer func() { w.indent = saved }()
   265  
   266  		switch v := x.BaseValue.(type) {
   267  		case nil:
   268  		case *adt.Bottom:
   269  			// TODO: reuse bottom.
   270  			saved := w.indent
   271  			w.indent += "// "
   272  			w.string("\n")
   273  			w.dst = fmt.Appendf(w.dst, "[%v]", v.Code)
   274  			if !v.ChildError {
   275  				msg := errors.Details(v.Err, &errors.Config{
   276  					Cwd:     w.cfg.Cwd,
   277  					ToSlash: true,
   278  				})
   279  				msg = strings.TrimSpace(msg)
   280  				if msg != "" {
   281  					w.string(" ")
   282  					w.string(msg)
   283  				}
   284  
   285  				// TODO: we could consider removing CycleError here. It does
   286  				// seem safer, however, as sometimes structural cycles are
   287  				// detected as regular cycles.
   288  				// Alternatively, we could consider to never report arcs if
   289  				// there is any error.
   290  				if v.Code == adt.CycleError || v.Code == adt.StructuralCycleError {
   291  					goto endVertex
   292  				}
   293  			}
   294  			w.indent = saved
   295  
   296  		case *adt.StructMarker, *adt.ListMarker:
   297  			// if len(x.Arcs) == 0 {
   298  			// 	// w.string("}")
   299  			// 	// return
   300  			// }
   301  
   302  		case adt.Value:
   303  			if len(x.Arcs) == 0 {
   304  				w.string(" ")
   305  				w.node(v)
   306  				w.string(" }")
   307  				return
   308  			}
   309  			w.string("\n")
   310  			w.node(v)
   311  		}
   312  
   313  		for _, a := range x.Arcs {
   314  			if a.ArcType == adt.ArcNotPresent {
   315  				continue
   316  			}
   317  			if a.Label.IsLet() {
   318  				w.string("\n")
   319  				w.string("let ")
   320  				w.label(a.Label)
   321  				if a.MultiLet {
   322  					w.string("multi")
   323  				}
   324  				w.string(" = ")
   325  				if c := a.ConjunctAt(0); a.MultiLet {
   326  					w.node(c.Expr())
   327  					continue
   328  				}
   329  				w.node(a)
   330  			} else {
   331  				w.string("\n")
   332  				w.label(a.Label)
   333  				w.string(a.ArcType.Suffix())
   334  				w.string(": ")
   335  				w.node(a)
   336  			}
   337  		}
   338  
   339  		if x.BaseValue == nil {
   340  			w.indent += "// "
   341  			w.string("// ")
   342  			for i, c := range x.Conjuncts {
   343  				if c.CloseInfo.FromDef || c.CloseInfo.FromEmbed {
   344  					w.string("[")
   345  					if c.CloseInfo.FromDef {
   346  						w.string("d")
   347  					}
   348  					if c.CloseInfo.FromEmbed {
   349  						w.string("e")
   350  					}
   351  					w.string("]")
   352  				}
   353  				if i > 0 {
   354  					w.string(" & ")
   355  				}
   356  				w.node(c.Elem()) // TODO: also include env?
   357  			}
   358  		}
   359  
   360  	endVertex:
   361  
   362  		w.indent = saved
   363  		w.string("\n")
   364  		w.string("}")
   365  
   366  	case *adt.StructMarker:
   367  		w.string("struct")
   368  
   369  	case *adt.ListMarker:
   370  		w.string("list")
   371  
   372  	case *adt.StructLit:
   373  		if len(x.Decls) == 0 {
   374  			w.string("{}")
   375  			break
   376  		}
   377  		w.string("{")
   378  		w.indent += "  "
   379  		for _, d := range x.Decls {
   380  			w.string("\n")
   381  			w.node(d)
   382  		}
   383  		w.indent = w.indent[:len(w.indent)-2]
   384  		w.string("\n}")
   385  
   386  	case *adt.ListLit:
   387  		if len(x.Elems) == 0 {
   388  			w.string("[]")
   389  			break
   390  		}
   391  		w.string("[")
   392  		w.indent += "  "
   393  		for _, d := range x.Elems {
   394  			w.string("\n")
   395  			w.node(d)
   396  			w.string(",")
   397  		}
   398  		w.indent = w.indent[:len(w.indent)-2]
   399  		w.string("\n]")
   400  
   401  	case *adt.Field:
   402  		w.label(x.Label)
   403  		w.string(x.ArcType.Suffix())
   404  		w.string(":")
   405  		w.string(" ")
   406  		w.node(x.Value)
   407  
   408  	case *adt.LetField:
   409  		w.string("let ")
   410  		w.label(x.Label)
   411  		if x.IsMulti {
   412  			w.string("multi")
   413  		}
   414  		w.string(" = ")
   415  		w.node(x.Value)
   416  
   417  	case *adt.BulkOptionalField:
   418  		w.string("[")
   419  		w.node(x.Filter)
   420  		w.string("]: ")
   421  		w.node(x.Value)
   422  
   423  	case *adt.DynamicField:
   424  		w.node(x.Key)
   425  		w.string(x.ArcType.Suffix())
   426  		w.string(": ")
   427  		w.node(x.Value)
   428  
   429  	case *adt.Ellipsis:
   430  		w.string("...")
   431  		if x.Value != nil {
   432  			w.node(x.Value)
   433  		}
   434  
   435  	case *adt.Bottom:
   436  		w.string(`_|_`)
   437  		if x.Err != nil {
   438  			w.string("(")
   439  			w.shortError(x.Err)
   440  			w.string(")")
   441  		}
   442  
   443  	case *adt.Null:
   444  		w.string("null")
   445  
   446  	case *adt.Bool:
   447  		w.dst = strconv.AppendBool(w.dst, x.B)
   448  
   449  	case *adt.Num:
   450  		w.string(x.X.String())
   451  
   452  	case *adt.String:
   453  		w.dst = literal.String.Append(w.dst, x.Str)
   454  
   455  	case *adt.Bytes:
   456  		w.dst = literal.Bytes.Append(w.dst, string(x.B))
   457  
   458  	case *adt.Top:
   459  		w.string("_")
   460  
   461  	case *adt.BasicType:
   462  		w.string(x.K.String())
   463  
   464  	case *adt.BoundExpr:
   465  		w.string(x.Op.String())
   466  		w.node(x.Expr)
   467  
   468  	case *adt.BoundValue:
   469  		w.string(x.Op.String())
   470  		w.node(x.Value)
   471  
   472  	case *adt.NodeLink:
   473  		w.string(openTuple)
   474  		for i, f := range x.Node.Path() {
   475  			if i > 0 {
   476  				w.string(".")
   477  			}
   478  			w.label(f)
   479  		}
   480  		w.string(closeTuple)
   481  
   482  	case *adt.FieldReference:
   483  		w.string(openTuple)
   484  		w.int(int64(x.UpCount))
   485  		w.string(";")
   486  		w.label(x.Label)
   487  		w.string(closeTuple)
   488  
   489  	case *adt.ValueReference:
   490  		w.string(openTuple)
   491  		w.int(int64(x.UpCount))
   492  		w.string(closeTuple)
   493  
   494  	case *adt.LabelReference:
   495  		w.string(openTuple)
   496  		w.int(int64(x.UpCount))
   497  		w.string(";-")
   498  		w.string(closeTuple)
   499  
   500  	case *adt.DynamicReference:
   501  		w.string(openTuple)
   502  		w.int(int64(x.UpCount))
   503  		w.string(";(")
   504  		w.node(x.Label)
   505  		w.string(")")
   506  		w.string(closeTuple)
   507  
   508  	case *adt.ImportReference:
   509  		w.string(openTuple + "import;")
   510  		w.label(x.ImportPath)
   511  		w.string(closeTuple)
   512  
   513  	case *adt.LetReference:
   514  		w.string(openTuple)
   515  		w.int(int64(x.UpCount))
   516  		w.string(";let ")
   517  		w.label(x.Label)
   518  		w.string(closeTuple)
   519  
   520  	case *adt.SelectorExpr:
   521  		w.node(x.X)
   522  		w.string(".")
   523  		w.label(x.Sel)
   524  
   525  	case *adt.IndexExpr:
   526  		w.node(x.X)
   527  		w.string("[")
   528  		w.node(x.Index)
   529  		w.string("]")
   530  
   531  	case *adt.SliceExpr:
   532  		w.node(x.X)
   533  		w.string("[")
   534  		if x.Lo != nil {
   535  			w.node(x.Lo)
   536  		}
   537  		w.string(":")
   538  		if x.Hi != nil {
   539  			w.node(x.Hi)
   540  		}
   541  		if x.Stride != nil {
   542  			w.string(":")
   543  			w.node(x.Stride)
   544  		}
   545  		w.string("]")
   546  
   547  	case *adt.Interpolation:
   548  		w.interpolation(x)
   549  
   550  	case *adt.UnaryExpr:
   551  		w.string(x.Op.String())
   552  		w.node(x.X)
   553  
   554  	case *adt.BinaryExpr:
   555  		w.string("(")
   556  		w.node(x.X)
   557  		w.string(" ")
   558  		w.string(x.Op.String())
   559  		w.string(" ")
   560  		w.node(x.Y)
   561  		w.string(")")
   562  
   563  	case *adt.CallExpr:
   564  		w.node(x.Fun)
   565  		w.string("(")
   566  		for i, a := range x.Args {
   567  			if i > 0 {
   568  				w.string(", ")
   569  			}
   570  			w.arg(a)
   571  		}
   572  		w.string(")")
   573  
   574  	case *adt.Builtin:
   575  		if x.Package != 0 {
   576  			w.label(x.Package)
   577  			w.string(".")
   578  		}
   579  		w.string(x.Name)
   580  
   581  	case *adt.BuiltinValidator:
   582  		w.node(x.Builtin)
   583  		w.string("(")
   584  		for i, a := range x.Args {
   585  			if i > 0 {
   586  				w.string(", ")
   587  			}
   588  			w.arg(a)
   589  		}
   590  		w.string(")")
   591  
   592  	case *adt.DisjunctionExpr:
   593  		w.string("(")
   594  		for i, a := range x.Values {
   595  			if i > 0 {
   596  				w.string("|")
   597  			}
   598  			// Disjunct
   599  			if a.Default {
   600  				w.string("*")
   601  			}
   602  			w.node(a.Val)
   603  		}
   604  		w.string(")")
   605  
   606  	case *adt.Conjunction:
   607  		w.string("&(")
   608  		for i, c := range x.Values {
   609  			if i > 0 {
   610  				w.string(", ")
   611  			}
   612  			w.node(c)
   613  		}
   614  		w.string(")")
   615  
   616  	case *adt.ConjunctGroup:
   617  		w.string("&[")
   618  		for i, c := range *x {
   619  			if i > 0 {
   620  				w.string(", ")
   621  			}
   622  			w.node(c.Expr())
   623  		}
   624  		w.string("]")
   625  
   626  	case *adt.Disjunction:
   627  		w.string("|(")
   628  		for i, c := range x.Values {
   629  			if i > 0 {
   630  				w.string(", ")
   631  			}
   632  			if i < x.NumDefaults {
   633  				w.string("*")
   634  			}
   635  			w.node(c)
   636  		}
   637  		w.string(")")
   638  
   639  	case *adt.Comprehension:
   640  		for _, c := range x.Clauses {
   641  			w.node(c)
   642  		}
   643  		w.node(adt.ToExpr(x.Value))
   644  
   645  	case *adt.ForClause:
   646  		w.string("for ")
   647  		w.ident(x.Key)
   648  		w.string(", ")
   649  		w.ident(x.Value)
   650  		w.string(" in ")
   651  		w.node(x.Src)
   652  		w.string(" ")
   653  
   654  	case *adt.IfClause:
   655  		w.string("if ")
   656  		w.node(x.Condition)
   657  		w.string(" ")
   658  
   659  	case *adt.LetClause:
   660  		w.string("let ")
   661  		w.ident(x.Label)
   662  		w.string(" = ")
   663  		w.node(x.Expr)
   664  		w.string(" ")
   665  
   666  	case *adt.ValueClause:
   667  
   668  	default:
   669  		panic(fmt.Sprintf("unknown type %T", x))
   670  	}
   671  }