xref: /aosp_15_r20/build/blueprint/parser/printer.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1// Copyright 2014 Google Inc. All rights reserved.
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 parser
16
17import (
18	"fmt"
19	"strconv"
20	"strings"
21	"text/scanner"
22	"unicode"
23)
24
25var noPos scanner.Position
26
27type printer struct {
28	defs     []Definition
29	comments []*CommentGroup
30
31	curComment int
32
33	pos scanner.Position
34
35	pendingSpace   bool
36	pendingNewline int
37
38	output []byte
39
40	indentList []int
41	wsBuf      []byte
42
43	skippedComments []*CommentGroup
44}
45
46func newPrinter(file *File) *printer {
47	return &printer{
48		defs:       file.Defs,
49		comments:   file.Comments,
50		indentList: []int{0},
51
52		// pendingNewLine is initialized to -1 to eat initial spaces if the first token is a comment
53		pendingNewline: -1,
54
55		pos: scanner.Position{
56			Line: 1,
57		},
58	}
59}
60
61func Print(file *File) ([]byte, error) {
62	p := newPrinter(file)
63
64	for _, def := range p.defs {
65		p.printDef(def)
66	}
67	p.flush()
68	return p.output, nil
69}
70
71func PrintExpression(expression Expression) ([]byte, error) {
72	dummyFile := &File{}
73	p := newPrinter(dummyFile)
74	p.printExpression(expression)
75	p.flush()
76	return p.output, nil
77}
78
79func (p *printer) Print() ([]byte, error) {
80	for _, def := range p.defs {
81		p.printDef(def)
82	}
83	p.flush()
84	return p.output, nil
85}
86
87func (p *printer) printDef(def Definition) {
88	if assignment, ok := def.(*Assignment); ok {
89		p.printAssignment(assignment)
90	} else if module, ok := def.(*Module); ok {
91		p.printModule(module)
92	} else {
93		panic("Unknown definition")
94	}
95}
96
97func (p *printer) printAssignment(assignment *Assignment) {
98	p.printToken(assignment.Name, assignment.NamePos)
99	p.requestSpace()
100	p.printToken(assignment.Assigner, assignment.EqualsPos)
101	p.requestSpace()
102	p.printExpression(assignment.Value)
103	p.requestNewline()
104}
105
106func (p *printer) printModule(module *Module) {
107	p.printToken(module.Type, module.TypePos)
108	p.printMap(&module.Map)
109	p.requestDoubleNewline()
110}
111
112func (p *printer) printExpression(value Expression) {
113	switch v := value.(type) {
114	case *Variable:
115		p.printToken(v.Name, v.NamePos)
116	case *Operator:
117		p.printOperator(v)
118	case *Bool:
119		var s string
120		if v.Value {
121			s = "true"
122		} else {
123			s = "false"
124		}
125		p.printToken(s, v.LiteralPos)
126	case *Int64:
127		p.printToken(strconv.FormatInt(v.Value, 10), v.LiteralPos)
128	case *String:
129		p.printToken(strconv.Quote(v.Value), v.LiteralPos)
130	case *List:
131		p.printList(v.Values, v.LBracePos, v.RBracePos)
132	case *Map:
133		p.printMap(v)
134	case *Select:
135		p.printSelect(v)
136	default:
137		panic(fmt.Errorf("bad property type: %v", value))
138	}
139}
140
141func (p *printer) printSelect(s *Select) {
142	print_append := func() {
143		if s.Append != nil {
144			p.requestSpace()
145			p.printToken("+", s.RBracePos)
146			p.requestSpace()
147			p.printExpression(s.Append)
148		}
149	}
150	if len(s.Cases) == 0 {
151		print_append()
152		return
153	}
154	if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 1 {
155		if str, ok := s.Cases[0].Patterns[0].Value.(*String); ok && str.Value == default_select_branch_name {
156			p.printExpression(s.Cases[0].Value)
157			p.pos = s.RBracePos
158			print_append()
159			return
160		}
161	}
162	if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 0 {
163		p.printExpression(s.Cases[0].Value)
164		p.pos = s.RBracePos
165		print_append()
166		return
167	}
168	p.requestSpace()
169	p.printToken("select(", s.KeywordPos)
170	multilineConditions := false
171	if len(s.Conditions) > 1 {
172		p.printToken("(", s.KeywordPos)
173		if s.Conditions[len(s.Conditions)-1].position.Line > s.KeywordPos.Line {
174			multilineConditions = true
175			p.requestNewline()
176			p.indent(p.curIndent() + 4)
177		}
178	}
179	for i, c := range s.Conditions {
180		p.printToken(c.FunctionName, c.position)
181		p.printToken("(", c.position)
182		for i, arg := range c.Args {
183			p.printToken(strconv.Quote(arg.Value), arg.LiteralPos)
184			if i < len(c.Args)-1 {
185				p.printToken(",", arg.LiteralPos)
186				p.requestSpace()
187			}
188		}
189		p.printToken(")", p.pos)
190		if len(s.Conditions) > 1 {
191			if multilineConditions {
192				p.printToken(",", p.pos)
193				p.requestNewline()
194			} else if i < len(s.Conditions)-1 {
195				p.printToken(",", p.pos)
196				p.requestSpace()
197			}
198		}
199	}
200	if len(s.Conditions) > 1 {
201		if multilineConditions {
202			p.unindent(p.pos)
203		}
204		p.printToken(")", p.pos)
205	}
206	p.printToken(", {", s.LBracePos)
207	p.requestNewline()
208	p.indent(p.curIndent() + 4)
209	for _, c := range s.Cases {
210		p.requestNewline()
211		if len(c.Patterns) > 1 {
212			p.printToken("(", p.pos)
213		}
214		for i, pat := range c.Patterns {
215			p.printSelectPattern(pat)
216			if i < len(c.Patterns)-1 {
217				p.printToken(",", p.pos)
218				p.requestSpace()
219			}
220		}
221		if len(c.Patterns) > 1 {
222			p.printToken(")", p.pos)
223		}
224		p.printToken(":", c.ColonPos)
225		p.requestSpace()
226		if unset, ok := c.Value.(*UnsetProperty); ok {
227			p.printToken(unset.String(), unset.Pos())
228		} else {
229			p.printExpression(c.Value)
230		}
231		p.printToken(",", c.Value.End())
232	}
233	p.requestNewline()
234	p.unindent(s.RBracePos)
235	p.printToken("})", s.RBracePos)
236	print_append()
237}
238
239func (p *printer) printSelectPattern(pat SelectPattern) {
240	switch pat := pat.Value.(type) {
241	case *String:
242		if pat.Value == default_select_branch_name {
243			p.printToken("default", pat.LiteralPos)
244		} else if pat.Value == any_select_branch_name {
245			p.printToken("any", pat.LiteralPos)
246		} else {
247			p.printToken(strconv.Quote(pat.Value), pat.LiteralPos)
248		}
249	case *Bool:
250		s := "false"
251		if pat.Value {
252			s = "true"
253		}
254		p.printToken(s, pat.LiteralPos)
255	default:
256		panic("Unhandled case")
257	}
258	if pat.Binding.Name != "" {
259		p.requestSpace()
260		p.printToken("@", pat.Binding.Pos())
261		p.requestSpace()
262		p.printExpression(&pat.Binding)
263	}
264}
265
266func (p *printer) printList(list []Expression, pos, endPos scanner.Position) {
267	p.requestSpace()
268	p.printToken("[", pos)
269	if len(list) > 1 || pos.Line != endPos.Line || listHasMap(list) {
270		p.requestNewline()
271		p.indent(p.curIndent() + 4)
272		for _, value := range list {
273			p.printExpression(value)
274			p.printToken(",", noPos)
275			p.requestNewline()
276		}
277		p.unindent(endPos)
278	} else {
279		for _, value := range list {
280			p.printExpression(value)
281		}
282	}
283	p.printToken("]", endPos)
284}
285
286func (p *printer) printMap(m *Map) {
287	p.requestSpace()
288	p.printToken("{", m.LBracePos)
289	if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line {
290		p.requestNewline()
291		p.indent(p.curIndent() + 4)
292		for _, prop := range m.Properties {
293			p.printProperty(prop)
294			p.printToken(",", noPos)
295			p.requestNewline()
296		}
297		p.unindent(m.RBracePos)
298	}
299	p.printToken("}", m.RBracePos)
300}
301
302func (p *printer) printOperator(operator *Operator) {
303	p.printOperatorInternal(operator, true)
304}
305
306func (p *printer) printOperatorInternal(operator *Operator, allowIndent bool) {
307	p.printExpression(operator.Args[0])
308	p.requestSpace()
309	p.printToken(string(operator.Operator), operator.OperatorPos)
310
311	indented := false
312	if operator.Args[0].End().Line == operator.Args[1].Pos().Line {
313		p.requestSpace()
314	} else {
315		if allowIndent {
316			indented = true
317			p.indent(p.curIndent() + 4)
318		}
319		p.requestNewline()
320	}
321
322	if op, isOp := operator.Args[1].(*Operator); isOp {
323		p.printOperatorInternal(op, false)
324	} else {
325		p.printExpression(operator.Args[1])
326	}
327
328	if indented {
329		p.unindent(p.pos)
330	}
331}
332
333func (p *printer) printProperty(property *Property) {
334	p.printToken(property.Name, property.NamePos)
335	p.printToken(":", property.ColonPos)
336	p.requestSpace()
337	p.printExpression(property.Value)
338}
339
340// Print a single token, including any necessary comments or whitespace between
341// this token and the previously printed token
342func (p *printer) printToken(s string, pos scanner.Position) {
343	newline := p.pendingNewline != 0
344
345	if pos == noPos {
346		pos = p.pos
347	}
348
349	if newline {
350		p.printEndOfLineCommentsBefore(pos)
351		p.requestNewlinesForPos(pos)
352	}
353
354	p.printInLineCommentsBefore(pos)
355
356	p.flushSpace()
357
358	p.output = append(p.output, s...)
359
360	p.pos = pos
361}
362
363// Print any in-line (single line /* */) comments that appear _before_ pos
364func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
365	for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset {
366		c := p.comments[p.curComment]
367		if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 {
368			p.skippedComments = append(p.skippedComments, c)
369		} else {
370			p.printComment(c)
371			p.requestSpace()
372		}
373		p.curComment++
374	}
375}
376
377// Print any comments, including end of line comments, that appear _before_ the line specified
378// by pos
379func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
380	if len(p.skippedComments) > 0 {
381		for _, c := range p.skippedComments {
382			p.printComment(c)
383			p._requestNewline()
384		}
385		p.skippedComments = nil
386	}
387	for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line {
388		c := p.comments[p.curComment]
389		p.printComment(c)
390		p._requestNewline()
391		p.curComment++
392	}
393}
394
395// Compare the line numbers of the previous and current positions to determine whether extra
396// newlines should be inserted.  A second newline is allowed anywhere requestNewline() is called.
397func (p *printer) requestNewlinesForPos(pos scanner.Position) bool {
398	if pos.Line > p.pos.Line {
399		p._requestNewline()
400		if pos.Line > p.pos.Line+1 {
401			p.pendingNewline = 2
402		}
403		return true
404	}
405
406	return false
407}
408
409func (p *printer) requestSpace() {
410	p.pendingSpace = true
411}
412
413// Ask for a newline to be inserted before the next token, but do not insert any comments.  Used
414// by the comment printers.
415func (p *printer) _requestNewline() {
416	if p.pendingNewline == 0 {
417		p.pendingNewline = 1
418	}
419}
420
421// Ask for a newline to be inserted before the next token.  Also inserts any end-of line comments
422// for the current line
423func (p *printer) requestNewline() {
424	pos := p.pos
425	pos.Line++
426	p.printEndOfLineCommentsBefore(pos)
427	p._requestNewline()
428}
429
430// Ask for two newlines to be inserted before the next token.  Also inserts any end-of line comments
431// for the current line
432func (p *printer) requestDoubleNewline() {
433	p.requestNewline()
434	p.pendingNewline = 2
435}
436
437// Flush any pending whitespace, ignoring pending spaces if there is a pending newline
438func (p *printer) flushSpace() {
439	if p.pendingNewline == 1 {
440		p.output = append(p.output, '\n')
441		p.pad(p.curIndent())
442	} else if p.pendingNewline == 2 {
443		p.output = append(p.output, "\n\n"...)
444		p.pad(p.curIndent())
445	} else if p.pendingSpace == true && p.pendingNewline != -1 {
446		p.output = append(p.output, ' ')
447	}
448
449	p.pendingSpace = false
450	p.pendingNewline = 0
451}
452
453// Print a single comment, which may be a multi-line comment
454func (p *printer) printComment(cg *CommentGroup) {
455	for _, comment := range cg.Comments {
456		if !p.requestNewlinesForPos(comment.Pos()) {
457			p.requestSpace()
458		}
459		for i, line := range comment.Comment {
460			line = strings.TrimRightFunc(line, unicode.IsSpace)
461			p.flushSpace()
462			if i != 0 {
463				lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) })
464				lineIndent = max(lineIndent, p.curIndent())
465				p.pad(lineIndent - p.curIndent())
466			}
467			p.output = append(p.output, strings.TrimSpace(line)...)
468			if i < len(comment.Comment)-1 {
469				p._requestNewline()
470			}
471		}
472		if p.pos.Offset < comment.End().Offset {
473			p.pos = comment.End()
474		}
475	}
476}
477
478// Print any comments that occur after the last token, and a trailing newline
479func (p *printer) flush() {
480	for _, c := range p.skippedComments {
481		if !p.requestNewlinesForPos(c.Pos()) {
482			p.requestSpace()
483		}
484		p.printComment(c)
485	}
486	for p.curComment < len(p.comments) {
487		p.printComment(p.comments[p.curComment])
488		p.curComment++
489	}
490	p.output = append(p.output, '\n')
491}
492
493// Print whitespace to pad from column l to column max
494func (p *printer) pad(l int) {
495	if l > len(p.wsBuf) {
496		p.wsBuf = make([]byte, l)
497		for i := range p.wsBuf {
498			p.wsBuf[i] = ' '
499		}
500	}
501	p.output = append(p.output, p.wsBuf[0:l]...)
502}
503
504func (p *printer) indent(i int) {
505	p.indentList = append(p.indentList, i)
506}
507
508func (p *printer) unindent(pos scanner.Position) {
509	p.printEndOfLineCommentsBefore(pos)
510	p.indentList = p.indentList[0 : len(p.indentList)-1]
511}
512
513func (p *printer) curIndent() int {
514	return p.indentList[len(p.indentList)-1]
515}
516
517func max(a, b int) int {
518	if a > b {
519		return a
520	} else {
521		return b
522	}
523}
524
525func listHasMap(list []Expression) bool {
526	for _, value := range list {
527		if _, ok := value.(*Map); ok {
528			return true
529		}
530	}
531	return false
532}
533