github.com/geniusesgroup/libgo@v0.0.0-20220713101832-828057a9d3d4/go-parser/parse.go (about)

     1  /* For license and copyright information please see LEGAL file in repository */
     2  
     3  package parser
     4  
     5  import (
     6  	"go/ast"
     7  	"go/parser"
     8  	"go/token"
     9  	"path"
    10  	"strings"
    11  )
    12  
    13  // Repository :
    14  type Repository struct {
    15  	Name         string
    16  	DependencyID [16]byte               // DependencyID in SabzCity version control
    17  	FSPath       string                 // Folder location in FileSystems
    18  	Files        map[string]*File       // Name
    19  	Imports      map[string]*Import     // UsageName
    20  	Functions    map[string]*Function   // Name
    21  	Types        map[string]*Type       // Name
    22  	Dependencies map[string]*Repository // Name
    23  }
    24  
    25  // AddFile use to add file to Repository
    26  func (repo *Repository) AddFile(f *File) {
    27  	repo.Files[f.Name] = f
    28  }
    29  
    30  // File :
    31  type File struct {
    32  	Name   string
    33  	Data   []byte
    34  	Parsed *ast.File
    35  }
    36  
    37  // Import :
    38  type Import struct {
    39  	UsageName    string
    40  	PackageName  string
    41  	DependencyID [16]byte // DependencyID in SabzCity version control
    42  	FSPath       string   // Folder location in FileSystems
    43  	File         *File
    44  	ImportSpec   *ast.ImportSpec
    45  }
    46  
    47  // Function store parsed data about logic Function!
    48  type Function struct {
    49  	Name      string
    50  	Comment   string
    51  	Parameter *Type // ChaparKhane just support one variable in Function input!
    52  	Result    *Type // ChaparKhane just support one variable in Function output!
    53  	Err       *Type // ChaparKhane just support one error in Function output!
    54  	File      *File
    55  	Decl      *ast.FuncDecl
    56  }
    57  
    58  // Type :
    59  type Type struct {
    60  	Name      string
    61  	ID        int
    62  	Package   *Import // If nil means local package not imported!
    63  	Type      string  // struct: embedded struct in this struct.
    64  	Len       uint64  // Use in Array, Slice, Map, ...
    65  	Exported  bool
    66  	Pointer   bool
    67  	InnerType []*Type
    68  	Tags      map[string]string
    69  	Comment   string
    70  	File      *File
    71  }
    72  
    73  // Parse use to add new file & parsed FileData and add to repo object!
    74  func (repo *Repository) Parse(FileName string, FileData []byte) (err error) {
    75  	// Parsed FileData
    76  	var (
    77  		file    = File{Name: FileName, Data: FileData}
    78  		fileSet = token.NewFileSet()
    79  	)
    80  	repo.Files[FileName] = &file
    81  
    82  	// Just parsed needed file!
    83  	if strings.HasSuffix(FileName, ".go") ||
    84  		!strings.HasSuffix(FileName, "_test.go") {
    85  
    86  		file.Parsed, err = parser.ParseFile(fileSet, "", FileData, parser.ParseComments)
    87  		if err != nil {
    88  			return err
    89  		}
    90  
    91  		// Set package name
    92  		repo.Name = file.Parsed.Name.Name
    93  
    94  		err = repo.parseFile(&file)
    95  		if err != nil {
    96  			return err
    97  		}
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func (repo *Repository) parseFile(file *File) (err error) {
   104  	for _, imp := range file.Parsed.Imports {
   105  		var impor = Import{
   106  			FSPath:     imp.Path.Value[1 : len(imp.Path.Value)-1],
   107  			ImportSpec: imp,
   108  		}
   109  		if imp.Name != nil {
   110  			impor.UsageName = imp.Name.Name
   111  			impor.PackageName = path.Base(imp.Path.Value[1 : len(imp.Path.Value)-1])
   112  		} else {
   113  			impor.UsageName = path.Base(imp.Path.Value[1 : len(imp.Path.Value)-1])
   114  			impor.PackageName = path.Base(imp.Path.Value[1 : len(imp.Path.Value)-1])
   115  		}
   116  
   117  		repo.Imports[impor.UsageName] = &impor
   118  	}
   119  
   120  	for _, decl := range file.Parsed.Decls {
   121  		switch d := decl.(type) {
   122  		case *ast.GenDecl:
   123  			for _, gDecl := range d.Specs {
   124  				switch gd := gDecl.(type) {
   125  				case *ast.ImportSpec:
   126  					// Check this before in file.Parsed.Imports!!!! WHY go WHY!!!? duplicate data!!!???
   127  				case *ast.ValueSpec:
   128  					// Don't need this so far!
   129  				case *ast.TypeSpec:
   130  					t := repo.parseType(gd.Type)
   131  					t.Name = gd.Name.Name
   132  					t.Exported = gd.Name.IsExported()
   133  					t.File = file
   134  
   135  					repo.Types[t.Name] = &t
   136  				}
   137  			}
   138  
   139  		case *ast.FuncDecl:
   140  			// Just exported function use in chaparkhane generation!
   141  			if !d.Name.IsExported() {
   142  				continue
   143  			}
   144  
   145  			function := Function{
   146  				Name: d.Name.Name,
   147  				File: file,
   148  				Decl: d,
   149  			}
   150  
   151  			if d.Type != nil && d.Type.Params != nil {
   152  				if len(d.Type.Params.List) != 2 {
   153  					continue
   154  				}
   155  				fp := repo.parseType(d.Type.Params.List[0].Type)
   156  				fp.Name = d.Type.Params.List[0].Names[0].Name
   157  				fp.Exported = d.Type.Params.List[0].Names[0].IsExported()
   158  				fp.File = file
   159  				function.Parameter = &fp
   160  			}
   161  
   162  			if d.Type != nil && d.Type.Results != nil {
   163  				if len(d.Type.Params.List) > 2 {
   164  					continue
   165  				}
   166  
   167  				for _, rField := range d.Type.Results.List {
   168  					for _, name := range rField.Names {
   169  						fr := repo.parseType(rField.Type)
   170  						fr.Name = name.Name
   171  						fr.Exported = name.IsExported()
   172  						fr.File = file
   173  						if fr.Type == "error" {
   174  							function.Err = &fr
   175  						} else {
   176  							function.Result = &fr
   177  						}
   178  					}
   179  				}
   180  			}
   181  
   182  			repo.Functions[function.Name] = &function
   183  		}
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  func (repo *Repository) parseType(expr ast.Expr) (ft Type) {
   190  	switch v := expr.(type) {
   191  	case *ast.StarExpr:
   192  		ft = repo.parseType(v.X)
   193  		ft.Pointer = true
   194  	case *ast.SelectorExpr:
   195  		if imp, ok := repo.Imports[v.X.(*ast.Ident).Name]; ok {
   196  			ft.Package = imp
   197  		}
   198  		ft.Type = v.Sel.Name
   199  	case *ast.ArrayType:
   200  		//ft.Type = "[" + repo.parseType(v.Len).Type + "]" + repo.parseType(v.Elt).Type
   201  	case *ast.MapType:
   202  		//"map[" + parseType(v.Key) + "]" + parseType(v.Value)
   203  	case *ast.InterfaceType:
   204  		//"interface{}"
   205  		// interface type forbidden
   206  	case *ast.Ident:
   207  		if v.Obj != nil {
   208  			switch obj := v.Obj.Decl.(type) {
   209  			case *ast.TypeSpec:
   210  				innerFT := repo.parseType(obj.Type)
   211  				for _, each := range innerFT.InnerType {
   212  					ft.InnerType = append(ft.InnerType, each)
   213  				}
   214  			}
   215  		}
   216  		ft.Type = v.Name
   217  	case *ast.StructType:
   218  		for _, field := range v.Fields.List {
   219  			if field.Names == nil {
   220  				// embedded struct forbidden
   221  			}
   222  			for _, innerField := range field.Names {
   223  				innerFT := repo.parseType(field.Type)
   224  				innerFT.Name = innerField.Name
   225  				innerFT.ID = len(ft.InnerType)
   226  				ft.InnerType = append(ft.InnerType, &innerFT)
   227  			}
   228  		}
   229  	case *ast.BasicLit:
   230  		// embedded basic type forbidden
   231  		// type test struct {
   232  		// 	 string
   233  		// }
   234  	}
   235  
   236  	return ft
   237  }