1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2014 Google Inc. All rights reserved. 2*1fa6dee9SAndroid Build Coastguard Worker// 3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*1fa6dee9SAndroid Build Coastguard Worker// 7*1fa6dee9SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*1fa6dee9SAndroid Build Coastguard Worker// 9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License. 14*1fa6dee9SAndroid Build Coastguard Worker 15*1fa6dee9SAndroid Build Coastguard Workerpackage parser 16*1fa6dee9SAndroid Build Coastguard Worker 17*1fa6dee9SAndroid Build Coastguard Workerimport ( 18*1fa6dee9SAndroid Build Coastguard Worker "fmt" 19*1fa6dee9SAndroid Build Coastguard Worker "strconv" 20*1fa6dee9SAndroid Build Coastguard Worker "strings" 21*1fa6dee9SAndroid Build Coastguard Worker "text/scanner" 22*1fa6dee9SAndroid Build Coastguard Worker "unicode" 23*1fa6dee9SAndroid Build Coastguard Worker) 24*1fa6dee9SAndroid Build Coastguard Worker 25*1fa6dee9SAndroid Build Coastguard Workervar noPos scanner.Position 26*1fa6dee9SAndroid Build Coastguard Worker 27*1fa6dee9SAndroid Build Coastguard Workertype printer struct { 28*1fa6dee9SAndroid Build Coastguard Worker defs []Definition 29*1fa6dee9SAndroid Build Coastguard Worker comments []*CommentGroup 30*1fa6dee9SAndroid Build Coastguard Worker 31*1fa6dee9SAndroid Build Coastguard Worker curComment int 32*1fa6dee9SAndroid Build Coastguard Worker 33*1fa6dee9SAndroid Build Coastguard Worker pos scanner.Position 34*1fa6dee9SAndroid Build Coastguard Worker 35*1fa6dee9SAndroid Build Coastguard Worker pendingSpace bool 36*1fa6dee9SAndroid Build Coastguard Worker pendingNewline int 37*1fa6dee9SAndroid Build Coastguard Worker 38*1fa6dee9SAndroid Build Coastguard Worker output []byte 39*1fa6dee9SAndroid Build Coastguard Worker 40*1fa6dee9SAndroid Build Coastguard Worker indentList []int 41*1fa6dee9SAndroid Build Coastguard Worker wsBuf []byte 42*1fa6dee9SAndroid Build Coastguard Worker 43*1fa6dee9SAndroid Build Coastguard Worker skippedComments []*CommentGroup 44*1fa6dee9SAndroid Build Coastguard Worker} 45*1fa6dee9SAndroid Build Coastguard Worker 46*1fa6dee9SAndroid Build Coastguard Workerfunc newPrinter(file *File) *printer { 47*1fa6dee9SAndroid Build Coastguard Worker return &printer{ 48*1fa6dee9SAndroid Build Coastguard Worker defs: file.Defs, 49*1fa6dee9SAndroid Build Coastguard Worker comments: file.Comments, 50*1fa6dee9SAndroid Build Coastguard Worker indentList: []int{0}, 51*1fa6dee9SAndroid Build Coastguard Worker 52*1fa6dee9SAndroid Build Coastguard Worker // pendingNewLine is initialized to -1 to eat initial spaces if the first token is a comment 53*1fa6dee9SAndroid Build Coastguard Worker pendingNewline: -1, 54*1fa6dee9SAndroid Build Coastguard Worker 55*1fa6dee9SAndroid Build Coastguard Worker pos: scanner.Position{ 56*1fa6dee9SAndroid Build Coastguard Worker Line: 1, 57*1fa6dee9SAndroid Build Coastguard Worker }, 58*1fa6dee9SAndroid Build Coastguard Worker } 59*1fa6dee9SAndroid Build Coastguard Worker} 60*1fa6dee9SAndroid Build Coastguard Worker 61*1fa6dee9SAndroid Build Coastguard Workerfunc Print(file *File) ([]byte, error) { 62*1fa6dee9SAndroid Build Coastguard Worker p := newPrinter(file) 63*1fa6dee9SAndroid Build Coastguard Worker 64*1fa6dee9SAndroid Build Coastguard Worker for _, def := range p.defs { 65*1fa6dee9SAndroid Build Coastguard Worker p.printDef(def) 66*1fa6dee9SAndroid Build Coastguard Worker } 67*1fa6dee9SAndroid Build Coastguard Worker p.flush() 68*1fa6dee9SAndroid Build Coastguard Worker return p.output, nil 69*1fa6dee9SAndroid Build Coastguard Worker} 70*1fa6dee9SAndroid Build Coastguard Worker 71*1fa6dee9SAndroid Build Coastguard Workerfunc PrintExpression(expression Expression) ([]byte, error) { 72*1fa6dee9SAndroid Build Coastguard Worker dummyFile := &File{} 73*1fa6dee9SAndroid Build Coastguard Worker p := newPrinter(dummyFile) 74*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(expression) 75*1fa6dee9SAndroid Build Coastguard Worker p.flush() 76*1fa6dee9SAndroid Build Coastguard Worker return p.output, nil 77*1fa6dee9SAndroid Build Coastguard Worker} 78*1fa6dee9SAndroid Build Coastguard Worker 79*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) Print() ([]byte, error) { 80*1fa6dee9SAndroid Build Coastguard Worker for _, def := range p.defs { 81*1fa6dee9SAndroid Build Coastguard Worker p.printDef(def) 82*1fa6dee9SAndroid Build Coastguard Worker } 83*1fa6dee9SAndroid Build Coastguard Worker p.flush() 84*1fa6dee9SAndroid Build Coastguard Worker return p.output, nil 85*1fa6dee9SAndroid Build Coastguard Worker} 86*1fa6dee9SAndroid Build Coastguard Worker 87*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printDef(def Definition) { 88*1fa6dee9SAndroid Build Coastguard Worker if assignment, ok := def.(*Assignment); ok { 89*1fa6dee9SAndroid Build Coastguard Worker p.printAssignment(assignment) 90*1fa6dee9SAndroid Build Coastguard Worker } else if module, ok := def.(*Module); ok { 91*1fa6dee9SAndroid Build Coastguard Worker p.printModule(module) 92*1fa6dee9SAndroid Build Coastguard Worker } else { 93*1fa6dee9SAndroid Build Coastguard Worker panic("Unknown definition") 94*1fa6dee9SAndroid Build Coastguard Worker } 95*1fa6dee9SAndroid Build Coastguard Worker} 96*1fa6dee9SAndroid Build Coastguard Worker 97*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printAssignment(assignment *Assignment) { 98*1fa6dee9SAndroid Build Coastguard Worker p.printToken(assignment.Name, assignment.NamePos) 99*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 100*1fa6dee9SAndroid Build Coastguard Worker p.printToken(assignment.Assigner, assignment.EqualsPos) 101*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 102*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(assignment.Value) 103*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 104*1fa6dee9SAndroid Build Coastguard Worker} 105*1fa6dee9SAndroid Build Coastguard Worker 106*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printModule(module *Module) { 107*1fa6dee9SAndroid Build Coastguard Worker p.printToken(module.Type, module.TypePos) 108*1fa6dee9SAndroid Build Coastguard Worker p.printMap(&module.Map) 109*1fa6dee9SAndroid Build Coastguard Worker p.requestDoubleNewline() 110*1fa6dee9SAndroid Build Coastguard Worker} 111*1fa6dee9SAndroid Build Coastguard Worker 112*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printExpression(value Expression) { 113*1fa6dee9SAndroid Build Coastguard Worker switch v := value.(type) { 114*1fa6dee9SAndroid Build Coastguard Worker case *Variable: 115*1fa6dee9SAndroid Build Coastguard Worker p.printToken(v.Name, v.NamePos) 116*1fa6dee9SAndroid Build Coastguard Worker case *Operator: 117*1fa6dee9SAndroid Build Coastguard Worker p.printOperator(v) 118*1fa6dee9SAndroid Build Coastguard Worker case *Bool: 119*1fa6dee9SAndroid Build Coastguard Worker var s string 120*1fa6dee9SAndroid Build Coastguard Worker if v.Value { 121*1fa6dee9SAndroid Build Coastguard Worker s = "true" 122*1fa6dee9SAndroid Build Coastguard Worker } else { 123*1fa6dee9SAndroid Build Coastguard Worker s = "false" 124*1fa6dee9SAndroid Build Coastguard Worker } 125*1fa6dee9SAndroid Build Coastguard Worker p.printToken(s, v.LiteralPos) 126*1fa6dee9SAndroid Build Coastguard Worker case *Int64: 127*1fa6dee9SAndroid Build Coastguard Worker p.printToken(strconv.FormatInt(v.Value, 10), v.LiteralPos) 128*1fa6dee9SAndroid Build Coastguard Worker case *String: 129*1fa6dee9SAndroid Build Coastguard Worker p.printToken(strconv.Quote(v.Value), v.LiteralPos) 130*1fa6dee9SAndroid Build Coastguard Worker case *List: 131*1fa6dee9SAndroid Build Coastguard Worker p.printList(v.Values, v.LBracePos, v.RBracePos) 132*1fa6dee9SAndroid Build Coastguard Worker case *Map: 133*1fa6dee9SAndroid Build Coastguard Worker p.printMap(v) 134*1fa6dee9SAndroid Build Coastguard Worker case *Select: 135*1fa6dee9SAndroid Build Coastguard Worker p.printSelect(v) 136*1fa6dee9SAndroid Build Coastguard Worker default: 137*1fa6dee9SAndroid Build Coastguard Worker panic(fmt.Errorf("bad property type: %v", value)) 138*1fa6dee9SAndroid Build Coastguard Worker } 139*1fa6dee9SAndroid Build Coastguard Worker} 140*1fa6dee9SAndroid Build Coastguard Worker 141*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printSelect(s *Select) { 142*1fa6dee9SAndroid Build Coastguard Worker print_append := func() { 143*1fa6dee9SAndroid Build Coastguard Worker if s.Append != nil { 144*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 145*1fa6dee9SAndroid Build Coastguard Worker p.printToken("+", s.RBracePos) 146*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 147*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(s.Append) 148*1fa6dee9SAndroid Build Coastguard Worker } 149*1fa6dee9SAndroid Build Coastguard Worker } 150*1fa6dee9SAndroid Build Coastguard Worker if len(s.Cases) == 0 { 151*1fa6dee9SAndroid Build Coastguard Worker print_append() 152*1fa6dee9SAndroid Build Coastguard Worker return 153*1fa6dee9SAndroid Build Coastguard Worker } 154*1fa6dee9SAndroid Build Coastguard Worker if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 1 { 155*1fa6dee9SAndroid Build Coastguard Worker if str, ok := s.Cases[0].Patterns[0].Value.(*String); ok && str.Value == default_select_branch_name { 156*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(s.Cases[0].Value) 157*1fa6dee9SAndroid Build Coastguard Worker p.pos = s.RBracePos 158*1fa6dee9SAndroid Build Coastguard Worker print_append() 159*1fa6dee9SAndroid Build Coastguard Worker return 160*1fa6dee9SAndroid Build Coastguard Worker } 161*1fa6dee9SAndroid Build Coastguard Worker } 162*1fa6dee9SAndroid Build Coastguard Worker if len(s.Cases) == 1 && len(s.Cases[0].Patterns) == 0 { 163*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(s.Cases[0].Value) 164*1fa6dee9SAndroid Build Coastguard Worker p.pos = s.RBracePos 165*1fa6dee9SAndroid Build Coastguard Worker print_append() 166*1fa6dee9SAndroid Build Coastguard Worker return 167*1fa6dee9SAndroid Build Coastguard Worker } 168*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 169*1fa6dee9SAndroid Build Coastguard Worker p.printToken("select(", s.KeywordPos) 170*1fa6dee9SAndroid Build Coastguard Worker multilineConditions := false 171*1fa6dee9SAndroid Build Coastguard Worker if len(s.Conditions) > 1 { 172*1fa6dee9SAndroid Build Coastguard Worker p.printToken("(", s.KeywordPos) 173*1fa6dee9SAndroid Build Coastguard Worker if s.Conditions[len(s.Conditions)-1].position.Line > s.KeywordPos.Line { 174*1fa6dee9SAndroid Build Coastguard Worker multilineConditions = true 175*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 176*1fa6dee9SAndroid Build Coastguard Worker p.indent(p.curIndent() + 4) 177*1fa6dee9SAndroid Build Coastguard Worker } 178*1fa6dee9SAndroid Build Coastguard Worker } 179*1fa6dee9SAndroid Build Coastguard Worker for i, c := range s.Conditions { 180*1fa6dee9SAndroid Build Coastguard Worker p.printToken(c.FunctionName, c.position) 181*1fa6dee9SAndroid Build Coastguard Worker p.printToken("(", c.position) 182*1fa6dee9SAndroid Build Coastguard Worker for i, arg := range c.Args { 183*1fa6dee9SAndroid Build Coastguard Worker p.printToken(strconv.Quote(arg.Value), arg.LiteralPos) 184*1fa6dee9SAndroid Build Coastguard Worker if i < len(c.Args)-1 { 185*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", arg.LiteralPos) 186*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 187*1fa6dee9SAndroid Build Coastguard Worker } 188*1fa6dee9SAndroid Build Coastguard Worker } 189*1fa6dee9SAndroid Build Coastguard Worker p.printToken(")", p.pos) 190*1fa6dee9SAndroid Build Coastguard Worker if len(s.Conditions) > 1 { 191*1fa6dee9SAndroid Build Coastguard Worker if multilineConditions { 192*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", p.pos) 193*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 194*1fa6dee9SAndroid Build Coastguard Worker } else if i < len(s.Conditions)-1 { 195*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", p.pos) 196*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 197*1fa6dee9SAndroid Build Coastguard Worker } 198*1fa6dee9SAndroid Build Coastguard Worker } 199*1fa6dee9SAndroid Build Coastguard Worker } 200*1fa6dee9SAndroid Build Coastguard Worker if len(s.Conditions) > 1 { 201*1fa6dee9SAndroid Build Coastguard Worker if multilineConditions { 202*1fa6dee9SAndroid Build Coastguard Worker p.unindent(p.pos) 203*1fa6dee9SAndroid Build Coastguard Worker } 204*1fa6dee9SAndroid Build Coastguard Worker p.printToken(")", p.pos) 205*1fa6dee9SAndroid Build Coastguard Worker } 206*1fa6dee9SAndroid Build Coastguard Worker p.printToken(", {", s.LBracePos) 207*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 208*1fa6dee9SAndroid Build Coastguard Worker p.indent(p.curIndent() + 4) 209*1fa6dee9SAndroid Build Coastguard Worker for _, c := range s.Cases { 210*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 211*1fa6dee9SAndroid Build Coastguard Worker if len(c.Patterns) > 1 { 212*1fa6dee9SAndroid Build Coastguard Worker p.printToken("(", p.pos) 213*1fa6dee9SAndroid Build Coastguard Worker } 214*1fa6dee9SAndroid Build Coastguard Worker for i, pat := range c.Patterns { 215*1fa6dee9SAndroid Build Coastguard Worker p.printSelectPattern(pat) 216*1fa6dee9SAndroid Build Coastguard Worker if i < len(c.Patterns)-1 { 217*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", p.pos) 218*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 219*1fa6dee9SAndroid Build Coastguard Worker } 220*1fa6dee9SAndroid Build Coastguard Worker } 221*1fa6dee9SAndroid Build Coastguard Worker if len(c.Patterns) > 1 { 222*1fa6dee9SAndroid Build Coastguard Worker p.printToken(")", p.pos) 223*1fa6dee9SAndroid Build Coastguard Worker } 224*1fa6dee9SAndroid Build Coastguard Worker p.printToken(":", c.ColonPos) 225*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 226*1fa6dee9SAndroid Build Coastguard Worker if unset, ok := c.Value.(*UnsetProperty); ok { 227*1fa6dee9SAndroid Build Coastguard Worker p.printToken(unset.String(), unset.Pos()) 228*1fa6dee9SAndroid Build Coastguard Worker } else { 229*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(c.Value) 230*1fa6dee9SAndroid Build Coastguard Worker } 231*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", c.Value.End()) 232*1fa6dee9SAndroid Build Coastguard Worker } 233*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 234*1fa6dee9SAndroid Build Coastguard Worker p.unindent(s.RBracePos) 235*1fa6dee9SAndroid Build Coastguard Worker p.printToken("})", s.RBracePos) 236*1fa6dee9SAndroid Build Coastguard Worker print_append() 237*1fa6dee9SAndroid Build Coastguard Worker} 238*1fa6dee9SAndroid Build Coastguard Worker 239*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printSelectPattern(pat SelectPattern) { 240*1fa6dee9SAndroid Build Coastguard Worker switch pat := pat.Value.(type) { 241*1fa6dee9SAndroid Build Coastguard Worker case *String: 242*1fa6dee9SAndroid Build Coastguard Worker if pat.Value == default_select_branch_name { 243*1fa6dee9SAndroid Build Coastguard Worker p.printToken("default", pat.LiteralPos) 244*1fa6dee9SAndroid Build Coastguard Worker } else if pat.Value == any_select_branch_name { 245*1fa6dee9SAndroid Build Coastguard Worker p.printToken("any", pat.LiteralPos) 246*1fa6dee9SAndroid Build Coastguard Worker } else { 247*1fa6dee9SAndroid Build Coastguard Worker p.printToken(strconv.Quote(pat.Value), pat.LiteralPos) 248*1fa6dee9SAndroid Build Coastguard Worker } 249*1fa6dee9SAndroid Build Coastguard Worker case *Bool: 250*1fa6dee9SAndroid Build Coastguard Worker s := "false" 251*1fa6dee9SAndroid Build Coastguard Worker if pat.Value { 252*1fa6dee9SAndroid Build Coastguard Worker s = "true" 253*1fa6dee9SAndroid Build Coastguard Worker } 254*1fa6dee9SAndroid Build Coastguard Worker p.printToken(s, pat.LiteralPos) 255*1fa6dee9SAndroid Build Coastguard Worker default: 256*1fa6dee9SAndroid Build Coastguard Worker panic("Unhandled case") 257*1fa6dee9SAndroid Build Coastguard Worker } 258*1fa6dee9SAndroid Build Coastguard Worker if pat.Binding.Name != "" { 259*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 260*1fa6dee9SAndroid Build Coastguard Worker p.printToken("@", pat.Binding.Pos()) 261*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 262*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(&pat.Binding) 263*1fa6dee9SAndroid Build Coastguard Worker } 264*1fa6dee9SAndroid Build Coastguard Worker} 265*1fa6dee9SAndroid Build Coastguard Worker 266*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printList(list []Expression, pos, endPos scanner.Position) { 267*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 268*1fa6dee9SAndroid Build Coastguard Worker p.printToken("[", pos) 269*1fa6dee9SAndroid Build Coastguard Worker if len(list) > 1 || pos.Line != endPos.Line || listHasMap(list) { 270*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 271*1fa6dee9SAndroid Build Coastguard Worker p.indent(p.curIndent() + 4) 272*1fa6dee9SAndroid Build Coastguard Worker for _, value := range list { 273*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(value) 274*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", noPos) 275*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 276*1fa6dee9SAndroid Build Coastguard Worker } 277*1fa6dee9SAndroid Build Coastguard Worker p.unindent(endPos) 278*1fa6dee9SAndroid Build Coastguard Worker } else { 279*1fa6dee9SAndroid Build Coastguard Worker for _, value := range list { 280*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(value) 281*1fa6dee9SAndroid Build Coastguard Worker } 282*1fa6dee9SAndroid Build Coastguard Worker } 283*1fa6dee9SAndroid Build Coastguard Worker p.printToken("]", endPos) 284*1fa6dee9SAndroid Build Coastguard Worker} 285*1fa6dee9SAndroid Build Coastguard Worker 286*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printMap(m *Map) { 287*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 288*1fa6dee9SAndroid Build Coastguard Worker p.printToken("{", m.LBracePos) 289*1fa6dee9SAndroid Build Coastguard Worker if len(m.Properties) > 0 || m.LBracePos.Line != m.RBracePos.Line { 290*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 291*1fa6dee9SAndroid Build Coastguard Worker p.indent(p.curIndent() + 4) 292*1fa6dee9SAndroid Build Coastguard Worker for _, prop := range m.Properties { 293*1fa6dee9SAndroid Build Coastguard Worker p.printProperty(prop) 294*1fa6dee9SAndroid Build Coastguard Worker p.printToken(",", noPos) 295*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 296*1fa6dee9SAndroid Build Coastguard Worker } 297*1fa6dee9SAndroid Build Coastguard Worker p.unindent(m.RBracePos) 298*1fa6dee9SAndroid Build Coastguard Worker } 299*1fa6dee9SAndroid Build Coastguard Worker p.printToken("}", m.RBracePos) 300*1fa6dee9SAndroid Build Coastguard Worker} 301*1fa6dee9SAndroid Build Coastguard Worker 302*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printOperator(operator *Operator) { 303*1fa6dee9SAndroid Build Coastguard Worker p.printOperatorInternal(operator, true) 304*1fa6dee9SAndroid Build Coastguard Worker} 305*1fa6dee9SAndroid Build Coastguard Worker 306*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printOperatorInternal(operator *Operator, allowIndent bool) { 307*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(operator.Args[0]) 308*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 309*1fa6dee9SAndroid Build Coastguard Worker p.printToken(string(operator.Operator), operator.OperatorPos) 310*1fa6dee9SAndroid Build Coastguard Worker 311*1fa6dee9SAndroid Build Coastguard Worker indented := false 312*1fa6dee9SAndroid Build Coastguard Worker if operator.Args[0].End().Line == operator.Args[1].Pos().Line { 313*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 314*1fa6dee9SAndroid Build Coastguard Worker } else { 315*1fa6dee9SAndroid Build Coastguard Worker if allowIndent { 316*1fa6dee9SAndroid Build Coastguard Worker indented = true 317*1fa6dee9SAndroid Build Coastguard Worker p.indent(p.curIndent() + 4) 318*1fa6dee9SAndroid Build Coastguard Worker } 319*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 320*1fa6dee9SAndroid Build Coastguard Worker } 321*1fa6dee9SAndroid Build Coastguard Worker 322*1fa6dee9SAndroid Build Coastguard Worker if op, isOp := operator.Args[1].(*Operator); isOp { 323*1fa6dee9SAndroid Build Coastguard Worker p.printOperatorInternal(op, false) 324*1fa6dee9SAndroid Build Coastguard Worker } else { 325*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(operator.Args[1]) 326*1fa6dee9SAndroid Build Coastguard Worker } 327*1fa6dee9SAndroid Build Coastguard Worker 328*1fa6dee9SAndroid Build Coastguard Worker if indented { 329*1fa6dee9SAndroid Build Coastguard Worker p.unindent(p.pos) 330*1fa6dee9SAndroid Build Coastguard Worker } 331*1fa6dee9SAndroid Build Coastguard Worker} 332*1fa6dee9SAndroid Build Coastguard Worker 333*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printProperty(property *Property) { 334*1fa6dee9SAndroid Build Coastguard Worker p.printToken(property.Name, property.NamePos) 335*1fa6dee9SAndroid Build Coastguard Worker p.printToken(":", property.ColonPos) 336*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 337*1fa6dee9SAndroid Build Coastguard Worker p.printExpression(property.Value) 338*1fa6dee9SAndroid Build Coastguard Worker} 339*1fa6dee9SAndroid Build Coastguard Worker 340*1fa6dee9SAndroid Build Coastguard Worker// Print a single token, including any necessary comments or whitespace between 341*1fa6dee9SAndroid Build Coastguard Worker// this token and the previously printed token 342*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printToken(s string, pos scanner.Position) { 343*1fa6dee9SAndroid Build Coastguard Worker newline := p.pendingNewline != 0 344*1fa6dee9SAndroid Build Coastguard Worker 345*1fa6dee9SAndroid Build Coastguard Worker if pos == noPos { 346*1fa6dee9SAndroid Build Coastguard Worker pos = p.pos 347*1fa6dee9SAndroid Build Coastguard Worker } 348*1fa6dee9SAndroid Build Coastguard Worker 349*1fa6dee9SAndroid Build Coastguard Worker if newline { 350*1fa6dee9SAndroid Build Coastguard Worker p.printEndOfLineCommentsBefore(pos) 351*1fa6dee9SAndroid Build Coastguard Worker p.requestNewlinesForPos(pos) 352*1fa6dee9SAndroid Build Coastguard Worker } 353*1fa6dee9SAndroid Build Coastguard Worker 354*1fa6dee9SAndroid Build Coastguard Worker p.printInLineCommentsBefore(pos) 355*1fa6dee9SAndroid Build Coastguard Worker 356*1fa6dee9SAndroid Build Coastguard Worker p.flushSpace() 357*1fa6dee9SAndroid Build Coastguard Worker 358*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, s...) 359*1fa6dee9SAndroid Build Coastguard Worker 360*1fa6dee9SAndroid Build Coastguard Worker p.pos = pos 361*1fa6dee9SAndroid Build Coastguard Worker} 362*1fa6dee9SAndroid Build Coastguard Worker 363*1fa6dee9SAndroid Build Coastguard Worker// Print any in-line (single line /* */) comments that appear _before_ pos 364*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printInLineCommentsBefore(pos scanner.Position) { 365*1fa6dee9SAndroid Build Coastguard Worker for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Offset < pos.Offset { 366*1fa6dee9SAndroid Build Coastguard Worker c := p.comments[p.curComment] 367*1fa6dee9SAndroid Build Coastguard Worker if c.Comments[0].Comment[0][0:2] == "//" || len(c.Comments[0].Comment) > 1 { 368*1fa6dee9SAndroid Build Coastguard Worker p.skippedComments = append(p.skippedComments, c) 369*1fa6dee9SAndroid Build Coastguard Worker } else { 370*1fa6dee9SAndroid Build Coastguard Worker p.printComment(c) 371*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 372*1fa6dee9SAndroid Build Coastguard Worker } 373*1fa6dee9SAndroid Build Coastguard Worker p.curComment++ 374*1fa6dee9SAndroid Build Coastguard Worker } 375*1fa6dee9SAndroid Build Coastguard Worker} 376*1fa6dee9SAndroid Build Coastguard Worker 377*1fa6dee9SAndroid Build Coastguard Worker// Print any comments, including end of line comments, that appear _before_ the line specified 378*1fa6dee9SAndroid Build Coastguard Worker// by pos 379*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printEndOfLineCommentsBefore(pos scanner.Position) { 380*1fa6dee9SAndroid Build Coastguard Worker if len(p.skippedComments) > 0 { 381*1fa6dee9SAndroid Build Coastguard Worker for _, c := range p.skippedComments { 382*1fa6dee9SAndroid Build Coastguard Worker p.printComment(c) 383*1fa6dee9SAndroid Build Coastguard Worker p._requestNewline() 384*1fa6dee9SAndroid Build Coastguard Worker } 385*1fa6dee9SAndroid Build Coastguard Worker p.skippedComments = nil 386*1fa6dee9SAndroid Build Coastguard Worker } 387*1fa6dee9SAndroid Build Coastguard Worker for p.curComment < len(p.comments) && p.comments[p.curComment].Pos().Line < pos.Line { 388*1fa6dee9SAndroid Build Coastguard Worker c := p.comments[p.curComment] 389*1fa6dee9SAndroid Build Coastguard Worker p.printComment(c) 390*1fa6dee9SAndroid Build Coastguard Worker p._requestNewline() 391*1fa6dee9SAndroid Build Coastguard Worker p.curComment++ 392*1fa6dee9SAndroid Build Coastguard Worker } 393*1fa6dee9SAndroid Build Coastguard Worker} 394*1fa6dee9SAndroid Build Coastguard Worker 395*1fa6dee9SAndroid Build Coastguard Worker// Compare the line numbers of the previous and current positions to determine whether extra 396*1fa6dee9SAndroid Build Coastguard Worker// newlines should be inserted. A second newline is allowed anywhere requestNewline() is called. 397*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) requestNewlinesForPos(pos scanner.Position) bool { 398*1fa6dee9SAndroid Build Coastguard Worker if pos.Line > p.pos.Line { 399*1fa6dee9SAndroid Build Coastguard Worker p._requestNewline() 400*1fa6dee9SAndroid Build Coastguard Worker if pos.Line > p.pos.Line+1 { 401*1fa6dee9SAndroid Build Coastguard Worker p.pendingNewline = 2 402*1fa6dee9SAndroid Build Coastguard Worker } 403*1fa6dee9SAndroid Build Coastguard Worker return true 404*1fa6dee9SAndroid Build Coastguard Worker } 405*1fa6dee9SAndroid Build Coastguard Worker 406*1fa6dee9SAndroid Build Coastguard Worker return false 407*1fa6dee9SAndroid Build Coastguard Worker} 408*1fa6dee9SAndroid Build Coastguard Worker 409*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) requestSpace() { 410*1fa6dee9SAndroid Build Coastguard Worker p.pendingSpace = true 411*1fa6dee9SAndroid Build Coastguard Worker} 412*1fa6dee9SAndroid Build Coastguard Worker 413*1fa6dee9SAndroid Build Coastguard Worker// Ask for a newline to be inserted before the next token, but do not insert any comments. Used 414*1fa6dee9SAndroid Build Coastguard Worker// by the comment printers. 415*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) _requestNewline() { 416*1fa6dee9SAndroid Build Coastguard Worker if p.pendingNewline == 0 { 417*1fa6dee9SAndroid Build Coastguard Worker p.pendingNewline = 1 418*1fa6dee9SAndroid Build Coastguard Worker } 419*1fa6dee9SAndroid Build Coastguard Worker} 420*1fa6dee9SAndroid Build Coastguard Worker 421*1fa6dee9SAndroid Build Coastguard Worker// Ask for a newline to be inserted before the next token. Also inserts any end-of line comments 422*1fa6dee9SAndroid Build Coastguard Worker// for the current line 423*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) requestNewline() { 424*1fa6dee9SAndroid Build Coastguard Worker pos := p.pos 425*1fa6dee9SAndroid Build Coastguard Worker pos.Line++ 426*1fa6dee9SAndroid Build Coastguard Worker p.printEndOfLineCommentsBefore(pos) 427*1fa6dee9SAndroid Build Coastguard Worker p._requestNewline() 428*1fa6dee9SAndroid Build Coastguard Worker} 429*1fa6dee9SAndroid Build Coastguard Worker 430*1fa6dee9SAndroid Build Coastguard Worker// Ask for two newlines to be inserted before the next token. Also inserts any end-of line comments 431*1fa6dee9SAndroid Build Coastguard Worker// for the current line 432*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) requestDoubleNewline() { 433*1fa6dee9SAndroid Build Coastguard Worker p.requestNewline() 434*1fa6dee9SAndroid Build Coastguard Worker p.pendingNewline = 2 435*1fa6dee9SAndroid Build Coastguard Worker} 436*1fa6dee9SAndroid Build Coastguard Worker 437*1fa6dee9SAndroid Build Coastguard Worker// Flush any pending whitespace, ignoring pending spaces if there is a pending newline 438*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) flushSpace() { 439*1fa6dee9SAndroid Build Coastguard Worker if p.pendingNewline == 1 { 440*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, '\n') 441*1fa6dee9SAndroid Build Coastguard Worker p.pad(p.curIndent()) 442*1fa6dee9SAndroid Build Coastguard Worker } else if p.pendingNewline == 2 { 443*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, "\n\n"...) 444*1fa6dee9SAndroid Build Coastguard Worker p.pad(p.curIndent()) 445*1fa6dee9SAndroid Build Coastguard Worker } else if p.pendingSpace == true && p.pendingNewline != -1 { 446*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, ' ') 447*1fa6dee9SAndroid Build Coastguard Worker } 448*1fa6dee9SAndroid Build Coastguard Worker 449*1fa6dee9SAndroid Build Coastguard Worker p.pendingSpace = false 450*1fa6dee9SAndroid Build Coastguard Worker p.pendingNewline = 0 451*1fa6dee9SAndroid Build Coastguard Worker} 452*1fa6dee9SAndroid Build Coastguard Worker 453*1fa6dee9SAndroid Build Coastguard Worker// Print a single comment, which may be a multi-line comment 454*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) printComment(cg *CommentGroup) { 455*1fa6dee9SAndroid Build Coastguard Worker for _, comment := range cg.Comments { 456*1fa6dee9SAndroid Build Coastguard Worker if !p.requestNewlinesForPos(comment.Pos()) { 457*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 458*1fa6dee9SAndroid Build Coastguard Worker } 459*1fa6dee9SAndroid Build Coastguard Worker for i, line := range comment.Comment { 460*1fa6dee9SAndroid Build Coastguard Worker line = strings.TrimRightFunc(line, unicode.IsSpace) 461*1fa6dee9SAndroid Build Coastguard Worker p.flushSpace() 462*1fa6dee9SAndroid Build Coastguard Worker if i != 0 { 463*1fa6dee9SAndroid Build Coastguard Worker lineIndent := strings.IndexFunc(line, func(r rune) bool { return !unicode.IsSpace(r) }) 464*1fa6dee9SAndroid Build Coastguard Worker lineIndent = max(lineIndent, p.curIndent()) 465*1fa6dee9SAndroid Build Coastguard Worker p.pad(lineIndent - p.curIndent()) 466*1fa6dee9SAndroid Build Coastguard Worker } 467*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, strings.TrimSpace(line)...) 468*1fa6dee9SAndroid Build Coastguard Worker if i < len(comment.Comment)-1 { 469*1fa6dee9SAndroid Build Coastguard Worker p._requestNewline() 470*1fa6dee9SAndroid Build Coastguard Worker } 471*1fa6dee9SAndroid Build Coastguard Worker } 472*1fa6dee9SAndroid Build Coastguard Worker if p.pos.Offset < comment.End().Offset { 473*1fa6dee9SAndroid Build Coastguard Worker p.pos = comment.End() 474*1fa6dee9SAndroid Build Coastguard Worker } 475*1fa6dee9SAndroid Build Coastguard Worker } 476*1fa6dee9SAndroid Build Coastguard Worker} 477*1fa6dee9SAndroid Build Coastguard Worker 478*1fa6dee9SAndroid Build Coastguard Worker// Print any comments that occur after the last token, and a trailing newline 479*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) flush() { 480*1fa6dee9SAndroid Build Coastguard Worker for _, c := range p.skippedComments { 481*1fa6dee9SAndroid Build Coastguard Worker if !p.requestNewlinesForPos(c.Pos()) { 482*1fa6dee9SAndroid Build Coastguard Worker p.requestSpace() 483*1fa6dee9SAndroid Build Coastguard Worker } 484*1fa6dee9SAndroid Build Coastguard Worker p.printComment(c) 485*1fa6dee9SAndroid Build Coastguard Worker } 486*1fa6dee9SAndroid Build Coastguard Worker for p.curComment < len(p.comments) { 487*1fa6dee9SAndroid Build Coastguard Worker p.printComment(p.comments[p.curComment]) 488*1fa6dee9SAndroid Build Coastguard Worker p.curComment++ 489*1fa6dee9SAndroid Build Coastguard Worker } 490*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, '\n') 491*1fa6dee9SAndroid Build Coastguard Worker} 492*1fa6dee9SAndroid Build Coastguard Worker 493*1fa6dee9SAndroid Build Coastguard Worker// Print whitespace to pad from column l to column max 494*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) pad(l int) { 495*1fa6dee9SAndroid Build Coastguard Worker if l > len(p.wsBuf) { 496*1fa6dee9SAndroid Build Coastguard Worker p.wsBuf = make([]byte, l) 497*1fa6dee9SAndroid Build Coastguard Worker for i := range p.wsBuf { 498*1fa6dee9SAndroid Build Coastguard Worker p.wsBuf[i] = ' ' 499*1fa6dee9SAndroid Build Coastguard Worker } 500*1fa6dee9SAndroid Build Coastguard Worker } 501*1fa6dee9SAndroid Build Coastguard Worker p.output = append(p.output, p.wsBuf[0:l]...) 502*1fa6dee9SAndroid Build Coastguard Worker} 503*1fa6dee9SAndroid Build Coastguard Worker 504*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) indent(i int) { 505*1fa6dee9SAndroid Build Coastguard Worker p.indentList = append(p.indentList, i) 506*1fa6dee9SAndroid Build Coastguard Worker} 507*1fa6dee9SAndroid Build Coastguard Worker 508*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) unindent(pos scanner.Position) { 509*1fa6dee9SAndroid Build Coastguard Worker p.printEndOfLineCommentsBefore(pos) 510*1fa6dee9SAndroid Build Coastguard Worker p.indentList = p.indentList[0 : len(p.indentList)-1] 511*1fa6dee9SAndroid Build Coastguard Worker} 512*1fa6dee9SAndroid Build Coastguard Worker 513*1fa6dee9SAndroid Build Coastguard Workerfunc (p *printer) curIndent() int { 514*1fa6dee9SAndroid Build Coastguard Worker return p.indentList[len(p.indentList)-1] 515*1fa6dee9SAndroid Build Coastguard Worker} 516*1fa6dee9SAndroid Build Coastguard Worker 517*1fa6dee9SAndroid Build Coastguard Workerfunc max(a, b int) int { 518*1fa6dee9SAndroid Build Coastguard Worker if a > b { 519*1fa6dee9SAndroid Build Coastguard Worker return a 520*1fa6dee9SAndroid Build Coastguard Worker } else { 521*1fa6dee9SAndroid Build Coastguard Worker return b 522*1fa6dee9SAndroid Build Coastguard Worker } 523*1fa6dee9SAndroid Build Coastguard Worker} 524*1fa6dee9SAndroid Build Coastguard Worker 525*1fa6dee9SAndroid Build Coastguard Workerfunc listHasMap(list []Expression) bool { 526*1fa6dee9SAndroid Build Coastguard Worker for _, value := range list { 527*1fa6dee9SAndroid Build Coastguard Worker if _, ok := value.(*Map); ok { 528*1fa6dee9SAndroid Build Coastguard Worker return true 529*1fa6dee9SAndroid Build Coastguard Worker } 530*1fa6dee9SAndroid Build Coastguard Worker } 531*1fa6dee9SAndroid Build Coastguard Worker return false 532*1fa6dee9SAndroid Build Coastguard Worker} 533