001: // Copyright (c) 2004 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.mapping;
005:
006: /**
007: * The abstract parent for all Scheme functions.
008: * @author Per Bothner
009: */
010:
011: public abstract class Procedure extends PropertySet {
012: private static final String sourceLocationKey = "source-location";
013: private static final String setterKey = "setter";
014:
015: public void setSourceLocation(String file, int line) {
016: setProperty(sourceLocationKey, file + ":" + line);
017: }
018:
019: public String getSourceLocation() {
020: Object value = getProperty(sourceLocationKey, null);
021: return value == null ? null : value.toString();
022: }
023:
024: public Procedure() {
025: }
026:
027: public Procedure(String n) {
028: setName(n);
029: }
030:
031: public abstract Object applyN(Object[] args) throws Throwable;
032:
033: public abstract Object apply0() throws Throwable;
034:
035: public abstract Object apply1(Object arg1) throws Throwable;
036:
037: public abstract Object apply2(Object arg1, Object arg2)
038: throws Throwable;
039:
040: public abstract Object apply3(Object arg1, Object arg2, Object arg3)
041: throws Throwable;
042:
043: public abstract Object apply4(Object arg1, Object arg2,
044: Object arg3, Object arg4) throws Throwable;
045:
046: /** Minimum number of arguments required. */
047: public final int minArgs() {
048: return numArgs() & 0xFFF;
049: }
050:
051: /** Maximum number of arguments allowed, or -1 for unlimited.
052: * (May also return -1 if there are keyword arguments, for implementation
053: * reasons.) */
054: public final int maxArgs() {
055: return numArgs() >> 12;
056: }
057:
058: /** Check that the number of arguments in a call is valid.
059: * @param proc the Procedure being called
060: * @param argCount the number of arguments in the call
061: * @exception WrongArguments there are too many or too
062: * few actual arguments
063: */
064: public static void checkArgCount(Procedure proc, int argCount) {
065: int num = proc.numArgs();
066: if (argCount < (num & 0xFFF)
067: || (num >= 0 && argCount > (num >> 12)))
068: throw new WrongArguments(proc, argCount);
069: }
070:
071: /** Return minArgs()|(maxArgs<<12). */
072:
073: /* We use a single virtual function to reduce the number of methods
074: * in the system, as well as the number of virtual method table entries.
075: * We shift by 12 so the number can normally be represented using a
076: * sipush instruction, without requiring a constant pool entry.
077: */
078: public int numArgs() {
079: return 0xfffff000;
080: }
081:
082: /* CPS: ??
083: public void apply1(Object arg, CallContext stack, CallFrame rlink, int rpc)
084: {
085: context.value = apply1(arg);
086: context.frame = rlink;
087: context.pc = rpc;
088: }
089: */
090:
091: /** Call this Procedure using the explicit-CallContext-convention.
092: * The input arguments are (by default) in stack.args;
093: * the result is written to ctx.consumer. */
094:
095: public void apply(CallContext ctx) throws Throwable {
096: apply(this , ctx);
097: }
098:
099: public static void apply(Procedure proc, CallContext ctx)
100: throws Throwable {
101: Object result;
102: int count = ctx.count;
103: if (ctx.where == 0 && count != 0)
104: result = proc.applyN(ctx.values);
105: else {
106: switch (count) {
107: case 0:
108: result = proc.apply0();
109: break;
110: case 1:
111: result = proc.apply1(ctx.getNextArg());
112: break;
113: case 2:
114: result = proc
115: .apply2(ctx.getNextArg(), ctx.getNextArg());
116: break;
117: case 3:
118: result = proc.apply3(ctx.getNextArg(),
119: ctx.getNextArg(), ctx.getNextArg());
120: break;
121: case 4:
122: result = proc.apply4(ctx.getNextArg(),
123: ctx.getNextArg(), ctx.getNextArg(), ctx
124: .getNextArg());
125: break;
126: default:
127: result = proc.applyN(ctx.getArgs());
128: break;
129: }
130: }
131: ctx.writeValue(result);
132: }
133:
134: /** Pass zero arguments.
135: * @return non-negative if the match succeeded, else negative.
136: */
137: public int match0(CallContext ctx) {
138: int num = numArgs();
139: int min = num & 0xFFF;
140: if (min > 0)
141: return MethodProc.NO_MATCH_TOO_FEW_ARGS | min;
142: if (num < 0)
143: return matchN(ProcedureN.noArgs, ctx);
144: ctx.count = 0;
145: ctx.where = 0;
146: ctx.next = 0;
147: ctx.proc = this ;
148: return 0;
149: }
150:
151: /** Pass one argument.
152: * @return non-negative if the match succeeded, else negative.
153: */
154: public int match1(Object arg1, CallContext ctx) {
155: int num = numArgs();
156: int min = num & 0xFFF;
157: if (min > 1)
158: return MethodProc.NO_MATCH_TOO_FEW_ARGS | min;
159: if (num >= 0) {
160: int max = num >> 12;
161: if (max < 1)
162: return MethodProc.NO_MATCH_TOO_MANY_ARGS | max;
163: ctx.value1 = arg1;
164: ctx.count = 1;
165: ctx.where = CallContext.ARG_IN_VALUE1;
166: ctx.next = 0;
167: ctx.proc = this ;
168: return 0;
169: }
170: Object[] args = { arg1 };
171: return matchN(args, ctx);
172: }
173:
174: /** Pass two arguments.
175: * @return non-negative if the match succeeded, else negative.
176: */
177: public int match2(Object arg1, Object arg2, CallContext ctx) {
178: int num = numArgs();
179: int min = num & 0xFFF;
180: if (min > 2)
181: return MethodProc.NO_MATCH_TOO_FEW_ARGS | min;
182: if (num >= 0) {
183: int max = num >> 12;
184: if (max < 2)
185: return MethodProc.NO_MATCH_TOO_MANY_ARGS | max;
186: ctx.value1 = arg1;
187: ctx.value2 = arg2;
188: ctx.count = 2;
189: ctx.where = CallContext.ARG_IN_VALUE1
190: | (CallContext.ARG_IN_VALUE2 << 4);
191: ctx.next = 0;
192: ctx.proc = this ;
193: return 0;
194: }
195: Object[] args = { arg1, arg2 };
196: return matchN(args, ctx);
197: }
198:
199: /** Pass three arguments.
200: * @return non-negative if the match succeeded, else negative.
201: */
202: public int match3(Object arg1, Object arg2, Object arg3,
203: CallContext ctx) {
204: int num = numArgs();
205: int min = num & 0xFFF;
206: if (min > 3)
207: return MethodProc.NO_MATCH_TOO_FEW_ARGS | min;
208: if (num >= 0) {
209: int max = num >> 12;
210: if (max < 3)
211: return MethodProc.NO_MATCH_TOO_MANY_ARGS | max;
212: ctx.value1 = arg1;
213: ctx.value2 = arg2;
214: ctx.value3 = arg3;
215: ctx.count = 3;
216: ctx.where = CallContext.ARG_IN_VALUE1
217: | (CallContext.ARG_IN_VALUE2 << 4)
218: | (CallContext.ARG_IN_VALUE3 << 8);
219: ctx.next = 0;
220: ctx.proc = this ;
221: return 0;
222: }
223: Object[] args = { arg1, arg2, arg3 };
224: return matchN(args, ctx);
225: }
226:
227: /** Pass four arguments.
228: * @return non-negative if the match succeeded, else negative.
229: */
230: public int match4(Object arg1, Object arg2, Object arg3,
231: Object arg4, CallContext ctx) {
232: int num = numArgs();
233: int min = num & 0xFFF;
234: if (min > 4)
235: return MethodProc.NO_MATCH_TOO_FEW_ARGS | min;
236: if (num >= 0) {
237: int max = num >> 12;
238: if (max < 4)
239: return MethodProc.NO_MATCH_TOO_MANY_ARGS | max;
240: ctx.value1 = arg1;
241: ctx.value2 = arg2;
242: ctx.value3 = arg3;
243: ctx.value4 = arg4;
244: ctx.count = 4;
245: ctx.where = (CallContext.ARG_IN_VALUE1
246: | (CallContext.ARG_IN_VALUE2 << 4)
247: | (CallContext.ARG_IN_VALUE3 << 8) | (CallContext.ARG_IN_VALUE4 << 12));
248: ctx.next = 0;
249: ctx.proc = this ;
250: return 0;
251: }
252: Object[] args = { arg1, arg2, arg3, arg4 };
253: return matchN(args, ctx);
254: }
255:
256: public int matchN(Object[] args, CallContext ctx) {
257: int num = numArgs();
258: int min = num & 0xFFF;
259: if (args.length < min)
260: return MethodProc.NO_MATCH_TOO_FEW_ARGS | min;
261: if (num >= 0) {
262: switch (args.length) {
263: case 0:
264: return match0(ctx);
265: case 1:
266: return match1(args[0], ctx);
267: case 2:
268: return match2(args[0], args[1], ctx);
269: case 3:
270: return match3(args[0], args[1], args[2], ctx);
271: case 4:
272: return match4(args[0], args[1], args[2], args[3], ctx);
273: default:
274: int max = num >> 12;
275: if (args.length > max)
276: return MethodProc.NO_MATCH_TOO_MANY_ARGS | max;
277: }
278: }
279: ctx.values = args;
280: ctx.count = args.length;
281: ctx.where = 0;
282: ctx.next = 0;
283: ctx.proc = this ;
284: return 0;
285: }
286:
287: /** Does match0, plus throws exception on argument mismatch. */
288: public void check0(CallContext ctx) {
289: int code = match0(ctx);
290: if (code != 0) {
291: throw MethodProc.matchFailAsException(code, this ,
292: ProcedureN.noArgs);
293: }
294: }
295:
296: /** Does match1, plus throws exception on argument mismatch. */
297: public void check1(Object arg1, CallContext ctx) {
298: int code = match1(arg1, ctx);
299: if (code != 0) {
300: Object[] args = { arg1 };
301: throw MethodProc.matchFailAsException(code, this , args);
302: }
303: }
304:
305: /** Does match, plus throws exception on argument mismatch. */
306: public void check2(Object arg1, Object arg2, CallContext ctx) {
307: int code = match2(arg1, arg2, ctx);
308: if (code != 0) {
309: Object[] args = { arg1, arg2 };
310: throw MethodProc.matchFailAsException(code, this , args);
311: }
312: }
313:
314: /** Does match3, plus throws exception on argument mismatch. */
315: public void check3(Object arg1, Object arg2, Object arg3,
316: CallContext ctx) {
317: int code = match3(arg1, arg2, arg3, ctx);
318: if (code != 0) {
319: Object[] args = { arg1, arg2, arg3 };
320: throw MethodProc.matchFailAsException(code, this , args);
321: }
322: }
323:
324: /** Does match4, plus throws exception on argument mismatch. */
325: public void check4(Object arg1, Object arg2, Object arg3,
326: Object arg4, CallContext ctx) {
327: int code = match4(arg1, arg2, arg3, arg4, ctx);
328: if (code != 0) {
329: Object[] args = { arg1, arg2, arg3, arg4 };
330: throw MethodProc.matchFailAsException(code, this , args);
331: }
332: }
333:
334: /** Does matchN, plus throws exception on argument mismatch. */
335: public void checkN(Object[] args, CallContext ctx) {
336: int code = matchN(args, ctx);
337: if (code != 0) {
338: throw MethodProc.matchFailAsException(code, this , args);
339: }
340: }
341:
342: public Procedure getSetter() {
343: if (!(this instanceof HasSetter)) {
344: Object setter = getProperty(setterKey, null);
345: if (setter instanceof Procedure)
346: return (Procedure) setter;
347: throw new RuntimeException("procedure '" + getName()
348: + "' has no setter");
349: }
350: int num_args = numArgs();
351: if (num_args == 0x0000)
352: return new Setter0(this );
353: if (num_args == 0x1001)
354: return new Setter1(this );
355: return new Setter(this );
356: }
357:
358: public void setSetter(Procedure setter) {
359: if (this instanceof HasSetter)
360: throw new RuntimeException("procedure '" + getName()
361: + "' has builtin setter - cannot be modified");
362: setProperty(Procedure.setterKey, setter);
363: }
364:
365: /** If HasSetter, the Procedure is called in the LHS of an assignment. */
366: public void set0(Object result) throws Throwable {
367: getSetter().apply1(result);
368: }
369:
370: public void set1(Object arg1, Object value) throws Throwable {
371: getSetter().apply2(arg1, value);
372: }
373:
374: public void setN(Object[] args) throws Throwable {
375: getSetter().applyN(args);
376: }
377:
378: public String toString() {
379: StringBuffer sbuf = new StringBuffer();
380: sbuf.append("#<procedure ");
381: String n = getName();
382: if (n == null)
383: n = getSourceLocation();
384: if (n == null)
385: n = getClass().getName();
386: sbuf.append(n);
387: sbuf.append('>');
388: return sbuf.toString();
389: }
390: }
|