home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD2.mdf / c / library / os2 / pgen_2 / expr_lex.c next >
Encoding:
C/C++ Source or Header  |  1993-06-08  |  10.6 KB  |  374 lines

  1. /*    EXPR_LEX.C
  2.     Copyright (C) 1992    Keith L. Robertson    All Rights Reserved
  3.  
  4.     Lexical analyzer for the EXPR expression evaluator.
  5. */
  6. #include <ctype.h>
  7. #include "expr_lex.h"
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11.  
  12.  
  13. /****************************************/
  14. /*        Options            */
  15. /****************************************/
  16.  
  17. /* Maximum length of identifiers and other lexemes EXCEPT strings. */
  18. #define        MAX_LEX_LENGTH        128
  19.  
  20. extern SHORT    ERROR_THRESHOLD = 5;    /* Max # errors before it quits. */
  21. extern SHORT    TAB_STOPS = 8;
  22.  
  23.  
  24. /****************************************/
  25. /*      Helper Functions        */
  26. /****************************************/
  27.  
  28. /* Convert a constant number to a string. */
  29. #define        to_string(num)    #num
  30.  
  31. /* Convert unsigned short to string. */
  32. #define     MX_NLEN    6
  33. extern STRING    ustoa (USHORT num)
  34. {   static CHAR        nstr [MX_NLEN];
  35.     STRING   p;
  36.  
  37.     p = nstr+MX_NLEN-1;
  38.     *p = 0;
  39.     do    {
  40.     *--p = num % 10 + '0';
  41.     num = num / 10;
  42.     }    while  (num != 0);
  43.  
  44.     return  p;
  45. }
  46.  
  47. /* Write a string to Standard Output. */
  48. extern VOID    put_string (CSTRING str)
  49. {   fputs (str, stdout);
  50. }
  51.  
  52.  
  53. /****************************************/
  54. /*      Tables & Constant Data    */
  55. /****************************************/
  56.  
  57. /* Character classification: */
  58. enum    {
  59.     ALPHAB,        /* A-Z, a-z, _                    */
  60.     NUMBER,        /* 0-9                        */
  61.     WHT_SP,        /* \t \n \v \f \r space                */
  62.     BAD_CH,        /* Bad character  c < 32 || 127 < c        */
  63.     SYM,        /* SYM+ 0 - SYM+10, symbols recognized by lexer    */
  64.     BD_SYM= SYM+12,    /* Bad symbol, not otherwise listed.        */
  65.     EOF_CH,        /* End-of-file character            */
  66.     ENDCLASS
  67. };
  68.  
  69. /* Map character to classification: */
  70. UCHAR   char_class[256] = {
  71.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  72.     BAD_CH, WHT_SP, WHT_SP, WHT_SP, WHT_SP, WHT_SP, BAD_CH, BAD_CH, /* \btnvfr?? */
  73.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  74.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  75.     WHT_SP, BD_SYM, BD_SYM, BD_SYM, BD_SYM, BD_SYM, BD_SYM, BD_SYM, /*  !"#$%&' */
  76.     SYM+ 0, SYM+ 1, SYM+ 2, SYM+ 3, SYM+11, SYM+ 4, SYM+ 5, SYM+ 6, /* ()*+,-./ */
  77.     NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, NUMBER, /* 01234567 */
  78.     NUMBER, NUMBER, BD_SYM, SYM+ 7, BD_SYM, SYM+ 8, BD_SYM, SYM+10, /* 89:;<=>? */
  79.     BD_SYM, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, /* @ABCDEFG */
  80.     ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, /* HIJKLMNO */
  81.     ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, /* PQRSTUVW */
  82.     ALPHAB, ALPHAB, ALPHAB, BD_SYM, BD_SYM, BD_SYM, SYM+ 9, ALPHAB, /* XYZ[\]^_ */
  83.     BD_SYM, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, /* `abcdefg */
  84.     ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, /* hijklmno */
  85.     ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, ALPHAB, /* pqrstuvw */
  86.     ALPHAB, ALPHAB, ALPHAB, BD_SYM, BD_SYM, BD_SYM, BD_SYM, BAD_CH, /* xyz{|}~Del*/
  87.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, /* 128+    */
  88.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  89.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  90.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  91.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  92.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  93.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  94.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  95.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  96.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  97.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  98.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  99.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  100.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  101.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH,
  102.     BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, BAD_CH, EOF_CH,
  103. };
  104.  
  105. #define        is_idchar(ch)    (char_class [ch] <= NUMBER)
  106. #define        is_symchar(ch)    (temp_ch=char_class [ch],\
  107.     SYM <= temp_ch  &&  temp_ch < EOF_CH)
  108.  
  109.  
  110. /****************************************/
  111. /*           Static Data        */
  112. /****************************************/
  113.  
  114. static UCHAR    next_char;    /* Next character to process */
  115.  
  116.     /* String corresponding to returned token. */
  117. static CHAR    lex_buffer [MAX_LEX_LENGTH + 1];
  118. static USHORT    lex_len;        /* Length of lexeme. */
  119.  
  120. static USHORT    c_line, c_posn;
  121.  
  122. /*    lexeme
  123.     l   c
  124. lexeme.line, lexeme.posn -- start of lexeme
  125. c_line, c_posn -- start of current character
  126. */
  127.  
  128.  
  129. /****************************************/
  130. /*    Initialization Function        */
  131. /****************************************/
  132.  
  133. extern USHORT        error_count = 0;    /* Forward decls */
  134. extern UCHAR _near    adv ();
  135.  
  136. /* Initialize lexical analyzer.  Called before any call to get_token. */
  137. extern VOID    init_lexer ()
  138. {   error_count = 0;
  139.     c_posn = 0;
  140.     c_line = 1;
  141.     lex_len = 0;
  142.  
  143.     next_char = adv ();
  144. }
  145.  
  146.  
  147. /****************************************/
  148. /*      Error Output Functions    */
  149. /****************************************/
  150.  
  151. static CSTRING  err_type_str [] = {
  152.     "Lexical",  "Syntax",  "Semantic", "Fatal",
  153. };
  154.  
  155. /* Mtype is message type, error or warning. */
  156. static VOID _near    log_output (USHORT etype,  CSTRING desc,  STRING mtype)
  157. {   put_string ("Posn ");
  158.     put_string (ustoa (etype==LEXICAL ? c_posn : lexeme.posn));
  159.     put_string (": ");
  160.     put_string (err_type_str [etype]);
  161.     put_string (mtype);
  162.     put_string (desc);
  163.     put_string ("\n");
  164. }
  165.  
  166. /* Print an error message, desc. */
  167. /* Locn is type of error, LEXICAL, SYNTAX, SEMANTIC, FATAL */
  168. extern VOID    log_error (USHORT etype,  CSTRING desc)
  169. {   log_output (etype, desc, " error: ");
  170.  
  171.     if  (etype == FATAL)  { exit (1); }
  172.  
  173.     if  (ERROR_THRESHOLD != 0)    {
  174.     ++error_count;
  175.     if  (error_count >= ERROR_THRESHOLD)  {
  176.         put_string ("Aborting...too many errors.\n");
  177.         exit (1);
  178.     }
  179.     }
  180. }
  181.  
  182. extern VOID    log_warning (USHORT etype,  CSTRING desc)
  183. {   log_output (etype, desc, " warning: ");
  184. }
  185.  
  186.  
  187. /****************************************/
  188. /*      Advance Input Functions    */
  189. /****************************************/
  190. /* Use static '_near' functions to improve call time and program space. */
  191.  
  192. static UCHAR _near    get_char ()
  193. {   UCHAR  ch;
  194.     /* Skip and ignore carriage returns */
  195.     do    { ch = getchar (); }        while    (ch == '\r');
  196.  
  197.     if  (ch=='\n')  { ++c_line;  c_posn = 0; }
  198.     else if  (ch=='\t')  { c_posn = c_posn - (c_posn % TAB_STOPS) + TAB_STOPS; }
  199.     else   { ++c_posn; }
  200.  
  201.     return  ch;
  202. }
  203.  
  204. /* Advance to next character and return it */
  205. static UCHAR _near    adv ()
  206. {   UCHAR    ch;
  207.     lex_buffer [lex_len++] = next_char;
  208.     ch = get_char ();
  209.     return  (next_char = ch);
  210. }
  211.  
  212.  
  213. /****************************************/
  214. /*      External 'get_' Functions    */
  215. /****************************************/
  216.  
  217. /* Public */
  218. LEX_INFO    lexeme;
  219. USHORT        last_token;
  220.  
  221.  
  222. /* Get token from input stream. */
  223. extern USHORT    get_token ()
  224. {   register SHORT    token;
  225.     register UCHAR    ch = next_char, temp_ch;
  226.  
  227.     lex_len    = 0;
  228.     lexeme.name = 0;
  229.     lexeme.line = c_line;
  230.     lexeme.posn = c_posn;
  231.  
  232.     while  (1)  {
  233.  
  234.     switch  (char_class [ch])  {
  235.     /*---------------------------------*/
  236.     case  EOF_CH:
  237.         token = EOS;  goto RETURN;
  238.  
  239.     /*---------------------------------*/
  240.     case  ALPHAB:
  241.         if  (ch == 'e')  {
  242.         if  ((ch=adv())=='x'  &&  (ch=adv())=='i'  &&  (ch=adv())=='t')  {
  243.             token = EOS;  goto ENDKEY;
  244.         }
  245.         else    { goto IDENT; }
  246.         }
  247.         else if  (ch == 'h')  {
  248.         if  ((ch=adv())=='e'  &&  (ch=adv())=='l'  &&  (ch=adv())=='p')  {
  249.             token = HELP;  goto ENDKEY;
  250.         }
  251.         else    { goto IDENT; }
  252.         }
  253.         else if  (ch == 'k')  {
  254.         if  ((ch=adv())=='i'  &&  (ch=adv())=='l'  &&  (ch=adv())=='l')  {
  255.             token = KILL;  goto ENDKEY;
  256.         }
  257.         else    { goto IDENT; }
  258.         }
  259.         else if  (ch == 'l')  {
  260.         if  ((ch=adv()) == 'i'  &&  (ch=adv()) == 's')  {
  261.             if  ((ch=adv()) == 'p')  {
  262.             token = LISP;  goto ENDKEY;
  263.             }
  264.             else if  (ch == 't')  {
  265.             token = LIST;  goto ENDKEY;
  266.             }
  267.             else    { goto IDENT; }
  268.         }
  269.         else    { goto IDENT; }
  270.         }
  271.         else    { goto IDENT; }
  272.  
  273.     ENDKEY:
  274.         if  (!is_idchar(ch=adv()))  { goto RETURN; }
  275.         ch = adv();
  276.     IDENT:
  277.         while  (is_idchar(ch))  { ch = adv(); }
  278.         token = IDENTIFIER;
  279.         goto RETURN_LEXEME_STRING;
  280.  
  281.     /*---------------------------------*/
  282.     case  NUMBER:
  283.         while  ('0' <= ch  &&  ch <= '9')  { ch=adv(); }
  284.     case  SYM+ 5:
  285.         if  (ch == '.')  { ch=adv(); }
  286.         while  ('0' <= ch  &&  ch <= '9')  { ch=adv(); }
  287.  
  288.         /* Convert to floating point value.  Don't return string. */
  289.         lex_buffer [lex_len] = '\0';
  290.         lexeme.val.f = strtod (lex_buffer, 0);
  291.         token = REAL;
  292.         goto RETURN;
  293.  
  294.     /*---------------------------------*/
  295.     case  WHT_SP:
  296.         do    {
  297.         ch = adv ();
  298.         }    while  (char_class [ch] == WHT_SP);
  299.         goto RESTART;
  300.  
  301.     /*---------------------------------*/
  302.     case  BAD_CH:
  303.         log_error (LEXICAL, "unrecognized character(s) in input stream.");
  304.         do    {
  305.         ch = adv ();
  306.         }    while  (char_class [ch] == BAD_CH);
  307.         goto RESTART;
  308.  
  309.     /*---------------------------------*/
  310.     case  SYM+ 0:    /* ( */
  311.         token = OPEN_P;        goto ENDSYM;
  312.  
  313.     case  SYM+ 1:    /* ) */
  314.         token = CLOSE_P;        goto ENDSYM;
  315.  
  316.     case  SYM+ 2:    /* * */
  317.         token = TIMES;        goto ENDSYM;
  318.  
  319.     case  SYM+ 3:    /* + */
  320.         token = PLUS;        goto ENDSYM;
  321.  
  322.     case  SYM+ 4:    /* - */
  323.         token = MINUS;        goto ENDSYM;
  324.  
  325.     case  SYM+ 6:    /* / */
  326.         token = DIVIDE;        goto ENDSYM;
  327.  
  328.     case  SYM+ 8:    /* = */
  329.         token = EQUAL;        goto ENDSYM;
  330.  
  331.     case  SYM+ 9:    /* ^ */
  332.         token = POWER;        goto ENDSYM;
  333.  
  334.     case  SYM+10:    /* ? */
  335.         token = HELP;        goto ENDSYM;
  336.  
  337.     case  SYM+11:    /* , */
  338.         token = COMMA;        goto ENDSYM;
  339.  
  340.     case  SYM+ 7:    /* ; */
  341.         token = SEMICOLON;
  342.  
  343.     ENDSYM:
  344.         ch = adv();
  345.         goto RETURN;
  346.  
  347.     /*---------------------------------*/
  348.     case  BD_SYM:
  349.         log_error (LEXICAL, "unknown symbol.");
  350.         do    {
  351.         ch = adv ();
  352.         }    while  (is_symchar (ch));
  353.     RESTART:
  354.         next_char = ch;
  355.         lex_len = 0;
  356.         lexeme.line = c_line;
  357.         lexeme.posn = c_posn;
  358.     }
  359.     }
  360.  
  361. RETURN_LEXEME_STRING:
  362.     if  (lex_len > MAX_LEX_LENGTH)  {
  363.     /* Memory after lex_buffer has been trampled on. */
  364.     log_error (FATAL,
  365.       "only strings can exceed " to_string(MAX_LEX_LENGTH) " characters in length.");
  366.     }
  367.     lex_buffer [lex_len] = '\0';
  368.     lexeme.name = malloc (lex_len + 1);
  369.     strcpy (lexeme.name, lex_buffer);
  370.  
  371. RETURN:
  372.     return  (lexeme.token = last_token = token);
  373. }
  374.