Preprocessor
The preprocessor handles several compiler directives that begin with a period (.) and processes them before the actual compilation occurs. Let’s take a look at the tasks that can be performed using the preprocessor:
Text replacement with .DEFINE
Think of .DEFINE as creating a search-and-replace rule for your code. It comes in two forms:
Simple replacement:
.DEFINE TTCHN, 1
This tells the compiler “whenever you see TTCHN, replace it with 1.” It’s similar to creating a constant but happens during preprocessing. A practical use would be
.DEFINE MAX_USERS, 100
.DEFINE DATABASE_PATH, "data/users.dat"
Parameterized macros:
.DEFINE SUBTOTAL(desc, amount) writes(pchan, "Subtotal for ''desc': "+%string(amount))
This creates a more sophisticated replacement pattern that accepts parameters. It’s like creating a template that can be filled in with different values. When you write
SUBTOTAL(Apples, apples_total)
the preprocessor expands it to
writes(pchan, "Subtotal for Apples: "+%string(apples_total))
Limitations
Preprocessor expansion in DBL is limited to a single statement (i.e., a single line). This means that the following attempt to expand three statements is not possible with a single macro:
.DEFINE SUBTOTAL(desc, amount) open(pchan, O, "TT:") & writes(pchan, "Subtotal for ''desc': "+%string(amount)) & close(pchan)
Conditional compilation
DBL offers three main ways to conditionally include or exclude code during compilation:
.IF for expression-based decisions:
.IF user_count > 100
writes(1, "Large user base detected")
.ELSE
writes(1, "Small user base")
.ENDC
.IFDEF for checking definitions:
.DEFINE DEBUG_MODE
.IFDEF DEBUG_MODE
writes(1, "Debug: Entering main routine")
.ENDC
.IFNDEF for checking missing definitions:
.IFNDEF ERROR_HANDLER
.DEFINE ERROR_HANDLER
; Define default error handling
.ENDC
Including external code
The .INCLUDE directive helps organize code by allowing you to split it across multiple files:
.INCLUDE "database_config.dbl"
.INCLUDE "LIBRARY:common_routines"
Best practices:
- Use UPPERCASE for defined constants to distinguish them from regular variables.
- Define constants at the beginning of your code or in a separate include file.
- Use parameterized macros for repeated code patterns that need slight variations.
- Use conditional compilation to handle different environments (development, production) or feature flags.
- Structure your includes to avoid circular dependencies.
For example, a well-organized DBL program might look like this:
.DEFINE VERSION, "1.0.0"
.DEFINE MAX_CONNECTIONS, 50
.DEFINE LOG(message) writes(log_channel, "["+ %date() +"] "+ message)
.IFNDEF PRODUCTION
.DEFINE DEBUG_MODE
.ENDC
.INCLUDE "config.dbl"
main
record
connection_count, i4
proc
.IFDEF DEBUG_MODE
LOG("Application starting in debug mode")
.ENDC
if connection_count > MAX_CONNECTIONS
LOG("Connection limit exceeded")
.ENDC
end