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