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  }