home *** CD-ROM | disk | FTP | other *** search
- {bt
- A Language Independent Conditional Compilation Tool
- (LICC)
-
-
-
- Jose L. Munoz, Ph.D.
- Naval Underwater Systems Center
- Fort Trumbull
- New London, CT 06320
-
-
- Introduction
-
- Situations often arise that require selection of segments of code to be considered (or not
- considered) for processing by a compiler, that is conditional compilation. The C programming
- language contains a pre-processor that supports this type of processing and is built-in as part of the
- language environment. This paper presents a language-independent conditional
- compilation tool, i.e. a pre-processor that may be used
- independent of the actual compilation language.
-
- Conditional compilation is most often used to insert code for testing or debugging purposes. In
- this manner production code would not contain these statements and thereby save (1) the additional
- time (in testing whether or not a particular condition is true, in real-time) and space incurred by the
- generation of the additional code supporting these statements and, (2) the run time "dead code" as a
- result of inserted code that is not executed. Conditional compilation can also be used to create
- variants of the "same" program. For example, to insert (or remove) code specific to a particular
- implementation (terminal types, operating systems, programming environment features, etc.) or to
- insert (or remove) code for language features that might exist on some implementations but not on
- others. As an example, the Simscript II.5 programming language, used for development of
- simulations, supports the animation of a simulation. However, the animation is only supported on
- certain graphical workstations while the set of machines on which the language is supported is
- much greater. By using a tool such as LICC it would be possible to make the simulation more
- portable by "turning off" the graphical code. In addition, the removal of graphical code also
- supports simulation analysis that does not require any animation support and avoids having to
- maintain multiple versions of what should be the same program.
-
- This paper presents a tool that could be used to provide the desired characteristics described above.
- It has features and capabilities (and syntax) similar to that provided as part of the C environment.
- Its principal benefits are: (a) its relatively low cost (with respect to additional time to process the
- stream of information), (b) by using AWK, the tool is readily available to Unix users but more
- importantly there exist public domain versions of AWK or AWK-like tools available and, (c) the
- various syntactic constructs required may be embedded in the particular compiler's comment
- syntax thereby making these syntactic constructs "invisible" to the compiler (hence the phrase
- "language independent").
-
- The LICC tool is a pre-processor taking incoming text, processing that text using the syntax and
- semantics described below, and finally outputting a "processed" version of the incoming text. The
- processed version would be either the complete input text or the input text with selected portions
- removed. A typical application of LICC would be found in a Unix command line such as:
-
- awk -f licc input_file | pc
-
-
- if the LICC output were intended for the pc compiler. The AWK "-f" switch is used to specify that
- AWK should use file licc for obtaining its pattern/action description (see below).
-
- LICC Syntax
-
- As previously stated the syntactic constructs for the LICC was freely adapted from the C
- programming language. Specifically, #define, #undef, #ifdef, #ifndef, #endif and, #else for
- "define", "un-define", "ifâ•©ddefined", "if not defined", "end of if condition" and, "if alternative",
- respectively. The goal is to copy text from the input stream to the output stream under control of
- these statements. The default mode is to copy a line of text. That is, in the absence of any of these
- constructs, simply echo the line. In the examples shown, text in italics will not appear in the
- output stream.
-
- The above constructs have the following syntax and semantics:
-
- #define identifier
- The specified identifier is made known to the LICC tool for later conditional testing.
- eg. #define debug
-
- #undef identifier
- The specified identifier is removed from the set of recognized LICC identifiers. If the identifier
- given was not previously #defined an error message is generated.
- eg. #undef debug
-
- #ifdef identifier
- If the specified identifier is recognized (i.e. was previously presented to LICC via a #define) the
- text following this statement will be copied to the output stream. Lines will be copied to the output
- stream until an #else or #endif statement is encountered (#else and #endif ar described
- subsequently).
- eg.
- #define debug
- #ifdef debug
- Now we are testing the 'debug' symbol previously defined...
- at this point it should be defined. Because it is defined this
- segment of text should appear in the output stream.
- #endif
-
- #ifndef identifier
- If the specified identifier is not currently defined (i.e. has not been previously defined via an
- #define or was explicitly undefined via an #undef) the text following this statement will be copied
- to the output stream. Lines will be copied until an #else or #endif statement is encountered.
- eg.
- #ifndef debug
- Here the 'debug' symbol is not defined, but since this is an
- 'ifndef' conditional this segment of text should appear in the output. Initially all
- symbols are "undefined".
- #endif
-
- #endif
- Terminates the scope of an #ifdef or #ifndef statement. If not currently in the scope of an #ifdef or
- an #ifndef an error message is generated.
-
-
- #else
- Provides an alternate path for the #ifdef and #ifndef statements. If the #ifdef or #ifndef conditional
- failed (that is the condition was found to be false) then the text following this statement will be
- copied to the output stream. Lines will be copied until an #endif statement is encountered. If not
- currently in the scope of an #ifdef or an #ifndef an error message is generated.
-
- #define debug
- #ifdef licctest
- The 'licctest' syymbol is not currently defined...so this segment of
- text should not appear in the output stream.
- #else
- However, this segment of text follows an 'else' clause and
- therefore should appear in the output as the first conditional test
- failed. The 'endif' is, of course, still required.
- #endif
- #ifndef debug
- Yet another 'else' test but this time using the 'ifndef' statement.
- Since 'debug' is defined...this test will fail...but
- #else
- this section of text should be printed as the first conditional failed
- and this segment follows an 'else' clause.
- #endif
- #ifdef debug
- One final test with 'else'... demonstrating that if the test is
- successful then the text following the 'else' clause will not be printed
- #else
- This text appears following an else clause for what should be
- a successful test...therefore, this text should not appear in
- the output stream.
- #endif
-
-
- The syntax/semantics is, as can be readily observed, very simple and intuitively consistent. The
- tool supports nesting of the various constructs using conventional scoping rules (that is, inner-
- most #else and #endif are satisfied first and are associated with the conditional most recently
- preceding them). While LICC does support this feature, this author has not found nesting to be
- often utilized in conditional compilation.
-
- #define debug
- #ifndef licctest
- The 'licctest' symbol is not defined so this segment of text should
- appear in the output stream.
- #ifdef debug
- The 'licctest' symbol is not defined, but the 'debug' symbol is...so you should see this
- segment of text in the output. Note that two 'endif's will be required.
- #endif
- #endif
-
- The reader should recall that the default mode is to copy text, the conditionals are used to control
- this process (i.e. copy/or not copy a segment of text). It should also be noted that an additional
- LICC goal is the ability to "hide" the LICC syntax from the compiler by embedding the constructs
- inside the target language's conventional comment syntax. This is demonstrated in the following
- code segment submitted to LICC for processing.
-
-
- One of the convenient features of LICC is that it can be used on
- different programming languages simply by embedding the LICC syntax
- inside the programming language comment syntax...thereby making
- it transparent to the particular compiler. Such as:
-
- {#ifdef debug } Let's try a Pascal comment
- Here we are utilizing the conventional Pascal programming language
- comment construct...the curly braces.
- {#endif}
-
- /*#ifdef debug */
- Here is yet another version of a popular Pascal syntax for comments.
- This also represents the comment syntax used by the C programming
- language.
- However, it would not make sense to use this tool in C because C
- has its own pre-processor and in fact was used to model the
- pre-processor being demo'd here.
- It also represents the comment construct used by the Prolog programming
- language, used for AI applications.
- /*#endif */
-
- ''#ifdef debug
- Now we are demonstrating the comment constructs used in Simscript 2.5
- These lines should appear in the output stream.
- ''#endif
-
- C#ifdef debug
- This is now using the FORTRAN comment constructs...
- FFORTRAN comments are recognized by a 'C' in column 1.
- C#endif
-
- --#ifdef debug
- Finally, the DoD standard programming language... Ada ...
- Ada syntax uses a double hyphen '--' to signify to the compiler that
- the remaining text on this line is a comment.
- --#endif
-
-
- AWK Implementation
-
- Readers familiar with the AWK programming language (available under Unix) will no doubt find
- an implementation of LICC straight forward (indeed, no doubt the implementation provided here
- could be made even "tighter" by AWK aficionados). The implementation provided herein
- represents a straight forward approach (hacker model?).
-
- For readers not familiar with AWK, a full tutorial would be outside the scope of this paper,
- however the AWK paradigm is: if a given pattern is encountered, execute the specified action for
- that pattern, i.e.
-
- pattern { action }
-
- where the pattern is represented using the usual regular expression syntax found in Unix and the
- action statements look (and behave) very much like C code.
-
- The full AWK implementation of LICC is given below:
-
- #
- # LICC: Language Independent Conditional Compilation syntax (written in AWK)
- # for selectingg parts of code to be (or not too be) compiled.
- # Compiler independent (i.e. C, Pascal, Ada, Simscript, etc.)
- # use the comment structure for your particular language to
- # 'hide' the LICC syntactic constructs from the compiler.
- #
- # The output from the tool can then be piped directly to the
- # compiler as in:
- # awk -f licc file|pc
- #
- BEGIN {iprint[[1] = 1; ptr = 1; ifs = 0;} # iprint[] is used as a stack
- /#define/ { defs[$2] = 1;
- if (!iprint[ptr]) print $0; } # defs[] is used as a symbol table
- /#undef/ { if (defs[$2]) { defs[$2] = 0; if(!iprint[ptr]) print $0; }
- else print "<<< LICC UNDEF ERROR >>>"; }
- /#ifdef/ { ifs++; if (ddefs[$2]) iprint[++ptr] = 1; else
- { iprint[++ptr] = 0; print $0;} }
- /#ifndef/ { ifs++; if (!defs[$2]) iprint[++ptr] = 1; else
- { iprint[++ptr] = 0; print $0;} }
- /#endif/ { if (!ifs) print "<<< LICC IF ERROR >>>"; else
- { --ifs; if (!iprint[--ptr]) print $0;} }
- /#else/ { if (ifs) if(iprint[ptr]) iprint[ptr] = 0; else
- iprint[ptr] = 1; else
- print "<<< LICC ELSE ERROR >>>";
- if (!ipriint[ptr]) print $0; }
- { if (iprint[ptr]) print $0; # copy lines to output stream
- if (!ptr) print "<<< LICC POINTER ERROR >>>"}
-
- The following segments of text represent actual input and generated output of text submitted to
- LICC for processing. Again, text appearing in italics form should not appearr in the final
- generated output of LICC.
-
-
- INPUT submitted to LICC:
-
- When no tokens are defined LICC works in a pass thru mode
- ...thus any input lines are simple copied to the output
- #ifdef debug
- At this point nothing has been defined so any conditional testing
- of any symbol will fail...such as this test on the 'debug' symbol
- as a result this segment of text will not be copied to the output
- #endif
- #ifndef debug
- Here the 'debug' symbol is still not defined, but since this is an
- 'ifndef' conditional this segment of text should appear in the output.
- #endif
- #define licctest Test 1
- #ifdef licctest
- This segment of code should appear in the output as the 'licctest'
- symbol should be recognized having been presented to LICC via a
- previous 'define' statment.
- #endif licctest Test 1
- #define debug Defining a second symbol for subsequent testing
- These lines of text are outside the scope of any conditional tests
- As a result they should be printed...text outside the scope
- of any 'ifdef', 'ifndef' or 'else' should be outputted.
- #ifndef licctest Test 2
- This piece of text is within the scope of an 'ifndef' conditional test.
- Since the 'licctest' symbol IS DEFINED...this segment of text
- should not appear in the output.
- #endif End Test 2
- #ifdef debug Test 3
- Now we are testing the 'debug' symbol previously defined...
- at this point it should be defined. Both 'licctest' and 'debug'
- should be recognized.
- #endif
- #undef licctest Test 4 (un-defining the 'licctest' symbol)
- #ifndef licctest Test 5
- Now the 'licctest' symbol is not defined...so this segment of text
- should appear in the output.
- #endif End Test 5
- #ifdef licctest Test 6
- The 'licctest' symbol was previously undefined...therefore this
- segment of text SHOULD NOT appear in the output stream.
- #endif End Test 6
- #ifdef debug Test 7
- While the 'graphics' symbol has been undefined...the 'debug' symbol
- should should still be recognized...this text should appear in output.
- #endif
- #ifndef licctest (Demonstrate nesting of conditionals)
- The 'licctest' symbol is not defined so this segment of text should
- appear in the output stream.
- #ifdef debug (Nested conditional)
- The 'licctest' symbol is not defined,
- but the 'debug' symbol is...so you should see this segment
- of text in the output. Note that two 'endif's will be required.
- #endif (Terminate the 'debug' test)
- #endif (Terminate the 'licctest' test)
-
- <<<<<<<<<<<<<<<< 'ELSE' clause Demonstrations >>>>>>>>>>>>>>>>
-
- #ifdef licctest (Demonstrate 'else' LICC clause processing)
- The 'licctest' symbol is not currently ddefined...so this segment of
- text should not appear in the output stream.
- #else
- However, this segment of text follows an 'else' clause and
- therefore should appear in the output as the first conditional test
- failed. The 'endif' is, of course, still required.
- #endif
- #ifndef debug
- Yet another 'else' test but this time using the 'ifndef' statement.
- Sinnce 'debug' is defined...this test will fail...but
- #else
- this section of text should be printed as the first conditional failed
- and this segment follows an 'else' clause.
- #endif
-
- #ifdef debug
- One final test with 'else'... confirming that if the test is
- successful then the text following the 'else' clause will not be printed
- #else
- This text appears following an else clause for what should be
- a successful test...therefore, this text should not appear in
- the output stream.
- #endif
-
-
-
- LICC Generated OUTPUT
-
- When no tokens are defined LICC works in a pass thru mode
- ...thus any input lines are simple copied to the output
- #ifdef debug
- #endif
- #ifndef debug
- Here the 'debug' symbol is still not defined, but since this is an
- 'ifndef' conditional this segment of text should appear in the output.
- #endif
- #define licctest Test 1
- #ifdef licctest
- This segment of code should appear in the output as the 'licctest'
- symbol should be recognized having been presented to LICC via a
- previous 'define' statment.
- #endif licctest Test 1
- #define debug Defining a second symbol for subsequent testing
- These lines of text are outside the scope of any conditional tests
- As a result they should be printed...text outside the scope
- of any 'ifdef', 'ifndef' or 'else' should be outputted.
- #ifndef licctest Test 2
- #endif End Test 2
- #ifdef debug Test 3
- Now we are testing the 'debug' symbol previously defined...
- at this point it should be defined. Both 'licctest' and 'debug'
- sshould be recognized.
- #endif
- #undef licctest Test 4 (un-defining the 'licctest' symbol)
- #ifndef licctest Test 5
- Now the 'licctest' symbol is not defined...so this segment of text
- should appear in the output.
- #endif End Test 5
- #ifdef licctest Test 6
- #endif End Test 6
- #ifdef debug Test 7
- While the 'grpahics' symbol has been undefined...the 'debug' symbol
- should should still be recognized...this text should appear in output.
- #endif
- #ifndef licctest (Demonstrate nesting of conditionals)
- The 'licctest' symbol is not defined so this seggment of text should
- appear in the output stream.
- #ifdef debug (Nested conditional)
- The 'licctest' symbol is not defined,
- but the 'debug' symbol is...so you should see this segment
- of text in the output. Note that two 'endif's will be required.
- #endif (Terminate the 'debug' test)
- #endif (Terminate the 'licctest' test)
-
- <<<<<<<<<<<<<<<< 'ELSE' clause Demonstrations >>>>>>>>>>>>>>>>
-
- #ifdef licctest (Demonstrate 'else' LICC clause processing)
- #else
- However, this segment of text follows an 'else' clause and
- therefore should appear in the output as the first conditional test
- failed. The 'endif' is, of course, still required.
- #endif
- #ifndef debug
- #else
- this section of text should be print as the first conditional failed
- and this segment follows an 'else' clause.
- #endif
- #ifdef debug
- One final test with 'else'... confirming that if the test is
- successful then the text following the 'else' clause will not be printed
- #else
- #endif
-
- The reader should note that text may be inserted following the LICC syntactic constructs and that
- these are just passed through by LICC, thus providing an ability to insert comments concerning the
- specific condition being tested.
-
-
- Summary
-
- Using AWK, it was possible to generate an "inexpensive" language independent conditional
- compilation tool that could be used to select (or de-select) segments of code for consideration by a
- compiler. The only requirement is that the compiler have some sort of comment syntax (to support
- hiding the LICC syntax from the compiler) and that AWK or an AWK-like language be available.
- The total processing overhead required to support the various language syntax semantics was only
- about 2.5 sec for 1000 lines of code (Sun 3/160).
- {et