001: /**
002: * Copyright John E. Lloyd, 2004. All rights reserved. Permission to use,
003: * copy, modify and redistribute is granted, provided that this copyright
004: * notice is retained and the author is given credit whenever appropriate.
005: *
006: * This software is distributed "as is", without any warranty, including
007: * any implied warranty of merchantability or fitness for a particular
008: * use. The author assumes no responsibility for, and shall not be liable
009: * for, any special, indirect, or consequential damages, or any damages
010: * whatsoever, arising out of or in connection with the use of this
011: * software.
012: */package argparser;
013:
014: class StringScanner {
015: private char[] buf;
016: private int idx;
017: private int len;
018: private String stringDelimiters = "";
019:
020: public StringScanner(String s) {
021: buf = new char[s.length() + 1];
022: s.getChars(0, s.length(), buf, 0);
023: len = s.length();
024: buf[len] = 0;
025: idx = 0;
026: }
027:
028: public int getIndex() {
029: return idx;
030: }
031:
032: public void setIndex(int i) {
033: if (i < 0) {
034: idx = 0;
035: } else if (i > len) {
036: idx = len;
037: } else {
038: idx = i;
039: }
040: }
041:
042: public void setStringDelimiters(String s) {
043: stringDelimiters = s;
044: }
045:
046: public String getStringDelimiters() {
047: return stringDelimiters;
048: }
049:
050: public char scanChar() throws StringScanException {
051: int idxSave = idx;
052: skipWhiteSpace();
053: try {
054: if (buf[idx] == '\'') {
055: return scanQuotedChar();
056: } else {
057: return scanUnquotedChar();
058: }
059: } catch (StringScanException e) {
060: idx = idxSave;
061: throw e;
062: }
063: }
064:
065: public char scanQuotedChar() throws StringScanException {
066: StringScanException exception = null;
067: char retval = 0;
068: int idxSave = idx;
069:
070: skipWhiteSpace();
071: if (idx == len) {
072: exception = new StringScanException(idx, "end of input");
073: } else if (buf[idx++] == '\'') {
074: try {
075: retval = scanUnquotedChar();
076: } catch (StringScanException e) {
077: exception = e;
078: }
079: if (exception == null) {
080: if (idx == len) {
081: exception = new StringScanException(idx,
082: "end of input");
083: } else if (buf[idx++] != '\'') {
084: exception = new StringScanException(idx - 1,
085: "unclosed quoted character");
086: }
087: }
088: } else {
089: exception = new StringScanException(idx - 1,
090: "uninitialized quoted character");
091: }
092: if (exception != null) {
093: idx = idxSave;
094: throw exception;
095: }
096: return retval;
097: }
098:
099: public char scanUnquotedChar() throws StringScanException {
100: StringScanException exception = null;
101: char c, retval = 0;
102: int idxSave = idx;
103:
104: if (idx == len) {
105: exception = new StringScanException(idx, "end of input");
106: } else if ((c = buf[idx++]) == '\\') {
107: if (idx == len) {
108: exception = new StringScanException(idx, "end of input");
109: } else {
110: c = buf[idx++];
111: if (c == '"') {
112: retval = '"';
113: } else if (c == '\'') {
114: retval = '\'';
115: } else if (c == '\\') {
116: retval = '\\';
117: } else if (c == 'n') {
118: retval = '\n';
119: } else if (c == 't') {
120: retval = '\t';
121: } else if (c == 'b') {
122: retval = '\b';
123: } else if (c == 'r') {
124: retval = '\r';
125: } else if (c == 'f') {
126: retval = '\f';
127: } else if ('0' <= c && c < '8') {
128: int v = c - '0';
129: for (int j = 0; j < 2; j++) {
130: if (idx == len) {
131: break;
132: }
133: c = buf[idx];
134: if ('0' <= c && c < '8'
135: && (v * 8 + (c - '0')) <= 255) {
136: v = v * 8 + (c - '0');
137: idx++;
138: } else {
139: break;
140: }
141: }
142: retval = (char) v;
143: } else {
144: exception = new StringScanException(idx - 1,
145: "illegal escape character '" + c + "'");
146: }
147: }
148: } else {
149: retval = c;
150: }
151: if (exception != null) {
152: idx = idxSave;
153: throw exception;
154: }
155: return retval;
156: }
157:
158: public String scanQuotedString() throws StringScanException {
159: StringScanException exception = null;
160: StringBuffer sbuf = new StringBuffer(len);
161: char c;
162: int idxSave = idx;
163:
164: skipWhiteSpace();
165: if (idx == len) {
166: exception = new StringScanException(idx, "end of input");
167: } else if ((c = buf[idx++]) == '"') {
168: while (idx < len && (c = buf[idx]) != '"' && c != '\n') {
169: if (c == '\\') {
170: try {
171: c = scanUnquotedChar();
172: } catch (StringScanException e) {
173: exception = e;
174: break;
175: }
176: } else {
177: idx++;
178: }
179: sbuf.append(c);
180: }
181: if (exception == null && idx >= len) {
182: exception = new StringScanException(len, "end of input");
183: } else if (exception == null && c == '\n') {
184: exception = new StringScanException(idx,
185: "unclosed quoted string");
186: } else {
187: idx++;
188: }
189: } else {
190: exception = new StringScanException(idx - 1,
191: "quoted string must start with \"");
192: }
193: if (exception != null) {
194: idx = idxSave;
195: throw exception;
196: }
197: return sbuf.toString();
198: }
199:
200: public String scanNonWhiteSpaceString() throws StringScanException {
201: StringBuffer sbuf = new StringBuffer(len);
202: int idxSave = idx;
203: char c;
204:
205: skipWhiteSpace();
206: if (idx == len) {
207: StringScanException e = new StringScanException(idx,
208: "end of input");
209: idx = idxSave;
210: throw e;
211: } else {
212: c = buf[idx++];
213: while (idx < len && !Character.isWhitespace(c)
214: && stringDelimiters.indexOf(c) == -1) {
215: sbuf.append(c);
216: c = buf[idx++];
217: }
218: if (Character.isWhitespace(c)
219: || stringDelimiters.indexOf(c) != -1) {
220: idx--;
221: } else {
222: sbuf.append(c);
223: }
224: }
225: return sbuf.toString();
226: }
227:
228: public String scanString() throws StringScanException {
229: int idxSave = idx;
230: skipWhiteSpace();
231: try {
232: if (buf[idx] == '"') {
233: return scanQuotedString();
234: } else {
235: return scanNonWhiteSpaceString();
236: }
237: } catch (StringScanException e) {
238: idx = idxSave;
239: throw e;
240: }
241: }
242:
243: public String getString() throws StringScanException {
244: StringBuffer sbuf = new StringBuffer(len);
245: while (idx < len) {
246: sbuf.append(buf[idx++]);
247: }
248: return sbuf.toString();
249: }
250:
251: public long scanInt() throws StringScanException {
252: int idxSave = idx;
253: char c;
254: int sign = 1;
255:
256: skipWhiteSpace();
257: if ((c = buf[idx]) == '-' || c == '+') {
258: sign = (c == '-' ? -1 : 1);
259: idx++;
260: }
261: try {
262: if (idx == len) {
263: throw new StringScanException(len, "end of input");
264: } else if ((c = buf[idx]) == '0') {
265: if ((c = buf[idx + 1]) == 'x' || c == 'X') {
266: idx += 2;
267: return sign * scanInt(16, false);
268: } else {
269: return sign * scanInt(8, false);
270: }
271: } else {
272: return sign * scanInt(10, false);
273: }
274: } catch (StringScanException e) {
275: idx = idxSave;
276: throw e;
277: }
278: }
279:
280: public long scanInt(int radix) throws StringScanException {
281: return scanInt(radix, /*skipWhite=*/true);
282: }
283:
284: private String baseDesc(int radix) {
285: switch (radix) {
286: case 10: {
287: return "decimal";
288: }
289: case 8: {
290: return "octal";
291: }
292: case 16: {
293: return "hex";
294: }
295: default: {
296: return "base " + radix;
297: }
298: }
299: }
300:
301: public long scanInt(int radix, boolean skipWhite)
302: throws StringScanException {
303: StringScanException exception = null;
304: int charval, idxSave = idx;
305: char c;
306: long val = 0;
307: boolean negate = false;
308:
309: if (skipWhite) {
310: skipWhiteSpace();
311: }
312: if ((c = buf[idx]) == '-' || c == '+') {
313: negate = (c == '-');
314: idx++;
315: }
316: if (idx >= len) {
317: exception = new StringScanException(len, "end of input");
318: } else if ((charval = Character.digit(buf[idx++], radix)) == -1) {
319: exception = new StringScanException(idx - 1, "malformed "
320: + baseDesc(radix) + " integer");
321: } else {
322: val = charval;
323: while ((charval = Character.digit(buf[idx], radix)) != -1) {
324: val = val * radix + charval;
325: idx++;
326: }
327: if (Character.isLetter(c = buf[idx])
328: || Character.isDigit(c) || c == '_') {
329: exception = new StringScanException(idx, "malformed "
330: + baseDesc(radix) + " integer");
331: }
332: }
333: if (exception != null) {
334: idx = idxSave;
335: throw exception;
336: }
337: return negate ? -val : val;
338: }
339:
340: public double scanDouble() throws StringScanException {
341: StringScanException exception = null;
342: int idxSave = idx;
343: char c;
344: // parse [-][0-9]*[.][0-9]*[eE][-][0-9]*
345: boolean hasDigits = false;
346: boolean signed;
347: double value = 0;
348:
349: skipWhiteSpace();
350: if (idx == len) {
351: exception = new StringScanException("end of input");
352: } else {
353: if ((c = buf[idx]) == '-' || c == '+') {
354: signed = true;
355: idx++;
356: }
357: if (matchDigits()) {
358: hasDigits = true;
359: }
360: if (buf[idx] == '.') {
361: idx++;
362: }
363: if (!hasDigits && (buf[idx] < '0' || buf[idx] > '9')) {
364: if (idx == len) {
365: exception = new StringScanException(idx,
366: "end of input");
367: } else {
368: exception = new StringScanException(idx,
369: "malformed floating number: no digits");
370: }
371: } else {
372: matchDigits();
373:
374: if ((c = buf[idx]) == 'e' || c == 'E') {
375: idx++;
376: if ((c = buf[idx]) == '-' || c == '+') {
377: signed = true;
378: idx++;
379: }
380: if (buf[idx] < '0' || buf[idx] > '9') {
381: if (idx == len) {
382: exception = new StringScanException(idx,
383: "end of input");
384: } else {
385: exception = new StringScanException(idx,
386: "malformed floating number: no digits in exponent");
387: }
388: } else {
389: matchDigits();
390: }
391: }
392: }
393: }
394: if (exception == null) {
395: // if (Character.isLetterOrDigit(c=buf[idx]) || c == '_')
396: // { exception = new StringScanException (idx,
397: //"malformed floating number");
398: // }
399: // else
400: {
401: try {
402: value = Double.parseDouble(new String(buf, idxSave,
403: idx - idxSave));
404: } catch (NumberFormatException e) {
405: exception = new StringScanException(idx,
406: "malformed floating number");
407: }
408: }
409: }
410: if (exception != null) {
411: idx = idxSave;
412: throw exception;
413: }
414: return value;
415: }
416:
417: public boolean scanBoolean() throws StringScanException {
418: StringScanException exception = null;
419: int idxSave = idx;
420: String testStr = "false";
421: boolean testval = false;
422: char c;
423:
424: skipWhiteSpace();
425: if (buf[idx] == 't') {
426: testStr = "true";
427: testval = true;
428: } else {
429: testval = false;
430: }
431: int i = 0;
432: for (i = 0; i < testStr.length(); i++) {
433: if (testStr.charAt(i) != buf[idx]) {
434: if (idx == len) {
435: exception = new StringScanException(idx,
436: "end of input");
437: }
438: break;
439: }
440: idx++;
441: }
442: if (exception == null) {
443: if (i < testStr.length()
444: || Character.isLetterOrDigit(c = buf[idx])
445: || c == '_') {
446: exception = new StringScanException(idx,
447: "illegal boolean");
448: }
449: }
450: if (exception != null) {
451: idx = idxSave;
452: throw exception;
453: }
454: return testval;
455: }
456:
457: public boolean matchString(String s) {
458: int k = idx;
459: for (int i = 0; i < s.length(); i++) {
460: if (k >= len || s.charAt(i) != buf[k++]) {
461: return false;
462: }
463: }
464: idx = k;
465: return true;
466: }
467:
468: public boolean matchDigits() {
469: int k = idx;
470: char c;
471:
472: while ((c = buf[k]) >= '0' && c <= '9') {
473: k++;
474: }
475: if (k > idx) {
476: idx = k;
477: return true;
478: } else {
479: return false;
480: }
481: }
482:
483: public void skipWhiteSpace() {
484: while (Character.isWhitespace(buf[idx])) {
485: idx++;
486: }
487: }
488:
489: private int skipWhiteSpace(int k) {
490: while (Character.isWhitespace(buf[k])) {
491: k++;
492: }
493: return k;
494: }
495:
496: public boolean atEnd() {
497: return idx == len;
498: }
499:
500: public boolean atBeginning() {
501: return idx == 0;
502: }
503:
504: public void ungetc() {
505: if (idx > 0) {
506: idx--;
507: }
508: }
509:
510: public char getc() {
511: char c = buf[idx];
512: if (idx < len) {
513: idx++;
514: }
515: return c;
516: }
517:
518: public char peekc() {
519: return buf[idx];
520: }
521:
522: public String substring(int i0, int i1) {
523: if (i0 < 0) {
524: i0 = 0;
525: } else if (i0 >= len) {
526: i0 = len - 1;
527: }
528: if (i1 < 0) {
529: i1 = 0;
530: } else if (i1 > len) {
531: i1 = len;
532: }
533: if (i1 <= i0) {
534: return "";
535: }
536: return new String(buf, i0, i1 - i0);
537: }
538:
539: public String substring(int i0) {
540: if (i0 < 0) {
541: i0 = 0;
542: }
543: if (i0 >= len) {
544: return "";
545: } else {
546: return new String(buf, i0, len - i0);
547: }
548: }
549: }
|