Macros & Preprocessor

V22 Feature: The macro expansion engine provides full support for MACRO, MACROFUNCTION, MACROTEMP, #DEFINE, and conditional compilation.

MACRO (Statement Macros)

Syntax: MACRO name(args) ... END MACRO

Description

Statement macros allow you to define multi-line blocks of code that are expanded inline at the point of invocation. They are evaluated during preprocessing and substituted directly into the source stream before parsing.

Example

MACRO DEBUG_PRINT(msg)
    IF %DEF(DEBUG) THEN
        PRINT "[DEBUG]: "; msg
    END IF
END MACRO

FUNCTION PBMAIN() AS LONG
    DEBUG_PRINT "Program started"
    FUNCTION = 0
END FUNCTION

Preprocessor Expansion

The compiler replaces the macro call with the body, substituting arguments:

' Expanded source sent to parser:
IF %DEF(DEBUG) THEN
    PRINT "[DEBUG]: "; "Program started"
END IF

MACROFUNCTION (Function Macros)

Syntax: MACROFUNCTION name(args) ... END MACRO = expr

Description

Function macros behave like inline functions. They can contain local variable declarations and statements, and must conclude with END MACRO = expression to specify the return value.

Example

MACROFUNCTION CLAMP(val, min, max)
    MACROTEMP t
    LOCAL t AS LONG
    t = val
    IF t < min THEN t = min
    IF t > max THEN t = max
END MACRO = t

FUNCTION PBMAIN() AS LONG
    LOCAL x AS LONG
    x = CLAMP(150, 0, 100)
    PRINT "Clamped value: "; x  ' Outputs: 100
    FUNCTION = 0
END FUNCTION

MACROTEMP (Unique Temporary Variables)

Syntax: MACROTEMP varname

Description

When writing multi-line macros, declaring a variable inside the macro can cause name collisions if the macro is used inside a loop or another macro. MACROTEMP tells the preprocessor to generate a unique, compiler-managed variable name for each expansion, preventing Duplicate declaration errors.

Expansion Behavior

' Source:
MACROFUNCTION Square(x)
    MACROTEMP res
    LOCAL res AS LONG
    res = x * x
END MACRO = res

' Expanded (compiler generates unique names):
' Expansion 1:
LOCAL __pb_mtemp_1_res AS LONG
__pb_mtemp_1_res = 5 * 5
' Expansion 2:
LOCAL __pb_mtemp_2_res AS LONG
__pb_mtemp_2_res = 10 * 10

#DEFINE & #UNDEF (Object & Function-like Macros)

Syntax: #DEFINE name value | #UNDEF name

Description

#DEFINE creates simple text-replacement macros. If parentheses follow the name immediately, it becomes a function-like macro. #UNDEF removes a symbol from the preprocessor context.

Example

#DEFINE PI 3.14159265358979
#DEFINE AREA(r) (PI * r * r)
#DEFINE DEBUG_MODE 1

LOCAL radius AS DOUBLE
radius = 5.0
PRINT "Area: "; AREA(radius)

#UNDEF DEBUG_MODE
' DEBUG_MODE is no longer defined below

Conditional Compilation

Syntax: #IF condition ... #ELSEIF ... #ELSE ... #ENDIF

Description

Conditionally include or exclude code blocks. Conditions support %DEF(name), numeric constants, and logical operators NOT, AND, OR. The dollar-form ($IF, $ENDIF) is also supported.

Example

#IF %DEF(PBXA64) AND %DEF(X64)
    PRINT "Running on 64-bit PBXA64"
#ELSEIF %DEF(DEBUG)
    PRINT "Debug build active"
#ELSE
    PRINT "Standard release build"
#ENDIF

Predefined Symbols

The following symbols are automatically defined by the preprocessor:

SymbolMeaning
PBXA64Always defined for PBXA64 builds
PBXA64_64Defined when compiling for x64
WIN64Defined for Windows 64-bit targets
X64Defined for x86-64 architecture

Macro Expansion Rules