Ż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
Quilt is a self-organizing data hub for S3

Quilt is a self-organizing data hub Python Quick start, tutorials If you have Python and an S3 bucket, you're ready to create versioned datasets with

Quilt Data 1.2k Dec 30, 2022
Literate-style documentation generator.

888888b. 888 Y88b 888 888 888 d88P 888 888 .d8888b .d8888b .d88b. 8888888P" 888 888 d88P" d88P" d88""88b 888 888 888

Pycco 808 Dec 27, 2022
A powerful Sphinx changelog-generating extension.

What is Releases? Releases is a Python (2.7, 3.4+) compatible Sphinx (1.8+) extension designed to help you keep a source control friendly, merge frien

Jeff Forcier 166 Dec 29, 2022
Searches a document for hash tags. Support multiple natural languages. Works in various contexts.

ht-getter Searches a document for hash tags. Supports multiple natural languages. Works in various contexts. This package uses a non-regex approach an

Rairye 1 Mar 01, 2022
YAML metadata extension for Python-Markdown

YAML metadata extension for Python-Markdown This extension adds YAML meta data handling to markdown with all YAML features. As in the original, metada

Nikita Sivakov 14 Dec 30, 2022
A clean customizable documentation theme for Sphinx

A clean customizable documentation theme for Sphinx

Pradyun Gedam 1.5k Jan 06, 2023
Documentation for GitHub Copilot

NOTE: GitHub Copilot discussions have moved to the Copilot Feedback forum. GitHub Copilot Welcome to the GitHub Copilot user community! In this reposi

GitHub 21.3k Dec 28, 2022
learn python in 100 days, a simple step could be follow from beginner to master of every aspect of python programming and project also include side project which you can use as demo project for your personal portfolio

learn python in 100 days, a simple step could be follow from beginner to master of every aspect of python programming and project also include side project which you can use as demo project for your

BDFD 6 Nov 05, 2022
Canonical source repository for PyYAML

PyYAML - The next generation YAML parser and emitter for Python. To install, type 'python setup.py install'. By default, the setup.py script checks

The YAML Project 2k Jan 01, 2023
Build AGNOS, the operating system for your comma three

agnos-builder This is the tool to build AGNOS, our Ubuntu based OS. AGNOS runs on the comma three devkit. NOTE: the edk2_tici and agnos-firmare submod

comma.ai 21 Dec 24, 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
A simple USI Shogi Engine written in python using python-shogi.

Revengeshogi My attempt at creating a USI Shogi Engine in python using python-shogi. Current State of Engine Currently only generating random moves us

1 Jan 06, 2022
Leetcode Practice

LeetCode Practice Description This is my LeetCode Practice. Visit LeetCode Website for detailed question description. The code in this repository has

Leo Hsieh 75 Dec 27, 2022
Python-slp - Side Ledger Protocol With Python

Side Ledger Protocol Run python-slp node First install Mongo DB and run the mong

Solar 3 Mar 02, 2022
The Python Dict that's better than heroin.

addict addict is a Python module that gives you dictionaries whose values are both gettable and settable using attributes, in addition to standard ite

Mats Julian Olsen 2.3k Dec 22, 2022
advance python series: Data Classes, OOPs, python

Working With Pydantic - Built-in Data Process ========================== Normal way to process data (reading json file): the normal princiople, it's f

Phung Hưng Binh 1 Nov 08, 2021
Explain yourself! Interrogate a codebase for docstring coverage.

interrogate: explain yourself Interrogate a codebase for docstring coverage. Why Do I Need This? interrogate checks your code base for missing docstri

Lynn Root 435 Dec 29, 2022
Plotting and analysis tools for ARTIS simulations

Artistools Artistools is collection of plotting, analysis, and file format conversion tools for the ARTIS radiative transfer code. Installation First

ARTIS Monte Carlo Radiative Transfer 8 Nov 07, 2022
PyPresent - create slide presentations from notes

PyPresent Create slide presentations from notes Add some formatting to text file

1 Jan 06, 2022
Manage your WordPress installation directly from SublimeText SideBar and Command Palette.

WordpressPluginManager Manage your WordPress installation directly from SublimeText SideBar and Command Palette. Installation Dependencies You will ne

Art-i desenvolvimento 1 Dec 14, 2021