Ż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
k3heap is a binary min heap implemented with reference

k3heap k3heap is a binary min heap implemented with reference k3heap is a component of pykit3 project: a python3 toolkit set. In this module RefHeap i

pykit3 1 Nov 13, 2021
A collection of simple python mini projects to enhance your python skills

A collection of simple python mini projects to enhance your python skills

PYTHON WORLD 12.1k Jan 05, 2023
Preview title and other information about links sent to chats.

Link Preview A small plugin for Nicotine+ to display preview information like title and description about links sent in chats. Plugin created with Nic

Nick 0 Sep 05, 2021
Numpy's Sphinx extensions

numpydoc -- Numpy's Sphinx extensions This package provides the numpydoc Sphinx extension for handling docstrings formatted according to the NumPy doc

NumPy 234 Dec 26, 2022
Source Code for 'Practical Python Projects' (video) by Sunil Gupta

Apress Source Code This repository accompanies %Practical Python Projects by Sunil Gupta (Apress, 2021). Download the files as a zip using the green b

Apress 2 Jun 01, 2022
A Material Design theme for MkDocs

A Material Design theme for MkDocs Create a branded static site from a set of Markdown files to host the documentation of your Open Source or commerci

Martin Donath 12.3k Jan 04, 2023
An interview engine for businesses, interview those who are actually qualified and are worth your time!

easyInterview V0.8B An interview engine for businesses, interview those who are actually qualified and are worth your time! Quick Overview You/the com

Vatsal Shukla 1 Nov 19, 2021
Elliptic curve cryptography (ed25519) beginner tutorials in Python 3

ed25519_tutorials Elliptic curve cryptography (ed25519) beginner tutorials in Python 3 Instructions Just download the repo and read the tutorial files

6 Dec 27, 2022
PySpark Cheat Sheet - learn PySpark and develop apps faster

This cheat sheet will help you learn PySpark and write PySpark apps faster. Everything in here is fully functional PySpark code you can run or adapt to your programs.

Carter Shanklin 168 Jan 01, 2023
My solutions to the Advent of Code 2021 problems in Go and Python 🎄

🎄 Advent of Code 2021 🎄 Summary Advent of Code is an annual Advent calendar of programming puzzles. This year I am doing it in Go and Python. Runnin

Orfeas Antoniou 16 Jun 16, 2022
DataRisk Detection Learning Resources

DataRisk Detection Learning Resources Data security: Based on the "data-centric security system" position, it generally refers to the entire security

Liao Wenzhe 59 Dec 05, 2022
Count the number of lines of code in a directory, minus the irrelevant stuff

countloc Simple library to count the lines of code in a directory (excluding stuff like node_modules) Simply just run: countloc node_modules args to

Anish 4 Feb 14, 2022
A simple flask application to collect annotations for the Turing Change Point Dataset, a benchmark dataset for change point detection algorithms

AnnotateChange Welcome to the repository of the "AnnotateChange" application. This application was created to collect annotations of time series data

The Alan Turing Institute 16 Jul 21, 2022
Feature Store for Machine Learning

Overview Feast is an open source feature store for machine learning. Feast is the fastest path to productionizing analytic data for model training and

Feast 3.8k Dec 30, 2022
Netbox Dns is a netbox plugin for managing zone, nameserver and record inventory.

Netbox DNS Netbox Dns is a netbox plugin for managing zone, nameserver and record inventory. Features Manage zones (domains) you have. Manage nameserv

Aurora Research Lab 155 Jan 06, 2023
Official Matplotlib cheat sheets

Official Matplotlib cheat sheets

Matplotlib Developers 6.7k Jan 09, 2023
EasyModerationKit is an open-source framework designed to moderate and filter inappropriate content.

EasyModerationKit is a public transparency statement. It declares any repositories and legalities used in the EasyModeration system. It allows for implementing EasyModeration into an advanced charact

Aarav 1 Jan 16, 2022
This is a small project written to help build documentation for projects in less time.

Documentation-Builder This is a small project written to help build documentation for projects in less time. About This project builds documentation f

Tom Jebbo 2 Jan 17, 2022
Materi workshop "Light up your Python!" Himpunan Mahasiswa Sistem Informasi Fakultas Ilmu Komputer Universitas Singaperbangsa Karawang, 4 September 2021 (Online via Zoom).

Workshop Python UNSIKA 2021 Materi workshop "Light up your Python!" Himpunan Mahasiswa Sistem Informasi Fakultas Ilmu Komputer Universitas Singaperban

Eka Putra 20 Mar 24, 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