| /* StreamTokenizer.java -- parses streams of characters into tokens | |
| Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.io; | |
| /** | |
| * This class parses streams of characters into tokens. There are a | |
| * million-zillion flags that can be set to control the parsing, as | |
| * described under the various method headings. | |
| * | |
| * @author Warren Levy (warrenl@cygnus.com) | |
| * @date October 25, 1998. | |
| */ | |
| /* Written using "Java Class Libraries", 2nd edition, ISBN 0-201-31002-3 | |
| * "The Java Language Specification", ISBN 0-201-63451-1 | |
| * plus online API docs for JDK 1.2 beta from http://www.javasoft.com. | |
| * Status: Believed complete and correct. | |
| */ | |
| public class StreamTokenizer | |
| { | |
| /** A constant indicating that the end of the stream has been read. */ | |
| public static final int TT_EOF = -1; | |
| /** A constant indicating that the end of the line has been read. */ | |
| public static final int TT_EOL = '\n'; | |
| /** A constant indicating that a number token has been read. */ | |
| public static final int TT_NUMBER = -2; | |
| /** A constant indicating that a word token has been read. */ | |
| public static final int TT_WORD = -3; | |
| /** A constant indicating that no tokens have been read yet. */ | |
| private static final int TT_NONE = -4; | |
| /** | |
| * Contains the type of the token read resulting from a call to nextToken | |
| * The rules are as follows: | |
| * <ul> | |
| * <li>For a token consisting of a single ordinary character, this is the | |
| * value of that character.</li> | |
| * <li>For a quoted string, this is the value of the quote character</li> | |
| * <li>For a word, this is TT_WORD</li> | |
| * <li>For a number, this is TT_NUMBER</li> | |
| * <li>For the end of the line, this is TT_EOL</li> | |
| * <li>For the end of the stream, this is TT_EOF</li> | |
| * </ul> | |
| */ | |
| public int ttype = TT_NONE; | |
| /** The String associated with word and string tokens. */ | |
| public String sval; | |
| /** The numeric value associated with number tokens. */ | |
| public double nval; | |
| /* Indicates whether end-of-line is recognized as a token. */ | |
| private boolean eolSignificant = false; | |
| /* Indicates whether word tokens are automatically made lower case. */ | |
| private boolean lowerCase = false; | |
| /* Indicates whether C++ style comments are recognized and skipped. */ | |
| private boolean slashSlash = false; | |
| /* Indicates whether C style comments are recognized and skipped. */ | |
| private boolean slashStar = false; | |
| /* Attribute tables of each byte from 0x00 to 0xFF. */ | |
| private boolean[] whitespace = new boolean[256]; | |
| private boolean[] alphabetic = new boolean[256]; | |
| private boolean[] numeric = new boolean[256]; | |
| private boolean[] quote = new boolean[256]; | |
| private boolean[] comment = new boolean[256]; | |
| /* The Reader associated with this class. */ | |
| private PushbackReader in; | |
| /* Indicates if a token has been pushed back. */ | |
| private boolean pushedBack = false; | |
| /* Contains the current line number of the reader. */ | |
| private int lineNumber = 1; | |
| /** | |
| * This method reads bytes from an <code>InputStream</code> and tokenizes | |
| * them. For details on how this method operates by default, see | |
| * <code>StreamTokenizer(Reader)</code>. | |
| * | |
| * @param is The <code>InputStream</code> to read from | |
| * | |
| * @deprecated Since JDK 1.1. | |
| */ | |
| public StreamTokenizer(InputStream is) | |
| { | |
| this(new InputStreamReader(is)); | |
| } | |
| /** | |
| * This method initializes a new <code>StreamTokenizer</code> to read | |
| * characters from a <code>Reader</code> and parse them. The char values | |
| * have their hight bits masked so that the value is treated a character | |
| * in the range of 0x0000 to 0x00FF. | |
| * <p> | |
| * This constructor sets up the parsing table to parse the stream in the | |
| * following manner: | |
| * <ul> | |
| * <li>The values 'A' through 'Z', 'a' through 'z' and 0xA0 through 0xFF | |
| * are initialized as alphabetic</li> | |
| * <li>The values 0x00 through 0x20 are initialized as whitespace</li> | |
| * <li>The values '\'' and '"' are initialized as quote characters</li> | |
| * <li>'/' is a comment character</li> | |
| * <li>Numbers will be parsed</li> | |
| * <li>EOL is not treated as significant</li> | |
| * <li>C and C++ (//) comments are not recognized</li> | |
| * </ul> | |
| * | |
| * @param r The <code>Reader</code> to read chars from | |
| */ | |
| public StreamTokenizer(Reader r) | |
| { | |
| in = new PushbackReader(r); | |
| whitespaceChars(0x00, 0x20); | |
| wordChars('A', 'Z'); | |
| wordChars('a', 'z'); | |
| wordChars(0xA0, 0xFF); | |
| commentChar('/'); | |
| quoteChar('\''); | |
| quoteChar('"'); | |
| parseNumbers(); | |
| } | |
| /** | |
| * This method sets the comment attribute on the specified | |
| * character. Other attributes for the character are cleared. | |
| * | |
| * @param ch The character to set the comment attribute for, passed as an int | |
| */ | |
| public void commentChar(int ch) | |
| { | |
| if (ch >= 0 && ch <= 255) | |
| { | |
| comment[ch] = true; | |
| whitespace[ch] = false; | |
| alphabetic[ch] = false; | |
| numeric[ch] = false; | |
| quote[ch] = false; | |
| } | |
| } | |
| /** | |
| * This method sets a flag that indicates whether or not the end of line | |
| * sequence terminates and is a token. The defaults to <code>false</code> | |
| * | |
| * @param flag <code>true</code> if EOF is significant, <code>false</code> | |
| * otherwise | |
| */ | |
| public void eolIsSignificant(boolean flag) | |
| { | |
| eolSignificant = flag; | |
| } | |
| /** | |
| * This method returns the current line number. Note that if the | |
| * <code>pushBack()</code> method is called, it has no effect on the | |
| * line number returned by this method. | |
| * | |
| * @return The current line number | |
| */ | |
| public int lineno() | |
| { | |
| return lineNumber; | |
| } | |
| /** | |
| * This method sets a flag that indicates whether or not alphabetic | |
| * tokens that are returned should be converted to lower case. | |
| * | |
| * @param flag <code>true</code> to convert to lower case, | |
| * <code>false</code> otherwise | |
| */ | |
| public void lowerCaseMode(boolean flag) | |
| { | |
| lowerCase = flag; | |
| } | |
| private boolean isWhitespace(int ch) | |
| { | |
| return (ch >= 0 && ch <= 255 && whitespace[ch]); | |
| } | |
| private boolean isAlphabetic(int ch) | |
| { | |
| return ((ch > 255) || (ch >= 0 && alphabetic[ch])); | |
| } | |
| private boolean isNumeric(int ch) | |
| { | |
| return (ch >= 0 && ch <= 255 && numeric[ch]); | |
| } | |
| private boolean isQuote(int ch) | |
| { | |
| return (ch >= 0 && ch <= 255 && quote[ch]); | |
| } | |
| private boolean isComment(int ch) | |
| { | |
| return (ch >= 0 && ch <= 255 && comment[ch]); | |
| } | |
| /** | |
| * This method reads the next token from the stream. It sets the | |
| * <code>ttype</code> variable to the appropriate token type and | |
| * returns it. It also can set <code>sval</code> or <code>nval</code> | |
| * as described below. The parsing strategy is as follows: | |
| * <ul> | |
| * <li>Skip any whitespace characters.</li> | |
| * <li>If a numeric character is encountered, attempt to parse a numeric | |
| * value. Leading '-' characters indicate a numeric only if followed by | |
| * another non-'-' numeric. The value of the numeric token is terminated | |
| * by either the first non-numeric encountered, or the second occurrence of | |
| * '-' or '.'. The token type returned is TT_NUMBER and <code>nval</code> | |
| * is set to the value parsed.</li> | |
| * <li>If an alphabetic character is parsed, all subsequent characters | |
| * are read until the first non-alphabetic or non-numeric character is | |
| * encountered. The token type returned is TT_WORD and the value parsed | |
| * is stored in <code>sval</code>. If lower case mode is set, the token | |
| * stored in <code>sval</code> is converted to lower case. The end of line | |
| * sequence terminates a word only if EOL signficance has been turned on. | |
| * The start of a comment also terminates a word. Any character with a | |
| * non-alphabetic and non-numeric attribute (such as white space, a quote, | |
| * or a commet) are treated as non-alphabetic and terminate the word.</li> | |
| * <li>If a comment character is parsed, then all remaining characters on | |
| * the current line are skipped and another token is parsed. Any EOL or | |
| * EOF's encountered are not discarded, but rather terminate the comment.</li> | |
| * <li>If a quote character is parsed, then all characters up to the | |
| * second occurrence of the same quote character are parsed into a | |
| * <code>String</code>. This <code>String</code> is stored as | |
| * <code>sval</code>, but is not converted to lower case, even if lower case | |
| * mode is enabled. The token type returned is the value of the quote | |
| * character encountered. Any escape sequences | |
| * (\b (backspace), \t (HTAB), \n (linefeed), \f (form feed), \r | |
| * (carriage return), \" (double quote), \' (single quote), \\ | |
| * (backslash), \XXX (octal esacpe)) are converted to the appropriate | |
| * char values. Invalid esacape sequences are left in untranslated. | |
| * Unicode characters like ('\ u0000') are not recognized. </li> | |
| * <li>If the C++ comment sequence "//" is encountered, and the parser | |
| * is configured to handle that sequence, then the remainder of the line | |
| * is skipped and another token is read exactly as if a character with | |
| * the comment attribute was encountered.</li> | |
| * <li>If the C comment sequence "/*" is encountered, and the parser | |
| * is configured to handle that sequence, then all characters up to and | |
| * including the comment terminator sequence are discarded and another | |
| * token is parsed.</li> | |
| * <li>If all cases above are not met, then the character is an ordinary | |
| * character that is parsed as a token by itself. The char encountered | |
| * is returned as the token type.</li> | |
| * </ul> | |
| * | |
| * @return The token type | |
| * @exception IOException If an I/O error occurs | |
| */ | |
| public int nextToken() throws IOException | |
| { | |
| if (pushedBack) | |
| { | |
| pushedBack = false; | |
| if (ttype != TT_NONE) | |
| return ttype; | |
| } | |
| sval = null; | |
| int ch; | |
| // Skip whitespace. Deal with EOL along the way. | |
| while (isWhitespace(ch = in.read())) | |
| if (ch == '\n' || ch == '\r') | |
| { | |
| lineNumber++; | |
| // Throw away \n if in combination with \r. | |
| if (ch == '\r' && (ch = in.read()) != '\n') | |
| { | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| } | |
| if (eolSignificant) | |
| return (ttype = TT_EOL); | |
| } | |
| if (ch == '/') | |
| if ((ch = in.read()) == '/' && slashSlash) | |
| { | |
| while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) | |
| ; | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| return nextToken(); // Recursive, but not too deep in normal cases | |
| } | |
| else if (ch == '*' && slashStar) | |
| { | |
| while (true) | |
| { | |
| ch = in.read(); | |
| if (ch == '*') | |
| { | |
| if ((ch = in.read()) == '/') | |
| break; | |
| else if (ch != TT_EOF) | |
| in.unread(ch); | |
| } | |
| else if (ch == '\n' || ch == '\r') | |
| { | |
| lineNumber++; | |
| if (ch == '\r' && (ch = in.read()) != '\n') | |
| { | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| } | |
| } | |
| else if (ch == TT_EOF) | |
| { | |
| break; | |
| } | |
| } | |
| return nextToken(); // Recursive, but not too deep in normal cases | |
| } | |
| else | |
| { | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| ch = '/'; | |
| } | |
| if (ch == TT_EOF) | |
| ttype = TT_EOF; | |
| else if (isNumeric(ch)) | |
| { | |
| boolean isNegative = false; | |
| if (ch == '-') | |
| { | |
| // Read ahead to see if this is an ordinary '-' rather than numeric. | |
| ch = in.read(); | |
| if (isNumeric(ch) && ch != '-') | |
| { | |
| isNegative = true; | |
| } | |
| else | |
| { | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| return (ttype = '-'); | |
| } | |
| } | |
| StringBuffer tokbuf = new StringBuffer(); | |
| tokbuf.append((char) ch); | |
| int decCount = 0; | |
| while (isNumeric(ch = in.read()) && ch != '-') | |
| if (ch == '.' && decCount++ > 0) | |
| break; | |
| else | |
| tokbuf.append((char) ch); | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| ttype = TT_NUMBER; | |
| try | |
| { | |
| nval = Double.valueOf(tokbuf.toString()).doubleValue(); | |
| } | |
| catch (NumberFormatException _) | |
| { | |
| nval = 0.0; | |
| } | |
| if (isNegative) | |
| nval = -nval; | |
| } | |
| else if (isAlphabetic(ch)) | |
| { | |
| StringBuffer tokbuf = new StringBuffer(); | |
| tokbuf.append((char) ch); | |
| while (isAlphabetic(ch = in.read()) || isNumeric(ch)) | |
| tokbuf.append((char) ch); | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| ttype = TT_WORD; | |
| sval = tokbuf.toString(); | |
| if (lowerCase) | |
| sval = sval.toLowerCase(); | |
| } | |
| else if (isComment(ch)) | |
| { | |
| while ((ch = in.read()) != '\n' && ch != '\r' && ch != TT_EOF) | |
| ; | |
| if (ch != TT_EOF) | |
| in.unread(ch); | |
| return nextToken(); // Recursive, but not too deep in normal cases. | |
| } | |
| else if (isQuote(ch)) | |
| { | |
| ttype = ch; | |
| StringBuffer tokbuf = new StringBuffer(); | |
| while ((ch = in.read()) != ttype && ch != '\n' && ch != '\r' && | |
| ch != TT_EOF) | |
| { | |
| if (ch == '\\') | |
| switch (ch = in.read()) | |
| { | |
| case 'a': ch = 0x7; | |
| break; | |
| case 'b': ch = '\b'; | |
| break; | |
| case 'f': ch = 0xC; | |
| break; | |
| case 'n': ch = '\n'; | |
| break; | |
| case 'r': ch = '\r'; | |
| break; | |
| case 't': ch = '\t'; | |
| break; | |
| case 'v': ch = 0xB; | |
| break; | |
| case '\n': ch = '\n'; | |
| break; | |
| case '\r': ch = '\r'; | |
| break; | |
| case '\"': | |
| case '\'': | |
| case '\\': | |
| break; | |
| default: | |
| int ch1, nextch; | |
| if ((nextch = ch1 = ch) >= '0' && ch <= '7') | |
| { | |
| ch -= '0'; | |
| if ((nextch = in.read()) >= '0' && nextch <= '7') | |
| { | |
| ch = ch * 8 + nextch - '0'; | |
| if ((nextch = in.read()) >= '0' && nextch <= '7' && | |
| ch1 >= '0' && ch1 <= '3') | |
| { | |
| ch = ch * 8 + nextch - '0'; | |
| nextch = in.read(); | |
| } | |
| } | |
| } | |
| if (nextch != TT_EOF) | |
| in.unread(nextch); | |
| } | |
| tokbuf.append((char) ch); | |
| } | |
| // Throw away matching quote char. | |
| if (ch != ttype && ch != TT_EOF) | |
| in.unread(ch); | |
| sval = tokbuf.toString(); | |
| } | |
| else | |
| { | |
| ttype = ch; | |
| } | |
| return ttype; | |
| } | |
| private void resetChar(int ch) | |
| { | |
| whitespace[ch] = alphabetic[ch] = numeric[ch] = quote[ch] = comment[ch] = | |
| false; | |
| } | |
| /** | |
| * This method makes the specified character an ordinary character. This | |
| * means that none of the attributes (whitespace, alphabetic, numeric, | |
| * quote, or comment) will be set on this character. This character will | |
| * parse as its own token. | |
| * | |
| * @param ch The character to make ordinary, passed as an int | |
| */ | |
| public void ordinaryChar(int ch) | |
| { | |
| if (ch >= 0 && ch <= 255) | |
| resetChar(ch); | |
| } | |
| /** | |
| * This method makes all the characters in the specified range, range | |
| * terminators included, ordinary. This means the none of the attributes | |
| * (whitespace, alphabetic, numeric, quote, or comment) will be set on | |
| * any of the characters in the range. This makes each character in this | |
| * range parse as its own token. | |
| * | |
| * @param low The low end of the range of values to set the whitespace | |
| * attribute for | |
| * @param hi The high end of the range of values to set the whitespace | |
| * attribute for | |
| */ | |
| public void ordinaryChars(int low, int hi) | |
| { | |
| if (low < 0) | |
| low = 0; | |
| if (hi > 255) | |
| hi = 255; | |
| for (int i = low; i <= hi; i++) | |
| resetChar(i); | |
| } | |
| /** | |
| * This method sets the numeric attribute on the characters '0' - '9' and | |
| * the characters '.' and '-'. | |
| * When this method is used, the result of giving other attributes | |
| * (whitespace, quote, or comment) to the numeric characters may | |
| * vary depending on the implementation. For example, if | |
| * parseNumbers() and then whitespaceChars('1', '1') are called, | |
| * this implementation reads "121" as 2, while some other implementation | |
| * will read it as 21. | |
| */ | |
| public void parseNumbers() | |
| { | |
| for (int i = 0; i <= 9; i++) | |
| numeric['0' + i] = true; | |
| numeric['.'] = true; | |
| numeric['-'] = true; | |
| } | |
| /** | |
| * Puts the current token back into the StreamTokenizer so | |
| * <code>nextToken</code> will return the same value on the next call. | |
| * May cause the lineno method to return an incorrect value | |
| * if lineno is called before the next call to nextToken. | |
| */ | |
| public void pushBack() | |
| { | |
| pushedBack = true; | |
| } | |
| /** | |
| * This method sets the quote attribute on the specified character. | |
| * Other attributes for the character are cleared. | |
| * | |
| * @param ch The character to set the quote attribute for, passed as an int. | |
| */ | |
| public void quoteChar(int ch) | |
| { | |
| if (ch >= 0 && ch <= 255) | |
| { | |
| quote[ch] = true; | |
| comment[ch] = false; | |
| whitespace[ch] = false; | |
| alphabetic[ch] = false; | |
| numeric[ch] = false; | |
| } | |
| } | |
| /** | |
| * This method removes all attributes (whitespace, alphabetic, numeric, | |
| * quote, and comment) from all characters. It is equivalent to calling | |
| * <code>ordinaryChars(0x00, 0xFF)</code>. | |
| * | |
| * @see #ordinaryChars(int, int) | |
| */ | |
| public void resetSyntax() | |
| { | |
| ordinaryChars(0x00, 0xFF); | |
| } | |
| /** | |
| * This method sets a flag that indicates whether or not "C++" language style | |
| * comments ("//" comments through EOL ) are handled by the parser. | |
| * If this is <code>true</code> commented out sequences are skipped and | |
| * ignored by the parser. This defaults to <code>false</code>. | |
| * | |
| * @param flag <code>true</code> to recognized and handle "C++" style | |
| * comments, <code>false</code> otherwise | |
| */ | |
| public void slashSlashComments(boolean flag) | |
| { | |
| slashSlash = flag; | |
| } | |
| /** | |
| * This method sets a flag that indicates whether or not "C" language style | |
| * comments (with nesting not allowed) are handled by the parser. | |
| * If this is <code>true</code> commented out sequences are skipped and | |
| * ignored by the parser. This defaults to <code>false</code>. | |
| * | |
| * @param flag <code>true</code> to recognized and handle "C" style comments, | |
| * <code>false</code> otherwise | |
| */ | |
| public void slashStarComments(boolean flag) | |
| { | |
| slashStar = flag; | |
| } | |
| /** | |
| * This method returns the current token value as a <code>String</code> in | |
| * the form "Token[x], line n", where 'n' is the current line numbers and | |
| * 'x' is determined as follows. | |
| * <p> | |
| * <ul> | |
| * <li>If no token has been read, then 'x' is "NOTHING" and 'n' is 0</li> | |
| * <li>If <code>ttype</code> is TT_EOF, then 'x' is "EOF"</li> | |
| * <li>If <code>ttype</code> is TT_EOL, then 'x' is "EOL"</li> | |
| * <li>If <code>ttype</code> is TT_WORD, then 'x' is <code>sval</code></li> | |
| * <li>If <code>ttype</code> is TT_NUMBER, then 'x' is "n=strnval" where | |
| * 'strnval' is <code>String.valueOf(nval)</code>.</li> | |
| * <li>If <code>ttype</code> is a quote character, then 'x' is | |
| * <code>sval</code></li> | |
| * <li>For all other cases, 'x' is <code>ttype</code></li> | |
| * </ul> | |
| */ | |
| public String toString() | |
| { | |
| String tempstr; | |
| if (ttype == TT_EOF) | |
| tempstr = "EOF"; | |
| else if (ttype == TT_EOL) | |
| tempstr = "EOL"; | |
| else if (ttype == TT_WORD) | |
| tempstr = sval; | |
| else if (ttype == TT_NUMBER) | |
| tempstr = "n=" + nval; | |
| else if (ttype == TT_NONE) | |
| tempstr = "NOTHING"; | |
| else // must be an ordinary char. | |
| tempstr = "\'" + (char) ttype + "\'"; | |
| return "Token[" + tempstr + "], line " + lineno(); | |
| } | |
| /** | |
| * This method sets the whitespace attribute for all characters in the | |
| * specified range, range terminators included. | |
| * | |
| * @param low The low end of the range of values to set the whitespace | |
| * attribute for | |
| * @param hi The high end of the range of values to set the whitespace | |
| * attribute for | |
| */ | |
| public void whitespaceChars(int low, int hi) | |
| { | |
| if (low < 0) | |
| low = 0; | |
| if (hi > 255) | |
| hi = 255; | |
| for (int i = low; i <= hi; i++) | |
| { | |
| resetChar(i); | |
| whitespace[i] = true; | |
| } | |
| } | |
| /** | |
| * This method sets the alphabetic attribute for all characters in the | |
| * specified range, range terminators included. | |
| * | |
| * @param low The low end of the range of values to set the alphabetic | |
| * attribute for | |
| * @param hi The high end of the range of values to set the alphabetic | |
| * attribute for | |
| */ | |
| public void wordChars(int low, int hi) | |
| { | |
| if (low < 0) | |
| low = 0; | |
| if (hi > 255) | |
| hi = 255; | |
| for (int i = low; i <= hi; i++) | |
| alphabetic[i] = true; | |
| } | |
| } |