1. /*
  2. * @(#)GTKScanner.java 1.39 03/01/23
  3. *
  4. * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package com.sun.java.swing.plaf.gtk;
  8. import java.io.*;
  9. import java.util.HashMap;
  10. /**
  11. * @author Shannon Hickey
  12. * @version 1.39 01/23/03
  13. */
  14. class GTKScanner {
  15. public static final String CHARS_a_2_z = "abcdefghijklmnopqrstuvwxyz";
  16. public static final String CHARS_A_2_Z = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
  17. public static final String CHARS_DIGITS = "0123456789";
  18. public static final int TOKEN_EOF = -1;
  19. public static final int TOKEN_LEFT_PAREN = '(';
  20. public static final int TOKEN_RIGHT_PAREN = ')';
  21. public static final int TOKEN_LEFT_CURLY = '{';
  22. public static final int TOKEN_RIGHT_CURLY = '}';
  23. public static final int TOKEN_LEFT_BRACE = '[';
  24. public static final int TOKEN_RIGHT_BRACE = ']';
  25. public static final int TOKEN_EQUAL_SIGN = '=';
  26. public static final int TOKEN_COMMA = ',';
  27. public static final int TOKEN_NONE = 256;
  28. public static final int TOKEN_ERROR = TOKEN_NONE + 1;
  29. public static final int TOKEN_CHAR = TOKEN_ERROR + 1;
  30. public static final int TOKEN_BINARY = TOKEN_CHAR + 1;
  31. public static final int TOKEN_OCTAL = TOKEN_BINARY + 1;
  32. public static final int TOKEN_INT = TOKEN_OCTAL + 1;
  33. public static final int TOKEN_HEX = TOKEN_INT + 1;
  34. public static final int TOKEN_FLOAT = TOKEN_HEX + 1;
  35. public static final int TOKEN_STRING = TOKEN_FLOAT + 1;
  36. public static final int TOKEN_SYMBOL = TOKEN_STRING + 1;
  37. public static final int TOKEN_IDENTIFIER = TOKEN_SYMBOL + 1;
  38. public static final int TOKEN_IDENTIFIER_NULL = TOKEN_IDENTIFIER + 1;
  39. public static final int TOKEN_LAST = TOKEN_IDENTIFIER_NULL + 1;
  40. public static final int ERR_UNKNOWN = 0;
  41. public static final int ERR_UNEXP_EOF = ERR_UNKNOWN + 1;
  42. public static final int ERR_UNEXP_EOF_IN_STRING = ERR_UNEXP_EOF + 1;
  43. public static final int ERR_UNEXP_EOF_IN_COMMENT = ERR_UNEXP_EOF_IN_STRING + 1;
  44. public static final int ERR_NON_DIGIT_IN_CONST = ERR_UNEXP_EOF_IN_COMMENT + 1;
  45. public static final int ERR_DIGIT_RADIX = ERR_NON_DIGIT_IN_CONST + 1;
  46. public static final int ERR_FLOAT_RADIX = ERR_DIGIT_RADIX + 1;
  47. public static final int ERR_FLOAT_MALFORMED = ERR_FLOAT_RADIX + 1;
  48. String whiteSpaceChars = " \t\r\n";
  49. String identifierFirst = CHARS_a_2_z + CHARS_A_2_Z + "_";
  50. String identifierNth = CHARS_a_2_z + CHARS_A_2_Z + "_-" + CHARS_DIGITS;
  51. String commentSingle = "#\n";
  52. boolean caseSensitive = false;
  53. boolean scanCommentMulti = true;
  54. boolean scanIdentifier = true;
  55. boolean scanIdentifier1Char = false;
  56. boolean scanIdentifierNULL = false;
  57. boolean scanSymbols = true;
  58. boolean scanBinary = false;
  59. boolean scanOctal = true;
  60. boolean scanFloat = true;
  61. boolean scanHex = true;
  62. boolean scanHexDollar = false;
  63. boolean scanStringSq = true;
  64. boolean scanStringDq = true;
  65. boolean numbers2Int = true;
  66. boolean int2Float = false;
  67. boolean identifier2String = false;
  68. boolean char2Token = true;
  69. boolean symbol2Token = false;
  70. private static class ScannerKey {
  71. private int scope;
  72. private String symbol;
  73. public int value = -1;
  74. ScannerKey(int scope, String symbol) {
  75. this.scope = scope;
  76. this.symbol = symbol;
  77. }
  78. public boolean equals(Object o) {
  79. if (o instanceof ScannerKey) {
  80. ScannerKey comp = (ScannerKey)o;
  81. return scope == comp.scope && symbol.equals(comp.symbol);
  82. }
  83. return false;
  84. }
  85. public int hashCode() {
  86. int result = 17;
  87. result = 37 * result + scope;
  88. result = 37 * result + symbol.hashCode();
  89. return result;
  90. }
  91. }
  92. static class TokenValue {
  93. long longVal;
  94. double doubleVal;
  95. char charVal;
  96. String stringVal;
  97. TokenValue() {
  98. clear();
  99. }
  100. void copyFrom(TokenValue other) {
  101. longVal = other.longVal;
  102. doubleVal = other.doubleVal;
  103. charVal = other.charVal;
  104. stringVal = other.stringVal;
  105. }
  106. void clear() {
  107. longVal = 0L;
  108. doubleVal = 0.0D;
  109. charVal = (char)0;
  110. stringVal = null;
  111. }
  112. }
  113. private String inputName;
  114. private HashMap symbolTable = new HashMap();
  115. private Reader reader;
  116. int currToken;
  117. TokenValue currValue = new TokenValue();
  118. int currLine;
  119. int currPosition;
  120. int nextToken;
  121. TokenValue nextValue = new TokenValue();
  122. int nextLine;
  123. int nextPosition;
  124. int currScope = 0;
  125. private static int nextUniqueScope = 1;
  126. private static final int CHAR_EOF = -1;
  127. private static final int CHAR_NONE = -2;
  128. private int peekedChar = CHAR_NONE;
  129. private ScannerKey lookupKey = new ScannerKey(0, null);
  130. public GTKScanner() {
  131. clearScanner();
  132. }
  133. public void clearScanner() {
  134. if (reader != null) {
  135. try {
  136. reader.close();
  137. } catch (IOException ioe) {
  138. }
  139. reader = null;
  140. }
  141. inputName = null;
  142. currToken = TOKEN_NONE;
  143. currValue.clear();
  144. currLine = 1;
  145. currPosition = 0;
  146. nextToken = TOKEN_NONE;
  147. nextValue.clear();
  148. nextLine = 1;
  149. nextPosition = 0;
  150. currScope = 0;
  151. peekedChar = CHAR_NONE;
  152. }
  153. public void scanReader(Reader r, String inputName) {
  154. if (r == null) {
  155. return;
  156. }
  157. if (reader != null) {
  158. clearScanner();
  159. }
  160. reader = r;
  161. this.inputName = inputName;
  162. }
  163. public static int getUniqueScopeID() {
  164. return nextUniqueScope++;
  165. }
  166. public int setScope(int scope) {
  167. int oldScope = currScope;
  168. currScope = scope;
  169. return oldScope;
  170. }
  171. public void addSymbol(String symbol, int value) {
  172. if (symbol == null) {
  173. return;
  174. }
  175. ScannerKey key = lookupSymbol(symbol);
  176. if (key == null) {
  177. key = new ScannerKey(currScope, caseSensitive ? symbol : symbol.toLowerCase());
  178. symbolTable.put(key, key);
  179. }
  180. key.value = value;
  181. }
  182. public boolean containsSymbol(String symbol) {
  183. return lookupSymbol(symbol) != null;
  184. }
  185. private ScannerKey lookupSymbol(String symbol) {
  186. lookupKey.scope = currScope;
  187. lookupKey.symbol = (caseSensitive ? symbol : symbol.toLowerCase());
  188. return (ScannerKey)symbolTable.get(lookupKey);
  189. }
  190. public void clearSymbolTable() {
  191. symbolTable.clear();
  192. }
  193. public int peekNextToken() throws IOException {
  194. if (nextToken == TOKEN_NONE) {
  195. readAToken();
  196. switch(nextToken) {
  197. case TOKEN_SYMBOL:
  198. if (symbol2Token) {
  199. nextToken = (int)nextValue.longVal;
  200. }
  201. break;
  202. case TOKEN_IDENTIFIER:
  203. if (identifier2String) {
  204. nextToken = TOKEN_STRING;
  205. }
  206. break;
  207. case TOKEN_HEX:
  208. case TOKEN_OCTAL:
  209. case TOKEN_BINARY:
  210. if (numbers2Int) {
  211. nextToken = TOKEN_INT;
  212. }
  213. break;
  214. }
  215. if (nextToken == TOKEN_INT && int2Float) {
  216. nextToken = TOKEN_FLOAT;
  217. nextValue.doubleVal = nextValue.longVal;
  218. }
  219. }
  220. return nextToken;
  221. }
  222. public int getToken() throws IOException {
  223. currToken = peekNextToken();
  224. currValue.copyFrom(nextValue);
  225. currLine = nextLine;
  226. currPosition = nextPosition;
  227. if (currToken != TOKEN_EOF) {
  228. nextToken = TOKEN_NONE;
  229. }
  230. return currToken;
  231. }
  232. private int peekNextChar() throws IOException {
  233. if (peekedChar == CHAR_NONE) {
  234. peekedChar = reader.read();
  235. }
  236. return peekedChar;
  237. }
  238. private int getChar() throws IOException {
  239. int ch = peekNextChar();
  240. if (ch != CHAR_EOF) {
  241. peekedChar = CHAR_NONE;
  242. if (ch == '\n') {
  243. nextPosition = 0;
  244. nextLine++;
  245. } else {
  246. nextPosition++;
  247. }
  248. }
  249. return ch;
  250. }
  251. // ----- scanning methods and variables ----- //
  252. private StringBuffer sb;
  253. private TokenValue value = new TokenValue();
  254. private int token;
  255. private int ch;
  256. private boolean skipSpaceAndComments() throws IOException {
  257. while(ch != CHAR_EOF) {
  258. if (whiteSpaceChars.indexOf(ch) != -1) {
  259. // continue
  260. } else if (scanCommentMulti && ch == '/' && peekNextChar() == '*') {
  261. getChar();
  262. while((ch = getChar()) != CHAR_EOF) {
  263. if (ch == '*' && peekNextChar() == '/') {
  264. getChar();
  265. break;
  266. }
  267. }
  268. if (ch == CHAR_EOF) {
  269. return false;
  270. }
  271. } else if (commentSingle.length() == 2 && ch == commentSingle.charAt(0)) {
  272. while((ch = getChar()) != CHAR_EOF) {
  273. if (ch == commentSingle.charAt(1)) {
  274. break;
  275. }
  276. }
  277. if (ch == CHAR_EOF) {
  278. return false;
  279. }
  280. } else {
  281. break;
  282. }
  283. ch = getChar();
  284. }
  285. return true;
  286. }
  287. private void readAToken() throws IOException {
  288. boolean inString = false;
  289. nextValue.clear();
  290. sb = null;
  291. do {
  292. value.clear();
  293. token = TOKEN_NONE;
  294. ch = getChar();
  295. if (!skipSpaceAndComments()) {
  296. token = TOKEN_ERROR;
  297. value.longVal = ERR_UNEXP_EOF_IN_COMMENT;
  298. } else if (scanIdentifier && ch != CHAR_EOF && identifierFirst.indexOf(ch) != -1) {
  299. checkForIdentifier();
  300. handleOrdinaryChar();
  301. } else {
  302. switch(ch) {
  303. case CHAR_EOF:
  304. token = TOKEN_EOF;
  305. break;
  306. case '"':
  307. if (!scanStringDq) {
  308. handleOrdinaryChar();
  309. } else {
  310. token = TOKEN_STRING;
  311. inString = true;
  312. sb = new StringBuffer();
  313. while ((ch = getChar()) != CHAR_EOF) {
  314. if (ch == '"') {
  315. inString = false;
  316. break;
  317. } else {
  318. if (ch == '\\') {
  319. ch = getChar();
  320. switch(ch) {
  321. case CHAR_EOF:
  322. break;
  323. case '\\':
  324. sb.append('\\');
  325. break;
  326. case 'n':
  327. sb.append('\n');
  328. break;
  329. case 'r':
  330. sb.append('\r');
  331. break;
  332. case 't':
  333. sb.append('\t');
  334. break;
  335. case 'f':
  336. sb.append('\f');
  337. break;
  338. case 'b':
  339. sb.append('\b');
  340. break;
  341. case '0':
  342. case '1':
  343. case '2':
  344. case '3':
  345. case '4':
  346. case '5':
  347. case '6':
  348. case '7':
  349. int i = ch - '0';
  350. int nextCh = peekNextChar();
  351. if (nextCh >= '0' && nextCh <= '7') {
  352. ch = getChar();
  353. i = i * 8 + ch - '0';
  354. nextCh = peekNextChar();
  355. if (nextCh >= '0' && nextCh <= '7') {
  356. ch = getChar();
  357. i = i * 8 + ch - '0';
  358. }
  359. }
  360. sb.append((char)i);
  361. break;
  362. default:
  363. sb.append((char)ch);
  364. break;
  365. }
  366. } else {
  367. sb.append((char)ch);
  368. }
  369. }
  370. }
  371. ch = CHAR_EOF;
  372. }
  373. break;
  374. case '\'':
  375. if (!scanStringSq) {
  376. handleOrdinaryChar();
  377. } else {
  378. token = TOKEN_STRING;
  379. inString = true;
  380. sb = new StringBuffer();
  381. while ((ch = getChar()) != CHAR_EOF) {
  382. if (ch == '\'') {
  383. inString = false;
  384. break;
  385. } else {
  386. sb.append((char)ch);
  387. }
  388. }
  389. ch = CHAR_EOF;
  390. }
  391. break;
  392. case '$':
  393. if (!scanHexDollar) {
  394. handleOrdinaryChar();
  395. } else {
  396. token = TOKEN_HEX;
  397. ch = getChar();
  398. scanNumber(false);
  399. }
  400. break;
  401. case '.':
  402. if (!scanFloat) {
  403. handleOrdinaryChar();
  404. } else {
  405. token = TOKEN_FLOAT;
  406. ch = getChar();
  407. scanNumber(true);
  408. }
  409. break;
  410. case '0':
  411. if (scanOctal) {
  412. token = TOKEN_OCTAL;
  413. } else {
  414. token = TOKEN_INT;
  415. }
  416. ch = peekNextChar();
  417. if (scanHex && (ch == 'x' || ch == 'X')) {
  418. token = TOKEN_HEX;
  419. getChar();
  420. ch = getChar();
  421. if (ch == CHAR_EOF) {
  422. token = TOKEN_ERROR;
  423. value.longVal = ERR_UNEXP_EOF;
  424. break;
  425. }
  426. if (char2int(ch, 16) < 0) {
  427. token = TOKEN_ERROR;
  428. value.longVal = ERR_DIGIT_RADIX;
  429. ch = CHAR_EOF;
  430. break;
  431. }
  432. } else if (scanBinary && (ch == 'b' || ch == 'B')) {
  433. token = TOKEN_BINARY;
  434. getChar();
  435. ch = getChar();
  436. if (ch == CHAR_EOF) {
  437. token = TOKEN_ERROR;
  438. value.longVal = ERR_UNEXP_EOF;
  439. break;
  440. }
  441. if (char2int(ch, 2) < 0) {
  442. token = TOKEN_ERROR;
  443. value.longVal = ERR_NON_DIGIT_IN_CONST;
  444. ch = CHAR_EOF;
  445. break;
  446. }
  447. } else {
  448. ch = '0';
  449. }
  450. // purposely fall through
  451. case '1':
  452. case '2':
  453. case '3':
  454. case '4':
  455. case '5':
  456. case '6':
  457. case '7':
  458. case '8':
  459. case '9':
  460. scanNumber(false);
  461. break;
  462. default:
  463. handleOrdinaryChar();
  464. break;
  465. }
  466. }
  467. } while (ch != CHAR_EOF);
  468. if (inString) {
  469. token = TOKEN_ERROR;
  470. value.longVal = ERR_UNEXP_EOF_IN_STRING;
  471. sb = null;
  472. }
  473. if (sb != null) {
  474. value.stringVal = sb.toString();
  475. sb = null;
  476. }
  477. if (token == TOKEN_IDENTIFIER) {
  478. if (scanSymbols) {
  479. int scope = currScope;
  480. ScannerKey key = lookupSymbol(value.stringVal);
  481. if (key != null) {
  482. value.stringVal = null;
  483. token = TOKEN_SYMBOL;
  484. value.longVal = key.value;
  485. }
  486. }
  487. if (token == TOKEN_IDENTIFIER && scanIdentifierNULL & value.stringVal.length() == 4) {
  488. if ("NULL".equals(caseSensitive ? value.stringVal : value.stringVal.toUpperCase())) {
  489. token = TOKEN_IDENTIFIER_NULL;
  490. }
  491. }
  492. }
  493. nextToken = token;
  494. nextValue.copyFrom(value);
  495. }
  496. private void handleOrdinaryChar() throws IOException {
  497. if (ch != CHAR_EOF) {
  498. if (char2Token) {
  499. token = ch;
  500. } else {
  501. token = TOKEN_CHAR;
  502. value.charVal = (char)ch;
  503. }
  504. ch = CHAR_EOF;
  505. }
  506. }
  507. private void checkForIdentifier() throws IOException {
  508. if (ch != CHAR_EOF && identifierNth.indexOf(peekNextChar()) != -1) {
  509. token = TOKEN_IDENTIFIER;
  510. sb = new StringBuffer();
  511. sb.append((char)ch);
  512. do {
  513. ch = getChar();
  514. sb.append((char)ch);
  515. ch = peekNextChar();
  516. } while (ch != CHAR_EOF && identifierNth.indexOf(ch) != -1);
  517. ch = CHAR_EOF;
  518. } else if (scanIdentifier1Char) {
  519. token = TOKEN_IDENTIFIER;
  520. value.stringVal = String.valueOf((char)ch);
  521. ch = CHAR_EOF;
  522. }
  523. }
  524. private static int char2int(int c, int base) {
  525. if (c >= '0' && c <= '9') {
  526. c -= '0';
  527. } else if (c >= 'A' && c <= 'Z') {
  528. c -= 'A' - 10;
  529. } else if (c >= 'a' && c <= 'z') {
  530. c -= 'a' - 10;
  531. } else {
  532. return -1;
  533. }
  534. return c < base ? c : -1;
  535. }
  536. private void scanNumber(boolean seenDot) throws IOException {
  537. boolean inNumber = true;
  538. if (token == TOKEN_NONE) {
  539. token = TOKEN_INT;
  540. }
  541. sb = new StringBuffer(seenDot ? "0." : "");
  542. sb.append((char)ch);
  543. do {
  544. boolean isExponent = (token == TOKEN_FLOAT && (ch == 'e' || ch == 'E'));
  545. ch = peekNextChar();
  546. if (char2int(ch, 36) >= 0
  547. || (scanFloat && ch == '.')
  548. || (isExponent && (ch == '+' || ch == '-'))) {
  549. ch = getChar();
  550. switch(ch) {
  551. case '0':
  552. case '1':
  553. case '2':
  554. case '3':
  555. case '4':
  556. case '5':
  557. case '6':
  558. case '7':
  559. case '8':
  560. case '9':
  561. sb.append((char)ch);
  562. break;
  563. case '.':
  564. if (token != TOKEN_INT && token != TOKEN_OCTAL) {
  565. value.longVal = (token == TOKEN_FLOAT ? ERR_FLOAT_MALFORMED : ERR_FLOAT_RADIX);
  566. token = TOKEN_ERROR;
  567. inNumber = false;
  568. } else {
  569. token = TOKEN_FLOAT;
  570. sb.append((char)ch);
  571. }
  572. break;
  573. case '+':
  574. case '-':
  575. if (token != TOKEN_FLOAT) {
  576. token = TOKEN_ERROR;
  577. value.longVal = ERR_NON_DIGIT_IN_CONST;
  578. inNumber = false;
  579. } else {
  580. sb.append((char)ch);
  581. }
  582. break;
  583. case 'E':
  584. case 'e':
  585. if ((token != TOKEN_HEX && !scanFloat)
  586. || (token != TOKEN_HEX
  587. && token != TOKEN_OCTAL
  588. && token != TOKEN_FLOAT
  589. && token != TOKEN_INT)) {
  590. token = TOKEN_ERROR;
  591. value.longVal = ERR_NON_DIGIT_IN_CONST;
  592. inNumber = false;
  593. } else {
  594. if (token != TOKEN_HEX) {
  595. token = TOKEN_FLOAT;
  596. }
  597. sb.append((char)ch);
  598. }
  599. break;
  600. default:
  601. if (token != TOKEN_HEX) {
  602. token = TOKEN_ERROR;
  603. value.longVal = ERR_NON_DIGIT_IN_CONST;
  604. } else {
  605. sb.append((char)ch);
  606. }
  607. break;
  608. }
  609. } else {
  610. inNumber = false;
  611. }
  612. } while (inNumber);
  613. try {
  614. switch(token) {
  615. case TOKEN_INT:
  616. value.longVal = Long.parseLong(sb.toString(), 10);
  617. break;
  618. case TOKEN_FLOAT:
  619. value.doubleVal = Double.parseDouble(sb.toString());
  620. break;
  621. case TOKEN_HEX:
  622. value.longVal = Long.parseLong(sb.toString(), 16);
  623. break;
  624. case TOKEN_OCTAL:
  625. value.longVal = Long.parseLong(sb.toString(), 8);
  626. break;
  627. case TOKEN_BINARY:
  628. value.longVal = Long.parseLong(sb.toString(), 2);
  629. break;
  630. }
  631. } catch (NumberFormatException nfe) {
  632. // PENDING(shannonh) - in some cases this could actually be ERR_DIGIT_RADIX
  633. token = TOKEN_ERROR;
  634. value.longVal = ERR_NON_DIGIT_IN_CONST;
  635. }
  636. sb = null;
  637. ch = CHAR_EOF;
  638. }
  639. public void printMessage(String message, boolean isError) {
  640. System.err.print(inputName + ":" + currLine + ": ");
  641. if (isError) {
  642. System.err.print("error: ");
  643. }
  644. System.err.println(message);
  645. }
  646. // PENDING(shannonh) - a good implementation of this method is needed
  647. public void unexpectedToken(int expected, String symbolName, String message, boolean isError) {
  648. String prefix = "lexical error or unexpected token, expected valid token";
  649. if (message != null) {
  650. prefix += " - " + message;
  651. }
  652. printMessage(prefix, isError);
  653. }
  654. }