blob: ac7ffe1e7ae9777eb43b88a3c8994d353a63304f [file] [log] [blame]
Colin Cross8e0c5112015-01-23 14:15:10 -08001// 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
Colin Cross5ad47f42015-01-08 19:35:10 -080015package parser
16
17import (
18 "fmt"
19 "strconv"
20 "strings"
21 "text/scanner"
Colin Cross85398a92015-03-19 14:49:38 -070022 "unicode"
Colin Cross5ad47f42015-01-08 19:35:10 -080023)
24
Colin Crosse32cc802016-06-07 12:28:16 -070025var noPos scanner.Position
Colin Cross5ad47f42015-01-08 19:35:10 -080026
Colin Cross5ad47f42015-01-08 19:35:10 -080027type printer struct {
28 defs []Definition
Colin Cross1e737942016-06-10 17:27:12 -070029 comments []*CommentGroup
Colin Cross5ad47f42015-01-08 19:35:10 -080030
31 curComment int
Colin Cross85398a92015-03-19 14:49:38 -070032
33 pos scanner.Position
34
35 pendingSpace bool
36 pendingNewline int
Colin Cross5ad47f42015-01-08 19:35:10 -080037
38 output []byte
39
Colin Cross9a103c02015-01-30 16:38:08 -080040 indentList []int
41 wsBuf []byte
42
Colin Cross6b6735d2016-07-12 16:03:08 -070043 skippedComments []*CommentGroup
Colin Cross5ad47f42015-01-08 19:35:10 -080044}
45
46func newPrinter(file *File) *printer {
47 return &printer{
48 defs: file.Defs,
49 comments: file.Comments,
50 indentList: []int{0},
Colin Cross85398a92015-03-19 14:49:38 -070051
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,
Colin Cross9a103c02015-01-30 16:38:08 -080057 },
Colin Cross5ad47f42015-01-08 19:35:10 -080058 }
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
Jeff Gastona1dae802017-06-28 14:35:14 -070071func 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
Colin Cross5ad47f42015-01-08 19:35:10 -080079func (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) {
Colin Crossc32c4792016-06-09 15:52:30 -070098 p.printToken(assignment.Name, assignment.NamePos)
Colin Cross85398a92015-03-19 14:49:38 -070099 p.requestSpace()
Colin Crossb3d0b8d2016-06-09 17:03:57 -0700100 p.printToken(assignment.Assigner, assignment.EqualsPos)
Colin Cross85398a92015-03-19 14:49:38 -0700101 p.requestSpace()
Colin Crosse32cc802016-06-07 12:28:16 -0700102 p.printExpression(assignment.OrigValue)
Colin Cross85398a92015-03-19 14:49:38 -0700103 p.requestNewline()
Colin Cross5ad47f42015-01-08 19:35:10 -0800104}
105
106func (p *printer) printModule(module *Module) {
Colin Crossc32c4792016-06-09 15:52:30 -0700107 p.printToken(module.Type, module.TypePos)
Colin Crosse32cc802016-06-07 12:28:16 -0700108 p.printMap(&module.Map)
Colin Cross85398a92015-03-19 14:49:38 -0700109 p.requestDoubleNewline()
Colin Cross5ad47f42015-01-08 19:35:10 -0800110}
111
Colin Crosse32cc802016-06-07 12:28:16 -0700112func (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"
Colin Cross5ad47f42015-01-08 19:35:10 -0800124 }
Colin Crosse32cc802016-06-07 12:28:16 -0700125 p.printToken(s, v.LiteralPos)
Nan Zhangf5865442017-11-01 14:03:28 -0700126 case *Int64:
127 p.printToken(strconv.FormatInt(v.Value, 10), v.LiteralPos)
Colin Crosse32cc802016-06-07 12:28:16 -0700128 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 default:
Colin Crossba5bf882016-07-15 16:40:37 -0700135 panic(fmt.Errorf("bad property type: %s", value.Type()))
Colin Cross5ad47f42015-01-08 19:35:10 -0800136 }
137}
138
Colin Crosse32cc802016-06-07 12:28:16 -0700139func (p *printer) printList(list []Expression, pos, endPos scanner.Position) {
Colin Cross85398a92015-03-19 14:49:38 -0700140 p.requestSpace()
141 p.printToken("[", pos)
Colin Cross5ad47f42015-01-08 19:35:10 -0800142 if len(list) > 1 || pos.Line != endPos.Line {
Colin Cross85398a92015-03-19 14:49:38 -0700143 p.requestNewline()
Colin Cross5ad47f42015-01-08 19:35:10 -0800144 p.indent(p.curIndent() + 4)
145 for _, value := range list {
Colin Crosse32cc802016-06-07 12:28:16 -0700146 p.printExpression(value)
Colin Cross85398a92015-03-19 14:49:38 -0700147 p.printToken(",", noPos)
148 p.requestNewline()
Colin Cross5ad47f42015-01-08 19:35:10 -0800149 }
Colin Cross85398a92015-03-19 14:49:38 -0700150 p.unindent(endPos)
Colin Cross5ad47f42015-01-08 19:35:10 -0800151 } else {
152 for _, value := range list {
Colin Crosse32cc802016-06-07 12:28:16 -0700153 p.printExpression(value)
Colin Cross5ad47f42015-01-08 19:35:10 -0800154 }
155 }
Colin Cross85398a92015-03-19 14:49:38 -0700156 p.printToken("]", endPos)
Colin Cross5ad47f42015-01-08 19:35:10 -0800157}
158
Colin Crosse32cc802016-06-07 12:28:16 -0700159func (p *printer) printMap(m *Map) {
Colin Cross85398a92015-03-19 14:49:38 -0700160 p.requestSpace()
Colin Crosse32cc802016-06-07 12:28:16 -0700161 p.printToken("{", m.LBracePos)
162 if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line {
Colin Cross85398a92015-03-19 14:49:38 -0700163 p.requestNewline()
Colin Cross5ad47f42015-01-08 19:35:10 -0800164 p.indent(p.curIndent() + 4)
Colin Crosse32cc802016-06-07 12:28:16 -0700165 for _, prop := range m.Properties {
Colin Crosscb7b9ad2015-03-02 15:32:36 -0800166 p.printProperty(prop)
Colin Cross85398a92015-03-19 14:49:38 -0700167 p.printToken(",", noPos)
168 p.requestNewline()
Colin Cross5ad47f42015-01-08 19:35:10 -0800169 }
Colin Crosse32cc802016-06-07 12:28:16 -0700170 p.unindent(m.RBracePos)
Colin Cross5ad47f42015-01-08 19:35:10 -0800171 }
Colin Crosse32cc802016-06-07 12:28:16 -0700172 p.printToken("}", m.RBracePos)
Colin Cross5ad47f42015-01-08 19:35:10 -0800173}
174
Colin Crosse32cc802016-06-07 12:28:16 -0700175func (p *printer) printOperator(operator *Operator) {
Dan Willemsend2c81622018-05-07 16:11:42 -0700176 p.printOperatorInternal(operator, true)
177}
178
179func (p *printer) printOperatorInternal(operator *Operator, allowIndent bool) {
Colin Crosse32cc802016-06-07 12:28:16 -0700180 p.printExpression(operator.Args[0])
Colin Cross85398a92015-03-19 14:49:38 -0700181 p.requestSpace()
Colin Crosse32cc802016-06-07 12:28:16 -0700182 p.printToken(string(operator.Operator), operator.OperatorPos)
Dan Willemsend2c81622018-05-07 16:11:42 -0700183
184 indented := false
Colin Crosse32cc802016-06-07 12:28:16 -0700185 if operator.Args[0].End().Line == operator.Args[1].Pos().Line {
Colin Cross85398a92015-03-19 14:49:38 -0700186 p.requestSpace()
187 } else {
Dan Willemsend2c81622018-05-07 16:11:42 -0700188 if allowIndent {
189 indented = true
190 p.indent(p.curIndent() + 4)
191 }
Colin Cross85398a92015-03-19 14:49:38 -0700192 p.requestNewline()
193 }
Dan Willemsend2c81622018-05-07 16:11:42 -0700194
195 if op, isOp := operator.Args[1].(*Operator); isOp {
196 p.printOperatorInternal(op, false)
197 } else {
198 p.printExpression(operator.Args[1])
199 }
200
201 if indented {
202 p.unindent(p.pos)
203 }
Colin Cross5ad47f42015-01-08 19:35:10 -0800204}
205
Colin Crosscb7b9ad2015-03-02 15:32:36 -0800206func (p *printer) printProperty(property *Property) {
Colin Crossc32c4792016-06-09 15:52:30 -0700207 p.printToken(property.Name, property.NamePos)
Colin Crossb3d0b8d2016-06-09 17:03:57 -0700208 p.printToken(":", property.ColonPos)
Colin Cross85398a92015-03-19 14:49:38 -0700209 p.requestSpace()
Colin Crosse32cc802016-06-07 12:28:16 -0700210 p.printExpression(property.Value)
Colin Cross5ad47f42015-01-08 19:35:10 -0800211}
212
213// Print a single token, including any necessary comments or whitespace between
214// this token and the previously printed token
Colin Cross85398a92015-03-19 14:49:38 -0700215func (p *printer) printToken(s string, pos scanner.Position) {
216 newline := p.pendingNewline != 0
217
218 if pos == noPos {
219 pos = p.pos
Colin Cross5ad47f42015-01-08 19:35:10 -0800220 }
Colin Cross9a103c02015-01-30 16:38:08 -0800221
Colin Cross85398a92015-03-19 14:49:38 -0700222 if newline {
223 p.printEndOfLineCommentsBefore(pos)
224 p.requestNewlinesForPos(pos)
Colin Cross9a103c02015-01-30 16:38:08 -0800225 }
226
Colin Cross85398a92015-03-19 14:49:38 -0700227 p.printInLineCommentsBefore(pos)
Colin Cross9a103c02015-01-30 16:38:08 -0800228
Colin Cross85398a92015-03-19 14:49:38 -0700229 p.flushSpace()
Colin Cross9a103c02015-01-30 16:38:08 -0800230
Colin Cross85398a92015-03-19 14:49:38 -0700231 p.output = append(p.output, s...)
Colin Cross9a103c02015-01-30 16:38:08 -0800232
Colin Cross85398a92015-03-19 14:49:38 -0700233 p.pos = pos
Colin Cross9a103c02015-01-30 16:38:08 -0800234}
235
Colin Cross85398a92015-03-19 14:49:38 -0700236// Print any in-line (single line /* */) comments that appear _before_ pos
237func (p *printer) printInLineCommentsBefore(pos scanner.Position) {
Colin Cross1e737942016-06-10 17:27:12 -0700238 for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset {
Colin Cross85398a92015-03-19 14:49:38 -0700239 c := p.comments[p.curComment]
Colin Cross1e737942016-06-10 17:27:12 -0700240 if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 {
Colin Cross6b6735d2016-07-12 16:03:08 -0700241 p.skippedComments = append(p.skippedComments, c)
Colin Cross9a103c02015-01-30 16:38:08 -0800242 } else {
Colin Cross85398a92015-03-19 14:49:38 -0700243 p.printComment(c)
244 p.requestSpace()
Colin Cross9a103c02015-01-30 16:38:08 -0800245 }
Colin Cross5ad47f42015-01-08 19:35:10 -0800246 p.curComment++
247 }
248}
249
Colin Cross85398a92015-03-19 14:49:38 -0700250// Print any comments, including end of line comments, that appear _before_ the line specified
251// by pos
252func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) {
Colin Cross6b6735d2016-07-12 16:03:08 -0700253 if len(p.skippedComments) > 0 {
254 for _, c := range p.skippedComments {
255 p.printComment(c)
256 }
Colin Cross85398a92015-03-19 14:49:38 -0700257 p._requestNewline()
Colin Cross1e737942016-06-10 17:27:12 -0700258 p.skippedComments = nil
Colin Cross5ad47f42015-01-08 19:35:10 -0800259 }
Colin Cross1e737942016-06-10 17:27:12 -0700260 for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line {
Colin Cross85398a92015-03-19 14:49:38 -0700261 c := p.comments[p.curComment]
Colin Cross85398a92015-03-19 14:49:38 -0700262 p.printComment(c)
263 p._requestNewline()
264 p.curComment++
Colin Cross9a103c02015-01-30 16:38:08 -0800265 }
Colin Cross5ad47f42015-01-08 19:35:10 -0800266}
267
Colin Cross85398a92015-03-19 14:49:38 -0700268// Compare the line numbers of the previous and current positions to determine whether extra
269// newlines should be inserted. A second newline is allowed anywhere requestNewline() is called.
270func (p *printer) requestNewlinesForPos(pos scanner.Position) bool {
271 if pos.Line > p.pos.Line {
272 p._requestNewline()
273 if pos.Line > p.pos.Line+1 {
274 p.pendingNewline = 2
275 }
276 return true
Colin Cross5ad47f42015-01-08 19:35:10 -0800277 }
278
Colin Cross85398a92015-03-19 14:49:38 -0700279 return false
280}
281
282func (p *printer) requestSpace() {
283 p.pendingSpace = true
284}
285
286// Ask for a newline to be inserted before the next token, but do not insert any comments. Used
287// by the comment printers.
288func (p *printer) _requestNewline() {
289 if p.pendingNewline == 0 {
290 p.pendingNewline = 1
291 }
292}
293
294// Ask for a newline to be inserted before the next token. Also inserts any end-of line comments
295// for the current line
296func (p *printer) requestNewline() {
297 pos := p.pos
298 pos.Line++
299 p.printEndOfLineCommentsBefore(pos)
300 p._requestNewline()
301}
302
303// Ask for two newlines to be inserted before the next token. Also inserts any end-of line comments
304// for the current line
305func (p *printer) requestDoubleNewline() {
306 p.requestNewline()
307 p.pendingNewline = 2
308}
309
310// Flush any pending whitespace, ignoring pending spaces if there is a pending newline
311func (p *printer) flushSpace() {
312 if p.pendingNewline == 1 {
Colin Cross5ad47f42015-01-08 19:35:10 -0800313 p.output = append(p.output, '\n')
Colin Cross85398a92015-03-19 14:49:38 -0700314 p.pad(p.curIndent())
315 } else if p.pendingNewline == 2 {
316 p.output = append(p.output, "\n\n"...)
317 p.pad(p.curIndent())
318 } else if p.pendingSpace == true && p.pendingNewline != -1 {
319 p.output = append(p.output, ' ')
Colin Cross5ad47f42015-01-08 19:35:10 -0800320 }
321
Colin Cross85398a92015-03-19 14:49:38 -0700322 p.pendingSpace = false
323 p.pendingNewline = 0
324}
325
326// Print a single comment, which may be a multi-line comment
Colin Cross1e737942016-06-10 17:27:12 -0700327func (p *printer) printComment(cg *CommentGroup) {
328 for _, comment := range cg.Comments {
329 if !p.requestNewlinesForPos(comment.Pos()) {
330 p.requestSpace()
Colin Cross85398a92015-03-19 14:49:38 -0700331 }
Colin Cross1e737942016-06-10 17:27:12 -0700332 for i, line := range comment.Comment {
333 line = strings.TrimRightFunc(line, unicode.IsSpace)
334 p.flushSpace()
335 if i != 0 {
336 lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) })
337 lineIndent = max(lineIndent, p.curIndent())
338 p.pad(lineIndent - p.curIndent())
339 }
340 p.output = append(p.output, strings.TrimSpace(line)...)
341 if i < len(comment.Comment)-1 {
342 p._requestNewline()
343 }
Colin Cross85398a92015-03-19 14:49:38 -0700344 }
Colin Cross1e737942016-06-10 17:27:12 -0700345 p.pos = comment.End()
Colin Cross85398a92015-03-19 14:49:38 -0700346 }
Colin Cross5ad47f42015-01-08 19:35:10 -0800347}
348
349// Print any comments that occur after the last token, and a trailing newline
350func (p *printer) flush() {
Colin Cross6b6735d2016-07-12 16:03:08 -0700351 for _, c := range p.skippedComments {
352 if !p.requestNewlinesForPos(c.Pos()) {
Colin Cross85398a92015-03-19 14:49:38 -0700353 p.requestSpace()
354 }
Colin Cross6b6735d2016-07-12 16:03:08 -0700355 p.printComment(c)
Colin Cross9a103c02015-01-30 16:38:08 -0800356 }
Colin Cross5ad47f42015-01-08 19:35:10 -0800357 for p.curComment < len(p.comments) {
Colin Cross1e737942016-06-10 17:27:12 -0700358 p.printComment(p.comments[p.curComment])
Colin Cross5ad47f42015-01-08 19:35:10 -0800359 p.curComment++
360 }
361 p.output = append(p.output, '\n')
362}
363
364// Print whitespace to pad from column l to column max
Colin Cross85398a92015-03-19 14:49:38 -0700365func (p *printer) pad(l int) {
Colin Cross5ad47f42015-01-08 19:35:10 -0800366 if l > len(p.wsBuf) {
367 p.wsBuf = make([]byte, l)
368 for i := range p.wsBuf {
369 p.wsBuf[i] = ' '
370 }
371 }
372 p.output = append(p.output, p.wsBuf[0:l]...)
373}
374
375func (p *printer) indent(i int) {
376 p.indentList = append(p.indentList, i)
377}
378
Colin Cross85398a92015-03-19 14:49:38 -0700379func (p *printer) unindent(pos scanner.Position) {
380 p.printEndOfLineCommentsBefore(pos)
Colin Cross5ad47f42015-01-08 19:35:10 -0800381 p.indentList = p.indentList[0 : len(p.indentList)-1]
382}
383
384func (p *printer) curIndent() int {
385 return p.indentList[len(p.indentList)-1]
386}
Colin Cross9a103c02015-01-30 16:38:08 -0800387
388func max(a, b int) int {
389 if a > b {
390 return a
391 } else {
392 return b
393 }
394}