...

Source file src/cuelang.org/go/internal/core/debug/debug.go

Documentation: cuelang.org/go/internal/core/debug

     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  	"io"
    25  	"strconv"
    26  	"strings"
    27  
    28  	"cuelang.org/go/cue/errors"
    29  	"cuelang.org/go/cue/literal"
    30  	"cuelang.org/go/internal/core/adt"
    31  )
    32  
    33  const (
    34  	openTuple  = "\u3008"
    35  	closeTuple = "\u3009"
    36  )
    37  
    38  type Config struct {
    39  	Cwd     string
    40  	Compact bool
    41  	Raw     bool
    42  }
    43  
    44  // WriteNode writes a string representation of the node to w.
    45  func WriteNode(w io.Writer, i adt.StringIndexer, n adt.Node, config *Config) {
    46  	if config == nil {
    47  		config = &Config{}
    48  	}
    49  	p := printer{Writer: w, index: i, cfg: config}
    50  	if config.Compact {
    51  		p := compactPrinter{p}
    52  		p.node(n)
    53  	} else {
    54  		p.node(n)
    55  	}
    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  	b := &strings.Builder{}
    64  	WriteNode(b, i, n, config)
    65  	return b.String()
    66  }
    67  
    68  type printer struct {
    69  	io.Writer
    70  	index  adt.StringIndexer
    71  	indent string
    72  	cfg    *Config
    73  
    74  	// modes:
    75  	// - show vertex
    76  	// - show original conjuncts
    77  	// - show unevaluated
    78  	// - auto
    79  }
    80  
    81  func (w *printer) string(s string) {
    82  	s = strings.Replace(s, "\n", "\n"+w.indent, -1)
    83  	_, _ = io.WriteString(w, s)
    84  }
    85  
    86  func (w *printer) label(f adt.Feature) {
    87  	w.string(w.labelString(f))
    88  }
    89  
    90  func (w *printer) ident(f adt.Feature) {
    91  	w.string(f.IdentString(w.index))
    92  }
    93  
    94  // TODO: fold into label once :: is no longer supported.
    95  func (w *printer) labelString(f adt.Feature) string {
    96  	switch {
    97  	case f.IsHidden():
    98  		ident := f.IdentString(w.index)
    99  		if pkgName := f.PkgID(w.index); pkgName != "_" {
   100  			ident = fmt.Sprintf("%s(%s)", ident, pkgName)
   101  		}
   102  		return ident
   103  
   104  	case f.IsLet():
   105  		ident := f.RawString(w.index)
   106  		ident = strings.Replace(ident, "\x00", "#", 1)
   107  		return ident
   108  
   109  	default:
   110  		return f.SelectorString(w.index)
   111  	}
   112  }
   113  
   114  func (w *printer) shortError(errs errors.Error) {
   115  	for {
   116  		msg, args := errs.Msg()
   117  		fmt.Fprintf(w, msg, args...)
   118  
   119  		err := errors.Unwrap(errs)
   120  		if err == nil {
   121  			break
   122  		}
   123  
   124  		if errs, _ = err.(errors.Error); errs != nil {
   125  			w.string(err.Error())
   126  			break
   127  		}
   128  	}
   129  }
   130  
   131  func (w *printer) interpolation(x *adt.Interpolation) {
   132  	quote := `"`
   133  	if x.K == adt.BytesKind {
   134  		quote = `'`
   135  	}
   136  	w.string(quote)
   137  	for i := 0; i < len(x.Parts); i += 2 {
   138  		switch x.K {
   139  		case adt.StringKind:
   140  			if s, ok := x.Parts[i].(*adt.String); ok {
   141  				w.string(s.Str)
   142  			} else {
   143  				w.string("<bad string>")
   144  			}
   145  		case adt.BytesKind:
   146  			if s, ok := x.Parts[i].(*adt.Bytes); ok {
   147  				_, _ = w.Write(s.B)
   148  			} else {
   149  				w.string("<bad bytes>")
   150  			}
   151  		}
   152  		if i+1 < len(x.Parts) {
   153  			w.string(`\(`)
   154  			w.node(x.Parts[i+1])
   155  			w.string(`)`)
   156  		}
   157  	}
   158  	w.string(quote)
   159  }
   160  
   161  func (w *printer) node(n adt.Node) {
   162  	switch x := n.(type) {
   163  	case *adt.Vertex:
   164  		var kind adt.Kind
   165  		if x.BaseValue != nil {
   166  			kind = x.BaseValue.Kind()
   167  		}
   168  
   169  		kindStr := kind.String()
   170  
   171  		// TODO: replace with showing full closedness data.
   172  		if x.IsClosedList() || x.IsClosedStruct() {
   173  			if kind == adt.ListKind || kind == adt.StructKind {
   174  				kindStr = "#" + kindStr
   175  			}
   176  		}
   177  
   178  		fmt.Fprintf(w, "(%s){", kindStr)
   179  
   180  		saved := w.indent
   181  		w.indent += "  "
   182  		defer func() { w.indent = saved }()
   183  
   184  		switch v := x.BaseValue.(type) {
   185  		case nil:
   186  		case *adt.Bottom:
   187  			// TODO: reuse bottom.
   188  			saved := w.indent
   189  			w.indent += "// "
   190  			w.string("\n")
   191  			fmt.Fprintf(w, "[%v]", v.Code)
   192  			if !v.ChildError {
   193  				msg := errors.Details(v.Err, &errors.Config{
   194  					Cwd:     w.cfg.Cwd,
   195  					ToSlash: true,
   196  				})
   197  				msg = strings.TrimSpace(msg)
   198  				if msg != "" {
   199  					w.string(" ")
   200  					w.string(msg)
   201  				}
   202  			}
   203  			w.indent = saved
   204  
   205  		case *adt.StructMarker, *adt.ListMarker:
   206  			// if len(x.Arcs) == 0 {
   207  			// 	// w.string("}")
   208  			// 	// return
   209  			// }
   210  
   211  		case adt.Value:
   212  			if len(x.Arcs) == 0 {
   213  				w.string(" ")
   214  				w.node(v)
   215  				w.string(" }")
   216  				return
   217  			}
   218  			w.string("\n")
   219  			w.node(v)
   220  		}
   221  
   222  		for _, a := range x.Arcs {
   223  			if a.ArcType == adt.ArcNotPresent {
   224  				continue
   225  			}
   226  			if a.Label.IsLet() {
   227  				w.string("\n")
   228  				w.string("let ")
   229  				w.label(a.Label)
   230  				if a.MultiLet {
   231  					w.string("multi")
   232  				}
   233  				w.string(" = ")
   234  				if c := a.Conjuncts[0]; a.MultiLet {
   235  					w.node(c.Expr())
   236  					continue
   237  				}
   238  				w.node(a)
   239  			} else {
   240  				w.string("\n")
   241  				w.label(a.Label)
   242  				w.string(a.ArcType.Suffix())
   243  				w.string(": ")
   244  				w.node(a)
   245  			}
   246  		}
   247  
   248  		if x.BaseValue == nil {
   249  			w.indent += "// "
   250  			w.string("// ")
   251  			for i, c := range x.Conjuncts {
   252  				if c.CloseInfo.FromDef || c.CloseInfo.FromEmbed {
   253  					w.string("[")
   254  					if c.CloseInfo.FromDef {
   255  						w.string("d")
   256  					}
   257  					if c.CloseInfo.FromEmbed {
   258  						w.string("e")
   259  					}
   260  					w.string("]")
   261  				}
   262  				if i > 0 {
   263  					w.string(" & ")
   264  				}
   265  				w.node(c.Elem()) // TODO: also include env?
   266  			}
   267  		}
   268  
   269  		w.indent = saved
   270  		w.string("\n")
   271  		w.string("}")
   272  
   273  	case *adt.StructMarker:
   274  		w.string("struct")
   275  
   276  	case *adt.ListMarker:
   277  		w.string("list")
   278  
   279  	case *adt.StructLit:
   280  		if len(x.Decls) == 0 {
   281  			w.string("{}")
   282  			break
   283  		}
   284  		w.string("{")
   285  		w.indent += "  "
   286  		for _, d := range x.Decls {
   287  			w.string("\n")
   288  			w.node(d)
   289  		}
   290  		w.indent = w.indent[:len(w.indent)-2]
   291  		w.string("\n}")
   292  
   293  	case *adt.ListLit:
   294  		if len(x.Elems) == 0 {
   295  			w.string("[]")
   296  			break
   297  		}
   298  		w.string("[")
   299  		w.indent += "  "
   300  		for _, d := range x.Elems {
   301  			w.string("\n")
   302  			w.node(d)
   303  			w.string(",")
   304  		}
   305  		w.indent = w.indent[:len(w.indent)-2]
   306  		w.string("\n]")
   307  
   308  	case *adt.Field:
   309  		s := w.labelString(x.Label)
   310  		w.string(s)
   311  		w.string(x.ArcType.Suffix())
   312  		w.string(":")
   313  		w.string(" ")
   314  		w.node(x.Value)
   315  
   316  	case *adt.LetField:
   317  		w.string("let ")
   318  		s := w.labelString(x.Label)
   319  		w.string(s)
   320  		if x.IsMulti {
   321  			w.string("multi")
   322  		}
   323  		w.string(" = ")
   324  		w.node(x.Value)
   325  
   326  	case *adt.BulkOptionalField:
   327  		w.string("[")
   328  		w.node(x.Filter)
   329  		w.string("]: ")
   330  		w.node(x.Value)
   331  
   332  	case *adt.DynamicField:
   333  		w.node(x.Key)
   334  		w.string(x.ArcType.Suffix())
   335  		w.string(": ")
   336  		w.node(x.Value)
   337  
   338  	case *adt.Ellipsis:
   339  		w.string("...")
   340  		if x.Value != nil {
   341  			w.node(x.Value)
   342  		}
   343  
   344  	case *adt.Bottom:
   345  		w.string(`_|_`)
   346  		if x.Err != nil {
   347  			w.string("(")
   348  			w.shortError(x.Err)
   349  			w.string(")")
   350  		}
   351  
   352  	case *adt.Null:
   353  		w.string("null")
   354  
   355  	case *adt.Bool:
   356  		fmt.Fprint(w, x.B)
   357  
   358  	case *adt.Num:
   359  		fmt.Fprint(w, &x.X)
   360  
   361  	case *adt.String:
   362  		w.string(literal.String.Quote(x.Str))
   363  
   364  	case *adt.Bytes:
   365  		w.string(literal.Bytes.Quote(string(x.B)))
   366  
   367  	case *adt.Top:
   368  		w.string("_")
   369  
   370  	case *adt.BasicType:
   371  		fmt.Fprint(w, x.K)
   372  
   373  	case *adt.BoundExpr:
   374  		fmt.Fprint(w, x.Op)
   375  		w.node(x.Expr)
   376  
   377  	case *adt.BoundValue:
   378  		fmt.Fprint(w, x.Op)
   379  		w.node(x.Value)
   380  
   381  	case *adt.NodeLink:
   382  		w.string(openTuple)
   383  		for i, f := range x.Node.Path() {
   384  			if i > 0 {
   385  				w.string(".")
   386  			}
   387  			w.label(f)
   388  		}
   389  		w.string(closeTuple)
   390  
   391  	case *adt.FieldReference:
   392  		w.string(openTuple)
   393  		w.string(strconv.Itoa(int(x.UpCount)))
   394  		w.string(";")
   395  		w.label(x.Label)
   396  		w.string(closeTuple)
   397  
   398  	case *adt.ValueReference:
   399  		w.string(openTuple)
   400  		w.string(strconv.Itoa(int(x.UpCount)))
   401  		w.string(closeTuple)
   402  
   403  	case *adt.LabelReference:
   404  		w.string(openTuple)
   405  		w.string(strconv.Itoa(int(x.UpCount)))
   406  		w.string(";-")
   407  		w.string(closeTuple)
   408  
   409  	case *adt.DynamicReference:
   410  		w.string(openTuple)
   411  		w.string(strconv.Itoa(int(x.UpCount)))
   412  		w.string(";(")
   413  		w.node(x.Label)
   414  		w.string(")")
   415  		w.string(closeTuple)
   416  
   417  	case *adt.ImportReference:
   418  		w.string(openTuple + "import;")
   419  		w.label(x.ImportPath)
   420  		w.string(closeTuple)
   421  
   422  	case *adt.LetReference:
   423  		w.string(openTuple)
   424  		w.string(strconv.Itoa(int(x.UpCount)))
   425  		w.string(";let ")
   426  		w.label(x.Label)
   427  		w.string(closeTuple)
   428  
   429  	case *adt.SelectorExpr:
   430  		w.node(x.X)
   431  		w.string(".")
   432  		w.label(x.Sel)
   433  
   434  	case *adt.IndexExpr:
   435  		w.node(x.X)
   436  		w.string("[")
   437  		w.node(x.Index)
   438  		w.string("]")
   439  
   440  	case *adt.SliceExpr:
   441  		w.node(x.X)
   442  		w.string("[")
   443  		if x.Lo != nil {
   444  			w.node(x.Lo)
   445  		}
   446  		w.string(":")
   447  		if x.Hi != nil {
   448  			w.node(x.Hi)
   449  		}
   450  		if x.Stride != nil {
   451  			w.string(":")
   452  			w.node(x.Stride)
   453  		}
   454  		w.string("]")
   455  
   456  	case *adt.Interpolation:
   457  		w.interpolation(x)
   458  
   459  	case *adt.UnaryExpr:
   460  		fmt.Fprint(w, x.Op)
   461  		w.node(x.X)
   462  
   463  	case *adt.BinaryExpr:
   464  		w.string("(")
   465  		w.node(x.X)
   466  		fmt.Fprint(w, " ", x.Op, " ")
   467  		w.node(x.Y)
   468  		w.string(")")
   469  
   470  	case *adt.CallExpr:
   471  		w.node(x.Fun)
   472  		w.string("(")
   473  		for i, a := range x.Args {
   474  			if i > 0 {
   475  				w.string(", ")
   476  			}
   477  			w.node(a)
   478  		}
   479  		w.string(")")
   480  
   481  	case *adt.Builtin:
   482  		if x.Package != 0 {
   483  			w.label(x.Package)
   484  			w.string(".")
   485  		}
   486  		w.string(x.Name)
   487  
   488  	case *adt.BuiltinValidator:
   489  		w.node(x.Builtin)
   490  		w.string("(")
   491  		for i, a := range x.Args {
   492  			if i > 0 {
   493  				w.string(", ")
   494  			}
   495  			w.node(a)
   496  		}
   497  		w.string(")")
   498  
   499  	case *adt.DisjunctionExpr:
   500  		w.string("(")
   501  		for i, a := range x.Values {
   502  			if i > 0 {
   503  				w.string("|")
   504  			}
   505  			// Disjunct
   506  			if a.Default {
   507  				w.string("*")
   508  			}
   509  			w.node(a.Val)
   510  		}
   511  		w.string(")")
   512  
   513  	case *adt.Conjunction:
   514  		w.string("&(")
   515  		for i, c := range x.Values {
   516  			if i > 0 {
   517  				w.string(", ")
   518  			}
   519  			w.node(c)
   520  		}
   521  		w.string(")")
   522  
   523  	case *adt.ConjunctGroup:
   524  		w.string("&[")
   525  		for i, c := range *x {
   526  			if i > 0 {
   527  				w.string(", ")
   528  			}
   529  			w.node(c.Expr())
   530  		}
   531  		w.string("]")
   532  
   533  	case *adt.Disjunction:
   534  		w.string("|(")
   535  		for i, c := range x.Values {
   536  			if i > 0 {
   537  				w.string(", ")
   538  			}
   539  			if i < x.NumDefaults {
   540  				w.string("*")
   541  			}
   542  			w.node(c)
   543  		}
   544  		w.string(")")
   545  
   546  	case *adt.Comprehension:
   547  		for _, c := range x.Clauses {
   548  			w.node(c)
   549  		}
   550  		w.node(adt.ToExpr(x.Value))
   551  
   552  	case *adt.ForClause:
   553  		w.string("for ")
   554  		w.ident(x.Key)
   555  		w.string(", ")
   556  		w.ident(x.Value)
   557  		w.string(" in ")
   558  		w.node(x.Src)
   559  		w.string(" ")
   560  
   561  	case *adt.IfClause:
   562  		w.string("if ")
   563  		w.node(x.Condition)
   564  		w.string(" ")
   565  
   566  	case *adt.LetClause:
   567  		w.string("let ")
   568  		w.ident(x.Label)
   569  		w.string(" = ")
   570  		w.node(x.Expr)
   571  		w.string(" ")
   572  
   573  	case *adt.ValueClause:
   574  
   575  	default:
   576  		panic(fmt.Sprintf("unknown type %T", x))
   577  	}
   578  }
   579  

View as plain text