blob: b789a05e0de7fb8098068de641feb89460e3db8e [file] [log] [blame]
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +02001// 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//
21package debug
22
23import (
24 "fmt"
25 "io"
26 "strconv"
27 "strings"
28
29 "cuelang.org/go/cue/errors"
Marcel van Lohuizenc8860942020-10-01 12:42:56 +020030 "cuelang.org/go/cue/literal"
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020031 "cuelang.org/go/internal"
32 "cuelang.org/go/internal/core/adt"
33 "golang.org/x/xerrors"
34)
35
36const (
37 openTuple = "\u3008"
38 closeTuple = "\u3009"
39)
40
41type Config struct {
Marcel van Lohuizen6be2b4f2020-06-28 11:32:33 +020042 Cwd string
43 Compact bool
44 Raw bool
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020045}
46
47func WriteNode(w io.Writer, i adt.StringIndexer, n adt.Node, config *Config) {
48 if config == nil {
49 config = &Config{}
50 }
51 p := printer{Writer: w, index: i, cfg: config}
Marcel van Lohuizen6be2b4f2020-06-28 11:32:33 +020052 if config.Compact {
53 p := compactPrinter{p}
54 p.node(n)
55 } else {
56 p.node(n)
57 }
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020058}
59
60func NodeString(i adt.StringIndexer, n adt.Node, config *Config) string {
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020061 b := &strings.Builder{}
Marcel van Lohuizen6be2b4f2020-06-28 11:32:33 +020062 WriteNode(b, i, n, config)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020063 return b.String()
64}
65
66type printer struct {
67 io.Writer
68 index adt.StringIndexer
69 indent string
70 cfg *Config
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +020071
72 // modes:
73 // - show vertex
74 // - show original conjuncts
75 // - show unevaluated
76 // - auto
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020077}
78
79func (w *printer) string(s string) {
80 s = strings.Replace(s, "\n", "\n"+w.indent, -1)
81 _, _ = io.WriteString(w, s)
82}
83
84func (w *printer) label(f adt.Feature) {
85 w.string(w.labelString(f))
86}
87
88// TODO: fold into label once :: is no longer supported.
89func (w *printer) labelString(f adt.Feature) string {
Marcel van Lohuizenb4aa96d2020-10-04 18:09:37 +020090 if f.IsHidden() {
91 ident := f.IdentString(w.index)
92 if pkgName := f.PkgID(w.index); pkgName != "main" {
93 ident = fmt.Sprintf("%s(%s)", ident, pkgName)
94 }
95 return ident
96 }
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +020097 return f.SelectorString(w.index)
98}
99
100func (w *printer) shortError(errs errors.Error) {
101 for {
102 msg, args := errs.Msg()
103 fmt.Fprintf(w, msg, args...)
104
105 err := xerrors.Unwrap(errs)
106 if err == nil {
107 break
108 }
109
110 if errs, _ = err.(errors.Error); errs != nil {
111 w.string(err.Error())
112 break
113 }
114 }
115}
116
Marcel van Lohuizen30ca0622020-08-22 14:07:59 +0200117func (w *printer) interpolation(x *adt.Interpolation) {
118 quote := `"`
119 if x.K == adt.BytesKind {
120 quote = `'`
121 }
122 w.string(quote)
123 for i := 0; i < len(x.Parts); i += 2 {
124 switch x.K {
125 case adt.StringKind:
126 if s, ok := x.Parts[i].(*adt.String); ok {
127 w.string(s.Str)
128 } else {
129 w.string("<bad string>")
130 }
131 case adt.BytesKind:
132 if s, ok := x.Parts[i].(*adt.Bytes); ok {
133 _, _ = w.Write(s.B)
134 } else {
135 w.string("<bad bytes>")
136 }
137 }
138 if i+1 < len(x.Parts) {
139 w.string(`\(`)
140 w.node(x.Parts[i+1])
141 w.string(`)`)
142 }
143 }
144 w.string(quote)
145}
146
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200147func (w *printer) node(n adt.Node) {
148 switch x := n.(type) {
149 case *adt.Vertex:
150 var kind adt.Kind
151 if x.Value != nil {
152 kind = x.Value.Kind()
153 }
154
155 kindStr := kind.String()
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200156
157 // TODO: replace with showing full closedness data.
158 if x.IsClosed(nil) {
159 if kind == adt.ListKind || kind == adt.StructKind {
160 kindStr = "#" + kindStr
161 }
162 }
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200163
164 fmt.Fprintf(w, "(%s){", kindStr)
165
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200166 saved := w.indent
167 w.indent += " "
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200168 defer func() { w.indent = saved }()
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200169
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200170 switch v := x.Value.(type) {
171 case nil:
172 case *adt.Bottom:
173 // TODO: reuse bottom.
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200174 saved := w.indent
175 w.indent += "// "
176 w.string("\n")
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200177 fmt.Fprintf(w, "[%v]", v.Code)
178 if !v.ChildError {
179 msg := errors.Details(v.Err, &errors.Config{
180 Cwd: w.cfg.Cwd,
181 ToSlash: true,
182 })
183 msg = strings.TrimSpace(msg)
184 if msg != "" {
185 w.string(" ")
186 w.string(msg)
187 }
188 }
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200189 w.indent = saved
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200190
191 case *adt.StructMarker, *adt.ListMarker:
192 // if len(x.Arcs) == 0 {
193 // // w.string("}")
194 // // return
195 // }
196
197 default:
198 if len(x.Arcs) == 0 {
199 w.string(" ")
200 w.node(x.Value)
201 w.string(" }")
202 return
203 }
204 w.string("\n")
205 w.node(x.Value)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200206 }
207
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200208 for _, a := range x.Arcs {
209 w.string("\n")
210 w.label(a.Label)
211 w.string(": ")
212 w.node(a)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200213 }
214
215 if x.Value == nil {
216 w.indent += "// "
217 w.string("// ")
218 for i, c := range x.Conjuncts {
219 if i > 0 {
220 w.string(" & ")
221 }
222 w.node(c.Expr()) // TODO: also include env?
223 }
224 }
225
226 w.indent = saved
227 w.string("\n")
228 w.string("}")
229
230 case *adt.StructMarker:
231 w.string("struct")
232
233 case *adt.ListMarker:
234 w.string("list")
235
236 case *adt.StructLit:
237 if len(x.Decls) == 0 {
238 w.string("{}")
239 break
240 }
241 w.string("{")
242 w.indent += " "
243 for _, d := range x.Decls {
244 w.string("\n")
245 w.node(d)
246 }
247 w.indent = w.indent[:len(w.indent)-2]
248 w.string("\n}")
249
250 case *adt.ListLit:
251 if len(x.Elems) == 0 {
252 w.string("[]")
253 break
254 }
255 w.string("[")
256 w.indent += " "
257 for _, d := range x.Elems {
258 w.string("\n")
259 w.node(d)
260 w.string(",")
261 }
262 w.indent = w.indent[:len(w.indent)-2]
263 w.string("\n]")
264
265 case *adt.Field:
266 s := w.labelString(x.Label)
267 w.string(s)
268 w.string(":")
269 if x.Label.IsDef() && !internal.IsDef(s) {
270 w.string(":")
271 }
272 w.string(" ")
273 w.node(x.Value)
274
275 case *adt.OptionalField:
276 s := w.labelString(x.Label)
277 w.string(s)
278 w.string("?:")
279 if x.Label.IsDef() && !internal.IsDef(s) {
280 w.string(":")
281 }
282 w.string(" ")
283 w.node(x.Value)
284
285 case *adt.BulkOptionalField:
286 w.string("[")
287 w.node(x.Filter)
288 w.string("]: ")
289 w.node(x.Value)
290
291 case *adt.DynamicField:
292 w.node(x.Key)
293 if x.IsOptional() {
294 w.string("?")
295 }
296 w.string(": ")
297 w.node(x.Value)
298
299 case *adt.Ellipsis:
300 w.string("...")
301 if x.Value != nil {
302 w.node(x.Value)
303 }
304
305 case *adt.Bottom:
306 w.string(`_|_`)
307 if x.Err != nil {
308 w.string("(")
309 w.shortError(x.Err)
310 w.string(")")
311 }
312
313 case *adt.Null:
314 w.string("null")
315
316 case *adt.Bool:
317 fmt.Fprint(w, x.B)
318
319 case *adt.Num:
320 fmt.Fprint(w, &x.X)
321
322 case *adt.String:
Marcel van Lohuizenc8860942020-10-01 12:42:56 +0200323 w.string(literal.String.Quote(x.Str))
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200324
325 case *adt.Bytes:
Marcel van Lohuizenc8860942020-10-01 12:42:56 +0200326 w.string(literal.Bytes.Quote(string(x.B)))
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200327
328 case *adt.Top:
329 w.string("_")
330
331 case *adt.BasicType:
332 fmt.Fprint(w, x.K)
333
334 case *adt.BoundExpr:
335 fmt.Fprint(w, x.Op)
336 w.node(x.Expr)
337
338 case *adt.BoundValue:
339 fmt.Fprint(w, x.Op)
340 w.node(x.Value)
341
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +0200342 case *adt.NodeLink:
343 w.string(openTuple)
344 for i, f := range x.Node.Path() {
345 if i > 0 {
346 w.string(".")
347 }
348 w.label(f)
349 }
350 w.string(closeTuple)
351
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200352 case *adt.FieldReference:
353 w.string(openTuple)
354 w.string(strconv.Itoa(int(x.UpCount)))
355 w.string(";")
356 w.label(x.Label)
357 w.string(closeTuple)
358
359 case *adt.LabelReference:
360 w.string(openTuple)
361 w.string(strconv.Itoa(int(x.UpCount)))
362 w.string(";-")
363 w.string(closeTuple)
364
365 case *adt.DynamicReference:
366 w.string(openTuple)
367 w.string(strconv.Itoa(int(x.UpCount)))
368 w.string(";(")
369 w.node(x.Label)
370 w.string(")")
371 w.string(closeTuple)
372
373 case *adt.ImportReference:
374 w.string(openTuple + "import;")
375 w.label(x.ImportPath)
376 w.string(closeTuple)
377
378 case *adt.LetReference:
379 w.string(openTuple)
380 w.string(strconv.Itoa(int(x.UpCount)))
381 w.string(";let ")
382 w.label(x.Label)
383 w.string(closeTuple)
384
385 case *adt.SelectorExpr:
386 w.node(x.X)
387 w.string(".")
388 w.label(x.Sel)
389
390 case *adt.IndexExpr:
391 w.node(x.X)
392 w.string("[")
393 w.node(x.Index)
394 w.string("]")
395
396 case *adt.SliceExpr:
397 w.node(x.X)
398 w.string("[")
399 if x.Lo != nil {
400 w.node(x.Lo)
401 }
402 w.string(":")
403 if x.Hi != nil {
404 w.node(x.Hi)
405 }
406 if x.Stride != nil {
407 w.string(":")
408 w.node(x.Stride)
409 }
410 w.string("]")
411
412 case *adt.Interpolation:
Marcel van Lohuizen30ca0622020-08-22 14:07:59 +0200413 w.interpolation(x)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200414
415 case *adt.UnaryExpr:
416 fmt.Fprint(w, x.Op)
417 w.node(x.X)
418
419 case *adt.BinaryExpr:
420 w.string("(")
421 w.node(x.X)
422 fmt.Fprint(w, " ", x.Op, " ")
423 w.node(x.Y)
424 w.string(")")
425
426 case *adt.CallExpr:
427 w.node(x.Fun)
428 w.string("(")
429 for i, a := range x.Args {
430 if i > 0 {
431 w.string(", ")
432 }
433 w.node(a)
434 }
435 w.string(")")
436
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +0200437 case *adt.Builtin:
438 if x.Package != 0 {
439 w.label(x.Package)
440 w.string(".")
441 }
442 w.string(x.Name)
443
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200444 case *adt.BuiltinValidator:
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +0200445 w.node(x.Builtin)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200446 w.string("(")
447 for i, a := range x.Args {
448 if i > 0 {
449 w.string(", ")
450 }
451 w.node(a)
452 }
453 w.string(")")
454
455 case *adt.DisjunctionExpr:
456 w.string("(")
457 for i, a := range x.Values {
458 if i > 0 {
459 w.string("|")
460 }
461 // Disjunct
462 if a.Default {
463 w.string("*")
464 }
465 w.node(a.Val)
466 }
467 w.string(")")
468
469 case *adt.Conjunction:
470 w.string("&(")
471 for i, c := range x.Values {
472 if i > 0 {
473 w.string(", ")
474 }
475 w.node(c)
476 }
477 w.string(")")
478
479 case *adt.Disjunction:
480 w.string("|(")
481 for i, c := range x.Values {
482 if i > 0 {
483 w.string(", ")
484 }
485 if i < x.NumDefaults {
486 w.string("*")
487 }
488 w.node(c)
489 }
490 w.string(")")
491
492 case *adt.ForClause:
493 w.string("for ")
494 w.label(x.Key)
495 w.string(", ")
496 w.label(x.Value)
497 w.string(" in ")
498 w.node(x.Src)
499 w.string(" ")
500 w.node(x.Dst)
501
502 case *adt.IfClause:
503 w.string("if ")
504 w.node(x.Condition)
505 w.string(" ")
506 w.node(x.Dst)
507
508 case *adt.LetClause:
509 w.string("let ")
510 w.label(x.Label)
511 w.string(" = ")
512 w.node(x.Expr)
513 w.string(" ")
514 w.node(x.Dst)
515
516 case *adt.ValueClause:
517 w.node(x.StructLit)
518
519 default:
520 panic(fmt.Sprintf("unknown type %T", x))
521 }
522}