mirror of
https://github.com/enso-org/enso.git
synced 2024-12-22 11:51:41 +03:00
Map Implementation (#1222)
This commit is contained in:
parent
989aa4b7d7
commit
207aaaccf5
@ -1,3 +1,3 @@
|
||||
COMP_PATH=$(dirname "$0")/../component
|
||||
exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" -Dpolyglot.engine.IterativePartialEscape=true $JAVA_OPTS $COMP_PATH/runner.jar "$@"
|
||||
exec java -jar -Dtruffle.class.path.append="$COMP_PATH/runtime.jar" $JAVA_OPTS $COMP_PATH/runner.jar "$@"
|
||||
exit
|
||||
|
79
distribution/std-lib/Base/src/Data/Map.enso
Normal file
79
distribution/std-lib/Base/src/Data/Map.enso
Normal file
@ -0,0 +1,79 @@
|
||||
from Base import all
|
||||
import Base.Data.Map.Internal
|
||||
|
||||
## An error for getting a missing value from a map.
|
||||
type No_Value_For_Key key
|
||||
|
||||
## A key-value store. This type assumes all keys are pairwise comparable,
|
||||
using the `<`, `>` and `==` operators.
|
||||
type Map
|
||||
type Tip
|
||||
type Bin s key value left right
|
||||
|
||||
## Checks if the map is empty.
|
||||
is_empty : Boolean
|
||||
is_empty = case this of
|
||||
Bin _ _ _ _ _ -> False
|
||||
Tip -> True
|
||||
|
||||
## Returns the number of entries in this map.
|
||||
size : Integer
|
||||
size = case this of
|
||||
Bin s _ _ _ _ -> s
|
||||
Tip -> 0
|
||||
|
||||
## Converts the map into a vector of `[key, value]` pairs.
|
||||
The returned vector is sorted in the increasing order of keys.
|
||||
to_vector : Vector
|
||||
to_vector =
|
||||
builder = Vector.new_builder
|
||||
to_vector_with_builder m = case m of
|
||||
Bin _ k v l r ->
|
||||
to_vector_with_builder l
|
||||
builder.append [k, v]
|
||||
to_vector_with_builder r
|
||||
Unit
|
||||
Tip -> Unit
|
||||
to_vector_with_builder this
|
||||
result = builder.to_vector
|
||||
result
|
||||
|
||||
## Returns a text representation of this map.
|
||||
to_text : Text
|
||||
to_text = this.to_vector.to_text
|
||||
|
||||
## Checks if this map is equal to another map.
|
||||
|
||||
Maps are equal when they contained the same keys and the values
|
||||
associated with each key are pairwise equal.
|
||||
== : Map -> Boolean
|
||||
== that = this.to_vector == that.to_vector
|
||||
|
||||
## Maps a function over each value in this map.
|
||||
map : (Any -> Any) -> Map
|
||||
map function = case this of
|
||||
Bin s k v l r ->
|
||||
Bin s k (function v) (l.map function) (r.map function)
|
||||
Tip -> Tip
|
||||
|
||||
## Gets the value associated with `key` in this map, or returns a
|
||||
`No_Value_For_Key` error, if `key` is not present.
|
||||
get : Any -> Any ! No_Value_For_Key
|
||||
get key =
|
||||
go map = case map of
|
||||
Tip -> Error.throw (No_Value_For_Key key)
|
||||
Bin _ k v l r ->
|
||||
if k == key then v else
|
||||
if k > key then @Tail_Call go l else @Tail_Call go r
|
||||
result = go this
|
||||
result
|
||||
|
||||
## Inserts a key-value mapping into this map. If `key` is already present,
|
||||
it will be overriden with the new `value`.
|
||||
insert : Any -> Any -> Map
|
||||
insert key value = Internal.insert this key value
|
||||
|
||||
## Returns an empty map.
|
||||
empty : Map
|
||||
empty = Tip
|
||||
|
113
distribution/std-lib/Base/src/Data/Map/Internal.enso
Normal file
113
distribution/std-lib/Base/src/Data/Map/Internal.enso
Normal file
@ -0,0 +1,113 @@
|
||||
from Base import all
|
||||
from Base.Data.Map import all
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Helper used in the insert operation.
|
||||
insert_l key value k v l r =
|
||||
new_left = here.insert l key value
|
||||
here.balance_left k v new_left r
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Helper used in the insert operation.
|
||||
insert_r key value k v l r =
|
||||
new_right = here.insert r key value
|
||||
here.balance_right k v l new_right
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Helper for inserting a new key-value pair into a map.
|
||||
|
||||
The algorithm used here is based on the paper "Implementing Sets Efficiently
|
||||
in a Functional Language" by Stephen Adams.
|
||||
Implementation is based on Haskell's `Data.Map.Strict` implemented in the
|
||||
`containers` package.
|
||||
insert map key value = case map of
|
||||
Bin s k v l r ->
|
||||
if key > k then @Tail_Call here.insert_r key value k v l r else
|
||||
if key == k then @Tail_Call Bin s k value l r else
|
||||
@Tail_Call here.insert_l key value k v l r
|
||||
_ -> Bin 1 key value Tip Tip
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Rebalances the map after the left subtree grows.
|
||||
balance_left k x l r = case r of
|
||||
Bin rs _ _ _ _ -> case l of
|
||||
Bin ls lk lx ll lr ->
|
||||
if ls <= Delta*rs then Bin 1+ls+rs k x l r else
|
||||
lls = here.size ll
|
||||
case lr of
|
||||
Bin lrs lrk lrx lrl lrr ->
|
||||
if lrs < Ratio*lls then Bin 1+ls+rs lk lx ll (Bin 1+rs+lrs k x lr r) else
|
||||
lrls = here.size lrl
|
||||
lrrs = here.size lrr
|
||||
Bin 1+ls+rs lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+rs+lrrs k x lrr r)
|
||||
_ -> Bin 1+rs k x Tip r
|
||||
_ -> case l of
|
||||
Tip -> Bin 1 k x Tip Tip
|
||||
Bin _ _ _ Tip Tip -> Bin 2 k x l Tip
|
||||
Bin _ lk lx Tip (Bin _ lrk lrx _ _) -> Bin 3 lrk lrx (Bin 1 lk lx Tip Tip) (Bin 1 k x Tip Tip)
|
||||
Bin _ lk lx ll Tip -> Bin 3 lk lx ll (Bin 1 k x Tip Tip)
|
||||
Bin ls lk lx ll lr -> case lr of
|
||||
Bin lrs lrk lrx lrl lrr ->
|
||||
lls = here.size ll
|
||||
if lrs < Ratio*lls then Bin 1+ls lk lx ll (Bin 1+lrs k x lr Tip) else
|
||||
lrls = here.size lrl
|
||||
lrrs = here.size lrr
|
||||
Bin 1+ls lrk lrx (Bin 1+lls+lrls lk lx ll lrl) (Bin 1+lrrs k x lrr Tip)
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Rebalances the map after the right subtree grows.
|
||||
balance_right k x l r = case l of
|
||||
Bin ls _ _ _ _ -> case r of
|
||||
Bin rs rk rx rl rr ->
|
||||
if rs <= Delta*ls then Bin 1+ls+rs k x l r else
|
||||
case rl of
|
||||
Bin rls rlk rlx rll rlr ->
|
||||
rrs = here.size rr
|
||||
if rls < Ratio*rrs then Bin 1+ls+rs rk rx (Bin 1+ls+rls k x l rl) rr else
|
||||
rlls = here.size rll
|
||||
rlrs = here.size rlr
|
||||
Bin 1+ls+rs rlk rlx (Bin 1+ls+rlls k x l rll) (Bin 1+rrs+rlrs rk rx rlr rr)
|
||||
_ -> Bin 1+ls k x l Tip
|
||||
_ -> case r of
|
||||
Tip -> Bin 1 k x Tip Tip
|
||||
Bin _ _ _ Tip Tip -> Bin 2 k x Tip r
|
||||
Bin _ rk rx Tip rr -> Bin 3 rk rx (Bin 1 k x Tip Tip) rr
|
||||
Bin _ rk rx (Bin _ rlk rlx _ _) Tip -> Bin 3 rlk rlx (Bin 1 k x Tip Tip) (Bin 1 rk rx Tip Tip)
|
||||
Bin rs rk rx (Bin rls rlk rlx rll rlr) rr -> case rr of
|
||||
Bin rrs _ _ _ _ ->
|
||||
if rls < Ratio*rrs then Bin 1+rs rk rx (Bin 1+rls k x Tip rl) rr else
|
||||
srll = here.size rll
|
||||
srlr = here.size rlr
|
||||
Bin 1+rs rlk rlx (Bin 1+srll k x Tip rll) (Bin 1+rrs+srlr rk rx rlr rr)
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Controls the difference between inner and outer siblings of a heavy subtree.
|
||||
Used to decide between a double and a single rotation.
|
||||
|
||||
The choice of values for `ratio` and `delta` is taken from the Haskell
|
||||
implementation.
|
||||
ratio : Integer
|
||||
ratio = 2
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Controls the maximum size difference between subtrees.
|
||||
|
||||
The choice of values for `ratio` and `delta` is taken from the Haskell
|
||||
implementation.
|
||||
delta : Integer
|
||||
delta = 3
|
||||
|
||||
## PRIVATE
|
||||
|
||||
Gets the size of a map.
|
||||
size m = case m of
|
||||
Bin s _ _ _ _ -> s
|
||||
_ -> 0
|
||||
|
@ -7,11 +7,13 @@ import Base.Meta.Enso_Project
|
||||
import Base.Meta.Meta
|
||||
import Base.Error.Extensions
|
||||
import Base.Polyglot.Java
|
||||
import Base.Data.Map
|
||||
from Builtins import Unit, Number, Integer, Any, True, False, Cons
|
||||
|
||||
export Base.Meta.Meta
|
||||
from Builtins export all hiding Meta
|
||||
|
||||
export Base.Data.Map
|
||||
from Base.Meta.Enso_Project export all
|
||||
from Base.List export Nil, Cons
|
||||
from Base.Vector export Vector
|
||||
|
@ -38,7 +38,7 @@ type Verbs
|
||||
|
||||
equal subject argument =
|
||||
if subject == argument then Success else
|
||||
msg = this.to_text + " did not equal " + that.to_text + "."
|
||||
msg = this.to_text + " did not equal " + argument.to_text + "."
|
||||
here.fail msg
|
||||
|
||||
be subject argument = this.equal subject argument
|
||||
|
@ -69,6 +69,14 @@ Text.split_at separator =
|
||||
Text.== : Text -> Boolean
|
||||
Text.== that = Text_Utils.equals [this, that]
|
||||
|
||||
## Checks if `this` is lexicographically before `that`.
|
||||
Text.< : Text -> Boolean
|
||||
Text.< that = Text_Utils.lt [this, that]
|
||||
|
||||
## Checks if `this` is lexicographically after `that`.
|
||||
Text.> : Text -> Boolean
|
||||
Text.> that = Text_Utils.lt [that, this]
|
||||
|
||||
## Returns a vector containing bytes representing the UTF-8 encoding of the
|
||||
input text.
|
||||
|
||||
@ -103,4 +111,5 @@ Text.from_codepoints : Vector -> Text
|
||||
Text.from_codepoints codepoints = Text_Utils.from_codepoints [codepoints.to_array]
|
||||
|
||||
## Checks whether `this` starts with `prefix`.
|
||||
Text.starts_with : Text -> Boolean
|
||||
Text.starts_with prefix = Text_Utils.starts_with [this, prefix]
|
||||
|
@ -1,17 +1,10 @@
|
||||
package org.enso.interpreter.bench.benchmarks.semantic;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.enso.interpreter.bench.fixtures.semantic.RecursionFixtures;
|
||||
import org.enso.interpreter.test.DefaultInterpreterRunner;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.OutputTimeUnit;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
import org.openjdk.jmh.annotations.*;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@BenchmarkMode(Mode.AverageTime)
|
||||
@Fork(1)
|
||||
|
@ -119,7 +119,7 @@ public abstract class IndirectInvokeCallableNode extends Node {
|
||||
Object selfArgument = arguments[thisArgumentPosition];
|
||||
if (argumentsExecutionMode.shouldExecute()) {
|
||||
Stateful selfResult =
|
||||
thisExecutor.executeThunk((Thunk) selfArgument, state, BaseNode.TailStatus.NOT_TAIL);
|
||||
thisExecutor.executeThunk(selfArgument, state, BaseNode.TailStatus.NOT_TAIL);
|
||||
selfArgument = selfResult.getValue();
|
||||
state = selfResult.getState();
|
||||
arguments[thisArgumentPosition] = selfArgument;
|
||||
|
@ -185,8 +185,7 @@ public abstract class InvokeCallableNode extends BaseNode {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
Stateful selfResult =
|
||||
thisExecutor.executeThunk((Thunk) selfArgument, state, TailStatus.NOT_TAIL);
|
||||
Stateful selfResult = thisExecutor.executeThunk(selfArgument, state, TailStatus.NOT_TAIL);
|
||||
selfArgument = selfResult.getValue();
|
||||
state = selfResult.getState();
|
||||
arguments[thisArgumentPosition] = selfArgument;
|
||||
|
@ -55,11 +55,11 @@ public class ArgumentSorterNode extends BaseNode {
|
||||
preApplicationSchema, mapping.getPostApplicationSchema(), mapping, argumentsExecutionMode);
|
||||
}
|
||||
|
||||
private void initArgumentExecutors(Object[] arguments) {
|
||||
private void initArgumentExecutors() {
|
||||
ThunkExecutorNode[] executors =
|
||||
new ThunkExecutorNode[mapping.getArgumentShouldExecute().length];
|
||||
for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) {
|
||||
if (mapping.getArgumentShouldExecute()[i] && TypesGen.isThunk(arguments[i])) {
|
||||
if (mapping.getArgumentShouldExecute()[i]) {
|
||||
executors[i] = insert(ThunkExecutorNode.build());
|
||||
}
|
||||
}
|
||||
@ -74,7 +74,7 @@ public class ArgumentSorterNode extends BaseNode {
|
||||
lock.lock();
|
||||
try {
|
||||
if (executors == null) {
|
||||
initArgumentExecutors(arguments);
|
||||
initArgumentExecutors();
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
@ -82,8 +82,7 @@ public class ArgumentSorterNode extends BaseNode {
|
||||
}
|
||||
for (int i = 0; i < mapping.getArgumentShouldExecute().length; i++) {
|
||||
if (executors[i] != null) {
|
||||
Stateful result =
|
||||
executors[i].executeThunk(TypesGen.asThunk(arguments[i]), state, TailStatus.NOT_TAIL);
|
||||
Stateful result = executors[i].executeThunk(arguments[i], state, TailStatus.NOT_TAIL);
|
||||
arguments[i] = result.getValue();
|
||||
state = result.getState();
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ public abstract class ForceNode extends ExpressionNode {
|
||||
@Specialization
|
||||
Object passToExecutorNode(
|
||||
VirtualFrame frame,
|
||||
Thunk thunk,
|
||||
Object thunk,
|
||||
@Cached("build()") ThunkExecutorNode thunkExecutorNode) {
|
||||
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
|
||||
Stateful result = thunkExecutorNode.executeThunk(thunk, state, getTailStatus());
|
||||
|
@ -12,6 +12,7 @@ import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/** Node responsible for executing (forcing) thunks passed to it as runtime values. */
|
||||
@GenerateUncached
|
||||
@ -37,7 +38,16 @@ public abstract class ThunkExecutorNode extends Node {
|
||||
* @param isTail is the execution happening in a tail-call position
|
||||
* @return the return value of this thunk
|
||||
*/
|
||||
public abstract Stateful executeThunk(Thunk thunk, Object state, BaseNode.TailStatus isTail);
|
||||
public abstract Stateful executeThunk(Object thunk, Object state, BaseNode.TailStatus isTail);
|
||||
|
||||
static boolean isThunk(Object th) {
|
||||
return TypesGen.isThunk(th);
|
||||
}
|
||||
|
||||
@Specialization(guards = "!isThunk(thunk)")
|
||||
Stateful doOther(Object thunk, Object state, BaseNode.TailStatus isTail) {
|
||||
return new Stateful(state, thunk);
|
||||
}
|
||||
|
||||
@Specialization(
|
||||
guards = "callNode.getCallTarget() == thunk.getCallTarget()",
|
||||
|
@ -1,9 +1,11 @@
|
||||
package org.enso.interpreter.node.controlflow;
|
||||
|
||||
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.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
@ -13,19 +15,19 @@ import org.enso.interpreter.node.callable.function.CreateFunctionNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/** An implementation of the case expression specialised to working on booleans. */
|
||||
@NodeInfo(shortName = "BooleanMatch")
|
||||
public abstract class BooleanBranchNode extends BranchNode {
|
||||
private final boolean matched;
|
||||
private @Child ExpressionNode branch;
|
||||
private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create();
|
||||
private final ConditionProfile profile = ConditionProfile.createCountingProfile();
|
||||
private @Child DirectCallNode callNode;
|
||||
|
||||
BooleanBranchNode(boolean matched, CreateFunctionNode branch) {
|
||||
BooleanBranchNode(boolean matched, RootCallTarget branch) {
|
||||
this.matched = matched;
|
||||
this.branch = branch;
|
||||
this.callNode = DirectCallNode.create(branch);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +37,7 @@ public abstract class BooleanBranchNode extends BranchNode {
|
||||
* @param branch the expression to be executed if (@code matcher} matches
|
||||
* @return a node for matching in a case expression
|
||||
*/
|
||||
public static BooleanBranchNode build(boolean matched, CreateFunctionNode branch) {
|
||||
public static BooleanBranchNode build(boolean matched, RootCallTarget branch) {
|
||||
return BooleanBranchNodeGen.create(matched, branch);
|
||||
}
|
||||
|
||||
@ -43,17 +45,19 @@ public abstract class BooleanBranchNode extends BranchNode {
|
||||
* Handles the boolean scrutinee case.
|
||||
*
|
||||
* @param frame the stack frame in which to execute
|
||||
* @param state current monadic state
|
||||
* @param target the atom to destructure
|
||||
*/
|
||||
@Specialization
|
||||
public void doAtom(VirtualFrame frame, boolean target) {
|
||||
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
|
||||
public void doAtom(VirtualFrame frame, Object state, boolean target) {
|
||||
if (profile.profile(matched == target)) {
|
||||
Function function = TypesGen.asFunction(branch.executeGeneric(frame));
|
||||
|
||||
Stateful result =
|
||||
(Stateful)
|
||||
callNode.call(
|
||||
Function.ArgumentsHelper.buildArguments(
|
||||
frame.materialize(), state, new Object[0]));
|
||||
// Note [Caller Info For Case Branches]
|
||||
throw new BranchSelectedException(
|
||||
executeCallNode.executeCall(function, null, state, new Object[0]));
|
||||
throw new BranchSelectedException(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +68,7 @@ public abstract class BooleanBranchNode extends BranchNode {
|
||||
* @param target the object to execute on
|
||||
*/
|
||||
@Fallback
|
||||
public void doFallback(VirtualFrame frame, Object target) {}
|
||||
public void doFallback(VirtualFrame frame, Object state, Object target) {}
|
||||
|
||||
/* Note [Caller Info For Case Branches]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -15,7 +15,8 @@ public abstract class BranchNode extends BaseNode {
|
||||
* Executes the case branch.
|
||||
*
|
||||
* @param frame the stack frame in which to execute
|
||||
* @param state current monadic state
|
||||
* @param target the object to match against
|
||||
*/
|
||||
public abstract void execute(VirtualFrame frame, Object target);
|
||||
public abstract void execute(VirtualFrame frame, Object state, Object target);
|
||||
}
|
||||
|
@ -3,13 +3,12 @@ package org.enso.interpreter.node.controlflow;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
@ -29,7 +28,6 @@ import org.enso.interpreter.runtime.type.TypesGen;
|
||||
public abstract class CaseNode extends ExpressionNode {
|
||||
|
||||
@Children private final BranchNode[] cases;
|
||||
private final BranchProfile typeErrorProfile = BranchProfile.create();
|
||||
|
||||
CaseNode(BranchNode[] cases) {
|
||||
this.cases = cases;
|
||||
@ -74,9 +72,10 @@ public abstract class CaseNode extends ExpressionNode {
|
||||
VirtualFrame frame,
|
||||
Object object,
|
||||
@CachedContext(Language.class) TruffleLanguage.ContextReference<Context> ctx) {
|
||||
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
|
||||
try {
|
||||
for (BranchNode branchNode : cases) {
|
||||
branchNode.execute(frame, object);
|
||||
branchNode.execute(frame, state, object);
|
||||
}
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw new PanicException(
|
||||
|
@ -1,15 +1,19 @@
|
||||
package org.enso.interpreter.node.controlflow;
|
||||
|
||||
import com.oracle.truffle.api.RootCallTarget;
|
||||
import com.oracle.truffle.api.frame.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNode;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
|
||||
import org.enso.interpreter.node.callable.function.CreateFunctionNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/**
|
||||
@ -20,11 +24,10 @@ import org.enso.interpreter.runtime.type.TypesGen;
|
||||
shortName = "Catch_All",
|
||||
description = "An explicit catch-all branch in a case expression")
|
||||
public class CatchAllBranchNode extends BranchNode {
|
||||
@Child private ExpressionNode functionNode;
|
||||
@Child private ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create();
|
||||
private @Child DirectCallNode callNode;
|
||||
|
||||
private CatchAllBranchNode(CreateFunctionNode functionNode) {
|
||||
this.functionNode = functionNode;
|
||||
private CatchAllBranchNode(RootCallTarget functionNode) {
|
||||
this.callNode = DirectCallNode.create(functionNode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -33,7 +36,7 @@ public class CatchAllBranchNode extends BranchNode {
|
||||
* @param functionNode the function to execute in this case
|
||||
* @return a catch-all node
|
||||
*/
|
||||
public static CatchAllBranchNode build(CreateFunctionNode functionNode) {
|
||||
public static CatchAllBranchNode build(RootCallTarget functionNode) {
|
||||
return new CatchAllBranchNode(functionNode);
|
||||
}
|
||||
|
||||
@ -41,15 +44,17 @@ public class CatchAllBranchNode extends BranchNode {
|
||||
* Executes the case branch on an arbitrary target.
|
||||
*
|
||||
* @param frame the stack frame in which to execute
|
||||
* @param state current monadic state
|
||||
* @param target the object to match against
|
||||
*/
|
||||
public void execute(VirtualFrame frame, Object target) {
|
||||
public void execute(VirtualFrame frame, Object state, Object target) {
|
||||
// Note [Safe Casting to Function in Catch All Branches]
|
||||
Function function = TypesGen.asFunction(functionNode.executeGeneric(frame));
|
||||
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
|
||||
throw new BranchSelectedException(
|
||||
// Note [Caller Info For Case Branches]
|
||||
executeCallNode.executeCall(function, null, state, new Object[] {target}));
|
||||
Stateful result =
|
||||
(Stateful)
|
||||
callNode.call(
|
||||
Function.ArgumentsHelper.buildArguments(
|
||||
frame.materialize(), state, new Object[] {target}));
|
||||
throw new BranchSelectedException(result);
|
||||
}
|
||||
|
||||
/* Note [Safe Casting to Function in Catch All Branches]
|
||||
|
@ -1,11 +1,16 @@
|
||||
package org.enso.interpreter.node.controlflow;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
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.FrameUtil;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.compiler.Compiler;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNode;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
|
||||
@ -13,19 +18,19 @@ import org.enso.interpreter.node.callable.function.CreateFunctionNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/** An implementation of the case expression specialised to working on constructors. */
|
||||
@NodeInfo(shortName = "ConstructorMatch")
|
||||
public abstract class ConstructorBranchNode extends BranchNode {
|
||||
private final AtomConstructor matcher;
|
||||
private @Child ExpressionNode branch;
|
||||
private @Child ExecuteCallNode executeCallNode = ExecuteCallNodeGen.create();
|
||||
private @Child DirectCallNode callNode;
|
||||
private final ConditionProfile profile = ConditionProfile.createCountingProfile();
|
||||
|
||||
ConstructorBranchNode(AtomConstructor matcher, CreateFunctionNode branch) {
|
||||
ConstructorBranchNode(AtomConstructor matcher, RootCallTarget branch) {
|
||||
this.matcher = matcher;
|
||||
this.branch = branch;
|
||||
this.callNode = DirectCallNode.create(branch);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,7 +40,7 @@ public abstract class ConstructorBranchNode extends BranchNode {
|
||||
* @param branch the expression to be executed if (@code matcher} matches
|
||||
* @return a node for matching in a case expression
|
||||
*/
|
||||
public static ConstructorBranchNode build(AtomConstructor matcher, CreateFunctionNode branch) {
|
||||
public static ConstructorBranchNode build(AtomConstructor matcher, RootCallTarget branch) {
|
||||
return ConstructorBranchNodeGen.create(matcher, branch);
|
||||
}
|
||||
|
||||
@ -46,17 +51,19 @@ public abstract class ConstructorBranchNode extends BranchNode {
|
||||
* all the atom's fields as arguments.
|
||||
*
|
||||
* @param frame the stack frame in which to execute
|
||||
* @param state current monadic state
|
||||
* @param target the atom to destructure
|
||||
*/
|
||||
@Specialization
|
||||
public void doAtom(VirtualFrame frame, Atom target) {
|
||||
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
|
||||
public void doAtom(VirtualFrame frame, Object state, Atom target) {
|
||||
if (profile.profile(matcher == target.getConstructor())) {
|
||||
Function function = TypesGen.asFunction(branch.executeGeneric(frame));
|
||||
|
||||
// Note [Caller Info For Case Branches]
|
||||
throw new BranchSelectedException(
|
||||
executeCallNode.executeCall(function, null, state, target.getFields()));
|
||||
Stateful result =
|
||||
(Stateful)
|
||||
callNode.call(
|
||||
Function.ArgumentsHelper.buildArguments(
|
||||
frame.materialize(), state, target.getFields()));
|
||||
throw new BranchSelectedException(result);
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,7 +74,7 @@ public abstract class ConstructorBranchNode extends BranchNode {
|
||||
* @param target the object to execute on
|
||||
*/
|
||||
@Fallback
|
||||
public void doFallback(VirtualFrame frame, Object target) {}
|
||||
public void doFallback(VirtualFrame frame, Object state, Object target) {}
|
||||
|
||||
/* Note [Caller Info For Case Branches]
|
||||
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -1,19 +1,17 @@
|
||||
package org.enso.interpreter.node.expression.builtin;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
/** This node represents the process of instantiating an atom at runtime. */
|
||||
@NodeInfo(shortName = "constructor::", description = "An atom instantiation at runtime.")
|
||||
public class InstantiateAtomNode extends RootNode {
|
||||
private @Node.Child ExpressionNode instantiator;
|
||||
private @Child ExpressionNode instantiator;
|
||||
private final String name;
|
||||
|
||||
private InstantiateAtomNode(Language language, String name, ExpressionNode instantiator) {
|
||||
@ -22,7 +20,8 @@ public class InstantiateAtomNode extends RootNode {
|
||||
this.instantiator = instantiator;
|
||||
}
|
||||
|
||||
/** Executes this node.
|
||||
/**
|
||||
* Executes this node.
|
||||
*
|
||||
* @param frame the language frame being executed
|
||||
* @return the result of executing this node
|
||||
@ -34,7 +33,8 @@ public class InstantiateAtomNode extends RootNode {
|
||||
instantiator.executeGeneric(frame));
|
||||
}
|
||||
|
||||
/** Returns a string representation of this node.
|
||||
/**
|
||||
* Returns a string representation of this node.
|
||||
*
|
||||
* @return a string representation of this node
|
||||
*/
|
||||
@ -43,7 +43,8 @@ public class InstantiateAtomNode extends RootNode {
|
||||
return "constructor::" + name;
|
||||
}
|
||||
|
||||
/** Creates an instance of this node.
|
||||
/**
|
||||
* Creates an instance of this node.
|
||||
*
|
||||
* @param language the language for which the node is created
|
||||
* @param name the name of the atom being instantated
|
||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
@ -18,7 +19,11 @@ public class IfThenElseNode extends Node {
|
||||
private @Child ThunkExecutorNode rightThunkExecutorNode = ThunkExecutorNode.build();
|
||||
private final ConditionProfile condProfile = ConditionProfile.createCountingProfile();
|
||||
|
||||
Stateful execute(@MonadicState Object state, boolean _this, Thunk if_true, Thunk if_false) {
|
||||
Stateful execute(
|
||||
@MonadicState Object state,
|
||||
boolean _this,
|
||||
@Suspend Object if_true,
|
||||
@Suspend Object if_false) {
|
||||
if (condProfile.profile(_this)) {
|
||||
return leftThunkExecutorNode.executeThunk(if_true, state, BaseNode.TailStatus.TAIL_DIRECT);
|
||||
} else {
|
||||
|
@ -7,6 +7,7 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
@ -25,11 +26,11 @@ public abstract class IfThenNode extends Node {
|
||||
return IfThenNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, boolean _this, Thunk if_true);
|
||||
abstract Stateful execute(@MonadicState Object state, boolean _this, @Suspend Object if_true);
|
||||
|
||||
@Specialization
|
||||
Stateful doExecute(
|
||||
Object state, boolean _this, Thunk if_true, @CachedContext(Language.class) Context context) {
|
||||
Object state, boolean _this, Object if_true, @CachedContext(Language.class) Context context) {
|
||||
if (condProfile.profile(_this)) {
|
||||
return leftThunkExecutorNode.executeThunk(if_true, state, BaseNode.TailStatus.TAIL_DIRECT);
|
||||
} else {
|
||||
|
@ -7,6 +7,7 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
@ -26,13 +27,13 @@ public abstract class CatchPanicNode extends Node {
|
||||
return CatchPanicNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Stateful execute(@MonadicState Object state, Object _this, Thunk action);
|
||||
abstract Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action);
|
||||
|
||||
@Specialization
|
||||
Stateful doExecute(
|
||||
@MonadicState Object state,
|
||||
Object _this,
|
||||
Thunk action,
|
||||
Object action,
|
||||
@CachedContext(Language.class) Context ctx) {
|
||||
try {
|
||||
return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL);
|
||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
@ -27,7 +28,8 @@ public class ApplicationOperator extends Node {
|
||||
invokeCallableNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT);
|
||||
}
|
||||
|
||||
Stateful execute(VirtualFrame frame, @MonadicState Object state, Function _this, Thunk argument) {
|
||||
Stateful execute(
|
||||
VirtualFrame frame, @MonadicState Object state, Function _this, @Suspend Object argument) {
|
||||
return invokeCallableNode.execute(_this, frame, state, new Object[] {argument});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
package org.enso.interpreter.node.expression.builtin.interop.generic;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.ReportPolymorphism;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.source.Source;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
|
||||
import org.enso.interpreter.node.expression.builtin.text.util.ToJavaStringNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.data.text.Text;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Polyglot",
|
||||
name = "eval",
|
||||
description = "Evaluates a foreign language string.")
|
||||
@ReportPolymorphism
|
||||
public abstract class EvalNode extends Node {
|
||||
static final int LIMIT = 10;
|
||||
|
||||
static EvalNode build() {
|
||||
return EvalNodeGen.create();
|
||||
}
|
||||
|
||||
abstract Object execute(Object _this, Text language, Text code);
|
||||
|
||||
@Specialization(
|
||||
guards = {"cachedLanguage == language", "cachedCode == code"},
|
||||
limit = "LIMIT")
|
||||
Object doCached(
|
||||
Object _this,
|
||||
Text language,
|
||||
Text code,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached("language") Text cachedLanguage,
|
||||
@Cached("code") Text cachedCode,
|
||||
@Cached("build()") ToJavaStringNode toJavaStringNode,
|
||||
@Cached("parse(context, cachedLanguage, cachedCode, toJavaStringNode)") CallTarget callTarget,
|
||||
@Cached("create(callTarget)") DirectCallNode callNode,
|
||||
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode) {
|
||||
return hostValueToEnsoNode.execute(callNode.call());
|
||||
}
|
||||
|
||||
@Specialization(replaces = "doCached")
|
||||
Object doUncached(
|
||||
Object _this,
|
||||
Text language,
|
||||
Text code,
|
||||
@CachedContext(Language.class) Context context,
|
||||
@Cached IndirectCallNode callNode,
|
||||
@Cached("build()") ToJavaStringNode toJavaStringNode,
|
||||
@Cached("build()") HostValueToEnsoNode hostValueToEnsoNode) {
|
||||
CallTarget ct = parse(context, language, code, toJavaStringNode);
|
||||
return hostValueToEnsoNode.execute(callNode.call(ct));
|
||||
}
|
||||
|
||||
CallTarget parse(Context context, Text language, Text code, ToJavaStringNode toJavaStringNode) {
|
||||
String languageStr = toJavaStringNode.execute(language);
|
||||
String codeStr = toJavaStringNode.execute(code);
|
||||
|
||||
Source source = Source.newBuilder(languageStr, codeStr, "<polyglot_eval>").build();
|
||||
return context.getEnvironment().parsePublic(source);
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ import com.oracle.truffle.api.nodes.Node;
|
||||
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||
import org.enso.interpreter.Constants;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.syntax.HostValueToEnsoNode;
|
||||
import org.enso.interpreter.runtime.data.Array;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
|
||||
@ -18,11 +19,12 @@ import org.enso.interpreter.runtime.error.PanicException;
|
||||
public class ExecuteNode extends Node {
|
||||
private @Child InteropLibrary library =
|
||||
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
|
||||
private @Child HostValueToEnsoNode hostValueToEnsoNode = HostValueToEnsoNode.build();
|
||||
private final BranchProfile err = BranchProfile.create();
|
||||
|
||||
Object execute(Object _this, Object callable, Array arguments) {
|
||||
try {
|
||||
return library.execute(callable, arguments.getItems());
|
||||
return hostValueToEnsoNode.execute(library.execute(callable, arguments.getItems()));
|
||||
} catch (UnsupportedMessageException | ArityException | UnsupportedTypeException e) {
|
||||
err.enter();
|
||||
throw new PanicException(e.getMessage(), this);
|
||||
|
@ -5,6 +5,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
@ -50,7 +51,7 @@ public abstract class BracketNode extends Node {
|
||||
@MonadicState Object state,
|
||||
VirtualFrame frame,
|
||||
Object _this,
|
||||
Thunk constructor,
|
||||
@Suspend Object constructor,
|
||||
Object destructor,
|
||||
Object action);
|
||||
|
||||
@ -59,7 +60,7 @@ public abstract class BracketNode extends Node {
|
||||
Object state,
|
||||
VirtualFrame frame,
|
||||
Object _this,
|
||||
Thunk constructor,
|
||||
Object constructor,
|
||||
Object destructor,
|
||||
Object action) {
|
||||
Stateful resourceStateful =
|
||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
@ -17,7 +18,7 @@ public class NoInlineNode extends Node {
|
||||
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
|
||||
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
Stateful execute(@MonadicState Object state, Object _this, Thunk action) {
|
||||
Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action) {
|
||||
return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
@ -29,11 +30,15 @@ public abstract class RunStateNode extends Node {
|
||||
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
|
||||
|
||||
abstract Stateful execute(
|
||||
@MonadicState Object state, Object _this, Object key, Object local_state, Thunk computation);
|
||||
@MonadicState Object state,
|
||||
Object _this,
|
||||
Object key,
|
||||
Object local_state,
|
||||
@Suspend Object computation);
|
||||
|
||||
@Specialization
|
||||
Stateful doEmpty(
|
||||
EmptyMap state, Object _this, Object key, Object local_state, Thunk computation) {
|
||||
EmptyMap state, Object _this, Object key, Object local_state, Object computation) {
|
||||
SingletonMap localStateMap = new SingletonMap(key, local_state);
|
||||
Object result =
|
||||
thunkExecutorNode
|
||||
@ -44,7 +49,7 @@ public abstract class RunStateNode extends Node {
|
||||
|
||||
@Specialization(guards = {"state.getKey() == key"})
|
||||
Stateful doSingletonSameKey(
|
||||
SingletonMap state, Object _this, Object key, Object local_state, Thunk computation) {
|
||||
SingletonMap state, Object _this, Object key, Object local_state, Object computation) {
|
||||
SingletonMap localStateContainer = new SingletonMap(state.getKey(), local_state);
|
||||
Stateful res =
|
||||
thunkExecutorNode.executeThunk(
|
||||
@ -63,7 +68,7 @@ public abstract class RunStateNode extends Node {
|
||||
Object _this,
|
||||
Object key,
|
||||
Object local_state,
|
||||
Thunk computation,
|
||||
Object computation,
|
||||
@Cached("key") Object cachedNewKey,
|
||||
@Cached("state.getKey()") Object cachedOldKey,
|
||||
@Cached(value = "buildSmallKeys(cachedNewKey, cachedOldKey)", dimensions = 1)
|
||||
@ -77,7 +82,7 @@ public abstract class RunStateNode extends Node {
|
||||
|
||||
@Specialization
|
||||
Stateful doSingletonNewKeyUncached(
|
||||
SingletonMap state, Object _this, Object key, Object local_state, Thunk computation) {
|
||||
SingletonMap state, Object _this, Object key, Object local_state, Object computation) {
|
||||
return doSingletonNewKeyCached(
|
||||
state,
|
||||
_this,
|
||||
@ -100,7 +105,7 @@ public abstract class RunStateNode extends Node {
|
||||
Object _this,
|
||||
Object key,
|
||||
Object local_state,
|
||||
Thunk computation,
|
||||
Object computation,
|
||||
@Cached("key") Object cachedNewKey,
|
||||
@Cached(value = "state.getKeys()", dimensions = 1) Object[] cachedOldKeys,
|
||||
@Cached("state.indexOf(key)") int index,
|
||||
@ -125,7 +130,7 @@ public abstract class RunStateNode extends Node {
|
||||
Object _this,
|
||||
Object key,
|
||||
Object local_state,
|
||||
Thunk computation,
|
||||
Object computation,
|
||||
@Cached("key") Object cachedNewKey,
|
||||
@Cached(value = "state.getKeys()", dimensions = 1) Object[] cachedOldKeys,
|
||||
@Cached("state.indexOf(key)") int index) {
|
||||
@ -144,7 +149,7 @@ public abstract class RunStateNode extends Node {
|
||||
|
||||
@Specialization
|
||||
Stateful doMultiUncached(
|
||||
SmallMap state, Object _this, Object key, Object local_state, Thunk computation) {
|
||||
SmallMap state, Object _this, Object key, Object local_state, Object computation) {
|
||||
int idx = state.indexOf(key);
|
||||
if (idx == SmallMap.NOT_FOUND) {
|
||||
return doMultiNewKeyCached(
|
||||
|
@ -3,6 +3,7 @@ package org.enso.interpreter.node.expression.builtin.thread;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
@ -18,7 +19,7 @@ public class WithInterruptHandlerNode extends Node {
|
||||
private @Child ThunkExecutorNode handlerExecutorNode = ThunkExecutorNode.build();
|
||||
|
||||
Stateful execute(
|
||||
@MonadicState Object state, Object _this, Thunk action, Thunk interrupt_handler) {
|
||||
@MonadicState Object state, Object _this, @Suspend Object action, @Suspend Object interrupt_handler) {
|
||||
try {
|
||||
return actExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL);
|
||||
} catch (ThreadInterruptedException e) {
|
||||
|
@ -65,6 +65,7 @@ public class Polyglot {
|
||||
scope.registerMethod(polyglot, "execute", ExecuteMethodGen.makeFunction(language));
|
||||
scope.registerMethod(polyglot, "invoke", InvokeMethodGen.makeFunction(language));
|
||||
scope.registerMethod(polyglot, "new", InstantiateMethodGen.makeFunction(language));
|
||||
scope.registerMethod(polyglot, "eval", EvalMethodGen.makeFunction(language));
|
||||
scope.registerMethod(polyglot, "get_member", GetMemberMethodGen.makeFunction(language));
|
||||
scope.registerMethod(polyglot, "get_members", GetMembersMethodGen.makeFunction(language));
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.interop.ArityException;
|
||||
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||
import com.oracle.truffle.api.interop.TruffleObject;
|
||||
@ -83,8 +84,7 @@ public final class Function implements TruffleObject {
|
||||
* @param args argument definitons
|
||||
* @return a Function object with specified behavior and arguments
|
||||
*/
|
||||
public static Function fromBuiltinRootNode(
|
||||
BuiltinRootNode node, ArgumentDefinition... args) {
|
||||
public static Function fromBuiltinRootNode(BuiltinRootNode node, ArgumentDefinition... args) {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
FunctionSchema schema = new FunctionSchema(args);
|
||||
return new Function(callTarget, null, schema);
|
||||
@ -103,8 +103,7 @@ public final class Function implements TruffleObject {
|
||||
public static Function fromBuiltinRootNodeWithCallerFrameAccess(
|
||||
BuiltinRootNode node, ArgumentDefinition... args) {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
FunctionSchema schema =
|
||||
new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, args);
|
||||
FunctionSchema schema = new FunctionSchema(FunctionSchema.CallerFrameAccess.FULL, args);
|
||||
return new Function(callTarget, null, schema);
|
||||
}
|
||||
|
||||
@ -288,6 +287,19 @@ public final class Function implements TruffleObject {
|
||||
return new Object[] {function.getScope(), callerInfo, state, positionalArguments};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of arguments using the schema to be passed to a call target.
|
||||
*
|
||||
* @param frame the frame becoming the lexical scope
|
||||
* @param state the state to execute the thunk with
|
||||
* @param positionalArguments the positional arguments to the call target
|
||||
* @return an array containing the necessary information to call an Enso function
|
||||
*/
|
||||
public static Object[] buildArguments(
|
||||
MaterializedFrame frame, Object state, Object[] positionalArguments) {
|
||||
return new Object[] {frame, null, state, positionalArguments};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array of arguments using the schema to be passed to a call target.
|
||||
*
|
||||
|
@ -596,7 +596,10 @@ class IrToTruffle(
|
||||
.toArray[BranchNode]
|
||||
|
||||
// Note [Pattern Match Fallbacks]
|
||||
val matchExpr = CaseNode.build(scrutineeNode, cases)
|
||||
val matchExpr = CaseNode.build(
|
||||
scrutineeNode,
|
||||
cases
|
||||
)
|
||||
setLocation(matchExpr, location)
|
||||
} else {
|
||||
val invalidBranches = maybeCases.collect {
|
||||
@ -644,7 +647,8 @@ class IrToTruffle(
|
||||
branch.location
|
||||
)
|
||||
|
||||
val branchNode = CatchAllBranchNode.build(branchCodeNode)
|
||||
val branchNode =
|
||||
CatchAllBranchNode.build(branchCodeNode.getCallTarget)
|
||||
|
||||
Right(branchNode)
|
||||
case cons @ Pattern.Constructor(constructor, _, _, _, _) =>
|
||||
@ -704,11 +708,14 @@ class IrToTruffle(
|
||||
val bool = context.getBuiltins.bool()
|
||||
val branchNode: BranchNode =
|
||||
if (atomCons == bool.getTrue) {
|
||||
BooleanBranchNode.build(true, branchCodeNode)
|
||||
BooleanBranchNode.build(true, branchCodeNode.getCallTarget)
|
||||
} else if (atomCons == bool.getFalse) {
|
||||
BooleanBranchNode.build(false, branchCodeNode)
|
||||
BooleanBranchNode.build(false, branchCodeNode.getCallTarget)
|
||||
} else {
|
||||
ConstructorBranchNode.build(atomCons, branchCodeNode)
|
||||
ConstructorBranchNode.build(
|
||||
atomCons,
|
||||
branchCodeNode.getCallTarget
|
||||
)
|
||||
}
|
||||
|
||||
branchNode
|
||||
@ -1170,11 +1177,17 @@ class IrToTruffle(
|
||||
)
|
||||
.unsafeAs[AliasAnalysis.Info.Scope.Child]
|
||||
|
||||
val shouldSuspend = shouldBeSuspended.getOrElse(
|
||||
throw new CompilerError(
|
||||
"Demand analysis information missing from call argument."
|
||||
)
|
||||
)
|
||||
val shouldSuspend = value match {
|
||||
case _: IR.Name => false
|
||||
case _: IR.Literal.Text => false
|
||||
case _: IR.Literal.Number => false
|
||||
case _ =>
|
||||
shouldBeSuspended.getOrElse(
|
||||
throw new CompilerError(
|
||||
"Demand analysis information missing from call argument."
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val childScope = if (shouldSuspend) {
|
||||
scope.createChild(scopeInfo.scope)
|
||||
|
@ -279,7 +279,6 @@ class CodeLocationsTest extends InterpreterTest {
|
||||
instrumenter.assertNodeExists(55, 67, classOf[CaseNode])
|
||||
instrumenter.assertNodeExists(60, 1, classOf[ReadLocalVariableNode])
|
||||
instrumenter.assertNodeExists(103, 3, classOf[IntegerLiteralNode])
|
||||
instrumenter.assertNodeExists(73, 33, classOf[CreateFunctionNode])
|
||||
|
||||
eval(code) shouldEqual 100
|
||||
}
|
||||
|
@ -0,0 +1,11 @@
|
||||
package org.enso.interpreter.dsl;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/** An interface marking an argument as suspended. */
|
||||
@Target(ElementType.PARAMETER)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Suspend {}
|
@ -2,6 +2,7 @@ package org.enso.interpreter.dsl.model;
|
||||
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.dsl.Suspend;
|
||||
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.*;
|
||||
@ -119,7 +120,10 @@ public class MethodDefinition {
|
||||
"The execute method does not take `this` argument. At least one positional argument must be named `_this`.",
|
||||
element);
|
||||
}
|
||||
return definesThis;
|
||||
|
||||
boolean argsValid = arguments.stream().allMatch(arg -> arg.validate(processingEnvironment));
|
||||
|
||||
return definesThis && argsValid;
|
||||
}
|
||||
|
||||
/** @return the package name this method was declared in. */
|
||||
@ -188,7 +192,9 @@ public class MethodDefinition {
|
||||
private final boolean isState;
|
||||
private final boolean isFrame;
|
||||
private final boolean isCallerInfo;
|
||||
private final boolean isSuspended;
|
||||
private final int position;
|
||||
private final VariableElement element;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this class.
|
||||
@ -197,17 +203,32 @@ public class MethodDefinition {
|
||||
* @param position the position (0-indexed) of this argument in the arguments list.
|
||||
*/
|
||||
public ArgumentDefinition(VariableElement element, int position) {
|
||||
this.element = element;
|
||||
type = element.asType();
|
||||
String[] typeNameSegments = type.toString().split("\\.");
|
||||
typeName = typeNameSegments[typeNameSegments.length - 1];
|
||||
String originalName = element.getSimpleName().toString();
|
||||
name = originalName.equals("_this") ? "this" : originalName;
|
||||
isState = element.getAnnotation(MonadicState.class) != null && type.toString().equals(OBJECT);
|
||||
isSuspended = element.getAnnotation(Suspend.class) != null;
|
||||
isFrame = type.toString().equals(VIRTUAL_FRAME);
|
||||
isCallerInfo = type.toString().equals(CALLER_INFO);
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public boolean validate(ProcessingEnvironment processingEnvironment) {
|
||||
if (type.toString().equals(THUNK)) {
|
||||
processingEnvironment
|
||||
.getMessager()
|
||||
.printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"Argument must not be typed as Thunk. Use @Suspend Object instead.",
|
||||
element);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @return whether this argument should be passed the monadic state. */
|
||||
public boolean isState() {
|
||||
return isState;
|
||||
@ -265,7 +286,7 @@ public class MethodDefinition {
|
||||
|
||||
/** @return whether this argument is expected to be passed suspended. */
|
||||
public boolean isSuspended() {
|
||||
return type.toString().equals(THUNK);
|
||||
return isSuspended;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,4 +95,15 @@ public class Text_Utils {
|
||||
public static boolean starts_with(String str, String prefix) {
|
||||
return str.startsWith(prefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether {@code a} is lexicographically before {@code b}.
|
||||
*
|
||||
* @param a the left operand
|
||||
* @param b the right operand
|
||||
* @return whether {@code a} is before {@code b}.
|
||||
*/
|
||||
public static boolean lt(String a, String b) {
|
||||
return a.compareTo(b) < 0;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ from Base import all
|
||||
import Builtins
|
||||
import Base.Bench_Utils
|
||||
|
||||
polyglot java import java.util.Random
|
||||
|
||||
gen_list len = 0.upto len . fold Nil (l -> i -> Cons i+1 l)
|
||||
|
||||
sum_list_meta list =
|
||||
@ -14,6 +16,12 @@ sum_list_meta list =
|
||||
res = folder 0 list
|
||||
res
|
||||
|
||||
sum_recur n = if n == 0 then 0 else 1 + here.sum_recur n-1
|
||||
|
||||
build_map size =
|
||||
rand = Random.new [].to_array
|
||||
0.upto size . fold Map.empty (m -> i -> m.insert (rand.nextInt [10000]) i)
|
||||
|
||||
main =
|
||||
mil = 1000000
|
||||
list = here.gen_list mil
|
||||
@ -23,3 +31,4 @@ main =
|
||||
Bench_Utils.measure (list.fold 0 (+)) "list fold" 1000 10
|
||||
Bench_Utils.measure (vec.fold 0 (+)) "vector fold" 1000 10
|
||||
Bench_Utils.measure (vec_decimal.fold 0 (+)) "vector decimal fold" 1000 10
|
||||
Bench_Utils.measure (here.build_map 10000) "build a map" 100 10
|
||||
|
@ -41,7 +41,8 @@ sum_co_state_body =
|
||||
acc = State.get Sum
|
||||
State.put Counter n-1
|
||||
State.put Sum acc+n
|
||||
if n == 0 then acc else @Tail_Call here.sum_co_state_body
|
||||
if n == 0 then acc else
|
||||
@Tail_Call here.sum_co_state_body
|
||||
|
||||
sum_co_state n =
|
||||
res = State.run Counter n (State.run Sum 0 here.sum_co_state_body)
|
||||
@ -67,7 +68,6 @@ sum_co n =
|
||||
|
||||
main =
|
||||
hundred_mil = 100000000
|
||||
IO.println (here.sum_co 1000)
|
||||
IO.println "Measuring Sum TCO Corecursive"
|
||||
Bench_Utils.measure (here.sum_co hundred_mil) "sum_tco_corecursive" 100 10
|
||||
IO.println "Measuring Sum TCO Decimal"
|
||||
|
14
test/Test/src/Data/Map_Spec.enso
Normal file
14
test/Test/src/Data/Map_Spec.enso
Normal file
@ -0,0 +1,14 @@
|
||||
from Base import all
|
||||
|
||||
import Base.Test
|
||||
|
||||
spec = describe "Maps" <|
|
||||
it "should allow inserting and looking up values" <|
|
||||
m = Map.empty . insert "foo" 134 . insert "bar" 654 . insert "baz" "spam"
|
||||
m.get "foo" . should equal 134
|
||||
m.get "bar" . should equal 654
|
||||
m.get "baz" . should equal "spam"
|
||||
m.get "nope" . catch e->e . should_equal (Map.No_Value_For_Key "nope")
|
||||
it "should convert the whole map to a vector" <|
|
||||
m = Map.empty . insert 0 0 . insert 3 -5 . insert 1 2
|
||||
m.to_vector.should equal [[0, 0], [1, 2], [3, -5]]
|
@ -8,6 +8,7 @@ import Test.Semantic.Names_Spec
|
||||
import Test.Semantic.Meta_Spec
|
||||
|
||||
import Test.List_Spec
|
||||
import Test.Data.Map_Spec
|
||||
import Test.Number_Spec
|
||||
import Test.Process_Spec
|
||||
import Test.Vector.Spec as Vector_Spec
|
||||
@ -31,3 +32,4 @@ main = Test.Suite.runMain <|
|
||||
Time_Spec.spec
|
||||
File_Spec.spec
|
||||
Meta_Spec.spec
|
||||
Map_Spec.spec
|
||||
|
@ -16,6 +16,7 @@ library:
|
||||
- base
|
||||
- deepseq
|
||||
- containers
|
||||
- random
|
||||
|
||||
benchmarks:
|
||||
haskell-benchmark:
|
||||
|
@ -4,8 +4,10 @@ import Prelude
|
||||
|
||||
import qualified Data.Map.Strict as Map
|
||||
|
||||
import Control.Monad (foldM)
|
||||
import Data.Int (Int64)
|
||||
import Data.List (foldl')
|
||||
import System.Random (randomRIO)
|
||||
|
||||
|
||||
------------------
|
||||
@ -69,4 +71,7 @@ myFoldl f z (Cons x xs) = let z' = z `f` x
|
||||
in seq z' $ myFoldl f z' xs
|
||||
|
||||
buildMap :: Integer -> Map.Map Integer Integer
|
||||
buildMap i = foldl' (\m i -> Map.insert i i m) Map.empty [0..i]
|
||||
buildMap n = foldl' (\m i -> Map.insert i i m) Map.empty [0..n]
|
||||
|
||||
buildRandomMap :: Integer -> IO (Map.Map Integer Integer)
|
||||
buildRandomMap n = foldM (\m i -> fmap (\key -> Map.insert key i m) $ randomRIO (0, 10000)) Map.empty [0..n]
|
||||
|
@ -10,6 +10,7 @@ main :: IO ()
|
||||
main = defaultMain
|
||||
[
|
||||
bench "buildMap" $ whnf Fixtures.buildMap Fixtures.tenThousand,
|
||||
bench "buildMapRandom" $ whnfIO $ Fixtures.buildRandomMap Fixtures.tenThousand,
|
||||
bench "sumTCO" $ whnf Fixtures.sumTCO Fixtures.hundredMillion,
|
||||
bench "sumList" $ whnf Fixtures.sumList Fixtures.millionElementList,
|
||||
bench "reverseList" $ whnf Fixtures.reverseList Fixtures.millionElementList,
|
||||
|
Loading…
Reference in New Issue
Block a user