home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_GEN / EDSV4064.ZIP / EPP-V1_1.ZIP / EPP.E < prev    next >
Text File  |  1993-06-26  |  25KB  |  825 lines

  1. /*
  2.    EPP V1.1 - E Preprocessor.
  3.    Copyright ⌐1993 Barry Wills.  All rights reserved.
  4. */
  5.  
  6.  
  7.  
  8. PMODULE 'PMODULES:commandLineArgs'
  9. PMODULE 'PMODULES:cSkipWhite'
  10. PMODULE 'PMODULES:cSkipToChar'
  11. PMODULE 'PMODULES:cSkipToEDelim'
  12.  
  13.  
  14.  
  15. /*===  Runtime exceptions.  ==============================================*/
  16.  
  17. ENUM ER_NONE,
  18.      ER_MEM,
  19.      ER_USAGE,
  20.      ER_OPENING_INFILE,
  21.      ER_OPENING_OUTFILE,
  22.      ER_WRITING_FILE,
  23.      ER_SYNTAX
  24.  
  25.  
  26.  
  27. RAISE ER_MEM IF New () = NIL,
  28.       ER_MEM IF List () = NIL,
  29.       ER_MEM IF String () = NIL
  30.  
  31.  
  32.  
  33. /*===  Global constants.  ================================================*/
  34.  
  35. CONST DEFAULT_BUFSIZE         = 128,
  36.       MINIMUM_BUFSIZE         = 20,
  37.       LARGEST_TOKEN_SUPPORTED = 64  /* Largest identifier expected. */
  38.  
  39.  
  40.  
  41. /*===  Object type definitions.  =========================================*/
  42.  
  43. /* V0.13b - My answer to the slow IO problem.  I decided to try an input */
  44. /* juggling act instead of restructuring the program.  String input was  */
  45. /* definitely the bottleneck, so that's where I decided to hack.  See    */
  46. /* function read () for more details.                                    */
  47.  
  48. OBJECT readDataType
  49.   inputBuffer : LONG
  50.   length : LONG        /* num bytes read into inputBuffer    */
  51.   pos : LONG           /* next available char in inputBuffer */
  52.   turboMode : LONG     /* TURBO mode for this module only.   */
  53. ENDOBJECT
  54.  
  55.  
  56.  
  57. /*===  Global variables.  ================================================*/
  58.  
  59.  
  60. DEF defsFileHandle    = NIL,
  61.     procsFileHandle   = NIL,
  62.     procsFileName     = NIL,
  63.     theModuleList     = NIL,
  64.     argMainModuleName = NIL,
  65.     argOutFileName    = NIL,
  66.     argBufSize,
  67.     argLineLength,
  68.     argIncludeComments = FALSE,
  69.     argProgressIsVerbose = TRUE,
  70.     argTurboMode = FALSE,
  71.     nestedCommentCount = 0,
  72.     userBreak = FALSE
  73.  
  74.  
  75. /*===  Main procedures.  =================================================*/
  76.  
  77. PROC getArgs ()
  78.   DEF tempArg [108] : STRING,
  79.       i = 1
  80.  
  81.   IF arg [] = 0 THEN Raise (ER_USAGE)
  82.  
  83.   argBufSize := DEFAULT_BUFSIZE
  84.   argLineLength := DEFAULT_BUFSIZE
  85.  
  86.   WHILE getArg (tempArg, i++)
  87.  
  88.     IF (tempArg [0] = "-")
  89.       /* Get switch. */
  90.       IF tempArg [1] = "b"  /* File buffer size. */
  91.         MidStr (tempArg, tempArg, 2, ALL)
  92.         IF (argBufSize := Val (tempArg, NIL)) = -1 THEN Raise (ER_USAGE)
  93.         IF (argBufSize < MINIMUM_BUFSIZE)
  94.           WriteF ('\n ╖╖╖ Using mimimum buffer size \d', MINIMUM_BUFSIZE)
  95.           argBufSize := MINIMUM_BUFSIZE
  96.         ENDIF
  97.       ELSEIF tempArg [1] = "l"  /* Line length. */
  98.         MidStr (tempArg, tempArg, 2, ALL)
  99.         IF (argLineLength := Val (tempArg, NIL)) = -1 THEN Raise (ER_USAGE)
  100.         IF (argLineLength < MINIMUM_BUFSIZE)
  101.           WriteF ('\n ╖╖╖ Using mimimum line length \d', MINIMUM_BUFSIZE)
  102.           argLineLength := MINIMUM_BUFSIZE
  103.         ENDIF
  104.       ELSEIF tempArg [1] = "s"  /* Progress messages. */
  105.         argProgressIsVerbose := FALSE
  106.       ELSEIF tempArg [1] = "c"  /* Include comments in output source. */
  107.         argIncludeComments := TRUE
  108.       ELSEIF tempArg [1] = "t"  /* Turbo proc copy mode. */
  109.         argTurboMode := TRUE
  110.       ENDIF
  111.     ELSEIF argMainModuleName = NIL
  112.       /* Not a switch:  get main input file name. */
  113.       argMainModuleName := String (StrLen (arg))
  114.       StrCopy (argMainModuleName, tempArg, ALL)
  115.     ELSEIF argOutFileName = NIL
  116.       /* Not a switch, already got main input file name: */
  117.       /* get output file name.                           */
  118.       argOutFileName := String (StrLen (arg))
  119.       StrCopy (argOutFileName, tempArg, ALL)
  120.     ELSE
  121.       /* Not a switch, already got main input file  */
  122.       /* name and output file name:  too many args. */
  123.       Raise (ER_USAGE)
  124.     ENDIF
  125.   ENDWHILE
  126.  
  127.   /* Must have these two args as a minimum. */
  128.   IF (argMainModuleName = NIL) OR
  129.      (argOutFileName = NIL) THEN Raise (ER_USAGE)
  130.  
  131. ENDPROC
  132.   /* getArgs */
  133.  
  134.  
  135.  
  136. PROC buildEProgramName (theFileName)
  137.   DEF length,
  138.       fileExtension [2] : STRING
  139.   length := StrLen (theFileName)
  140.   MidStr (fileExtension, theFileName, length - 2, 2)
  141.   LowerStr (fileExtension)
  142.   IF StrCmp (fileExtension, '.e', ALL) = FALSE THEN length := length + 2
  143.   IF StrCmp (fileExtension, '.e', ALL) = FALSE
  144.     StrAdd (theFileName, '.e', ALL)
  145.   ENDIF
  146. ENDPROC
  147.   /* buildEProgramName */
  148.  
  149.  
  150.  
  151. PROC isInList (theModuleName, theList)
  152.   DEF moduleName
  153.   IF (moduleName := theList) = NIL THEN RETURN FALSE
  154.   REPEAT
  155.     IF StrCmp (moduleName, theModuleName, ALL) THEN RETURN TRUE
  156.   UNTIL (moduleName := Next (moduleName)) = NIL
  157. ENDPROC  FALSE
  158.   /* isInList */
  159.  
  160.  
  161.  
  162. PROC continueGettingComments (pos)
  163.   DEF p : PTR TO CHAR
  164.   p := ^pos
  165.  
  166.   WHILE (p [] > 0) AND (nestedCommentCount)
  167.     IF (p [] = "*")    /* Closing comment? */
  168.       IF (p [1] = "/")
  169.         DEC nestedCommentCount
  170.         INC p
  171.       ENDIF
  172.     ELSEIF (p [] = "/")    /* Nested comment? */
  173.       IF (p [1] = "*")
  174.         INC nestedCommentCount
  175.         INC p
  176.       ENDIF
  177.     ENDIF
  178.     INC p
  179.   ENDWHILE
  180.  
  181.   ^pos := p
  182. ENDPROC
  183.  
  184.  
  185.  
  186. PROC checkEnclosing (pos, c)
  187.   DEF p : PTR TO CHAR
  188.   p := ^pos
  189.  
  190.   /* If found an open quote, "'", or comment, search for it's partner */
  191.  
  192.   IF c = "'"
  193.     REPEAT
  194.       INC p
  195.     UNTIL ((p [] = 0) OR (p [] = "'"))
  196.   ELSEIF c = 34  /* QUOTE */
  197.     REPEAT
  198.       INC p
  199.     UNTIL ((p [] = 0) OR (p [] = 34))
  200.   ELSEIF c = "/"
  201.     IF (p [1] = "*")
  202.       p := p + 2
  203.       INC nestedCommentCount
  204.       continueGettingComments ({p})
  205.     ENDIF
  206.   ELSEIF c = "*"
  207.     IF (p [1] = "/")
  208.       p := p + 2
  209.       DEC nestedCommentCount
  210.       continueGettingComments ({p})
  211.     ENDIF
  212.   ENDIF
  213.  
  214.   ^pos := p
  215. ENDPROC
  216.   /* checkEnclosing */
  217.  
  218.  
  219.  
  220. PROC skipToValidToken (pos : PTR TO CHAR,
  221.                        proc_endproc)
  222.   DEF c
  223.  
  224.   /* Finds the next important E keyword in theString and returns its pos. */
  225.   /* Thanks to Son Le for coming up with this routine to speed up the     */
  226.   /* parsing!                                                             */
  227.  
  228.   /* Nested comments?  If so, find closing comment brackets. */
  229.   IF nestedCommentCount THEN continueGettingComments ({pos})
  230.  
  231.   WHILE (c := pos [])
  232.     SELECT c
  233.       CASE "'"
  234.         checkEnclosing ({pos}, c)
  235.       CASE 34  /* Quote */
  236.         checkEnclosing ({pos}, c)
  237.       CASE "/"
  238.         checkEnclosing ({pos}, c)
  239.       CASE "*"
  240.         checkEnclosing ({pos}, c)
  241.  
  242.       CASE "P"  /* PROC or PMODULE */
  243.         IF ((pos [1] = "R") OR
  244.             (pos [1] = "M")) THEN RETURN pos
  245.  
  246.       CASE "E"  /* ENDPROC or ENUM */
  247.         IF (pos [1] = "N") THEN RETURN pos
  248.     ENDSELECT
  249.     IF (proc_endproc = FALSE)   /* if proc_endproc is true, we search only */
  250.       SELECT c                  /* for the keywords PROC or ENDPROC (ENUM) */
  251.  
  252.         CASE "O"  /* OPT or OBJECT */
  253.           IF ((pos [1] = "P") OR (pos [1] = "B")) THEN RETURN pos
  254.  
  255.         CASE "R"  /* RAISE */
  256.           IF (pos [1] = "A") THEN RETURN pos
  257.  
  258.         CASE "C"  /* CONST */
  259.           IF (pos [1] = "O") THEN RETURN pos
  260.  
  261.         CASE "D"  /* DEF */
  262.           IF (pos [1] = "E") THEN RETURN pos
  263.       ENDSELECT
  264.     ENDIF
  265.     IF pos [] THEN INC pos
  266.   ENDWHILE
  267. ENDPROC  pos
  268.   /* skipToValidToken */
  269.  
  270.  
  271.  
  272. PROC getToken (token, startPos : PTR TO CHAR)
  273.   /*  Get an E token and place it in token. */
  274.   DEF endPos
  275.   startPos := cSkipWhite (startPos)
  276.   endPos := (cSkipToEDelim (startPos) - startPos)
  277.   MidStr (token, startPos, 0, endPos)
  278. ENDPROC  (startPos + endPos)
  279.   /* getToken */
  280.  
  281.  
  282.  
  283. PROC getModuleName (name, startPos)
  284.   /* Look for a module name ('identifier') and place it in name. */
  285.   DEF endPos
  286.  
  287.   ^startPos := cSkipToChar ("'", ^startPos)
  288.   IF Char (^startPos) <> "'" THEN Raise (ER_SYNTAX)
  289.   ^startPos := ^startPos + 1
  290.  
  291.   endPos := (cSkipToChar ("'", ^startPos) - ^startPos)
  292.   IF Char (^startPos + endPos) = "'"
  293.     MidStr (name, ^startPos, 0, endPos)
  294.     StrAdd (name, '.e', ALL)
  295.     ^startPos := ^startPos + endPos
  296.   ELSE
  297.     ^startPos := ^startPos + endPos
  298.     Raise (ER_SYNTAX)
  299.   ENDIF
  300. ENDPROC
  301.   /* getModuleName */
  302.  
  303.  
  304.  
  305. PROC read (fileHandle,
  306.            rData : PTR TO readDataType,
  307.            sourceLine)
  308.   DEF inputBuffer,
  309.       i = 0,
  310.       pos
  311.  
  312.   inputBuffer := rData.inputBuffer
  313.   pos := rData.pos
  314.  
  315.   WHILE i < argBufSize
  316.     IF pos >= rData.length
  317.       rData.length := Read (fileHandle, inputBuffer, argBufSize)
  318.       IF rData.length < 1
  319.           IF i > 0
  320.             sourceLine [i++] := 10
  321.             sourceLine [i] := 0
  322.             rData.length := i
  323.           ENDIF
  324.           rData.pos := rData.length
  325.           RETURN i
  326.       ENDIF
  327.       pos := 0
  328.     ENDIF
  329.     sourceLine [i] := inputBuffer [pos++]
  330.     IF (sourceLine [i++] = 10) OR  /* LF */
  331.        (i = argLineLength)
  332.       SetStr (sourceLine, i)
  333.       rData.pos := pos
  334.       RETURN i
  335.     ENDIF
  336.   ENDWHILE
  337.  
  338. ENDPROC
  339.   /* read */
  340.  
  341.  
  342.  
  343. PROC copyProc (rData : PTR TO readDataType,  /* Input file buffer info. */
  344.                sourceLine,         /* The current source line being considered. */
  345.                inFileHandle,       /* Which file to continue reading from. */
  346.                moduleName,         /* The name of the module for inclusion in comments. */
  347.                tempStr,            /* Why allocate another work variable? */
  348.                currentPos,         /* Where sourceLine examination is to continue. */
  349.                length,             /* Length of current sourceLine. */
  350.                lineCount)          /* Keep track in case an error occurs. */
  351.  
  352.   /* Copy until uncommented ENDPROC is found.  When this procedure is */
  353.   /* called we are never within a comment.                            */
  354.  
  355.   DEF keywordENDPROCfound,  /* Indicates we found the whole proc. */
  356.       bytesRead,            /* used only in Turbo mode. */
  357.       pos : PTR TO CHAR
  358.  
  359.   /* Comments to EPP code. */
  360.   IF argIncludeComments
  361.     Write (procsFileHandle, '\n/*** Procedure included from ', 30)
  362.     Write (procsFileHandle, moduleName, StrLen (moduleName))
  363.     Write (procsFileHandle, ' ***/\n', 6)
  364.   ENDIF
  365.  
  366.   IF (argTurboMode) OR
  367.      (rData.turboMode)
  368.     /* Status indication to stdout. */
  369.     IF argProgressIsVerbose
  370.       WriteF ('\n     Turbo module:  \a\s\a', moduleName)
  371.     ENDIF
  372.     IF argIncludeComments
  373.       Write (procsFileHandle, '/*** Turbo dump of module ', 26)
  374.       Write (procsFileHandle, moduleName, StrLen (moduleName))
  375.       Write (procsFileHandle, ' ***/\n', 6)
  376.     ENDIF
  377.     Write (procsFileHandle, sourceLine, ^length)
  378.     /* Empty the inputBuffer before trying to read again. */
  379.     IF (rData.pos < rData.length)
  380.       bytesRead := rData.length - rData.pos
  381.       IF Write (procsFileHandle,
  382.                 (rData.inputBuffer + rData.pos),
  383.                 bytesRead) < bytesRead THEN Raise (ER_WRITING_FILE)
  384.     ENDIF
  385.     WHILE (bytesRead := Read (inFileHandle, rData.inputBuffer, argBufSize)) > 0
  386.       IF Write (procsFileHandle, rData.inputBuffer,
  387.                 bytesRead) < bytesRead THEN Raise (ER_WRITING_FILE)
  388.     ENDWHILE
  389.     StrCopy (sourceLine, '', ALL)
  390.     ^length := 0
  391.     ^currentPos := sourceLine
  392.     rData.length := 0
  393.     rData.pos := 0
  394.     RETURN
  395.   ENDIF
  396.  
  397.   /* Omit PROC main () in all but main module. */
  398.   ^currentPos := getToken (tempStr, ^currentPos)
  399.  
  400.   IF StrCmp (tempStr, 'main', ALL)
  401.     IF StrCmp (moduleName, argMainModuleName, ALL) = FALSE
  402.       IF argIncludeComments THEN Write (procsFileHandle,
  403.                                         '/*** Superfluous PROC main () '+
  404.                                         'omitted ***/\n\n', 44)
  405.       WHILE Read (inFileHandle, rData.inputBuffer, argBufSize) > 0 DO NOP
  406.       SetStr (sourceLine, 0)
  407.       ^currentPos := sourceLine
  408.       ^length := 0
  409.       rData.pos := 0
  410.       rData.length := 0
  411.       RETURN
  412.     ENDIF
  413.   ENDIF
  414.  
  415.   /* Status indication to stdout. */
  416.   IF argProgressIsVerbose THEN WriteF ('\n     Copying proc:  \a\s\a', tempStr)
  417.  
  418.   pos := ^currentPos
  419.  
  420.   LOOP  /* Only ways out of this loop are:               */
  421.         /*  1. RETURN when uncommented ENDPROC is found, */
  422.         /*  2. Encounter another PROC statement,         */
  423.         /*  3. Encounter end of file.                    */
  424.  
  425.     keywordENDPROCfound := FALSE
  426.  
  427.     WHILE pos []
  428.  
  429.       /* Quick skip to valid character, instead of parsing each char */
  430.       pos := skipToValidToken (pos, TRUE)
  431.  
  432.       IF (pos [] = "E")
  433.         /* Could be we found ENDPROC. */
  434.         pos := getToken (tempStr, pos)
  435.         IF StrCmp (tempStr, 'ENDPROC', ALL) THEN keywordENDPROCfound := TRUE
  436.       ELSEIF (pos [] = "P")
  437.         /* Could be we found another PROC (this was a one-liner). */
  438.         pos := getToken (tempStr, pos)
  439.         IF StrCmp (tempStr, 'PROC', ALL)
  440.           copyProc (rData, sourceLine, inFileHandle, moduleName,
  441.                     tempStr, {pos}, length, lineCount)
  442.           StrCopy (sourceLine, '', ALL)  /* sourceLine comes back with 'ENDPROC'  */
  443.           ^length := 0                   /* in it.  Don't want to write it again. */
  444.           pos := sourceLine
  445.           keywordENDPROCfound := TRUE
  446.         ENDIF
  447.       ENDIF
  448.  
  449.       IF pos [] THEN INC pos
  450.     ENDWHILE
  451.  
  452.     IF Write (procsFileHandle, sourceLine, ^length) < 0 THEN Raise (ER_WRITING_FILE)
  453.  
  454.     IF keywordENDPROCfound
  455.       ^currentPos := pos
  456.       RETURN
  457.     ENDIF
  458.  
  459.     ^length := read (inFileHandle, rData, sourceLine)
  460.     IF ^length < 1
  461.       /* If EOF and ENDPROC not found, we have to assume one-line PROC */
  462.       /* transcending more than one line.  There is no way to enforce  */
  463.       /* ENDPROC without doing full-blown syntax checking.             */
  464.       ^currentPos := pos
  465.       RETURN
  466.     ENDIF
  467.  
  468.     /*^length := StrLen (sourceLine)*/
  469.     pos := sourceLine
  470.     ^lineCount := ^lineCount + 1
  471.   ENDLOOP
  472.  
  473. ENDPROC
  474.   /* copyProc */
  475.  
  476.  
  477.  
  478. PROC copyDefs (rData : PTR TO readDataType,  /* Input file buffer info. */
  479.                sourceLine,         /* The current source line being considered. */
  480.                inFileHandle,       /* Which file to continue reading from. */
  481.                moduleName,         /* The name of the module for inclusion in comments. */
  482.                tempStr,            /* Why allocate another work variable? */
  483.                currentPos,         /* Where sourceLine examination is to continue. */
  484.                length,             /* Length of current sourceLine. */
  485.                lineCount,          /* Keep track in case an error occurs. */
  486.                lastFileWrittenTo)  /* Must change if we need to make a call to copyProcs. */
  487.  
  488.   /* Copy until a PROC keyword or eof is encountered. */
  489.  
  490.   DEF pos : PTR TO CHAR
  491.  
  492.   /* Status indication to stdout. */
  493.   IF argProgressIsVerbose THEN WriteF ('\n     Copying defs:  \a\s\a', moduleName)
  494.  
  495.   /* Comment EPP code. */
  496.   IF argIncludeComments
  497.     Write (defsFileHandle, '/*** Declarations included from ', 32)
  498.     Write (defsFileHandle, moduleName, StrLen (moduleName))
  499.     Write (defsFileHandle, ' ***/\n', 6)
  500.   ENDIF
  501.  
  502.   pos := ^currentPos
  503.  
  504.   LOOP  /* Only two ways out of this loop:             */
  505.         /*  1.  RETURN when uncommented PROC is found, */
  506.         /*  2.  End of file is encountered.            */
  507.  
  508.     WHILE pos []
  509.  
  510.       /* Quick skip to valid character, instead of parsing each one */
  511.       pos := skipToValidToken (pos, TRUE)
  512.  
  513.       IF (pos [] = "P")
  514.         /* See if we found beginning of PROC section. */
  515.         pos := getToken (tempStr, pos)
  516.         IF StrCmp (tempStr, 'PROC', ALL)
  517.           ^lastFileWrittenTo := procsFileHandle
  518.           copyProc (rData, sourceLine, inFileHandle, moduleName,
  519.                     tempStr, {pos}, length, lineCount)
  520.           ^currentPos := pos
  521.           RETURN
  522.         ELSEIF StrCmp (tempStr, 'PMODULE', ALL)
  523.           getModuleName (tempStr, {pos})
  524.           getModule (tempStr)
  525.           /* Shorten sourceLine to get rid of the PMODULE statement. */
  526.           MidStr (sourceLine, (pos + 1), 0, ALL)
  527.           ^length := StrLen (sourceLine)
  528.           pos := sourceLine
  529.           IF ^length = 2
  530.             IF StrCmp (sourceLine, ';\n', ALL) THEN SetStr (sourceLine, 0)
  531.             ^length := 0
  532.           ENDIF
  533.         ENDIF
  534.       ELSEIF (pos [] = "O")
  535.         /* See if we found OPT TURBO option. */
  536.         pos := getToken (tempStr, pos)
  537.         IF StrCmp (tempStr, 'OPT', ALL)
  538.           pos := getToken (tempStr, pos)
  539.           IF StrCmp (tempStr, 'TURBO', ALL)
  540.             rData.turboMode := TRUE
  541.             StrCopy (sourceLine, pos, ALL)
  542.             length := StrLen (sourceLine)
  543.             pos := sourceLine
  544.           ENDIF
  545.         ENDIF
  546.       ENDIF
  547.  
  548.       IF pos [] THEN INC pos
  549.     ENDWHILE
  550.  
  551.     IF Write (defsFileHandle, sourceLine, ^length) < 0 THEN Raise (ER_WRITING_FILE)
  552.  
  553.     ^length := read (inFileHandle, rData, sourceLine)
  554.     IF ^length < 1
  555.       ^currentPos := pos
  556.       RETURN
  557.     ENDIF
  558.  
  559.     /*^length := StrLen (sourceLine)*/
  560.     pos := sourceLine
  561.     ^lineCount := ^lineCount + 1
  562.  
  563.   ENDLOOP
  564.  
  565. ENDPROC
  566.   /* copyDefs */
  567.  
  568.  
  569.  
  570. PROC getModule (theModuleName) HANDLE
  571.   DEF rData : PTR TO readDataType,  /* File buffer info used for theModuleName. */
  572.       sourceLine,            /* The line of source; output buffer. */
  573.       fileHandle = NIL,      /* File handle used for theModuleName. */
  574.       lastFileWrittenTo,     /* Where to put a line that doesn't need to be handled specifically. */
  575.       length,                /* StrLen (sourceLine) for speed in repetitious access. */
  576.       pos : PTR TO CHAR,     /* Current character index. */
  577.       lineCount = 0,         /* Count line number for use in error message. */
  578.       tempStr,               /* Holds a string value for comparison, etc. */
  579.       lineRecognized,        /* Flag indicates if the line has been handled       */
  580.                              /*  specifically or if it still needs to be written. */
  581.       doCopyDefs             /* Controls logic based on OPT keyword. */
  582.  
  583.   IF isInList (theModuleName, theModuleList)
  584.     IF argProgressIsVerbose THEN WriteF ('\n ╖╖╖ Duplicate definition omitted:  \a\s\a', theModuleName)
  585.     RETURN
  586.   ELSE
  587.     IF argProgressIsVerbose
  588.       WriteF ('\n ╗╗╗ \s   \a\s\a',
  589.               IF StrCmp (theModuleName,
  590.                          argMainModuleName, ALL) THEN 'Main module:' ELSE 'New module: ',
  591.               theModuleName)
  592.     ENDIF
  593.     tempStr := String (StrLen (theModuleName))
  594.     StrCopy (tempStr, theModuleName, ALL)
  595.     theModuleList := Link (tempStr, theModuleList)
  596.   ENDIF
  597.  
  598.   fileHandle := Open (theModuleName, OLDFILE)
  599.   IF fileHandle = NIL THEN Raise (ER_OPENING_INFILE)
  600.  
  601.   rData := New (SIZEOF readDataType)
  602.   rData.inputBuffer := String (argBufSize)
  603.   rData.pos := 0
  604.   rData.length := 0
  605.   rData.turboMode := FALSE
  606.   sourceLine := String (argLineLength)
  607.   tempStr := String (LARGEST_TOKEN_SUPPORTED)
  608.   lastFileWrittenTo := defsFileHandle  /* Where to put a line that doesn't */
  609.                                        /* need to be handled specifically. */
  610.  
  611.   /* Keep reading until module keyword not found or eof. */
  612.   WHILE (length := read (fileHandle, rData, sourceLine)) > 0 AND
  613.         (userBreak = FALSE)
  614.  
  615.     /* Initialize substring search. */
  616.     /*length := StrLen (sourceLine)*/
  617.     pos := sourceLine
  618.     INC lineCount
  619.     lineRecognized := FALSE
  620.  
  621.     WHILE pos []  /* Until end of line. */
  622.  
  623.       /* Quick skip to valid character, instead of parsing each one */
  624.       pos := skipToValidToken (pos, FALSE)
  625.  
  626.       IF pos [] = "P"
  627.         pos := getToken (tempStr, pos)
  628.         IF StrCmp (tempStr, 'PMODULE', ALL)
  629.           getModuleName (tempStr, {pos})
  630.           getModule (tempStr)
  631.           /* Shorten sourceLine to get rid of the PMODULE statement. */
  632.           MidStr (sourceLine, (pos + 1), 0, ALL)
  633.           length := StrLen (sourceLine)
  634.           pos := sourceLine
  635.           IF length = 2
  636.             IF StrCmp (sourceLine, ';\n', ALL) THEN SetStr (sourceLine, 0)
  637.             length := 0
  638.           ENDIF
  639.         ELSEIF StrCmp (tempStr, 'PROC', ALL)
  640.           lastFileWrittenTo := procsFileHandle
  641.           copyProc (rData, sourceLine, fileHandle, theModuleName,
  642.                     tempStr, {pos}, {length}, {lineCount})
  643.           lineRecognized := TRUE
  644.         ENDIF
  645.       ELSEIF ((pos [] = "O") OR
  646.               (pos [] = "D") OR
  647.               (pos [] = "C") OR
  648.               (pos [] = "E") OR
  649.               (pos [] = "R"))
  650.         pos := getToken (tempStr, pos)
  651.         IF InStr ('OPT OBJECT DEF CONST ENUM RAISE', tempStr, NIL) > -1
  652.           doCopyDefs := TRUE  /* Controls the following IF block. */
  653.           IF StrCmp (tempStr, 'OPT', ALL)
  654.             doCopyDefs := FALSE
  655.             /* See if we found OPT TURBO option. */
  656.             pos := getToken (tempStr, pos)
  657.             IF StrCmp (tempStr, 'TURBO', ALL)
  658.               rData.turboMode := TRUE
  659.               StrCopy (sourceLine, pos, ALL)  /* Remove OPT TURBO statement. */
  660.               length := StrLen (sourceLine)
  661.               pos := sourceLine
  662.               SetStr (tempStr, 0)
  663.               doCopyDefs := FALSE
  664.             ENDIF
  665.           ENDIF
  666.           /* Was it a def, or was it OPT TURBO? */
  667.           IF (doCopyDefs)
  668.             lastFileWrittenTo := defsFileHandle
  669.             copyDefs (rData, sourceLine, fileHandle, theModuleName,
  670.                       tempStr, {pos}, {length}, {lineCount},
  671.                       {lastFileWrittenTo})
  672.             Write (defsFileHandle, '\n', 1)
  673.             lineRecognized := TRUE
  674.           ENDIF
  675.         ENDIF
  676.       ENDIF
  677.  
  678.       IF pos [] THEN INC pos
  679.     ENDWHILE  /* EOLN */
  680.  
  681.     IF lineRecognized = FALSE THEN IF Write (lastFileWrittenTo,
  682.                                              sourceLine,
  683.                                              length) < 0 THEN Raise (ER_WRITING_FILE)
  684.  
  685.     IF CtrlC () THEN userBreak := TRUE
  686.  
  687.   ENDWHILE  /* EOF */
  688.  
  689.   IF fileHandle THEN Close (fileHandle)
  690.   Dispose (rData.inputBuffer)
  691.   Dispose (rData)
  692.   Dispose (sourceLine)
  693.   Dispose (tempStr)
  694.  
  695. EXCEPT
  696.  
  697.   IF fileHandle THEN Close (fileHandle)
  698.  
  699.   SELECT exception
  700.     CASE ER_SYNTAX
  701.       WriteF ('\n *** Syntax error: module \a\s\a, line \d, pos \d.\n\n',
  702.               theModuleName, lineCount, (pos - sourceLine))
  703.       Raise (ER_NONE)  /* Avoid more messages upon return from recursion. */
  704.     CASE ER_WRITING_FILE
  705.       WriteF ('\n *** Error occurred while writing to ')
  706.       IF lastFileWrittenTo = procsFileHandle
  707.         WriteF ('Procs File, \s.\n', procsFileName)
  708.       ELSE
  709.         WriteF ('Defs File, \s.\n', argMainModuleName)
  710.       ENDIF
  711.       Raise (ER_NONE)  /* Avoid more messages upon return from recursion. */
  712.   ENDSELECT
  713.  
  714.   Raise (exception)
  715.  
  716. ENDPROC
  717.   /* getModule */
  718.  
  719.  
  720.  
  721. PROC appendProcsToDefs ()
  722.   DEF sourceLine,
  723.       bytesRead = 0
  724.  
  725.   IF argProgressIsVerbose THEN WriteF ('\n ╗╗╗ Writing procs-file' +
  726.                                        ' to end of defs-file.')
  727.  
  728.   sourceLine := New (argBufSize)
  729.  
  730.   Close (procsFileHandle)
  731.   procsFileHandle := Open (procsFileName, OLDFILE)
  732.   IF procsFileHandle = NIL THEN Raise (ER_OPENING_INFILE)
  733.  
  734.   WHILE (bytesRead := Read (procsFileHandle, sourceLine, argBufSize)) > 0
  735.     IF Write (defsFileHandle, sourceLine, bytesRead) <> bytesRead
  736.       WriteF ('\n *** Error writing output file.\n\n')
  737. /*    Dispose (sourceLine) --> Calling CleanUp() anyway. */
  738.       Raise (ER_WRITING_FILE)
  739.     ENDIF
  740.   ENDWHILE
  741.  
  742. /*Dispose (sourceLine) --> Calling CleanUp() anyway. */
  743.  
  744. ENDPROC
  745.   /* appendProcsToDefs */
  746.  
  747.  
  748.  
  749. PROC main () HANDLE
  750.   DEF saveDefsFileHandle = NIL,
  751.       saveProcsFileHandle = NIL
  752.   WbenchToFront()
  753.  
  754.   WriteF ('\n  EPP V1.1 - E Preprocessor.')
  755.   WriteF ('\n  Copyright ⌐1993 Barry Wills.  All rights reserved.\n')
  756.  
  757.   /* Get command-line arguments. */
  758.   getArgs ()
  759.  
  760.   /* Build names for E source files. */
  761.   buildEProgramName (argMainModuleName)  /* main input file  */
  762.   buildEProgramName (argOutFileName)     /* main output file */
  763.  
  764.   /* Open output files. */
  765.   procsFileName := String (13)
  766.   StrCopy (procsFileName, 'T:procsfile.e', ALL)
  767.   defsFileHandle := Open (argOutFileName, NEWFILE)
  768.   IF defsFileHandle = NIL
  769.     WriteF ('\n *** \s', argOutFileName)
  770.     Raise (ER_OPENING_OUTFILE)
  771.   ENDIF
  772.   saveDefsFileHandle := defsFileHandle
  773.   procsFileHandle := Open (procsFileName, NEWFILE)
  774.   IF procsFileHandle = NIL
  775.     WriteF ('\n *** \s', procsFileName)
  776.     Raise (ER_OPENING_OUTFILE)
  777.   ENDIF
  778.   saveProcsFileHandle := procsFileHandle
  779.  
  780.   /* Start recursive process. */
  781.   getModule (argMainModuleName)
  782.  
  783.   /* Copy tempfile containing procs onto the end of the main output file. */
  784.   IF userBreak = FALSE THEN appendProcsToDefs ()
  785.  
  786.   Close (defsFileHandle)
  787.   Close (procsFileHandle)
  788.  
  789.   IF DeleteFile (procsFileName) = FALSE
  790.     WriteF (' *** Unable to remove temporary file \s\n', procsFileName)
  791.   ENDIF
  792.  
  793.   IF userBreak
  794.     WriteF ('\n\n *** User break.\n\n')
  795.   ELSE
  796.     WriteF ('\s', IF argProgressIsVerbose THEN '\n ╗╗╗ Done.\n\n' ELSE '\n')
  797.   ENDIF
  798.  
  799.   CleanUp (0)
  800.  
  801. EXCEPT
  802.  
  803.   SELECT exception
  804.     CASE ER_USAGE;
  805.       WriteF ('\n *** Usage:  EPP [switches] infile[.e] outfile[.e]' +
  806.               '\n      switches: -b### = file buffer size (20+; default 128)' +
  807.               '\n                -l### = source line length (20+; default 128)' +
  808.               '\n                -s    = silence progress messages' +
  809.               '\n                -c    = insert EPP comments in final output source' +
  810.               '\n                -t    = Turbo mode\n\n')
  811.     CASE ER_MEM;
  812.       WriteF ('\n *** Insufficient memory.  ' +
  813.               'Try shorter line length if possible.\n\n')
  814.     CASE ER_OPENING_INFILE;  WriteF ('\n     Error opening input file.\n\n')
  815.     CASE ER_OPENING_OUTFILE; WriteF ('\n     Error opening output file.\n\n')
  816.   ENDSELECT
  817.  
  818.   IF defsFileHandle THEN Close (defsFileHandle)
  819.   IF procsFileHandle THEN Close (procsFileHandle)
  820.  
  821.   CleanUp (5)
  822.  
  823. ENDPROC
  824.   /* main */
  825.