home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 319_02 / tok.c < prev    next >
C/C++ Source or Header  |  1990-06-18  |  9KB  |  531 lines

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