public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { if (IsCacheNeeded) { // Call CompileToDoubleOrNan via base class CompileToDoubleProper base.CompileToDoubleProper(ifProper, ifOther); } else { expr.CompileToDoubleProper(ifProper, ifOther); } }
// Special case for the argumentless and always-proper // functions RAND and NOW, often used in conditions public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { if (es.Length == 0 && (functionInfo.name == "RAND" || functionInfo.name == "NOW")) { ilg.Emit(OpCodes.Call, functionInfo.methodInfo); ifProper.Generate(ilg); } else { base.CompileToDoubleProper(ifProper, ifOther); } }
public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { if (IsCacheNeeded) { // Call CompileToDoubleProper via base class CompileCondition base.CompileCondition(ifTrue, ifFalse, ifOther); } else { expr.CompileCondition(ifTrue, ifFalse, ifOther); } }
public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { for (int i = es.Length - 1; i >= 0; i--) { // These declarations are needed to capture rvalues rather than lvalues: CGExpr ei = es[i]; Gen localIfFalse = ifFalse; ifFalse = new Gen(delegate { ei.CompileCondition(ifTrue, localIfFalse, ifOther); }); } ifFalse.Generate(ilg); }
public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { if (double.IsInfinity(number.value) || double.IsNaN(number.value)) { ilg.Emit(OpCodes.Ldc_R8, number.value); ilg.Emit(OpCodes.Stloc, testDouble); ifOther.Generate(ilg); } else { ilg.Emit(OpCodes.Ldc_R8, number.value); ifProper.Generate(ilg); } }
// This handles compilation of IF(IF(e00,e01,e02), e1, e2) and such public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { if (es.Length != 3) { SetArgCountErrorNan(); ifOther.Generate(ilg); } else { es[0].CompileCondition( new Gen(delegate { es[1].CompileCondition(ifTrue, ifFalse, ifOther); }), new Gen(delegate { es[2].CompileCondition(ifTrue, ifFalse, ifOther); }), ifOther); } }
public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { CompileCondition( new Gen(delegate { ilg.Emit(OpCodes.Ldc_R8, 1.0); ifProper.Generate(ilg); }), new Gen(delegate { ilg.Emit(OpCodes.Ldc_R8, 0.0); ifProper.Generate(ilg); }), ifOther); }
public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { if (Double.IsInfinity(number.value) || Double.IsNaN(number.value)) { ilg.Emit(OpCodes.Ldc_R8, number.value); ilg.Emit(OpCodes.Stloc, testDouble); ifOther.Generate(ilg); } else if (number.value != 0) { ifTrue.Generate(ilg); } else { ifFalse.Generate(ilg); } }
public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { if (es.Length != Arity) { SetArgCountErrorNan(); ifOther.Generate(ilg); } else { es[0].CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Neg); ifProper.Generate(ilg); }), ifOther); } }
// A comparison evaluates to a proper double only if both operands do public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { es[0].CompileToDoubleProper( new Gen(delegate { es[1].CompileToDoubleProper( new Gen(delegate { GenCompareDouble(); ilg.Emit(OpCodes.Conv_R8); ifProper.Generate(ilg); }), new Gen(delegate { ilg.Emit(OpCodes.Pop); ifOther.Generate(ilg); })); }), ifOther); }
// General version in terms of CompileToDoubleOrNan. // Should be overridden in CGNumberConst, CGTextConst, CGError, CGComposite ... /// <summary> /// Compile expression that is expected to evaluate to a proper (finite /// and non-NaN) number; generate code to test whether it is actually a /// proper number and then execute the code generated by ifProper, or /// else execute the code generated by ifOther. /// </summary> /// <param name="ifProper">Generates code for the case where the expression /// evaluates to a proper number; the generated code expects to find the value as /// an unwrapped proper float64 on the stack top.</param> /// <param name="ifOther"></param> public virtual void CompileToDoubleProper(Gen ifProper, Gen ifOther) { CompileToDoubleOrNan(); ilg.Emit(OpCodes.Stloc, testDouble); ilg.Emit(OpCodes.Ldloc, testDouble); ilg.Emit(OpCodes.Call, isInfinityMethod); ilg.Emit(OpCodes.Brtrue, ifOther.GetLabel(ilg)); ilg.Emit(OpCodes.Ldloc, testDouble); ilg.Emit(OpCodes.Call, isNaNMethod); ilg.Emit(OpCodes.Brtrue, ifOther.GetLabel(ilg)); ilg.Emit(OpCodes.Ldloc, testDouble); ifProper.Generate(ilg); if (!ifOther.Generated) { Label endLabel = ilg.DefineLabel(); ilg.Emit(OpCodes.Br, endLabel); ifOther.Generate(ilg); ilg.MarkLabel(endLabel); } }
// This override combines the ordering predicate and the conditional jump public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { es[0].CompileToDoubleProper( new Gen(delegate { es[1].CompileToDoubleProper( new Gen(delegate { GenDoubleFalseJump(ifFalse.GetLabel(ilg)); ifTrue.Generate(ilg); if (!ifFalse.Generated) { Label endLabel = ilg.DefineLabel(); ilg.Emit(OpCodes.Br, endLabel); ifFalse.Generate(ilg); ilg.MarkLabel(endLabel); } }), new Gen(delegate { ilg.Emit(OpCodes.Pop); ifOther.Generate(ilg); })); }), ifOther); }
public override void CompileToDoubleOrNan() { Gen success = new Gen(delegate { ilg.Emit(OpCodes.Call, functionInfo.methodInfo); if (functionInfo.signature.retType != Typ.Number) { UnwrapToDoubleOrNan(); } }); if (Arity < 0) { // Variable arity CompileToValueArray(es.Length, 0, es); success.Generate(ilg); } else if (es.Length != Arity) { LoadErrorNan(ErrorValue.argCountError); } else { // TODO: ifOther should probably load error from testValue instead? Gen ifOther = GenLoadErrorNan(ErrorValue.argTypeError); CompileArgumentsAndApply(es, success, ifOther); } }
// Generate code to evaluate all argument expressions, including the receiver es[1] // if the method is an instance method, and convert their values to .NET types. private void CompileArgumentsAndApply(CGExpr[] es, Gen ifSuccess, Gen ifOther) { int argCount = es.Length - 1; // The error continuations must pop the arguments computed so far: Gen[] errorCont = new Gen[argCount]; if (argCount > 0) { errorCont[0] = ifOther; } for (int i = 1; i < argCount; i++) { int ii = i; // Capture lvalue -- do NOT inline! errorCont[ii] = new Gen(delegate { ilg.Emit(OpCodes.Pop); errorCont[ii - 1].Generate(ilg); }); } // Generate code, backwards, to evaluate argument expressions and // convert to external method's argument types for (int i = argCount - 1; i >= 0; i--) { // These local vars capture rvalue rather than lvalue -- do NOT inline them! CGExpr ei = es[i + 1]; Gen localSuccess = ifSuccess; int argIndex = i; Type argType = ef.ArgType(i); Gen ifError = errorCont[i]; // First some special cases to avoid boxing: if (argType == typeof (System.Double)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleOrNan(); localSuccess.Generate(ilg); }); } else if (argType == typeof (System.Single)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleOrNan(); ilg.Emit(OpCodes.Conv_R4); localSuccess.Generate(ilg); }); } else if (signed32.Contains(argType)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Conv_I4); localSuccess.Generate(ilg); }), ifError); }); } else if (unsigned32.Contains(argType)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Conv_U4); localSuccess.Generate(ilg); }), ifError); }); } else if (argType == typeof (System.Int64)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Conv_I8); localSuccess.Generate(ilg); }), ifError); }); } else if (argType == typeof (System.UInt64)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Conv_U8); localSuccess.Generate(ilg); }), ifError); }); } else if (argType == typeof (System.Boolean)) { ifSuccess = new Gen( delegate { ei.CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Ldc_R8, 0.0); ilg.Emit(OpCodes.Ceq); localSuccess.Generate(ilg); }), ifError); }); } else if (argType == typeof (System.Char)) { ifSuccess = new Gen( delegate { ei.Compile(); ilg.Emit(OpCodes.Call, TextValue.toNakedCharMethod); localSuccess.Generate(ilg); }); } else if (argType == typeof (System.String)) { ifSuccess = new Gen( delegate { ei.Compile(); UnwrapToString(localSuccess, ifError); }); } else // General cases: String[], double[], double[,], ... { ifSuccess = new Gen( delegate { ei.Compile(); ilg.Emit(OpCodes.Call, ef.ArgConverter(argIndex).Method); if (argType.IsValueType) // must unbox wrapped value type, but this is too simple-minded { ilg.Emit(OpCodes.Unbox, argType); } localSuccess.Generate(ilg); }); } } ifSuccess.Generate(ilg); }
// Generate code to evaluate all argument expressions, including the receiver es[1] // if the method is an instance method, and convert their values to .NET types. private void CompileArgumentsAndApply(CGExpr[] es, Gen ifSuccess, Gen ifOther) { int argCount = es.Length; // The error continuations must pop the arguments computed so far. Gen[] errorCont = new Gen[argCount]; if (argCount > 0) { errorCont[0] = ifOther; } for (int i = 1; i < argCount; i++) { int ii = i; // Capture lvalue -- do NOT inline! errorCont[ii] = new Gen(delegate { ilg.Emit(OpCodes.Pop); errorCont[ii - 1].Generate(ilg); }); } // Generate code, backwards, to evaluate argument expressions and // convert to the .NET method's argument types for (int i = argCount - 1; i >= 0; i--) { // These local vars capture rvalue rather than lvalue -- do NOT inline them! CGExpr ei = es[i]; Gen localSuccess = ifSuccess; Typ argType = functionInfo.signature.argTypes[i]; Gen ifError = errorCont[i]; if (argType == Typ.Number) { ifSuccess = new Gen(delegate { ei.CompileToDoubleOrNan(); localSuccess.Generate(ilg); }); } else if (argType == Typ.Function) { ifSuccess = new Gen(delegate { ei.Compile(); CheckType(FunctionValue.type, localSuccess, ifError); }); } else if (argType == Typ.Array) { ifSuccess = new Gen(delegate { ei.Compile(); CheckType(ArrayValue.type, localSuccess, ifError); }); } else if (argType == Typ.Text) { ifSuccess = new Gen(delegate { ei.Compile(); CheckType(TextValue.type, localSuccess, ifError); }); } else // argType.Value -- TODO: neglects to propagate ErrorValue from argument { ifSuccess = new Gen(delegate { ei.Compile(); localSuccess.Generate(ilg); }); } } ifSuccess.Generate(ilg); }
/// <summary> /// Generate code to check the type of the stack top value, and leave it /// there if it is of the expected type t followed by success code; else /// jump to failure code. /// </summary> /// <param name="t">The expected type of the stack top value.</param> /// <param name="ifType">Generate success code -- the value is of the expected type.</param> /// <param name="ifOther">Generate failure code -- the value is not of the expected type.</param> protected void CheckType(Type t, Gen ifType, Gen ifOther) { ilg.Emit(OpCodes.Stloc, testValue); ilg.Emit(OpCodes.Ldloc, testValue); ilg.Emit(OpCodes.Isinst, t); ilg.Emit(OpCodes.Brfalse, ifOther.GetLabel(ilg)); ilg.Emit(OpCodes.Ldloc, testValue); ifType.Generate(ilg); if (!ifOther.Generated) { Label endLabel = ilg.DefineLabel(); ilg.Emit(OpCodes.Br, endLabel); ifOther.Generate(ilg); ilg.MarkLabel(endLabel); } }
public override void CompileToDoubleProper(Gen ifProper, Gen ifOther) { ifOther.Generate(ilg); }
// In Excel, a text used in a conditional produces the error #VALUE! -- mostly public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { ifOther.Generate(ilg); }
protected void UnwrapToString(Gen ifString, Gen ifError) { CheckType(TextValue.type, new Gen(delegate { ilg.Emit(OpCodes.Ldfld, TextValue.valueField); ifString.Generate(ilg); }), ifError); }
public override void CompileToDoubleOrNan() { if (ef == null) { ilg.Emit(OpCodes.Ldc_R8, errorValue.ErrorNan); } else { // sestoft: This is maybe correct Gen ifOther = GenLoadErrorNan(ErrorValue.argTypeError); if (ef.ResType == typeof (System.Double)) { // If argument evaluation is successful, call external function // and continue with ifDouble; otherwise continue with ifOther Gen success = new Gen(delegate { ef.EmitCall(ilg); }); CompileArgumentsAndApply(es, success, ifOther); } else if (numeric.Contains(ef.ResType)) { // If argument evaluation is successful, call external function, convert // to float64 Gen success = new Gen(delegate { ef.EmitCall(ilg); ilg.Emit(OpCodes.Conv_R8); }); CompileArgumentsAndApply(es, success, ifOther); } else // Result type cannot be converted to a float64 { ifOther.Generate(ilg); } } }
public override void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { es[0].CompileCondition(ifFalse, ifTrue, ifOther); }
public override void Compile() { if (ef == null) { LoadErrorValue(errorValue); } else { // If argument evaluation is successful, call the external function // and convert its result to Value; if unsuccessful, return ArgTypeError Gen success; // First some return type special cases, avoid boxing: if (ef.ResType == typeof (System.Double)) { success = new Gen(delegate { ef.EmitCall(ilg); ilg.Emit(OpCodes.Call, NumberValue.makeMethod); }); } else if (numeric.Contains(ef.ResType)) { success = new Gen(delegate { ef.EmitCall(ilg); ilg.Emit(OpCodes.Conv_R8); ilg.Emit(OpCodes.Call, NumberValue.makeMethod); }); } else if (ef.ResType == typeof (char)) { success = new Gen(delegate { ef.EmitCall(ilg); ilg.Emit(OpCodes.Call, TextValue.fromNakedCharMethod); }); } else if (ef.ResType == typeof (void)) { success = new Gen(delegate { ef.EmitCall(ilg); ilg.Emit(OpCodes.Ldsfld, TextValue.voidField); }); } else { success = new Gen(delegate { ef.EmitCall(ilg); if (ef.ResType.IsValueType) { ilg.Emit(OpCodes.Box, ef.ResType); } ilg.Emit(OpCodes.Call, ef.ResConverter.Method); }); } Gen ifOther = GenLoadErrorValue(ErrorValue.argTypeError); CompileArgumentsAndApply(es, success, ifOther); } }
/// <summary> /// Compiles an expression as a condition, that can be true (if non-zero) or /// false (if zero) or other (if +/-infinity or NaN). If possible, avoids /// computing and pushing a value and then testing it, instead performing /// comparisons directly on arguments, or even statically. This implementation /// is a general version in terms of CompileToDoubleProper. Should be overridden /// in CGNumberConst, CGTextConst, CGError, CGIf, CGComparison, ... /// </summary> /// <param name="ifTrue">Generates code for the true branch</param> /// <param name="ifFalse">Generates code for the false branch</param> /// <param name="ifOther">Generates code for the other (neither true nor /// false) branch</param> public virtual void CompileCondition(Gen ifTrue, Gen ifFalse, Gen ifOther) { CompileToDoubleProper( new Gen(delegate { ilg.Emit(OpCodes.Ldc_R8, 0.0); ilg.Emit(OpCodes.Beq, ifFalse.GetLabel(ilg)); ifTrue.Generate(ilg); if (!ifFalse.Generated) { Label endLabel = ilg.DefineLabel(); ilg.Emit(OpCodes.Br, endLabel); ifFalse.Generate(ilg); ilg.MarkLabel(endLabel); } }), ifOther); }