mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 13:02:07 +03:00
Enso Date shall be converted to java.time.LocalDate when passed to Java (#3374)
This commit is contained in:
parent
d5d5d3aac5
commit
ab692b3b74
@ -176,6 +176,7 @@
|
||||
- [Fixed issues related to constructors' default arguments][3330]
|
||||
- [Fixed compiler issue related to module cache.][3367]
|
||||
- [Fixed execution of defaulted arguments of Atom Constructors][3358]
|
||||
- [Converting Enso Date to java.time.LocalDate and back][3374]
|
||||
|
||||
[3227]: https://github.com/enso-org/enso/pull/3227
|
||||
[3248]: https://github.com/enso-org/enso/pull/3248
|
||||
@ -184,6 +185,7 @@
|
||||
[3358]: https://github.com/enso-org/enso/pull/3358
|
||||
[3360]: https://github.com/enso-org/enso/pull/3360
|
||||
[3367]: https://github.com/enso-org/enso/pull/3367
|
||||
[3374]: https://github.com/enso-org/enso/pull/3374
|
||||
|
||||
# Enso 2.0.0-alpha.18 (2021-10-12)
|
||||
|
||||
|
@ -20,7 +20,7 @@ polyglot java import org.enso.base.Time_Utils
|
||||
|
||||
example_now = Date.now
|
||||
now : Date
|
||||
now = Date LocalDate.now
|
||||
now = LocalDate.now
|
||||
|
||||
## ALIAS Current Date
|
||||
|
||||
@ -60,7 +60,7 @@ today = here.now
|
||||
|
||||
new : Integer -> Integer -> Integer -> Date ! Time.Time_Error
|
||||
new year (month = 1) (day = 1) =
|
||||
Panic.recover Any (Date (LocalDate.of year month day)) . catch e-> case e of
|
||||
Panic.recover Any (LocalDate.of year month day) . catch e-> case e of
|
||||
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
|
||||
x -> x
|
||||
|
||||
@ -253,7 +253,7 @@ type Date
|
||||
example_subtract = Date.new 2020 - 7.days
|
||||
- : Duration -> Date
|
||||
- amount = if amount.is_time then Error.throw (Time.Time_Error "Date does not support time intervals") else
|
||||
Date (this . internal_local_date . minus amount.internal_period)
|
||||
(this . internal_local_date . minus amount.internal_period)
|
||||
|
||||
## Format this date using the default formatter.
|
||||
|
||||
|
@ -547,47 +547,36 @@ For more details about the CI setup, you can check the
|
||||
|
||||
### Running Enso
|
||||
|
||||
The only component in this repository with a proper executable is the Enso
|
||||
interpreter. It can be run using the sbt `run` command in the project `runner`
|
||||
and provides a rudimentary command-line interface to the basic capabilities of
|
||||
the interpreter.
|
||||
|
||||
Enso should be launched using the `distribution/bin` scripts.
|
||||
|
||||
#### Interpreter
|
||||
|
||||
Interpreter is started with the `distribution/bin/enso` script and requires
|
||||
`runner.jar` and `runtime.jar` (see
|
||||
The language interpreter can be started by the `bin/enso` launcher script
|
||||
located inside of the Enso runtime distribution. Use the following `sbt` command
|
||||
to compile necessary bits (see
|
||||
[Building the Interperter CLI Fat Jar](#building-the-interpreter-cli-fat-jar))
|
||||
to be built and copied (or linked) to the `distribution/component` directory.
|
||||
and generate the Enso distribution:
|
||||
|
||||
##### Bash
|
||||
|
||||
```bash
|
||||
# build runtime.jar and runner.jar
|
||||
sbt engine-runner/assembly
|
||||
# link or copy jars to the distributiong
|
||||
mkdir -p distribution/component
|
||||
cd distribution/component
|
||||
ln -s ../../runtime.jar .
|
||||
ln -s ../../runner.jar .
|
||||
$ sbt buildEngineDistribution
|
||||
...
|
||||
Engine package created at built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev
|
||||
```
|
||||
|
||||
##### PowerShell
|
||||
|
||||
```powershell
|
||||
# build runtime.jar and runner.jar
|
||||
sbt.bat engine-runner/assembly
|
||||
# copy jars to the distributiong
|
||||
mkdir -p .\distribution\component
|
||||
cp .\runtime.jar .\distribution\component\
|
||||
cp .\runner.jar .\distribution\component\
|
||||
sbt.bat buildEngineDistribution
|
||||
```
|
||||
|
||||
Detailed information on the flags it supports is shown by the `--help` flag, but
|
||||
the primary functionality is as follows:
|
||||
Then one can execute the launcher:
|
||||
|
||||
- `--new PATH`: Creates a new Enso project at the location spcified by `PATH`.
|
||||
```bash
|
||||
$ built-distribution/enso-engine-0.0.0-dev-linux-amd64/enso-0.0.0-dev/bin/enso
|
||||
```
|
||||
|
||||
Detailed information on the flags it supports can be shown with the `--help`
|
||||
flag, but the primary functionality is as follows:
|
||||
|
||||
- `--new PATH`: Creates a new Enso project at the location specified by `PATH`.
|
||||
- `--run PATH`: Executes the interpreter on the Enso source specified by `PATH`.
|
||||
In this case, `PATH` must point to either a standalone Enso file or an Enso
|
||||
project.
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.enso.interpreter.node.callable;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.dsl.*;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
@ -15,7 +14,6 @@ import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.dispatch.InvokeFunctionNode;
|
||||
import org.enso.interpreter.node.callable.resolver.*;
|
||||
@ -34,7 +32,6 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
public abstract class InvokeMethodNode extends BaseNode {
|
||||
private @Child InvokeFunctionNode invokeFunctionNode;
|
||||
private final ConditionProfile errorReceiverProfile = ConditionProfile.createCountingProfile();
|
||||
private final BranchProfile polyglotArgumentErrorProfile = BranchProfile.create();
|
||||
private @Child InvokeMethodNode childDispatch;
|
||||
private final int argumentCount;
|
||||
private final int thisArgumentPosition;
|
||||
@ -162,8 +159,7 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(_this)",
|
||||
"!methods.hasSpecialDispatch(_this)",
|
||||
"polyglotCallType != NOT_SUPPORTED",
|
||||
"polyglotCallType != CONVERT_TO_TEXT"
|
||||
"polyglotCallType.isInteropLibrary()",
|
||||
})
|
||||
Stateful doPolyglot(
|
||||
VirtualFrame frame,
|
||||
@ -235,6 +231,34 @@ public abstract class InvokeMethodNode extends BaseNode {
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(_this)",
|
||||
"!methods.hasSpecialDispatch(_this)",
|
||||
"getPolyglotCallType(_this, symbol.getName(), interop) == CONVERT_TO_DATE"
|
||||
})
|
||||
Stateful doConvertDate(
|
||||
VirtualFrame frame,
|
||||
Object state,
|
||||
UnresolvedSymbol symbol,
|
||||
Object _this,
|
||||
Object[] arguments,
|
||||
@CachedLibrary(limit = "10") MethodDispatchLibrary methods,
|
||||
@CachedLibrary(limit = "1") MethodDispatchLibrary dateDispatch,
|
||||
@CachedLibrary(limit = "10") InteropLibrary interop
|
||||
) {
|
||||
try {
|
||||
var dateConstructor = Context.get(this).getDateConstructor();
|
||||
Object date = dateConstructor.isPresent() ? dateConstructor.get().newInstance(_this) : _this;
|
||||
Function function = dateDispatch.getFunctionalDispatch(date, symbol);
|
||||
arguments[0] = date;
|
||||
return invokeFunctionNode.execute(function, frame, state, arguments);
|
||||
} catch (MethodDispatchLibrary.NoSuchMethodException e) {
|
||||
throw new PanicException(
|
||||
Context.get(this).getBuiltins().error().makeNoSuchMethodError(_this, symbol), this);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = {
|
||||
"!methods.hasFunctionalDispatch(_this)",
|
||||
|
@ -43,8 +43,25 @@ public abstract class HostMethodCallNode extends Node {
|
||||
* org.enso.interpreter.runtime.data.text.Text} and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_TEXT,
|
||||
/**
|
||||
* The method call should be handled by converting {@code _this} to a {@code
|
||||
* Standard.Base.Data.Time.Date} and dispatching natively.
|
||||
*/
|
||||
CONVERT_TO_DATE,
|
||||
/** The method call should be handled by dispatching through the {@code Any} type. */
|
||||
NOT_SUPPORTED
|
||||
NOT_SUPPORTED;
|
||||
|
||||
/**
|
||||
* Directly use {@link InteropLibrary}, or not. Types that return false are
|
||||
* either {@link #NOT_SUPPORTED unsupported} or require
|
||||
* additional conversions like {@link #CONVERT_TO_TEXT} and {@link #CONVERT_TO_DATE}.
|
||||
*
|
||||
* @return true if one can directly pass this object to
|
||||
* {@link InteropLibrary}
|
||||
*/
|
||||
public boolean isInteropLibrary() {
|
||||
return this != NOT_SUPPORTED && this != CONVERT_TO_TEXT && this != CONVERT_TO_DATE;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String ARRAY_LENGTH_NAME = "length";
|
||||
@ -76,9 +93,12 @@ public abstract class HostMethodCallNode extends Node {
|
||||
return PolyglotCallType.READ_ARRAY_ELEMENT;
|
||||
} else if (library.isString(_this)) {
|
||||
return PolyglotCallType.CONVERT_TO_TEXT;
|
||||
} else {
|
||||
return PolyglotCallType.NOT_SUPPORTED;
|
||||
} else if (library.isDate(_this)) {
|
||||
if (!library.isTime(_this)) {
|
||||
return PolyglotCallType.CONVERT_TO_DATE;
|
||||
}
|
||||
}
|
||||
return PolyglotCallType.NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.TruffleFile;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
@ -64,6 +65,8 @@ public class Context {
|
||||
private final DistributionManager distributionManager;
|
||||
private final LockManager lockManager;
|
||||
private final AtomicLong clock = new AtomicLong();
|
||||
@CompilerDirectives.CompilationFinal
|
||||
private Optional<AtomConstructor> date;
|
||||
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
@ -463,4 +466,24 @@ public class Context {
|
||||
public long clockTick() {
|
||||
return clock.getAndIncrement();
|
||||
}
|
||||
|
||||
/** Return the {@code Standard.Base.Data.Time.Date} constructor.
|
||||
*
|
||||
* @return optional with {@link AtomConstructor} for the date, if it can be found
|
||||
*/
|
||||
public Optional<AtomConstructor> getDateConstructor() {
|
||||
if (date == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
final String stdDateModuleName = "Standard.Base.Data.Time.Date";
|
||||
final String stdDateConstructorName = "Date";
|
||||
ensureModuleIsLoaded(stdDateModuleName);
|
||||
Optional<Module> dateModule = findModule(stdDateModuleName);
|
||||
if (dateModule.isPresent()) {
|
||||
date = Optional.ofNullable(dateModule.get().getScope().getConstructors().get(stdDateConstructorName));
|
||||
} else {
|
||||
date = Optional.empty();
|
||||
}
|
||||
}
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.enso.interpreter.runtime.callable.atom;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
@ -203,6 +205,21 @@ public class Atom implements TruffleObject {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
boolean isDate(@CachedLibrary("this") InteropLibrary iop) {
|
||||
var dateConstructor = Context.get(iop).getDateConstructor();
|
||||
if (dateConstructor.isPresent()) {
|
||||
return dateConstructor.get() == this.constructor;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
LocalDate asDate(@CachedLibrary(limit = "3") InteropLibrary iop) throws UnsupportedMessageException {
|
||||
return iop.asDate(fields[0]);
|
||||
}
|
||||
|
||||
@ExportMessage
|
||||
static class GetFunctionalDispatch {
|
||||
static final int CACHE_SIZE = 10;
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.enso.interpreter.runtime.scope;
|
||||
|
||||
import com.google.common.base.Joiner;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
|
||||
import java.util.*;
|
||||
|
@ -0,0 +1,94 @@
|
||||
package org.enso.interpreter.test.semantic
|
||||
|
||||
import org.enso.interpreter.test.{InterpreterContext, InterpreterTest}
|
||||
|
||||
class DateTest extends InterpreterTest {
|
||||
override def subject: String = "LocalDate"
|
||||
|
||||
override def specify(implicit
|
||||
interpreterContext: InterpreterContext
|
||||
): Unit = {
|
||||
"evaluate a date expression" in {
|
||||
val code =
|
||||
s"""from Standard.Builtins import all
|
||||
|
|
||||
|import Standard.Base.Data.Time.Date
|
||||
|
|
||||
|main =
|
||||
| IO.println (Date.new 2022 04 01)
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List("2022-04-01")
|
||||
}
|
||||
|
||||
"print out java date" in {
|
||||
val code =
|
||||
s"""from Standard.Builtins import all
|
||||
|polyglot java import java.time.LocalDate
|
||||
|
|
||||
|main =
|
||||
| IO.println (LocalDate.of 2022 04 01)
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List("2022-04-01")
|
||||
}
|
||||
|
||||
"send enso date into java" in {
|
||||
val code =
|
||||
s"""from Standard.Builtins import all
|
||||
|polyglot java import java.time.LocalTime
|
||||
|import Standard.Base.Data.Time.Date
|
||||
|
|
||||
|main =
|
||||
| ensodate = Date.new 2022 04 01
|
||||
| javatime = LocalTime.of 10 26
|
||||
| javatimedate = javatime.atDate ensodate
|
||||
| javadate = javatimedate . toLocalDate
|
||||
| IO.println javadate
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List("2022-04-01")
|
||||
}
|
||||
|
||||
"check java date has enso methods" in {
|
||||
val code =
|
||||
s"""from Standard.Builtins import all
|
||||
|polyglot java import java.time.LocalDate
|
||||
|import Standard.Base.Data.Time.Date
|
||||
|
|
||||
|main =
|
||||
| javadate = LocalDate.of 2022 4 1
|
||||
| ensoyear = javadate.year
|
||||
| ensomonth = javadate.month
|
||||
| ensoday = javadate.day
|
||||
| ensotext = javadate.to_text
|
||||
| IO.println ensoyear
|
||||
| IO.println ensomonth
|
||||
| IO.println ensoday
|
||||
| IO.println ensotext
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List("2022", "4", "1", "2022-04-01")
|
||||
}
|
||||
|
||||
"check enso date has enso methods" in {
|
||||
val code =
|
||||
s"""from Standard.Builtins import all
|
||||
|import Standard.Base.Data.Time.Date
|
||||
|
|
||||
|main =
|
||||
| ensodate = Date.new 2022 4 1
|
||||
| ensoyear = ensodate.year
|
||||
| ensomonth = ensodate.month
|
||||
| ensoday = ensodate.day
|
||||
| ensotext = ensodate.to_text
|
||||
| IO.println ensoyear
|
||||
| IO.println ensomonth
|
||||
| IO.println ensoday
|
||||
| IO.println ensotext
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
consumeOut shouldEqual List("2022", "4", "1", "2022-04-01")
|
||||
}
|
||||
}
|
||||
}
|
@ -9,22 +9,46 @@ polyglot java import java.lang.String
|
||||
polyglot java import java.lang.StringBuilder as Java_String_Builder
|
||||
polyglot java import java.util.ArrayList
|
||||
|
||||
spec = Test.group "Java FFI" <|
|
||||
Test.specify "should call methods imported from Java" <|
|
||||
Long.sum 1 2 . should_equal 3
|
||||
import Standard.Base.Data.Time.Date
|
||||
polyglot java import java.time.LocalDate
|
||||
polyglot java import java.time.LocalTime
|
||||
|
||||
Test.specify "should call constructors imported from Java" <|
|
||||
list = ArrayList.new
|
||||
list.add 432
|
||||
list.get 0 . should_equal 432
|
||||
Test.specify "should auto-convert numeric types across the polyglot boundary" <|
|
||||
(Float.valueOf "123.3" + 5).should_equal 128.3 epsilon=0.0001
|
||||
(Integer.sum 1 2 + 3) . should_equal 6
|
||||
Test.specify "should auto-convert strings across the polyglot boundary" <|
|
||||
(String.format "%s bar %s" "baz" "quux" + " foo").should_equal "baz bar quux foo"
|
||||
Test.specify "should support Java import renaming" <|
|
||||
builder = Java_String_Builder.new
|
||||
builder.append "foo"
|
||||
builder.append "bar"
|
||||
str = builder.toString
|
||||
str.should_equal "foobar"
|
||||
spec =
|
||||
Test.group "Java FFI" <|
|
||||
Test.specify "should call methods imported from Java" <|
|
||||
Long.sum 1 2 . should_equal 3
|
||||
|
||||
Test.specify "should call constructors imported from Java" <|
|
||||
list = ArrayList.new
|
||||
list.add 432
|
||||
list.get 0 . should_equal 432
|
||||
Test.specify "should auto-convert numeric types across the polyglot boundary" <|
|
||||
(Float.valueOf "123.3" + 5).should_equal 128.3 epsilon=0.0001
|
||||
(Integer.sum 1 2 + 3) . should_equal 6
|
||||
Test.specify "should auto-convert strings across the polyglot boundary" <|
|
||||
(String.format "%s bar %s" "baz" "quux" + " foo").should_equal "baz bar quux foo"
|
||||
Test.specify "should support Java import renaming" <|
|
||||
builder = Java_String_Builder.new
|
||||
builder.append "foo"
|
||||
builder.append "bar"
|
||||
str = builder.toString
|
||||
str.should_equal "foobar"
|
||||
|
||||
Test.group "Java/Enso Date" <|
|
||||
Test.specify "Java date has Enso properties" <|
|
||||
april1st = LocalDate.of 2022 04 01
|
||||
april1st.year.should_equal 2022
|
||||
april1st.month.should_equal 4
|
||||
april1st.day.should_equal 1
|
||||
|
||||
Test.specify "send Enso date into Java" <|
|
||||
ensodate = Date.new 2022 04 01
|
||||
javatime = LocalTime.of 10 26
|
||||
javatimedate = javatime.atDate ensodate
|
||||
april1st = javatimedate . toLocalDate
|
||||
april1st.year.should_equal 2022
|
||||
april1st.month.should_equal 4
|
||||
april1st.day.should_equal 1
|
||||
|
||||
|
||||
main = Test.Suite.run_main here.spec
|
||||
|
Loading…
Reference in New Issue
Block a user