github.com/bingoohuang/pkger@v0.0.0-20210127185155-a71b9df4c4c7/parser/parser.go (about) 1 package parser 2 3 import ( 4 "fmt" 5 "go/parser" 6 "go/token" 7 "io" 8 "io/ioutil" 9 "os" 10 "path/filepath" 11 "strings" 12 "sync" 13 14 "github.com/bingoohuang/pkger/here" 15 ) 16 17 var defaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "testdata"} 18 19 func New(her here.Info) (*Parser, error) { 20 return &Parser{ 21 Info: her, 22 decls: map[string]Decls{}, 23 }, nil 24 } 25 26 type Parser struct { 27 here.Info 28 decls map[string]Decls 29 once sync.Once 30 includes []string 31 err error 32 } 33 34 func Parse(her here.Info, includes ...string) (Decls, error) { 35 p, err := New(her) 36 if err != nil { 37 return nil, err 38 } 39 p.includes = includes 40 41 return p.Decls() 42 } 43 44 func (p *Parser) ParseSource(source Source, mode parser.Mode) (*ParsedSource, error) { 45 pf := &ParsedSource{ 46 Source: source, 47 FileSet: token.NewFileSet(), 48 } 49 50 b, err := ioutil.ReadFile(source.Abs) 51 if err != nil { 52 return nil, err 53 } 54 src := string(b) 55 56 pff, err := parser.ParseFile(pf.FileSet, source.Abs, src, mode) 57 if err != nil && err != io.EOF { 58 return nil, err 59 } 60 pf.Ast = pff 61 62 return pf, nil 63 } 64 65 func (p *Parser) ParseFile(abs string, mode parser.Mode) (*ParsedSource, error) { 66 s := Source{ 67 Abs: abs, 68 } 69 70 info, err := os.Stat(abs) 71 if err != nil { 72 return nil, err 73 } 74 75 if info.IsDir() { 76 return nil, fmt.Errorf("%s is a directory", abs) 77 } 78 79 dir := filepath.Dir(abs) 80 81 s.Here, err = here.Dir(dir) 82 if err != nil { 83 return nil, err 84 } 85 86 s.Path, err = s.Here.Parse(strings.TrimPrefix(abs, dir)) 87 88 return p.ParseSource(s, 0) 89 } 90 91 func (p *Parser) ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error) { 92 info, err := os.Stat(abs) 93 if err != nil { 94 return nil, err 95 } 96 97 if !info.IsDir() { 98 return nil, fmt.Errorf("%s is not a directory", abs) 99 } 100 101 her, err := here.Dir(abs) 102 if err != nil { 103 return nil, fmt.Errorf("%s: here.Dir failed %s", err, abs) 104 } 105 106 pt, err := her.Parse(strings.TrimPrefix(abs, filepath.Dir(abs))) 107 if err != nil { 108 return nil, fmt.Errorf("%s: here.Parse failed %s", err, abs) 109 } 110 111 filter := func(f os.FileInfo) bool { 112 return !f.IsDir() 113 } 114 115 fset := token.NewFileSet() 116 pkgs, err := parser.ParseDir(fset, abs, filter, 0) 117 if err != nil { 118 return nil, fmt.Errorf("%s: ParseDir failed %s", err, abs) 119 } 120 121 var srcs []*ParsedSource 122 for _, pkg := range pkgs { 123 for name, pf := range pkg.Files { 124 s := &ParsedSource{ 125 Source: Source{ 126 Abs: name, 127 Path: pt, 128 Here: her, 129 }, 130 FileSet: fset, 131 Ast: pf, 132 } 133 srcs = append(srcs, s) 134 } 135 } 136 137 return srcs, nil 138 } 139 140 func (p *Parser) Decls() (Decls, error) { 141 if err := p.parse(); err != nil { 142 return nil, err 143 } 144 145 var decls Decls 146 orderedNames := []string{ 147 "MkdirAll", 148 "Create", 149 "Include", 150 "Stat", 151 "Open", 152 "Dir", 153 "Walk", 154 "Read", 155 "ReadStr", 156 "MustRead", 157 "MustReadStr", 158 } 159 160 for _, n := range orderedNames { 161 decls = append(decls, p.decls[n]...) 162 } 163 164 return decls, nil 165 } 166 167 func (p *Parser) DeclsMap() (map[string]Decls, error) { 168 err := p.Parse() 169 return p.decls, err 170 } 171 172 func (p *Parser) Parse() error { 173 (&p.once).Do(func() { 174 p.err = p.parse() 175 }) 176 return p.err 177 } 178 179 func (p *Parser) parse() error { 180 p.decls = map[string]Decls{} 181 182 root := p.Dir 183 184 if err := p.parseIncludes(); err != nil { 185 return err 186 } 187 188 fi, err := os.Stat(root) 189 if err != nil { 190 return err 191 } 192 193 if !fi.IsDir() { 194 return fmt.Errorf("%q is not a directory", root) 195 } 196 197 err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { 198 if err != nil { 199 return err 200 } 201 202 if !info.IsDir() { 203 return nil 204 } 205 206 base := filepath.Base(path) 207 for _, x := range defaultIgnoredFolders { 208 if strings.HasPrefix(base, x) { 209 return filepath.SkipDir 210 } 211 } 212 213 srcs, err := p.ParseDir(path, 0) 214 if err != nil { 215 return fmt.Errorf("%s: %s", err, path) 216 } 217 218 for _, src := range srcs { 219 mm, err := src.DeclsMap() 220 if err != nil { 221 return fmt.Errorf("%s: %s", err, src.Abs) 222 } 223 for k, v := range mm { 224 p.decls[k] = append(p.decls[k], v...) 225 } 226 } 227 228 return nil 229 }) 230 231 return err 232 } 233 234 func (p *Parser) parseIncludes() error { 235 for _, i := range p.includes { 236 ds, err := NewInclude(p.Info, i) 237 if err != nil { 238 return err 239 } 240 241 for _, d := range ds { 242 k := strings.TrimPrefix(d.typ, "pkger.") 243 p.decls[k] = append(p.decls[k], d) 244 } 245 } 246 return nil 247 }