;$Header: /usr/local/cvs/www.xmeta.net/omlette/WebContent/doug/xompre.xom,v 1.1 2006/08/17 14:25:34 geimer Exp $ ; ; File Name: ; xompre.xom ; ; Syntax: ; omnimark | omle ; -s xompre.xom ; ; -of ; [-submit ]* ; -brief ; ; Description: ; This omnimark application is a preprocessor for omnimark source files that ; supports a subset of the C preprocessor directives to perform conditional ; compilation. ; ; Before running or compiling a source file with omnimark, run it through ; the preprocessor to create a single input file for omnimark with all ; directives resolved and include files included. ; ; $Author: geimer $ ; ; $Log: xompre.xom,v $ ;$Header: $ ; ; File Name: ; xompre.xom ; ; Syntax: ; omnimark | omle ; -s xompre.xom ; ; -of ; [-submit ]* ; -brief ; ; Description: ; This omnimark application is a preprocessor for omnimark source files that ; supports a subset of the C preprocessor directives to perform conditional ; compilation. ; ; Before running or compiling a source file with omnimark, run it through ; the preprocessor to create a single input file for omnimark with all ; directives resolved and include files included. ; ; $Author: $ ; ; Revision 1.1 2006/08/17 14:25:34 geimer ;$Header: $ ; ; File Name: ; xompre.xom ; ; Syntax: ; omnimark | omle ; -s xompre.xom ; ; -of ; [-submit ]* ; -brief ; ; Description: ; This omnimark application is a preprocessor for omnimark source files that ; supports a subset of the C preprocessor directives to perform conditional ; compilation. ; ; Before running or compiling a source file with omnimark, run it through ; the preprocessor to create a single input file for omnimark with all ; directives resolved and include files included. ; ; $Author: $ ; ; Moved omlette from xmeta.com to xmeta.net. ;$Header: $ ; ; File Name: ; xompre.xom ; ; Syntax: ; omnimark | omle ; -s xompre.xom ; ; -of ; [-submit ]* ; -brief ; ; Description: ; This omnimark application is a preprocessor for omnimark source files that ; supports a subset of the C preprocessor directives to perform conditional ; compilation. ; ; Before running or compiling a source file with omnimark, run it through ; the preprocessor to create a single input file for omnimark with all ; directives resolved and include files included. ; ; $Author: $ ; ; ; ;------------------------------------------------------------------------------- CROSS-TRANSLATE ;=============================================================================== ; static library includes ;=============================================================================== INCLUDE "xomc.xin" ;=============================================================================== ; global variable declarations ;=============================================================================== ; following stream contains defined constants from the command line GLOBAL STREAM D SIZE 1 ; following stream used to contain include paths from the command line GLOBAL STREAM I SIZE 1 GLOBAL STREAM gsConstants VARIABLE INITIAL-SIZE 0 GLOBAL STREAM gsIncludePaths VARIABLE INITIAL-SIZE 0 ; count of input lines processed. ; multi-item to support maintaining correct line counts across nested ; include files. GLOBAL COUNTER gctLines VARIABLE INITIAL-SIZE 0 ; switch used to determine whether to output the lines within a ; part of a directive. multi-item to support nested directives. GLOBAL SWITCH gbIgnoreLine VARIABLE INITIAL-SIZE 1 INITIAL { FALSE } GLOBAL SWITCH gbPrevLineIsComment SIZE 1 INITIAL { FALSE } GLOBAL STREAM gsProjectName SIZE 1 INITIAL { "" } ;=============================================================================== ; functions ;=============================================================================== DEFINE ; @METAGS Msg FUNCTION Msg ( VALUE STREAM asStr ) ;--------------------------------------------------------------------------- ; Description: ; this function outputs a simple message string. It is used mostly ; for progress/debug information but currently is writing to #SUPPRESS ; to hide these messages. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body PUT (#SUPPRESS) ";" || asStr || "%n" ;--------------------------------------------------------------------------- ; end of function Msg() ;--------------------------------------------------------------------------- DEFINE ; @METAGS SetIgnoreSwitch FUNCTION SetIgnoreSwitch ( VALUE SWITCH abDefValue ) ;--------------------------------------------------------------------------- ; Description: ; this function is called anytime the switch bIgnoreLine, needs to be set ; due to a 'nested' scope. It allows nested directives to be processed ; and output, or not, correctly. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body NEW gbIgnoreLine DO WHEN abDefValue = TRUE SET gbIgnoreLine LASTMOST TO abDefValue ELSE SET gbIgnoreLine LASTMOST TO gbIgnoreLine ITEM (NUMBER OF gbIgnoreLine - 1 ) DONE DO WHEN gbIgnoreLIne LASTMOST Msg("gbIgnoreLine = TRUE") ELSE Msg("gbIgnoreLine = TRUE") DONE ;--------------------------------------------------------------------------- ; end of function SetIgnoreSwitch() ;--------------------------------------------------------------------------- DEFINE ; @METAGS ClearIgnoreSwitch FUNCTION ClearIgnoreSwitch() ;--------------------------------------------------------------------------- ; Description: ; this function is called when leaving a directive scope that ends the ; current ignore setting. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body REMOVE gbIgnoreLine LASTMOST ;--------------------------------------------------------------------------- ; end of function ClearIgnoreSwitch() ;--------------------------------------------------------------------------- DEFINE ; @METAGS SetIncludePaths FUNCTION SetIncludePaths ( MODIFIABLE STREAM asPaths ) ;--------------------------------------------------------------------------- ; Description: ; this function parses the stream I as defined on the command line ; to find each individual include directory and create an item ; on the modifiable stream argument for each directory ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body DO WHEN I IS ATTACHED REPEAT SCAN I MATCH ;------------------------------------- ; TODO ; the colon in the following pattern ; is the OS directory separator. ; ; colon for most UNIX OS ; semicolon for Windows ; ; should remove the hard coded character ; and replace with a stream determined ; at run-time. ;------------------------------------- [ANY-TEXT EXCEPT ":"]+ => pIncludePath SET NEW asPaths TO pIncludePath MATCH ANY AGAIN DONE ;--------------------------------------------------------------------------- ; end of function SetIncludePaths() ;--------------------------------------------------------------------------- DEFINE ; @METAGS GetIncludePath SWITCH FUNCTION GetIncludePath ( READ-ONLY STREAM asIncludePaths, VALUE STREAM asFileName, MODIFIABLE STREAM asFoundPath ) ;--------------------------------------------------------------------------- ; Description: ; this function tries to find a file within the include directories and ; sets the modifiable argument to the path if the file is found. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body CLEAR asFoundPath REPEAT OVER asIncludePaths DO WHEN FILE "%g(asIncludePaths)%g(asFileName)" IS FILE SET NEW asFoundPath TO asIncludePaths ITEM #ITEM RETURN TRUE ELSE WHEN FILE "%g(asIncludePaths)/%g(asFileName)" IS FILE SET NEW asFoundPath TO asIncludePaths ITEM #ITEM || "/" RETURN TRUE DONE AGAIN RETURN FALSE ;--------------------------------------------------------------------------- ; end of function GetIncludePath() ;--------------------------------------------------------------------------- DEFINE ; @METAGS SetConstants FUNCTION SetConstants ( MODIFIABLE STREAM asConsts ) ;--------------------------------------------------------------------------- ; Description: ; this function is called to store all the constants defined on the ; command line. ; ; Each constant is stores as the key of the shelf argument passed by the ; caller. If a value for the constant was also specified on the command ; line, that value becomes the value of the shelf item. The default ; value is 1. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body DO WHEN D IS ATTACHED REPEAT SCAN D MATCH [ANY-TEXT EXCEPT " ="]+ => pConstName [" ="]+ [ANY-TEXT EXCEPT ";"]+ => pConstVal SET NEW asConsts KEY pConstName TO pConstVal MATCH [ANY-TEXT EXCEPT ";"]+ => pConstName SET NEW asConsts KEY pConstName TO "1" MATCH ANY AGAIN DONE ;--------------------------------------------------------------------------- ; end of function SetConstants() ;--------------------------------------------------------------------------- DEFINE ; @METAGS IfDefined SWITCH FUNCTION IfDefined ( VALUE STREAM asConstName, READ-ONLY STREAM asConsts ) ;--------------------------------------------------------------------------- ; Description: ; this function returns TRUE when the constant name argument is a defined ; constant on the constants shelf. Otherwise, FALSE is returned. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body DO WHEN asConsts HAS KEY asConstName RETURN TRUE ELSE RETURN FALSE DONE ;--------------------------------------------------------------------------- ; end of function IfDefined() ;--------------------------------------------------------------------------- DEFINE ; @METAGS OutputLineDirective FUNCTION OutputLineDirective() ;--------------------------------------------------------------------------- ; Description: ; this function outputs a #line directive to the current output file. ; ; #line directives are used by the post processor to reconcile line ; numbers and file names in any Omnimark error messages. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body ; OUTPUT the #line directive with the line number and name of the ; current file. USING gctLines LASTMOST OUTPUT ";#line %d(gctLines), " || KEY OF gctLines || "%n" ;--------------------------------------------------------------------------- ; end of function OutputLineDirective() ;--------------------------------------------------------------------------- DEFINE ; @METAGS NewFileDirective FUNCTION NewFileDirective ( VALUE STREAM asFileName ) ;--------------------------------------------------------------------------- ; Description: ; this function creates a new filename/line number 'scope' for keeping ; track of the include file and line number while that file is being ; processed. ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body ; allocate a new file scope and set the line number to 1 SET NEW gctLines KEY asFileName TO 1 ; OUTPUT a #line directive for the new file scope OutputLineDirective() ; make sure that the first line of the file isn't ignored, even if ; the last line processed was a comment. SET gbIgnoreLine TO FALSE ;--------------------------------------------------------------------------- ; end of function NewFileDirective() ;--------------------------------------------------------------------------- DEFINE ; @METAGS EOFDirective FUNCTION EOFDirective() ;--------------------------------------------------------------------------- ; Description: ; this function ends the current file scope begun by calling the function ; NewFileDirective(). ;--------------------------------------------------------------------------- AS ; local variable declarations ; function body ; OUTPUT a #line directive for the end of the current file scope OutputLineDirective() ; FREE the current file scope REMOVE gctLines LASTMOST ; OUTPUT a #line directive for the file scope being returned to ; IF no file scope exists. ; ; NOTE: No file scope exists at the end of the file scope for the ; master omnimark source file. OutputLineDirective() WHEN NUMBER OF gctLines > 0 ; make sure that the next line of the file isn't ignored, even if ; the last line processed of the previous file scope was a comment. SET gbIgnoreLine TO FALSE ;--------------------------------------------------------------------------- ; end of function EOFDirective() ;--------------------------------------------------------------------------- ;=============================================================================== GROUP #IMPLIED ;=============================================================================== FIND-START ;--------------------------------------------------------------------------- ; Description: ; this rule processes the start of the input file. It performs startup ; initialization of command line constants and initiates the first file ; scope ;--------------------------------------------------------------------------- ; local variable declarations ; rule body PUT #ERROR "building " || gsProjectName || " ...%n" ; store all the constants defined on the command line SetConstants(gsConstants) SetIncludePaths(gsIncludePaths) ; initiate the first file scope. NewFileDirective(#COMMAND-LINE-NAMES LASTMOST) ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND-END ;--------------------------------------------------------------------------- ; Description: ; this rule processes the end of the input document and ends the first ; file scope. ;--------------------------------------------------------------------------- ; local variable declarations ; rule body ; end the first file scope EOFDirective() HALT WITH gnExitVal ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ;--------------------------------------------------------------------------- ; Description: ; this rule matches blank lines in the input document, keeping them from ; being processed for preprocessor directives ;--------------------------------------------------------------------------- ; start of pattern LINE-START BLANK* LINE-END "%n" ; end of pattern ; local variable declarations ; rule body ; OUTPUT a blank line so that line counts in the output docuement are ; accurate OUTPUT "%n" ; keep track of the number of lines read from the input file. INCREMENT gctLines LASTMOST ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ;--------------------------------------------------------------------------- ; Description: ; this rule processes comment only line and keeps them from being ; processed for preprocessor directives. ;--------------------------------------------------------------------------- ; start of pattern LINE-START WHITE-SPACE* ";" ANY-TEXT* LINE-END "%n" ; end of pattern ; local variable declarations ; rule body ; OUTPUT a blank line OUTPUT "%n" ; keep track of the number of lines read INCREMENT gctLines LASTMOST ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ;--------------------------------------------------------------------------- ; Description: ; this rule processes non-blank/non-comment lines in the input document ; and passes each one on to preprocessor directive processing. ;--------------------------------------------------------------------------- ; start of pattern (LINE-START ANY-TEXT+ LINE-END "%n") => pLineOfText ; end of pattern ; local variable declarations ; rule body ; IF the previous line was a comment DO WHEN gbPrevLineIsComment ; OUTPUT a #line directive OutputLineDirective() WHEN !gbIgnoreLine ; SET gbPrevLineIsComment TO FALSE DONE ; send the current line of for directive processing USING GROUP ProcessLine SUBMIT pLineOfText ; keep track of the number of lines read INCREMENT gctLines LASTMOST ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- ;=============================================================================== GROUP ProcessLine ;=============================================================================== FIND ; #define ;--------------------------------------------------------------------------- ; Description: ; this rule matches the #define directive and adds the named constant ; to the list of defined constants. ;--------------------------------------------------------------------------- ; start of pattern WHITE-SPACE* "#define" SPACE+ [ANY-TEXT EXCEPT " "]+ => pConstToDefine WHITE-SPACE* ; end of pattern ; local variable declarations ; body of rule ; IF the constant being defined isnt already defined DO WHEN gsConstants HASNT KEY pConstToDefine Msg("defining " || pConstToDefine) ; add the new constant to the other defined constants SET NEW gsConstants KEY pConstToDefine TO "1" ELSE Msg(pConstToDefine || " already defined.") DONE ; indiacte to the rest of the program that the previous line read in was ; a comment SET gbPrevLineIsComment TO TRUE ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; #if defined ;--------------------------------------------------------------------------- ; Description: ; this rule matches the #if defined(CONST) directive and checks to see if ; the constant is defined. If it is, the statements within the directive ; will be output. ;--------------------------------------------------------------------------- ; start of pattern WHITE-SPACE* "#if defined(" [ANY-TEXT EXCEPT ")"]+ => pTestConst ")" WHITE-SPACE* ; end of pattern ; local variable declarations ; rule body Msg("#if") Msg("testing if " || pTestConst || " is defined.") ; IF the constant being tested is defined DO WHEN IfDefined(pTestConst, gsConstants) ; THEN do not ignore the conditional lines SetIgnoreSwitch(FALSE) ELSE ; ignore the conditional lines SetIgnoreSwitch(TRUE) DONE ; indiacte to the rest of the program that the previous line read in was ; a comment SET gbPrevLineIsComment TO TRUE ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; #if !defined ;--------------------------------------------------------------------------- ; Description: ; this rule matches the #if !defined(CONST) directive and checks to see if ; the constant is NOT defined. If it is NOT, the statements within the ; directive will be output. ;--------------------------------------------------------------------------- ; start of pattern WHITE-SPACE* "#if !defined(" [ANY-TEXT EXCEPT ")"]+ => pTestConst ")" WHITE-SPACE* ; end of pattern ; local variable declarations ; rule body Msg("testing if " || pTestConst || " is not defined.") ; IF the constant being test is defined DO WHEN IfDefined(pTestConst, gsConstants) ; THEN ignore the conditional lines Msg(".FALSE") SetIgnoreSwitch(TRUE) ELSE ; do NOT ignore the conditional lines Msg(".TRUE") SetIgnoreSwitch(FALSE) DONE ; indiacte to the rest of the program that the previous line read in was ; a comment SET gbPrevLineIsComment TO TRUE ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; #else ;--------------------------------------------------------------------------- ; Description: ; this rule matches the #else directive and switches the ignore logic to ; NOT what it previously was. ;--------------------------------------------------------------------------- ; start of pattern WHITE-SPACE* "#else" WHITE-SPACE* ; end of pattern ; local variable declarations ; rule body Msg("#else") ; indicate to the rest of the program that the previous line read in was ; a comment SET gbPrevLineIsComment TO TRUE ; flip the ignore logic to NOT what it was in the #if part of the directive ; block SET gbIgnoreLine LASTMOST ; NED 3/2/98, 2:15PM -- change start ; modified item selection clause to reverse based on the last item ; instead of the next to last item TO !gbIgnoreLine LASTMOST ;ITEM ( NUMBER OF bIgnoreLine - 1 ) ; NED 3/2/98, 2:16PM -- change end ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; #endif ;--------------------------------------------------------------------------- ; Description: ; this rule matches the #endif directive and ends the current directive ; scope ;--------------------------------------------------------------------------- ; start of pattern WHITE-SPACE* "#endif" WHITE-SPACE* ; end of pattern ; local variable declarations ; rule body Msg("#endif") ; indiacte to the rest of the program that the previous line read in was ; a comment SET gbPrevLineIsComment TO TRUE ClearIgnoreSwitch() ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; INCLUDE ;--------------------------------------------------------------------------- ; Description: ; this rule matches Omnimark's INCLUDE keyword and the names include file. ; A new file scope is created and the file processed. ;--------------------------------------------------------------------------- ; start of pattern WHITE-SPACE* UL"INCLUDE" WHITE-SPACE* "%"" [ANY-TEXT EXCEPT "%""]+ => pInclFile "%"" ; end of pattern ; local variable declarations LOCAL STREAM lsFileFoundPath VARIABLE ; rule body DO WHEN GetIncludePath(gsIncludePaths, pInclFile, lsFileFoundPath) NewFileDirective(pInclFile) USING GROUP #IMPLIED SUBMIT FILE "%g(lsFileFoundPath)%x(pInclFile)" EOFDirective() ELSE USING gctLines LASTMOST DO USING OUTPUT AS #ERROR OutputCompilerErrorMessage(KEY OF gctLines, "%d(gctLines)", "Unable to open file for input. For file '%x(pInclFile)'.") DONE DONE ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; _FILE_ ;--------------------------------------------------------------------------- ; Description: ; this rule matches the _FILE_ macro and substitutes the current file ; name. ;--------------------------------------------------------------------------- ; start of pattern "_FILE_" ; end of pattern ; local variable declarations ; rule body USING gctLines LASTMOST OUTPUT KEY OF gctLines WHEN !gbIgnoreLine ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ; _LINE_ ;--------------------------------------------------------------------------- ; Description: ; this rule matches the _LINE_ preprocessor macro and substitutes the ; current line number. ;--------------------------------------------------------------------------- ; start of pattern "_LINE_" ; end of pattern ; local variable declarations ; rule body USING gctLines LASTMOST OUTPUT "%d(gctLines)" WHEN !gbIgnoreLine ;--------------------------------------------------------------------------- ; end of rule ;--------------------------------------------------------------------------- FIND ;--------------------------------------------------------------------------- ; Description: ; ;--------------------------------------------------------------------------- ; start of pattern ; NED 1/7/98, 4:13PM -- change start ; modified pattern to NOT match entire lines ; this is necessary for support of the _FILE and _LINE preprocessor ; macros which can occur inline. ;(BLANK* ;ANY-TEXT+ ;"%n") => MatchedLine ANY => pMatchedText ; NED 1/7/98, 4:14PM -- change end ; end of pattern ; local variable declarations ; body of rule OUTPUT pMatchedText WHEN !gbIgnoreLine LASTMOST ;--------------------------------------------------------------------------- ; end of rule ;---------------------------------------------------------------------------