ibi0tux ibi0tux - 1 month ago 8
Python Question

Python PLY parsing : definition scope

I'm using PLY to parse files containing nested blocks.
Typically :

a {
b {

}
c {
d {

}
}
}


I am using a simple grammar like so :

def p_nodes(p):
'''
nodes : node nodes
| node
'''
# ??

def p_node(p):
'''
node : IDENTIFIER OPEN_CURLY_BRACE node_├žontent CLOSE_CURLY_BRACE
'''
p[0] = Node(p[3])#FIXME?

def p_node_content(p):
'''
node_content : nodes
|
'''
if len(p) > 1:
p[0] = p[1]
else
p[0] = None


I would like to know I could be able to access a 'parent' node in the parser. In other terms how can I build the AST so that I can retrieve in my example that
d
is a child of
c
which is itself child of
a
since I have to visibility to parent rule in the parser.

What should i put in
p_nodes
and
p_node
so that a valid AST can be built ? Thanks.

Answer

We would have need your Node class, but I presume it is something like:

class Node:
    def __init__(self, children):
        self.children = children
        self.type = None

Then your parser could look like that:

def p_nodes(p):
    '''
    nodes : node nodes
          | node
    '''
    if len(p) > 2:
        p[0] = [p[1]] + p[2]
    else
        p[0] = [p[1]] 

def p_node(p):
    '''
    node : IDENTIFIER OPEN_CURLY_BRACE node_content CLOSE_CURLY_BRACE
    '''
    p[0] = Node(p[3])

def p_node_content(p):
    '''
    node_content : nodes
                 |
    '''
    if len(p) > 1:
        p[0] = p[1]
    else
        p[0] = None

Then you will have a real AST, with each node containing a reference to all its childs.

Eventually, if you want your node to have a reference to their parent, you have to iterate on all the AST from the root and set it as an attribute on all his children, then do the same on it's children...

To do that, change your Node class to look like this:

class Node:
    def __init__(self, children):
        self.children = children
        self.parent = None

    def set_parent(self, parent):
        self.parent = parent

And run a similar function as this:

def set_parent_to_AST(root_node):
    for node in root_node.children:
        node.set_parent(root_node)
        set_parent_to_AST(node)