home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / cmplangm / 1989_6 / munoz < prev   
Text File  |  1989-05-24  |  19KB  |  419 lines

  1. {bt
  2. A Language Independent Conditional Compilation Tool
  3. (LICC)
  4.  
  5.  
  6.  
  7. Jose L. Munoz, Ph.D.
  8. Naval Underwater Systems Center
  9. Fort Trumbull
  10. New London, CT 06320
  11.  
  12.  
  13. Introduction
  14.  
  15. Situations often arise that require selection of segments of code to be considered (or not 
  16. considered) for processing by a compiler, that is conditional compilation.  The C programming 
  17. language contains a pre-processor that supports this type of processing and is built-in as part of the 
  18. language environment.  This paper presents a language-independent conditional 
  19. compilation tool, i.e. a pre-processor that may be used 
  20. independent of the actual compilation language.
  21.  
  22. Conditional compilation is most often used to insert code for testing or debugging purposes.  In 
  23. this manner production code would not contain these statements and thereby save (1) the additional 
  24. time (in testing whether or not a particular condition is true, in real-time) and space incurred by the 
  25. generation of the additional code supporting these statements and, (2) the run time "dead code" as a 
  26. result of inserted code that is not executed.  Conditional compilation can also be used to create 
  27. variants of the "same" program. For example, to insert (or remove) code specific to a particular 
  28. implementation (terminal types, operating systems, programming environment features, etc.) or to 
  29. insert (or remove) code for language features that might exist on some implementations but not on 
  30. others.  As an example, the Simscript II.5 programming language, used for development of 
  31. simulations, supports the animation of a simulation.  However, the animation is only supported on 
  32. certain graphical workstations while the set of machines on which the language is supported is 
  33. much greater.  By using a tool such as LICC it would be possible to make the simulation more 
  34. portable by "turning off" the graphical code.  In addition, the removal of graphical code also 
  35. supports simulation analysis that does not require any animation support and avoids having to 
  36. maintain multiple versions of what should be the same program.
  37.  
  38. This paper presents a tool that could be used to provide the desired characteristics described above.  
  39. It has features and capabilities (and syntax) similar to that provided as part of the C environment.  
  40. Its principal benefits are: (a) its relatively low cost (with respect to additional time to process the 
  41. stream of information), (b) by using AWK, the tool is readily available to Unix users but more 
  42. importantly there exist public domain versions of AWK or AWK-like tools available and, (c) the 
  43. various syntactic constructs required may be embedded in the particular compiler's comment 
  44. syntax thereby making these syntactic constructs "invisible" to the compiler (hence the phrase 
  45. "language independent").
  46.  
  47. The LICC tool is a pre-processor taking incoming text, processing that text using the syntax and 
  48. semantics described below, and finally outputting a "processed" version of the incoming text.  The 
  49. processed version would be either the complete input text or the input text with selected portions 
  50. removed.  A typical application of LICC would be found in a Unix command line such as:
  51.  
  52.     awk -f licc input_file | pc
  53.  
  54.  
  55. if the LICC output were intended for the pc compiler.  The AWK "-f" switch is used to specify that 
  56. AWK should use file licc for obtaining its pattern/action description (see below).
  57.  
  58. LICC Syntax
  59.  
  60. As previously stated the syntactic constructs for the LICC was freely adapted from the C 
  61. programming language.  Specifically, #define, #undef, #ifdef, #ifndef, #endif and, #else for 
  62. "define", "un-define", "if╩ddefined", "if not defined", "end of if condition" and, "if alternative", 
  63. respectively.  The goal is to copy text from the input stream to the output stream under control of 
  64. these statements.  The default mode is to copy a line of text.  That is, in the absence of any of these 
  65. constructs, simply echo the line.  In the examples shown, text in italics will not appear in the 
  66. output stream.
  67.  
  68. The above constructs have the following syntax and semantics:
  69.  
  70. #define identifier
  71. The specified identifier is made known to the LICC tool for later conditional testing.
  72.     eg. #define debug
  73.  
  74. #undef identifier
  75. The specified identifier is removed from the set of recognized LICC identifiers.  If the identifier 
  76. given was not previously #defined an error message is generated.
  77.     eg. #undef debug
  78.  
  79. #ifdef identifier
  80. If the specified identifier is recognized (i.e. was previously presented to LICC via a #define) the 
  81. text following this statement will be copied to the output stream.  Lines will be copied to the output 
  82. stream until an #else or #endif statement is encountered (#else and #endif ar described 
  83. subsequently).
  84.     eg. 
  85. #define debug
  86. #ifdef debug
  87. Now we are testing the 'debug' symbol previously defined...
  88. at this point it should be defined.  Because it is defined this 
  89. segment of text should appear in the output stream.
  90. #endif
  91.  
  92. #ifndef identifier
  93. If the specified identifier is not currently defined (i.e. has not been previously defined via an 
  94. #define or was explicitly undefined via an #undef) the text following this statement will be copied 
  95. to the output stream.  Lines will be copied until an #else or #endif statement is encountered.
  96.     eg.
  97. #ifndef debug
  98. Here the 'debug' symbol is not defined, but since this is an 
  99. 'ifndef' conditional this segment of text should appear in the output.  Initially all 
  100. symbols are "undefined".
  101. #endif
  102.  
  103. #endif
  104. Terminates the scope of an #ifdef or #ifndef statement.  If not currently in the scope of an #ifdef or 
  105. an #ifndef an error message is generated.
  106.  
  107.  
  108. #else
  109. Provides an alternate path for the #ifdef and #ifndef statements.  If the #ifdef or #ifndef conditional 
  110. failed (that is the condition was found to be false) then the text following this statement will be 
  111. copied to the output stream.  Lines will be copied until an #endif statement is encountered.  If not 
  112. currently in the scope of an #ifdef or an #ifndef an error message is generated.
  113.  
  114. #define debug
  115. #ifdef licctest
  116. The 'licctest' syymbol is not currently defined...so this segment of
  117. text should not appear in the output stream.
  118. #else
  119. However, this segment of text follows an 'else' clause and
  120. therefore should appear in the output as the first conditional test
  121. failed. The 'endif' is, of course, still required.
  122. #endif
  123. #ifndef debug
  124. Yet another 'else' test but this time using the 'ifndef' statement.
  125. Since 'debug' is defined...this test will fail...but
  126. #else
  127. this section of text should be printed as the first conditional failed
  128. and this segment follows an 'else' clause.
  129. #endif
  130. #ifdef debug
  131. One final test with 'else'... demonstrating that if the test is
  132. successful then the text following the 'else' clause will not be printed
  133. #else
  134. This text appears following an else clause for what should be
  135. a successful test...therefore, this text should not appear in
  136. the output stream.
  137. #endif
  138.  
  139.  
  140. The syntax/semantics is, as can be readily observed, very simple and intuitively consistent.  The 
  141. tool supports nesting of the various constructs using conventional scoping rules (that is, inner-
  142. most #else and #endif are satisfied first and are associated with the conditional most recently 
  143. preceding them).  While LICC does support this feature, this author has not found nesting to be 
  144. often utilized in conditional compilation.
  145.  
  146. #define debug
  147. #ifndef licctest
  148. The 'licctest' symbol is not defined so this segment of text should
  149. appear in the output stream.
  150. #ifdef debug
  151. The 'licctest' symbol is not defined, but the 'debug' symbol is...so you should see this 
  152. segment of text in the output.  Note that two 'endif's will be required.
  153. #endif
  154. #endif
  155.  
  156. The reader should recall that the default mode is to copy text, the conditionals are used to control 
  157. this process (i.e. copy/or not copy a segment of text).  It should also be noted that an additional 
  158. LICC goal is the ability to "hide" the LICC syntax from the compiler by embedding the constructs 
  159. inside the target language's conventional comment syntax.  This is demonstrated in the following 
  160. code segment submitted to LICC for processing.
  161.  
  162.  
  163. One of the convenient features of LICC is that it can be used on
  164. different programming languages simply by embedding the LICC syntax
  165. inside the programming language comment syntax...thereby making
  166. it transparent to the particular compiler.  Such as:
  167.  
  168. {#ifdef debug } Let's try a Pascal comment
  169. Here we are utilizing the conventional Pascal programming language
  170. comment construct...the curly braces.
  171. {#endif}
  172.  
  173. /*#ifdef  debug */
  174. Here is yet another version of a popular Pascal syntax for comments.
  175. This also represents the comment syntax used by the C programming 
  176. language.
  177. However, it would not make sense to use this tool in C because C
  178. has its own pre-processor and in fact was used to model the
  179. pre-processor being demo'd here.
  180. It also represents the comment construct used by the Prolog programming
  181. language, used for AI applications.
  182. /*#endif */
  183.  
  184. ''#ifdef debug
  185. Now we are demonstrating the comment constructs used in Simscript 2.5
  186. These lines should appear in the output stream.
  187. ''#endif
  188.  
  189. C#ifdef debug
  190. This is now using the FORTRAN comment constructs...
  191. FFORTRAN comments are recognized by a 'C' in column 1.
  192. C#endif
  193.  
  194. --#ifdef debug
  195. Finally, the DoD standard programming language... Ada ... 
  196. Ada syntax uses a double hyphen '--' to signify to the compiler that
  197. the remaining text on this line is a comment.
  198. --#endif
  199.  
  200.  
  201. AWK Implementation
  202.  
  203. Readers familiar with the AWK programming language (available under Unix) will no doubt find 
  204. an implementation of LICC straight forward (indeed, no doubt the implementation provided here 
  205. could be made even "tighter" by AWK aficionados).  The implementation provided herein 
  206. represents a straight forward approach (hacker model?).
  207.  
  208. For readers not familiar with AWK, a full tutorial would be outside the scope of this paper, 
  209. however the AWK paradigm is: if a given pattern is encountered, execute the specified action for 
  210. that pattern, i.e.
  211.  
  212.     pattern  { action }
  213.  
  214. where the pattern is represented using the usual regular expression syntax found in Unix and the 
  215. action statements look (and behave) very much like C code.
  216.  
  217. The full AWK implementation of LICC is given below:
  218.  
  219. #
  220. #       LICC: Language Independent Conditional Compilation syntax  (written in AWK) 
  221. #               for selectingg parts of code to be (or not too be) compiled.
  222. #               Compiler independent (i.e. C, Pascal, Ada, Simscript, etc.)
  223. #               use the comment structure for your particular language to
  224. #               'hide' the LICC syntactic constructs from the compiler.
  225. #
  226. #               The output from the tool can then be piped directly to the
  227. #               compiler as in:
  228. #                      awk -f licc file|pc
  229. #
  230. BEGIN {iprint[[1] = 1; ptr = 1; ifs = 0;}    # iprint[] is used as a stack
  231. /#define/ { defs[$2] = 1; 
  232.  if (!iprint[ptr]) print $0; }     # defs[] is used as a symbol table
  233. /#undef/ { if (defs[$2]) { defs[$2] = 0; if(!iprint[ptr]) print  $0; }
  234.                           else print "<<< LICC UNDEF ERROR >>>"; }
  235. /#ifdef/ { ifs++; if (ddefs[$2]) iprint[++ptr] = 1; else 
  236.                    { iprint[++ptr] = 0; print $0;} }
  237. /#ifndef/ { ifs++; if (!defs[$2]) iprint[++ptr] = 1; else 
  238.                    { iprint[++ptr] = 0; print $0;} }
  239. /#endif/ { if (!ifs) print "<<< LICC IF ERROR >>>"; else 
  240.                           { --ifs; if (!iprint[--ptr]) print $0;} }
  241. /#else/  { if (ifs) if(iprint[ptr]) iprint[ptr] = 0; else 
  242.                                           iprint[ptr] = 1;  else 
  243.                           print "<<< LICC ELSE ERROR >>>";
  244.                    if (!ipriint[ptr]) print $0; }
  245. { if (iprint[ptr]) print $0;     # copy lines to output stream
  246.   if (!ptr) print "<<< LICC POINTER ERROR >>>"}
  247.  
  248. The following segments of text represent actual input and generated output of text submitted to 
  249. LICC for processing.  Again, text appearing in italics form should not appearr in the final 
  250. generated output of LICC.
  251.  
  252.  
  253. INPUT submitted to LICC:
  254.  
  255. When no tokens are defined LICC works in a pass thru mode
  256. ...thus any input lines are simple copied to the output
  257. #ifdef debug
  258. At this point nothing has been defined so any conditional testing 
  259. of any symbol will fail...such as this test on the 'debug' symbol
  260. as a result this segment of text will not be copied to the output
  261. #endif
  262. #ifndef debug
  263. Here the 'debug' symbol is still not defined, but since this is an 
  264. 'ifndef' conditional this segment of text should appear in the output.
  265. #endif
  266. #define licctest Test 1
  267. #ifdef licctest 
  268. This segment of code should appear in the output as the 'licctest'
  269. symbol should be recognized having been presented to LICC via a
  270. previous 'define' statment.
  271. #endif licctest Test 1
  272. #define debug  Defining a second symbol for subsequent testing 
  273. These lines of text are outside the scope of any conditional tests
  274. As a result they should be printed...text outside the scope
  275. of any 'ifdef', 'ifndef' or 'else' should be outputted.
  276. #ifndef licctest Test 2
  277. This piece of text is within the scope of an 'ifndef' conditional test.
  278. Since the 'licctest' symbol IS DEFINED...this segment of text
  279. should not appear in the output.
  280. #endif End Test 2
  281. #ifdef debug Test 3
  282. Now we are testing the 'debug' symbol previously defined...
  283. at this point it should be defined.  Both 'licctest' and 'debug'
  284. should be recognized.
  285. #endif
  286. #undef licctest Test 4 (un-defining the 'licctest' symbol)
  287. #ifndef licctest Test 5
  288. Now the 'licctest' symbol is not defined...so this segment of text
  289. should appear in the output.
  290. #endif End Test 5
  291. #ifdef licctest Test 6
  292. The 'licctest' symbol was previously undefined...therefore this
  293. segment of text SHOULD NOT appear in the output stream.
  294. #endif End Test 6
  295. #ifdef debug Test 7
  296. While the 'graphics' symbol has been undefined...the 'debug' symbol 
  297. should should still be recognized...this text should appear in output.
  298. #endif
  299. #ifndef licctest (Demonstrate nesting of conditionals)
  300. The 'licctest' symbol is not defined so this segment of text should
  301. appear in the output stream.
  302. #ifdef debug     (Nested conditional)
  303. The 'licctest' symbol is not defined,
  304. but the 'debug' symbol is...so you should  see this segment
  305. of text in the output.  Note that two 'endif's will be required.
  306. #endif  (Terminate the 'debug' test)
  307. #endif  (Terminate the 'licctest' test)
  308.  
  309. <<<<<<<<<<<<<<<< 'ELSE' clause Demonstrations >>>>>>>>>>>>>>>>
  310.  
  311. #ifdef licctest  (Demonstrate 'else' LICC clause processing)
  312. The 'licctest' symbol is not currently ddefined...so this segment of
  313. text should not appear in the output stream.
  314. #else
  315. However, this segment of text follows an 'else' clause and
  316. therefore should appear in the output as the first conditional test
  317. failed. The 'endif' is, of course, still required.
  318. #endif
  319. #ifndef debug
  320. Yet another 'else' test but this time using the 'ifndef' statement.
  321. Sinnce 'debug' is defined...this test will fail...but
  322. #else
  323. this section of text should be printed as the first conditional failed
  324. and this segment follows an 'else' clause.
  325. #endif
  326.  
  327. #ifdef debug
  328. One final test with 'else'... confirming that if the test is
  329. successful then the text following the 'else' clause will not be printed
  330. #else
  331. This text appears following an else clause for what should be
  332. a successful test...therefore, this text should not appear in
  333. the output stream.
  334. #endif
  335.  
  336.  
  337.  
  338. LICC Generated OUTPUT
  339.  
  340. When no tokens are defined LICC works in a pass thru mode
  341. ...thus any input lines are simple copied to the output
  342. #ifdef debug
  343. #endif
  344. #ifndef debug
  345. Here the 'debug' symbol is still not defined, but since this is an 
  346. 'ifndef' conditional this segment of text should appear in the output.
  347. #endif
  348. #define licctest Test 1
  349. #ifdef licctest 
  350. This segment of code should appear in the output as the 'licctest'
  351. symbol should be recognized having been presented to LICC via a
  352. previous 'define' statment.
  353. #endif licctest Test 1
  354. #define debug  Defining a second symbol for subsequent testing 
  355. These lines of text are outside the scope of any conditional tests
  356. As a result they should be printed...text outside the scope
  357. of any 'ifdef', 'ifndef' or 'else' should be outputted.
  358. #ifndef licctest Test 2
  359. #endif End Test 2
  360. #ifdef debug Test 3
  361. Now we are testing the 'debug' symbol previously defined...
  362. at this point it should be defined.  Both 'licctest' and 'debug'
  363. sshould be recognized.
  364. #endif
  365. #undef licctest Test 4 (un-defining the 'licctest' symbol)
  366. #ifndef licctest Test 5
  367. Now the 'licctest' symbol is not defined...so this segment of text
  368. should appear in the output.
  369. #endif End Test 5
  370. #ifdef licctest Test 6
  371. #endif End Test 6
  372. #ifdef debug Test 7
  373. While the 'grpahics' symbol has been undefined...the 'debug' symbol 
  374. should should still be recognized...this text should appear in output.
  375. #endif
  376. #ifndef licctest (Demonstrate nesting of conditionals)
  377. The 'licctest' symbol is not defined so this seggment of text should
  378. appear in the output stream.
  379. #ifdef debug     (Nested conditional)
  380. The 'licctest' symbol is not defined,
  381. but the 'debug' symbol is...so you should see this segment
  382. of text in the output.  Note that two 'endif's will be required.
  383. #endif  (Terminate the 'debug' test)
  384. #endif  (Terminate the 'licctest' test)
  385.  
  386. <<<<<<<<<<<<<<<< 'ELSE' clause Demonstrations >>>>>>>>>>>>>>>>
  387.  
  388. #ifdef licctest  (Demonstrate 'else' LICC clause processing)
  389. #else
  390. However, this segment of text follows an 'else' clause and
  391. therefore should appear in the output as the first conditional test
  392. failed. The 'endif' is, of course, still required.
  393. #endif
  394. #ifndef debug
  395. #else
  396. this section of text should be print as the first conditional failed
  397. and this segment follows an 'else' clause.
  398. #endif
  399. #ifdef debug
  400. One final test with 'else'... confirming that if the test is
  401. successful then the text following the 'else' clause will not be printed
  402. #else
  403. #endif
  404.  
  405. The reader should note that text may be inserted following the LICC syntactic constructs and that 
  406. these are just passed through by LICC, thus providing an ability to insert comments concerning the 
  407. specific condition being tested.
  408.  
  409.  
  410. Summary
  411.  
  412. Using AWK, it was possible to generate an "inexpensive" language independent conditional 
  413. compilation tool that could be used to select (or de-select) segments of code for consideration by a 
  414. compiler.  The only requirement is that the compiler have some sort of comment syntax (to support 
  415. hiding the LICC syntax from the compiler) and that AWK or an AWK-like language be available.  
  416. The total processing overhead required to support the various language syntax semantics was only 
  417. about 2.5 sec for 1000 lines of code (Sun 3/160).
  418. {et
  419.