Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enso Date shall be converted to java.time.LocalDate when passed to Java #3374

Merged
merged 6 commits into from
Apr 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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.

Expand Down
45 changes: 17 additions & 28 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Then one can execute the launcher:

```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 is shown by the `--help` flag, but
the primary functionality is as follows:
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 spcified by `PATH`.
- `--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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -15,7 +14,6 @@
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.*;
Expand All @@ -34,7 +32,6 @@
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;
Expand Down Expand Up @@ -162,8 +159,7 @@ Stateful doWarning(
guards = {
"!methods.hasFunctionalDispatch(_this)",
"!methods.hasSpecialDispatch(_this)",
"polyglotCallType != NOT_SUPPORTED",
"polyglotCallType != CONVERT_TO_TEXT"
"polyglotCallType.isInteropLibrary()",
})
Stateful doPolyglot(
VirtualFrame frame,
Expand Down Expand Up @@ -235,6 +231,34 @@ Stateful doConvertText(
}
}

@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)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,25 @@ public enum PolyglotCallType {
* 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";
Expand Down Expand Up @@ -76,9 +93,12 @@ public static PolyglotCallType getPolyglotCallType(
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;
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -463,4 +466,24 @@ public TruffleLogger getLogger(Class<?> klass) {
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;
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -203,6 +205,21 @@ boolean hasFunctionalDispatch() {
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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.*;
Expand Down
Original file line number Diff line number Diff line change
@@ -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")
}
}
}
Loading