blob: a5d8e0ba7da573886a4e59b63e87a45fd480f030 [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 {
90 return f.SelectorString(w.index)
91}
92
93func (w *printer) shortError(errs errors.Error) {
94 for {
95 msg, args := errs.Msg()
96 fmt.Fprintf(w, msg, args...)
97
98 err := xerrors.Unwrap(errs)
99 if err == nil {
100 break
101 }
102
103 if errs, _ = err.(errors.Error); errs != nil {
104 w.string(err.Error())
105 break
106 }
107 }
108}
109
Marcel van Lohuizen30ca0622020-08-22 14:07:59 +0200110func (w *printer) interpolation(x *adt.Interpolation) {
111 quote := `"`
112 if x.K == adt.BytesKind {
113 quote = `'`
114 }
115 w.string(quote)
116 for i := 0; i < len(x.Parts); i += 2 {
117 switch x.K {
118 case adt.StringKind:
119 if s, ok := x.Parts[i].(*adt.String); ok {
120 w.string(s.Str)
121 } else {
122 w.string("<bad string>")
123 }
124 case adt.BytesKind:
125 if s, ok := x.Parts[i].(*adt.Bytes); ok {
126 _, _ = w.Write(s.B)
127 } else {
128 w.string("<bad bytes>")
129 }
130 }
131 if i+1 < len(x.Parts) {
132 w.string(`\(`)
133 w.node(x.Parts[i+1])
134 w.string(`)`)
135 }
136 }
137 w.string(quote)
138}
139
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200140func (w *printer) node(n adt.Node) {
141 switch x := n.(type) {
142 case *adt.Vertex:
143 var kind adt.Kind
144 if x.Value != nil {
145 kind = x.Value.Kind()
146 }
147
148 kindStr := kind.String()
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200149
150 // TODO: replace with showing full closedness data.
151 if x.IsClosed(nil) {
152 if kind == adt.ListKind || kind == adt.StructKind {
153 kindStr = "#" + kindStr
154 }
155 }
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200156
157 fmt.Fprintf(w, "(%s){", kindStr)
158
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200159 saved := w.indent
160 w.indent += " "
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200161 defer func() { w.indent = saved }()
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200162
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200163 switch v := x.Value.(type) {
164 case nil:
165 case *adt.Bottom:
166 // TODO: reuse bottom.
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200167 saved := w.indent
168 w.indent += "// "
169 w.string("\n")
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200170 fmt.Fprintf(w, "[%v]", v.Code)
171 if !v.ChildError {
172 msg := errors.Details(v.Err, &errors.Config{
173 Cwd: w.cfg.Cwd,
174 ToSlash: true,
175 })
176 msg = strings.TrimSpace(msg)
177 if msg != "" {
178 w.string(" ")
179 w.string(msg)
180 }
181 }
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200182 w.indent = saved
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200183
184 case *adt.StructMarker, *adt.ListMarker:
185 // if len(x.Arcs) == 0 {
186 // // w.string("}")
187 // // return
188 // }
189
190 default:
191 if len(x.Arcs) == 0 {
192 w.string(" ")
193 w.node(x.Value)
194 w.string(" }")
195 return
196 }
197 w.string("\n")
198 w.node(x.Value)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200199 }
200
Marcel van Lohuizen0f935f82020-06-09 09:13:01 +0200201 for _, a := range x.Arcs {
202 w.string("\n")
203 w.label(a.Label)
204 w.string(": ")
205 w.node(a)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200206 }
207
208 if x.Value == nil {
209 w.indent += "// "
210 w.string("// ")
211 for i, c := range x.Conjuncts {
212 if i > 0 {
213 w.string(" & ")
214 }
215 w.node(c.Expr()) // TODO: also include env?
216 }
217 }
218
219 w.indent = saved
220 w.string("\n")
221 w.string("}")
222
223 case *adt.StructMarker:
224 w.string("struct")
225
226 case *adt.ListMarker:
227 w.string("list")
228
229 case *adt.StructLit:
230 if len(x.Decls) == 0 {
231 w.string("{}")
232 break
233 }
234 w.string("{")
235 w.indent += " "
236 for _, d := range x.Decls {
237 w.string("\n")
238 w.node(d)
239 }
240 w.indent = w.indent[:len(w.indent)-2]
241 w.string("\n}")
242
243 case *adt.ListLit:
244 if len(x.Elems) == 0 {
245 w.string("[]")
246 break
247 }
248 w.string("[")
249 w.indent += " "
250 for _, d := range x.Elems {
251 w.string("\n")
252 w.node(d)
253 w.string(",")
254 }
255 w.indent = w.indent[:len(w.indent)-2]
256 w.string("\n]")
257
258 case *adt.Field:
259 s := w.labelString(x.Label)
260 w.string(s)
261 w.string(":")
262 if x.Label.IsDef() && !internal.IsDef(s) {
263 w.string(":")
264 }
265 w.string(" ")
266 w.node(x.Value)
267
268 case *adt.OptionalField:
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.BulkOptionalField:
279 w.string("[")
280 w.node(x.Filter)
281 w.string("]: ")
282 w.node(x.Value)
283
284 case *adt.DynamicField:
285 w.node(x.Key)
286 if x.IsOptional() {
287 w.string("?")
288 }
289 w.string(": ")
290 w.node(x.Value)
291
292 case *adt.Ellipsis:
293 w.string("...")
294 if x.Value != nil {
295 w.node(x.Value)
296 }
297
298 case *adt.Bottom:
299 w.string(`_|_`)
300 if x.Err != nil {
301 w.string("(")
302 w.shortError(x.Err)
303 w.string(")")
304 }
305
306 case *adt.Null:
307 w.string("null")
308
309 case *adt.Bool:
310 fmt.Fprint(w, x.B)
311
312 case *adt.Num:
313 fmt.Fprint(w, &x.X)
314
315 case *adt.String:
Marcel van Lohuizenc8860942020-10-01 12:42:56 +0200316 w.string(literal.String.Quote(x.Str))
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200317
318 case *adt.Bytes:
Marcel van Lohuizenc8860942020-10-01 12:42:56 +0200319 w.string(literal.Bytes.Quote(string(x.B)))
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200320
321 case *adt.Top:
322 w.string("_")
323
324 case *adt.BasicType:
325 fmt.Fprint(w, x.K)
326
327 case *adt.BoundExpr:
328 fmt.Fprint(w, x.Op)
329 w.node(x.Expr)
330
331 case *adt.BoundValue:
332 fmt.Fprint(w, x.Op)
333 w.node(x.Value)
334
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +0200335 case *adt.NodeLink:
336 w.string(openTuple)
337 for i, f := range x.Node.Path() {
338 if i > 0 {
339 w.string(".")
340 }
341 w.label(f)
342 }
343 w.string(closeTuple)
344
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200345 case *adt.FieldReference:
346 w.string(openTuple)
347 w.string(strconv.Itoa(int(x.UpCount)))
348 w.string(";")
349 w.label(x.Label)
350 w.string(closeTuple)
351
352 case *adt.LabelReference:
353 w.string(openTuple)
354 w.string(strconv.Itoa(int(x.UpCount)))
355 w.string(";-")
356 w.string(closeTuple)
357
358 case *adt.DynamicReference:
359 w.string(openTuple)
360 w.string(strconv.Itoa(int(x.UpCount)))
361 w.string(";(")
362 w.node(x.Label)
363 w.string(")")
364 w.string(closeTuple)
365
366 case *adt.ImportReference:
367 w.string(openTuple + "import;")
368 w.label(x.ImportPath)
369 w.string(closeTuple)
370
371 case *adt.LetReference:
372 w.string(openTuple)
373 w.string(strconv.Itoa(int(x.UpCount)))
374 w.string(";let ")
375 w.label(x.Label)
376 w.string(closeTuple)
377
378 case *adt.SelectorExpr:
379 w.node(x.X)
380 w.string(".")
381 w.label(x.Sel)
382
383 case *adt.IndexExpr:
384 w.node(x.X)
385 w.string("[")
386 w.node(x.Index)
387 w.string("]")
388
389 case *adt.SliceExpr:
390 w.node(x.X)
391 w.string("[")
392 if x.Lo != nil {
393 w.node(x.Lo)
394 }
395 w.string(":")
396 if x.Hi != nil {
397 w.node(x.Hi)
398 }
399 if x.Stride != nil {
400 w.string(":")
401 w.node(x.Stride)
402 }
403 w.string("]")
404
405 case *adt.Interpolation:
Marcel van Lohuizen30ca0622020-08-22 14:07:59 +0200406 w.interpolation(x)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200407
408 case *adt.UnaryExpr:
409 fmt.Fprint(w, x.Op)
410 w.node(x.X)
411
412 case *adt.BinaryExpr:
413 w.string("(")
414 w.node(x.X)
415 fmt.Fprint(w, " ", x.Op, " ")
416 w.node(x.Y)
417 w.string(")")
418
419 case *adt.CallExpr:
420 w.node(x.Fun)
421 w.string("(")
422 for i, a := range x.Args {
423 if i > 0 {
424 w.string(", ")
425 }
426 w.node(a)
427 }
428 w.string(")")
429
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +0200430 case *adt.Builtin:
431 if x.Package != 0 {
432 w.label(x.Package)
433 w.string(".")
434 }
435 w.string(x.Name)
436
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200437 case *adt.BuiltinValidator:
Marcel van Lohuizen0cfe4112020-06-27 15:01:34 +0200438 w.node(x.Builtin)
Marcel van Lohuizen1f42c812020-05-01 16:00:06 +0200439 w.string("(")
440 for i, a := range x.Args {
441 if i > 0 {
442 w.string(", ")
443 }
444 w.node(a)
445 }
446 w.string(")")
447
448 case *adt.DisjunctionExpr:
449 w.string("(")
450 for i, a := range x.Values {
451 if i > 0 {
452 w.string("|")
453 }
454 // Disjunct
455 if a.Default {
456 w.string("*")
457 }
458 w.node(a.Val)
459 }
460 w.string(")")
461
462 case *adt.Conjunction:
463 w.string("&(")
464 for i, c := range x.Values {
465 if i > 0 {
466 w.string(", ")
467 }
468 w.node(c)
469 }
470 w.string(")")
471
472 case *adt.Disjunction:
473 w.string("|(")
474 for i, c := range x.Values {
475 if i > 0 {
476 w.string(", ")
477 }
478 if i < x.NumDefaults {
479 w.string("*")
480 }
481 w.node(c)
482 }
483 w.string(")")
484
485 case *adt.ForClause:
486 w.string("for ")
487 w.label(x.Key)
488 w.string(", ")
489 w.label(x.Value)
490 w.string(" in ")
491 w.node(x.Src)
492 w.string(" ")
493 w.node(x.Dst)
494
495 case *adt.IfClause:
496 w.string("if ")
497 w.node(x.Condition)
498 w.string(" ")
499 w.node(x.Dst)
500
501 case *adt.LetClause:
502 w.string("let ")
503 w.label(x.Label)
504 w.string(" = ")
505 w.node(x.Expr)
506 w.string(" ")
507 w.node(x.Dst)
508
509 case *adt.ValueClause:
510 w.node(x.StructLit)
511
512 default:
513 panic(fmt.Sprintf("unknown type %T", x))
514 }
515}