0001: package jsint;
0002:
0003: import java.io.*;
0004: import java.lang.reflect.Array;
0005:
0006: /**
0007: A class to hold static utility methods; the name "U" stands for
0008: "Utility", but is short because it will be used a lot.
0009: @author Peter Norvig, Copyright 1998, peter@norvig.com, <a href="license.txt">license</a>
0010: subsequently modified by Jscheme project members
0011: licensed under zlib licence (see license.txt)
0012: **/
0013:
0014: public abstract class U {
0015:
0016: /** a flag which specifies whether Java (or Scheme) syntax
0017: * should be used when printing Scheme terms
0018: **/
0019: public static boolean useJavaSyntax = true;
0020:
0021: //////////////// Constants ////////////////
0022:
0023: /** Same as Boolean.TRUE. **/
0024: public static final Boolean TRUE = Boolean.TRUE;
0025:
0026: /** Same as Boolean.FALSE. **/
0027: public static final Boolean FALSE = Boolean.FALSE;
0028:
0029: /** The value to return when a variable is not defined. **/
0030: public static final Symbol UNDEFINED = Symbol.intern("#!undefined");
0031:
0032: /** The value to use when a parameter is not supplied to a procedure. **/
0033: // public static final Pair MISSING = Pair.EMPTY;
0034: public static final Symbol MISSING = Symbol.intern("#!missing");
0035: /** An argument list with zero arguments. **/
0036: public static final Object[] NO_ARGS = new Object[0];
0037: public static final Object[] EMPTY_ARGS = new Object[] { Pair.EMPTY };
0038:
0039: //////////////// Conversion Routines ////////////////
0040:
0041: // The following convert or coerce objects to the right type.
0042:
0043: /** #f and #null are treated as false **/
0044: public static boolean isFalse(Object x) {
0045: return x == null || FALSE.equals(x);
0046: }
0047:
0048: /** One argument and used by (and) macro. **/
0049: public static Object and1(Object x) {
0050: return isFalse(x) ? FALSE : x;
0051: }
0052:
0053: /** Convert Scheme object to boolean. **/
0054: public static boolean to_bool(Object x) {
0055: return !(isFalse(x));
0056: }
0057:
0058: /** Convert boolean to Boolean. **/
0059: public static Boolean toBool(boolean x) {
0060: return x ? TRUE : FALSE;
0061: }
0062:
0063: /** Convert Scheme object to Boolean. **/
0064: public static Boolean toBool(Object x) {
0065: return isFalse(x) ? FALSE : TRUE;
0066: }
0067:
0068: /** Returns TRUE if x is FALSE or null. **/
0069: public static Boolean not(Object x) {
0070: return isFalse(x) ? TRUE : FALSE;
0071: }
0072:
0073: /** Converts a Character to a char, or calls error for non-Characters. **/
0074: public static char to_char(Object x) {
0075: if (x instanceof Character)
0076: return ((Character) x).charValue();
0077: else
0078: return to_char(E.typeError("char", x));
0079: }
0080:
0081: /** Converts a Character to a lowercase char, or calls error for non-Characters. **/
0082: public static char to_lc_char(Object x) {
0083: if (x instanceof Character)
0084: return Character.toLowerCase(((Character) x).charValue());
0085: else
0086: return to_lc_char(E.typeError("char", x));
0087: }
0088:
0089: /** Number of chars, positive, negative ints to cache. **/
0090: private final static int NUM_CACHED = 128;
0091: private final static Character[] cachedCharacters = new Character[128];
0092:
0093: /** Converts a char to a Character. Caches low-numbered chars. **/
0094: public static Character toChar(char ch) {
0095: if (ch < NUM_CACHED) {
0096: Character c = cachedCharacters[ch];
0097: return (c != null) ? c
0098: : (cachedCharacters[ch] = new Character(ch));
0099: } else
0100: return new Character(ch);
0101: }
0102:
0103: public static Class toClass(Object c) {
0104: if (c instanceof Class)
0105: return (Class) c;
0106: else
0107: return Import.classNamed(stringify(c, false));
0108: }
0109:
0110: public static Class maybeToClass(Object c) {
0111: if (c instanceof Class)
0112: return (Class) c;
0113: else
0114: return Import.maybeClassNamed(stringify(c, false));
0115: }
0116:
0117: private final static Integer[] cachedPosInts = new Integer[NUM_CACHED];
0118: private final static Integer[] cachedNegInts = new Integer[NUM_CACHED];
0119:
0120: /** Convert int to Integer. Caches small ints so that we only ever
0121: * make one copy of new Integer(0), new Integer(1), etc. **/
0122: public static Integer toNum(int i) {
0123: if (i >= 0) {
0124: if (i < NUM_CACHED) {
0125: Integer in = cachedPosInts[i];
0126: return (in != null) ? in
0127: : (cachedPosInts[i] = new Integer(i));
0128: } else
0129: return new Integer(i);
0130: } else if (i > -NUM_CACHED) {
0131: Integer in = cachedNegInts[-i];
0132: return (in != null) ? in
0133: : (cachedNegInts[-i] = new Integer(i));
0134: }
0135: return new Integer(i);
0136: }
0137:
0138: /** Convert long to Number, either Integer or Long. **/
0139: public static Number toNum(long i) {
0140: if (i <= Integer.MAX_VALUE && i >= Integer.MIN_VALUE)
0141: return toNum((int) i);
0142: else
0143: return new Long(i);
0144: }
0145:
0146: /** A Double with value 0.0. Defined here so that we need only one. **/
0147: public static final Double ZERO = new Double(0.0);
0148:
0149: /** A Double with value 1.0. Defined here so that we need only one. **/
0150: public static final Double ONE = new Double(1.0);
0151:
0152: /** Convert double to Double. Caches 0 and 1; makes new for others. **/
0153: public static Double toNum(double x) {
0154: return (x == 0.0) ? ZERO : (x == 1.0) ? ONE : new Double(x);
0155: }
0156:
0157: /** Converts a Scheme object to a double, or calls error. **/
0158: public static double toReal(Object x) {
0159: if (x instanceof Number)
0160: return ((Number) x).doubleValue();
0161: else
0162: return toReal(E.typeError("real number", x));
0163: }
0164:
0165: /** Converts a Scheme object to an int, or calls error. **/
0166: public static int toInt(Object x) {
0167: try {
0168: return ((Number) x).intValue();
0169: } catch (ClassCastException e) {
0170: return toInt(E.typeError("integer", x));
0171: }
0172: }
0173:
0174: /** Converts a Scheme object to an int, return the default
0175: * if it is not possible to convert the object to an int. **/
0176: public static int toInt(Object x, int defaultVal) {
0177: if (x instanceof Number)
0178: return ((Number) x).intValue();
0179: else
0180: return defaultVal;
0181: }
0182:
0183: /** Cast a Scheme object to a String, or call error. **/
0184: public static String toStr(Object x) {
0185: return (x instanceof String) ? (String) x : toStr(E.typeError(
0186: "string", x));
0187: }
0188:
0189: /** Cast a Scheme object to a Scheme symbol, or call error. **/
0190: public static Symbol toSym(Object x) {
0191: if (x instanceof Symbol)
0192: return (Symbol) x;
0193: else
0194: return toSym(E.typeError("symbol", x));
0195: }
0196:
0197: /** Cast a Scheme object to a procedure, or call error. **/
0198: public static Procedure toProc(Object x) {
0199: try {
0200: return (Procedure) x;
0201: } catch (ClassCastException e) {
0202: return toProc(E.typeError("procedure", x));
0203: }
0204: }
0205:
0206: /** Check if the argument is a non-empty list. **/
0207: public static boolean isPair(Object x) {
0208: return x instanceof Pair && x != Pair.EMPTY;
0209: }
0210:
0211: /** Cast a Scheme object to a Pair (can't be the empty list). **/
0212: public static Pair toPair(Object x) {
0213: try {
0214: if (x != Pair.EMPTY)
0215: return (Pair) x;
0216: else
0217: return toPair(E.typeError("pair(i.e. non-empty list)",
0218: x));
0219: } catch (ClassCastException e) {
0220: return toPair(E.typeError("pair(i.e. non-empty list)", x));
0221: }
0222: /*
0223: if (x != Pair.EMPTY && x instanceof Pair) return (Pair) x;
0224: else return toPair(E.typeError("pair(i.e. non-empty list)", x));
0225: */
0226: }
0227:
0228: /** Cast a Scheme object to a Pair or the empty list. **/
0229: public static Pair toList(Object x) {
0230: try {
0231: return (Pair) x;
0232: } catch (ClassCastException e) {
0233: return toPair(E.typeError("list (i.e. pair or empty)", x));
0234: }
0235: }
0236:
0237: /** Cast a Scheme object to a Scheme input port, which is an InputPort.
0238: * If the argument is missing, returns Scheme.getInput(). **/
0239: public static InputPort toInPort(Object x) {
0240: if (x == MISSING)
0241: return Scheme.currentEvaluator().getInput();
0242: else if (x instanceof InputPort)
0243: return (InputPort) x;
0244: else
0245: return toInPort(E.typeError("input port", x));
0246: }
0247:
0248: /** Cast a Scheme object to a Scheme input port, which is a PrintWriter.
0249: * If the argument is missing, returns Scheme.getOutput(). **/
0250: public static PrintWriter toOutPort(Object x) {
0251: if (x == MISSING)
0252: return Scheme.currentEvaluator().getOutput();
0253: else if (x instanceof PrintWriter)
0254: return (PrintWriter) x;
0255: else
0256: return toOutPort(E.typeError("output port", x));
0257: }
0258:
0259: //////////////// Basic manipulation Routines ////////////////
0260:
0261: /** Return the first element of a Pair, or error. **/
0262: public static Object first(Object x) {
0263: return toPair(x).first;
0264: }
0265:
0266: /** Return the rest of a Pair, or error. **/
0267: public static Object rest(Object x) {
0268: return toPair(x).rest;
0269: }
0270:
0271: /** Return the second element of a list. **/
0272: public static Object second(Object x) {
0273: return toPair(x).second();
0274: }
0275:
0276: /** Creates a three element list. **/
0277: public static Pair list(Object a, Object b, Object c) {
0278: return new Pair(a, new Pair(b, new Pair(c, Pair.EMPTY)));
0279: }
0280:
0281: /** Creates a two element list. **/
0282: public static Pair list(Object a, Object b) {
0283: return new Pair(a, new Pair(b, Pair.EMPTY));
0284: }
0285:
0286: /** Creates a one element list. **/
0287: public static Pair list(Object a) {
0288: return new Pair(a, Pair.EMPTY);
0289: }
0290:
0291: /** Structural equality. **/
0292: public static boolean equal(Object x, Object y) {
0293: if (x == null || y == null)
0294: return x == y;
0295: else if (x == Pair.EMPTY || y == Pair.EMPTY)
0296: return x == y;
0297: else if (x instanceof Object[]) {
0298: if (!(y instanceof Object[]))
0299: return false;
0300: Object[] xo = (Object[]) x, yo = (Object[]) y;
0301: if (xo.length != yo.length)
0302: return false;
0303: for (int i = xo.length - 1; i >= 0; i--)
0304: if (!equal(xo[i], yo[i]))
0305: return false;
0306: return true;
0307: } else
0308: return (x.equals(y) || eqv(x, y));
0309: }
0310:
0311: // public static boolean eqv(Object x, Object y) {
0312: // try {
0313: // return x == y
0314: // || (x instanceof Number && y instanceof Number &&
0315: // ((x instanceof Integer || x instanceof Long || x instanceof Short || x instanceof Byte) &&
0316: // (((Number)x).longValue()== ((Number) y).longValue()))
0317: // ||
0318: // ((x instanceof Float || x instanceof Double) &&
0319: // (((Number)x).doubleValue()== ((Number) y).doubleValue())))
0320: // || (x instanceof Character && x.equals(y))
0321: // || (x instanceof Boolean && x.equals(y));
0322: // } catch (ClassCastException e) { return false; }
0323: // }
0324:
0325: /** Atomic equality. **/
0326: public static boolean eqv(Object x, Object y) {
0327: return x == y || x == null && y == null || x != null
0328: && y != null
0329: && sameAtomicClasses(x.getClass(), y.getClass())
0330: && x.equals(y);
0331: }
0332:
0333: private static boolean sameAtomicClasses(Class cx, Class cy) {
0334: return cx == cy
0335: && (cx.getSuperclass() == Number.class
0336: || cx == Symbol.class || cx == Character.class || cx == Boolean.class);
0337: }
0338:
0339: /** Write the object to a port. If quoted is true, use "str" and #\c,
0340: * otherwise use str and c. **/
0341: public static Object write(Object x, PrintWriter port,
0342: boolean quoted) {
0343: port.print(stringify(x, quoted));
0344: port.flush();
0345: return x;
0346: }
0347:
0348: /** Check that the form has between min and max arguments (exclusive
0349: * of the first element of the form). If the form has the wrong
0350: * number of arguments, then complain. **/
0351: public static boolean checkNargs(int min, int max, int given,
0352: Object form) {
0353: if (given >= min && given <= max)
0354: return true;
0355: else {
0356: E.warn("expected "
0357: + min
0358: + (min == max ? ""
0359: : (max == Integer.MAX_VALUE) ? " or more"
0360: : (" to " + max))
0361: + " arguments, but got " + given, form);
0362: return false;
0363: }
0364: }
0365:
0366: //////////////// Output ////////////////
0367:
0368: /** Convert a Scheme object to its printed representation, as a java
0369: * String. If quoted is true, use "str" and #\c, otherwise use str and
0370: * c. You need to pass in a StringBuffer that is used to accumulate the
0371: * results. (If the interface didn't work that way, the system would use
0372: * lots of little internal StringBuffers. But note that you can still call
0373: * <tt>stringify(x)</tt> and a new StringBuffer will be created for you.
0374: * If useJavaSyntax is true, then literals are printed using a Java syntax
0375: **/
0376:
0377: public static StringBuffer stringify(Object x, boolean quoted,
0378: StringBuffer buf) {
0379: if (x == Pair.EMPTY) {
0380: buf.append("()");
0381: } else if (x == null) {
0382: buf.append("#null");
0383: } else if (x instanceof Boolean) {
0384: if (Boolean.TRUE.equals(x))
0385: buf.append("#t");
0386: else
0387: buf.append("#f");
0388: } else if (x == TRUE) {
0389: buf.append("#t");
0390: } else if (x == FALSE) {
0391: buf.append("#f");
0392: } else if (x instanceof Pair) {
0393: ((Pair) x).stringifyPair(quoted, buf);
0394: } else if (x instanceof Character) {
0395: char ch = ((Character) x).charValue();
0396: if (ch == '\'')
0397: buf.append(quoted ? (useJavaSyntax ? "#'\\\'\'"
0398: : "#\\'") : "'");
0399: else if (useJavaSyntax) {
0400: if (quoted)
0401: buf.append("#'");
0402: stringifyChar(buf, ch, quoted);
0403: if (quoted)
0404: buf.append("'");
0405: } else {
0406: if (quoted)
0407: buf.append("#\\");
0408: if (quoted && (ch == ' ' || ch == '\n'))
0409: buf.append((ch == ' ') ? "space" : "newline");
0410: else
0411: buf.append(ch);
0412: }
0413: } else if (x instanceof String) { // string
0414: String s = (String) x;
0415: if (quoted)
0416: buf.append('"');
0417: if (useJavaSyntax)
0418: for (int i = 0; i < s.length(); i++)
0419: stringifyChar(buf, s.charAt(i), quoted);
0420: else
0421: for (int i = 0; i < s.length(); i++) {
0422: if (quoted
0423: && (s.charAt(i) == '"' || s.charAt(i) == '\\'))
0424: buf.append('\\');
0425: buf.append(s.charAt(i));
0426: }
0427:
0428: if (quoted)
0429: buf.append('"');
0430: } else if (x instanceof Object[]) { // vector
0431: Object[] v = (Object[]) x;
0432: buf.append("#(");
0433: for (int i = 0; i < v.length; i++) {
0434: stringify(v[i], quoted, buf);
0435: if (i != v.length - 1)
0436: buf.append(' ');
0437: }
0438: buf.append(')');
0439: } else if (x instanceof Number) { // number
0440: buf.append(x);
0441: if ((x instanceof Integer) || (x instanceof Double)) /* do nothing */
0442: ;
0443: else if (x instanceof Byte)
0444: buf.append("B");
0445: else if (x instanceof Short)
0446: buf.append("S");
0447: else if (x instanceof Long)
0448: buf.append("L");
0449: else if (x instanceof Float)
0450: buf.append("F");
0451: } else {
0452: buf.append(x);
0453: }
0454: return buf;
0455: }
0456:
0457: private static void stringifyChar(StringBuffer buf, char ch,
0458: boolean quoted) {
0459: switch (ch) {
0460: case '\b':
0461: buf.append(quoted ? "\\b" : "\b");
0462: break;
0463: case '\t':
0464: buf.append(quoted ? "\\t" : "\t");
0465: break;
0466: case '\n':
0467: buf.append(quoted ? "\\n" : "\n");
0468: break;
0469: case '\f':
0470: buf.append(quoted ? "\\f" : "\f");
0471: break;
0472: case '\r':
0473: buf.append(quoted ? "\\r" : "\r");
0474: break;
0475: case '\"':
0476: buf.append(quoted ? "\\\"" : "\"");
0477: break;
0478: case '\\':
0479: buf.append(quoted ? "\\\\" : "\\");
0480: break;
0481: default:
0482: buf.append(ch);
0483: }
0484: }
0485:
0486: /** Convert x to a String giving its external representation.
0487: * Strings and characters are quoted. **/
0488: public static String stringify(Object x) {
0489: return stringify(x, true);
0490: }
0491:
0492: /** Convert x to a String giving its external representation.
0493: * Strings and characters are quoted iff <tt>quoted</tt> is true.. **/
0494: public static String stringify(Object x, boolean quoted) {
0495: // Handle these cases without consing:
0496: if (x instanceof String && !quoted)
0497: return ((String) x);
0498: else if (x instanceof Symbol)
0499: return ((Symbol) x).toString();
0500: else
0501: return stringify(x, quoted, new StringBuffer()).toString();
0502: }
0503:
0504: //////////////// Various ////////////////
0505:
0506: public static String makeString(int size, Object fill) {
0507: char[] chars = new char[size];
0508: if (fill != MISSING) {
0509: char ch = to_char(fill);
0510: for (int i = 0; i < size; i++)
0511: chars[i] = ch;
0512: }
0513: return new String(chars);
0514: }
0515:
0516: public static String stringAppend(Pair args) {
0517: if (args == Pair.EMPTY)
0518: return "";
0519: StringBuffer result = new StringBuffer();
0520: while (args != Pair.EMPTY) {
0521: if (Scheme.isInterruptable())
0522: Scheme.interruptCheck();
0523: result.append(stringify(args.first, false));
0524: args = toList(args.rest);
0525: }
0526: return result.toString();
0527: }
0528:
0529: public static Object memberAssoc(Object obj, Object list,
0530: boolean member, int eq) {
0531: boolean found = false;
0532: while (isPair(list)) {
0533: if (Scheme.isInterruptable())
0534: Scheme.interruptCheck();
0535: Object target = (member) ? first(list) : first(first(list));
0536: switch (eq) {
0537: case 1:
0538: found = (target == obj);
0539: break;
0540: case 2:
0541: found = eqv(target, obj);
0542: break;
0543: case 3:
0544: found = equal(target, obj);
0545: break;
0546: default:
0547: E.warn("Bad option to memberAssoc:" + eq);
0548: return FALSE;
0549: }
0550: if (found)
0551: return (member) ? list : first(list);
0552: list = rest(list);
0553: }
0554: return FALSE;
0555: }
0556:
0557: /** Compute (x op arg1 op arg2 op ...), in ints or doubles. **/
0558: public static Object numCompute(Object x, Pair args, char op) {
0559: return (x instanceof Integer) ? numCompute(toInt(x), args, op)
0560: : numCompute(toReal(x), args, op);
0561: }
0562:
0563: /** Compute (result op arg1 op arg2 op ...). Return the result
0564: * as an Object, but along the way, use result (a long) as an accumulator of
0565: * the result so far. For compare ops, returns FALSE for false, and
0566: * a number for true. **/
0567: public static Object numCompute(long result, Pair args, char op) {
0568: for (; isPair(args); args = toList(args.rest)) {
0569: if (!(args.first instanceof Integer))
0570: return numCompute((double) result, args, op);
0571: long y = toInt(args.first);
0572: switch (op) {
0573: case '>':
0574: if (!(result > y))
0575: return FALSE;
0576: else
0577: result = y;
0578: break;
0579: case '<':
0580: if (!(result < y))
0581: return FALSE;
0582: else
0583: result = y;
0584: break;
0585: case '=':
0586: if (!(result == y))
0587: return FALSE;
0588: else
0589: result = y;
0590: break;
0591: case 'L':
0592: if (!(result <= y))
0593: return FALSE;
0594: else
0595: result = y;
0596: break;
0597: case 'G':
0598: if (!(result >= y))
0599: return FALSE;
0600: else
0601: result = y;
0602: break;
0603: case 'X':
0604: if (y > result)
0605: result = y;
0606: break; // max
0607: case 'N':
0608: if (y < result)
0609: result = y;
0610: break; // min
0611: case '+':
0612: result += y;
0613: break;
0614: case '-':
0615: result -= y;
0616: break;
0617: case '*':
0618: result *= y;
0619: break;
0620: case '/':
0621: if (result % y == 0)
0622: result /= y;
0623: else
0624: return numCompute((double) result, args, op);
0625: break;
0626: default:
0627: return E
0628: .error("internal error: unrecognized op: " + op);
0629: }
0630: // If overflow ints, move to doubles
0631: if (result < Integer.MIN_VALUE
0632: || result > Integer.MAX_VALUE)
0633: return numCompute((double) result, args, op);
0634: }
0635: return toNum(result);
0636: }
0637:
0638: /** Compute (result op arg1 op arg2 op ...). Return the result
0639: * as an Object, but along the way, use result (a double) as an accumulator of
0640: * the result so far. For compare ops, returns FALSE for false, and
0641: * a number for true. **/
0642: public static Object numCompute(double result, Pair args, char op) {
0643: for (; isPair(args); args = toList(args.rest)) {
0644: double y = toReal(args.first);
0645: switch (op) {
0646: case '>':
0647: if (!(result > y))
0648: return FALSE;
0649: else
0650: result = y;
0651: break;
0652: case '<':
0653: if (!(result < y))
0654: return FALSE;
0655: else
0656: result = y;
0657: break;
0658: case '=':
0659: if (!(result == y))
0660: return FALSE;
0661: else
0662: result = y;
0663: break;
0664: case 'L':
0665: if (!(result <= y))
0666: return FALSE;
0667: else
0668: result = y;
0669: break;
0670: case 'G':
0671: if (!(result >= y))
0672: return FALSE;
0673: else
0674: result = y;
0675: break;
0676: case 'X':
0677: if (y > result)
0678: result = y;
0679: break; // max
0680: case 'N':
0681: if (y < result)
0682: result = y;
0683: break; // min
0684: case '+':
0685: result += y;
0686: break;
0687: case '-':
0688: result -= y;
0689: break;
0690: case '*':
0691: result *= y;
0692: break;
0693: case '/':
0694: result /= y;
0695: break;
0696: default:
0697: return E
0698: .error("internal error: unrecognized op: " + op);
0699: }
0700: }
0701: return toNum(result);
0702: }
0703:
0704: public static Object numberToString(Object x, Object y) {
0705: int base = (y instanceof Number) ? toInt(y) : 10;
0706: if (base != 10 && x instanceof Integer) { // An integer
0707: return Long.toString(toInt(x), base);
0708: } else { // A floating point number
0709: return x.toString();
0710: }
0711: }
0712:
0713: public static Object stringToNumber(Object x, Object y) {
0714: return InputPort.schemeStringToNumber(stringify(x, false),
0715: (y instanceof Number) ? toInt(y) : 10);
0716: }
0717:
0718: public static Object stringToList(Object x) {
0719: Pair result = Pair.EMPTY;
0720: String str = toStr(x);
0721: for (int i = str.length() - 1; i >= 0; i--)
0722: result = new Pair(toChar(str.charAt(i)), result);
0723: return result;
0724: }
0725:
0726: /** Convert a list of characters to a String. **/
0727: public static String listToString(Object chars) {
0728: char[] str = new char[toList(chars).length()];
0729: for (int i = 0; isPair(chars); i++) {
0730: str[i] = to_char(first(chars));
0731: chars = rest(chars);
0732: }
0733: return new String(str);
0734: }
0735:
0736: /** Return <0 if x is alphabetically first, >0 if y is first,
0737: * 0 if same. Case insensitive iff ci is true. Error if not strings.
0738: * NOTE: In Java 2, just use String.CompareIgnorecase. But that
0739: * method is missing in Java 1.0 and 1.1. **/
0740: public static int stringCompareIgnoreCase(Object x, Object y) {
0741: String xs = toStr(x), ys = toStr(y);
0742: for (int i = 0; i < xs.length(); i++) {
0743: int diff = Character.toUpperCase(xs.charAt(i))
0744: - Character.toUpperCase(ys.charAt(i));
0745: if (diff != 0)
0746: return diff;
0747: }
0748: return xs.length() - ys.length();
0749: }
0750:
0751: public static long gcd(Pair args) {
0752: return (args.rest == Pair.EMPTY) ? toInt(args.first) : gcd(Math
0753: .abs(toInt(args.first)), gcd((Pair) args.rest));
0754: }
0755:
0756: static long gcd(long a, long b) {
0757: return (b == 0) ? a : gcd(b, a % b);
0758: }
0759:
0760: static long lcm(Object args) {
0761: long L = 1, g = 1;
0762: while (isPair(args)) {
0763: long n = Math.abs((long) toInt(first(args)));
0764: g = gcd(n, L);
0765: L = (g == 0) ? g : (n / g) * L;
0766: args = toList(rest(args));
0767: }
0768: return L;
0769: }
0770:
0771: public static PrintWriter openOutputFile(Object filename) {
0772: try {
0773: return new PrintWriter(new FileWriter(stringify(filename,
0774: false)));
0775: } catch (FileNotFoundException e) {
0776: return (PrintWriter) E.error(e.toString());
0777: } catch (IOException e) {
0778: return (PrintWriter) E.error("IOException: " + e);
0779: }
0780: }
0781:
0782: /** Opens a file, resource, or URL. Returns null if unsuccessful.**/
0783: public static InputPort openInputFile(Object filename) {
0784: return Scheme.open(toStr(filename));
0785: }
0786:
0787: public static Object callWithInputFile(Object filename,
0788: Procedure proc) {
0789: InputPort in = null;
0790: Object result = null;
0791: try {
0792: in = openInputFile(filename);
0793: if (in == null)
0794: E.error("could not find '" + filename
0795: + "' as a resource URL, or File.");
0796: result = proc.apply(list(in));
0797: } finally {
0798: if (in != null)
0799: in.close();
0800: }
0801: return result;
0802: }
0803:
0804: public static Object callWithOutputFile(Object filename,
0805: Procedure proc) {
0806: PrintWriter out = null;
0807: Object result = null;
0808: try {
0809: out = openOutputFile(filename);
0810: result = proc.apply(list(out));
0811: } finally {
0812: if (out != null)
0813: out.close();
0814: }
0815: return result;
0816: }
0817:
0818: /** Return true if x is a proper list: null-terminated and finite.
0819: * Return false if it is an infinite or non-null-terminated list.
0820: * Not to be confused with <tt>(or (pair? x) (null? x))</tt>. **/
0821: public static boolean isList(Object x) {
0822: Object slow = x, fast = x;
0823: for (;;) {
0824: if (fast == Pair.EMPTY)
0825: return true;
0826: if (!isPair(fast) || !isPair(slow) || slow == rest(fast))
0827: return false;
0828: slow = rest(slow);
0829: fast = rest(fast);
0830: if (fast == Pair.EMPTY)
0831: return true;
0832: if (!isPair(fast))
0833: return false;
0834: fast = rest(fast);
0835: }
0836: }
0837:
0838: /* Original not tail recursive version.
0839: public static Object append(Pair args) {
0840: if (isPair(args))
0841: if (isPair(args.rest))
0842: return append(args.first, append(toList(args.rest)));
0843: else return args.first;
0844: else return args;
0845: }
0846:
0847: public static Object append(Object x, Object y) {
0848: return isPair(x) ? new Pair(first(x), append(rest(x), y)) : y;
0849: }
0850: Original not tail recursive version. */
0851:
0852: /** args is a list of lists to be appended together. **/
0853: public static Object append(Object args) {
0854: if (isPair(args)) {
0855: Queue queue = new Queue();
0856: while (isPair(rest(args))) {
0857: if (Scheme.isInterruptable())
0858: Scheme.interruptCheck();
0859: for (Object x = first(args); isPair(x); x = rest(x))
0860: queue.add(first(x));
0861: args = rest(args);
0862: }
0863: queue.getLast().rest = first(args);
0864: return queue.getContent();
0865: } else
0866: return args;
0867: }
0868:
0869: /*
0870: ;;; Append unit tests:
0871: (define (grow n)
0872: (if (<= n 0) (list (list 1))
0873: (let ((L (grow (- n 1))))
0874: (append L L))))
0875:
0876: ;;; Original version:
0877: (length (apply append (grow 13))) ; -> 8192
0878: (length (apply append (grow 14))) ; Stack overflow.
0879:
0880: ;;; New version:
0881: (length (apply append (grow 20))) ; 1048576
0882:
0883: (assert (eqv? (append 3) 3))
0884: (assert (eqv? (append '() '() '()) '()))
0885: (assert (= (append 3 3) 3))
0886: (assert (equal? (append '(1) '() 2) '(1 . 2)))
0887: (assert (equal? (append '(1) '() '(2 3)) '(1 2 3)))
0888: (let ((x '(1 2 3))) (assert (eq? (append '() x) x)))
0889: */
0890: /** A continuation exception is a specially marked RuntimeException. **/
0891: public static Object callCC(Procedure k) {
0892: ContinuationException cc = new ContinuationException();
0893: Continuation proc = new Continuation(cc);
0894: try {
0895: return k.apply(list(proc));
0896: } catch (ContinuationException e) {
0897: if (e == cc)
0898: return proc.value;
0899: else
0900: throw e;
0901: }
0902: }
0903:
0904: /** Map proc over a list of lists of args.
0905: * If result is a passed in as a Pair, then accumulate values there.
0906: * Otherwise, just return the empty list. **/
0907: public static Pair map(Procedure proc, Object args, Pair result) {
0908: Pair end = result;
0909: if (rest(args) == Pair.EMPTY) { // One-argument map
0910: Object argList = first(args);
0911: while (isPair(argList)) {
0912: if (Scheme.isInterruptable())
0913: Scheme.interruptCheck();
0914: Object x = proc.apply(list(first(argList)));
0915: if (end != Pair.EMPTY)
0916: end = (Pair) (end.rest = list(x));
0917: argList = rest(argList);
0918: }
0919: } else { // Multi-argument map
0920: Procedure car = toProc(Symbol.CAR.getGlobalValue()), cdr = toProc(Symbol.CDR
0921: .getGlobalValue());
0922: while (isPair(first(args))) {
0923: if (Scheme.isInterruptable())
0924: Scheme.interruptCheck();
0925: Object x = proc.apply(map(car, list(args), list(TRUE)));
0926: if (end != Pair.EMPTY)
0927: end = (Pair) (end.rest = list(x));
0928: args = map(cdr, list(args), list(TRUE));
0929: }
0930: }
0931: return ((U.isPair(result)) ? (Pair) rest(result) : result);
0932: }
0933:
0934: /** Call the procedure repeatedly nTimes, and return a list of the
0935: * the last result, the elapsed time, and the memory used. **/
0936: public static Pair timeCall(Procedure proc, int nTimes) {
0937: Runtime runtime = Runtime.getRuntime();
0938: runtime.gc();
0939: long startTime = System.currentTimeMillis();
0940: long startMem = runtime.freeMemory();
0941: Object ans = FALSE;
0942: for (int i = 0; i < nTimes; i++) {
0943: ans = proc.apply(Pair.EMPTY);
0944: }
0945: long time = System.currentTimeMillis() - startTime;
0946: long mem = startMem - runtime.freeMemory();
0947: return new Pair(ans, list(list(U.toNum(time), Symbol
0948: .intern("msec")), list(U.toNum(mem), Symbol
0949: .intern("bytes"))));
0950: }
0951:
0952: /** Used for debugging. **/
0953: public static Object p(String x, Object y) {
0954: if (Symbol.intern("debug").getGlobalValue() == U.TRUE) {
0955: Scheme.currentEvaluator().getError().println(
0956: x + stringify(y));
0957: }
0958: return y;
0959: }
0960:
0961: /** KRA 01OCT01: New vector semantics.
0962: <p>
0963:
0964: Any Java array is now treated as a Scheme vector. Type tests
0965: are inlined to keep the same performance for (vector-length),
0966: (vector-ref) and (vector-set!) as the old code when accessing
0967: Object[]'s
0968: **/
0969:
0970: /** Cast a Scheme object to a Scheme vector, or call error. **/
0971: public static Object toVec(Object x) {
0972: return x instanceof Object[] ? x : x != null
0973: && x.getClass().isArray() ? x : E
0974: .typeError("vector", x);
0975: }
0976:
0977: public static boolean isVector(Object x) {
0978: return x instanceof Object[] || x != null
0979: && x.getClass().isArray();
0980: }
0981:
0982: public static Object makeVector(Object x) {
0983: return new Object[U.toInt(x)];
0984: }
0985:
0986: public static Object makeVector(Object x, Object fill) {
0987: Object[] v = new Object[U.toInt(x)];
0988: for (int i = 0; i < U.toInt(x); i++)
0989: v[i] = fill;
0990: return v;
0991: }
0992:
0993: public static Object vectorFill(Object vec, Object fill) {
0994: for (int i = Array.getLength(vec) - 1; i >= 0; i--)
0995: Array.set(vec, i, fill);
0996: return U.UNDEFINED;
0997: }
0998:
0999: public static Object vectorLength(Object x) {
1000: return x instanceof Object[] ? U.toNum(((Object[]) x).length)
1001: : x != null && x.getClass().isArray() ? U.toNum(Array
1002: .getLength(x)) : E.typeError("vector", x);
1003: }
1004:
1005: public static Object vectorRef(Object x, Object y) {
1006: return x instanceof Object[] ? ((Object[]) x)[U.toInt(y)]
1007: : x != null && x.getClass().isArray() ? Array.get(x, U
1008: .toInt(y)) : E.typeError("vector", x);
1009: }
1010:
1011: public static Object vectorSet(Object x, Object y, Object z) {
1012: if (x instanceof Object[])
1013: return ((Object[]) x)[U.toInt(y)] = z;
1014: else if (x != null && x.getClass().isArray()) {
1015: Array.set(x, U.toInt(y), z);
1016: return z;
1017: } else
1018: return E.typeError("vector", x);
1019: }
1020:
1021: public static Pair vectorToList(Object vec) {
1022: Pair result = Pair.EMPTY;
1023: for (int i = Array.getLength(vec) - 1; i >= 0; i--) {
1024: result = new Pair(Array.get(vec, i), result);
1025: }
1026: return result;
1027: }
1028:
1029: public static Object[] listToVector(Object x) {
1030: Pair list = toList(x);
1031: int L = list.length();
1032: Object[] result = new Object[L];
1033: for (int i = 0; isPair(list); i++, list = toList(list.rest))
1034: result[i] = first(list);
1035: return result;
1036: }
1037:
1038: public static Object listToArray(Class C, Object x) {
1039: Pair list = toList(x);
1040: int L = list.length();
1041: if (L == 0)
1042: return java.lang.reflect.Array.newInstance(C, 0);
1043: else {
1044: Object result = java.lang.reflect.Array.newInstance(C, L);
1045: for (int i = 0; isPair(list); i++, list = toList(list.rest)) {
1046: java.lang.reflect.Array.set(result, i, first(list));
1047: }
1048: return result;
1049: }
1050: }
1051:
1052: public static Pair arrayToList(Object x) {
1053: Pair result = Pair.EMPTY;
1054: for (int i = Array.getLength(x) - 1; i >= 0; i--) {
1055: result = new Pair(Array.get(x, i), result);
1056: }
1057: return result;
1058: }
1059:
1060: /** R5RS apply as requested by
1061: "Hoehle, Joerg-Cyril" <Joerg-Cyril.Hoehle@t-systems.com>.
1062: We splice the last argument onto the end of the argument list.
1063: **/
1064: public static Object apply(Procedure p, Pair args) {
1065: Pair previous = null;
1066: Pair last = args;
1067: Pair next;
1068: while (!(next = ((Pair) last.getRest())).isEmpty()) {
1069: if (Scheme.isInterruptable())
1070: Scheme.interruptCheck();
1071: previous = last;
1072: last = next;
1073: }
1074: if (previous == null)
1075: args = ((Pair) args.getFirst());
1076: else
1077: previous.rest = ((Pair) (last.first)); // KRA 27MAY04:
1078: return p.apply(args);
1079: }
1080: }
|