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 []*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