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 15 package parser 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 "text/scanner" 22 "unicode" 23 ) 24 25 var noPos scanner.Position 26 27 type 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 46 func 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 61 func 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 71 func 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 79 func (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 87 func (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 97 func (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 106 func (p *printer) printModule(module *Module) { 107 p.printToken(module.Type, module.TypePos) 108 p.printMap(&module.Map) 109 p.requestDoubleNewline() 110 } 111 112 func (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 default: 135 panic(fmt.Errorf("bad property type: %s", value.Type())) 136 } 137 } 138 139 func (p *printer) printList(list []Expression, pos, endPos scanner.Position) { 140 p.requestSpace() 141 p.printToken("[", pos) 142 if len(list) > 1 || pos.Line != endPos.Line { 143 p.requestNewline() 144 p.indent(p.curIndent() + 4) 145 for _, value := range list { 146 p.printExpression(value) 147 p.printToken(",", noPos) 148 p.requestNewline() 149 } 150 p.unindent(endPos) 151 } else { 152 for _, value := range list { 153 p.printExpression(value) 154 } 155 } 156 p.printToken("]", endPos) 157 } 158 159 func (p *printer) printMap(m *Map) { 160 p.requestSpace() 161 p.printToken("{", m.LBracePos) 162 if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line { 163 p.requestNewline() 164 p.indent(p.curIndent() + 4) 165 for _, prop := range m.Properties { 166 p.printProperty(prop) 167 p.printToken(",", noPos) 168 p.requestNewline() 169 } 170 p.unindent(m.RBracePos) 171 } 172 p.printToken("}", m.RBracePos) 173 } 174 175 func (p *printer) printOperator(operator *Operator) { 176 p.printOperatorInternal(operator, true) 177 } 178 179 func (p *printer) printOperatorInternal(operator *Operator, allowIndent bool) { 180 p.printExpression(operator.Args[0]) 181 p.requestSpace() 182 p.printToken(string(operator.Operator), operator.OperatorPos) 183 184 indented := false 185 if operator.Args[0].End().Line == operator.Args[1].Pos().Line { 186 p.requestSpace() 187 } else { 188 if allowIndent { 189 indented = true 190 p.indent(p.curIndent() + 4) 191 } 192 p.requestNewline() 193 } 194 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 } 204 } 205 206 func (p *printer) printProperty(property *Property) { 207 p.printToken(property.Name, property.NamePos) 208 p.printToken(":", property.ColonPos) 209 p.requestSpace() 210 p.printExpression(property.Value) 211 } 212 213 // Print a single token, including any necessary comments or whitespace between 214 // this token and the previously printed token 215 func (p *printer) printToken(s string, pos scanner.Position) { 216 newline := p.pendingNewline != 0 217 218 if pos == noPos { 219 pos = p.pos 220 } 221 222 if newline { 223 p.printEndOfLineCommentsBefore(pos) 224 p.requestNewlinesForPos(pos) 225 } 226 227 p.printInLineCommentsBefore(pos) 228 229 p.flushSpace() 230 231 p.output = append(p.output, s...) 232 233 p.pos = pos 234 } 235 236 // Print any in-line (single line /* */) comments that appear _before_ pos 237 func (p *printer) printInLineCommentsBefore(pos scanner.Position) { 238 for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset { 239 c := p.comments[p.curComment] 240 if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 { 241 p.skippedComments = append(p.skippedComments, c) 242 } else { 243 p.printComment(c) 244 p.requestSpace() 245 } 246 p.curComment++ 247 } 248 } 249 250 // Print any comments, including end of line comments, that appear _before_ the line specified 251 // by pos 252 func (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) { 253 if len(p.skippedComments) > 0 { 254 for _, c := range p.skippedComments { 255 p.printComment(c) 256 } 257 p._requestNewline() 258 p.skippedComments = nil 259 } 260 for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line { 261 c := p.comments[p.curComment] 262 p.printComment(c) 263 p._requestNewline() 264 p.curComment++ 265 } 266 } 267 268 // 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. 270 func (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 277 } 278 279 return false 280 } 281 282 func (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. 288 func (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 296 func (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 305 func (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 311 func (p *printer) flushSpace() { 312 if p.pendingNewline == 1 { 313 p.output = append(p.output, '\n') 314 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, ' ') 320 } 321 322 p.pendingSpace = false 323 p.pendingNewline = 0 324 } 325 326 // Print a single comment, which may be a multi-line comment 327 func (p *printer) printComment(cg *CommentGroup) { 328 for _, comment := range cg.Comments { 329 if !p.requestNewlinesForPos(comment.Pos()) { 330 p.requestSpace() 331 } 332 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 } 344 } 345 p.pos = comment.End() 346 } 347 } 348 349 // Print any comments that occur after the last token, and a trailing newline 350 func (p *printer) flush() { 351 for _, c := range p.skippedComments { 352 if !p.requestNewlinesForPos(c.Pos()) { 353 p.requestSpace() 354 } 355 p.printComment(c) 356 } 357 for p.curComment < len(p.comments) { 358 p.printComment(p.comments[p.curComment]) 359 p.curComment++ 360 } 361 p.output = append(p.output, '\n') 362 } 363 364 // Print whitespace to pad from column l to column max 365 func (p *printer) pad(l int) { 366 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 375 func (p *printer) indent(i int) { 376 p.indentList = append(p.indentList, i) 377 } 378 379 func (p *printer) unindent(pos scanner.Position) { 380 p.printEndOfLineCommentsBefore(pos) 381 p.indentList = p.indentList[0 : len(p.indentList)-1] 382 } 383 384 func (p *printer) curIndent() int { 385 return p.indentList[len(p.indentList)-1] 386 } 387 388 func max(a, b int) int { 389 if a > b { 390 return a 391 } else { 392 return b 393 } 394 } 395