Home | History | Annotate | Download | only in parser
      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