home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1994 November / macformat-018.iso / Utility Spectacular / Utilities / Calc / token.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-10  |  11.8 KB  |  587 lines  |  [TEXT/????]

  1. /*
  2.  * Copyright (c) 1992 David I. Bell
  3.  * Permission is granted to use, distribute, or modify this source,
  4.  * provided that this copyright notice remains intact.
  5.  *
  6.  * Read input file characters into tokens
  7.  */
  8.  
  9. #include "xstdarg.h"
  10. #include "calc.h"
  11. #include "token.h"
  12. #include "xstring.h"
  13.  
  14.  
  15. #define isletter(ch)    ((((ch) >= 'a') && ((ch) <= 'z')) || \
  16.                 (((ch) >= 'A') && ((ch) <= 'Z')))
  17. #define isdigit(ch)    (((ch) >= '0') && ((ch) <= '9'))
  18. #define issymbol(ch)    (isletter(ch) || isdigit(ch) || ((ch) == '_'))
  19.  
  20.  
  21. /*
  22.  * Current token.
  23.  */
  24. static struct {
  25.     short t_type;        /* type of token */
  26.     char *t_str;        /* string value or symbol name */
  27.     long t_numindex;    /* index of numeric value */
  28. } curtoken;
  29.  
  30.  
  31. static BOOL rescan;        /* TRUE to reread current token */
  32. static BOOL newlines;        /* TRUE to return newlines as tokens */
  33. static BOOL allsyms;        /* TRUE if always want a symbol token */
  34. static STRINGHEAD strings;    /* list of constant strings */
  35. static char *numbuf;        /* buffer for numeric tokens */
  36. static long numbufsize;        /* current size of numeric buffer */
  37.  
  38. long errorcount;        /* number of compilation errors */
  39.  
  40.  
  41. /*
  42.  * Table of keywords
  43.  */
  44. struct keyword {
  45.     char *k_name;    /* keyword name */
  46.     int k_token;    /* token number */
  47. };
  48.  
  49. static struct keyword keywords[] = {
  50.     "if",        T_IF,
  51.     "else",        T_ELSE,
  52.     "for",        T_FOR,
  53.     "while",    T_WHILE,
  54.     "do",        T_DO,
  55.     "continue",    T_CONTINUE,
  56.     "break",    T_BREAK,
  57.     "goto",        T_GOTO,
  58.     "return",    T_RETURN,
  59.     "local",    T_LOCAL,
  60.     "global",    T_GLOBAL,
  61.     "print",    T_PRINT,
  62.     "switch",    T_SWITCH,
  63.     "case",        T_CASE,
  64.     "default",    T_DEFAULT,
  65.     "quit",        T_QUIT,
  66.     "exit",        T_QUIT,
  67.     "define",    T_DEFINE,
  68.     "read",        T_READ,
  69.     "show",        T_SHOW,
  70.     "help",        T_HELP,
  71.     "write",    T_WRITE,
  72.     "mat",        T_MAT,
  73.     "obj",        T_OBJ,
  74.     NULL,        0
  75. };
  76.  
  77.  
  78. static void eatcomment(), eatstring();
  79. static int eatsymbol(), eatnumber();
  80.  
  81.  
  82. /*
  83.  * Initialize all token information.
  84.  */
  85. void
  86. inittokens()
  87. {
  88.     initstr(&strings);
  89.     newlines = FALSE;
  90.     allsyms = FALSE;
  91.     rescan = FALSE;
  92.     setprompt(PROMPT1);
  93. }
  94.  
  95.  
  96. void
  97. tokenmode(flag)
  98. {
  99.     newlines = FALSE;
  100.     allsyms = FALSE;
  101.     if (flag & TM_NEWLINES)
  102.         newlines = TRUE;
  103.     if (flag & TM_ALLSYMS)
  104.         allsyms = TRUE;
  105.     setprompt(newlines ? PROMPT1 : PROMPT2);
  106. }
  107.  
  108.  
  109. /*
  110.  * Routine to read in the next token from the input stream.
  111.  * The type of token is returned as a value.  If the token is a string or
  112.  * symbol name, information is saved so that the value can be retrieved.
  113.  */
  114. int
  115. gettoken()
  116. {
  117.     int ch;            /* current input character */
  118.     int type;        /* token type */
  119.  
  120.     if (rescan) {        /* rescanning */
  121.         rescan = FALSE;
  122.         return curtoken.t_type;
  123.     }
  124.     curtoken.t_str = NULL;
  125.     curtoken.t_numindex = 0;
  126.     type = T_NULL;
  127.     while (type == T_NULL) {
  128.         ch = nextchar();
  129.         if (allsyms && ((ch!=' ') && (ch!=';') && (ch!='"') && (ch!='\n'))) {
  130.             reread();
  131.             type = eatsymbol();
  132.             break;
  133.         }
  134.         switch (ch) {
  135.         case ' ':
  136.         case '\t':
  137.         case '\0':
  138.             break;
  139.         case '\n':
  140.             if (newlines)
  141.                 type = T_NEWLINE;
  142.             break;
  143.         case EOF: type = T_EOF; break;
  144.         case '{': type = T_LEFTBRACE; break;
  145.         case '}': type = T_RIGHTBRACE; break;
  146.         case '(': type = T_LEFTPAREN; break;
  147.         case ')': type = T_RIGHTPAREN; break;
  148.         case '[': type = T_LEFTBRACKET; break;
  149.         case ']': type = T_RIGHTBRACKET; break;
  150.         case ';': type = T_SEMICOLON; break;
  151.         case ':': type = T_COLON; break;
  152.         case ',': type = T_COMMA; break;
  153.         case '?': type = T_QUESTIONMARK; break;
  154.         case '"':
  155.         case '\'':
  156.             type = T_STRING;
  157.             eatstring(ch);
  158.             break;
  159.         case '^':
  160.             switch (nextchar()) {
  161.                 case '=': type = T_POWEREQUALS; break;
  162.                 default: type = T_POWER; reread();
  163.             }
  164.             break;
  165.         case '=':
  166.             switch (nextchar()) {
  167.                 case '=': type = T_EQ; break;
  168.                 default: type = T_ASSIGN; reread();
  169.             }
  170.             break;
  171.         case '+':
  172.             switch (nextchar()) {
  173.                 case '+': type = T_PLUSPLUS; break;
  174.                 case '=': type = T_PLUSEQUALS; break;
  175.                 default: type = T_PLUS; reread();
  176.             }
  177.             break;
  178.         case '-':
  179.             switch (nextchar()) {
  180.                 case '-': type = T_MINUSMINUS; break;
  181.                 case '=': type = T_MINUSEQUALS; break;
  182.                 default: type = T_MINUS; reread();
  183.             }
  184.             break;
  185.         case '*':
  186.             switch (nextchar()) {
  187.                 case '=': type = T_MULTEQUALS; break;
  188.                 case '*':
  189.                     switch (nextchar()) {
  190.                         case '=': type = T_POWEREQUALS; break;
  191.                         default: type = T_POWER; reread();
  192.                     }
  193.                     break;
  194.                 default: type = T_MULT; reread();
  195.             }
  196.             break;
  197.         case '/':
  198.             switch (nextchar()) {
  199.                 case '/':
  200.                     switch (nextchar()) {
  201.                         case '=': type = T_SLASHSLASHEQUALS; break;
  202.                         default: reread(); type = T_SLASHSLASH; break;
  203.                     }
  204.                     break;
  205.                 case '=': type = T_DIVEQUALS; break;
  206.                 case '*': eatcomment(); break;
  207.                 default: type = T_DIV; reread();
  208.             }
  209.             break;
  210.         case '%':
  211.             switch (nextchar()) {
  212.                 case '=': type = T_MODEQUALS; break;
  213.                 default: type = T_MOD; reread();
  214.             }
  215.             break;
  216.         case '<':
  217.             switch (nextchar()) {
  218.                 case '=': type = T_LE; break;
  219.                 case '<':
  220.                     switch (nextchar()) {
  221.                         case '=': type = T_LSHIFTEQUALS; break;
  222.                         default:  reread(); type = T_LEFTSHIFT; break;
  223.                     }
  224.                     break;
  225.                 default: type = T_LT; reread();
  226.             }
  227.             break;
  228.         case '>':
  229.             switch (nextchar()) {
  230.                 case '=': type = T_GE; break;
  231.                 case '>':
  232.                     switch (nextchar()) {
  233.                         case '=': type = T_RSHIFTEQUALS; break;
  234.                         default:  reread(); type = T_RIGHTSHIFT; break;
  235.                     }
  236.                     break;
  237.                 default: type = T_GT; reread();
  238.             }
  239.             break;
  240.         case '&':
  241.             switch (nextchar()) {
  242.                 case '&': type = T_ANDAND; break;
  243.                 case '=': type = T_ANDEQUALS; break;
  244.                 default: type = T_AND; reread(); break;
  245.             }
  246.             break;
  247.         case '|':
  248.             switch (nextchar()) {
  249.                 case '|': type = T_OROR; break;
  250.                 case '=': type = T_OREQUALS; break;
  251.                 default: type = T_OR; reread(); break;
  252.             }
  253.             break;
  254.         case '!':
  255.             switch (nextchar()) {
  256.                 case '=': type = T_NE; break;
  257.                 default: type = T_NOT; reread(); break;
  258.             }
  259.             break;
  260.         case '\\':
  261.             switch (nextchar()) {
  262.                 case '\n': setprompt(PROMPT2); break;
  263.                 default: scanerror(T_NULL, "Unknown token character '%c'", ch);
  264.             }
  265.             break;
  266.         default:
  267.             if (isletter(ch)) {
  268.                 reread();
  269.                 type = eatsymbol();
  270.                 break;
  271.             }
  272.             if (isdigit(ch) || (ch == '.')) {
  273.                 reread();
  274.                 type = eatnumber();
  275.                 break;
  276.             }
  277.             scanerror(T_NULL, "Unknown token character '%c'", ch);
  278.         }
  279.     }
  280.     curtoken.t_type = (short)type;
  281.     return type;
  282. }
  283.  
  284.  
  285. /*
  286.  * Continue to eat up a comment string.
  287.  * The leading slash-asterisk has just been scanned at this point.
  288.  */
  289. static void
  290. eatcomment()
  291. {
  292.     int ch;
  293.  
  294.     for (;;) {
  295.         ch = nextchar();
  296.         if (ch == '*') {
  297.             ch = nextchar();
  298.             if (ch == '/')
  299.                 return;
  300.             reread();
  301.         }
  302.         if ((ch == EOF) || (ch == '\0') ||
  303.             (newlines && (ch == '\n') && inputisterminal())) {
  304.                 reread();
  305.                 scanerror(T_NULL, "Unterminated comment");
  306.                 return;
  307.         }
  308.     }
  309. }
  310.  
  311.  
  312. /*
  313.  * Read in a string and add it to the literal string pool.
  314.  * The leading single or double quote has been read in at this point.
  315.  */
  316. static void
  317. eatstring(quotechar)
  318. {
  319.     register char *cp;    /* current character address */
  320.     int ch;            /* current character */
  321.     char buf[MAXSTRING+1];    /* buffer for string */
  322.  
  323.     cp = buf;
  324.     for (;;) {
  325.         ch = nextchar();
  326.         switch (ch) {
  327.             case '\0':
  328.             case EOF:
  329.             case '\n':
  330.                 reread();
  331.                 scanerror(T_NULL, "Unterminated string constant");
  332.                 *cp = '\0';
  333.                 curtoken.t_str = addliteral(buf);
  334.                 return;
  335.  
  336.             case '\\':
  337.                 ch = nextchar();
  338.                 switch (ch) {
  339.                     case 'n': ch = '\n'; break;
  340.                     case 'r': ch = '\r'; break;
  341.                     case 't': ch = '\t'; break;
  342.                     case 'b': ch = '\b'; break;
  343.                     case 'f': ch = '\f'; break;
  344.                     case '\n':
  345.                         setprompt(PROMPT2);
  346.                         continue;
  347.                     case EOF:
  348.                         reread();
  349.                         continue;
  350.                 }
  351.                 *cp++ = (char)ch;
  352.                 break;
  353.  
  354.             case '"':
  355.             case '\'':
  356.                 if (ch == quotechar) {
  357.                     *cp = '\0';
  358.                     curtoken.t_str = addliteral(buf);
  359.                     return;
  360.                 }
  361.                 /* fall into default case */
  362.  
  363.             default:
  364.                 *cp++ = (char)ch;
  365.         }
  366.     }
  367. }
  368.  
  369.  
  370. /*
  371.  * Read in a symbol name which may or may not be a keyword.
  372.  * If allsyms is set, keywords are not looked up and almost all chars
  373.  * will be accepted for the symbol.  Returns the type of symbol found.
  374.  */
  375. static int
  376. eatsymbol()
  377. {
  378.     register struct keyword *kp;    /* pointer to current keyword */
  379.     register char *cp;        /* current character pointer */
  380.     int ch;                /* current character */
  381.     int cc;                /* character count */
  382.     static char buf[SYMBOLSIZE+1];    /* temporary buffer */
  383.  
  384.     cp = buf;
  385.     cc = SYMBOLSIZE;
  386.     if (allsyms) {
  387.         for (;;) {
  388.             ch = nextchar();
  389.             if ((ch == ' ') || (ch == ';') || (ch == '\n'))
  390.                 break;
  391.             if (cc-- > 0)
  392.                 *cp++ = (char)ch;
  393.         }
  394.         reread();
  395.         *cp = '\0';
  396.         if (cc < 0)
  397.             scanerror(T_NULL, "Symbol too long");
  398.         curtoken.t_str = buf;
  399.         return T_SYMBOL;
  400.     }
  401.     for (;;) {
  402.         ch = nextchar();
  403.         if (!issymbol(ch))
  404.             break;
  405.         if (cc-- > 0)
  406.             *cp++ = (char)ch;
  407.     }
  408.     reread();
  409.     *cp = '\0';
  410.     if (cc < 0)
  411.         scanerror(T_NULL, "Symbol too long");
  412.     for (kp = keywords; kp->k_name; kp++)
  413.         if (strcmp(kp->k_name, buf) == 0)
  414.             return kp->k_token;
  415.     curtoken.t_str = buf;
  416.     return T_SYMBOL;
  417. }
  418.  
  419.  
  420. /*
  421.  * Read in and remember a possibly numeric constant value.
  422.  * The constant is inserted into a constant table so further uses
  423.  * of the same constant will not take more memory.  This can also
  424.  * return just a period, which is used for element accesses and for
  425.  * the old numeric value.
  426.  */
  427. static int
  428. eatnumber()
  429. {
  430.     register char *cp;    /* current character pointer */
  431.     long len;        /* parsed size of number */
  432.     long res;        /* result of parsing number */
  433.  
  434.     if (numbufsize == 0) {
  435.         numbuf = (char *)malloc(128+1);
  436.         if (numbuf == NULL)
  437.             error("Cannot allocate number buffer");
  438.         numbufsize = 128;
  439.     }
  440.     cp = numbuf;
  441.     len = 0;
  442.     for (;;) {
  443.         if (len >= numbufsize) {
  444.             cp = (char *)realloc(numbuf, numbufsize + 1001);
  445.             if (cp == NULL)
  446.                 error("Cannot reallocate number buffer");
  447.             numbuf = cp;
  448.             numbufsize += 1000;
  449.             cp = &numbuf[len];
  450.         }
  451.         *cp = nextchar();
  452.         *(++cp) = '\0';
  453.         if ((numbuf[0] == '.') && isletter(numbuf[1])) {
  454.             reread();
  455.             return T_PERIOD;
  456.         }
  457.         res = qparse(numbuf, QPF_IMAG);
  458.         if (res < 0) {
  459.             reread();
  460.             scanerror(T_NULL, "Badly formatted number");
  461.             curtoken.t_numindex = addnumber("0");
  462.             return T_NUMBER;
  463.         }
  464.         if (res != ++len)
  465.             break;
  466.     }
  467.     cp[-1] = '\0';
  468.     reread();
  469.     if ((numbuf[0] == '.') && (numbuf[1] == '\0')) {
  470.         curtoken.t_numindex = 0;
  471.         return T_OLDVALUE;
  472.     }
  473.     cp -= 2;
  474.     res = T_NUMBER;
  475.     if ((*cp == 'i') || (*cp == 'I')) {
  476.         *cp = '\0';
  477.         res = T_IMAGINARY;
  478.     }
  479.     curtoken.t_numindex = addnumber(numbuf);
  480.     return res;
  481. }
  482.  
  483.  
  484. /*
  485.  * Return the string value of the current token.
  486.  */
  487. char *
  488. tokenstring()
  489. {
  490.     return curtoken.t_str;
  491. }
  492.  
  493.  
  494. /*
  495.  * Return the constant index of a numeric token.
  496.  */
  497. long
  498. tokennumber()
  499. {
  500.     return curtoken.t_numindex;
  501. }
  502.  
  503.  
  504. /*
  505.  * Push back the token just read so that it will be seen again.
  506.  */
  507. void
  508. rescantoken()
  509. {
  510.     rescan = TRUE;
  511. }
  512.  
  513.  
  514. /*
  515.  * Describe an error message.
  516.  * Then skip to the next specified token (or one more powerful).
  517.  */
  518. #ifdef VARARGS
  519. # define VA_ALIST skip, fmt, va_alist
  520. # define VA_DCL int skip; char *fmt; va_dcl
  521. #else
  522. # ifdef __STDC__
  523. #  define VA_ALIST int skip, char *fmt, ...
  524. #  define VA_DCL
  525. # else
  526. #  define VA_ALIST skip, fmt
  527. #  define VA_DCL int skip; char *fmt;
  528. # endif
  529. #endif
  530. /*VARARGS*/
  531. void
  532. scanerror(VA_ALIST)
  533.     VA_DCL
  534. {
  535.     va_list ap;
  536.     char *name;        /* name of file with error */
  537.     char buf[MAXERROR+1];
  538.  
  539.     errorcount++;
  540.     name = inputname();
  541.     if (name)
  542.         fprintf(stderr, "\"%s\", line %ld: ", name, linenumber());
  543. #ifdef VARARGS
  544.     va_start(ap);
  545. #else
  546.     va_start(ap, fmt);
  547. #endif
  548.     vsprintf(buf, fmt, ap);
  549.     va_end(ap);
  550.     fprintf(stderr, "%s\n", buf);
  551.     switch (skip) {
  552.         case T_NULL:
  553.             return;
  554.         case T_COMMA:
  555.             rescan = TRUE;
  556.             for (;;) {
  557.                 switch (gettoken()) {
  558.                 case T_NEWLINE:
  559.                 case T_SEMICOLON:
  560.                 case T_LEFTBRACE:
  561.                 case T_RIGHTBRACE:
  562.                 case T_EOF:
  563.                 case T_COMMA:
  564.                     rescan = TRUE;
  565.                     return;
  566.                 }
  567.             }
  568.         default:
  569.             fprintf(stderr, "Unknown skip token for scanerror\n");
  570.             /* fall into semicolon case */
  571.             /*FALLTHRU*/
  572.         case T_SEMICOLON:
  573.             rescan = TRUE;
  574.             for (;;) switch (gettoken()) {
  575.                 case T_NEWLINE:
  576.                 case T_SEMICOLON:
  577.                 case T_LEFTBRACE:
  578.                 case T_RIGHTBRACE:
  579.                 case T_EOF:
  580.                     rescan = TRUE;
  581.                     return;
  582.             }
  583.     }
  584. }
  585.  
  586. /* END CODE */
  587.