{$optimize 7}
{---------------------------------------------------------------}
{                                                               }
{  Scanner                                                      }
{                                                               }
{  External Variables:                                          }
{                                                               }
{  ch - next character to process                               }
{  printMacroExpansions - print the token list?                 }
{  reportEOL - report eolsy as a token?                         }
{  token - next token to process                                }
{                                                               }
{  External Subroutines:                                        }
{                                                               }
{  Error - flag an error                                        }
{  IsDefined - see if a macro name is in the macro table        }
{  InitScanner - initialize the scanner                         }
{  NextCh - Read the next character from the file, skipping     }
{       comments.                                               }
{  NextToken - read the next token from the file                }
{  PutBackToken - place a token into the token stream           }
{  TermScanner - Shut down the scanner.                         }
{                                                               }
{---------------------------------------------------------------}

unit Scanner;

interface

{$LibPrefix '0/obj/'}

uses CCommon, Table, CGI, MM;

{$segment 'scanner'}

type
   pragmas =				{kinds of pragmas}
      (p_startofenum,p_cda,p_cdev,p_float,p_keep,
       p_nda,p_debug,p_lint,p_memorymodel,p_expand,
       p_optimize,p_stacksize,p_toolparms,p_databank,p_rtl,
       p_noroot,p_path,p_ignore,p_segment,p_nba,
       p_xcmd,p_unix,p_line,p_endofenum);

                                        {preprocessor types}
                                        {------------------}
   tokenListRecordPtr = ^tokenListRecord;
   tokenListRecord = record             {element of a list of tokens}
      next: tokenListRecordPtr;         {next element in list}
      tokenString: longStringPtr;       {string making up the token}
      token: tokenType;                 {token}
      expandEnabled: boolean;           {can this token be macro expanded?}
      tokenStart,tokenEnd: ptr;         {token start/end markers}
      end;
   macroRecordPtr = ^macroRecord;
   macroRecord = record                 {preprocessor macro definition}
      next: macroRecordPtr;
      saved: boolean;
      name: stringPtr;
      parameters: integer;
      tokens: tokenListRecordPtr;
      readOnly: boolean;
      algorithm: integer;
      end;
   macroTable = array[0..hashSize] of macroRecordPtr; {preprocessor macro list}

					{path name lists}
                                        {---------------}
   pathRecordPtr = ^pathRecord;
   pathRecord = record
      next: pathRecordPtr;
      path: stringPtr;
      end;

var
   ch: char;                            {next character to process}
   macros: ^macroTable;                 {preprocessor macro list}
   pathList: pathRecordPtr;		{additional search paths}
   printMacroExpansions: boolean;       {print the token list?}
   reportEOL: boolean;                  {report eolsy as a token?}
   skipIllegalTokens: boolean;		{skip flagging illegal tokens in skipped code?}
   slashSlashComments: boolean;		{allow // comments?}
   token: tokenType;                    {next token to process}

{---------------------------------------------------------------}

procedure DoDefaultsDotH;

{ Handle the defaults.h file					}


procedure Error (err: integer);

{ flag an error                                                 }
{                                                               }
{ err - error number                                            }


{procedure Error2 (loc, err: integer); {debug}

{ flag an error                                                 }
{                                                               }
{ loc - error location                                          }
{ err - error number                                            }


procedure InitScanner (start, endPtr: ptr);

{ initialize the scanner                                        }
{                                                               }
{ start - pointer to the first character in the file            }
{ endPtr - points one byte past the last character in the file  }


function IsDefined (name: stringPtr): boolean;

{ See if a macro name is in the macro table                     }
{                                                               }
{ The returned value is true if the macro exists, else false.   }
{                                                               }
{ parameters:                                                   }
{       name - name of the macro to search for                  }


procedure NextCh; extern;

{ Read the next character from the file, skipping comments.     }
{                                                               }
{ Globals:                                                      }
{       ch - character read                                     }


procedure NextToken;

{ Read the next token from the file.                            }


procedure PutBackToken (var token: tokenType; expandEnabled: boolean);

{ place a token into the token stream                           }
{                                                               }
{ parameters:                                                   }
{       token - token to put back into the token stream         }
{       expandEnabled - can macro expansion be performed?       }


procedure TermScanner;

{ Shut down the scanner.                                        }

{---------------------------------------------------------------}

implementation

const
                                        {special key values}
                                        {------------------}
   BS           = 8;                    {backspace}
   FF           = 12;                   {form feed}
   HT           = 9;                    {horizontal tab}
   NEWLINE      = 10;                   {newline}
   RETURN       = 13;                   {RETURN key code}
   VT           = 11;                   {vertical tab}

                                        {misc}
                                        {----}
   defaultName  = '13:ORCACDefs:Defaults.h'; {default include file name}
   maxErr       = 10;                   {max errors on one line}

type
   errorType = record                   {record of a single error}
      num: integer;                     {error number}
      line: integer;                    {line number}
      col: integer;                     {column number}
      end;

                                        {file inclusion}
                                        {--------------}
   filePtr = ^fileRecord;
   fileRecord = record			{NOTE: used in scanner.asm}
      next: filePtr;                    {next file in include stack}
      name: gsosOutString;		{name of the file}
      sname: gsosOutString;		{name of the file for __FILE__}
      lineNumber: integer;              {line number at the #include}
      disp: longint;                    {disp of next character to process}
      end;

   getFileInfoOSDCB = record
      pcount: integer;
      pathName: gsosInStringPtr;
      access: integer;
      fileType: integer;
      auxType: longint;
      storageType: integer;
      createDateTime: timeField;
      modDateTime: timeField;
      optionList: optionListPtr;
      dataEOF: longint;
      blocksUsed: longint;
      resourceEOF: longint;
      resourceBlocks: longint;
      end;

   expandDevicesDCBGS = record
      pcount: integer;
      inName: gsosInStringPtr;
      outName: gsosOutStringPtr;
      end;
   
                                        {conditional compilation parsing}
                                        {-------------------------------}
   ifPtr = ^ifRecord;
   ifRecord = record
      next: ifPtr;                      {next record in if stack}
      status:                           {what are we doing?}
         (processing,skippingToEndif,skippingToElse);
      elseFound: boolean;               {has an #else been found?}
      end;

var
   dateStr: longStringPtr;              {macro date string}
   doingstring: boolean;                {used to supress comments in strings}
   errors: array[1..maxErr] of errorType; {errors in this line}
   eofPtr: ptr;                         {points one byte past the last char in the file}
   fileList: filePtr;                   {include file list}
   flagOverflows: boolean;              {flag numeric overflows?}
   gettingFileName: boolean;            {are we in GetFileName?}
   lastWasReturn: boolean;              {was the last character an eol?}
   lineStr: string[5];			{string form of __LINE__}
   ifList: ifPtr;                       {points to the top prep. parse record}
   includeChPtr: ptr;			{chPtr at start of current token}
   includeCount: 0..maxint;		{nested include files (for EndInclude)}
   macroFound: macroRecordPtr;          {last macro found by IsDefined}
   needWriteLine: boolean;              {is there a line that needs to be written?}
   numErr: 0..maxErr;                   {number of errors in this line}
   oneStr: string[2];			{string form of __STDC__}
   ispstring: boolean;                  {is the current string a p-string?}
   saveNumber: boolean;                 {save the characters in a number?}
   skipping: boolean;                   {skipping tokens?}
   timeStr: longStringPtr;              {macro time string}
   tokenColumn: 0..maxint;              {column number at start of this token}
   tokenLine: 0..maxint;                {line number at start of this token}
   tokenList: tokenListRecordPtr;       {token putback buffer}
   tokenStart: ptr;                     {pointer to the first char in the token}
   tokenEnd: ptr;                       {pointer to the first char past the token}
   versionStrL: longStringPtr;          {macro version string}
   workString: pstring;                 {for building strings and identifiers}

{-- External procedures; see expresssion evaluator for notes ---}

procedure EndInclude (chPtr: ptr); extern;

{ Saves symbols created by the include file			}
{								}
{ Parameters:							}
{    chPtr - chPtr when the file returned			}
{								}
{ Notes:							}
{    1. Call this subroutine right after processing an		}
{       include file.						}
{    2. Fron Header.pas						}


procedure ExpandDevicesGS (var parms: expandDevicesDCBGS); prodos ($0154);
           

procedure Expression (kind: expressionKind; stopSym: tokenSet); extern;

{ handle an expression                                          }


function FindSymbol (var tk: tokenType; class: spaceType; oneLevel: boolean;
                     staticAllowed: boolean): identPtr; extern;

{ locate a symbol in the symbol table                           }
{                                                               }
{ parameters:                                                   }
{       tk - token record for the identifier to find            }
{       class - the kind of variable space to search            }
{       oneLevel - search one level only? (used to check for    }
{               duplicate symbols)                              }
{       staticAllowed - can we check for static variables?      }
{                                                               }
{ returns:                                                      }
{       A pointer to the symbol table entry is returned.  If    }
{       there is no entry, nil is returned.                     }


procedure FlagPragmas (pragma: pragmas); extern;

{ record the effects of a pragma				}
{								}
{ parameters:							}
{    pragma - pragma to record					}
{								}
{ Notes:							}
{    1. From Header.pas						}


procedure GetFileInfoGS (var parms: getFileInfoOSDCB); prodos ($2006);


procedure StartInclude (name: gsosOutStringPtr); extern;

{ Marks the start of an include file				}
{								}
{ Notes:							}
{    1. Call this subroutine right after opening an include	}
{       file.							}
{    2. From Header.pas						}

{-- Scanner support --------------------------------------------}

procedure CheckDelimiters (var name: pString);

{ Check for delimiters, making sure they are ':'		}
{								}
{ parameters:							}
{    name - path name to check					}

label 1;

var
   dc: char;				{delimiter character}
   i: 0..255;				{loop/index variable}

begin {CheckDelimiters}
dc := ':';				{determine what the delimiter is}
for i := 1 to length(name) do
   if name[i] in [':','/'] then begin
      dc := name[i];
      goto 1;
      end; {if}
1:  ;
if dc = '/' then			{replace '/' delimiters with ':'}
   for i := 1 to length(name) do
      if name[i] = '/' then
         name[i] := ':';
end; {CheckDelimiters}


procedure AddPath (name: pString);

{ Add a path name to the path name table			}
{								}
{ parameters:							}
{    name - path name to add					}

var
   pp, ppe: pathRecordPtr;		{work pointers}

begin {AddPath}
if length(name) <> 0 then begin
   CheckDelimiters(name);		{make sure ':' is used}
   if name[length(name)] <> ':' then	{make sure there is a trailing delimiter}
      name := concat(name, ':');
					{create the new path record}
   pp := pathRecordPtr(GMalloc(sizeof(pathRecord)));
   pp^.next := nil;
   pp^.path := stringPtr(GMalloc(length(name)+1));
   pp^.path^ := name;
   if pathList = nil then		{add the path to the path list}
      pathList := pp
   else begin
      ppe := pathList;
      while ppe^.next <> nil do
         ppe := ppe^.next;
      ppe^.next := pp;
      end; {else}
   end; {if}
end; {AddPath}


function Convertsl(var str: pString): longint; extern;

{ Return the integer equivalent of the string.  Assumes a valid }
{ 4-byte integer string; supporst unsigned values.              }


procedure SetDateTime; extern;

{ set up the macro date/time strings                            }


function KeyPress: boolean; extern;
 
{ Has a key been presed?                                        }
{                                                               }
{ If a key has not been pressed, this function returns          }
{ false.  If a key has been pressed, it clears the key          }
{ strobe.  If the key was an open-apple ., a terminal exit      }
{ is performed; otherwise, the function returns true.           }
 

function IsDefined {name: stringPtr): boolean};

{ See if a macro name is in the macro table                     }
{                                                               }
{ The returned value is true if the macro exists, else false.   }
{                                                               }
{ parameters:                                                   }
{       name - name of the macro to search for                  }
{                                                               }
{ outputs:                                                      }
{       macroFound - pointer to the macro found                 }

label 1;

var
   bPtr: ^macroRecordPtr;               {pointer to hash bucket}
   mPtr: macroRecordPtr;                {for checking list of macros}

begin {IsDefined}
IsDefined := false;
bPtr := pointer(ord4(macros) + Hash(name));
mPtr := bPtr^;
while mPtr <> nil do begin
   if mPtr^.name^ = name^ then begin
      IsDefined := true;
      goto 1;
      end; {if}
   mPtr := mPtr^.next;
   end; {while}
1:
macroFound := mPtr;
end; {IsDefined}


procedure PutBackToken {var token: tokenType; expandEnabled: boolean};

{ place a token into the token stream                           }
{                                                               }
{ parameters:                                                   }
{       token - token to put back into the token stream         }
{       expandEnabled - can macro expansion be performed?       }

var
   tPtr: tokenListRecordPtr;            {work pointer}

begin {PutBackToken}
new(tPtr);
tPtr^.next := tokenList;
tokenList := tPtr;
tPtr^.token := token;
tPtr^.expandEnabled := expandEnabled;
tPtr^.tokenStart := tokenStart;
tPtr^.tokenEnd := tokenEnd;
end; {PutBackToken}


procedure WriteLine;

{ Write the current character to the screen.                    }
{                                                               }
{ Global Variables:                                             }
{   firstPtr - points to the first char in the line             }
{   chPtr - points to the end of line character                 }

var
   cl: 0..maxint;                       {column number loop index}
   cp: ptr;                             {work pointer}
   i: 1..maxErr;                        {error loop index}
   msg: stringPtr;                      {pointer to the error message}
 
begin {WriteLine}
if list or (numErr <> 0) then begin
   write(lineNumber:4, ' ');            {write the line #}
   cp := firstPtr;                      {write the characters in the line}
   while cp <> chPtr do begin
     if cp^ <> RETURN then
       write(chr(cp^));
     cp := pointer(ord4(cp) + 1);
     end; {while}
   writeln;                             {write the end of line character}
   for i := 1 to numErr do              {write any errors}
     with errors[i] do begin
       if line = lineNumber then begin
         for cl := 1 to col+4 do
           write(' ');
         write('^ ');
         end {if}
       else
         write('     Error in column ', col:1, ' of line ', line:1, ': ');
       case num of
         1 : msg := @'illegal character';
         2 : msg := @'a character constant must contain exactly one character';
         3 : msg := @'no end was found to the string';
         4 : msg := @'further errors suppressed';
         5 : msg := @'cannot redefine a macro';
         6 : msg := @'integer overflow';
         7 : msg := @'''8'' and ''9'' cannot be used in octal constants';
         8 : msg := @'unknown preprocessor command';
         9 : msg := @'identifier expected';
         10: msg := @'cannot undefine standard macros';
         11: msg := @'end of line expected';
         12: msg := @''')'' expected';
         13: msg := @'''('' expected';
         14: msg := @'incorrect number of macro parameters';
         15: msg := @'''>'' expected';
         16: msg := @'file name is too long';
         17: msg := @'keep must appear before any functions';
         18: msg := @'integer constant expected';
         19: msg := @'only one #else may be used per #if';
         20: msg := @'there is no #if for this directive';
         21: msg := @'an #if had no closing #endif';
         22: msg := @''';'' expected';
         23: msg := @'''}'' expected';
         24: msg := @''']'' expected';
         25: msg := @'the else has no matching if';
         26: msg := @'type expected';
         27: msg := @'''{'' expected';
         28: msg := @'a function cannot be defined here';
         29: msg := @''':'' expected';
         30: msg := @'''while'' expected';
         31: msg := @'undeclared identifier';
         32: msg := @'the last if statement was not finished';
         33: msg := @'the last do statement was not finished';
         34: msg := @'the last compound statement was not finished';
         35: msg := @'expression expected';
         36: msg := @'expression syntax error';
         37: msg := @'operand expected';
         38: msg := @'operation expected';
         39: msg := @'no matching ''?'' found for this '':'' operator';
         40: msg := @'illegal type cast';
         41: msg := @'illegal operand in a constant expression';
         42: msg := @'duplicate symbol';
         43: msg := @'the function''s type must match the previous declaration';
         44: msg := @'too many initializers';
         45: msg := @'the number of array elements must be greater than 0';
         46: msg := @'you must initialize the individual elements of a struct, union, or non-char array';
         47: msg := @'type conflict';
         48: msg := @'pointer initializers must resolve to an integer, address or string';
         49: msg := @'the array size could not be determined';
         50: msg := @'only parameters or types may be declared here';
         51: msg := @'lint: undefined function';
         52: msg := @'you cannot initialize a type';
         53: msg := @'the structure has already been defined';
         54: msg := @'bit fields must be less than 32 bits wide';
         55: msg := @'a value cannot be zero bits wide';
         56: msg := @'unions cannot have bit fields';
         57: msg := @'compiler error';
         58: msg := @'implementation restriction: too many local labels';
         59: msg := @'file name expected';
         60: msg := @'implementation restriction: string space exhausted';
         61: msg := @'implementation restriction: run-time stack space exhausted';
         62: msg := @'auto or register can only be used in a function body';
         63: msg := @'token merging produced an illegal token';
         64: msg := @'assignment to an array is not allowed';
         65: msg := @'assignment to void is not allowed';
         66: msg := @'the operation cannot be performed on operands of the type given';
         67: msg := @'the last else clause was not finished';
         68: msg := @'the last while statement was not finished';
         69: msg := @'the last for statement was not finished';
         70: msg := @'the last switch statement was not finished';
         71: msg := @'switch expressions must evaluate to integers';
         72: msg := @'case and default labels must appear in a switch statement';
         73: msg := @'duplicate case label';
         74: msg := @'only one default label is allowed in a switch statement';
         75: msg := @'continue must appear in a while, do or for loop';
         76: msg := @'break must appear in a while, do, for or switch statement';
         77: msg := @'duplicate label';
         78: msg := @'l-value required';
         79: msg := @'illegal operand for the indirection operator';
         80: msg := @'the selection operator must be used on a structure or union';
         81: msg := @'the selected field does not exist in the structure or union';
         82: msg := @'''('', ''['' or ''*'' expected';
         83: msg := @'string constant expected';
         84: msg := @'''dynamic'' expected';
         85: msg := @'the number of parameters does not agree with the prototype';
         86: msg := @''','' expected';
         87: msg := @'invalid storage type for a parameter';
         88: msg := @'you cannot initialize a parameter';
         89: msg := @'''.'' expected';
         90: msg := @'string too long';
         91: msg := @'real constants cannot be unsigned';
         92: msg := @'statement expected';
         93: msg := @'assignment to const is not allowed';
         94: msg := @'pascal qualifier is only allowed on functions';
         95: msg := @'unidentified operation code';
         96: msg := @'incorrect operand size';
         97: msg := @'operand syntax error';
         98: msg := @'invalid operand';
         99: msg := @'comp data type is not supported by the 68881';
        100: msg := @'integer constants cannot use the f designator';
        101: msg := @'digits expected in the exponent';
       {102: msg := @'extern variables cannot be initialized';}
        103: msg := @'functions cannot return functions or arrays';
        104: msg := @'lint: missing function type';
        105: msg := @'lint: parameter list not prototyped';
        106: msg := @'cannot take the address of a bit field';
        107: msg := @'illegal use of forward declaration';
        108: msg := @'unknown cc= option on command line';
        109: msg := @'illegal math operation in a constant expression';
        110: msg := @'lint: unknown pragma';
        111: msg := @'the & operator cannot be applied to arrays';
        112: msg := @'segment buffer overflow';
        113: msg := @'all parameters must have a name';
        114: msg := @'a function call was made to a non-function';
         otherwise: Error(57);
         end; {case}
       writeln(msg^);
       if terminalErrors then begin
          if enterEditor then
             ExitToEditor(msg, ord4(firstPtr)+col-ord4(bofPtr)-1)
          else
             TermError(0);
          end; {if}
       end; {with}
					{handle pauses}
   if ((numErr <> 0) and wait) or KeyPress then begin
      DrawHourglass;
      while not KeyPress do {nothing};               
      ClearHourglass;
      end; {if}
   numErr := 0;                         {no errors on next line...}
   end {if}
else
   if KeyPress then begin               {handle pauses}
      DrawHourglass;
      while not KeyPress do {nothing};
      ClearHourglass;
      end; {if}
Spin;					{twirl the spinner}
end; {WriteLine}


procedure PrintToken (token: tokenType);

{ Write a token to standard out                                 }
{								}
{ parameters:							}
{    token - token to print					}

label 1;

var
   ch: char;                            {work character}
   i: integer;                          {loop counter}


   procedure PrintHexDigit(i: integer);

   { Print a digit as a hex character                           }
   {                                                            }
   { Parameters:                                                }
   {    i: value to print in least significant 4 bits           }

   begin {PrintHexDigit}
   i := i & $000F;
   if i < 10 then
      write(chr(i | ord('0')))
   else
      write(chr(i + ord('A') - 10));
   end; {PrintHexDigit}


begin {PrintToken}
case token.kind of
   typedef,
   ident:            write(token.name^);

   intconst,
   uintconst:        write(token.ival:1);

   longConst,
   ulongConst:       write(token.lval:1);

   doubleConst:      write(token.rval:1);

   stringConst:      begin
                     write('"');
                     for i := 1 to token.sval^.length do begin
                        ch := token.sval^.str[i];
                        if ch in [' '..'~'] then
                           write(ch)
                        else begin
                           write('\x0');
                           PrintHexDigit(ord(ch)>>4);
                           PrintHexDigit(ord(ch));
                           end; {else}
                        end; {for}
                     write('"');
                     end;

   autosy,asmsy,breaksy,casesy,charsy,
   continuesy,constsy,compsy,defaultsy,dosy,
   doublesy,elsesy,enumsy,externsy,extendedsy,
   floatsy,forsy,gotosy,ifsy,intsy,
   inlinesy,longsy,pascalsy,registersy,returnsy,
   shortsy,sizeofsy,staticsy,structsy,switchsy,
   segmentsy,signedsy,typedefsy,unionsy,unsignedsy,
   voidsy,volatilesy,whilesy:
                     write(reservedWords[token.kind]);

   tildech,questionch,lparench,rparench,lbrackch,rbrackch,lbracech,
   rbracech,commach,semicolonch,colonch,poundch:
                     begin
                     for i := minChar to maxChar do
                        if charSym[i] = token.kind then begin
                           write(chr(i));
                           goto 1;
                           end; {if}
                     end;

   minusch:          write('-');

   plusch:           write('+');

   ltch:             write('<');

   gtch:             write('>');

   eqch:             write('=');

   excch:            write('!');

   andch:            write('&');

   barch:            write('|');

   percentch:        write('%');

   carotch:          write('^');

   asteriskch:       write('*');

   slashch:          write('/');

   dotch:            write('.');

   minusgtop:        write('->');

   opplusplus,
   plusplusop:       write('++');

   opminusminus,
   minusminusop:     write('--');

   ltltop:           write('<<');

   gtgtop:           write('>>');

   lteqop:           write('<=');

   gteqop:           write('>=');

   eqeqop:           write('==');

   exceqop:          write('!=');

   andandop:         write('&&');

   barbarop:         write('||');

   pluseqop:         write('+=');

   minuseqop:        write('-=');

   asteriskeqop:     write('*=');

   slasheqop:        write('/=');

   percenteqop:      write('%=');

   ltlteqop:         write('<<=');

   gtgteqop:         write('>>=');

   andeqop:          write('&=');

   caroteqop:        write('^=');

   bareqop:          write('!=');

   uminus:           write('-');

   uand:             write('+');

   uasterisk:        write('*');

   macroParm:        write('$', token.pnum:1);

   poundpoundop,
   parameteroper,
   castoper,
   eolsy,
   eofsy:	;
   end; {case}
1:
write(' ');
end; {PrintToken}

{ copy 'Scanner.debug'} {debug}

{-- The Preprocessor -------------------------------------------}

procedure CheckIdentifier; forward;

{ See if an identifier is a reserved word, macro or typedef     }


procedure DoNumber (scanWork: boolean); forward;

{  The current character starts a number - scan it            }
{                                                             }
{  Parameters:                                                }
{     scanWork - get characters from workString?              }
{                                                             }
{  Globals:                                                   }
{     ch - first character in sequence; set to first char     }
{             after sequence                                  }
{     workString - string to take numbers from                }


function GetFileType (var name: pString): integer; forward;

{ Checks to see if a file exists				}
{								}
{ parameters:							}
{    name - file name to check for				}
{								}
{ Returns: File type if the file exists, or -1 if the file does	}
{    not exist (or if GetFileInfo returns an error)		}


function OpenFile (doInclude, default: boolean): boolean; forward;

{ Open a new file and start scanning it				}
{								}
{ Parameters:							}
{    doInclude - are we doing a #include?			}
{    default - use the name <defaults.h>?			}
{								}
{ Returns: result from GetFileName				}


function FindMacro (name: stringPtr): macroRecordPtr;

{ If the current token is a macro, find the macro table entry   }
{                                                               }
{ Parameters:                                                   }
{       name - name of the suspected macro                      }
{                                                               }
{ Returns:                                                      }
{       Pointer to macro table entry; nil for none              }

label 1;

var
   bPtr: ^macroRecordPtr;               {pointer to hash bucket}
   mPtr: macroRecordPtr;                {pointer to macro entry}

begin {FindMacro}
FindMacro := nil;
bPtr := pointer(ord4(macros)+Hash(name));
mPtr := bPtr^;
while mPtr <> nil do begin
   if mPtr^.name^ = name^ then begin
      if mPtr^.parameters = -1 then
	 FindMacro := mPtr
      else if tokenList = nil then begin
	 while charKinds[ord(ch)] in [ch_white, ch_eol] do begin
	    if printMacroExpansions then
	       if charKinds[ord(ch)] = ch_eol then
        	  writeln
	       else
        	  write(ch);
	    NextCh;
	    end; {while}
         if ch = '(' then
            FindMacro := mPtr;
         end {else if}
      else if tokenList^.token.kind = lparench then
         FindMacro := mPtr;
      goto 1;
      end; {if}
   mPtr := mPtr^.next;
   end; {while}
1:
end; {FindMacro}


procedure LongToPString (pstr: stringPtr; lstr: longStringPtr);

{ Convert a long string into a p string                         }
{                                                               }
{ Parameters:                                                   }
{       pstr - pointer to the p-string                          }
{       lstr - pointer to the long string                       }

var
   i: integer;                          {loop variable}
   len: integer;                        {string length}

begin {LongToPString}
len := lstr^.length;
if len > 255 then
   len := 255;
pstr^[0] := chr(len);
for i := 1 to len do
   pstr^[i] := lstr^.str[i];
end; {LongToPString}


procedure Merge (var tk1: tokenType; tk2: tokenType);

{ Merge two tokens                                              }
{                                                               }
{ Parameters:                                                   }
{       tk1 - first token; result is stored here                }
{       tk2 - second token                                      }

label 1;

var
   class1,class2: tokenClass;           {token classes}
   cp: longstringPtr;                   {pointer to work string}
   i: integer;                          {loop variable}
   kind1,kind2: tokenEnum;              {token kinds}
   len,len1: integer;                   {length of strings}
   lt: tokenType;                       {local copy of token}
   str1,str2: stringPtr;                {identifier strings}

begin {Merge}
kind1 := tk1.kind;
class1 := tk1.class;
kind2 := tk2.kind;
class2 := tk2.class;
if class1 in [identifier,reservedWord] then begin
   if class1 = identifier then
      str1 := tk1.name
   else
      str1 := @reservedWords[kind1];
   if class2 = identifier then
      str2 := tk2.name
   else if class2 = reservedWord then
      str2 := @reservedWords[kind2]
   else if class2 in [intConstant,longConstant,doubleConstant] then
      str2 := tk2.numString                   
   else begin
      Error(63);
      goto 1;
      end; {else}
   workString := concat(str1^, str2^);
   for i := 1 to length(workString) do
      if not (charKinds[ord(workString[i])] in [letter,digit]) then begin
         Error(63);
         goto 1;
         end; {if}
   lt := token;
   token.kind := ident;
   token.class := identifier;
   token.numString := nil;
   token.name := @workString;
   token.symbolPtr := nil;
   CheckIdentifier;
   tk1 := token;
   token := lt;
   goto 1;
   end {class1 in [identifier,reservedWord]}

else if class1 in [intConstant,longConstant,doubleConstant] then begin
   if class2 in [intConstant,longConstant,doubleConstant] then
      str2 := tk2.numString
   else if class2 = identifier then
      str2 := tk2.name
   else if class2 = reservedWord then
      str2 := @reservedWords[kind2]
   else if kind2 = dotch then
      str2 := @'.'
   else begin
      Error(63);
      goto 1;
      end; {else}
   workString := concat(tk1.numString^, str2^);
   lt := token;
   DoNumber(true);
   tk1 := token;
   token := lt;
   goto 1;
   end {else if class1 in [intConstant,longConstant,doubleConstant]}

else if class1 = stringConstant then begin
   if class2 = stringConstant then begin
      len1 := tk1.sval^.length;
      len := len1+tk2.sval^.length;
      cp := pointer(Malloc(len+2));
      for i := 1 to len1 do
         cp^.str[i] := tk1.sval^.str[i];
      for i := 1 to len-len1 do
         cp^.str[i+len1] := tk2.sval^.str[i];
      cp^.length := len;
      if tk1.ispstring then
         cp^.str[1] := chr(len-1);
      tk1.sval := cp;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = dotch then begin
   if class2 in [intConstant,longConstant,doubleConstant] then begin
      workString := concat(tk1.numString^, tk2.numString^);
      lt := token;
      DoNumber(true);
      tk1 := token;
      token := lt;
      goto 1;
      end; {if}
   end {else if class1 in [intConstant,longConstant,doubleConstant]}

else if kind1 = poundch then begin
   if kind2 = poundch then begin
      tk1.kind := poundpoundop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = minusch then begin
   if kind2 = gtch then begin
      tk1.kind := minusgtop;
      goto 1;
      end {if}
   else if kind2 = minusch then begin
      tk1.kind := minusminusop;
      goto 1;
      end {else if}
   else if kind2 = eqch then begin
      tk1.kind := minuseqop;
      goto 1;
      end; {else if}
   end {else if}

else if kind1 = plusch then begin
   if kind2 = plusch then begin
      tk1.kind := plusplusop;
      goto 1;
      end {else if}
   else if kind2 = eqch then begin
      tk1.kind := pluseqop;
      goto 1;
      end; {else if}
   end {else if}

else if kind1 = ltch then begin
   if kind2 = ltch then begin
      tk1.kind := ltltop;
      goto 1;
      end {if}
   else if kind2 = lteqop then begin
      tk1.kind := ltlteqop;
      goto 1;
      end {else if}
   else if kind2 = eqch then begin
      tk1.kind := lteqop;
      goto 1;
      end; {else if}
   end {else if}

else if kind1 = ltltop then begin
   if kind2 = eqch then begin
      tk1.kind := ltlteqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = gtch then begin
   if kind2 = gtch then begin
      tk1.kind := gtgtop;
      goto 1;
      end {if}
   else if kind2 = gteqop then begin
      tk1.kind := gtgteqop;
      goto 1;
      end {else if}
   else if kind2 = eqch then begin
      tk1.kind := gteqop;
      goto 1;
      end; {else if}
   end {else if}

else if kind1 = gtgtop then begin
   if kind2 = eqch then begin
      tk1.kind := gtgteqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = eqch then begin
   if kind2 = eqch then begin
      tk1.kind := eqeqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = excch then begin
   if kind2 = eqch then begin
      tk1.kind := exceqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = andch then begin
   if kind2 = andch then begin
      tk1.kind := andandop;
      goto 1;
      end {if}
   else if kind2 = eqch then begin
      tk1.kind := andeqop;
      goto 1;
      end; {else if}
   end {else if}

else if kind1 = barch then begin
   if kind2 = barch then begin
      tk1.kind := barbarop;
      goto 1;
      end {if}
   else if kind2 = eqch then begin
      tk1.kind := bareqop;
      goto 1;
      end; {else if}
   end {else if}

else if kind1 = percentch then begin
   if kind2 = eqch then begin
      tk1.kind := percenteqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = carotch then begin
   if kind2 = eqch then begin
      tk1.kind := caroteqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = asteriskch then begin
   if kind2 = eqch then begin
      tk1.kind := asteriskeqop;
      goto 1;
      end; {if}
   end {else if}

else if kind1 = slashch then begin
   if kind2 = eqch then begin
      tk1.kind := slasheqop;
      goto 1;
      end; {if}
   end; {else if}

Error(63);
1:
end; {Merge}


procedure BuildStringToken (cp: ptr; len: integer);

{ Create a string token from a string                           }
{                                                               }
{ Used to stringize macros.                                     }
{                                                               }
{ Parameters:                                                   }
{       cp - pointer to the first character                     }
{       len - number of characters in the string                }

var
   i: integer;                          {loop variable}

begin {BuildStringToken}
token.kind := stringconst;
token.class := stringConstant;
token.ispstring := false;
token.sval := pointer(GMalloc(len+2));
for i := 1 to len do begin
   token.sval^.str[i] := chr(cp^);
   cp := pointer(ord4(cp)+1);
   end; {for}
token.sval^.length := len;
PutBackToken(token, true);
end; {BuildStringToken}


procedure DoInclude (default: boolean);

{ #include							}
{								}
{ Parameters:							}
{    default - open <defaults.h>?				}

var
   fp: filePtr;				{pointer to an include file}

begin {DoInclude}
new(fp);				{get a file record for the current file}
fp^.next := fileList;
fileList := fp;
fp^.name := includeFileGS;
fp^.sname := sourceFileGS;
fp^.lineNumber := lineNumber+1;
if OpenFile(true, default) then begin	{open a new file and proceed from there}
   lineNumber := 1;
   StartInclude(@includeFileGS);
   end {if}
else begin				{handle a file name error}
   fileList := fp^.next;
   dispose(fp);
   end; {else}
end; {DoInclude}


procedure Expand (macro: macroRecordPtr);

{ Expand a preprocessor macro                                   }
{                                                               }
{ Expands a preprocessor macro by putting tokens from the macro }
{ definition into the scanner's putback buffer.                 }
{                                                               }
{ Parameters:                                                   }
{       macro - pointer to the macro to expand                  }
{                                                               }
{ Globals:                                                      }
{       macroList - scanner putback buffer                      }

type
   parameterPtr = ^parameterRecord;
   parameterRecord = record             {parameter list element}
      next: parameterPtr;               {next parameter}
      tokens: tokenListRecordPtr;       {token list}
      tokenStart,tokenEnd: ptr;         {source pointers (for stringization)}
      end;

var
   bPtr: ^macroRecordPtr;               {pointer to hash bucket}
   done: boolean;                       {used to check for loop termination}
   expandEnabled: boolean;              {can the token be expanded?}
   i: integer;                          {loop counter}
   inhibit: boolean;                    {inhibit parameter expansion?}
   lexpandMacros: boolean;              {local copy of expandMacros}
   lPrintMacroExpansions: boolean;      {local copy of printMacroExpansions}
   mPtr: macroRecordPtr;                {for checking list of macros}
   newParm: parameterPtr;               {for building a new parameter entry}
   tlPtr, tPtr, tcPtr, lastPtr: tokenListRecordPtr; {work pointers}
   paramCount: integer;                 {# of parameters found in the invocation}
   parenCount: integer;                 {paren count; for balancing parenthesis}
   parmEnd: parameterPtr;               {for building a parameter list}
   parms: parameterPtr;                 {points to the list of parameters}
   pptr: parameterPtr;                  {work pointer for tracing parms list}
   sp: longStringPtr;                   {work pointer}
   stringization: boolean;              {are we stringizing a parameter?}

begin {Expand}
lPrintMacroExpansions := printMacroExpansions; {inhibit token printing}
printMacroExpansions := false;
lexpandMacros := expandMacros;          {prevent expansion of parameters}
expandMacros := false;
saveNumber := true;                     {save numeric strings}
parms := nil;                           {no parms so far}
if macro^.parameters >= 0 then begin    {find the values of the parameters}
   NextToken;                           {get the '(' (we hope...)}
   if token.kind = lparench then begin
      NextToken;                        {skip the '('}
      paramCount := 0;                  {process the parameters}
      parmEnd := nil;
      repeat
         done := true;
         if token.kind <> rparench then begin
            parenCount := 0;
            paramCount := paramCount+1;
            new(newParm);
            newParm^.next := nil;
            if parmEnd = nil then
               parms := newParm
            else
               parmEnd^.next := newParm;
            parmEnd := newParm;
            newParm^.tokens := nil;
            while (token.kind <> eofsy)
               and ((parenCount <> 0)
               or (not (token.kind in [rparench,commach]))) do begin
               new(tPtr);
               tPtr^.next := newParm^.tokens;
               newParm^.tokens := tPtr;
               tPtr^.token := token;
               tPtr^.tokenStart := tokenStart;
               tPtr^.tokenEnd := tokenEnd;
               if token.kind = lparench then
                  parenCount := parenCount+1
               else if token.kind = rparench then
                  parenCount := parenCount-1;
               NextToken;
               end; {while}
            if token.kind = commach then begin
               NextToken;
               done := false;
               end; {if}
            end; {if}
      until done;
      if paramCount <> macro^.parameters then
         Error(14);
      if token.kind = rparench then     {insist on a closing ')'}
         begin
         if not gettingFileName then
            NextToken
         end {if}
      else
         Error(12);
      end {if}
   else
      Error(13);
   if not gettingFileName then          {put back the source stream token}
      PutBackToken(token, true);
   end; {if}
if macro^.readOnly then begin           {handle special macros}
   case macro^.algorithm of

      1: begin                          {__LINE__}
         token.kind := intconst;
         token.numString := @lineStr;
         token.class := intconstant;
         token.ival := lineNumber;
         lineStr := cnvis(token.ival);
         tokenStart := @lineStr[1];
         tokenEnd := pointer(ord4(tokenStart)+length(lineStr));
         end;

      2: begin                          {__FILE__}
         token.kind := stringConst;
         token.class := stringConstant;
         token.ispstring := false;
         sp := pointer(Malloc(5+sourceFileGS.theString.size));
         sp^.length := sourceFileGS.theString.size;
         for i := 1 to sourceFileGS.theString.size do
            sp^.str[i] := sourceFileGS.theString.theString[i];
         token.sval := sp;
         tokenStart := @sp^.str;
         tokenEnd := pointer(ord4(tokenStart)+sp^.length);
         end;

      3: begin                          {__DATE__}
         token.kind := stringConst;
         token.class := stringConstant;
         token.ispstring := false;
         token.sval := dateStr;
         tokenStart := @dateStr^.str;
         tokenEnd := pointer(ord4(tokenStart)+dateStr^.length);
         end;                                   

      4: begin                          {__TIME__}
         token.kind := stringConst;
         token.class := stringConstant;
         token.ispstring := false;
         token.sval := timeStr;
         tokenStart := @timeStr^.str;
         tokenEnd := pointer(ord4(tokenStart)+timeStr^.length);
         end;

      5: begin                          {__STDC__}
         token.kind := intConst;        {__ORCAC__}
         token.numString := @oneStr;
         token.class := intConstant;
         token.ival := 1;
         oneStr := '1';
         tokenStart := @oneStr[1];
         tokenEnd := pointer(ord4(tokenStart)+1);
         end;

      6: begin                          {__VERSION__}
         token.kind := stringConst;
         token.class := stringConstant;
         token.ispstring := false;
         token.sval := versionStrL;
         tokenStart := @versionStrL^.str;
         tokenEnd := pointer(ord4(tokenStart)+versionStrL^.length);
         end;                     

      otherwise: Error(57);

      end; {case}
   PutBackToken(token, true);
   end {if}
else begin

   {expand the macro}
   tlPtr := macro^.tokens;              {place the tokens in the buffer...}
   lastPtr := nil;
   while tlPtr <> nil do begin
      if tlPtr^.token.kind = macroParm then begin
         pptr := parms;                 {find the correct parameter}
         for i := 1 to tlPtr^.token.pnum do
            if pptr <> nil then
               pptr := pptr^.next;
         if pptr <> nil then begin

            {see if the macro is stringized}
            stringization := false;
            if tlPtr^.next <> nil then
               stringization := tlPtr^.next^.token.kind = poundch;

            {handle macro stringization}
            if stringization then begin
               tcPtr := pptr^.tokens;
               while tcPtr <> nil do begin
                  if tcPtr^.token.kind = stringconst then
                     BuildStringToken(@tcPtr^.token.sval^.str,
                        tcPtr^.token.sval^.length)
                  else
                     BuildStringToken(tcPtr^.tokenStart,
                        ord(ord4(tcPtr^.tokenEnd)-ord4(tcPtr^.tokenStart)));
                  tcPtr := tcPtr^.next;
                  end; {while}
               tlPtr := tlPtr^.next;
               end {if}

            {expand a macro parameter}
            else begin
               tcPtr := pptr^.tokens;
               while tcPtr <> nil do begin
                  tokenStart := tcPtr^.tokenStart;
                  tokenEnd := tcPtr^.tokenEnd;
                  if tcPtr^.token.kind = ident then begin
                     mPtr := FindMacro(tcPtr^.token.name);
                     inhibit := false;
                     if tlPtr^.next <> nil then
                        if tlPtr^.next^.token.kind = poundpoundop then
                           inhibit := true;
                     if lastPtr <> nil then
                        if lastPtr^.token.kind = poundpoundop then
                           inhibit := true;
                     if (mPtr <> nil) and (not inhibit) then
                        Expand(mPtr)
                     else
                        PutBackToken(tcPtr^.token, true);
                     end {if}
                  else
                     PutBackToken(tcPtr^.token, true);
                  tcPtr := tcPtr^.next;
                  end; {while}
               end; {else}
            end; {if pptr <> nil}
         end {if tlPtr^.token.kind = macroParm}
      else begin

         {place an explicit parm in the token list}
         expandEnabled := true;
         if tlPtr^.token.kind = ident then
            if tlPtr^.token.name^ = macro^.name^ then
               expandEnabled := false;
         tokenStart := tlPtr^.tokenStart;
         tokenEnd := tlPtr^.tokenEnd;
         PutBackToken(tlPtr^.token, expandEnabled);
         end; {else}
      lastPtr := tlPtr;
      tlPtr := tlPtr^.next;
      end; {while}
   end; {else}
while parms <> nil do begin             {dispose of the parameter list}
   tPtr := parms^.tokens;
   while tPtr <> nil do begin
      tlPtr := tPtr^.next;
      dispose(tPtr);
      tPtr := tlPtr;
      end; {while}
   parmEnd := parms^.next;
   dispose(parms);
   parms := parmEnd;
   end; {while}
expandMacros := lexpandMacros;          {restore the flags}
printMacroExpansions := lPrintMacroExpansions;
saveNumber := false;                    {stop saving numeric strings}
end; {Expand}


function GetFileName (mustExist: boolean): boolean;

{ Read a file name from a directive line			}
{								}
{ parameters:							}
{    mustExist - should we look for an existing file?		}
{								}
{ Returns true if successful, false if not.			}
{								}
{ Note: The file name is placed in workString.			}

const
   SRC = $B0;				{source file type}

var
   i,j: integer;			{string index & loop vars}


   procedure Expand (var name: pString);

   { Expands a name to a full pathname				}
   {								}
   { parameters:						}
   {    name - file name to expand				}

   var
      exRec: expandDevicesDCBGS;	{expand devices}

   begin {Expand}
   exRec.pcount := 2;
   new(exRec.inName);
   exRec.inName^.theString := name;
   exRec.inName^.size := length(name);
   new(exRec.outName);
   exRec.outName^.maxSize := maxPath+4;
   ExpandDevicesGS(exRec);
   if toolerror = 0 then
      with exRec.outName^.theString do begin
         if size < maxPath then
            theString[size+1] := chr(0);
         name := theString;
         end; {with}
   dispose(exRec.inName);
   dispose(exRec.outName);
   end; {Expand}


   function GetLibraryName (var name: pstring): boolean;

   { See if a library pathname is available			}
   {								}
   { Parameters:						}
   {    name - file name; set to pathname if result is true	}
   {								}
   { Returns: True if a name is available, else false		}

   var
      lname: pString;			{local copy of name}

   begin {GetLibraryName}
   lname := concat('13:ORCACDefs:', name);
   Expand(lname);
   if GetFileType(lname) = SRC then begin
      name := lname;
      GetLibraryName := true;
      end {if}
   else
      GetLibraryName := false;
   end; {GetLibraryName}


   function GetLocalName (var name: pstring): boolean;

   { See if a local pathname is available			}
   {								}
   { Parameters:						}
   {    name - file name; set to pathname if result is true	}
   {								}
   { Returns: True if a name is available, else false		}

   var
      lname: pstring;			{work string}
      pp: pathRecordPtr;		{used to trace the path list}

   begin {GetLocalName}
   lname := name;
   Expand(lname);
   if GetFileType(lname) = SRC then begin
      GetLocalName := true;
      name := lname;
      end {if}
   else begin
      GetLocalName := false;
      pp := pathList;
      while pp <> nil do begin
         lname := concat(pp^.path^, name);
         if GetFileType(lname) = SRC then begin
            GetLocalName := true;
            name := lname;
            Expand(name);
            pp := nil;
            end {if}
         else
            pp := pp^.next;
         end; {while}
      end; {else}
   end; {GetLocalName}


   procedure MakeLibraryName (var name: pstring);

   { Create the library path name for an error message		}
   {								}
   { Parameters:						}
   {    name - file name; set to pathname			}

   begin {MakeLibraryName}
   name := concat('13:ORCACDefs:', name);
   Expand(name);
   end; {MakeLibraryName}


   procedure MakeLocalName (var name: pstring);

   { Create the local path name for an error message		}
   {								}
   { Parameters:						}
   {    name - file name; set to pathname			}

   begin {MakeLocalName}
   Expand(name);
   end; {MakeLocalName}


begin {GetFileName}
GetFileName := true;
gettingFileName := true;		{in GetFileName}
while charKinds[ord(ch)] = ch_white do	{finish processing the current line}
   NextCh;
if ch = '<' then begin			{process a library file...}
   NextToken;				{skip the '<'}
   token.kind := stringconst;		{convert a <> style name to a string}
   token.class := stringConstant;
   token.ispstring := false;
   i := 0;
   while not (charKinds[ord(ch)] in [ch_eol,ch_gt]) do begin
      i := i+1;
      if (i = maxLine) then begin
         Error(16);
         GetFileName := false;
         i := 0;
         end;
      workString[i] := ch;
      NextCh;
      end; {while}
   workString[0] := chr(i);
   CheckDelimiters(workString);
   if mustExist then begin
      if not GetLibraryName(workString) then
         if not GetLocalName(workString) then
            MakeLibraryName(workString);
      end {if}
   else
      MakeLibraryName(workString);
   if ch = '>' then
      NextCh
   else begin
      Error(15);
      GetFileName := false;
      end; {else}
   end {if}
else begin

   {handle file names that are strings or macro expansions}
   expandMacros := true;		{allow macros to be used in the name}
   NextToken;				{skip the command name}
   if token.kind = stringConst then begin
      LongToPString(@workString, token.sval);
      CheckDelimiters(workString);
      if mustExist then begin
         if not GetLocalName(workString) then
	    if not GetLibraryName(workString) then
               MakeLocalName(workString);
         end {if}
      else
         MakeLocalName(workString);
      end {if}    
   else if token.kind = ltch then begin

      {expand a macro to create a <filename> form name}
      NextToken;
      workString[0] := chr(0);
      while
         (token.class in [reservedWord,intconstant,longconstant,doubleconstant])
         or (token.kind in [dotch,ident]) do begin
         if token.kind = ident then
            workstring := concat(workstring, token.name^)
         else if token.kind = dotch then
            workstring := concat(workstring, '.')
         else if token.class = reservedWord then
            workstring := concat(workstring, reservedWords[token.kind])
         else {if token.class in [intconst,longconst,doubleconst] then}
            workstring := concat(workstring, token.numstring^);
         NextToken;
         end; {while}
      CheckDelimiters(workString);
      if mustExist then begin
	 if not GetLibraryName(workString) then
            if not GetLocalName(workString) then
               MakeLibraryName(workString);
         end {if}
      else
         MakeLibraryName(workString);
      if token.kind <> gtch then begin
         Error(15);
         GetFileName := false;
         end; {if}
      end {else if}
   else begin
      Error(59);
      GetFileName := false;
      end; {else}
   end; {else}
while charKinds[ord(ch)] = ch_white	{finish processing the current line}
   do NextCh;
if charKinds[ord(ch)] <> ch_eol then	{check for extra stuff on the line}
   begin
   Error(11);
   GetFileName := false;
   end; {if}
gettingFileName := false;		{not in GetFileName}
end; {GetFileName}


function GetFileType {var name: pString): integer};

{ Checks to see if a file exists				}
{								}
{ parameters:							}
{    name - file name to check for				}
{								}
{ Returns: File type if the file exists, or -1 if the file does	}
{    not exist (or if GetFileInfo returns an error)		}

var
   pathname: gsosInString;		{GS/OS style name}
   giRec: getFileInfoOSDCB;		{GetFileInfo record}

begin {GetFileType}
giRec.pcount := 3;
giRec.pathName := @pathname;
pathname.theString := name;
pathname.size := length(name);
GetFileInfoGS(giRec);
if ToolError = 0 then
   GetFileType := giRec.fileType
else
   GetFileType := -1;
end; {GetFileType}


function OpenFile {doInclude, default: boolean): boolean};

{ Open a new file and start scanning it				}
{								}
{ Parameters:							}
{    doInclude - are we doing a #include?			}
{    default - use the name <defaults.h>?			}
{								}
{ Returns: result from GetFileName				}

var
   gotName: boolean;			{did we get a file name?}

begin {OpenFile}
if default then begin			{get the file name}
   workString := defaultName;
   gotName := true;
   end {if}
else
   gotName := GetFileName(true);

if gotName then begin			{read the file name from the line}
   OpenFile := true;			{we opened it}
   if doInclude and progress then	{note our progress}
      writeln('Including ', workString);
   WriteLine;				{write the source line}
   lineNumber := lineNumber+1;
   firstPtr := pointer(ord4(chPtr)+2);
   needWriteLine := false;
   if doInclude then			{set the disp in the file}
      fileList^.disp := ord4(chPtr)-ord4(bofPtr);
   with ffDCBGS do begin		{purge the source file}
      pCount := 5;
      action := 7;
      pathName := @includeFileGS.theString;
      end; {with}
   FastFileGS(ffDCBGS);
   oldincludeFileGS := includeFileGS;	{set the file name}
   includeFileGS.theString.theString := workString;
   includeFileGS.theString.size := length(workString);
   ReadFile;				{read the file}
   chPtr := bofPtr;			{set the start, end pointers}
   eofPtr := pointer(ord4(bofPtr)+ffDCBGS.fileLength);
   firstPtr := chPtr;			{first char in line}
   ch := chr(RETURN);			{set the initial character}
   if languageNumber <> long(ffDCBGS.auxType).lsw then begin
      switchLanguages := true;		{switch languages}
      chPtr := eofPtr;
      if doInclude then
         TermError(7);
      if fileList <> nil then
         TermError(8);
      end; {if}
   end {if}
else
   OpenFile := false;			{we failed to opened it}
end; {OpenFile}


procedure PreProcess;

{ Handle preprocessor commands                                  }

label 2;

var
   lPrintMacroExpansions: boolean;      {local copy of printMacroExpansions}
   lReportEOL: boolean;                 {local copy of reportEOL}
   tSkipping: boolean;                  {temp copy of the skipping variable}
   val: integer;                        {expression value}


   function Defined: boolean;
 
   { See if a macro is defined                                   }
 
   begin {Defined}
   expandMacros := false;               {block expansions}
   NextToken;                           {skip the command name}
   if token.class in [reservedWord,identifier] then begin
      Defined := IsDefined(token.name); {see if the macro is defined}
      expandMacros := true;             {enable expansions}
      NextToken;                        {skip the macro name}
      if token.kind <> eolsy then       {check for extra stuff on the line}
         Error(11);
      end {if}
   else
      Error(9);
   end; {Defined}


   procedure NumericDirective;
 
   { Process a constant expression for a directive that has a    }
   { single number as the operand.                               }
   {                                                             }
   { Notes:  The expression evaluator returns the value in the   }
   { global variable expressionValue.                            }
 
   begin {NumericDirective}
   NextToken;                            {skip the directive name}
   Expression(preprocessorExpression, []); {evaluate the expression}
   end; {NumericDirective}


   procedure ProcessIf (skip: boolean);
 
   { handle the processing for #if, #ifdef and #ifndef           }
   {                                                             }
   { parameter:                                                  }
   {     skip - should we skip to the #else                      }
 
   var
      ip: ifPtr;                        {used to create a new if record}
 
   begin {ProcessIf}
   if token.kind <> eolsy then          {check for extra stuff on the line}
      Error(11);
   new(ip);                             {create a new if record}
   ip^.next := ifList;
   ifList := ip;
   if tSkipping then                    {set the status of the record}
      ip^.status := skippingToEndif
   else if skip then
      ip^.status := skippingToElse
   else
      ip^.status := processing;
   ip^.elseFound := false;              {no else has been found...}
   tSkipping := ip^.status <> processing; {decide if we should be skipping}
   end; {ProcessIf}


   procedure DoAppend;
 
   { #append                                                     }

   var
      tbool: boolean;                   {temp boolean}

   begin {DoAppend}
   tbool := OpenFile(false, false);	{open a new file and proceed from there}
   lineNumber := 1;
   end; {DoAppend}


   procedure DoCDA;

   { #pragma cda NAME START SHUTDOWN                            }

   begin {DoCDA}
   FlagPragmas(p_cda);
   isClassicDeskAcc := true;
   NextToken;                           {skip the command name}
   if token.kind = stringconst then     {get the name}
      begin
      LongToPString(@menuLine, token.sval);
      NextToken;
      end {if}
   else begin
      isClassicDeskAcc := false;
      Error(83);
      end; {else}
   if token.kind = ident then begin     {get the start name}
      openName := token.name;
      NextToken;
      end {if}
   else begin
      isClassicDeskAcc := false;
      Error(9);
      end; {else}
   if token.kind = ident then begin     {get the shutdown name}
      closeName := token.name;
      NextToken;
      end {if}
   else begin
      isClassicDeskAcc := false;
      Error(9);
      end; {else}
   if token.kind <> eolsy then          {make sure there is nothing else on the line}
      Error(11);
   end; {DoCDA}


   procedure DoCDev;

   { #pragma cdev START                                         }

   begin {DoCDev}
   FlagPragmas(p_cdev);
   isCDev := true;
   NextToken;                           {skip the command name}
   if token.kind = ident then begin     {get the start name}
      openName := token.name;
      NextToken;
      end {if}
   else begin
      isCDev := false;
      Error(9);
      end; {else}
   if token.kind <> eolsy then          {make sure there is nothing else on the line}
      Error(11);
   end; {DoCDev}


   procedure DoDefine;

   { #define                                                     }
   {                                                             }
   { The way parameters are handled is a bit obtuse.  Parameters }
   { have their own token type, with the token having an         }
   { associated parameter number, pnum.  Pnum is the number of   }
   { parameters to skip to get to the parameter in the parameter }
   { list.                                                       }
   {                                                             }
   { In the macro record, parameters indicates how many          }
   { parameters there are in the definition.  -1 indicates that  }
   { there is no parameter list, while 0 indicates that a list   }
   { must exist, but that there are no parameters in the list.   }
 
   label 1,2,3;
 
   type
      stringListPtr = ^stringList;
      stringList = record               {for the parameter list}
         next: stringListPtr;
         str: pString;
         end;

   var
      bPtr: ^macroRecordPtr;            {pointer to head of hash bucket}
      done: boolean;                    {used to test for loop termination}
      i: integer;                       {loop variable}
      mf: macroRecordPtr;               {pointer to existing macro record}
      mPtr: macroRecordPtr;             {pointer to new macro record}
      np: stringListPtr;                {new parameter}
      parameterList: stringListPtr;     {list of parameter names}
      parameters: integer;              {local copy of mPtr^.parameters}
      ple: stringListPtr;               {pointer to the last element in parameterList}
      pnum: integer;                    {for counting parameters}
      tPtr,tk1,tk2: tokenListRecordPtr; {pointer to a token}

                                        {for building token strings}
      sptr: longStringPtr;              {token string work pointer}
      tcp: ptr;                         {temp character pointer}
      slen: integer;                    {token string length}

   begin {DoDefine}
   expandMacros := false;               {block expansions}
   saveNumber := true;                  {save characters in numeric tokens}
   parameterList := nil;                {no parameters yet}
   NextToken;                           {get the token name}
                                        {convert reserved words to identifiers}
   if token.class = reservedWord then begin
      token.name := @reservedWords[token.kind];
      token.kind := ident;
      token.class := identifier;
      end {if}
   else if token.kind = typedef then
      token.kind := ident;

   if token.kind = ident then begin     {we have a name...}
      mPtr := pointer(GMalloc(sizeof(macroRecord))); {create a macro record}
      mPtr^.name := token.name;         {record the name}
      mPtr^.saved := false;		{not saved in symbol file}
      mPtr^.tokens := nil;              {no tokens yet}
      charKinds[ord('#')] := ch_pound;  {allow # as a token}
      if ch = '(' then begin            {scan the parameter list...}
         NextToken;                     {done with the name token...}
         NextToken;                     {skip the opening '('}
         parameters := 0;               {no parameters yet}
         ple := nil;
         repeat                         {get the parameter names}
            done := true;

	    if token.class = reservedWord then begin
	       token.name := @reservedWords[token.kind];
	       token.kind := ident;
	       token.class := identifier;
	       end {if}
	    else if token.kind = typedef then
	       token.kind := ident;

            if token.kind = ident then begin
               new(np);
               np^.next := nil;
               np^.str := token.name^;
               if ple = nil then
                  parameterList := np
               else
                  ple^.next := np;
               ple := np;
               NextToken;
               parameters := parameters+1;
               if token.kind = commach then begin
                  NextToken;
                  done := false;
                  end; {if}
               end; {if}
         until done;
         if token.kind = rparench then  {insist on a matching ')'}
            NextToken
         else
            Error(12);
         end {if}
      else begin
         parameters := -1;              {no parameter list exists}
         NextToken;                     {done with the name token...}
         end; {else}
      mPtr^.parameters := parameters;   {record the # of parameters}
      while token.kind <> eolsy do begin {place tokens in the replace list...}

	 if token.class = reservedWord then begin
	    token.name := @reservedWords[token.kind];
	    token.kind := ident;
	    token.class := identifier;
	    end {if}
	 else if token.kind = typedef then
	    token.kind := ident;

         if token.kind = ident then begin {special handling for identifiers}
            np := parameterList;        {change parameters to macroParm}
            pnum := 0;
            while np <> nil do begin
               if np^.str = token.name^ then begin
                  token.kind := macroParm;
                  token.class := macroParameter;
                  token.pnum := pnum;
                  goto 1;
                  end; {if}
               pnum := pnum+1;
               np := np^.next;
               end; {while}
            end; {if}
1:       tPtr := pointer(GMalloc(sizeof(tokenListRecord)));
         tPtr^.next := mPtr^.tokens;
         mPtr^.tokens := tPtr;
         tPtr^.token := token;
         tPtr^.tokenStart := tokenStart;
         tPtr^.tokenEnd := tokenEnd;
         slen := ord(ord4(chPtr) - ord4(tokenStart));
         sptr := pointer(GMalloc(slen+2));
         sptr^.length := slen;
         tcp := tokenStart;
         for i := 1 to slen do begin
            sptr^.str[i] := chr(tcp^);
            tcp := pointer(ord4(tcp)+1);
            end; {for}
         tPtr^.tokenString := sptr;
         NextToken;
         end; {while}
      mPtr^.readOnly := false;
      mPtr^.algorithm := 0;
      if IsDefined(mPtr^.name) then begin
         mf := macroFound;
         if mf^.parameters = mPtr^.parameters then begin
            tk1 := mf^.tokens;
            tk2 := mPtr^.tokens;
            while (tk1 <> nil) and (tk2 <> nil) do begin
               if tk1^.token.kind <> tk2^.token.kind then
                  goto 3;
               if tk1^.token.class = tk2^.token.class then
                  case tk1^.token.class of
                     reservedWord, reservedSymbol: ;
                     identifier:
                        if tk1^.token.name^ <> tk2^.token.name^ then
                           goto 3;
                     intConstant:
                        if tk1^.token.ival <> tk2^.token.ival then
                           goto 3;
                     longConstant:
                        if tk1^.token.lval <> tk2^.token.lval then
                           goto 3;
                     doubleConstant:
                        if tk1^.token.rval <> tk2^.token.rval then
                           goto 3;
                     stringConstant: begin
                        if tk1^.token.sval^.length <> tk2^.token.sval^.length
                           then goto 3;
                        for i := 1 to tk1^.token.sval^.length do
                           if tk1^.token.sval^.str[i] <>
                              tk2^.token.sval^.str[i] then
                              goto 3;
                        end;
                     macroParameter:
                        if tk1^.token.pnum <> tk2^.token.pnum then
                           goto 3;
                     otherwise:
                        Error(57);
                     end; {case}
               tk1 := tk1^.next;
               tk2 := tk2^.next;
               end; {while}
            if (tk1 = nil) and (tk2 = nil) then
               goto 2;
            end; {if}
3:       Error(5);
         goto 2;
         end; {if}
                                        {insert the macro in the macro list}
      bPtr := pointer(ord4(macros) + Hash(mPtr^.name));
      mPtr^.next := bPtr^;
      bPtr^ := mPtr;
      end {if}
   else
      Error(9);                         {identifier expected}
2:
   expandMacros := true;                {enable expansions}
   while parameterList <> nil do begin  {dump the parameter names}
      np := parameterList;
      parameterList := np^.next;
      dispose(np);
      end; {while}
   charKinds[ord('#')] := illegal;      {don't allow # as a token}
   saveNumber := false;                 {stop saving numeric strings}
   end; {DoDefine}


   procedure DoElif;
 
   { #elif expression                                            }
 
   var
      ip: ifPtr;                        {temp; for efficiency}

   begin {DoElif}
   ip := ifList;
   if ip <> nil then begin
                                        {decide if we should be skipping}
      tSkipping := ip^.status <> skippingToElse;
      if tSkipping then
         ip^.status := skippingToEndif
      else begin
         {evaluate the condition}
         NumericDirective;              {evaluate the condition}
         if token.kind <> eolsy then    {check for extra stuff on the line}
            Error(11);
         if expressionValue = 0 then
            ip^.status := skippingToElse
         else
            ip^.status := processing;
         tSkipping := ip^.status <> processing; {decide if we should be skipping}
         end; {else}
      end
   else
      Error(20);
   end; {DoElif}


   procedure DoElse;
 
   { #else                                                       }
 
   begin {DoElse}
   NextToken;                            {skip the command name}
   if token.kind <> eolsy then           {check for extra stuff on the line}
      Error(11);
   if ifList <> nil then begin
      if ifList^.elseFound then         {check for multiple elses}
         Error(19)
      else
         ifList^.elseFound := true;
                                        {decide if we should be skipping}
      tSkipping := ifList^.status <> skippingToElse;
      if tSkipping then                 {set the status}
         ifList^.status := skippingToEndif
      else
         ifList^.status := processing;
      end
   else
      Error(20);
   end; {DoElse}


   procedure DoEndif;
 
   { #endif                                                      }
 
   var
      ip: ifPtr;                        {used to create a new if record}
 
   begin {DoEndif}
   NextToken;                           {skip the command name}
   if token.kind <> eolsy then          {check for extra stuff on the line}
      Error(11);
   if ifList <> nil then begin
      ip := ifList;                     {remove the top if record from the list}
      ifList := ip^.next;
      dispose(ip);
      if ifList = nil then              {decide if we should be skipping}
         tSkipping := false
      else
         tSkipping := ifList^.status <> processing;
      end {if}
   else
      Error(20);
   end; {DoEndif}


   procedure DoError;
 
   { #error STRING                                               }
 
   var
      i: integer;                       {loop variable}
      len: integer;                     {string length}
      msg: stringPtr;                   {error message ptr}

   begin {DoError}
   NextToken;                           {skip the command name}
   if token.kind = stringConst then begin
      numErrors := numErrors+1;
      new(msg);
      len := token.sval^.length;
      if len > 246 then
         len := 246;
      msg^ := '#error: ';
      for i := 1 to len do
         msg^ := concat(msg^, token.sval^.str[i]);
      writeln(msg^);
      if terminalErrors then begin
         if enterEditor then
            ExitToEditor(msg, ord4(firstPtr)-ord4(bofPtr))
         else
            TermError(0);
         end; {if}
      end {if}
   else
      Error(83);
   NextToken;                            {skip the command name}
   if token.kind <> eolsy then           {check for extra stuff on the line}
      Error(11);
   end; {DoError}


   procedure DoFloat;

   { #pragma float NUMBER NUMBER                                }

   begin {DoFloat}
   FlagPragmas(p_float);
   NextToken;
   if token.kind in [intconst,uintconst] then begin
      floatCard := token.ival;
      NextToken;
      end {if}
   else
      Error(18);
   if token.kind in [intconst,uintconst] then begin
      floatSlot := $C080 | (token.ival << 4);
      NextToken;
      end {if}
   else
      Error(18);
   end; {DoFloat}


   procedure DoKeep;
 
   { #pragma keep FILENAME                                      }

   begin {DoKeep}
   FlagPragmas(p_keep);
   if GetFileName(false) then begin	{read the file name}
      if foundFunction then
         Error(17);
      if liDCBGS.kFlag = 0 then begin	{use the old name if there is one...}
         liDCBGS.kFlag := 1;
         outFileGS.theString.theString := workString;
         outFileGS.theString.size := length(workString);
         end; {if}
      end; {if}
   end; {DoKeep}


   procedure DoNBA;

   { #pragma nba MAIN						}

   begin {DoNBA}
   FlagPragmas(p_nba);
   isNBA := true;
   NextToken;                           {skip the command name}
   if token.kind = ident then begin     {get the open name}
      openName := token.name;
      NextToken;
      end {if}
   else begin
      isNBA := false;
      Error(9);
      end; {else}
   if token.kind <> eolsy then          {make sure there is nothing else on the line}
      Error(11);
   end; {DoNBA}


   procedure DoNDA;

   { #pragma nda OPEN CLOSE ACTION INIT PERIOD EVENTMASK MENULINE}


      function GetInteger: integer;

      { Get a signed integer constant				}

      var
         isNegative: boolean;		{is the value negative?}
         value: integer;		{value to return}

      begin {GetInteger}
      isNegative := false;
      value := 0;
      if token.kind = plusch then
         NextToken
      else if token.kind = minusch then begin
         NextToken;
         isNegative := true;
         end; {else if}
      if token.kind in [intconst,uintconst] then begin
	 value := token.ival;
	 NextToken;
	 end {if}
      else begin
	 isNewDeskAcc := false;
	 Error(18);
	 end; {else}
      if isNegative then
         GetInteger := -value
      else
         GetInteger := value;
      end; {GetInteger}


   begin {DoNDA}
   FlagPragmas(p_nda);
   isNewDeskAcc := true;
   NextToken;                           {skip the command name}
   if token.kind = ident then begin     {get the open name}
      openName := token.name;
      NextToken;
      end {if}
   else begin
      isNewDeskAcc := false;
      Error(9);
      end; {else}
   if token.kind = ident then begin     {get the close name}
      closeName := token.name;
      NextToken;
      end {if}
   else begin
      isNewDeskAcc := false;
      Error(9);
      end; {else}
   if token.kind = ident then begin     {get the action name}
      actionName := token.name;
      NextToken;
      end {if}
   else begin
      isNewDeskAcc := false;
      Error(9);
      end; {else}
   if token.kind = ident then begin     {get the init name}
      initName := token.name;
      NextToken;
      end {if}
   else begin
      isNewDeskAcc := false;
      Error(9);
      end; {else}
   refreshPeriod := GetInteger;		{get the period}
   eventMask := GetInteger;		{get the event Mask}
   if token.kind = stringconst then     {get the name}
      begin
      LongToPString(@menuLine, token.sval);
      NextToken;
      end {if}
   else begin
      isNewDeskAcc := false;
      Error(83);
      end; {else}
   if token.kind <> eolsy then          {make sure there is nothing else on the line}
      Error(11);
   end; {DoNDA}


   procedure DoUndef;
 
   { #undef                                                      }
 
   label 1;
 
   var
      bPtr: ^macroRecordPtr;            {hash bucket pointer}
      mPtr,lastPtr: macroRecordPtr;     {work pointers}
 
   begin {DoUndef}
   expandMacros := false;               {block expansions}
   NextToken;                           {get the token name}
                                        {convert reserved words to identifiers}
   if token.class = reservedWord then begin
      token.name := @reservedWords[token.kind];
      token.kind := ident;
      token.class := identifier;
      end; {if}
   if token.kind = ident then begin
                                        {find the bucket to search}
      bPtr := pointer(ord4(macros)+Hash(token.name));
      lastPtr := nil;                   {find and delete the macro entry}
      mPtr := bPtr^;
      while mPtr <> nil do begin
         if mPtr^.name^ = token.name^ then begin
            if mPtr^.readOnly then
               Error(10)
            else begin
               if lastPtr = nil then
                  bPtr^ := mPtr^.next
               else
                  lastPtr^.next := mPtr^.next;
               end; {else}
            goto 1;
            end; {if}
         lastPtr := mPtr;
         mPtr := mPtr^.next;
         end; {while}
      end {if}
   else
      Error(9);                         {identifier expected}
1:
   expandMacros := true;                {enable expansions}
   NextToken;                           {skip the macro name}
   if token.kind <> eolsy then          {make sure there's no junk on the line}
      Error(11);
   end; {DoUndef}


   procedure DoXCMD;

   { #pragma xcmd MAIN						}

   begin {DoXCMD}
   FlagPragmas(p_xcmd);
   isXCMD := true;
   NextToken;                           {skip the command name}
   if token.kind = ident then begin     {get the open name}
      openName := token.name;
      NextToken;
      end {if}
   else begin
      isXCMD := false;
      Error(9);
      end; {else}
   if token.kind <> eolsy then          {make sure there is nothing else on the line}
      Error(11);
   end; {DoXCMD}


begin {PreProcess}
lPrintMacroExpansions := printMacroExpansions; {inhibit token printing}
printMacroExpansions := false;
lReportEOL := reportEOL;                {we need to see eol's}
reportEOL := true;
tSkipping := skipping;                  {don't skip the directive name!}
skipping := false;
NextCh;                                 {skip the '#' char}
while charKinds[ord(ch)] = ch_white do  {skip white space}
   NextCh;
if ch in ['a','d','e','i','l','p','u'] then begin
   NextToken;
   case token.kind of
      ifsy: begin
         NumericDirective;
         ProcessIf(expressionValue = 0);
         goto 2;
         end;
      elsesy: begin
         DoElse;
         goto 2;
         end;
      ident: begin
         case token.name^[1] of
            'a':
               if token.name^ = 'append' then begin
                  if tskipping then goto 2;
                  DoAppend;
                  goto 2;
                  end; {if}
            'd':
               if token.name^ = 'define' then begin
                  if tskipping then goto 2;
                  DoDefine;
                  goto 2;
                  end; {if}
            'e':
               if token.name^ = 'endif' then begin
                  DoEndif;
                  goto 2;
                  end {if}
               else if token.name^ = 'else' then begin
                  DoElse;
                  goto 2;
                  end {else if}
               else if token.name^ = 'elif' then begin
                  DoElif;
                  goto 2;
                  end {else if}
               else if token.name^ = 'error' then begin
                  if tskipping then goto 2;
                  DoError;
                  goto 2;
                  end; {else if}
            'i':
               if token.name^ = 'if' then begin
                  NumericDirective;
                  ProcessIf(expressionValue = 0);
                  goto 2;
                  end {if}
               else if token.name^ = 'ifdef' then begin
                  ProcessIf(not Defined);
                  goto 2;
                  end {else}
               else if token.name^ = 'ifndef' then begin
                  ProcessIf(Defined);
                  goto 2;
                  end {else}
               else if token.name^ = 'include' then begin
                  if tskipping then goto 2;
                  DoInclude(false);
                  goto 2;
                  end; {else}
            'l':
               if token.name^ = 'line' then begin
                  if tskipping then goto 2;
                  FlagPragmas(p_line);
                  NextToken;
                  if token.kind = intconst then begin
                     lineNumber := token.ival;
                     NextToken;
                     end {if}
                  else
                     Error(18);
                  if lineNumber < 0 then
                     lineNumber := 0;
                  if token.kind = stringconst then begin
                     LongToPString(
                        pointer(ord4(@sourceFileGS.theString)+1),
                        token.sval);
                     sourceFileGS.theString.size := token.sval^.length;
                     NextToken;
                     end; {if}
                  if token.kind <> eolsy then
                     Error(11);
                  goto 2;
                  end; {if}
            'p':
               if token.name^ = 'pragma' then begin
                  if tskipping then goto 2;
                  NextToken;
                  if token.name^ = 'keep' then
                     DoKeep
                  else if token.name^ = 'debug' then begin
                     { debug bits:                                 }
                     {     1 - range checking                      }
                     {     2 - create debug code                   }
                     {     4 - generate profiles                   }
                     {     8 - generate traceback code             }
                     {    16 - check for stack errors              }
		     FlagPragmas(p_debug);
                     NumericDirective;
                     val := long(expressionValue).lsw;
                     rangeCheck  := odd(val);
                     debugFlag   := odd(val >> 1);
                     profileFlag := odd(val >> 2);
                     traceBack   := odd(val >> 3);
                     checkStack  := odd(val >> 4);
                     profileFlag := profileFlag or debugFlag;
                     if token.kind <> eolsy then
                        Error(11);
                     goto 2;
                     end {else}
                  else if token.name^ = 'lint' then begin
		     FlagPragmas(p_lint);
                     NumericDirective;   
                     lint := long(expressionValue).lsw;
                     if token.kind <> eolsy then
                        Error(11);
                     goto 2;
                     end {else}
                  else if token.name^ = 'memorymodel' then begin
		     FlagPragmas(p_memorymodel);
                     NumericDirective;  
                     smallMemoryModel := expressionValue = 0;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'expand' then begin
		     FlagPragmas(p_expand);
                     NumericDirective;         
                     lPrintMacroExpansions := expressionValue <> 0;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'optimize' then begin
                     { optimize bits:                              }
                     {     1 - intermediate code peephole          }
                     {     2 - native peephole                     }
                     {     4 - register value tracking             }
                     {     8 - remove stack checks                 }
                     {    16 - common subexpression elimination    }
                     {    32 - loop invariant removal		   }
		     FlagPragmas(p_optimize);
                     NumericDirective;    
                     val := long(expressionValue).lsw;
                     peepHole  := odd(val);
                     npeepHole := odd(val >> 1);
                     registers := odd(val >> 2);
                     saveStack := not odd(val >> 3);
                     commonSubexpression := odd(val >> 4);
                     loopOptimizations := odd(val >> 5);
                     strictVararg := not odd(val >> 6);
                     if saveStack or strictVararg then
                        npeepHole := false;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'unix' then begin
                     { unix bits:                                  }
                     {     1 - int is 32 bits			   }
		     FlagPragmas(p_unix);
                     NumericDirective;    
                     val := long(expressionValue).lsw;
                     unix_1 := odd(val);
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'stacksize' then begin
		     FlagPragmas(p_stacksize);
                     NumericDirective;      
                     stackSize := long(expressionValue).lsw;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'cda' then
                     DoCDA
                  else if token.name^ = 'cdev' then
                     DoCDev
                  else if token.name^ = 'nda' then
                     DoNDA
                  else if token.name^ = 'nba' then
                     DoNBA
                  else if token.name^ = 'xcmd' then
                     DoXCMD
                  else if token.name^ = 'toolparms' then begin
		     FlagPragmas(p_toolparms);
                     NumericDirective;       
                     toolParms := expressionValue <> 0;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'databank' then begin
		     FlagPragmas(p_databank);
                     NumericDirective;       
                     dataBank := expressionValue <> 0;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'float' then
                     DoFloat
                  else if token.name^ = 'rtl' then begin
		     FlagPragmas(p_rtl);
                     rtl := true;           
                     NextToken;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'noroot' then begin
		     FlagPragmas(p_noroot);
                     noroot := true;   
                     NextToken;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
{                 else if token.name^ = 'printmacros' then begin {debug}
{                    PrintMacroTable;
                     NextToken;
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'path' then begin
                     NextToken;
		     if token.kind = stringConst then begin
                	LongToPString(workString, token.sval);
                	AddPath(workString);
                        NextToken;
			end {if}
		     else
			Error(83);
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if token.name^ = 'ignore' then begin
                     { ignore bits:                                        }
                     {     1 - don't flag illegal tokens in skipped source }
                     {     8 - allow // comments                           }
                     FlagPragmas(p_ignore);
                     NumericDirective;    
                     val := long(expressionValue).lsw;
                     skipIllegalTokens := odd(val);
                     slashSlashComments := odd(val >> 3);
                     if token.kind <> eolsy then
                        Error(11);
                     end {else if}
                  else if (lint & lintPragmas) <> 0 then
                     Error(110);
                  goto 2;
                  end; {if}
            'u':
               if token.name^ = 'undef' then begin
                  if tskipping then goto 2;
                  DoUndef;
                  goto 2;
                  end; {if}
            otherwise: Error(57);
            end; {case}
         end;
      otherwise: ;
      end; {case}
   end {if}
else if charKinds[ord(ch)] = ch_eol     {allow null commands}
   then begin
   NextToken;
   goto 2;
   end; {else if}
Error(8);                               {bad preprocessor command}
2:
charKinds[ord('#')] := ch_pound;        {allow # as a token}
expandMacros := false;                  {skip to the end of the line}
flagOverflows := false;
skipping := tSkipping;
while not (token.kind in [eolsy,eofsy]) do
   NextToken;
flagOverflows := true;
expandMacros := true;
charKinds[ord('#')] := illegal;         {don't allow # as a token}
reportEOL := lReportEOL;                {restore flags}
printMacroExpansions := lPrintMacroExpansions;
skipping := tskipping;
end; {PreProcess}

{-- Externally available routines ------------------------------}

procedure DoDefaultsDotH;

{ Handle the defaults.h file					}

var
   name: pString;			{name of the default file}

begin {DoDefaultsDotH}
name := defaultName;
if GetFileType(name) <> -1 then
   DoInclude(true);
end; {DoDefaultsDotH}


procedure Error {err: integer};

{ flag an error                                                 }
{                                                               }
{ err - error number                                            }

begin {Error}
if numErr = maxErr then                 {set the error number}
   errors[maxErr].num := 4
else begin
   numErr := numErr+1;
   numErrors := numErrors+1;
   liDCBGS.merrf := 16;
   errors[numErr].num := err;
   end; {else}
with errors[numErr] do begin            {record the position of the error}
   line := tokenLine;
   col := tokenColumn;
   end; {with}
codeGeneration := false;		{inhibit code generation}
end; {Error}


{procedure Error2 {loc, err: integer} {debug}

{ flag an error                                                 }
{                                                               }
{ loc - error location                                          }
{ err - error number                                            }

{begin {Error2}
{writeln('Error ', err:1, ' flagged at location ', loc:1);
Error(err);
end; {Error2}


procedure DoNumber {scanWork: boolean};

{  The current character starts a number - scan it            }
{                                                             }
{  Parameters:                                                }
{     scanWork - get characters from workString?              }
{                                                             }
{  Globals:                                                   }
{     ch - first character in sequence; set to first char     }
{             after sequence                                  }
{     workString - string to take numbers from                }

label 1;

var
   c2: char;                            {next character to process}
   i: integer;                          {loop index}
   isHex: boolean;                      {is the value a hex number?}
   isLong: boolean;                     {is the value a long number?}
   isReal: boolean;                     {is the value a real number?}
   numIndex: 0..maxLine;                {index into workString}
   sp: stringPtr;                       {for saving identifier names}
   stringIndex: 0..maxLine;             {length of the number string}
   unsigned: boolean;                   {is the number unsigned?}
   val: integer;                        {value of a digit}

   numString: pString;                  {characters in the number}


   procedure NextChar;

   {  Return the next character that is a part of the number    }

   begin {NextChar}
   if scanWork then begin
      if ord(workString[0]) <> numIndex then begin
         numIndex := numIndex+1;
         c2 := workString[numIndex];
         end {if}
      else
         c2 := ' ';
      end {if}
   else begin
      NextCh;
      c2 := ch;
      end; {else}
   end; {NextChar}


   procedure GetDigits;

   {  Read in a digit stream                                    }
   {                                                            }
   {  Variables:                                                }
   {     c2 - next character to process                         }
   {     numString - digit sequence added to this string        }
   {     stringIndex - length of the string                     }

   begin {GetDigits}
   while (charKinds[ord(c2)] = digit) or
      (isHex and (c2 in ['a'..'f','A'..'F'])) do begin
      if c2 in ['a'..'f'] then
         c2 := chr(ord(c2) & $5F);
      stringIndex := stringIndex+1;
      if stringIndex > 255 then begin
         Error(6);
         stringIndex := 1;
         end; {if}
      numString[stringIndex] := c2;
      NextChar;
      end; {while}
   end; {GetDigits}


begin {DoNumber}
isHex := false;                         {assume it's not hex}
isReal := false;                        {assume it's an integer}
isLong := false;                        {assume a short integer}
unsigned := false;                      {assume signed numbers}
stringIndex := 0;                       {no digits so far...}
if scanWork then begin                  {set up the scanner}
   numIndex := 0;
   NextChar;
   end {if}
else
   c2 := ch;
if c2 = '.' then begin                  {handle the case of no leading digits}
   stringIndex := 1;
   numString[1] := '0';
   end {if}
else begin
   GetDigits;                           {read the leading digit stream}
   if c2 in ['x','X'] then              {detect hex numbers}
      if stringIndex = 1 then
         if numString[1] = '0' then begin
            stringIndex := 2;
            numString[2] := 'X';
            NextChar;
            isHex := true;
            GetDigits;
            goto 1;
            end; {if}
   end;
if c2 = '.' then begin                  {handle a decimal}
   stringIndex := stringIndex+1;
   numString[stringIndex] := '.';
   NextChar;
   isReal := true;
   if charKinds[ord(c2)] = digit then
      GetDigits
   else if stringIndex = 2 then begin
      numString[3] := '0';
      stringIndex := 3;
      end; {else}
   end; {if}
if c2 in ['e','E'] then begin           {handle an exponent}
   stringIndex := stringIndex+1;
   numString[stringIndex] := 'e';
   NextChar;
   isReal := true;
   if c2 in ['+','-'] then begin
      stringIndex := stringIndex+1;
      numString[stringIndex] := c2;
      NextChar;
      end; {if}
   if c2 in ['0'..'9'] then
      GetDigits
   else begin
      stringIndex := stringIndex+1;
      numString[stringIndex] := '0';
      Error(101);
      end; {else}
   end; {if}
1:
while c2 in ['l','u','L','U'] do        {check for long or unsigned}
   if c2 in ['l','L'] then begin
      NextChar;
      if not isReal then
         isLong := true;
      end {if}
   else {if c2 in ['u','U'] then} begin
      NextChar;
      unsigned := true;
      if isReal then
         Error(91);
      end; {else}
if c2 in ['f','F'] then begin           {allow F designator on reals}
   if unsigned then
      Error(91);
   if not isReal then begin
      Error(100);
      isReal := true;
      end; {if}
   NextChar;
   end; {if}
numString[0] := chr(stringIndex);       {set the length of the string}
if isReal then begin                    {convert a real constant}
   token.kind := doubleConst;
   token.class := doubleConstant;
   if stringIndex > 80 then begin
      Error(6);
      token.rval := 0.0;
      end {if}
   else
      token.rval := cnvsd(numString);
   end {if}
else if numString[1] <> '0' then begin {convert a decimal integer}
   if (stringIndex > 5)
      or (not unsigned and (stringIndex = 5) and (numString > '32767'))
      or (unsigned and (stringIndex = 5) and (numString > '65535')) then
      isLong := true;
   if (stringIndex > 10) or
      ((stringIndex = 10) and (numString > '4294967295')) then begin
      numString := '0';
      if flagOverflows then
         Error(6);
      end; {if}
   if isLong then begin
      token.class := longConstant;
      token.lval := Convertsl(numString);
      if unsigned then
         token.kind := ulongConst
      else begin
         token.kind := longConst;
         if token.lval < 0 then
            token.kind := ulongConst;
         end; {else}
      end {if}
   else begin
      if unsigned then
         token.kind := uintConst
      else
         token.kind := intConst;
      token.class := intConstant;
      token.lval := Convertsl(numString);
      end; {else}
   end {else if}
else begin                            {hex & octal}
   token.lval := 0;
   if isHex then begin
      i := 3;
      while i <= length(numString) do begin
         if token.lval & $F0000000 <> 0 then begin
            i := maxint;
            if flagOverflows then
               Error(6);
            end {if}
         else begin
            if numString[i] > '9' then
               val := (ord(numString[i])-7) & $000F
            else
               val := ord(numString[i]) & $000F;
            token.lval := (token.lval << 4) | val;
            i := i+1;
            end; {else}
         end; {while}
      end {if}
   else begin
      i := 1;
      while i <= length(numString) do begin
         if token.lval & $E0000000 <> 0 then begin
            i := maxint;
            if flagOverflows then
               Error(6);
            end {if}
         else begin
            if numString[i] in ['8','9'] then
               Error(7);
            token.lval := (token.lval << 3) | (ord(numString[i]) & $0007);
            i := i+1;
            end; {else}
         end; {while}
      end; {else}
   if long(token.lval).msw <> 0 then
      isLong := true;
   if isLong then begin
      if unsigned then
         token.kind := ulongConst
      else
         token.kind := longConst;
      token.class := longConstant;
      end {if}
   else begin
      if (long(token.lval).lsw & $8000) <> 0 then
         unsigned := true;
      if unsigned then
         token.kind := uintConst
      else
         token.kind := intConst;
      token.class := intConstant;
      end; {else}
   end; {else}
if saveNumber then begin
   sp := pointer(GMalloc(length(numString)+1));
   CopyString(pointer(sp), @numString);
   token.numString := sp;
   end; {if}
if scanWork then                        {make sure we read all characters}
   if ord(workString[0]) <> numIndex then
      Error(63);
end; {DoNumber}


procedure InitScanner {start, end: ptr};

{ initialize the scanner                                        }
{                                                               }
{ start - pointer to the first character in the file            }
{ end - points one byte past the last character in the file     }

var
   chi: minChar..maxChar;               {loop variable}
   lch: char;                           {next command line character}
   cp: ptr;                             {character pointer}
   i: 0..hashSize;                      {loop variable}
   negative: boolean;                   {is a number nagative?}

   mp: macroRecordPtr;                  {for building the predefined macros}
   bp: ^macroRecordPtr;

   timeString: packed array[1..20] of char; {time from misc. tools}


   procedure NextCh;

   { Get the next character from the command line               }

   begin {NextCh}
   lch := chr(cp^);
   cp := pointer(ord4(cp)+1);
   tokenColumn := tokenColumn+1;
   if tokenColumn > infoStringGS.theString.size then
      lch := chr(0);
   end; {NextCh}


   function GetWord: stringPtr;

   { Read a word from the command line                          }

   var
      i: integer;                       {string index}
      sp: stringPtr;                    {string pointer}

   begin {GetWord}
   i := 0;
   while not (lch in [' ', chr(0), chr(9), '=']) do begin
      i := i+1;
      workString[i] := lch;
      NextCh;
      end; {while}
   workString[0] := chr(i);
   sp := pointer(malloc(length(workString)+1));
   CopyString(pointer(sp), @workString);
   GetWord := sp;
   end; {GetWord}


   function EscapeCh: integer;

   {  Find and return the next character in a string or char     }
   {  constant.  Handle escape sequences if they are found.      }
   {  (The character is returned as an ordinal value.)           }
   {                                                             }
   {  Globals:                                                   }
   {     lch - first character in sequence; set to first char    }
   {             after sequence                                  }
 
   label 1;
 
   var
      dig: 0..15;                       {value of a hex digit}
      skipChar: boolean;                {get next char when done?}
      val: 0..4095;                     {hex escape code value (scaled to 0..255)}

   begin {EscapeCh}
1: skipChar := true;
   if lch = '\' then begin
      NextCh;
      if lch in ['0'..'7','a','b','t','n','v','f','p','r','x'] then
         case lch of
            '0','1','2','3','4','5','6','7': begin
               val := 0;
               while lch in ['0'..'7'] do begin
                  val := (val << 3) | (ord(lch) & 7);
                  NextCh;
                  end; {while}
               EscapeCh := val & $FF;
               skipChar := false;
               end;
            'a': EscapeCh := 7;
            'b': EscapeCh := 8;
            't': EscapeCh := 9;
            'n': EscapeCh := 10;
            'v': EscapeCh := 11;
            'f': EscapeCh := 12;
            'p': begin
               EscapeCh := ord('p');
               ispstring := true;
               end;
            'r': EscapeCh := 13;
            'x': begin
               val := 0;
               NextCh;
               while lch in ['0'..'9','a'..'f','A'..'F'] do begin
                  if lch in ['0'..'9'] then
                     dig := ord(lch) & $0F
                  else begin
                     lch := chr(ord(lch)&$5F);
                     dig := ord(lch)-ord('A')+10;
                     end; {else}
                  val := (val << 4) | dig;
                  NextCh;
                  end; {while}
               skipChar := false;
               EscapeCh := val & $FF;
               end;
            otherwise: Error(57);
            end {case}
      else
         EscapeCh := ord(lch);
      end {if}
   else
      EscapeCh := ord(lch);
   if skipChar then
      NextCh;
   end; {EscapeCh}


   procedure GetString;

   { read a string token from the command line                  }

   var
      i: integer;                       {string length}
      setLength: boolean;               {is the current string a p-string?}
      sPtr: longstringPtr;              {work string pointer}

   begin {GetString}
   token.kind := stringconst;           {set up the token}
   token.class := stringConstant;
   i := 0;                              {set up for the string scan}
   ispstring := false;
   setLength := false;
   new(sPtr);
   NextCh;                              {skip the opening "}
                                        {read the characters}
   while not (charKinds[ord(lch)] in [ch_string,ch_eol,ch_eof]) do begin
      i := i+1;
      if i = longstringlen then begin
         i := 1001;
         Error(90);
         end; {if}
      sPtr^.str[i] := chr(EscapeCh);
      if (i = 1) and ispstring then
         setLength := true;
      end; {while}
   if lch = '"' then                    {process the end of the string}
      NextCh
   else
      Error(3);
   if setLength then                    {check for a p-string}
      sPtr^.str[1] := chr(i-1);
   token.ispstring := setLength;
   sPtr^.length := i;                   {set the string length}
   token.sval := pointer(Malloc(i+3));  {put the string in the string pool}
   CopyLongString(token.sval, pointer(sPtr));
   dispose(sPtr);
   token.sval^.str[i+1] := chr(0);      {add null in case the string is extended}
   end; {GetString}


begin {InitScanner}
printMacroExpansions := false;          {don't print the token list}
skipIllegalTokens := false;		{flag illegal tokens in skipped code}
slashSlashComments := true;		{allow // comments}
foundFunction := false;                 {no functions found so far}
fileList := nil;                        {no included files}
gettingFileName := false;               {not in GetFileName}
ifList := nil;                          {no conditional comp. records}
skipping := false;                      {not skipping tokens}
flagOverflows := true;                  {flag overflow errors?}
new(macros);                            {no preprocessor macros so far}
for i := 0 to hashSize do
   macros^[i] := nil;
pathList := nil;			{no additional search paths}
charKinds[ord('#')] := illegal;         {don't allow # as a token}
tokenList := nil;                       {nothing in putback buffer}
saveNumber := false;                    {don't save numbers}
expandMacros := true;                   {enable macro expansion}
reportEOL := false;                     {report eolsy as a token?}
lineNumber := 1;                        {start the line counter}
chPtr := start;                         {set the start, end pointers}
eofPtr := endPtr;
firstPtr := start;                      {first char in line}
numErr := 0;                            {no errors so far}
numErrors := 0;
includeCount := 0;			{no pending calls to EndInclude}
lint := 0;                              {turn off lint checks}
ch := chr(RETURN);                      {set the initial character}
needWriteLine := false;                 {no lines are pending}
switchLanguages := false;               {not switching languages}
lastWasReturn := false;                 {last char was not return}
doingstring := false;                   {not doing a string}
unix_1 := false;			{int is 16 bits}

new(mp);                                {__LINE__}
mp^.name := @'__LINE__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 1;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp);                                {__FILE__}
mp^.name := @'__FILE__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 2;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp);                                {__DATE__}
mp^.name := @'__DATE__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 3;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp);                                {__TIME__}
mp^.name := @'__TIME__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 4;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp);                                {__STDC__}
mp^.name := @'__STDC__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp);                                {__ORCAC__}
mp^.name := @'__ORCAC__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 5;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
new(mp);                                {__VERSION__}
mp^.name := @'__VERSION__';
mp^.parameters := -1;
mp^.tokens := nil;
mp^.readOnly := true;
mp^.algorithm := 6;
bp := pointer(ord4(macros) + hash(mp^.name));
mp^.next := bp^;
bp^ := mp;
SetDateTime;                            {set up the macro date/time strings}
					{set up the version string}
versionStrL := pointer(GMalloc(3 + length(versionStr)));
versionStrL^.length := length(versionStr);
versionStrL^.str := versionStr;

{Scan the command line options}
cp := @infoStringGS.theString.theString;
tokenLine := 0;
tokenColumn := 0;
NextCh;
repeat
   while lch in [' ', chr(9)] do        {skip leading blanks}
      NextCh;
   if lch = '-' then begin              {see if we have found one}
      NextCh;
      if lch in ['d','D'] then begin
         NextCh;                        {yes -> get the name}
         new(mp);                       {form the macro table entry}
         mp^.name := GetWord;
         mp^.parameters := -1;
         mp^.tokens := nil;
         mp^.readOnly := false;
         bp := pointer(ord4(macros) + hash(mp^.name));
         mp^.next := bp^;
         bp^ := mp;
         if lch = '=' then begin
            NextCh;                     {record the value}
            token.numString := nil;
            if lch in ['a'..'z', 'A'..'Z', '_'] then begin
               token.kind := ident;
               token.class := identifier;
               token.name := GetWord;
               token.symbolPtr := nil;
               end {if}
            else if lch in ['+','-'] then begin
               negative := lch = '-';
               NextCh;
               if lch in ['.','0'..'9'] then begin
                  token.name := GetWord;
                  DoNumber(true);
                  if negative then
                     case token.class of
                        intConstant   : token.ival := -token.ival;
                        longConstant  : token.lval := -token.lval;
                        doubleConstant: token.rval := -token.rval;
                        otherwise: ;
                        end; {case}
                  end {if}
               else begin
                  token.kind := intconst;
                  token.numString := nil;
                  token.class := intConstant;
                  token.ival := 0;
                  end; {else}
               end {else if}
            else if lch in ['.','0'..'9'] then begin
               token.name := GetWord;
               DoNumber(true);
               end {else if}
            else if lch = '"' then 
               GetString
            else
               Error(108);
            end {if}
         else begin
            token.kind := intconst;     {create the default value}
            token.numString := nil;
            token.class := intConstant;
            token.ival := 1;
            end; {else}
         new(mp^.tokens);               {add the value to the definition}
         with mp^.tokens^ do begin
            next := nil;
            tokenString := nil;
            expandEnabled := true;
            tokenStart := nil;
            tokenEnd := nil;
            end; {with}
         mp^.tokens^.token := token;
         end {if}
      else if lch in ['i','I'] then begin
         NextCh;                        {gat the pathname}
         if lch = '"' then begin
            GetString;
            LongToPString(workString, token.sval);
            AddPath(workString);
            end {if}
         else
            Error(103);
         end {if}
      else                              {not -p, -i: flag the error}
         Error(108);
      end {if}
   else if lch <> chr(0) then begin
      Error(108);                       {unknown option: flag the error}
      lch := chr(0);
      end; {else}
until lch = chr(0);                     {if more characters, loop}
end; {InitScanner}


procedure CheckIdentifier;

{ See if an identifier is a reserved word, macro or typedef     }

label 1;

var
   bPtr: ^macroRecordPtr;               {pointer to hash bucket}
   mPtr: macroRecordPtr;                {for checking list of macros}
   rword: tokenEnum;                    {loop variable}
   sp: stringPtr;                       {for saving identifier names}
   lPrintMacroExpansions: boolean;      {local copy of printMacroExpansions}

begin {CheckIdentifier}
if expandMacros then                    {handle macro expansions}
   if not skipping then begin
      mPtr := FindMacro(@workstring);
      if mPtr <> nil then begin
         Expand(mPtr);
         lPrintMacroExpansions := printMacroExpansions;
         printMacroExpansions := false;
         NextToken;
         printMacroExpansions := lPrintMacroExpansions;
         goto 1;
         end;
      end; {if}
                                        {see if it's a reserved word}
if workString[1] in ['a'..'g','i','l','p','r'..'w'] then
   for rword := wordHash[ord(workString[1])-ord('a')] to
      pred(wordHash[ord(succ(workString[1]))-ord('a')]) do
      if reservedWords[rword] = workString then begin
         token.kind := rword;
         token.class := reservedWord;
         goto 1;
         end; {if}
token.symbolPtr := nil;                 {see if it's a typedef name}
if FindSymbol(token,allSpaces,false,false) <> nil then begin
   if token.symbolPtr^.class = typedefsy then
      token.kind := typedef;
   token.name := token.symbolPtr^.name; {use the old name}
   end {if}
else begin                              {record the name}
   sp := pointer(Malloc(length(workString)+1));
   CopyString(pointer(sp), @workString);
   token.name := sp;
   end; {else}
1:
end; {CheckIdentifier}


procedure NextToken;

{ Read the next token from the file.                            }

label 1,2,3,4;

type
   three = (s100,s1000,s4000);          {these declarations are used for a}
   gstringPtr = ^gstringRecord;         { variable length string record   }
   gstringRecord = record
      case three of
          s100: (len1: integer;
                 str1: packed array[1..100] of char;
                 );
         s1000: (len2: integer;
                 str2: packed array[1..1000] of char;
                 );
         s4000: (len3: integer;
                 str3: packed array[1..longstringlen] of char;
                 );
      end;

var
   done: boolean;                       {loop termination}
   expandEnabled: boolean;              {can a token be expanded?}
   i: 0..maxint;                        {loop/index counter}
   inhibit: boolean;                    {inhibit macro expansion?}
   lPrintMacroExpansions: boolean;      {local copy of printMacroExpansions}
   mPtr: macroRecordPtr;                {for checking list of macros}
   setLength: boolean;                  {is the current string a p-string?}
   tPtr: tokenListRecordPtr;            {for removing tokens from putback buffer}
   tToken: tokenType;                   {for merging tokens}
   sPtr,tsPtr: gstringPtr;              {for forming string constants}


   function EscapeCh: integer;
 
   {  Find and return the next character in a string or char     }
   {  constant.  Handle escape sequences if they are found.      }
   {  (The character is returned as an ordinal value.)           }
   {                                                             }
   {  Globals:                                                   }
   {     ch - first character in sequence; set to first char     }
   {             after sequence                                  }
 
   label 1;
 
   var
      dig: 0..15;                       {value of a hex digit}
      skipChar: boolean;                {get next char when done?}
      val: 0..4095;                     {hex escape code value (scaled to 0..255)}

   begin {EscapeCh}
1: skipChar := true;
   if ch = '\' then begin
      NextCh;
      if ch in ['0'..'7','a','b','t','n','v','f','p','r','x'] then
         case ch of
            '0','1','2','3','4','5','6','7': begin
               val := 0;
               while ch in ['0'..'7'] do begin
                  val := (val << 3) | (ord(ch) & 7);
                  NextCh;
                  end; {while}
               EscapeCh := val & $FF;
               skipChar := false;
               end;
            'a': EscapeCh := 7;
            'b': EscapeCh := 8;
            't': EscapeCh := 9;
            'n': EscapeCh := 10;
            'v': EscapeCh := 11;
            'f': EscapeCh := 12;
            'p': begin
               EscapeCh := ord('p');
               ispstring := true;
               end;
            'r': EscapeCh := 13;
            'x': begin
               val := 0;
               NextCh;
               while ch in ['0'..'9','a'..'f','A'..'F'] do begin
                  if ch in ['0'..'9'] then
                     dig := ord(ch) & $0F
                  else begin
                     ch := chr(ord(ch)&$5F);
                     dig := ord(ch)-ord('A')+10;
                     end; {else}
                  val := (val << 4) | dig;
                  NextCh;
                  end; {while}
               skipChar := false;
               EscapeCh := val & $FF;
               end;
            otherwise: Error(57);
            end {case}
      else
         EscapeCh := ord(ch);
      end {if}
   else
      EscapeCh := ord(ch);
   if skipChar then
      NextCh;
   end; {EscapeCh}


begin {NextToken}
if ifList = nil then			{do pending EndInclude calls}
   while includeCount <> 0 do begin
      EndInclude(includeChPtr);
      includeCount := includeCount - 1;
      end; {while}
includeChPtr := chPtr;
3:
token.numstring := nil;			{wipe out old numstrings}
if tokenList <> nil then begin          {get a token put back by a macro}
   tPtr := tokenList;
   tokenList := tPtr^.next;
   expandEnabled := tPtr^.expandEnabled;
   token := tPtr^.token;
   tokenStart := tPtr^.tokenStart;
   tokenEnd := tPtr^.tokenEnd;
   dispose(tPtr);
   if token.kind = typedef then         {allow for typedefs in a macro}
      token.kind := ident;
   if token.kind = ident then begin
      CopyString(@workString, token.name);
      CheckIdentifier;
      end; {if}
{ dead code
   if token.kind = ident then
      if FindSymbol(token,allSpaces,false,false) <> nil then
         if token.symbolPtr^.class = typedefsy then
            token.kind := typedef;
}
4:
   while (token.kind = stringconst)
      and (tokenList <> nil)
      and (tokenList^.token.kind = stringconst) do begin
      Merge(token, tokenList^.token);
      tPtr := tokenList;
      tokenList := tPtr^.next;
      dispose(tPtr);
      end; {while}
   if expandMacros and expandEnabled and (not skipping) then
      if token.kind = ident then begin  {handle macro expansions}
         inhibit := false;
         if tokenList <> nil then
            if tokenList^.token.kind = poundpoundop then
               inhibit := true;
         if not inhibit then begin
            mPtr := FindMacro(token.name);
            if mPtr <> nil then begin
               Expand(mPtr);
               goto 3;
               end; {if}
            end; {if}
         end; {if}
   if tokenList <> nil then
      if tokenList^.token.kind = poundpoundop then begin
         tPtr := tokenList;
         tokenList := tPtr^.next;
         dispose(tPtr);
         if tokenList <> nil then begin
            tPtr := tokenList;
            tToken := token;
            Merge(tToken, tPtr^.token);
            tokenList := tPtr^.next;
            token := tToken;
            dispose(tPtr);
            goto 4;
            end; {if}
         end; {if}
   goto 2;
   end; {if}
                                        {skip white space}
while charKinds[ord(ch)] in [illegal,ch_white,ch_eol] do begin
   if charKinds[ord(ch)] = illegal then begin
      if (ch = '#') and (lastWasReturn or (token.kind = eolsy)) then
         PreProcess                     {call the preprocessor}
      else begin
         tokenLine := lineNumber;       {record a # token}
         tokenColumn := ord(ord4(chPtr)-ord4(firstPtr));
         tokenStart := pointer(ord4(chPtr)-1);
         tokenEnd := chPtr;
         if (not skipping) or (not (skipIllegalTokens or (ch = '#'))) then
            Error(1);
         NextCh;
         end; {else}
      end {if}
   else if (charKinds[ord(ch)] = ch_eol) and reportEOL then begin
      token.class := reservedSymbol;    {record an eol token}
      token.kind := eolsy;
      tokenLine := lineNumber;
      tokenColumn := ord(ord4(chPtr)-ord4(firstPtr));
      tokenStart := pointer(ord4(chPtr)-1);
      tokenEnd := chPtr;
      NextCh;
      goto 2;
      end {if}
   else begin                           {skip white space}
      if printMacroExpansions then
         if charKinds[ord(ch)] = ch_eol then
            writeln
         else
            write(ch);
      NextCh;
      end;
   end; {while}
tokenLine := lineNumber;                {record the position of the token}
tokenColumn := ord(ord4(chPtr)-ord4(firstPtr));
tokenStart := pointer(ord4(chPtr)-1);
token.class := reservedSymbol;          {default to the most common class}
case charKinds[ord(ch)] of

   ch_special  : begin
      token.kind := charSym[ord(ch)];
      NextCh;
      end;

   ch_eof:                              {end of file}
      token.kind := eofsy;

   ch_pound : begin                     {tokens that start with '#'}
      NextCh;
      if ch = '#' then begin
         token.kind := poundpoundop;
         NextCh;
         end
      else
         token.kind := poundch;
      end;

   ch_dash  : begin                     {tokens that start with '-'}
      NextCh;
      if ch = '>' then begin
         token.kind := minusgtop;
         NextCh;
         end
      else if ch = '-' then begin
         token.kind := minusminusop;
         NextCh;
         end
      else if ch = '=' then begin
         token.kind := minuseqop;
         NextCh;
         end
      else
         token.kind := minusch;
      end;

   ch_plus  : begin                     {tokens that start with '+'}
      NextCh;
      if ch = '+' then begin
         token.kind := plusplusop;
         NextCh;
         end
      else if ch = '=' then begin
         token.kind := pluseqop;
         NextCh;
         end
      else
         token.kind := plusch;
      end;

   ch_lt    : begin                     {tokens that start with '<'}
      NextCh;
      if ch = '<' then begin
         NextCh;
         if ch = '=' then begin
            token.kind := ltlteqop;
            NextCh;
            end
         else
            token.kind := ltltop;
         end
      else if ch = '=' then begin
         token.kind := lteqop;
         NextCh;
         end
      else
         token.kind := ltch;
      end;

   ch_gt    : begin                     {tokens that start with '>'}
      NextCh;
      if ch = '>' then begin
         NextCh;
         if ch = '=' then begin
            token.kind := gtgteqop;
            NextCh;
            end
         else
            token.kind := gtgtop;
         end
      else if ch = '=' then begin
         token.kind := gteqop;
         NextCh;
         end
      else
         token.kind := gtch;
      end;

   ch_eq    : begin                     {tokens that start with '='}
      NextCh;
      if ch = '=' then begin
         token.kind := eqeqop;
         NextCh;
         end
      else
         token.kind := eqch;
      end;

   ch_exc   : begin                     {tokens that start with '!'}
      NextCh;
      if ch = '=' then begin
         token.kind := exceqop;
         NextCh;
         end
      else
         token.kind := excch;
      end;

   ch_and   : begin                     {tokens that start with '&'}
      NextCh;
      if ch = '&' then begin
         token.kind := andandop;
         NextCh;
         end
      else if ch = '=' then begin
         token.kind := andeqop;
         NextCh;
         end
      else
         token.kind := andch;
      end;

   ch_bar   : begin                     {tokens that start with '|'}
      NextCh;
      if ch = '|' then begin
         token.kind := barbarop;
         NextCh;
         end
      else if ch = '=' then begin
         token.kind := bareqop;
         NextCh;
         end
      else
         token.kind := barch;
      end;

   ch_percent: begin                      {tokens that start with '%'}
      NextCh;
      if ch = '=' then begin
         token.kind := percenteqop;
         NextCh;
         end
      else
         token.kind := percentch;
      end;

   ch_carot : begin                     {tokens that start with '^'}
      NextCh;
      if ch = '=' then begin
         token.kind := caroteqop;
         NextCh;
         end
      else
         token.kind := carotch;
      end;

   ch_asterisk: begin                   {tokens that start with '*'}
      NextCh;
      if ch = '=' then begin
         token.kind := asteriskeqop;
         NextCh;
         end
      else
         token.kind := asteriskch;
      end;

   ch_slash : begin                     {tokens that start with '/'}
      NextCh;
      if ch = '=' then begin
         token.kind := slasheqop;
         NextCh;
         end
      else
         token.kind := slashch;
      end;

   ch_dot   : begin                     {tokens that start with '.'}
      if charKinds[chPtr^] = digit then
         DoNumber(false)
      else begin
         NextCh;
         token.kind := dotch;
         end; {else}
      end;

   ch_char  : begin                      {character constants}
      NextCh;
      token.kind := intconst;
      token.class := intConstant;
      if ch = '''' then begin
         if (not skipping) or (not skipIllegalTokens) then
            Error(2);
         token.ival := ord(' ');
         end {if}
      else
         token.ival := EscapeCh;
      if ch = '''' then
         NextCh
      else
         if (not skipping) or (not skipIllegalTokens) then
            Error(2);
      end;

   ch_string: begin                     {string constants}
      doingstring := true;              {change character scanning}
      token.kind := stringconst;        {set up the token}
      token.class := stringConstant;
      i := 0;                           {set up for the string scan}
      ispstring := false;
      setLength := false;
      new(sPtr,s100);
      NextCh;                           {skip the opening "}
                                        {read the characters}
      while not (charKinds[ord(ch)] in [ch_string,ch_eol,ch_eof]) do begin
         i := i+1;
         if i = 101 then begin
            sPtr^.len1 := 100;
            new(tsPtr,s1000);
            CopyLongString(pointer(tsPtr), pointer(sPtr));
            dispose(sPtr);
            sPtr := tsPtr;
            end {if}
         else if i = 1001 then begin
            sPtr^.len2 := 1000;
            new(tsPtr,s4000);
            CopyLongString(pointer(tsPtr), pointer(sPtr));
            dispose(sPtr);
            sPtr := tsPtr;
            end {else if}
         else if i = longstringlen then begin
            i := 1001;
            Error(90);
            end; {else if}
         sPtr^.str1[i] := chr(EscapeCh);
         if (i = 1) and ispstring then
            setLength := true;
         end; {while}
      doingstring := false;             {process the end of the string}
      if ch = '"' then
         NextCh
      else
         Error(3);
      if setLength then                 {check for a p-string}
         sPtr^.str1[1] := chr(i-1);
      token.ispstring := setLength;
      sPtr^.len1 := i;                  {set the string length}
      token.sval := pointer(Malloc(i+3)); {put the string in the string pool}
      CopyLongString(token.sval, pointer(sPtr));
      dispose(sPtr);
      doingstring := false;
      token.sval^.str[i+1] := chr(0);   {add null in case the string is extended}
      end;

   letter: begin                         {reserved words and identifiers}
      token.kind := ident;
      token.class := identifier;
      token.name := @workString;
      i := 0;
      while charKinds[ord(ch)] in [letter,digit] do begin
         i := i+1;
         workString[i] := ch;
         NextCh;
         end; {while}
      workString[0] := chr(i);
      CheckIdentifier;
      end;

   digit :                               {numeric constants}
      DoNumber(false);

   otherwise: Error(57);
   end; {case}
tokenEnd := pointer(ord4(chPtr)-1);     {record the end of the token}
2:
if skipping then                        {conditional compilation branch}
   if not (token.kind in [eofsy,eolsy]) then
      goto 3;
if token.kind = stringconst then        {handle adjacent strings}
   repeat
      if reportEOL then begin
         while charKinds[ord(ch)] = ch_white do
            NextCh;
         if charKinds[ord(ch)] = ch_eol then
            goto 1;
         end; {if}
      tToken := token;
      lPrintMacroExpansions := printMacroExpansions;
      printMacroExpansions := false;
      NextToken;
      printMacroExpansions := lPrintMacroExpansions;
      if token.kind = stringconst then begin
         Merge(tToken, token);
         done := false;
         end {if}
      else begin
         PutBackToken(token, true);
         done := true;
         end; {else}
      token := tToken;
   until done;
1:
if printMacroExpansions then		{print the token stream}
   PrintToken(token);
end; {NextToken}


procedure TermScanner;

{ Shut down the scanner.                                        }

begin {TermScanner}
if ifList <> nil then
   Error(21);
if numErr <> 0 then begin               {write any pending errors}
   firstPtr := chPtr;
   WriteLine;
   end; {if}
end; {TermScanner}

end.

{$append 'scanner.asm'}