github.com/neugram/ng@v0.0.0-20180309130942-d472ff93d872/format/expr.go (about) 1 // Copyright 2017 The Neugram Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package format 6 7 // TODO: general pretty printer 8 9 import ( 10 "bytes" 11 "fmt" 12 13 "neugram.io/ng/syntax/expr" 14 "neugram.io/ng/syntax/stmt" 15 "neugram.io/ng/syntax/token" 16 ) 17 18 type printer struct { 19 buf *bytes.Buffer 20 indent int 21 } 22 23 func (p *printer) expr(e expr.Expr) { 24 switch e := e.(type) { 25 case *expr.Binary: 26 WriteExpr(p.buf, e.Left) 27 p.buf.WriteString(e.Op.String()) 28 WriteExpr(p.buf, e.Right) 29 case *expr.Unary: 30 p.buf.WriteString(e.Op.String()) 31 WriteExpr(p.buf, e.Expr) 32 if e.Op == token.LeftParen { 33 p.buf.WriteByte(')') 34 } 35 case *expr.Bad: 36 fmt.Fprintf(p.buf, "bad(%q)", e.Error) 37 case *expr.Slice: 38 if e.Low != nil { 39 p.expr(e.Low) 40 } 41 p.buf.WriteString(":") 42 if e.High != nil { 43 p.expr(e.High) 44 } 45 if e.Max != nil { 46 p.buf.WriteString(":") 47 p.expr(e.Max) 48 } 49 case *expr.Selector: 50 p.expr(e.Left) 51 p.buf.WriteString("." + e.Right.Name) 52 case *expr.BasicLiteral: 53 p.buf.WriteString(fmt.Sprintf("%v", e.Value)) 54 case *expr.FuncLiteral: 55 p.buf.WriteString("func") 56 if e.ReceiverName != "" { 57 ptr := "" 58 if e.PointerReceiver { 59 ptr = "*" 60 } 61 fmt.Fprintf(p.buf, " (%s%s)", ptr, e.ReceiverName) 62 } 63 if e.Name != "" { 64 p.buf.WriteByte(' ') 65 p.buf.WriteString(e.Name) 66 } 67 p.buf.WriteByte('(') 68 69 // Similar to tipeFuncSig, but with parameter names. 70 if len(e.ParamNames) > 0 { 71 for i, name := range e.ParamNames { 72 if i > 0 { 73 p.buf.WriteString(", ") 74 } 75 if name != "" { 76 p.buf.WriteString(name) 77 p.buf.WriteByte(' ') 78 } 79 // TODO: elide types for equal subsequent params 80 p.tipe(e.Type.Params.Elems[i]) 81 } 82 } 83 p.buf.WriteString(")") 84 if len(e.ResultNames) == 1 && e.ResultNames[0] == "" { 85 p.buf.WriteString(" ") 86 p.tipe(e.Type.Results.Elems[0]) 87 } else if len(e.ResultNames) != 0 { 88 p.buf.WriteString(" (") 89 for i, name := range e.ResultNames { 90 if i > 0 { 91 p.buf.WriteString(", ") 92 } 93 if name != "" { 94 p.buf.WriteString(name) 95 p.buf.WriteByte(' ') 96 } 97 // TODO: elide types for equal subsequent params 98 p.tipe(e.Type.Results.Elems[i]) 99 } 100 p.buf.WriteString(")") 101 } 102 103 if e.Body != nil { 104 p.buf.WriteString(" ") 105 p.stmt(e.Body.(*stmt.Block)) 106 } 107 case *expr.CompLiteral: 108 p.tipe(e.Type) 109 p.print("{") 110 if len(e.Keys) > 0 { 111 p.indent++ 112 for i, key := range e.Keys { 113 p.newline() 114 p.expr(key) 115 p.print(": ") 116 p.expr(e.Values[i]) 117 p.print(",") 118 } 119 p.indent-- 120 p.newline() 121 } else if len(e.Values) > 0 { 122 for i, elem := range e.Values { 123 if i > 0 { 124 p.print(", ") 125 } 126 p.expr(elem) 127 } 128 } 129 p.print("}") 130 case *expr.MapLiteral: 131 p.tipe(e.Type) 132 p.print("{") 133 p.indent++ 134 for i, key := range e.Keys { 135 p.newline() 136 p.expr(key) 137 p.print(": ") 138 p.expr(e.Values[i]) 139 p.print(",") 140 } 141 p.indent-- 142 p.newline() 143 p.print("}") 144 case *expr.ArrayLiteral: 145 p.tipe(e.Type) 146 p.print("{") 147 switch len(e.Keys) { 148 case 0: 149 for i, elem := range e.Values { 150 if i > 0 { 151 p.print(", ") 152 } 153 p.expr(elem) 154 } 155 default: 156 for i, elem := range e.Values { 157 if i > 0 { 158 p.print(", ") 159 } 160 p.expr(e.Keys[i]) 161 p.print(": ") 162 p.expr(elem) 163 } 164 } 165 p.print("}") 166 case *expr.SliceLiteral: 167 p.tipe(e.Type) 168 p.print("{") 169 switch len(e.Keys) { 170 case 0: 171 for i, elem := range e.Values { 172 if i > 0 { 173 p.print(", ") 174 } 175 p.expr(elem) 176 } 177 default: 178 for i, elem := range e.Values { 179 if i > 0 { 180 p.print(", ") 181 } 182 p.expr(e.Keys[i]) 183 p.print(": ") 184 p.expr(elem) 185 } 186 } 187 p.print("}") 188 case *expr.TableLiteral: 189 p.tipe(e.Type) 190 p.print("{") 191 if len(e.ColNames) > 0 { 192 p.print("{|") 193 for i, col := range e.ColNames { 194 if i > 0 { 195 p.print(", ") 196 } 197 p.expr(col) 198 } 199 p.print("|}") 200 } 201 if len(e.Rows) > 0 { 202 p.print(", ") 203 for i, row := range e.Rows { 204 if i > 0 { 205 p.print(", ") 206 } 207 p.print("{") 208 for j, r := range row { 209 if j > 0 { 210 p.print(", ") 211 } 212 p.expr(r) 213 } 214 p.print("}") 215 } 216 p.print("}") 217 } 218 p.print("}") 219 case *expr.Type: 220 p.tipe(e.Type) 221 case *expr.Ident: 222 p.buf.WriteString(e.Name) 223 case *expr.Index: 224 p.expr(e.Left) 225 p.buf.WriteString("[") 226 for i, idx := range e.Indicies { 227 if i > 0 { 228 p.buf.WriteString(":") 229 } 230 p.expr(idx) 231 } 232 p.buf.WriteString("]") 233 case *expr.TypeAssert: 234 p.expr(e.Left) 235 p.buf.WriteString(".(") 236 if e.Type == nil { 237 p.buf.WriteString("type") 238 } else { 239 WriteType(p.buf, e.Type) 240 } 241 p.buf.WriteString(")") 242 case *expr.Call: 243 WriteExpr(p.buf, e.Func) 244 p.buf.WriteString("(") 245 for i, arg := range e.Args { 246 if i > 0 { 247 p.buf.WriteString(", ") 248 } 249 WriteExpr(p.buf, arg) 250 } 251 p.buf.WriteString(")") 252 case *expr.Shell: 253 if len(e.Cmds) == 1 { 254 p.buf.WriteString("$$ ") 255 p.expr(e.Cmds[0]) 256 p.buf.WriteString(" $$") 257 } else { 258 p.buf.WriteString("$$") 259 for _, cmd := range e.Cmds { 260 p.newline() 261 p.expr(cmd) 262 } 263 p.newline() 264 p.buf.WriteString("$$") 265 } 266 case *expr.ShellList: 267 for i, andor := range e.AndOr { 268 if i > 0 { 269 if e.AndOr[i-1].Background { 270 p.buf.WriteByte(' ') 271 } else { 272 p.buf.WriteString("; ") 273 } 274 } 275 p.expr(andor) 276 } 277 case *expr.ShellAndOr: 278 for i, pl := range e.Pipeline { 279 p.expr(pl) 280 if i < len(e.Sep) { 281 switch e.Sep[i] { 282 case token.LogicalAnd: 283 p.buf.WriteString(" && ") 284 case token.LogicalOr: 285 p.buf.WriteString(" || ") 286 default: 287 p.printf(" <bad separator: %v> ", e.Sep[i]) 288 } 289 } else if len(e.Sep) < len(e.Pipeline)-1 { 290 p.buf.WriteString(" <missing separator> ") 291 } 292 } 293 if e.Background { 294 p.buf.WriteString(" &") 295 } 296 case *expr.ShellPipeline: 297 if e.Bang { 298 p.buf.WriteString("! ") 299 } 300 for i, cmd := range e.Cmd { 301 if i > 0 { 302 p.buf.WriteString(" | ") 303 } 304 p.expr(cmd) 305 } 306 case *expr.ShellCmd: 307 if e.SimpleCmd != nil { 308 if e.Subshell != nil { 309 p.printf("<bad shellcmd has simple and subshell> ") 310 } 311 p.expr(e.SimpleCmd) 312 } else if e.Subshell != nil { 313 p.buf.WriteByte('(') 314 p.expr(e.Subshell) 315 p.buf.WriteByte(')') 316 } else { 317 p.printf("<bad shellcmd is empty>") 318 } 319 case *expr.ShellSimpleCmd: 320 for i, kv := range e.Assign { 321 if i > 0 { 322 p.buf.WriteByte(' ') 323 } 324 p.printf("%s=%s", kv.Key, kv.Value) 325 } 326 if len(e.Assign) > 0 && len(e.Args) > 0 { 327 p.buf.WriteByte(' ') 328 } 329 for i, arg := range e.Args { 330 if i > 0 { 331 p.buf.WriteByte(' ') 332 } 333 p.buf.WriteString(arg) 334 } 335 if (len(e.Assign) > 0 || len(e.Args) > 0) && len(e.Redirect) > 0 { 336 p.buf.WriteByte(' ') 337 } 338 for i, r := range e.Redirect { 339 if i > 0 { 340 p.buf.WriteByte(' ') 341 } 342 if r.Number != nil { 343 p.printf("%d", *r.Number) 344 } 345 p.buf.WriteString(r.Token.String()) 346 p.buf.WriteString(r.Filename) 347 } 348 default: 349 p.printf("format: unknown expr %T: ", e) 350 WriteDebug(p.buf, e) 351 } 352 } 353 354 func (p *printer) printf(format string, args ...interface{}) { 355 fmt.Fprintf(p.buf, format, args...) 356 } 357 358 func (p *printer) print(args ...interface{}) { 359 fmt.Fprint(p.buf, args...) 360 } 361 362 func (p *printer) newline() { 363 p.buf.WriteByte('\n') 364 for i := 0; i < p.indent; i++ { 365 p.buf.WriteByte('\t') 366 } 367 } 368 369 func WriteExpr(buf *bytes.Buffer, e expr.Expr) { 370 p := &printer{ 371 buf: buf, 372 } 373 p.expr(e) 374 } 375 376 func Expr(e expr.Expr) string { 377 buf := new(bytes.Buffer) 378 WriteExpr(buf, e) 379 return buf.String() 380 }