Map Implementation (#1222)

This commit is contained in:
Marcin Kostrzewa 2020-10-20 13:43:04 +02:00 committed by GitHub
parent 989aa4b7d7
commit 207aaaccf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 517 additions and 107 deletions

View File

@ -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

View 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

View 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

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -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());

View File

@ -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()",

View File

@ -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]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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);
}

View File

@ -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(

View File

@ -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]

View File

@ -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]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -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);

View File

@ -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});
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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 =

View File

@ -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);
}
}

View File

@ -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(

View File

@ -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) {

View File

@ -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));
}

View File

@ -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.
*

View File

@ -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)

View File

@ -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
}

View File

@ -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 {}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -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"

View 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]]

View File

@ -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

View File

@ -16,6 +16,7 @@ library:
- base
- deepseq
- containers
- random
benchmarks:
haskell-benchmark:

View File

@ -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]

View File

@ -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,