home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / io / StreamTokenizer.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  23.4 KB  |  747 lines

  1. /*
  2.  * @(#)StreamTokenizer.java    1.24 98/03/18
  3.  *
  4.  * Copyright 1995-1997 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.io;
  16.  
  17.  
  18. /**
  19.  * The <code>StreamTokenizer</code> class takes an input stream and 
  20.  * parses it into "tokens", allowing the tokens to be 
  21.  * read one at a time. The parsing process is controlled by a table 
  22.  * and a number of flags that can be set to various states. The 
  23.  * stream tokenizer can recognize identifiers, numbers, quoted 
  24.  * strings, and various comment styles. 
  25.  * <p>
  26.  * Each byte read from the input stream is regarded as a character 
  27.  * in the range <code>'\u0000'</code> through <code>'\u00FF'</code>. 
  28.  * The character value is used to look up five possible attributes of 
  29.  * the character: <i>white space</i>, <i>alphabetic</i>, 
  30.  * <i>numeric</i>, <i>string quote</i>, and <i>comment character</i>. 
  31.  * Each character can have zero or more of these attributes. 
  32.  * <p>
  33.  * In addition, an instance has four flags. These flags indicate: 
  34.  * <ul>
  35.  * <li>Whether line terminators are to be returned as tokens or treated 
  36.  *     as white space that merely separates tokens. 
  37.  * <li>Whether C-style comments are to be recognized and skipped. 
  38.  * <li>Whether C++-style comments are to be recognized and skipped. 
  39.  * <li>Whether the characters of identifiers are converted to lowercase. 
  40.  * </ul>
  41.  * <p>
  42.  * A typical application first constructs an instance of this class, 
  43.  * sets up the syntax tables, and then repeatedly loops calling the 
  44.  * <code>nextToken</code> method in each iteration of the loop until 
  45.  * it returns the value <code>TT_EOF</code>. 
  46.  *
  47.  * @author  James Gosling
  48.  * @version 1.24, 03/18/98
  49.  * @see     java.io.StreamTokenizer#nextToken()
  50.  * @see     java.io.StreamTokenizer#TT_EOF
  51.  * @since   JDK1.0
  52.  */
  53.  
  54. public class StreamTokenizer {
  55.  
  56.     /* Only one of these will be non-null */
  57.     private Reader reader = null;
  58.     private InputStream input = null;
  59.  
  60.     private char buf[] = new char[20];
  61.     private int peekc;
  62.     private boolean pushedBack;
  63.     private boolean forceLower;
  64.     /** The line number of the last token read */
  65.     private int LINENO = 1;
  66.  
  67.     private boolean eolIsSignificantP = false;
  68.     private boolean slashSlashCommentsP = false;
  69.     private boolean slashStarCommentsP = false;
  70.  
  71.     private byte ctype[] = new byte[256];
  72.     private static final byte CT_WHITESPACE = 1;
  73.     private static final byte CT_DIGIT = 2;
  74.     private static final byte CT_ALPHA = 4;
  75.     private static final byte CT_QUOTE = 8;
  76.     private static final byte CT_COMMENT = 16;
  77.  
  78.     /** 
  79.      * After a call to the <code>nextToken</code> method, this field 
  80.      * contains the type of the token just read. For a single character 
  81.      * token, its value is the single character, converted to an integer. 
  82.      * For a quoted string token (see , its value is the quote character. 
  83.      * Otherwise, its value is one of the following: 
  84.      * <ul>
  85.      * <li><code>TT_WORD</code> indicates that the token is a word.
  86.      * <li><code>TT_NUMBER</code> indicates that the token is a number.
  87.      * <li><code>TT_EOL</code> indicates that the end of line has been read. 
  88.      *     The field can only have this value if the 
  89.      *     <code>eolIsSignificant</code> method has been called with the 
  90.      *     argument <code>true</code>. 
  91.      * <li><code>TT_EOF</code> indicates that the end of the input stream 
  92.      *     has been reached. 
  93.      * </ul>
  94.      *
  95.      * @see     java.io.StreamTokenizer#eolIsSignificant(boolean)
  96.      * @see     java.io.StreamTokenizer#nextToken()
  97.      * @see     java.io.StreamTokenizer#quoteChar(int)
  98.      * @see     java.io.StreamTokenizer#TT_EOF
  99.      * @see     java.io.StreamTokenizer#TT_EOL
  100.      * @see     java.io.StreamTokenizer#TT_NUMBER
  101.      * @see     java.io.StreamTokenizer#TT_WORD
  102.      */
  103.     public int ttype = TT_NOTHING;
  104.  
  105.     /** 
  106.      * A constant indicating that the end of the stream has been read. 
  107.      */
  108.     public static final int TT_EOF = -1;
  109.  
  110.     /** 
  111.      * A constant indicating that the end of the line has been read. 
  112.      */
  113.     public static final int TT_EOL = '\n';
  114.  
  115.     /** 
  116.      * A constant indicating that a number token has been read. 
  117.      */
  118.     public static final int TT_NUMBER = -2;
  119.  
  120.     /** 
  121.      * A constant indicating that a word token has been read. 
  122.      */
  123.     public static final int TT_WORD = -3;
  124.  
  125.     /* A constant indicating that no token has been read, used for
  126.      * initializing ttype.  FIXME This could be made public and
  127.      * made available as the part of the API in a future release.
  128.      */
  129.     private static final int TT_NOTHING = -4;
  130.     
  131.     /**
  132.      * If the current token is a word token, this field contains a 
  133.      * string giving the characters of the word token. When the current 
  134.      * token is a quoted string token, this field contains the body of 
  135.      * the string. 
  136.      * <p>
  137.      * The current token is a word when the value of the 
  138.      * <code>ttype</code> field is <code>TT_WORD</code>. The current token is
  139.      * a quoted string token when the value of the <code>ttype</code> field is
  140.      * a quote character.
  141.      *
  142.      * @see     java.io.StreamTokenizer#quoteChar(int)
  143.      * @see     java.io.StreamTokenizer#TT_WORD
  144.      * @see     java.io.StreamTokenizer#ttype
  145.      */
  146.     public String sval;
  147.  
  148.     /**
  149.      * If the current token is a number, this field contains the value 
  150.      * of that number. The current token is a number when the value of 
  151.      * the <code>ttype</code> field is <code>TT_NUMBER</code>. 
  152.      *
  153.      * @see     java.io.StreamTokenizer#TT_NUMBER
  154.      * @see     java.io.StreamTokenizer#ttype
  155.      */
  156.     public double nval;
  157.  
  158.     /** Private constructor that initializes everything except the streams. */
  159.     private StreamTokenizer() {
  160.     wordChars('a', 'z');
  161.     wordChars('A', 'Z');
  162.     wordChars(128 + 32, 255);
  163.     whitespaceChars(0, ' ');
  164.     commentChar('/');
  165.     quoteChar('"');
  166.     quoteChar('\'');
  167.     parseNumbers();
  168.     }
  169.  
  170.     /**
  171.      * Creates a stream tokenizer that parses the specified input 
  172.      * stream. The stream tokenizer is initialized to the following 
  173.      * default state: 
  174.      * <ul>
  175.      * <li>All byte values <code>'A'</code> through <code>'Z'</code>, 
  176.      *     <code>'a'</code> through <code>'z'</code>, and 
  177.      *     <code>'\u00A0'</code> through <code>'\u00FF'</code> are
  178.      *     considered to be alphabetic. 
  179.      * <li>All byte values <code>'\u0000'</code> through 
  180.      *     <code>'\u0020'</code> are considered to be white space. 
  181.      * <li><code>'/'</code> is a comment character. 
  182.      * <li>Single quote <code>'\''</code> and double quote <code>'"'</code> 
  183.      *     are string quote characters. 
  184.      * <li>Numbers are parsed. 
  185.      * <li>Ends of lines are treated as white space, not as separate tokens. 
  186.      * <li>C-style and C++-style comments are not recognized. 
  187.      * </ul>
  188.      *
  189.      * @deprecated As of JDK version 1.1, the preferred way to tokenize an
  190.      * input stream is to convert it into a character stream, for example:
  191.      * <blockquote><pre>
  192.      *   Reader r = new BufferedReader(new InputStreamReader(is));
  193.      *   StreamTokenizer st = new StreamTokenizer(r);
  194.      * </pre></blockquote>
  195.      *
  196.      * @param      is        an input stream.
  197.      * @see        java.io.BufferedReader
  198.      * @see        java.io.InputStreamReader
  199.      * @see        java.io.StreamTokenizer#StreamTokenizer(java.io.Reader)
  200.      */
  201.     public StreamTokenizer(InputStream is) {
  202.     this();
  203.     input = is;
  204.     }
  205.  
  206.     /**
  207.      * Create a tokenizer that parses the given character stream.
  208.      * @since   JDK1.1
  209.      */
  210.     public StreamTokenizer(Reader r) {
  211.     this();
  212.     reader = r;
  213.     }
  214.  
  215.     /** 
  216.      * Resets this tokenizer's syntax table so that all characters are 
  217.      * "ordinary." See the <code>ordinaryChar</code> method 
  218.      * for more information on a character being ordinary. 
  219.      *
  220.      * @see     java.io.StreamTokenizer#ordinaryChar(int)
  221.      */
  222.     public void resetSyntax() {
  223.     for (int i = ctype.length; --i >= 0;)
  224.         ctype[i] = 0;
  225.     }
  226.  
  227.     /** 
  228.      * Specifies that all characters <i>c</i> in the range 
  229.      * <code>low <= <i>c</i> <= high</code> 
  230.      * are word constituents. A word token consists of a word constituent 
  231.      * followed by zero or more word constituents or number constituents. 
  232.      *
  233.      * @param   low   the low end of the range.
  234.      * @param   hi    the high end of the range.
  235.      */
  236.     public void wordChars(int low, int hi) {
  237.     if (low < 0)
  238.         low = 0;
  239.     if (hi >= ctype.length)
  240.         hi = ctype.length - 1;  
  241.     while (low <= hi)
  242.         ctype[low++] |= CT_ALPHA;
  243.     }
  244.  
  245.     /** 
  246.      * Specifies that all characters <i>c</i> in the range 
  247.      * <code>low <= <i>c</i> <= high</code> 
  248.      * are white space characters. White space characters serve only to 
  249.      * separate tokens in the input stream. 
  250.      *
  251.      * @param   low   the low end of the range.
  252.      * @param   hi    the high end of the range.
  253.      */
  254.     public void whitespaceChars(int low, int hi) {
  255.     if (low < 0)
  256.         low = 0;
  257.     if (hi >= ctype.length)
  258.         hi = ctype.length - 1;
  259.     while (low <= hi)
  260.         ctype[low++] = CT_WHITESPACE;
  261.     }
  262.  
  263.     /** 
  264.      * Specifies that all characters <i>c</i> in the range 
  265.      * <code>low <= <i>c</i> <= high</code> 
  266.      * are "ordinary" in this tokenizer. See the 
  267.      * <code>ordinaryChar</code> method for more information on a 
  268.      * character being ordinary. 
  269.      *
  270.      * @param   low   the low end of the range.
  271.      * @param   hi    the high end of the range.
  272.      * @see     java.io.StreamTokenizer#ordinaryChar(int)
  273.      */
  274.     public void ordinaryChars(int low, int hi) {
  275.     if (low < 0)
  276.         low = 0;
  277.     if (hi >= ctype.length)
  278.         hi = ctype.length - 1;
  279.     while (low <= hi)
  280.         ctype[low++] = 0;
  281.     }
  282.  
  283.     /** 
  284.      * Specifies that the character argument is "ordinary" 
  285.      * in this tokenizer. It removes any special significance the 
  286.      * character has as a comment character, word component, string 
  287.      * delimiter, white space, or number character. When such a character 
  288.      * is encountered by the parser, the parser treates it as a
  289.      * single-character token and sets <code>ttype</code> field to the
  290.      * character value. 
  291.      *
  292.      * @param   ch   the character.
  293.      * @see     java.io.StreamTokenizer#ttype
  294.      */
  295.     public void ordinaryChar(int ch) {
  296.         if (ch >= 0 && ch < ctype.length)
  297.           ctype[ch] = 0;
  298.     }
  299.  
  300.     /** 
  301.      * Specified that the character argument starts a single-line 
  302.      * comment. All characters from the comment character to the end of 
  303.      * the line are ignored by this stream tokenizer. 
  304.      *
  305.      * @param   ch   the character.
  306.      */
  307.     public void commentChar(int ch) {
  308.         if (ch >= 0 && ch < ctype.length)
  309.         ctype[ch] = CT_COMMENT;
  310.     }
  311.  
  312.     /** 
  313.      * Specifies that matching pairs of this character delimit string 
  314.      * constants in this tokenizer. 
  315.      * <p>
  316.      * When the <code>nextToken</code> method encounters a string 
  317.      * constant, the <code>ttype</code> field is set to the string 
  318.      * delimiter and the <code>sval</code> field is set to the body of 
  319.      * the string. 
  320.      * <p>
  321.      * If a string quote character is encountered, then a string is 
  322.      * recognized, consisting of all characters after (but not including) 
  323.      * the string quote character, up to (but not including) the next 
  324.      * occurrence of that same string quote character, or a line 
  325.      * terminator, or end of file. The usual escape sequences such as 
  326.      * <code>"\n"</code> and <code>"\t"</code> are recognized and 
  327.      * converted to single characters as the string is parsed. 
  328.      *
  329.      * @param   ch   the character.
  330.      * @see     java.io.StreamTokenizer#nextToken()
  331.      * @see     java.io.StreamTokenizer#sval
  332.      * @see     java.io.StreamTokenizer#ttype
  333.      */
  334.     public void quoteChar(int ch) {
  335.         if (ch >= 0 && ch < ctype.length)
  336.          ctype[ch] = CT_QUOTE;
  337.     }
  338.  
  339.     /** 
  340.      * Specifies that numbers should be parsed by this tokenizer. The 
  341.      * syntax table of this tokenizer is modified so that each of the twelve
  342.      * characters:
  343.      * <blockquote><pre>
  344.      *      0 1 2 3 4 5 6 7 8 9 . -
  345.      * </pre></blockquote>
  346.      * <p>
  347.      * has the "numeric" attribute. 
  348.      * <p>
  349.      * When the parser encounters a word token that has the format of a 
  350.      * double precision floating-point number, it treats the token as a 
  351.      * number rather than a word, by setting the the <code>ttype</code> 
  352.      * field to the value <code>TT_NUMBER</code> and putting the numeric 
  353.      * value of the token into the <code>nval</code> field. 
  354.      *
  355.      * @see     java.io.StreamTokenizer#nval
  356.      * @see     java.io.StreamTokenizer#TT_NUMBER
  357.      * @see     java.io.StreamTokenizer#ttype
  358.      */
  359.     public void parseNumbers() {
  360.     for (int i = '0'; i <= '9'; i++)
  361.         ctype[i] |= CT_DIGIT;
  362.     ctype['.'] |= CT_DIGIT;
  363.     ctype['-'] |= CT_DIGIT;
  364.     }
  365.  
  366.     /**
  367.      * Determines whether or not ends of line are treated as tokens.
  368.      * If the flag argument is true, this tokenizer treats end of lines 
  369.      * as tokens; the <code>nextToken</code> method returns 
  370.      * <code>TT_EOL</code> and also sets the <code>ttype</code> field to 
  371.      * this value when an end of line is read. 
  372.      * <p>
  373.      * A line is a sequence of characters ending with either a 
  374.      * carriage-return character (<code>'\r'</code>) or a newline 
  375.      * character (<code>'\n'</code>). In addition, a carriage-return 
  376.      * character followed immediately by a newline character is treated 
  377.      * as a single end-of-line token. 
  378.      * <p>
  379.      * If the <code>flag</code> is false, end-of-line characters are 
  380.      * treated as white space and serve only to separate tokens. 
  381.      *
  382.      * @param   flag   <code>true</code> indicates that end-of-line characters
  383.      *                 are separate tokens; <code>false</code> indicates that
  384.      *                 end-of-line characters are white space.
  385.      * @see     java.io.StreamTokenizer#nextToken()
  386.      * @see     java.io.StreamTokenizer#ttype
  387.      * @see     java.io.StreamTokenizer#TT_EOL
  388.      */
  389.     public void eolIsSignificant(boolean flag) {
  390.     eolIsSignificantP = flag;
  391.     }
  392.  
  393.     /** 
  394.      * Determines whether or not the tokenizer recognizes C-style comments.
  395.      * If the flag argument is <code>true</code>, this stream tokenizer 
  396.      * recognizes C-style comments. All text between successive 
  397.      * occurrences of <code>/*</code> and <code>*/</code> are discarded. 
  398.      * <p>
  399.      * If the flag argument is <code>false</code>, then C-style comments 
  400.      * are not treated specially. 
  401.      *
  402.      * @param   flag   <code>true</code> indicates to recognize and ignore
  403.      *                 C-style comments.
  404.      */
  405.     public void slashStarComments(boolean flag) {
  406.     slashStarCommentsP = flag;
  407.     }
  408.  
  409.     /** 
  410.      * Determines whether or not the tokenizer recognizes C++-style comments.
  411.      * If the flag argument is <code>true</code>, this stream tokenizer 
  412.      * recognizes C++-style comments. Any occurrence of two consecutive 
  413.      * slash characters (<code>'/'</code>) is treated as the beginning of 
  414.      * a comment that extends to the end of the line. 
  415.      * <p>
  416.      * If the flag argument is <code>false</code>, then C++-style 
  417.      * comments are not treated specially. 
  418.      *
  419.      * @param   flag   <code>true</code> indicates to recognize and ignore
  420.      *                 C++-style comments.
  421.      */
  422.     public void slashSlashComments(boolean flag) {
  423.     slashSlashCommentsP = flag;
  424.     }
  425.  
  426.     /**
  427.      * Determines whether or not word token are automatically lowercased.
  428.      * If the flag argument is <code>true</code>, then the value in the 
  429.      * <code>sval</code> field is lowercased whenever a word token is 
  430.      * returned (the <code>ttype</code> field has the 
  431.      * value <code>TT_WORD</code> by the <code>nextToken</code> method 
  432.      * of this tokenizer. 
  433.      * <p>
  434.      * If the flag argument is <code>false</code>, then the 
  435.      * <code>sval</code> field is not modified. 
  436.      *
  437.      * @param   fl   <code>true</code> indicates that all word tokens should
  438.      *               be lowercased.
  439.      * @see     java.io.StreamTokenizer#nextToken()
  440.      * @see     java.io.StreamTokenizer#ttype
  441.      * @see     java.io.StreamTokenizer#TT_WORD
  442.      */
  443.     public void lowerCaseMode(boolean fl) {
  444.     forceLower = fl;
  445.     }
  446.  
  447.     /** Read the next character */
  448.     private int read() throws IOException {
  449.     if (reader != null)
  450.         return reader.read();
  451.     else if (input != null)
  452.         return input.read();
  453.     else
  454.         throw new IllegalStateException();
  455.     }
  456.  
  457.     /** 
  458.      * Parses the next token from the input stream of this tokenizer. 
  459.      * The type of the next token is returned in the <code>ttype</code> 
  460.      * field. Additional information about the token may be in the 
  461.      * <code>nval</code> field or the <code>sval</code> field of this 
  462.      * tokenizer. 
  463.      * <p>
  464.      * Typical clients of this
  465.      * class first set up the syntax tables and then sit in a loop
  466.      * calling nextToken to parse successive tokens until TT_EOF
  467.      * is returned. 
  468.      *
  469.      * @return     the value of the <code>ttype</code> field.
  470.      * @exception  IOException  if an I/O error occurs.
  471.      * @see        java.io.StreamTokenizer#nval
  472.      * @see        java.io.StreamTokenizer#sval
  473.      * @see        java.io.StreamTokenizer#ttype
  474.      */
  475.     public int nextToken() throws IOException {
  476.     if (pushedBack) {
  477.         pushedBack = false;
  478.         return ttype;
  479.     }
  480.     byte ct[] = ctype;
  481.     int c; 
  482.     sval = null;
  483.  
  484.     if (ttype == TT_NOTHING) {
  485.         c = read();
  486.         if (c >= 0)    // ttype is surely overwritten below to its correct value.
  487.             ttype = c; // for now we just make sure it isn't TT_NOTHING
  488.     } else {
  489.         c = peekc;
  490.     }
  491.     
  492.     if (c < 0)
  493.         return ttype = TT_EOF;
  494.     int ctype = c < 256 ? ct[c] : CT_ALPHA;
  495.     while ((ctype & CT_WHITESPACE) != 0) {
  496.         if (c == '\r') {
  497.         LINENO++;
  498.         c = read();
  499.         if (c == '\n')
  500.             c = read();
  501.         if (eolIsSignificantP) {
  502.             peekc = c;
  503.             return ttype = TT_EOL;
  504.         }
  505.         } else {
  506.         if (c == '\n') {
  507.             LINENO++;
  508.             if (eolIsSignificantP) {
  509.             peekc = read();
  510.             return ttype = TT_EOL;
  511.             }
  512.         }
  513.         c = read();
  514.         }
  515.         if (c < 0)
  516.         return ttype = TT_EOF;
  517.         ctype = c < 256 ? ct[c] : CT_ALPHA;
  518.     }
  519.     if ((ctype & CT_DIGIT) != 0) {
  520.         boolean neg = false;
  521.         if (c == '-') {
  522.         c = read();
  523.         if (c != '.' && (c < '0' || c > '9')) {
  524.             peekc = c;
  525.             return ttype = '-';
  526.         }
  527.         neg = true;
  528.         }
  529.         double v = 0;
  530.         int decexp = 0;
  531.         int seendot = 0;
  532.         while (true) {
  533.         if (c == '.' && seendot == 0)
  534.             seendot = 1;
  535.         else if ('0' <= c && c <= '9') {
  536.             v = v * 10 + (c - '0');
  537.             decexp += seendot;
  538.         } else
  539.             break;
  540.         c = read();
  541.         }
  542.         peekc = c;
  543.         if (decexp != 0) {
  544.         double denom = 10;
  545.         decexp--;
  546.         while (decexp > 0) {
  547.             denom *= 10;
  548.             decexp--;
  549.         }
  550.         /* do one division of a likely-to-be-more-accurate number */
  551.         v = v / denom;
  552.         }
  553.         nval = neg ? -v : v;
  554.         return ttype = TT_NUMBER;
  555.     }
  556.     if ((ctype & CT_ALPHA) != 0) {
  557.         int i = 0;
  558.         do {
  559.         if (i >= buf.length) {
  560.             char nb[] = new char[buf.length * 2];
  561.             System.arraycopy(buf, 0, nb, 0, buf.length);
  562.             buf = nb;
  563.         }
  564.         buf[i++] = (char) c;
  565.         c = read();
  566.         ctype = c < 0 ? CT_WHITESPACE : c < 256 ? ct[c] : CT_ALPHA;
  567.         } while ((ctype & (CT_ALPHA | CT_DIGIT)) != 0);
  568.         peekc = c;
  569.         sval = String.copyValueOf(buf, 0, i);
  570.         if (forceLower)
  571.         sval = sval.toLowerCase();
  572.         return ttype = TT_WORD;
  573.     }
  574.     if ((ctype & CT_COMMENT) != 0) {
  575.         while ((c = read()) != '\n' && c != '\r' && c >= 0);
  576.         peekc = c;
  577.         return nextToken();
  578.     }
  579.     if ((ctype & CT_QUOTE) != 0) {
  580.         ttype = c;
  581.         int i = 0;
  582.         // invariants (because \Octal needs a lookahead):
  583.         //      (i)  c contains char value 
  584.         //      (ii) peekc contains the lookahead
  585.         peekc = read(); 
  586.         while (peekc >= 0 && peekc != ttype && peekc != '\n' && peekc != '\r') {
  587.             if (peekc == '\\') {
  588.                c = read();
  589.             int first = c;   // to allow \377, but not \477
  590.             if (c >= '0' && c <= '7') {
  591.             c = c - '0';
  592.             int c2 = read();
  593.             if ('0' <= c2 && c2 <= '7') {
  594.                 c = (c << 3) + (c2 - '0');
  595.                 c2 = read();
  596.                 if ('0' <= c2 && c2 <= '7' && first <= '3') {
  597.                 c = (c << 3) + (c2 - '0');
  598.                 peekc = read();
  599.                 } else
  600.                 peekc = c2;
  601.             } else
  602.               peekc = c2;
  603.             } else {
  604.                   switch (c) {
  605.             case 'a':
  606.                 c = 0x7;
  607.                 break;
  608.             case 'b':
  609.                 c = '\b';
  610.                 break;
  611.             case 'f':
  612.                 c = 0xC;
  613.                 break;
  614.             case 'n':
  615.                 c = '\n';
  616.                 break;
  617.                 case 'r':
  618.                 c = '\r';
  619.                 break;
  620.             case 't':
  621.                 c = '\t';
  622.                 break;
  623.             case 'v':
  624.                 c = 0xB;
  625.                 break;
  626.             }
  627.             peekc = read();
  628.             }
  629.         } else {
  630.             c = peekc;
  631.             peekc = read();
  632.         }
  633.         
  634.         if (i >= buf.length) {
  635.             char nb[] = new char[buf.length * 2];
  636.             System.arraycopy(buf, 0, nb, 0, buf.length);
  637.             buf = nb;
  638.         }
  639.         buf[i++] = (char) c;
  640.         }
  641.         if (peekc == ttype)  // keep \n or \r intact in peekc
  642.             peekc = read();
  643.         sval = String.copyValueOf(buf, 0, i);
  644.         return ttype;
  645.     }
  646.     if (c == '/' && (slashSlashCommentsP || slashStarCommentsP)) {
  647.         c = read();
  648.         if (c == '*' && slashStarCommentsP) {
  649.         int prevc = 0;
  650.         while ((c = read()) != '/' || prevc != '*') {
  651.             if (c == '\r') {
  652.             LINENO++;
  653.             c = read();
  654.             if (c == '\n') {
  655.                 c = read();
  656.             }
  657.             } else {
  658.                 if (c == '\n') {
  659.                 LINENO++;
  660.                 c = read();
  661.             }
  662.             }
  663.             if (c < 0)
  664.                 return ttype = TT_EOF;
  665.             prevc = c;
  666.         }
  667.         peekc = read();
  668.         return nextToken();
  669.         } else if (c == '/' && slashSlashCommentsP) {
  670.             while ((c = read()) != '\n' && c != '\r' && c >= 0);
  671.             peekc = c;
  672.         return nextToken();
  673.         } else {
  674.         peekc = c;
  675.         return ttype = '/';
  676.         }
  677.     }
  678.     peekc = read();
  679.     return ttype = c;
  680.     }
  681.  
  682.     /**
  683.      * Causes the next call to the <code>nextToken</code> method of this 
  684.      * tokenizer to return the current value in the <code>ttype</code> 
  685.      * field, and not to modify the value in the <code>nval</code> or 
  686.      * <code>sval</code> field. 
  687.      *
  688.      * @see     java.io.StreamTokenizer#nextToken()
  689.      * @see     java.io.StreamTokenizer#nval
  690.      * @see     java.io.StreamTokenizer#sval
  691.      * @see     java.io.StreamTokenizer#ttype
  692.      */
  693.     public void pushBack() {
  694.         if (ttype != TT_NOTHING)   // no-op if nextToken() not called
  695.         pushedBack = true;
  696.     }
  697.  
  698.     /**
  699.      * Return the current line number.
  700.      *
  701.      * @return  the current line number of this stream tokenizer.
  702.      */
  703.     public int lineno() {
  704.     return LINENO;
  705.     }
  706.  
  707.     /**
  708.      * Returns the string representation of the current stream token. 
  709.      *
  710.      * @return  a string representation of the token specified by the
  711.      *          <code>ttype</code>, <code>nval</code>, and <code>sval</code>
  712.      *          fields.
  713.      * @see     java.io.StreamTokenizer#nval
  714.      * @see     java.io.StreamTokenizer#sval
  715.      * @see     java.io.StreamTokenizer#ttype
  716.      */
  717.     public String toString() {
  718.     String ret;
  719.     switch (ttype) {
  720.       case TT_EOF:
  721.         ret = "EOF";
  722.         break;
  723.       case TT_EOL:
  724.         ret = "EOL";
  725.         break;
  726.       case TT_WORD:
  727.         ret = sval;
  728.         break;
  729.       case TT_NUMBER:
  730.         ret = "n=" + nval;
  731.         break;
  732.          case TT_NOTHING:  
  733.         ret = "NOTHING";
  734.         break;
  735.       default:{
  736.         char s[] = new char[3];
  737.         s[0] = s[2] = '\'';
  738.         s[1] = (char) ttype;
  739.         ret = new String(s);
  740.         break;
  741.         }
  742.     }
  743.     return "Token[" + ret + "], line " + LINENO;
  744.     }
  745.  
  746. }
  747.