001: // Copyright (c) 2000, 2001, 2003, 2005 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.kawa.functions;
005:
006: import gnu.math.*;
007: import java.math.*;
008: import gnu.mapping.*;
009: import gnu.expr.*;
010: import gnu.bytecode.*;
011: import gnu.kawa.lispexpr.LangPrimType;
012:
013: /**
014: * Implement the Scheme standard functions "+" and "-".
015: * @author Per Bothner
016: */
017:
018: public class AddOp extends ProcedureN implements CanInline, Inlineable {
019: int plusOrMinus = 1;
020:
021: public AddOp(String name, int plusOrMinus) {
022: setName(name);
023: this .plusOrMinus = plusOrMinus;
024: }
025:
026: public static final AddOp $Pl = new AddOp("+", 1);
027: public static final AddOp $Mn = new AddOp("-", -1);
028:
029: public static Object apply2(int plusOrMinus, Object arg1,
030: Object arg2) {
031: int code1 = Arithmetic.classifyValue(arg1);
032: int code2 = Arithmetic.classifyValue(arg2);
033: /*
034: if (code1 < 0 || code2 < 0)
035: throw new ClasscastException(); // FIXME
036: */
037: int code = code1 < code2 ? code2 : code1;
038: switch (code) {
039: case Arithmetic.INT_CODE:
040: int i1 = Arithmetic.asInt(arg1);
041: int i2 = Arithmetic.asInt(arg2);
042: return new Integer(plusOrMinus > 0 ? i1 + i2 : i1 - i2);
043: case Arithmetic.LONG_CODE:
044: long l1 = Arithmetic.asLong(arg1);
045: long l2 = Arithmetic.asLong(arg2);
046: return new Long(plusOrMinus > 0 ? l1 + l2 : l1 - l2);
047: case Arithmetic.BIGINTEGER_CODE:
048: BigInteger bi1 = Arithmetic.asBigInteger(arg1);
049: BigInteger bi2 = Arithmetic.asBigInteger(arg2);
050: return plusOrMinus > 0 ? bi1.add(bi2) : bi1.subtract(bi2);
051: case Arithmetic.INTNUM_CODE:
052: return IntNum.add(Arithmetic.asIntNum(arg1), Arithmetic
053: .asIntNum(arg2), plusOrMinus);
054: case Arithmetic.BIGDECIMAL_CODE:
055: BigDecimal bd1 = Arithmetic.asBigDecimal(arg1);
056: BigDecimal bd2 = Arithmetic.asBigDecimal(arg2);
057: return plusOrMinus > 0 ? bd1.add(bd2) : bd1.subtract(bd2);
058: case Arithmetic.RATNUM_CODE:
059: return RatNum.add(Arithmetic.asRatNum(arg1), Arithmetic
060: .asRatNum(arg2), plusOrMinus);
061: case Arithmetic.FLOAT_CODE:
062: float f1 = Arithmetic.asFloat(arg1);
063: float f2 = Arithmetic.asFloat(arg2);
064: return new Float(plusOrMinus > 0 ? f1 + f2 : f1 - f2);
065: case Arithmetic.DOUBLE_CODE:
066: double d1 = Arithmetic.asDouble(arg1);
067: double d2 = Arithmetic.asDouble(arg2);
068: return new Double(plusOrMinus > 0 ? d1 + d2 : d1 - d2);
069: case Arithmetic.FLONUM_CODE:
070: d1 = Arithmetic.asDouble(arg1);
071: d2 = Arithmetic.asDouble(arg2);
072: return new DFloNum(plusOrMinus > 0 ? d1 + d2 : d1 - d2);
073: default:
074: Numeric num1 = Arithmetic.asNumeric(arg1);
075: Numeric num2 = Arithmetic.asNumeric(arg2);
076: return num1.add(num2, plusOrMinus);
077: }
078: }
079:
080: public static Object $Pl(Object arg1, Object arg2) {
081: return apply2(1, arg1, arg2);
082: }
083:
084: public static Object $Mn(Object arg1, Object arg2) {
085: return apply2(-1, arg1, arg2);
086: }
087:
088: public static Object $Mn(Object arg1) {
089: return ((Numeric) arg1).neg();
090: }
091:
092: public static Object $Pl$V(Object arg1, Object arg2, Object arg3,
093: Object[] rest) {
094: return applyN(1, apply2(1, apply2(1, arg1, arg2), arg3), rest);
095: }
096:
097: public static Object $Mn$V(Object arg1, Object arg2, Object arg3,
098: Object[] rest) {
099: return applyN(-1, apply2(-1, apply2(-1, arg1, arg2), arg3),
100: rest);
101: }
102:
103: public static Object applyN(int plusOrMinus, Object[] args) {
104: int len = args.length;
105: if (len == 0)
106: return IntNum.zero();
107: Object result = args[0];
108: if (len == 1 && plusOrMinus < 0)
109: return $Mn(result);
110: for (int i = 1; i < len; i++)
111: result = apply2(plusOrMinus, result, args[i]);
112: return result;
113: }
114:
115: public static Object applyN(int plusOrMinus, Object init,
116: Object[] args) {
117: int len = args.length;
118: Object result = init;
119: for (int i = 0; i < len; i++)
120: result = apply2(plusOrMinus, result, args[i]);
121: return result;
122: }
123:
124: public Object applyN(Object[] args) {
125: return applyN(plusOrMinus, args);
126: }
127:
128: /** Convert (PROC A B C) to (PROC (PROC A B) C) etc. */
129:
130: public static Expression pairwise(Procedure proc, Expression rproc,
131: Expression[] args, ExpWalker walker) {
132: int len = args.length;
133: Expression prev = args[0];
134: for (int i = 1; i < len; i++) {
135: Expression[] args2 = new Expression[2];
136: args2[0] = prev;
137: args2[1] = args[i];
138: ApplyExp next = new ApplyExp(rproc, args2);
139: if (proc instanceof CanInline)
140: prev = ((CanInline) proc).inline(next, walker);
141: else
142: prev = next;
143: }
144: return prev;
145: }
146:
147: public Expression inline(ApplyExp exp, ExpWalker walker) {
148: // Inlining may yield PrimProcedure instructions of bytecode instructions
149: // which we don't know how to interpret (yet).
150: if (!walker.getCompilation().mustCompile)
151: return exp;
152: Expression folded = exp.inlineIfConstant(this , walker);
153: if (folded != exp)
154: return folded;
155: Expression[] args = exp.getArgs();
156: if (args.length > 2)
157: return pairwise(this , exp.getFunction(), args, walker);
158: if (args.length == 1 && plusOrMinus < 0) {
159: Type type0 = args[0].getType();
160: if (type0 instanceof PrimType) {
161: char sig0 = type0.getSignature().charAt(0);
162: Type type = null;
163: int opcode = 0;
164: if (sig0 == 'V' || sig0 == 'Z' || sig0 == 'C') {
165: // error
166: } else if (sig0 == 'D') {
167: opcode = 119 /* dneg */;
168: type = LangPrimType.doubleType;
169: } else if (sig0 == 'F') {
170: opcode = 118 /* fneg */;
171: type = LangPrimType.floatType;
172: } else if (sig0 == 'J') {
173: opcode = 117 /* lneg */;
174: type = LangPrimType.longType;
175: } else {
176: opcode = 116 /* ineg */;
177: type = LangPrimType.intType;
178: }
179: if (type != null) {
180: PrimProcedure prim = PrimProcedure
181: .makeBuiltinUnary(opcode, type);
182: return new ApplyExp(prim, args);
183: }
184: }
185: }
186: if (args.length == 2) {
187: return primInline(
188: plusOrMinus > 0 ? 96 /* iadd */: 100 /* isub */,
189: exp);
190: }
191: return exp;
192: }
193:
194: public static Expression primInline(int opcode, ApplyExp exp) {
195: Expression[] args = exp.getArgs();
196: if (args.length == 2) {
197: Type type0 = args[0].getType();
198: Type type1 = args[1].getType();
199: if (type0 instanceof PrimType && type1 instanceof PrimType) {
200: char sig0 = type0.getSignature().charAt(0);
201: char sig1 = type1.getSignature().charAt(0);
202: Type type = null;
203: if (sig0 == 'V' || sig0 == 'Z' || sig0 == 'C'
204: || sig1 == 'V' || sig1 == 'Z' || sig1 == 'C') {
205: // error
206: } else if (sig0 == 'D' || sig1 == 'D') {
207: opcode += 3;
208: type = LangPrimType.doubleType;
209: } else if (sig0 == 'F' || sig1 == 'F') {
210: opcode += 2;
211: type = LangPrimType.floatType;
212: } else if (sig0 == 'J' || sig1 == 'J') {
213: opcode += 1;
214: type = LangPrimType.longType;
215: } else {
216: type = LangPrimType.intType;
217: }
218: if (type != null) {
219: PrimProcedure prim = PrimProcedure
220: .makeBuiltinBinary(opcode, type);
221: return new ApplyExp(prim, args);
222: }
223: }
224: }
225: return exp;
226: }
227:
228: /*
229: static ClassType typeInteger = ClassType.make("java.lang.Integer");
230: static ClassType typeLong = ClassType.make("java.lang.Long");
231: */
232: static ClassType typeIntNum = ClassType.make("gnu.math.IntNum");
233: static ClassType typeDFloNum = ClassType.make("gnu.math.DFloNum");
234: static ClassType typeRealNum = ClassType.make("gnu.math.RealNum");
235: static ClassType typeNumeric = ClassType.make("gnu.math.Numeric");
236:
237: public void compile(ApplyExp exp, Compilation comp, Target target) {
238: Expression[] args = exp.getArgs();
239: int len = args.length;
240: if (len == 0) {
241: comp.compileConstant(IntNum.zero(), target);
242: return;
243: }
244: Type type = getReturnType(args);
245: Type ttype = target.getType();
246: if (len == 1 || target instanceof IgnoreTarget) {
247: // FIXME implement optimization for unary
248: ApplyExp.compile(exp, comp, target);
249: return;
250: }
251: PrimType ptype = null;
252: if (ttype instanceof PrimType) {
253: char sig = type.getSignature().charAt(0);
254: if (sig == 'V' || sig == 'Z' || sig == 'C')
255: ptype = null; // error
256: else if (sig == 'D' || sig == 'F') {
257: if (type.isSubtype(typeRealNum))
258: ptype = LangPrimType.doubleType;
259: } else {
260: if (type.isSubtype(typeIntNum))
261: ptype = sig == 'J' ? LangPrimType.longType
262: : LangPrimType.intType;
263: }
264: }
265: if (ptype != null) {
266: CodeAttr code = comp.getCode();
267: // FIXME would be nice to use iinc when appropriate!
268: // We would need to use a special LocalVariableTarget,
269: // created by SetExp when dest is a local variable.
270: // Then if len==2 && ptype==LangPrimType.intType
271: // && target instanceof LocalVariableTarget
272: // && one arg is QuoteExp && other arg is same local as target
273: // => then emit iinc.
274: args[0].compile(comp, ttype);
275: for (int i = 1; i < len; i++) {
276: args[i].compile(comp, ptype);
277: if (plusOrMinus > 0)
278: code.emitAdd(ptype);
279: else
280: code.emitSub(ptype);
281: }
282: target.compileFromStack(comp, ttype);
283: } else if (type.isSubtype(typeDFloNum)) {
284: PrimType dtype = LangPrimType.doubleType;
285: Target dtarget = new StackTarget(dtype);
286: CodeAttr code = comp.getCode();
287: args[0].compile(comp, dtarget);
288: for (int i = 1; i < len; i++) {
289: args[i].compile(comp, dtarget);
290: if (plusOrMinus > 0)
291: code.emitAdd(dtype);
292: else
293: code.emitSub(dtype);
294: }
295: target.compileFromStack(comp, dtype);
296: } else
297: ApplyExp.compile(exp, comp, target);
298: }
299:
300: /** Classify an expression according to its numeric type.
301: * kind==0: not a number.
302: * kind==1: a non-real number
303: * kind==2: real number
304: * kind==3: floating-point
305: * kind==4: exact integer
306: */
307: public static int classify(Type type) {
308: int kind = 0;
309: if (type instanceof PrimType) {
310: char sig = type.getSignature().charAt(0);
311: if (sig == 'V' || sig == 'Z' || sig == 'C')
312: return 0;
313: else if (sig == 'D' || sig == 'F')
314: return 3;
315: else
316: return 4;
317: } else if (type.isSubtype(typeIntNum))
318: return 4;
319: else if (type.isSubtype(typeDFloNum))
320: return 3;
321: else if (type.isSubtype(typeRealNum))
322: return 2;
323: else if (type.isSubtype(typeNumeric))
324: return 1;
325: else
326: return 0;
327: }
328:
329: public Type getReturnType(Expression[] args) {
330: int len = args.length;
331: if (len == 0)
332: return typeIntNum;
333: Type type = Type.pointer_type;
334: int kind0 = 0;
335: for (int i = 0; i < len; i++) {
336: Expression arg = args[i];
337: int kind = classify(arg.getType());
338:
339: if (kind == 0)
340: return Type.pointer_type;
341:
342: if (i == 0)
343: kind0 = kind;
344:
345: if (kind0 == 4 && kind == 4)
346: type = typeIntNum;
347: else if (kind0 >= 2 && kind >= 2) {
348: if (kind0 == 3 || kind == 3) {
349: type = typeDFloNum;
350: kind0 = 3;
351: } else {
352: type = typeRealNum;
353: kind0 = 2;
354: }
355: } else if (kind0 >= 1 && kind >= 1) {
356: type = typeNumeric;
357: kind0 = 1;
358: } else
359: return Type.pointer_type;
360: }
361: return type;
362: }
363: }
|