{"id":18374927,"url":"https://github.com/grieferatwork/tpp","last_synced_at":"2025-04-06T20:30:45.505Z","repository":{"id":40457479,"uuid":"87530121","full_name":"GrieferAtWork/tpp","owner":"GrieferAtWork","description":"Tiny PreProcessor","archived":false,"fork":false,"pushed_at":"2025-03-15T11:57:04.000Z","size":2257,"stargazers_count":17,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-15T12:30:48.376Z","etag":null,"topics":["c","commandline","extension","extensions","generic","pipe","preprocessor","std-c","tpp"],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GrieferAtWork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-04-07T09:37:42.000Z","updated_at":"2025-03-15T11:57:08.000Z","dependencies_parsed_at":"2024-03-10T12:40:34.540Z","dependency_job_id":"30b4f87a-813a-43a1-a7f7-0bdd9583f44b","html_url":"https://github.com/GrieferAtWork/tpp","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrieferAtWork%2Ftpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrieferAtWork%2Ftpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrieferAtWork%2Ftpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GrieferAtWork%2Ftpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GrieferAtWork","download_url":"https://codeload.github.com/GrieferAtWork/tpp/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247547072,"owners_count":20956481,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c","commandline","extension","extensions","generic","pipe","preprocessor","std-c","tpp"],"created_at":"2024-11-06T00:16:41.884Z","updated_at":"2025-04-06T20:30:45.098Z","avatar_url":"https://github.com/GrieferAtWork.png","language":"C","readme":"# tpp - Tiny PreProcessor\n\nTPP is a tiny (single source file) C Preprocessor, meant as a low-level backend for compilers of C and C-like languages, whilst implementing pretty much all preprocessor extensions supported by other compilers, ontop of a large number of its own (tpp-specific) extensions.\n\n### Usage\n\nTPP is designed to be used in 1 of 2 ways:\n\n- As a back-end for writing C and C-like compilers with all of the work needed for dealing with macros, directives, warnings, and preprocessor-related pragmas already taken care of.  \n  For this purpose, TPP is meant to be included statically as part of another project's source tree.\n  - For samples on how this can be done, look in `/samples`\n\n- As a standalone Preprocessor (similar to GNU's `cpp` utility).  \n  For this purpose, you may build `src/frontend.c` to produce the commandline utility\n\n\n### Features\n\n- TPP operates without any intermediate buffers, in that it is capable of preprocessing/tokenizing text in real-time from stream-like input sources:\n\n  ```sh\n  cat file.c | tpp\n  ```\n\n  The above command can be used to preprocess text, without any need for any intermediate buffers (beyond what is needed for a single token) before TPP will start producing output.\n\n  This means that TPP will be able to parse input, and print preprocessed output in real-time, without needing to wait for you to close a PIPE, or reach the end of its input file, first.\n\n  In a sense, this can be compared to python's interactive commandline, which allows it to execute code as you write it. So while an interactive C commandline ~~is probably impossible~~ [is possible](https://github.com/GrieferAtWork/dcc), an interactive C preprocessor ~~most definitely isn't~~ obviously is, too. - Because that's what TPP is.\n\n- Invocation of the TPP frontend is also designed to be usable by other utilities via the `--pp` switch, which, rather than producing preprocessed output, will emit individual tokens seperated by `'\\0'`-characters, allowing you to leave all of the tokenization to tpp, and do the processing yourself in a seperate process.\n\n\n### Preprocessor Extensions\n\nAll extensions can be hard enabled/disabled when building tpp, or soft-enabled/disabled via `#pragma extension(\"-fname\")` / `#pragma extension(\"-fno-name\")` directives.\n\nSupported extensions can be tested for with `__has_known_extension(\"name\")`, and supported+enabled extensions with `__has_extension(\"name\")`. In turn, `__has_known_extension` and `__has_extension` can be tested for with `#ifdef`.\n\n- Trigraphs (`??*`)\n- Digraphs (`\u003c%` is `{`, etc...)\n- STDC `__VA_ARGS__` (`#define printf(...) fprintf(stderr, __VA_ARGS__)`)\n- GCC-style varargs macros (`#define printf(format...) fprintf(stderr, format)`)\n- GCC-style va-comma (`#define printf(format, ...) fprintf(stderr, format, ##__VA_ARGS__)`)\n- GCC-style if-else in expressions (`#if foo ?: bar`; same as `#if foo ? foo : bar`)\n- c++20 `__VA_OPT__` (`#define printf(format, ...) fprintf(stderr, format __VA_OPT__(,) __VA_ARGS__)`)\n- `'\\e'` escape codes in strings and characters (for ASCII `ESC`, iow: `'\\x1b'`)\n- Binary literals (`0b00101010`)\n- MSVC's `__pragma(foo)` built-in function-like directive/macro\n- STDC's `_Pragma(\"foo\")` built-in function-like directive/macro\n- GCC-style `#warning My Message Here` directives\n- GCC-style `#include_next \u003cheader.h\u003e` directives\n- OBJc-style `#import \u003cheader.h\u003e` directives\n- `#ident` and `#sccs` directives (true origin unknown; accepted by some ancient compilers for placing comments into `.o`-files)\n- STDC magic macros `__FILE__`, `__LINE__`, `__TIME__` and `__DATE__`\n- Magic macro `__BASE_FILE__`\n- Magic macros `__INCLUDE_LEVEL__` and `__INCLUDE_DEPTH__`\n- Magic macro `__COUNTER__`\n- Magic macro `__TIMESTAMP__`\n- Magic macro `__COLUMN__` (tpp-specific; like `__LINE__`, but the current column-position)\n- STDC-compliant preprocessor `#if`-expressions\n- All STDC-required preprocessor directives (`#define`, `#undef`, `#if`, `#ifdef`, `#ifndef`, `#elif`, `#else`, `#endif`, `#include`, `#pragma`, `#error`)\n- Clang-style feature test macros:\n\t- `__has_include()`\n\t- `__has_include_next()`\n\t- `__is_identifier()`, `__is_deprecated()`, `__is_poisoned()`\n\t- `__has_attribute()`, `__has_builtin()`, ...\n\t- `__is_builtin_identifier()` (tpp-specific)\n\t- `__has_extension()`, `__has_known_extension()` (tpp-specific)\n\t- `__has_warning()`, `__has_known_warning()` (tpp-specific)\n- Treat `#!`-directives as comments (i.e. `#!/bin/tpp` is treated as a comment)\n- Treat `$` as a character, which may thus appear in an identifier\n- Preprocessor assertions (no; not those from `\u003cassert.h\u003e`..., but this):\n\t- `#assert cpu(mycpu)`\n\t- `#unassert cpu(yourcpu)`\n\t- `#if #cpu(mycpu)`\n- Multi-char character literals (e.g. `#if 'abc' == 6513249`)\n- `__VA_COMMA__` (tpp-specific extension)\n\t- `#define printf(format, ...) fprintf(stderr, format __VA_COMMA__ __VA_ARGS__)`\n\t- Expands to `,` when `__VA_ARGS__` are non-empty and `/**/` when empty.\n\t- Essentially the same as `__VA_OPT__(,)`\n- `__VA_NARGS__` (tpp-specific extension)\n\t- Expands to the decimal representation of the # of arguments in `__VA_ARGS__`\n\t- Useful for overloading macros by argument count  \n\n\t  ```c\n\t  #define min_1(a)       a\n\t  #define min_2(a, b)    ((a) \u003c (b) ? (a) : (b))\n\t  #define min_3(a, b, c) min_2(min_2(a, b), c)\n\t  #define min(...)       min_##__VA_NARGS__(__VA_ARGS__)\n\t  ```\n- Optional support for traditional macro expansion rules (disabled by default, can be enabled temporarily via `#pragma extension`)\n\t- ```c\n\t  #define CAT(a,b) a ## b\n\t  #define STR(x)   #x\n\t  #pragma extension(push, \"-ftraditional-macro\")\n\t  #define T_CAT(a, b) a/**/b\n\t  #define T_STR(x)    \"x\"\n\t  #pragma extension(pop)\n\t  CAT(10, 20)   // 1020\n\t  STR(10)       // \"10\"\n\t  T_CAT(10, 20) // 1020\n\t  T_STR(10)     // \"10\"\n\t  ```\n\t- Test for with `#if __has_known_extension(\"traditional-macro\")`\n- Alternative parenthesis for macro definitions (tpp-specific extension):\n\t- Allow use of:\n\t\t- `#define foo(a,b) ...`\n\t\t- `#define foo[a,b] ...`\n\t\t- `#define foo{a,b} ...`\n\t\t- `#define foo\u003ca,b\u003e ...`\n\t- In other words: define macros that use tokens other than `(...)`-pairs for arguments\n\t- Test for with `#if __has_extension(\"alternative-macro-parenthesis\")`\n- Self-recursive macros (tpp-specific extension):\n\t- STDC specifies that macros that try to expand to themselves should not be allowed to do so when this happens\n\t- TPP has an optional extension to relax this rule for function-like macros defined while this extension is active\n\t- Such function-like macros are allowed to expand to themselves, so-long as arguments passed during expansion differ from all other invocations of the same macro along the macro-expansion call-stack\n\t- Test for with `#if __has_known_extension(\"macro-recursion\")`\n\t- Enable/disable with `#pragma extension(\"-fmacro-recursion\")` and `#pragma extension(\"-fno-macro-recursion\")`\n- Whitespace-sensitivity (tpp-specific extension):\n\t- STDC specifies that whitespace should be stripped around macro bodies. - However, some programming languages are whitespace sensitive, meaning that the removal of whitespace in such cincumstances may alter program behavior.\n\t- For this purpose, TPP has an option to keep whitespace if necessary.\n\t- Test for with `#if __has_extension(\"macro-argument-whitespace\")`\n- Allow select operations on string literals in preprocessor expressions (tpp-specific extension):\n\t- `#if \"FOO\" != \"BAR\"`\n\t- `#if \"FOO\"[0] == 'F'`\n\t- `#if \"FOO\"[1:] == \"OO\"`\n\t- `#if #\"FOO\" == 3`\n\t- Test for with `#if __has_extension(\"strings-in-expressions\")`\n- Allow for GCC-style `__builtin_*` function calls in preprocessor expressions (tpp-specific extension):\n\t- `#if __builtin_ffs(0x100) == 9`\n\t- Test for with `#if __has_extension(\"builtins-in-expressions\")`\n- Allow for `#@` as a replacement for `#` to convert macro arguments into character literals, rather than strings:\n\t- ```c\n\t  #define STR(x) #x\n\t  #define CHR(x) #@x\n\t  STR(f) // \"f\"\n\t  CHR(f) // 'f'\n\t  ```\n\t- Test for with `#if __has_extension(\"charize-macro-argument\")`\n- Prevent in-place expansion of macro arguments during macro-function-expansion (tpp-specific extension) (s.a. `/test/pound_xclaim.h`)\n\t- Test for with `#if __has_extension(\"dont-expand-macro-argument\")`\n- Logical Xor operators (`A ^^ B`; same as `!!(A) ^ !!(B)`) (tpp-specific extension)\n- Precise date/time macros (tpp-specific extension):\n\t- `__DATE_DAY__`, `__DATE_WDAY__`, `__DATE_YDAY__`, `__DATE_MONTH__`, `__DATE_YEAR__`\n\t- `__TIME_SEC__`, `__TIME_MIN__`, `__TIME_HOUR__`\n\t- Test for with `#ifdef \u003cmacro-name\u003e`\n- Evaluate preprocessor expressions, and expand to their decimal/string representation (tpp-specific extension):\n\t- For example: `__TPP_EVAL(10 + 20)` would expand to exactly 1 token `30`\n\t- Highly useful because you can do stuff like:\n\n\t  ```c\n\t  #define CAT2(a, b) a##b\n\t  #define CAT(a, b) CAT2(a, b)\n\t  #define ISLESS_0 more than\n\t  #define ISLESS_1 less than\n\t  #define SELECT(n) CAT(ISLESS_, __TPP_EVAL(n \u003c 10))\n\t  SELECT(7)  // Expands to `less than'\n\t  SELECT(10) // Expands to `more than'\n\t  ```\n\t- Can also be combined with `\"strings-in-expressions\"` to operate on strings:\n\n\t  ```c\n\t  __TPP_EVAL(\"foobar\"[3:]) // Expands to \"bar\"\n\t  ```\n\t- Test for with `#ifdef __TPP_EVAL`\n- Generate unique IDs for a given keyword/name (that remain unchanged when re-compiling the same file without altering its contents) (tpp-specific extension):\n\t- For example, `__TPP_UNIQUE(foo)` expands to a unique decimal integer that will remain the same for `foo` within the entire input (though not necessarily within other files that may be preprocessed seperately)\n\t- Test for with `#ifdef __TPP_UNIQUE`\n- Load the contents of a #include-file into a string (tpp-specific extension):\n\t- For example, `__TPP_LOAD_FILE(\"file.c\")` will expand to the the contents of `file.c`, contained in, and escaped as a `\"string\"`\n\t- Test for with `#ifdef __TPP_LOAD_FILE`\n- An arbitrary number of `__COUNTER__`-like counters (tpp-specific extension):\n\t- For example, each time `__TPP_COUNTER(foo)` is encountered, it will expand to 1 greater than its previous expansion (starting at `0`)\n\t- Similar behavior is also possible with `__COUNTER__`, however using `__TPP_COUNTER`, you can have an arbitrary number of counters operating independent of each other.\n\t- Test for with `#ifdef __TPP_COUNTER`\n- Random number generation (tpp-specific extension):\n\t- Generate a random number in `[0,hi)` with `__TPP_RANDOM(hi)`\n\t- Generate a random number in `[lo,hi)` with `__TPP_RANDOM(lo, hi)`\n\t- Test for with `#ifdef __TPP_RANDOM`\n- Re-interprete the contents of a string token as input text (tpp-specific extension):\n\t- ```c\n\t  #define foo 42\n\t  __TPP_STR_DECOMPILE(\"foo bar\") // Expands to `42 bar'\n\t  ```\n\t- Test for with `#ifdef __TPP_STR_DECOMPILE`\n- Pack together a string token from the ASCII codes of its individual characters (tpp-specific extension):\n\t- For example, `__TPP_STR_PACK(0x48, 0x65, 0x6c, 0x6c, 0x6f)` expands to `\"Hello\"`\n\t- Test for with `#ifdef __TPP_STR_PACK`\n- More extensions exist, but I feel those are not important enough to document here\n\t- Most notably, supported `#pragma` directives, such as `#pragma once`, `#pragma push_macro()`, etc...\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrieferatwork%2Ftpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrieferatwork%2Ftpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrieferatwork%2Ftpp/lists"}