Preprocessor

The preprocessor is used to perform certain translation operations on the source program, such as replacing text, conditionally compiling areas of code, or including macros, definitions, or access to a pre-compiled library within a program.

Text Replacement Macro (#define/#undef)

The #define preprocessor directive is used to define a macro, which associates some value with an identifier. The syntax variations for a #define directive are as follows.

#define <Identifier>
#define <Identifier> <Value>
#define <Identifier1>(<Identifer2>, ...)

#define <Identifier1>(<Identifer2>, ...) <Value>

The <Identifier*> arguments must be valid identifiers (see Valid Identifier Format). Macros have two general purposes:

  • They allow text to be substituted in a program before the program is compiled.

  • They allow an identifier to be defined, which can be used to perform conditional compilation based on the existence of that identifier.

// Define a macro without a token string
#define DEBUG_ENABLE
 

// Define a macro with some replacement text

#define MY_ADD(x, y) (x + y)
 

// Define some macros with replacement strings

#define MY_STRING "Hello"

#define MY_VALUE  2
 

// The following is replaced with var $myStr as string = "Hello"

var
$myStr as string = MY_STRING 
#ifdef DEBUG_ENABLE
   // This code compiles because DEBUG_ENABLE has been #defined
   Enable([X, Y])
#endif
 

// The following is replaced with $rglobal[0] = (1 + 2)

$rglobal
[0] = MY_ADD(1, MY_VALUE)

The #define preprocessor directive cannot be used to replace text within string literals.

// Define a macro with a token string
#define MY_VALUE 3

// Text replacement occurs and $myInt is assigned a value of 3
var
$myInt as integer = MY_VALUE
// No text replacement occurs, so $myStr is assigned "MY_VALUE"
var
$myStr as string = "MY_VALUE"

The #undef preprocessor directive can be used to undo a definition that was previously defined with the #define preprocessor directive. The syntax of an #undef preprocessor directive is as follows.

#undef <Identifier>

Similar to the #define preprocessor directive, the <Identifier> argument must be a valid identifier (see Valid Identifier Format).

#define DEBUG_MODE
#undef DEBUG_MODE 


#ifdef DEBUG_MODE
   // This code is not compiled because DEBUG_MODE is no longer #defined
   Enable(X)
#endif

The backslash character (\) can be used to allow a #define preprocessor directive macro to span multiple lines.

#define M300 \
Enable
(X) \
Home
(X) \
AnalogOutputSet
(X, 0, 0.0

// The following will be replaced with all 3 lines specified in the

// above #define macro

M300

IMPORTANT: If a macro refers to its own <Identifier> in the definition of its value, then the macro will not be replaced recursively when the preprocessor does text replacement. This applies only to macros that do not take arguments. Macros that take arguments will be recursively replaced. This can cause the compiler to not respond.

If the preprocessor detects the <Identifier> of a macro while it is doing text replacement for that macro, the inner macro will not be replaced. The example that follows shows how a macro will not be replaced recursively.

#define EXAMPLE_A (1 + EXAMPLE_B)
#define EXAMPLE_B (EXAMPLE_A - 1)

// EXAMPLE_B below will be replaced with the text that follows:
// ((1 + EXAMPLE_B) - 1)
// This is because EXAMPLE_B will not be replaced a second time
// while it is already in the process of replacement.
var $myInt as integer = EXAMPLE_B

You can refer to a #define preprocessor directive macro <Identifier> if it is used as an <Identifier2> or other <Identifier*> argument for a macro that takes arguments. This is only for when the macro is used and is not for its definition.

For macros that take arguments, the AeroScript compiler recursively replaces a macro if it refers to its own <Identifier1> in its definition. The AeroScript compiler does this so that it can use a macro as an argument to the same macro. But, you should not define a macro that refers to itself in its own definition because the compiler can stop responding while doing text replacement. The example that follows show you how to use a macro <Identifier> as an argument to the same macro.

#define MY_ADD(A, B) (A + B)

// This will be replaced with ((1 + 2) + 3).
$iglobal[0] = MY_ADD(MY_ADD(1, 2), 3)

// This will cause the compiler to become unresponsive.
#define MY_SUB(A, B) (B - MY_SUB(A, 1))
$iglobal[0] = MY_SUB(3, 2)

Source File Inclusion (#include)

The #include preprocessor directive is used to directly include the text from another source file into the program in the line directly following the directive. This can be used, for example, to include another program that contains #defines. Only preprocessor directives and comments can be specified in a file that you include with the #include preprocessor directive.

// defines.ascript
#define VAL1 123.4
#define VAL2 0xFF
// main.ascript
#include "defines.ascript"
$rglobal[0] = VAL1
$rglobal[2] = VAL2

Conditional Inclusion (#if/#ifdef/#ifndef)

Various preprocessor directives exist to perform conditional compilation of a source program:

  • #if
  • #ifdef
  • #ifndef

The #if directive is used to conditionally compile an area of code when a condition evaluates as true (non-zero). The syntax for the #if directive is as follows.

#if <Condition>
	<Code>
#endif

The <Condition> expression can contain any of the following:

  • Integer constants (e.g., 3 or 0xFF)

  • A subset of operators from the language

    • Arithmetic operators (e.g., +, -, *, /)

    • Bitwise operators (e.g., &, |, ~, <<, >>)

    • Comparison/relational operators (e.g., >, <, >=, <=)

    • Logical operators (e.g., ||, &&)

  • Macros created with the #define preprocessor directive

  • The defined preprocessor operator, which returns 1 if a macro has previously been defined with #define, or 0 if it has not been defined

#if (5 > 3)
   // This block will be included
#endif
#if 0
   // This block will not be included
#endif

The #elif and #else directives can be used within an #if block. These directives allow code to be conditionally included when the #if conditional is not true or non-zero. Refer to the following example.

#define DEBUG_LEVEL 2

#if !defined(DEBUG_LEVEL)
   // This block will not be included (DEBUG_LEVEL is defined)
#elif DEBUG_LEVEL == 1

   // This block will not be included (DEBUG_LEVEL is not 1)
#else
   // This block will be included (the other conditions were false)
#endif

The #ifdef preprocessor directive is used to conditionally compile an area of code when a macro has been previously defined with the #define directive. Similarly, the #ifndef directive is used to perform conditional compilation when a macro has not been previously defined. The syntax for these two directives is as follows.

#ifdef <Name>
   <Code>
#endif
#ifndef <Name>
   <Code>
#endif

Similar to the #if preprocessor directive, the #elif and #else preprocessor directives can be used within #ifdef and #ifndef blocks.

#define DEBUG_MODE
#define LOG_MODE   3


#ifndef DEBUG_MODE
   // This block will not be included (DEBUG_MODE is defined)
#elif (LOG_MODE > 0)

   // This block will be included (LOG_MODE is 3)
#else

   // This block will not be included (the previous block was included)
#endif

Next Topic: Data Types and Variables