package jscheme; import java.io.*; /** A primitive is a procedure that is defined as part of the Scheme report, * and is implemented in Java code. * @author Peter Norvig, peter@norvig.com http://www.norvig.com * Copyright 1998 Peter Norvig, see http://www.norvig.com/license.html **/ public class Primitive extends Procedure { int minArgs; int maxArgs; int idNumber; public Primitive(int id, int minArgs, int maxArgs) { this.idNumber = id; this.minArgs = minArgs; this.maxArgs = maxArgs; } private static final int EQ = 0, LT = 1, GT = 2, GE = 3, LE = 4, ABS = 5, EOF_OBJECT = 6, EQQ = 7, EQUALQ = 8, FORCE = 9, CAR = 10, FLOOR = 11, CEILING = 12, CONS = 13, DIVIDE = 14, LENGTH = 15, LIST = 16, LISTQ = 17, APPLY = 18, MAX = 19, MIN = 20, MINUS = 21, NEWLINE = 22, NOT = 23, NULLQ = 24, NUMBERQ = 25, PAIRQ = 26, PLUS = 27, PROCEDUREQ = 28, READ = 29, CDR = 30, ROUND = 31, SECOND = 32, SYMBOLQ = 33, TIMES = 34, TRUNCATE = 35, WRITE = 36, APPEND = 37, BOOLEANQ = 38, SQRT = 39, EXPT = 40, REVERSE = 41, ASSOC = 42, ASSQ = 43, ASSV = 44, MEMBER = 45, MEMQ = 46, MEMV = 47, EQVQ = 48, LISTREF = 49, LISTTAIL = 50, STRINQ = 51, MAKESTRING = 52, STRING = 53, STRINGLENGTH = 54, STRINGREF = 55, STRINGSET = 56, SUBSTRING = 57, STRINGAPPEND = 58, STRINGTOLIST = 59, LISTTOSTRING = 60, SYMBOLTOSTRING = 61, STRINGTOSYMBOL = 62, EXP = 63, LOG = 64, SIN = 65, COS = 66, TAN = 67, ACOS = 68, ASIN = 69, ATAN = 70, NUMBERTOSTRING = 71, STRINGTONUMBER = 72, CHARQ = 73, CHARALPHABETICQ = 74, CHARNUMERICQ = 75, CHARWHITESPACEQ = 76, CHARUPPERCASEQ = 77, CHARLOWERCASEQ = 78, CHARTOINTEGER = 79, INTEGERTOCHAR = 80, CHARUPCASE = 81, CHARDOWNCASE = 82, STRINGQ = 83, VECTORQ = 84, MAKEVECTOR = 85, VECTOR = 86, VECTORLENGTH = 87, VECTORREF = 88, VECTORSET = 89, LISTTOVECTOR = 90, MAP = 91, FOREACH = 92, CALLCC = 93, VECTORTOLIST = 94, LOAD = 95, DISPLAY = 96, INPUTPORTQ = 98, CURRENTINPUTPORT = 99, OPENINPUTFILE = 100, CLOSEINPUTPORT = 101, OUTPUTPORTQ = 103, CURRENTOUTPUTPORT = 104, OPENOUTPUTFILE = 105, CLOSEOUTPUTPORT = 106, READCHAR = 107, PEEKCHAR = 108, EVAL = 109, QUOTIENT = 110, REMAINDER = 111, MODULO = 112, THIRD = 113, EOFOBJECTQ = 114, GCD = 115, LCM = 116, CXR = 117, ODDQ = 118, EVENQ = 119, ZEROQ = 120, POSITIVEQ = 121, NEGATIVEQ = 122, CHARCMP = 123 /* to 127 */, CHARCICMP = 128 /* to 132 */, STRINGCMP = 133 /* to 137 */, STRINGCICMP = 138 /* to 142 */, EXACTQ = 143, INEXACTQ = 144, INTEGERQ = 145, CALLWITHINPUTFILE = 146, CALLWITHOUTPUTFILE = 147; //////////////// Extensions //////////////// static final int NEW = -1, CLASS = -2, METHOD = -3, EXIT = -4, SETCAR = -5, SETCDR = -6, TIMECALL = -11, MACROEXPAND = -12, ERROR = -13, LISTSTAR = -14; public static Environment installPrimitives(Environment env) { int n = Integer.MAX_VALUE; env .defPrim("*", TIMES, 0, n) .defPrim("*", TIMES, 0, n) .defPrim("+", PLUS, 0, n) .defPrim("-", MINUS, 1, n) .defPrim("/", DIVIDE, 1, n) .defPrim("<", LT, 2, n) .defPrim("<=", LE, 2, n) .defPrim("=", EQ, 2, n) .defPrim(">", GT, 2, n) .defPrim(">=", GE, 2, n) .defPrim("abs", ABS, 1) .defPrim("acos", ACOS, 1) .defPrim("append", APPEND, 0, n) .defPrim("apply", APPLY, 2, n) .defPrim("asin", ASIN, 1) .defPrim("assoc", ASSOC, 2) .defPrim("assq", ASSQ, 2) .defPrim("assv", ASSV, 2) .defPrim("atan", ATAN, 1) .defPrim("boolean?", BOOLEANQ, 1) .defPrim("caaaar", CXR, 1) .defPrim("caaadr", CXR, 1) .defPrim("caaar", CXR, 1) .defPrim("caadar", CXR, 1) .defPrim("caaddr", CXR, 1) .defPrim("caadr", CXR, 1) .defPrim("caar", CXR, 1) .defPrim("cadaar", CXR, 1) .defPrim("cadadr", CXR, 1) .defPrim("cadar", CXR, 1) .defPrim("caddar", CXR, 1) .defPrim("cadddr", CXR, 1) .defPrim("caddr", THIRD, 1) .defPrim("cadr", SECOND, 1) .defPrim("call-with-current-continuation", CALLCC, 1) .defPrim("call-with-input-file", CALLWITHINPUTFILE, 2) .defPrim("call-with-output-file", CALLWITHOUTPUTFILE, 2) .defPrim("car", CAR, 1) .defPrim("cdaaar", CXR, 1) .defPrim("cdaadr", CXR, 1) .defPrim("cdaar", CXR, 1) .defPrim("cdadar", CXR, 1) .defPrim("cdaddr", CXR, 1) .defPrim("cdadr", CXR, 1) .defPrim("cdar", CXR, 1) .defPrim("cddaar", CXR, 1) .defPrim("cddadr", CXR, 1) .defPrim("cddar", CXR, 1) .defPrim("cdddar", CXR, 1) .defPrim("cddddr", CXR, 1) .defPrim("cdddr", CXR, 1) .defPrim("cddr", CXR, 1) .defPrim("cdr", CDR, 1) .defPrim("char->integer", CHARTOINTEGER, 1) .defPrim("char-alphabetic?",CHARALPHABETICQ, 1) .defPrim("char-ci<=?", CHARCICMP+LE, 2) .defPrim("char-ci<?" , CHARCICMP+LT, 2) .defPrim("char-ci=?" , CHARCICMP+EQ, 2) .defPrim("char-ci>=?", CHARCICMP+GE, 2) .defPrim("char-ci>?" , CHARCICMP+GT, 2) .defPrim("char-downcase", CHARDOWNCASE, 1) .defPrim("char-lower-case?",CHARLOWERCASEQ, 1) .defPrim("char-numeric?", CHARNUMERICQ, 1) .defPrim("char-upcase", CHARUPCASE, 1) .defPrim("char-upper-case?",CHARUPPERCASEQ, 1) .defPrim("char-whitespace?",CHARWHITESPACEQ, 1) .defPrim("char<=?", CHARCMP+LE, 2) .defPrim("char<?", CHARCMP+LT, 2) .defPrim("char=?", CHARCMP+EQ, 2) .defPrim("char>=?", CHARCMP+GE, 2) .defPrim("char>?", CHARCMP+GT, 2) .defPrim("char?", CHARQ, 1) .defPrim("close-input-port", CLOSEINPUTPORT, 1) .defPrim("close-output-port", CLOSEOUTPUTPORT, 1) .defPrim("complex?", NUMBERQ, 1) .defPrim("cons", CONS, 2) .defPrim("cos", COS, 1) .defPrim("current-input-port", CURRENTINPUTPORT, 0) .defPrim("current-output-port", CURRENTOUTPUTPORT, 0) .defPrim("display", DISPLAY, 1, 2) .defPrim("eof-object?", EOFOBJECTQ, 1) .defPrim("eq?", EQQ, 2) .defPrim("equal?", EQUALQ, 2) .defPrim("eqv?", EQVQ, 2) .defPrim("eval", EVAL, 1, 2) .defPrim("even?", EVENQ, 1) .defPrim("exact?", INTEGERQ, 1) .defPrim("exp", EXP, 1) .defPrim("expt", EXPT, 2) .defPrim("force", FORCE, 1) .defPrim("for-each", FOREACH, 1, n) .defPrim("gcd", GCD, 0, n) .defPrim("inexact?", INEXACTQ, 1) .defPrim("input-port?", INPUTPORTQ, 1) .defPrim("integer->char", INTEGERTOCHAR, 1) .defPrim("integer?", INTEGERQ, 1) .defPrim("lcm", LCM, 0, n) .defPrim("length", LENGTH, 1) .defPrim("list", LIST, 0, n) .defPrim("list->string", LISTTOSTRING, 1) .defPrim("list->vector", LISTTOVECTOR, 1) .defPrim("list-ref", LISTREF, 2) .defPrim("list-tail", LISTTAIL, 2) .defPrim("list?", LISTQ, 1) .defPrim("load", LOAD, 1) .defPrim("log", LOG, 1) .defPrim("macro-expand", MACROEXPAND,1) .defPrim("make-string", MAKESTRING,1, 2) .defPrim("make-vector", MAKEVECTOR,1, 2) .defPrim("map", MAP, 1, n) .defPrim("max", MAX, 1, n) .defPrim("member", MEMBER, 2) .defPrim("memq", MEMQ, 2) .defPrim("memv", MEMV, 2) .defPrim("min", MIN, 1, n) .defPrim("modulo", MODULO, 2) .defPrim("negative?", NEGATIVEQ, 1) .defPrim("newline", NEWLINE, 0, 1) .defPrim("not", NOT, 1) .defPrim("null?", NULLQ, 1) .defPrim("number->string", NUMBERTOSTRING, 1, 2) .defPrim("number?", NUMBERQ, 1) .defPrim("odd?", ODDQ, 1) .defPrim("open-input-file",OPENINPUTFILE, 1) .defPrim("open-output-file", OPENOUTPUTFILE, 1) .defPrim("output-port?", OUTPUTPORTQ, 1) .defPrim("pair?", PAIRQ, 1) .defPrim("peek-char", PEEKCHAR, 0, 1) .defPrim("positive?", POSITIVEQ, 1) .defPrim("procedure?", PROCEDUREQ,1) .defPrim("quotient", QUOTIENT, 2) .defPrim("rational?", INTEGERQ, 1) .defPrim("read", READ, 0, 1) .defPrim("read-char", READCHAR, 0, 1) .defPrim("real?", NUMBERQ, 1) .defPrim("remainder", REMAINDER, 2) .defPrim("reverse", REVERSE, 1) .defPrim("round", ROUND, 1) .defPrim("set-car!", SETCAR, 2) .defPrim("set-cdr!", SETCDR, 2) .defPrim("sin", SIN, 1) .defPrim("sqrt", SQRT, 1) .defPrim("string", STRING, 0, n) .defPrim("string->list", STRINGTOLIST, 1) .defPrim("string->number", STRINGTONUMBER, 1, 2) .defPrim("string->symbol", STRINGTOSYMBOL, 1) .defPrim("string-append", STRINGAPPEND, 0, n) .defPrim("string-ci<=?", STRINGCICMP+LE, 2) .defPrim("string-ci<?" , STRINGCICMP+LT, 2) .defPrim("string-ci=?" , STRINGCICMP+EQ, 2) .defPrim("string-ci>=?", STRINGCICMP+GE, 2) .defPrim("string-ci>?" , STRINGCICMP+GT, 2) .defPrim("string-length", STRINGLENGTH, 1) .defPrim("string-ref", STRINGREF, 2) .defPrim("string-set!", STRINGSET, 3) .defPrim("string<=?", STRINGCMP+LE, 2) .defPrim("string<?", STRINGCMP+LT, 2) .defPrim("string=?", STRINGCMP+EQ, 2) .defPrim("string>=?", STRINGCMP+GE, 2) .defPrim("string>?", STRINGCMP+GT, 2) .defPrim("string?", STRINGQ, 1) .defPrim("substring", SUBSTRING, 3) .defPrim("symbol->string", SYMBOLTOSTRING, 1) .defPrim("symbol?", SYMBOLQ, 1) .defPrim("tan", TAN, 1) .defPrim("vector", VECTOR, 0, n) .defPrim("vector->list", VECTORTOLIST, 1) .defPrim("vector-length", VECTORLENGTH, 1) .defPrim("vector-ref", VECTORREF, 2) .defPrim("vector-set!", VECTORSET, 3) .defPrim("vector?", VECTORQ, 1) .defPrim("write", WRITE, 1, 2) .defPrim("write-char", DISPLAY, 1, 2) .defPrim("zero?", ZEROQ, 1) ///////////// Extensions //////////////// .defPrim("new", NEW, 1) .defPrim("class", CLASS, 1) .defPrim("method", METHOD, 2, n) .defPrim("exit", EXIT, 0, 1) .defPrim("error", ERROR, 0, n) .defPrim("time-call", TIMECALL, 1, 2) .defPrim("_list*", LISTSTAR, 0, n) ; return env; } /** Apply a primitive to a list of arguments. **/ public Object apply(Scheme interp, Object args) { //First make sure there are the right number of arguments. int nArgs = length(args); if (nArgs < minArgs) return error("too few args, " + nArgs + ", for " + this.name + ": " + args); else if (nArgs > maxArgs) return error("too many args, " + nArgs + ", for " + this.name + ": " + args); Object x = first(args); Object y = second(args); switch (idNumber) { //////////////// SECTION 6.1 BOOLEANS case NOT: return truth(x == FALSE); case BOOLEANQ: return truth(x == TRUE || x == FALSE); //////////////// SECTION 6.2 EQUIVALENCE PREDICATES case EQVQ: return truth(eqv(x, y)); case EQQ: return truth(x == y); case EQUALQ: return truth(equal(x, y)); //////////////// SECTION 6.3 LISTS AND PAIRS case PAIRQ: return truth(x instanceof Pair); case LISTQ: return truth(isList(x)); case CXR: for (int i = name.length() - 2; i >= 1; i--) x = (name.charAt(i) == 'a') ? first(x) : rest(x); return x; case CONS: return cons(x, y); case CAR: return first(x); case CDR: return rest(x); case SETCAR: return setFirst(x, y); case SETCDR: return setRest(x, y); case SECOND: return second(x); case THIRD: return third(x); case NULLQ: return truth(x == null); case LIST: return args; case LENGTH: return num(length(x)); case APPEND: return (args == null) ? null : append(args); case REVERSE: return reverse(x); case LISTTAIL: for (int k = (int) num(y); k > 0; k--) x = rest(x); return x; case LISTREF: for (int k = (int) num(y); k > 0; k--) x = rest(x); return first(x); case MEMQ: return memberAssoc(x, y, 'm', 'q'); case MEMV: return memberAssoc(x, y, 'm', 'v'); case MEMBER: return memberAssoc(x, y, 'm', ' '); case ASSQ: return memberAssoc(x, y, 'a', 'q'); case ASSV: return memberAssoc(x, y, 'a', 'v'); case ASSOC: return memberAssoc(x, y, 'a', ' '); //////////////// SECTION 6.4 SYMBOLS case SYMBOLQ: return truth(x instanceof String); case SYMBOLTOSTRING: return sym(x).toCharArray(); case STRINGTOSYMBOL: return new String(str(x)).intern(); //////////////// SECTION 6.5 NUMBERS case NUMBERQ: return truth(x instanceof Number); case ODDQ: return truth(Math.abs(num(x)) % 2 != 0); case EVENQ: return truth(Math.abs(num(x)) % 2 == 0); case ZEROQ: return truth(num(x) == 0); case POSITIVEQ: return truth(num(x) > 0); case NEGATIVEQ: return truth(num(x) < 0); case INTEGERQ: return truth(isExact(x)); case INEXACTQ: return truth(!isExact(x)); case LT: return numCompare(args, '<'); case GT: return numCompare(args, '>'); case EQ: return numCompare(args, '='); case LE: return numCompare(args, 'L'); case GE: return numCompare(args, 'G'); case MAX: return numCompute(args, 'X', num(x)); case MIN: return numCompute(args, 'N', num(x)); case PLUS: return numCompute(args, '+', 0.0); case MINUS: return numCompute(rest(args), '-', num(x)); case TIMES: return numCompute(args, '*', 1.0); case DIVIDE: return numCompute(rest(args), '/', num(x)); case QUOTIENT: double d = num(x) / num(y); return num(d > 0 ? Math.floor(d) : Math.ceil(d)); case REMAINDER: return num((long) num(x) % (long) num(y)); case MODULO: long xi = (long) num(x), yi = (long) num(y), m = xi % yi; return num((xi * yi > 0 || m == 0) ? m : m + yi); case ABS: return num(Math.abs(num(x))); case FLOOR: return num(Math.floor(num(x))); case CEILING: return num(Math.ceil(num(x))); case TRUNCATE: d = num(x); return num((d < 0.0) ? Math.ceil(d) : Math.floor(d)); case ROUND: return num(Math.round(num(x))); case EXP: return num(Math.exp(num(x))); case LOG: return num(Math.log(num(x))); case SIN: return num(Math.sin(num(x))); case COS: return num(Math.cos(num(x))); case TAN: return num(Math.tan(num(x))); case ASIN: return num(Math.asin(num(x))); case ACOS: return num(Math.acos(num(x))); case ATAN: return num(Math.atan(num(x))); case SQRT: return num(Math.sqrt(num(x))); case EXPT: return num(Math.pow(num(x), num(y))); case NUMBERTOSTRING: return numberToString(x, y); case STRINGTONUMBER: return stringToNumber(x, y); case GCD: return (args == null) ? ZERO : gcd(args); case LCM: return (args == null) ? ONE : lcm(args); //////////////// SECTION 6.6 CHARACTERS case CHARQ: return truth(x instanceof Character); case CHARALPHABETICQ: return truth(Character.isLetter(chr(x))); case CHARNUMERICQ: return truth(Character.isDigit(chr(x))); case CHARWHITESPACEQ: return truth(Character.isWhitespace(chr(x))); case CHARUPPERCASEQ: return truth(Character.isUpperCase(chr(x))); case CHARLOWERCASEQ: return truth(Character.isLowerCase(chr(x))); case CHARTOINTEGER: return new Double((double) chr(x)); case INTEGERTOCHAR: return chr((char) (int) num(x)); case CHARUPCASE: return chr(Character.toUpperCase(chr(x))); case CHARDOWNCASE: return chr(Character.toLowerCase(chr(x))); case CHARCMP + EQ: return truth(charCompare(x, y, false) == 0); case CHARCMP + LT: return truth(charCompare(x, y, false) < 0); case CHARCMP + GT: return truth(charCompare(x, y, false) > 0); case CHARCMP + GE: return truth(charCompare(x, y, false) >= 0); case CHARCMP + LE: return truth(charCompare(x, y, false) <= 0); case CHARCICMP + EQ: return truth(charCompare(x, y, true) == 0); case CHARCICMP + LT: return truth(charCompare(x, y, true) < 0); case CHARCICMP + GT: return truth(charCompare(x, y, true) > 0); case CHARCICMP + GE: return truth(charCompare(x, y, true) >= 0); case CHARCICMP + LE: return truth(charCompare(x, y, true) <= 0); case ERROR: return error(stringify(args)); //////////////// SECTION 6.7 STRINGS case STRINGQ: return truth(x instanceof char[]); case MAKESTRING: char[] str = new char[(int) num(x)]; if (y != null) { char c = chr(y); for (int i = str.length - 1; i >= 0; i--) str[i] = c; } return str; case STRING: return listToString(args); case STRINGLENGTH: return num(str(x).length); case STRINGREF: return chr(str(x)[(int) num(y)]); case STRINGSET: Object z = third(args); str(x)[(int) num(y)] = chr(z); return z; case SUBSTRING: int start = (int) num(y), end = (int) num(third(args)); return new String(str(x), start, end - start).toCharArray(); case STRINGAPPEND: return stringAppend(args); case STRINGTOLIST: Pair result = null; char[] str2 = str(x); for (int i = str2.length - 1; i >= 0; i--) result = cons(chr(str2[i]), result); return result; case LISTTOSTRING: return listToString(x); case STRINGCMP + EQ: return truth(stringCompare(x, y, false) == 0); case STRINGCMP + LT: return truth(stringCompare(x, y, false) < 0); case STRINGCMP + GT: return truth(stringCompare(x, y, false) > 0); case STRINGCMP + GE: return truth(stringCompare(x, y, false) >= 0); case STRINGCMP + LE: return truth(stringCompare(x, y, false) <= 0); case STRINGCICMP + EQ: return truth(stringCompare(x, y, true) == 0); case STRINGCICMP + LT: return truth(stringCompare(x, y, true) < 0); case STRINGCICMP + GT: return truth(stringCompare(x, y, true) > 0); case STRINGCICMP + GE: return truth(stringCompare(x, y, true) >= 0); case STRINGCICMP + LE: return truth(stringCompare(x, y, true) <= 0); //////////////// SECTION 6.8 VECTORS case VECTORQ: return truth(x instanceof Object[]); case MAKEVECTOR: Object[] vec = new Object[(int) num(x)]; if (y != null) { for (int i = 0; i < vec.length; i++) vec[i] = y; } return vec; case VECTOR: return listToVector(args); case VECTORLENGTH: return num(vec(x).length); case VECTORREF: return vec(x)[(int) num(y)]; case VECTORSET: return vec(x)[(int) num(y)] = third(args); case VECTORTOLIST: return vectorToList(x); case LISTTOVECTOR: return listToVector(x); //////////////// SECTION 6.9 CONTROL FEATURES case EVAL: return interp.eval(x); case FORCE: return (!(x instanceof Procedure)) ? x : proc(x) .apply(interp, null); case MACROEXPAND: return Macro.macroExpand(interp, x); case PROCEDUREQ: return truth(x instanceof Procedure); case APPLY: return proc(x).apply(interp, listStar(rest(args))); case MAP: return map(proc(x), rest(args), interp, list(null)); case FOREACH: return map(proc(x), rest(args), interp, null); case CALLCC: RuntimeException cc = new RuntimeException(); Continuation proc = new Continuation(cc); try { return proc(x).apply(interp, list(proc)); } catch (RuntimeException e) { if (e == cc) return proc.value; else throw e; } //////////////// SECTION 6.10 INPUT AND OUPUT case EOFOBJECTQ: return truth(x == InputPort.EOF); case INPUTPORTQ: return truth(x instanceof InputPort); case CURRENTINPUTPORT: return interp.input; case OPENINPUTFILE: return openInputFile(x); case CLOSEINPUTPORT: return inPort(x, interp).close(); case OUTPUTPORTQ: return truth(x instanceof PrintWriter); case CURRENTOUTPUTPORT: return interp.output; case OPENOUTPUTFILE: return openOutputFile(x); case CALLWITHOUTPUTFILE: PrintWriter p = null; try { p = openOutputFile(x); z = proc(y).apply(interp, list(p)); } finally { if (p != null) p.close(); } return z; case CALLWITHINPUTFILE: InputPort p2 = null; try { p2 = openInputFile(x); z = proc(y).apply(interp, list(p2)); } finally { if (p2 != null) p2.close(); } return z; case CLOSEOUTPUTPORT: outPort(x, interp).close(); return TRUE; case READCHAR: return inPort(x, interp).readChar(); case PEEKCHAR: return inPort(x, interp).peekChar(); case LOAD: return interp.load(x); case READ: return inPort(x, interp).read(); case EOF_OBJECT: return truth(InputPort.isEOF(x)); case WRITE: return write(x, outPort(y, interp), true); case DISPLAY: return write(x, outPort(y, interp), false); case NEWLINE: outPort(x, interp).println(); outPort(x, interp).flush(); return TRUE; //////////////// EXTENSIONS case CLASS: try { return Class.forName(stringify(x, false)); } catch (ClassNotFoundException e) { return FALSE; } case NEW: try { return JavaMethod.toClass(x).newInstance(); } catch (ClassCastException e) { ; } catch (NoSuchMethodError e) { ; } catch (InstantiationException e) { ; } catch (ClassNotFoundException e) { ; } catch (IllegalAccessException e) { ; } return FALSE; case METHOD: return new JavaMethod(stringify(x, false), y, rest(rest(args))); case EXIT: System.exit((x == null) ? 0 : (int) num(x)); case LISTSTAR: return listStar(args); case TIMECALL: Runtime runtime = Runtime.getRuntime(); runtime.gc(); long startTime = System.currentTimeMillis(); long startMem = runtime.freeMemory(); Object ans = FALSE; int nTimes = (y == null ? 1 : (int) num(y)); for (int i = 0; i < nTimes; i++) { ans = proc(x).apply(interp, null); } long time = System.currentTimeMillis() - startTime; long mem = startMem - runtime.freeMemory(); return cons(ans, list(list(num(time), "msec"), list(num(mem), "bytes"))); default: return error("internal error: unknown primitive: " + this + " applied to " + args); } } public static char[] stringAppend(Object args) { StringBuffer result = new StringBuffer(); for (; args instanceof Pair; args = rest(args)) { result.append(stringify(first(args), false)); } return result.toString().toCharArray(); } public static Object memberAssoc(Object obj, Object list, char m, char eq) { while (list instanceof Pair) { Object target = (m == 'm') ? first(list) : first(first(list)); boolean found; switch (eq) { case 'q': found = (target == obj); break; case 'v': found = eqv(target, obj); break; case ' ': found = equal(target, obj); break; default: warn("Bad option to memberAssoc:" + eq); return FALSE; } if (found) return (m == 'm') ? list : first(list); list = rest(list); } return FALSE; } public static Object numCompare(Object args, char op) { while (rest(args) instanceof Pair) { double x = num(first(args)); args = rest(args); double y = num(first(args)); switch (op) { case '>': if (!(x > y)) return FALSE; break; case '<': if (!(x < y)) return FALSE; break; case '=': if (!(x == y)) return FALSE; break; case 'L': if (!(x <= y)) return FALSE; break; case 'G': if (!(x >= y)) return FALSE; break; default: error("internal error: unrecognized op: " + op); break; } } return TRUE; } public static Object numCompute(Object args, char op, double result) { if (args == null) { switch (op) { case '-': return num(0 - result); case '/': return num(1 / result); default: return num(result); } } else { while (args instanceof Pair) { double x = num(first(args)); args = rest(args); switch (op) { case 'X': if (x > result) result = x; break; case 'N': if (x < result) result = x; break; case '+': result += x; break; case '-': result -= x; break; case '*': result *= x; break; case '/': result /= x; break; default: error("internal error: unrecognized op: " + op); break; } } return num(result); } } /** Return the sign of the argument: +1, -1, or 0. **/ static int sign(int x) { return (x > 0) ? +1 : (x < 0) ? -1 : 0; } /** Return <0 if x is alphabetically first, >0 if y is first, * 0 if same. Case insensitive iff ci is true. Error if not both chars. **/ public static int charCompare(Object x, Object y, boolean ci) { char xc = chr(x), yc = chr(y); if (ci) { xc = Character.toLowerCase(xc); yc = Character.toLowerCase(yc); } return xc - yc; } /** Return <0 if x is alphabetically first, >0 if y is first, * 0 if same. Case insensitive iff ci is true. Error if not strings. **/ public static int stringCompare(Object x, Object y, boolean ci) { if (x instanceof char[] && y instanceof char[]) { char[] xc = (char[]) x, yc = (char[]) y; for (int i = 0; i < xc.length; i++) { int diff = (!ci) ? xc[i] - yc[i] : Character.toUpperCase(xc[i]) - Character.toUpperCase(yc[i]); if (diff != 0) return diff; } return xc.length - yc.length; } else { error("expected two strings, got: " + stringify(list(x, y))); return 0; } } static Object numberToString(Object x, Object y) { int base = (y instanceof Number) ? (int) num(y) : 10; if (base != 10 || num(x) == Math.round(num(x))) { // An integer return Long.toString((long) num(x), base).toCharArray(); } else { // A floating point number return x.toString().toCharArray(); } } static Object stringToNumber(Object x, Object y) { int base = (y instanceof Number) ? (int) num(y) : 10; try { return (base == 10) ? Double.valueOf(stringify(x, false)) : num(Long.parseLong(stringify(x, false), base)); } catch (NumberFormatException e) { return FALSE; } } static Object gcd(Object args) { long gcd = 0; while (args instanceof Pair) { gcd = gcd2(Math.abs((long) num(first(args))), gcd); args = rest(args); } return num(gcd); } static long gcd2(long a, long b) { if (b == 0) return a; else return gcd2(b, a % b); } static Object lcm(Object args) { long L = 1, g = 1; while (args instanceof Pair) { long n = Math.abs((long) num(first(args))); g = gcd2(n, L); L = (g == 0) ? g : (n / g) * L; args = rest(args); } return num(L); } static boolean isExact(Object x) { if (!(x instanceof Double)) return false; double d = num(x); return (d == Math.round(d) && Math.abs(d) < 102962884861573423.0); } static PrintWriter openOutputFile(Object filename) { try { return new PrintWriter(new FileWriter(stringify(filename, false))); } catch (FileNotFoundException e) { return (PrintWriter) error("No such file: " + stringify(filename)); } catch (IOException e) { return (PrintWriter) error("IOException: " + e); } } static InputPort openInputFile(Object filename) { try { return new InputPort( new FileInputStream(stringify(filename, false))); } catch (FileNotFoundException e) { return (InputPort) error("No such file: " + stringify(filename)); } catch (IOException e) { return (InputPort) error("IOException: " + e); } } static boolean isList(Object x) { Object slow = x, fast = x; for (;;) { if (fast == null) return true; if (slow == rest(fast) || !(fast instanceof Pair) || !(slow instanceof Pair)) return false; slow = rest(slow); fast = rest(fast); if (fast == null) return true; if (!(fast instanceof Pair)) return false; fast = rest(fast); } } static Object append(Object args) { if (rest(args) == null) return first(args); else return append2(first(args), append(rest(args))); } static Object append2(Object x, Object y) { if (x instanceof Pair) return cons(first(x), append2(rest(x), y)); else return y; } /** Map proc over a list of lists of args, in the given interpreter. * If result is non-null, accumulate the results of each call there * and return that at the end. Otherwise, just return null. **/ static Pair map(Procedure proc, Object args, Scheme interp, Pair result) { Pair accum = result; if (rest(args) == null) { args = first(args); while (args instanceof Pair) { Object x = proc.apply(interp, list(first(args))); if (accum != null) accum = (Pair) (accum.rest = list(x)); args = rest(args); } } else { Procedure car = proc(interp.eval("car")), cdr = proc(interp .eval("cdr")); while (first(args) instanceof Pair) { Object x = proc.apply(interp, map(car, list(args), interp, list(null))); if (accum != null) accum = (Pair) (accum.rest = list(x)); args = map(cdr, list(args), interp, list(null)); } } return (Pair) rest(result); } }