home *** CD-ROM | disk | FTP | other *** search
/ ftp.ee.pdx.edu / 2014.02.ftp.ee.pdx.edu.tar / ftp.ee.pdx.edu / pub / users / Harry / Blitz / BlitzSrc / lexer.cc < prev    next >
C/C++ Source or Header  |  2007-09-19  |  46KB  |  1,523 lines

  1. // lexer.cc  --  Routines for lexical analysis
  2. //
  3. // KPL Compiler
  4. //
  5. // Copyright 2002-2007, Harry H. Porter III
  6. //
  7. // This file may be freely copied, modified and compiled, on the sole
  8. // condition that if you modify it...
  9. //   (1) Your name and the date of modification is added to this comment
  10. //       under "Modifications by", and
  11. //   (2) Your name and the date of modification is added to the printHelp()
  12. //       routine in file "main.cc" under "Modifications by".
  13. //
  14. // Original Author:
  15. //   06/15/02 - Harry H. Porter III
  16. //
  17. // Modifcations by:
  18. //   03/15/06 - Harry H. Porter III
  19. //
  20. // The primary routines provided in this file are...
  21. //
  22. //   initScanner (filename)
  23. //   scan ()
  24. //
  25. // Information about the current token will be found in...
  26. //
  27. //   token        - the type of the token
  28. //     token.type        - the type of the token
  29. //     token.value       - additional info about the token
  30. //     token.tokenPos    - the position of the token (file, line no, char pos)
  31. //
  32. // Look-ahead tokens can also be found in...
  33. //
  34. //   token2
  35. //   token3
  36. //   token4
  37. //   token5
  38. //
  39. // The input is obtained from the operating system character-by-character,
  40. // using the following interface:
  41. //
  42. //   getc (inputFile)
  43. //   ungetc (ch, inputFile)
  44. //
  45. // After all tokens have been scanned, the pseudo-random integer "hashVal" will have been
  46. // computed.  Any change to the token stream will (with high probability) result in
  47. // a different value being stored in "hashVal".
  48.  
  49.  
  50.  
  51. #include "main.h"
  52.  
  53.  
  54.  
  55. // initScanner (filename)  -->  actualFileName
  56. //
  57. // This routine initializes the 5 token look-ahead buffer.  The tokens will come from the
  58. // file with the given name.  We try to open the file (for read-only) and if that fails,
  59. // we prepend the directory path prefix to the name and try again.
  60. //
  61. // We return the actual file name if we succeed and NULL if problems occurred
  62. // while opening the file.
  63. //
  64. // If 'filename' is NULL, we will open stdin and return "<stdin>".
  65. //
  66. char * initScanner (char * filename) {
  67.   char * currentInputFileName = "<filename missing>";
  68.   currentLineOfToken = 1;
  69.   currentCharPosOfToken = 0;
  70.   // newlinePreceedsThisToken = 0;
  71.   // newlinePreceedsToken2 = 0;
  72.   posOfNextToken = createTokenPos ();
  73.   token.type = COLON;
  74.   token.value.ivalue = 0;
  75.   token.tokenPos = posOfNextToken;
  76.   token2 = token;
  77.   token3 = token;
  78.   token4 = token;
  79.   token5 = token;
  80.   eofCount = 0;
  81.   hashVal = 0x12345678;
  82.   hashCount = 20;
  83.   if (filename == NULL) {
  84.     currentInputFileName = lookupAndAdd ("<stdin>", ID) -> chars;
  85.     inputFile = stdin;
  86.   } else {
  87.     currentInputFileName = filename;
  88.     // Open the input file.
  89.     inputFile = fopen (currentInputFileName, "r");
  90.     // If that fails, prepend the directory path prefix and try again.
  91.     if ((inputFile == NULL) && (commandDirectoryName != NULL)) {
  92.       currentInputFileName = appendStrings (commandDirectoryName, filename, "");
  93.       inputFile = fopen (currentInputFileName, "r");
  94.     }
  95.     // If that fails, then print an error, set things to the EOF state, and return.
  96.     if (inputFile == NULL) {
  97.       if (commandDirectoryName == NULL) {
  98.         fprintf (stderr, "%s:0: *****  LEXICAL ERROR: File could not be opened for reading\n", filename);
  99.       } else {
  100.         fprintf (stderr, "%s:0: *****  LEXICAL ERROR: File could not be opened for reading (also tried %s)\n", filename, currentInputFileName);
  101.       }
  102.       errorsDetected++;
  103.       token.type = EOF;
  104.       token.tokenPos = posOfNextToken;
  105.       tokenMinusOne = token;
  106.       token2 = token;
  107.       token3 = token;
  108.       token4 = token;
  109.       token5 = token;
  110.       return NULL;
  111.     }   
  112.   }
  113.   addToInputFilenames (currentInputFileName);
  114.   token5.type = getToken ();
  115.   token5.value = currentTokenValue;
  116.   token5.tokenPos = posOfNextToken;
  117.   addTokenToHash (token5);
  118.   token4 = token5;
  119.   scan ();
  120.   scan ();
  121.   scan ();
  122.   scan ();
  123.   return currentInputFileName;
  124. }
  125.  
  126.  
  127.  
  128. // scan ()
  129. //
  130. // This routine advances to the next token.  It shifts the 5 token look-ahead
  131. // buffer ahead by one token.  At EOF, the token types will all be EOF.
  132. //
  133. void scan () {
  134.   tokenMinusOne  = token;
  135.   token  = token2;
  136.   token2 = token3;
  137.   token3 = token4;
  138.   token4 = token5;
  139.   if (token5.type != EOF) {
  140.     token5.type = getToken ();
  141.     token5.value = currentTokenValue;
  142.     token5.tokenPos = posOfNextToken;
  143.     addTokenToHash (token5);
  144.   } else {
  145.     eofCount++;
  146.     if (eofCount > 10) {
  147.       programLogicError ("Parser is looping on EOF");
  148.     }
  149.   }
  150. }
  151.  
  152.  
  153.  
  154. // getToken ()
  155. //
  156. // Scan the next token and return it's type.  Side-effects the following:
  157. //   currentTokenValue
  158. //   currentInputFileIndex
  159. //   currentLineOfToken
  160. //   currentCharPosOfToken
  161. // Returns EOF repeatedly after end-of-file is reached.
  162. //
  163. int getToken (void) {
  164.   int ch, ch2, lengthError, numDigits;
  165.   int intVal, t, intOverflow, realOverflow, sign;
  166.   double realValue, exp, power;
  167.   char lexError2 [200];                 // buffer for varying error messages
  168.   char buffer [MAX_STR_LEN+1];          // buffer for saving a string
  169.   int next, i;
  170.  
  171.   while (1) {
  172.     ch = getNextChar ();
  173.  
  174.     // Process end-of-file...
  175.     if (ch == EOF) {
  176.       unGetChar (ch);
  177.       posOfNextToken = createTokenPos ();
  178.       return EOF;
  179.  
  180.     // Process newline...
  181.     } else if (ch == '\n') {
  182.       incrLineNumber ();
  183.       continue;
  184.  
  185.     // Process other white space...
  186.     } else if (ch == ' ' || ch == '\t') {
  187.       // do nothing
  188.       continue;
  189.     }
  190.  
  191.     posOfNextToken = createTokenPos ();
  192.  
  193.     // Process identifiers...
  194.     if (isalpha (ch)) {
  195.       lengthError = 0;
  196.       next = 0;
  197.       while (isalpha (ch) || isdigit (ch) || (ch=='_')) {
  198.         if (next >= MAX_STR_LEN) {
  199.           lengthError = 1;
  200.         } else {
  201.           buffer [next++] = ch;
  202.         }
  203.         ch = getNextChar ();
  204.       }
  205.       unGetChar (ch);
  206.       currentTokenValue.svalue = lookupAndAdd2 (buffer, next, ID);
  207.       // If already there, then its type may be BOOL, ..., IF, ..., or ID
  208.       if (lengthError) {
  209.         lexError ("Identifier exceeds maximum allowable length");
  210.       }
  211.       i = currentTokenValue.svalue->type;
  212.       return i;
  213.  
  214.     // Process strings...
  215.     } else if (ch == '"') {
  216.       next = 0;
  217.       lengthError = 0;
  218.       while (1) {
  219.         ch2 = getNextChar ();
  220.         if (ch2 == '"') {
  221.           break;
  222.         } else if (ch2 == '\n') {
  223.           incrLineNumber ();
  224.           if (next >= MAX_STR_LEN) {
  225.             lengthError = 1;
  226.           } else {
  227.             buffer [next++] = ch2;
  228.           }
  229.         } else if (ch2 == EOF) {
  230.           lexError ("End-of-file encountered within a string");
  231.           unGetChar (ch2);
  232.           break;
  233.         } else if ((ch2 < 32) || (ch2 > 126)) {
  234.           sprintf (lexError2,
  235.               "Illegal character 0x%02x in string ignored", ch2);
  236.           lexError (lexError2);
  237.         } else {
  238.           if (ch2 == '\\') {
  239.             ch2 = scanEscape ();
  240.           }
  241.           if (next >= MAX_STR_LEN) {
  242.             lengthError = 1;
  243.           } else {
  244.             buffer [next++] = ch2;
  245.           }
  246.         }
  247.       }
  248.       currentTokenValue.svalue = lookupAndAdd2 (buffer, next, ID);
  249.       if (lengthError) {
  250.         lexError ("Maximum string length exceeded");
  251.       }
  252.       return STRING_CONST;
  253.  
  254.     // Process operators...
  255.     } else if (isOpChar (ch)) {
  256.       lengthError = 0;
  257.       next = 0;
  258.       buffer [next++] = ch;
  259.       ch2 = getNextChar ();
  260.       // Check for -- comments...
  261.       if ((ch == '-') && (ch2 == '-')) {
  262.         while (1) {
  263.           ch = getNextChar ();
  264.           if (ch == EOF) {
  265.             lexError ("End-of-file encountered within a -- comment");
  266.             unGetChar (ch);
  267.             return EOF;
  268.           } else if (ch == '\n') {
  269.             incrLineNumber ();
  270.             break;
  271.           }
  272.         }
  273.         continue;
  274.       // Check for /* comments...
  275.       } else if ((ch == '/') && (ch2 == '*')) {
  276.         ch2 = ' ';
  277.         i = 1;
  278.         while (1) {
  279.           ch = ch2;
  280.           ch2 = getNextChar ();
  281.           if (ch2 == EOF) {
  282.             lexError ("End-of-file encountered within a /* comment");
  283.             unGetChar (ch2);
  284.             return EOF;
  285.           } else if (ch2 == '\n') {
  286.             incrLineNumber ();
  287.           }
  288.           if (ch == '/' && ch2 == '*') {
  289.             i++;
  290.             ch = ' ';
  291.             ch2 = ' ';
  292.           }
  293.           if (ch == '*' && ch2 == '/') {
  294.             if (i <= 1) {
  295.               break;
  296.             } else {
  297.               i--;
  298.               ch = ' ';
  299.               ch2 = ' ';
  300.             }
  301.           }
  302.         }
  303.         continue;
  304.       } else if (isOpChar (ch2)) {
  305.         buffer [next++] = ch2;
  306.         ch = getNextChar ();
  307.         while (isOpChar (ch)) {
  308.           if (next >= MAX_STR_LEN) {
  309.             lengthError = 1;
  310.           } else {
  311.             buffer [next++] = ch;
  312.           }
  313.           ch = getNextChar ();
  314.         }
  315.         unGetChar (ch);
  316.       } else {
  317.         unGetChar (ch2);
  318.       }
  319.       currentTokenValue.svalue = lookupAndAdd2 (buffer, next, OPERATOR);
  320.       if (lengthError) {
  321.         lexError ("Operator exceeds maximum allowable length");
  322.       }
  323.       return currentTokenValue.svalue->type;
  324.  
  325.     // Process character constants...
  326.     } else if (ch == '\'') {
  327.       currentTokenValue.ivalue = '?';
  328.       ch2 = getNextChar ();
  329.       if (ch2 == EOF) {
  330.         lexError ("End-of-file encountered within a char constant");
  331.         unGetChar (ch2);
  332.         return EOF;
  333.       } else if (ch2 == '\n') {
  334.         lexError ("End-of-line encountered within a char constant");
  335.         incrLineNumber ();
  336.         return CHAR_CONST;
  337.       }
  338.       if (ch2 == '\\') {
  339.         ch2 = scanEscape ();
  340.       }
  341.       currentTokenValue.ivalue = ch2;
  342.       ch2 = getNextChar ();
  343.       if (ch2 != '\'') {
  344.         unGetChar (ch2);
  345.         lexError ("Expecting closing quote in character constant");
  346.       }
  347.       return CHAR_CONST;
  348.  
  349.     // Process integer and double constants...
  350.     } else if (isdigit (ch)) {
  351.  
  352.       // See if we have 0x...*/
  353.       if (ch == '0') {
  354.         ch2 = getNextChar ();
  355.         if (ch2 == 'x') {
  356.           numDigits = 0;
  357.           ch = getNextChar ();
  358.           if (hexCharToInt (ch) < 0) {
  359.             lexError ("Must have a hex digit after 0x");
  360.           }
  361.           next = intVal = intOverflow = 0;
  362.           while (hexCharToInt (ch) >= 0) {
  363.             intVal = (intVal << 4) + hexCharToInt (ch);
  364.             numDigits++;
  365.             ch = getNextChar ();
  366.           }
  367.           unGetChar (ch);
  368.           if (numDigits > 8) {
  369.             lexError ("Hex constants must be 8 or fewer digits");
  370.             intVal = 0;
  371.           }
  372.           currentTokenValue.ivalue = intVal;
  373.           return INT_CONST;
  374.         }
  375.         unGetChar (ch2);
  376.       }
  377.  
  378.       // Otherwise we have a string of decimal numerals.
  379.       intVal = intOverflow = realOverflow = 0;
  380.       exp = 1.0;
  381.       realValue = 0.0;
  382.       while (isdigit (ch)) {
  383.         t = intVal * 10 + (ch - '0');
  384.         if (t < intVal) {
  385.           intOverflow = 1;
  386.         }
  387.         intVal = t;
  388.         realValue = (realValue * 10.0) + (double) (ch - '0');
  389.         if (realValue > DBL_MAX) {
  390.           realOverflow = 1;
  391.         }
  392.         ch = getNextChar ();
  393.       }
  394.       // If we have a real number...
  395.       if ((ch == '.') || (ch == 'e') || (ch == 'E')) {
  396.         // Read in the fractional part.
  397.         if (ch == '.') {
  398.           ch = getNextChar ();
  399.           if (!isdigit (ch)) {
  400.             lexError ("At least one digit is required after decimal point");
  401.           }
  402.           while (isdigit (ch)) {
  403.             exp *= 10.0;
  404.             realValue = realValue + ((double) (ch - '0') / exp);
  405.             ch = getNextChar ();
  406.           }
  407.         }
  408.         intVal = 0;
  409.         sign = 1;
  410.         if ((ch == 'e') || (ch == 'E')) {
  411.           ch = getNextChar ();
  412.           // Process the exponent sign, if there is one.
  413.           if (ch == '+') {
  414.             ch = getNextChar ();
  415.           } else if (ch == '-') {
  416.             ch = getNextChar ();
  417.             sign = -1;
  418.           }
  419.           // Read in the exponent integer into intVal.
  420.           if (!isdigit (ch)) {
  421.             lexError ("Expecting exponent numerals");
  422.           } else {
  423.             intVal = intOverflow = 0;
  424.             while (isdigit (ch)) {
  425.               t = intVal * 10 + (ch - '0');
  426.               if (t < intVal) {
  427.                 intOverflow = 1;
  428.               }
  429.               intVal = t;
  430.               ch = getNextChar ();
  431.             }
  432.             if (intOverflow) {
  433.               lexError ("Exponent is out of range");
  434.               intVal = 0;
  435.             }
  436.           }
  437.         }
  438.         unGetChar (ch);
  439.         currentTokenValue.rvalue = 999.888;
  440.         // power =  pow ((double) 10.0, (double) (sign * intVal));
  441.         power =  raisedToThePower (sign * intVal);
  442.         realValue = realValue * power;
  443.         if (realValue > DBL_MAX) {
  444.           realOverflow = 1;
  445.         }
  446.         currentTokenValue.rvalue = realValue;
  447.         if (realOverflow) {
  448.           lexError ("Real number is out of range");
  449.           currentTokenValue.rvalue = 0.0;
  450.         }
  451.         return DOUBLE_CONST;
  452.       } else {  // If we have an integer...
  453.         unGetChar (ch);
  454.         if (intOverflow) {
  455.           lexError ("Integer out of range (0..2147483647); use 0x80000000 for -2147483648");
  456.           intVal = 0;
  457.         }
  458.         currentTokenValue.ivalue = intVal;
  459.         return INT_CONST;
  460.       }
  461.  
  462.     // Check for one character symbols.
  463.     } else if (ch == '(') {
  464.       return L_PAREN;
  465.     } else if (ch == ')') {
  466.       return R_PAREN;
  467.     } else if (ch == '{') {
  468.       return L_BRACE;
  469.     } else if (ch == '}') {
  470.       return R_BRACE;
  471.     } else if (ch == '[') {
  472.       return L_BRACK;
  473.     } else if (ch == ']') {
  474.       return R_BRACK;
  475.     } else if (ch == ',') {
  476.       return COMMA;
  477.     } else if (ch == '.') {
  478.       return PERIOD;
  479.     } else if (ch == ':') {
  480.       return COLON;
  481.     } else if (ch == ';') {
  482.       return SEMI_COLON;
  483.  
  484.     // // Check for semi-colon and print special message.
  485.     // } else if (ch == ';') {
  486.     //   lexError ("The semi-colon is not used in the KPL programming language");
  487.  
  488.     // Otherwise, we have an invalid character; ignore it.
  489.     } else {
  490.       if ((ch>=' ') && (ch<='~')) {
  491.         sprintf (lexError2, "Illegal character '%c' ignored", ch);
  492.       } else {
  493.         sprintf (lexError2, "Illegal character 0x%02x ignored", ch);
  494.       }
  495.       lexError (lexError2);
  496.     }
  497.   }
  498. }
  499.  
  500.  
  501.  
  502. // lexError (msg)
  503. //
  504. // This routine is called to print an error message and the current line
  505. // number.  It returns.
  506. //
  507. void lexError (char *msg) {
  508.     fprintf (stderr, "%s", inputFileNames [currentInputFileIndex]);
  509.     if (currentLineOfToken >= 65535) {
  510.       fprintf (stderr, ":<line number not available>: ");
  511.     } else {
  512.       fprintf (stderr, ":%d: ", currentLineOfToken);
  513.     }
  514.     fprintf (stderr, "*****  LEXICAL ERROR: %s\n", msg);
  515.     errorsDetected++;
  516. }
  517.  
  518.  
  519.  
  520. // initKeywords ()
  521. //
  522. // This routine adds each keyword to the symbol table with the corresponding
  523. // type code.
  524. // This routine initializes the "keywords" and the "keywordTokens" arrays.
  525. //
  526. void initKeywords () {
  527.  
  528.   lookupAndAdd ("=", EQUAL);
  529.   lookupAndAdd ("alloc", ALLOC);
  530.   lookupAndAdd ("anyType", ANY_TYPE);
  531.   lookupAndAdd ("array", ARRAY);
  532.   lookupAndAdd ("arraySize", ARRAY_SIZE);
  533.   lookupAndAdd ("asInteger", AS_INTEGER);
  534.   lookupAndAdd ("asPtrTo", AS_PTR_TO);
  535.   lookupAndAdd ("behavior", BEHAVIOR);
  536.   lookupAndAdd ("bool", BOOL);
  537.   lookupAndAdd ("break", BREAK);
  538.   lookupAndAdd ("by", BY);
  539.   lookupAndAdd ("case", CASE);
  540.   lookupAndAdd ("catch", CATCH);
  541.   lookupAndAdd ("char", CHAR);
  542.   lookupAndAdd ("class", CLASS);
  543.   lookupAndAdd ("code", CODE);
  544.   lookupAndAdd ("const", CONST);
  545.   lookupAndAdd ("continue", CONTINUE);
  546.   lookupAndAdd ("debug", DEBUG);
  547.   lookupAndAdd ("default", DEFAULT);
  548.   lookupAndAdd ("do", DO);
  549.   lookupAndAdd ("double", DOUBLE);
  550.   lookupAndAdd ("else", ELSE);
  551.   lookupAndAdd ("elseIf", ELSE_IF);
  552.   lookupAndAdd ("endBehavior", END_BEHAVIOR);
  553.   lookupAndAdd ("endClass", END_CLASS);
  554.   lookupAndAdd ("endCode", END_CODE);
  555.   lookupAndAdd ("endFor", END_FOR);
  556.   lookupAndAdd ("endFunction", END_FUNCTION);
  557.   lookupAndAdd ("endHeader", END_HEADER);
  558.   lookupAndAdd ("endIf", END_IF);
  559.   lookupAndAdd ("endInterface", END_INTERFACE);
  560.   lookupAndAdd ("endMethod", END_METHOD);
  561.   lookupAndAdd ("endRecord", END_RECORD);
  562.   lookupAndAdd ("endSwitch", END_SWITCH);
  563.   lookupAndAdd ("endTry", END_TRY);
  564.   lookupAndAdd ("endWhile", END_WHILE);
  565.   lookupAndAdd ("enum", ENUM);
  566.   lookupAndAdd ("errors", ERRORS);
  567.   lookupAndAdd ("extends", EXTENDS);
  568.   lookupAndAdd ("external", EXTERNAL);
  569.   lookupAndAdd ("false", FALSE);
  570.   lookupAndAdd ("fields", FIELDS);
  571.   lookupAndAdd ("for", FOR);
  572.   lookupAndAdd ("free", FREE);
  573.   lookupAndAdd ("function", FUNCTION);
  574.   lookupAndAdd ("functions", FUNCTIONS);
  575.   lookupAndAdd ("header", HEADER);
  576.   lookupAndAdd ("if", IF);
  577.   lookupAndAdd ("implements", IMPLEMENTS);
  578.   lookupAndAdd ("infix", INFIX);
  579.   lookupAndAdd ("int", INT);
  580.   lookupAndAdd ("interface", INTERFACE);
  581.   lookupAndAdd ("isInstanceOf", IS_INSTANCE_OF);
  582.   lookupAndAdd ("isKindOf", IS_KIND_OF);
  583.   lookupAndAdd ("messages", MESSAGES);
  584.   lookupAndAdd ("method", METHOD);
  585.   lookupAndAdd ("methods", METHODS);
  586.   lookupAndAdd ("new", NEW);
  587.   lookupAndAdd ("null", NULL_KEYWORD);
  588.   lookupAndAdd ("of", OF);
  589.   lookupAndAdd ("prefix", PREFIX);
  590.   lookupAndAdd ("ptr", PTR);
  591.   lookupAndAdd ("record", RECORD);
  592.   lookupAndAdd ("renaming", RENAMING);
  593.   lookupAndAdd ("return", RETURN);
  594.   lookupAndAdd ("returns", RETURNS);
  595.   lookupAndAdd ("self", SELF);
  596.   lookupAndAdd ("sizeOf", SIZE_OF);
  597.   lookupAndAdd ("super", SUPER);
  598.   lookupAndAdd ("superclass", SUPER_CLASS);
  599.   lookupAndAdd ("switch", SWITCH);
  600.   lookupAndAdd ("throw", THROW);
  601.   lookupAndAdd ("to", TO);
  602.   lookupAndAdd ("true", TRUE);
  603.   lookupAndAdd ("try", TRY);
  604.   lookupAndAdd ("type", TYPE);
  605.   lookupAndAdd ("typeOfNull", TYPE_OF_NULL);
  606.   lookupAndAdd ("until", UNTIL);
  607.   lookupAndAdd ("uses", USES);
  608.   lookupAndAdd ("var", VAR);
  609.   lookupAndAdd ("void", VOID);
  610.   lookupAndAdd ("while", WHILE);
  611.  
  612. }
  613.  
  614.  
  615. // lookupAndAdd (givenStr, type)
  616. //
  617. // This routine is passed a pointer to a string of characters, terminated
  618. // by '\0'.  It looks it up in the string table.  If there is already an entry
  619. // in the table, it returns a pointer to the previously stored entry.
  620. // If not found, it allocates a new table entry, copies the new string into
  621. // the table, and returns a pointer to the new String.
  622. // If the string is new, then its 'type' is set according to newType.
  623. //
  624. String * lookupAndAdd (char * givenStr, int newType) {
  625.   return lookupAndAdd2 (givenStr, strlen(givenStr), newType);
  626. }
  627.  
  628.  
  629.  
  630. // lookupAndAdd2 (givenStr, length, type)
  631. //
  632. // This routine is passed a pointer to a sequence of any characters (possibly
  633. // containing \0, of length "length".  It looks this string up in the
  634. // string table.  If there is already an entry in the table, it returns
  635. // a pointer to the previously created entry.  If not found, it allocates
  636. // a new String, copies the new string into the entry, and returns
  637. // a pointer to the new String.
  638. //
  639. // After calling this routine, you can use pointer comparisons to check
  640. // for equality, rather than the more expensive test for character equality.
  641. // Furthermore, each different string will only be stored once, saving space.
  642. //
  643. // If the string is new, then its 'type' is set according to newType.
  644. //
  645. //
  646. String * lookupAndAdd2 (char * givenStr, int length, int newType) {
  647.   unsigned hashVal = 0, g;
  648.   char * p, * q;
  649.   int i;
  650.   String * stringPtr;
  651.  
  652.    // Compute the hash value for the givenStr and set hashVal to it.
  653.   for ( p = givenStr, i=0;
  654.         i < length;
  655.         p++, i++ ) {
  656.     hashVal = (hashVal << 4) + (*p);
  657.     if (g = hashVal & 0xf0000000) {
  658.       hashVal = hashVal ^ (g >> 24);
  659.       hashVal = hashVal ^ g;
  660.     }
  661.   }
  662.   hashVal %= STRING_TABLE_HASH_SIZE;
  663.  
  664.   // Search the linked list and return if we find it.
  665.   for (stringPtr = stringTableIndex [hashVal];
  666.                       stringPtr;
  667.                       stringPtr = stringPtr->next) {
  668.     if ((length == stringPtr->length) &&
  669.         (bytesEqual (stringPtr->chars, givenStr, length))) {
  670.       return stringPtr;
  671.     }
  672.   }
  673.  
  674.   // Create an entry and initialize it.
  675.   stringPtr = (String *)
  676.                 calloc (1, sizeof (String) + length + 1);
  677.   if (stringPtr == 0) {
  678.     fatalError ("Calloc failed in lookupAndAdd!");
  679.   }
  680.   for (p=givenStr, q=stringPtr->chars, i=length;
  681.        i>0;
  682.        p++, q++, i--) {
  683.     *q = *p;
  684.   }
  685.   *q = '\0';
  686.   stringPtr->length = length;
  687.   stringPtr->type = newType;
  688.   stringPtr->primitiveSymbol = 0;
  689.  
  690.   // Add the new entry to the appropriate linked list and return.
  691.   stringPtr->next = stringTableIndex [hashVal];
  692.   stringTableIndex [hashVal] = stringPtr;
  693.   return stringPtr;
  694. }
  695.  
  696.  
  697.  
  698. // bytesEqual (p, q, length)
  699. //
  700. // This function is passed two pointers to blocks of characters, and a
  701. // length.  It compares the two sequences of bytes and returns true iff
  702. // they are both equal.
  703. //
  704. int bytesEqual (char * p, char * q, int length) {
  705.   for (; length>0; length--, p++, q++) {
  706.     if (*p != *q) return 0;
  707.   }
  708.   return 1;
  709. }
  710.  
  711.  
  712.  
  713. // printStringTable ()
  714. //
  715. // This routine runs through the string table and prints each entry.
  716. //
  717. void printStringTable () {
  718.   int hashVal;
  719.   String * stringPtr;
  720.   printf ("LIST OF ALL STRINGS AND IDS\n");
  721.   printf ("===========================\n");
  722.   for (hashVal = 0; hashVal<STRING_TABLE_HASH_SIZE; hashVal++) {
  723.     for (stringPtr = stringTableIndex [hashVal];
  724.                        stringPtr;
  725.                        stringPtr = stringPtr->next) {
  726.       printString (stdout, stringPtr);
  727.       printf ("\n");
  728.     }
  729.   }
  730. }
  731.  
  732.  
  733.  
  734. // printString (file, str)
  735. //
  736. // This routine is passed a pointer to a String.  It prints the
  737. // string of the given file, translating non-printable characters into
  738. // escape sequences.  This routine will not print a terminating newline.
  739. //
  740. void printString (FILE * file, String * str) {
  741.   int i = 0;
  742.   if (str == NULL) {
  743.     fprintf (file, "*****NULL-STRING*****");
  744.     return;
  745.   }
  746.   for (i=0; i<str->length; i++) {
  747.     printChar (file, str->chars [i]);
  748.   }
  749. }
  750.     
  751.  
  752.  
  753. // printChar (file, int)
  754. //
  755. // This routine is passed a char, which could be any value 0..255.
  756. // It prints it on the given file (e.g. stdout) in a form such as
  757. //     a   \n   \xEF   ...etc...
  758. //
  759. void printChar (FILE * file, int c) {
  760.   int i, j;
  761.   if (c == '\"') {
  762.     fprintf (file, "\\\"");
  763.   } else if (c == '\'') {
  764.     fprintf (file, "\\\'");
  765.   } else if (c == '\\') {
  766.     fprintf (file, "\\\\");
  767.   } else if ((c>=32) && (c<127)) {
  768.     fprintf (file, "%c", c);
  769.   } else if (c == '\0') {
  770.     fprintf (file, "\\0");
  771.   } else if (c == '\a') {
  772.     fprintf (file, "\\a");
  773.   } else if (c == '\b') {
  774.     fprintf (file, "\\b");
  775.   } else if (c == '\t') {
  776.     fprintf (file, "\\t");
  777.   } else if (c == '\n') {
  778.     fprintf (file, "\\n");
  779.   } else if (c == '\v') {
  780.     fprintf (file, "\\v");
  781.   } else if (c == '\f') {
  782.     fprintf (file, "\\f");
  783.   } else if (c == '\r') {
  784.     fprintf (file, "\\r");
  785.   } else {
  786.     i = intToHexChar ((c>>4) & 0x0000000f);
  787.     j = intToHexChar (c & 0x0000000f);
  788.     fprintf (file, "\\x%c%c", i, j);
  789.   }
  790. }
  791.  
  792.  
  793.  
  794. // symbolName (s)
  795. //
  796. // This routine is passed a symbol such as BOOL.  It returns a pointer
  797. // to a string of characters, such as "BOOL".
  798. //
  799. char * symbolName (int i) {
  800.   switch (i) {
  801.     case EOF:            return "EOF";
  802.     case ID:            return "ID";
  803.     case INT_CONST:        return "INT_CONST";
  804.     case DOUBLE_CONST:        return "DOUBLE_CONST";
  805.     case CHAR_CONST:        return "CHAR_CONST";
  806.     case STRING_CONST:        return "STRING_CONST";
  807.     case OPERATOR:        return "OPERATOR";
  808.  
  809.     // keywords
  810.     case ALLOC:            return "ALLOC";
  811.     case ANY_TYPE:        return "ANY_TYPE";
  812.     case ARRAY:            return "ARRAY";
  813.     case ARRAY_SIZE:        return "ARRAY_SIZE";
  814.     case AS_INTEGER:        return "AS_INTEGER";
  815.     case AS_PTR_TO:        return "AS_PTR_TO";
  816.     case BEHAVIOR:        return "BEHAVIOR";
  817.     case BOOL:            return "BOOL";
  818.     case BREAK:            return "BREAK";
  819.     case BY:            return "BY";
  820.     case CASE:            return "CASE";
  821.     case CATCH:            return "CATCH";
  822.     case CHAR:            return "CHAR";
  823.     case CLASS:            return "CLASS";
  824.     case CODE:            return "CODE";
  825.     case CONST:            return "CONST";
  826.     case CONTINUE:        return "CONTINUE";
  827.     case DEBUG:            return "DEBUG";
  828.     case DEFAULT:        return "DEFAULT";
  829.     case DO:            return "DO";
  830.     case DOUBLE:        return "DOUBLE";
  831.     case ELSE:            return "ELSE";
  832.     case ELSE_IF:        return "ELSE_IF";
  833.     case END_BEHAVIOR:        return "END_BEHAVIOR";
  834.     case END_CLASS:        return "END_CLASS";
  835.     case END_CODE:        return "END_CODE";
  836.     case END_FOR:        return "END_FOR";
  837.     case END_FUNCTION:        return "END_FUNCTION";
  838.     case END_HEADER:        return "END_HEADER";
  839.     case END_IF:        return "END_IF";
  840.     case END_INTERFACE:        return "END_INTERFACE";
  841.     case END_METHOD:        return "END_METHOD";
  842.     case END_RECORD:        return "END_RECORD";
  843.     case END_SWITCH:        return "END_SWITCH";
  844.     case END_TRY:        return "END_TRY";
  845.     case END_WHILE:        return "END_WHILE";
  846.     case ENUM:            return "ENUM";
  847.     case ERRORS:        return "ERRORS";
  848.     case EXTENDS:        return "EXTENDS";
  849.     case EXTERNAL:        return "EXTERNAL";
  850.     case FALSE:            return "FALSE";
  851.     case FIELDS:        return "FIELDS";
  852.     case FOR:            return "FOR";
  853.     case FREE:            return "FREE";
  854.     case FUNCTION:        return "FUNCTION";
  855.     case FUNCTIONS:        return "FUNCTIONS";
  856.     case HEADER:        return "HEADER";
  857.     case IF:            return "IF";
  858.     case IMPLEMENTS:        return "IMPLEMENTS";
  859.     case INFIX:            return "INFIX";
  860.     case INT:            return "INT";
  861.     case INTERFACE:        return "INTERFACE";
  862.     case IS_INSTANCE_OF:    return "IS_INSTANCE_OF";
  863.     case IS_KIND_OF:        return "IS_KIND_OF";
  864.     case MESSAGES:        return "MESSAGES";
  865.     case METHOD:        return "METHOD";
  866.     case METHODS:        return "METHODS";
  867.     case NEW:            return "NEW";
  868.     case NULL_KEYWORD:        return "NULL";
  869.     case OF:            return "OF";
  870.     case PREFIX:        return "PREFIX";
  871.     case PTR:            return "PTR";
  872.     case RECORD:        return "RECORD";
  873.     case RENAMING:        return "RENAMING";
  874.     case RETURN:        return "RETURN";
  875.     case RETURNS:        return "RETURNS";
  876.     case SELF:            return "SELF";
  877.     case SIZE_OF:        return "SIZE_OF";
  878.     case SUPER:            return "SUPER";
  879.     case SUPER_CLASS:        return "SUPER_CLASS";
  880.     case SWITCH:        return "SWITCH";
  881.     case THROW:            return "THROW";
  882.     case TO:            return "TO";
  883.     case TRUE:            return "TRUE";
  884.     case TRY:            return "TRY";
  885.     case TYPE:            return "TYPE";
  886.     case TYPE_OF_NULL:        return "TYPE_OF_NULL";
  887.     case UNTIL:            return "UNTIL";
  888.     case USES:            return "USES";
  889.     case VAR:            return "VAR";
  890.     case VOID:            return "VOID";
  891.     case WHILE:            return "WHILE";
  892.  
  893.     // Punctuation tokens
  894.     case L_PAREN:        return "L_PAREN";
  895.     case R_PAREN:        return "R_PAREN";
  896.     case L_BRACE:        return "L_BRACE";
  897.     case R_BRACE:        return "R_BRACE";
  898.     case L_BRACK:        return "L_BRACK";
  899.     case R_BRACK:        return "R_BRACK";
  900.     case COLON:            return "COLON";
  901.     case SEMI_COLON:        return "SEMI_COLON";
  902.     case COMMA:            return "COMMA";
  903.     case PERIOD:        return "PERIOD";
  904.     case EQUAL:            return "EQUAL";
  905.  
  906.     // Symbols describing nodes in the Abstract Syntax Tree
  907.     case ALLOC_EXPR:        return "ALLOC_EXPR";
  908. //  case ANY_TYPE:        return "ANY_TYPE";
  909.     case ARGUMENT:        return "ARGUMENT";
  910.     case ARRAY_ACCESS:        return "ARRAY_ACCESS";
  911.     case ARRAY_SIZE_EXPR:    return "ARRAY_SIZE_EXPR";
  912.     case ARRAY_TYPE:        return "ARRAY_TYPE";
  913.     case AS_INTEGER_EXPR:    return "AS_INTEGER_EXPR";
  914.     case AS_PTR_TO_EXPR:    return "AS_PTR_TO_EXPR";
  915.     case ASSIGN_STMT:        return "ASSIGN_STMT";
  916. //  case BEHAVIOR:        return "BEHAVIOR";
  917.     case BOOL_CONST:        return "BOOL_CONST";
  918.     case BOOL_TYPE:        return "BOOL_TYPE";
  919.     case BREAK_STMT:        return "BREAK_STMT";
  920.     case CALL_EXPR:        return "CALL_EXPR";
  921.     case CALL_STMT:        return "CALL_STMT";
  922. //  case CASE:            return "CASE";
  923. //  case CATCH:            return "CATCH";
  924. //  case CHAR_CONST:        return "CHAR_CONST";
  925.     case CHAR_TYPE:        return "CHAR_TYPE";
  926.     case CLASS_DEF:        return "CLASS_DEF";
  927.     case CLASS_FIELD:        return "CLASS_FIELD";
  928.     case CLOSURE_EXPR:        return "CLOSURE_EXPR";
  929. //  case CODE:            return "CODE";
  930.     case CONST_DECL:        return "CONST_DECL";
  931.     case CONSTRUCTOR:        return "CONSTRUCTOR";
  932.     case CONTINUE_STMT:        return "CONTINUE_STMT";
  933.     case COUNT_VALUE:        return "COUNT_VALUE";
  934.     case DO_STMT:        return "DO_STMT";
  935. //  case DOUBLE_CONST:        return "DOUBLE_CONST";
  936.     case DOUBLE_TYPE:        return "DOUBLE_TYPE";
  937.     case DYNAMIC_CHECK:        return "DYNAMIC_CHECK";
  938.     case ERROR_DECL:        return "ERROR_DECL";
  939.     case FIELD_ACCESS:        return "FIELD_ACCESS";
  940.     case FIELD_INIT:        return "FIELD_INIT";
  941.     case FOR_STMT:        return "FOR_STMT";
  942. //  case FUNCTION:        return "FUNCTION";
  943.     case FUNCTION_PROTO:    return "FUNCTION_PROTO";
  944.     case FUNCTION_TYPE:        return "FUNCTION_TYPE";
  945.     case GLOBAL:        return "GLOBAL";
  946. //  case HEADER:        return "HEADER";
  947.     case IF_STMT:        return "IF_STMT";
  948. //  case INT_CONST:        return "INT_CONST";
  949.     case INT_TYPE:        return "INT_TYPE";
  950. //  case INTERFACE:        return "INTERFACE";
  951.     case IS_INSTANCE_OF_EXPR:    return "IS_INSTANCE_OF_EXPR";
  952.     case IS_KIND_OF_EXPR:    return "IS_KIND_OF_EXPR";
  953.     case LOCAL:            return "LOCAL";
  954. //  case METHOD:        return "METHOD";
  955.     case METHOD_PROTO:        return "METHOD_PROTO";
  956.     case NAMED_TYPE:        return "NAMED_TYPE";
  957.     case NEW_EXPR:        return "NEW_EXPR";
  958.     case NULL_CONST:        return "NULL_CONST";
  959.     case PARAMETER:        return "PARAMETER";
  960.     case PTR_TYPE:        return "PTR_TYPE";
  961.     case RECORD_FIELD:        return "RECORD_FIELD";
  962.     case RECORD_TYPE:        return "RECORD_TYPE";
  963.     case RETURN_STMT:        return "RETURN_STMT";
  964.     case SELF_EXPR:        return "SELF_EXPR";
  965.     case SEND_EXPR:        return "SEND_EXPR";
  966.     case SEND_STMT:        return "SEND_STMT";
  967.     case SIZE_OF_EXPR:        return "SIZE_OF_EXPR";
  968. //  case STRING_CONST:        return "STRING_CONST";
  969.     case SUPER_EXPR:        return "SUPER_EXPR";
  970.     case SWITCH_STMT:        return "SWITCH_STMT";
  971.     case THROW_STMT:        return "THROW_STMT";
  972.     case TRY_STMT:        return "TRY_STMT";
  973.     case TYPE_ARG:        return "TYPE_ARG";
  974.     case TYPE_DEF:        return "TYPE_DEF";
  975.     case TYPE_OF_NULL_TYPE:    return "TYPE_OF_NULL_TYPE";
  976.     case TYPE_PARM:        return "TYPE_PARM";
  977. //  case USES:            return "USES";
  978. //  case RENAMING:        return "RENAMING";
  979.     case VARIABLE_EXPR:        return "VARIABLE_EXPR";
  980.     case VOID_TYPE:        return "VOID_TYPE";
  981.     case WHILE_STMT:        return "WHILE_STMT";
  982.     case FREE_STMT:        return "FREE_STMT";
  983.     case DEBUG_STMT:        return "DEBUG_STMT";
  984.  
  985.     //  Symbols used to identify build-in function ids, and mess selectors
  986.     case DOUBLE_TO_INT:        return "DOUBLE_TO_INT";
  987.     case INT_TO_DOUBLE:        return "INT_TO_DOUBLE";
  988.     case INT_TO_CHAR:        return "INT_TO_CHAR";
  989.     case CHAR_TO_INT:        return "CHAR_TO_INT";
  990.     case PTR_TO_BOOL:        return "PTR_TO_BOOL";
  991.     case POS_INF:        return "POS_INF";
  992.     case NEG_INF:        return "NEG_INF";
  993.     case NEG_ZERO:        return "NEG_ZERO";
  994.     case I_IS_ZERO:        return "I_IS_ZERO";
  995.     case I_NOT_ZERO:        return "I_NOT_ZERO";
  996.     case BAR:            return "BAR";
  997.     case CARET:            return "CARET";
  998.     case AMP:            return "AMP";
  999.     case BAR_BAR:        return "BAR_BAR";
  1000.     case AMP_AMP:        return "AMP_AMP";
  1001.     case EQUAL_EQUAL:        return "EQUAL_EQUAL";
  1002.     case NOT_EQUAL:        return "NOT_EQUAL";
  1003.     case LESS:            return "LESS";
  1004.     case LESS_EQUAL:        return "LESS_EQUAL";
  1005.     case GREATER:        return "GREATER";
  1006.     case GREATER_EQUAL:        return "GREATER_EQUAL";
  1007.     case LESS_LESS:        return "LESS_LESS";
  1008.     case GREATER_GREATER:    return "GREATER_GREATER";
  1009.     case GREATER_GREATER_GREATER:    return "GREATER_GREATER_GREATER";
  1010.     case PLUS:            return "PLUS";
  1011.     case MINUS:            return "MINUS";
  1012.     case STAR:            return "STAR";
  1013.     case SLASH:            return "SLASH";
  1014.     case PERCENT:        return "PERCENT";
  1015.  
  1016.     case UNARY_BANG:        return "UNARY_BANG";
  1017.     case UNARY_STAR:        return "UNARY_STAR";
  1018.     case UNARY_AMP:        return "UNARY_AMP";
  1019.     case UNARY_MINUS:        return "UNARY_MINUS";
  1020.  
  1021.     // Symbols identifying primitive functions
  1022.     case PRIMITIVE_I_ADD:        return "PRIMITIVE_I_ADD";
  1023.     case PRIMITIVE_I_SUB:        return "PRIMITIVE_I_SUB";
  1024.     case PRIMITIVE_I_MUL:        return "PRIMITIVE_I_MUL";
  1025.     case PRIMITIVE_I_DIV:        return "PRIMITIVE_I_DIV";
  1026.     case PRIMITIVE_I_REM:        return "PRIMITIVE_I_REM";
  1027.     case PRIMITIVE_I_SLL:        return "PRIMITIVE_I_SLL";
  1028.     case PRIMITIVE_I_SRA:        return "PRIMITIVE_I_SRA";
  1029.     case PRIMITIVE_I_SRL:        return "PRIMITIVE_I_SRL";
  1030.     case PRIMITIVE_I_LT:        return "PRIMITIVE_I_LT";
  1031.     case PRIMITIVE_I_LE:        return "PRIMITIVE_I_LE";
  1032.     case PRIMITIVE_I_GT:        return "PRIMITIVE_I_GT";
  1033.     case PRIMITIVE_I_GE:        return "PRIMITIVE_I_GE";
  1034.     case PRIMITIVE_I_EQ:        return "PRIMITIVE_I_EQ";
  1035.     case PRIMITIVE_I_NE:        return "PRIMITIVE_I_NE";
  1036.     case PRIMITIVE_I_AND:        return "PRIMITIVE_I_AND";
  1037.     case PRIMITIVE_I_OR:        return "PRIMITIVE_I_OR";
  1038.     case PRIMITIVE_I_XOR:        return "PRIMITIVE_I_XOR";
  1039.     case PRIMITIVE_I_NOT:        return "PRIMITIVE_I_NOT";
  1040.     case PRIMITIVE_I_NEG:        return "PRIMITIVE_I_NEG";
  1041.     case PRIMITIVE_I_IS_ZERO:        return "PRIMITIVE_I_IS_ZERO";
  1042.     case PRIMITIVE_I_NOT_ZERO:        return "PRIMITIVE_I_NOT_ZERO";
  1043.     case PRIMITIVE_D_ADD:        return "PRIMITIVE_D_ADD";
  1044.     case PRIMITIVE_D_SUB:        return "PRIMITIVE_D_SUB";
  1045.     case PRIMITIVE_D_MUL:        return "PRIMITIVE_D_MUL";
  1046.     case PRIMITIVE_D_DIV:        return "PRIMITIVE_D_DIV";
  1047.     case PRIMITIVE_D_LT:        return "PRIMITIVE_D_LT";
  1048.     case PRIMITIVE_D_LE:        return "PRIMITIVE_D_LE";
  1049.     case PRIMITIVE_D_GT:        return "PRIMITIVE_D_GT";
  1050.     case PRIMITIVE_D_GE:        return "PRIMITIVE_D_GE";
  1051.     case PRIMITIVE_D_EQ:        return "PRIMITIVE_D_EQ";
  1052.     case PRIMITIVE_D_NE:        return "PRIMITIVE_D_NE";
  1053.     case PRIMITIVE_D_NEG:        return "PRIMITIVE_D_NEG";
  1054.     case PRIMITIVE_B_AND:        return "PRIMITIVE_B_AND";
  1055.     case PRIMITIVE_B_OR:        return "PRIMITIVE_B_OR";
  1056.     case PRIMITIVE_B_NOT:        return "PRIMITIVE_B_NOT";
  1057.     case PRIMITIVE_B_EQ:        return "PRIMITIVE_B_EQ";
  1058.     case PRIMITIVE_B_NE:        return "PRIMITIVE_B_NE";
  1059.     case PRIMITIVE_OBJECT_EQ:        return "PRIMITIVE_OBJECT_EQ";
  1060.     case PRIMITIVE_OBJECT_NE:        return "PRIMITIVE_OBJECT_NE";
  1061.     case PRIMITIVE_DEREF:        return "PRIMITIVE_DEREF";
  1062.     case PRIMITIVE_ADDRESS_OF:        return "PRIMITIVE_ADDRESS_OF";
  1063.  
  1064.     // Symbols identifying primitive functions
  1065.     case PRIMITIVE_INT_TO_DOUBLE:    return "PRIMITIVE_INT_TO_DOUBLE";
  1066.     case PRIMITIVE_DOUBLE_TO_INT:    return "PRIMITIVE_DOUBLE_TO_INT";
  1067.     case PRIMITIVE_INT_TO_CHAR:        return "PRIMITIVE_INT_TO_CHAR";
  1068.     case PRIMITIVE_CHAR_TO_INT:        return "PRIMITIVE_CHAR_TO_INT";
  1069.     case PRIMITIVE_PTR_TO_BOOL:        return "PRIMITIVE_PTR_TO_BOOL";
  1070.     case PRIMITIVE_POS_INF:        return "PRIMITIVE_POS_INF";
  1071.     case PRIMITIVE_NEG_INF:        return "PRIMITIVE_NEG_INF";
  1072.     case PRIMITIVE_NEG_ZERO:        return "PRIMITIVE_NEG_ZERO";
  1073.  
  1074. //    case PRIMITIVE_xxx:    return "yyy";
  1075.  
  1076.     // Misc Symbols
  1077.     case KEYWORD:        return "KEYWORD";
  1078.     case NORMAL:        return "NORMAL";
  1079.  
  1080.     default:
  1081.       printf ("\nSYMBOL = %d\n", i);
  1082.       return "*****  unknown symbol  *****";
  1083.   }
  1084. }
  1085.  
  1086.  
  1087.  
  1088. // printSymbol (int)
  1089. //
  1090. // Print this symbol out.  Used for debugging.
  1091. //
  1092. void printSymbol (int sym) {
  1093.   printf ("*****  Symbol = %s  *****\n", symbolName (sym));
  1094. }
  1095.  
  1096.  
  1097.  
  1098. // hexCharToInt (char)
  1099. //
  1100. // This routine is passed a character. If it is a hex digit, i.e.,
  1101. //    0, 1, 2, ... 9, a, b, ... f, A, B, ... F
  1102. // then it returns its value (0..15).  Otherwise, it returns -1.
  1103. //
  1104. int hexCharToInt (char ch) {
  1105.   if (('0'<=ch) && (ch<='9')) {
  1106.     return ch-'0';
  1107.   } else if (('a'<=ch) && (ch<='f')) {
  1108.     return ch-'a' + 10;
  1109.   } else if (('A'<=ch) && (ch<='F')) {
  1110.     return ch-'A' + 10;
  1111.   } else {
  1112.     return -1;
  1113.   }
  1114. }
  1115.  
  1116.  
  1117.  
  1118. // intToHexChar (int)
  1119. //
  1120. // This routine is passed an integer 0..15.  It returns a char
  1121. // from 0 1 2 3 4 5 6 7 8 9 A B C D E F
  1122. //
  1123. char intToHexChar (int i) {
  1124.   if (i<10) {
  1125.     return '0' + i;
  1126.   } else {
  1127.     return 'A' + (i-10);
  1128.   }
  1129. }
  1130.  
  1131.  
  1132.  
  1133. // raisedToThePower (int i)
  1134. //
  1135. // This routine returns 10**i.  "i" may be positive or negative.
  1136. // It uses a brute force approach.  There must be a better way...
  1137. //
  1138. double raisedToThePower (int i) {
  1139.   double d;
  1140.   if (i==0) {
  1141.     return 1.0;
  1142.   } else if (i > 0) {
  1143.     for (d=1.0; i>0; i--) {
  1144.       d = d * 10.0;
  1145.     }
  1146.     return d;
  1147.   } else {
  1148.     for (d=1.0; i<0; i++) {
  1149.       d = d / 10.0;
  1150.     }
  1151.     return d;
  1152.   }
  1153. }
  1154.  
  1155.  
  1156.  
  1157. // isEscapeChar (char)  -->  ASCII Value
  1158. //
  1159. // This routine is passed a char, such as 'n'.  If this char is one
  1160. // of the escape characters, (e.g., \n), then this routine returns the
  1161. // ASCII value (e.g., 10).  Otherwise, it returns -1.
  1162. //
  1163. int isEscape (char ch) {
  1164.   if (ch == '0') {
  1165.     return 0;
  1166.   } else if (ch == 'a') {
  1167.     return '\a';
  1168.   } else if (ch == 'b') {
  1169.     return '\b';
  1170.   } else if (ch == 't') {
  1171.     return '\t';
  1172.   } else if (ch == 'n') {
  1173.     return '\n';
  1174.   } else if (ch == 'v') {
  1175.     return '\v';
  1176.   } else if (ch == 'f') {
  1177.     return '\f';
  1178.   } else if (ch == 'r') {
  1179.     return '\r';
  1180.   } else if (ch == '\"') {
  1181.     return '\"';
  1182.   } else if (ch == '\'') {
  1183.     return '\'';
  1184.   } else if (ch == '\\') {
  1185.     return '\\';
  1186.   } else {
  1187.     return -1;
  1188.   }
  1189. }
  1190.  
  1191.  
  1192.  
  1193. // scanEscape ()
  1194. //
  1195. // This routine is called after we have gotten a back-slash.  It
  1196. // reads whatever characters follow and returns the character.  If
  1197. // problems arise it prints a message and returns '?'.  If EOF is
  1198. // encountered, it prints a message, calls unGetChar(EOF) and returns '?'.
  1199. //
  1200. int scanEscape () {
  1201.   int ch, ch2, i, j;
  1202.   ch2 = getNextChar ();
  1203.   if (ch2 == '\n') {
  1204.     lexError ("End-of-line encountered after a \\ escape");
  1205.     incrLineNumber ();
  1206.     return '?';
  1207.   }
  1208.   if (ch2 == EOF) {
  1209.     lexError ("End-of-file encountered after a \\ escape");
  1210.     unGetChar (ch2);
  1211.     return '?';
  1212.   }
  1213.   i = isEscape(ch2);
  1214.   if (i != -1) {
  1215.     return i;
  1216.   } else if (ch2 == 'x') {
  1217.     ch = getNextChar ();  // Get 1st hex digit
  1218.     if (ch == '\n') {
  1219.       lexError ("End-of-line encountered after a \\x escape");
  1220.       incrLineNumber ();
  1221.       return '?';
  1222.     }
  1223.     if (ch == EOF) {
  1224.       lexError ("End-of-file encountered after a \\x escape");
  1225.       unGetChar (ch);
  1226.       return '?';
  1227.     }
  1228.     i = hexCharToInt (ch);
  1229.     if (i < 0) {
  1230.       lexError ("Must have a hex digit after \\x");
  1231.       return '?';
  1232.     } else {
  1233.       ch2 = getNextChar ();
  1234.       if (ch2 == '\n') {
  1235.         lexError ("End-of-line encountered after a \\x escape");
  1236.         incrLineNumber ();
  1237.         return '?';
  1238.       }
  1239.       if (ch2 == EOF) {
  1240.         lexError ("End-of-file encountered after a \\x escape");
  1241.         unGetChar (ch2);
  1242.         return '?';
  1243.       }
  1244.       j = hexCharToInt (ch2);
  1245.       if (j < 0) {
  1246.         lexError ("Must have two hex digits after \\x");
  1247.         return '?';
  1248.       }
  1249.       return (i<<4) + j;
  1250.     }
  1251.   } else {
  1252.     lexError ("Illegal escape (only \\0, \\a, \\b, \\t, \\n, \\v, \\f, \\r, \\\", \\\', \\\\, and \\xHH allowed)");
  1253.     return '?';
  1254.   }
  1255. }
  1256.     
  1257.     
  1258.     
  1259. // isOpChar (char)
  1260. //
  1261. // Return true if this char is one of...
  1262. //     +  -  *  \  /  !  @  #  $  %  ^  &  ~  `  |  ?  <  >  =
  1263. // 
  1264. int isOpChar (char ch) {
  1265.   if (ch == '+') {
  1266.     return 1;
  1267.   } else if (ch == '-') { 
  1268.     return 1;
  1269.   } else if (ch == '*') { 
  1270.     return 1;
  1271.   } else if (ch == '/') { 
  1272.     return 1;
  1273.   } else if (ch == '\\') { 
  1274.     return 1;
  1275.   } else if (ch == '!') { 
  1276.     return 1;
  1277.   } else if (ch == '@') { 
  1278.     return 1;
  1279.   } else if (ch == '#') { 
  1280.     return 1;
  1281.   } else if (ch == '$') { 
  1282.     return 1;
  1283.   } else if (ch == '%') { 
  1284.     return 1;
  1285.   } else if (ch == '^') { 
  1286.     return 1;
  1287.   } else if (ch == '&') { 
  1288.     return 1;
  1289.   } else if (ch == '~') { 
  1290.     return 1;
  1291.   } else if (ch == '`') { 
  1292.     return 1;
  1293.   } else if (ch == '|') { 
  1294.     return 1;
  1295.   } else if (ch == '?') { 
  1296.     return 1;
  1297.   } else if (ch == '<') { 
  1298.     return 1;
  1299.   } else if (ch == '>') { 
  1300.     return 1;
  1301.   } else if (ch == '=') { 
  1302.     return 1;
  1303.   } else {
  1304.     return 0;
  1305.   }
  1306. }
  1307.  
  1308.  
  1309.  
  1310. // addToHash (int i)
  1311. //
  1312. // This routine is passed an integer.  It adds it into the running "hashVal".
  1313. // The "hashVal" is initialized before each file is processed.  Every token is used to
  1314. // modify this value (by calling this routine) so that, after completely processing a file,\
  1315. // the resulting "hashVal" will be a psuedo-random number, dependent on exactly what tokens
  1316. // were in the file and what order they were in.
  1317. //
  1318. // The idea is that any change to the tokens will almost certainly alter the computed
  1319. // "hashVal".  Comments and formatting changes, however, will not change the value.  This
  1320. // will be used later to check to make sure that compiled versions of files are consistent
  1321. // and that the header files have not been changed.  This technique is not fool-proof
  1322. // and should not be relied on; the programmer should make sure that header files are
  1323. // not changed between the several compilations that go into building an executable.
  1324. // Nevertheless, with this technique there is a reasonably good chance that a change
  1325. // to a header file will be detected and a runtime error can be signaled during
  1326. // program initialization.
  1327. //
  1328. void addToHash (int i) {
  1329.                 // printf ("     Starting:     0x%08x\n", hashVal);
  1330.                 // printf ("       i:            0x%08x   (%d)\n", i, i);
  1331.   // Rotate the current hashVal 1 bit to the left.
  1332.   if (hashVal < 0) {
  1333.     hashVal = (hashVal << 1) + 1;
  1334.   } else {
  1335.     hashVal = hashVal << 1;
  1336.   }
  1337.                 // printf ("       after <<:     0x%08x\n", hashVal);
  1338.   // Add the current integer in by xor-ing it.
  1339.   hashVal = hashVal ^ i;
  1340.                 // printf ("       After xor:    0x%08x\n", hashVal);
  1341.   // Next use the sequence number to add more info into the hashVal.
  1342.   hashCount = hashCount + 1;
  1343.   hashVal = hashVal ^ (hashCount * i);
  1344.                 // printf ("       hashCount:    0x%08x\n", hashCount);
  1345.                 // printf ("       hashCount*i:  0x%08x\n", hashCount*i);
  1346.                 // printf ("       Ending:       0x%08x\n", hashVal);
  1347. }
  1348.  
  1349.  
  1350.  
  1351. // addTokenToHash (token)
  1352. //
  1353. // This routine is passed a token.  It adds info about that token into the running
  1354. // hashVal computation.
  1355. //
  1356. void addTokenToHash (Token token) {
  1357.   int i;
  1358.   int * p;
  1359.   String * str;
  1360.   addToHash (token.type);
  1361.   switch (token.type) {
  1362.     case ID:
  1363.     case OPERATOR:
  1364.     case STRING_CONST:
  1365.       str = token.value.svalue;
  1366.       for (i=0; i<str->length; i++) {
  1367.         addToHash (str->chars [i]);
  1368.       }
  1369.       return;
  1370.     case CHAR_CONST:
  1371.     case INT_CONST:
  1372.       addToHash(token.value.ivalue);
  1373.       return;
  1374.     case DOUBLE_CONST:
  1375.       p = (int *) (& token.value.rvalue);
  1376.       addToHash(*p);
  1377.       p++;
  1378.       addToHash(*p);
  1379.       return;
  1380.   }
  1381. }
  1382.  
  1383.  
  1384.  
  1385. // createTokenPos () --> int
  1386. //
  1387. // This routine takes the following:
  1388. //   currentInputFileIndex  (FF = 0..255)
  1389. //   currentLineOfToken     (LLLL = 0..65535)
  1390. //   currentCharPosOfToken  (PP = 0..255)
  1391. // and combines them into one 32-bit integer, as follows:
  1392. //   FFLLLLPP
  1393. //
  1394. int createTokenPos () {
  1395.   return
  1396.     ((currentInputFileIndex & 0x000000ff) << 24) |
  1397.     ((currentLineOfToken & 0x0000ffff) << 8) |
  1398.     (currentCharPosOfToken & 0x000000ff);
  1399. }
  1400.  
  1401.  
  1402.  
  1403. // extractLineNumber (token) --> int
  1404. //
  1405. // Given a token, this routine returns the line number.
  1406. //
  1407. // This routine is not reliable for files of size >= 65535.
  1408. //
  1409. int extractLineNumber (Token token) {
  1410.   int i = (token.tokenPos & 0x00ffff00) >> 8;
  1411.   if (i >= 65535) {
  1412.     return -1;
  1413.   } else {
  1414.     return i;
  1415.   }
  1416. }
  1417.  
  1418.  
  1419.  
  1420. // extractCharPos (token) --> int
  1421. //
  1422. // Given a token, this routine returns the character position within the line.
  1423. //
  1424. int extractCharPos (Token token) {
  1425.   int i = token.tokenPos & 0x000000ff;
  1426.   if (i >= 255) {
  1427.     return -1;
  1428.   } else {
  1429.     return i;
  1430.   }
  1431. }
  1432.  
  1433.  
  1434.  
  1435. // extractFilename (token) --> char *
  1436. //
  1437. // Given a token, this routine returns the name of the file.
  1438. //
  1439. char * extractFilename (Token token) {
  1440.   int i = (token.tokenPos >> 24) & 0x000000ff;
  1441.   return inputFileNames [i];
  1442. }
  1443.  
  1444.  
  1445.  
  1446. // incrLineNumber ()
  1447. //
  1448. // This routine increments "currentLineOfToken".  The largest value (65535) is sticky.
  1449. //
  1450. void incrLineNumber () {
  1451.   if (currentLineOfToken >= 65535) {
  1452.     currentLineOfToken = 65535;
  1453.   } else {
  1454.     currentLineOfToken++;
  1455.   } 
  1456.   currentCharPosOfToken = 0;
  1457.   return;
  1458. }
  1459.  
  1460.  
  1461.  
  1462. // getNextChar () --> char
  1463. //
  1464. // This routine reads and returns the next character from the input file.
  1465. // It also increments "currentCharPosOfToken".  The largest value (255) is sticky.
  1466. //
  1467. int getNextChar () {
  1468.   if (currentCharPosOfToken >= 255) {
  1469.     currentCharPosOfToken = 255;
  1470.   } else {
  1471.     currentCharPosOfToken++;
  1472.   }
  1473.   return getc (inputFile);
  1474. }
  1475.  
  1476.  
  1477.  
  1478. // unGetChar (ch)
  1479. //
  1480. // This routine returns the given character to the input buffer.  It also
  1481. // decrements "currentCharPosOfToken".  The largest value (255) is sticky.
  1482. //   
  1483. void unGetChar (char ch) {
  1484.   if (currentCharPosOfToken >= 255) {
  1485.     currentCharPosOfToken = 255;
  1486.   } else {        
  1487.     currentCharPosOfToken--;
  1488.   }    
  1489.   ungetc (ch, inputFile);
  1490.   return;
  1491. }   
  1492.  
  1493.  
  1494.  
  1495. // addToInputFileNames (filename)
  1496. //
  1497. // This routine is passed a filename.  It addes it to the array "inputFileNames".
  1498. // The global variable "currentInputIndex" is:
  1499. //    -1 = initial value
  1500. //     0 = first file
  1501. //     1 = second file
  1502. //     ...
  1503. //     255 = max value, indicating too many files
  1504. // We keep an array of pointers to the file names.  Within the token, we store only
  1505. // an 8 bit value, which is used to index into this array.  This value can range from
  1506. // 0 to 255, but 255 is used to represent problems, and will be <filename not available>.
  1507. // The value 255 is sticky; note that MAX_INPUT_FILES = 255.
  1508. //
  1509. void addToInputFilenames (char * filename) {
  1510.   // printf ("addToInputFilenames: %s\n", filename);
  1511.   // printf ("currentInputFileIndex, before: %d\n", currentInputFileIndex);
  1512.   if (currentInputFileIndex >= MAX_INPUT_FILES-1) {
  1513.     fprintf (stderr, "%s:0: *****  LEXICAL ERROR: Maximum number of input files (%d) exceeded\n", filename, MAX_INPUT_FILES);
  1514.     errorsDetected++;
  1515.     currentInputFileIndex = MAX_INPUT_FILES;
  1516.     inputFileNames [currentInputFileIndex] = "<filename NOT available>";
  1517.   } else {
  1518.     currentInputFileIndex++;
  1519.     inputFileNames [currentInputFileIndex] = filename;
  1520.   }
  1521.   // printf ("currentInputFileIndex, after: %d\n", currentInputFileIndex);
  1522. }
  1523.