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 }