Tutorial I: A little tour of CNORM¶
At least, for using CNORM, you just need few imports.:
from cnorm.parsing.declaration import Declaration
from cnorm.passes import to_c
cparse = Declaration()
ast = cparse.parse_file("test.c")
print(ast.to_c())
This little script processes the file test.c
, creates an AST and prints a re-generated C source code:
- The object
Declaration
is a pyrser grammar of a C parser.parse_file()
is provide by pyrser to parse a file and return an AST.- The
to_c()
method allow you to re-generate a C source code from an AST.
Let’s code a little tool using CNORM called hfile. Our goal is to process a C implementation file (.c) and to generate an corresponding header file.
1- Going thru Declarations¶
We need to:
- Parse a C file and get an AST.
- Walk thru that AST and identify
declaration
.- Create a new AST containing only external declaration for the future header file.
- Re-generate C code from our new AST to get the header file.
We could begin with our typical minimal script.:
import sys
from cnorm.parsing.declaration import Declaration
from cnorm.passes import to_c
cparse = Declaration()
ast = cparse.parse_file(sys.argv[1])
Now, we need to iterate thru the ast. By default, the nodes return by parse
methods are RootBlockStmt. A RootBlockStmt is a special BlockStmt to hold
global scope definition. This class provide an iterable body.:
for node in ast.body:
print("%s" % type(node))
This kind of program show us that all things we could get from a RootBlockStmt is just <class 'cnorm.nodes.Decl'>
instance.
See Cnorm AST for more information about AST nodes.
2- Modifying AST nodes¶
For our header file we need to store all declaration and made some transformation:
- Create a RootBlockStmt for our final header file.
- Copy declaration nodes
- Transform global variable definition into
extern
variable declaration.- Copy function prototype without the body.
To do safe transformation, we could use the module copy
to do a swallow copy of each node and alter some flags.
Begins by creating a variable to store our futur global scope.:
from cnorm import nodes header_file = nodes.RootBlockStmt([])
Copy safely the nodes.:
from copy import copy for node in ast.body: futur_node = copy(node) futur_node._ctype = copy(node._ctype)
Made some change.:
# Function become prototype if hasattr(futur_node, 'body'): delattr(futur_node, 'body') # No assignement of variable if hasattr(futur_node, '_assign_expr'): delattr(futur_node, '_assign_expr') if hasattr(futur_node, '_colon_expr'): delattr(futur_node, '_colon_expr') # Add extern storage futur_node._storage = nodes.Storages.EXTERN
Store the new node.:
header_file.body.append(futur_node)
Understanding AST is essential to play with CNORM. See below the complete example:
import sys
from cnorm.parsing.declaration import Declaration
from cnorm.passes import to_c
cparse = Declaration()
ast = cparse.parse_file(sys.argv[1])
from cnorm import nodes
header_file = nodes.RootBlockStmt([])
from copy import copy
for node in ast.body:
futur_node = copy(node)
futur_node._ctype = copy(node._ctype)
# Function become prototype
if hasattr(futur_node, 'body'):
delattr(futur_node, 'body')
# No assignement of variable
if hasattr(futur_node, '_assign_expr'):
delattr(futur_node, '_assign_expr')
if hasattr(futur_node, '_colon_expr'):
delattr(futur_node, '_colon_expr')
# Add extern storage
futur_node.ctype._storage = nodes.Storages.EXTERN
# store
header_file.body.append(futur_node)
fout = open(sys.argv[1].rpartition('.')[0] + ".h", "w")
fout.write(str(header_file.to_c()))
fout.close()