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