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