Unbounded Integers (#1134)

This commit is contained in:
Marcin Kostrzewa 2020-09-10 15:02:47 +02:00 committed by GitHub
parent f9ce402a49
commit 7373da2f1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 884 additions and 102 deletions

View File

@ -12,6 +12,7 @@ import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.builtin.Bool;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Number;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
@ -19,6 +20,7 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.MethodDoesNotExistException;
import org.enso.interpreter.runtime.error.RuntimeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
/**
* A node performing lookups of method definitions.
@ -76,11 +78,21 @@ public abstract class MethodResolverNode extends Node {
}
@Specialization(guards = "cachedSymbol == symbol")
Function resolveNumber(
Function resolveBigInteger(
UnresolvedSymbol symbol,
EnsoBigInteger self,
@Cached(value = "symbol", allowUncached = true) UnresolvedSymbol cachedSymbol,
@Cached(value = "resolveMethodOnBigInteger(cachedSymbol)", allowUncached = true)
Function function) {
return function;
}
@Specialization(guards = "cachedSymbol == symbol")
Function resolveLong(
UnresolvedSymbol symbol,
long self,
@Cached(value = "symbol", allowUncached = true) UnresolvedSymbol cachedSymbol,
@Cached(value = "resolveMethodOnNumber(cachedSymbol)", allowUncached = true)
@Cached(value = "resolveMethodOnLong(cachedSymbol)", allowUncached = true)
Function function) {
return function;
}
@ -190,9 +202,22 @@ public abstract class MethodResolverNode extends Node {
return ensureMethodExists(symbol.resolveFor(cons, getBuiltins().any()), cons, symbol);
}
Function resolveMethodOnNumber(UnresolvedSymbol symbol) {
Function resolveMethodOnLong(UnresolvedSymbol symbol) {
Number number = getBuiltins().number();
return ensureMethodExists(
symbol.resolveFor(getBuiltins().number(), getBuiltins().any()), "Number", symbol);
symbol.resolveFor(
number.getSmallInteger(), number.getInteger(), number.getNumber(), getBuiltins().any()),
"Integer",
symbol);
}
Function resolveMethodOnBigInteger(UnresolvedSymbol symbol) {
Number number = getBuiltins().number();
return ensureMethodExists(
symbol.resolveFor(
number.getBigInteger(), number.getInteger(), number.getNumber(), getBuiltins().any()),
"Integer",
symbol);
}
@CompilerDirectives.TruffleBoundary

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "+", description = "Addition on numbers.")
public class AddNode extends Node {
long execute(long _this, long that) {
return _this + that;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "/", description = "Division for numbers.")
public class DivideNode extends Node {
long execute(long _this, long that) {
return _this / that;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "==", description = "Equality on numbers.")
public class EqualsNode extends Node {
boolean execute(long _this, long that) {
return _this == that;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "%", description = "Modulo operation for numbers.")
public class ModNode extends Node {
long execute(long _this, long that) {
return _this % that;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "*", description = "Multiplication on numbers.")
public class MultiplyNode extends Node {
long execute(long _this, long that) {
return _this * that;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "negate", description = "Negation for numbers.")
public class NegateNode extends Node {
long execute(long _this) {
return -_this;
}
}

View File

@ -1,11 +0,0 @@
package org.enso.interpreter.node.expression.builtin.number;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Number", name = "-", description = "Subtraction on numbers.")
public class SubtractNode extends Node {
long execute(long _this, long that) {
return _this - that;
}
}

View File

@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "+", description = "Big integer addition.")
public abstract class AddNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(EnsoBigInteger _this, Object that);
static AddNode build() {
return AddNodeGen.create();
}
@Specialization
Object doLong(EnsoBigInteger _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.add(_this.getValue(), that));
}
@Specialization
Object doBigInteger(EnsoBigInteger _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.add(_this.getValue(), that.getValue()));
}
@Fallback
Object doOther(EnsoBigInteger _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.+", this);
}
}

View File

@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "/", description = "Big integer division.")
public abstract class DivideNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(EnsoBigInteger _this, Object that);
static DivideNode build() {
return DivideNodeGen.create();
}
@Specialization
Object doLong(EnsoBigInteger _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.divide(_this.getValue(), that));
}
@Specialization
Object doBigInteger(EnsoBigInteger _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.divide(_this.getValue(), that.getValue()));
}
@Fallback
Object doOther(EnsoBigInteger _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer./", this);
}
}

View File

@ -0,0 +1,28 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "==", description = "Big integer equality.")
public abstract class EqualsNode extends Node {
abstract boolean execute(EnsoBigInteger _this, Object that);
static EqualsNode build() {
return EqualsNodeGen.create();
}
@Specialization
boolean doBigInt(EnsoBigInteger _this, EnsoBigInteger that) {
return BigIntegerOps.equals(_this.getValue(), that.getValue());
}
@Fallback
boolean doOther(EnsoBigInteger _this, Object that) {
return false;
}
}

View File

@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "%", description = "Big integer modulo division.")
public abstract class ModNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(EnsoBigInteger _this, Object that);
static ModNode build() {
return ModNodeGen.create();
}
@Specialization
Object doLong(EnsoBigInteger _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.modulo(_this.getValue(), that));
}
@Specialization
Object doBigInteger(EnsoBigInteger _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.modulo(_this.getValue(), that.getValue()));
}
@Fallback
Object doOther(EnsoBigInteger _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.%", this);
}
}

View File

@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "*", description = "Big integer multiplication.")
public abstract class MultiplyNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(EnsoBigInteger _this, Object that);
static MultiplyNode build() {
return MultiplyNodeGen.create();
}
@Specialization
Object doLong(EnsoBigInteger _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.multiply(_this.getValue(), that));
}
@Specialization
Object doBigInteger(EnsoBigInteger _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.multiply(_this.getValue(), that.getValue()));
}
@Fallback
Object doOther(EnsoBigInteger _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.*", this);
}
}

View File

@ -0,0 +1,16 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "negate", description = "Big integer negation.")
public class NegateNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
Object execute(EnsoBigInteger _this) {
return toEnsoNumberNode.execute(BigIntegerOps.negate(_this.getValue()));
}
}

View File

@ -0,0 +1,36 @@
package org.enso.interpreter.node.expression.builtin.number.bigInteger;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Big_Integer", name = "-", description = "Big integer subtraction.")
public abstract class SubtractNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(EnsoBigInteger _this, Object that);
static SubtractNode build() {
return SubtractNodeGen.create();
}
@Specialization
Object doLong(EnsoBigInteger _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.subtract(_this.getValue(), that));
}
@Specialization
Object doBigInteger(EnsoBigInteger _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.subtract(_this.getValue(), that.getValue()));
}
@Fallback
Object doOther(EnsoBigInteger _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.-", this);
}
}

View File

@ -0,0 +1,41 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Int_64", name = "+", description = "Addition of numbers.")
public abstract class AddNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(long _this, Object that);
static AddNode build() {
return AddNodeGen.create();
}
@Specialization(rewriteOn = ArithmeticException.class)
long doLong(long _this, long that) {
return Math.addExact(_this, that);
}
@Specialization
Object doOverflow(long _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.add(_this, that));
}
@Specialization
Object doBigInteger(long _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.add(that.getValue(), _this));
}
@Fallback
Object doOther(long _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.+", this);
}
}

View File

@ -0,0 +1,32 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Int_64", name = "/", description = "Division of numbers.")
public abstract class DivideNode extends Node {
abstract Object execute(long _this, Object that);
static DivideNode build() {
return DivideNodeGen.create();
}
@Specialization
long doLong(long _this, long that) {
return _this / that;
}
@Specialization
long doBigInteger(long _this, EnsoBigInteger that) {
return 0L;
}
@Fallback
Object doOther(long _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer./", this);
}
}

View File

@ -0,0 +1,26 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
@BuiltinMethod(type = "Int_64", name = "==", description = "Equality on numbers.")
public abstract class EqualsNode extends Node {
abstract boolean execute(long _this, Object that);
static EqualsNode build() {
return EqualsNodeGen.create();
}
@Specialization
boolean doLong(long _this, long that) {
return _this == that;
}
@Fallback
boolean doOther(long _this, Object that) {
return false;
}
}

View File

@ -0,0 +1,32 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Int_64", name = "%", description = "Modulo division of numbers.")
public abstract class ModNode extends Node {
abstract Object execute(long _this, Object that);
static ModNode build() {
return ModNodeGen.create();
}
@Specialization
long doLong(long _this, long that) {
return _this % that;
}
@Specialization
long doBigInteger(long _this, EnsoBigInteger that) {
return _this;
}
@Fallback
Object doOther(long _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.%", this);
}
}

View File

@ -0,0 +1,41 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Int_64", name = "*", description = "Multiplication of numbers.")
public abstract class MultiplyNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(long _this, Object that);
static MultiplyNode build() {
return MultiplyNodeGen.create();
}
@Specialization(rewriteOn = ArithmeticException.class)
long doLong(long _this, long that) {
return Math.multiplyExact(_this, that);
}
@Specialization
Object doOverflow(long _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.multiply(_this, that));
}
@Specialization
Object doBigInteger(long _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.multiply(that.getValue(), _this));
}
@Fallback
Object doOther(long _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.*", this);
}
}

View File

@ -0,0 +1,28 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
@BuiltinMethod(type = "Int_64", name = "negate", description = "Negation for numbers.")
public abstract class NegateNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
static NegateNode build() {
return NegateNodeGen.create();
}
abstract Object execute(long _this);
@Specialization(rewriteOn = ArithmeticException.class)
long doNormal(long _this) {
return Math.negateExact(_this);
}
@Specialization
Object doOverflow(long _this) {
return toEnsoNumberNode.execute(BigIntegerOps.negate(_this));
}
}

View File

@ -0,0 +1,41 @@
package org.enso.interpreter.node.expression.builtin.number.int64;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.number.utils.BigIntegerOps;
import org.enso.interpreter.node.expression.builtin.number.utils.ToEnsoNumberNode;
import org.enso.interpreter.runtime.error.TypeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
@BuiltinMethod(type = "Int_64", name = "-", description = "Subtraction of numbers.")
public abstract class SubtractNode extends Node {
private @Child ToEnsoNumberNode toEnsoNumberNode = ToEnsoNumberNode.build();
abstract Object execute(long _this, Object that);
static SubtractNode build() {
return SubtractNodeGen.create();
}
@Specialization(rewriteOn = ArithmeticException.class)
long doLong(long _this, long that) {
return Math.subtractExact(_this, that);
}
@Specialization
Object doOverflow(long _this, long that) {
return toEnsoNumberNode.execute(BigIntegerOps.subtract(_this, that));
}
@Specialization
Object doBigInteger(long _this, EnsoBigInteger that) {
return toEnsoNumberNode.execute(BigIntegerOps.subtract(_this, that.getValue()));
}
@Fallback
Object doOther(long _this, Object that) {
throw new TypeError("Unexpected type provided for argument `that` in Integer.-", this);
}
}

View File

@ -0,0 +1,93 @@
package org.enso.interpreter.node.expression.builtin.number.utils;
import com.oracle.truffle.api.CompilerDirectives;
import java.math.BigInteger;
/** Re-exposes big-integer operations behind a truffle boundary. */
public class BigIntegerOps {
@CompilerDirectives.TruffleBoundary
public static BigInteger multiply(long a, long b) {
return BigInteger.valueOf(a).multiply(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger multiply(BigInteger a, long b) {
return a.multiply(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger multiply(BigInteger a, BigInteger b) {
return a.multiply(b);
}
@CompilerDirectives.TruffleBoundary
public static BigInteger add(long a, long b) {
return BigInteger.valueOf(a).add(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger add(BigInteger a, long b) {
return a.add(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger add(BigInteger a, BigInteger b) {
return a.add(b);
}
@CompilerDirectives.TruffleBoundary
public static BigInteger subtract(long a, long b) {
return BigInteger.valueOf(a).subtract(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger subtract(BigInteger a, long b) {
return a.subtract(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger subtract(long a, BigInteger b) {
return BigInteger.valueOf(a).subtract(b);
}
@CompilerDirectives.TruffleBoundary
public static BigInteger subtract(BigInteger a, BigInteger b) {
return a.subtract(b);
}
@CompilerDirectives.TruffleBoundary
public static BigInteger divide(BigInteger a, long b) {
return a.divide(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger divide(BigInteger a, BigInteger b) {
return a.divide(b);
}
@CompilerDirectives.TruffleBoundary
public static BigInteger modulo(BigInteger a, long b) {
return a.mod(BigInteger.valueOf(b));
}
@CompilerDirectives.TruffleBoundary
public static BigInteger modulo(BigInteger a, BigInteger b) {
return a.mod(b);
}
@CompilerDirectives.TruffleBoundary
public static BigInteger negate(BigInteger a) {
return a.negate();
}
@CompilerDirectives.TruffleBoundary
public static BigInteger negate(long a) {
return BigInteger.valueOf(a).negate();
}
@CompilerDirectives.TruffleBoundary
public static boolean equals(BigInteger a, BigInteger b) {
return a.equals(b);
}
}

View File

@ -0,0 +1,48 @@
package org.enso.interpreter.node.expression.builtin.number.utils;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import java.math.BigInteger;
@ReportPolymorphism
@NodeInfo(description = "Takes a big integer and casts it to a long, if the operation is safe.")
public class ToEnsoNumberNode extends Node {
private final ConditionProfile fitsProfile = ConditionProfile.createCountingProfile();
private static final BigInteger MIN_LONG_BIGINT = BigInteger.valueOf(Long.MIN_VALUE);
private static final BigInteger MAX_LONG_BIGINT = BigInteger.valueOf(Long.MAX_VALUE);
/** @return a new instance of this node. */
public static ToEnsoNumberNode build() {
return new ToEnsoNumberNode();
}
/**
* Execute the node.
*
* @param bigInteger the big integer to downcast.
* @return a long with the same value if possible, the original integer wrapped in {@link
* org.enso.interpreter.runtime.number.EnsoBigInteger} otherwise.
*/
public Object execute(BigInteger bigInteger) {
if (fitsProfile.profile(fitsInLong(bigInteger))) {
return toLong(bigInteger);
}
return new EnsoBigInteger(bigInteger);
}
@CompilerDirectives.TruffleBoundary
private static boolean fitsInLong(BigInteger bigInteger) {
return bigInteger.compareTo(MIN_LONG_BIGINT) >= 0 && bigInteger.compareTo(MAX_LONG_BIGINT) <= 0;
}
@CompilerDirectives.TruffleBoundary
private static long toLong(BigInteger bigInteger) {
return bigInteger.longValue();
}
}

View File

@ -0,0 +1,39 @@
package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
import java.math.BigInteger;
/** A representation of big integer literals in Enso. */
@NodeInfo(shortName = "BigIntegerLiteral")
public final class BigIntegerLiteralNode extends ExpressionNode {
private final EnsoBigInteger value;
private BigIntegerLiteralNode(BigInteger value) {
this.value = new EnsoBigInteger(value);
}
/**
* Creates an instance of this node.
*
* @param value the value for the node to represent
* @return a node representing the literal given by {@code value}
*/
public static BigIntegerLiteralNode build(BigInteger value) {
return new BigIntegerLiteralNode(value);
}
/**
* Gets the value of the literal.
*
* @param frame the stack frame for execution
* @return the value of the integer literal
*/
@Override
public Object executeGeneric(VirtualFrame frame) {
return this.value;
}
}

View File

@ -12,7 +12,6 @@ import org.enso.interpreter.node.expression.builtin.interop.generic.*;
import org.enso.interpreter.node.expression.builtin.interop.syntax.MethodDispatchNode;
import org.enso.interpreter.node.expression.builtin.interop.syntax.ConstructorDispatchNode;
import org.enso.interpreter.node.expression.builtin.io.*;
import org.enso.interpreter.node.expression.builtin.number.*;
import org.enso.interpreter.node.expression.builtin.runtime.GCMethodGen;
import org.enso.interpreter.node.expression.builtin.runtime.NoInlineMethodGen;
import org.enso.interpreter.node.expression.builtin.state.*;
@ -46,7 +45,7 @@ public class Builtins {
private final ModuleScope scope;
private final AtomConstructor unit;
private final AtomConstructor any;
private final AtomConstructor number;
private final Number number;
private final AtomConstructor function;
private final AtomConstructor text;
private final AtomConstructor debug;
@ -71,7 +70,6 @@ public class Builtins {
scope = module.compileScope(context);
unit = new AtomConstructor("Unit", scope).initializeFields();
any = new AtomConstructor("Any", scope).initializeFields();
number = new AtomConstructor("Number", scope).initializeFields();
bool = new Bool(language, scope);
error = new Error(language, scope);
array = new Array(language, scope);
@ -79,6 +77,7 @@ public class Builtins {
text = new AtomConstructor("Text", scope).initializeFields();
debug = new AtomConstructor("Debug", scope).initializeFields();
system = new System(language, scope);
number = new Number(language, scope);
AtomConstructor nil = new AtomConstructor("Nil", scope).initializeFields();
AtomConstructor cons =
@ -96,10 +95,8 @@ public class Builtins {
AtomConstructor thread = new AtomConstructor("Thread", scope).initializeFields();
AtomConstructor unsafe = new AtomConstructor("Unsafe", scope).initializeFields();
scope.registerConstructor(unit);
scope.registerConstructor(any);
scope.registerConstructor(number);
scope.registerConstructor(function);
scope.registerConstructor(text);
@ -131,14 +128,6 @@ public class Builtins {
scope.registerMethod(error, "throw", ThrowErrorMethodGen.makeFunction(language));
scope.registerMethod(any, "catch", CatchErrorMethodGen.makeFunction(language));
scope.registerMethod(number, "+", AddMethodGen.makeFunction(language));
scope.registerMethod(number, "-", SubtractMethodGen.makeFunction(language));
scope.registerMethod(number, "*", MultiplyMethodGen.makeFunction(language));
scope.registerMethod(number, "/", DivideMethodGen.makeFunction(language));
scope.registerMethod(number, "%", ModMethodGen.makeFunction(language));
scope.registerMethod(number, "negate", NegateMethodGen.makeFunction(language));
scope.registerMethod(number, "==", EqualsMethodGen.makeFunction(language));
scope.registerMethod(state, "get", GetStateMethodGen.makeFunction(language));
scope.registerMethod(state, "put", PutStateMethodGen.makeFunction(language));
scope.registerMethod(state, "run", RunStateMethodGen.makeFunction(language));
@ -220,11 +209,11 @@ public class Builtins {
}
/**
* Returns the {@code Number} atom constructor.
* Returns the number-related entities.
*
* @return the {@code Number} atom constructor
* @return the number-related part of builtins.
*/
public AtomConstructor number() {
public Number number() {
return number;
}

View File

@ -0,0 +1,131 @@
package org.enso.interpreter.runtime.builtin;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A container for all number-related builtins. */
public class Number {
private final AtomConstructor smallInteger;
private final AtomConstructor bigInteger;
private final AtomConstructor integer;
private final AtomConstructor number;
/**
* Creates and registers number builtins.
*
* @param language the current language instance.
* @param scope the builtins scope.
*/
public Number(Language language, ModuleScope scope) {
number = new AtomConstructor("Number", scope).initializeFields();
smallInteger = new AtomConstructor("Small_Integer", scope).initializeFields();
integer = new AtomConstructor("Integer", scope).initializeFields();
bigInteger = new AtomConstructor("Big_Integer", scope).initializeFields();
registerInt64Methods(language, scope);
registerBigIntegerMethods(language, scope);
scope.registerConstructor(number);
scope.registerConstructor(smallInteger);
scope.registerConstructor(integer);
scope.registerConstructor(bigInteger);
}
private void registerInt64Methods(Language language, ModuleScope scope) {
scope.registerMethod(
smallInteger,
"+",
org.enso.interpreter.node.expression.builtin.number.int64.AddMethodGen.makeFunction(
language));
scope.registerMethod(
smallInteger,
"-",
org.enso.interpreter.node.expression.builtin.number.int64.SubtractMethodGen.makeFunction(
language));
scope.registerMethod(
smallInteger,
"*",
org.enso.interpreter.node.expression.builtin.number.int64.MultiplyMethodGen.makeFunction(
language));
scope.registerMethod(
smallInteger,
"/",
org.enso.interpreter.node.expression.builtin.number.int64.DivideMethodGen.makeFunction(
language));
scope.registerMethod(
smallInteger,
"%",
org.enso.interpreter.node.expression.builtin.number.int64.ModMethodGen.makeFunction(
language));
scope.registerMethod(
smallInteger,
"negate",
org.enso.interpreter.node.expression.builtin.number.int64.NegateMethodGen.makeFunction(
language));
scope.registerMethod(
smallInteger,
"==",
org.enso.interpreter.node.expression.builtin.number.int64.EqualsMethodGen.makeFunction(
language));
}
private void registerBigIntegerMethods(Language language, ModuleScope scope) {
scope.registerMethod(
bigInteger,
"+",
org.enso.interpreter.node.expression.builtin.number.bigInteger.AddMethodGen.makeFunction(
language));
scope.registerMethod(
bigInteger,
"-",
org.enso.interpreter.node.expression.builtin.number.bigInteger.SubtractMethodGen
.makeFunction(language));
scope.registerMethod(
bigInteger,
"*",
org.enso.interpreter.node.expression.builtin.number.bigInteger.MultiplyMethodGen
.makeFunction(language));
scope.registerMethod(
bigInteger,
"/",
org.enso.interpreter.node.expression.builtin.number.bigInteger.DivideMethodGen.makeFunction(
language));
scope.registerMethod(
bigInteger,
"%",
org.enso.interpreter.node.expression.builtin.number.bigInteger.ModMethodGen.makeFunction(
language));
scope.registerMethod(
bigInteger,
"negate",
org.enso.interpreter.node.expression.builtin.number.bigInteger.NegateMethodGen.makeFunction(
language));
scope.registerMethod(
bigInteger,
"==",
org.enso.interpreter.node.expression.builtin.number.bigInteger.EqualsMethodGen.makeFunction(
language));
}
/** @return the Int64 atom constructor. */
public AtomConstructor getSmallInteger() {
return smallInteger;
}
/** @return the Big_Integer atom constructor. */
public AtomConstructor getBigInteger() {
return bigInteger;
}
/** @return the Integer atom constructor */
public AtomConstructor getInteger() {
return integer;
}
/** @return the Number atom constructor */
public AtomConstructor getNumber() {
return number;
}
}

View File

@ -0,0 +1,22 @@
package org.enso.interpreter.runtime.number;
import java.math.BigInteger;
/** Internal wrapper for a {@link BigInteger}. */
public class EnsoBigInteger {
private final BigInteger value;
/**
* Wraps a {@link BigInteger}.
*
* @param value the value to wrap.
*/
public EnsoBigInteger(BigInteger value) {
this.value = value;
}
/** @return the contained {@link BigInteger}. */
public BigInteger getValue() {
return value;
}
}

View File

@ -12,6 +12,7 @@ import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.RuntimeError;
import org.enso.interpreter.runtime.number.EnsoBigInteger;
/**
* This class defines the interpreter-level type system for Enso.
@ -33,7 +34,8 @@ import org.enso.interpreter.runtime.error.RuntimeError;
Thunk.class,
RuntimeError.class,
UnresolvedSymbol.class,
Array.class
Array.class,
EnsoBigInteger.class
})
public class Types {

View File

@ -1,5 +1,7 @@
package org.enso.compiler.codegen
import java.math.BigInteger
import com.oracle.truffle.api.Truffle
import com.oracle.truffle.api.source.{Source, SourceSection}
import org.enso.compiler.core.IR
@ -41,6 +43,7 @@ import org.enso.interpreter.node.expression.constant.{
ErrorNode
}
import org.enso.interpreter.node.expression.literal.{
BigIntegerLiteralNode,
IntegerLiteralNode,
TextLiteralNode
}
@ -857,7 +860,10 @@ class IrToTruffle(
def processLiteral(literal: IR.Literal): RuntimeExpression =
literal match {
case IR.Literal.Number(value, location, _, _) =>
setLocation(IntegerLiteralNode.build(value.toLong), location)
val node = value.toLongOption
.map(IntegerLiteralNode.build)
.getOrElse(BigIntegerLiteralNode.build(new BigInteger(value)))
setLocation(node, location)
case IR.Literal.Text(text, location, _, _) =>
setLocation(TextLiteralNode.build(text), location)
}

View File

@ -239,7 +239,7 @@ class ReplTest extends InterpreterTest with BeforeAndAfter with EitherValues {
}
eval(code)
val errorMsg =
"Unexpected type provided for argument `that` in Number.+"
"Unexpected type provided for argument `that` in Integer.+"
evalResult.left.value.getMessage shouldEqual errorMsg
}

View File

@ -127,7 +127,7 @@ class MethodsTest extends InterpreterTest {
"""
|main = foo 7
|""".stripMargin
the[InterpreterException] thrownBy eval(code) should have message "Object Number does not define method foo."
the[InterpreterException] thrownBy eval(code) should have message "Object Integer does not define method foo."
}
"be callable for any type when defined on Any" in {

View File

@ -41,7 +41,7 @@ sum_state = sum_to ->
main =
hundred_mil = 100000000
IO.println "Measuring SumTCO"
Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 100 10
Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 500 10
IO.println "Measuring SumTCO Java"
Bench_Utils.measure (here.sum_tco_java hundred_mil) "sum_tco" 100 10
IO.println "Measuring State"

View File

@ -7,6 +7,7 @@ import Test.Names_Spec
import Test.Process_Spec
import Test.Java_Interop.Spec as Java_Spec
import Test.Vector.Spec as Vector_Spec
import Test.Numbers.Spec as Numbers_Spec
main = Test.Suite.runMain <|
List_Spec.spec
@ -17,3 +18,4 @@ main = Test.Suite.runMain <|
Process_Spec.spec
Java_Spec.spec
Vector_Spec.spec
Numbers_Spec.spec

View File

@ -0,0 +1,37 @@
from Base import all
import Base.Test
Integer.is_even = this % 2 == 0
spec =
almost_max_long = 9223372036854775806
almost_max_long_times_three = 27670116110564327418
hundred_factorial = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
describe "Integers" <|
it "should be of unbound size when multiplied" <|
1.upto 101 . fold 1 (*) . should_equal hundred_factorial
it "should be of unbound size when added" <|
(almost_max_long + almost_max_long + almost_max_long).should_equal almost_max_long_times_three
it "should be of unbound size when subtracted" <|
(0 - almost_max_long - almost_max_long - almost_max_long).should_equal almost_max_long_times_three.negate
it "should be of unbound size when dividing" <|
expected = 3372816184472482867110284450043137767873196479305249187406461598235841786750685581361224832688174410089430537516012695688121622150430744676
((1.upto 101 . fold 1 (*)) / 3*almost_max_long).should_equal expected
it "should be of unbound size when taking remainder" <|
expected = 3191479909175673432
((1.upto 101 . fold 1 (*)) % 3*almost_max_long).should_equal expected
it "should allow defining extension methods through the Integer type for any number size" <|
876543.is_even.should_be_false
(1.upto 101 . fold 1 (*)).is_even.should_be_true
it "should handle the negation edge cases" <|
x = 9223372036854775808
y = -x
z = -9223372036854775808
y.should_equal z
it "should handle equality between small and big integers" <|
(1 == hundred_factorial).should_be_false
(hundred_factorial == 1).should_be_false
it "should properly handle going to big numbers and back" <|
((almost_max_long * 3) / 3) . should_equal almost_max_long