Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/tools/java/Scanner.java
38918 views
/*1* Copyright (c) 1994, 2004, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.tools.java;2627import java.io.IOException;28import java.io.InputStream;29import java.util.Hashtable;3031/**32* A Scanner for Java tokens. Errors are reported33* to the environment object.<p>34*35* The scanner keeps track of the current token,36* the value of the current token (if any), and the start37* position of the current token.<p>38*39* The scan() method advances the scanner to the next40* token in the input.<p>41*42* The match() method is used to quickly match opening43* brackets (ie: '(', '{', or '[') with their closing44* counter part. This is useful during error recovery.<p>45*46* An position consists of: ((linenr << WHEREOFFSETBITS) | offset)47* this means that both the line number and the exact offset into48* the file are encoded in each position value.<p>49*50* The compiler treats either "\n", "\r" or "\r\n" as the51* end of a line.<p>52*53* WARNING: The contents of this source file are not part of any54* supported API. Code that depends on them does so at its own risk:55* they are subject to change or removal without notice.56*57* @author Arthur van Hoff58*/5960public61class Scanner implements Constants {62/**63* The increment for each character.64*/65public static final long OFFSETINC = 1;6667/**68* The increment for each line.69*/70public static final long LINEINC = 1L << WHEREOFFSETBITS;7172/**73* End of input74*/75public static final int EOF = -1;7677/**78* Where errors are reported79*/80public Environment env;8182/**83* Input reader84*/85protected ScannerInputReader in;8687/**88* If true, present all comments as tokens.89* Contents are not saved, but positions are recorded accurately,90* so the comment can be recovered from the text.91* Line terminations are also returned as comment tokens,92* and may be distinguished by their start and end positions,93* which are equal (meaning, these tokens contain no chars).94*/95public boolean scanComments = false;9697/**98* Current token99*/100public int token;101102/**103* The position of the current token104*/105public long pos;106107/**108* The position of the previous token109*/110public long prevPos;111112/**113* The current character114*/115protected int ch;116117/*118* Token values.119*/120public char charValue;121public int intValue;122public long longValue;123public float floatValue;124public double doubleValue;125public String stringValue;126public Identifier idValue;127public int radix; // Radix, when reading int or long128129/*130* A doc comment preceding the most recent token131*/132public String docComment;133134/*135* A growable character buffer.136*/137private int count;138private char buffer[] = new char[1024];139private void growBuffer() {140char newBuffer[] = new char[buffer.length * 2];141System.arraycopy(buffer, 0, newBuffer, 0, buffer.length);142buffer = newBuffer;143}144145// The following two methods have been hand-inlined in146// scanDocComment. If you make changes here, you should147// check to see if scanDocComment also needs modification.148private void putc(int ch) {149if (count == buffer.length) {150growBuffer();151}152buffer[count++] = (char)ch;153}154155private String bufferString() {156return new String(buffer, 0, count);157}158159/**160* Create a scanner to scan an input stream.161*/162public Scanner(Environment env, InputStream in) throws IOException {163this.env = env;164useInputStream(in);165}166167/**168* Setup input from the given input stream,169* and scan the first token from it.170*/171protected void useInputStream(InputStream in) throws IOException {172try {173this.in = new ScannerInputReader(env, in);174} catch (Exception e) {175env.setCharacterEncoding(null);176this.in = new ScannerInputReader(env, in);177}178179ch = this.in.read();180prevPos = this.in.pos;181182scan();183}184185/**186* Create a scanner to scan an input stream.187*/188protected Scanner(Environment env) {189this.env = env;190// Expect the subclass to call useInputStream at the right time.191}192193/**194* Define a keyword.195*/196private static void defineKeyword(int val) {197Identifier.lookup(opNames[val]).setType(val);198}199200/**201* Initialized keyword and token Hashtables202*/203static {204// Statement keywords205defineKeyword(FOR);206defineKeyword(IF);207defineKeyword(ELSE);208defineKeyword(WHILE);209defineKeyword(DO);210defineKeyword(SWITCH);211defineKeyword(CASE);212defineKeyword(DEFAULT);213defineKeyword(BREAK);214defineKeyword(CONTINUE);215defineKeyword(RETURN);216defineKeyword(TRY);217defineKeyword(CATCH);218defineKeyword(FINALLY);219defineKeyword(THROW);220221// Type defineKeywords222defineKeyword(BYTE);223defineKeyword(CHAR);224defineKeyword(SHORT);225defineKeyword(INT);226defineKeyword(LONG);227defineKeyword(FLOAT);228defineKeyword(DOUBLE);229defineKeyword(VOID);230defineKeyword(BOOLEAN);231232// Expression keywords233defineKeyword(INSTANCEOF);234defineKeyword(TRUE);235defineKeyword(FALSE);236defineKeyword(NEW);237defineKeyword(THIS);238defineKeyword(SUPER);239defineKeyword(NULL);240241// Declaration keywords242defineKeyword(IMPORT);243defineKeyword(CLASS);244defineKeyword(EXTENDS);245defineKeyword(IMPLEMENTS);246defineKeyword(INTERFACE);247defineKeyword(PACKAGE);248defineKeyword(THROWS);249250// Modifier keywords251defineKeyword(PRIVATE);252defineKeyword(PUBLIC);253defineKeyword(PROTECTED);254defineKeyword(STATIC);255defineKeyword(TRANSIENT);256defineKeyword(SYNCHRONIZED);257defineKeyword(NATIVE);258defineKeyword(ABSTRACT);259defineKeyword(VOLATILE);260defineKeyword(FINAL);261defineKeyword(STRICTFP);262263// reserved keywords264defineKeyword(CONST);265defineKeyword(GOTO);266}267268/**269* Scan a comment. This method should be270* called once the initial /, * and the next271* character have been read.272*/273private void skipComment() throws IOException {274while (true) {275switch (ch) {276case EOF:277env.error(pos, "eof.in.comment");278return;279280case '*':281if ((ch = in.read()) == '/') {282ch = in.read();283return;284}285break;286287default:288ch = in.read();289break;290}291}292}293294/**295* Scan a doc comment. This method should be called296* once the initial /, * and * have been read. It gathers297* the content of the comment (witout leading spaces and '*'s)298* in the string buffer.299*/300private String scanDocComment() throws IOException {301// Note: this method has been hand-optimized to yield302// better performance. This was done after it was noted303// that javadoc spent a great deal of its time here.304// This should also help the performance of the compiler305// as well -- it scans the doc comments to find306// @deprecated tags.307//308// The logic of the method has been completely rewritten309// to avoid the use of flags that need to be looked at310// for every character read. Members that are accessed311// more than once have been stored in local variables.312// The methods putc() and bufferString() have been313// inlined by hand. Extra cases have been added to314// switch statements to trick the compiler into generating315// a tableswitch instead of a lookupswitch.316//317// This implementation aims to preserve the previous318// behavior of this method.319320int c;321322// Put `in' in a local variable.323final ScannerInputReader in = this.in;324325// We maintain the buffer locally rather than calling putc().326char[] buffer = this.buffer;327int count = 0;328329// We are called pointing at the second star of the doc330// comment:331//332// Input: /** the rest of the comment ... */333// ^334//335// We rely on this in the code below.336337// Consume any number of stars.338while ((c = in.read()) == '*')339;340341// Is the comment of the form /**/, /***/, /****/, etc.?342if (c == '/') {343// Set ch and return344ch = in.read();345return "";346}347348// Skip a newline on the first line of the comment.349if (c == '\n') {350c = in.read();351}352353outerLoop:354// The outerLoop processes the doc comment, looping once355// for each line. For each line, it first strips off356// whitespace, then it consumes any stars, then it357// puts the rest of the line into our buffer.358while (true) {359360// The wsLoop consumes whitespace from the beginning361// of each line.362wsLoop:363while (true) {364switch (c) {365case ' ':366case '\t':367// We could check for other forms of whitespace368// as well, but this is left as is for minimum369// disturbance of functionality.370//371// Just skip whitespace.372c = in.read();373break;374375// We have added extra cases here to trick the376// compiler into using a tableswitch instead of377// a lookupswitch. They can be removed without378// a change in meaning.379case 10: case 11: case 12: case 13: case 14: case 15:380case 16: case 17: case 18: case 19: case 20: case 21:381case 22: case 23: case 24: case 25: case 26: case 27:382case 28: case 29: case 30: case 31:383default:384// We've seen something that isn't whitespace,385// jump out.386break wsLoop;387}388} // end wsLoop.389390// Are there stars here? If so, consume them all391// and check for the end of comment.392if (c == '*') {393// Skip all of the stars...394do {395c = in.read();396} while (c == '*');397398// ...then check for the closing slash.399if (c == '/') {400// We're done with the doc comment.401// Set ch and break out.402ch = in.read();403break outerLoop;404}405}406407// The textLoop processes the rest of the characters408// on the line, adding them to our buffer.409textLoop:410while (true) {411switch (c) {412case EOF:413// We've seen a premature EOF. Break out414// of the loop.415env.error(pos, "eof.in.comment");416ch = EOF;417break outerLoop;418419case '*':420// Is this just a star? Or is this the421// end of a comment?422c = in.read();423if (c == '/') {424// This is the end of the comment,425// set ch and return our buffer.426ch = in.read();427break outerLoop;428}429// This is just an ordinary star. Add it to430// the buffer.431if (count == buffer.length) {432growBuffer();433buffer = this.buffer;434}435buffer[count++] = '*';436break;437438case '\n':439// We've seen a newline. Add it to our440// buffer and break out of this loop,441// starting fresh on a new line.442if (count == buffer.length) {443growBuffer();444buffer = this.buffer;445}446buffer[count++] = '\n';447c = in.read();448break textLoop;449450// Again, the extra cases here are a trick451// to get the compiler to generate a tableswitch.452case 0: case 1: case 2: case 3: case 4: case 5:453case 6: case 7: case 8: case 11: case 12: case 13:454case 14: case 15: case 16: case 17: case 18: case 19:455case 20: case 21: case 22: case 23: case 24: case 25:456case 26: case 27: case 28: case 29: case 30: case 31:457case 32: case 33: case 34: case 35: case 36: case 37:458case 38: case 39: case 40:459default:460// Add the character to our buffer.461if (count == buffer.length) {462growBuffer();463buffer = this.buffer;464}465buffer[count++] = (char)c;466c = in.read();467break;468}469} // end textLoop470} // end outerLoop471472// We have scanned our doc comment. It is stored in473// buffer. The previous implementation of scanDocComment474// stripped off all trailing spaces and stars from the comment.475// We will do this as well, so as to cause a minimum of476// disturbance. Is this what we want?477if (count > 0) {478int i = count - 1;479trailLoop:480while (i > -1) {481switch (buffer[i]) {482case ' ':483case '\t':484case '*':485i--;486break;487// And again, the extra cases here are a trick488// to get the compiler to generate a tableswitch.489case 0: case 1: case 2: case 3: case 4: case 5:490case 6: case 7: case 8: case 10: case 11: case 12:491case 13: case 14: case 15: case 16: case 17: case 18:492case 19: case 20: case 21: case 22: case 23: case 24:493case 25: case 26: case 27: case 28: case 29: case 30:494case 31: case 33: case 34: case 35: case 36: case 37:495case 38: case 39: case 40:496default:497break trailLoop;498}499}500count = i + 1;501502// Return the text of the doc comment.503return new String(buffer, 0, count);504} else {505return "";506}507}508509/**510* Scan a number. The first digit of the number should be the current511* character. We may be scanning hex, decimal, or octal at this point512*/513private void scanNumber() throws IOException {514boolean seenNonOctal = false;515boolean overflow = false;516boolean seenDigit = false; // used to detect invalid hex number 0xL517radix = (ch == '0' ? 8 : 10);518long value = ch - '0';519count = 0;520putc(ch); // save character in buffer521numberLoop:522for (;;) {523switch (ch = in.read()) {524case '.':525if (radix == 16)526break numberLoop; // an illegal character527scanReal();528return;529530case '8': case '9':531// We can't yet throw an error if reading an octal. We might532// discover we're really reading a real.533seenNonOctal = true;534case '0': case '1': case '2': case '3':535case '4': case '5': case '6': case '7':536seenDigit = true;537putc(ch);538if (radix == 10) {539overflow = overflow || (value * 10)/10 != value;540value = (value * 10) + (ch - '0');541overflow = overflow || (value - 1 < -1);542} else if (radix == 8) {543overflow = overflow || (value >>> 61) != 0;544value = (value << 3) + (ch - '0');545} else {546overflow = overflow || (value >>> 60) != 0;547value = (value << 4) + (ch - '0');548}549break;550551case 'd': case 'D': case 'e': case 'E': case 'f': case 'F':552if (radix != 16) {553scanReal();554return;555}556// fall through557case 'a': case 'A': case 'b': case 'B': case 'c': case 'C':558seenDigit = true;559putc(ch);560if (radix != 16)561break numberLoop; // an illegal character562overflow = overflow || (value >>> 60) != 0;563value = (value << 4) + 10 +564Character.toLowerCase((char)ch) - 'a';565break;566567case 'l': case 'L':568ch = in.read(); // skip over 'l'569longValue = value;570token = LONGVAL;571break numberLoop;572573case 'x': case 'X':574// if the first character is a '0' and this is the second575// letter, then read in a hexadecimal number. Otherwise, error.576if (count == 1 && radix == 8) {577radix = 16;578seenDigit = false;579break;580} else {581// we'll get an illegal character error582break numberLoop;583}584585default:586intValue = (int)value;587token = INTVAL;588break numberLoop;589}590} // while true591592// We have just finished reading the number. The next thing better593// not be a letter or digit.594// Note: There will be deprecation warnings against these uses595// of Character.isJavaLetterOrDigit and Character.isJavaLetter.596// Do not fix them yet; allow the compiler to run on pre-JDK1.1 VMs.597if (Character.isJavaLetterOrDigit((char)ch) || ch == '.') {598env.error(in.pos, "invalid.number");599do { ch = in.read(); }600while (Character.isJavaLetterOrDigit((char)ch) || ch == '.');601intValue = 0;602token = INTVAL;603} else if (radix == 8 && seenNonOctal) {604// A bogus octal literal.605intValue = 0;606token = INTVAL;607env.error(pos, "invalid.octal.number");608} else if (radix == 16 && seenDigit == false) {609// A hex literal with no digits, 0xL, for example.610intValue = 0;611token = INTVAL;612env.error(pos, "invalid.hex.number");613} else {614if (token == INTVAL) {615// Check for overflow. Note that base 10 literals616// have different rules than base 8 and 16.617overflow = overflow ||618(value & 0xFFFFFFFF00000000L) != 0 ||619(radix == 10 && value > 2147483648L);620621if (overflow) {622intValue = 0;623624// Give a specific error message which tells625// the user the range.626switch (radix) {627case 8:628env.error(pos, "overflow.int.oct");629break;630case 10:631env.error(pos, "overflow.int.dec");632break;633case 16:634env.error(pos, "overflow.int.hex");635break;636default:637throw new CompilerError("invalid radix");638}639}640} else {641if (overflow) {642longValue = 0;643644// Give a specific error message which tells645// the user the range.646switch (radix) {647case 8:648env.error(pos, "overflow.long.oct");649break;650case 10:651env.error(pos, "overflow.long.dec");652break;653case 16:654env.error(pos, "overflow.long.hex");655break;656default:657throw new CompilerError("invalid radix");658}659}660}661}662}663664/**665* Scan a float. We are either looking at the decimal, or we have already666* seen it and put it into the buffer. We haven't seen an exponent.667* Scan a float. Should be called with the current character is either668* the 'e', 'E' or '.'669*/670private void scanReal() throws IOException {671boolean seenExponent = false;672boolean isSingleFloat = false;673char lastChar;674if (ch == '.') {675putc(ch);676ch = in.read();677}678679numberLoop:680for ( ; ; ch = in.read()) {681switch (ch) {682case '0': case '1': case '2': case '3': case '4':683case '5': case '6': case '7': case '8': case '9':684putc(ch);685break;686687case 'e': case 'E':688if (seenExponent)689break numberLoop; // we'll get a format error690putc(ch);691seenExponent = true;692break;693694case '+': case '-':695lastChar = buffer[count - 1];696if (lastChar != 'e' && lastChar != 'E')697break numberLoop; // this isn't an error, though!698putc(ch);699break;700701case 'f': case 'F':702ch = in.read(); // skip over 'f'703isSingleFloat = true;704break numberLoop;705706case 'd': case 'D':707ch = in.read(); // skip over 'd'708// fall through709default:710break numberLoop;711} // sswitch712} // loop713714// we have just finished reading the number. The next thing better715// not be a letter or digit.716if (Character.isJavaLetterOrDigit((char)ch) || ch == '.') {717env.error(in.pos, "invalid.number");718do { ch = in.read(); }719while (Character.isJavaLetterOrDigit((char)ch) || ch == '.');720doubleValue = 0;721token = DOUBLEVAL;722} else {723token = isSingleFloat ? FLOATVAL : DOUBLEVAL;724try {725lastChar = buffer[count - 1];726if (lastChar == 'e' || lastChar == 'E'727|| lastChar == '+' || lastChar == '-') {728env.error(in.pos -1, "float.format");729} else if (isSingleFloat) {730String string = bufferString();731floatValue = Float.valueOf(string).floatValue();732if (Float.isInfinite(floatValue)) {733env.error(pos, "overflow.float");734} else if (floatValue == 0 && !looksLikeZero(string)) {735env.error(pos, "underflow.float");736}737} else {738String string = bufferString();739doubleValue = Double.valueOf(string).doubleValue();740if (Double.isInfinite(doubleValue)) {741env.error(pos, "overflow.double");742} else if (doubleValue == 0 && !looksLikeZero(string)) {743env.error(pos, "underflow.double");744}745}746} catch (NumberFormatException ee) {747env.error(pos, "float.format");748doubleValue = 0;749floatValue = 0;750}751}752return;753}754755// We have a token that parses as a number. Is this token possibly zero?756// i.e. does it have a non-zero value in the mantissa?757private static boolean looksLikeZero(String token) {758int length = token.length();759for (int i = 0; i < length; i++) {760switch (token.charAt(i)) {761case 0: case '.':762continue;763case '1': case '2': case '3': case '4': case '5':764case '6': case '7': case '8': case '9':765return false;766case 'e': case 'E': case 'f': case 'F':767return true;768}769}770return true;771}772773/**774* Scan an escape character.775* @return the character or -1 if it escaped an776* end-of-line.777*/778private int scanEscapeChar() throws IOException {779long p = in.pos;780781switch (ch = in.read()) {782case '0': case '1': case '2': case '3':783case '4': case '5': case '6': case '7': {784int n = ch - '0';785for (int i = 2 ; i > 0 ; i--) {786switch (ch = in.read()) {787case '0': case '1': case '2': case '3':788case '4': case '5': case '6': case '7':789n = (n << 3) + ch - '0';790break;791792default:793if (n > 0xFF) {794env.error(p, "invalid.escape.char");795}796return n;797}798}799ch = in.read();800if (n > 0xFF) {801env.error(p, "invalid.escape.char");802}803return n;804}805806case 'r': ch = in.read(); return '\r';807case 'n': ch = in.read(); return '\n';808case 'f': ch = in.read(); return '\f';809case 'b': ch = in.read(); return '\b';810case 't': ch = in.read(); return '\t';811case '\\': ch = in.read(); return '\\';812case '\"': ch = in.read(); return '\"';813case '\'': ch = in.read(); return '\'';814}815816env.error(p, "invalid.escape.char");817ch = in.read();818return -1;819}820821/**822* Scan a string. The current character823* should be the opening " of the string.824*/825private void scanString() throws IOException {826token = STRINGVAL;827count = 0;828ch = in.read();829830// Scan a String831while (true) {832switch (ch) {833case EOF:834env.error(pos, "eof.in.string");835stringValue = bufferString();836return;837838case '\r':839case '\n':840ch = in.read();841env.error(pos, "newline.in.string");842stringValue = bufferString();843return;844845case '"':846ch = in.read();847stringValue = bufferString();848return;849850case '\\': {851int c = scanEscapeChar();852if (c >= 0) {853putc((char)c);854}855break;856}857858default:859putc(ch);860ch = in.read();861break;862}863}864}865866/**867* Scan a character. The current character should be868* the opening ' of the character constant.869*/870private void scanCharacter() throws IOException {871token = CHARVAL;872873switch (ch = in.read()) {874case '\\':875int c = scanEscapeChar();876charValue = (char)((c >= 0) ? c : 0);877break;878879case '\'':880// There are two standard problems this case deals with. One881// is the malformed single quote constant (i.e. the programmer882// uses ''' instead of '\'') and the other is the empty883// character constant (i.e. ''). Just consume any number of884// single quotes and emit an error message.885charValue = 0;886env.error(pos, "invalid.char.constant");887ch = in.read();888while (ch == '\'') {889ch = in.read();890}891return;892893case '\r':894case '\n':895charValue = 0;896env.error(pos, "invalid.char.constant");897return;898899default:900charValue = (char)ch;901ch = in.read();902break;903}904905if (ch == '\'') {906ch = in.read();907} else {908env.error(pos, "invalid.char.constant");909while (true) {910switch (ch) {911case '\'':912ch = in.read();913return;914case ';':915case '\n':916case EOF:917return;918default:919ch = in.read();920}921}922}923}924925/**926* Scan an Identifier. The current character should927* be the first character of the identifier.928*/929private void scanIdentifier() throws IOException {930count = 0;931932while (true) {933putc(ch);934switch (ch = in.read()) {935case 'a': case 'b': case 'c': case 'd': case 'e':936case 'f': case 'g': case 'h': case 'i': case 'j':937case 'k': case 'l': case 'm': case 'n': case 'o':938case 'p': case 'q': case 'r': case 's': case 't':939case 'u': case 'v': case 'w': case 'x': case 'y':940case 'z':941case 'A': case 'B': case 'C': case 'D': case 'E':942case 'F': case 'G': case 'H': case 'I': case 'J':943case 'K': case 'L': case 'M': case 'N': case 'O':944case 'P': case 'Q': case 'R': case 'S': case 'T':945case 'U': case 'V': case 'W': case 'X': case 'Y':946case 'Z':947case '0': case '1': case '2': case '3': case '4':948case '5': case '6': case '7': case '8': case '9':949case '$': case '_':950break;951952default:953if (!Character.isJavaLetterOrDigit((char)ch)) {954idValue = Identifier.lookup(bufferString());955token = idValue.getType();956return;957}958}959}960}961962/**963* The ending position of the current token964*/965// Note: This should be part of the pos itself.966public long getEndPos() {967return in.pos;968}969970/**971* If the current token is IDENT, return the identifier occurrence.972* It will be freshly allocated.973*/974public IdentifierToken getIdToken() {975return (token != IDENT) ? null : new IdentifierToken(pos, idValue);976}977978/**979* Scan the next token.980* @return the position of the previous token.981*/982public long scan() throws IOException {983return xscan();984}985986protected long xscan() throws IOException {987final ScannerInputReader in = this.in;988long retPos = pos;989prevPos = in.pos;990docComment = null;991while (true) {992pos = in.pos;993994switch (ch) {995case EOF:996token = EOF;997return retPos;998999case '\n':1000if (scanComments) {1001ch = ' ';1002// Avoid this path the next time around.1003// Do not just call in.read; we want to present1004// a null token (and also avoid read-ahead).1005token = COMMENT;1006return retPos;1007}1008case ' ':1009case '\t':1010case '\f':1011ch = in.read();1012break;10131014case '/':1015switch (ch = in.read()) {1016case '/':1017// Parse a // comment1018while (((ch = in.read()) != EOF) && (ch != '\n'));1019if (scanComments) {1020token = COMMENT;1021return retPos;1022}1023break;10241025case '*':1026ch = in.read();1027if (ch == '*') {1028docComment = scanDocComment();1029} else {1030skipComment();1031}1032if (scanComments) {1033return retPos;1034}1035break;10361037case '=':1038ch = in.read();1039token = ASGDIV;1040return retPos;10411042default:1043token = DIV;1044return retPos;1045}1046break;10471048case '"':1049scanString();1050return retPos;10511052case '\'':1053scanCharacter();1054return retPos;10551056case '0': case '1': case '2': case '3': case '4':1057case '5': case '6': case '7': case '8': case '9':1058scanNumber();1059return retPos;10601061case '.':1062switch (ch = in.read()) {1063case '0': case '1': case '2': case '3': case '4':1064case '5': case '6': case '7': case '8': case '9':1065count = 0;1066putc('.');1067scanReal();1068break;1069default:1070token = FIELD;1071}1072return retPos;10731074case '{':1075ch = in.read();1076token = LBRACE;1077return retPos;10781079case '}':1080ch = in.read();1081token = RBRACE;1082return retPos;10831084case '(':1085ch = in.read();1086token = LPAREN;1087return retPos;10881089case ')':1090ch = in.read();1091token = RPAREN;1092return retPos;10931094case '[':1095ch = in.read();1096token = LSQBRACKET;1097return retPos;10981099case ']':1100ch = in.read();1101token = RSQBRACKET;1102return retPos;11031104case ',':1105ch = in.read();1106token = COMMA;1107return retPos;11081109case ';':1110ch = in.read();1111token = SEMICOLON;1112return retPos;11131114case '?':1115ch = in.read();1116token = QUESTIONMARK;1117return retPos;11181119case '~':1120ch = in.read();1121token = BITNOT;1122return retPos;11231124case ':':1125ch = in.read();1126token = COLON;1127return retPos;11281129case '-':1130switch (ch = in.read()) {1131case '-':1132ch = in.read();1133token = DEC;1134return retPos;11351136case '=':1137ch = in.read();1138token = ASGSUB;1139return retPos;1140}1141token = SUB;1142return retPos;11431144case '+':1145switch (ch = in.read()) {1146case '+':1147ch = in.read();1148token = INC;1149return retPos;11501151case '=':1152ch = in.read();1153token = ASGADD;1154return retPos;1155}1156token = ADD;1157return retPos;11581159case '<':1160switch (ch = in.read()) {1161case '<':1162if ((ch = in.read()) == '=') {1163ch = in.read();1164token = ASGLSHIFT;1165return retPos;1166}1167token = LSHIFT;1168return retPos;11691170case '=':1171ch = in.read();1172token = LE;1173return retPos;1174}1175token = LT;1176return retPos;11771178case '>':1179switch (ch = in.read()) {1180case '>':1181switch (ch = in.read()) {1182case '=':1183ch = in.read();1184token = ASGRSHIFT;1185return retPos;11861187case '>':1188if ((ch = in.read()) == '=') {1189ch = in.read();1190token = ASGURSHIFT;1191return retPos;1192}1193token = URSHIFT;1194return retPos;1195}1196token = RSHIFT;1197return retPos;11981199case '=':1200ch = in.read();1201token = GE;1202return retPos;1203}1204token = GT;1205return retPos;12061207case '|':1208switch (ch = in.read()) {1209case '|':1210ch = in.read();1211token = OR;1212return retPos;12131214case '=':1215ch = in.read();1216token = ASGBITOR;1217return retPos;1218}1219token = BITOR;1220return retPos;12211222case '&':1223switch (ch = in.read()) {1224case '&':1225ch = in.read();1226token = AND;1227return retPos;12281229case '=':1230ch = in.read();1231token = ASGBITAND;1232return retPos;1233}1234token = BITAND;1235return retPos;12361237case '=':1238if ((ch = in.read()) == '=') {1239ch = in.read();1240token = EQ;1241return retPos;1242}1243token = ASSIGN;1244return retPos;12451246case '%':1247if ((ch = in.read()) == '=') {1248ch = in.read();1249token = ASGREM;1250return retPos;1251}1252token = REM;1253return retPos;12541255case '^':1256if ((ch = in.read()) == '=') {1257ch = in.read();1258token = ASGBITXOR;1259return retPos;1260}1261token = BITXOR;1262return retPos;12631264case '!':1265if ((ch = in.read()) == '=') {1266ch = in.read();1267token = NE;1268return retPos;1269}1270token = NOT;1271return retPos;12721273case '*':1274if ((ch = in.read()) == '=') {1275ch = in.read();1276token = ASGMUL;1277return retPos;1278}1279token = MUL;1280return retPos;12811282case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':1283case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':1284case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':1285case 's': case 't': case 'u': case 'v': case 'w': case 'x':1286case 'y': case 'z':1287case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':1288case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':1289case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':1290case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':1291case 'Y': case 'Z':1292case '$': case '_':1293scanIdentifier();1294return retPos;12951296case '\u001a':1297// Our one concession to DOS.1298if ((ch = in.read()) == EOF) {1299token = EOF;1300return retPos;1301}1302env.error(pos, "funny.char");1303ch = in.read();1304break;130513061307default:1308if (Character.isJavaLetter((char)ch)) {1309scanIdentifier();1310return retPos;1311}1312env.error(pos, "funny.char");1313ch = in.read();1314break;1315}1316}1317}13181319/**1320* Scan to a matching '}', ']' or ')'. The current token must be1321* a '{', '[' or '(';1322*/1323public void match(int open, int close) throws IOException {1324int depth = 1;13251326while (true) {1327scan();1328if (token == open) {1329depth++;1330} else if (token == close) {1331if (--depth == 0) {1332return;1333}1334} else if (token == EOF) {1335env.error(pos, "unbalanced.paren");1336return;1337}1338}1339}1340}134113421343