package modules import ( "fmt" "path/filepath" "github.com/marzeq/quokka/parser" "github.com/marzeq/quokka/tokeniser" ) type FileNode struct { Path string Imports []*FileNode } type DepGraph struct { Files map[string]*FileNode ASTs map[string]*parser.RootNode } func NewDepGraph(path string, rn *parser.RootNode) (*DepGraph, error) { dg := &DepGraph{ Files: make(map[string]*FileNode), ASTs: make(map[string]*parser.RootNode), } err := dg.Construct(path, rn) if err != nil { return nil, err } return dg, nil } func (g *DepGraph) Construct(path string, rn *parser.RootNode) error { path, err := filepath.Abs(path) if err != nil { return err } if _, ok := g.ASTs[path]; ok { return nil } node := &FileNode{ Path: path, Imports: []*FileNode{}, } g.Files[path] = node g.ASTs[path] = rn imports := getImports(rn) for _, imp := range imports { imp, err := filepath.Abs(imp) if err != nil { return err } t, err := tokeniser.NewTokeniserFromFile(imp) if err != nil { return err } toks, err := t.Tokenise() if err != nil { return err } p := parser.NewParser(toks) ast, err := p.Parse() if err != nil { return err } if err := g.Construct(imp, ast); err != nil { return err } node.Imports = append(node.Imports, g.Files[imp]) } return nil } func getImports(rn *parser.RootNode) []string { imports := []string{} for _, n := range rn.Body { switch node := n.(type) { case *parser.ImportNode: imports = append(imports, node.Modules...) } } return imports } func (g *DepGraph) TopoSort() ([]string, error) { visited := map[string]bool{} temp := map[string]bool{} result := []string{} var visit func(n *FileNode) error visit = func(n *FileNode) error { if temp[n.Path] { return fmt.Errorf("cycle detected at file: %s", n.Path) } if visited[n.Path] { return nil } temp[n.Path] = true for _, child := range n.Imports { if err := visit(child); err != nil { return err } } temp[n.Path] = false visited[n.Path] = true result = append(result, n.Path) return nil } for _, n := range g.Files { if !visited[n.Path] { if err := visit(n); err != nil { return nil, err } } } for i, j := 0, len(result)-1; i < j; i, j = i+1, j-1 { result[i], result[j] = result[j], result[i] } return result, nil }