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