.. image:: https://travis-ci.com/marmeladema/calligra.svg?branch=master :target: https://travis-ci.com/marmeladema/calligra
:Author: Elie ROUDNINSKI <mailto:xademax@gmail.com>
_
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:: :backlinks: none
.. sectnum::
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
.. |tests/| replace:: tests/
.. _tests/: tests/
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:
.. code-block:: Python
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:
.. code-block:: Python
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:
.. code-block:: Python
print(person.define())
This should generate something similar to:
.. code-block:: C
struct person {
char *name;
uint8_t age;
};
More advanced examples are located in the |examples/|_ directory.
.. |examples/| replace:: examples/
.. _examples/: examples/
Conversion modules are located in the |calligra/convert/|_ directory and are meant to (un)serialize C types to and from another format (like JSON).
.. |calligra/convert/| replace:: calligra/convert/
.. _calligra/convert/: calligra/convert/
Currently available conversion modules are:
-
calligra.convert.jansson
: to convert C types to and from JSON using theJansson <https://github.com/akheron/jansson/>
_ library.
Jansson
In order to use the jansson conversion module, just import the `calligra.convert.jansson` module:
.. code-block:: Python
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:
.. code-block:: Python
print(person.to_json.define())
Which should generate something similar to:
.. code-block:: C
json_t *person_to_json(struct person const *person);
And for the function body:
.. code-block:: Python
print(person.to_json.code(body = True))
Which should generate something similar to (non-contractual code):
.. code-block:: C
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
--------
Importer modules are located in the |calligra/importer/|_ directory and are meant to import C types from another format (like C).
.. |calligra/importer/| replace:: ``calligra/importer/``
.. _calligra/importer/: calligra/importer/
Currently available importer modules are:
- `calligra.importer.cparser`: to import C types directly from C code using the `pycparser <https://github.com/eliben/pycparser/>`_ package.