home *** CD-ROM | disk | FTP | other *** search
/ ftp.cs.arizona.edu / ftp.cs.arizona.edu.tar / ftp.cs.arizona.edu / icon / historic / v941.tgz / icon.v941src.tar / icon.v941src / src / common / yylex.h < prev   
C/C++ Source or Header  |  2002-01-18  |  15KB  |  650 lines

  1. /*
  2.  * yylex.h -- the lexical analyzer.
  3.  *
  4.  * This source file contains the lexical analyzer, yylex(), and its
  5.  * support routines. It is built by inclusion in ../icont/tlex.c and
  6.  * ../iconc/clex.c, with slight variations depending on whether "Iconc"
  7.  * is defined.
  8.  */
  9.  
  10. #if !defined(Iconc) && !defined(VarTran)
  11.    #include "../h/esctab.h"
  12. #endif                    /* !Iconc && !VarTran */
  13.  
  14. /*
  15.  * Prototypes.
  16.  */
  17.  
  18. static  int             bufcmp          (char *s);
  19. static    struct toktab   *findres    (void);
  20. static    struct toktab   *getident    (int ac,int *cc);
  21. static    struct toktab   *getnum        (int ac,int *cc);
  22. static    struct toktab   *getstring    (int ac,int *cc);
  23. static    int        setfilenm    (int c);
  24. static    int        setlineno    (void);
  25.  
  26. #if !defined(Iconc) && !defined(VarTran)
  27.    static    int    ctlesc        (void);
  28.    static    int    hexesc        (void);
  29.    static    int    octesc        (int ac);
  30. #endif                    /* !Iconc && !VarTran */
  31.  
  32. #define isletter(s)    (isupper(c) | islower(c))
  33. #define tonum(c)        (isdigit(c) ? (c - '0') : ((c & 037) + 9))
  34.  
  35. struct node tok_loc =
  36.    {0, NULL, 0, 0};    /* "model" node containing location of current token */
  37.  
  38. struct str_buf lex_sbuf;    /* string buffer for lexical analyzer */
  39.  
  40. /*
  41.  * yylex - find the next token in the input stream, and return its token
  42.  *  type and value to the parser.
  43.  *
  44.  * Variables of interest:
  45.  *
  46.  *  cc - character following last token.
  47.  *  nlflag - set if a newline was between the last token and the current token
  48.  *  lastend - set if the last token was an Ender.
  49.  *  lastval - when a semicolon is inserted and returned, lastval gets the
  50.  *   token value that would have been returned if the semicolon hadn't
  51.  *   been inserted.
  52.  */
  53.  
  54. static struct toktab *lasttok = NULL;
  55. static int lastend = 0;
  56. static int eofflag = 0;
  57. static int cc = '\n';
  58.  
  59. int yylex()
  60.    {
  61.    register struct toktab *t;
  62.    register int c;
  63.    int n;
  64.    int nlflag;
  65.    static nodeptr lastval;
  66.    static struct node semi_loc;
  67.  
  68.    if (lasttok != NULL) {
  69.       /*
  70.        * A semicolon was inserted and returned on the last call to yylex,
  71.        *  instead of going to the input, return lasttok and set the
  72.        *  appropriate variables.
  73.        */
  74.  
  75.       yylval = lastval;
  76.       tok_loc = *lastval;
  77.       t = lasttok;
  78.       goto ret;
  79.       }
  80.    nlflag = 0;
  81. loop:
  82.    c = cc;
  83.    /*
  84.     * Remember where a semicolon will go if we insert one.
  85.     */
  86.    semi_loc.n_file = tok_loc.n_file;
  87.    semi_loc.n_line = in_line;
  88.    if (cc == '\n')
  89.       --semi_loc.n_line;
  90.    semi_loc.n_col = incol;
  91.    /*
  92.     * Skip whitespace and comments and process #line directives.
  93.     */
  94.    while (c == Comment || isspace(c)) {
  95.       if (c == '\n') {
  96.          nlflag++;
  97.          c = NextChar;
  98.      if (c == Comment) {
  99.             /*
  100.          * Check for #line directive at start of line.
  101.              */
  102.             if (('l' == (c = NextChar)) &&
  103.                 ('i' == (c = NextChar)) &&
  104.                 ('n' == (c = NextChar)) &&
  105.                 ('e' == (c = NextChar))) {
  106.                c = setlineno();
  107.            while ((c == ' ') || (c == '\t'))
  108.           c = NextChar;
  109.                if (c != EOF && c != '\n')
  110.                   c = setfilenm(c);
  111.            }
  112.         while (c != EOF && c != '\n')
  113.                c = NextChar;
  114.         }
  115.          }
  116.       else {
  117.      if (c == Comment) {
  118.         while (c != EOF && c != '\n')
  119.                c = NextChar;
  120.         }
  121.          else {
  122.             c = NextChar;
  123.             }
  124.          }
  125.       }
  126.    /*
  127.     * A token is the next thing in the input.  Set token location to
  128.     *  the current line and column.
  129.     */
  130.    tok_loc.n_line = in_line;
  131.    tok_loc.n_col = incol;
  132.  
  133.    if (c == EOF) {
  134.       /*
  135.        * End of file has been reached.    Set eofflag, return T_Eof, and
  136.        *  set cc to EOF so that any subsequent scans also return T_Eof.
  137.        */
  138.       if (eofflag++) {
  139.      eofflag = 0;
  140.      cc = '\n';
  141.      yylval = NULL;
  142.      return 0;
  143.      }
  144.       cc = EOF;
  145.       t = T_Eof;
  146.       yylval = NULL;
  147.       goto ret;
  148.       }
  149.  
  150.    /*
  151.     * Look at current input character to determine what class of token
  152.     *  is next and take the appropriate action.  Note that the various
  153.     *  token gathering routines write a value into cc.
  154.     */
  155.    if (isalpha(c) || (c == '_')) {   /* gather ident or reserved word */
  156.       if ((t = getident(c, &cc)) == NULL)
  157.      goto loop;
  158.       }
  159.    else if (isdigit(c) || (c == '.')) {    /* gather numeric literal or "." */
  160.       if ((t = getnum(c, &cc)) == NULL)
  161.      goto loop;
  162.       }
  163.    else if (c == '"' || c == '\'') {    /* gather string or cset literal */
  164.       if ((t = getstring(c, &cc)) == NULL)
  165.      goto loop;
  166.       }
  167.    else {            /* gather longest legal operator */
  168.       if ((n = getopr(c, &cc)) == -1)
  169.      goto loop;
  170.       t = &(optab[n].tok);
  171.       yylval = OpNode(n);
  172.       }
  173.    if (nlflag && lastend && (t->t_flags & Beginner)) {
  174.       /*
  175.        * A newline was encountered between the current token and the last,
  176.        *  the last token was an Ender, and the current token is a Beginner.
  177.        *  Return a semicolon and save the current token in lastval.
  178.        */
  179.       lastval = yylval;
  180.       lasttok = t;
  181.       tok_loc = semi_loc;
  182.       yylval = OpNode(semicol_loc);
  183.       return SEMICOL;
  184.       }
  185. ret:
  186.    /*
  187.     * Clear lasttok, set lastend if the token being returned is an
  188.     *  Ender, and return the token.
  189.     */
  190.    lasttok = 0;
  191.    lastend = t->t_flags & Ender;
  192.    return (t->t_type);
  193.    }
  194.  
  195. #ifdef MultipleRuns
  196. /*
  197.  * yylexinit - initialize variables for multiple runs
  198.  */
  199. void yylexinit()
  200.    {
  201.    lasttok = NULL;
  202.    lastend = 0;
  203.    eofflag = 0;
  204.    cc = '\n';
  205.    }
  206. #endif                    /* MultipleRuns */
  207.  
  208. /*
  209.  * getident - gather an identifier beginning with ac.  The character
  210.  *  following identifier goes in cc.
  211.  */
  212.  
  213. static struct toktab *getident(ac, cc)
  214. int ac;
  215. int *cc;
  216.    {
  217.    register int c;
  218.    register struct toktab *t;
  219.  
  220.    c = ac;
  221.    /*
  222.     * Copy characters into string space until a non-alphanumeric character
  223.     *  is found.
  224.     */
  225.    do {
  226.       AppChar(lex_sbuf, c);
  227.       c = NextChar;
  228.       } while (isalnum(c) || (c == '_'));
  229.    *cc = c;
  230.    /*
  231.     * If the identifier is a reserved word, make a ResNode for it and return
  232.     *  the token value.  Otherwise, install it with putid, make an
  233.     *  IdNode for it, and return.
  234.     */
  235.    if ((t = findres()) != NULL) {
  236.       lex_sbuf.endimage = lex_sbuf.strtimage;
  237.       yylval = ResNode(t->t_type);
  238.       return t;
  239.       }
  240.    else {
  241.       yylval = IdNode(str_install(&lex_sbuf));
  242.       return (struct toktab *)T_Ident;
  243.       }
  244.    }
  245.  
  246. /*
  247.  * findres - if the string just copied into the string space by getident
  248.  *  is a reserved word, return a pointer to its entry in the token table.
  249.  *  Return NULL if the string isn't a reserved word.
  250.  */
  251.  
  252. static struct toktab *findres()
  253.    {
  254.    register struct toktab *t;
  255.    register char c;
  256.  
  257.    c = *lex_sbuf.strtimage;
  258.    if (!islower(c))
  259.       return NULL;
  260.    /*
  261.     * Point t at first reserved word that starts with c (if any).
  262.     */
  263.    if ((t = restab[c - 'a']) == NULL)
  264.       return NULL;
  265.    /*
  266.     * Search through reserved words, stopping when a match is found
  267.     *  or when the current reserved word doesn't start with c.
  268.     */
  269.    while (t->t_word[0] == c) {
  270.       if (bufcmp(t->t_word))
  271.      return t;
  272.       t++;
  273.       }
  274.    return NULL;
  275.    }
  276.  
  277. /*
  278.  * bufcmp - compare a null terminated string to what is in the string buffer.
  279.  */
  280. static int bufcmp(s)
  281. char *s;
  282.    {
  283.    register char *s1;
  284.    s1 = lex_sbuf.strtimage;
  285.    while (s != '\0' && s1 < lex_sbuf.endimage && *s == *s1) {
  286.       ++s;
  287.       ++s1;
  288.       }
  289.    if (*s == '\0' && s1 == lex_sbuf.endimage)
  290.       return 1;
  291.    else
  292.       return 0;
  293.    }
  294.  
  295. /*
  296.  * getnum - gather a numeric literal starting with ac and put the
  297.  *  character following the literal into *cc.
  298.  *
  299.  * getnum also handles the "." operator, which is distinguished from
  300.  *  a numeric literal by what follows it.
  301.  */
  302.  
  303. static struct toktab *getnum(ac, cc)
  304. int ac;
  305. int *cc;
  306.    {
  307.    register int c, r, state;
  308.    int realflag, n, dummy;
  309.  
  310.    c = ac;
  311.    if (c == '.') {
  312.       r = 0;
  313.       state = 7;
  314.       realflag = 1;
  315.       }
  316.    else {
  317.       r = tonum(c);
  318.       state = 0;
  319.       realflag = 0;
  320.       }
  321.    for (;;) {
  322.       AppChar(lex_sbuf, c);
  323.       c = NextChar;
  324.       switch (state) {
  325.      case 0:        /* integer part */
  326.         if (isdigit(c))        { r = r * 10 + tonum(c); continue; }
  327.         if (c == '.')           { state = 1; realflag++; continue; }
  328.         if (c == 'e' || c == 'E')  { state = 2; realflag++; continue; }
  329.         if (c == 'r' || c == 'R')  {
  330.            state = 5;
  331.            if (r < 2 || r > 36)
  332.           tfatal("invalid radix for integer literal", (char *)NULL);
  333.            continue;
  334.            }
  335.         break;
  336.      case 1:        /* fractional part */
  337.         if (isdigit(c))   continue;
  338.         if (c == 'e' || c == 'E')   { state = 2; continue; }
  339.         break;
  340.      case 2:        /* optional exponent sign */
  341.         if (c == '+' || c == '-') { state = 3; continue; }
  342.      case 3:        /* first digit after e, e+, or e- */
  343.         if (isdigit(c)) { state = 4; continue; }
  344.         tfatal("invalid real literal", (char *)NULL);
  345.         break;
  346.      case 4:        /* remaining digits after e */
  347.         if (isdigit(c))   continue;
  348.         break;
  349.      case 5:        /* first digit after r */
  350.         if ((isdigit(c) || isletter(c)) && tonum(c) < r)
  351.            { state = 6; continue; }
  352.         tfatal("invalid integer literal", (char *)NULL);
  353.         break;
  354.      case 6:        /* remaining digits after r */
  355.         if (isdigit(c) || isletter(c)) {
  356.            if (tonum(c) >= r) {    /* illegal digit for radix r */
  357.           tfatal("invalid digit in integer literal", (char *)NULL);
  358.           r = tonum('z');       /* prevent more messages */
  359.           }
  360.            continue;
  361.            }
  362.         break;
  363.          case 7:        /* token began with "." */
  364.             if (isdigit(c)) {
  365.                state = 1;        /* followed by digit is a real const */
  366.            realflag = 1;
  367.                continue;
  368.                }
  369.             *cc = c;            /* anything else is just a dot */
  370.         lex_sbuf.endimage--;    /* remove dot (undo AppChar) */
  371.         n = getopr((int)'.', &dummy);
  372.         yylval = OpNode(n);
  373.         return &(optab[n].tok);
  374.          }
  375.       break;
  376.       }
  377.    *cc = c;
  378.    if (realflag) {
  379.       yylval = RealNode(str_install(&lex_sbuf));
  380.       return T_Real;
  381.       }
  382.    yylval = IntNode(str_install(&lex_sbuf));
  383.    return T_Int;
  384.    }
  385.  
  386. /*
  387.  * getstring - gather a string literal starting with ac and place the
  388.  *  character following the literal in *cc.
  389.  */
  390. static struct toktab *getstring(ac, cc)
  391. int ac;
  392. int *cc;
  393.    {
  394.    register int c, sc;
  395.    int sav_indx;
  396.    int len;
  397.  
  398.    sc = ac;
  399.    sav_indx = -1;
  400.    c = NextChar;
  401.    while (c != sc && c != '\n' && c != EOF) {
  402.       /*
  403.        * If a '_' is the last non-white space before a new-line,
  404.        *  we must remember where it is.
  405.        */
  406.       if (c == '_')
  407.          sav_indx = lex_sbuf.endimage - lex_sbuf.strtimage;
  408.       else if (!isspace(c))
  409.          sav_indx = -1;
  410.  
  411.       if (c == Escape) {
  412.          c = NextChar;
  413.          if (c == EOF)
  414.             break;
  415.  
  416. #if defined(Iconc) || defined(VarTran)
  417.          AppChar(lex_sbuf, Escape);
  418.          if (c == '^') {
  419.             c = NextChar;
  420.             if (c == EOF)
  421.                break;
  422.             AppChar(lex_sbuf, '^');
  423.             }
  424. #else                    /* Iconc || VarTran */
  425.      if (isoctal(c))
  426.         c = octesc(c);
  427.      else if (c == 'x')
  428.         c = hexesc();
  429.      else if (c == '^')
  430.         c = ctlesc();
  431.      else
  432.         c = esctab[c];
  433. #endif                    /* Iconc || VarTran */
  434.  
  435.      }
  436.       AppChar(lex_sbuf, c);
  437.       c = NextChar;
  438.  
  439.       /*
  440.        * If a '_' is the last non-white space before a new-line, the
  441.        *  string continues at the first non-white space on the next line
  442.        *  and everything from the '_' to the end of this line is ignored.
  443.        */
  444.       if (c == '\n' && sav_indx >= 0) {
  445.          lex_sbuf.endimage = lex_sbuf.strtimage + sav_indx;
  446.          while ((c = NextChar) != EOF && isspace(c))
  447.             ;
  448.          }
  449.       }
  450.    if (c == sc)
  451.       *cc = ' ';
  452.    else {
  453.       tfatal("unclosed quote", (char *)NULL);
  454.       *cc = c;
  455.       }
  456.    len = lex_sbuf.endimage - lex_sbuf.strtimage;
  457.    if (ac == '"') {     /* a string literal */
  458.       yylval = StrNode(str_install(&lex_sbuf), len);
  459.       return T_String;
  460.       }
  461.    else {        /* a cset literal */
  462.       yylval = CsetNode(str_install(&lex_sbuf), len);
  463.       return T_Cset;
  464.       }
  465.    }
  466.  
  467. #if !defined(Iconc) && !defined(VarTran)
  468.  
  469. /*
  470.  * ctlesc - translate a control escape -- backslash followed by
  471.  *  caret and one character.
  472.  */
  473.  
  474. static int ctlesc()
  475.    {
  476.    register int c;
  477.  
  478.    c = NextChar;
  479.    if (c == EOF)
  480.       return EOF;
  481.  
  482.    return (c & 037);
  483.    }
  484.  
  485. /*
  486.  * octesc - translate an octal escape -- backslash followed by
  487.  *  one, two, or three octal digits.
  488.  */
  489.  
  490. static int octesc(ac)
  491. int ac;
  492.    {
  493.    register int c, nc, i;
  494.  
  495.    c = 0;
  496.    nc = ac;
  497.    i = 1;
  498.    do {
  499.       c = (c << 3) | (nc - '0');
  500.       nc = NextChar;
  501.       if (nc == EOF)
  502.      return EOF;
  503.       } while (isoctal(nc) && i++ < 3);
  504.    PushChar(nc);
  505.  
  506.    return (c & 0377);
  507.    }
  508.  
  509. /*
  510.  * hexesc - translate a hexadecimal escape -- backslash-x
  511.  *  followed by one or two hexadecimal digits.
  512.  */
  513.  
  514. static int hexesc()
  515.    {
  516.    register int c, nc, i;
  517.  
  518.    c = 0;
  519.    i = 0;
  520.    while (i++ < 2) {
  521.       nc = NextChar;
  522.       if (nc == EOF)
  523.      return EOF;
  524.       if (nc >= 'a' && nc <= 'f')
  525.      nc -= 'a' - 10;
  526.       else if (nc >= 'A' && nc <= 'F')
  527.      nc -= 'A' - 10;
  528.       else if (isdigit(nc))
  529.      nc -= '0';
  530.       else {
  531.      PushChar(nc);
  532.      break;
  533.      }
  534.       c = (c << 4) | nc;
  535.       }
  536.  
  537.    return c;
  538.    }
  539.  
  540. #endif                    /* !Iconc && !VarTran */
  541.  
  542. /*
  543.  * setlineno - set line number from #line comment, return following char.
  544.  */
  545.  
  546. static int setlineno()
  547.    {
  548.    register int c;
  549.  
  550.    while ((c = NextChar) == ' ' || c == '\t')
  551.       ;
  552.    if (c < '0' || c > '9') {
  553.       tfatal("no line number in #line directive", "");
  554.       while (c != EOF && c != '\n')
  555.      c = NextChar;
  556.       return c;
  557.       }
  558.    in_line = 0;
  559.    while (c >= '0' && c <= '9') {
  560.       in_line = in_line * 10 + (c - '0');
  561.       c = NextChar;
  562.       }
  563.    return c;
  564.    }
  565.  
  566. /*
  567.  * setfilenm -    set file name from #line comment, return following char.
  568.  */
  569.  
  570. static int setfilenm(c)
  571. register int c;
  572.    {
  573.    while (c == ' ' || c == '\t')
  574.       c = NextChar;
  575.    if (c != '"') {
  576.       tfatal("'\"' missing from file name in #line directive", "");
  577.       while (c != EOF && c != '\n')
  578.      c = NextChar;
  579.       return c;
  580.       }
  581.    while ((c = NextChar) != '"' && c != EOF && c != '\n')
  582.       AppChar(lex_sbuf, c);
  583.    if (c == '"') {
  584.       tok_loc.n_file = str_install(&lex_sbuf);
  585.       return NextChar;
  586.       }
  587.    else {
  588.       tfatal("'\"' missing from file name in #line directive", "");
  589.       return c;
  590.       }
  591.    }
  592.  
  593. /*
  594.  * nextchar - return the next character in the input.
  595.  *
  596.  *  Called from the lexical analyzer; interfaces it to the preprocessor.
  597.  */
  598.  
  599. int nextchar()
  600.    {
  601.    register int c;
  602.  
  603. #if MACINTOSH
  604. #if MPW
  605.    {
  606.    static short cursorcount = CURSORINTERVAL;
  607.    if (--cursorcount == 0) {
  608.       RotateCursor(0);
  609.       cursorcount = CURSORINTERVAL;
  610.       }
  611.    }
  612. #endif                    /* MPW */
  613. #endif                    /* MACINTOSH */
  614.  
  615.    if ((c = peekc) != 0) {
  616.       peekc = 0;
  617.       return c;
  618.       }
  619.    c = ppch();
  620.    switch (c) {
  621.       case EOF:
  622.      if (incol) {
  623.         c = '\n';
  624.         in_line++;
  625.         incol = 0;
  626.         peekc = EOF;
  627.         break;
  628.         }
  629.      else {
  630.         in_line = 0;
  631.         incol = 0;
  632.         break;
  633.         }
  634.       case '\n':
  635.      in_line++;
  636.      incol = 0;
  637.      break;
  638.       case '\t':
  639.      incol = (incol | 7) + 1;
  640.      break;
  641.       case '\b':
  642.      if (incol)
  643.         incol--;
  644.      break;
  645.       default:
  646.      incol++;
  647.       }
  648.    return c;
  649.    }
  650.