Skip to content

Latest commit

 

History

History
318 lines (251 loc) · 8.47 KB

README.md

File metadata and controls

318 lines (251 loc) · 8.47 KB

In-game sprite of Metang from Pokémon Black and White

metang

A metaprogramming utility to generate enumerated constants from plain-text for C and Python.

Table of Contents

Background

C's support for enumerated constants is less than stellar. Member values of enum don't contain any identifying information at runtime, which makes employing them as waymarks awkward when parsing textual data. An easy way to ameliorate this is by ingesting some simple input schema containing enumeration members and generating a header file which exposes both the enumeration and a lookup table mapping string values to members.

metang is a simple implementation of such a program. Its feature-set is basic, yet allows a user to customize the content of their generated header as they see fit. The headers that it generates are usable both in data parsing programs in need of its lookup tables and in production code where only the enumeration itself is desired.

Etymology

metang takes its moniker from the Pokémon of the same name.

Install

Build from Source

> git clone https://github.com/lhearachel/metang.git
> cd metang
> make install

Once installed, verify that you can run the executable:

> metang -v
0.1.1

By default, metang will install to ~/.local/bin. If you wish to install it to a different directory, run make install with your desired directory as an argument:

> DESTDIR=path/to/target make install

metang will install itself into the bin subdirectory of the given value for DESTDIR.

Integrate with a Meson Project

metang also ships with a meson.build file to support integration as a Meson subproject. To add metang's tooling support to your project, create the following metang.wrap file in your subprojects directory:

[wrap-git]
url = https://github.com/lhearachel/metang.git
; Replace <main> here with a release tag or commit hash, if desired.
revision = main
depth = 1

[provide]
program_names = metang

Usage

metang will generate a C header from some input file with the following content sections, each of which is surrounded by a corresponding preprocessor guard:

  1. An enum type definition.
  2. Preprocessor definitions.
  3. A lookup table mapping enumerators to a string identifier.

To illustrate, suppose that we have the following input listing, fed from standard input:

Bulbasaur
Ivysaur
Venusaur
Charmander
Charmeleon
Charizard
Squirtle
Wartortle
Blastoise
Porygon2
Porygon-Z
Farfetch'd
Mr. Mime
Mime Jr.

metang will then emit the following to standard output:

/*
 * This file was generated by metang; DO NOT MODIFY IT!!
 * Base command: enum
 * Source file: stdin
 * Program options:
 */

#ifndef METANG_STDOUT
#define METANG_STDOUT

#ifdef __cplusplus
extern "C" {
#endif

#ifdef METANG_ENUM

enum stdin {
    BULBASAUR  =  0,
    IVYSAUR    =  1,
    VENUSAUR   =  2,
    CHARMANDER =  3,
    CHARMELEON =  4,
    CHARIZARD  =  5,
    SQUIRTLE   =  6,
    WARTORTLE  =  7,
    BLASTOISE  =  8,
    PORYGON2   =  9,
    PORYGON_Z  = 10,
    FARFETCHD  = 11,
    MR_MIME    = 12,
    MIME_JR    = 13,
};

#else

#define BULBASAUR   0
#define IVYSAUR     1
#define VENUSAUR    2
#define CHARMANDER  3
#define CHARMELEON  4
#define CHARIZARD   5
#define SQUIRTLE    6
#define WARTORTLE   7
#define BLASTOISE   8
#define PORYGON2    9
#define PORYGON_Z  10
#define FARFETCHD  11
#define MR_MIME    12
#define MIME_JR    13

#endif /* METANG_ENUM */

#ifdef METANG_LOOKUP

typedef struct entry__stdin {
    const long value;
    const char *def;
} entry__stdin;

#ifndef METANG_LOOKUP_IMPL

extern const long lengthof__stdin;
extern const entry__stdin lookup__stdin[];

#else

const long lengthof__stdin = 14;
const entry__stdin lookup__stdin[] = {
    { BLASTOISE,  "BLASTOISE",  },
    { BULBASAUR,  "BULBASAUR",  },
    { CHARIZARD,  "CHARIZARD",  },
    { CHARMANDER, "CHARMANDER", },
    { CHARMELEON, "CHARMELEON", },
    { FARFETCHD,  "FARFETCHD",  },
    { IVYSAUR,    "IVYSAUR",    },
    { MIME_JR,    "MIME_JR",    },
    { MR_MIME,    "MR_MIME",    },
    { PORYGON2,   "PORYGON2",   },
    { PORYGON_Z,  "PORYGON_Z",  },
    { SQUIRTLE,   "SQUIRTLE",   },
    { VENUSAUR,   "VENUSAUR",   },
    { WARTORTLE,  "WARTORTLE",  },
};

#endif /* METANG_LOOKUP_IMPL */

#endif /* METANG_LOOKUP */

#ifdef __cplusplus
}
#endif

#endif /* METANG_STDOUT */

The default values in this emission are worth noting:

  1. METANG_ as a prefix on all preprocessor guards. This is a simple default to make output consistent.
  2. STDOUT as the suffix on the inclusion sentinels and leading prefix on the generated symbols. This is adapted from the output file name.
  3. stdin as the enum tag. This is adapted from the input file name.
  4. A starting index of 0 for the enumeration.

All of these defaults are configurable via program options. As a summary of available options, metang's built-in help-text should be sufficient. For further reading, each of these options has a corresponding example in the tests directory to illustrate how its behavior differs from the defaults.

> metang help
metang - Generate enumerated constants from plain-text

Usage: metang COMMAND [OPTIONS] [<FILE>]

Commands:
  enum     Generate an integral enumeration.
  mask     Generate a bitmask enumeration.
  help     Display this help text.
  version  Display the version number of this program.

Global Options:
  -L, --lang <LANG>        Generate the enumeration for a target language.
                           If unspecified, generate for the C language.
                           Options: c, py
  -o, --output <OFILE>     Write output to <OFILE>.
                           If unspecified, write to standard output.
  -l, --leader <LEADER>    Use <LEADER> as a prefix for generated symbols.
  -t, --tag-name <NAME>    Use <NAME> as the base tag for enums and lookup
                           tables.
                           If unspecified, <NAME> will be derived from the
                           input file's basename, minus any extension.
  -G, --guard <GUARD>      Prefix conditional directives with <GUARD>. For
                           example, in C, this will prefix inclusion guards.

When using the “enum” command, the following additional options are supported:
  -a, --append <ENTRY>         Append <ENTRY> to the input listing.
  -p, --prepend <ENTRY>        Prepend <ENTRY> to the input listing.
  -n, --start-from <NUMBER>    Start enumeration from <NUMBER>.


When using the “mask” command, the user should mind the following:
  1. The magic values NONE and ANY are automatically prepended and appended
     to user input, respectively. The NONE value is always assigned the value
     0; the ANY value is always assigned the sum of all previous mask indices.
  2. Overrides on assignment values from user input are not permitted. This is
     to ensure that the generated bitmask is contiguous.
  3. As a consequence of (1) and (2), overrides to the starting value are not
     permitted.

For more detailed reading on the various options, metang's installation procedure will install manual page in section 1. You can view this page by invoking man metang, as with any other shell-based utility.

Python

metang can also generate constants-files for Python using the --lang option with argument py:

> metang --lang py
"""
    This file was generated by metang; DO NOT MODIFY IT!!
    Base command: enum
    Source file: stdin
    Program options:
      --lang py
"""

import enum

class stdin(enum.IntEnum):
    BULBASAUR  =  0
    IVYSAUR    =  1
    VENUSAUR   =  2
    CHARMANDER =  3
    CHARMELEON =  4
    CHARIZARD  =  5
    SQUIRTLE   =  6
    WARTORTLE  =  7
    BLASTOISE  =  8
    PORYGON2   =  9
    PORYGON_Z  = 10
    FARFETCHD  = 11
    MR_MIME    = 12
    MIME_JR    = 13

Contributing

metang's small size and problem-scope mean that contribution guidelines are loose. Feel free to file an issue or a pull request!

License

metang is free software licensed under the Apache License, version 2.0. For further details, refer to the included license text.