PageRenderTime 62ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/OpenWIGLibrary/src/se/krka/kahlua/stdlib/StringLib.java

http://openwig.googlecode.com/
Java | 1502 lines | 1268 code | 152 blank | 82 comment | 375 complexity | 398fec01e6ab315e53ea0c273d80ef16 MD5 | raw file
  1. /*
  2. Copyright (c) 2007-2009 Kristofer Karlsson <kristofer.karlsson@gmail.com>
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  12. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  13. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  14. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  15. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  16. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  17. THE SOFTWARE.
  18. */
  19. package se.krka.kahlua.stdlib;
  20. import se.krka.kahlua.vm.JavaFunction;
  21. import se.krka.kahlua.vm.LuaCallFrame;
  22. import se.krka.kahlua.vm.LuaState;
  23. import se.krka.kahlua.vm.LuaTable;
  24. import se.krka.kahlua.vm.LuaTableImpl;
  25. public final class StringLib implements JavaFunction {
  26. private static final int SUB = 0;
  27. private static final int CHAR = 1;
  28. private static final int BYTE = 2;
  29. private static final int LOWER = 3;
  30. private static final int UPPER = 4;
  31. private static final int REVERSE = 5;
  32. private static final int FORMAT = 6;
  33. private static final int FIND = 7;
  34. private static final int MATCH = 8;
  35. private static final int GSUB = 9;
  36. private static final int NUM_FUNCTIONS = 10;
  37. private static final boolean[] SPECIALS = new boolean[256];
  38. static {
  39. String s = "^$*+?.([%-";
  40. for (int i = 0; i < s.length(); i++) {
  41. SPECIALS[(int) s.charAt(i)] = true;
  42. }
  43. }
  44. private static final int LUA_MAXCAPTURES = 32;
  45. private static final char L_ESC = '%';
  46. private static final int CAP_UNFINISHED = ( -1 );
  47. private static final int CAP_POSITION = ( -2 );
  48. private static final String[] names;
  49. private static StringLib[] functions;
  50. // NOTE: String.class won't work in J2ME - so this is used as a workaround
  51. public static final Class STRING_CLASS = "".getClass();
  52. static {
  53. names = new String[NUM_FUNCTIONS];
  54. names[SUB] = "sub";
  55. names[CHAR] = "char";
  56. names[BYTE] = "byte";
  57. names[LOWER] = "lower";
  58. names[UPPER] = "upper";
  59. names[REVERSE] = "reverse";
  60. names[FORMAT] = "format";
  61. names[FIND] = "find";
  62. names[MATCH] = "match";
  63. names[GSUB] = "gsub";
  64. functions = new StringLib[NUM_FUNCTIONS];
  65. for (int i = 0; i < NUM_FUNCTIONS; i++) {
  66. functions[i] = new StringLib(i);
  67. }
  68. }
  69. private int methodId;
  70. public StringLib(int index) {
  71. this.methodId = index;
  72. }
  73. public static void register(LuaState state) {
  74. LuaTable string = new LuaTableImpl();
  75. state.getEnvironment().rawset("string", string);
  76. for (int i = 0; i < NUM_FUNCTIONS; i++) {
  77. string.rawset(names[i], functions[i]);
  78. }
  79. string.rawset("__index", string);
  80. state.setClassMetatable(STRING_CLASS, string);
  81. }
  82. public String toString() {
  83. return names[methodId];
  84. }
  85. public int call(LuaCallFrame callFrame, int nArguments) {
  86. switch (methodId) {
  87. case SUB: return sub(callFrame, nArguments);
  88. case CHAR: return stringChar(callFrame, nArguments);
  89. case BYTE: return stringByte(callFrame, nArguments);
  90. case LOWER: return lower(callFrame, nArguments);
  91. case UPPER: return upper(callFrame, nArguments);
  92. case REVERSE: return reverse(callFrame, nArguments);
  93. case FORMAT: return format(callFrame, nArguments);
  94. case FIND: return findAux(callFrame, true);
  95. case MATCH: return findAux(callFrame, false);
  96. case GSUB: return gsub(callFrame, nArguments);
  97. default: return 0; // Should never happen.
  98. }
  99. }
  100. private long unsigned(long v) {
  101. if (v < 0L) {
  102. v += (1L << 32);
  103. }
  104. return v;
  105. }
  106. private int format(LuaCallFrame callFrame, int nArguments) {
  107. String f = (String) BaseLib.getArg(callFrame, 1, BaseLib.TYPE_STRING, names[FORMAT]);
  108. int len = f.length();
  109. int argc = 2;
  110. StringBuffer result = new StringBuffer();
  111. for (int i = 0; i < len; i++) {
  112. char c = f.charAt(i);
  113. if (c == '%') {
  114. i++;
  115. BaseLib.luaAssert(i < len, "incomplete option to 'format'");
  116. c = f.charAt(i);
  117. if (c == '%') {
  118. result.append('%');
  119. } else {
  120. // Detect flags
  121. boolean repr = false;
  122. boolean zeroPadding = false;
  123. boolean leftJustify = false;
  124. boolean showPlus = false;
  125. boolean spaceForSign = false;
  126. flagLoop: while (true) {
  127. switch (c) {
  128. case '-':
  129. leftJustify = true;
  130. break;
  131. case '+':
  132. showPlus = true;
  133. break;
  134. case ' ':
  135. spaceForSign = true;
  136. break;
  137. case '#':
  138. repr = true;
  139. break;
  140. case '0':
  141. zeroPadding = true;
  142. break;
  143. default:
  144. break flagLoop;
  145. }
  146. i++;
  147. BaseLib.luaAssert(i < len, "incomplete option to 'format'");
  148. c = f.charAt(i);
  149. }
  150. // Detect width
  151. int width = 0;
  152. while (c >= '0' && c <= '9') {
  153. width = 10 * width + (int) (c - '0');
  154. i++;
  155. BaseLib.luaAssert(i < len, "incomplete option to 'format'");
  156. c = f.charAt(i);
  157. }
  158. // Detect precision
  159. int precision = 0;
  160. boolean hasPrecision = false;
  161. if (c == '.') {
  162. hasPrecision = true;
  163. i++;
  164. BaseLib.luaAssert(i < len, "incomplete option to 'format'");
  165. c = f.charAt(i);
  166. while (c >= '0' && c <= '9') {
  167. precision = 10 * precision + (int) (c - '0');
  168. i++;
  169. BaseLib.luaAssert(i < len, "incomplete option to 'format'");
  170. c = f.charAt(i);
  171. }
  172. }
  173. if (leftJustify) {
  174. zeroPadding = false;
  175. }
  176. // This will be overriden to space for the appropiate specifiers
  177. // Pass 1: set up various variables needed for each specifier
  178. // This simplifies the second pass by being able to combine several specifiers.
  179. int base = 10;
  180. boolean upperCase = false;
  181. int defaultPrecision = 6; // This is the default for all float numerics
  182. String basePrepend = "";
  183. switch (c) {
  184. // Simple character
  185. case 'c':
  186. zeroPadding = false;
  187. break;
  188. // change base
  189. case 'o':
  190. base = 8;
  191. defaultPrecision = 1;
  192. basePrepend = "0";
  193. break;
  194. case 'x':
  195. base = 16;
  196. defaultPrecision = 1;
  197. basePrepend = "0x";
  198. break;
  199. case 'X':
  200. base = 16;
  201. defaultPrecision = 1;
  202. upperCase = true;
  203. basePrepend = "0X";
  204. break;
  205. // unsigned integer and signed integer
  206. case 'u':
  207. defaultPrecision = 1;
  208. break;
  209. case 'd':
  210. case 'i':
  211. defaultPrecision = 1;
  212. break;
  213. case 'e':
  214. break;
  215. case 'E':
  216. upperCase = true;
  217. break;
  218. case 'g':
  219. break;
  220. case 'G':
  221. upperCase = true;
  222. break;
  223. case 'f':
  224. break;
  225. case 's':
  226. zeroPadding = false;
  227. break;
  228. case 'q':
  229. // %q neither needs nor supports width
  230. width = 0;
  231. break;
  232. default:
  233. throw new RuntimeException("invalid option '%" + c +
  234. "' to 'format'");
  235. }
  236. // Set precision
  237. if (!hasPrecision) {
  238. precision = defaultPrecision;
  239. }
  240. if (hasPrecision && base != 10) {
  241. zeroPadding = false;
  242. }
  243. char padCharacter = zeroPadding ? '0' : ' ';
  244. // extend the string by "width" characters, and delete a subsection of them later to get the correct padding width
  245. int resultStartLength = result.length();
  246. if (!leftJustify) {
  247. extend(result, width, padCharacter);
  248. }
  249. // Detect specifier and compute result
  250. switch (c) {
  251. case 'c':
  252. result.append((char)(getDoubleArg(callFrame, argc)).shortValue());
  253. break;
  254. case 'o':
  255. case 'x':
  256. case 'X':
  257. case 'u': {
  258. long vLong = getDoubleArg(callFrame, argc).longValue();
  259. vLong = unsigned(vLong);
  260. if (repr) {
  261. if (base == 8) {
  262. int digits = 0;
  263. long vLong2 = vLong;
  264. while (vLong2 > 0) {
  265. vLong2 /= 8;
  266. digits++;
  267. }
  268. if (precision <= digits) {
  269. result.append(basePrepend);
  270. }
  271. } else if (base == 16) {
  272. if (vLong != 0) {
  273. result.append(basePrepend);
  274. }
  275. }
  276. }
  277. if (vLong != 0 || precision > 0) {
  278. stringBufferAppend(result, vLong, base, false, precision);
  279. }
  280. break;
  281. }
  282. case 'd':
  283. case 'i': {
  284. Double v = getDoubleArg(callFrame, argc);
  285. long vLong = v.longValue();
  286. if (vLong < 0) {
  287. result.append('-');
  288. vLong = -vLong;
  289. } else if (showPlus) {
  290. result.append('+');
  291. } else if (spaceForSign) {
  292. result.append(' ');
  293. }
  294. if (vLong != 0 || precision > 0) {
  295. stringBufferAppend(result, vLong, base, false, precision);
  296. }
  297. break;
  298. }
  299. case 'e':
  300. case 'E':
  301. case 'f': {
  302. Double v = getDoubleArg(callFrame, argc);
  303. boolean isNaN = v.isInfinite() || v.isNaN();
  304. double vDouble = v.doubleValue();
  305. if (MathLib.isNegative(vDouble)) {
  306. if (!isNaN) {
  307. result.append('-');
  308. }
  309. vDouble = -vDouble;
  310. } else if (showPlus) {
  311. result.append('+');
  312. } else if (spaceForSign) {
  313. result.append(' ');
  314. }
  315. if (isNaN) {
  316. result.append(BaseLib.numberToString(v));
  317. } else {
  318. if (c == 'f') {
  319. appendPrecisionNumber(result, vDouble, precision, repr);
  320. } else {
  321. appendScientificNumber(result, vDouble, precision, repr, false);
  322. }
  323. }
  324. break;
  325. }
  326. case 'g':
  327. case 'G':
  328. {
  329. // Precision is significant digits for %g
  330. if (precision <= 0) {
  331. precision = 1;
  332. }
  333. // first round to correct significant digits (precision),
  334. // then check which formatting to be used.
  335. Double v = getDoubleArg(callFrame, argc);
  336. boolean isNaN = v.isInfinite() || v.isNaN();
  337. double vDouble = v.doubleValue();
  338. if (MathLib.isNegative(vDouble)) {
  339. if (!isNaN) {
  340. result.append('-');
  341. }
  342. vDouble = -vDouble;
  343. } else if (showPlus) {
  344. result.append('+');
  345. } else if (spaceForSign) {
  346. result.append(' ');
  347. }
  348. if (isNaN) {
  349. result.append(BaseLib.numberToString(v));
  350. } else {
  351. double x = MathLib.roundToSignificantNumbers(vDouble, precision);
  352. /*
  353. * Choose %f version if:
  354. * |v| >= 10^(-4)
  355. * AND
  356. * |v| < 10^(precision)
  357. *
  358. * otherwise, choose %e
  359. */
  360. if (x == 0 || (x >= 1e-4 && x < MathLib.ipow(10, precision))) {
  361. int iPartSize;
  362. if (x == 0) {
  363. iPartSize = 1;
  364. } else if (Math.floor(x) == 0) {
  365. iPartSize = 0;
  366. } else {
  367. double longValue = x;
  368. iPartSize = 1;
  369. while (longValue >= 10.0) {
  370. longValue /= 10.0;
  371. iPartSize++;
  372. }
  373. }
  374. // format with %f, with precision significant numbers
  375. appendSignificantNumber(result, x, precision - iPartSize, repr);
  376. } else {
  377. // format with %e, with precision significant numbers, i.e. precision -1 digits
  378. // but skip trailing zeros unless repr
  379. appendScientificNumber(result, x, precision - 1, repr, true);
  380. }
  381. }
  382. break;
  383. }
  384. case 's': {
  385. String s = getStringArg(callFrame, argc);
  386. int n = s.length();
  387. if (hasPrecision) {
  388. n = Math.min(precision, s.length());
  389. }
  390. append(result, s, 0, n);
  391. break;
  392. }
  393. case 'q':
  394. String q = getStringArg(callFrame, argc);
  395. result.append('"');
  396. for (int j = 0; j < q.length(); j++) {
  397. char d = q.charAt(j);
  398. switch (d) {
  399. case '\\': result.append("\\"); break;
  400. case '\n': result.append("\\\n"); break;
  401. case '\r': result.append("\\r"); break;
  402. case '"': result.append("\\\""); break;
  403. default: result.append(d);
  404. }
  405. }
  406. result.append('"');
  407. break;
  408. default:
  409. throw new RuntimeException("Internal error");
  410. }
  411. if (leftJustify) {
  412. int currentResultLength = result.length();
  413. int d = width - (currentResultLength - resultStartLength);
  414. if (d > 0) {
  415. extend(result, d, ' ');
  416. }
  417. } else {
  418. int currentResultLength = result.length();
  419. int d = currentResultLength - resultStartLength - width;
  420. d = Math.min(d, width);
  421. if (d > 0) {
  422. result.delete(resultStartLength, resultStartLength + d);
  423. }
  424. if (zeroPadding) {
  425. int signPos = resultStartLength + (width - d);
  426. char ch = result.charAt(signPos);
  427. if (ch == '+' || ch == '-' || ch == ' ') {
  428. result.setCharAt(signPos, '0');
  429. result.setCharAt(resultStartLength, ch);
  430. }
  431. }
  432. }
  433. if (upperCase) {
  434. stringBufferUpperCase(result, resultStartLength);
  435. }
  436. argc++;
  437. }
  438. } else {
  439. result.append(c);
  440. }
  441. }
  442. callFrame.push(result.toString());
  443. return 1;
  444. }
  445. private void append(StringBuffer buffer, String s, int start, int end) {
  446. for (int i = start; i < end; i++) {
  447. buffer.append(s.charAt(i));
  448. }
  449. }
  450. private void extend(StringBuffer buffer, int extraWidth, char padCharacter) {
  451. int preLength = buffer.length();
  452. buffer.setLength(preLength + extraWidth);
  453. for (int i = extraWidth - 1; i >= 0; i--) {
  454. buffer.setCharAt(preLength + i, padCharacter);
  455. }
  456. }
  457. private void stringBufferUpperCase(StringBuffer buffer, int start) {
  458. int length = buffer.length();
  459. for (int i = start; i < length; i++) {
  460. char c = buffer.charAt(i);
  461. if (c >= 'a' && c <= 'z') {
  462. buffer.setCharAt(i, (char) (c - 32));
  463. }
  464. }
  465. }
  466. private static final char[] digits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
  467. /**
  468. * Precondition: value >= 0
  469. * Precondition: 2 <= base <= 16
  470. * @param sb the stringbuffer to append to
  471. * @param value the value to append
  472. * @param base the base to use when formatting (typically 8, 10 or 16)
  473. * @param minDigits
  474. * @param zeroIsEmpty if the value is 0, should the zero be printed or not?
  475. */
  476. private static void stringBufferAppend(StringBuffer sb, double value, int base, boolean printZero, int minDigits) {
  477. int startPos = sb.length();
  478. while (value > 0 || minDigits > 0) {
  479. double newValue = Math.floor(value / base);
  480. sb.append(digits[(int) (value - (newValue * base))]);
  481. value = newValue;
  482. minDigits--;
  483. }
  484. int endPos = sb.length() - 1;
  485. if (startPos > endPos && printZero) {
  486. sb.append('0');
  487. } else {
  488. // Note that the digits are in reverse order now, so we need to correct it.
  489. // We can't use StringBuffer.reverse because that reverses the entire string
  490. int swapCount = (1 + endPos - startPos) / 2;
  491. for (int i = swapCount - 1; i >= 0; i--) {
  492. int leftPos = startPos + i;
  493. int rightPos = endPos - i;
  494. char left = sb.charAt(leftPos);
  495. char right = sb.charAt(rightPos);
  496. sb.setCharAt(leftPos, right);
  497. sb.setCharAt(rightPos, left);
  498. }
  499. }
  500. }
  501. /**
  502. * Only works with non-negative numbers
  503. * @param buffer
  504. * @param number
  505. * @param precision
  506. * @param requirePeriod
  507. */
  508. private void appendPrecisionNumber(StringBuffer buffer, double number, int precision, boolean requirePeriod) {
  509. number = MathLib.roundToPrecision(number, precision);
  510. double iPart = Math.floor(number);
  511. double fPart = number - iPart;
  512. for (int i = 0; i < precision; i++) {
  513. fPart *= 10.0;
  514. }
  515. fPart = MathLib.round(iPart + fPart) - iPart;
  516. stringBufferAppend(buffer, iPart, 10, true, 0);
  517. if (requirePeriod || precision > 0) {
  518. buffer.append('.');
  519. }
  520. stringBufferAppend(buffer, fPart, 10, false, precision);
  521. }
  522. /**
  523. * Only works with non-negative numbers
  524. * @param buffer
  525. * @param number
  526. * @param significantDecimals
  527. * @param includeTrailingZeros
  528. */
  529. private void appendSignificantNumber(StringBuffer buffer, double number, int significantDecimals, boolean includeTrailingZeros) {
  530. double iPart = Math.floor(number);
  531. stringBufferAppend(buffer, iPart, 10, true, 0);
  532. double fPart = MathLib.roundToSignificantNumbers(number - iPart, significantDecimals);
  533. boolean hasNotStarted = iPart == 0 && fPart != 0;
  534. int zeroPaddingBefore = 0;
  535. int scanLength = significantDecimals;
  536. for (int i = 0; i < scanLength; i++) {
  537. fPart *= 10.0;
  538. if (Math.floor(fPart) == 0 && fPart != 0) {
  539. zeroPaddingBefore++;
  540. if (hasNotStarted) {
  541. scanLength++;
  542. }
  543. }
  544. }
  545. fPart = MathLib.round(fPart);
  546. if (!includeTrailingZeros) {
  547. while (fPart > 0 && (fPart % 10) == 0) {
  548. fPart /= 10;
  549. significantDecimals--;
  550. }
  551. }
  552. buffer.append('.');
  553. int periodPos = buffer.length();
  554. extend(buffer, zeroPaddingBefore, '0');
  555. int prePos = buffer.length();
  556. stringBufferAppend(buffer, fPart, 10, false, 0);
  557. int postPos = buffer.length();
  558. int len = postPos - prePos;
  559. if (includeTrailingZeros && len < significantDecimals) {
  560. int padRightSize = significantDecimals - len - zeroPaddingBefore;
  561. extend(buffer, padRightSize, '0');
  562. }
  563. if (!includeTrailingZeros && periodPos == buffer.length()) {
  564. buffer.delete(periodPos - 1, buffer.length());
  565. }
  566. }
  567. private void appendScientificNumber(StringBuffer buffer, double x, int precision, boolean repr, boolean useSignificantNumbers) {
  568. int exponent = 0;
  569. // Run two passes to handle cases such as %.2e with the value 95.
  570. for (int i = 0; i < 2; i++) {
  571. if (x >= 1.0) {
  572. while (x >= 10.0) {
  573. x /= 10.0;
  574. exponent++;
  575. }
  576. } else {
  577. while (x > 0 && x < 1.0) {
  578. x *= 10.0;
  579. exponent--;
  580. }
  581. }
  582. x = MathLib.roundToPrecision(x, precision);
  583. }
  584. int absExponent = Math.abs(exponent);
  585. char expSign;
  586. if (exponent >= 0) {
  587. expSign = '+';
  588. } else {
  589. expSign = '-';
  590. }
  591. if (useSignificantNumbers) {
  592. appendSignificantNumber(buffer, x, precision, repr);
  593. } else {
  594. appendPrecisionNumber(buffer, x, precision, repr);
  595. }
  596. buffer.append('e');
  597. buffer.append(expSign);
  598. stringBufferAppend(buffer, absExponent, 10, true, 2);
  599. }
  600. private String getStringArg(LuaCallFrame callFrame, int argc) {
  601. return getStringArg(callFrame, argc, names[FORMAT]);
  602. }
  603. private String getStringArg(LuaCallFrame callFrame, int argc, String funcname) {
  604. return (String) BaseLib.getArg(callFrame, argc, BaseLib.TYPE_STRING, funcname);
  605. }
  606. private Double getDoubleArg(LuaCallFrame callFrame, int argc) {
  607. return getDoubleArg(callFrame, argc, names[FORMAT]);
  608. }
  609. private Double getDoubleArg(LuaCallFrame callFrame, int argc, String funcname) {
  610. return (Double)BaseLib.getArg(callFrame, argc, BaseLib.TYPE_NUMBER, funcname);
  611. }
  612. private int lower(LuaCallFrame callFrame, int nArguments) {
  613. BaseLib.luaAssert(nArguments >= 1, "not enough arguments");
  614. String s = getStringArg(callFrame,1,names[LOWER]);
  615. callFrame.push(s.toLowerCase());
  616. return 1;
  617. }
  618. private int upper(LuaCallFrame callFrame, int nArguments) {
  619. BaseLib.luaAssert(nArguments >= 1, "not enough arguments");
  620. String s = getStringArg(callFrame,1,names[UPPER]);
  621. callFrame.push(s.toUpperCase());
  622. return 1;
  623. }
  624. private int reverse(LuaCallFrame callFrame, int nArguments) {
  625. BaseLib.luaAssert(nArguments >= 1, "not enough arguments");
  626. String s = getStringArg(callFrame, 1, names[REVERSE]);
  627. s = new StringBuffer(s).reverse().toString();
  628. callFrame.push(s);
  629. return 1;
  630. }
  631. private int stringByte(LuaCallFrame callFrame, int nArguments) {
  632. BaseLib.luaAssert(nArguments >= 1, "not enough arguments");
  633. String s = getStringArg(callFrame, 1, names[BYTE]);
  634. Double di = null;
  635. Double dj = null;
  636. if (nArguments >= 2) {
  637. di = getDoubleArg(callFrame, 2, names[BYTE]);
  638. if (nArguments >= 3) {
  639. dj = getDoubleArg(callFrame, 3, names[BYTE]);
  640. }
  641. }
  642. double di2 = 1;
  643. if (di != null) {
  644. di2 = LuaState.fromDouble(di);
  645. }
  646. double dj2 = di2;
  647. if (dj != null) {
  648. dj2 = LuaState.fromDouble(dj);
  649. }
  650. int ii = (int) di2, ij = (int) dj2;
  651. int len = s.length();
  652. if (ii < 0) {
  653. ii += len + 1;
  654. }
  655. if (ii <= 0) {
  656. ii = 1;
  657. }
  658. if (ij < 0) {
  659. ij += len + 1;
  660. } else if (ij > len) {
  661. ij = len;
  662. }
  663. int nReturns = 1 +ij - ii;
  664. if (nReturns <= 0) {
  665. return 0;
  666. }
  667. callFrame.setTop(nReturns);
  668. int offset = ii - 1;
  669. for (int i = 0; i < nReturns; i++) {
  670. char c = s.charAt(offset + i);
  671. callFrame.set(i, new Double((double) c));
  672. }
  673. return nReturns;
  674. }
  675. private int stringChar(LuaCallFrame callFrame, int nArguments) {
  676. StringBuffer sb = new StringBuffer();
  677. for (int i = 0; i < nArguments; i++) {
  678. int num = getDoubleArg(callFrame, i + 1, names[CHAR]).intValue();
  679. sb.append((char) num);
  680. }
  681. return callFrame.push(sb.toString());
  682. }
  683. private int sub(LuaCallFrame callFrame, int nArguments) {
  684. String s = getStringArg(callFrame, 1, names[SUB]);
  685. double start = getDoubleArg(callFrame, 2, names[SUB]).doubleValue();
  686. double end = -1;
  687. if (nArguments >= 3) {
  688. end = getDoubleArg(callFrame, 3, names[SUB]).doubleValue();
  689. }
  690. String res;
  691. int istart = (int) start;
  692. int iend = (int) end;
  693. int len = s.length();
  694. if (istart < 0) {
  695. istart = Math.max(len + istart + 1, 1);
  696. } else if (istart == 0) {
  697. istart = 1;
  698. }
  699. if (iend < 0) {
  700. iend = Math.max(0, iend + len + 1);
  701. } else if (iend > len) {
  702. iend = len;
  703. }
  704. if (istart > iend) {
  705. return callFrame.push("");
  706. }
  707. res = s.substring(istart - 1, iend);
  708. return callFrame.push(res);
  709. }
  710. /* Pattern Matching
  711. * Original code that this was adapted from is copyright (c) 2008 groundspeak, inc.
  712. */
  713. public static class MatchState {
  714. public MatchState () {
  715. capture = new Capture[ LUA_MAXCAPTURES ];
  716. for ( int i = 0; i < LUA_MAXCAPTURES; i ++ ) {
  717. capture[i] = new Capture ();
  718. }
  719. }
  720. public StringPointer src_init; /* init of source string */
  721. public int endIndex; /* end (`\0') of source string */
  722. public LuaCallFrame callFrame;
  723. public int level; /* total number of captures (finished or unfinished) */
  724. public Capture[] capture;
  725. public static class Capture {
  726. public StringPointer init;
  727. public int len;
  728. }
  729. public Object[] getCaptures() {
  730. if (level <= 0) {
  731. return null;
  732. }
  733. Object[] caps = new String[level];
  734. for (int i = 0; i < level; i++) {
  735. if (capture[i].len == CAP_POSITION) {
  736. caps[i] = new Double(src_init.length() - capture[i].init.length() + 1);
  737. } else {
  738. caps[i] = capture[i].init.getString().substring(0, capture[i].len);
  739. }
  740. }
  741. return caps;
  742. }
  743. }
  744. public static class StringPointer {
  745. private String string;
  746. private int index = 0;
  747. public StringPointer(String original) {
  748. this.string = original;
  749. }
  750. public StringPointer(String original, int index) {
  751. this.string = original;
  752. this.index = index;
  753. }
  754. public StringPointer getClone() {
  755. StringPointer newSP = new StringPointer( this.getOriginalString(), this.getIndex() );
  756. return newSP;
  757. }
  758. public int getIndex () {
  759. return index;
  760. }
  761. public void setIndex ( int ind ) {
  762. index = ind;
  763. }
  764. public String getOriginalString () {
  765. return string;
  766. }
  767. public void setOriginalString(String orStr) {
  768. string = orStr;
  769. }
  770. public String getString() {
  771. return getString(0);
  772. }
  773. public String getString(int i) {
  774. return string.substring ( index + i, string.length () );
  775. }
  776. public char getChar() {
  777. return getChar(0);
  778. }
  779. public char getChar(int strIndex) {
  780. if ( index + strIndex >= string.length () )
  781. return '\0';
  782. else
  783. return string.charAt ( index + strIndex );
  784. }
  785. public int length() {
  786. return string.length () - index;
  787. }
  788. public int postIncrStringI ( int num ) {
  789. int oldIndex = index;
  790. index += num;
  791. return oldIndex;
  792. }
  793. public int preIncrStringI ( int num ) {
  794. index += num;
  795. return index;
  796. }
  797. public char postIncrString ( int num ) {
  798. char c = getChar();
  799. index += num;
  800. return c;
  801. }
  802. public char preIncrString ( int num ) {
  803. index += num;
  804. return getChar();
  805. }
  806. public int compareTo(StringPointer cmp, int len) {
  807. return this.string.substring(this.index,this.index+len).compareTo(
  808. cmp.string.substring(cmp.index, cmp.index+len));
  809. }
  810. }
  811. private static Object push_onecapture ( MatchState ms, int i, StringPointer s, StringPointer e ) {
  812. if (i >= ms.level) {
  813. if ( i == 0 ) { // ms->level == 0, too
  814. String res = s.string.substring(s.index, e.index);
  815. ms.callFrame.push(res);
  816. return res;
  817. } else {
  818. throw new RuntimeException("invalid capture index");
  819. }
  820. } else {
  821. int l = ms.capture[i].len;
  822. if (l == CAP_UNFINISHED) {
  823. throw new RuntimeException("unfinished capture");
  824. } else if (l == CAP_POSITION) {
  825. Double res = new Double(ms.src_init.length() - ms.capture[i].init.length() + 1);
  826. ms.callFrame.push(res);
  827. return res;
  828. } else {
  829. int index = ms.capture[i].init.index;
  830. String res = ms.capture[i].init.string.substring(index, index+l);
  831. ms.callFrame.push(res);
  832. return res;
  833. }
  834. }
  835. }
  836. private static int push_captures ( MatchState ms, StringPointer s, StringPointer e ) {
  837. int nlevels = ( ms.level == 0 && s != null ) ? 1 : ms.level;
  838. BaseLib.luaAssert(nlevels <= LUA_MAXCAPTURES, "too many captures");
  839. for (int i = 0; i < nlevels; i++) {
  840. push_onecapture (ms, i, s, e);
  841. }
  842. return nlevels; // number of strings pushed
  843. }
  844. private static boolean noSpecialChars(String pattern) {
  845. for (int i = 0; i < pattern.length(); i++) {
  846. char c = pattern.charAt(i);
  847. if (c < 256 && SPECIALS[c]) {
  848. return false;
  849. }
  850. }
  851. return true;
  852. }
  853. private static int findAux (LuaCallFrame callFrame, boolean find ) {
  854. String f = find ? names[FIND] : names[MATCH];
  855. String source = (String) BaseLib.getArg(callFrame, 1, BaseLib.TYPE_STRING, f);
  856. String pattern = (String) BaseLib.getArg(callFrame, 2, BaseLib.TYPE_STRING, f);
  857. Double i = ((Double)(BaseLib.getOptArg(callFrame, 3, BaseLib.TYPE_NUMBER)));
  858. boolean plain = LuaState.boolEval(BaseLib.getOptArg(callFrame, 4, BaseLib.TYPE_BOOLEAN));
  859. int init = (i == null ? 0 : i.intValue() - 1);
  860. if ( init < 0 ) {
  861. // negative numbers count back from the end of the string.
  862. init += source.length();
  863. if ( init < 0 ) {
  864. init = 0; // if we are still negative, just start at the beginning.
  865. }
  866. } else if ( init > source.length() ) {
  867. init = source.length();
  868. }
  869. if ( find && ( plain || noSpecialChars(pattern) ) ) { // explicit plain request or no special characters?
  870. // do a plain search
  871. int pos = source.indexOf(pattern, init);
  872. if ( pos > -1 ) {
  873. return callFrame.push(LuaState.toDouble(pos + 1), LuaState.toDouble(pos + pattern.length()));
  874. }
  875. } else {
  876. StringPointer s = new StringPointer(source);
  877. StringPointer p = new StringPointer(pattern);
  878. MatchState ms = new MatchState ();
  879. boolean anchor = false;
  880. if ( p.getChar () == '^' ) {
  881. anchor = true;
  882. p.postIncrString ( 1 );
  883. }
  884. StringPointer s1 = s.getClone();
  885. s1.postIncrString ( init );
  886. ms.callFrame = callFrame;
  887. ms.src_init = s.getClone();
  888. ms.endIndex = s.getString().length();
  889. do {
  890. StringPointer res;
  891. ms.level = 0;
  892. if ( ( res = match ( ms, s1, p ) ) != null ) {
  893. if ( find ) {
  894. return callFrame.push(new Double(s.length () - s1.length () + 1), new Double(s.length () - res.length ())) +
  895. push_captures ( ms, null, null );
  896. } else {
  897. return push_captures ( ms, s1, res );
  898. }
  899. }
  900. } while ( s1.postIncrStringI ( 1 ) < ms.endIndex && !anchor );
  901. }
  902. return callFrame.pushNil(); // not found
  903. }
  904. private static StringPointer startCapture ( MatchState ms, StringPointer s, StringPointer p, int what ) {
  905. StringPointer res;
  906. int level = ms.level;
  907. BaseLib.luaAssert(level < LUA_MAXCAPTURES, "too many captures");
  908. ms.capture[level].init = s.getClone();
  909. ms.capture[level].init.setIndex ( s.getIndex () );
  910. ms.capture[level].len = what;
  911. ms.level = level + 1;
  912. if ( ( res = match ( ms, s, p ) ) == null ) /* match failed? */ {
  913. ms.level --; /* undo capture */
  914. }
  915. return res;
  916. }
  917. private static int captureToClose ( MatchState ms ) {
  918. int level = ms.level;
  919. for ( level --; level >= 0; level -- ) {
  920. if ( ms.capture[level].len == CAP_UNFINISHED ) {
  921. return level;
  922. }
  923. }
  924. throw new RuntimeException("invalid pattern capture");
  925. }
  926. private static StringPointer endCapture ( MatchState ms, StringPointer s, StringPointer p ) {
  927. int l = captureToClose ( ms );
  928. StringPointer res;
  929. ms.capture[l].len = ms.capture[l].init.length () - s.length (); /* close capture */
  930. if ( ( res = match ( ms, s, p ) ) == null ) /* match failed? */ {
  931. ms.capture[l].len = CAP_UNFINISHED; /* undo capture */
  932. }
  933. return res;
  934. }
  935. private static int checkCapture ( MatchState ms, int l ) {
  936. l -= '1'; // convert chars 1-9 to actual ints 1-9
  937. BaseLib.luaAssert(l < 0 || l >= ms.level || ms.capture[l].len == CAP_UNFINISHED,
  938. "invalid capture index");
  939. return l;
  940. }
  941. private static StringPointer matchCapture ( MatchState ms, StringPointer s, int l ) {
  942. int len;
  943. l = checkCapture ( ms, l );
  944. len = ms.capture[l].len;
  945. if ( ( ms.endIndex - s.length () ) >= len && ms.capture[l].init.compareTo(s, len) == 0 ) {
  946. StringPointer sp = s.getClone();
  947. sp.postIncrString ( len );
  948. return sp;
  949. }
  950. else {
  951. return null;
  952. }
  953. }
  954. private static StringPointer matchBalance ( MatchState ms, StringPointer ss, StringPointer p ) {
  955. BaseLib.luaAssert(!(p.getChar () == 0 || p.getChar ( 1 ) == 0), "unbalanced pattern");
  956. StringPointer s = ss.getClone();
  957. if ( s.getChar () != p.getChar () ) {
  958. return null;
  959. } else {
  960. int b = p.getChar ();
  961. int e = p.getChar ( 1 );
  962. int cont = 1;
  963. while ( s.preIncrStringI ( 1 ) < ms.endIndex ) {
  964. if ( s.getChar () == e ) {
  965. if ( -- cont == 0 ) {
  966. StringPointer sp = s.getClone();
  967. sp.postIncrString ( 1 );
  968. return sp;
  969. }
  970. } else if ( s.getChar () == b ) {
  971. cont ++;
  972. }
  973. }
  974. }
  975. return null; /* string ends out of balance */
  976. }
  977. private static StringPointer classEnd ( MatchState ms, StringPointer pp ) {
  978. StringPointer p = pp.getClone();
  979. switch ( p.postIncrString ( 1 ) ) {
  980. case L_ESC: {
  981. BaseLib.luaAssert(p.getChar () != '\0', "malformed pattern (ends with '%')");
  982. p.postIncrString ( 1 );
  983. return p;
  984. }
  985. case '[': {
  986. if ( p.getChar () == '^' ) {
  987. p.postIncrString ( 1 );
  988. }
  989. do { // look for a `]'
  990. BaseLib.luaAssert(p.getChar () != '\0', "malformed pattern (missing ']')");
  991. if ( p.postIncrString ( 1 ) == L_ESC && p.getChar () != '\0' ) {
  992. p.postIncrString ( 1 ); // skip escapes (e.g. `%]')
  993. }
  994. } while ( p.getChar () != ']' );
  995. p.postIncrString ( 1 );
  996. return p;
  997. }
  998. default: {
  999. return p;
  1000. }
  1001. }
  1002. }
  1003. private static boolean singleMatch ( char c, StringPointer p, StringPointer ep ) {
  1004. switch ( p.getChar () ) {
  1005. case '.':
  1006. return true; // matches any char
  1007. case L_ESC:
  1008. return matchClass ( p.getChar ( 1 ), c );
  1009. case '[': {
  1010. StringPointer sp = ep.getClone();
  1011. sp.postIncrString ( -1 );
  1012. return matchBracketClass ( c, p, sp );
  1013. }
  1014. default:
  1015. return ( p.getChar () == c );
  1016. }
  1017. }
  1018. private static StringPointer minExpand ( MatchState ms, StringPointer ss, StringPointer p, StringPointer ep ) {
  1019. StringPointer sp = ep.getClone();
  1020. StringPointer s = ss.getClone();
  1021. sp.postIncrString ( 1 );
  1022. while (true) {
  1023. StringPointer res = match ( ms, s, sp );
  1024. if ( res != null ) {
  1025. return res;
  1026. } else if ( s.getIndex () < ms.endIndex && singleMatch ( s.getChar (), p, ep ) ) {
  1027. s.postIncrString ( 1 ); // try with one more repetition
  1028. } else {
  1029. return null;
  1030. }
  1031. }
  1032. }
  1033. private static StringPointer maxExpand(MatchState ms, StringPointer s, StringPointer p, StringPointer ep) {
  1034. int i = 0; // counts maximum expand for item
  1035. while (s.getIndex () + i < ms.endIndex && singleMatch(s.getChar(i), p, ep)) {
  1036. i ++;
  1037. }
  1038. // keeps trying to match with the maximum repetitions
  1039. while (i >= 0) {
  1040. StringPointer sp1 = s.getClone();
  1041. sp1.postIncrString(i);
  1042. StringPointer sp2 = ep.getClone();
  1043. sp2.postIncrString(1);
  1044. StringPointer res = match(ms, sp1, sp2);
  1045. if (res != null) {
  1046. return res;
  1047. }
  1048. i --; // else didn't match; reduce 1 repetition to try again
  1049. }
  1050. return null;
  1051. }
  1052. private static boolean matchBracketClass(char c, StringPointer pp, StringPointer ecc) {
  1053. StringPointer p = pp.getClone();
  1054. StringPointer ec = ecc.getClone();
  1055. boolean sig = true;
  1056. if (p.getChar(1) == '^') {
  1057. sig = false;
  1058. p.postIncrString(1); // skip the `^'
  1059. }
  1060. while (p.preIncrStringI(1) < ec.getIndex()) {
  1061. if (p.getChar() == L_ESC) {
  1062. p.postIncrString(1);
  1063. if (matchClass(p.getChar(), c)) {
  1064. return sig;
  1065. }
  1066. } else if ((p.getChar(1) == '-') && (p.getIndex() + 2 < ec.getIndex())) {
  1067. p.postIncrString(2);
  1068. if (p.getChar(-2) <= c && c <= p.getChar()) {
  1069. return sig;
  1070. }
  1071. } else if (p.getChar () == c) {
  1072. return sig;
  1073. }
  1074. }
  1075. return !sig;
  1076. }
  1077. private static StringPointer match(MatchState ms, StringPointer ss, StringPointer pp) {
  1078. StringPointer s = ss.getClone();
  1079. StringPointer p = pp.getClone();
  1080. boolean isContinue = true;
  1081. boolean isDefault = false;
  1082. while (isContinue) {
  1083. isContinue = false;
  1084. isDefault = false;
  1085. switch (p.getChar()) {
  1086. case '(': { // start capture
  1087. StringPointer p1 = p.getClone();
  1088. if (p.getChar(1) == ')') { // position capture?
  1089. p1.postIncrString(2);
  1090. return startCapture(ms, s, p1, CAP_POSITION);
  1091. } else {
  1092. p1.postIncrString(1);
  1093. return startCapture(ms, s, p1, CAP_UNFINISHED);
  1094. }
  1095. }
  1096. case ')': { // end capture
  1097. StringPointer p1 = p.getClone();
  1098. p1.postIncrString(1);
  1099. return endCapture(ms, s, p1);
  1100. }
  1101. case L_ESC: {
  1102. switch (p.getChar(1)) {
  1103. case 'b': { // balanced string?
  1104. StringPointer p1 = p.getClone();
  1105. p1.postIncrString(2);
  1106. s = matchBalance(ms, s, p1);
  1107. if (s == null) {
  1108. return null;
  1109. }
  1110. p.postIncrString(4);
  1111. isContinue = true;
  1112. continue; // else return match(ms, s, p+4);
  1113. }
  1114. case 'f': { // frontier?
  1115. p.postIncrString (2);
  1116. BaseLib.luaAssert(p.getChar() == '[' , "missing '[' after '%%f' in pattern");
  1117. StringPointer ep = classEnd(ms, p); // points to what is next
  1118. char previous = (s.getIndex() == ms.src_init.getIndex()) ? '\0' : s.getChar(-1);
  1119. StringPointer ep1 = ep.getClone();
  1120. ep1.postIncrString(-1);
  1121. if (matchBracketClass(previous, p, ep1) || !matchBracketClass(s.getChar(), p, ep1)) {
  1122. return null;
  1123. }
  1124. p = ep;
  1125. isContinue = true;
  1126. continue; // else return match(ms, s, ep);
  1127. }
  1128. default: {
  1129. if (Character.isDigit(p.getChar(1))) { // capture results (%0-%9)?
  1130. s = matchCapture(ms, s, p.getChar(1));
  1131. if (s == null) {
  1132. return null;
  1133. }
  1134. p.postIncrString(2);
  1135. isContinue = true;
  1136. continue; // else return match(ms, s, p+2)
  1137. }
  1138. isDefault = true; // case default
  1139. }
  1140. }
  1141. break;
  1142. }
  1143. case '\0': { // end of pattern
  1144. return s; // match succeeded
  1145. }
  1146. case '$': {
  1147. if (p.getChar(1) == '\0') { // is the `$' the last char in pattern?
  1148. return (s.getIndex() == ms.endIndex) ? s : null; // check end of string
  1149. }
  1150. }
  1151. default: { // it is a pattern item
  1152. isDefault = true;
  1153. }
  1154. }
  1155. if (isDefault) { // it is a pattern item
  1156. isDefault = false;
  1157. StringPointer ep = classEnd(ms, p); // points to what is next
  1158. boolean m = (s.getIndex () < ms.endIndex && singleMatch(s.getChar(), p, ep));
  1159. switch (ep.getChar()) {
  1160. case '?': { // optional
  1161. StringPointer res;
  1162. StringPointer s1 = s.getClone();
  1163. s1.postIncrString ( 1 );
  1164. StringPointer ep1 = ep.getClone();
  1165. ep1.postIncrString ( 1 );
  1166. if (m && ((res = match(ms, s1, ep1)) != null)) {
  1167. return res;
  1168. }
  1169. p = ep;
  1170. p.postIncrString(1);
  1171. isContinue = true;
  1172. continue; // else return match(ms, s, ep+1);
  1173. }
  1174. case '*': { // 0 or more repetitions
  1175. return maxExpand(ms, s, p, ep);
  1176. }
  1177. case '+': { // 1 or more repetitions
  1178. StringPointer s1 = s.getClone();
  1179. s1.postIncrString(1);
  1180. return (m ? maxExpand(ms, s1, p, ep) : null);
  1181. }
  1182. case '-': { // 0 or more repetitions (minimum)
  1183. return minExpand(ms, s, p, ep);
  1184. }
  1185. default: {
  1186. if (!m) {
  1187. return null;
  1188. }
  1189. s.postIncrString(1);
  1190. p = ep;
  1191. isContinue = true;
  1192. continue; // else return match(ms, s+1, ep);
  1193. }
  1194. }
  1195. }
  1196. }
  1197. return null;
  1198. }
  1199. private static boolean matchClass(char classIdentifier, char c) {
  1200. boolean res;
  1201. char lowerClassIdentifier = Character.toLowerCase(classIdentifier);
  1202. switch (lowerClassIdentifier) {
  1203. case 'a': res = Character.isLowerCase(c) || Character.isUpperCase(c); break;
  1204. case 'c': res = isControl(c); break;
  1205. case 'd': res = Character.isDigit(c); break;
  1206. case 'l': res = Character.isLowerCase(c); break;
  1207. case 'p': res = isPunct(c); break;
  1208. case 's': res = isSpace(c); break;
  1209. case 'u': res = Character.isUpperCase(c); break;
  1210. case 'w': res = Character.isLowerCase(c) || Character.isUpperCase(c) || Character.isDigit(c); break;
  1211. case 'x': res = isHex(c); break;
  1212. case 'z': res = (c == 0); break;
  1213. default: return (classIdentifier == c);
  1214. }
  1215. return (lowerClassIdentifier == classIdentifier) == res;
  1216. }
  1217. private static boolean isPunct(char c) {
  1218. return ( c >= 0x21 && c <= 0x2F ) ||
  1219. ( c >= 0x3a && c <= 0x40 ) ||
  1220. ( c >= 0x5B && c <= 0x60 ) ||
  1221. ( c >= 0x7B && c <= 0x7E );
  1222. }
  1223. private static boolean isSpace(char c) {
  1224. return ( c >= 0x09 && c <= 0x0D ) || c == 0x20 ;
  1225. }
  1226. private static boolean isControl(char c) {
  1227. return ( c >= 0x00 && c <= 0x1f ) || c == 0x7f;
  1228. }
  1229. private static boolean isHex(char c) {
  1230. return ( c >= '0' && c <= '9' ) || ( c >= 'a' && c <= 'f' ) || ( c >= 'A' && c <= 'F' );
  1231. }
  1232. private static int gsub(LuaCallFrame cf, int nargs) {
  1233. String srcTemp = (String)BaseLib.getArg(cf, 1, BaseLib.TYPE_STRING, names[GSUB]);
  1234. String pTemp = (String)BaseLib.getArg(cf, 2, BaseLib.TYPE_STRING, names[GSUB]);
  1235. Object repl = BaseLib.getArg(cf, 3, null, names[GSUB]);
  1236. {
  1237. String tmp = BaseLib.rawTostring(repl);
  1238. if (tmp != null) {
  1239. repl = tmp;
  1240. }
  1241. }
  1242. Double num = (Double)BaseLib.getOptArg(cf, 4, BaseLib.TYPE_NUMBER);
  1243. // if i isn't supplied, we want to substitute all occurrences of the pattern
  1244. int maxSubstitutions = (num == null) ? Integer.MAX_VALUE : num.intValue();
  1245. StringPointer pattern = new StringPointer (pTemp);
  1246. StringPointer src = new StringPointer (srcTemp);
  1247. boolean anchor = false;
  1248. if (pattern.getChar() == '^') {
  1249. anchor = true;
  1250. pattern.postIncrString ( 1 );
  1251. }
  1252. String replType = BaseLib.type(repl);
  1253. if (!(replType == BaseLib.TYPE_FUNCTION ||
  1254. replType == BaseLib.TYPE_STRING ||
  1255. replType == BaseLib.TYPE_TABLE)) {
  1256. BaseLib.fail(("string/function/table expected, got "+replType));
  1257. }
  1258. MatchState ms = new MatchState ();
  1259. ms.callFrame = cf;
  1260. ms.src_init = src.getClone();
  1261. ms.endIndex = src.length();
  1262. int n = 0;
  1263. StringBuffer b = new StringBuffer();
  1264. StringPointer e = null;
  1265. while (n < maxSubstitutions) {
  1266. ms.level = 0;
  1267. e = match(ms, src, pattern);
  1268. if (e != null) {
  1269. n++;
  1270. addValue(ms, repl, b, src, e);
  1271. }
  1272. if (e != null && e.getIndex() > src.getIndex()) { // non empty match?
  1273. src.setIndex (e.getIndex()); // skip it
  1274. } else if (src.getIndex() < ms.endIndex) {
  1275. b.append(src.postIncrString(1));
  1276. } else {
  1277. break;
  1278. }
  1279. if (anchor) {
  1280. break;
  1281. }
  1282. }
  1283. return cf.push(b.append(src.getString()).toString(), new Double(n));
  1284. }
  1285. private static void addValue(MatchState ms, Object repl, StringBuffer b, StringPointer src, StringPointer e) {
  1286. String type = BaseLib.type(repl);
  1287. if (type == BaseLib.TYPE_NUMBER || type == BaseLib.TYPE_STRING) {
  1288. b.append(addString (ms, repl, src, e));
  1289. } else {
  1290. String match = src.getString().substring(0, e.getIndex() - src.getIndex());
  1291. Object[] captures = ms.getCaptures();
  1292. if (captures != null) {
  1293. match = BaseLib.rawTostring(captures[0]);
  1294. }
  1295. Object res = null;
  1296. if (type == BaseLib.TYPE_FUNCTION) {
  1297. res = ms.callFrame.thread.state.call(repl, match, null, null);
  1298. } else if (type == BaseLib.TYPE_TABLE) {
  1299. res = ((LuaTable)repl).rawget(match);
  1300. }
  1301. if (res == null) {
  1302. res = match;
  1303. }
  1304. b.append(BaseLib.rawTostring(res));
  1305. }
  1306. }
  1307. private static String addString(MatchState ms, Object repl, StringPointer s, StringPointer e) {
  1308. String replTemp = BaseLib.tostring(repl, ms.callFrame.thread.state);
  1309. StringPointer replStr = new StringPointer (replTemp);
  1310. StringBuffer buf = new StringBuffer();
  1311. for (int i = 0; i < replTemp.length(); i++) {
  1312. if (replStr.getChar ( i ) != L_ESC) {
  1313. buf.append(replStr.getChar(i));
  1314. } else {
  1315. i ++; // skip ESC
  1316. if (!Character.isDigit(replStr.getChar(i))) {
  1317. buf.append(replStr.getChar(i));
  1318. } else if (replStr.getChar(i) == '0') {
  1319. String str = s.getString ();
  1320. int len = s.length() - e.length();
  1321. if (len > str.length() ) {
  1322. len = str.length();
  1323. }
  1324. buf.append(str.substring(0, len));
  1325. } else {
  1326. int captureIndex = replStr.getChar(i) - '1';
  1327. Object[] captures = ms.getCaptures();
  1328. if (captures == null || captureIndex > ms.level) {
  1329. throw new RuntimeException("invalid capture index");
  1330. }
  1331. Object o = captures[captureIndex];
  1332. if(o instanceof Double) {
  1333. Double doubleValue = ((Double)o);
  1334. if( doubleValue.doubleValue() - doubleValue.intValue() == 0 ) {
  1335. buf.append(String.valueOf(((Double)o).intValue()));
  1336. } else {
  1337. buf.append(String.valueOf(((Double)o).doubleValue()));
  1338. }
  1339. } else {
  1340. buf.append(o);
  1341. }
  1342. }
  1343. }
  1344. }
  1345. return buf.toString();
  1346. }
  1347. }