gpp is a Generic PreProcessor written in Rust.
- Simple macros, no function macros
- #define and #undef
- #ifdef, #ifndef, #elifdef, #elifndef, #else and #endif
- #exec for running commands
- #in and #endin for giving input to commands
#includes work differently from C, as they do not require (and do not work with) quotes or <>,
#include file.txt is the correct syntax. It does not support #if or #elif, and recursive
macros will cause the library to get stuck.
The hash in any command may be succeeded by optional whitespace, so for example
# undef Macro
is valid, but
# undef Macro is not.
#define works similar to C:
#define [name] [value], and #undef too:
#undef [name]. Be
careful though, because unlike C macro expansion is recursive: if you
#define A A and then
use A, gpp will run forever.
If #define is not given a value, then it will default to an empty string.
Includes, unlike C, do not require quotes or angle brackets, so this:
#include "file.txt" or
#include <file.txt> will not work; you must write
Also, unlike C the directory does not change when you #include; otherwise, gpp would change its
current directory and wouldn’t be thread safe. This means that if you
dir/file.txt it says
#include other_file.txt, that would refer to
The #ifdef, #ifndef, #elifdef, #elifndef, #else and #endif commands work exactly as you expect. I did not add generic #if commands to gpp, as it would make it much more complex and require a lot of parsing, and most of the time these are all you need anyway.
The exec command executes the given command with
cmd /C for Windows and
sh -c for
everything else, and captures the command’s standard output. For example,
#exec echo Hi! will
Hi!. It does not capture the command’s standard error, and parsing stops if the
command exits with a nonzero status.
Due to the security risk enabling #exec causes, by default exec is disabled, however you can
enable it by changing the
allow_exec flag in your context. If the input tries to
exec is disabled, it will cause an error.
The in command is similar to exec, but all text until the endin command is passed into the program’s standard input. For example,
#in sed 's/tree/three/g' One, two, tree. #endin
One, two, three.. Note that you shouldn’t do this, just using
#define tree three would be much faster and less platform-dependent. You can also place more commands in
the in block, including other in blocks. For a useful example:
<style> #in sassc -s #endin </style>
This compiles your scss file into css using Sassc and includes in the HTML every time you generate your webpage with gpp.
In order to insert literal hash symbols at the start of the line, simply use two hashes.
##some text will convert into
#some text, while
#some text will throw an error as
is not a command.
// Create a context for preprocessing let mut context = gpp::Context::new(); // Add a macro to that context manually (context.macros is a HashMap) context.macros.insert("my_macro".to_owned(), "my_value".to_owned()); // Process some text using that assert_eq!(gpp::process_str("My macro is my_macro\n", &mut context).unwrap(), "My macro is my_value\n"); // Process some multi-line text, changing the context assert_eq!(gpp::process_str(" #define Line Row Line One Line Two The Third Line", &mut context).unwrap(), " Row One Row Two The Third Row "); // The context persists assert_eq!(context.macros.get("Line").unwrap(), "Row"); // Try some more advanced commands assert_eq!(gpp::process_str(" Line Four #ifdef Line #undef Line #endif Line Five", &mut context).unwrap(), " Row Four Line Five ");
Context of the current processing.
Error enum for parsing errors.