home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / misc / volume20 / rc / part02 / lex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-05-22  |  9.8 KB  |  395 lines

  1. /* lex.c: rc's lexical analyzer */
  2.  
  3. #include "rc.h"
  4. #include "lex.h"
  5. #include "y.tab.h"
  6. #include "nalloc.h"
  7. #include "input.h"
  8. #include "utils.h"
  9. #include "hash.h"
  10. #include "heredoc.h"
  11.  
  12. /*
  13.     Special characters (i.e., "non-word") in rc:
  14.         \t \n # ; & | ^ $ = ~ ` ' { } @ ! ( ) < > \
  15.  
  16.     The lexical analyzer is fairly straightforward. The only really unclean part
  17.     concerns backslash continuation and "double backslashes". A backslash followed by
  18.     a newline is treated as a space, otherwise backslash is not a special characeter
  19.     (i.e., it can be part of a word).  This introduces a host of unwanted special
  20.     cases. In our case, \ cannot be a word character, since we wish to read in all
  21.     word characters in a tight loop.
  22.  
  23.     Note: to save the trouble of declaring these arrays with TRUEs and FALSEs, I am assuming
  24.     that FALSE = 0, TRUE = 1. (and so is it declared in rc.h)
  25. */
  26.  
  27. #define BUFSIZE ((SIZE_T) 1000)    /*    malloc hates power of 2 buffers? */
  28. #define BUFMAX (8 * BUFSIZE)    /*     How big the buffer can get before we re-allocate the
  29.                     space at BUFSIZE again. Premature optimization? Maybe.
  30.                 */
  31.  
  32. enum wordstates { NW, RW, KW }; /* "nonword", "realword", "keyword" */
  33.  
  34. static void getpair(int);
  35.  
  36. int lineno;
  37.  
  38. const char nw[] = {
  39.     1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  40.     1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
  41.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0,
  42.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
  43.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  44.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  45.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  46.     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
  47. };
  48.  
  49. const char dnw[] = {
  50.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  51.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
  52.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,
  53.     1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
  54.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  55.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  56.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
  57.     1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
  58. };
  59.  
  60. static SIZE_T bufsize = BUFSIZE;
  61. static char *realbuf = NULL;
  62. static boolean newline = FALSE;
  63. static enum wordstates word = NW;
  64. static int fd_left, fd_right;
  65.  
  66. #define checkfreecaret {if (*wp != NW) { *wp = NW; ugchar(c); return '^'; }}
  67.  
  68. enum filedescriptors { UNSET = -9, CLOSED = -1 };
  69.  
  70. int yylex() {
  71.     static boolean dollar = FALSE;
  72.     boolean saw_meta = FALSE;
  73.     int c;
  74.     SIZE_T i;            /* The purpose of all these local assignments is to    */
  75.     const char *meta;        /* allow optimizing compilers like gcc to load these    */
  76.     char *buf = realbuf;        /* values into registers. On a sparc this is a big    */
  77.     YYSTYPE *y = &yylval;        /* win, in code size *and* execution time        */
  78.     enum wordstates *wp = &word;
  79.  
  80.     /* rc variable-names may contain only alnum, '*' and '_', so use dnw if we are scanning one. */
  81.     meta = (dollar ? dnw : nw);
  82.     dollar = FALSE;
  83.  
  84.     if (newline) {
  85.         --lineno; /* slight space optimization; print_prompt2() always increments lineno */
  86.         print_prompt2();
  87.         newline = FALSE;
  88.     }
  89.  
  90. top:    while ((c = gchar()) == ' ' || c == '\t')
  91.         *wp = NW;
  92.  
  93.     if (c == EOF)
  94.         return END;
  95.  
  96.     if (!meta[c]) {    /* it's a word or keyword. */
  97.         checkfreecaret;
  98.         *wp = RW;
  99.         i = 0;
  100.     read:    do {
  101.             buf[i++] = c;
  102.             if (c == '?' || c == '[' || c == '*')
  103.                 saw_meta = TRUE;
  104.             if (i >= bufsize)
  105.                 buf = realbuf = erealloc(buf, bufsize *= 2);
  106.         } while ((c = gchar()) != EOF && !meta[c]);
  107.         if (c == '\\') {
  108.             if ((c = gchar()) == '\n') {
  109.                 print_prompt2();
  110.                 c = ' '; /* Pretend a space was read */
  111.             } else {
  112.     bs:            buf[i++] = '\\';
  113.                 if (!meta[c] || c == '\\')
  114.                     goto read;
  115.             }
  116.         }
  117.         ugchar(c);
  118.         buf[i] = '\0';
  119.         *wp = KW;
  120.         if (i == 2) {
  121.             if (*buf == 'i' && buf[1] == 'f') return IF;
  122.             if (*buf == 'f' && buf[1] == 'n') return FN;
  123.             if (*buf == 'i' && buf[1] == 'n') return IN;
  124.         }
  125.         if (streq(buf,"for")) return FOR;
  126.         if (streq(buf,"else")) return ELSE;
  127.         if (streq(buf,"switch")) return SWITCH;
  128.         if (streq(buf,"while")) return WHILE;
  129.         *wp = RW;
  130.         y->word.w = ncpy(buf);
  131.         if (saw_meta) {
  132.             char *r, *s;
  133.  
  134.             y->word.m = nalloc(strlen(buf) + 1);
  135.             for (r = buf, s = y->word.m; *r != '\0'; r++, s++)
  136.                 *s = (*r == '?' || *r == '[' || *r == '*');
  137.         } else {
  138.             y->word.m = NULL;
  139.         }
  140.         return WORD;
  141.     }
  142.  
  143.     if (c == '`' || c == '!' || c == '@' || c == '~' || c == '$' || c == '\'') {
  144.         checkfreecaret;
  145.         if (c == '!' || c == '@' || c == '~')
  146.             *wp = KW;
  147.     }
  148.  
  149.     switch (c) {
  150.     case '\0':
  151.         scanerror("null character");
  152.         /* NOTREACHED */
  153.     case '!':
  154.         return BANG;
  155.     case '@':
  156.         return SUBSHELL;
  157.     case '~':
  158.         return TWIDDLE;
  159.     case '`':
  160.         c = gchar();
  161.         if (c == '`')
  162.             return BACKBACK;
  163.         ugchar(c);
  164.         return '`';
  165.     case '$':
  166.         dollar = TRUE;
  167.         c = gchar();
  168.         if (c == '#')
  169.             return COUNT;
  170.         if (c == '^')
  171.             return FLAT;
  172.         ugchar(c);
  173.         return '$';
  174.     case '\'':
  175.         *wp = RW;
  176.         i = 0;
  177.         do {
  178.             buf[i++] = c;
  179.             if (c == '\n')
  180.                 print_prompt2();
  181.             if (c == EOF)
  182.                 scanerror("eof in quoted string");
  183.             if (i >= bufsize)
  184.                 buf = realbuf = erealloc(buf, bufsize *= 2);
  185.         } while ((c = gchar()) != '\'' || (c = gchar()) == '\''); /* quote "'" thus: 'how''s it going?' */
  186.         ugchar(c);
  187.         buf[i] = '\0';
  188.         y->word.w = ncpy(buf);
  189.         y->word.m = NULL;
  190.         return WORD;
  191.     case '\\':
  192.         if ((c = gchar()) == '\n') {
  193.             print_prompt2();
  194.             goto top; /* Pretend it was just another space. */
  195.         }
  196.         ugchar(c);
  197.         c = '\\';
  198.         checkfreecaret;
  199.         c = gchar();
  200.         i = 0;
  201.         goto bs;
  202.     case '(':
  203.         if (*wp == RW) /* SUB's happen only after real words, not keyowrds, so if () and while () work */
  204.             c = SUB;
  205.         *wp = NW;
  206.         return c;
  207.     case '#':
  208.         while ((c = gchar()) != '\n') /* skip comment until newline */
  209.             if (c == EOF)
  210.                 return END;
  211.         /* FALLTHROUGH */
  212.     case '\n':
  213.         lineno++;
  214.         newline = TRUE;
  215.         /* FALLTHROUGH */
  216.     case ';':
  217.     case '^':
  218.     case ')':
  219.     case '=':
  220.     case '{': case '}':
  221.         *wp = NW;
  222.         return c;
  223.     case '&':
  224.         *wp = NW;
  225.         c = gchar();
  226.         if (c == '&')
  227.             return ANDAND;
  228.         ugchar(c);
  229.         return '&';
  230.     case '|':
  231.         *wp = NW;
  232.         c = gchar();
  233.         if (c == '|')
  234.             return OROR;
  235.         getpair(c);
  236.         if ((y->pipe.left = fd_left) == UNSET)
  237.             y->pipe.left = 1;                /* default to fd 1 */
  238.         if ((y->pipe.right = fd_right) == UNSET)
  239.             y->pipe.right = 0;                /* default to fd 0 */
  240.         if (y->pipe.right == CLOSED)
  241.             scanerror("expected digit after '='");        /* can't close a pipe */
  242.         return PIPE;
  243.     case '>':
  244.         c = gchar();
  245.         if (c == '>') {
  246.             c = gchar();
  247.             y->redir.type = APPEND;
  248.         } else
  249.             y->redir.type = CREATE;
  250.         y->redir.fd = 1;
  251.         goto common;
  252.     case '<':
  253.         c = gchar();
  254.         if (c == '<') {
  255.             c = gchar();
  256.             if (c == '<') {
  257.                 c = gchar();
  258.                 y->redir.type = HERESTRING;
  259.             } else {
  260.                 y->redir.type = HEREDOC;
  261.             }
  262.         } else
  263.             y->redir.type = FROM;
  264.         y->redir.fd = 0;
  265.     common:
  266.         *wp = NW;
  267.         getpair(c);
  268.         if (fd_right == UNSET) { /* redirection, not dup */
  269.             if (fd_left != UNSET)
  270.                 y->redir.fd = fd_left;
  271.             return REDIR;
  272.         } else { /* dup; recast yylval */
  273.             y->dup.type = y->redir.type;
  274.             y->dup.left = fd_left;
  275.             y->dup.right = fd_right;
  276.             return DUP;
  277.         }
  278.     default:
  279.         *wp = NW;
  280.         return c; /* don't know what it is, let yacc barf on it */
  281.     }
  282. }
  283.  
  284. void skipnl(void) {
  285.     int c;
  286.  
  287.     while ((c = gchar()) == ' ' || c == '\t' || c == '#' || c == '\n') {
  288.         if (c == '\n' || c == '#') {
  289.             for (; c != '\n'; c = gchar()) /* skip comments */
  290.                 if (c == EOF) {
  291.                     ugchar(c);
  292.                     return;
  293.                 }
  294.             print_prompt2();
  295.         }
  296.     }
  297.     ugchar(c);
  298. }
  299.  
  300. void yyerror(const char *s) {
  301.     char *tok;
  302.     char tokbuf[128];
  303.  
  304.     if (!interactive) {
  305.         if (word != NW)
  306.             tok = realbuf;
  307.         else if (last == EOF)
  308.             tok = "end of input";
  309.         else if (last == '\n')
  310.             tok = "end of line";
  311.         else
  312.             sprint(tok = tokbuf, (last < 32 || last > 126) ? "(decimal %d)" : "'%c'",last);
  313.         fprint(2,"line %d: %s near %s\n", lineno - (last == '\n'), s, tok);
  314.     } else
  315.         fprint(2,"%s\n",s);
  316. }
  317.  
  318. void scanerror(char *s) {
  319.     flushu(); /* flush upto newline */
  320.     rc_error(s);
  321. }
  322.  
  323. void inityy(void) {
  324.     newline = FALSE;
  325.     word = NW;
  326.     hq = NULL;
  327.  
  328.     /* return memory to the system if the buffer got too large */
  329.  
  330.     if (bufsize > BUFMAX && realbuf != NULL) {
  331.         efree(realbuf);
  332.         bufsize = BUFSIZE;
  333.         realbuf = ealloc(bufsize);
  334.     } else if (realbuf == NULL)
  335.         realbuf = ealloc(bufsize);
  336. }
  337.  
  338. void print_prompt2() {
  339.     lineno++;
  340. #ifdef READLINE
  341.     prompt = prompt2;
  342. #else
  343.     if (interactive)
  344.         fprint(2,"%s",prompt2);
  345. #endif
  346. }
  347.  
  348. /*
  349.    Scan in a pair of integers for redirections like >[2=1]. CLOSED represents a closed file
  350.    descriptor (i.e., >[2=]) and UNSET represents an undesignated file descriptor (e.g.,
  351.    >[2] is represented as (2,UNSET).
  352.  
  353.    This function makes use of unsigned compares to make range tests in one compare operation.
  354. */
  355.  
  356. static void getpair(int c) {
  357.     int n;
  358.  
  359.     fd_left = fd_right = UNSET;
  360.  
  361.     if (c != '[') {
  362.         ugchar(c);
  363.         return;
  364.     }
  365.  
  366.     if ((unsigned int) (n = gchar() - '0') > 9)
  367.         scanerror("expected digit after '['");
  368.  
  369.     while ((unsigned int) (c = gchar() - '0') <= 9)
  370.         n = n * 10 + c;
  371.  
  372.     fd_left = n;
  373.     c += '0';
  374.  
  375.     switch (c) {
  376.     default:
  377.         scanerror("expected '=' or ']' after digit");
  378.         /* NOTREACHED */
  379.     case ']':
  380.         return;
  381.     case '=':
  382.         if ((unsigned int) (n = gchar() - '0') > 9) {
  383.             if (n != ']' - '0')
  384.                 scanerror("expected digit or ']' after '='");
  385.             fd_right = CLOSED;
  386.         } else {
  387.             while ((unsigned int) (c = gchar() - '0') <= 9)
  388.                 n = n * 10 + c;
  389.             if (c != ']' - '0')
  390.                 scanerror("expected ']' after digit");
  391.             fd_right = n;
  392.         }
  393.     }
  394. }
  395.