mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 02:01:47 +03:00
Introducing generic Any.to type conversion method (#7704)
This commit is contained in:
parent
54689510a4
commit
1437a671e1
@ -560,6 +560,7 @@
|
||||
- [Expose `Text.normalize`.][7425]
|
||||
- [Implemented new value types (various sizes of `Integer` type, fixed-length
|
||||
and length-limited `Char` type) for the in-memory `Table` backend.][7557]
|
||||
- [Introducing generic `Any.to` conversion method][7704]
|
||||
- [Added `take` and `drop` to database tables.][7615]
|
||||
- [Added ability to specify expected value type in `Column.from_vector`,
|
||||
`Column.map` and `Column.zip`.][7637]
|
||||
@ -797,6 +798,7 @@
|
||||
[7297]: https://github.com/enso-org/enso/pull/7297
|
||||
[7425]: https://github.com/enso-org/enso/pull/7425
|
||||
[7557]: https://github.com/enso-org/enso/pull/7557
|
||||
[7704]: https://github.com/enso-org/enso/pull/7704
|
||||
[7615]: https://github.com/enso-org/enso/pull/7615
|
||||
[7637]: https://github.com/enso-org/enso/pull/7637
|
||||
|
||||
|
@ -20,6 +20,51 @@ from project.Function import const
|
||||
be used in that position.
|
||||
@Builtin_Type
|
||||
type Any
|
||||
## GROUP Conversions
|
||||
Generic conversion of an arbitrary Enso value to requested type.
|
||||
Delegates to appropriate `.from` conversion method, if it exists.
|
||||
If such method doesn't exist, `No_Such_Conversion` panic is raised.
|
||||
|
||||
Arguments:
|
||||
- typ: the requested type.
|
||||
|
||||
> Example
|
||||
Following code defines conversion of a `Complex` type to a `Number`
|
||||
by computing absolute distance from `0`. The code yields `5.0`:
|
||||
|
||||
type Complex
|
||||
Value re:Number im:Number
|
||||
|
||||
Number.from (that:Complex) = that.re*that.re+that.im*that.im . sqrt
|
||||
|
||||
Complex.Value 3 4 . to Number
|
||||
|
||||
> Example
|
||||
`.from` conversion methods may have additional arguments
|
||||
with default values. Thus the conversion from `Complex` to
|
||||
`Number` may take additional argument:
|
||||
|
||||
type Complex
|
||||
Value re:Number im:Number
|
||||
|
||||
Number.from (that:Complex) = that.re*that.re+that.im*that.im . sqrt
|
||||
|
||||
Complex.Value 3 4 . to Number
|
||||
|
||||
type Complex
|
||||
Value re:Number im:Number
|
||||
|
||||
Number.from (that:Complex) (ignore_im:Boolean=False) = case ignore_im of
|
||||
False -> that.re*that.re+that.im*that.im . sqrt
|
||||
True -> that.re
|
||||
|
||||
yields_3 = Complex.Value 3 4 . to Number ignore_im=True
|
||||
yields_5 = Complex.Value 3 4 . to Number ignore_im=False
|
||||
default5 = Complex.Value 3 4 . to Number
|
||||
|
||||
to : Any -> Any ! No_Such_Conversion
|
||||
to self typ = typ.from self ...
|
||||
|
||||
## GROUP Conversions
|
||||
Generic conversion of an arbitrary Enso value to a corresponding textual
|
||||
representation.
|
||||
|
@ -62,10 +62,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
IndirectInvokeFunctionNode indirectInvokeFunctionNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
that,
|
||||
InvokeConversionNode.extractConstructor(this, self),
|
||||
typesLib.getType(that),
|
||||
conversion);
|
||||
that, InvokeConversionNode.extractType(this, self), typesLib.getType(that), conversion);
|
||||
return indirectInvokeFunctionNode.execute(
|
||||
function,
|
||||
frame,
|
||||
@ -95,7 +92,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.execute(
|
||||
InvokeConversionNode.extractConstructor(this, self),
|
||||
InvokeConversionNode.extractType(this, self),
|
||||
EnsoContext.get(this).getBuiltins().dataflowError(),
|
||||
conversion);
|
||||
if (function != null) {
|
||||
@ -184,7 +181,7 @@ public abstract class IndirectInvokeConversionNode extends Node {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
txt,
|
||||
InvokeConversionNode.extractConstructor(this, self),
|
||||
InvokeConversionNode.extractType(this, self),
|
||||
EnsoContext.get(this).getBuiltins().text(),
|
||||
conversion);
|
||||
arguments[0] = txt;
|
||||
|
@ -1,16 +1,8 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.*;
|
||||
import com.oracle.truffle.api.dsl.Cached.Shared;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
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 com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
|
||||
import org.enso.interpreter.node.callable.resolver.ConversionResolverNode;
|
||||
@ -22,10 +14,25 @@ import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.runtime.data.ArrayRope;
|
||||
import org.enso.interpreter.runtime.data.Type;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
import org.enso.interpreter.runtime.error.*;
|
||||
import org.enso.interpreter.runtime.error.DataflowError;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.PanicSentinel;
|
||||
import org.enso.interpreter.runtime.error.Warning;
|
||||
import org.enso.interpreter.runtime.error.WithWarnings;
|
||||
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
|
||||
import org.enso.interpreter.runtime.state.State;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Cached.Shared;
|
||||
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.interop.UnsupportedMessageException;
|
||||
import com.oracle.truffle.api.library.CachedLibrary;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
|
||||
public abstract class InvokeConversionNode extends BaseNode {
|
||||
private @Child InvokeFunctionNode invokeFunctionNode;
|
||||
private @Child InvokeConversionNode childDispatch;
|
||||
@ -79,18 +86,18 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
Object that,
|
||||
Object[] arguments);
|
||||
|
||||
static Type extractConstructor(Node thisNode, Object self) {
|
||||
if (self instanceof Type) {
|
||||
return (Type) self;
|
||||
static Type extractType(Node thisNode, Object self) {
|
||||
if (self instanceof Type type) {
|
||||
return type;
|
||||
} else {
|
||||
throw new PanicException(
|
||||
EnsoContext.get(thisNode).getBuiltins().error().makeInvalidConversionTarget(self),
|
||||
thisNode);
|
||||
var ctx = EnsoContext.get(thisNode);
|
||||
var err = ctx.getBuiltins().error().makeInvalidConversionTarget(self);
|
||||
throw new PanicException(err, thisNode);
|
||||
}
|
||||
}
|
||||
|
||||
Type extractConstructor(Object self) {
|
||||
return extractConstructor(this, self);
|
||||
private Type extractType(Object self) {
|
||||
return extractType(this, self);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"dispatch.hasType(that)", "!dispatch.hasSpecialDispatch(that)"})
|
||||
@ -102,11 +109,15 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
Object that,
|
||||
Object[] arguments,
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary dispatch,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
that, extractConstructor(self), dispatch.getType(that), conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode resolveNode) {
|
||||
var thatType = dispatch.getType(that);
|
||||
if (thatType == self) {
|
||||
return that;
|
||||
} else {
|
||||
var selfType = extractType(self);
|
||||
var function = resolveNode.expectNonNull(that, selfType, thatType, conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
@ -120,8 +131,7 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary dispatch,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.execute(
|
||||
extractConstructor(self),
|
||||
conversionResolverNode.execute(extractType(self),
|
||||
EnsoContext.get(this).getBuiltins().dataflowError(),
|
||||
conversion);
|
||||
if (function != null) {
|
||||
@ -197,9 +207,8 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
String str = interop.asString(that);
|
||||
Text txt = Text.create(str);
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
txt,
|
||||
extractConstructor(self),
|
||||
conversionResolverNode.expectNonNull(txt,
|
||||
extractType(self),
|
||||
EnsoContext.get(this).getBuiltins().text(),
|
||||
conversion);
|
||||
arguments[0] = txt;
|
||||
@ -227,8 +236,7 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary typesLib,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
that, extractConstructor(self), EnsoContext.get(this).getBuiltins().date(), conversion);
|
||||
conversionResolverNode.expectNonNull(that, extractType(self), EnsoContext.get(this).getBuiltins().date(), conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
}
|
||||
|
||||
@ -250,9 +258,8 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary typesLib,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
that,
|
||||
extractConstructor(self),
|
||||
conversionResolverNode.expectNonNull(that,
|
||||
extractType(self),
|
||||
EnsoContext.get(this).getBuiltins().timeOfDay(),
|
||||
conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
@ -276,9 +283,8 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary typesLib,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
that,
|
||||
extractConstructor(self),
|
||||
conversionResolverNode.expectNonNull(that,
|
||||
extractType(self),
|
||||
EnsoContext.get(this).getBuiltins().dateTime(),
|
||||
conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
@ -301,9 +307,8 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary typesLib,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
that,
|
||||
extractConstructor(self),
|
||||
conversionResolverNode.expectNonNull(that,
|
||||
extractType(self),
|
||||
EnsoContext.get(this).getBuiltins().duration(),
|
||||
conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
@ -326,9 +331,8 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("typesLib") @CachedLibrary(limit = "10") TypesLibrary typesLib,
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
Function function =
|
||||
conversionResolverNode.expectNonNull(
|
||||
thatMap,
|
||||
extractConstructor(self),
|
||||
conversionResolverNode.expectNonNull(thatMap,
|
||||
extractType(self),
|
||||
EnsoContext.get(this).getBuiltins().map(),
|
||||
conversion);
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
@ -352,8 +356,7 @@ public abstract class InvokeConversionNode extends BaseNode {
|
||||
@Shared("conversionResolverNode") @Cached ConversionResolverNode conversionResolverNode) {
|
||||
var ctx = EnsoContext.get(this);
|
||||
var function =
|
||||
conversionResolverNode.execute(
|
||||
extractConstructor(self), ctx.getBuiltins().any(), conversion);
|
||||
conversionResolverNode.execute(extractType(self), ctx.getBuiltins().any(), conversion);
|
||||
if (function == null) {
|
||||
throw new PanicException(
|
||||
ctx.getBuiltins().error().makeNoSuchConversion(self, that, conversion), this);
|
||||
|
@ -1,4 +1,5 @@
|
||||
from Standard.Base import all
|
||||
import Standard.Base.Errors.Common.No_Such_Conversion
|
||||
|
||||
import project.Semantic.Conversion.Methods
|
||||
import project.Semantic.Conversion.Types
|
||||
@ -120,7 +121,30 @@ spec =
|
||||
Hello.formulate [ Hello.Say "Proper", Hello.Say "Type" ] . should_equal "ProperType"
|
||||
Hello.formulate [ Foo.Value "Perform", Bar.Value "Conversion" ] . should_equal "PERFORM conversion!"
|
||||
|
||||
Hello.from (that:Foo) = Hello.Say <| (that.foo.to_case Case.Upper) + " "
|
||||
Hello.from (that:Bar) = Hello.Say <| (that.bar.to_case Case.Lower) + "!"
|
||||
Test.specify "Convert Foo.to Hello" <|
|
||||
hello = Foo.Value "Perform" . to Hello
|
||||
hello . msg . should_equal "PERFORM "
|
||||
|
||||
Test.specify "Convert Bar.to Hello" <|
|
||||
hello = Bar.Value "Conversion" . to Hello
|
||||
hello . msg . should_equal "conversion!"
|
||||
|
||||
Test.specify "Convert Bar.to Hello with other suffix" <|
|
||||
hello = Bar.Value "Conversion" . to Hello suffix="?"
|
||||
hello . msg . should_equal "conversion?"
|
||||
|
||||
Test.specify "Idempotent convert Hello.to Hello" <|
|
||||
Hello.Say "Hi there!" . to Hello . msg . should_equal "Hi there!"
|
||||
|
||||
Test.specify "Unknown convertion Text.to Hello" <|
|
||||
h = Panic.recover No_Such_Conversion <| "Hi there!" . to Hello
|
||||
h . should_fail_with No_Such_Conversion
|
||||
|
||||
Test.specify "Use Any.to in Conversion_Use module" <|
|
||||
Hello.formulate_with_to [ Hello.Say "Proper", Hello.Say "Type" ] . should_equal "ProperType"
|
||||
Hello.formulate_with_to [ Foo.Value "Perform", Bar.Value "Conversion" ] . should_equal "PERFORM conversion!"
|
||||
|
||||
Hello.from (that:Foo) suffix=" " = Hello.Say <| (that.foo.to_case Case.Upper) + suffix
|
||||
Hello.from (that:Bar) suffix="!" = Hello.Say <| (that.bar.to_case Case.Lower) + suffix
|
||||
|
||||
main = Test_Suite.run_main spec
|
||||
|
@ -7,3 +7,9 @@ type Hello
|
||||
formulate arr =
|
||||
process (t:Text) (h:Hello) = t + h.msg
|
||||
arr.fold "" process
|
||||
|
||||
formulate_with_to : Vector Hello -> Text
|
||||
formulate_with_to arr =
|
||||
arr.fold "" t-> h->
|
||||
m = h.to Hello . msg
|
||||
t + m
|
||||
|
Loading…
Reference in New Issue
Block a user