14 releases

0.6.2 Sep 22, 2021
0.6.1 Sep 22, 2021
0.6.0 Aug 31, 2020
0.5.0 Dec 17, 2019
0.1.0 Nov 1, 2019

#2064 in Rust patterns

Download history 117/week @ 2024-05-11 178/week @ 2024-05-18 127/week @ 2024-05-25 78/week @ 2024-06-01 103/week @ 2024-06-08 149/week @ 2024-06-15 89/week @ 2024-06-22 49/week @ 2024-06-29 64/week @ 2024-07-06 46/week @ 2024-07-13 53/week @ 2024-07-20 77/week @ 2024-07-27 33/week @ 2024-08-03 43/week @ 2024-08-10 37/week @ 2024-08-17 33/week @ 2024-08-24

156 downloads per month
Used in 2 crates (via web-rwkv)

MIT/Apache

28KB
605 lines

gpp

This is a simple generic C-like preprocessor for Rust. See the documentation (cargo doc --open) for details.


lib.rs:

gpp is a Generic PreProcessor written in Rust.

It supports:

  • Simple macros, no function macros
  • #include
  • #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 <>, so #include file.txt is the correct syntax. It does not support #if or #elif, and recursive macros will cause the library to get stuck.

About

The hash in any command may be succeeded by optional whitespace, so for example is valid, but # undef Macro is not.

#define and #undef

#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.

#include

Includes, unlike C, do not require quotes or angle brackets, so this: #include "file.txt" or this: #include <file.txt> will not work; you must write #include file.txt.

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 #include dir/file.txt and in dir/file.txt it says #include other_file.txt, that would refer to other_file.txt, not dir/other_file.txt.

Ifs

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.

#exec, #in and #endin

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 output 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 when 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

Would output 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
# include styles.scss
#endin
</style>

This compiles your scss file into css using Sassc and includes in the HTML every time you generate your webpage with gpp.

Literal hashes

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 some is not a command.

Examples

// 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
");

Dependencies

~125KB