github.com/goplus/gop@v1.2.6/x/format/gopstyle.go (about)

     1  /*
     2   * Copyright (c) 2021 The GoPlus Authors (goplus.org). All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package format
    18  
    19  import (
    20  	"bytes"
    21  	"go/types"
    22  	"path"
    23  	"strconv"
    24  
    25  	"github.com/goplus/gop/ast"
    26  	"github.com/goplus/gop/format"
    27  	"github.com/goplus/gop/parser"
    28  	"github.com/goplus/gop/token"
    29  )
    30  
    31  // -----------------------------------------------------------------------------
    32  
    33  func GopstyleSource(src []byte, filename ...string) (ret []byte, err error) {
    34  	var fname string
    35  	if filename != nil {
    36  		fname = filename[0]
    37  	}
    38  	fset := token.NewFileSet()
    39  	var f *ast.File
    40  	if f, err = parser.ParseFile(fset, fname, src, parser.ParseComments); err == nil {
    41  		Gopstyle(f)
    42  		var buf bytes.Buffer
    43  		if err = format.Node(&buf, fset, f); err == nil {
    44  			ret = buf.Bytes()
    45  		}
    46  	}
    47  	return
    48  }
    49  
    50  // -----------------------------------------------------------------------------
    51  
    52  func Gopstyle(file *ast.File) {
    53  	if identEqual(file.Name, "main") {
    54  		file.NoPkgDecl = true
    55  	}
    56  	if idx, fn := findFuncDecl(file.Decls, "main"); idx >= 0 {
    57  		last := len(file.Decls) - 1
    58  		if idx == last {
    59  			file.ShadowEntry = fn
    60  			// TODO: idx != last: swap main func to last
    61  			// TODO: should also swap file.Comments
    62  			/*
    63  				fn := file.Decls[idx]
    64  				copy(file.Decls[idx:], file.Decls[idx+1:])
    65  				file.Decls[last] = fn
    66  			*/
    67  		}
    68  	}
    69  	formatFile(file)
    70  }
    71  
    72  func findFuncDecl(decls []ast.Decl, name string) (int, *ast.FuncDecl) {
    73  	for i, decl := range decls {
    74  		if fn, ok := decl.(*ast.FuncDecl); ok {
    75  			if identEqual(fn.Name, name) {
    76  				return i, fn
    77  			}
    78  		}
    79  	}
    80  	return -1, nil
    81  }
    82  
    83  func findDecl(decls []ast.Decl, v ast.Decl) int {
    84  	for i, decl := range decls {
    85  		if decl == v {
    86  			return i
    87  		}
    88  	}
    89  	return -1
    90  }
    91  
    92  func findSpec(specs []ast.Spec, v ast.Spec) int {
    93  	for i, spec := range specs {
    94  		if spec == v {
    95  			return i
    96  		}
    97  	}
    98  	return -1
    99  }
   100  
   101  func deleteDecl(decls []ast.Decl, v ast.Decl) []ast.Decl {
   102  	if idx := findDecl(decls, v); idx >= 0 {
   103  		decls = append(decls[:idx], decls[idx+1:]...)
   104  	}
   105  	return decls
   106  }
   107  
   108  func deleteSpec(specs []ast.Spec, v ast.Spec) []ast.Spec {
   109  	if idx := findSpec(specs, v); idx >= 0 {
   110  		specs = append(specs[:idx], specs[idx+1:]...)
   111  	}
   112  	return specs
   113  }
   114  
   115  func startWithLowerCase(v *ast.Ident) {
   116  	if c := v.Name[0]; c >= 'A' && c <= 'Z' {
   117  		v.Name = string(c+('a'-'A')) + v.Name[1:]
   118  	}
   119  }
   120  
   121  func identEqual(v *ast.Ident, name string) bool {
   122  	return v != nil && v.Name == name
   123  }
   124  
   125  func toString(l *ast.BasicLit) string {
   126  	if l.Kind == token.STRING {
   127  		s, err := strconv.Unquote(l.Value)
   128  		if err == nil {
   129  			return s
   130  		}
   131  	}
   132  	panic("TODO: toString - convert ast.BasicLit to string failed")
   133  }
   134  
   135  // -----------------------------------------------------------------------------
   136  
   137  type importCtx struct {
   138  	pkgPath string
   139  	decl    *ast.GenDecl
   140  	spec    *ast.ImportSpec
   141  	isUsed  bool
   142  }
   143  
   144  type formatCtx struct {
   145  	imports map[string]*importCtx
   146  	scope   *types.Scope
   147  }
   148  
   149  func (ctx *formatCtx) insert(name string) {
   150  	o := types.NewParam(token.NoPos, nil, name, types.Typ[types.UntypedNil])
   151  	ctx.scope.Insert(o)
   152  }
   153  
   154  func (ctx *formatCtx) enterBlock() *types.Scope {
   155  	old := ctx.scope
   156  	ctx.scope = types.NewScope(old, token.NoPos, token.NoPos, "")
   157  	return old
   158  }
   159  
   160  func (ctx *formatCtx) leaveBlock(old *types.Scope) {
   161  	ctx.scope = old
   162  }
   163  
   164  func formatFile(file *ast.File) {
   165  	var funcs []*ast.FuncDecl
   166  	ctx := &formatCtx{
   167  		imports: make(map[string]*importCtx),
   168  		scope:   types.NewScope(nil, token.NoPos, token.NoPos, ""),
   169  	}
   170  	for _, decl := range file.Decls {
   171  		switch v := decl.(type) {
   172  		case *ast.FuncDecl:
   173  			// delay the process, because package level vars need to be processed first.
   174  			funcs = append(funcs, v)
   175  		case *ast.GenDecl:
   176  			switch v.Tok {
   177  			case token.IMPORT:
   178  				for _, item := range v.Specs {
   179  					var spec = item.(*ast.ImportSpec)
   180  					var pkgPath = toString(spec.Path)
   181  					var name string
   182  					if spec.Name == nil {
   183  						name = path.Base(pkgPath) // TODO: open pkgPath to get pkgName
   184  					} else {
   185  						name = spec.Name.Name
   186  						if name == "." || name == "_" {
   187  							continue
   188  						}
   189  					}
   190  					ctx.imports[name] = &importCtx{pkgPath: pkgPath, decl: v, spec: spec}
   191  				}
   192  			default:
   193  				formatGenDecl(ctx, v)
   194  			}
   195  		}
   196  	}
   197  
   198  	for _, fn := range funcs {
   199  		formatFuncDecl(ctx, fn)
   200  	}
   201  	for _, imp := range ctx.imports {
   202  		if imp.pkgPath == "fmt" && !imp.isUsed {
   203  			if len(imp.decl.Specs) == 1 {
   204  				file.Decls = deleteDecl(file.Decls, imp.decl)
   205  			} else {
   206  				imp.decl.Specs = deleteSpec(imp.decl.Specs, imp.spec)
   207  			}
   208  		}
   209  	}
   210  }
   211  
   212  func formatGenDecl(ctx *formatCtx, v *ast.GenDecl) {
   213  	switch v.Tok {
   214  	case token.VAR, token.CONST:
   215  		for _, item := range v.Specs {
   216  			spec := item.(*ast.ValueSpec)
   217  			formatType(ctx, spec.Type, &spec.Type)
   218  			formatExprs(ctx, spec.Values)
   219  			for _, name := range spec.Names {
   220  				ctx.insert(name.Name)
   221  			}
   222  		}
   223  	case token.TYPE:
   224  		for _, item := range v.Specs {
   225  			spec := item.(*ast.TypeSpec)
   226  			formatType(ctx, spec.Type, &spec.Type)
   227  		}
   228  	}
   229  }
   230  
   231  func formatFuncDecl(ctx *formatCtx, v *ast.FuncDecl) {
   232  	formatFuncType(ctx, v.Type)
   233  	formatBlockStmt(ctx, v.Body)
   234  }
   235  
   236  /*
   237  func fillVarCtx(ctx *formatCtx, spec *ast.ValueSpec) {
   238  	for _, name := range spec.Names {
   239  		ctx.scp.addVar(name.Name)
   240  	}
   241  }
   242  
   243  type scope struct {
   244  	vars []map[string]bool
   245  }
   246  
   247  func newScope() *scope {
   248  	return &scope{
   249  		vars: []map[string]bool{make(map[string]bool)},
   250  	}
   251  }
   252  
   253  func (s *scope) addVar(name string) {
   254  	s.vars[len(s.vars)-1][name] = true
   255  }
   256  
   257  func (s *scope) containsVar(name string) bool {
   258  	for _, m := range s.vars {
   259  		if m[name] {
   260  			return true
   261  		}
   262  	}
   263  
   264  	return false
   265  }
   266  
   267  func (s *scope) enterScope() {
   268  	s.vars = append(s.vars, make(map[string]bool))
   269  }
   270  
   271  func (s *scope) exitScope() {
   272  	s.vars = s.vars[:len(s.vars)-1]
   273  }
   274  */
   275  // -----------------------------------------------------------------------------