001: package jsint;
002:
003: import java.io.*;
004:
005: /** InputPort is to Scheme as InputStream is to Java.
006:
007: * @author Peter Norvig, Copyright 1998, peter@norvig.com, <a href="license.txt">license</a>
008: * subsequently modified by Jscheme project members
009: * licensed under zlib licence (see license.txt)
010: **/
011:
012: public class InputPort implements java.util.Enumeration {
013:
014: /** The distinguished end of file marking object. **/
015: // public static final Symbol EOF = Symbol.intern("#!eof");
016: public static final Object EOF = token("!eof");
017: public static final boolean defaultBrlsMode = true;
018: public boolean brlsMode = defaultBrlsMode;
019: private boolean keepComments = false;
020:
021: /** These tokens must be distinguishable from anything else returned
022: by readToken().
023: **/
024: private static class Token {
025: String name;
026:
027: Token(String name) {
028: this .name = name;
029: }
030:
031: public String toString() {
032: return "#" + name + "#";
033: }
034: }
035:
036: private static final Object BACKQUOTE = token("`");
037: private static final Object CLOSE = token(")");
038: private static final Object COMMA = token(",");
039: private static final Object COMMA_AT = token(",@");
040: private static final Object DOT = token(".");
041: private static final Object OPEN = token("(");
042: private static final Object QUOTE = token("'");
043: private static final Object HASH_ESCAPE_CLOSE = token("#]");
044: private static final Object HASH_ESCAPE_OPEN = token("#[");
045: private static final Object HASH_CLOSE = token("#}");
046:
047: /* These are used when reading the #\s,#\space,#\n,#\newline
048: character literals */
049: private static final Symbol CHAR_s = Symbol.intern("s");
050: private static final Symbol CHAR_S = Symbol.intern("S");
051: private static final Symbol CHAR_n = Symbol.intern("n");
052: private static final Symbol CHAR_N = Symbol.intern("N");
053: private static final Symbol NEWLINE_UC = Symbol.intern("NEWLINE");
054: private static final Symbol NEWLINE_LC = Symbol.intern("newline");
055: private static final Symbol SPACE_UC = Symbol.intern("SPACE");
056: private static final Symbol SPACE_LC = Symbol.intern("space");
057: private static final Symbol CURLY = Symbol.intern("!{}");
058: private static final Symbol HASH_CURLY = Symbol.intern("!#{}");
059:
060: /* This stack is used to keep track of the current state viz-a-viz
061: quasi-strings. The states are listed below the stack declaration */
062: private java.util.Stack quasiStack = new java.util.Stack();
063:
064: private static final Integer TOP = new Integer(0);
065: private static final Integer STR = new Integer(1); // inside a "...." string
066: private static final Integer QSTR = new Integer(2); // inside a {....} quasi-string
067: private static final Integer HQSTR = new Integer(3); // inside a #{....#} quasi-string
068: private static final Integer QSTRESC = new Integer(4); // inside a [...] escape of a {...} quasi-string
069: private static final Integer HQSTRESC = new Integer(5); // inside a #[...#] escape of a #{...#} quasi-string
070:
071: private static Object token(String x) {
072: return new Token(x);
073: }
074:
075: boolean isPushedChar = false;
076: int pushedChar = -1;
077: int radix = 10;
078: LineNumberReader in;
079: StringBuffer buff = new StringBuffer(8);
080:
081: public InputPort(Reader in, boolean keepComments) {
082: quasiStack.push(InputPort.TOP);
083: if (in instanceof LineNumberReader)
084: this .in = (LineNumberReader) in;
085: else
086: this .in = new LineNumberReader(in);
087: this .keepComments = keepComments;
088: }
089:
090: /** Construct an InputPort from an InputStream. **/
091: public InputPort(InputStream in) {
092: this (new InputStreamReader(in), false);
093: }
094:
095: /** Construct an InputPort from a Reader. **/
096: public InputPort(Reader in) {
097: this (in, false);
098: }
099:
100: /** Read and return a Scheme character or EOF. **/
101: public synchronized Object readChar() {
102: try {
103: if (isPushedChar) {
104: isPushedChar = false;
105: if (pushedChar == -1)
106: return EOF;
107: else
108: return U.toChar((char) pushedChar);
109: } else {
110: int ch = in.read();
111: if (ch == -1)
112: return EOF;
113: else
114: return U.toChar((char) ch);
115: }
116: } catch (IOException e) {
117: E.warn("On input, exception A: " + e);
118: return EOF;
119: }
120: }
121:
122: /** Peek at and return the next Scheme character (or EOF).
123: * However, don't consume the character. **/
124: public synchronized Object peekChar() {
125: int p = peekCh();
126: if (p == -1)
127: return EOF;
128: else
129: return U.toChar((char) p);
130: }
131:
132: private Pair pushedTokens = Pair.EMPTY;
133:
134: private boolean isPushedToken() {
135: return (pushedTokens != Pair.EMPTY);
136: }
137:
138: /** Push a token back to be re-used later. **/
139: private Object pushToken(Object token) {
140: pushedTokens = new Pair(token, pushedTokens);
141: return token;
142: }
143:
144: /** Pop off the previously pushed token. **/
145: private Object popToken() {
146: Object token = pushedTokens.first;
147: pushedTokens = (Pair) (pushedTokens.rest);
148: return token;
149: }
150:
151: /** Push a character back to be re-used later. **/
152: private int pushChar(int ch) {
153: isPushedChar = true;
154: return pushedChar = ch;
155: }
156:
157: /** Pop off the previously pushed character. **/
158: private int popChar() {
159: isPushedChar = false;
160: return pushedChar;
161: }
162:
163: /** Peek at and return the next Scheme character as an int, -1 for EOF.
164: * However, don't consume the character. **/
165: private int peekCh() {
166: try {
167: return isPushedChar ? pushedChar : pushChar(in.read());
168: } catch (IOException e) {
169: E.warn("On input, exception B: " + e);
170: return -1;
171: }
172: }
173:
174: public int getLineNumber() {
175: return in.getLineNumber();
176: }
177:
178: public Object nextElement() {
179: return read();
180: }
181:
182: public boolean hasMoreElements() {
183: try {
184: Object token = readToken();
185: pushToken(token);
186: return (token != EOF);
187: } catch (IOException e) {
188: E.warn("On input, exception C: " + e);
189: return false;
190: }
191: }
192:
193: public synchronized Object read() {
194: try {
195: Object token = readToken();
196: if (token instanceof Symbol)
197: return token; // return lookupGlobal((Symbol) token);
198: else if (token instanceof Token) {
199: // if (token == OPEN) return readTail(false);
200: if (token == OPEN)
201: return readList();
202: else if (token == QUOTE)
203: return U.list(Symbol.QUOTE, read());
204: else if (token == COMMA)
205: return U.list(Symbol.UNQUOTE, read());
206: else if (token == BACKQUOTE)
207: return U.list(Symbol.QUASIQUOTE, read());
208: else if (token == COMMA_AT)
209: return U.list(Symbol.UNQUOTE_SPLICING, read());
210: else if (token == CLOSE) {
211: E.warn("Extra ) ignored -- line number "
212: + in.getLineNumber());
213: return read();
214: } else if (token == DOT) {
215: E.warn("Extra . ignored -- line number "
216: + in.getLineNumber());
217: return read();
218: } else
219: return token;
220: } else
221: return token;
222: } catch (IOException e) {
223: E.warn("On input, exception D: " + e);
224: return EOF;
225: }
226: }
227:
228: /** Close the port. Return TRUE if ok. **/
229: public Object close() {
230: try {
231: this .in.close();
232: return U.TRUE;
233: } catch (IOException e) {
234: return E.error("On input, IOException E: " + e);
235: }
236: }
237:
238: private Object readList() throws IOException { // Saw "("
239: Object token = readToken();
240: if (token instanceof Token)
241: return readListToken((Token) token);
242: return readListRest(new Queue(token));
243: }
244:
245: private Object readListToken(Token token) throws IOException {
246: if (token == CLOSE)
247: return Pair.EMPTY; // "()"
248: else if (token == DOT)
249: return E.error("'.' not allowed immediately after '('");
250: else if (token == EOF)
251: return E.error("EOF during read.");
252: else {
253: pushToken(token);
254: return readListRest(new Queue(read()));
255: }
256: }
257:
258: private Object readListRest(Queue queue) throws IOException {
259: int lineNum = getLineNumber();
260: // Saw "( x ..."
261: while (true) {
262: if (Scheme.isInterruptable())
263: Scheme.interruptCheck();
264: Object token = readToken();
265: if (token instanceof Token) {
266: if (token == CLOSE)
267: return queue.getContent();
268: if (token == DOT) {
269: Object result = read();
270: token = readToken();
271: if (token != CLOSE)
272: return E.error("Where's the ')'? Got " + token
273: + " after .");
274: queue.getLast().rest = result;
275: return queue.getContent();
276: }
277: if (token == EOF)
278: return E
279: .error("EOF during read: open paren on line "
280: + lineNum + " not closed.");
281: pushToken(token);
282: queue.add(read());
283: } else
284: queue.add(token);
285: }
286: }
287:
288: /** Returns either a Token or a primitive Scheme type. **/
289: private Object readToken() throws IOException {
290: Integer qstate = (Integer) quasiStack.peek();
291:
292: if (isPushedToken()) {
293: Object t = popToken();
294: // handle case where a #] was pushed in readSymbolOrNumber
295: if (HASH_ESCAPE_CLOSE.equals(t)) {
296: quasiStack.pop();
297: return readString();
298: } else
299: return t;
300: }
301:
302: int ch = (isPushedChar) ? popChar() : in.read();
303: while (Character.isWhitespace((char) ch))
304: ch = in.read();
305: // See what kind of non-white character we got
306: switch (ch) {
307: case '(':
308: return OPEN;
309: case ')':
310: return CLOSE;
311: case '\'':
312: return QUOTE;
313: case '`':
314: return BACKQUOTE;
315: case '#':
316: return readHashToken();
317: case '"':
318: return readString();
319: case ',':
320: ch = in.read();
321: if (ch == '@')
322: return COMMA_AT;
323: else {
324: pushChar(ch);
325: return COMMA;
326: }
327: case ';':
328: return readComment(ch);
329: case -1:
330: return EOF;
331: case '{':
332: if (brlsMode) {
333: quasiStack.push(InputPort.QSTR);
334: pushToken(CURLY);
335: isPushedChar = true;
336: pushedChar = '"';
337: return OPEN;
338: }
339: case ']':
340: if (brlsMode) {
341: if (QSTRESC == qstate) {
342: quasiStack.pop();
343: return readString();
344: } else if (HQSTRESC == qstate) {
345: ch = in.read();
346: if (ch == '#') {
347: quasiStack.pop();
348: return readString();
349: }
350: }
351: }
352:
353: default:
354: return readNumberOrSymbol(ch);
355: }
356: }
357:
358: // Comment: skip to end of line and then read next token
359: private Object readComment(int ch) throws IOException {
360: if (keepComments)
361: return readCommentKeeping(ch);
362: else {
363: while (ch != -1 && ch != '\n' && ch != '\r')
364: ch = in.read();
365: return readToken();
366: }
367: }
368:
369: private Object readCommentKeeping(int ch) throws IOException {
370: buff.setLength(0);
371: int count = 0;
372: while (ch == ';') {
373: count = count + 1;
374: ch = in.read();
375: }
376: while (ch != -1 && ch != '\n' && ch != '\r') {
377: buff.append((char) ch);
378: ch = in.read();
379: }
380: return jscheme.JS.list(Symbol.intern("comment"), new Integer(
381: count), buff.toString());
382: }
383:
384: /*
385: Here we read a string or quasi-string or hash-quasi-string
386: A string is terminated by a '"' which is not itself quoted.
387: A quasi-string is terminated by a '[' or a '}'
388: A hash-quasi-string is terminated by a #[ or }#, so we need to check
389: whether the [ is preceded by a hash or the } is followed by a hash. The proper way to
390: escape such a string is #\[ or }/# ...
391: */
392: private String readString() {
393: Integer qstate = (Integer) quasiStack.peek();
394: int ch = 0;
395: int lastchar = -1;
396: buff.setLength(0);
397:
398: if (qstate == QSTR)
399: try {
400: while (((ch = in.read()) != '}') && (ch != '[')
401: && (ch != -1)) {
402: lastchar = ch;
403: buff
404: .append((char) ((ch == '\\') ? (U.useJavaSyntax ? escapechar(in
405: .read())
406: : in.read())
407: : ch));
408: }
409: if (ch == -1)
410: E.warn("EOF inside of a string.");
411: if (ch == '}') {
412: quasiStack.pop();
413: pushToken(CLOSE);
414: }
415: if (ch == '[') {
416: quasiStack.push(QSTRESC);
417: }
418: return internStringBuffer(buff);
419: } catch (IOException e) {
420: E.warn("On input, IOException G:", e);
421: return "";
422: }
423:
424: else if (qstate == HQSTR)
425: try {
426: while ((!(((ch = in.read()) == '#') && (lastchar == '}')))
427: && (!((ch == '[') && (lastchar == '#')))
428: && (ch != -1)) {
429: lastchar = ch;
430: buff
431: .append((char) ((ch == '\\') ? (U.useJavaSyntax ? escapechar(in
432: .read())
433: : in.read())
434: : ch));
435: }
436: if (ch == -1)
437: E.warn("EOF inside of a string.");
438: if (ch == '#') {
439: quasiStack.pop();
440: pushToken(CLOSE);
441: }
442: if (ch == '[') {
443: quasiStack.push(HQSTRESC);
444: }
445: buff.deleteCharAt(buff.length() - 1); // get rid of the extra #
446: return internStringBuffer(buff);
447: } catch (IOException e) {
448: E.warn("On input, IOException H1:", e);
449: return "";
450: }
451: else
452: try {
453: while (((ch = in.read()) != '"') && (ch != -1)) {
454: buff
455: .append((char) ((ch == '\\') ? (U.useJavaSyntax ? escapechar(in
456: .read())
457: : in.read())
458: : ch));
459: }
460: if (ch == -1)
461: E.warn("EOF inside of a string.");
462: return internStringBuffer(buff);
463: } catch (IOException e) {
464: E.warn("On input, IOException F:", e);
465: return "";
466: }
467: }
468:
469: private String internStringBuffer(StringBuffer b) {
470: return b.toString().intern();
471: }
472:
473: private String moveBufToString(StringBuffer b) {
474: int L = b.length();
475: char[] chars = new char[L];
476: if (L > 0) {
477: b.getChars(0, L, chars, 0);
478: b.setLength(0);
479: }
480: return new String(chars);
481: }
482:
483: /*
484: KRA 31MAR01: 4.3% of loading elf/basic.scm was spent thowing
485: NumberFormatException i think because + and - are commonly used
486: symbols. So added length test.
487:
488: KRA 06APR03: This did not work for #xff. so add tests for each
489: radix.
490: */
491: private boolean maybeNumber(int c, String buff, int radix) {
492: return (radix == 10 && (c >= '0' && c <= '9' || ((c == '.'
493: || c == '+' || c == '-') && buff.length() > 1)))
494: || (radix == 16 && ((c >= '0' && c <= '9')
495: || (c >= 'a' && c <= 'f') || (c >= 'a' && c <= 'A')))
496: || (radix == 8 && (c >= '0' && c <= '7'))
497: || (radix == 2 && (c >= '0' && c <= '1'));
498: }
499:
500: /*
501: read a number or a symbol
502: if in quasi-string-escape mode, then a ] delimits a number or symbol
503: if in hash-quasi-string-escape mode, then a #] delimits a number or symbol
504: */
505: private Object readNumberOrSymbol(int ch) throws IOException {
506: Integer qstate = (Integer) quasiStack.peek();
507: buff.setLength(0);
508: int c = ch; // save the first character
509: boolean foundDelimiter = false;
510:
511: do { // accumulate characters up to the next delimiter
512: buff.append((char) ch);
513: ch = in.read();
514: foundDelimiter = isDelimiter(ch);
515:
516: /* check for a ]# delimiting a number or symbol */
517: if ((ch == ']') && (qstate == HQSTRESC)) {
518: ch = in.read();
519: if (ch == '#') {
520: foundDelimiter = true;
521: pushToken(HASH_ESCAPE_CLOSE);
522: ch = ' ';
523: continue;
524: } else {
525: buff.append(']');
526: }
527: }
528:
529: if ((ch == ']') && (qstate == QSTRESC)) {
530: foundDelimiter = true;
531: }
532:
533: /* this next if expression handles the quasi-string exception
534: which allows [] to appear within Symbols in quasi-string escapes */
535: if ((ch == '[') && (qstate == QSTRESC)) {
536: buff.append((char) ch);
537: ch = in.read();
538: foundDelimiter = isDelimiter(ch) && (ch != ']');
539: }
540:
541: } while (!foundDelimiter);
542: pushChar(ch);
543: if (c == '.' && buff.length() == 1)
544: return DOT;
545: String tok = moveBufToString(buff);
546: // Try potential numbers, but catch any format errors.
547: try {
548: if (maybeNumber(c, tok, radix)) {
549: Object it = stringToNumber(tok, radix);
550: if (it instanceof Number)
551: return it;
552: }
553: } catch (NumberFormatException e) {
554: return Symbol.intern(tok);
555: }
556: return Symbol.intern(tok);
557: }
558:
559: public static Object schemeStringToNumber(String tok, int rdx) {
560: try {
561: return U.toNum(Long.parseLong(tok, rdx));
562: } catch (NumberFormatException e) {
563: try {
564: if (rdx != 10)
565: return U.FALSE;
566: else
567: return new Double(tok);
568: } catch (NumberFormatException e2) {
569: return U.FALSE;
570: }
571: }
572: }
573:
574: public static Object stringToNumber(String tok, int rdx) {
575: if (rdx == 10) {
576: if (U.useJavaSyntax) {
577: try {
578: return readWholeNumber(tok);
579: } catch (NumberFormatException e1) {
580: try {
581: return readFloatingPoint(tok);
582: } catch (NumberFormatException e3) {
583: return U.FALSE;
584: }
585: }
586: } else {
587: try {
588: return U.toNum(Long.parseLong(tok, rdx));
589: } catch (NumberFormatException e) {
590: try {
591: return new Double(tok);
592: } catch (NumberFormatException e2) {
593: return U.FALSE;
594: }
595: }
596: }
597: } else
598: return U
599: .toNum(Long.parseLong(U.stringify(tok, false), rdx));
600: }
601:
602: /* attempt to parse a long or an int in Java syntax
603: this currently accepts +---+013LLBSBSL --> -11L
604: we may want to disallow multiple signs and multiple type characters
605: */
606: public static Number readWholeNumber(String s)
607: throws NumberFormatException {
608: long n;
609: if (s.endsWith("l") || s.endsWith("L"))
610: return new Long(readWholeNumber(
611: s.substring(0, s.length() - 1)).longValue());
612: if (s.endsWith("b") || s.endsWith("B"))
613: return new Byte(readWholeNumber(
614: s.substring(0, s.length() - 1)).byteValue());
615: if (s.endsWith("s") || s.endsWith("S"))
616: return new Short(readWholeNumber(
617: s.substring(0, s.length() - 1)).shortValue());
618: else if (s.startsWith("+") && (s.length() > 1))
619: return readWholeNumber(s.substring(1, s.length()));
620: else if (s.startsWith("-") && (s.length() > 1))
621: return negate(readWholeNumber(s.substring(1, s.length())));
622: // else if (s.startsWith("0x"))
623: // n = Long.parseLong(s.substring(2,s.length()),16);
624: // else if ((s.startsWith("0")) && (s.length()>1))
625: // n = Long.parseLong(s.substring(1,s.length()),8);
626: else
627: n = Long.parseLong(s, 10);
628: return U.toNum(n);
629: }
630:
631: public static Number negate(Number n) {
632: if (n instanceof Integer)
633: return U.toNum(-n.intValue());
634: else if (n instanceof Long)
635: return new Long(-n.longValue());
636: else if (n instanceof Float)
637: return new Float(-n.floatValue());
638: else if (n instanceof Double)
639: return new Double(-n.doubleValue());
640: else { // this should never happen!
641: E.warn("ERROR in Inputport.negate called with "
642: + n.getClass() + " returning " + n);
643: return n;
644: }
645: }
646:
647: /* attempt to read a float or double in Java syntax */
648: public static Number readFloatingPoint(String s)
649: throws NumberFormatException {
650: if (s.endsWith("f") || s.endsWith("F"))
651: return new Float(readFloatingPoint(
652: s.substring(0, s.length() - 1)).floatValue());
653: else if (s.endsWith("d") || s.endsWith("D"))
654: return new Double(readFloatingPoint(
655: s.substring(0, s.length() - 1)).doubleValue());
656: else if (s.startsWith("+") && (s.length() > 1))
657: return readFloatingPoint(s.substring(1, s.length()));
658: else if (s.startsWith("-") && (s.length() > 1))
659: return negate(readFloatingPoint(s.substring(1, s.length())));
660: else if (s.startsWith("0") && (s.length() > 1)
661: && (Character.isDigit(s.charAt(1))))
662: throw (new NumberFormatException(
663: "floating point starting with 0 is either an octal or 0.ddd"));
664: else
665: return new Double(s);
666: }
667:
668: private Object readHashToken() throws IOException {
669: int ch;
670: Object token = null;
671: switch (ch = in.read()) {
672: case 't':
673: case 'T':
674: return U.TRUE;
675: case 'f':
676: case 'F':
677: return U.FALSE;
678: case 'n': { // #null
679: pushChar(ch);
680: token = readToken();
681: if (token == Symbol.NULL)
682: return null;
683: else
684: E.warn("illegal syntax #" + token + " ignored");
685: return readToken();
686: }
687: case '(': // #(...) vector.
688: pushChar('(');
689: return U.listToVector(read());
690: case '\\': // #\...
691: ch = in.read();
692: if (ch == 's' || ch == 'S' || ch == 'n' || ch == 'N') {
693: pushChar(ch);
694: token = readToken();
695: /* at this point token must be one of
696: SPACE, space, NEWLINE, newline, s, S, n, or N
697: or we have an error, and we warn the user and ignore the token
698: */
699: if ((token == SPACE_UC) || (token == SPACE_LC))
700: return U.toChar(' ');
701: else if ((token == NEWLINE_UC) || (token == NEWLINE_LC))
702: return U.toChar('\n');
703: else if (token == CHAR_s)
704: return U.toChar('s');
705: else if (token == CHAR_S)
706: return U.toChar('S');
707: else if (token == CHAR_n)
708: return U.toChar('n');
709: else if (token == CHAR_N)
710: return U.toChar('N');
711: else {
712: E.warn("illegal syntax #" + token + " ignored");
713: return readToken();
714: }
715: } else
716: return U.toChar((char) ch);
717: case '\'':
718: if (U.useJavaSyntax) {
719: ch = in.read();
720: Character x;
721: if (ch != '\\')
722: x = new Character((char) ch);
723: else
724: x = new Character(escapechar(in.read()));
725: ch = in.read();
726: if (ch != '\'')
727: E.warn("character syntax is #'C', or #\\C, not #'C"
728: + ((char) ch));
729: return x;
730: }
731:
732: // start of hashQuasiString #{ .... #} with hash escapes #[...#]
733: // implement by converting to (!#{} A B ... Z)
734: case '{':
735: if (brlsMode) {
736: quasiStack.push(InputPort.HQSTR);
737: pushToken(HASH_CURLY);
738: isPushedChar = true;
739: pushedChar = '"';
740: return OPEN;
741: }
742: // Unix #! treated as comment.
743: case '!':
744: return readComment(ch);
745:
746: // else fall through to the default
747: default:
748: if (U.useJavaSyntax) {
749: E.warn("#" + ((char) ch) + " not recognized, ignored.");
750: return readToken();
751: } else
752: switch (ch) {
753: case 'e':
754: return U.toNum(U.toInt(readToken()));
755: case 'i':
756: return U.toNum(U.toReal(readToken()));
757: case 'd':
758: return readToken();
759: case 'b':
760: case 'o':
761: case 'x':
762: return readHashNumber(ch == 'b' ? 2 : ch == 'o' ? 8
763: : 16);
764: default:
765: E.warn("#" + ((char) ch)
766: + " not recognized, ignored.");
767: return readToken();
768: }
769: }
770: }
771:
772: private Object readHashNumber(int radix) throws IOException {
773: synchronized (this ) { // synchonize to potect radix
774: this .radix = radix;
775: Object token = readToken(); // should be readNumberOrSymbol(...);
776: this .radix = 10;
777: return token;
778: }
779: }
780:
781: private char escapechar(int c) throws IOException {
782: switch (c) {
783: case 'b':
784: return '\b';
785: case 't':
786: return '\t';
787: case 'n':
788: return '\n';
789: case 'f':
790: return '\f';
791: case 'r':
792: return '\r';
793: case '"':
794: return '"';
795: case '\'':
796: return '\'';
797: case '\\':
798: return '\\';
799: case '0':
800: return new Character((char) Integer.parseInt((""
801: + (char) in.read() + (char) in.read()), 8))
802: .charValue();
803: case 'u':
804: return new Character((char) Integer.parseInt((""
805: + (char) in.read() + (char) in.read()
806: + (char) in.read() + (char) in.read()), 16))
807: .charValue();
808: case '[':
809: case ']':
810: case '{':
811: case '}':
812: return (char) c;
813:
814: default: {
815: E
816: .warn("Expected a Java escape sequence for a character, found "
817: + ((char) c) + " with ascii code " + c);
818: return (char) c;
819: }
820: }
821: }
822:
823: private boolean isDelimiter(int ch) {
824: switch (ch) {
825: case -1:
826: case '(':
827: case ')':
828: case '\'':
829: case ';':
830: case ',':
831: case '"':
832: case '`':
833: case ' ':
834: case '\t':
835: case '\n':
836: return true;
837: default:
838: return Character.isWhitespace((char) ch);
839: }
840: }
841:
842: }
|