Programmatic Code Generation: Composability


After working some more with Tiles, my mini-library for programmatic code generation, I've realized it has a feature that I haven't knowingly baked in, but which makes it a really good tool for the job. Namely, the code you write is composable.

Here's what I mean: If you want to generate a C function that prints out names of countries' capitals then you can generate the code that does the actual printing first:

 data = { "Afghanistan": "Kabul", "Brazil": "Brasilia", "Canada": "Ottawa", } body = t/'' for country, capital in data.items(): body |= t/""" printf("@{capital} is the capital of @{country}"); """ 

Once done, you cen generate the scaffolding:

 program = t/""" #include <stdio.h> int main(void) { @{body} return 0; } """ 

The example, of course, shows just two levels of nesting. In practice, however, you'd have many levels of nesting.

But whatever the nesting level, you, as a programmer, can focus on a single task at a time. First, you think about how to print the capitals. Then you think about the scaffolding. There's no overlap.

Compare that to your typical templating solution:

 template = Template(""" #include <stdio.h> int main(void) { {% for country, capital in data.items() %} printf("{{ capital }} is the capital of {{ country }}"); {% endfor %} return 0; } """) template.render(data=data) 

Note how you start with writing scaffolding (include, main) then you switch to the business logic (printf), then you get back to scaffolding (return).

The example is trivial and as such, the both solutions are kind of comparable.

However, imagine there are five levels of nesting.

The code written using Tiles would, logically, look like this:

 level1 level2 level3 level4 level5 

The code written using one the classic templating tools, on the other hand, would look like this:

 level1 level2 level3 level4 level5 level4 level3 level2 level1 

March 2nd, 2019

by martin_sustrik