bash - format_print.go
1 package bash
2
3 import (
4 "strings"
5
6 "vimagination.zapto.org/parser"
7 )
8
9 func (a ArithmeticExpansion) printSource(w writer, v bool) {
10 if a.Expression {
11 w.WriteString("((")
12 } else {
13 w.WriteString("$((")
14 }
15
16 if len(a.WordsAndOperators) > 0 {
17 if v {
18 w.WriteString(" ")
19 }
20
21 a.WordsAndOperators[0].printSource(w, v)
22
23 for _, wo := range a.WordsAndOperators[1:] {
24 if v && !wo.operatorIsToken(parser.Token{Type: TokenPunctuator, Data: ";"}) {
25 w.WriteString(" ")
26 }
27
28 wo.printSource(w, v)
29 }
30
31 if v {
32 w.WriteString(" ")
33 }
34 }
35
36 w.WriteString("))")
37 }
38
39 func (a ArrayWord) printSource(w writer, v bool) {
40 if len(a.Comments[0]) > 0 {
41 w.WriteString("\n")
42 a.Comments[0].printSource(w, true)
43 }
44
45 a.Word.printSource(w, v)
46
47 if len(a.Comments[1]) > 0 {
48 w.WriteString(" ")
49 a.Comments[1].printSource(w, false)
50 }
51 }
52
53 func (a Assignment) printSource(w writer, v bool) {
54 if a.Assignment == AssignmentAssign || a.Assignment == AssignmentAppend {
55 a.Identifier.printSource(w, v)
56 a.Assignment.printSource(w, v)
57
58 if a.Value != nil {
59 a.Value.printSource(w, v)
60 } else {
61 parens := 0
62
63 for _, e := range a.Expression {
64 if parens > 0 {
65 w.WriteString(" ")
66 }
67
68 e.printSource(w, v)
69
70 if v && e.Operator != nil {
71 if e.Operator.Token == (parser.Token{Type: TokenPunctuator, Data: "("}) {
72 parens++
73 } else if e.Operator.Token == (parser.Token{Type: TokenPunctuator, Data: ")"}) {
74 parens--
75 }
76 }
77 }
78 }
79 }
80 }
81
82 func (a AssignmentOrWord) printSource(w writer, v bool) {
83 if a.Assignment != nil {
84 a.Assignment.printSource(w, v)
85 } else if a.Word != nil {
86 a.Word.printSource(w, v)
87 }
88 }
89
90 func (c CaseCompound) printSource(w writer, v bool) {
91 w.WriteString("case ")
92 c.Word.printSource(w, v)
93
94 if len(c.Comments[0]) > 0 {
95 w.WriteString(" ")
96 c.Comments[0].printSource(w, false)
97 w.WriteString("\nin")
98 } else {
99 w.WriteString(" in")
100 }
101
102 if len(c.Comments[1]) > 0 {
103 w.WriteString(" ")
104 c.Comments[1].printSource(w, false)
105 }
106
107 for _, m := range c.Matches {
108 w.WriteString("\n")
109 m.printSource(w, v)
110 }
111
112 if len(c.Comments[2]) > 0 {
113 w.WriteString("\n")
114 c.Comments[2].printSource(w, false)
115 }
116
117 w.WriteString("\nesac")
118 }
119
120 func (c Command) printSource(w writer, v bool) {
121 if len(c.Vars) > 0 {
122 c.Vars[0].printSource(w, v)
123
124 for _, vr := range c.Vars[1:] {
125 w.WriteString(" ")
126 vr.printSource(w, v)
127 }
128 }
129
130 if len(c.AssignmentsOrWords) > 0 {
131 if len(c.Vars) > 0 {
132 w.WriteString(" ")
133 }
134
135 c.AssignmentsOrWords[0].printSource(w, v)
136
137 for _, wd := range c.AssignmentsOrWords[1:] {
138 w.WriteString(" ")
139 wd.printSource(w, v)
140 }
141 }
142
143 if len(c.Redirections) > 0 {
144 if len(c.Vars) > 0 || len(c.AssignmentsOrWords) > 0 {
145 w.WriteString(" ")
146 }
147
148 c.Redirections[0].printSource(w, v)
149
150 for _, r := range c.Redirections[1:] {
151 w.WriteString(" ")
152 r.printSource(w, v)
153 }
154 }
155 }
156
157 func (c Command) printHeredoc(w writer, v bool) {
158 for _, r := range c.Redirections {
159 r.printHeredoc(w, v)
160 }
161 }
162
163 func (c CommandOrCompound) printSource(w writer, v bool) {
164 if c.Command != nil {
165 c.Command.printSource(w, v)
166 } else if c.Compound != nil {
167 c.Compound.printSource(w, v)
168 }
169 }
170
171 func (c CommandOrCompound) printHeredoc(w writer, v bool) {
172 if c.Command != nil {
173 c.Command.printHeredoc(w, v)
174 } else if c.Compound != nil {
175 c.Compound.printHeredoc(w, v)
176 }
177 }
178
179 func (c CommandSubstitution) printSource(w writer, v bool) {
180 w.WriteString("$(")
181
182 if c.Command.isMultiline(v) {
183 ip := indentPrinter{Writer: w}
184
185 ip.WriteString("\n")
186 c.Command.printSource(&ip, v)
187 w.WriteString("\n")
188 } else {
189 c.Command.printSourceEnd(w, v, false)
190 }
191
192 w.WriteString(")")
193 }
194
195 func (c Compound) printSource(w writer, v bool) {
196 if c.IfCompound != nil {
197 c.IfCompound.printSource(w, v)
198 } else if c.CaseCompound != nil {
199 c.CaseCompound.printSource(w, v)
200 } else if c.LoopCompound != nil {
201 c.LoopCompound.printSource(w, v)
202 } else if c.ForCompound != nil {
203 c.ForCompound.printSource(w, v)
204 } else if c.SelectCompound != nil {
205 c.SelectCompound.printSource(w, v)
206 } else if c.GroupingCompound != nil {
207 c.GroupingCompound.printSource(w, v)
208 } else if c.TestCompound != nil {
209 c.TestCompound.printSource(w, v)
210 } else if c.ArithmeticCompound != nil {
211 c.ArithmeticCompound.printSource(w, v)
212 } else if c.FunctionCompound != nil {
213 c.FunctionCompound.printSource(w, v)
214 }
215
216 for _, r := range c.Redirections {
217 w.WriteString(" ")
218 r.printSource(w, v)
219 }
220 }
221
222 func (c Compound) printHeredoc(w writer, v bool) {
223 for _, r := range c.Redirections {
224 r.printHeredoc(w, v)
225 }
226 }
227
228 func (f File) printSource(w writer, v bool) {
229 f.printSourceEnd(w, v, true)
230 }
231
232 func (f File) printSourceEnd(w writer, v, end bool) {
233 f.Comments[0].printSource(w, true)
234
235 if len(f.Lines) > 0 {
236 f.Lines[0].printSourceEnd(w, v, end || len(f.Lines) > 1)
237
238 lastLine := lastTokenPos(f.Lines[0].Tokens)
239
240 for n, l := range f.Lines[1:] {
241 if firstTokenPos(l.Tokens) > lastLine+1 {
242 w.WriteString("\n")
243 }
244
245 w.WriteString("\n")
246 l.printSourceEnd(w, v, end || len(f.Lines) > n+1)
247
248 lastLine = lastTokenPos(l.Tokens)
249 }
250 }
251
252 if len(f.Comments[1]) > 0 {
253 w.WriteString("\n\n")
254 f.Comments[1].printSource(w, false)
255 }
256 }
257
258 func firstTokenPos(tk Tokens) (pos uint64) {
259 if len(tk) > 0 {
260 pos = tk[0].Line
261 }
262
263 return pos
264 }
265
266 func lastTokenPos(tk Tokens) (pos uint64) {
267 if len(tk) > 0 {
268 pos = tk[len(tk)-1].Line
269 }
270
271 return pos
272 }
273
274 func (f ForCompound) printSource(w writer, v bool) {
275 if f.ArithmeticExpansion != nil || f.Identifier != nil {
276 w.WriteString("for ")
277
278 if f.ArithmeticExpansion != nil {
279 f.ArithmeticExpansion.printSource(w, v)
280 } else {
281 w.WriteString(f.Identifier.Data)
282
283 if f.Words != nil {
284 w.WriteString(" ")
285 f.Comments[0].printSource(w, true)
286 w.WriteString("in")
287
288 for _, wd := range f.Words {
289 w.WriteString(" ")
290 wd.printSource(w, v)
291 }
292 }
293 }
294
295 ip := indentPrinter{Writer: w}
296
297 ip.WriteString("; ")
298 f.Comments[1].printSource(&ip, true)
299 ip.WriteString("do\n")
300 f.File.printSource(&ip, v)
301 w.WriteString("\ndone")
302 }
303 }
304
305 func (f FunctionCompound) printSource(w writer, v bool) {
306 if f.Identifier != nil {
307 if f.HasKeyword {
308 w.WriteString("function ")
309 }
310
311 w.WriteString(f.Identifier.Data)
312 w.WriteString("() ")
313 f.Comments.printSource(w, true)
314 f.Body.printSource(w, v)
315 }
316 }
317
318 func (g GroupingCompound) printSource(w writer, v bool) {
319 if g.SubShell {
320 w.WriteString("(")
321 } else {
322 w.WriteString("{")
323 }
324
325 ip := indentPrinter{Writer: w}
326
327 multiline := v || g.File.isMultiline(v)
328
329 if len(g.File.Comments[0]) > 0 || !multiline {
330 w.WriteString(" ")
331 } else {
332 ip.WriteString("\n")
333 }
334
335 g.File.printSource(&ip, v)
336
337 if multiline {
338 w.WriteString("\n")
339 } else {
340 w.WriteString(" ")
341 }
342
343 if g.SubShell {
344 w.WriteString(")")
345 } else {
346 w.WriteString("}")
347 }
348 }
349
350 func (h Heredoc) printSource(w writer, v bool) {
351 w.WriteString("\n")
352
353 for _, p := range h.HeredocPartsOrWords {
354 p.printSource(w, v)
355 }
356 }
357
358 func (h HeredocPartOrWord) printSource(w writer, v bool) {
359 if h.HeredocPart != nil {
360 w.WriteString(h.HeredocPart.Data)
361 } else if h.Word != nil {
362 h.Word.printSource(w, v)
363 }
364 }
365
366 func (i IfCompound) printSource(w writer, v bool) {
367 w.WriteString("if ")
368 i.If.printSource(w, v)
369
370 for _, e := range i.ElIf {
371 w.WriteString("\nelif ")
372 e.printSource(w, v)
373 }
374
375 if i.Else != nil {
376 ip := indentPrinter{Writer: w}
377
378 w.WriteString("\nelse")
379 ip.WriteString("\n")
380 i.Else.printSource(&ip, v)
381 }
382
383 w.WriteString("\nfi")
384 }
385
386 func (l Line) printSource(w writer, v bool) {
387 l.printSourceEnd(w, v, true)
388 }
389
390 func (l Line) printSourceEnd(w writer, v, end bool) {
391 if len(l.Statements) > 0 {
392 l.Comments[0].printSource(w, true)
393 l.Statements[0].printSourceEnd(w, v, end || len(l.Statements) > 1)
394
395 eos := " "
396
397 if v {
398 eos = "\n"
399 }
400
401 for n, s := range l.Statements[1:] {
402 w.WriteString(eos)
403 s.printSourceEnd(w, v, end || len(l.Statements) > n+1)
404 }
405
406 if len(l.Comments[1]) > 0 {
407 w.WriteString(" ")
408 l.Comments[1].printSource(w, false)
409 }
410
411 for _, s := range l.Statements {
412 s.printHeredoc(w, v)
413 }
414 }
415 }
416
417 func (l LoopCompound) printSource(w writer, v bool) {
418 if l.Until {
419 w.WriteString("until ")
420 } else {
421 w.WriteString("while ")
422 }
423
424 l.Statement.printSource(w, v)
425
426 if len(l.Comments) > 0 {
427 w.WriteString(" ")
428 l.Comments.printSource(w, true)
429 w.WriteString("do")
430 } else {
431 w.WriteString(" do")
432 }
433
434 ip := indentPrinter{Writer: w}
435
436 ip.WriteString("\n")
437 l.File.printSource(&ip, v)
438 w.WriteString("\ndone")
439 }
440
441 func (p ParameterAssign) printSource(w writer, v bool) {
442 if p.Identifier != nil {
443 w.WriteString(p.Identifier.Data)
444
445 if len(p.Subscript) > 0 {
446 w.WriteString("[")
447 p.Subscript[0].printSource(w, v)
448
449 for _, s := range p.Subscript[1:] {
450 if v {
451 w.WriteString(" ")
452 }
453
454 s.printSource(w, v)
455 }
456
457 w.WriteString("]")
458 }
459 }
460 }
461
462 func (p ParameterExpansion) printSource(w writer, v bool) {
463 w.WriteString("${")
464
465 if p.Indirect || p.Type == ParameterPrefix || p.Type == ParameterPrefixSeperate {
466 w.WriteString("!")
467 } else if p.Type == ParameterLength {
468 w.WriteString("#")
469 }
470
471 p.Parameter.printSource(w, v)
472
473 if p.Word != nil {
474 switch p.Type {
475 case ParameterSubstitution:
476 w.WriteString(":=")
477 p.Word.printSource(w, v)
478 case ParameterAssignment:
479 w.WriteString(":?")
480 p.Word.printSource(w, v)
481 case ParameterMessage:
482 w.WriteString(":+")
483 p.Word.printSource(w, v)
484 case ParameterSetAssign:
485 w.WriteString(":-")
486 p.Word.printSource(w, v)
487 case ParameterUnsetSubstitution:
488 w.WriteString("=")
489 p.Word.printSource(w, v)
490 case ParameterUnsetAssignment:
491 w.WriteString("?")
492 p.Word.printSource(w, v)
493 case ParameterUnsetMessage:
494 w.WriteString("+")
495 p.Word.printSource(w, v)
496 case ParameterUnsetSetAssign:
497 w.WriteString("-")
498 p.Word.printSource(w, v)
499 case ParameterRemoveStartShortest:
500 w.WriteString("#")
501 p.Word.printSource(w, v)
502 case ParameterRemoveStartLongest:
503 w.WriteString("##")
504 p.Word.printSource(w, v)
505 case ParameterRemoveEndShortest:
506 w.WriteString("%")
507 p.Word.printSource(w, v)
508 case ParameterRemoveEndLongest:
509 w.WriteString("%%")
510 p.Word.printSource(w, v)
511 }
512 } else if p.Pattern != nil {
513 isReplacement := false
514
515 switch p.Type {
516 case ParameterReplace:
517 isReplacement = true
518
519 w.WriteString("/")
520 w.WriteString(p.Pattern.Data)
521 case ParameterReplaceAll:
522 isReplacement = true
523
524 w.WriteString("//")
525 w.WriteString(p.Pattern.Data)
526 case ParameterReplaceStart:
527 isReplacement = true
528
529 w.WriteString("/#")
530 w.WriteString(p.Pattern.Data)
531 case ParameterReplaceEnd:
532 isReplacement = true
533
534 w.WriteString("/%")
535 w.WriteString(p.Pattern.Data)
536 case ParameterLowercaseFirstMatch:
537 w.WriteString(",")
538 w.WriteString(p.Pattern.Data)
539 case ParameterLowercaseAllMatches:
540 w.WriteString(",,")
541 w.WriteString(p.Pattern.Data)
542 case ParameterUppercaseFirstMatch:
543 w.WriteString("^")
544 w.WriteString(p.Pattern.Data)
545 case ParameterUppercaseAllMatches:
546 w.WriteString("^^")
547 w.WriteString(p.Pattern.Data)
548 }
549
550 if isReplacement && p.String != nil {
551 w.WriteString("/")
552 p.String.printSource(w, v)
553 }
554 } else if !p.Indirect {
555 switch p.Type {
556 case ParameterPrefix:
557 w.WriteString("*")
558 case ParameterPrefixSeperate:
559 w.WriteString("@")
560 }
561 }
562
563 switch p.Type {
564 case ParameterSubstring:
565 if p.SubstringStart != nil {
566 w.WriteString(":")
567
568 if strings.HasPrefix(p.SubstringStart.Data, "-") {
569 w.WriteString(" ")
570 }
571
572 w.WriteString(p.SubstringStart.Data)
573
574 if p.SubstringEnd != nil {
575 w.WriteString(":")
576 w.WriteString(p.SubstringEnd.Data)
577 }
578 }
579 case ParameterUppercase:
580 w.WriteString("@U")
581 case ParameterUppercaseFirst:
582 w.WriteString("@u")
583 case ParameterLowercase:
584 w.WriteString("@L")
585 case ParameterQuoted:
586 w.WriteString("@Q")
587 case ParameterEscaped:
588 w.WriteString("@E")
589 case ParameterPrompt:
590 w.WriteString("@P")
591 case ParameterDeclare:
592 w.WriteString("@A")
593 case ParameterQuotedArrays:
594 w.WriteString("@K")
595 case ParameterAttributes:
596 w.WriteString("@a")
597 case ParameterQuotedArraysSeperate:
598 w.WriteString("@k")
599 }
600
601 w.WriteString("}")
602 }
603
604 func (p Parameter) printSource(w writer, v bool) {
605 if p.Parameter != nil {
606 w.WriteString(p.Parameter.Data)
607
608 if p.Array != nil {
609 w.WriteString("[")
610
611 for _, a := range p.Array {
612 a.printSource(w, v)
613 }
614
615 w.WriteString("]")
616 }
617 }
618 }
619
620 func (p Pattern) printSource(w writer, v bool) {
621 for _, word := range p.Parts {
622 word.printSource(w, v)
623 }
624 }
625
626 func (p PatternLines) printSource(w writer, v bool) {
627 if len(p.Patterns) > 0 {
628 p.Comments.printSource(w, true)
629
630 p.Patterns[0].printSource(w, v)
631
632 for _, pattern := range p.Patterns[1:] {
633 w.WriteString("|")
634 pattern.printSource(w, v)
635 }
636
637 ip := indentPrinter{Writer: w}
638
639 w.WriteString(")")
640 ip.WriteString("\n")
641
642 if len(p.Lines.Lines) > 0 {
643 p.Lines.printSource(&ip, v)
644 } else {
645 w.WriteString(";")
646 }
647
648 p.CaseTerminationType.printSource(w, v)
649 }
650 }
651
652 func (p Pipeline) printSource(w writer, v bool) {
653 p.PipelineTime.printSource(w, v)
654
655 if p.Not {
656 w.WriteString("! ")
657 }
658
659 if p.Coproc {
660 w.WriteString("coproc ")
661
662 if p.CoprocIdentifier != nil {
663 w.WriteString(p.CoprocIdentifier.Data)
664 w.WriteString(" ")
665 }
666 }
667
668 p.CommandOrCompound.printSource(w, v)
669
670 if p.Pipeline != nil {
671 w.WriteString(" | ")
672 p.Pipeline.printSource(w, v)
673 }
674 }
675
676 func (p Pipeline) endsWithGrouping() bool {
677 return p.CommandOrCompound.Compound != nil && len(p.CommandOrCompound.Compound.Redirections) == 0 && (p.CommandOrCompound.Compound.GroupingCompound != nil || p.CommandOrCompound.Compound.FunctionCompound != nil && p.CommandOrCompound.Compound.FunctionCompound.Body.GroupingCompound != nil)
678 }
679
680 func (p Pipeline) printHeredoc(w writer, v bool) {
681 p.CommandOrCompound.printHeredoc(w, v)
682
683 if p.Pipeline != nil {
684 p.Pipeline.printHeredoc(w, v)
685 }
686 }
687
688 func (r Redirection) printSource(w writer, v bool) {
689 if r.Redirector != nil {
690 if r.Input != nil {
691 w.WriteString(r.Input.Data)
692 }
693
694 w.WriteString(r.Redirector.Data)
695 r.Output.printSource(w, v)
696 }
697 }
698
699 func (r Redirection) printHeredoc(w writer, v bool) {
700 if r.Redirector != nil && r.Heredoc != nil && (r.Redirector.Data == "<<" || r.Redirector.Data == "<<-") {
701 if r.Redirector.Data == "<<" {
702 w = w.Underlying()
703 }
704
705 r.Heredoc.printSource(w, v)
706 r.Output.printSource(w, v)
707 }
708 }
709
710 func (s SelectCompound) printSource(w writer, v bool) {
711 if s.Identifier != nil {
712 w.WriteString("select ")
713 w.WriteString(s.Identifier.Data)
714
715 if s.Words != nil {
716 w.WriteString(" ")
717 s.Comments[0].printSource(w, true)
718 w.WriteString("in")
719
720 for _, wd := range s.Words {
721 w.WriteString(" ")
722 wd.printSource(w, v)
723 }
724 }
725
726 ip := indentPrinter{Writer: w}
727
728 ip.WriteString("; ")
729 s.Comments[1].printSource(w, true)
730 ip.WriteString("do\n")
731 s.File.printSource(&ip, v)
732 w.WriteString("\ndone")
733 }
734 }
735
736 func (s Statement) printSource(w writer, v bool) {
737 s.printSourceEnd(w, v, true)
738 }
739
740 func (s Statement) printSourceEnd(w writer, v, end bool) {
741 s.Pipeline.printSource(w, v)
742
743 if (s.LogicalOperator == LogicalOperatorAnd || s.LogicalOperator == LogicalOperatorOr) && s.Statement != nil {
744 s.LogicalOperator.printSource(w, v)
745 s.Statement.printSourceEnd(w, v, false)
746 }
747
748 if s.JobControl == JobControlBackground {
749 if v {
750 w.WriteString(" &")
751 } else {
752 w.WriteString("&")
753 }
754 } else if end && !s.endsWithGrouping() {
755 w.WriteString(";")
756 }
757 }
758
759 func (s Statement) endsWithGrouping() bool {
760 if s.Statement != nil {
761 return s.Statement.endsWithGrouping()
762 }
763
764 return s.Pipeline.endsWithGrouping()
765 }
766
767 func (s Statement) printHeredoc(w writer, v bool) {
768 s.Pipeline.printHeredoc(w, v)
769
770 if s.Statement != nil {
771 s.Statement.printHeredoc(w, v)
772 }
773 }
774
775 func (s String) printSource(w writer, v bool) {
776 for _, p := range s.WordsOrTokens {
777 p.printSource(w, v)
778 }
779 }
780
781 func (t TestCompound) printSource(w writer, v bool) {
782 w.WriteString("[[")
783
784 iw := w
785 multi := t.isMultiline(v)
786
787 if multi {
788 iw = &indentPrinter{Writer: w}
789 multi = true
790
791 if len(t.Comments[0]) > 0 {
792 w.WriteString(" ")
793 t.Comments[0].printSource(w, false)
794 }
795
796 iw.WriteString("\n")
797 } else {
798 w.WriteString(" ")
799 }
800
801 t.Tests.printSource(iw, v)
802
803 if len(t.Comments[1]) > 0 {
804 w.WriteString("\n")
805 t.Comments[1].printSource(w, true)
806 } else if multi {
807 w.WriteString("\n")
808 } else {
809 w.WriteString(" ")
810 }
811
812 w.WriteString("]]")
813 }
814
815 func (t Tests) printSource(w writer, v bool) {
816 t.Comments[0].printSource(w, true)
817
818 if t.Not {
819 w.WriteString("! ")
820
821 t.Comments[1].printSource(w, true)
822 }
823
824 if t.Parens != nil {
825 w.WriteString("(")
826
827 multi := t.Parens.isMultiline(v) || len(t.Comments[2]) > 0 || len(t.Comments[3]) > 0
828 iw := w
829
830 if multi {
831 iw = &indentPrinter{Writer: w}
832
833 if len(t.Comments[2]) > 0 {
834 w.WriteString(" ")
835 t.Comments[2].printSource(w, false)
836 }
837
838 iw.WriteString("\n")
839 } else {
840 w.WriteString(" ")
841 }
842
843 t.Parens.printSource(iw, v)
844
845 if multi {
846 w.WriteString("\n")
847 } else {
848 w.WriteString(" ")
849 }
850
851 t.Comments[3].printSource(w, true)
852 w.WriteString(")")
853 } else if t.Word != nil && t.Test == TestOperatorNone {
854 t.Word.printSource(w, v)
855 } else if t.Word != nil && t.Pattern != nil && t.Test >= TestOperatorStringsEqual {
856 t.Word.printSource(w, v)
857 w.WriteString(" ")
858 t.Test.printSource(w, v)
859 w.WriteString(" ")
860 t.Comments[2].printSource(w, true)
861 t.Pattern.printSource(w, v)
862 } else if t.Word != nil && t.Test >= TestOperatorFileExists && t.Test <= TestOperatorVarnameIsRef {
863 t.Test.printSource(w, v)
864 w.WriteString(" ")
865 t.Comments[2].printSource(w, true)
866 t.Word.printSource(w, v)
867 } else {
868 return
869 }
870
871 if t.Tests != nil && (t.LogicalOperator == LogicalOperatorOr || t.LogicalOperator == LogicalOperatorAnd) {
872 w.WriteString(" ")
873 t.Comments[4].printSource(w, true)
874 t.LogicalOperator.printSource(w, v)
875 t.Tests.printSource(w, v)
876 } else if len(t.Comments[4]) > 0 {
877 w.WriteString(" ")
878 t.Comments[4].printSource(w, false)
879 }
880 }
881
882 func (t TestConsequence) printSource(w writer, v bool) {
883 t.Test.printSource(w, v)
884
885 ip := indentPrinter{Writer: w}
886
887 if len(t.Comments) > 0 {
888 w.WriteString(" ")
889 t.Comments.printSource(&ip, true)
890 ip.WriteString("then\n")
891 } else {
892 ip.WriteString(" then\n")
893 }
894
895 t.Consequence.printSource(&ip, v)
896 }
897
898 func (ve Value) printSource(w writer, v bool) {
899 if ve.Word != nil {
900 ve.Word.printSource(w, v)
901 } else if ve.Array != nil {
902 ip := indentPrinter{Writer: w}
903
904 if len(ve.Comments[0]) > 0 {
905 w.WriteString("(")
906 ve.Comments[0].printSource(&ip, true)
907 } else {
908 w.WriteString("(")
909 }
910
911 var lastHadComment bool
912
913 if len(ve.Array) > 0 {
914
915 for _, word := range ve.Array {
916 if lastHadComment {
917 ip.WriteString("\n")
918 } else if v && len(word.Comments[0]) == 0 {
919 w.WriteString(" ")
920 }
921
922 word.printSource(&ip, v)
923
924 lastHadComment = len(word.Comments[1]) != 0
925 }
926
927 if v && !lastHadComment {
928 w.WriteString(" ")
929 }
930 }
931
932 if len(ve.Comments[1]) != 0 {
933 ve.Comments[1].printSource(&ip, false)
934 lastHadComment = true
935 }
936
937 if lastHadComment {
938 w.WriteString("\n")
939 }
940
941 w.WriteString(")")
942 }
943 }
944
945 func (wo WordOrOperator) printSource(w writer, v bool) {
946 if wo.Operator != nil {
947 w.WriteString(wo.Operator.Data)
948 } else if wo.Word != nil {
949 wo.Word.printSource(w, v)
950 }
951 }
952
953 func (wt WordOrToken) printSource(w writer, v bool) {
954 if wt.Word != nil {
955 wt.Word.printSource(w, v)
956 } else if wt.Token != nil {
957 w.WriteString(wt.Token.Data)
958 }
959 }
960
961 func (wp WordPart) printSource(w writer, v bool) {
962 if wp.Part != nil {
963 w.WriteString(wp.Part.Data)
964 } else if wp.ArithmeticExpansion != nil {
965 wp.ArithmeticExpansion.printSource(w, v)
966 } else if wp.CommandSubstitution != nil {
967 wp.CommandSubstitution.printSource(w, v)
968 } else if wp.ParameterExpansion != nil {
969 wp.ParameterExpansion.printSource(w, v)
970 }
971 }
972
973 func (wd Word) printSource(w writer, v bool) {
974 for _, word := range wd.Parts {
975 word.printSource(w, v)
976 }
977 }
978