TPP 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.
TPP is designed to be used in 1 of 2 ways:
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.
For this purpose, TPP is meant to be included statically as part of another project's source tree.
/samples
As a standalone Preprocessor (similar to GNU's cpp
utility).
For this purpose, you may build src/frontend.c
to produce the commandline utility
TPP operates without any intermediate buffers, in that it is capable of preprocessing/tokenizing text in real-time from stream-like input sources:
cat file.c | tpp
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.
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.
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, an interactive C preprocessor most definitely isn't obviously is, too. - Because that's what TPP is.
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.
All extensions can be hard enabled/disabled when building tpp, or soft-enabled/disabled via #pragma extension("-fname")
/ #pragma extension("-fno-name")
directives.
Supported 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
.
??*
)<%
is {
, etc...)__VA_ARGS__
(#define printf(...) fprintf(stderr, __VA_ARGS__)
)#define printf(format...) fprintf(stderr, format)
)#define printf(format, ...) fprintf(stderr, format, ##__VA_ARGS__)
)#if foo ?: bar
; same as #if foo ? foo : bar
)__VA_OPT__
(#define printf(format, ...) fprintf(stderr, format __VA_OPT__(,) __VA_ARGS__)
)'\e'
escape codes in strings and characters (for ASCII ESC
, iow: '\x1b'
)0b00101010
)__pragma(foo)
built-in function-like directive/macro_Pragma("foo")
built-in function-like directive/macro#warning My Message Here
directives#include_next <header.h>
directives#import <header.h>
directives#ident
and #sccs
directives (true origin unknown; accepted by some ancient compilers for placing comments into .o
-files)__FILE__
, __LINE__
, __TIME__
and __DATE__
__BASE_FILE__
__INCLUDE_LEVEL__
and __INCLUDE_DEPTH__
__COUNTER__
__TIMESTAMP__
__COLUMN__
(tpp-specific; like __LINE__
, but the current column-position)#if
-expressions#define
, #undef
, #if
, #ifdef
, #ifndef
, #elif
, #else
, #endif
, #include
, #pragma
, #error
)__has_include()
__has_include_next()
__is_identifier()
, __is_deprecated()
, __is_poisoned()
__has_attribute()
, __has_builtin()
, ...__is_builtin_identifier()
(tpp-specific)__has_extension()
, __has_known_extension()
(tpp-specific)__has_warning()
, __has_known_warning()
(tpp-specific)#!
-directives as comments (i.e. #!/bin/tpp
is treated as a comment)$
as a character, which may thus appear in an identifier<assert.h>
..., but this):
#assert cpu(mycpu)
#unassert cpu(yourcpu)
#if #cpu(mycpu)
#if 'abc' == 6513249
)__VA_COMMA__
(tpp-specific extension)
#define printf(format, ...) fprintf(stderr, format __VA_COMMA__ __VA_ARGS__)
,
when __VA_ARGS__
are non-empty and /**/
when empty.__VA_OPT__(,)
__VA_NARGS__
(tpp-specific extension)
__VA_ARGS__
Useful for overloading macros by argument count
#define min_1(a) a
#define min_2(a, b) ((a) < (b) ? (a) : (b))
#define min_3(a, b, c) min_2(min_2(a, b), c)
#define min(...) min_##__VA_NARGS__(__VA_ARGS__)
#pragma extension
)
#define CAT(a,b) a ## b
#define STR(x) #x
#pragma extension(push, "-ftraditional-macro")
#define T_CAT(a, b) a/**/b
#define T_STR(x) "x"
#pragma extension(pop)
CAT(10, 20) // 1020
STR(10) // "10"
T_CAT(10, 20) // 1020
T_STR(10) // "10"
#if __has_known_extension("traditional-macro")
#define foo(a,b) ...
#define foo[a,b] ...
#define foo{a,b} ...
#define foo<a,b> ...
(...)
-pairs for arguments#if __has_extension("alternative-macro-parenthesis")
#if __has_known_extension("macro-recursion")
#pragma extension("-fmacro-recursion")
and #pragma extension("-fno-macro-recursion")
#if __has_extension("macro-argument-whitespace")
#if "FOO" != "BAR"
#if "FOO"[0] == 'F'
#if "FOO"[1:] == "OO"
#if #"FOO" == 3
#if __has_extension("strings-in-expressions")
__builtin_*
function calls in preprocessor expressions (tpp-specific extension):
#if __builtin_ffs(0x100) == 9
#if __has_extension("builtins-in-expressions")
#@
as a replacement for #
to convert macro arguments into character literals, rather than strings:
#define STR(x) #x
#define CHR(x) #@x
STR(f) // "f"
CHR(f) // 'f'
#if __has_extension("charize-macro-argument")
/test/pound_xclaim.h
)
#if __has_extension("dont-expand-macro-argument")
A ^^ B
; same as !!(A) ^ !!(B)
) (tpp-specific extension)__DATE_DAY__
, __DATE_WDAY__
, __DATE_YDAY__
, __DATE_MONTH__
, __DATE_YEAR__
__TIME_SEC__
, __TIME_MIN__
, __TIME_HOUR__
#ifdef <macro-name>
Evaluate preprocessor expressions, and expand to their decimal/string representation (tpp-specific extension):
__TPP_EVAL(10 + 20)
would expand to exactly 1 token 30
Highly useful because you can do stuff like:
#define CAT2(a, b) a##b
#define CAT(a, b) CAT2(a, b)
#define ISLESS_0 more than
#define ISLESS_1 less than
#define SELECT(n) CAT(ISLESS_, __TPP_EVAL(n < 10))
SELECT(7) // Expands to `less than'
SELECT(10) // Expands to `more than'
Can also be combined with "strings-in-expressions"
to operate on strings:
__TPP_EVAL("foobar"[3:]) // Expands to "bar"
#ifdef __TPP_EVAL
__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)#ifdef __TPP_UNIQUE
__TPP_LOAD_FILE("file.c")
will expand to the the contents of file.c
, contained in, and escaped as a "string"
#ifdef __TPP_LOAD_FILE
__COUNTER__
-like counters (tpp-specific extension):
__TPP_COUNTER(foo)
is encountered, it will expand to 1 greater than its previous expansion (starting at 0
)__COUNTER__
, however using __TPP_COUNTER
, you can have an arbitrary number of counters operating independent of each other.#ifdef __TPP_COUNTER
[0,hi)
with __TPP_RANDOM(hi)
[lo,hi)
with __TPP_RANDOM(lo, hi)
#ifdef __TPP_RANDOM
#define foo 42
__TPP_STR_DECOMPILE("foo bar") // Expands to `42 bar'
#ifdef __TPP_STR_DECOMPILE
__TPP_STR_PACK(0x48, 0x65, 0x6c, 0x6c, 0x6f)
expands to "Hello"
#ifdef __TPP_STR_PACK
#pragma
directives, such as #pragma once
, #pragma push_macro()
, etc...