From 519f3216e9face695b6fe65c536b0efeea2ba8eb Mon Sep 17 00:00:00 2001 From: Augusto Date: Wed, 1 Apr 2020 00:51:02 +0100 Subject: [PATCH] Modifies the load process to create and fill the scopes of the prog --- go/tests/rc/rc.go | 297 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 256 insertions(+), 41 deletions(-) diff --git a/go/tests/rc/rc.go b/go/tests/rc/rc.go index 9888d06a..b0c4acae 100644 --- a/go/tests/rc/rc.go +++ b/go/tests/rc/rc.go @@ -50,7 +50,7 @@ func (a *arrFlag) String() string { func (a *arrFlag) Set(s string) error { a.active = true - a.content = strings.Split(s, " ") + a.content = strings.Split(s, ",") return nil } @@ -201,7 +201,168 @@ func getPkgFunc(path string, fsetPkg *token.FileSet) { } -// reformat from the data base +// Returns the smallest block containing the position pos. It can +// return nil if `pos` is not inside any ast.BlockStmt +func smallestBlock(pos token.Pos, blocks []*ast.BlockStmt) *ast.BlockStmt { + var minBlk *ast.BlockStmt + var minSize token.Pos + for _, v := range blocks { + if pos > v.Pos() && pos < v.End() { + size := v.End() - v.Pos() + if minBlk == nil || size < minSize { + minBlk = v + minSize = size + } + } + } + return minBlk +} + +type data struct { + argument bool +} + +func fillScope(funcDefs []*fDefInfo, scope *ast.Scope, scopes map[*ast.BlockStmt]*ast.Scope) { + for _, fun := range funcDefs { + scope.Insert(fun.obj) + for _, name := range fun.paramsFunc { + obj := ast.NewObj(ast.Fun, name) + + data := data{ + argument: true, + } + obj.Data = data + scopes[fun.body].Insert(obj) + } + } +} + +// Create the scopes for a BlockStmt contained inside another BlockStmt +func createChildScope(block *ast.BlockStmt, l *loadVisitor, scopes map[*ast.BlockStmt]*ast.Scope) { + blocks := l.blocks + // The smalles block containing the beggining of the block + parentBlock := smallestBlock(block.Pos(), blocks) + if scopes[parentBlock] == nil { + createChildScope(parentBlock, l, scopes) + } + scopes[block] = ast.NewScope(scopes[parentBlock]) +} + +// Returns true `block` is contained inside another block +func isContained(block *ast.BlockStmt, blocks []*ast.BlockStmt) bool { + for _, v := range blocks { + if block == v { + continue + } + if block.Pos() > v.Pos() && block.End() < v.End() { + return true + } + } + return false +} + +// Creates all the scopes in the package +func createScopes(l *loadVisitor, pkgScope *ast.Scope) map[*ast.BlockStmt]*ast.Scope { + blocks := l.blocks + scopes := make(map[*ast.BlockStmt]*ast.Scope) + if blocks == nil { + return nil + } + for _, b := range blocks { + if !isContained(b, blocks) { + scopes[b] = ast.NewScope(pkgScope) + continue + } + } + for _, b := range blocks { + if scopes[b] != nil { + continue + } + createChildScope(b, l, scopes) + } + return scopes +} + +type blockVisitor struct { + fdef []*fDefInfo // All functions defined in the scope in any + // way: as a funcDecl, GenDecl or AssigmentStmt + oneBlock bool // Indicates if the visitor already encounter a + // blockStmt +} + +func (b *blockVisitor) Visit(n ast.Node) ast.Visitor { + switch t := n.(type) { + case *ast.BlockStmt: + if b.oneBlock { + return nil + } + return b + case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt: + def := funcInfo(t) + if def == nil || def.obj == nil { + return b + } + b.fdef = append(b.fdef, def) + return nil + default: + return b + } +} + +type loadedSource map[string]*loadVisitor + +// Returns information about the function defined in the block node +func defs(block ast.Node) []*fDefInfo { + b := &blockVisitor{} + ast.Walk(b, block) + return b.fdef +} + +func loadProgram(path string, load loadedSource) error { + l := &loadVisitor{ + functions: make(map[string]ast.Node), + absImports: make(map[string]*element), + relImports: make(map[string]*element), + objFunc: make(map[*ast.Object]ast.Node), + fset: token.NewFileSet(), + scopes: make(map[*ast.BlockStmt]*ast.Scope), + } + + pkgs, err := parser.ParseDir(l.fset, path, nil, parser.AllErrors) + + if err != nil { + return err + } + + for _, pkg := range pkgs { + ast.Walk(l, pkg) + l.pkgScope = ast.NewScope(nil) + def := defs(pkg) + for _, v := range def { + l.pkgScope.Insert(v.obj) + } + l.scopes = createScopes(l, l.pkgScope) + fillScope(def, l.pkgScope, l.scopes) + for block, scope := range l.scopes { + defs := defs(block) + fillScope(defs, scope, l.scopes) + } + load[path] = l + } + + for _, v := range l.relImports { + if load[v.name] == nil { + newPath, _ := filepath.Abs(path + "/" + v.name) + err = loadProgram(newPath, load) + if err != nil { + return err + } + } + } + return err +} + +//reformat from the data base func splitArgs(args string) []string { result := strings.Split(args, " ") return result @@ -368,54 +529,108 @@ func main() { } } -func loadProgram(path string, functions map[string]*loadVisitor) { - l := &loadVisitor{ - functions: make(map[string]ast.Node), - relImports: make(map[string]string), - fset: token.NewFileSet(), - } - - pkgs, err := parser.ParseDir(l.fset, path, nil, parser.AllErrors) - - if err != nil { - panic(err) - } +type element struct { + name string + pos token.Pos +} - for _, pkg := range pkgs { - ast.Walk(l, pkg) - functions[path] = l - fmt.Println(l.relImports) - } +type loadVisitor struct { + relImports map[string]*element + absImports map[string]*element + functions map[string]ast.Node + fset *token.FileSet + objFunc map[*ast.Object]ast.Node + blocks []*ast.BlockStmt + scopes map[*ast.BlockStmt]*ast.Scope // nil after the visit + // used to keep the result of the createScope function + pkgScope *ast.Scope +} - for _, v := range l.relImports { - if functions[v] == nil { - newPath, _ := filepath.Abs(path + "/" + v) - loadProgram(newPath, functions) +// Returns all the parameter of a function that identify a function +func listParamFunc(params *ast.FieldList) []string { + var funcs []string + for _, param := range params.List { + if _, ok := param.Type.(*ast.FuncType); ok { + for _, name := range param.Names { + funcs = append(funcs, name.Name) + } } } + return funcs } -type loadVisitor struct { - relImports map[string]string - functions map[string]ast.Node - fset *token.FileSet +type fDefInfo struct { + obj *ast.Object // the object that represents a function + paramsFunc []string // the name of the parameter that represent + // functions + body *ast.BlockStmt } -func (l *loadVisitor) Visit(n ast.Node) ast.Visitor { - if spec, ok := n.(*ast.ImportSpec); ok { - path, _ := strconv.Unquote(spec.Path.Value) - if isRelativeImport(path) { - var name string - if spec.Name != nil { - name = spec.Name.Name - } else { - name = filepath.Base(path) +// Returns information about a node representing a function declaration +func funcInfo(n ast.Node) *fDefInfo { + fdef := &fDefInfo{} + switch t := n.(type) { + case *ast.FuncDecl: + fdef.obj = t.Name.Obj + fdef.paramsFunc = listParamFunc(t.Type.Params) + fdef.body = t.Body + return fdef + case *ast.GenDecl: + for _, v := range t.Specs { + if val, ok := v.(*ast.ValueSpec); ok { + for i, value := range val.Values { + if funcLit, ok := value.(*ast.FuncLit); ok { + fdef.obj = val.Names[i].Obj + fdef.paramsFunc = listParamFunc(funcLit.Type.Params) + fdef.body = funcLit.Body + } + } } - l.relImports[name] = path } + return fdef + case *ast.AssignStmt: + for i, right := range t.Rhs { + if funcLit, ok := right.(*ast.FuncLit); ok { + if ident, ok := t.Lhs[i].(*ast.Ident); ok { + fdef.obj = ident.Obj + fdef.paramsFunc = listParamFunc(funcLit.Type.Params) + } + } + return fdef + } + default: + return fdef } - if decl, ok := n.(*ast.FuncDecl); ok { - l.functions[decl.Name.Name] = n + return fdef +} +func (l *loadVisitor) Visit(n ast.Node) ast.Visitor { + switch t := n.(type) { + case *ast.ImportSpec: + path, _ := strconv.Unquote(t.Path.Value) + var name string + if t.Name != nil { + name = t.Name.Name + } else { + name = filepath.Base(path) + } + el := &element{ + name: path, + pos: n.Pos(), + } + + if isRelativeImport(path) { + l.relImports[name] = el + break + } + l.absImports[name] = el + case *ast.FuncDecl, *ast.GenDecl, *ast.AssignStmt: + fdef := funcInfo(t) + if fdef == nil || fdef.obj == nil { + return l + } + l.objFunc[fdef.obj] = n + case *ast.BlockStmt: + l.blocks = append(l.blocks, t) } return l } @@ -496,8 +711,8 @@ func allow(s string, casted bool) bool { // Returns true if the string matches the format of a relative import func isRelativeImport(s string) bool { - relativeImport, _ := regexp.MatchString(`\.\.\\??`, s) - return relativeImport + reg := regexp.MustCompile(`^\.`) + return reg.Match([]byte(s)) } // Returns true if the string represents an import package