/*
* @(#)GTKScanner.java 1.40 03/12/19
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.sun.java.swing.plaf.gtk;
import java.io.*;
import java.util.HashMap;
/**
* @author Shannon Hickey
* @version 1.40 12/19/03
*/
class GTKScanner {
public static final String CHARS_a_2_z = "abcdefghijklmnopqrstuvwxyz";
public static final String CHARS_A_2_Z = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
public static final String CHARS_DIGITS = "0123456789";
public static final int TOKEN_EOF = -1;
public static final int TOKEN_LEFT_PAREN = '(';
public static final int TOKEN_RIGHT_PAREN = ')';
public static final int TOKEN_LEFT_CURLY = '{';
public static final int TOKEN_RIGHT_CURLY = '}';
public static final int TOKEN_LEFT_BRACE = '[';
public static final int TOKEN_RIGHT_BRACE = ']';
public static final int TOKEN_EQUAL_SIGN = '=';
public static final int TOKEN_COMMA = ',';
public static final int TOKEN_NONE = 256;
public static final int TOKEN_ERROR = TOKEN_NONE + 1;
public static final int TOKEN_CHAR = TOKEN_ERROR + 1;
public static final int TOKEN_BINARY = TOKEN_CHAR + 1;
public static final int TOKEN_OCTAL = TOKEN_BINARY + 1;
public static final int TOKEN_INT = TOKEN_OCTAL + 1;
public static final int TOKEN_HEX = TOKEN_INT + 1;
public static final int TOKEN_FLOAT = TOKEN_HEX + 1;
public static final int TOKEN_STRING = TOKEN_FLOAT + 1;
public static final int TOKEN_SYMBOL = TOKEN_STRING + 1;
public static final int TOKEN_IDENTIFIER = TOKEN_SYMBOL + 1;
public static final int TOKEN_IDENTIFIER_NULL = TOKEN_IDENTIFIER + 1;
public static final int TOKEN_LAST = TOKEN_IDENTIFIER_NULL + 1;
public static final int ERR_UNKNOWN = 0;
public static final int ERR_UNEXP_EOF = ERR_UNKNOWN + 1;
public static final int ERR_UNEXP_EOF_IN_STRING = ERR_UNEXP_EOF + 1;
public static final int ERR_UNEXP_EOF_IN_COMMENT = ERR_UNEXP_EOF_IN_STRING + 1;
public static final int ERR_NON_DIGIT_IN_CONST = ERR_UNEXP_EOF_IN_COMMENT + 1;
public static final int ERR_DIGIT_RADIX = ERR_NON_DIGIT_IN_CONST + 1;
public static final int ERR_FLOAT_RADIX = ERR_DIGIT_RADIX + 1;
public static final int ERR_FLOAT_MALFORMED = ERR_FLOAT_RADIX + 1;
String whiteSpaceChars = " \t\r\n";
String identifierFirst = CHARS_a_2_z + CHARS_A_2_Z + "_";
String identifierNth = CHARS_a_2_z + CHARS_A_2_Z + "_-" + CHARS_DIGITS;
String commentSingle = "#\n";
boolean caseSensitive = false;
boolean scanCommentMulti = true;
boolean scanIdentifier = true;
boolean scanIdentifier1Char = false;
boolean scanIdentifierNULL = false;
boolean scanSymbols = true;
boolean scanBinary = false;
boolean scanOctal = true;
boolean scanFloat = true;
boolean scanHex = true;
boolean scanHexDollar = false;
boolean scanStringSq = true;
boolean scanStringDq = true;
boolean numbers2Int = true;
boolean int2Float = false;
boolean identifier2String = false;
boolean char2Token = true;
boolean symbol2Token = false;
private static class ScannerKey {
private int scope;
private String symbol;
public int value = -1;
ScannerKey(int scope, String symbol) {
this.scope = scope;
this.symbol = symbol;
}
public boolean equals(Object o) {
if (o instanceof ScannerKey) {
ScannerKey comp = (ScannerKey)o;
return scope == comp.scope && symbol.equals(comp.symbol);
}
return false;
}
public int hashCode() {
int result = 17;
result = 37 * result + scope;
result = 37 * result + symbol.hashCode();
return result;
}
}
static class TokenValue {
long longVal;
double doubleVal;
char charVal;
String stringVal;
TokenValue() {
clear();
}
void copyFrom(TokenValue other) {
longVal = other.longVal;
doubleVal = other.doubleVal;
charVal = other.charVal;
stringVal = other.stringVal;
}
void clear() {
longVal = 0L;
doubleVal = 0.0D;
charVal = (char)0;
stringVal = null;
}
}
private String inputName;
private HashMap symbolTable = new HashMap();
private Reader reader;
int currToken;
TokenValue currValue = new TokenValue();
int currLine;
int currPosition;
int nextToken;
TokenValue nextValue = new TokenValue();
int nextLine;
int nextPosition;
int currScope = 0;
private static int nextUniqueScope = 1;
private static final int CHAR_EOF = -1;
private static final int CHAR_NONE = -2;
private int peekedChar = CHAR_NONE;
private ScannerKey lookupKey = new ScannerKey(0, null);
public GTKScanner() {
clearScanner();
}
public void clearScanner() {
if (reader != null) {
try {
reader.close();
} catch (IOException ioe) {
}
reader = null;
}
inputName = null;
currToken = TOKEN_NONE;
currValue.clear();
currLine = 1;
currPosition = 0;
nextToken = TOKEN_NONE;
nextValue.clear();
nextLine = 1;
nextPosition = 0;
currScope = 0;
peekedChar = CHAR_NONE;
}
public void scanReader(Reader r, String inputName) {
if (r == null) {
return;
}
if (reader != null) {
clearScanner();
}
reader = r;
this.inputName = inputName;
}
public static int getUniqueScopeID() {
return nextUniqueScope++;
}
public int setScope(int scope) {
int oldScope = currScope;
currScope = scope;
return oldScope;
}
public void addSymbol(String symbol, int value) {
if (symbol == null) {
return;
}
ScannerKey key = lookupSymbol(symbol);
if (key == null) {
key = new ScannerKey(currScope, caseSensitive ? symbol : symbol.toLowerCase());
symbolTable.put(key, key);
}
key.value = value;
}
public boolean containsSymbol(String symbol) {
return lookupSymbol(symbol) != null;
}
private ScannerKey lookupSymbol(String symbol) {
lookupKey.scope = currScope;
lookupKey.symbol = (caseSensitive ? symbol : symbol.toLowerCase());
return (ScannerKey)symbolTable.get(lookupKey);
}
public void clearSymbolTable() {
symbolTable.clear();
}
public int peekNextToken() throws IOException {
if (nextToken == TOKEN_NONE) {
readAToken();
switch(nextToken) {
case TOKEN_SYMBOL:
if (symbol2Token) {
nextToken = (int)nextValue.longVal;
}
break;
case TOKEN_IDENTIFIER:
if (identifier2String) {
nextToken = TOKEN_STRING;
}
break;
case TOKEN_HEX:
case TOKEN_OCTAL:
case TOKEN_BINARY:
if (numbers2Int) {
nextToken = TOKEN_INT;
}
break;
}
if (nextToken == TOKEN_INT && int2Float) {
nextToken = TOKEN_FLOAT;
nextValue.doubleVal = nextValue.longVal;
}
}
return nextToken;
}
public int getToken() throws IOException {
currToken = peekNextToken();
currValue.copyFrom(nextValue);
currLine = nextLine;
currPosition = nextPosition;
if (currToken != TOKEN_EOF) {
nextToken = TOKEN_NONE;
}
return currToken;
}
private int peekNextChar() throws IOException {
if (peekedChar == CHAR_NONE) {
peekedChar = reader.read();
}
return peekedChar;
}
private int getChar() throws IOException {
int ch = peekNextChar();
if (ch != CHAR_EOF) {
peekedChar = CHAR_NONE;
if (ch == '\n') {
nextPosition = 0;
nextLine++;
} else {
nextPosition++;
}
}
return ch;
}
// ----- scanning methods and variables ----- //
private StringBuffer sb;
private TokenValue value = new TokenValue();
private int token;
private int ch;
private boolean skipSpaceAndComments() throws IOException {
while(ch != CHAR_EOF) {
if (whiteSpaceChars.indexOf(ch) != -1) {
// continue
} else if (scanCommentMulti && ch == '/' && peekNextChar() == '*') {
getChar();
while((ch = getChar()) != CHAR_EOF) {
if (ch == '*' && peekNextChar() == '/') {
getChar();
break;
}
}
if (ch == CHAR_EOF) {
return false;
}
} else if (commentSingle.length() == 2 && ch == commentSingle.charAt(0)) {
while((ch = getChar()) != CHAR_EOF) {
if (ch == commentSingle.charAt(1)) {
break;
}
}
if (ch == CHAR_EOF) {
return false;
}
} else {
break;
}
ch = getChar();
}
return true;
}
private void readAToken() throws IOException {
boolean inString = false;
nextValue.clear();
sb = null;
do {
value.clear();
token = TOKEN_NONE;
ch = getChar();
if (!skipSpaceAndComments()) {
token = TOKEN_ERROR;
value.longVal = ERR_UNEXP_EOF_IN_COMMENT;
} else if (scanIdentifier && ch != CHAR_EOF && identifierFirst.indexOf(ch) != -1) {
checkForIdentifier();
handleOrdinaryChar();
} else {
switch(ch) {
case CHAR_EOF:
token = TOKEN_EOF;
break;
case '"':
if (!scanStringDq) {
handleOrdinaryChar();
} else {
token = TOKEN_STRING;
inString = true;
sb = new StringBuffer();
while ((ch = getChar()) != CHAR_EOF) {
if (ch == '"') {
inString = false;
break;
} else {
if (ch == '\\') {
ch = getChar();
switch(ch) {
case CHAR_EOF:
break;
case '\\':
sb.append('\\');
break;
case 'n':
sb.append('\n');
break;
case 'r':
sb.append('\r');
break;
case 't':
sb.append('\t');
break;
case 'f':
sb.append('\f');
break;
case 'b':
sb.append('\b');
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
int i = ch - '0';
int nextCh = peekNextChar();
if (nextCh >= '0' && nextCh <= '7') {
ch = getChar();
i = i * 8 + ch - '0';
nextCh = peekNextChar();
if (nextCh >= '0' && nextCh <= '7') {
ch = getChar();
i = i * 8 + ch - '0';
}
}
sb.append((char)i);
break;
default:
sb.append((char)ch);
break;
}
} else {
sb.append((char)ch);
}
}
}
ch = CHAR_EOF;
}
break;
case '\'':
if (!scanStringSq) {
handleOrdinaryChar();
} else {
token = TOKEN_STRING;
inString = true;
sb = new StringBuffer();
while ((ch = getChar()) != CHAR_EOF) {
if (ch == '\'') {
inString = false;
break;
} else {
sb.append((char)ch);
}
}
ch = CHAR_EOF;
}
break;
case '$':
if (!scanHexDollar) {
handleOrdinaryChar();
} else {
token = TOKEN_HEX;
ch = getChar();
scanNumber(false);
}
break;
case '.':
if (!scanFloat) {
handleOrdinaryChar();
} else {
token = TOKEN_FLOAT;
ch = getChar();
scanNumber(true);
}
break;
case '0':
if (scanOctal) {
token = TOKEN_OCTAL;
} else {
token = TOKEN_INT;
}
ch = peekNextChar();
if (scanHex && (ch == 'x' || ch == 'X')) {
token = TOKEN_HEX;
getChar();
ch = getChar();
if (ch == CHAR_EOF) {
token = TOKEN_ERROR;
value.longVal = ERR_UNEXP_EOF;
break;
}
if (char2int(ch, 16) < 0) {
token = TOKEN_ERROR;
value.longVal = ERR_DIGIT_RADIX;
ch = CHAR_EOF;
break;
}
} else if (scanBinary && (ch == 'b' || ch == 'B')) {
token = TOKEN_BINARY;
getChar();
ch = getChar();
if (ch == CHAR_EOF) {
token = TOKEN_ERROR;
value.longVal = ERR_UNEXP_EOF;
break;
}
if (char2int(ch, 2) < 0) {
token = TOKEN_ERROR;
value.longVal = ERR_NON_DIGIT_IN_CONST;
ch = CHAR_EOF;
break;
}
} else {
ch = '0';
}
// purposely fall through
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
scanNumber(false);
break;
default:
handleOrdinaryChar();
break;
}
}
} while (ch != CHAR_EOF);
if (inString) {
token = TOKEN_ERROR;
value.longVal = ERR_UNEXP_EOF_IN_STRING;
sb = null;
}
if (sb != null) {
value.stringVal = sb.toString();
sb = null;
}
if (token == TOKEN_IDENTIFIER) {
if (scanSymbols) {
int scope = currScope;
ScannerKey key = lookupSymbol(value.stringVal);
if (key != null) {
value.stringVal = null;
token = TOKEN_SYMBOL;
value.longVal = key.value;
}
}
if (token == TOKEN_IDENTIFIER && scanIdentifierNULL & value.stringVal.length() == 4) {
if ("NULL".equals(caseSensitive ? value.stringVal : value.stringVal.toUpperCase())) {
token = TOKEN_IDENTIFIER_NULL;
}
}
}
nextToken = token;
nextValue.copyFrom(value);
}
private void handleOrdinaryChar() throws IOException {
if (ch != CHAR_EOF) {
if (char2Token) {
token = ch;
} else {
token = TOKEN_CHAR;
value.charVal = (char)ch;
}
ch = CHAR_EOF;
}
}
private void checkForIdentifier() throws IOException {
if (ch != CHAR_EOF && identifierNth.indexOf(peekNextChar()) != -1) {
token = TOKEN_IDENTIFIER;
sb = new StringBuffer();
sb.append((char)ch);
do {
ch = getChar();
sb.append((char)ch);
ch = peekNextChar();
} while (ch != CHAR_EOF && identifierNth.indexOf(ch) != -1);
ch = CHAR_EOF;
} else if (scanIdentifier1Char) {
token = TOKEN_IDENTIFIER;
value.stringVal = String.valueOf((char)ch);
ch = CHAR_EOF;
}
}
private static int char2int(int c, int base) {
if (c >= '0' && c <= '9') {
c -= '0';
} else if (c >= 'A' && c <= 'Z') {
c -= 'A' - 10;
} else if (c >= 'a' && c <= 'z') {
c -= 'a' - 10;
} else {
return -1;
}
return c < base ? c : -1;
}
private void scanNumber(boolean seenDot) throws IOException {
boolean inNumber = true;
if (token == TOKEN_NONE) {
token = TOKEN_INT;
}
sb = new StringBuffer(seenDot ? "0." : "");
sb.append((char)ch);
do {
boolean isExponent = (token == TOKEN_FLOAT && (ch == 'e' || ch == 'E'));
ch = peekNextChar();
if (char2int(ch, 36) >= 0
|| (scanFloat && ch == '.')
|| (isExponent && (ch == '+' || ch == '-'))) {
ch = getChar();
switch(ch) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
sb.append((char)ch);
break;
case '.':
if (token != TOKEN_INT && token != TOKEN_OCTAL) {
value.longVal = (token == TOKEN_FLOAT ? ERR_FLOAT_MALFORMED : ERR_FLOAT_RADIX);
token = TOKEN_ERROR;
inNumber = false;
} else {
token = TOKEN_FLOAT;
sb.append((char)ch);
}
break;
case '+':
case '-':
if (token != TOKEN_FLOAT) {
token = TOKEN_ERROR;
value.longVal = ERR_NON_DIGIT_IN_CONST;
inNumber = false;
} else {
sb.append((char)ch);
}
break;
case 'E':
case 'e':
if ((token != TOKEN_HEX && !scanFloat)
|| (token != TOKEN_HEX
&& token != TOKEN_OCTAL
&& token != TOKEN_FLOAT
&& token != TOKEN_INT)) {
token = TOKEN_ERROR;
value.longVal = ERR_NON_DIGIT_IN_CONST;
inNumber = false;
} else {
if (token != TOKEN_HEX) {
token = TOKEN_FLOAT;
}
sb.append((char)ch);
}
break;
default:
if (token != TOKEN_HEX) {
token = TOKEN_ERROR;
value.longVal = ERR_NON_DIGIT_IN_CONST;
} else {
sb.append((char)ch);
}
break;
}
} else {
inNumber = false;
}
} while (inNumber);
try {
switch(token) {
case TOKEN_INT:
value.longVal = Long.parseLong(sb.toString(), 10);
break;
case TOKEN_FLOAT:
value.doubleVal = Double.parseDouble(sb.toString());
break;
case TOKEN_HEX:
value.longVal = Long.parseLong(sb.toString(), 16);
break;
case TOKEN_OCTAL:
value.longVal = Long.parseLong(sb.toString(), 8);
break;
case TOKEN_BINARY:
value.longVal = Long.parseLong(sb.toString(), 2);
break;
}
} catch (NumberFormatException nfe) {
// PENDING(shannonh) - in some cases this could actually be ERR_DIGIT_RADIX
token = TOKEN_ERROR;
value.longVal = ERR_NON_DIGIT_IN_CONST;
}
sb = null;
ch = CHAR_EOF;
}
public void printMessage(String message, boolean isError) {
System.err.print(inputName + ":" + currLine + ": ");
if (isError) {
System.err.print("error: ");
}
System.err.println(message);
}
// PENDING(shannonh) - a good implementation of this method is needed
public void unexpectedToken(int expected, String symbolName, String message, boolean isError) {
String prefix = "lexical error or unexpected token, expected valid token";
if (message != null) {
prefix += " - " + message;
}
printMessage(prefix, isError);
}
}
|