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