pkger/parser/parser.go

238 lines
4.0 KiB
Go
Raw Permalink Normal View History

2019-08-01 19:03:12 +03:00
package parser
import (
2019-08-30 00:05:44 +03:00
"fmt"
2019-09-23 16:53:30 +03:00
"go/parser"
"go/token"
2019-11-05 23:04:38 +03:00
"io"
"io/ioutil"
2019-08-01 19:03:12 +03:00
"os"
2019-10-30 18:31:39 +03:00
"path/filepath"
2019-11-04 19:48:06 +03:00
"strings"
2019-11-05 23:04:38 +03:00
"sync"
2019-08-01 19:03:12 +03:00
2019-12-03 23:07:51 +03:00
"github.com/gobuffalo/here"
2019-08-01 19:03:12 +03:00
)
2019-11-04 19:48:06 +03:00
var defaultIgnoredFolders = []string{".", "_", "vendor", "node_modules", "testdata"}
2019-08-01 19:03:12 +03:00
func New(her here.Info) (*Parser, error) {
return &Parser{
Info: her,
decls: map[string]Decls{},
}, nil
}
type Parser struct {
here.Info
decls map[string]Decls
once sync.Once
includes []string
err error
}
func Parse(her here.Info, includes ...string) (Decls, error) {
2019-11-05 23:04:38 +03:00
p, err := New(her)
2019-10-09 20:21:54 +03:00
if err != nil {
return nil, err
}
p.includes = includes
2019-11-05 23:04:38 +03:00
return p.Decls()
}
func (p *Parser) ParseSource(source Source, mode parser.Mode) (*ParsedSource, error) {
2019-11-05 23:04:38 +03:00
pf := &ParsedSource{
Source: source,
FileSet: token.NewFileSet(),
}
b, err := ioutil.ReadFile(source.Abs)
if err != nil {
return nil, err
}
src := string(b)
pff, err := parser.ParseFile(pf.FileSet, source.Abs, src, mode)
if err != nil && err != io.EOF {
return nil, err
}
pf.Ast = pff
2019-08-01 19:03:12 +03:00
2019-11-05 23:04:38 +03:00
return pf, nil
2019-08-01 19:03:12 +03:00
}
func (p *Parser) ParseFile(abs string, mode parser.Mode) (*ParsedSource, error) {
2019-11-05 23:04:38 +03:00
s := Source{
Abs: abs,
}
info, err := os.Stat(abs)
2019-08-01 21:37:01 +03:00
if err != nil {
2019-09-23 16:53:30 +03:00
return nil, err
2019-08-01 21:37:01 +03:00
}
2019-11-05 23:04:38 +03:00
if info.IsDir() {
return nil, fmt.Errorf("%s is a directory", abs)
}
dir := filepath.Dir(abs)
s.Here, err = here.Dir(dir)
if err != nil {
return nil, err
}
s.Path, err = s.Here.Parse(strings.TrimPrefix(abs, dir))
return p.ParseSource(s, 0)
2019-11-05 23:04:38 +03:00
}
func (p *Parser) ParseDir(abs string, mode parser.Mode) ([]*ParsedSource, error) {
2019-11-05 23:04:38 +03:00
info, err := os.Stat(abs)
if err != nil {
return nil, err
}
if !info.IsDir() {
return nil, fmt.Errorf("%s is not a directory", abs)
}
2019-11-08 22:05:03 +03:00
her, err := here.Dir(abs)
2019-11-05 23:04:38 +03:00
if err != nil {
2019-12-03 23:29:17 +03:00
return nil, fmt.Errorf("%s: here.Dir failed %s", err, abs)
2019-11-05 23:04:38 +03:00
}
2019-11-08 22:05:03 +03:00
pt, err := her.Parse(strings.TrimPrefix(abs, filepath.Dir(abs)))
2019-11-05 23:04:38 +03:00
if err != nil {
2019-12-03 23:29:17 +03:00
return nil, fmt.Errorf("%s: here.Parse failed %s", err, abs)
2019-11-05 23:04:38 +03:00
}
fset := token.NewFileSet()
pkgs, err := parser.ParseDir(fset, abs, nil, 0)
if err != nil {
2019-12-03 23:29:17 +03:00
return nil, fmt.Errorf("%s: ParseDir failed %s", err, abs)
2019-11-05 23:04:38 +03:00
}
var srcs []*ParsedSource
for _, pkg := range pkgs {
for name, pf := range pkg.Files {
s := &ParsedSource{
Source: Source{
Abs: name,
Path: pt,
Here: her,
},
FileSet: fset,
Ast: pf,
}
srcs = append(srcs, s)
}
}
return srcs, nil
}
func (p *Parser) Decls() (Decls, error) {
if err := p.parse(); err != nil {
return nil, err
2019-08-01 21:37:01 +03:00
}
2019-08-02 00:56:37 +03:00
2019-10-30 18:31:39 +03:00
var decls Decls
2019-11-05 23:04:38 +03:00
orderedNames := []string{
"MkdirAll",
"Create",
"Include",
2019-11-05 23:04:38 +03:00
"Stat",
"Open",
"Dir",
"Walk",
}
for _, n := range orderedNames {
decls = append(decls, p.decls[n]...)
}
return decls, nil
}
func (p *Parser) DeclsMap() (map[string]Decls, error) {
err := p.Parse()
return p.decls, err
}
func (p *Parser) Parse() error {
(&p.once).Do(func() {
p.err = p.parse()
})
return p.err
}
func (p *Parser) parse() error {
p.decls = map[string]Decls{}
2019-11-05 23:04:38 +03:00
root := p.Dir
if err := p.parseIncludes(); err != nil {
return err
}
2019-11-05 23:04:38 +03:00
fi, err := os.Stat(root)
if err != nil {
return err
}
2019-11-05 23:04:38 +03:00
if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", root)
}
2019-08-01 19:03:12 +03:00
2019-10-30 18:31:39 +03:00
err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
2019-10-21 17:27:04 +03:00
2019-10-30 18:31:39 +03:00
if !info.IsDir() {
return nil
}
2019-11-04 19:48:06 +03:00
base := filepath.Base(path)
for _, x := range defaultIgnoredFolders {
if strings.HasPrefix(base, x) {
return filepath.SkipDir
}
}
srcs, err := p.ParseDir(path, 0)
2019-10-30 18:31:39 +03:00
if err != nil {
2019-12-03 23:29:17 +03:00
return fmt.Errorf("%s: %s", err, path)
2019-10-30 18:31:39 +03:00
}
2019-08-01 19:03:12 +03:00
2019-11-05 23:04:38 +03:00
for _, src := range srcs {
mm, err := src.DeclsMap()
if err != nil {
2019-12-03 23:29:17 +03:00
return fmt.Errorf("%s: %s", err, src.Abs)
2019-11-05 23:04:38 +03:00
}
for k, v := range mm {
p.decls[k] = append(p.decls[k], v...)
2019-09-23 16:53:30 +03:00
}
2019-08-01 19:03:12 +03:00
}
2019-11-05 23:04:38 +03:00
2019-10-30 18:31:39 +03:00
return nil
})
2019-10-15 23:40:45 +03:00
2019-11-05 23:04:38 +03:00
return err
2019-08-01 19:03:12 +03:00
}
func (p *Parser) parseIncludes() error {
for _, i := range p.includes {
2019-12-13 19:24:17 +03:00
d, err := NewInclude(p.Info, i)
if err != nil {
return err
}
2019-12-13 19:24:17 +03:00
p.decls["Include"] = append(p.decls["Include"], d)
}
return nil
}