blob: e1e23e1e3f1796b333c7fc03c31aa6c234c0571c [file] [log] [blame]
/*
* Copyright (c) 1999, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* COMPONENT_NAME: idl.parser
*
* ORIGINS: 27
*
* Licensed Materials - Property of IBM
* 5639-D57 (C) COPYRIGHT International Business Machines Corp. 1997, 1999
* RMI-IIOP v1.0
*
*/
package com.sun.tools.corba.se.idl;
// NOTES:
// -F46082.51<daz> Remove -stateful feature.
// -D56351<daz> Update computation of RepositoryIDs to CORBA 2.3 (see spec.).
// -D59166<daz> Add escaped-id. info. to identifiers.
// -F60858.1<daz> Add support for -corba option, levels 2.2 and 2.3: accept 2.3
// keywords as ids.; accept ids. that match keywords in letter, but not in case.
// -D62023<daz> Add support for -corba option, level 2.4: see keyword checking.
import java.io.EOFException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.Enumeration;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.Vector;
/**
*
**/
class Scanner
{
// <f46082.51> -Remove stateful feature.
//Scanner (IncludeEntry file, String[] keywords, boolean vbose, boolean scanStateful, boolean emitAllIncludes) throws IOException
// <f60858.1>
//Scanner (IncludeEntry file, String[] keywords, boolean vbose, boolean emitAllIncludes) throws IOException
/**
*
**/
Scanner (IncludeEntry file, String[] keywords, boolean vbose,
boolean emitAllIncludes, float cLevel, boolean debug) throws IOException
{
readFile (file);
verbose = vbose;
// <f46082.51>
//stateful = scanStateful;
emitAll = emitAllIncludes;
sortKeywords (keywords);
corbaLevel = cLevel;
this.debug = debug ;
} // ctor
/**
*
**/
void sortKeywords (String[] keywords)
{
for (int i = 0; i < keywords.length; ++i)
if (wildcardAtEitherEnd (keywords[i]))
this.openEndedKeywords.addElement (keywords[i]);
else if (wildcardsInside (keywords[i]))
this.wildcardKeywords.addElement (keywords[i]);
else
this.keywords.addElement (keywords[i]);
} // sortKeywords
/**
*
**/
private boolean wildcardAtEitherEnd (String string)
{
return string.startsWith ("*") ||
string.startsWith ("+") ||
string.startsWith (".") ||
string.endsWith ("*") ||
string.endsWith ("+") ||
string.endsWith (".");
} // wildcardAtEitherEnd
/**
*
**/
private boolean wildcardsInside (String string)
{
return string.indexOf ("*") > 0 ||
string.indexOf ("+") > 0 ||
string.indexOf (".") > 0;
} // wildcardsInside
/**
*
**/
void readFile (IncludeEntry file) throws IOException
{
String filename = file.name ();
filename = filename.substring (1, filename.length () - 1);
readFile (file, filename);
} // readFile
/**
*
**/
void readFile (IncludeEntry file, String filename) throws IOException
{
data.fileEntry = file;
data.filename = filename;
// <f49747.1>
//FileInputStream stream = new FileInputStream (data.filename);
//data.fileBytes = new byte [stream.available ()];
//stream.read (data.fileBytes);
//stream.close (); <ajb>
File idlFile = new File (data.filename);
int len = (int)idlFile.length ();
FileReader fileReader = new FileReader (idlFile);
// <d41679> data.fileBytes = new char [len];
final String EOL = System.getProperty ("line.separator");
data.fileBytes = new char [len + EOL.length ()];
fileReader.read (data.fileBytes, 0, len);
fileReader.close ();
// <d41679>
for (int i = 0; i < EOL.length (); i++)
data.fileBytes[len + i] = EOL.charAt (i);
readChar ();
} // readFile
/**
*
**/
Token getToken () throws IOException
{
//System.out.println ("Scanner.getToken char = |" + data.ch + "| (ASCII " + (int)data.ch + ").");
// The token could be null if the next set of characters
// is not a token: white space, comments, ignored preprocessor
// commands.
Token token = null;
String commentText = new String ("");
while (token == null)
try
{
data.oldIndex = data.fileIndex;
data.oldLine = data.line;
if (data.ch <= ' ') {
skipWhiteSpace ();
continue;
}
// Special case for wchar and wstring literals.
// The values are preceded by an L.
//
// Bug fix 4382578: Can't compile a wchar literal.
//
// REVISIT. This maps wchar/wstring literals to
// our char/string literal types. Eventually, we
// need to write stronger checking to be spec
// compliant in negative cases such as leaving the
// L off of a wide string or putting it on a string.
if (data.ch == 'L') {
// Check to see if the next character is an
// apostrophe.
readChar();
// Note: This doesn't allow for space between
// the L and the apostrophe or quote.
if (data.ch == '\'') {
// It was a wchar literal. Get the value
// and return the token.
token = getCharacterToken(true);
readChar();
continue;
} else
if (data.ch == '"') {
// It was a wstring literal.
//
// getUntil assumes we've already passed the
// first quote.
readChar ();
token = new Token (Token.StringLiteral, getUntil ('"'), true);
readChar ();
continue;
} else {
// It must not have been a wchar literal.
// Push the input back into the buffer, and
// fall to the next if case.
unread(data.ch);
unread('L');
readChar();
}
}
if ((data.ch >= 'a' && data.ch <= 'z') ||
(data.ch >= 'A' && data.ch <= 'Z') ||
// <f46082.40> Escaped identifier; see data member comments.
//(data.ch == '_' && underscoreOK) || <daz>
(data.ch == '_') ||
Character.isLetter (data.ch)) {
token = getString ();
} else
if ((data.ch >= '0' && data.ch <= '9') || data.ch == '.') {
token = getNumber ();
} else {
switch (data.ch)
{
case ';':
token = new Token (Token.Semicolon);
break;
case '{':
token = new Token (Token.LeftBrace);
break;
case '}':
token = new Token (Token.RightBrace);
break;
case ':':
readChar ();
if (data.ch == ':')
token = new Token (Token.DoubleColon);
else
{
unread (data.ch);
token = new Token (Token.Colon);
}
break;
case ',':
token = new Token (Token.Comma);
break;
case '=':
readChar ();
if (data.ch == '=')
token = new Token (Token.DoubleEqual);
else
{
unread (data.ch);
token = new Token (Token.Equal);
}
break;
case '+':
token = new Token (Token.Plus);
break;
case '-':
token = new Token (Token.Minus);
break;
case '(':
token = new Token (Token.LeftParen);
break;
case ')':
token = new Token (Token.RightParen);
break;
case '<':
readChar ();
if (data.ch == '<')
token = new Token (Token.ShiftLeft);
else if (data.ch == '=')
token = new Token (Token.LessEqual);
else
{
unread (data.ch);
token = new Token (Token.LessThan);
}
break;
case '>':
readChar ();
if (data.ch == '>')
token = new Token (Token.ShiftRight);
else if (data.ch == '=')
token = new Token (Token.GreaterEqual);
else
{
unread (data.ch);
token = new Token (Token.GreaterThan);
}
break;
case '[':
token = new Token (Token.LeftBracket);
break;
case ']':
token = new Token (Token.RightBracket);
break;
case '\'':
token = getCharacterToken(false);
break;
case '"':
readChar ();
token = new Token (Token.StringLiteral, getUntil ('"', false, false, false));
break;
case '\\':
readChar ();
// If this is at the end of a line, then it is the
// line continuation character - treat it as white space
if (data.ch == '\n' || data.ch == '\r')
token = null;
else
token = new Token (Token.Backslash);
break;
case '|':
readChar ();
if (data.ch == '|')
token = new Token (Token.DoubleBar);
else
{
unread (data.ch);
token = new Token (Token.Bar);
}
break;
case '^':
token = new Token (Token.Carat);
break;
case '&':
readChar ();
if (data.ch == '&')
token = new Token (Token.DoubleAmpersand);
else
{
unread (data.ch);
token = new Token (Token.Ampersand);
}
break;
case '*':
token = new Token (Token.Star);
break;
case '/':
readChar ();
// <21jul1997daz> Extract comments rather than skipping them.
// Preserve only the comment immediately preceding the next token.
if (data.ch == '/')
//skipLineComment ();
commentText = getLineComment();
else if (data.ch == '*')
//skipBlockComment ();
commentText = getBlockComment();
else
{
unread (data.ch);
token = new Token (Token.Slash);
}
break;
case '%':
token = new Token (Token.Percent);
break;
case '~':
token = new Token (Token.Tilde);
break;
// The period token is recognized in getNumber.
// The period is only valid in a floating ponit number.
//case '.':
// token = new Token (Token.Period);
// break;
case '#':
token = getDirective ();
break;
case '!':
readChar ();
if (data.ch == '=')
token = new Token (Token.NotEqual);
else
{
unread (data.ch);
token = new Token (Token.Exclamation);
}
break;
case '?':
try
{
token = replaceTrigraph ();
break;
}
catch (InvalidCharacter e) {}
default:
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
}
readChar ();
}
}
catch (EOFException e)
{
token = new Token (Token.EOF);
}
// Transfer comment to parser via token. <daz>21jul1997
token.comment = new Comment( commentText );
//System.out.println ("Scanner.getToken returning token.type = " + token.type);
//if (token.type == Token.Identifier || token.type == Token.MacroIdentifier || (token.type >= Token.BooleanLiteral && token.type <= Token.StringLiteral))
// System.out.println ("Scanner.getToken returns token.name = " + token.name);
if (debug)
System.out.println( "Token: " + token ) ;
return token;
} // getToken
/**
*
**/
void scanString (String string)
{
dataStack.push (data);
data = new ScannerData (data);
data.fileIndex = 0;
data.oldIndex = 0;
// <f49747.1> data.fileBytes = string.getBytes (); <ajb>
int strLen = string.length();
data.fileBytes = new char[strLen];
string.getChars (0, strLen, data.fileBytes, 0);
data.macrodata = true;
try {readChar ();} catch (IOException e) {}
} // scanString
/**
*
**/
void scanIncludedFile (IncludeEntry file, String filename, boolean includeIsImport) throws IOException
{
dataStack.push (data);
data = new ScannerData ();
data.indent = ((ScannerData)dataStack.peek ()).indent + ' ';
data.includeIsImport = includeIsImport;
try
{
readFile (file, filename);
if (!emitAll && includeIsImport)
SymtabEntry.enteringInclude ();
// <d56351> As of CORBA 2.3, include files define new scope for Repository
// ID prefixes. The previous Rep. ID is just below the top of the stack and
// must be restored when the contents of this include file are parsed (see readCh()).
Parser.enteringInclude ();
if (verbose)
System.out.println (data.indent + Util.getMessage ("Compile.parsing", filename));
}
catch (IOException e)
{
data = (ScannerData)dataStack.pop ();
throw e;
}
} // scanIncludedFile
/**
*
**/
private void unread (char ch)
{
if (ch == '\n' && !data.macrodata) --data.line;
--data.fileIndex;
} // unread
/**
*
**/
void readChar () throws IOException
{
if (data.fileIndex >= data.fileBytes.length)
if (dataStack.empty ())
throw new EOFException ();
else
{
// <d56351> Indicate end-of-scope for include file to parser.
//Parser.exitingInclude ();
// IBM.11666 - begin
//if (!emitAll && data.includeIsImport && !data.macrodata)
//{
//SymtabEntry.exitingInclude ();
//Parser.exitingInclude (); // <d59469>
//}
if (!data.macrodata)
{
if (!emitAll && data.includeIsImport)
SymtabEntry.exitingInclude();
Parser.exitingInclude();
} // IBM.11666 - end
if (verbose && !data.macrodata)
System.out.println (data.indent + Util.getMessage ("Compile.parseDone", data.filename));
data = (ScannerData)dataStack.pop ();
}
else
{
data.ch = (char)(data.fileBytes[data.fileIndex++] & 0x00ff);
if (data.ch == '\n' && !data.macrodata) ++data.line;
}
} // readChar
/**
* Starting at a quote, reads a string with possible
* unicode or octal values until an end quote. Doesn't
* handle line feeds or comments.
*/
private String getWString() throws IOException
{
readChar();
StringBuffer result = new StringBuffer();
while (data.ch != '"') {
if (data.ch == '\\') {
// Could be a \ooo octal or
// unicode hex
readChar();
if (data.ch == 'u') {
// Unicode hex
int num = getNDigitHexNumber(4);
System.out.println("Got num: " + num);
System.out.println("Which is: " + (int)(char)num);
result.append((char)num);
// result.append((char)getNDigitHexNumber(4));
// getNDigitHexNumber reads the next
// character, so loop without reading another
continue;
} else
if (data.ch >= '0' && data.ch <= '7') {
// Octal
result.append((char)get3DigitOctalNumber());
// get3DigitOctalNumber reads the next
// character, so loop without reading another
continue;
} else {
// Wasn't either, so just append the
// slash and current character.
result.append('\\');
result.append(data.ch);
}
} else {
// Just append the character
result.append(data.ch);
}
// Advance to the next character
readChar();
}
return result.toString();
}
/**
*
**/
private Token getCharacterToken(boolean isWide) throws IOException
{
// The token name returned contains a string with two elements:
// first the character appears, then the representation of the
// character. These are typically the same, but they CAN be
// different, for example "O\117"
Token token = null;
readChar ();
if ( data.ch == '\\' )
{
readChar ();
if ((data.ch == 'x') || (data.ch == 'u'))
{
char charType = data.ch;
int hexNum = getNDigitHexNumber ((charType == 'x') ? 2 : 4);
return new Token (Token.CharacterLiteral,
((char)hexNum) + "\\" + charType + Integer.toString (hexNum, 16), isWide );
}
if ((data.ch >= '0') && (data.ch <= '7'))
{
int octNum = get3DigitOctalNumber ();
return new Token (Token.CharacterLiteral,
((char)octNum) + "\\" + Integer.toString (octNum, 8), isWide );
}
return singleCharEscapeSequence (isWide);
}
token = new Token (Token.CharacterLiteral, "" + data.ch + data.ch, isWide );
readChar ();
return token;
} // getCharacterToken
/**
*
**/
private Token singleCharEscapeSequence (boolean isWide) throws IOException
{
Token token;
if (data.ch == 'n')
// newline
token = new Token (Token.CharacterLiteral, "\n\\n", isWide);
else if (data.ch == 't')
// horizontal tab
token = new Token (Token.CharacterLiteral, "\t\\t", isWide);
else if (data.ch == 'v')
// vertical tab
token = new Token (Token.CharacterLiteral, "\013\\v", isWide);
else if (data.ch == 'b')
// backspace
token = new Token (Token.CharacterLiteral, "\b\\b", isWide);
else if (data.ch == 'r')
// carriage return
token = new Token (Token.CharacterLiteral, "\r\\r", isWide);
else if (data.ch == 'f')
// form feed
token = new Token (Token.CharacterLiteral, "\f\\f", isWide);
else if (data.ch == 'a')
// alert
token = new Token (Token.CharacterLiteral, "\007\\a", isWide);
else if (data.ch == '\\')
// backslash
token = new Token (Token.CharacterLiteral, "\\\\\\", isWide);
else if (data.ch == '?')
// question mark
token = new Token (Token.CharacterLiteral, "?\\?", isWide);
else if (data.ch == '\'')
// single quote
token = new Token (Token.CharacterLiteral, "'\\'", isWide);
else if (data.ch == '"')
// double quote
token = new Token (Token.CharacterLiteral, "\"\\\"", isWide);
else
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
readChar ();
return token;
} // singleCharEscapeSequence
private Token getString () throws IOException
{
StringBuffer sbuf = new StringBuffer() ;
boolean escaped = false; // <d59166>
boolean[] collidesWithKeyword = { false } ; // <d62023>
// <f46082.40> An escaped id. begins with '_', which is followed by a normal
// identifier. Disallow prefixes of '_' having length > 1.
if (data.ch == '_') {
sbuf.append( data.ch ) ;
readChar ();
if (escaped = escapedOK)
if (data.ch == '_')
throw new InvalidCharacter (data.filename, currentLine (),
currentLineNumber (), currentLinePosition (), data.ch);
}
// Build up the string of valid characters until a non-string
// character is encountered.
while (Character.isLetterOrDigit( data.ch ) || (data.ch == '_')) {
sbuf.append( data.ch ) ;
readChar() ;
}
String string = sbuf.toString() ;
// <f46082.40> Escaped identifiers - If identifier has '_' prefix, ignore
// keyword check and strip '_'; otherwise, perform keyword check.
if (!escaped) { // Escaped id ==> ignore keyword check
Token result = Token.makeKeywordToken( string, corbaLevel, escapedOK,
collidesWithKeyword ) ;
if (result != null)
return result ;
}
// At this point the string is an identifier. If it is a
// string which is also a Java keyword, prepend an underscore
// so that it doesn't generate a compiler error.
string = getIdentifier (string);
// If a left paren immediately follows, this could be a
// macro definition, return a MacroIdentifier
if (data.ch == '(') {
readChar ();
return new Token (Token.MacroIdentifier, string, escaped,
collidesWithKeyword[0], false);
} else
return new Token (Token.Identifier, string, escaped,
collidesWithKeyword[0], false);
}
// Wildcard values
static final int Star = 0, Plus = 1, Dot = 2, None = 3;
/**
*
**/
private boolean matchesClosedWildKeyword (String string)
{
boolean found = true;
String tmpString = string;
Enumeration e = wildcardKeywords.elements ();
while (e.hasMoreElements ())
{
int wildcard = None;
StringTokenizer tokens = new StringTokenizer ((String)e.nextElement (), "*+.", true);
if (tokens.hasMoreTokens ())
{
String token = tokens.nextToken ();
if (tmpString.startsWith (token))
{
tmpString = tmpString.substring (token.length ());
while (tokens.hasMoreTokens () && found)
{
token = tokens.nextToken ();
if (token.equals ("*"))
wildcard = Star;
else if (token.equals ("+"))
wildcard = Plus;
else if (token.equals ("."))
wildcard = Dot;
else if (wildcard == Star)
{
int index = tmpString.indexOf (token);
if (index >= 0)
tmpString = tmpString.substring (index + token.length ());
else
found = false;
}
else if (wildcard == Plus)
{
int index = tmpString.indexOf (token);
if (index > 0)
tmpString = tmpString.substring (index + token.length ());
else
found = false;
}
else if (wildcard == Dot)
{
int index = tmpString.indexOf (token);
if (index == 1)
tmpString = tmpString.substring (1 + token.length ());
else
found = false;
}
}
if (found && tmpString.equals (""))
break;
}
}
}
return found && tmpString.equals ("");
} // matchesClosedWildKeyword
/**
*
**/
private String matchesOpenWildcard (String string)
{
Enumeration e = openEndedKeywords.elements ();
String prepend = "";
while (e.hasMoreElements ())
{
int wildcard = None;
boolean found = true;
String tmpString = string;
StringTokenizer tokens = new StringTokenizer ((String)e.nextElement (), "*+.", true);
while (tokens.hasMoreTokens () && found)
{
String token = tokens.nextToken ();
if (token.equals ("*"))
wildcard = Star;
else if (token.equals ("+"))
wildcard = Plus;
else if (token.equals ("."))
wildcard = Dot;
else if (wildcard == Star)
{
wildcard = None;
int index = tmpString.lastIndexOf (token);
if (index >= 0)
tmpString = blankOutMatch (tmpString, index, token.length ());
else
found = false;
}
else if (wildcard == Plus)
{
wildcard = None;
int index = tmpString.lastIndexOf (token);
if (index > 0)
tmpString = blankOutMatch (tmpString, index, token.length ());
else
found = false;
}
else if (wildcard == Dot)
{
wildcard = None;
int index = tmpString.lastIndexOf (token);
if (index == 1)
tmpString = blankOutMatch (tmpString, 1, token.length ());
else
found = false;
}
else if (wildcard == None)
if (tmpString.startsWith (token))
tmpString = blankOutMatch (tmpString, 0, token.length ());
else
found = false;
}
// Make sure that, if the last character of the keyword is a
// wildcard, that the string matches what the wildcard
// requires.
if (found)
{
if (wildcard == Star)
;
else if (wildcard == Plus && tmpString.lastIndexOf (' ') != tmpString.length () - 1)
;
else if (wildcard == Dot && tmpString.lastIndexOf (' ') == tmpString.length () - 2)
;
else if (wildcard == None && tmpString.lastIndexOf (' ') == tmpString.length () - 1)
;
else
found = false;
}
// If found, then prepend an underscore. But also try matching
// again after leading and trailing blanks are removed from
// tmpString. This isn't quite right, but it solves a problem
// which surfaced in the Java mapping. For example:
// openEndedKeywords = {"+Helper", "+Holder", "+Package"};
// string = fooHelperPackage.
// Given the mechanics of the Java mapping, _fooHelperPackage
// COULD have a conflict, so for each occurance of a keyword,
// an underscore is added, so this would cause two underscores:
// __fooHelperPackage. To accomplish this, the first time thru
// tmpString is "fooHelper " at this point, strip off the
// trailing blanks and try matching "fooHelper". This also
// matches, so two underscores are prepended.
if (found)
{
prepend = prepend + "_" + matchesOpenWildcard (tmpString.trim ());
break;
}
}
return prepend;
} // matchesOpenWildcard
/**
*
**/
private String blankOutMatch (String string, int start, int length)
{
char[] blanks = new char [length];
for (int i = 0; i < length; ++i)
blanks[i] = ' ';
return string.substring (0, start) + new String (blanks) + string.substring (start + length);
} // blankOutMatch
/**
*
**/
private String getIdentifier (String string)
{
if (keywords.contains (string))
// string matches a non-wildcard keyword
string = '_' + string;
else
{
// Check to see if string matches any wildcard keywords that
// aren't open ended (don't have a wildcard as the first or
// last character.
String prepend = "";
if (matchesClosedWildKeyword (string))
prepend = "_";
else
// string did not match any closed wildcard keywords (that
// is, keywords with wildcards anywhere but at the beginning
// or end of the word).
// Now check for * + or . at the beginning or end.
// These require special handling because they could match
// more than one keyword. prepend an underscore for each
// matched keyword.
prepend = matchesOpenWildcard (string);
string = prepend + string;
}
return string;
} // getIdentifier
/**
*
**/
private Token getDirective () throws IOException
{
readChar ();
String string = new String ();
while ((data.ch >= 'a' && data.ch <= 'z') || (data.ch >= 'A' && data.ch <= 'Z'))
{
string = string + data.ch;
readChar ();
}
unread (data.ch);
for (int i = 0; i < Token.Directives.length; ++i)
if (string.equals (Token.Directives[i]))
return new Token (Token.FirstDirective + i);
// If it got this far, it is an unknown preprocessor directive.
return new Token (Token.Unknown, string);
} // getDirective
/**
*
**/
private Token getNumber () throws IOException
{
if (data.ch == '.')
return getFractionNoInteger ();
else if (data.ch == '0')
return isItHex ();
else // the only other possibliities are 1..9
return getInteger ();
} // getNumber
/**
*
**/
private Token getFractionNoInteger () throws IOException
{
readChar ();
if (data.ch >= '0' && data.ch <= '9')
return getFraction (".");
else
return new Token (Token.Period);
} // getFractionNoInteger
/**
*
**/
private Token getFraction (String string) throws IOException
{
while (data.ch >= '0' && data.ch <= '9')
{
string = string + data.ch;
readChar ();
}
if (data.ch == 'e' || data.ch == 'E')
return getExponent (string + 'E');
else
return new Token (Token.FloatingPointLiteral, string);
} // getFraction
/**
*
**/
private Token getExponent (String string) throws IOException
{
readChar ();
if (data.ch == '+' || data.ch == '-')
{
string = string + data.ch;
readChar ();
}
else if (data.ch < '0' || data.ch > '9')
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
while (data.ch >= '0' && data.ch <= '9')
{
string = string + data.ch;
readChar ();
}
return new Token (Token.FloatingPointLiteral, string);
} // getExponent
/**
*
**/
private Token isItHex () throws IOException
{
readChar ();
if (data.ch == '.')
{
readChar ();
return getFraction ("0.");
}
else if (data.ch == 'x' || data.ch == 'X')
return getHexNumber ("0x");
else if (data.ch == '8' || data.ch == '9')
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
else if (data.ch >= '0' && data.ch <= '7')
return getOctalNumber ();
else if (data.ch == 'e' || data.ch == 'E')
return getExponent ("0E");
else
return new Token (Token.IntegerLiteral, "0");
} // isItHex
/**
*
**/
private Token getOctalNumber () throws IOException
{
String string = "0" + data.ch;
readChar ();
while ((data.ch >= '0' && data.ch <= '9'))
{
if (data.ch == '8' || data.ch == '9')
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
string = string + data.ch;
readChar ();
}
return new Token (Token.IntegerLiteral, string);
} // getOctalNumber
/**
*
**/
private Token getHexNumber (String string) throws IOException
{
readChar ();
if ((data.ch < '0' || data.ch > '9') && (data.ch < 'a' || data.ch > 'f') && (data.ch < 'A' || data.ch > 'F'))
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
else
while ((data.ch >= '0' && data.ch <= '9') || (data.ch >= 'a' && data.ch <= 'f') || (data.ch >= 'A' && data.ch <= 'F'))
{
string = string + data.ch;
readChar ();
}
return new Token (Token.IntegerLiteral, string);
} // getHexNumber
/**
*
**/
private int getNDigitHexNumber (int n) throws IOException
{
readChar ();
if (!isHexChar (data.ch))
throw new InvalidCharacter (data.filename, currentLine (),
currentLineNumber (), currentLinePosition (), data.ch);
String string = "" + data.ch;
readChar ();
for (int i = 2; i <= n; i++)
{
if (!isHexChar( data.ch))
break;
string += data.ch;
readChar ();
}
try
{
return Integer.parseInt (string, 16);
}
catch (NumberFormatException e)
{
}
return 0;
} // getNDigitHexNumber
/**
*
**/
private boolean isHexChar ( char hex )
{
return ((data.ch >= '0') && (data.ch <= '9')) ||
((data.ch >= 'a') && (data.ch <= 'f')) ||
((data.ch >= 'A') && (data.ch <= 'F'));
}
/**
*
**/
private int get3DigitOctalNumber () throws IOException
{
char firstDigit = data.ch;
String string = "" + data.ch;
readChar ();
if (data.ch >= '0' && data.ch <= '7')
{
string = string + data.ch;
readChar ();
if (data.ch >= '0' && data.ch <= '7')
{
string = string + data.ch;
if (firstDigit > '3')
// This is a 3-digit number bigger than 377
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), firstDigit);
readChar ();
}
}
int ret = 0;
try
{
ret = Integer.parseInt (string, 8);
}
catch (NumberFormatException e)
{
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), string.charAt (0));
}
return ret;
} // get3DigitOctalNumber
/**
*
**/
private Token getInteger () throws IOException
{
String string = "" + data.ch;
readChar ();
if (data.ch == '.')
{
readChar ();
return getFraction (string + '.');
}
else if (data.ch == 'e' || data.ch == 'E')
return getExponent (string + 'E');
else if (data.ch >= '0' && data.ch <= '9')
while (data.ch >= '0' && data.ch <= '9')
{
string = string + data.ch;
readChar ();
if (data.ch == '.')
{
readChar ();
return getFraction (string + '.');
}
}
return new Token (Token.IntegerLiteral, string);
} // getInteger
/**
*
**/
private Token replaceTrigraph () throws IOException
{
readChar ();
if (data.ch == '?')
{
readChar ();
if (data.ch == '=')
data.ch = '#';
else if (data.ch == '/')
data.ch = '\\';
else if (data.ch == '\'')
data.ch = '^';
else if (data.ch == '(')
data.ch = '[';
else if (data.ch == ')')
data.ch = ']';
else if (data.ch == '!')
data.ch = '|';
else if (data.ch == '<')
data.ch = '{';
else if (data.ch == '>')
data.ch = '}';
else if (data.ch == '-')
data.ch = '~';
else
{
unread (data.ch);
unread ('?');
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
}
return getToken ();
}
else
{
unread ('?');
throw new InvalidCharacter (data.filename, currentLine (), currentLineNumber (), currentLinePosition (), data.ch);
}
} // replaceTrigraph
/**
*
**/
void skipWhiteSpace () throws IOException
{
while (data.ch <= ' ')
readChar ();
} // skipWhiteSpace
/**
*
**/
private void skipBlockComment () throws IOException
{
try
{
boolean done = false;
readChar ();
while (!done)
{
while (data.ch != '*')
readChar ();
readChar ();
if (data.ch == '/')
done = true;
}
}
catch (EOFException e)
{
ParseException.unclosedComment (data.filename);
throw e;
}
} // skipBlockComment
/**
*
**/
void skipLineComment () throws IOException
{
while (data.ch != '\n')
readChar ();
} // skipLineComment
// The following two routines added to extract comments rather
// than ignore them.
/**
* Extract a line comment from the input buffer.
**/
private String getLineComment () throws IOException
{
StringBuffer sb = new StringBuffer( "/" );
while (data.ch != '\n')
{
if (data.ch != '\r')
sb.append (data.ch);
readChar ();
}
return sb.toString();
} // getLineComment
/**
* Extract a block comment from the input buffer.
**/
private String getBlockComment () throws IOException
{
StringBuffer sb = new StringBuffer ("/*");
try
{
boolean done = false;
readChar ();
sb.append (data.ch);
while (!done)
{
while (data.ch != '*')
{
readChar ();
sb.append (data.ch);
}
readChar ();
sb.append (data.ch);
if (data.ch == '/')
done = true;
}
}
catch (EOFException e)
{
ParseException.unclosedComment (data.filename);
throw e;
}
return sb.toString ();
} // getBlockComment
/**
*
**/
Token skipUntil (char c) throws IOException
{
while (data.ch != c)
{
if (data.ch == '/')
{
readChar ();
if (data.ch == '/')
{
skipLineComment ();
// If this is skipping until the newline, skipLineComment
// reads past the newline, so it won't be seen by the
// while loop conditional check.
if (c == '\n') break;
}
else if (data.ch == '*')
skipBlockComment ();
}
else
readChar ();
}
return getToken ();
} // skipUntil
// getUntil is used for macro definitions and to get quoted
// strings, so characters within "("...")" and '"'...'"' are
// ignored. Ie getUntil ',' on (,,,,),X will return (,,,,)
String getUntil (char c) throws IOException
{
return getUntil (c, true, true, true);
}
String getUntil (char c, boolean allowQuote, boolean allowCharLit, boolean allowComment) throws IOException
{
String string = "";
while (data.ch != c)
string = appendToString (string, allowQuote, allowCharLit, allowComment);
return string;
} // getUntil
/**
*
**/
String getUntil (char c1, char c2) throws IOException
{
String string = "";
while (data.ch != c1 && data.ch != c2)
string = appendToString (string, false, false, false);
return string;
} // getUntil
/**
*
**/
private String appendToString (String string, boolean allowQuote, boolean allowCharLit, boolean allowComment) throws IOException
{
// Ignore any comments if they are allowed
if (allowComment && data.ch == '/')
{
readChar ();
if (data.ch == '/')
skipLineComment ();
else if (data.ch == '*')
skipBlockComment ();
else
string = string + '/';
}
// Handle line continuation character
else if (data.ch == '\\')
{
readChar ();
if (data.ch == '\n')
readChar ();
else if (data.ch == '\r')
{
readChar ();
if (data.ch == '\n')
readChar ();
}
else
{
string = string + '\\' + data.ch;
readChar ();
}
}
// characters within "("...")" and '"'...'"' are ignored.
// Ie getUntil ',' on (,,,,),X will return (,,,)
else
{
if (allowCharLit && data.ch == '"')
{
readChar ();
string = string + '"';
while (data.ch != '"')
string = appendToString (string, true, false, allowComment);
}
else if (allowQuote && allowCharLit && data.ch == '(')
{
readChar ();
string = string + '(';
while (data.ch != ')')
string = appendToString (string, false, false, allowComment);
}
else if (allowQuote && data.ch == '\'')
{
readChar ();
string = string + "'";
while (data.ch != '\'')
string = appendToString (string, false, true, allowComment);
}
string = string + data.ch;
readChar ();
}
return string;
} // appendToString
/**
*
**/
String getStringToEOL () throws IOException
{
String string = new String ();
while (data.ch != '\n')
{
if (data.ch == '\\')
{
readChar ();
if (data.ch == '\n')
readChar ();
else if (data.ch == '\r')
{
readChar ();
if (data.ch == '\n')
readChar ();
}
else
{
string = string + data.ch;
readChar ();
}
}
else
{
string = string + data.ch;
readChar ();
}
}
return string;
} // getStringToEOL
/**
*
**/
String filename ()
{
return data.filename;
} // filename
/**
*
**/
IncludeEntry fileEntry ()
{
return data.fileEntry;
} // fileEntry
/**
*
**/
int currentLineNumber ()
{
return data.line;
} // currentLineNumber
/**
*
**/
int lastTokenLineNumber ()
{
return data.oldLine;
} // lastTokenLineNumber
private int BOL; // Beginning Of Line
/**
*
**/
String currentLine ()
{
BOL = data.fileIndex - 1;
try
{
// If the current position is at the end of the line,
// set BOL to before the end of the line so the whole
// line is returned.
if (data.fileBytes[BOL - 1] == '\r' && data.fileBytes[BOL] == '\n')
BOL -= 2;
else if (data.fileBytes[BOL] == '\n')
--BOL;
while (data.fileBytes[BOL] != '\n')
--BOL;
}
catch (ArrayIndexOutOfBoundsException e)
{
BOL = -1;
}
++BOL; // Go to the first character AFTER the newline
int EOL = data.fileIndex - 1;
try
{
while (data.fileBytes[EOL] != '\n' && data.fileBytes[EOL] != '\r')
++EOL;
}
catch (ArrayIndexOutOfBoundsException e)
{
EOL = data.fileBytes.length;
}
if (BOL < EOL)
return new String (data.fileBytes, BOL, EOL - BOL);
else
return "";
} // currentLine
/**
*
**/
String lastTokenLine ()
{
int saveFileIndex = data.fileIndex;
data.fileIndex = data.oldIndex;
String ret = currentLine ();
data.fileIndex = saveFileIndex;
return ret;
} // lastTokenLine
/**
*
**/
int currentLinePosition ()
{
return data.fileIndex - BOL;
} // currentLinePosition
/**
*
**/
int lastTokenLinePosition ()
{
return data.oldIndex - BOL;
} // lastTokenLinePosition
// The scanner data is moved to a separate class so that all of the
// data can easily be pushed and popped to a stack.
// The data must be stackable for macros and #included files. When
// a macro is encountered: the current stack data is reserved on
// the stack; the stack is loaded with the macro info; processing
// proceeds with this data. The same is true for #included files.
// It may seem that the entire Scanner should be put on a stack in
// the Parser since all the scanner data is stackable. But that
// would mean instantiating a new scanner. The scanner must
// continue from where it left off; when certain things cross file
// boundaries, they must be handled by the scanner, not the parser,
// things like: block comments, quoted strings, tokens.
private ScannerData data = new ScannerData ();
private Stack dataStack = new Stack ();
private Vector keywords = new Vector ();
private Vector openEndedKeywords = new Vector ();
private Vector wildcardKeywords = new Vector ();
private boolean verbose;
// <f46082.40> Identifiers starting with '_' are considered "Escaped",
// except when scanned during preprocessing. Class Preprocessor is
// responsible to modify the escapedOK flag accordingly. Since preceding
// underscores are now legal when scanning identifiers as well as
// macro identifier, underscoreOK is obsolete.
//
// boolean underscoreOK = false;
boolean escapedOK = true;
// <f46082.51> Remove -stateful feature.
// boolean stateful;
private boolean emitAll;
private float corbaLevel;
private boolean debug ;
} // class Scanner
// This is a dumb class, really just a struct. It contains all of the
// scanner class's data in one place so that that data can be easily
// pushed and popped to a stack.
/**
*
**/
class ScannerData
{
/**
*
**/
public ScannerData ()
{
} // ctor
/**
*
**/
public ScannerData (ScannerData that)
{
indent = that.indent;
fileEntry = that.fileEntry;
filename = that.filename;
fileBytes = that.fileBytes;
fileIndex = that.fileIndex;
oldIndex = that.oldIndex;
ch = that.ch;
line = that.line;
oldLine = that.oldLine;
macrodata = that.macrodata;
includeIsImport = that.includeIsImport;
} // copy ctor
String indent = "";
IncludeEntry fileEntry = null;
String filename = "";
// fileBytes is a byte array rather than a char array. This is
// safe because OMG IDL is specified to be ISO Latin-1 whose high-
// order byte is always 0x0. <f49747.1> Converted from byte[] to char[]
// to employ Reader classes, which have Character encoding features. <ajb>
//byte[] fileBytes = null;
char[] fileBytes = null;
int fileIndex = 0;
int oldIndex = 0;
char ch;
int line = 1;
int oldLine = 1;
boolean macrodata = false;
boolean includeIsImport = false;
} // class ScannerData