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 (p *printer) Print() ([]byte, error) { 72 for _, def := range p.defs { 73 p.printDef(def) 74 } 75 p.flush() 76 return p.output, nil 77} 78 79func (p *printer) printDef(def Definition) { 80 if assignment, ok := def.(*Assignment); ok { 81 p.printAssignment(assignment) 82 } else if module, ok := def.(*Module); ok { 83 p.printModule(module) 84 } else { 85 panic("Unknown definition") 86 } 87} 88 89func (p *printer) printAssignment(assignment *Assignment) { 90 p.printToken(assignment.Name, assignment.NamePos) 91 p.requestSpace() 92 p.printToken(assignment.Assigner, assignment.EqualsPos) 93 p.requestSpace() 94 p.printExpression(assignment.OrigValue) 95 p.requestNewline() 96} 97 98func (p *printer) printModule(module *Module) { 99 p.printToken(module.Type, module.TypePos) 100 p.printMap(&module.Map) 101 p.requestDoubleNewline() 102} 103 104func (p *printer) printExpression(value Expression) { 105 switch v := value.(type) { 106 case *Variable: 107 p.printToken(v.Name, v.NamePos) 108 case *Operator: 109 p.printOperator(v) 110 case *Bool: 111 var s string 112 if v.Value { 113 s = "true" 114 } else { 115 s = "false" 116 } 117 p.printToken(s, v.LiteralPos) 118 case *String: 119 p.printToken(strconv.Quote(v.Value), v.LiteralPos) 120 case *List: 121 p.printList(v.Values, v.LBracePos, v.RBracePos) 122 case *Map: 123 p.printMap(v) 124 default: 125 panic(fmt.Errorf("bad property type: %s", value.Type())) 126 } 127} 128 129func (p *printer) printList(list []Expression, pos, endPos scanner.Position) { 130 p.requestSpace() 131 p.printToken("[", pos) 132 if len(list) > 1 || pos.Line != endPos.Line { 133 p.requestNewline() 134 p.indent(p.curIndent() + 4) 135 for _, value := range list { 136 p.printExpression(value) 137 p.printToken(",", noPos) 138 p.requestNewline() 139 } 140 p.unindent(endPos) 141 } else { 142 for _, value := range list { 143 p.printExpression(value) 144 } 145 } 146 p.printToken("]", endPos) 147} 148 149func (p *printer) printMap(m *Map) { 150 p.requestSpace() 151 p.printToken("{", m.LBracePos) 152 if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line { 153 p.requestNewline() 154 p.indent(p.curIndent() + 4) 155 for _, prop := range m.Properties { 156 p.printProperty(prop) 157 p.printToken(",", noPos) 158 p.requestNewline() 159 } 160 p.unindent(m.RBracePos) 161 } 162 p.printToken("}", m.RBracePos) 163} 164 165func (p *printer) printOperator(operator *Operator) { 166 p.printExpression(operator.Args[0]) 167 p.requestSpace() 168 p.printToken(string(operator.Operator), operator.OperatorPos) 169 if operator.Args[0].End().Line == operator.Args[1].Pos().Line { 170 p.requestSpace() 171 } else { 172 p.requestNewline() 173 } 174 p.printExpression(operator.Args[1]) 175} 176 177func (p *printer) printProperty(property *Property) { 178 p.printToken(property.Name, property.NamePos) 179 p.printToken(":", property.ColonPos) 180 p.requestSpace() 181 p.printExpression(property.Value) 182} 183 184// Print a single token, including any necessary comments or whitespace between 185// this token and the previously printed token 186func (p *printer) printToken(s string, pos scanner.Position) { 187 newline := p.pendingNewline != 0 188 189 if pos == noPos { 190 pos = p.pos 191 } 192 193 if newline { 194 p.printEndOfLineCommentsBefore(pos) 195 p.requestNewlinesForPos(pos) 196 } 197 198 p.printInLineCommentsBefore(pos) 199 200 p.flushSpace() 201 202 p.output = append(p.output, s...) 203 204 p.pos = pos 205} 206 207// Print any in-line (single line /* */) comments that appear _before_ pos 208func (p *printer) printInLineCommentsBefore(pos scanner.Position) { 209 for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset { 210 c := p.comments[p.curComment] 211 if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 { 212 p.skippedComments = append(p.skippedComments, c) 213 } else { 214 p.printComment(c) 215 p.requestSpace() 216 } 217 p.curComment++ 218 } 219} 220 221// Print any comments, including end of line comments, that appear _before_ the line specified 222// by pos 223func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) { 224 if len(p.skippedComments) > 0 { 225 for _, c := range p.skippedComments { 226 p.printComment(c) 227 } 228 p._requestNewline() 229 p.skippedComments = nil 230 } 231 for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line { 232 c := p.comments[p.curComment] 233 p.printComment(c) 234 p._requestNewline() 235 p.curComment++ 236 } 237} 238 239// Compare the line numbers of the previous and current positions to determine whether extra 240// newlines should be inserted. A second newline is allowed anywhere requestNewline() is called. 241func (p *printer) requestNewlinesForPos(pos scanner.Position) bool { 242 if pos.Line > p.pos.Line { 243 p._requestNewline() 244 if pos.Line > p.pos.Line+1 { 245 p.pendingNewline = 2 246 } 247 return true 248 } 249 250 return false 251} 252 253func (p *printer) requestSpace() { 254 p.pendingSpace = true 255} 256 257// Ask for a newline to be inserted before the next token, but do not insert any comments. Used 258// by the comment printers. 259func (p *printer) _requestNewline() { 260 if p.pendingNewline == 0 { 261 p.pendingNewline = 1 262 } 263} 264 265// Ask for a newline to be inserted before the next token. Also inserts any end-of line comments 266// for the current line 267func (p *printer) requestNewline() { 268 pos := p.pos 269 pos.Line++ 270 p.printEndOfLineCommentsBefore(pos) 271 p._requestNewline() 272} 273 274// Ask for two newlines to be inserted before the next token. Also inserts any end-of line comments 275// for the current line 276func (p *printer) requestDoubleNewline() { 277 p.requestNewline() 278 p.pendingNewline = 2 279} 280 281// Flush any pending whitespace, ignoring pending spaces if there is a pending newline 282func (p *printer) flushSpace() { 283 if p.pendingNewline == 1 { 284 p.output = append(p.output, '\n') 285 p.pad(p.curIndent()) 286 } else if p.pendingNewline == 2 { 287 p.output = append(p.output, "\n\n"...) 288 p.pad(p.curIndent()) 289 } else if p.pendingSpace == true && p.pendingNewline != -1 { 290 p.output = append(p.output, ' ') 291 } 292 293 p.pendingSpace = false 294 p.pendingNewline = 0 295} 296 297// Print a single comment, which may be a multi-line comment 298func (p *printer) printComment(cg *CommentGroup) { 299 for _, comment := range cg.Comments { 300 if !p.requestNewlinesForPos(comment.Pos()) { 301 p.requestSpace() 302 } 303 for i, line := range comment.Comment { 304 line = strings.TrimRightFunc(line, unicode.IsSpace) 305 p.flushSpace() 306 if i != 0 { 307 lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) }) 308 lineIndent = max(lineIndent, p.curIndent()) 309 p.pad(lineIndent - p.curIndent()) 310 } 311 p.output = append(p.output, strings.TrimSpace(line)...) 312 if i < len(comment.Comment)-1 { 313 p._requestNewline() 314 } 315 } 316 p.pos = comment.End() 317 } 318} 319 320// Print any comments that occur after the last token, and a trailing newline 321func (p *printer) flush() { 322 for _, c := range p.skippedComments { 323 if !p.requestNewlinesForPos(c.Pos()) { 324 p.requestSpace() 325 } 326 p.printComment(c) 327 } 328 for p.curComment < len(p.comments) { 329 p.printComment(p.comments[p.curComment]) 330 p.curComment++ 331 } 332 p.output = append(p.output, '\n') 333} 334 335// Print whitespace to pad from column l to column max 336func (p *printer) pad(l int) { 337 if l > len(p.wsBuf) { 338 p.wsBuf = make([]byte, l) 339 for i := range p.wsBuf { 340 p.wsBuf[i] = ' ' 341 } 342 } 343 p.output = append(p.output, p.wsBuf[0:l]...) 344} 345 346func (p *printer) indent(i int) { 347 p.indentList = append(p.indentList, i) 348} 349 350func (p *printer) unindent(pos scanner.Position) { 351 p.printEndOfLineCommentsBefore(pos) 352 p.indentList = p.indentList[0 : len(p.indentList)-1] 353} 354 355func (p *printer) curIndent() int { 356 return p.indentList[len(p.indentList)-1] 357} 358 359func max(a, b int) int { 360 if a > b { 361 return a 362 } else { 363 return b 364 } 365} 366