简体   繁体   English

如何在golang ast遍历中从子节点检索父节点?

[英]How to retrieve parent node from child node in golang ast traversal?

Here is test file这是测试文件

func demo()  {
    name:=xxx()
    fmt.Println(name)
}

And my ast traversal code还有我的ast遍历代码

ast.Inspect(f, func(node ast.Node) bool {
    assign, ok := node.(*ast.AssignStmt) // find -> name:=xxx()
    if !ok {
        return true
    }
    if assign == nil {
        return true
    }
    var expr = assign.Lhs[0]
    fmt.Println(expr) // find -> name
    fmt.Println(nodeString(assign,pass.Fset))
    return true
})

I find I have to travel ast from up to down like this.我发现我必须像这样从上到下旅行。 Find AssignStmt and then get AssignStmt->Lhs找到AssignStmt然后得到AssignStmt->Lhs

But I need to find ast.Ident(name) first and then to find whether its parent is AssignStmt in some cases.但是我需要先找到 ast.Ident(name) ,然后在某些情况下才能找到它的父级是否为 AssignStmt 。

I am not sure is it possible that I can go from bottom to top.我不确定我是否可以从下到上。

Manage a stack of ancestor nodes as the inspector is called:在调用检查器时管理一堆祖先节点:

var stack []ast.Node
ast.Inspect(f, func(n ast.Node) bool {

    // Look for the identifier.

    if n, ok := n.(*ast.Ident); ok {
        if n.Name == "name" {

            // Parent is stack[len(stack)-1]

            fmt.Println("found name")
            for _, n := range stack {
                fmt.Printf("%T\n", n)
            }
        }
    }

    // Manage the stack. Inspect calls a function like this:
    //   f(node)
    //   for each child {
    //      f(child) // and recursively for child's children
    //   }
    //   f(nil)
    if n == nil {
        // Done with node's children. Pop.
        stack = stack[:len(stack)-1]
    } else {
        // Push the current node for children.
        stack = append(stack, n)
    }

    return true
})

Run it on the playground .在操场上运行它

I came across similar problem that I needed to find the index of the child amongst its siblings as well as the parent node of it.我遇到了类似的问题,我需要在其兄弟姐妹及其父节点中找到孩子的索引。 I could not find closer problem so I am posting here.我找不到更近的问题所以我在这里发帖。

I isolated the logic in Cerise's answer into a function and moved the answer specific parts to outside of function.我将 Cerise 的答案中的逻辑隔离到一个函数中,并将答案的特定部分移到了函数之外。 So more people can leverage this generic form and save time.所以更多的人可以利用这种通用形式并节省时间。

Logic for finding the index is similar to finding parent.查找索引的逻辑类似于查找父项。 Another array for tracing which -index sequence- is followed to reach current node is enough.另一个用于跟踪哪个索引序列到达当前节点的数组就足够了。 At each call where n is nil we remove the last item from sequence and increment the new last item.在 n 为nil的每次调用中,我们从序列中删除最后一项并增加新的最后一项。

func FindParentNodeAndChildIndex(root ast.Node, child ast.Node) (parent ast.Node, childIndex int) {
    var (
        parentTrace     []ast.Node
        childIndexTrace []int
        found           = false
    )
    var (
        updateParentTrace = func(n ast.Node) {
            if n != nil {
                parentTrace = append(parentTrace, n)
            } else {
                parentTrace = parentTrace[:len(parentTrace)-1]
            }
        }
        updateChildIndexTrace = func(n ast.Node) {
            if n != nil {
                childIndexTrace = append(childIndexTrace, 0)
            } else {
                l := len(childIndexTrace)
                childIndexTrace = childIndexTrace[:l-1]
                childIndexTrace[l-2]++
            }
        }
    )
    ast.Inspect(root, func(n ast.Node) bool {
        if !found {
            updateParentTrace(n)
            updateChildIndexTrace(n)
        }
        if n != nil && n == child {
            found = true
        }
        return !found
    })
    if found {
        return parentTrace[len(parentTrace)-2], childIndexTrace[len(childIndexTrace)-2]
    }
    return nil, -1
}

It looks like there are more alternative methods for finding parent then the date question answered.看起来有更多替代方法可以找到父母然后回答日期问题。 One is astutil.Apply() another is inspector.WithStack()一个是astutil.Apply()另一个是inspector.WithStack()

astutil.Apply() is used with two callbacks one fired before and another after visiting each node's children; astutil.Apply()与两个回调一起使用,一个在访问每个节点的子节点之前触发,另一个在访问每个节点的子节点之后触发; and accepts a Cursor instance that holds details about visited node's Index and Parent.并接受一个Cursor实例,该实例包含有关已访问节点的索引和父节点的详细信息。

For WithStack there is a detailed example对于WithStack有一个详细的例子

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM