• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.OrigValue)
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: %s", value.Type()))
138	}
139}
140
141func (p *printer) printSelect(s *Select) {
142	if len(s.Cases) == 0 {
143		return
144	}
145	if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 1 {
146		if str, ok := s.Cases[0].Patterns[0].(*String); ok && str.Value == default_select_branch_name {
147			p.printExpression(s.Cases[0].Value)
148			p.pos = s.RBracePos
149			return
150		}
151	}
152	p.requestSpace()
153	p.printToken("select(", s.KeywordPos)
154	multilineConditions := false
155	if len(s.Conditions) > 1 {
156		p.printToken("(", s.KeywordPos)
157		if s.Conditions[len(s.Conditions)-1].position.Line > s.KeywordPos.Line {
158			multilineConditions = true
159			p.requestNewline()
160			p.indent(p.curIndent() + 4)
161		}
162	}
163	for i, c := range s.Conditions {
164		p.printToken(c.FunctionName, c.position)
165		p.printToken("(", c.position)
166		for i, arg := range c.Args {
167			p.printToken(strconv.Quote(arg.Value), arg.LiteralPos)
168			if i < len(c.Args)-1 {
169				p.printToken(",", arg.LiteralPos)
170				p.requestSpace()
171			}
172		}
173		p.printToken(")", p.pos)
174		if len(s.Conditions) > 1 {
175			if multilineConditions {
176				p.printToken(",", p.pos)
177				p.requestNewline()
178			} else if i < len(s.Conditions)-1 {
179				p.printToken(",", p.pos)
180				p.requestSpace()
181			}
182		}
183	}
184	if len(s.Conditions) > 1 {
185		if multilineConditions {
186			p.unindent(p.pos)
187		}
188		p.printToken(")", p.pos)
189	}
190	p.printToken(", {", s.LBracePos)
191	p.requestNewline()
192	p.indent(p.curIndent() + 4)
193	for _, c := range s.Cases {
194		p.requestNewline()
195		if len(c.Patterns) > 1 {
196			p.printToken("(", p.pos)
197		}
198		for i, pat := range c.Patterns {
199			switch pat := pat.(type) {
200			case *String:
201				if pat.Value != default_select_branch_name {
202					p.printToken(strconv.Quote(pat.Value), pat.LiteralPos)
203				} else {
204					p.printToken("default", pat.LiteralPos)
205				}
206			case *Bool:
207				s := "false"
208				if pat.Value {
209					s = "true"
210				}
211				p.printToken(s, pat.LiteralPos)
212			default:
213				panic("Unhandled case")
214			}
215			if i < len(c.Patterns)-1 {
216				p.printToken(",", p.pos)
217				p.requestSpace()
218			}
219		}
220		if len(c.Patterns) > 1 {
221			p.printToken(")", p.pos)
222		}
223		p.printToken(":", c.ColonPos)
224		p.requestSpace()
225		if unset, ok := c.Value.(UnsetProperty); ok {
226			p.printToken(unset.String(), unset.Pos())
227		} else {
228			p.printExpression(c.Value)
229		}
230		p.printToken(",", c.Value.End())
231	}
232	p.requestNewline()
233	p.unindent(s.RBracePos)
234	p.printToken("})", s.RBracePos)
235	if s.Append != nil {
236		p.requestSpace()
237		p.printToken("+", s.RBracePos)
238		p.requestSpace()
239		p.printExpression(s.Append)
240	}
241}
242
243func (p *printer) printList(list []Expression, pos, endPos scanner.Position) {
244	p.requestSpace()
245	p.printToken("[", pos)
246	if len(list) > 1 || pos.Line != endPos.Line || listHasMap(list) {
247		p.requestNewline()
248		p.indent(p.curIndent() + 4)
249		for _, value := range list {
250			p.printExpression(value)
251			p.printToken(",", noPos)
252			p.requestNewline()
253		}
254		p.unindent(endPos)
255	} else {
256		for _, value := range list {
257			p.printExpression(value)
258		}
259	}
260	p.printToken("]", endPos)
261}
262
263func (p *printer) printMap(m *Map) {
264	p.requestSpace()
265	p.printToken("{", m.LBracePos)
266	if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line {
267		p.requestNewline()
268		p.indent(p.curIndent() + 4)
269		for _, prop := range m.Properties {
270			p.printProperty(prop)
271			p.printToken(",", noPos)
272			p.requestNewline()
273		}
274		p.unindent(m.RBracePos)
275	}
276	p.printToken("}", m.RBracePos)
277}
278
279func (p *printer) printOperator(operator *Operator) {
280	p.printOperatorInternal(operator, true)
281}
282
283func (p *printer) printOperatorInternal(operator *Operator, allowIndent bool) {
284	p.printExpression(operator.Args[0])
285	p.requestSpace()
286	p.printToken(string(operator.Operator), operator.OperatorPos)
287
288	indented := false
289	if operator.Args[0].End().Line == operator.Args[1].Pos().Line {
290		p.requestSpace()
291	} else {
292		if allowIndent {
293			indented = true
294			p.indent(p.curIndent() + 4)
295		}
296		p.requestNewline()
297	}
298
299	if op, isOp := operator.Args[1].(*Operator); isOp {
300		p.printOperatorInternal(op, false)
301	} else {
302		p.printExpression(operator.Args[1])
303	}
304
305	if indented {
306		p.unindent(p.pos)
307	}
308}
309
310func (p *printer) printProperty(property *Property) {
311	p.printToken(property.Name, property.NamePos)
312	p.printToken(":", property.ColonPos)
313	p.requestSpace()
314	p.printExpression(property.Value)
315}
316
317// Print a single token, including any necessary comments or whitespace between
318// this token and the previously printed token
319func (p *printer) printToken(s string, pos scanner.Position) {
320	newline := p.pendingNewline != 0
321
322	if pos == noPos {
323		pos = p.pos
324	}
325
326	if newline {
327		p.printEndOfLineCommentsBefore(pos)
328		p.requestNewlinesForPos(pos)
329	}
330
331	p.printInLineCommentsBefore(pos)
332
333	p.flushSpace()
334
335	p.output = append(p.output, s...)
336
337	p.pos = pos
338}
339
340// Print any in-line (single line /* */) comments that appear _before_ pos
341func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
342	for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset {
343		c := p.comments[p.curComment]
344		if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 {
345			p.skippedComments = append(p.skippedComments, c)
346		} else {
347			p.printComment(c)
348			p.requestSpace()
349		}
350		p.curComment++
351	}
352}
353
354// Print any comments, including end of line comments, that appear _before_ the line specified
355// by pos
356func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
357	if len(p.skippedComments) > 0 {
358		for _, c := range p.skippedComments {
359			p.printComment(c)
360			p._requestNewline()
361		}
362		p.skippedComments = nil
363	}
364	for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line {
365		c := p.comments[p.curComment]
366		p.printComment(c)
367		p._requestNewline()
368		p.curComment++
369	}
370}
371
372// Compare the line numbers of the previous and current positions to determine whether extra
373// newlines should be inserted.  A second newline is allowed anywhere requestNewline() is called.
374func (p *printer) requestNewlinesForPos(pos scanner.Position) bool {
375	if pos.Line > p.pos.Line {
376		p._requestNewline()
377		if pos.Line > p.pos.Line+1 {
378			p.pendingNewline = 2
379		}
380		return true
381	}
382
383	return false
384}
385
386func (p *printer) requestSpace() {
387	p.pendingSpace = true
388}
389
390// Ask for a newline to be inserted before the next token, but do not insert any comments.  Used
391// by the comment printers.
392func (p *printer) _requestNewline() {
393	if p.pendingNewline == 0 {
394		p.pendingNewline = 1
395	}
396}
397
398// Ask for a newline to be inserted before the next token.  Also inserts any end-of line comments
399// for the current line
400func (p *printer) requestNewline() {
401	pos := p.pos
402	pos.Line++
403	p.printEndOfLineCommentsBefore(pos)
404	p._requestNewline()
405}
406
407// Ask for two newlines to be inserted before the next token.  Also inserts any end-of line comments
408// for the current line
409func (p *printer) requestDoubleNewline() {
410	p.requestNewline()
411	p.pendingNewline = 2
412}
413
414// Flush any pending whitespace, ignoring pending spaces if there is a pending newline
415func (p *printer) flushSpace() {
416	if p.pendingNewline == 1 {
417		p.output = append(p.output, '\n')
418		p.pad(p.curIndent())
419	} else if p.pendingNewline == 2 {
420		p.output = append(p.output, "\n\n"...)
421		p.pad(p.curIndent())
422	} else if p.pendingSpace == true && p.pendingNewline != -1 {
423		p.output = append(p.output, ' ')
424	}
425
426	p.pendingSpace = false
427	p.pendingNewline = 0
428}
429
430// Print a single comment, which may be a multi-line comment
431func (p *printer) printComment(cg *CommentGroup) {
432	for _, comment := range cg.Comments {
433		if !p.requestNewlinesForPos(comment.Pos()) {
434			p.requestSpace()
435		}
436		for i, line := range comment.Comment {
437			line = strings.TrimRightFunc(line, unicode.IsSpace)
438			p.flushSpace()
439			if i != 0 {
440				lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) })
441				lineIndent = max(lineIndent, p.curIndent())
442				p.pad(lineIndent - p.curIndent())
443			}
444			p.output = append(p.output, strings.TrimSpace(line)...)
445			if i < len(comment.Comment)-1 {
446				p._requestNewline()
447			}
448		}
449		if p.pos.Offset < comment.End().Offset {
450			p.pos = comment.End()
451		}
452	}
453}
454
455// Print any comments that occur after the last token, and a trailing newline
456func (p *printer) flush() {
457	for _, c := range p.skippedComments {
458		if !p.requestNewlinesForPos(c.Pos()) {
459			p.requestSpace()
460		}
461		p.printComment(c)
462	}
463	for p.curComment < len(p.comments) {
464		p.printComment(p.comments[p.curComment])
465		p.curComment++
466	}
467	p.output = append(p.output, '\n')
468}
469
470// Print whitespace to pad from column l to column max
471func (p *printer) pad(l int) {
472	if l > len(p.wsBuf) {
473		p.wsBuf = make([]byte, l)
474		for i := range p.wsBuf {
475			p.wsBuf[i] = ' '
476		}
477	}
478	p.output = append(p.output, p.wsBuf[0:l]...)
479}
480
481func (p *printer) indent(i int) {
482	p.indentList = append(p.indentList, i)
483}
484
485func (p *printer) unindent(pos scanner.Position) {
486	p.printEndOfLineCommentsBefore(pos)
487	p.indentList = p.indentList[0 : len(p.indentList)-1]
488}
489
490func (p *printer) curIndent() int {
491	return p.indentList[len(p.indentList)-1]
492}
493
494func max(a, b int) int {
495	if a > b {
496		return a
497	} else {
498		return b
499	}
500}
501
502func listHasMap(list []Expression) bool {
503	for _, value := range list {
504		if _, ok := value.(*Map); ok {
505			return true
506		}
507	}
508	return false
509}
510