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 }