clex.c
上传用户:hbdengju
上传日期:2007-01-06
资源大小:11k
文件大小:15k
源码类别:

编译器/解释器

开发平台:

C/C++

  1. #ifndef INCLUDED_STREAM
  2. #include <stream.h>
  3. #endif
  4. #ifndef INCLUDED_STRING
  5. #include <string.h>
  6. #endif
  7. #ifndef INCLUDED_STDLIB
  8. #include <stdlib.h>
  9. #endif
  10. #ifndef INCLUDED_ASSERT
  11. #include <assert.h>
  12. #endif
  13. #ifndef INCLUDED_CTYPE
  14. #include <ctype.h>
  15. #endif
  16. #include "clex.h"
  17. // get string value tables, sym_str[] and keyword[] :
  18. #define CLEX_IMPLEMENTATION 1
  19. #include "clex_sym.h"
  20. /******************************************************************************
  21. *                                                                             *
  22. *  KWTABLE -- keyword hash table (internal use only)                          *
  23. *     KWtable implements a collision-free hash table of C++ keywords.  The    *
  24. *     table size and hash function are computed by use of a standalone C      *
  25. *     program, kwhash.c, included in this directory.                          *
  26. *                                                                             *
  27. ******************************************************************************/
  28. #define U_short unsigned short
  29. #define U_char  unsigned char
  30. struct KWtable
  31.     {
  32.     enum { HASHSIZE = 131 };  // as computed by kwhash.c, for a=9,b=2,c=2
  33.     struct  {
  34.             char* kwp;
  35.             Clex_sym sym;
  36.             } kwhash[HASHSIZE];
  37.     KWtable(char**);
  38.     U_short hash(const U_char*, U_short len);
  39.     void insert(char*, Clex_sym);
  40.     Clex_sym lookup(char*, short len);
  41.     };
  42. static KWtable kwt = KWtable(keywords); // keywords[] defined in Clex_sym.h
  43. KWtable::
  44. KWtable (char** kwl)
  45.     {
  46.     short int i;
  47.     for (i = 0; i < HASHSIZE; ++i)
  48.         kwhash[i].kwp = NULL;
  49.     for (i = 0; i < CLEX_NUMKEYS; ++i)
  50.         insert(kwl[i], KEYWORD_S + i);
  51.     // rely on assert() to prevent hash collisions -- may need
  52.     //  a new hash function or table size when keyword added.
  53.     }
  54. // the values used in the following hash function, and HASHSIZE, were
  55. // determined by use of the standalone C program kwhash.c, to
  56. // ensure that no collisions occur.
  57. inline
  58. U_short KWtable::
  59. hash (const U_char* cp, U_short len)
  60.     {
  61.     return (((U_short)cp[0]         ) ^
  62.             ((U_short)cp[1]     << 9) ^
  63.             ((U_short)cp[len-1] << 2) ^
  64.             (len                << 2) ) % HASHSIZE;
  65.     }
  66. void KWtable::
  67. insert (char* cp, Clex_sym s)
  68.     {
  69.     U_short h = hash(cp, strlen(cp));
  70.     assert(kwt.kwhash[h].kwp == NULL);  // collisions not permitted.
  71.     kwt.kwhash[h].kwp = cp;
  72.     kwt.kwhash[h].sym = s;
  73.     }
  74. Clex_sym KWtable::
  75. lookup (char* cp, short len)
  76.     {
  77.     if (len < 2 || len > 9) return (IDENT_S);
  78.     short h = hash(cp, len);
  79.     if (kwt.kwhash[h].kwp == NULL) return (IDENT_S);
  80.     if (strcmp(kwt.kwhash[h].kwp, cp)) return (IDENT_S);
  81.     return (kwt.kwhash[h].sym);
  82.     }
  83. /******************************************************************************
  84. *                                                                             *
  85. *  CLEX -- c++ lexical scanner                                               *
  86. *                                                                             *
  87. ******************************************************************************/
  88. // CONSTRUCTOR Clex:
  89. //   The argument block_brack, if TRUE, dictates that the contents
  90. //   of square brackets "[]" be returned as a string in the string
  91. //   buffer.  If false, square brackets are treated as simple tokens.
  92. Clex::
  93. Clex (FILE* f, Boolean b)
  94.     {
  95.     fp = f;
  96.     block_brack = b;
  97.     filename[0] = '';
  98.     bufsiz = 0; buf[0] = '';
  99.     // prime the pipeline:
  100.     line_num = 0;
  101.     look = 'n';    // be prepared to handle '#' as first char
  102.     }
  103. Clex_sym Clex::
  104. num (char c)
  105.     {
  106.     Clex_sym s = NUM_S;
  107.     bufsiz = 0;
  108.     put_in_buf(c);
  109.     while (isdigit(look))
  110.         buf_one();
  111.     // hexadecimal
  112.     if (bufsiz == 1 && *buf == '0' && (look == 'x' || look == 'X'))
  113.         {
  114.         do { buf_one(); }
  115.             while (isxdigit(look));
  116.         if (look == 'L' || look == 'l' || look == 'U' || look == 'u')
  117.             buf_one();
  118.         return terminate(s);
  119.         }
  120.     // long or unsigned
  121.     if (look == 'L' || look == 'l' || look == 'U' || look == 'u')
  122.         { buf_one(); return terminate(NUM_S); }
  123.     // floating point
  124.     else if (look == '.')
  125.         {
  126.         s = FLOATNUM_S;
  127.         do { buf_one(); }
  128.             while (isdigit(look));
  129.         }
  130.     // scientific notation
  131.     if (look == 'e' || look == 'E')
  132.          {
  133.          s = FLOATNUM_S;
  134.          do { buf_one(); }
  135.             while (isdigit(look));
  136.          }
  137.     else
  138.         return terminate(s);
  139.     if (look == '+' || look == '-')
  140.          do { buf_one(); }
  141.             while (isdigit(look));
  142.     return terminate(s);
  143.     }
  144. Clex_sym Clex::
  145. ident (char first)
  146.     {
  147.     register Boolean maybe_kw = TRUE;
  148.     register short bs = 0;
  149.     buf[bs++] = first;
  150.     while (isalnum(look) || look == '_' || look == '$')
  151.         {
  152.         // note: this function accounts for 30% of the total scan time
  153.         if (maybe_kw && (isupper(look) || look == '_' ))
  154.             maybe_kw = FALSE;
  155.         buf[bs++] = look;       // don't worry about overflow
  156.         eat_one();
  157.         }
  158.     buf[bs] = '';
  159.     bufsiz = bs;
  160.     if (maybe_kw)
  161.         return kwt.lookup(buf, bufsiz);
  162.     return IDENT_S;
  163.     }
  164. Clex_sym Clex::
  165. quote (char c, Clex_sym s, Clex_mode m)
  166.     {
  167.     if (m == CL_NONE)
  168.         bufsiz = 0;
  169.     while (look != c)
  170.         {
  171.         if (look == EOF)
  172.             { return terminate(ERROR_EOF_S); }
  173.         else if (look == 'n')
  174.             { return terminate(ERROR_EOLN_S); }
  175.         else if (look == '\')
  176.             {
  177.             eat_one();
  178.             if (look == 'n')
  179.                 { eat_one(); eoln(m|CL_QUOTE); continue; }
  180.             else if (look == EOF)
  181.                 { return terminate(ERROR_EOF_S); }
  182.             else
  183.                 put_in_buf('\');   // this handles ' and " too.
  184.             }
  185.         buf_one();
  186.         }
  187.     eat_one();  // eat the closing quote
  188.     return terminate(s);
  189.     }
  190. // lbrack() accumulates the contents between "[" and "]" into
  191. //  the string buffer, handling syntactically quoted strings,
  192. //  comments, and nested brackets.  Note that lbrack() is
  193. //  called recursively in the case of nested brackets.
  194. Clex_sym Clex::
  195. lbrack (Clex_mode m)
  196.     {
  197.     if (m == CL_NONE)
  198.         bufsiz = 0;
  199.     while (look != ']')
  200.         {
  201.         if (look == EOF)
  202.             return terminate(ERROR_EOF_S);
  203.         else if (look == 'n')
  204.             { eat_one(); eoln(m|CL_BRACK); }
  205.         else if (look == '[')
  206.             {
  207.             buf_one();
  208.             if (lbrack(m|CL_BRACK) == ERROR_EOF_S)
  209.                 return ERROR_EOF_S;     // already cleaned up.
  210.             else put_in_buf(']');
  211.             }
  212.         else if (look == ''' || look == '"')
  213.             {
  214.             char c = look;
  215.             buf_one();
  216.             (void) quote(c, NONE_S, m|CL_BRACK);
  217.             put_in_buf(c);
  218.             }
  219.         else if (look == '/')           // maybe a comment
  220.             {
  221.             eat_one();
  222.             if (look == '/')
  223.                 line_comment();
  224.             else if (look == '*')
  225.                 {
  226.                 block_comment(m|CL_BRACK);
  227.                 if (look == EOF) return terminate(ERROR_EOF_S);
  228.                 }
  229.             else                        // stash the '/' and the char after
  230.                 { put_in_buf('/'); buf_one(); }
  231.             }
  232.         else                            // just a character to save
  233.             buf_one();
  234.         }
  235.     eat_one(); // eat the ']'.
  236.     return terminate(LBRACK_S);
  237.     }
  238. void Clex::
  239. block_comment(Clex_mode m)
  240.     {
  241.     eat_one(); // eat the '*'
  242.     while (! (look == '*' && (eat_one(), look == '/')) )
  243.         {
  244.         if (look == EOF) return;
  245.         if (look == 'n') { eat_one(); eoln(m|CL_COMMENT); }
  246.         else if (look != '*') eat_one();
  247.         }
  248.     eat_one(); // eat the '/'
  249.     }
  250. void Clex::
  251. line_comment()
  252.     {
  253.     do { eat_one(); }
  254.      while (look != 'n' && look != EOF);
  255.     }
  256. // eat_return() is intended to save space in Clex::next() -- the
  257. //  inline function eat_one() produces quite a lot of code.
  258. Clex_sym Clex::
  259. eat_return(Clex_sym s)
  260.     { eat_one(); return s; }
  261. Clex_sym Clex::
  262. next()
  263.     {
  264.     short val;
  265.     while (val = look, eat_one(), val != EOF)
  266.         {
  267.         char ch = char(val);
  268.         switch (ch)
  269.             {
  270.         case ' ' : continue;
  271.         case '_' :
  272.         case '$' : return ident(ch);
  273.         case '0' : case '1' : case '2' : case '3' : case '4' :
  274.         case '5' : case '6' : case '7' : case '8' : case '9' :
  275.                    return num(ch);
  276.         case ',' : return COMMA_S;
  277.         case ';' : return SEMI_S;
  278.         case '[' : if (block_brack) return lbrack(CL_NONE);
  279.                    else             return LBRACK_S;
  280.         case ']' : return RBRACK_S;
  281.         case '{' : return LBRACE_S;
  282.         case '}' : return RBRACE_S;
  283.         case '(' : return LPAR_S;
  284.         case ')' : return RPAR_S;
  285.         case '~' : return TILDE_S;
  286.         case '?' : return QUEST_S;
  287.         case '"' : return quote(ch, QUOTE_S, CL_NONE);
  288.         case ''': return quote(ch, APOS_S, CL_NONE);
  289.         case '=' :                              // '=', '=='
  290.             if (look != '=') return AS_S;
  291.             else  return eat_return(EQ_S);
  292.         case ':' :                              // ":", "::"
  293.             if (look != ':') return COLON_S;
  294.             else  return eat_return(SCOPE_S);
  295.         case '!' :                              // "!", "!="
  296.             if (look != '=') return BANG_S;
  297.             else  return eat_return(NE_S);
  298.         case '^' :                              // "^", "^="
  299.             if (look != '=') return CARET_S;
  300.             else  return eat_return(XORAS_S);
  301.         case '*' :                              // '*', '*='
  302.             if (look != '=') return STAR_S;
  303.             else  return eat_return(MULAS_S);
  304.         case '%' :                              // '%', '%='
  305.             if (look != '=') return MOD_S;
  306.             else  return eat_return(MODAS_S);
  307.         case '|' :                              //  "|=", "||", "|"
  308.             if      (look == '|') return eat_return(LOR_S);
  309.             else if (look == '=') return eat_return(ORAS_S);
  310.             else                             return VBAR_S;
  311.         case '&' :                              // "&", "&=", "&&"
  312.             if      (look == '&') return eat_return(LAND_S);
  313.             else if (look == '=') return eat_return(ANDAS_S);
  314.             else                             return AMPER_S;
  315.         case '+' :                              // '+', '++', '+='
  316.             if      (look == '+') return eat_return(INCRE_S);
  317.             else if (look == '=') return eat_return(ADDAS_S);
  318.             else                             return PLUS_S;
  319.         case '-' :                              // '--', '-=', '->', '-', 
  320.             if      (look == '-') return eat_return(DECRE_S);
  321.             else if (look == '=') return eat_return(SUBAS_S);
  322.             else if (look == '>') return eat_return(DEREF_S);
  323.             else                             return MINUS_S;
  324.         case '/' :                              // '/*', '//', '/=', '/'
  325.             if (look == '*')
  326.                 {
  327.                 block_comment(CL_NONE);
  328.                 if (look == EOF)       // almost certainly a mistake:
  329.                     return ERROR_EOF_S;
  330.                 else continue;
  331.                 }
  332.             else if (look == '/')
  333.                 { line_comment(); continue; }
  334.             else if (look == '=') return eat_return(DIVAS_S);
  335.             else                             return SLASH_S;
  336.         case '.' :                              // ".", "..."
  337.             if (isdigit(look))      return num(ch);
  338.             else if (look == '.')
  339.                 {
  340.                 eat_one();          // check for "..", undefined.
  341.                 if (look != '.')    return ERROR_UNKN_S;
  342.                 else    return  eat_return(ELLIP_S);
  343.                 }
  344.             else                    return DOT_S;
  345.         case '<' :                              // '<=', '<', '<<', '<<='
  346.             if      (look == '=')   return eat_return(LE_S);
  347.             else if (look == '<')
  348.                 {
  349.                 eat_one();
  350.                 if  (look != '=')   return SHL_S;
  351.                 else     return eat_return(SHLAS_S);
  352.                 }
  353.             else                    return LT_S;
  354.         case '>' :                              // '>=', '>', '>>', '>>='
  355.             if      (look == '=')   return eat_return(GE_S);
  356.             else if (look == '>')
  357.                 {
  358.                 eat_one();
  359.                 if  (look != '=')   return SHR_S;
  360.                 else     return eat_return(SHRAS_S);
  361.                 }
  362.             else                    return GT_S;
  363.         default:
  364.             if (isalpha(ch))
  365.                 return ident(ch);
  366.             if (ch == 'n')
  367.                 eoln(CL_NONE);
  368.             else if (iscntrl(ch))
  369.                 continue;
  370.             else
  371.                 return ERROR_UNKN_S;
  372.             }
  373.         }
  374.     return EOF_S;
  375.     }
  376. struct Quickbuf
  377.     {
  378.     short len;
  379.     char line[10240];
  380.     void put_in(char c) { if (len < sizeof(line)-1) line[len++] = c; }
  381.     void terminate()    { line[len] = ''; }
  382.     Quickbuf() { len = 0; }
  383.     };
  384. void Clex::
  385. eoln(Clex_mode m)
  386.     {
  387.     // assume NL character already eaten.
  388.     ++line_num;
  389.     // don't process '#' lines in quotes, comments, or '#' continuations.
  390.     if (m & (CL_QUOTE|CL_POUND|CL_COMMENT))
  391.         return;
  392.     // eat whitespace
  393.     while (look != EOF && look != 'n')
  394.         {
  395.         if (look == ' ' || iscntrl(char(look))) eat_one();
  396.         else break;
  397.         }
  398.     if (look != '#')
  399.         return;
  400.     // eat the '#' and subsequent whitespace
  401.     do { eat_one(); if (look == EOF || look == 'n') break; }
  402.        while (look == ' ' || iscntrl(char(look)));
  403.     // collect the '#' line
  404.     Quickbuf b;
  405.     do  {   // record line
  406.         if (look == '\')       // check for continuation line
  407.             {
  408.             eat_one();
  409.             if (look == 'n') { eat_one(); eoln(m|CL_POUND); }
  410.             else { b.put_in('\'); }
  411.             }
  412.         else if (look == '/')   // check for comment in '#' line
  413.             {
  414.             eat_one();
  415.             if (look == '*')
  416.                 {
  417.                 block_comment(m|CL_POUND);
  418.                 if (look == EOF) break;
  419.                 }
  420.             else if (look == '/') line_comment();
  421.             else { b.put_in('/'); }
  422.             }
  423.         else
  424.             {
  425.             if (iscntrl(char(look))) look = ' ';
  426.             b.put_in(look);
  427.             eat_one();
  428.             }
  429.  
  430.         } while (look != 'n' && look != EOF);
  431.     b.terminate();
  432.     (void) pound(m, b.line, b.len);     // call virtual handler
  433.     }
  434. Boolean Clex::
  435. pound (Clex_mode m, char* line, short len)
  436.     {
  437.     void(m);                // to keep cfront blissful
  438.     char* cp = line;
  439.     if (!isdigit(*cp))
  440.         {
  441.         if (len < 5) return FALSE;
  442.         if (strncmp(cp, "line ", 5) != 0)
  443.             return FALSE;   // don't know what it is
  444.         cp += 4;
  445.         while (*cp == ' ') ++cp;
  446.         if (!isdigit(*cp))
  447.             return FALSE;
  448.         }
  449.     // # <line> "<filename>"   or    #line <line> "<filename>"
  450.     line_num = atoi(cp) - 1;    // will be incremented by eoln() later
  451.     while (isdigit(*cp)) ++cp;
  452.     while (*cp == ' ')   ++cp;
  453.     if (*cp == '"')
  454.         {
  455.         char* cpq = cp;
  456.         do { ++cpq; }
  457.            while (*cpq != '"' && *cpq != '');
  458.         strncpy(filename, cp+1, cpq - cp - 1);
  459.         filename[cpq - cp - 1] = '';
  460.         }
  461.     return TRUE;
  462.     }
  463. const char* Clex::
  464. debug (Clex_sym s)
  465.     {
  466.     return (s >= KEYWORD_S) ? keywords[s - KEYWORD_S] : sym_str[s] ;
  467.     }