Żmija is a simple universal code generation tool.

Overview

Zmija


GitHub code size in bytes GitHub issues GitHub last commit GitHub commit activity GitHub


Żmija

Żmija is a simple universal code generation tool. It is intended to be used as a means to generate code that is both efficient and easily maintainable.

It is intended to be used in embedded systems with limited resources, however it can be used anywhere else as well.


Usage

Żmija lets you define sections in your code where code is generated automatically in accordance to a provided Python script. Such a section typically looks like this:

/* ~ZMIJA.GENERATOR:
def declare(variables):
	pass
	
def init(variables):
	pass
	
def generate(variables):
	return ""
*/// ~ZMIJA.GENERATED_CODE:

// ~ZMIJA.END

The section is defined inside a multi-line comment as to not affect the compilation of the code it is located in. Żmija supports any languge, including those that have non C-style comment styles (hence it is universal).

This is what the same section might look like inside a Lua script, for example:

--[[ ~ZMIJA.GENERATOR:
def declare(variables):
	pass
	
def init(variables):
	pass
	
def generate(variables):
	return ""
]]-- ~ZMIJA.GENERATED_CODE:

-- ~ZMIJA.END

Each section consists of a declare-function, an init-function and a generate-function. Each function is provided with the variables argument, which is a dictionary that is intended to be used for the storage of variables.

The declare-function is executed first. It is meant for variable declaration and should only reference its own variables.

The init-function is meant to initialize variables, including those of other sections. It is executed only after the declare-function has been executed for all sections in the project.

The generate-function returns the generated code for the section it is located in. It is executed only after the declare and init-functions of all sections have been executed.

Note: Empty functions can safely be removed.


Run python3 ./src/zmija.py /path/to/your/project/directory/ to perform the code generation. The generated code will be placed between the ~ZMIJA.GENERATED_CODE: and the ~ZMIJA.END lines.


Help output

Zmija. Simple universal code generation.

Usage:
	zmija.py path
	zmija.py path -d | --delete
	zmija.py path -c | --check-only
	zmija.py path --config-path="path/to/config"
	zmija.py -h | --help
	
Options:
	-h --help         Show this screen.
	-d --delete       Delete all generated code.
	-c --check-only   Check Python code for syntax and runtime errors without writing the
	                  changes to file.
	-u --unsafe       Skip the test pass. May cause data loss if the Python code raises
	                  exceptions, but offers better performance. Use with caution.
	--config-path     Provides a path to a configuration file.
	
Config:
	file_filter(file_path)     A function intended to filter file paths. Any file path
	                           for which this function returns False is ignored.

Config

You can define a file path filter function inside a config file, such that certain files are ignored by Żmija.

Here's what an example config file may look like:

def file_filter(file_path):
	return file_path.endswith('.cpp') or file_path.endswith('.h')

The file path of the config file needs to be supplied using the --config-file argument, like so:

python3 ./src/zmija.py /path/to/your/project/directory/ --config-file="/path/to/your/config/file"


Example

Say you have two modules, a ButtonController and a LedController. You would like to implement the observer pattern to allow the ButtonController to communicate with the LedController without depending on it.

The following C++ code implements this. It is a simple example where pressing the button toggles the LED.

register_callback([this]() { toggle_led(); }); } int main() { button_controller = new ButtonController(); led_controller = new LedController(); button_controller->on_button_pressed(); return 0; } ">
#include <stdio.h>
#include <vector>
#include <functional>

// This would typically go into .h files:
struct ButtonController {
private:
    // Callbacks are functions that will be called
    // when the button is pressed. Notice how the
    // vector is constructed at runtime and held in
    // RAM.
    std::vector
   <
   void()>> callbacks;


   public:
    
   void 
   on_button_pressed();
    
   void 
   register_callback(std::function<
   void()> cb);
};


   struct 
   LedController {

   public:
    
   void 
   toggle_led();

    
   LedController();
};




   // This would typically go into .cpp files:
ButtonController *button_controller;
LedController *led_controller;


   // This function is meant to be automatically

   // called whenever a button is pressed.

   void 
   ButtonController::on_button_pressed() {
    
   // call all registered callbacks
    
   for (
   auto &cb : callbacks) 
   cb();
}


   // This function is meant to be called by other

   // modules that would like to react to button

   // presses.

   void 
   ButtonController::register_callback(std::function<
   void()> cb) {
    callbacks.
   push_back(cb);
}



   void 
   LedController::toggle_led() {
    
   printf(
   "LED toggled.\n");
}


   LedController::LedController() {
    
   // Registering a new callback consumes precious RAM.
    button_controller->
   register_callback([
   this]() {
        
   toggle_led();
    });
}


   int 
   main() {
    button_controller = 
   new 
   ButtonController();
    led_controller = 
   new 
   LedController();

    button_controller->
   on_button_pressed();

    
   return 
   0;
}
  

Calling the main() function will print LED toggled. to the console, as intended.

However, the ButtonController's callbacks vector is built during runtime and held in RAM. This causes an unnecessary overhead regarding both memory usage and execution speed.

Since the registered callbacks do not change after they have been registered, it may be beneficial to register them during compile time instead.


The following C++ code attempts to achieve this by using Żmija to generate the callbacks during compile time:

toggle_led();") def generate(variables): # Nothing to do. This function can safely be removed. return '' */// ~ZMIJA.GENERATED_CODE: // ~ZMIJA.END } int main() { button_controller = new ButtonController(); led_controller = new LedController(); button_controller->on_button_pressed(); return 0; } ">
#include <stdio.h>

// This would typically go into .h files:
struct ButtonController {
public:
	void on_button_pressed();
};

struct LedController {
public:
	void toggle_led();

	LedController();
};



// This would typically go into .cpp files:
ButtonController *button_controller;
LedController *led_controller;

// This function is meant to be automatically
// called whenever a button is pressed.
void ButtonController::on_button_pressed() {
	/* ~ZMIJA.GENERATOR:
	def declare(variables):
		# Declare a new list called "on_button_pressed".
		# This list will contain calls to callback functions
		# in string form.
		variables["on_button_pressed"] = []
		
	def init(variables):
		# Nothing to do. This function can safely be removed.
		pass
		
	def generate(variables):
		# Return a string containing all callback calls,
		# separated by a newline character.
		return "\n".join(variables["on_button_pressed"])
	*/// ~ZMIJA.GENERATED_CODE:
	
	// ~ZMIJA.END
}


void LedController::toggle_led() {
	printf("LED toggled.\n");
}

LedController::LedController() {
	/* ~ZMIJA.GENERATOR:
	def declare(variables):
		# Nothing to do. This function can safely be removed.
		pass
	
	def init(variables):
		# Add a callback call in string form.
		# This string will be added to the ButtonController's 
		# generated code.
		variables["on_button_pressed"].append("led_controller->toggle_led();")
		
	def generate(variables):
		# Nothing to do. This function can safely be removed.
		return ''
	*/// ~ZMIJA.GENERATED_CODE:
	
	// ~ZMIJA.END
}

int main() {
	button_controller = new ButtonController();
	led_controller = new LedController();

	button_controller->on_button_pressed();

	return 0;
}

Let's run Żmija:

python3 ./src/zmija.py /path/to/your/project/directory/

This is what our newly generated .cpp file looks like now:

toggle_led(); // ~ZMIJA.END } void LedController::toggle_led() { printf("LED toggled.\n"); } LedController::LedController() { /* ~ZMIJA.GENERATOR: def declare(variables): # Nothing to do. This function can safely be removed. pass def init(variables): # Add a callback call in string form. # This string will be added to the ButtonController's # generated code. variables["on_button_pressed"].append("led_controller->toggle_led();") def generate(variables): # Nothing to do. This function can safely be removed. return '' */// ~ZMIJA.GENERATED_CODE: // ~ZMIJA.END } int main() { button_controller = new ButtonController(); led_controller = new LedController(); button_controller->on_button_pressed(); return 0; } ">
#include <stdio.h>

// This would typically go into .h files:
struct ButtonController {
public:
	void on_button_pressed();
};

struct LedController {
public:
	void toggle_led();

	LedController();
};



// This would typically go into .cpp files:
ButtonController *button_controller;
LedController *led_controller;

// This function is meant to be automatically
// called whenever a button is pressed.
void ButtonController::on_button_pressed() {
	/* ~ZMIJA.GENERATOR:
	def declare(variables):
		# Declare a new list called "on_button_pressed".
		# This list will contain calls to callback functions
		# in string form.
		variables["on_button_pressed"] = []
		
	def init(variables):
		# Nothing to do. This function can safely be removed.
		pass
		
	def generate(variables):
		# Return a string containing all callback calls,
		# separated by a newline character.
		return "\n".join(variables["on_button_pressed"])
	*/// ~ZMIJA.GENERATED_CODE:
	led_controller->toggle_led();
	// ~ZMIJA.END
}


void LedController::toggle_led() {
	printf("LED toggled.\n");
}

LedController::LedController() {
	/* ~ZMIJA.GENERATOR:
	def declare(variables):
		# Nothing to do. This function can safely be removed.
		pass
	
	def init(variables):
		# Add a callback call in string form.
		# This string will be added to the ButtonController's 
		# generated code.
		variables["on_button_pressed"].append("led_controller->toggle_led();")
		
	def generate(variables):
		# Nothing to do. This function can safely be removed.
		return ''
	*/// ~ZMIJA.GENERATED_CODE:
	
	// ~ZMIJA.END
}

int main() {
	button_controller = new ButtonController();
	led_controller = new LedController();

	button_controller->on_button_pressed();

	return 0;
}

As you can see, Żmija has generated the led_controller->toggle_led();-line, just as intended.

Owner
Adrian Samoticha
Adrian Samoticha
Zero configuration Airflow plugin that let you manage your DAG files.

simple-dag-editor SimpleDagEditor is a zero configuration plugin for Apache Airflow. It provides a file managing interface that points to your dag_fol

30 Jul 20, 2022
Resource hub for Obsidian resources.

Obsidian Community Vault Welcome! This is an experimental vault that is maintained by the Obsidian community. For best results we recommend downloadin

Obsidian Community 320 Jan 02, 2023
Quick tutorial on orchest.io that shows how to build multiple deep learning models on your data with a single line of code using python

Deep AutoViML Pipeline for orchest.io Quickstart Build Deep Learning models with a single line of code: deep_autoviml Deep AutoViML helps you build te

Ram Seshadri 6 Oct 02, 2022
30 days of Python programming challenge is a step-by-step guide to learn the Python programming language in 30 days

30 days of Python programming challenge is a step-by-step guide to learn the Python programming language in 30 days. This challenge may take more than100 days, follow your own pace.

Asabeneh 17.7k Jan 07, 2023
Soccerdata - Efficiently scrape soccer data from various sources

SoccerData is a collection of wrappers over soccer data from Club Elo, ESPN, FBr

Pieter Robberechts 195 Jan 04, 2023
Version bêta d'un système pour suivre les prix des livres chez Books to Scrape,

Version bêta d'un système pour suivre les prix des livres chez Books to Scrape, un revendeur de livres en ligne. En pratique, dans cette version bêta, le programme n'effectuera pas une véritable surv

Mouhamed Dia 1 Jan 06, 2022
Generating a report CSV and send it to an email - Python / Django Rest Framework

Generating a report in CSV format and sending it to a email How to start project. Create a folder in your machine Create a virtual environment python3

alexandre Lopes 1 Jan 17, 2022
📚 Papers & tech blogs by companies sharing their work on data science & machine learning in production.

applied-ml Curated papers, articles, and blogs on data science & machine learning in production. ⚙️ Figuring out how to implement your ML project? Lea

Eugene Yan 22.1k Jan 03, 2023
A python package to avoid writing and maintaining duplicated python docstrings.

docstring-inheritance is a python package to avoid writing and maintaining duplicated python docstrings.

Antoine Dechaume 15 Dec 07, 2022
Data science on SDGs - Udemy Online Course Material: Data Science on Sustainable Development Goals

Data Science on Sustainable Development Goals (SDGs) Udemy Online Course Material: Data Science on Sustainable Development Goals https://bit.ly/data_s

Frank Kienle 1 Jan 04, 2022
freeCodeCamp Scientific Computing with Python Project for Certification.

Polygon_Area_Calculator freeCodeCamp Python Project freeCodeCamp Scientific Computing with Python Project for Certification. In this project you will

Rajdeep Mondal 1 Dec 23, 2021
Minimal reproducible example for `mkdocstrings` Python handler issue

Minimal reproducible example for `mkdocstrings` Python handler issue

Hayden Richards 0 Feb 17, 2022
A collection and example code of every topic you need to know about in the basics of Python.

The Python Beginners Guide: Master The Python Basics Tonight This guide is a collection of every topic you need to know about in the basics of Python.

Ahmed Baari 1 Dec 19, 2021
NoVmpy - NoVmpy with python

git clone -b dev-1 https://github.com/wallds/VTIL-Python.git cd VTIL-Python py s

263 Dec 23, 2022
Swagger Documentation Generator for Django REST Framework: deprecated

Django REST Swagger: deprecated (2019-06-04) This project is no longer being maintained. Please consider drf-yasg as an alternative/successor. I haven

Marc Gibbons 2.6k Jan 03, 2023
A document format conversion service based on Pandoc.

reformed Document format conversion service based on Pandoc. Usage The API specification for the Reformed server is as follows: GET /api/v1/formats: L

David Lougheed 3 Jul 18, 2022
JMESPath is a query language for JSON.

JMESPath JMESPath (pronounced "james path") allows you to declaratively specify how to extract elements from a JSON document. For example, given this

1.7k Dec 31, 2022
A plugin to introduce a generic API for Decompiler support in GEF

decomp2gef A plugin to introduce a generic API for Decompiler support in GEF. Like GEF, the plugin is battery-included and requires no external depend

Zion 379 Jan 08, 2023
Crystal Smp plugin for show scoreboards

MCDR-CrystalScoreboards Crystal plugin for show scoreboards | Only 1.12 Usage !!s : Plugin help message !!s hide : Hide scoreboard !!s show : Show Sco

CristhianCd 3 Oct 12, 2021
A tool that allows for versioning sites built with mkdocs

mkdocs-versioning mkdocs-versioning is a plugin for mkdocs, a tool designed to create static websites usually for generating project documentation. mk

Zayd Patel 38 Feb 26, 2022