diff --git a/CHANGELOG.md b/CHANGELOG.md index 0580ebe59fd9..b6c019a1c695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -198,6 +198,8 @@ - [Added `databases`, `schemas`, `tables` support to database Connection.][3632] - [Implemented `start_of` and `end_of` methods for date/time types allowing to find start and end of a period of time containing the provided time.][3695] +- [Implemented `type_of` and `is_of_type` methods for getting the type of a + value and comparing types, respectively.][3722] - [Implemented `work_days_until` for counting work dys between dates and `add_work_days` which allows to shift a date by a number of work days.][3726] - [Added `query` and `read` functions to Database connections.][3727] @@ -320,6 +322,7 @@ [3684]: https://github.com/enso-org/enso/pull/3684 [3691]: https://github.com/enso-org/enso/pull/3691 [3695]: https://github.com/enso-org/enso/pull/3695 +[3722]: https://github.com/enso-org/enso/pull/3722 [3726]: https://github.com/enso-org/enso/pull/3726 [3727]: https://github.com/enso-org/enso/pull/3727 [3733]: https://github.com/enso-org/enso/pull/3733 diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso index c08c7b3cde48..4f5520203066 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date.enso @@ -502,8 +502,11 @@ type Date ## Compares two Dates for equality. == : Date -> Boolean == self that = - sign = Time_Utils.compare_to_localdate self that - 0 == sign + case that of + Date -> + sign = Time_Utils.compare_to_localdate self that + 0 == sign + _ -> False ## PRIVATE week_days_between start end = diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso index 6626de84d837..82ab926be374 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Date_Time.enso @@ -564,5 +564,9 @@ type Date_Time ## Compares two Date_Time for equality. == : Date_Time -> Boolean == self that = - sign = Time_Utils.compare_to_zoneddatetime self that - 0 == sign + case that of + Date_Time -> + sign = Time_Utils.compare_to_zoneddatetime self that + 0 == sign + _ -> + False diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso index 4020e65e48a0..169c4a91dea4 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Of_Day.enso @@ -330,5 +330,9 @@ type Time_Of_Day ## Compares two Time_Of_Day for equality. == : Date -> Boolean == self that = - sign = Time_Utils.compare_to_localtime self that - 0 == sign + case that of + Time_Of_Day -> + sign = Time_Utils.compare_to_localtime self that + 0 == sign + _ -> + False diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso index 51e558187cda..4d17f2d7cd99 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Data/Time/Time_Zone.enso @@ -140,4 +140,7 @@ type Time_Zone ## Compares two Zones for equality. == : Time_Zone -> Boolean - == self that = Time_Utils.equals_zone self that + == self that = + case that of + Time_Zone -> Time_Utils.equals_zone self that + _ -> False diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso index 4f1124cadcbb..c80cf47b0a79 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/Meta.enso @@ -253,12 +253,13 @@ is_same_object value_1 value_2 = @Builtin_Method "Meta.is_same_object" ## UNSTABLE ADVANCED - Checks if `self` is an instance of `typ`. + Checks whether `self` represents the same underlying reference as `value`. Arguments: - - typ: The type to check `self` against. -Any.is_a : Any -> Boolean -Any.is_a self typ = is_a self typ + - value_1: The first value. + - value_2: The second value. +Any.is_same_object_as : Any -> Boolean +Any.is_same_object_as self value = is_same_object self value ## UNSTABLE ADVANCED @@ -267,8 +268,8 @@ Any.is_a self typ = is_a self typ Arguments: - typ: The type to check `self` against. -Any.is_an : Any -> Boolean -Any.is_an self typ = is_a self typ +Any.is_a : Any -> Boolean +Any.is_a self typ = is_a self typ ## UNSTABLE ADVANCED @@ -278,17 +279,7 @@ Any.is_an self typ = is_a self typ Arguments: - typ: The type to check `self` against. Base.Error.is_a : Any -> Boolean -Base.Error.is_a self typ = self.is_an typ - -## UNSTABLE - ADVANCED - - Checks if `self` is an instance of `typ`. - - Arguments: - - typ: The type to check `self` against. -Base.Error.is_an : Any -> Boolean -Base.Error.is_an self typ = typ==Any || typ==Base.Error +Base.Error.is_a self typ = typ==Any || typ==Base.Error ## UNSTABLE ADVANCED @@ -303,12 +294,17 @@ is_a value typ = if is_same_object value typ then True else if typ == Any then True else if is_error value then typ == Base.Error else case value of - Array -> typ == Array + Vector.Vector -> typ.is_same_object_as Vector.Vector + Array -> typ.is_same_object_as Array Boolean -> if typ == Boolean then True else value == typ Text -> typ == Text Number -> if typ == Number then True else case value of Integer -> typ == Integer Decimal -> typ == Decimal + Date_Time.Date_Time -> typ.is_same_object_as Date_Time.Date_Time + Date.Date -> typ.is_same_object_as Date.Date + Time_Of_Day.Time_Of_Day -> typ.is_same_object_as Time_Of_Day.Time_Of_Day + Time_Zone.Time_Zone -> typ.is_same_object_as Time_Zone.Time_Zone Base.Polyglot -> typ==Base.Polyglot || java_instance_check value typ _ -> @@ -335,13 +331,12 @@ java_instance_check value typ = ## UNSTABLE ADVANCED - Checks if `value` is an instance of `typ`. + Returns the type of the given value. Arguments: - - value: The value to check for being an instance of `typ`. - - typ: The type to check `self` against. -is_an : Any -> Any -> Boolean -is_an value typ = is_a value typ + - value: the value to get the type of. +type_of : Any -> Any +type_of value = @Builtin_Method "Meta.type_of_builtin" ## Represents a polyglot language. type Language diff --git a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso index 50e2bb831866..abc147df8255 100644 --- a/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso +++ b/distribution/lib/Standard/Database/0.0.0-dev/src/Data/Table.enso @@ -705,7 +705,7 @@ type Table agg = p.second new_name = p.first Aggregate_Helper.make_aggregate_column self agg new_name . catch - partitioned = results.partition (_.is_an Internal_Column_Data) + partitioned = results.partition (_.is_a Internal_Column_Data) ## When working on join we may encounter further issues with having aggregate columns exposed directly, it may be useful to re-use the `lift_aggregate` method to push the aggregates into a diff --git a/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso b/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso index ff3d5b7a4d50..e945601095d2 100644 --- a/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso +++ b/distribution/lib/Standard/Table/0.0.0-dev/src/Excel/Range.enso @@ -79,7 +79,7 @@ type Excel_Range unchanged. column_index : (Text|Integer) -> Integer column_index column = - if column.is_an Integer then column else Java_Range.parseA1Column column + if column.is_a Integer then column else Java_Range.parseA1Column column ## Creates a Range from an address. from_address : Text -> Excel_Range diff --git a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso index 1c99434ca78c..3d62f4518dd9 100644 --- a/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso +++ b/distribution/lib/Standard/Test/0.0.0-dev/src/Main.enso @@ -286,6 +286,28 @@ Any.should_equal self that frames_to_skip=0 = case self == that of msg = self.to_text + " did not equal " + that.to_text + " (at " + loc + ")." fail msg +## Asserts that `self` value is equal to the expected type value. + + Arguments: + - that: The type to check `self` for equality with. + - frames_to_skip (optional, advanced): used to alter the location which is + displayed as the source of this error. + + > Example + Assert that some type is equal to another., + + import Standard.Examples + import Standard.Test + + example_should_equal = Examples.some_tpe . should_equal_tpe Vector.Vector +Any.should_equal_type : Any -> Integer -> Assertion +Any.should_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that) of + True -> Success + False -> + loc = Meta.get_source_location 2+frames_to_skip + msg = self.to_text + " did not equal type " + that.to_text + " (at " + loc + ")." + fail msg + ## Asserts that `self` value is not equal to the expected value. Arguments: @@ -308,6 +330,28 @@ Any.should_not_equal self that frames_to_skip=0 = case self != that of msg = self.to_text + " did equal " + that.to_text + " (at " + loc + ")." fail msg +## Asserts that `self` value is not equal to the expected type value. + + Arguments: + - that: The type to check `self` for equality with. + - frames_to_skip (optional, advanced): used to alter the location which is + displayed as the source of this error. + + > Example + Assert that some type is equal to another., + + import Standard.Examples + import Standard.Test + + example_should_not_equal = Examples.some_tpe . should_not_equal_tpe Vector.Vector +Any.should_not_equal_type : Any -> Integer -> Assertion +Any.should_not_equal_type self that frames_to_skip=0 = case (self.is_same_object_as that . not) of + True -> Success + False -> + loc = Meta.get_source_location 2+frames_to_skip + msg = self.to_text + " did equal type " + that.to_text + " (at " + loc + ")." + fail msg + ## Asserts that `self` value is equal to the expected value. Arguments: diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java new file mode 100644 index 000000000000..f07f2ae840bd --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateBranchNode.java @@ -0,0 +1,59 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.Type; +import org.enso.interpreter.runtime.data.EnsoDate; + +/** An implementation of the case expression specialised to working on Date. */ +@NodeInfo(shortName = "DateMatch") +public abstract class DateBranchNode extends BranchNode { + private final Type date; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + DateBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.date = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param date the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static DateBranchNode build(Type date, RootCallTarget branch) { + return DateBranchNodeGen.create(date, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(date == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoDate(VirtualFrame frame, Object state, EnsoDate date) { + accept(frame, state, new Object[0]); + } + + @Specialization(guards = {"interop.isDate(date)", "!interop.isTime(date)"}) + void doDate( + VirtualFrame frame, + Object state, + Object date, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java new file mode 100644 index 000000000000..07475b5b11cb --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/DateTimeBranchNode.java @@ -0,0 +1,63 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.EnsoDateTime; +import org.enso.interpreter.runtime.data.Type; + +/** An implementation of the case expression specialised to working on Date_Time. */ +@NodeInfo(shortName = "DateTimeMatch") +public abstract class DateTimeBranchNode extends BranchNode { + private final Type dateTime; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + DateTimeBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.dateTime = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param dateTime the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static DateTimeBranchNode build(Type dateTime, RootCallTarget branch) { + return DateTimeBranchNodeGen.create(dateTime, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(dateTime == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoDateTime(VirtualFrame frame, Object state, EnsoDateTime dateTime) { + accept(frame, state, new Object[0]); + } + + @Specialization( + guards = { + "interop.isDate(dateTime)", + "interop.isTime(dateTime)", + }) + void doDateTime( + VirtualFrame frame, + Object state, + Object dateTime, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java new file mode 100644 index 000000000000..453f87586bb9 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeOfDayBranchNode.java @@ -0,0 +1,59 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.EnsoTimeOfDay; +import org.enso.interpreter.runtime.data.Type; + +/** An implementation of the case expression specialised to working on Time_Of_Day. */ +@NodeInfo(shortName = "TimeOfDayMatch") +public abstract class TimeOfDayBranchNode extends BranchNode { + private final Type timeOfDay; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + TimeOfDayBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.timeOfDay = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param timeOfDay the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static TimeOfDayBranchNode build(Type timeOfDay, RootCallTarget branch) { + return TimeOfDayBranchNodeGen.create(timeOfDay, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(timeOfDay == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoTimeOfDay(VirtualFrame frame, Object state, EnsoTimeOfDay timeOfDay) { + accept(frame, state, new Object[0]); + } + + @Specialization(guards = {"!interop.isDate(timeOfDay)", "interop.isTime(timeOfDay)"}) + void doTime( + VirtualFrame frame, + Object state, + Object timeOfDay, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java new file mode 100644 index 000000000000..dbcfc9bf9e36 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/controlflow/caseexpr/TimeZoneBranchNode.java @@ -0,0 +1,60 @@ +package org.enso.interpreter.node.controlflow.caseexpr; + +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.NodeInfo; +import com.oracle.truffle.api.profiles.ConditionProfile; +import org.enso.interpreter.runtime.data.EnsoTimeZone; +import org.enso.interpreter.runtime.data.Type; + +/** An implementation of the case expression specialised to working on Time_Zone. */ +@NodeInfo(shortName = "TimeZoneMatch") +public abstract class TimeZoneBranchNode extends BranchNode { + private final Type timeZone; + private final ConditionProfile profile = ConditionProfile.createCountingProfile(); + + TimeZoneBranchNode(Type vector, RootCallTarget branch) { + super(branch); + this.timeZone = vector; + } + + /** + * Creates a new node for handling matching on a case expression. + * + * @param timeZone the expression to use for matching + * @param branch the expression to be executed if (@code matcher} matches + * @return a node for matching in a case expression + */ + public static TimeZoneBranchNode build(Type timeZone, RootCallTarget branch) { + return TimeZoneBranchNodeGen.create(timeZone, branch); + } + + @Specialization + void doType(VirtualFrame frame, Object state, Type target) { + if (profile.profile(timeZone == target)) { + accept(frame, state, new Object[0]); + } + } + + @Specialization + void doEnsoZone(VirtualFrame frame, Object state, EnsoTimeZone timeZone) { + accept(frame, state, new Object[0]); + } + + @Specialization( + guards = {"!interop.isDate(zone)", "!interop.isTime(zone)", "interop.isTimeZone(zone)"}) + void doZone( + VirtualFrame frame, + Object state, + Object zone, + @CachedLibrary(limit = "10") InteropLibrary interop) { + accept(frame, state, new Object[0]); + } + + @Fallback + void doFallback(VirtualFrame frame, Object state, Object target) {} +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java index e14d899c7d6d..329935d958d6 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/IsSameObjectNode.java @@ -1,5 +1,8 @@ package org.enso.interpreter.node.expression.builtin.meta; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.nodes.Node; import org.enso.interpreter.dsl.AcceptsError; import org.enso.interpreter.dsl.BuiltinMethod; @@ -8,8 +11,20 @@ type = "Meta", name = "is_same_object", description = "Checks if the two arguments share an underlying reference.") -public class IsSameObjectNode extends Node { - boolean execute(@AcceptsError Object value_1, @AcceptsError Object value_2) { - return value_1 == value_2; +public abstract class IsSameObjectNode extends Node { + + static IsSameObjectNode build() { + return IsSameObjectNodeGen.create(); + } + + abstract boolean execute(@AcceptsError Object left, @AcceptsError Object right); + + @Specialization(limit = "3") + boolean doExecute( + Object left, + Object right, + @CachedLibrary("left") InteropLibrary leftInterop, + @CachedLibrary("right") InteropLibrary rightInteropp) { + return (left == right) || leftInterop.isIdentical(left, right, rightInteropp); } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java new file mode 100644 index 000000000000..c4fec7c7bfb2 --- /dev/null +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/expression/builtin/meta/TypeOfNode.java @@ -0,0 +1,108 @@ +package org.enso.interpreter.node.expression.builtin.meta; + +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.dsl.Fallback; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.library.CachedLibrary; +import com.oracle.truffle.api.nodes.Node; +import org.enso.interpreter.Constants; +import org.enso.interpreter.dsl.AcceptsError; +import org.enso.interpreter.dsl.BuiltinMethod; +import org.enso.interpreter.runtime.Context; +import org.enso.interpreter.runtime.builtin.Builtins; +import org.enso.interpreter.runtime.error.PanicException; +import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; +import org.enso.interpreter.runtime.number.EnsoBigInteger; +import org.enso.interpreter.runtime.type.TypesGen; + +@BuiltinMethod( + type = "Meta", + name = "type_of_builtin", + description = "Returns the type of a value.") +public abstract class TypeOfNode extends Node { + + abstract Object execute(@AcceptsError Object value); + + static TypeOfNode build() { + return TypeOfNodeGen.create(); + } + + @Specialization + Object doDouble(double value) { + Context ctx = Context.get(this); + return ctx.getBuiltins().number().getDecimal(); + } + + @Specialization + Object doLong(long value) { + Context ctx = Context.get(this); + return ctx.getBuiltins().number().getInteger(); + } + + @Specialization + Object doBigInteger(EnsoBigInteger value) { + Context ctx = Context.get(this); + return ctx.getBuiltins().number().getInteger(); + } + + @Specialization(guards = {"interop.isTime(value)", "interop.isDate(value)"}) + Object doDateTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().dateTime(); + } + + @Specialization( + guards = {"interop.isTimeZone(value)", "!interop.isDate(value)", "!interop.isTime(value)"}) + Object doTimeZone(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().timeZone(); + } + + @Specialization(guards = {"interop.isDate(value)", "!interop.isTime(value)"}) + Object doDate(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().date(); + } + + @Specialization(guards = {"interop.isTime(value)", "!interop.isDate(value)"}) + Object doTime(Object value, @CachedLibrary(limit = "3") InteropLibrary interop) { + Context ctx = Context.get(this); + return ctx.getBuiltins().timeOfDay(); + } + + @Specialization( + guards = { + "interop.hasMetaObject(value)", + "!types.hasType(value)", + "!interop.isDate(value)", + "!interop.isTime(value)", + "!interop.isTimeZone(value)" + }) + Object doMetaObject( + Object value, + @CachedLibrary(limit = "3") InteropLibrary interop, + @CachedLibrary(limit = "3") TypesLibrary types) { + try { + return interop.getMetaObject(value); + } catch (UnsupportedMessageException e) { + CompilerDirectives.transferToInterpreter(); + Builtins builtins = Context.get(this).getBuiltins(); + throw new PanicException(builtins.error().makeCompileError("invalid meta object"), this); + } + } + + @Specialization(guards = {"types.hasType(value)", "!interop.isNumber(value)"}) + Object doType( + Object value, + @CachedLibrary(limit = "3") InteropLibrary interop, + @CachedLibrary(limit = "3") TypesLibrary types) { + return types.getType(value); + } + + @Fallback + Object doAny(Object value) { + return Context.get(this).getBuiltins().error().makeCompileError("unknown type_of for " + value); + } +} diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java index b673e4142151..87b91904090c 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/type/TypesFromProxy.java @@ -34,6 +34,10 @@ public static Type fromTypeSystem(Builtins builtins, String typeName) { return builtins.array(); case ConstantsGen.BOOLEAN: return builtins.bool().getType(); + case ConstantsGen.DATE: + return builtins.date(); + case ConstantsGen.DATE_TIME: + return builtins.dateTime(); case ConstantsGen.DECIMAL: return builtins.number().getDecimal(); case ConstantsGen.ERROR: @@ -56,6 +60,12 @@ public static Type fromTypeSystem(Builtins builtins, String typeName) { return builtins.ref(); case ConstantsGen.TEXT: return builtins.text(); + case ConstantsGen.TIME_OF_DAY: + return builtins.timeOfDay(); + case ConstantsGen.TIME_ZONE: + return builtins.timeZone(); + case ConstantsGen.VECTOR: + return builtins.vector(); default: throw new CompilerError("Invalid builtin type " + typeName); } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala index 77125559c11c..c8c6befc2b26 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/codegen/IrToTruffle.scala @@ -951,6 +951,10 @@ class IrToTruffle( val any = context.getBuiltins.any val array = context.getBuiltins.array val vector = context.getBuiltins.vector + val date = context.getBuiltins.date + val dateTime = context.getBuiltins.dateTime + val timeOfDay = context.getBuiltins.timeOfDay + val timeZone = context.getBuiltins.timeZone val file = context.getBuiltins.file val builtinBool = context.getBuiltins.bool.getType val number = context.getBuiltins.number @@ -976,6 +980,14 @@ class IrToTruffle( ArrayBranchNode.build(tpe, branchCodeNode.getCallTarget) } else if (tpe == vector) { VectorBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == date) { + DateBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == dateTime) { + DateTimeBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == timeOfDay) { + TimeOfDayBranchNode.build(tpe, branchCodeNode.getCallTarget) + } else if (tpe == timeZone) { + TimeZoneBranchNode.build(tpe, branchCodeNode.getCallTarget) } else if (tpe == file) { FileBranchNode.build(tpe, branchCodeNode.getCallTarget) } else if (tpe == polyglot) { diff --git a/test/Table_Tests/src/Aggregate_Spec.enso b/test/Table_Tests/src/Aggregate_Spec.enso index 2c2ffe6b4800..38e786ba9fa4 100644 --- a/test/Table_Tests/src/Aggregate_Spec.enso +++ b/test/Table_Tests/src/Aggregate_Spec.enso @@ -1326,14 +1326,14 @@ aggregate_spec prefix table empty_table table_builder materialize is_database te new_table = table.aggregate [Group_By "Key", Concatenate "Value"] problems = Warning.get_all new_table . map .value problems.length . should_equal 1 - problems.at 0 . is_an Invalid_Aggregation_Data . should_be_true + problems.at 0 . is_a Invalid_Aggregation_Data . should_be_true problems.at 0 . rows . length . should_equal 15 Test.specify "should merge Floating Point Grouping warnings" <| new_table = table.aggregate [Group_By "Float", Count Nothing] problems = Warning.get_all new_table . map .value problems.length . should_equal 1 - problems.at 0 . is_an Floating_Point_Grouping_Data . should_be_true + problems.at 0 . is_a Floating_Point_Grouping_Data . should_be_true problems.at 0 . rows . length . should_equal 15 if is_database then diff --git a/test/Tests/src/Data/Time/Date_Spec.enso b/test/Tests/src/Data/Time/Date_Spec.enso index 54e16493ffc8..f8d0aa61d222 100644 --- a/test/Tests/src/Data/Time/Date_Spec.enso +++ b/test/Tests/src/Data/Time/Date_Spec.enso @@ -133,6 +133,13 @@ spec_with name create_new_date parse_date = date_1>date_2 . should_be_true date_1time_2 . should_be_true time_1time_2 . should_be_true time_1 Test.fail ("Unexpected result: " + result.to_text) + Test.specify "should correctly determine the type of zone" <| + zone = Time_Zone.parse "Europe/Warsaw" + Meta.type_of zone . should_equal_type Time_Zone.Time_Zone Test.group "JavaZoneId" <| Test.specify "should get system zone id" <| defaultZone = ZoneId.systemDefault @@ -61,5 +65,8 @@ spec = Json.from_pairs [["type", "Time_Zone"], ["id", "+01:02:03"]] (ZoneId.of "UTC").to_json.should_equal <| Json.from_pairs [["type", "Time_Zone"], ["id", "UTC"]] + Test.specify "should correctly determine the type of zone" <| + zone = ZoneId.systemDefault + Meta.type_of zone . should_equal_type Time_Zone.Time_Zone main = Test.Suite.run_main spec diff --git a/test/Tests/src/Semantic/Case_Spec.enso b/test/Tests/src/Semantic/Case_Spec.enso index 2c513ccc537d..69ac27aed71b 100644 --- a/test/Tests/src/Semantic/Case_Spec.enso +++ b/test/Tests/src/Semantic/Case_Spec.enso @@ -88,6 +88,40 @@ spec = Test.group "Pattern Matches" <| case Any of Any -> Nothing _ -> Test.fail "Expected the Any constructor to match." + Test.specify "should be able to match on date/time values" <| + new_date = Date.new 2020 6 1 + new_date_time = Date_Time.new 2020 6 1 + new_time = Time_Of_Day.new 11 11 + new_zone = Time_Zone.system + + case new_date of + Date_Time.Date_Time -> Test.fail "Expected date value to match Date." + Time_Of_Day.Time_Of_Day -> Test.fail "Expected date value to match Date." + Time_Zone.Time_Zone -> Test.fail "Expected date value to match Date." + Date.Date -> Nothing + _ -> Test.fail "Expected date value to match Date." + + case new_date_time of + Date.Date -> Test.fail "Expected datetime value to match Date_Time." + Time_Of_Day.Time_Of_Day -> Test.fail "Expected datetime value to match Date_Time." + Time_Zone.Time_Zone -> Test.fail "Expected datetime value to match Date_Time." + Date_Time.Date_Time -> Nothing + _ -> Test.fail "Expected datetime value to match Date_Time." + + case new_time of + Date.Date -> Test.fail "Expected time value to match Time_Of_Day." + Date_Time.Date_Time -> Test.fail "Expected time value to match Time_Of_Day." + Time_Zone.Time_Zone -> Test.fail "Expected time value to match Time_Of_Day." + Time_Of_Day.Time_Of_Day -> Nothing + _ -> Test.fail "Expected time value to match Time_Of_Day." + + case new_zone of + Date.Date -> Test.fail "Expected timezone value to match Time_Zone." + Date_Time.Date_Time -> Test.fail "Expected timezone to match Time_Zone." + Time_Of_Day.Time_Of_Day -> Test.fail "Expected timezone to match Time_Zone." + Time_Zone.Time_Zone -> Nothing + _ -> Test.fail "Expected timezone value to match Time_Zone." + Test.specify "should be able to match on literal values" <| value_1 = 42 value_2 = "foo" @@ -112,8 +146,6 @@ spec = Test.group "Pattern Matches" <| "ę" -> Test.fail "Expected value to match constant." '\u0065\u{301}' -> Nothing _ -> Test.fail "Expected value to match constant." - - Test.specify "should be able to match on literal values nested in constructors" <| value_1 = Cons 42 Nil value_2 = Cons (Cons 42 Nil) Nil diff --git a/test/Tests/src/Semantic/Meta_Spec.enso b/test/Tests/src/Semantic/Meta_Spec.enso index cceb7e36eba8..1dd189d3ba0c 100644 --- a/test/Tests/src/Semantic/Meta_Spec.enso +++ b/test/Tests/src/Semantic/Meta_Spec.enso @@ -3,6 +3,11 @@ import Standard.Base import Standard.Base.System.Platform +polyglot java import java.lang.Object as JObject +polyglot java import java.lang.Long as JLong +polyglot java import java.lang.Exception as JException +polyglot java import java.io.IOException +polyglot java import java.util.ArrayList polyglot java import java.util.Random polyglot java import java.util.Locale as JavaLocale @@ -39,53 +44,110 @@ spec = Test.group "Meta-Value Manipulation" <| meta_err.is_a Meta.Error_Data . should_be_true meta_err.value . should_equal "My Error" Test.specify "should allow checking if a value is of a certain type" <| - 1.is_an Any . should_be_true - 1.2.is_an Any . should_be_true - (My_Type_Data 1 "foo" Nothing).is_an Any . should_be_true + 1.is_a Any . should_be_true + 1.2.is_a Any . should_be_true + (My_Type_Data 1 "foo" Nothing).is_a Any . should_be_true - Array.is_an Array . should_be_true - [].to_array.is_an Array . should_be_true + Array.is_a Array . should_be_true + [].to_array.is_a Array . should_be_true [].to_array.is_a Decimal . should_be_false + [1,2,3].is_a Vector.Vector . should_be_true + [1,2,3].is_a Array . should_be_false Boolean.is_a Boolean . should_be_true True.is_a Boolean . should_be_true False.is_a Boolean . should_be_true - True.is_an Integer . should_be_false + True.is_a Integer . should_be_false "".is_a Text . should_be_true "".is_a Decimal . should_be_false - 1.is_an Array . should_be_false - 1.is_an Integer . should_be_true + 1.is_a Array . should_be_false + 1.is_a Integer . should_be_true 1.is_a Number . should_be_true 1.is_a Decimal . should_be_false 1.0.is_a Number . should_be_true 1.0.is_a Decimal . should_be_true - 1.0.is_an Integer . should_be_false + 1.0.is_a Integer . should_be_false 1.0.is_a Text . should_be_false random_gen = Random.new Meta.is_a random_gen Polyglot . should_be_true - Meta.is_an random_gen Integer . should_be_false + Meta.is_a random_gen Integer . should_be_false (My_Type_Data 1 "foo" Nothing).is_a My_Type_Data . should_be_true (My_Type_Data 1 "foo" Nothing).is_a Test_Type_Data . should_be_false (My_Type_Data 1 "foo" Nothing).is_a Number . should_be_false err = Error.throw "Error Value" - 1.is_an Error . should_be_false - err.is_an Error . should_be_true + 1.is_a Error . should_be_false + err.is_a Error . should_be_true err.is_a Text . should_be_false - Meta.is_an err Error . should_be_true + Meta.is_a err Error . should_be_true Meta.is_a err Text . should_be_false + Meta.is_a Date.now Date.Date . should_be_true + Meta.is_a Date_Time.now Date_Time.Date_Time . should_be_true + Meta.is_a Date_Time.now Date.Date . should_be_false + Meta.is_a Time_Of_Day.now Time_Of_Day.Time_Of_Day . should_be_true + Meta.is_a Time_Of_Day.now Date.Date . should_be_false + Meta.is_a Date_Time.now.zone Time_Zone.Time_Zone . should_be_true + Meta.is_a Date_Time.now.zone Date.Date . should_be_false + + Test.specify "should allow for returning the type of a value" <| + n_1 = Meta.type_of 42 + n_1 . should_equal_type Integer + n_1 . should_not_equal_type Decimal + + n_2 = Meta.type_of 2.81 + n_2 . should_equal_type Decimal + n_2 . should_not_equal_type Integer + + n_3 = Meta.type_of (Long.MAX_VALUE * 2) + n_3 . should_equal_type Integer + n_3 . should_not_equal_type Decimal + + v_tpe = Meta.type_of [1,2,3] + v_tpe . should_equal_type Vector.Vector + v_tpe . should_not_equal_type Array + Meta.type_of [1,2,3].to_array . should_equal_type Array + + Meta.type_of "foo" . should_equal_type Text + Meta.type_of "0" . should_not_equal_type Integer + + Meta.type_of True . should_equal_type Boolean + Meta.type_of False . should_not_equal_type Any + + (Meta.type_of Date.now) . should_equal_type Date.Date + (Meta.type_of Date.now) . should_not_equal_type Date_Time.Date_Time + (Meta.type_of Date_Time.now) . should_equal_type Date_Time.Date_Time + (Meta.type_of Date_Time.now) . should_not_equal_type Date.Date + (Meta.type_of Time_Of_Day.now) . should_equal_type Time_Of_Day.Time_Of_Day + (Meta.type_of Time_Of_Day.now) . should_not_equal_type Date.Date + (Meta.type_of Date_Time.now.zone) . should_equal_type Time_Zone.Time_Zone + (Meta.type_of Date_Time.now.zone) . should_not_equal_type Date.Date + (Meta.type_of Time_Zone.local) . should_equal_type Time_Zone.Time_Zone + (Meta.type_of Time_Zone.system) . should_equal_type Time_Zone.Time_Zone + + list = ArrayList.new + list.add 123 + list_tpe = Meta.type_of list + list_tpe . should_not_equal_type JObject + list_tpe . should_equal_type ArrayList + + e = IOException.new "meh" + e_tpe = Meta.type_of e + e_tpe . should_equal_type IOException + e_tpe . should_not_equal_type JException + location_pending = case Platform.os of Platform.Windows -> "This test is disabled on Windows until issue 1561 is fixed." _ -> Nothing + Test.specify "should allow to get the source location of a frame" pending=location_pending <| src = Meta.get_source_location 0 - loc = "Meta_Spec.enso:87:15-40" + loc = "Meta_Spec.enso:149:15-40" src.take (Last loc.length) . should_equal loc Test.specify "should allow to get qualified type names of values" <|