2015-01-23 23:15:10 +01:00
|
|
|
// Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
2015-01-09 04:35:10 +01:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"text/scanner"
|
|
|
|
)
|
|
|
|
|
|
|
|
var noPos = scanner.Position{}
|
|
|
|
|
|
|
|
type whitespace int
|
|
|
|
|
|
|
|
const (
|
2015-01-31 01:38:08 +01:00
|
|
|
wsDontCare whitespace = iota
|
2015-01-09 04:35:10 +01:00
|
|
|
wsBoth
|
|
|
|
wsAfter
|
|
|
|
wsBefore
|
2015-01-31 01:38:08 +01:00
|
|
|
wsCanBreak // allow extra line breaks or comments
|
|
|
|
wsBothCanBreak // wsBoth plus allow extra line breaks or comments
|
|
|
|
wsForceBreak
|
|
|
|
wsForceDoubleBreak
|
2015-01-09 04:35:10 +01:00
|
|
|
)
|
|
|
|
|
2015-01-31 01:38:08 +01:00
|
|
|
type tokenState struct {
|
|
|
|
ws whitespace
|
|
|
|
token string
|
|
|
|
pos scanner.Position
|
|
|
|
indent int
|
|
|
|
}
|
|
|
|
|
2015-01-09 04:35:10 +01:00
|
|
|
type printer struct {
|
|
|
|
defs []Definition
|
|
|
|
comments []Comment
|
|
|
|
|
|
|
|
curComment int
|
2015-01-31 01:38:08 +01:00
|
|
|
prev tokenState
|
2015-01-09 04:35:10 +01:00
|
|
|
|
|
|
|
output []byte
|
|
|
|
|
2015-01-31 01:38:08 +01:00
|
|
|
indentList []int
|
|
|
|
wsBuf []byte
|
|
|
|
|
|
|
|
skippedComments []Comment
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func newPrinter(file *File) *printer {
|
|
|
|
return &printer{
|
|
|
|
defs: file.Defs,
|
|
|
|
comments: file.Comments,
|
|
|
|
indentList: []int{0},
|
2015-01-31 01:38:08 +01:00
|
|
|
prev: tokenState{
|
|
|
|
ws: wsCanBreak,
|
|
|
|
},
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Print(file *File) ([]byte, error) {
|
|
|
|
p := newPrinter(file)
|
|
|
|
|
|
|
|
for _, def := range p.defs {
|
|
|
|
p.printDef(def)
|
|
|
|
}
|
2015-01-31 01:38:08 +01:00
|
|
|
p.prev.ws = wsDontCare
|
2015-01-09 04:35:10 +01:00
|
|
|
p.flush()
|
|
|
|
return p.output, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) Print() ([]byte, error) {
|
|
|
|
for _, def := range p.defs {
|
|
|
|
p.printDef(def)
|
|
|
|
}
|
|
|
|
p.flush()
|
|
|
|
return p.output, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printDef(def Definition) {
|
|
|
|
if assignment, ok := def.(*Assignment); ok {
|
|
|
|
p.printAssignment(assignment)
|
|
|
|
} else if module, ok := def.(*Module); ok {
|
|
|
|
p.printModule(module)
|
|
|
|
} else {
|
|
|
|
panic("Unknown definition")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printAssignment(assignment *Assignment) {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(assignment.Name.Name, assignment.Name.Pos, wsDontCare)
|
2015-02-04 19:50:22 +01:00
|
|
|
p.printToken(assignment.Assigner, assignment.Pos, wsBoth)
|
|
|
|
p.printValue(assignment.OrigValue)
|
2015-01-31 01:38:08 +01:00
|
|
|
p.prev.ws = wsForceBreak
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printModule(module *Module) {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(module.Type.Name, module.Type.Pos, wsDontCare)
|
2015-01-15 02:04:13 +01:00
|
|
|
p.printMap(module.Properties, module.LbracePos, module.RbracePos, true)
|
2015-01-31 01:38:08 +01:00
|
|
|
p.prev.ws = wsForceDoubleBreak
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printValue(value Value) {
|
|
|
|
if value.Variable != "" {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(value.Variable, value.Pos, wsDontCare)
|
2015-01-09 04:35:10 +01:00
|
|
|
} else if value.Expression != nil {
|
|
|
|
p.printExpression(*value.Expression)
|
|
|
|
} else {
|
|
|
|
switch value.Type {
|
|
|
|
case Bool:
|
|
|
|
var s string
|
|
|
|
if value.BoolValue {
|
|
|
|
s = "true"
|
|
|
|
} else {
|
|
|
|
s = "false"
|
|
|
|
}
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(s, value.Pos, wsDontCare)
|
2015-01-09 04:35:10 +01:00
|
|
|
case String:
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(strconv.Quote(value.StringValue), value.Pos, wsDontCare)
|
2015-01-09 04:35:10 +01:00
|
|
|
case List:
|
|
|
|
p.printList(value.ListValue, value.Pos, value.EndPos)
|
|
|
|
case Map:
|
2015-01-15 02:04:13 +01:00
|
|
|
p.printMap(value.MapValue, value.Pos, value.EndPos, false)
|
2015-01-09 04:35:10 +01:00
|
|
|
default:
|
|
|
|
panic(fmt.Errorf("bad property type: %d", value.Type))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printList(list []Value, pos, endPos scanner.Position) {
|
|
|
|
p.printToken("[", pos, wsBefore)
|
|
|
|
if len(list) > 1 || pos.Line != endPos.Line {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.prev.ws = wsForceBreak
|
2015-01-09 04:35:10 +01:00
|
|
|
p.indent(p.curIndent() + 4)
|
|
|
|
for _, value := range list {
|
|
|
|
p.printValue(value)
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(",", noPos, wsForceBreak)
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
p.unindent()
|
|
|
|
} else {
|
|
|
|
for _, value := range list {
|
|
|
|
p.printValue(value)
|
|
|
|
}
|
|
|
|
}
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken("]", endPos, wsDontCare)
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
2015-01-15 02:04:13 +01:00
|
|
|
func (p *printer) printMap(list []*Property, pos, endPos scanner.Position, isModule bool) {
|
|
|
|
if isModule {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken("(", pos, wsDontCare)
|
2015-01-15 02:04:13 +01:00
|
|
|
} else {
|
|
|
|
p.printToken("{", pos, wsBefore)
|
|
|
|
}
|
2015-01-09 04:35:10 +01:00
|
|
|
if len(list) > 0 || pos.Line != endPos.Line {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.prev.ws = wsForceBreak
|
2015-01-09 04:35:10 +01:00
|
|
|
p.indent(p.curIndent() + 4)
|
|
|
|
for _, prop := range list {
|
2015-01-15 02:04:13 +01:00
|
|
|
p.printProperty(prop, isModule)
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(",", noPos, wsForceBreak)
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
p.unindent()
|
|
|
|
}
|
2015-01-15 02:04:13 +01:00
|
|
|
if isModule {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(")", endPos, wsForceDoubleBreak)
|
2015-01-15 02:04:13 +01:00
|
|
|
} else {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken("}", endPos, wsDontCare)
|
2015-01-15 02:04:13 +01:00
|
|
|
}
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printExpression(expression Expression) {
|
|
|
|
p.printValue(expression.Args[0])
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(string(expression.Operator), expression.Pos, wsBothCanBreak)
|
2015-01-09 04:35:10 +01:00
|
|
|
p.printValue(expression.Args[1])
|
|
|
|
}
|
|
|
|
|
2015-01-15 02:04:13 +01:00
|
|
|
func (p *printer) printProperty(property *Property, isModule bool) {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printToken(property.Name.Name, property.Name.Pos, wsDontCare)
|
2015-01-15 02:04:13 +01:00
|
|
|
if isModule {
|
|
|
|
p.printToken("=", property.Pos, wsBoth)
|
|
|
|
} else {
|
|
|
|
p.printToken(":", property.Pos, wsAfter)
|
|
|
|
}
|
2015-01-09 04:35:10 +01:00
|
|
|
p.printValue(property.Value)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print a single token, including any necessary comments or whitespace between
|
|
|
|
// this token and the previously printed token
|
|
|
|
func (p *printer) printToken(s string, pos scanner.Position, ws whitespace) {
|
2015-01-31 01:38:08 +01:00
|
|
|
this := tokenState{
|
|
|
|
token: s,
|
|
|
|
pos: pos,
|
|
|
|
ws: ws,
|
|
|
|
indent: p.curIndent(),
|
|
|
|
}
|
|
|
|
|
|
|
|
if this.pos == noPos {
|
|
|
|
this.pos = p.prev.pos
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print the previously stored token
|
|
|
|
allowLineBreak := false
|
|
|
|
lineBreak := 0
|
|
|
|
|
|
|
|
switch p.prev.ws {
|
|
|
|
case wsBothCanBreak, wsCanBreak, wsForceBreak, wsForceDoubleBreak:
|
|
|
|
allowLineBreak = true
|
|
|
|
}
|
|
|
|
|
|
|
|
p.output = append(p.output, p.prev.token...)
|
|
|
|
|
|
|
|
commentIndent := max(p.prev.indent, this.indent)
|
|
|
|
p.printComments(this.pos, allowLineBreak, commentIndent)
|
|
|
|
|
|
|
|
switch p.prev.ws {
|
|
|
|
case wsForceBreak:
|
|
|
|
lineBreak = 1
|
|
|
|
case wsForceDoubleBreak:
|
|
|
|
lineBreak = 2
|
|
|
|
}
|
|
|
|
|
|
|
|
if allowLineBreak && p.prev.pos.IsValid() &&
|
|
|
|
pos.Line-p.prev.pos.Line > lineBreak {
|
|
|
|
lineBreak = pos.Line - p.prev.pos.Line
|
|
|
|
}
|
|
|
|
|
|
|
|
if lineBreak > 0 {
|
|
|
|
p.printLineBreak(lineBreak, this.indent)
|
2015-01-09 04:35:10 +01:00
|
|
|
} else {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printWhitespace(this.ws)
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
2015-01-31 01:38:08 +01:00
|
|
|
|
|
|
|
p.prev = this
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) printWhitespace(ws whitespace) {
|
|
|
|
if p.prev.ws == wsBoth || ws == wsBoth ||
|
|
|
|
p.prev.ws == wsBothCanBreak || ws == wsBothCanBreak ||
|
|
|
|
p.prev.ws == wsAfter || ws == wsBefore {
|
|
|
|
p.output = append(p.output, ' ')
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-31 01:38:08 +01:00
|
|
|
// Pr int all comments that occur before position pos
|
|
|
|
func (p *printer) printComments(pos scanner.Position, allowLineBreak bool, indent int) {
|
|
|
|
if allowLineBreak {
|
|
|
|
for _, c := range p.skippedComments {
|
|
|
|
p.printComment(c, indent)
|
|
|
|
}
|
|
|
|
p.skippedComments = []Comment{}
|
|
|
|
}
|
2015-01-09 04:35:10 +01:00
|
|
|
for p.curComment < len(p.comments) && p.comments[p.curComment].Pos.Offset < pos.Offset {
|
2015-01-31 01:38:08 +01:00
|
|
|
if !allowLineBreak && p.comments[p.curComment].Comment[0:2] == "//" {
|
|
|
|
p.skippedComments = append(p.skippedComments, p.comments[p.curComment])
|
|
|
|
} else {
|
|
|
|
p.printComment(p.comments[p.curComment], indent)
|
|
|
|
}
|
2015-01-09 04:35:10 +01:00
|
|
|
p.curComment++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print a single comment, which may be a multi-line comment
|
2015-01-31 01:38:08 +01:00
|
|
|
func (p *printer) printComment(comment Comment, indent int) {
|
2015-01-09 04:35:10 +01:00
|
|
|
commentLines := strings.Split(comment.Comment, "\n")
|
|
|
|
pos := comment.Pos
|
|
|
|
for _, line := range commentLines {
|
2015-01-31 01:38:08 +01:00
|
|
|
if p.prev.pos.IsValid() && pos.Line > p.prev.pos.Line {
|
2015-01-09 04:35:10 +01:00
|
|
|
// Comment is on the next line
|
2015-01-31 01:38:08 +01:00
|
|
|
if p.prev.ws == wsForceDoubleBreak {
|
|
|
|
p.printLineBreak(2, indent)
|
|
|
|
p.prev.ws = wsForceBreak
|
|
|
|
} else {
|
|
|
|
p.printLineBreak(pos.Line-p.prev.pos.Line, indent)
|
|
|
|
}
|
|
|
|
} else if p.prev.pos.IsValid() {
|
2015-01-09 04:35:10 +01:00
|
|
|
// Comment is on the current line
|
|
|
|
p.printWhitespace(wsBoth)
|
|
|
|
}
|
|
|
|
p.output = append(p.output, strings.TrimSpace(line)...)
|
2015-01-31 01:38:08 +01:00
|
|
|
p.prev.pos = pos
|
2015-01-09 04:35:10 +01:00
|
|
|
pos.Line++
|
|
|
|
}
|
2015-01-31 01:38:08 +01:00
|
|
|
if comment.Comment[0:2] == "//" {
|
|
|
|
if p.prev.ws != wsForceDoubleBreak {
|
|
|
|
p.prev.ws = wsForceBreak
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
p.prev.ws = wsBothCanBreak
|
|
|
|
}
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print one or two line breaks. n <= 0 is only valid if forceLineBreak is set,
|
|
|
|
// n > 2 is collapsed to a single blank line.
|
2015-01-31 01:38:08 +01:00
|
|
|
func (p *printer) printLineBreak(n, indent int) {
|
2015-01-09 04:35:10 +01:00
|
|
|
if n > 2 {
|
|
|
|
n = 2
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < n; i++ {
|
|
|
|
p.output = append(p.output, '\n')
|
|
|
|
}
|
|
|
|
|
2015-01-31 01:38:08 +01:00
|
|
|
p.pad(0, indent)
|
2015-01-09 04:35:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Print any comments that occur after the last token, and a trailing newline
|
|
|
|
func (p *printer) flush() {
|
2015-01-31 01:38:08 +01:00
|
|
|
for _, c := range p.skippedComments {
|
|
|
|
p.printComment(c, p.curIndent())
|
|
|
|
}
|
|
|
|
p.printToken("", noPos, wsDontCare)
|
2015-01-09 04:35:10 +01:00
|
|
|
for p.curComment < len(p.comments) {
|
2015-01-31 01:38:08 +01:00
|
|
|
p.printComment(p.comments[p.curComment], p.curIndent())
|
2015-01-09 04:35:10 +01:00
|
|
|
p.curComment++
|
|
|
|
}
|
|
|
|
p.output = append(p.output, '\n')
|
|
|
|
}
|
|
|
|
|
|
|
|
// Print whitespace to pad from column l to column max
|
|
|
|
func (p *printer) pad(l, max int) {
|
|
|
|
l = max - l
|
|
|
|
if l > len(p.wsBuf) {
|
|
|
|
p.wsBuf = make([]byte, l)
|
|
|
|
for i := range p.wsBuf {
|
|
|
|
p.wsBuf[i] = ' '
|
|
|
|
}
|
|
|
|
}
|
|
|
|
p.output = append(p.output, p.wsBuf[0:l]...)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) indent(i int) {
|
|
|
|
p.indentList = append(p.indentList, i)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) unindent() {
|
|
|
|
p.indentList = p.indentList[0 : len(p.indentList)-1]
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *printer) curIndent() int {
|
|
|
|
return p.indentList[len(p.indentList)-1]
|
|
|
|
}
|
2015-01-31 01:38:08 +01:00
|
|
|
|
|
|
|
func max(a, b int) int {
|
|
|
|
if a > b {
|
|
|
|
return a
|
|
|
|
} else {
|
|
|
|
return b
|
|
|
|
}
|
|
|
|
}
|