The BPP Basic Pre-Processor
BPP is a pre-processor designed to be used on files containing BASIC
language source code.
The source files read by BPP contain a mixture of BASIC language source
code and of BPP-specific commands. The BPP commands allow for conditional
processing of the source code, for definition of macros, and for other
specific actions.
BPP reads the source file, processes it according to the conditional
processing commands, performs macro expansions, and generates a processed
output file. This output file will typically be compiled by a BASIC
compiler.
- Command-line Syntax
The BPP command-line syntax is the following:
bpp [options] <input_file> <output_file>
Where <input_file> is the name of the input file and <output_file> is the
name of the output file.
The following options are accepted by BPP:
- -D<symbol>[=<value>]
This defines a symbol (macro) with name <symbol>
and optionally assigns it the value <value>.
Multiple symbols can be defined on the command line by the use
of multiple -D options.
E.g. The following are all valid bpp command-lines:
- bpp source.in source.out
- bpp -DDEBUG=1 -DLANG=QB test.src test.bas
- bpp -DDEBUG test.src test.bas
- BPP Commands
BPP commands have a pound sign (#) as the first non-blank character of a line, and are immediately followed by a command keyword. Note that BPP commands can be indented within a line, but they MUST be the first non-blank text of the line.
The following is a list of all the BPP commands.
#INCLUDE | Process specified file. |
#INCLUDESRC | Process specified file outputting its source code. |
#DEFINE | Define a macro. |
#UNDEF | Undefine a previously defined macro |
#IF | Process next block if symbol is non-zero |
#IFDEF | Process next block if symbol is defined |
#IFNDEF | Process next block if symbol is not defined |
#ELIF | Process next block if symbol is non-zero and previous blocks have not been processed. |
#ELIFDEF | Process next block if symbol is defined and previous blocks have not been processed. |
#ELSE | Process next block if previous blocks have not been processed. |
#ENDIF | Terminate a conditional block |
#OUT | Output text |
#ERROR | Display error message and terminate |
- Including Files
#INCLUDE
The #INCLUDE command opens and processes a file, then continues processing the
current file. A file opened through the #INCLUDE command can contain itself
other #INCLUDE commands. There is no limit to the number of files nested in
this fashion.
The syntax of the #INCLUDE command is:
#INCLUDE "<file>"
Where <file> is the name of the file to be processed. Note that the name of
the file must be enclosed with double quotes.
E.g.
#INCLUDE "source2.bas"
IMPORTANT!
Note that BPP only outputs code from the top-level file it processes,
i.e. the file specified in the BPP command line. All files included via
the #INCLUDE command are only processed for BPP commands, any other code
being ignored. If you want the code from the file being included to be
written to the output file, then use the #INCLUDESRC command instead of
#INCLUDE
The purpose of the above behavior is to allow BPP to process BASIC include
files without writing the output of those include files into the output of
the source file that includes those files.
This way macros related to a specific BASIC include file (say xxx.bi) can be
processed when processing the BASIC source file (say xxx.bi) that includes
it, without having the actual BASIC code in xxx.bi being written to the
output file.
Example:
The file "source.bas" contains the following:
#INCLUDE "source.bi"
SUB FOO
x = BAR
END SUB
And the file "source.bi" contains the following:
DECLARE SUB FOO()
#DEFINE BAR 35
Then bpp source.bas source.out will generate the following file
"source.out":
SUB FOO
x = 35
END SUB
Note that the text "DECLARE SUB FOO()" in file "source.bi"
was not written to "source.out".
How to handle BASIC commands
Say that you have a BASIC source file "xxx.bas" that includes a
file "xxx.bi". Your QBasic code looks something like this:
': 'xxx.bi'
... Rest of source code...
But now you have defined macros in the file "xxx.bi", and you want
any file which includes "xxx.bi" to be able to use those macros.
You can do one of two things:
1.- Substitute every line containing ': 'xxx.bi' for the following
two lines:
#INCLUDE "xxx.bi"
': 'xxx.bi'
2.- Define a macro that will expand to the two lines you need:
#DEFINE INCLUDEFILE(name) |
#INCLUDE "name"_ |
': 'name' |
And now substitute every line containing ': 'xxx.bi' for the
following line:
INCLUDEFILE(xxx.bi)
Which will expand to the following two lines:
#INCLUDE "xxx.bi"
: 'xxx.bi'
The first one will instruct BPP to include xxx.bi, while the second
one will be output to the output file.
#INCLUDESRC
The #INCLUDESRC command is similar to the #INCLUDE command, but while
#INCLUDE only processes BPP commands while ignoring any other source code
in the included file, the #INCLUDESRC command outputs the source code
present in the included file, just as it does with the main file being
processed by BPP.
The syntax of the #INCLUDESRC command is:
#INCLUDE "<file>"
Where<file> is the name of the file to be processed. Note that
the name of the file must be enclosed with double quotes.
- Defining Macros
Macros can be defined in the bpp command line using the -D option, or in
the source file using the #DEFINE command. The syntax of the #DEFINE command
is:
#DEFINE <name>[(Arguments)] [Value]
Where <name> is the name of the macro being defined, and [Value] is the
optional value that the macro will expand to. The macro can optionally
contain parameters.
E.g.
#DEFINE FOO | Defines FOO with no value |
#DEFINE PI 3.1416 | Defines PI with value 3.1416 |
#DEFINE SAY(x) PRINT x | Macro with argument |
#DEFINE ASSERT(x) _
IF (NOT (x)) THEN_
CALL ProgramError( "__FILE__", __LINE__ )_
END IF |
Macro expands to multiple lines |
In the first example, FOO is defined but no value is assigned to it. If the
source code contains FOO, it is substituted by nothing. FOO can be tested
using #IFDEF, #IFNDEF or #ELIFDEF.
In the second example, PI is assigned a value of "3.1416:". Any appearance
of PI in the source code will be subsituted by 3.1415. For example, the
following line:
Area = PI * Diameter
will generate the following output:
Area = 3.1416 * Diameter
In the third example, a macro SAY is defined which requires one argument.
Text in the source such as:
SAY("Hello world")
will generate the following output:
PRINT "Hello world"
The fourth example shows a macro that expands to multiple lines of output.
An underscore at the end of a line indicates that the macro value continues
in the following line. So the following source text:
ASSERT(y=2)
will generate the following output:
IF (NOT (y=2)) THEN
CALL ProgramError( "source.bas", 132 )
END IF
Note that when expanding a macro, the expanded value is itself processed so
one macro can use other macros. e.g.
#DEFINE SAY(x) PRINT x
#DEFINE GREETING(name) SAY("Hello "); : SAY("name")
GREETING(Joe)
The above will result in the following output:
PRINT "Hello "; : PRINT "Joe"
Undefining macros
Macros can be undefined via the #UNDEF command:
#UNDEF FOO
Undefines the macro FOO. The macro must have been previously defined.
Once undefined, FOO can be re-defined.
- Predefined Macros
The following macros are predefined in BPP. Their names are reserved and
they cannot be undefined.
__FILE__ | Name of output file |
__LINE__ | Line number in the output file |
__DATE__ | Current date |
__TIME__ | Current time |
__VERSION__ | BPP version |
- Conditional processing
BPP supports conditional processing. The conditional compilation directives
in BPP work like the similar directives on C or C++:
#IF symbol-1
<section-1>
#ELIF symbol-2
...
#ELIF symbol-n
<section-n>
#ELSE
<section-n+1>
#ENDIF
#IF and #ELIF require a symbol that expands to a numeric expression.
If the numeric expression is non-zero, the corresponding section is
processed. All other sections are ignored. The section after #ELSE is
processed if no other section within the current conditional block has
been processed.
#IFDEF symbol-1
<section-1>
#ELIFDEF symbol-2
...
#ELIFDEF symbol-n
<section-n>
#ELSE
<section-n+1>
#ENDIF
In #IFDEF and #ELIFDEF, If the specified symbol is defined, the
corresponding section is processed. All other sections are ignored.
The section after #ELSE is processed if no other section within the
current conditional block has been processed. #IFNDEF is similar to
#IFDEF, but the section is processed if the specified symbol is NOT
defined.
Note that only one #ELSE is allowed in a conditional block.
Note that #ELIFDEF and #ELIF can coexist with a conditional block:
#IFNDEF symbol-1
<section-1>
#ELIF symbol-2
...
#ELSE
<section-n>
#ENDIF
Conditional blocks can be nested:
#IFNDEF symbol-1
<section-1>
#ELIF symbol-2
#IF symbol-3
<section-3>
#ELSE
<section-4>
#ENDIF
#ELSE
<section-5>
#ENDIF
- Other commands
BPP supports the following commands:
#OUT
#OUT processes the rest of the line and outputs it to the output file.
Note that this command is executed even in files included via the
#INCLUDE command.
E.g.
#OUT ' The date today is __DATE__
will output something like:
' The date today is 1/7/2002
#ERROR
#Error processes the rest of the line, outputs it to the standard error
file (usually the screen), and stops execution of BPP.
E.g.
#ERROR Attention! An error was encountered.
will terminate BPP after displaying the following message:
Attention! An error was encountered.