github.com/neugram/ng@v0.0.0-20180309130942-d472ff93d872/gengo/gengo.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 gengo implements a backend for the Neugram parser and
     6  // typechecker that generates a Go package.
     7  package gengo
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	goformat "go/format"
    13  	"path"
    14  	"path/filepath"
    15  	"sort"
    16  	"strings"
    17  	"unicode"
    18  	"unicode/utf8"
    19  
    20  	"neugram.io/ng/format"
    21  	"neugram.io/ng/syntax"
    22  	"neugram.io/ng/syntax/expr"
    23  	"neugram.io/ng/syntax/stmt"
    24  	"neugram.io/ng/syntax/tipe"
    25  	"neugram.io/ng/syntax/token"
    26  	"neugram.io/ng/typecheck"
    27  )
    28  
    29  func GenGo(filename, outGoPkgName string) (result []byte, err error) {
    30  	p := &printer{
    31  		buf:     new(bytes.Buffer),
    32  		c:       typecheck.New(filepath.Base(filename)), // TODO: extract a pkg name
    33  		imports: make(map[*tipe.Package]string),
    34  		eliders: make(map[tipe.Type]string),
    35  	}
    36  
    37  	abspath, err := filepath.Abs(filename)
    38  	if err != nil {
    39  		return nil, err
    40  	}
    41  
    42  	p.pkg, err = p.c.Check(abspath)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	if outGoPkgName == "" {
    48  		outGoPkgName = "gengo_" + strings.TrimSuffix(filepath.Base(filename), ".ng")
    49  	}
    50  	p.printf(`// generated by ng, do not edit
    51  
    52  package %s
    53  
    54  `, outGoPkgName)
    55  
    56  	usesShell := false
    57  	builtins := make(map[string]bool)
    58  	importPaths := []string{}
    59  	preFn := func(c *syntax.Cursor) bool {
    60  		switch node := c.Node.(type) {
    61  		case *stmt.Import:
    62  			importPaths = append(importPaths, node.Path)
    63  		case *expr.Ident:
    64  			// TODO: look up the typecheck.Obj for builtins
    65  			switch node.Name {
    66  			case "printf":
    67  				builtins["printf"] = true
    68  			case "print":
    69  				builtins["print"] = true
    70  			case "errorf":
    71  				builtins["errorf"] = true
    72  			}
    73  		case *expr.ShellList:
    74  			usesShell = true
    75  		}
    76  		return true
    77  	}
    78  	syntax.Walk(p.pkg.Syntax, preFn, nil)
    79  
    80  	// Lift imports to the top-level.
    81  	importSet := make(map[string]bool)
    82  	for _, imp := range importPaths {
    83  		importSet[imp] = true
    84  	}
    85  	// Name.
    86  	namedImports := make(map[string]string) // name -> path
    87  	for imp := range importSet {
    88  		name := "gengoimp_" + path.Base(imp)
    89  		i := 0
    90  		for namedImports[name] != "" {
    91  			i++
    92  			name = fmt.Sprintf("gengoimp_%s_%d", path.Base(imp), i)
    93  		}
    94  		namedImports[name] = imp
    95  		p.imports[p.c.Pkg(imp).Type] = name
    96  	}
    97  
    98  	p.printf("import (")
    99  	p.indent++
   100  
   101  	if builtins["printf"] || builtins["print"] || builtins["errorf"] {
   102  		p.newline()
   103  		p.printf(`"fmt"`)
   104  	}
   105  	if usesShell {
   106  		p.newline()
   107  		p.printf(`"fmt"`)
   108  		p.newline()
   109  		p.printf(`"os"`)
   110  		p.newline()
   111  		p.printf(`"reflect"`)
   112  		p.newline()
   113  		p.printf(`"neugram.io/ng/eval/environ"`)
   114  		p.newline()
   115  		p.printf(`"neugram.io/ng/eval/shell"`)
   116  		p.newline()
   117  		p.printf(`"neugram.io/ng/syntax/expr"`)
   118  		p.newline()
   119  		p.printf(`"neugram.io/ng/syntax/src"`)
   120  		p.newline()
   121  		p.printf(`"neugram.io/ng/syntax/token"`)
   122  	}
   123  
   124  	// Stable output is ensured by gofmt's sorting later.
   125  	for name, imp := range namedImports {
   126  		p.newline()
   127  		p.printf("%s %q", name, imp)
   128  	}
   129  
   130  	p.indent--
   131  	p.newline()
   132  	p.print(")")
   133  	p.newline()
   134  	p.newline()
   135  
   136  	if outGoPkgName == "main" {
   137  		p.printf("func main() {}")
   138  		p.newline()
   139  		p.newline()
   140  	}
   141  
   142  	// Lift package-level declarations to the top-level.
   143  	for _, obj := range p.pkg.Globals {
   144  		switch obj.Kind {
   145  		case typecheck.ObjType:
   146  			n := obj.Type.(*tipe.Named)
   147  			if len(n.Methods) > 0 {
   148  				continue // methodiks are hoisted elsewhere
   149  			}
   150  			p.printf("type %s ", obj.Name)
   151  			p.tipe(n.Type)
   152  		case typecheck.ObjVar:
   153  			p.printf("var %s ", obj.Name)
   154  			p.tipe(obj.Type)
   155  		case typecheck.ObjConst:
   156  			p.printf("const %s = %s", obj.Name, obj.Decl)
   157  		}
   158  		p.newline()
   159  		p.newline()
   160  	}
   161  
   162  	// Lift methodik declarations to the top-level.
   163  	methodiks := make(map[string][]*stmt.MethodikDecl)
   164  	preFn = func(c *syntax.Cursor) bool {
   165  		switch node := c.Node.(type) {
   166  		case *stmt.MethodikDecl:
   167  			methodiks[node.Name] = append(methodiks[node.Name], node)
   168  		}
   169  		return true
   170  	}
   171  	syntax.Walk(p.pkg.Syntax, preFn, nil)
   172  	methodiksFlat := make(map[string]*stmt.MethodikDecl)
   173  	for name, ms := range methodiks {
   174  		methodiksFlat[name] = ms[0]
   175  		if len(ms) == 1 {
   176  			continue
   177  		}
   178  		for i, m := range ms[1:] {
   179  			var newName string
   180  			for {
   181  				newName = fmt.Sprintf("%s_gengo%d", name, i)
   182  				if _, exists := methodiks[newName]; !exists {
   183  					break
   184  				}
   185  				i++
   186  			}
   187  			methodiksFlat[newName] = m
   188  			m.Type.Name = newName
   189  		}
   190  	}
   191  	var methodikNames []string
   192  	for name := range methodiksFlat {
   193  		methodikNames = append(methodikNames, name)
   194  	}
   195  	sort.Strings(methodikNames)
   196  	for _, name := range methodikNames {
   197  		m := methodiksFlat[name]
   198  		p.printf("// methodik %s", m.Name)
   199  		p.newline()
   200  		p.printf("type %s %s", m.Name, format.Type(m.Type.Type))
   201  		p.newline()
   202  		p.newline()
   203  		for _, method := range m.Methods {
   204  			p.funcLiteral(method, m.Name)
   205  			p.newline()
   206  			p.newline()
   207  		}
   208  	}
   209  
   210  	p.print("func init() {")
   211  	p.indent++
   212  	for _, s := range p.pkg.Syntax.Stmts {
   213  		switch s.(type) {
   214  		case *stmt.TypeDecl:
   215  			// handled above
   216  			continue
   217  		}
   218  
   219  		p.newline()
   220  		p.stmt(s)
   221  
   222  		if s, isAssign := s.(*stmt.Assign); isAssign {
   223  			// TODO: look to see if typecheck object is used,
   224  			//       only emit this if it isn't.
   225  			if s.Decl {
   226  				for _, e := range s.Left {
   227  					if ident, isIdent := e.(*expr.Ident); isIdent && ident.Name == "_" {
   228  						continue
   229  					}
   230  					p.newline()
   231  					p.print("_ = ")
   232  					p.expr(e)
   233  				}
   234  			}
   235  		}
   236  	}
   237  	p.indent--
   238  	p.newline()
   239  	p.print("}")
   240  
   241  	p.printBuiltins(builtins)
   242  	p.printEliders()
   243  	if usesShell {
   244  		p.printShell()
   245  	}
   246  
   247  	res, err := goformat.Source(p.buf.Bytes())
   248  	if err != nil {
   249  		lines := new(bytes.Buffer)
   250  		for i, line := range strings.Split(p.buf.String(), "\n") {
   251  			fmt.Fprintf(lines, "%3d: %s\n", i+1, line)
   252  		}
   253  		return nil, fmt.Errorf("gengo: bad generated source: %v\n%s", err, lines.String())
   254  	}
   255  
   256  	return res, nil
   257  }
   258  
   259  type printer struct {
   260  	buf    *bytes.Buffer
   261  	indent int
   262  
   263  	imports map[*tipe.Package]string // import package -> name
   264  	c       *typecheck.Checker
   265  	pkg     *typecheck.Package
   266  	eliders map[tipe.Type]string
   267  
   268  	underlying      bool // always print underlying type
   269  	typeCur         *tipe.Named
   270  	typePlugins     map[*tipe.Named]string // plugin pkg path
   271  	typePluginsUsed map[*tipe.Named]bool
   272  }
   273  
   274  func (p *printer) printShell() {
   275  	p.newline()
   276  	p.newline()
   277  	p.printf(`var _ = src.Pos{} // used in some expr.Shell prints`)
   278  	p.newline()
   279  	p.printf(`var _ = token.Token(0)`)
   280  	p.newline()
   281  	p.printf(`var shellState = &shell.State{
   282  	Env:   environ.NewFrom(os.Environ()),
   283  	Alias: environ.New(),
   284  }`)
   285  
   286  	p.newline()
   287  	p.newline()
   288  	p.printf(`func init() {
   289  	wd, err := os.Getwd()
   290  	if err == nil {
   291  		shellState.Env.Set("PWD", wd)
   292  	}
   293  }`)
   294  
   295  	p.newline()
   296  	p.newline()
   297  	p.printf(`func gengo_shell(e *expr.Shell, p gengo_shell_params) (string, error) {
   298  	str, err := shell.Run(shellState, p, e)
   299  	return str, err
   300  }
   301  
   302  func gengo_shell_elide(e *expr.Shell, p gengo_shell_params) string {
   303  	str, err := gengo_shell(e, p)
   304  	if err != nil {
   305  		panic(err)
   306  	}
   307  	return str
   308  }
   309  
   310  type gengo_shell_params map[string]reflect.Value
   311  
   312  func (p gengo_shell_params) Get(name string) string {
   313  	if v, found := p[name]; found {
   314  		vi := v.Interface()
   315  		if s, ok := vi.(string); ok {
   316  			return s
   317  		}
   318  		return fmt.Sprint(vi)
   319  	}
   320  	return shellState.Env.Get(name)
   321  }
   322  
   323  func (p gengo_shell_params) Set(name, value string) {
   324  	v, found := p[name]
   325  	if !found {
   326  		v = reflect.ValueOf(&value).Elem()
   327  		p[name] = v
   328  	}
   329  	if v.Kind() == reflect.String {
   330  		v.SetString(value)
   331  	} else {
   332  		fmt.Sscan(value, v)
   333  	}
   334  }
   335  
   336  func init() { shell.Init() }
   337  `)
   338  }
   339  
   340  func (p *printer) printBuiltins(builtins map[string]bool) {
   341  	if builtins["print"] {
   342  		p.newline()
   343  		p.newline()
   344  		p.print(`func print(args ...interface{}) {
   345  	for _, arg := range args {
   346  		fmt.Printf("%v", arg)
   347  	}
   348  	fmt.Print("\n")
   349  }`)
   350  	}
   351  
   352  	if builtins["printf"] {
   353  		p.newline()
   354  		p.newline()
   355  		p.print("func printf(f string, args ...interface{}) { fmt.Printf(f, args...) }")
   356  	}
   357  
   358  	if builtins["errorf"] {
   359  		p.newline()
   360  		p.newline()
   361  		p.print("func errorf(f string, args ...interface{}) error { return fmt.Errorf(f, args...) }")
   362  	}
   363  }
   364  
   365  func (p *printer) printEliders() {
   366  	for t, name := range p.eliders {
   367  		p.newline()
   368  		p.newline()
   369  		if typecheck.IsError(t) {
   370  			p.printf("func %s(err error) {", name)
   371  			p.indent++
   372  			p.newline()
   373  			p.printf("if err != nil { panic(err) }")
   374  			p.indent++
   375  			p.newline()
   376  			p.printf("}")
   377  			continue
   378  		}
   379  
   380  		p.printf("func %s(", name)
   381  		elems := t.(*tipe.Tuple).Elems
   382  		for i, elem := range elems {
   383  			if i == len(elems)-1 {
   384  				p.printf("err error")
   385  				continue
   386  			}
   387  			p.printf("arg%d ", i)
   388  			p.tipe(elem)
   389  			p.printf(", ")
   390  		}
   391  		p.printf(") (")
   392  		for i, elem := range elems[:len(elems)-1] {
   393  			if i > 0 {
   394  				p.printf(", ")
   395  			}
   396  			p.tipe(elem)
   397  		}
   398  		p.printf(") {")
   399  		p.indent++
   400  		p.newline()
   401  		p.printf("if err != nil { panic(err) }")
   402  		p.newline()
   403  		p.printf("return ")
   404  		for i := range elems[:len(elems)-1] {
   405  			if i > 0 {
   406  				p.printf(", ")
   407  			}
   408  			p.printf("arg%d", i)
   409  		}
   410  		p.indent++
   411  		p.newline()
   412  		p.printf("}")
   413  	}
   414  }
   415  
   416  func (p *printer) printf(format string, args ...interface{}) {
   417  	fmt.Fprintf(p.buf, format, args...)
   418  }
   419  
   420  func (p *printer) print(str string) {
   421  	p.buf.WriteString(str)
   422  }
   423  
   424  func (p *printer) newline() {
   425  	p.buf.WriteByte('\n')
   426  	for i := 0; i < p.indent; i++ {
   427  		p.buf.WriteByte('\t')
   428  	}
   429  }
   430  
   431  func (p *printer) expr(e expr.Expr) {
   432  	switch e := e.(type) {
   433  	case *expr.BasicLiteral:
   434  		if str, isStr := e.Value.(string); isStr {
   435  			p.printf("%q", str)
   436  		} else {
   437  			p.printf("%v", e.Value)
   438  		}
   439  	case *expr.Binary:
   440  		p.expr(e.Left)
   441  		p.printf(" %s ", e.Op)
   442  		p.expr(e.Right)
   443  	case *expr.Call:
   444  		if e.ElideError {
   445  			fnName := p.elider(p.c.Type(e))
   446  			p.printf("%s(", fnName)
   447  		}
   448  		p.expr(e.Func)
   449  		p.print("(")
   450  		for i, arg := range e.Args {
   451  			if i != 0 {
   452  				p.print(", ")
   453  			}
   454  			p.expr(arg)
   455  		}
   456  		if e.Ellipsis {
   457  			p.print("...")
   458  		}
   459  		p.print(")")
   460  		if e.ElideError {
   461  			p.print(")")
   462  		}
   463  	case *expr.CompLiteral:
   464  		p.tipe(e.Type)
   465  		p.print("{")
   466  		if len(e.Keys) > 0 {
   467  			p.indent++
   468  			for i, key := range e.Keys {
   469  				p.newline()
   470  				p.expr(key)
   471  				p.print(": ")
   472  				p.expr(e.Values[i])
   473  				p.print(",")
   474  			}
   475  			p.indent--
   476  			p.newline()
   477  		} else if len(e.Values) > 0 {
   478  			for i, elem := range e.Values {
   479  				if i > 0 {
   480  					p.print(", ")
   481  				}
   482  				p.expr(elem)
   483  			}
   484  		}
   485  		p.print("}")
   486  	case *expr.FuncLiteral:
   487  		if e.Name != "" {
   488  			gobj := p.pkg.GlobalNames[e.Name]
   489  			if gobj != nil && gobj.Decl == e {
   490  				p.printf("%s = ", e.Name)
   491  			} else {
   492  				p.printf("%s := ", e.Name)
   493  			}
   494  		}
   495  		p.funcLiteral(e, "")
   496  	case *expr.Ident:
   497  		if pkgType, isPkg := p.c.Type(e).(*tipe.Package); isPkg {
   498  			p.print(p.imports[pkgType])
   499  		} else {
   500  			p.print(e.Name)
   501  		}
   502  	case *expr.Index:
   503  		p.expr(e.Left)
   504  		p.print("[")
   505  		for i, index := range e.Indicies {
   506  			if i > 0 {
   507  				p.print(", ")
   508  			}
   509  			p.expr(index)
   510  		}
   511  		p.print("]")
   512  	case *expr.MapLiteral:
   513  		p.tipe(e.Type)
   514  		p.print("{")
   515  		p.indent++
   516  		for i, key := range e.Keys {
   517  			p.newline()
   518  			p.expr(key)
   519  			p.print(": ")
   520  			p.expr(e.Values[i])
   521  			p.print(",")
   522  		}
   523  		p.indent--
   524  		p.newline()
   525  		p.print("}")
   526  	case *expr.Selector:
   527  		p.expr(e.Left)
   528  		p.print(".")
   529  		p.expr(e.Right)
   530  	case *expr.Slice:
   531  		if e.Low != nil {
   532  			p.expr(e.Low)
   533  		}
   534  		p.buf.WriteString(":")
   535  		if e.High != nil {
   536  			p.expr(e.High)
   537  		}
   538  		if e.Max != nil {
   539  			p.buf.WriteString(":")
   540  			p.expr(e.Max)
   541  		}
   542  	case *expr.Shell:
   543  		if e.ElideError {
   544  			p.printf("gengo_shell_elide(%s, gengo_shell_params{", format.Debug(e))
   545  		} else {
   546  			p.printf("gengo_shell(%s, gengo_shell_params{", format.Debug(e))
   547  		}
   548  		if len(e.FreeVars) > 0 {
   549  			p.indent++
   550  			for _, name := range e.FreeVars {
   551  				p.newline()
   552  				p.printf("%q: reflect.ValueOf(&%s).Elem(),", name, name)
   553  			}
   554  			p.indent--
   555  			p.newline()
   556  		}
   557  		p.printf("})")
   558  	case *expr.ArrayLiteral:
   559  		p.tipe(e.Type)
   560  		p.print("{")
   561  		switch len(e.Keys) {
   562  		case 0:
   563  			for i, elem := range e.Values {
   564  				if i > 0 {
   565  					p.print(", ")
   566  				}
   567  				p.expr(elem)
   568  			}
   569  		default:
   570  			for i, elem := range e.Values {
   571  				if i > 0 {
   572  					p.print(", ")
   573  				}
   574  				p.expr(e.Keys[i])
   575  				p.print(": ")
   576  				p.expr(elem)
   577  			}
   578  		}
   579  		p.print("}")
   580  	case *expr.SliceLiteral:
   581  		p.tipe(e.Type)
   582  		p.print("{")
   583  		switch len(e.Keys) {
   584  		case 0:
   585  			for i, elem := range e.Values {
   586  				if i > 0 {
   587  					p.print(", ")
   588  				}
   589  				p.expr(elem)
   590  			}
   591  		default:
   592  			for i, elem := range e.Values {
   593  				if i > 0 {
   594  					p.print(", ")
   595  				}
   596  				p.expr(e.Keys[i])
   597  				p.print(": ")
   598  				p.expr(elem)
   599  			}
   600  		}
   601  		p.print("}")
   602  	case *expr.Type:
   603  		p.tipe(e.Type)
   604  	case *expr.TypeAssert:
   605  		p.expr(e.Left)
   606  		p.print(".(")
   607  		if e.Type == nil {
   608  			p.print("type")
   609  		} else {
   610  			p.tipe(e.Type)
   611  		}
   612  		p.print(")")
   613  	case *expr.Unary:
   614  		p.print(e.Op.String())
   615  		p.expr(e.Expr)
   616  		if e.Op == token.LeftParen {
   617  			p.print(")")
   618  		}
   619  	}
   620  }
   621  
   622  func (p *printer) stmt(s stmt.Stmt) {
   623  	switch s := s.(type) {
   624  	case *stmt.ConstSet:
   625  		p.print("const (")
   626  		p.indent++
   627  		for _, v := range s.Consts {
   628  			p.newline()
   629  			p.stmtConst(v)
   630  		}
   631  		p.indent--
   632  		p.newline()
   633  		p.print(")")
   634  	case *stmt.Const:
   635  		p.print("const ")
   636  		p.stmtConst(s)
   637  	case *stmt.VarSet:
   638  		p.print("var (")
   639  		p.indent++
   640  		for _, v := range s.Vars {
   641  			p.newline()
   642  			p.stmtVar(v)
   643  		}
   644  		p.indent--
   645  		p.newline()
   646  		p.print(")")
   647  	case *stmt.Var:
   648  		p.print("var ")
   649  		p.stmtVar(s)
   650  	case *stmt.Assign:
   651  		for i, e := range s.Left {
   652  			if i != 0 {
   653  				p.print(", ")
   654  			}
   655  			p.expr(e)
   656  		}
   657  		// TODO: A, b := ...
   658  		ident, _ := s.Left[0].(*expr.Ident)
   659  		var obj *typecheck.Obj
   660  		if ident != nil {
   661  			obj = p.c.Ident(ident)
   662  		}
   663  		if !s.Decl || (obj != nil && p.pkg.GlobalNames[obj.Name] == obj) {
   664  			p.print(" = ")
   665  		} else {
   666  			p.print(" := ")
   667  		}
   668  		for i, e := range s.Right {
   669  			if i != 0 {
   670  				p.print(", ")
   671  			}
   672  			p.expr(e)
   673  		}
   674  	case *stmt.Block:
   675  		p.print("{")
   676  		p.indent++
   677  		for _, s := range s.Stmts {
   678  			p.newline()
   679  			p.stmt(s)
   680  		}
   681  		p.indent--
   682  		p.newline()
   683  		p.print("}")
   684  	case *stmt.For:
   685  		p.print("for ")
   686  		if s.Init != nil {
   687  			p.stmt(s.Init)
   688  			p.print("; ")
   689  		}
   690  		if s.Cond != nil {
   691  			p.expr(s.Cond)
   692  			p.print("; ")
   693  		}
   694  		if s.Post != nil {
   695  			p.stmt(s.Post)
   696  		}
   697  		p.stmt(s.Body)
   698  	case *stmt.Go:
   699  		p.print("go ")
   700  		p.expr(s.Call)
   701  	case *stmt.If:
   702  		p.print("if ")
   703  		if s.Init != nil {
   704  			p.stmt(s.Init)
   705  			p.print("; ")
   706  		}
   707  		p.expr(s.Cond)
   708  		p.print(" ")
   709  		p.stmt(s.Body)
   710  		if s.Else != nil {
   711  			p.print(" else ")
   712  			p.stmt(s.Else)
   713  		}
   714  	case *stmt.ImportSet:
   715  		// lifted to top-level earlier
   716  	case *stmt.Import:
   717  		// lifted to top-level earlier
   718  	case *stmt.Range:
   719  		p.print("for ")
   720  		if s.Key != nil {
   721  			p.expr(s.Key)
   722  		}
   723  		if s.Val != nil {
   724  			p.print(", ")
   725  			p.expr(s.Val)
   726  		}
   727  		if s.Decl {
   728  			p.print(":")
   729  		}
   730  		if s.Key != nil || s.Val != nil {
   731  			p.print("= ")
   732  		}
   733  		p.print("range ")
   734  		p.expr(s.Expr)
   735  		p.stmt(s.Body)
   736  	case *stmt.Defer:
   737  		p.print("defer ")
   738  		p.expr(s.Expr)
   739  	case *stmt.Return:
   740  		p.print("return")
   741  		for i, e := range s.Exprs {
   742  			if i == 0 {
   743  				p.print(" ")
   744  			} else {
   745  				p.print(", ")
   746  			}
   747  			p.expr(e)
   748  		}
   749  	case *stmt.Simple:
   750  		// In Neugram it is valid to evaluate and not use an expression.
   751  		// (Because evaluating it has the standard side effect of
   752  		// printing its result.)
   753  		if p.isPure(s.Expr) {
   754  			p.print("_ = ")
   755  		}
   756  		p.expr(s.Expr)
   757  	case *stmt.Send:
   758  		p.expr(s.Chan)
   759  		p.print(" <- ")
   760  		p.expr(s.Value)
   761  	case *stmt.TypeDecl:
   762  		p.printf("type %s ", s.Name)
   763  		p.tipe(s.Type.Type)
   764  	case *stmt.TypeDeclSet:
   765  		p.print("type (")
   766  		p.indent++
   767  		for _, t := range s.TypeDecls {
   768  			p.newline()
   769  			p.printf("%s ", t.Name)
   770  			p.tipe(t.Type.Type)
   771  		}
   772  		p.indent--
   773  		p.newline()
   774  		p.print(")")
   775  	case *stmt.MethodikDecl:
   776  		// lifted to top-level earlier
   777  	case *stmt.Labeled:
   778  		p.indent--
   779  		p.newline()
   780  		p.printf("%s:", s.Label)
   781  		p.indent++
   782  		p.newline()
   783  		p.stmt(s.Stmt)
   784  	case *stmt.Branch:
   785  		p.printf("%s", s.Type)
   786  		if s.Label != "" {
   787  			p.printf(" %s", s.Label)
   788  		}
   789  	case *stmt.Switch:
   790  		p.print("switch ")
   791  		if s.Init != nil {
   792  			p.stmt(s.Init)
   793  			p.print("; ")
   794  		}
   795  		if s.Cond != nil {
   796  			p.expr(s.Cond)
   797  		}
   798  		p.print(" {")
   799  
   800  		for _, c := range s.Cases {
   801  			p.newline()
   802  			if c.Default {
   803  				p.print("default:")
   804  			} else {
   805  				p.print("case ")
   806  				for i, e := range c.Conds {
   807  					if i > 0 {
   808  						p.print(", ")
   809  					}
   810  					p.expr(e)
   811  				}
   812  				p.print(":")
   813  			}
   814  			p.indent++
   815  			for _, s := range c.Body.Stmts {
   816  				p.newline()
   817  				p.stmt(s)
   818  			}
   819  			p.indent--
   820  		}
   821  
   822  		p.newline()
   823  		p.print("}")
   824  
   825  	case *stmt.TypeSwitch:
   826  		p.print("switch ")
   827  		if s.Init != nil {
   828  			p.stmt(s.Init)
   829  			p.print("; ")
   830  		}
   831  		p.stmt(s.Assign)
   832  		p.print(" {")
   833  
   834  		for _, c := range s.Cases {
   835  			p.newline()
   836  			if c.Default {
   837  				p.print("default:")
   838  			} else {
   839  				p.print("case ")
   840  				for i, t := range c.Types {
   841  					if i > 0 {
   842  						p.print(", ")
   843  					}
   844  					p.tipe(t)
   845  				}
   846  				p.print(":")
   847  			}
   848  			p.indent++
   849  			for _, s := range c.Body.Stmts {
   850  				p.newline()
   851  				p.stmt(s)
   852  			}
   853  			p.indent--
   854  		}
   855  
   856  		p.newline()
   857  		p.print("}")
   858  	case *stmt.Select:
   859  		p.print("select {")
   860  		for _, c := range s.Cases {
   861  			p.newline()
   862  			if c.Default {
   863  				p.print("default:")
   864  			} else {
   865  				p.print("case ")
   866  				p.stmt(c.Stmt)
   867  				p.print(":")
   868  			}
   869  			p.indent++
   870  			for _, s := range c.Body.Stmts {
   871  				p.newline()
   872  				p.stmt(s)
   873  			}
   874  			p.indent--
   875  		}
   876  		p.newline()
   877  		p.print("}")
   878  	}
   879  }
   880  
   881  func (p *printer) isPure(e expr.Expr) bool {
   882  	switch e := e.(type) {
   883  	case *expr.Binary, *expr.Unary, *expr.Selector, *expr.Slice, *expr.CompLiteral, *expr.MapLiteral, *expr.ArrayLiteral, *expr.SliceLiteral, *expr.TableLiteral, *expr.Ident:
   884  		return true
   885  	case *expr.FuncLiteral:
   886  		return e.Name == ""
   887  	case *expr.Call:
   888  		t := p.c.Type(e.Func)
   889  		switch t {
   890  		case tipe.ComplexFunc, tipe.Imag, tipe.Real, tipe.New:
   891  			return true
   892  		}
   893  		return false
   894  	default:
   895  		return false
   896  	}
   897  }
   898  
   899  func (p *printer) stmtConst(s *stmt.Const) {
   900  	for i, n := range s.NameList {
   901  		if i != 0 {
   902  			p.print(", ")
   903  		}
   904  		p.print(n)
   905  	}
   906  	if s.Type != nil {
   907  		p.print(" ")
   908  		p.tipe(s.Type)
   909  	}
   910  	if len(s.Values) == 0 {
   911  		return
   912  	}
   913  	p.print(" = ")
   914  	for i, e := range s.Values {
   915  		if i != 0 {
   916  			p.print(", ")
   917  		}
   918  		p.expr(e)
   919  	}
   920  }
   921  
   922  func (p *printer) stmtVar(s *stmt.Var) {
   923  	for i, n := range s.NameList {
   924  		if i != 0 {
   925  			p.print(", ")
   926  		}
   927  		p.print(n)
   928  	}
   929  	if s.Type != nil {
   930  		p.print(" ")
   931  		p.tipe(s.Type)
   932  	}
   933  	if len(s.Values) == 0 {
   934  		return
   935  	}
   936  	p.print(" = ")
   937  	for i, e := range s.Values {
   938  		if i != 0 {
   939  			p.print(", ")
   940  		}
   941  		p.expr(e)
   942  	}
   943  }
   944  
   945  // TODO there is a huge amount of overlap here with the format package.
   946  //      deduplicate somehow.
   947  func (p *printer) tipe(t tipe.Type) {
   948  	switch t := t.(type) {
   949  	case tipe.Basic:
   950  		p.print(string(t))
   951  	case *tipe.Struct:
   952  		if len(t.Fields) == 0 {
   953  			p.print("struct{}")
   954  			return
   955  		}
   956  		p.print("struct {")
   957  		p.indent++
   958  		maxlen := 0
   959  		for _, sf := range t.Fields {
   960  			if len(sf.Name) > maxlen {
   961  				maxlen = len(sf.Name)
   962  			}
   963  		}
   964  		for _, sf := range t.Fields {
   965  			p.newline()
   966  			name := sf.Name
   967  			if name == "" {
   968  				name = "*ERROR*No*Name*"
   969  			}
   970  			if !sf.Embedded {
   971  				p.print(name)
   972  				for i := len(name); i <= maxlen; i++ {
   973  					p.print(" ")
   974  				}
   975  			}
   976  			p.tipe(sf.Type)
   977  			if sf.Tag != "" {
   978  				p.printf(" %q", sf.Tag)
   979  			}
   980  		}
   981  		p.indent--
   982  		p.newline()
   983  		p.print("}")
   984  	case *tipe.Named:
   985  		if t.PkgPath != "" && t.PkgPath != p.pkg.Path {
   986  			pkg := p.c.Pkg(t.PkgPath)
   987  			p.print(p.imports[pkg.Type])
   988  			p.print(".")
   989  			p.print(t.Name)
   990  		} else if p.underlying && t.Name != "error" {
   991  			if t == p.typeCur {
   992  				p.print(t.Name)
   993  			} else if pkgpath := p.typePlugins[t]; pkgpath != "" {
   994  				// This type has methods and previously
   995  				// declared in a plugin. Use it.
   996  				p.typePluginsUsed[t] = true
   997  				p.print(path.Base(pkgpath))
   998  				p.print(".")
   999  				p.print(t.Name)
  1000  			} else {
  1001  				p.tipe(tipe.Underlying(t))
  1002  			}
  1003  		} else {
  1004  			p.print(t.Name)
  1005  		}
  1006  	case *tipe.Pointer:
  1007  		p.print("*")
  1008  		p.tipe(t.Elem)
  1009  	case *tipe.Unresolved:
  1010  		if t.Package != "" {
  1011  			p.print(t.Package)
  1012  			p.print(".")
  1013  		}
  1014  		p.print(t.Name)
  1015  	case *tipe.Array:
  1016  		// Do not print the ellipsis as we may be printing
  1017  		// a variable declaration for a global without the
  1018  		// initializer.
  1019  		p.printf("[%d]", t.Len)
  1020  		p.tipe(t.Elem)
  1021  	case *tipe.Slice:
  1022  		p.print("[]")
  1023  		p.tipe(t.Elem)
  1024  	case *tipe.Interface:
  1025  		if len(t.Methods) == 0 {
  1026  			p.print("interface{}")
  1027  			return
  1028  		}
  1029  		p.print("interface {")
  1030  		p.indent++
  1031  		names := make([]string, 0, len(t.Methods))
  1032  		for name := range t.Methods {
  1033  			names = append(names, name)
  1034  		}
  1035  		sort.Strings(names)
  1036  		for _, name := range names {
  1037  			p.newline()
  1038  			p.print(name)
  1039  			p.tipeFuncSig(t.Methods[name])
  1040  		}
  1041  		p.indent--
  1042  		p.newline()
  1043  		p.print("}")
  1044  	case *tipe.Map:
  1045  		p.print("map[")
  1046  		p.tipe(t.Key)
  1047  		p.print("]")
  1048  		p.tipe(t.Value)
  1049  	case *tipe.Chan:
  1050  		if t.Direction == tipe.ChanRecv {
  1051  			p.print("<-")
  1052  		}
  1053  		p.print("chan")
  1054  		if t.Direction == tipe.ChanSend {
  1055  			p.print("<-")
  1056  		}
  1057  		p.print(" ")
  1058  		p.tipe(t.Elem)
  1059  	case *tipe.Func:
  1060  		p.print("func")
  1061  		p.tipeFuncSig(t)
  1062  	case *tipe.Alias:
  1063  		p.print(t.Name)
  1064  	case *tipe.Tuple:
  1065  		p.print("(")
  1066  		for i, elt := range t.Elems {
  1067  			if i > 0 {
  1068  				p.print(", ")
  1069  			}
  1070  			p.tipe(elt)
  1071  		}
  1072  		p.print(")")
  1073  	case *tipe.Ellipsis:
  1074  		p.print("...")
  1075  		p.tipe(t.Elem)
  1076  	default:
  1077  		panic(fmt.Sprintf("unknown type: %T", t))
  1078  	}
  1079  }
  1080  
  1081  func (p *printer) tipeFuncSig(t *tipe.Func) {
  1082  	p.print("(")
  1083  	if t.Params != nil {
  1084  		for i, elem := range t.Params.Elems {
  1085  			if i > 0 {
  1086  				p.print(", ")
  1087  			}
  1088  			p.tipe(elem)
  1089  		}
  1090  	}
  1091  	p.print(")")
  1092  	if t.Results != nil && len(t.Results.Elems) > 0 {
  1093  		p.print(" ")
  1094  		if len(t.Results.Elems) > 1 {
  1095  			p.print("(")
  1096  		}
  1097  		for i, elem := range t.Results.Elems {
  1098  			if i > 0 {
  1099  				p.print(", ")
  1100  			}
  1101  			p.tipe(elem)
  1102  		}
  1103  		if len(t.Results.Elems) > 1 {
  1104  			p.print(")")
  1105  		}
  1106  	}
  1107  }
  1108  
  1109  func (p *printer) funcLiteral(e *expr.FuncLiteral, recvTypeName string) {
  1110  	if recvTypeName != "" {
  1111  		ptr := ""
  1112  		if e.PointerReceiver {
  1113  			ptr = "*"
  1114  		}
  1115  		p.printf("func (%s %s%s) %s(", e.ReceiverName, ptr, recvTypeName, e.Name)
  1116  	} else {
  1117  		p.print("func(")
  1118  	}
  1119  	for i, name := range e.ParamNames {
  1120  		if i != 0 {
  1121  			p.print(", ")
  1122  		}
  1123  		p.print(name)
  1124  		p.print(" ")
  1125  		p.tipe(e.Type.Params.Elems[i])
  1126  	}
  1127  	p.print(") ")
  1128  	if len(e.ResultNames) != 0 {
  1129  		p.print("(")
  1130  		for i, name := range e.ResultNames {
  1131  			if i != 0 {
  1132  				p.print(", ")
  1133  			}
  1134  			p.print(name)
  1135  			p.print(" ")
  1136  			p.tipe(e.Type.Results.Elems[i])
  1137  		}
  1138  		p.print(")")
  1139  	}
  1140  	if e.Body != nil {
  1141  		p.print(" ")
  1142  		p.stmt(e.Body.(*stmt.Block))
  1143  	}
  1144  }
  1145  
  1146  func (p *printer) elider(t tipe.Type) string {
  1147  	name := p.eliders[t]
  1148  	if name == "" {
  1149  		name = fmt.Sprintf("gengo_elider%d", len(p.eliders))
  1150  		p.eliders[t] = name
  1151  	}
  1152  	return name
  1153  }
  1154  
  1155  func isExported(name string) bool {
  1156  	ch, _ := utf8.DecodeRuneInString(name)
  1157  	return unicode.IsUpper(ch)
  1158  }
  1159  
  1160  func GenNamedType(t *tipe.Named, methods []*expr.FuncLiteral, pkgPath string, typePlugins map[*tipe.Named]string) (pkgb, mainb []byte, err error) {
  1161  	p := &printer{
  1162  		buf:             new(bytes.Buffer),
  1163  		imports:         make(map[*tipe.Package]string),
  1164  		eliders:         make(map[tipe.Type]string),
  1165  		underlying:      true,
  1166  		typeCur:         t,
  1167  		typePlugins:     typePlugins,
  1168  		typePluginsUsed: make(map[*tipe.Named]bool),
  1169  	}
  1170  
  1171  	p.printf("var Zero %s", t.Name)
  1172  	p.newline()
  1173  	p.newline()
  1174  
  1175  	p.printf("type %s ", t.Name)
  1176  	p.tipe(tipe.Underlying(t.Type))
  1177  
  1178  	for _, mOrig := range methods {
  1179  		p.newline()
  1180  		p.newline()
  1181  
  1182  		m := new(expr.FuncLiteral)
  1183  		*m = *mOrig
  1184  		m.PointerReceiver = true
  1185  		for i := range m.ParamNames {
  1186  			if m.ParamNames[i] == "" {
  1187  				m.ParamNames[i] = fmt.Sprintf("gengo_param_%d", i)
  1188  			}
  1189  		}
  1190  		for i := range m.ResultNames {
  1191  			if m.ResultNames[i] == "" {
  1192  				m.ResultNames[i] = fmt.Sprintf("gengo_result_%d", i)
  1193  			}
  1194  		}
  1195  		m.Body = nil
  1196  		p.funcLiteral(m, t.Name)
  1197  		p.printf(" {")
  1198  		p.indent++
  1199  		p.newline()
  1200  
  1201  		p.printf("gengo_in := make([]reflect.Value, %d)", 1+len(m.ParamNames))
  1202  		p.newline()
  1203  		p.printf("gengo_in[0] = reflect.ValueOf(unsafe.Pointer(%s))", m.ReceiverName)
  1204  		for i, name := range m.ParamNames {
  1205  			p.newline()
  1206  			p.printf("gengo_in[%d] = reflect.ValueOf(%s)", 1+i, name)
  1207  		}
  1208  		p.newline()
  1209  		if len(m.ResultNames) > 0 {
  1210  			p.printf("var res interface{}")
  1211  			p.newline()
  1212  			p.printf("gengo_out := ")
  1213  		}
  1214  		p.printf("Type_Method_%s.Call(gengo_in)", m.Name)
  1215  		for i, name := range m.ResultNames {
  1216  			p.newline()
  1217  			p.printf("res = gengo_out[%d].Interface()", i)
  1218  			p.newline()
  1219  			p.printf("if res != nil {")
  1220  			p.indent++
  1221  			p.newline()
  1222  			p.printf("%s = gengo_out[%d].Interface().(", name, i)
  1223  			p.tipe(m.Type.Results.Elems[i])
  1224  			p.printf(")")
  1225  			p.indent--
  1226  			p.newline()
  1227  			p.printf("}")
  1228  		}
  1229  
  1230  		p.newline()
  1231  		p.printf("return ")
  1232  		for i, name := range m.ResultNames {
  1233  			if i > 0 {
  1234  				p.printf(", ")
  1235  			}
  1236  			p.printf("%s", name)
  1237  		}
  1238  
  1239  		p.indent--
  1240  		p.newline()
  1241  		p.printf("}")
  1242  	}
  1243  
  1244  	p.newline()
  1245  	p.newline()
  1246  	p.printf("var (")
  1247  	p.indent++
  1248  	for _, m := range methods {
  1249  		p.newline()
  1250  		p.printf("Type_Method_%s reflect.Value", m.Name)
  1251  	}
  1252  	p.indent--
  1253  	p.newline()
  1254  	p.printf(")")
  1255  
  1256  	rem := p.buf.Bytes()
  1257  	p.buf = new(bytes.Buffer)
  1258  
  1259  	// Put file header on after building contents so we know what
  1260  	// packages to import.
  1261  	p.printf("// generated by ng, do not edit")
  1262  	p.newline()
  1263  	p.newline()
  1264  	p.printf("package %s", t.Name)
  1265  	p.newline()
  1266  	p.newline()
  1267  
  1268  	imports := []string{}
  1269  	if len(methods) > 0 {
  1270  		imports = append(imports, "reflect", "unsafe")
  1271  	}
  1272  	for t := range p.typePluginsUsed {
  1273  		imports = append(imports, p.typePlugins[t])
  1274  	}
  1275  	sort.Strings(imports)
  1276  	p.printf("import (")
  1277  	p.indent++
  1278  	for _, imp := range imports {
  1279  		p.newline()
  1280  		p.printf("%q", imp)
  1281  	}
  1282  	p.indent--
  1283  	p.newline()
  1284  	p.printf(")")
  1285  	p.newline()
  1286  	p.newline()
  1287  
  1288  	p.buf.Write(rem)
  1289  
  1290  	pkgb, err = goformat.Source(p.buf.Bytes())
  1291  	fmt.Printf("pkgb: %s\n", pkgb)
  1292  	if err != nil {
  1293  		lines := new(bytes.Buffer)
  1294  		for i, line := range strings.Split(p.buf.String(), "\n") {
  1295  			fmt.Fprintf(lines, "%3d: %s\n", i+1, line)
  1296  		}
  1297  		return nil, nil, fmt.Errorf("gengo: bad generated pkg source: %v\n%s", err, lines.String())
  1298  	}
  1299  
  1300  	p.buf.Reset()
  1301  	p.printf("// generated by ng, do not edit")
  1302  	p.newline()
  1303  	p.newline()
  1304  	p.printf("package main")
  1305  	p.newline()
  1306  	p.newline()
  1307  
  1308  	p.printf("import (")
  1309  	p.indent++
  1310  	p.newline()
  1311  	p.printf("%q", pkgPath)
  1312  	p.indent--
  1313  	p.newline()
  1314  	p.printf(")")
  1315  	p.newline()
  1316  	p.newline()
  1317  	pkgImport := path.Base(pkgPath)
  1318  	p.printf("var Zero = %s.Zero", pkgImport)
  1319  	for _, m := range methods {
  1320  		p.newline()
  1321  		p.printf("var Type_Method_%s = &%s.Type_Method_%s", m.Name, pkgImport, m.Name)
  1322  	}
  1323  
  1324  	mainb, err = goformat.Source(p.buf.Bytes())
  1325  	if err != nil {
  1326  		lines := new(bytes.Buffer)
  1327  		for i, line := range strings.Split(p.buf.String(), "\n") {
  1328  			fmt.Fprintf(lines, "%3d: %s\n", i+1, line)
  1329  		}
  1330  		return nil, nil, fmt.Errorf("gengo: bad generated source: %v\n%s", err, lines.String())
  1331  	}
  1332  
  1333  	return pkgb, mainb, nil
  1334  }