001: package jsint;
002:
003: /**
004: * Abstract superclass of Procedures. Procedures of no arguments,
005: * called "thunks", implement the Runnable interface.
006: *
007: * To invoke a Procedure from Java, use apply(Pair).
008: *
009: * @author Peter Norvig, Copyright 1998, peter@norvig.com, <a href="license.txt">license</a>
010: * subsequently modified by Jscheme project members
011: * licensed under zlib licence (see license.txt)
012: **/
013: public abstract class Procedure implements Runnable,
014: java.io.Serializable, jscheme.SchemeProcedure {
015:
016: public String name = "??";
017: public int minArgs = 0;
018: public int maxArgs = Integer.MAX_VALUE;
019:
020: // bogus.
021: public Procedure() {
022: ;
023: }
024:
025: public Procedure(int minArgs, int maxArgs) {
026: this .minArgs = minArgs;
027: this .maxArgs = maxArgs;
028: }
029:
030: // end bogus.
031:
032: public String getName() {
033: return this .name;
034: }
035:
036: /** If the procedure has not yet been named, name it.
037: * The idea is if I do <tt>(define (id x) x)</tt> and then
038: * <tt>(define default-proc id)</tt>, then the procedure is called "id". **/
039: public Object setName(Object name) {
040: if (this .name == "??")
041: this .name = name.toString();
042: return name;
043: }
044:
045: /** How many parameters does this procedure have. This is different than the
046: * number of arguments it expects. For example, <tt>list</tt> expects any
047: * number of arguments, but it has only one parameter. **/
048: public int nParms() {
049: return (minArgs == maxArgs) ? minArgs : minArgs + 1;
050: }
051:
052: protected String toStringArgs() {
053: if (minArgs == maxArgs)
054: return "[" + minArgs + "]";
055: else
056: return "["
057: + minArgs
058: + ","
059: + ((maxArgs == Integer.MAX_VALUE) ? "n" : String
060: .valueOf(maxArgs)) + "]";
061: }
062:
063: public String toString() {
064: return "{" + this .getClass().getName() + " " + name
065: + toStringArgs() + "}";
066: }
067:
068: public void run() {
069: if (this .minArgs == 0)
070: this .apply(Pair.EMPTY);
071: else
072: E.error("This procedure can't be run()", this );
073: }
074:
075: /** Apply the procedure to an argument list, which is represented as a
076: * parameter-oriented array. That is, if the procedure p has the parameter
077: * list <tt>(x y . z)</tt> and the call is <tt>(p 1 2 3 4)</tt> then
078: * <tt>args</tt> will be the array <tt>{1, 2, (3 4)}</tt>. **/
079: public abstract Object apply(Object[] args);
080:
081: public Object apply(jscheme.SchemePair args) {
082: return apply(this .makeArgArray((Pair) args));
083: }
084:
085: /** Convert an argument list into an argument array, and call <tt>apply</tt>
086: * on that. **/
087: public Object apply(Pair args) {
088: return apply(this .makeArgArray(args));
089: }
090:
091: /** Like tryCatch, but returns wrapped exception. */
092: public static Object catching(Procedure E, Procedure F) {
093: try {
094: return E.apply(Pair.EMPTY);
095: } catch (Throwable e) {
096: return F.apply(new Pair(e, Pair.EMPTY));
097: }
098: }
099:
100: /** provide scheme access to the "try/catch" expression of Java */
101: public static Object tryCatch(Object E, Object F) {
102: try {
103: return ((Procedure) E).apply(Pair.EMPTY);
104: } catch (Throwable e) {
105: return ((Procedure) F).apply(new Pair(
106: stripExceptionWrapper(e), Pair.EMPTY));
107: }
108: }
109:
110: /* this strips off the wrappers so that the tryCatch receives the
111: underlying exception */
112: public static Object stripExceptionWrapper(Object e) {
113: if (e instanceof BacktraceException)
114: return stripExceptionWrapper(((BacktraceException) e)
115: .getBaseException());
116: else if (e instanceof JschemeThrowable)
117: return stripExceptionWrapper(((JschemeThrowable) e).contents);
118: else
119: return e;
120: }
121:
122: /** Provide scheme access to finally - unwind-protect. **/
123: public static Object tryFinally(Object e, Object f) {
124: try {
125: return ((Procedure) e).apply(Pair.EMPTY);
126: } finally {
127: ((Procedure) f).apply(Pair.EMPTY);
128: }
129: }
130:
131: /** provide scheme access to the exception throwing */
132: public static Object throwRuntimeException(RuntimeException E)
133: throws RuntimeException {
134: throw (E);
135: }
136:
137: public static Object throwObject(Object e) throws Throwable {
138: if (e instanceof Throwable)
139: throw ((Throwable) e);
140: else
141: return E.error("can't throw object " + e);
142: }
143:
144: public static Object synchronize(Object x, Procedure p) {
145: synchronized (x) {
146: return p.apply(new Pair(x, Pair.EMPTY));
147: }
148: }
149:
150: /** This is called during function application in Evaluator.execute. **/
151: /** Take the code that represents a call, evaluate arguments in the calling
152: * lexical environment and put results into an argument array. Note that
153: * <tt>code[0]</tt> is the procedure, not an argument. For example,
154: * <tt>(p 1 2 3 (+ 2 2))</tt> turns into the code <tt>{p, 1, 2, 3, {+, 2, 2}}</tt>,
155: * and if <tt>p</tt> has the parameter list <tt>(x y . z)</tt> then
156: * <tt>p.makeArgArray(code, lexenv)</tt> would return <tt>{1, 2, (3 4)}</tt>. **/
157:
158: public Object[] makeArgArray(Object[] code, Evaluator eval,
159: LexicalEnvironment lexenv) {
160: // KRA 07MAR02: Inlined to reduce stack space.
161: // return Scheme.makeArgArray(this, code, lexenv);
162: Procedure p = this ;
163: Object[] argArray = new Object[p.nParms()];
164: int pminargs = p.minArgs;
165: int nargs = code.length - 1;
166: if (nargs < pminargs)
167: E.error("\nToo few arguments to procedure " + p.name
168: + " expected at least " + pminargs + ", but found "
169: + nargs + " arguments:\n***************\n "
170: + U.stringify(code) + "\n************\n");
171: if (nargs > p.maxArgs)
172: E.error("\nToo many arguments to procedure " + p.name
173: + " expected at most " + p.maxArgs + ", but found "
174: + nargs + " arguments:\n***************\n "
175: + U.stringify(code) + "\n************\n");
176:
177: // Fill in the required parameters
178: for (int i = 0; i < pminargs; i++) {
179: argArray[i] = eval.execute(code[i + 1], lexenv);
180: }
181: // Add the remaining parameter (if there is one)
182: if (p.maxArgs > pminargs) {
183: if (p.maxArgs == pminargs + 1) // single optional argument for primitive.
184: argArray[pminargs] = (code.length > p.maxArgs) ? eval
185: .execute(code[pminargs + 1], lexenv)
186: : U.MISSING;
187: else { // "rest" argument
188: Object tail = Pair.EMPTY;
189: for (int i = nargs; i > pminargs; i--)
190: tail = new Pair(eval.execute(code[i], lexenv), tail);
191: argArray[pminargs] = tail;
192:
193: }
194: }
195: return argArray;
196: }
197:
198: /** Convert a list of arguments into an array expected by the procedure.
199: * (See <tt>apply</tt> for an explanation of what is expected.) **/
200: public Object[] makeArgArray(Pair args) {
201: Procedure p = this ;
202: Object[] argArray = new Object[p.nParms()];
203: int nargs = args.length();
204: if (nargs < p.minArgs)
205: E.error("\nToo few arguments to procedure " + p.name
206: + " expected at least " + p.minArgs
207: + ", but found " + nargs
208: + " arguments:\n***************\n "
209: + U.stringify(args) + "\n************\n");
210: if (nargs > p.maxArgs)
211: E.error("\nToo many arguments to procedure " + p.name
212: + " expected at most " + p.maxArgs + ", but found "
213: + nargs + " arguments:\n***************\n "
214: + U.stringify(args) + "\n************\n");
215: // Fill in the required parameters
216: for (int i = 0; i < p.minArgs; i++, args = U.toList(args.rest)) {
217: argArray[i] = args.first;
218: }
219: // Add the remaining parameter (if there is one)
220: if (p.maxArgs > p.minArgs) {
221: if (p.maxArgs == p.minArgs + 1) // single optional argument
222: argArray[p.minArgs] = (U.isPair(args)) ? args.first
223: : U.MISSING;
224: else
225: // "rest" argument
226: argArray[p.minArgs] = args;
227: }
228: return argArray;
229: }
230: }
|