public virtual IokeObject ConvertToDecimal(IokeObject self, IokeObject m, IokeObject context, bool signalCondition) { if (signalCondition) { IokeObject condition = IokeObject.As(IokeObject.GetCellChain(context.runtime.Condition, m, context, "Error", "Type", "IncorrectType"), context).Mimic(m, context); condition.SetCell("message", m); condition.SetCell("context", context); condition.SetCell("receiver", self); condition.SetCell("expectedType", context.runtime.GetSymbol("Decimal")); object[] newCell = new object[] { self }; context.runtime.WithRestartReturningArguments(() => { context.runtime.ErrorCondition(condition); }, context, new IokeObject.UseValue("decimal", newCell)); return(IokeObject.ConvertToDecimal(newCell[0], m, context, signalCondition)); } return(null); }
public override void Init(IokeObject obj) { Runtime runtime = obj.runtime; obj.Kind = "Number Decimal"; runtime.Decimal = obj; obj.RegisterMethod(runtime.NewNativeMethod("returns true if the left hand side decimal is equal to the right hand side decimal.", new TypeCheckingNativeMethod("==", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(runtime.Decimal) .WithRequiredPositional("other") .Arguments, (method, on, args, keywords, context, message) => { Decimal d = (Decimal)IokeObject.dataOf(on); object other = args[0]; return(((other is IokeObject) && (IokeObject.dataOf(other) is Decimal) && ((on == context.runtime.Decimal && other == on) || d.value.Equals(((Decimal)IokeObject.dataOf(other)).value))) ? context.runtime.True : context.runtime.False); }))); obj.RegisterMethod(runtime.NewNativeMethod("Returns a text representation of the object", new TypeCheckingNativeMethod.WithNoArguments("asText", obj, (method, on, args, keywords, context, message) => { return(runtime.NewText(on.ToString())); }))); obj.RegisterMethod(runtime.NewNativeMethod("returns the square root of the receiver. this should return the same result as calling ** with 0.5", new TypeCheckingNativeMethod.WithNoArguments("sqrt", obj, (method, on, args, keywords, context, message) => { BigDecimal value = ((Decimal)IokeObject.dataOf(on)).value; if (value.CompareTo(BigDecimal.ZERO) < 1) { IokeObject condition = IokeObject.As(IokeObject.GetCellChain(context.runtime.Condition, message, context, "Error", "Arithmetic"), context).Mimic(message, context); condition.SetCell("message", message); condition.SetCell("context", context); condition.SetCell("receiver", on); context.runtime.ErrorCondition(condition); } return(runtime.NewDecimal(new BigSquareRoot().Get(value))); }))); obj.RegisterMethod(obj.runtime.NewNativeMethod("Returns a text inspection of the object", new TypeCheckingNativeMethod.WithNoArguments("inspect", obj, (method, on, args, keywords, context, message) => { return(method.runtime.NewText(Decimal.GetInspect(on))); }))); obj.RegisterMethod(obj.runtime.NewNativeMethod("Returns a brief text inspection of the object", new TypeCheckingNativeMethod.WithNoArguments("notice", obj, (method, on, args, keywords, context, message) => { return(method.runtime.NewText(Decimal.GetInspect(on))); }))); obj.RegisterMethod(obj.runtime.NewNativeMethod("returns a hash for the decimal number", new NativeMethod.WithNoArguments("hash", (method, context, message, on, outer) => { outer.ArgumentsDefinition.CheckArgumentCount(context, message, on); return(context.runtime.NewNumber(Decimal.GetValue(on).GetHashCode())); }))); obj.RegisterMethod(runtime.NewNativeMethod("compares this number against the argument, true if this number is the same, otherwise false", new TypeCheckingNativeMethod("==", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("other") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; if (IokeObject.dataOf(arg) is Number) { return((Decimal.GetValue(on).CompareTo(Number.GetValue(arg).AsBigDecimal()) == 0) ? context.runtime.True : context.runtime.False); } else if (IokeObject.dataOf(arg) is Decimal) { return((Decimal.GetValue(on).CompareTo(Decimal.GetValue(arg)) == 0) ? context.runtime.True : context.runtime.False); } else { return(context.runtime.False); } }))); obj.RegisterMethod(runtime.NewNativeMethod("compares this number against the argument, returning -1, 0 or 1 based on which one is larger. if the argument is a rational, it will be converted into a form suitable for comparing against a decimal, and then compared. if the argument is neither a Rational nor a Decimal, it tries to call asDecimal, and if that doesn't work it returns nil.", new TypeCheckingNativeMethod("<=>", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("other") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; IokeData data = IokeObject.dataOf(arg); if (data is Number) { return(context.runtime.NewNumber(Decimal.GetValue(on).CompareTo(Number.GetValue(arg).AsBigDecimal()))); } else { if (!(data is Decimal)) { arg = IokeObject.ConvertToDecimal(arg, message, context, false); if (!(IokeObject.dataOf(arg) is Decimal)) { // Can't compare, so bail out return(context.runtime.nil); } } if (on == context.runtime.Decimal || arg == context.runtime.Decimal) { if (arg == on) { return(context.runtime.NewNumber(0)); } return(context.runtime.nil); } return(context.runtime.NewNumber(Decimal.GetValue(on).CompareTo(Decimal.GetValue(arg)))); } }))); obj.RegisterMethod(runtime.NewNativeMethod("returns the difference between this number and the argument. if the argument is a rational, it will be converted into a form suitable for subtracting against a decimal, and then subtracted. if the argument is neither a Rational nor a Decimal, it tries to call asDecimal, and if that fails it signals a condition.", new TypeCheckingNativeMethod("-", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("subtrahend") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; IokeData data = IokeObject.dataOf(arg); if (data is Number) { return(context.runtime.NewDecimal(Decimal.GetValue(on).subtract(Number.GetValue(arg).AsBigDecimal()))); } else { if (!(data is Decimal)) { arg = IokeObject.ConvertToDecimal(arg, message, context, true); } return(context.runtime.NewDecimal(Decimal.GetValue(on).subtract(Decimal.GetValue(arg)))); } }))); obj.RegisterMethod(runtime.NewNativeMethod("returns the sum of this number and the argument. if the argument is a rational, it will be converted into a form suitable for addition against a decimal, and then added. if the argument is neither a Rational nor a Decimal, it tries to call asDecimal, and if that fails it signals a condition.", new TypeCheckingNativeMethod("+", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("addend") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; IokeData data = IokeObject.dataOf(arg); if (data is Number) { return(context.runtime.NewDecimal(Decimal.GetValue(on).add(Number.GetValue(arg).AsBigDecimal()))); } else { if (!(data is Decimal)) { arg = IokeObject.ConvertToDecimal(arg, message, context, true); } return(context.runtime.NewDecimal(Decimal.GetValue(on).add(Decimal.GetValue(arg)))); } }))); obj.RegisterMethod(runtime.NewNativeMethod("returns the product of this number and the argument. if the argument is a rational, the receiver will be converted into a form suitable for multiplying against a decimal, and then multiplied. if the argument is neither a Rational nor a Decimal, it tries to call asDecimal, and if that fails it signals a condition.", new TypeCheckingNativeMethod("*", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("multiplier") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; IokeData data = IokeObject.dataOf(arg); if (data is Number) { return(context.runtime.NewDecimal(Decimal.GetValue(on).multiply(Number.GetValue(arg).AsBigDecimal()))); } else { if (!(data is Decimal)) { arg = IokeObject.ConvertToDecimal(arg, message, context, true); } return(context.runtime.NewDecimal(Decimal.GetValue(on).multiply(Decimal.GetValue(arg)))); } }))); obj.RegisterMethod(runtime.NewNativeMethod("returns this number to the power of the argument (which has to be an integer)", new TypeCheckingNativeMethod("**", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("exponent") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; IokeData data = IokeObject.dataOf(arg); if (!(data is Number)) { arg = IokeObject.ConvertToRational(arg, message, context, true); } return(context.runtime.NewDecimal(Decimal.GetValue(on).pow(Number.IntValue(arg).intValue()))); }))); obj.RegisterMethod(runtime.NewNativeMethod("returns the quotient of this number and the argument.", new TypeCheckingNativeMethod("/", TypeCheckingArgumentsDefinition.builder() .ReceiverMustMimic(obj) .WithRequiredPositional("divisor") .Arguments, (method, on, args, keywords, context, message) => { object arg = args[0]; IokeData data = IokeObject.dataOf(arg); if (data is Number) { return(context.runtime.NewDecimal(Decimal.GetValue(on).divide(Number.GetValue(arg).AsBigDecimal()))); } else { if (!(data is Decimal)) { arg = IokeObject.ConvertToDecimal(arg, message, context, true); } while (Decimal.GetValue(arg).CompareTo(BigDecimal.ZERO) == 0) { IokeObject condition = IokeObject.As(IokeObject.GetCellChain(context.runtime.Condition, message, context, "Error", "Arithmetic", "DivisionByZero"), context).Mimic(message, context); condition.SetCell("message", message); condition.SetCell("context", context); condition.SetCell("receiver", on); object[] newCell = new object[] { arg }; context.runtime.WithRestartReturningArguments(() => { context.runtime.ErrorCondition(condition); }, context, new IokeObject.UseValue("newValue", newCell)); arg = newCell[0]; } BigDecimal result = null; try { result = Decimal.GetValue(on).divide(Decimal.GetValue(arg), BigDecimal.ROUND_UNNECESSARY); } catch (System.ArithmeticException) { result = Decimal.GetValue(on).divide(Decimal.GetValue(arg), MathContext.DECIMAL128); } return(context.runtime.NewDecimal(result)); } }))); }