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