home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 355_02 / slk2.exe / SPP / TOK.C < prev    next >
C/C++ Source or Header  |  1991-06-09  |  10KB  |  516 lines

  1. /*
  2.     New Sherlock Preprocessor -- Token parsing routines.
  3.  
  4.     source:  tok.c
  5.     started: October 7, 1985
  6.     version:
  7.         July 18, 1988
  8.         February 16, 1989
  9.             periods removed from error messages.
  10.         June 22, 1989
  11.             backslash-newline logic removed because sysnext()
  12.             now removes all backslash-newline combinations.
  13.         June 26, 1989
  14.             bug fix in skip_comment().
  15.  
  16.  
  17.     PUBLIC DOMAIN SOFTWARE
  18.  
  19.     Sherlock, including the SPP, SDEL and SDIF programs, was placed in
  20.     the public domain on June 15, 1991, by its author,
  21.  
  22.         Edward K. Ream
  23.         166 North Prospect Ave.
  24.         Madison, WI 53705.
  25.         (608) 257-0802
  26.  
  27.     Sherlock may be used for any commercial or non-commercial purpose.
  28.  
  29.  
  30.     DISCLAIMER OF WARRANTIES
  31.  
  32.     Edward K. Ream (Ream) specifically disclaims all warranties,
  33.     expressed or implied, with respect to this computer software,
  34.     including but not limited to implied warranties of merchantability
  35.     and fitness for a particular purpose.  In no event shall Ream be
  36.     liable for any loss of profit or any commercial damage, including
  37.     but not limited to special, incidental consequential or other damages.
  38. */
  39.  
  40. #include "spp.h"
  41.  
  42. /* Declare internal variables and routines. */
  43. static int     is_base_digit    (int);
  44. static void     scan_number    (int);
  45. static en_tokens t_int        (void);
  46.  
  47. /*
  48.     Return >= 0 if ch is a digit in the indicated base.
  49.     Otherwise, return -1.
  50. */
  51. static int
  52. is_base_digit(base)
  53. int base;
  54. {
  55.     TRACECH("is_base_digit");
  56.  
  57.     if (ch >= '0' && ch <= '9') {
  58.         RETURN_INT("is_base_digit", ch - '0');
  59.     }
  60.     else if (base != 16) {
  61.         RETURN_INT("is_base_digit", -1);
  62.     }
  63.     else if (ch >= 'a' && ch <= 'f') {
  64.         RETURN_INT("is_base_digit", ch - 'a' + 10);
  65.     }
  66.     else if (ch >= 'A' && ch <= 'F') {
  67.          RETURN_INT("is_base_digit", ch - 'A' + 10);
  68.     }
  69.     else {
  70.         RETURN_INT("is_base_digit", -1);
  71.     }
  72. }
  73.  
  74. /*
  75.     Get value of a string of digits into t_value.
  76.     Continue until a non base digit is found.
  77. */
  78. static void
  79. scan_number(base)
  80. int base;
  81. {
  82.     int result;
  83.  
  84.     TRACECH("scan_number");
  85.  
  86.     t_value = 0;
  87.     while (    (result = is_base_digit(base)) != -1) {
  88.         t_value = ((long)base * t_value) + (long) result;
  89.         sysnext();
  90.     }
  91.  
  92.     LEAVE("scan_number");
  93. }
  94.  
  95. /*
  96.     Scan past a comment. Allow nested comments if nest_flag is TRUE.
  97.     Surprisingly, this routine needs to be as fast as possible.
  98. */
  99. void
  100. skip_comment(void)
  101. {
  102.     register int clevel;
  103.     int start_line;
  104.     char line [LONG_DIGITS];
  105.  
  106.     TRACECH("skip_comment");
  107.  
  108.     /* Save starting line number for run-on comments. */
  109.     start_line = t_line;
  110.     clevel = 1;
  111.  
  112.     /* Do not buffer the comment. */
  113.     syshflush();
  114.     hold_flag = FALSE;
  115.  
  116.     for (;;) {
  117.         switch (ch) {
  118.  
  119.         case END_FILE:
  120.             conv2s(start_line, line);
  121.             err2("File ends in a comment starting at line ", line);
  122.             hold_flag = TRUE;
  123.             RETURN_VOID("skip_comment");
  124.  
  125.         case '\n':
  126.             /* Keep track of line numbering. */
  127.             sysnext();
  128.  
  129.             /*
  130.                 Bump the line count and output a newline
  131.                 if this is used in a standalone preprocessor.
  132.                 Do NOT allow directives here.
  133.             */
  134.             bump_line();
  135.             /* Bug fix: 6/26/89 do NOT call begin_line!! */
  136.             /* --- begin_line(FALSE); --- */
  137.             first_tok = TRUE;
  138.             continue;
  139.  
  140.         case '/':
  141.  
  142.             sysnext();
  143.             if (ch == '*') {
  144.                 sysnext();
  145.                 if (nest_flag) {
  146.                     clevel++;
  147.                 }
  148.             }
  149.             continue;
  150.  
  151.         case '*':
  152.  
  153.             sysnext();
  154.             if (ch == '/') {
  155.                 sysnext();
  156.                 if (--clevel == 0) {
  157.                     hold_flag = TRUE;
  158.                     RETURN_VOID("skip_comment");
  159.                 }
  160.             }
  161.             continue;
  162.  
  163.  
  164.         default:
  165.             sysnext();
  166.         }
  167.     }
  168. }
  169.  
  170. /*
  171.     Copy an identifier into symbol[] and its length in the global t_length.
  172.     Surprisingly, this routine should be as fast as possible.
  173. */
  174. void
  175. t_id(symbol, max_length)
  176. char *    symbol;
  177. int    max_length;
  178. {
  179.     int length = 0;
  180.  
  181.     ENTER_TRACE("t_id", printf("(%p, %d)\n", symbol, max_length));
  182.  
  183.     max_length--;
  184.     while (isid2(ch) && length < max_length) {
  185.         *symbol++ = ch;
  186.         length++;
  187.         sysnext();
  188.     }
  189.     *symbol = '\0';
  190.     t_length = length;
  191.  
  192.     if (length >= max_length) {
  193.         error("Identifier too long");
  194.     }
  195.  
  196.     LEAVE("t_id");
  197. }
  198.  
  199. /*
  200.     Parse an integer constant (octal, decimal or hexadecimal) or float.
  201.     Put the value in t_value if it is an integer.
  202.  
  203.     dot_flag is TRUE if a decimal point has been seen.
  204.  
  205.     Return the token type (INT_TOK, LONG_TOK, FLOAT_TOK).
  206.  
  207.     Legal integer forms:    ddd,    0ddd,    0xddd
  208.     Legal float forms:    xxx.yyyE+zzz
  209.  
  210.     +-zzz is optional
  211.     one of xxx and yyy may be omitted
  212.     one of . and E may be omitted
  213. */
  214. en_tokens
  215. t_number(dot_flag)
  216. bool dot_flag;
  217. {
  218.     en_tokens result;
  219.     bool need_exp = FALSE;
  220.  
  221.     TRACECH("t_number");
  222.  
  223.     /* Defaults. */
  224.     t_value = 0;
  225.  
  226.     if (dot_flag) {
  227.         goto frac_part;
  228.     }
  229.  
  230.     /* Integer part. */
  231.  
  232.     result = t_int();
  233.     if (ch == '.') {
  234.         sysnext();
  235.         goto frac_part;
  236.     }
  237.     else if (ch == 'e' || ch == 'E') {
  238.         goto exp_part;
  239.     }
  240.     else {
  241.         RETURN_INT("t_number", result);
  242.     }
  243.  
  244.     /* Fraction part. */
  245. frac_part:    
  246.     t_int();
  247.  
  248. exp_part:
  249.     if (ch == 'e' || ch == 'E') {
  250.         need_exp = TRUE;
  251.         sysnext();
  252.     }
  253.     if (ch == '+' || ch == '-') {
  254.         need_exp = TRUE;
  255.         sysnext();
  256.     }
  257.     if (ch >= '0' && ch <= '9') {
  258.         t_int();
  259.     }
  260.     else if (need_exp) {
  261.         error("Ill formed floating constant");
  262.     }
  263.  
  264.     if (ch == 'l' || ch == 'L' || ch == 'f' || ch == 'F') {
  265.         sysnext();
  266.     }
  267.  
  268.     RETURN_INT("t_number", FLOAT_TOK);
  269. }
  270.  
  271. static en_tokens
  272. t_int()
  273. {
  274.     TRACECH("t_int");
  275.  
  276.     /* Leading 0 or 0x changes base. */    
  277.     if (ch == '0') {
  278.         sysnext();
  279.         if (ch == 'x' || ch == 'X') {
  280.             sysnext();
  281.             scan_number(16);
  282.         }
  283.         else if (isdigit(ch)) {
  284.             scan_number(8);
  285.         }
  286.         else {
  287.             /* Lone '0'. */
  288.             t_value = 0;
  289.         }
  290.     }
  291.     else {
  292.         scan_number(10);
  293.     }
  294.  
  295.     if (ch == 'l' || ch == 'L') {
  296.         sysnext();
  297.         if(ch == 'u' || ch == 'U') {
  298.             sysnext();
  299.         }
  300.         RETURN_INT("t_int", LONG_TOK);
  301.     }
  302.     else if (ch == 'u' || ch == 'U') {
  303.         sysnext();
  304.         if (ch == 'l' || ch == 'L') {
  305.             RETURN_INT("t_int", LONG_TOK);
  306.         }
  307.         else {
  308.             RETURN_INT("t_int", INT_TOK);
  309.         }
  310.     }
  311.     else {
  312.         RETURN_INT("t_int", INT_TOK);
  313.     }
  314. }
  315.  
  316. /*
  317.     Copy a string into out[] and puts its length in the global t_length.
  318.     Copy the opening or closing delimiters if the copy_flag is TRUE.
  319.  
  320.     This is used to parse both strings and character constants.
  321. */
  322. void
  323. t_string(out, max_out, copy_flag)
  324. register char * out;    /* The output buffer.                */
  325. int    max_out;    /* The size of out[].                */
  326. bool    copy_flag;    /* Copy the delimiters if copy_flag is TRUE.    */
  327. {
  328.     register int length;
  329.     int    start_line;
  330.     char *    start_string;
  331.     char    line [10];
  332.     int    delim;
  333.  
  334.     TRACECH("t_string");
  335.  
  336.     /* Save starting line number for error messages. */
  337.     start_line = t_line;
  338.     start_string = out;
  339.  
  340.     /* Handle the opening single or double quote */
  341.     delim = ch;
  342.     sysnext();
  343.     length = 0;
  344.     if (copy_flag) {
  345.         *out++ = delim;
  346.         length++;
  347.     }
  348.  
  349.     max_out--;
  350.     while (length < max_out) {
  351.  
  352.         switch(ch) {
  353.  
  354.         case END_FILE:
  355.         case '\n':
  356.             goto runon1;
  357.  
  358.         case '"':
  359.         case '\'':
  360.             if (ch == delim) {
  361.                 sysnext();
  362.                 if(copy_flag) {
  363.                     *out++ = delim;
  364.                     length++;
  365.                 }
  366.                 *out++ = '\0';
  367.                 t_length  = length;
  368.                 TRACEP("t_string", printf("<%s>\n",
  369.                     start_string));
  370.                 RETURN_VOID("t_string");
  371.             }
  372.             else{
  373.                 *out++ = ch;
  374.                 length++;
  375.                 sysnext();
  376.             }
  377.             continue;
  378.  
  379.         case '\\':
  380.  
  381.             sysnext();
  382.             if (ch == END_FILE) {
  383.                 goto runon1;
  384.             }
  385.  
  386. #ifdef BACKSLASH_NEWLINE
  387.             else if (ch == '\n') {
  388.                 /* Ignore back slash and newline. */
  389.                 t_line++;
  390.                 sysnext();
  391.             }
  392. #endif
  393.  
  394.             else {
  395.                 *out++ = '\\';
  396.                 *out++ = ch;
  397.                 length += 2;
  398.                 sysnext();
  399.             }
  400.             continue;
  401.  
  402.         default:
  403.             *out++ = ch;
  404.             length++;
  405.             sysnext();
  406.         }
  407.     }
  408.  
  409.     conv2s(start_line, line);
  410.     err3("String starting at line ", line, " is too long");
  411.  
  412.     *out = '\0';
  413.     t_length = length;
  414.     RETURN_VOID("t_string");
  415.  
  416. runon1:
  417.     *out   = '\0';
  418.     err3("String crosses a line: \"", start_string, "\"");
  419.     t_length  = length;
  420.     LEAVE("t_string");
  421. }
  422.  
  423. /*
  424.     Parse a string (including delimiters) from in[] to out.
  425.     Return the length of the ORIGINAL string.
  426. */
  427. int
  428. in_string(in, out, max_out)
  429. char *    in;        /* The output buffer    */
  430. char *    out;        /* The output buffer.    */
  431. int    max_out;    /* The size of out[].    */
  432. {
  433.     register int length;
  434.     int    start_line;
  435.     char *    start_string;
  436.     char    line [10];
  437.     int    delim;
  438.  
  439.     ENTER_TRACE("in_string", printf("(<%s>, %p, %d)\n",
  440.         in, out, max_out));
  441.  
  442.     /* Save starting line number for error messages. */
  443.     start_line = t_line;
  444.     start_string = out;
  445.  
  446.     /* Copy the opening single or double quote */
  447.     *out++ = delim = *in++;
  448.     length = 1;
  449.     max_out--;
  450.     while (length < max_out) {
  451.  
  452.         switch(*in) {
  453.  
  454.         case END_FILE:
  455.         case '\n':
  456.             goto runon1;
  457.  
  458.         case '"':
  459.         case '\'':
  460.             if (*in == delim) {
  461.                 *out++ = *in++;
  462.                 *out++ = '\0';
  463.                 length++;
  464.                 hold_flag = TRUE;
  465.                 RETURN_INT("in_string", length);
  466.             }
  467.             else{
  468.                 *out++ = *in++;
  469.                 length++;
  470.             }
  471.             continue;
  472.  
  473.         case '\\':
  474.  
  475.             in++;
  476.             if (*in == END_FILE) {
  477.                 goto runon1;
  478.             }
  479.  
  480. #ifdef BACKSLASH_NEWLINE
  481.             else if (*in == '\n') {
  482.                 /* Ignore back slash and newline. */
  483.                 t_line++;
  484.                 in++;
  485.  
  486.                 /* Keep track of ORIGINAL length. */
  487.                 length += 2;
  488.             }
  489. #endif
  490.  
  491.             else {
  492.                 *out++ = '\\';
  493.                 *out++ = *in++;
  494.                 length += 2;
  495.             }
  496.             continue;
  497.  
  498.         default:
  499.             *out++ = *in++;
  500.             length++;
  501.         }
  502.     }
  503.  
  504.     conv2s(start_line, line);
  505.     err3("Stringized argument starting at line ", line, " is too long");
  506.     *out = '\0';
  507.     hold_flag = TRUE;
  508.     RETURN_INT("in_string", length);
  509.  
  510. runon1:
  511.     *out   = '\0';
  512.     err2("Stringized argument crosses a line: ", start_string);
  513.     hold_flag = TRUE;
  514.     RETURN_INT("in_string", length);
  515. }
  516.