Author: | Elie ROUDNINSKI |
---|
calligra is a pure Python package that tries to modelize a subset of the C langage syntax in order to reason about C types, from Python scripts. Its main goals are to do metaprogrammation and code generation. It does not parse C code itself at all.
calligra was first designed to (un)serialize complex (but not too complex ...) C structures from JSON.
Contents
calligra requires Python 3. It has been tested on Python 3.5 and 3.6 on Linux. At the moment, it requires only pycparser in order to import pre-existing type from already written C code.
As calligra is intended to generate code, some external dependencies might be needed to compile the generated code.
You can clone this repository and install it with setuptools directly:
$ python3 setup.py install --user
As every pip available package, you can install it easily with the pip package:
$ python3 -m pip install --user calligra
Tests are available in the source distribution (either from github or from pip) and are located in the tests/
directory.
You can run them with setuptools:
$ python3 setup.py test
As specified before, calligra is intended to reason about C types at a Python level.
Currently, you can modelize the following types:
- primary types like char, int, float, double etc.
- C strings (char*)
- enum
- struct, named and anonymous
- union, named and anonymous
and the following declaration modifiers:
- pointers
- array
Nested array of pointers or pointers to array are not supported.
At the moment, you have two choices:
- define everything from Python
- parse C code with the cparser importer module
In the future, you will be able to import definitions from:
- JSON Schema
Lets start with a basic example. In the following code snippets we will be defining a C structure called person with 2 members:
- a string name
- an unsigned integer age
And then we will generate the associated C code.
First import the main modules:
import calligra
import calligra.stdlib
calligra module is where the C type/declaration syntax is modelized. calligra.stdlib is where standard C types are defined.
Then define the structure:
namespace = calligra.stdlib.namespace
person = calligra.struct(namespace, 'person')
person.add(
calligra.declaration(
namespace, namespace.get('char'), 'name', pointer = True
)
)
person.add(
calligra.declaration(
namespace, namespace.get('uint8_t'), 'age'
)
)
Finally, generate the C code:
print(person.define())
This should generate something similar to:
struct person {
char *name;
uint8_t age;
};
More advanced examples are located in the examples/
directory.
Conversion modules are located in the calligra/convert/
directory and are meant to (un)serialize C types to and from another format (like JSON).
Currently available conversion modules are:
- calligra.convert.jansson: to convert C types to and from JSON using the Jansson library.
In order to use the jansson conversion module, just import the calligra.convert.jansson module:
import calligra.convert.jansson
After that, every type should now have a to_json and a from_json method. Those are actually calligra.functions object which you can define to generate the corresponding C code:
print(person.to_json.define())
Which should generate something similar to:
json_t *person_to_json(struct person const *person);
And for the function body:
print(person.to_json.code(body = True))
Which should generate something similar to (non-contractual code):
json_t *person_to_json(struct person const *person) {
json_t *json = json_object(), *child;
if(!json) {
return NULL;
}
/*name*/
if((person != NULL) && ((*person).name != NULL) && (*(*person).name != 0)) {
child = json_string((*person).name);
if(!child || json_object_set_new_nocheck(json, "name", child) != 0) {
if(child) {
json_decref(child);
}
json_decref(json);
return NULL;
}
}
/*age*/
if(person != NULL) {
child = json_integer((*person).age);
if(!child || json_object_set_new_nocheck(json, "age", child) != 0) {
if(child) {
json_decref(child);
}
json_decref(json);
return NULL;
}
}
return json;
}
Importer modules are located in the calligra/importer/
directory and are meant to import C types from another format (like C).
Currently available importer modules are:
- calligra.importer.cparser: to import C types directly from C code using the pycparser package.