Skip to content

Conditional compilation

To support a wider set of scenarios where WCPL code is also compiled by a regular C compiler (which is not that hard due to WCPL being basically a subset of C), WCPL supports a limited set of conditional compilation constructs. The goal was to allow the insertion of fragments targeting various C compilers and platforms to be ignored by WCPL compiler on line-by-line basis.

These constructs look similar to what C programmers are used to, but are not based on defined macros and constant expressions. The conditions being tested for inclusion or exclusion of certain blocks of text are just lexical variations on the always true and always false themes.

The WCPL compiler treats the following starting constructs as always true:

#ifdef __WCPL__
#if defined(__WCPL__)
#if 1

This is the complementary list of always false starting constructs:

#ifndef __WCPL__
#if !defined(__WCPL__)
#if 0

WCPL conditional compilation block should start with one of the above constructs at the top level of a module or a header file, and end with the matching ending construct:

#endif

The above constructs can contain any amount of whitespace between tokens, but should each be limited to a single line. They cannot appear inside a top-level form, only between the forms.

There are additional WCPL constructs which may appear between the starting and ending lines. The text following the always true starting line can contain a switch to inactive line that tells WCPL to ignore the rest of the block until the corresponding #endif line. This switch-to-inactive line can have one of the following two forms:

#elif // any tokens can follow #elif on the same line
#else

The text between the always true starting line and the switch to inactive line, or the ending line if there is no switch, is called active block. The text between the switch to inactive line and the ending line is called inactive block. The compiler processes the top-level forms from the active block and skips all the lines of the inactive block, just looking for its ending line.

The overall layout of the conditional compilation block that begins with the always false starting line is more complex. The text between its start and the corresponding ending line can contain multiple potential switch lines, but WCPL is looking for the always true switching line that may take one of the following forms:

#elif defined(__WCPL__)
#elif 1
#else

Any one of these lines will act as switch to active line that would end current inactive block. The potential switch lines that do not switch may take one of the following forms:

#elif !defined(__WCPL__)
#elif 0

If a switch to active line is encountered, WCPL considers the text that follows to be an active block and continues in the manner described above for the conditional compilation block that opens with always true starting line.

Effectively, the contents of the conditional compilation block consist of at most one active block, optionally preceded or followed by inactive blocks. It is important to note that WCPL’s active block can contain not only regular WCPL top-level forms but also other properly nested WCPL conditional compilation blocks, recursively. The active parts that are taken for processing should be sequences of 0 or more top-level forms; conditional compilation constructs (lines) cannot interrupt top-level forms by appearing inside them.

Since inactive blocks are just skipped line-by-line, their contents does not have to be restricted as much. In fact, WCPL allows any sequence of C99 tokens, including arbitrary but properly nested C99 conditional compilation constructs within its inactive blocks. For inactive blocks, there is no limitation on where nested conditional compilation blocks appear with respect to other language constructs, and what expressions are used as conditions (they are skipped by WCPL anyway).

These sequences usually contain C code targeting other compilers and platforms, e.g.:

#if defined(__WCPL__)
// WCPL-specific code
#elif defined(__GNUC__) && defined(__linux)
// Linux-specific code (GCC/Clang)
#elif defined(__GNUC__) && defined(__APPLE__)
// Mac-specific code (GCC/Clang)
#elif defined(_MSC_VER)
// MSVC-specific code
#elif defined(__STDC__)
// code targeting other C compilers
#else
#error "put in your definitions..."
#endif

While marginally useful in WCPL-only code, these constructs support some degree of mix-and-match between code targeting different platforms, which may be useful for code that needs to be compiled as both a WCPL program and a C program.