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