001: /*
002: * StringFunctions.java
003: *
004: * Copyright (C) 2003 Peter Graves
005: * $Id: StringFunctions.java,v 1.6 2003/11/15 11:03:30 beedlem Exp $
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License
009: * as published by the Free Software Foundation; either version 2
010: * of the License, or (at your option) any later version.
011: *
012: * This program is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
015: * GNU General Public License for more details.
016: *
017: * You should have received a copy of the GNU General Public License
018: * along with this program; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
020: */
021:
022: package org.armedbear.lisp;
023:
024: public final class StringFunctions extends Lisp {
025: // ### %string=
026: // Case sensitive.
027: private static final Primitive _STRING_EQUAL = new Primitive(
028: "%string=", PACKAGE_SYS, true) {
029: public LispObject execute(LispObject[] args)
030: throws ConditionThrowable {
031: if (args.length != 6)
032: throw new ConditionThrowable(
033: new WrongNumberOfArgumentsException(this ));
034: char[] array1 = string(args[0]).chars();
035: char[] array2 = string(args[1]).chars();
036: int start1 = Fixnum.getInt(args[2]);
037: int end1 = Fixnum.getInt(args[3]);
038: int start2 = Fixnum.getInt(args[4]);
039: int end2 = Fixnum.getInt(args[5]);
040: if ((end1 - start1) != (end2 - start2))
041: return NIL;
042: try {
043: for (int i = start1, j = start2; i < end1 && j < end2; i++, j++) {
044: if (array1[i] != array2[j])
045: return NIL;
046: }
047: } catch (ArrayIndexOutOfBoundsException e) {
048: return NIL;
049: }
050: return T;
051: }
052: };
053:
054: // ### %string/=
055: // Case sensitive.
056: private static final Primitive _STRING_NOT_EQUAL = new Primitive(
057: "%string/=", PACKAGE_SYS, true) {
058: public LispObject execute(LispObject[] args)
059: throws ConditionThrowable {
060: if (args.length != 6)
061: throw new ConditionThrowable(
062: new WrongNumberOfArgumentsException(this ));
063: char[] array1 = string(args[0]).chars();
064: char[] array2 = string(args[1]).chars();
065: int start1 = Fixnum.getInt(args[2]);
066: int end1 = Fixnum.getInt(args[3]);
067: int start2 = Fixnum.getInt(args[4]);
068: int end2 = Fixnum.getInt(args[5]);
069: int i = start1;
070: int j = start2;
071: while (true) {
072: if (i == end1) {
073: // Reached end of string1.
074: if (j == end2)
075: return NIL; // Strings are identical.
076: return new Fixnum(i);
077: }
078: if (j == end2) {
079: // Reached end of string2 before end of string1.
080: return new Fixnum(i);
081: }
082: if (array1[i] != array2[j])
083: return new Fixnum(i);
084: ++i;
085: ++j;
086: }
087: }
088: };
089:
090: // ### %string-equal
091: // Case insensitive.
092: private static final Primitive _STRING_EQUAL_IGNORE_CASE = new Primitive(
093: "%string-equal", PACKAGE_SYS, true) {
094: public LispObject execute(LispObject[] args)
095: throws ConditionThrowable {
096: if (args.length != 6)
097: throw new ConditionThrowable(
098: new WrongNumberOfArgumentsException(this ));
099: char[] array1 = string(args[0]).chars();
100: char[] array2 = string(args[1]).chars();
101: int start1 = Fixnum.getInt(args[2]);
102: int end1 = Fixnum.getInt(args[3]);
103: int start2 = Fixnum.getInt(args[4]);
104: int end2 = Fixnum.getInt(args[5]);
105: if ((end1 - start1) != (end2 - start2))
106: return NIL;
107: int i, j;
108: for (i = start1, j = start2; i < end1 && j < end2; i++, j++) {
109: char c1 = array1[i];
110: char c2 = array2[j];
111: if (c1 == c2)
112: continue;
113: if (Utilities.toUpperCase(c1) == Utilities
114: .toUpperCase(c2))
115: continue;
116: if (Utilities.toLowerCase(c1) == Utilities
117: .toLowerCase(c2))
118: continue;
119: return NIL;
120: }
121: return T;
122: }
123: };
124:
125: // ### %string-not-equal
126: // Case sensitive.
127: private static final Primitive _STRING_NOT_EQUAL_IGNORE_CASE = new Primitive(
128: "%string-not-equal", PACKAGE_SYS, true) {
129: public LispObject execute(LispObject[] args)
130: throws ConditionThrowable {
131: if (args.length != 6)
132: throw new ConditionThrowable(
133: new WrongNumberOfArgumentsException(this ));
134: char[] array1 = string(args[0]).chars();
135: char[] array2 = string(args[1]).chars();
136: int start1 = Fixnum.getInt(args[2]);
137: int end1 = Fixnum.getInt(args[3]);
138: int start2 = Fixnum.getInt(args[4]);
139: int end2 = Fixnum.getInt(args[5]);
140: int i = start1;
141: int j = start2;
142: while (true) {
143: if (i == end1) {
144: // Reached end of string1.
145: if (j == end2)
146: return NIL; // Strings are identical.
147: return new Fixnum(i);
148: }
149: if (j == end2) {
150: // Reached end of string2.
151: return new Fixnum(i);
152: }
153: char c1 = array1[i];
154: char c2 = array2[j];
155: if (c1 == c2
156: || Utilities.toUpperCase(c1) == Utilities
157: .toUpperCase(c2)
158: || Utilities.toLowerCase(c1) == Utilities
159: .toLowerCase(c2)) {
160: ++i;
161: ++j;
162: continue;
163: }
164: return new Fixnum(i);
165: }
166: }
167: };
168:
169: // ### %string<
170: // Case sensitive.
171: private static final Primitive _STRING_LESS_THAN = new Primitive(
172: "%string<", PACKAGE_SYS, true) {
173: public LispObject execute(LispObject[] args)
174: throws ConditionThrowable {
175: if (args.length != 6)
176: throw new ConditionThrowable(
177: new WrongNumberOfArgumentsException(this ));
178: char[] array1 = string(args[0]).chars();
179: char[] array2 = string(args[1]).chars();
180: int start1 = Fixnum.getInt(args[2]);
181: int end1 = Fixnum.getInt(args[3]);
182: int start2 = Fixnum.getInt(args[4]);
183: int end2 = Fixnum.getInt(args[5]);
184: int i = start1;
185: int j = start2;
186: while (true) {
187: if (i == end1) {
188: // Reached end of string1.
189: if (j == end2)
190: return NIL; // Strings are identical.
191: return new Fixnum(i);
192: }
193: if (j == end2) {
194: // Reached end of string2.
195: return NIL;
196: }
197: char c1 = array1[i];
198: char c2 = array2[j];
199: if (c1 == c2) {
200: ++i;
201: ++j;
202: continue;
203: }
204: if (c1 < c2)
205: return new Fixnum(i);
206: // c1 > c2
207: return NIL;
208: }
209: }
210: };
211:
212: // ### %string<=
213: // Case sensitive.
214: private static final Primitive _STRING_GREATER_THAN = new Primitive(
215: "%string>", PACKAGE_SYS, true) {
216: public LispObject execute(LispObject[] args)
217: throws ConditionThrowable {
218: if (args.length != 6)
219: throw new ConditionThrowable(
220: new WrongNumberOfArgumentsException(this ));
221: char[] array1 = string(args[0]).chars();
222: char[] array2 = string(args[1]).chars();
223: int start1 = Fixnum.getInt(args[2]);
224: int end1 = Fixnum.getInt(args[3]);
225: int start2 = Fixnum.getInt(args[4]);
226: int end2 = Fixnum.getInt(args[5]);
227: int i = start1;
228: int j = start2;
229: while (true) {
230: if (i == end1) {
231: // Reached end of string1.
232: return NIL;
233: }
234: if (j == end2) {
235: // Reached end of string2.
236: return new Fixnum(i);
237: }
238: char c1 = array1[i];
239: char c2 = array2[j];
240: if (c1 == c2) {
241: ++i;
242: ++j;
243: continue;
244: }
245: if (c1 < c2)
246: return NIL;
247: // c1 > c2
248: return new Fixnum(i);
249: }
250: }
251: };
252:
253: // ### %string<=
254: // Case sensitive.
255: private static final Primitive _STRING_LE = new Primitive(
256: "%string<=", PACKAGE_SYS, true) {
257: public LispObject execute(LispObject[] args)
258: throws ConditionThrowable {
259: if (args.length != 6)
260: throw new ConditionThrowable(
261: new WrongNumberOfArgumentsException(this ));
262: char[] array1 = string(args[0]).chars();
263: char[] array2 = string(args[1]).chars();
264: int start1 = Fixnum.getInt(args[2]);
265: int end1 = Fixnum.getInt(args[3]);
266: int start2 = Fixnum.getInt(args[4]);
267: int end2 = Fixnum.getInt(args[5]);
268: int i = start1;
269: int j = start2;
270: while (true) {
271: if (i == end1) {
272: // Reached end of string1.
273: return new Fixnum(i);
274: }
275: if (j == end2) {
276: // Reached end of string2.
277: return NIL;
278: }
279: char c1 = array1[i];
280: char c2 = array2[j];
281: if (c1 == c2) {
282: ++i;
283: ++j;
284: continue;
285: }
286: if (c1 > c2)
287: return NIL;
288: // c1 < c2
289: return new Fixnum(i);
290: }
291: }
292: };
293:
294: // ### %string<=
295: // Case sensitive.
296: private static final Primitive _STRING_GE = new Primitive(
297: "%string>=", PACKAGE_SYS, true) {
298: public LispObject execute(LispObject[] args)
299: throws ConditionThrowable {
300: if (args.length != 6)
301: throw new ConditionThrowable(
302: new WrongNumberOfArgumentsException(this ));
303: char[] array1 = string(args[0]).chars();
304: char[] array2 = string(args[1]).chars();
305: int start1 = Fixnum.getInt(args[2]);
306: int end1 = Fixnum.getInt(args[3]);
307: int start2 = Fixnum.getInt(args[4]);
308: int end2 = Fixnum.getInt(args[5]);
309: int i = start1;
310: int j = start2;
311: while (true) {
312: if (i == end1) {
313: // Reached end of string1.
314: if (j == end2)
315: return new Fixnum(i); // Strings are identical.
316: return NIL;
317: }
318: if (j == end2) {
319: // Reached end of string2.
320: return new Fixnum(i);
321: }
322: char c1 = array1[i];
323: char c2 = array2[j];
324: if (c1 == c2) {
325: ++i;
326: ++j;
327: continue;
328: }
329: if (c1 < c2)
330: return NIL;
331: // c1 > c2
332: return new Fixnum(i);
333: }
334: }
335: };
336:
337: // ### %string-lessp
338: // Case insensitive.
339: private static final Primitive _STRING_LESSP = new Primitive(
340: "%string-lessp", PACKAGE_SYS, true) {
341: public LispObject execute(LispObject[] args)
342: throws ConditionThrowable {
343: if (args.length != 6)
344: throw new ConditionThrowable(
345: new WrongNumberOfArgumentsException(this ));
346: char[] array1 = string(args[0]).chars();
347: char[] array2 = string(args[1]).chars();
348: int start1 = Fixnum.getInt(args[2]);
349: int end1 = Fixnum.getInt(args[3]);
350: int start2 = Fixnum.getInt(args[4]);
351: int end2 = Fixnum.getInt(args[5]);
352: int i = start1;
353: int j = start2;
354: while (true) {
355: if (i == end1) {
356: // Reached end of string1.
357: if (j == end2)
358: return NIL; // Strings are identical.
359: return new Fixnum(i);
360: }
361: if (j == end2) {
362: // Reached end of string2.
363: return NIL;
364: }
365: char c1 = Utilities.toUpperCase(array1[i]);
366: char c2 = Utilities.toUpperCase(array2[j]);
367: if (c1 == c2) {
368: ++i;
369: ++j;
370: continue;
371: }
372: if (c1 > c2)
373: return NIL;
374: // c1 < c2
375: return new Fixnum(i);
376: }
377: }
378: };
379:
380: // ### %string-greaterp
381: // Case insensitive.
382: private static final Primitive _STRING_GREATERP = new Primitive(
383: "%string-greaterp", PACKAGE_SYS, true) {
384: public LispObject execute(LispObject[] args)
385: throws ConditionThrowable {
386: if (args.length != 6)
387: throw new ConditionThrowable(
388: new WrongNumberOfArgumentsException(this ));
389: char[] array1 = string(args[0]).chars();
390: char[] array2 = string(args[1]).chars();
391: int start1 = Fixnum.getInt(args[2]);
392: int end1 = Fixnum.getInt(args[3]);
393: int start2 = Fixnum.getInt(args[4]);
394: int end2 = Fixnum.getInt(args[5]);
395: int i = start1;
396: int j = start2;
397: while (true) {
398: if (i == end1) {
399: // Reached end of string1.
400: return NIL;
401: }
402: if (j == end2) {
403: // Reached end of string2.
404: return new Fixnum(i);
405: }
406: char c1 = Utilities.toUpperCase(array1[i]);
407: char c2 = Utilities.toUpperCase(array2[j]);
408: if (c1 == c2) {
409: ++i;
410: ++j;
411: continue;
412: }
413: if (c1 < c2)
414: return NIL;
415: // c1 > c2
416: return new Fixnum(i);
417: }
418: }
419: };
420:
421: // ### %string-not-lessp
422: // Case insensitive.
423: private static final Primitive _STRING_NOT_LESSP = new Primitive(
424: "%string-not-lessp", PACKAGE_SYS, true) {
425: public LispObject execute(LispObject[] args)
426: throws ConditionThrowable {
427: if (args.length != 6)
428: throw new ConditionThrowable(
429: new WrongNumberOfArgumentsException(this ));
430: char[] array1 = string(args[0]).chars();
431: char[] array2 = string(args[1]).chars();
432: int start1 = Fixnum.getInt(args[2]);
433: int end1 = Fixnum.getInt(args[3]);
434: int start2 = Fixnum.getInt(args[4]);
435: int end2 = Fixnum.getInt(args[5]);
436: int i = start1;
437: int j = start2;
438: while (true) {
439: if (i == end1) {
440: // Reached end of string1.
441: if (j == end2)
442: return new Fixnum(i); // Strings are identical.
443: return NIL;
444: }
445: if (j == end2) {
446: // Reached end of string2.
447: return new Fixnum(i);
448: }
449: char c1 = Utilities.toUpperCase(array1[i]);
450: char c2 = Utilities.toUpperCase(array2[j]);
451: if (c1 == c2) {
452: ++i;
453: ++j;
454: continue;
455: }
456: if (c1 > c2)
457: return new Fixnum(i);
458: // c1 < c2
459: return NIL;
460: }
461: }
462: };
463:
464: // ### %string-not-greaterp
465: // Case insensitive.
466: private static final Primitive _STRING_NOT_GREATERP = new Primitive(
467: "%string-not-greaterp", PACKAGE_SYS, true) {
468: public LispObject execute(LispObject[] args)
469: throws ConditionThrowable {
470: if (args.length != 6)
471: throw new ConditionThrowable(
472: new WrongNumberOfArgumentsException(this ));
473: char[] array1 = string(args[0]).chars();
474: char[] array2 = string(args[1]).chars();
475: int start1 = Fixnum.getInt(args[2]);
476: int end1 = Fixnum.getInt(args[3]);
477: int start2 = Fixnum.getInt(args[4]);
478: int end2 = Fixnum.getInt(args[5]);
479: int i = start1;
480: int j = start2;
481: while (true) {
482: if (i == end1) {
483: // Reached end of string1.
484: return new Fixnum(i);
485: }
486: if (j == end2) {
487: // Reached end of string2.
488: return NIL;
489: }
490: char c1 = Utilities.toUpperCase(array1[i]);
491: char c2 = Utilities.toUpperCase(array2[j]);
492: if (c1 == c2) {
493: ++i;
494: ++j;
495: continue;
496: }
497: if (c1 > c2)
498: return NIL;
499: // c1 < c2
500: return new Fixnum(i);
501: }
502: }
503: };
504:
505: // ### %string-upcase
506: private static final Primitive3 _STRING_UPCASE = new Primitive3(
507: "%string-upcase", PACKAGE_SYS, true) {
508: public LispObject execute(LispObject first, LispObject second,
509: LispObject third) throws ConditionThrowable {
510: LispString s = string(first);
511: final int length = s.length();
512: int start = (int) Fixnum.getValue(second);
513: if (start < 0 || start > length)
514: throw new ConditionThrowable(new TypeError(
515: "invalid start position " + start));
516: int end;
517: if (third == NIL)
518: end = length;
519: else
520: end = (int) Fixnum.getValue(third);
521: if (end < 0 || end > length)
522: throw new ConditionThrowable(new TypeError(
523: "invalid end position " + start));
524: if (start > end)
525: throw new ConditionThrowable(
526: new TypeError("start (" + start
527: + ") is greater than end (" + end + ")"));
528: StringBuffer sb = new StringBuffer(length);
529: char[] array = s.chars();
530: int i;
531: for (i = 0; i < start; i++)
532: sb.append(array[i]);
533: for (i = start; i < end; i++)
534: sb.append(Utilities.toUpperCase(array[i]));
535: for (i = end; i < length; i++)
536: sb.append(array[i]);
537: return new LispString(sb.toString());
538: }
539: };
540:
541: // ### %string-downcase
542: private static final Primitive3 _STRING_DOWNCASE = new Primitive3(
543: "%string-downcase", PACKAGE_SYS, true) {
544: public LispObject execute(LispObject first, LispObject second,
545: LispObject third) throws ConditionThrowable {
546: LispString s = string(first);
547: final int length = s.length();
548: int start = (int) Fixnum.getValue(second);
549: if (start < 0 || start > length)
550: throw new ConditionThrowable(new TypeError(
551: "invalid start position " + start));
552: int end;
553: if (third == NIL)
554: end = length;
555: else
556: end = (int) Fixnum.getValue(third);
557: if (end < 0 || end > length)
558: throw new ConditionThrowable(new TypeError(
559: "invalid end position " + start));
560: if (start > end)
561: throw new ConditionThrowable(
562: new TypeError("start (" + start
563: + ") is greater than end (" + end + ")"));
564: StringBuffer sb = new StringBuffer(length);
565: char[] array = s.chars();
566: int i;
567: for (i = 0; i < start; i++)
568: sb.append(array[i]);
569: for (i = start; i < end; i++)
570: sb.append(Utilities.toLowerCase(array[i]));
571: for (i = end; i < length; i++)
572: sb.append(array[i]);
573: return new LispString(sb.toString());
574: }
575: };
576:
577: // ### %string-capitalize
578: private static final Primitive3 _STRING_CAPITALIZE = new Primitive3(
579: "%string-capitalize", PACKAGE_SYS, true) {
580: public LispObject execute(LispObject first, LispObject second,
581: LispObject third) throws ConditionThrowable {
582: LispString s = string(first);
583: final int length = s.length();
584: int start = (int) Fixnum.getValue(second);
585: if (start < 0 || start > length)
586: throw new ConditionThrowable(new TypeError(
587: "invalid start position " + start));
588: int end;
589: if (third == NIL)
590: end = length;
591: else
592: end = (int) Fixnum.getValue(third);
593: if (end < 0 || end > length)
594: throw new ConditionThrowable(new TypeError(
595: "invalid end position " + start));
596: if (start > end)
597: throw new ConditionThrowable(
598: new TypeError("start (" + start
599: + ") is greater than end (" + end + ")"));
600: StringBuffer sb = new StringBuffer(length);
601: char[] array = s.chars();
602: boolean lastCharWasAlphanumeric = false;
603: int i;
604: for (i = 0; i < start; i++)
605: sb.append(array[i]);
606: for (i = start; i < end; i++) {
607: char c = array[i];
608: if (Character.isLowerCase(c)) {
609: sb.append(lastCharWasAlphanumeric ? c : Utilities
610: .toUpperCase(c));
611: lastCharWasAlphanumeric = true;
612: } else if (Character.isUpperCase(c)) {
613: sb.append(lastCharWasAlphanumeric ? Utilities
614: .toLowerCase(c) : c);
615: lastCharWasAlphanumeric = true;
616: } else {
617: sb.append(c);
618: lastCharWasAlphanumeric = Character.isDigit(c);
619: }
620: }
621: for (i = end; i < length; i++)
622: sb.append(array[i]);
623: return new LispString(sb.toString());
624: }
625: };
626:
627: // ### %nstring-upcase
628: private static final Primitive3 _NSTRING_UPCASE = new Primitive3(
629: "%nstring-upcase", PACKAGE_SYS, true) {
630: public LispObject execute(LispObject first, LispObject second,
631: LispObject third) throws ConditionThrowable {
632: LispString s = checkString(first);
633: final int length = s.length();
634: int start = (int) Fixnum.getValue(second);
635: if (start < 0 || start > length)
636: throw new ConditionThrowable(new TypeError(
637: "invalid start position " + start));
638: int end;
639: if (third == NIL)
640: end = length;
641: else
642: end = (int) Fixnum.getValue(third);
643: if (end < 0 || end > length)
644: throw new ConditionThrowable(new TypeError(
645: "invalid end position " + start));
646: if (start > end)
647: throw new ConditionThrowable(
648: new TypeError("start (" + start
649: + ") is greater than end (" + end + ")"));
650: char[] array = s.chars();
651: for (int i = start; i < end; i++)
652: array[i] = Utilities.toUpperCase(array[i]);
653: return s;
654: }
655: };
656:
657: // ### %nstring-downcase
658: private static final Primitive3 _NSTRING_DOWNCASE = new Primitive3(
659: "%nstring-downcase", PACKAGE_SYS, true) {
660: public LispObject execute(LispObject first, LispObject second,
661: LispObject third) throws ConditionThrowable {
662: LispString s = checkString(first);
663: final int length = s.length();
664: int start = (int) Fixnum.getValue(second);
665: if (start < 0 || start > length)
666: throw new ConditionThrowable(new TypeError(
667: "invalid start position " + start));
668: int end;
669: if (third == NIL)
670: end = s.length();
671: else
672: end = (int) Fixnum.getValue(third);
673: if (end < 0 || end > length)
674: throw new ConditionThrowable(new TypeError(
675: "invalid end position " + start));
676: if (start > end)
677: throw new ConditionThrowable(
678: new TypeError("start (" + start
679: + ") is greater than end (" + end + ")"));
680: char[] array = s.chars();
681: for (int i = start; i < end; i++)
682: array[i] = Utilities.toLowerCase(array[i]);
683: return s;
684: }
685: };
686:
687: // ### %nstring-capitalize
688: private static final Primitive3 _NSTRING_CAPITALIZE = new Primitive3(
689: "%nstring-capitalize", PACKAGE_SYS, true) {
690: public LispObject execute(LispObject first, LispObject second,
691: LispObject third) throws ConditionThrowable {
692: LispString s = checkString(first);
693: final int length = s.length();
694: int start = (int) Fixnum.getValue(second);
695: if (start < 0 || start > length)
696: throw new ConditionThrowable(new TypeError(
697: "invalid start position " + start));
698: int end;
699: if (third == NIL)
700: end = s.length();
701: else
702: end = (int) Fixnum.getValue(third);
703: if (end < 0 || end > length)
704: throw new ConditionThrowable(new TypeError(
705: "invalid end position " + start));
706: if (start > end)
707: throw new ConditionThrowable(
708: new TypeError("start (" + start
709: + ") is greater than end (" + end + ")"));
710: char[] array = s.chars();
711: boolean lastCharWasAlphanumeric = false;
712: for (int i = start; i < end; i++) {
713: char c = array[i];
714: if (Character.isLowerCase(c)) {
715: if (!lastCharWasAlphanumeric)
716: array[i] = Utilities.toUpperCase(c);
717: lastCharWasAlphanumeric = true;
718: } else if (Character.isUpperCase(c)) {
719: if (lastCharWasAlphanumeric)
720: array[i] = Utilities.toLowerCase(c);
721: lastCharWasAlphanumeric = true;
722: } else
723: lastCharWasAlphanumeric = Character.isDigit(c);
724: }
725: return s;
726: }
727: };
728: }
|