home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / smallt~1 / smallt~1.zoo / mstlex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-26  |  24.7 KB  |  1,104 lines

  1. /***********************************************************************
  2.  *
  3.  *    Lexer Module.
  4.  *
  5.  ***********************************************************************/
  6.  
  7. /***********************************************************************
  8.  *
  9.  * Copyright (C) 1988, 1989, 1990 Free Software Foundation, Inc.
  10.  * Written by Steve Byrne.
  11.  *
  12.  * This file is part of GNU Smalltalk.
  13.  *
  14.  * GNU Smalltalk is free software; you can redistribute it and/or modify it
  15.  * under the terms of the GNU General Public License as published by the Free
  16.  * Software Foundation; either version 1, or (at your option) any later 
  17.  * version.
  18.  * 
  19.  * GNU Smalltalk is distributed in the hope that it will be useful, but WITHOUT
  20.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
  21.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  22.  * more details.
  23.  * 
  24.  * You should have received a copy of the GNU General Public License along with
  25.  * GNU Smalltalk; see the file COPYING.  If not, write to the Free Software
  26.  * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
  27.  *
  28.  ***********************************************************************/
  29.  
  30. /*
  31.  *    Change Log
  32.  * ============================================================================
  33.  * Author      Date       Change 
  34.  * sbyrne    24 Apr 90      Error checking for integers too large.
  35.  *
  36.  * sbyrne    20 Apr 90      Added the closeIt argument to popStream so that the
  37.  *              closing behavior could be separated from the popping
  38.  *              behavior (in particular, for fileIn).
  39.  *
  40.  * sbyrne     7 Apr 90      Character lexing routines (such as nextChar) now
  41.  *              return ints to get around problems on implementations
  42.  *              that don't sign extend characters by default.
  43.  *
  44.  * sbyrne    15 Feb 90      Added support for := as alternative assignment
  45.  *              operator.
  46.  *
  47.  * sbyrne     3 Sep 89      added getCurFileName
  48.  *
  49.  * sbyrne    30 Aug 89      Fixed a bug in parseIdent which was parsing foo:2
  50.  *              (note no space) not as foo: and 2, but as a mixed up
  51.  *              token.
  52.  *
  53.  * sbyrne     8 Jul 89      Added prompt when input is a terminal.  This should
  54.  *              help Emacs's shell mode determine what has been typed
  55.  *              as input to the system.
  56.  *
  57.  * sbyrne    24 Jan 89      Added 2 chars of push back, because 3. needs to look
  58.  *              ahead one more character to see if its 3.DIGIT or 3.
  59.  *              next statement.
  60.  *
  61.  * sbyrne    27 Dec 88    Created.
  62.  */
  63.  
  64.  
  65. #include "mst.h"
  66. #include "mstlex.h"
  67. #include "mst.tab.h"
  68. #include "mststr.h"
  69. #include "mstid.h"
  70. #include "mstdict.h"
  71. #include "mstcomp.h"
  72. #include "msttree.h"
  73. #include <stdio.h>
  74. #include <math.h>
  75. #ifdef USE_READLINE
  76. #include <readline/readline.h>
  77. #endif /* USE_READLINE */
  78.  
  79. #define WHITE_SPACE        1
  80. #define DIGIT            2
  81. #define ID_CHAR            4
  82. #define SPECIAL_CHAR        8
  83.  
  84. #define ERROR_ARGS        arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8
  85.  
  86. #define EMACS_PROCESS_MARKER    '\001' /* ^A as marker -- random choice */
  87.  
  88. typedef struct StringStreamStruct {
  89.   char        *strBase;    /* base of asciz string */
  90.   char        *str;        /* pointer into asciz string */
  91. } StringStream;
  92.  
  93. struct StreamStruct {
  94.   StreamType    type;
  95.   int        pushedBackChar;    /* holds the 1 character of buffering */
  96.   int        pushedBackCount; /* number of chars pushed back */
  97.   int        line;
  98.   int        column;
  99.   char        *fileName;
  100.   Boolean    prompt;
  101.   union {
  102.     FILE    *u_st_file;
  103.     StringStream u_st_str;
  104.   } st;
  105.   Stream    prevStream;
  106. };
  107.  
  108. #define st_file        st.u_st_file
  109. #define st_str        st.u_st_str
  110.  
  111. Stream    inStream = NULL;
  112. int    lexDebug;
  113. char    *processingFile = nil;
  114.  
  115. static int            byteAddr, methodStartPos = 0;
  116. static Boolean            produceInternalToken;
  117.  
  118. static Boolean            isDigit(), isBaseDigit(), parsePrimitive(),
  119.                 parseDigits(), parseFraction();
  120. static int            illegal(), comment(), charLiteral(),
  121.                 parseBinOP(), stringLiteral(), parseNumber(),
  122.                 parseIdent(), myGetC(), parseColon(),
  123.                   nextChar();
  124. static char            *scanStringoid();
  125. static void             unreadChar(), lineStamp(), myUngetC(), 
  126.                 myClose();
  127. static Stream            pushNewStream();
  128.  
  129. typedef struct {
  130.   int        (*lexFunc)();
  131.   int        retToken;
  132.   int        charClass;
  133. } LexTabElt;
  134.  
  135. static LexTabElt charTab[128] = {
  136. /*   0 */    illegal,    0,    0,
  137. /*   1 */    illegal,    0,    0,
  138. /*   2 */    illegal,    0,    0,
  139. /*   3 */    illegal,    0,    0,
  140. /*   4 */    illegal,    0,    0,
  141. /*   5 */    illegal,    0,    0,
  142. /*   6 */    illegal,    0,    0,
  143. /*   7 */    illegal,    0,    0,
  144. /*   8 */    illegal,    0,    0,
  145. /*   9 */    0,        0,    WHITE_SPACE,
  146. /*  10 */    0,        0,    WHITE_SPACE,
  147. /*  11 */    illegal,    0,    0,
  148. /*  12 */    0,        0,    WHITE_SPACE, 
  149. /*  13 */    0,        0,    WHITE_SPACE,
  150. /*  14 */    illegal,    0,    0,    
  151. /*  15 */    illegal,    0,    0,
  152. /*  16 */    illegal,    0,    0,
  153. /*  17 */    illegal,    0,    0,
  154. /*  18 */    illegal,    0,    0,
  155. /*  19 */    illegal,    0,    0,
  156. /*  20 */    illegal,    0,    0,
  157. /*  21 */    illegal,    0,    0,
  158. /*  22 */    illegal,    0,    0,
  159. /*  23 */    illegal,    0,    0,
  160. /*  24 */    illegal,    0,    0,
  161. /*  25 */    illegal,    0,    0,
  162. /*  26 */    illegal,    0,    0,
  163. /*  27 */    illegal,    0,    0,
  164. /*  28 */    illegal,    0,    0,
  165. /*  29 */    illegal,    0,    0,
  166. /*  30 */    illegal,    0,    0,
  167. /*  31 */    illegal,    0,    0,
  168. /*     */    0,        0,    WHITE_SPACE,
  169. /*   ! */    parseBinOP,    0,    SPECIAL_CHAR, 
  170. /*   " */    comment,    0,    0,
  171. /*   # */    0,        SHARP,    0,
  172. /*   $ */    charLiteral,    0,    0,
  173. /*   % */    parseBinOP,    0,    SPECIAL_CHAR,
  174. /*   & */    parseBinOP,    0,    SPECIAL_CHAR,
  175. /*   ' */    stringLiteral,    0,    0,
  176. /*   ( */    0,        OPEN_PAREN, 0,
  177. /*   ) */    0,        CLOSE_PAREN, 0,
  178. /*   * */    parseBinOP,    0,    SPECIAL_CHAR,
  179. /*   + */    parseBinOP,    0,    SPECIAL_CHAR,
  180. /*   , */    parseBinOP,    0,    SPECIAL_CHAR,
  181. /*   - */    parseBinOP,    0,    SPECIAL_CHAR,
  182. /*   . */    0,        DOT,    0,
  183. /*   / */    parseBinOP,    0,    SPECIAL_CHAR,
  184. /*   0 */    parseNumber,    0,    DIGIT | ID_CHAR,
  185. /*   1 */    parseNumber,    0,    DIGIT | ID_CHAR,
  186. /*   2 */    parseNumber,    0,    DIGIT | ID_CHAR,
  187. /*   3 */    parseNumber,    0,    DIGIT | ID_CHAR,
  188. /*   4 */    parseNumber,    0,    DIGIT | ID_CHAR,
  189. /*   5 */    parseNumber,    0,    DIGIT | ID_CHAR,
  190. /*   6 */    parseNumber,    0,    DIGIT | ID_CHAR,
  191. /*   7 */    parseNumber,    0,    DIGIT | ID_CHAR,
  192. /*   8 */    parseNumber,    0,    DIGIT | ID_CHAR,
  193. /*   9 */    parseNumber,    0,    DIGIT | ID_CHAR,
  194. /*   : */    parseColon,    0,    0,
  195. /*   ; */    0,        SEMICOLON, 0,
  196. /*   < */    parseBinOP,    0,    SPECIAL_CHAR,
  197. /*   = */    parseBinOP,    0,    SPECIAL_CHAR,
  198. /*   > */    parseBinOP,    0,    SPECIAL_CHAR,
  199. /*   ? */    parseBinOP,    0,    SPECIAL_CHAR,
  200. /*   @ */    parseBinOP,    0,    SPECIAL_CHAR,
  201. /*   A */    parseIdent,    0,    ID_CHAR,
  202. /*   B */    parseIdent,    0,    ID_CHAR,
  203. /*   C */    parseIdent,    0,    ID_CHAR,
  204. /*   D */    parseIdent,    0,    ID_CHAR,
  205. /*   E */    parseIdent,    0,    ID_CHAR,
  206. /*   F */    parseIdent,    0,    ID_CHAR,
  207. /*   G */    parseIdent,    0,    ID_CHAR,
  208. /*   H */    parseIdent,    0,    ID_CHAR,
  209. /*   I */    parseIdent,    0,    ID_CHAR,
  210. /*   J */    parseIdent,    0,    ID_CHAR,
  211. /*   K */    parseIdent,    0,    ID_CHAR,
  212. /*   L */    parseIdent,    0,    ID_CHAR,
  213. /*   M */    parseIdent,    0,    ID_CHAR,
  214. /*   N */    parseIdent,    0,    ID_CHAR,
  215. /*   O */    parseIdent,    0,    ID_CHAR,
  216. /*   P */    parseIdent,    0,    ID_CHAR,
  217. /*   Q */    parseIdent,    0,    ID_CHAR,
  218. /*   R */    parseIdent,    0,    ID_CHAR,
  219. /*   S */    parseIdent,    0,    ID_CHAR,
  220. /*   T */    parseIdent,    0,    ID_CHAR,
  221. /*   U */    parseIdent,    0,    ID_CHAR,
  222. /*   V */    parseIdent,    0,    ID_CHAR,
  223. /*   W */    parseIdent,    0,    ID_CHAR,
  224. /*   X */    parseIdent,    0,    ID_CHAR,
  225. /*   Y */    parseIdent,    0,    ID_CHAR,
  226. /*   Z */    parseIdent,    0,    ID_CHAR,
  227. /*   [ */    0,        OPEN_BRACKET, 0,
  228. /*   \ */    parseBinOP,    0,    SPECIAL_CHAR,
  229. /*   ] */    0,        CLOSE_BRACKET, 0,
  230. /*   ^ */    0,        UPARROW, 0,
  231. /*   _ */    0,        ASSIGN,    0,
  232. /*   ` */    illegal,    0,    0,
  233. /*   a */    parseIdent,    0,    ID_CHAR,
  234. /*   b */    parseIdent,    0,    ID_CHAR,
  235. /*   c */    parseIdent,    0,    ID_CHAR,
  236. /*   d */    parseIdent,    0,    ID_CHAR,
  237. /*   e */    parseIdent,    0,    ID_CHAR,
  238. /*   f */    parseIdent,    0,    ID_CHAR,
  239. /*   g */    parseIdent,    0,    ID_CHAR,
  240. /*   h */    parseIdent,    0,    ID_CHAR,
  241. /*   i */    parseIdent,    0,    ID_CHAR,
  242. /*   j */    parseIdent,    0,    ID_CHAR,
  243. /*   k */    parseIdent,    0,    ID_CHAR,
  244. /*   l */    parseIdent,    0,    ID_CHAR,
  245. /*   m */    parseIdent,    0,    ID_CHAR,
  246. /*   n */    parseIdent,    0,    ID_CHAR,
  247. /*   o */    parseIdent,    0,    ID_CHAR,
  248. /*   p */    parseIdent,    0,    ID_CHAR,
  249. /*   q */    parseIdent,    0,    ID_CHAR,
  250. /*   r */    parseIdent,    0,    ID_CHAR,
  251. /*   s */    parseIdent,    0,    ID_CHAR,
  252. /*   t */    parseIdent,    0,    ID_CHAR,
  253. /*   u */    parseIdent,    0,    ID_CHAR,
  254. /*   v */    parseIdent,    0,    ID_CHAR,
  255. /*   w */    parseIdent,    0,    ID_CHAR,
  256. /*   x */    parseIdent,    0,    ID_CHAR,
  257. /*   y */    parseIdent,    0,    ID_CHAR,
  258. /*   z */    parseIdent,    0,    ID_CHAR,
  259. /*   { */    illegal,    0,    0,
  260. /*   | */    parseBinOP,    0,    SPECIAL_CHAR,
  261. /*   } */    illegal,    0,    0,
  262. /*   ~ */    parseBinOP,    0,    SPECIAL_CHAR,
  263. /*  ^? */    illegal,    0,    0
  264. };
  265.  
  266.  
  267. int yylex(lvalp, llocp)
  268. YYSTYPE *lvalp;
  269. voidPtr *llocp;        /* Bison is broken, doesn define type YYLTYPE!! */
  270. {
  271.   int        ic, result, tokenStartPos;
  272.  
  273.   if (produceInternalToken) {
  274.     /* The internal token is a trick to make the grammar be "conditional".
  275.      * Normally, the allowable syntax for internal compilation is that of
  276.      * a single method (without the terminating BANG).  However, would make
  277.      * for an ambiguous grammar since a method declaration could look like
  278.      * an immediate expression.  So, when the compiler is called internally,
  279.      * we force the first token returned by the lexer to be INTERNAL_TOKEN
  280.      * and make the top most production in the grammar use INTERNAL_TOKEN as
  281.      * the first token of an internal method. */
  282.     produceInternalToken = false;
  283.     return (INTERNAL_TOKEN);
  284.   }
  285.  
  286.   while ((ic = nextChar()) != EOF) {
  287.     if ((charTab[ic].charClass & WHITE_SPACE) == 0) {
  288.       if (methodStartPos < 0) {
  289.     tokenStartPos = getCurFilePos();
  290.       }
  291.  
  292.       if (charTab[ic].lexFunc) {
  293.     result = (*charTab[ic].lexFunc)(ic, lvalp);
  294.     if (result) {
  295.       if (methodStartPos < 0) { /* only do this here to ignore comments */
  296.         methodStartPos = tokenStartPos - 1;
  297.       }
  298.       return (result);
  299.     }
  300.       } else if (charTab[ic].retToken) {
  301.     return (charTab[ic].retToken);
  302.       } else {
  303.     errorf("Unknown character %d in input stream, ignoring", ic);
  304.     hadError = true;
  305.       }
  306.     }
  307.   }
  308.   return (0);            /* keep fussy compilers happy */
  309. }
  310.  
  311. initLexer(calledInternally)
  312. Boolean calledInternally;
  313. {
  314.   byteAddr = 0;
  315.   
  316.   produceInternalToken = calledInternally;
  317. }
  318.  
  319. static int illegal(c, lvalp)
  320. char    c;
  321. YYSTYPE *lvalp;
  322. {
  323.   char        charName[3], *cp;
  324.  
  325.   cp = charName;
  326.  
  327.   if (c < ' ' || c > '~') {
  328.     *cp++ = '^';
  329.     c &= 127;            /* strip high bit */
  330.     c ^= 64;            /* uncontrolify */
  331.   }
  332.  
  333.   *cp++ = c;
  334.   *cp++ = '\0';
  335.   
  336.   errorf("Illegal character %s", charName);
  337.   hadError = true;
  338. }
  339.  
  340.  
  341. /*
  342.  *    static int comment(c, lvalp)
  343.  *
  344.  * Description
  345.  *
  346.  *    Scan a comment, but return 0 to indicate to the lexer that it's to be
  347.  *    ignored (since it is a comment).
  348.  *
  349.  * Inputs
  350.  *
  351.  *    c     : first character of the comment (the delimiter).  Not terribly
  352.  *        useful, but it's what the lexer calls us with.
  353.  *    lvalp : ignored...passed because we're invoked indirectly and some of
  354.  *        the functions that could be called require this parameter.
  355.  *
  356.  * Outputs
  357.  *
  358.  *    0, which tells the lexer to ignore this token.
  359.  */
  360. static int comment(c, lvalp)
  361. char    c;
  362. YYSTYPE *lvalp;
  363. {
  364.   scanStringoid(c, "comment");
  365.  
  366.   return (0);
  367. }
  368.  
  369. /*
  370.  *    static int charLiteral(c, lvalp)
  371.  *
  372.  * Description
  373.  *
  374.  *    parse a character literal.
  375.  *
  376.  * Inputs
  377.  *
  378.  *    c     : input character -- ignored
  379.  *    lvalp : ignored -- passed because we're called indirectly.
  380.  *
  381.  * Outputs
  382.  *
  383.  *    CHAR_LITERAL token normally; 0 on EOF.
  384.  */
  385. static int charLiteral(c, lvalp)
  386. char    c;
  387. YYSTYPE *lvalp;
  388. {
  389.   int        ic;
  390.  
  391.   ic = nextChar();
  392.   if (ic == EOF) {
  393.     errorf("Unterminated character literal, attempting recovery");
  394.     unreadChar(ic);
  395.     hadError = true;
  396.     return (0);
  397.   } else {
  398.     lvalp->cval = ic;
  399.     return (CHAR_LITERAL);
  400.   }
  401. }
  402.  
  403. static int parseColon(c, lvalp)
  404. char    c;
  405. YYSTYPE *lvalp;
  406. {
  407.   int        ic;
  408.  
  409.   ic = nextChar();
  410.   if (ic == '=') {        /* parse :=, compatibility mode assign */
  411.     return (ASSIGN);
  412.   } else {
  413.     unreadChar(ic);
  414.   }
  415.  
  416.   return (COLON);
  417. }
  418.  
  419.  
  420. static int parseBinOP(c, lvalp)
  421. char    c;
  422. YYSTYPE *lvalp;
  423. {
  424.   char        buf[3], *bp;
  425.   int        ic;
  426.  
  427.   bp = buf;
  428.   *bp++ = c;
  429.  
  430.   ic = nextChar();
  431.   if (c == '<') {
  432.     if (ic == 'p') {
  433.       if (parsePrimitive(ic, lvalp)) {
  434.     return (PRIMITIVE_START);
  435.       }
  436.     }
  437.   }
  438.   if (c == '-') {
  439.     unreadChar(ic);
  440.     if (isDigit(ic)) {
  441.       return (parseNumber('-', lvalp));
  442.     }
  443.   } else {
  444.     if (ic != EOF && (charTab[ic].charClass & SPECIAL_CHAR)) {
  445.       *bp++ = ic;
  446.     } else {
  447.       unreadChar(ic);
  448.     }
  449.   }
  450.  
  451.   *bp = '\0';
  452.  
  453.   if (strcmp(buf, "!") == 0) {
  454.     /* technically, token BANG has no string value, it's just a simple token,
  455.        so we return from here before we set the token's value */
  456.     return (BANG);
  457.   } else if (strcmp(buf, "!!") == 0) {
  458.     unreadChar('!');
  459.     return (BANG);
  460.   }
  461.   
  462.   lvalp->sval = copyStr(buf);
  463.   
  464.   if (strcmp(buf, "|") == 0) {
  465.     return (VERTICAL_BAR);
  466.   } else {
  467.     return (BINOP);
  468.   }
  469. }
  470.  
  471. static int stringLiteral(c, lvalp)
  472. char    c;
  473. YYSTYPE *lvalp;
  474. {
  475.   lvalp->sval = copyStr(scanStringoid(c, "string"));
  476.   return (STRING_LITERAL);
  477. }
  478.  
  479. static Boolean parsePrimitive(c, lvalp)
  480. char    c;
  481. YYSTYPE *lvalp;
  482. {
  483.   Boolean     result;
  484.  
  485.   parseIdent(c, lvalp);
  486.   result = (strcmp(lvalp->sval, "primitive:") == 0);
  487.   free(lvalp->sval);
  488.   return (result);
  489. }
  490.  
  491. static int parseIdent(c, lvalp)
  492. char    c;
  493. YYSTYPE *lvalp;
  494. {
  495.   int        ic, identType;
  496.   
  497.   initIdent(c);
  498.   
  499.   identType = IDENTIFIER;
  500.   
  501.   for (;;) {
  502.     while (((ic = nextChar()) != EOF) && (charTab[ic].charClass & ID_CHAR)) {
  503.       addIdentChar(ic);
  504.     }
  505.     
  506.     if (ic == ':') {        /* we have a keyword if id ends with : */
  507.       addIdentChar(ic);
  508.       ic = nextChar();
  509.       unreadChar(ic);
  510.       if (ic == EOF || (charTab[ic].charClass & ID_CHAR) == 0
  511.       || (charTab[ic].charClass & DIGIT) != 0) {
  512.     if (identType == IDENTIFIER) {
  513.       /* first time through */
  514.       identType = KEYWORD;
  515.     }
  516.     break;
  517.       }
  518.       identType = SYMBOL_KEYWORD;
  519.     } else {
  520.       unreadChar(ic);
  521.       break;
  522.     }
  523.   }
  524.   
  525.   lvalp->sval = copyStr(doneIdent());
  526.   
  527.   return (identType);
  528. }
  529.  
  530. static int parseNumber(c, lvalp)
  531. char    c;
  532. YYSTYPE *lvalp;
  533. {
  534.   int        base, exponent, ic;
  535.   double    num, sign, floatExponent;
  536.   Boolean    mantissaParsed = false, isNegative = false, dotSeen = false;
  537.   double scale;
  538.   
  539.   base = 10;
  540.   exponent = 0;
  541.   scale = 1;
  542.   ic = c;
  543.  
  544.   if (ic != '-') {
  545.     parseDigits(ic, 10, &num);
  546.     ic = nextChar();
  547.     if (ic == 'r') {
  548.       base = num;
  549.       ic = nextChar();
  550.     } else {
  551.       mantissaParsed = true;
  552.     }
  553.   }
  554.  
  555.   /*
  556.    * here we've either
  557.    *  a) parsed base, an 'r' and are sitting on the following character
  558.    *  b) parsed the integer part of the mantissa, and are sitting on the char
  559.    *     following it, or
  560.    *  c) parsed nothing and are sitting on a - sign.
  561.    */
  562.   if (!mantissaParsed) {
  563.     if (ic == '-') {
  564.       isNegative = true;
  565.       ic = nextChar();
  566.     }
  567.     
  568.     parseDigits(ic, base, &num);
  569.     ic = nextChar();
  570.   }
  571.  
  572.   if (ic == '.') {
  573.     ic = nextChar();
  574.     if (!isDigit(ic)) {
  575.       /* OOPS...we gobbled the '.' by mistake...it was a statement boundary
  576.      delimiter.  We have an integer that we need to return, and need to
  577.      push back both the . and the character that we just read. */
  578.       unreadChar(ic);
  579.       ic = '.';
  580.     } else {
  581.       dotSeen = true;
  582.       parseFraction(ic, base, &num, &scale);
  583.       ic = nextChar();
  584.     }
  585.   }
  586.  
  587.   if (ic == 'e') {
  588.     ic = nextChar();
  589.     if (ic == '-') {
  590.       parseDigits(nextChar(), 10, &floatExponent);
  591.       exponent -= floatExponent;
  592.     } else {
  593.       parseDigits(ic, 10, &floatExponent);
  594.       exponent += floatExponent;
  595.     }
  596.   } else {
  597.     unreadChar(ic);
  598.   }
  599.  
  600.   if (scale != 0.0) {
  601.     num *= scale/*pow((double)base, (double)exponent)*/;
  602.   }
  603.  
  604.   if (exponent != 0) {
  605.     num *= pow((double)base, (double)exponent);
  606.   }
  607.  
  608.   if (isNegative) {
  609.     num = -num;
  610.   }
  611.     
  612.   if (dotSeen) {
  613.     lvalp->fval = num;
  614.     return (FLOATING_LITERAL);
  615.   } else {
  616.     lvalp->ival = num;
  617.     if (num < -(1<<30) || num >= (1 << 30)) {
  618.       /* at least warn the guy... */
  619.       errorf("Integer literal %d too large to be represented in Smalltalk",
  620.          num);
  621.       hadError = true;
  622.     }
  623.       
  624.     return (INTEGER_LITERAL);
  625.   }
  626. }
  627.  
  628. static Boolean parseDigits(c, base, numPtr)
  629. char    c;
  630. int    base;
  631. double    *numPtr;
  632. {
  633.   double    result;
  634.   Boolean    oneDigit = false;
  635.  
  636.   for (result = 0.0; isBaseDigit(c, base); c = nextChar()) {
  637.     result *= base;
  638.     oneDigit = true;
  639.     result += digitToInt(c, base);
  640.   }
  641.  
  642.   if (!oneDigit) {
  643.     errorf("Unexpected EOF while scanning number");
  644.     hadError = true;
  645.   }
  646.  
  647.   unreadChar(c);
  648.  
  649.   *numPtr = result;
  650.  
  651.   return (true);
  652. }
  653.  
  654. static Boolean parseFraction(c, base, numPtr, scalePtr)
  655. char    c;
  656. int    base;
  657. double    *numPtr, *scalePtr;
  658. {
  659.   double    scale;
  660.   double    num;
  661.  
  662.   scale = 1.0;
  663.  
  664.   for (num = *numPtr; isBaseDigit(c, base); c = nextChar()) {
  665.     num *= base;
  666.     num += digitToInt(c, base);
  667.     scale /= base;
  668.   }
  669.  
  670.   unreadChar(c);
  671.  
  672.   *numPtr = num;
  673.   *scalePtr = scale;
  674.  
  675.   return (true);
  676. }
  677.  
  678.  
  679. int digitToInt(c, base)
  680. char    c;
  681. int    base;
  682. {
  683.   if (c < '0' || (c > '9' && c < 'A') || c > 'Z') {
  684.     errorf("Illegal digit %c in number", c);
  685.     hadError = true;
  686.     return (0);
  687.   }
  688.  
  689.   if (c >= 'A') {
  690.     c = c - 'A' + 10;
  691.   } else {
  692.     c -= '0';
  693.   }
  694.  
  695.   if (c >= base) {
  696.     errorf("Digit '%c' too large for base %d", c, base);
  697.     hadError = true;
  698.     return (0);
  699.   }
  700.  
  701.   return (c);
  702. }
  703.  
  704. static Boolean isBaseDigit(c, base)
  705. char    c;
  706. int    base;
  707. {
  708.   if (c < '0' || (c > '9' && c < 'A') || c > 'Z') {
  709.     return (false);
  710.   }
  711.   
  712.   if (c >= 'A') {
  713.     c = c - 'A' + 10;
  714.   } else {
  715.     c -= '0';
  716.   }
  717.  
  718.   return (c < base);
  719. }
  720.  
  721.  
  722. static Boolean isDigit(c)
  723. char    c;
  724. {
  725.   return ((charTab[c].charClass & DIGIT) != 0);
  726. }
  727.  
  728. static char *scanStringoid(startChar, typeName)
  729. char    startChar, *typeName;
  730. {
  731.   int        ic;
  732.  
  733.   initStrBuf();
  734.  
  735.   for (;;) {
  736.     ic = nextChar();
  737.     if (ic == EOF) {
  738.       errorf("Unterminated %s, attempting recovery", typeName);
  739.       hadError = true;
  740.       return (curStrBuf());
  741.     }
  742.     if (ic == startChar) {
  743.       /* check for doubled delimiters */
  744.       ic = nextChar();
  745.       if (ic != startChar) {
  746.     unreadChar(ic);
  747.     return (curStrBuf());
  748.       }
  749.     }
  750.     addStrBufChar(ic);
  751.   }
  752.   
  753. }
  754.  
  755. /*
  756.  * Report an error to the user.  ### Will show position in the text of
  757.  * the error at some point.
  758.  */
  759. errorf(str, ERROR_ARGS)
  760. char    *str;
  761. int    ERROR_ARGS;
  762. {
  763.   fflush(stdout);
  764.   lineStamp();
  765.   fprintf(stderr, str, ERROR_ARGS);
  766.   fprintf(stderr, "\n");
  767.   fflush(stderr);
  768. }
  769.  
  770.  
  771. yyerror(s)
  772. char    *s;
  773. {
  774.   lineStamp();
  775.   fprintf(stderr, "%s\n", s);
  776. }
  777.  
  778. static void lineStamp()
  779. {
  780.   if (inStream) {
  781.     if (inStream->fileName) {
  782.       fprintf(stderr, "\"%s\", ", inStream->fileName);
  783.     }
  784.     fprintf(stderr, "line %d: ", inStream->line);
  785.   } else {
  786.     fprintf(stderr, "<unknown position> ");
  787.   }
  788. }
  789.  
  790. StreamType getCurStreamType()
  791. {
  792.   if (inStream) {
  793.     return (inStream->type);
  794.   } else {
  795.     return (unknownStreamType);
  796.   }
  797. }
  798.  
  799. OOP getCurString()
  800. {
  801.   if (inStream && inStream->type == stringStreamType) {
  802.     return (stringNew(inStream->st_str.strBase));
  803.   } else {
  804.     return (nilOOP);
  805.   }
  806. }
  807.  
  808. OOP getCurFileName()
  809. {
  810.   if (inStream && inStream->type == fileStreamType) {
  811.     return (stringNew(inStream->fileName));
  812.   } else {
  813.     return (nilOOP);
  814.   }
  815. }
  816.  
  817. #ifdef USE_READLINE
  818. OOP getCurReadline()
  819. {
  820.   if (inStream && inStream->type == readlineStreamType) {
  821.     return (stringNew(inStream->st_str.strBase));
  822.   } else {
  823.     return (nilOOP);
  824.   }
  825. }
  826. #endif /* USE_READLINE */
  827.  
  828. int getMethodStartPos()
  829. {
  830.   return (methodStartPos);
  831. }
  832.  
  833. void clearMethodStartPos()
  834. {
  835.   methodStartPos = -1;
  836. }
  837.  
  838. int getCurFilePos()
  839. {
  840.   if (inStream && inStream->type == fileStreamType) {
  841.     return (ftell(inStream->st_file));
  842.   } else {
  843.     return (-1);
  844.   }
  845. }
  846.  
  847. static int nextChar()
  848. {
  849.   register int        ic;
  850.  
  851.   if (inStream->pushedBackCount == 1) {
  852.     ic = inStream->pushedBackChar;
  853.     inStream->pushedBackChar = -1;
  854.     inStream->pushedBackCount--;
  855.     return (ic);
  856.   } else {
  857.     if (inStream->pushedBackCount == 0 && inStream->column == 0
  858.     && inStream->prompt) {
  859.       if (emacsProcess) {
  860.     printf("%c", EMACS_PROCESS_MARKER);
  861.       }
  862.       printf("st> ");
  863.     }
  864.     ic = myGetC(inStream);
  865.     if (inStream->pushedBackCount) {
  866.       inStream->pushedBackCount--;
  867.     } else {
  868.       /* byteAddr++; */
  869.       if (ic == '\n') {        /* a new line that was not pushed back */
  870.     inStream->line++;
  871.     inStream->column = 0;
  872.       } else {
  873.     inStream->column++;
  874.       }
  875.     }
  876.     return (ic);
  877.   }
  878. }
  879.  
  880. /*
  881.  *    static void unreadChar(ic)
  882.  *
  883.  * Description
  884.  *
  885.  *    Push character 'ic' back into the input queue.  Allows for two
  886.  *    character pushback currently.  This solves the problem of lexing 3. and
  887.  *    then finding out that what we should have lexed was 3 followed by . as
  888.  *    a statement terminator.
  889.  *
  890.  * Inputs
  891.  *
  892.  *    ic    : character to push back into the input stream.
  893.  *
  894.  */
  895. static void unreadChar(ic)
  896. int    ic;
  897. {
  898.   inStream->pushedBackCount++;
  899.   if (inStream->pushedBackChar < 0) {
  900.     inStream->pushedBackChar = ic;
  901.   } else {
  902.     myUngetC(ic, inStream);
  903.   }
  904. }
  905.  
  906.  
  907.  
  908. /***********************************************************************
  909.  *
  910.  *    Generic "stream" interface.  A stream is an abstraction for input and
  911.  *    output.  It is most like common lisp streams.  Basically, these streams
  912.  *    provide transparent reading from either a Smalltalk string, or a UNIX
  913.  *    file.  They stack, and the previous stream can be restored by doing a
  914.  *    "popStream" which optionally "closes" the current stream and goes back 
  915.  *    to using the previous stream.
  916.  *
  917.  *     The `readline()' interface:
  918.  *
  919.  *         The behavior is like the Smalltalk-String interface.
  920.  *         The end-of-string or a NULL strBase-pointer decides
  921.  *         to read in a new line. The prompt is still shown by
  922.  *         the readline() call.
  923.  *         
  924.  ***********************************************************************/
  925.  
  926. void popStream(closeIt)
  927. Boolean closeIt;
  928. {
  929.   Stream    oldStream;
  930.  
  931.   oldStream = inStream;
  932.   inStream = inStream->prevStream;
  933.  
  934.   if (closeIt && oldStream) {
  935.     myClose(oldStream);
  936.     free(oldStream);
  937.   }
  938. }
  939.  
  940. void pushUNIXFile(file, fileName)
  941. FILE    *file;
  942. char    *fileName;
  943. {
  944.   Stream    newStream;
  945.  
  946.   newStream = pushNewStream(fileStreamType);
  947.  
  948.   newStream->st_file = file;
  949.   newStream->fileName = fileName;
  950.   newStream->prompt = isatty(fileno(file));
  951. }
  952.  
  953. void pushSmalltalkString(stringOOP)
  954. OOP    stringOOP;
  955. {
  956.   Stream    newStream;
  957.  
  958.   newStream = pushNewStream(stringStreamType);
  959.  
  960.   newStream->st_str.strBase = (char *)toCString(stringOOP);
  961.   newStream->st_str.str = newStream->st_str.strBase;
  962.   newStream->fileName = "a Smalltalk string";
  963.   newStream->prompt = false;
  964. }
  965.  
  966. #ifdef USE_READLINE
  967. void pushReadlineString()
  968. {
  969.   Stream    newStream;
  970.  
  971.   newStream = pushNewStream(readlineStreamType);
  972.  
  973.   newStream->st_str.strBase = 0;    /* force readline() but no free() */
  974.   newStream->st_str.str = 0;
  975.   newStream->fileName = "a Readline string";
  976.   newStream->prompt = false;        /* prompt is shown by readline() */
  977. }
  978. #endif /* USE_READLINE */
  979.  
  980. static Stream pushNewStream(type)
  981. StreamType type;
  982. {
  983.   Stream    newStream;
  984.  
  985.   newStream = (Stream)malloc(sizeof(struct StreamStruct));
  986.  
  987.   newStream->pushedBackChar = -1;
  988.   newStream->pushedBackCount = 0;
  989.   newStream->line = 1;
  990.   newStream->column = 0;
  991.   newStream->type = type;
  992.   newStream->prevStream = inStream;
  993.   inStream = newStream;
  994.  
  995.   return (newStream);
  996. }
  997.  
  998.  
  999. static int myGetC(stream)
  1000. Stream    stream;
  1001. {
  1002.   int        ic;
  1003.  
  1004.   if (stream->type == stringStreamType) {
  1005.     ic = *stream->st_str.str++;
  1006.     return ((ic == '\0') ? EOF : ic);
  1007.   } else if (stream->type == fileStreamType) {
  1008.     return (getc(stream->st_file));
  1009. #ifdef USE_READLINE
  1010.   } else if (stream->type == readlineStreamType) {
  1011.     char *r_line;
  1012.     extern char *realloc();
  1013.     int r_len;
  1014.  
  1015.     if (stream->st_str.strBase) {
  1016.       ic = *stream->st_str.str++;
  1017.     } else {
  1018.       ic = '\0';
  1019.     }
  1020.  
  1021.     if (ic == '\0') {
  1022.       if(stream->st_str.strBase) {
  1023.     free(stream->st_str.strBase);
  1024.     stream->st_str.strBase = 0;
  1025.       }
  1026.       r_line = readline("st> ");
  1027.       if (!r_line) {
  1028.     /*
  1029.      * return value of NULL indicates EOF:
  1030.      */
  1031.     return EOF;
  1032.       }
  1033.       if (*r_line) {
  1034.     /*
  1035.      * add only non-empty lines.
  1036.      */
  1037.     add_history(r_line);
  1038.       }
  1039.       /*
  1040.        * tack on the newline, not returned by readline() :
  1041.        */
  1042.       r_len =  strlen(r_line);
  1043.       r_line = realloc(r_line, (unsigned) (r_len + 2));
  1044.       if (!r_line) {
  1045.     errorf("Out of memory, reallocating linebuffer space");
  1046.     stream->st_str.str = stream->st_str.strBase = 0;
  1047.     ic = '\n';            /* return a newline ... */
  1048.       } else {
  1049.     r_line[r_len] = '\n';
  1050.     r_line[r_len+1] = '\0';
  1051.     stream->st_str.str = stream->st_str.strBase = r_line;
  1052.     ic = *stream->st_str.str++;
  1053.       }
  1054.     }
  1055.     return (ic);
  1056. #endif /* USE_READLINE */
  1057.   } else {
  1058.     errorf("Bad stream type passed to myGetC");
  1059.     hadError = true;
  1060.   }
  1061. }
  1062.  
  1063. static void myUngetC(ic, stream)
  1064. int    ic;
  1065. Stream    stream;
  1066. {
  1067.   if (stream->type == stringStreamType) {
  1068.     stream->st_str.str--;
  1069.   } else if (stream->type == fileStreamType) {
  1070.     ungetc(ic, stream->st_file);
  1071. #ifdef USE_READLINE
  1072.   } else if (stream->type == readlineStreamType) {
  1073.     if (stream->st_str.str > stream->st_str.strBase) {
  1074.       stream->st_str.str--;
  1075.     } else {
  1076.       errorf("Cannot unget character to readline stream");
  1077.     }
  1078. #endif /* USE_READLINE */
  1079.   } else {
  1080.     errorf("Bad stream type passed to myUngetC");
  1081.     hadError = true;
  1082.   }
  1083. }
  1084.  
  1085. static void myClose(stream)
  1086. Stream    stream;
  1087. {
  1088.   if (stream->type == stringStreamType) {
  1089.     free(stream->st_str.strBase);
  1090.   } else if (stream->type == fileStreamType) {
  1091.     fclose(stream->st_file);
  1092. #ifdef USE_READLINE
  1093.   } else if (stream->type == readlineStreamType) {
  1094.     if (stream->st_str.strBase) {
  1095.       free(stream->st_str.strBase);
  1096.       stream->st_str.strBase = 0;
  1097.     }
  1098. #endif /* USE_READLINE */
  1099.   } else {
  1100.     errorf("Bad stream type passed to myClose");
  1101.     hadError = true;
  1102.   }
  1103. }
  1104.