Project

General

Profile

Preprocessor

Summary

FWD includes a preprocessor that is largely byte for byte compatible with the preprocessor included in the Progress 4GL. On input, a file or stream is read and on output the preprocessor writes a modified stream based on a set of deterministic transformations. Some of these transformations are documented as Progress features and some of the transformations are undocumented features or behaviors that happen to occur due to Progress 4GL implementation choices. The FWD preprocessor attempts to duplicate the exact output results for the same set of given inputs, as the Progress 4GL would produce.

The preprocessor is executed once for each procedure file processed by the FWD conversion front end. It must complete its operation on each file before the FWD lexer and parser can process the resulting stream. The reason for this is that the input file is not necessarily valid Progress 4GL until after all preprocessing steps have successfully completed.

This chapter provides a reference to the features that are supported and which are not supported in FWD. Since the specific syntax and results are already somewhat documented in the Progress 4GL manuals, that content will not be duplicated here. Please reference those Progress 4GL manuals for details. Where there is some undocumented feature that is supported, this chapter may provide some details.

With limited exceptions, as a general rule the FWD preprocessor fully supports and is fully compatible with all features of the Progress 4GL preprocessor through OpenEdge v10.B. The exceptions to this are documented in this chapter in the sections on Unsupported Features and Other Limitations.

In regards to the details of the internals of the FWD preprocessor, please see the book entitled FWD Internals. The preprocessor code is contained inside the com.goldencode.p2j.preproc package and the main driver program is com.goldencode.p2j.preproc.Preprocessor. The actual work of the preprocessor is largely implemented in three ANTLR grammars (text.g, stat.g and braces.g) which all reside in that same package. Each grammar includes a lexer to tokenize the input stream and a parser to syntactically check and structure the input. The preprocessor switches between these tools as needed to process the stream, each grammar implementing a different function. A number of manually written Java support classes are also utilized to provide the full feature set. The combination implements a filter approach where the input stream (a text file) is preprocessed and written to an output stream (stdout or an output file).

Supported Features

; Special Character

The ; special character is used to create character combinations are replaced wherever they appear outside of string literals in the input stream. When used inside a string literal, the ; character has no effect. Outside of a string literal, the matched characters are converted into a specific replacement character. In Progress, these are also referred to as “alternative representations”.

The following list of supported conversions are implemented:

Input Output
;& @
;< [
;> ]
;* ^
;' '
;( {
;% |
;) }
;? ~

Custom alternative replacements are not supported, please see the Unsupported Features section below.

Comments

Comments in the form /* */ are matched on input and the entire contents are passed through to the output stream unchanged. Nested comments are supported.

As in Progress 4GL, comments can be placed inside preprocessor directives without causing a failure.

Escape Characters

The tilde ~ character is treated as a valid escape character on any platform. On UNIX/Linux systems the backslash \ character is supported as the equivalent of the tilde (as an additional escape character).

The combination of a valid escape character followed by one of the following characters will be translated into a single character result. This is a different feature than is described above with the ; Special Character.

Input Character Output Character
nnn nnn must be a valid octal number from 000 through 377. A 3 digit octal number in that range yields a value between 0 and 255. The binary representation of that byte will be output.
b 0x08 (backspace)
E 0x1B (escape)
f 0x0C (form feed)
n 0x0A (new line/line feed)
r 0x0D (carriage return)
t 0x09 (tab)

For example, a ~177 is the ASCII DEL character (0x7F) and ~t will emit an ASCII tab (0x09) character.

String Literals

Progress 4GL string literals (both single-quoted and double-quoted) are fully supported by the FWD preprocessor.

Any preprocessor name or argument name references are expanded/replaced by the value for that name or argument.

Newline characters (0x0D or 0x0A or the combination 0x0D0A) that are not prefixed with an escape character are removed from the string. This allows a string literal to be split across multiple lines and the preprocessor will “put it back together”.

Any character or octal sequence prefixed by a valid escape character is left unmodified in the output string.

In a single quote ' delimited string, double quote characters () and two sequential single quote characters ('') can be embedded as any other content (these constructs don't end the string). Likewise, in a double quote delimited string, single quote characters (') and two sequential double quote characters (””) can be embedded as any other content (these constructs don't end the string). Only a non-escaped and non-sequentially doubled matching quote character will end a string.

If there is an escaped null character (a null character that is prefixed by a valid escape character) in the string literal, it must be "space-ified" by converting it and all following characters into spaces (including the proper reduction of other following escape sequences into a single space character). One can encode a null escape sequence in a string literal in Progress 4GL but the null character will NEVER end up in the result! This is the way Progress works and FWD duplicates this behavior. The resulting length of the string (in terms of the number of characters that the string will include at runtime) will NOT change, although the length of the string representation (as a source code representation stored in a Java string) may be reduced as the null character's escape sequence (which would require 4 characters to represent as in ~000) and any following escaped sequences will be converted to a space which only consumes a single character position. This is how Progress handles such cases.

Examples:

Input Output
"This has a ~000 (null) char." "This has a "
'This has a ~000 (null) char.' 'This has a '
"~000" " "
'~000' ' '
"\000" " "
'\000' ' '

The above transformations are implemented on each string. Any other contents (including international characters or control characters that are not escaped) are left behind untouched. The resulting string (with transformations or unaltered content) will be passed through to the output.

Tab Expansion

Tabs characters (0x09) are expanded into spaces on output. The tab character itself is replaced with enough spaces to extend the current output column to the next tab stop (the next multiple of 8).

This is an undocumented behavior of Progress.

Line Continuations

If a valid escape character (tilde and sometimes the backslash) is the last character on a line, this is treated as a line continuation. That means that the escape character and the newline will be deleted on output so that the two lines are combined into one single line.

The use of the tilde for line continuations is partially documented in Progress. The use of the backslash for this same purpose is supported but that usage is an undocumented behavior of Progress. The fact that line continuations can be used anywhere in a file and not just in preprocessor name definitions, is also not documented.

{ } Argument Reference

Each file being processed by the preprocessor may have had arguments defined. See the section on { } Include File Reference for how arguments get defined and parsed. For procedure files that require arguments, the conversion process requires that custom hints be defined. See the chapter on Conversion Hints in the FWD Conversion Handbook for details.

All argument references are parsed to find the type and identifier of the argument being referenced. Once known, the value of that argument is looked up. If that identifier does reference a valid argument, then the argument reference is replaced with the value found (the text is substituted into the output in place of the reference). If no valid argument is found, then the reference is simply deleted on output.

Arguments can only be referenced within the scope of the specific file for which they were defined. If there are nested files, arguments references within those files cannot resolve to arguments that are defined in a containing file's scope.

Scoped preprocessor definitions are allowed to hide named arguments for the current file which have the same name as the scoped definition.

The following are all supported:

Reference Format Argument Type Result Notes
{&name} Named If an argument of the given name exists for the current file, the value of that argument is substituted. The name includes any text between the ampersand character and the next whitespace or right curly brace }. Anything after the name (including whitespace and any characters following the whitespace, except for the closing right curly brace) is considered trash and is ignored.
{&*} Named All named arguments will be emitted as a single string. The resulting string will be in the following format (in the case of 2 named arguments):

&name1=”value1” &name2=”value2”

The order will be the same order the arguments were defined. Any duplicate names are dropped (the first instance of a given name will defined the value for that name).
Any text after the asterisk and before the right curly brace is ignored.
{0} Positional The name of the file currently being processed will be substituted for the reference on output. Any non-numeric text after the digits and before the right curly brace is ignored.
{n} Positional This syntax assumes that n is a decimal number. If there is an argument at that 1-based index position, its value will be substituted on output in place of the argument reference. If there is no argument at that index position, the reference is simply removed from the output (no substitution).

Named arguments can also be referenced by their corresponding 1-based position in the arguments list. Although duplicate named arguments are not honored when there is a named argument reference (the value is dropped and is not associated with that name) , the value is still visible and accessible as its corresponding 1-based index position. This is not documented but does work the same way in Progress.
Any non-numeric text after the digits and before the right curly brace is ignored.
{*} Positional All positional arguments will be emitted as a single string. The resulting string will be in the following format (in the case of 3 arguments):

value1 value2 value3

The order will be the same order the arguments were defined. If named arguments were used, this will still yield their values in order (including the values for any duplicates).
 
{} Null The braces are removed from the input and nothing is substituted into the output. Any unknown name or number is essentially treated the same as the null or empty braces.

{ } Include File Reference

Include file references are expanded into the contents of the include file (if it exists) and are removed in the case that the include file does not exist or cannot be found in the PROPATH. When an include file reference is encountered, processing of the current file is paused and the referenced include file's contents are preprocessed instead. That preprocessing may also reference other include files, arguments or any other preprocessor expansion or replacement. This means that include files can be nested and include files can even recursively include themselves.

After all processing of an include file (and all nested or recursively referenced includes) is complete, a space character is appended to the resulting preprocessed contents and the entire result is inserted into the stream of output. At that point the processing of the original referencing file is continued at the character following the right curly brace that ends the include file reference.

The following basic forms are recognized:

Example Result
{filename} filename is included if it can be found. There are no arguments specified.
{filename argument1 argument2 argument3} filename is included if it can be found. Positional arguments are supplied and are available while the included file is being processed. These arguments can only be referenced by their 1-based index position (see Argument Reference). In this example {1} will reference argument1, {2} will reference argument2 and so on.
{filename &name1=value1 &name2=value2} filename is included if it can be found. Named arguments are supplied and are available while the included file is being processed. These arguments can be referenced by their names (name1 or name2 in this example) or by their 1-based index position (see Argument Reference).

While reading the stream of characters inside of braces some unique behavior is implemented. First, any escaped escape characters (any escape characters that are directly preceded by an escape character) will have the leading escape character deleted. This means that the following escape character will be inserted into the output but that character will not be escaped. Second, any escaped right curly brace is ignored (it will not be treated as a right brace) and the leading escape character will be deleted on output.

The filename is the first text after the left curly brace, optionally whitespace can be placed between the left curly brace and the filename. The filename can be specified in 2 forms: double quote delimited (which can include whitespace inside) and whitespace delimited.

Double quote delimited filenames end when another double quote character is found. If the other quote isn't found before the right curly brace, the parsing will fail with an error. Quote delimited filenames may be empty (no filename) like {“”}. This matches no filename and is removed on output just as if the include file could not be found.

In the whitespace delimited filename form there can be any text except for double quote and right brace as the first character. After that first character, any other text except for the right curly brace and whitespace can be included. The first whitespace or right curly brace after the text starts will end the filename parsing.

Any other form of text that cannot be taken as a syntactically correct argument reference will be treated as a filename in an include file reference. In particular, if there is whitespace between the left curly braces and any text (including ampersand or asterisk) that is not a valid argument reference. For example, { *} and { &name} are include file references to a file named * and &name respectively, while {*} and {&name} are argument references (to all positional arguments for the current file and to the named argument name respectively).

Arguments can be specified in 2 forms, named and positional. The parsing strategy changes based on the type of the leftmost (first) argument. If the first argument is in the named form, then all arguments are expected to be named arguments and any non-named arguments are discarded as trash. If the first argument is in the positional form, then all arguments are treated as positional, even if an argument that appears to be a valid named argument format (it will still be parsed as a positional argument and cannot be referenced by name)

The named argument format is &name=value. The name is parsed as everything between the ampersand & character and the equal sign = (not inclusive). All whitespace is deleted from the name (both embedded and trailing). Whitespace is allowed in both before and after the equal sign. After the equal sign, the value is parsed the same way that a positional argument is parsed (see below). Everything between the value of a named argument and the start of the next valid named argument (which must start with an ampersand) is discarded. Malformed named arguments (missing equal sign or any value before the right curly brace) are ignored and everything between there and the right curly brace is silently discarded.

Duplicate names are "dropped" as named arguments but must are available as a positional argument and as part of the {*} all positional arguments reference.

With the positional argument format, the value of a valid argument extends to (but does not include) the first unquoted whitespace or to the next right brace character, whichever comes first.

When parsing an argument's value any preceding whitespace is dropped though it is not required to have preceding whitespace. At that point, normal parsing of the value occurs. In this parsing, a double quote character will cause a special sub-parsing mode to occur where ampersands, equal signs and white space (characters that normally can't be part of an argument value) can be inserted into the argument value. This mode continues until a closing double quote is encountered or until the right brace is encountered. This means that the closing double quote is optional, but in such a case it will consume everything (including whitespace) up to the end of the include specification.

When not in the special quoted mode, the normal value parsing will continue until encountering whitespace or a double quote. Whitespace ends the parsing of a value and the captured text is returned. A double quote character will drop into the special quoted sub-parsing mode described above. Sequences of quoted "strings" and random characters can be freely intermixed to create the value for an argument. The only issue is that any whitespace that exists outside of the double quoted section will mark the end of the value. The double quote characters are only used for parsing and are dropped from the value itself, so references to that argument will not include the double quote characters (except if they have been properly escaped).

It is valid to match nothing in this rule (no whitespace and no value) or to only match whitespace. In such a case, the parsing will end if a right curly brace is encountered. The argument value will be an empty string.

{ } Preprocessor Name Reference

Preprocessor names are a way of substituting arbitrary text to replace a placeholder that just refers to a specific name. Preprocessor names are defined by the programmer in 4GL input files using the &GLOBAL-DEFINE or &SCOPED-DEFINE directives. The preprocessor also ensures that there are some special names that are always available (see below for a list).

Any preprocessor name reference to a name that exists is replaced on output with the text that was defined for that name.

The form of a preprocessor name reference is {&name}. One difference between a named argument reference and a preprocessor name reference is the origin of the name. The origin of a name can be inspected using the DEFINED(name) preprocessor built-in function. Another difference is that arguments are only accessible within the specific file for which they were defined, while preprocessor names are accessible to all files preprocessed via include file references within the file in which the definition occurred. Any include file references that occur in a given file before the preprocessor name is defined, will not have visibility to that preprocessor name.

Other than the above differences, argument references and preprocessor these two kinds of references work identically. Arguments and preprocessor names exist inside the same namespace, which means that the most recent definition of the same name will be the definition that is used when referenced.

The following special preprocessor names are supported. These are essentially like global preprocessor variables that always exist. These cannot be re-defined (hidden) by the use of the &GLOBAL-DEFINE or &SCOPED-DEFINE directives.

Name Description
BATCH-MODE Defaults to false. This can be overridden on a per-file, per-directory or project-wide basis using conversion hints. See the Conversion Hints chapter of the FWD Conversion Handbook.
FILE-NAME Replaced with the name of the current file being processed.
LINE-NUMBER Replaced with the line number (as a decimal integer) of the current file being processed.
OPSYS By default this is read from the current operating system using J2SE properties. That value is translated into an operating system name that would match one seen in the Progress 4GL (e.g. UNIX or WIN32). This can be overridden on a per-file, per-directory or project-wide basis using conversion hints. See the Conversion Hints chapter of the FWD Conversion Handbook.
SEQUENCE Replaced with a unique decimal integer value that starts as 0 for the first reference and is incremented by 1 for each subsequent reference.
WINDOW-SYSTEM Defaults to TTY. This can be overridden on a per-file, per-directory or project-wide basis using conversion hints. See the Conversion Hints chapter of the FWD Conversion Handbook.

The following SpeedScript / WebSpeed built-in preprocessor names are supported (but may be redefined which is different from how real built-ins work).

Name Value
AMP &amp~;
DISPLAY DISPLAY STREAM WebStream
END .
GT &gt~;
LT &lt~;
OUT PUT STREAM WebStream UNFORMATTED
OUT-FILE PUT STREAM WebStream UNFORMATTED
OUT-FMT PUT STREAM WebStream
OUT-LONG EXPORT STREAM WebStream
OUT-STREAM PUT STREAM WebStream UNFORMATTED
QUOT &quot~;
WEBSTREAM STREAM WebStream

&ANALYZE-RESUME Preprocessor Directive

This is an undocumented Progress preprocessor feature that is used by Progress 4GL development tools (the user interface builder) to leave behind some configuration data inside source files. Normally this is paired with an &ANALYZE-SUSPEND directive.

The directive causes everything on that line (including the new line character at the end) to be discarded from the output.

&ANALYZE-SUSPEND Preprocessor Directive

This is an undocumented Progress preprocessor feature that is used by Progress 4GL development tools (the user interface builder) to leave behind some configuration data inside source files. Normally this is paired with an &ANALYZE-RESUME directive.

The directive causes everything on that line (including the new line character at the end) to be discarded from the output.

&GLOBAL-DEFINE Preprocessor Directive

A preprocessor name is created using a directive in the form:

&GLOBAL-DEFINE name replacement_text

The &GLOBAL-DEFINE directive can be abbreviated down to &GLOB. Only whitespace and comments can precede the directive on the line being processed.

Whitespace delimits the name and the replacement text may contain any characters (including whitespace). The replacement text is done at the end of the line.

The replacement text may contain argument references, preprocessor name references or include file references which will be expanded once at the time the &GLOBAL-DEFINE directive is processed/parsed.

The result is a named variable in the global preprocessor namespace. If no scoped variable (preprocessor name) with the same name exists, then the value of the global name will be used to replace the matching preprocessor name reference.

Deferred expansion can be implemented by using a tilde immediately preceding the left curly brace as in ~{&name}. When this text is placed in the replacement text, it will not be expanded/replaced at the time that the directive is processed (but the tilde will be dropped). This means that the construct will not be treated as a preprocessor name reference, argument reference or include reference until that text is used (replaced) and then it will be evaluated in the context of the file where the text was replaced.

The entire line is removed from the output stream.

&IF, &THEN, &ELSEIF, &ELSE and &ENDIF Preprocessor Directives

The conditional inclusion directives are fully supported. The syntax:

&IF expression &THEN
  ..block..
[&ELSEIF expression &THEN
  ..block..]
[&ELSE
  ..block..]
{ &ENDIF | EOF }

The idea here is that the preprocessor will evaluate one or more expressions and change which text is emitted into the output as a result. A single expression can be specified for each &IF and &ELSEIF directive and there must always be at least one expression (for the &IF). If the expression evaluates to true, then the associated block is included. Once one of the expressions evaluates to true, then all other blocks are known to be excluded and no further evaluation of expressions occurs. Each ..block.. section in the above syntax is arbitrary text that is included or excluded from the output. That is why these directives are referred to as conditional inclusion directives.

The expression is parsed as all text between the &IF (or &ELSEIF) and the corresponding &THEN directive. This text can cross lines as needed.

The ..block.. content is parsed as all text between the &THEN and the next &ELSEIF, &ELSE or &ENDIF directive.

If all &IF and &ELSEIF expressions evaluate to false and there exists a &ELSE section, that section will be included in the output. If an &ELSE section does not exist, then no section will be included in the output.

Conditionals can be nested and both the expressions and the blocks can cross newline boundaries. This means that inside any of the ..block.. content in the syntax diagram above, can be additional &IF .. &ENDIF directives. The directive ends when either the outer &ENDIF or EOF is encountered. There is no limit to the nesting that is possible, so long as all the directives are properly matched with their required components (such as the expression and the blocks of text) and the other directives (like the &THEN and &ENDIF which are both required).

The expressions can be comprised of the following features:

  • decimal, integer, character, date and logical data types are all supported
  • data can be specified as literals (any of the valid forms of constants for the above listed types), including unknown value
  • there is a list of supported expression features (operators and functions) that can be used (see below)
  • the DEFINED preprocessor function can be used

The expressions must return a value with a data type of logical, integer, decimal or character. Progress 4GL documentation states that decimal types cannot be returned but this is not correct in practice. Numeric (integer or decimal) data is always converted to an integer on return and compared to 0 (0 is false, non-zero is true). Character values which are not empty are true and empty values are false. Logical values are directly used as true or false.

Empty expressions occur when there is no text except whitespace between the &IF or &ELSEIF and the &THEN. All empty expressions evaluate to false.

Supported functions and operators:

Operator/Function Type
+ operator
- operator
* operator
/ operator
= or EQ operator
<> or NE operator
< or LT operator
> or GT operator
<= or LE operator
>= or GE operator
AND operator
OR operator
NOT operator
BEGINS operator
MATCHES operator
MODULO operator
( ) operator
DEFINED() preprocessor function
ABSOLUTE 4GL built-in function
ASC 4GL built-in function
DATE 4GL built-in function
DAY 4GL built-in function
DECIMAL 4GL built-in function
ENCODE 4GL built-in function
ENTRY 4GL built-in function
ETIME 4GL built-in function
EXP 4GL built-in function
FILL 4GL built-in function
INDEX 4GL built-in function
INTEGER 4GL built-in function
KEYWORD 4GL built-in function
KEYWORD-ALL 4GL built-in function
LEFT-TRIM 4GL built-in function
LC 4GL built-in function
LENGTH 4GL built-in function
LOG 4GL built-in function
LOOKUP 4GL built-in function
MAXIMUM 4GL built-in function
MINIMUM 4GL built-in function
MONTH 4GL built-in function
NUM-ENTRIES 4GL built-in function
OPSYS 4GL built-in function
PROPATH 4GL built-in function
PROVERSION 4GL built-in function
R-INDEX 4GL built-in function
RIGHT-TRIM 4GL built-in function
RANDOM 4GL built-in function
REPLACE 4GL built-in function
ROUND 4GL built-in function
SQRT 4GL built-in function
STRING 4GL built-in function
SUBSTITUTE 4GL built-in function
SUBSTRING 4GL built-in function
TIME 4GL built-in function
TODAY 4GL built-in function
TRUNCATE 4GL built-in function
WEEKDAY 4GL built-in function
YEAR 4GL built-in function

See the section entitled Missing Built-In Functions for a list of those Progress 4GL built-in functions that the Progress preprocessor allows and which the FWD preprocessor does not support.

&MESSAGE Preprocessor Directive

Provides a mechanism to write output to the console. The format:

&MESSAGE text

All text until the end of the line is parsed and is written to the console (usually STDERR).

The entire line is removed from the output stream.

&SCOPED-DEFINE Preprocessor Directive

A preprocessor name is created using a directive in the form:

&SCOPED-DEFINE name replacement_text

The &SCOPED-DEFINE directive can be abbreviated down to &SCOP. Only whitespace and comments can precede the directive on the line being processed.

Whitespace delimits the name and the replacement text may contain any characters (including whitespace). The replacement text is done at the end of the line.

The replacement text may contain argument references, preprocessor name references or include file references which will be expanded once at the time the &SCOPED-DEFINE directive is processed/parsed.

The result is a named variable in the scoped preprocessor namespace. If no scoped variable (preprocessor name) with the same name exists in a more nested scope, then the value of the name from the outer scope will be used to replace the matching preprocessor name reference.

Deferred expansion can be implemented by using a tilde immediately preceding the left curly brace as in ~{&name}. When this text is placed in the replacement text, it will not be expanded/replaced at the time that the directive is processed (but the tilde will be dropped). This means that the construct will not be treated as a preprocessor name reference, argument reference or include reference until that text is used (replaced) and then it will be evaluated in the context of the file where the text was replaced.

A scoped symbol dictionary is maintained with these names. Each new file being processed opens a new scope in this dictionary. When a preprocessor name reference is handled, name lookups occur first in the current scope, then in the immediately previous scope, then in the scope previous to that and so on. The final lookup for names is the global namespace which is only checked after all file-specific scopes are checked.

Preprocessor arguments are added to the same scoped namespace but are only accessible from the current scope (as in Progress).

The entire line is removed from the output stream.

&UNDEFINE Preprocessor Directive

A preprocessor name is deleted using a directive in the form:

&UNDEFINE name

This will remove the first instance of the name that can be found, using the same lookup rules as for preprocessor name references. This means that the most recent scoped version of a name is found first and the global name is found last. &UNDEFINE can be used to remove scoped names, named preprocessor arguments or global names.

The entire line is removed from the output stream.

DEFINED Preprocessor Function

This function is not available to the 4GL code, it is only allowed in preprocessor expressions (see the section on &IF, &THEN, &ELSEIF, &ELSE and &ENDIF Preprocessor Directives). The function takes a single parameter which is the name of the preprocessor name (defined using &GLOBAL-DEFINE or &SCOPED-DEFINE) or an argument name. The function returns a code to report the source of that name. Names which do not refer to any valid preprocessor name and which do not refer to an argument which is in scope for the current file, naturally refer to undefined names.

Attempting to pass the name of the special built-in preprocessor names (e.g. OPSYS) causes an error in the preprocessor (just as it does in the Progress preprocessor).

Return Value Reason
0 The name is undefined.
1 The name is valid and was defined using &GLOBAL-DEFINE.
2 The name is a valid argument for the current file.
3 The name is valid and was defined using &SCOPED-DEFINE.

Unrecognized Text

Everything not recognized based on the preprocessor features documented above is assumed to be Progress 4GL source code. This unrecognized input is supposed to be unchanged and is copied directly to output.

Unsupported Features

Custom Alternative Replacement Characters

In Progress 4GL, the protermcap file can support an IN clause to assign specific extended ASCII characters to custom alternative replacements. In such a case, the input character is prefixed by ; and the combination is translated to the custom replacement. While the standard replacements are supported using the ; Special Character (see section above), the custom alternative replacement facility is not supported.

Missing Built-In Functions

FWD supports the vast majority of built-in functions that are available for use in preprocessor expressions in Progress. The following functions are not supported:

  • INT64
  • LIBRARY
  • MEMBER

Other Limitations

When only supported features are used, there are only two areas in which the output from Progress and FWD preprocessors are expected to differ.

Tab Expansion Issue

The preprocessor support for tabs expansion is particularly sensitive to the internal order of actions. If a line of a source file contains both a preprocessor reference {...} of any kind as well as tab characters (0x09), then the result would vary depending on the order of tab expansion and reference resolution.

Consider this example line:

            {11}                      /* comment */

This line has three tabs following the reference {11}. Let's assume the positional argument 11 has the value of '.'. First, the reference is resolved:

            .                         /* comment */

Next, tabs are expanded:

            .                    /* comment */

If the same two actions are executed in reverse order, a different result occurs. First, tabs are expanded:

            {11}                         /* comment */

Next the reference is resolved:

            .                         /* comment */

If the results are compared, there are a different number of spaces that replaced tabs:

            .                    /* comment */
            .                         /* comment */

Currently, the FWD Preprocessor does all processing in one pass, producing the first line. The Progress preprocessor apparently runs in multiple passes, expanding tabs in a pass before resolving references. This produces the second line.

Another known case where the tab expansion issue can be seen is with references to preprocessor variables, if their definitions contain tabs.

Any differences in whitespace caused by this issue should not have any effect on the 4GL code itself. However, any developer comparing the two files must be aware of this difference.

Progress Tilde Bug

The Progress preprocessor has a bug that can be seen when the PREPROCESS option is used in the COMPILE statement. The bug only affects the output saved in the text file. The version of output that is passed to the 4GL compilation step does not show any problems.

The problem is that the Progress preprocessor emits ~ tilde characters (0x7E) on output where they are deleted from the file to be compiled. In other words, the preprocessor removes the tilde characters from the actual output to the compiler, but leaves the tilde characters in the cached output to the file system. Presumably, the Progress preprocessor must save off it's output to the file system before it is done with all expansions or processing. This can be seen by the fact that the cached Progress preprocessor output file fails to compile when the original procedure had no such problem.

An example procedure file:

def var txt as char.
txt = "hel~nlo".
message txt.

The Progress preprocessor cache output:

def var txt as char.
txt = "hel~
lo".
message txt.

The results of running the original procedure file (the message output):

hel

The results of running the preprocessor output file (the message output):

hello

Such cases may have differences between the output from the Progress and FWD preprocessors.


© 2004-2017 Golden Code Development Corporation. ALL RIGHTS RESERVED.