blob: 85035a5e9e2d80011ff9e54844c1beb70639e52e [file] [log] [blame]
Marcel van Lohuizenb001da52018-12-10 15:34:30 +01001// Copyright 2018 The 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
15package format
16
17import (
18 "fmt"
19 "strconv"
20 "strings"
21
22 "cuelang.org/go/cue/ast"
23 "cuelang.org/go/cue/parser"
24 "cuelang.org/go/cue/token"
25)
26
27func printNode(node interface{}, f *printer) error {
28 s := newFormatter(f)
29
30 // format node
31 f.allowed = nooverride // gobble initial whitespace.
32 switch x := node.(type) {
33 case *ast.File:
34 s.file(x)
35 case ast.Expr:
36 s.expr(x)
37 case ast.Decl:
38 s.decl(x)
39 // case ast.Node: // TODO: do we need this?
40 // s.walk(x)
41 case []ast.Decl:
42 s.walkDeclList(x)
43 default:
44 goto unsupported
45 }
46
47 return nil
48
49unsupported:
50 return fmt.Errorf("cue/format: unsupported node type %T", node)
51}
52
53// Helper functions for common node lists. They may be empty.
54
55func (f *formatter) walkDeclList(list []ast.Decl) {
56 f.before(nil)
57 for i, x := range list {
58 if i > 0 {
59 f.print(declcomma)
60 }
61 f.decl(x)
62 f.print(f.current.parentSep)
63 }
64 f.after(nil)
65}
66
67func (f *formatter) walkSpecList(list []*ast.ImportSpec) {
68 f.before(nil)
69 for _, x := range list {
70 f.importSpec(x)
71 }
72 f.after(nil)
73}
74
75func (f *formatter) walkClauseList(list []ast.Clause) {
76 f.before(nil)
77 for _, x := range list {
78 f.clause(x)
79 }
80 f.after(nil)
81}
82
83func (f *formatter) walkExprList(list []ast.Expr, depth int) {
84 f.before(nil)
85 for _, x := range list {
86 f.before(x)
87 f.exprRaw(x, token.LowestPrec, depth)
88 f.print(comma, blank)
89 f.after(x)
90 }
91 f.after(nil)
92}
93
94func (f *formatter) file(file *ast.File) {
95 f.before(file)
96 if file.Name != nil {
97 f.print(file.Package, "package")
98 f.print(blank, file.Name, newline, newsection)
99 }
100 f.current.pos = 3
101 f.visitComments(3)
102 f.walkDeclList(file.Decls)
103 f.after(file)
104 f.print(token.EOF)
105}
106func (f *formatter) decl(decl ast.Decl) {
107 if decl == nil {
108 return
109 }
110 if !f.before(decl) {
111 goto after
112 }
113 switch n := decl.(type) {
114 case *ast.Field:
115 // shortcut single-element structs.
116 lastSize := len(f.labelBuf)
117 f.labelBuf = f.labelBuf[:0]
118 first := n.Label
119 for {
120 obj, ok := n.Value.(*ast.StructLit)
121 if !ok || len(obj.Elts) != 1 || (obj.Lbrace.IsValid() && !f.printer.cfg.simplify) {
122 break
123 }
124
125 // Verify that struct doesn't have inside comments and that
126 // element doesn't have doc comments.
127 hasComments := len(obj.Elts[0].Comments()) > 0
128 for _, c := range obj.Comments() {
129 if c.Position == 1 || c.Position == 2 {
130 hasComments = true
131 }
132 }
133 if hasComments {
134 break
135 }
136
137 mem, ok := obj.Elts[0].(*ast.Field)
138 if !ok {
139 break
140 }
141 f.labelBuf = append(f.labelBuf, mem.Label)
142 n = mem
143 }
144
145 if lastSize != len(f.labelBuf) {
146 f.print(formfeed)
147 }
148
149 f.before(nil)
150 f.label(first)
151 for _, x := range f.labelBuf {
152 f.print(blank, nooverride)
153 f.label(x)
154 }
155 f.after(nil)
156
157 nextFF := f.nextNeedsFormfeed(n.Value)
158 tab := vtab
Marcel van Lohuizen15fab6c2018-12-19 08:51:47 +0100159 if nextFF {
Marcel van Lohuizenb001da52018-12-10 15:34:30 +0100160 tab = blank
161 }
162
163 f.print(n.Colon, token.COLON, tab)
164 if n.Value != nil {
165 f.expr(n.Value)
166 } else {
167 f.current.pos++
168 f.visitComments(f.current.pos)
169 }
170
171 if nextFF {
172 f.print(formfeed)
173 }
174
175 case *ast.ComprehensionDecl:
176 f.decl(n.Field)
177 f.print(blank)
178 if n.Select != token.NoPos {
179 f.print(n.Select, token.ARROW, blank)
180 }
181 f.print(indent)
182 f.walkClauseList(n.Clauses)
183 f.print(unindent)
184 f.print("") // force whitespace to be written
185
186 case *ast.BadDecl:
187 f.print(n.From, "*bad decl*", declcomma)
188
189 case *ast.ImportDecl:
190 f.print(n.Import, "import")
191 if len(n.Specs) == 0 {
192 f.print(blank, n.Lparen, token.LPAREN, n.Rparen, token.RPAREN, newline)
193 }
194 switch {
195 case len(n.Specs) == 1:
196 if !n.Lparen.IsValid() {
197 f.print(blank)
198 f.walkSpecList(n.Specs)
199 break
200 }
201 fallthrough
202 default:
203 f.print(blank, n.Lparen, token.LPAREN, newline, indent)
204 f.walkSpecList(n.Specs)
205 f.print(unindent, newline, n.Rparen, token.RPAREN, newline)
206 }
207 f.print(newsection)
208
209 case *ast.EmitDecl:
210 f.expr(n.Expr)
211 f.print(newline, newsection) // force newline
212
213 case *ast.Alias:
214 f.expr(n.Ident)
215 f.print(blank, n.Equal, token.BIND, blank)
216 f.expr(n.Expr)
217 f.print(declcomma, newline) // implied
218 }
219after:
220 f.after(decl)
221}
222
223func (f *formatter) nextNeedsFormfeed(n ast.Expr) bool {
224 switch x := n.(type) {
225 case *ast.StructLit:
226 return true
227 case *ast.BasicLit:
228 return strings.IndexByte(x.Value, '\n') >= 0
229 case *ast.ListLit:
230 return true
231 }
232 return false
233}
234
235func (f *formatter) importSpec(x *ast.ImportSpec) {
236 if x.Name != nil {
237 f.label(x.Name)
238 f.print(blank)
239 } else {
240 f.current.pos++
241 f.visitComments(f.current.pos)
242 }
243 f.expr(x.Path)
244 f.print(newline)
245}
246
247func (f *formatter) label(l ast.Label) {
248 switch n := l.(type) {
249 case *ast.Ident:
250 f.print(n.NamePos, n)
251
252 case *ast.BasicLit:
253 if f.cfg.simplify && n.Kind == token.STRING && len(n.Value) > 2 {
254 s := n.Value
255 unquoted, err := strconv.Unquote(s)
256 fset := token.NewFileSet()
257 if err == nil {
258 e, _ := parser.ParseExpr(fset, "check", unquoted)
259 if _, ok := e.(*ast.Ident); ok {
260 f.print(n.ValuePos, unquoted)
261 break
262 }
263 }
264 }
265 f.print(n.ValuePos, n.Value)
266
267 case *ast.TemplateLabel:
268 f.print(n.Langle, token.LSS, indent)
269 f.label(n.Ident)
270 f.print(unindent, n.Rangle, token.GTR)
271
272 case *ast.Interpolation:
273 f.expr(n)
274
Marcel van Lohuizenb001da52018-12-10 15:34:30 +0100275 default:
276 panic(fmt.Sprintf("unknown label type %T", n))
277 }
278}
279
280func (f *formatter) expr(x ast.Expr) {
281 const depth = 1
282 f.expr1(x, token.LowestPrec, depth)
283}
284
285func (f *formatter) expr0(x ast.Expr, depth int) {
286 f.expr1(x, token.LowestPrec, depth)
287}
288
289func (f *formatter) expr1(expr ast.Expr, prec1, depth int) {
290 if f.before(expr) {
291 f.exprRaw(expr, prec1, depth)
292 }
293 f.after(expr)
294}
295
296func (f *formatter) exprRaw(expr ast.Expr, prec1, depth int) {
297
298 switch x := expr.(type) {
299 case *ast.BadExpr:
300 f.print(x.From, "BadExpr")
301
302 case *ast.BottomLit:
303 f.print(x.Bottom, token.BOTTOM)
304
305 case *ast.Ident:
306 f.print(x.NamePos, x)
307
308 case *ast.BinaryExpr:
309 if depth < 1 {
310 f.internalError("depth < 1:", depth)
311 depth = 1
312 }
Marcel van Lohuizenb001da52018-12-10 15:34:30 +0100313 f.binaryExpr(x, prec1, cutoff(x, depth), depth)
314
315 case *ast.UnaryExpr:
316 const prec = token.UnaryPrec
317 if prec < prec1 {
318 // parenthesis needed
319 f.print(token.LPAREN, nooverride)
320 f.expr(x)
321 f.print(token.RPAREN)
322 } else {
323 // no parenthesis needed
324 f.print(x.OpPos, x.Op, nooverride)
325 f.expr1(x.X, prec, depth)
326 }
327
328 case *ast.BasicLit:
329 f.print(x.ValuePos, x)
330
331 case *ast.Interpolation:
332 f.before(nil)
333 for _, x := range x.Elts {
334 f.expr0(x, depth+1)
335 }
336 f.after(nil)
337
338 case *ast.ParenExpr:
339 if _, hasParens := x.X.(*ast.ParenExpr); hasParens {
340 // don't print parentheses around an already parenthesized expression
341 // TODO: consider making this more general and incorporate precedence levels
342 f.expr0(x.X, depth)
343 } else {
344 f.print(x.Lparen, token.LPAREN)
345 f.expr0(x.X, reduceDepth(depth)) // parentheses undo one level of depth
346 f.print(x.Rparen, token.RPAREN)
347 }
348
349 case *ast.SelectorExpr:
350 f.selectorExpr(x, depth)
351
352 case *ast.IndexExpr:
353 f.expr1(x.X, token.HighestPrec, 1)
354 f.print(x.Lbrack, token.LBRACK)
355 f.expr0(x.Index, depth+1)
356 f.print(x.Rbrack, token.RBRACK)
357
358 case *ast.SliceExpr:
359 f.expr1(x.X, token.HighestPrec, 1)
360 f.print(x.Lbrack, token.LBRACK)
361 indices := []ast.Expr{x.Low, x.High}
362 for i, y := range indices {
363 if i > 0 {
364 // blanks around ":" if both sides exist and either side is a binary expression
365 x := indices[i-1]
366 if depth <= 1 && x != nil && y != nil && (isBinary(x) || isBinary(y)) {
367 f.print(blank, token.COLON, blank)
368 } else {
369 f.print(token.COLON)
370 }
371 }
372 if y != nil {
373 f.expr0(y, depth+1)
374 }
375 }
376 f.print(x.Rbrack, token.RBRACK)
377
378 case *ast.CallExpr:
379 if len(x.Args) > 1 {
380 depth++
381 }
382 wasIndented := f.possibleSelectorExpr(x.Fun, token.HighestPrec, depth)
383 f.print(x.Lparen, token.LPAREN)
384 f.walkExprList(x.Args, depth)
385 f.print(trailcomma, noblank, x.Rparen, token.RPAREN)
386 if wasIndented {
387 f.print(unindent)
388 }
389
390 case *ast.Ellipsis:
391 f.print(x.Ellipsis, token.ELLIPSIS)
392 if x.Elt != nil {
393 f.expr(x.Elt) // TODO
394 }
395
396 case *ast.StructLit:
397 f.print(x.Lbrace, token.LBRACE, noblank, f.formfeed(), indent)
398 f.walkDeclList(x.Elts)
399 f.matchUnindent()
400 f.print(noblank, x.Rbrace, token.RBRACE)
401
402 case *ast.ListLit:
403 f.print(x.Lbrack, token.LBRACK, indent)
404 f.walkExprList(x.Elts, 1)
405 if x.Ellipsis != token.NoPos || x.Type != nil {
406 f.print(x.Ellipsis, token.ELLIPSIS)
407 if x.Type != nil && !isTop(x.Type) {
408 f.expr(x.Type)
409 }
410 } else {
411 f.print(trailcomma, noblank)
412 f.current.pos += 2
413 f.visitComments(f.current.pos)
414 }
415 f.matchUnindent()
416 f.print(noblank, x.Rbrack, token.RBRACK)
417
418 case *ast.ListComprehension:
419 f.print(x.Lbrack, token.LBRACK, blank, indent)
420 f.expr(x.Expr)
421 f.print(blank)
422 f.walkClauseList(x.Clauses)
423 f.print(unindent, f.wsOverride(blank), x.Rbrack, token.RBRACK)
424
Marcel van Lohuizenb001da52018-12-10 15:34:30 +0100425 default:
426 panic(fmt.Sprintf("unimplemented type %T", x))
427 }
428 return
429}
430
431func (f *formatter) clause(clause ast.Clause) {
432 switch n := clause.(type) {
433 case *ast.ForClause:
434 f.print(blank, n.For, "for", blank)
435 if n.Key != nil {
436 f.label(n.Key)
437 f.print(n.Colon, token.COMMA, blank)
438 } else {
439 f.current.pos++
440 f.visitComments(f.current.pos)
441 }
442 f.label(n.Value)
443 f.print(blank, n.In, "in", blank)
444 f.expr(n.Source)
445
446 case *ast.IfClause:
447 f.print(blank, n.If, "if", blank)
448 f.expr(n.Condition)
449
450 default:
451 panic("unknown clause type")
452 }
453}
454
455func walkBinary(e *ast.BinaryExpr) (has6, has7, has8 bool, maxProblem int) {
456 switch e.Op.Precedence() {
457 case 6:
458 has6 = true
459 case 7:
460 has7 = true
461 case 8:
462 has8 = true
463 }
464
465 switch l := e.X.(type) {
466 case *ast.BinaryExpr:
467 if l.Op.Precedence() < e.Op.Precedence() {
468 // parens will be inserted.
469 // pretend this is an *syntax.ParenExpr and do nothing.
470 break
471 }
472 h6, h7, h8, mp := walkBinary(l)
473 has6 = has6 || h6
474 has7 = has7 || h7
475 has8 = has8 || h8
476 if maxProblem < mp {
477 maxProblem = mp
478 }
479 }
480
481 switch r := e.Y.(type) {
482 case *ast.BinaryExpr:
483 if r.Op.Precedence() <= e.Op.Precedence() {
484 // parens will be inserted.
485 // pretend this is an *syntax.ParenExpr and do nothing.
486 break
487 }
488 h6, h7, h8, mp := walkBinary(r)
489 has6 = has6 || h6
490 has7 = has7 || h7
491 has8 = has8 || h8
492 if maxProblem < mp {
493 maxProblem = mp
494 }
495
496 case *ast.UnaryExpr:
497 switch e.Op.String() + r.Op.String() {
498 case "/*":
499 maxProblem = 8
500 case "++", "--":
501 if maxProblem < 6 {
502 maxProblem = 6
503 }
504 }
505 }
506 return
507}
508
509func cutoff(e *ast.BinaryExpr, depth int) int {
510 has6, has7, has8, maxProblem := walkBinary(e)
511 if maxProblem > 0 {
512 return maxProblem + 1
513 }
514 if (has6 || has7) && has8 {
515 if depth == 1 {
516 return 8
517 }
518 if has7 {
519 return 7
520 }
521 return 6
522 }
523 if has6 && has7 {
524 if depth == 1 {
525 return 7
526 }
527 return 6
528 }
529 if depth == 1 {
530 return 8
531 }
532 return 6
533}
534
535func diffPrec(expr ast.Expr, prec int) int {
536 x, ok := expr.(*ast.BinaryExpr)
537 if !ok || prec != x.Op.Precedence() {
538 return 1
539 }
540 return 0
541}
542
543func reduceDepth(depth int) int {
544 depth--
545 if depth < 1 {
546 depth = 1
547 }
548 return depth
549}
550
551// Format the binary expression: decide the cutoff and then format.
552// Let's call depth == 1 Normal mode, and depth > 1 Compact mode.
553// (Algorithm suggestion by Russ Cox.)
554//
555// The precedences are:
Marcel van Lohuizenb001da52018-12-10 15:34:30 +0100556// 7 * / % quo rem div mod
557// 6 + -
558// 5 == != < <= > >=
559// 4 &&
560// 3 ||
561// 2 &
562// 1 |
563//
564// The only decision is whether there will be spaces around levels 6 and 7.
565// There are never spaces at level 8 (unary), and always spaces at levels 5 and below.
566//
567// To choose the cutoff, look at the whole expression but excluding primary
568// expressions (function calls, parenthesized exprs), and apply these rules:
569//
570// 1) If there is a binary operator with a right side unary operand
571// that would clash without a space, the cutoff must be (in order):
572//
573// /* 8
574// ++ 7 // not necessary, but to avoid confusion
575// -- 7
576//
577// (Comparison operators always have spaces around them.)
578//
579// 2) If there is a mix of level 7 and level 6 operators, then the cutoff
580// is 7 (use spaces to distinguish precedence) in Normal mode
581// and 6 (never use spaces) in Compact mode.
582//
583// 3) If there are no level 6 operators or no level 7 operators, then the
584// cutoff is 8 (always use spaces) in Normal mode
585// and 6 (never use spaces) in Compact mode.
586//
587func (f *formatter) binaryExpr(x *ast.BinaryExpr, prec1, cutoff, depth int) {
588 f.nestExpr++
589 defer func() { f.nestExpr-- }()
590
591 prec := x.Op.Precedence()
592 if prec < prec1 {
593 // parenthesis needed
594 // Note: The parser inserts an syntax.ParenExpr node; thus this case
595 // can only occur if the AST is created in a different way.
596 // defer p.pushComment(nil).pop()
597 f.print(token.LPAREN, nooverride)
598 f.expr0(x, reduceDepth(depth)) // parentheses undo one level of depth
599 f.print(token.RPAREN)
600 return
601 }
602
603 printBlank := prec < cutoff
604
605 ws := indent
606 f.expr1(x.X, prec, depth+diffPrec(x.X, prec))
607 f.print(nooverride)
608 if printBlank {
609 f.print(blank)
610 }
611 f.print(x.OpPos, x.Op)
612 if x.Y.Pos().IsNewline() {
613 // at least one line break, but respect an extra empty line
614 // in the source
615 f.print(formfeed)
616 printBlank = false // no blank after line break
617 } else {
618 f.print(nooverride)
619 }
620 if printBlank {
621 f.print(blank)
622 }
623 f.expr1(x.Y, prec+1, depth+1)
624 if ws == ignore {
625 f.print(unindent)
626 }
627}
628
629func isBinary(expr ast.Expr) bool {
630 _, ok := expr.(*ast.BinaryExpr)
631 return ok
632}
633
634func (f *formatter) possibleSelectorExpr(expr ast.Expr, prec1, depth int) bool {
635 if x, ok := expr.(*ast.SelectorExpr); ok {
636 return f.selectorExpr(x, depth)
637 }
638 f.expr1(expr, prec1, depth)
639 return false
640}
641
642// selectorExpr handles an *syntax.SelectorExpr node and returns whether x spans
643// multiple lines.
644func (f *formatter) selectorExpr(x *ast.SelectorExpr, depth int) bool {
645 f.expr1(x.X, token.HighestPrec, depth)
646 f.print(token.PERIOD)
647 if x.Sel.Pos().IsNewline() {
648 f.print(indent, formfeed, x.Sel.Pos(), x.Sel)
649 f.print(unindent)
650 return true
651 }
652 f.print(x.Sel.Pos(), x.Sel)
653 return false
654}
655
656func isTop(e ast.Expr) bool {
657 ident, ok := e.(*ast.Ident)
658 return ok && ident.Name == "_"
659}