Pybites Logo

Abstract syntax tree (ast) printer

Level: Intermediate (score: 3)

In this Bite we familiarise with Abstract Syntax Trees (ASTs). These are tree data structures used to describe source code based on a programming language grammar.

In Python, an AST can be generated using the ast module provided in the standard library. If you are not familiar with this module, we recommend you to check out this read this PyBites tutorial before continuing with this bite.

In this bite we are going to work with an AST visitor, i.e., a class which applies some logic when visiting the nodes of an AST.

More specifically, you need to complete the AST visitor AstPrinter provided in the template so to print the content of a tree according the following formatting rules:

The content of a node is composed of two info: the node name, and a list of attribute name and value pairs.

- Format node names by appending () to their class name.

- Format node attributes names preceding them with . and adding : at the end.

- If an attribute value is neither a node or list of nodes, its formatted value is obtained applying repr() to it.

Print content by "nesting" it.

- A node name is always reported on the next line, adding three white spaces ' ' more than the one used in the previous line.

- Node attributes are nested under a node name adding three white spaces ' ' more then the line showing the related node name.

- Given a node, its attributes share the same level of nesting.

When handling node attributes:

- First, print attributes which are neither nodes nor list of nodes. Sort those attributes by their name, and for each of them, print the formatted name and value pair one per line, separating the two info with a single white space.

- Then, sort the remaining attributes by name. For each element, format and print the attribute name on one line, while nesting its content on the following line.

- When the attribute value is a list of nodes, print the formatted attribute name, then nest the nodes content in the following lines without changing their order.

The visitor object can be initialized with show_empty=False to suppress the print of attributes which value is either None, an empty lists [], or an empty string "".

Here a reference example:

>>> import ast
>>> code = """
one_plus_two = 1+2
one_plus_two+10
"""
>>> tree = ast.parse(code)
>>> print(ast.dump(tree)) # using the printing provided by the ast module
Module(body=[Assign(targets=[Name(id='one_plus_two', ctx=Store())], value=BinOp(left=Constant(value=1), op=Add(), right=Constant(value=2))), Expr(value=BinOp(left=Name(id='one_plus_two', ctx=Load()), op=Add(), right=Constant(value=10)))], type_ignores=[])
>>> vst = AstPrinter()
>>> vst.visit(tree)
Module()
   .type_ignores: []
   .body:
      Assign()
         .type_comment: None
         .targets:
            Name()
               .id: 'one_plus_two'
               .ctx:
                  Store()
         .value:
            BinOp()
               .left:
                  Constant()
                     .kind: None
                     .value: 1
               .op:
                  Add()
               .right:
                  Constant()
                     .kind: None
                     .value: 2
      Expr()
         .value:
            BinOp()
               .left:
                  Name()
                     .id: 'one_plus_two'
                     .ctx:
                        Load()
               .op:
                  Add()
               .right:
                  Constant()
                     .kind: None
                     .value: 10
>>> vst = AstPrinter(show_empty=False) # suppressing empty attributes
>>> vst.visit(tree)
Module()
   .body:
      Assign()
         .targets:
            Name()
               .id: 'one_plus_two'
               .ctx:
                  Store()
         .value:
            BinOp()
               .left:
                  Constant()
                     .value: 1
               .op:
                  Add()
               .right:
                  Constant()
                     .value: 2
      Expr()
         .value:
            BinOp()
               .left:
                  Name()
                     .id: 'one_plus_two'
                     .ctx:
                        Load()
               .op:
                  Add()
               .right:
                  Constant()
                     .value: 10

Happy coding in Python!

AST