mirror of
https://github.com/enso-org/enso.git
synced 2025-01-09 01:26:59 +03:00
Implement call strategy controls and a functional if statement. (#328)
This commit is contained in:
parent
43f51e9dac
commit
3929b3f72c
@ -8,7 +8,7 @@ class AtomFixtures extends InterpreterRunner {
|
||||
val generateListCode =
|
||||
"""
|
||||
|{ |length|
|
||||
| generator = { |acc, i| ifZero: [i, acc, @generator [@Cons [i, acc], i - 1]] };
|
||||
| generator = { |acc, i| @ifZero [i, acc, @generator [@Cons [i, acc], i - 1]] };
|
||||
| res = @generator [@Nil, length];
|
||||
| res
|
||||
|}
|
||||
|
@ -9,7 +9,7 @@ class NamedDefaultedArgumentFixtures extends InterpreterRunner {
|
||||
"""
|
||||
|{ |sumTo|
|
||||
| summator = { |acc, current|
|
||||
| ifZero: [current, acc, @summator [current = current - 1, acc = acc + current]]
|
||||
| @ifZero [current, acc, @summator [current = current - 1, acc = acc + current]]
|
||||
| };
|
||||
| res = @summator [current = sumTo, acc = 0];
|
||||
| res
|
||||
@ -22,7 +22,7 @@ class NamedDefaultedArgumentFixtures extends InterpreterRunner {
|
||||
"""
|
||||
|{ |sumTo|
|
||||
| summator = { |acc = 0, current|
|
||||
| ifZero: [current, acc, @summator [current = current - 1, acc = acc + current]]
|
||||
| @ifZero [current, acc, @summator [current = current - 1, acc = acc + current]]
|
||||
| };
|
||||
| res = @summator [current = sumTo];
|
||||
| res
|
||||
|
@ -13,7 +13,7 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
val mutRecursiveCode =
|
||||
"""
|
||||
|summator = { |acc, current|
|
||||
| ifZero: [current, acc, @summator [acc + current, current - 1]]
|
||||
| @ifZero [current, acc, @summator [acc + current, current - 1]]
|
||||
|}
|
||||
|
|
||||
|{ |sumTo|
|
||||
@ -26,7 +26,7 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
"""
|
||||
|{ |sumTo|
|
||||
| summator = { |acc, current|
|
||||
| ifZero: [current, acc, @summator [acc + current, current - 1]]
|
||||
| @ifZero [current, acc, @summator [acc + current, current - 1]]
|
||||
| };
|
||||
| res = @summator [0, sumTo];
|
||||
| res
|
||||
@ -38,7 +38,7 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
val sumTCOFoldLikeCode =
|
||||
"""
|
||||
|{ |sumTo|
|
||||
| summator = { |acc, i, f| ifZero: [i, acc, @summator [@f [acc, i], i - 1, f]] };
|
||||
| summator = { |acc, i, f| @ifZero [i, acc, @summator [@f [acc, i], i - 1, f]] };
|
||||
| res = @summator [0, sumTo, {|x, y| x + y }];
|
||||
| res
|
||||
|}
|
||||
@ -49,7 +49,7 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
val sumRecursiveCode =
|
||||
"""
|
||||
|{ |sumTo|
|
||||
| summator = { |i| ifZero: [i, 0, i + (@summator [i - 1])] };
|
||||
| summator = { |i| @ifZero [i, 0, i + (@summator [i - 1])] };
|
||||
| res = @summator [sumTo];
|
||||
| res
|
||||
|}
|
||||
@ -60,7 +60,7 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
val oversaturatedRecursiveCallTCOCode =
|
||||
"""
|
||||
|{ |sumTo|
|
||||
| summator = { |acc, i, f| ifZero: [i, acc, @summator [@f [acc, i], i - 1, f]] };
|
||||
| summator = { |acc, i, f| @ifZero [i, acc, @summator [@f [acc, i], i - 1, f]] };
|
||||
| res = @summator [0, sumTo, {|x| { |y| x + y }}];
|
||||
| res
|
||||
|}
|
||||
@ -75,7 +75,7 @@ class RecursionFixtures extends InterpreterRunner {
|
||||
| stateSum = { |n|
|
||||
| acc = @get [@State];
|
||||
| @put [@State, acc + n];
|
||||
| ifZero: [n, 0, @stateSum [n-1]]
|
||||
| @ifZero [n, @get [@State], @stateSum [n-1]]
|
||||
| };
|
||||
| @put [@State, 0];
|
||||
| @stateSum [sumTo]
|
||||
|
@ -330,19 +330,6 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
|
||||
function.visit(this), callArgs.toArray(new CallArgument[0]), defaultsExecutionMode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a runtime node representing a conditional expression.
|
||||
*
|
||||
* @param cond the condition
|
||||
* @param ifTrue the code to execute if {@code cond} is true
|
||||
* @param ifFalse the code to execute if {@code cond} is false
|
||||
* @return a runtime node representing the conditional
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode visitIf(AstExpression cond, AstExpression ifTrue, AstExpression ifFalse) {
|
||||
return new IfZeroNode(cond.visit(this), ifTrue.visit(this), ifFalse.visit(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a runtime node representing an assignment expression.
|
||||
*
|
||||
|
@ -6,7 +6,7 @@ import org.enso.interpreter.node.callable.function.CreateFunctionNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.error.VariableDoesNotExistException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
@ -97,7 +97,10 @@ public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor<Expre
|
||||
method.fun().getArguments(), method.fun().getStatements(), method.fun().ret());
|
||||
funNode.markTail();
|
||||
Function function =
|
||||
new Function(funNode.getCallTarget(), null, new ArgumentSchema(funNode.getArgs()));
|
||||
new Function(
|
||||
funNode.getCallTarget(),
|
||||
null,
|
||||
new FunctionSchema(FunctionSchema.CallStrategy.CALL_LOOP, funNode.getArgs()));
|
||||
|
||||
if (method.typeName().equals(Constants.ANY_TYPE_NAME)) {
|
||||
moduleScope.registerMethodForAny(method.methodName(), function);
|
||||
|
@ -71,4 +71,8 @@ public abstract class ExecuteCallNode extends Node {
|
||||
* @return the result of executing {@code function} on {@code arguments}
|
||||
*/
|
||||
public abstract Stateful executeCall(Object function, Object state, Object[] arguments);
|
||||
|
||||
public static ExecuteCallNode build() {
|
||||
return ExecuteCallNodeGen.create();
|
||||
}
|
||||
}
|
||||
|
@ -66,9 +66,8 @@ public abstract class ArgumentSorterNode extends BaseNode {
|
||||
Object[] arguments,
|
||||
@Cached(
|
||||
"build(function, getSchema(), getDefaultsExecutionMode(), getArgumentsExecutionMode(), isTail())")
|
||||
CachedArgumentSorterNode mappingNode,
|
||||
@Cached CallOptimiserNode optimiser) {
|
||||
return mappingNode.execute(function, state, arguments, optimiser);
|
||||
CachedArgumentSorterNode mappingNode) {
|
||||
return mappingNode.execute(function, state, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -91,8 +90,7 @@ public abstract class ArgumentSorterNode extends BaseNode {
|
||||
getSchema(),
|
||||
getDefaultsExecutionMode(),
|
||||
getArgumentsExecutionMode(),
|
||||
isTail()),
|
||||
CallOptimiserNode.create());
|
||||
isTail()));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,6 +4,7 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import org.enso.interpreter.node.BaseNode;
|
||||
import org.enso.interpreter.node.callable.ExecuteCallNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNodeGen;
|
||||
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
|
||||
@ -12,7 +13,7 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMapping;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo.ArgumentMappingBuilder;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.control.TailCallException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
@ -26,12 +27,14 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
|
||||
private final Function originalFunction;
|
||||
private final ArgumentMapping mapping;
|
||||
private final ArgumentSchema postApplicationSchema;
|
||||
private final FunctionSchema postApplicationSchema;
|
||||
private @CompilerDirectives.CompilationFinal(dimensions = 1) boolean[] argumentShouldExecute;
|
||||
@Children private ThunkExecutorNode[] executors;
|
||||
private @Children ThunkExecutorNode[] executors;
|
||||
private final boolean appliesFully;
|
||||
@Child private InvokeCallableNode oversaturatedCallableNode = null;
|
||||
private @Child InvokeCallableNode oversaturatedCallableNode;
|
||||
private final InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode;
|
||||
private @Child ExecuteCallNode directCall;
|
||||
private @Child CallOptimiserNode loopingCall;
|
||||
|
||||
/**
|
||||
* Creates a node that generates and then caches the argument mapping.
|
||||
@ -56,6 +59,39 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
this.mapping = mapping.getAppliedMapping();
|
||||
this.postApplicationSchema = mapping.getPostApplicationSchema();
|
||||
|
||||
appliesFully = isFunctionFullyApplied(defaultsExecutionMode);
|
||||
|
||||
initializeOversaturatedCallNode(defaultsExecutionMode, argumentsExecutionMode);
|
||||
|
||||
argumentShouldExecute = this.mapping.getArgumentShouldExecute();
|
||||
|
||||
initializeCallNodes();
|
||||
}
|
||||
|
||||
private void initializeCallNodes() {
|
||||
if (postApplicationSchema.hasOversaturatedArgs()
|
||||
|| !originalFunction.getCallStrategy().shouldCallDirect(isTail())) {
|
||||
this.loopingCall = CallOptimiserNode.build();
|
||||
} else {
|
||||
this.directCall = ExecuteCallNode.build();
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeOversaturatedCallNode(
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
|
||||
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode) {
|
||||
if (postApplicationSchema.hasOversaturatedArgs()) {
|
||||
oversaturatedCallableNode =
|
||||
InvokeCallableNodeGen.create(
|
||||
postApplicationSchema.getOversaturatedArguments(),
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode);
|
||||
oversaturatedCallableNode.setTail(isTail());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isFunctionFullyApplied(
|
||||
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode) {
|
||||
boolean functionIsFullyApplied = true;
|
||||
for (int i = 0; i < postApplicationSchema.getArgumentsCount(); i++) {
|
||||
boolean hasValidDefault =
|
||||
@ -67,18 +103,7 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
break;
|
||||
}
|
||||
}
|
||||
appliesFully = functionIsFullyApplied;
|
||||
|
||||
if (postApplicationSchema.hasOversaturatedArgs()) {
|
||||
oversaturatedCallableNode =
|
||||
InvokeCallableNodeGen.create(
|
||||
postApplicationSchema.getOversaturatedArguments(),
|
||||
defaultsExecutionMode,
|
||||
argumentsExecutionMode);
|
||||
oversaturatedCallableNode.setTail(isTail);
|
||||
}
|
||||
|
||||
argumentShouldExecute = this.mapping.getArgumentShouldExecute();
|
||||
return functionIsFullyApplied;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,33 +158,21 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
* @param function the function this node is reordering arguments for
|
||||
* @param state the state to pass to the function
|
||||
* @param arguments the arguments to reorder
|
||||
* @param optimiser a call optimiser node, capable of performing the actual function call
|
||||
* @return the provided {@code arguments} in the order expected by the cached {@link Function}
|
||||
*/
|
||||
public Stateful execute(
|
||||
Function function, Object state, Object[] arguments, CallOptimiserNode optimiser) {
|
||||
Object[] mappedAppliedArguments;
|
||||
public Stateful execute(Function function, Object state, Object[] arguments) {
|
||||
if (argumentsExecutionMode.shouldExecute()) {
|
||||
state = executeArguments(arguments, state);
|
||||
}
|
||||
|
||||
if (originalFunction.getSchema().hasAnyPreApplied()) {
|
||||
mappedAppliedArguments = function.clonePreAppliedArguments();
|
||||
} else {
|
||||
mappedAppliedArguments = new Object[this.postApplicationSchema.getArgumentsCount()];
|
||||
}
|
||||
|
||||
mapping.reorderAppliedArguments(arguments, mappedAppliedArguments);
|
||||
Object[] mappedAppliedArguments = prepareArguments(function, arguments);
|
||||
|
||||
if (this.appliesFully()) {
|
||||
if (!postApplicationSchema.hasOversaturatedArgs()) {
|
||||
if (this.isTail()) {
|
||||
throw new TailCallException(function, state, mappedAppliedArguments);
|
||||
} else {
|
||||
return optimiser.executeDispatch(function, state, mappedAppliedArguments);
|
||||
}
|
||||
return doCall(function, state, mappedAppliedArguments);
|
||||
} else {
|
||||
Stateful evaluatedVal = optimiser.executeDispatch(function, state, mappedAppliedArguments);
|
||||
Stateful evaluatedVal =
|
||||
loopingCall.executeDispatch(function, state, mappedAppliedArguments);
|
||||
|
||||
return this.oversaturatedCallableNode.execute(
|
||||
evaluatedVal.getValue(),
|
||||
@ -178,6 +191,17 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
}
|
||||
}
|
||||
|
||||
private Object[] prepareArguments(Function function, Object[] arguments) {
|
||||
Object[] mappedAppliedArguments;
|
||||
if (originalFunction.getSchema().hasAnyPreApplied()) {
|
||||
mappedAppliedArguments = function.clonePreAppliedArguments();
|
||||
} else {
|
||||
mappedAppliedArguments = new Object[this.postApplicationSchema.getArgumentsCount()];
|
||||
}
|
||||
mapping.reorderAppliedArguments(arguments, mappedAppliedArguments);
|
||||
return mappedAppliedArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an array containing the oversaturated arguments for the function being executed (if
|
||||
* any).
|
||||
@ -208,6 +232,16 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
return oversaturatedArguments;
|
||||
}
|
||||
|
||||
private Stateful doCall(Function function, Object state, Object[] arguments) {
|
||||
if (getOriginalFunction().getCallStrategy().shouldCallDirect(isTail())) {
|
||||
return directCall.executeCall(function, state, arguments);
|
||||
} else if (isTail()) {
|
||||
throw new TailCallException(function, state, arguments);
|
||||
} else {
|
||||
return loopingCall.executeDispatch(function, state, arguments);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the provided function is the same as the cached one.
|
||||
*
|
||||
@ -228,11 +262,11 @@ public class CachedArgumentSorterNode extends BaseNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link ArgumentSchema} to use in case the function call is not fully saturated.
|
||||
* Returns the {@link FunctionSchema} to use in case the function call is not fully saturated.
|
||||
*
|
||||
* @return the call result {@link ArgumentSchema}.
|
||||
* @return the call result {@link FunctionSchema}.
|
||||
*/
|
||||
public ArgumentSchema getPostApplicationSchema() {
|
||||
public FunctionSchema getPostApplicationSchema() {
|
||||
return postApplicationSchema;
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ public abstract class CallOptimiserNode extends Node {
|
||||
*
|
||||
* @return a fresh instance of {@link CallOptimiserNode}
|
||||
*/
|
||||
public static CallOptimiserNode create() {
|
||||
public static CallOptimiserNode build() {
|
||||
return new SimpleCallOptimiserNode();
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.enso.interpreter.node.ClosureRootNode;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
/**
|
||||
@ -15,7 +15,7 @@ import org.enso.interpreter.runtime.callable.function.Function;
|
||||
*/
|
||||
public class CreateFunctionNode extends ExpressionNode {
|
||||
private final RootCallTarget callTarget;
|
||||
private final ArgumentSchema schema;
|
||||
private final FunctionSchema schema;
|
||||
|
||||
/**
|
||||
* Creates a new node to represent a function definition.
|
||||
@ -25,7 +25,7 @@ public class CreateFunctionNode extends ExpressionNode {
|
||||
*/
|
||||
public CreateFunctionNode(RootCallTarget callTarget, ArgumentDefinition[] args) {
|
||||
this.callTarget = callTarget;
|
||||
this.schema = new ArgumentSchema(args);
|
||||
this.schema = new FunctionSchema(FunctionSchema.CallStrategy.CALL_LOOP, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,74 +0,0 @@
|
||||
package org.enso.interpreter.node.controlflow;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
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.runtime.error.TypeError;
|
||||
|
||||
/**
|
||||
* This node represents a very simple form of the if-then-else expression, specialised to the case
|
||||
* of checking whether its input is equal to zero.
|
||||
*/
|
||||
@NodeInfo(shortName = "if_then_else", description = "if arg0 = 0 then arg1 else arg2")
|
||||
public class IfZeroNode extends ExpressionNode {
|
||||
private final ConditionProfile conditionProf = ConditionProfile.createCountingProfile();
|
||||
@Child private ExpressionNode condition;
|
||||
@Child private ExpressionNode ifTrue;
|
||||
@Child private ExpressionNode ifFalse;
|
||||
|
||||
/**
|
||||
* Creates a new conditional expression node.
|
||||
*
|
||||
* @param condition the condition to execute and check for equality with zero
|
||||
* @param ifTrue the code to execute if {@code condition} is true
|
||||
* @param ifFalse the code to execute if {@code condition} is false
|
||||
*/
|
||||
public IfZeroNode(ExpressionNode condition, ExpressionNode ifTrue, ExpressionNode ifFalse) {
|
||||
this.condition = condition;
|
||||
this.ifTrue = ifTrue;
|
||||
this.ifFalse = ifFalse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the conditional expression.
|
||||
*
|
||||
* @param frame the stack frame for execution
|
||||
* @return the result of the {@code isTrue} branch if the condition is {@code true}, otherwise the
|
||||
* result of the {@code isFalse} branch
|
||||
*/
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
if (conditionProf.profile(executeCondition(frame) == 0)) {
|
||||
return ifTrue.executeGeneric(frame);
|
||||
} else {
|
||||
return ifFalse.executeGeneric(frame);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the condition to be checked.
|
||||
*
|
||||
* @param frame the stack frame for execution
|
||||
* @return the result of executing the condition
|
||||
*/
|
||||
private long executeCondition(VirtualFrame frame) {
|
||||
try {
|
||||
return condition.executeLong(frame);
|
||||
} catch (UnexpectedResultException ex) {
|
||||
throw new TypeError("Type error, expected long", this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the conditional is tail-recursive or not.
|
||||
*
|
||||
* @param isTail whether or not the conditional is tail recursive
|
||||
*/
|
||||
@Override
|
||||
public void setTail(boolean isTail) {
|
||||
ifTrue.setTail(isTail);
|
||||
ifFalse.setTail(isTail);
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package org.enso.interpreter.node.expression.builtin;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
/** Root node for use by all the builtin functions. */
|
||||
@NodeInfo(description = "Root node for builtin functions.")
|
||||
public abstract class BuiltinRootNode extends RootNode {
|
||||
protected BuiltinRootNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes this node's logic, returning a pair of return value and the new state.
|
||||
*
|
||||
* @param frame current execution frame
|
||||
* @return the {@link Stateful} instance containing new state and the result value of executing
|
||||
* the logic.
|
||||
*/
|
||||
@Override
|
||||
public abstract Stateful execute(VirtualFrame frame);
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package org.enso.interpreter.node.expression.builtin;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/** The root node for the basic control flow structure of the language. */
|
||||
@NodeInfo(shortName = "Number.ifZero", description = "Root node of the Number.ifZero method.")
|
||||
public class IfZeroNode extends BuiltinRootNode {
|
||||
private @Child ThunkExecutorNode leftThunkExecutorNode = ThunkExecutorNode.build(true);
|
||||
private @Child ThunkExecutorNode rightThunkExecutorNode = ThunkExecutorNode.build(true);
|
||||
private final ConditionProfile condProfile = ConditionProfile.createCountingProfile();
|
||||
|
||||
private IfZeroNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a number and two thunks and executes the first thunk if the number was 0 and second
|
||||
* otherwise.
|
||||
*
|
||||
* <p>Assumes the number is the first argument in the current execution frame, while the first and
|
||||
* second thunks are respectively the second and third arguments.
|
||||
*
|
||||
* @param frame current execution frame
|
||||
* @return the result of executing the proper thunk
|
||||
*/
|
||||
@Override
|
||||
public Stateful execute(VirtualFrame frame) {
|
||||
long self =
|
||||
TypesGen.asLong(Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[0]);
|
||||
Thunk ifT =
|
||||
TypesGen.asThunk(Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]);
|
||||
Thunk ifF =
|
||||
TypesGen.asThunk(Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[2]);
|
||||
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
|
||||
if (condProfile.profile(self == 0)) {
|
||||
return leftThunkExecutorNode.executeThunk(ifT, state);
|
||||
} else {
|
||||
return rightThunkExecutorNode.executeThunk(ifF, state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a three-argument function wrapping this node.
|
||||
*
|
||||
* @param language current language instance
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromBuiltinRootNode(
|
||||
new IfZeroNode(language),
|
||||
FunctionSchema.CallStrategy.DIRECT_WHEN_TAIL,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "ifTrue", ArgumentDefinition.ExecutionMode.PASS_THUNK),
|
||||
new ArgumentDefinition(2, "ifFalse", ArgumentDefinition.ExecutionMode.PASS_THUNK));
|
||||
}
|
||||
}
|
@ -2,19 +2,20 @@ package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
/** Root node for the {@code catch} function. */
|
||||
@NodeInfo(shortName = "Error.catch", description = "Root node for the catch function.")
|
||||
public class CatchErrorNode extends RootNode {
|
||||
public class CatchErrorNode extends BuiltinRootNode {
|
||||
private @Child InvokeCallableNode invokeCallableNode;
|
||||
private final ConditionProfile executionProfile = ConditionProfile.createCountingProfile();
|
||||
|
||||
@ -25,6 +26,7 @@ public class CatchErrorNode extends RootNode {
|
||||
new CallArgumentInfo[] {new CallArgumentInfo()},
|
||||
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
|
||||
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
|
||||
this.invokeCallableNode.markTail();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,8 +58,9 @@ public class CatchErrorNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new CatchErrorNode(language),
|
||||
FunctionSchema.CallStrategy.DIRECT_WHEN_TAIL,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "handler", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
}
|
||||
|
@ -2,11 +2,12 @@ package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.error.RuntimeError;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
@ -16,7 +17,7 @@ import org.enso.interpreter.runtime.type.TypesGen;
|
||||
@NodeInfo(
|
||||
shortName = "Panic.catch",
|
||||
description = "Root node for the builtin catch panic function.")
|
||||
public class CatchPanicNode extends RootNode {
|
||||
public class CatchPanicNode extends BuiltinRootNode {
|
||||
private ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build(false);
|
||||
|
||||
private CatchPanicNode(Language language) {
|
||||
@ -54,8 +55,9 @@ public class CatchPanicNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new CatchPanicNode(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "value", ArgumentDefinition.ExecutionMode.PASS_THUNK));
|
||||
}
|
||||
|
@ -2,16 +2,17 @@ package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.error.PanicException;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
/** Root node for the builtin panic function. */
|
||||
@NodeInfo(shortName = "Panic.throw", description = "Root node for the builtin panic function.")
|
||||
public class PanicNode extends RootNode {
|
||||
public class PanicNode extends BuiltinRootNode {
|
||||
private PanicNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
@ -37,8 +38,9 @@ public class PanicNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new PanicNode(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "value", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
}
|
||||
|
@ -2,10 +2,11 @@ package org.enso.interpreter.node.expression.builtin.error;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.error.RuntimeError;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@ -13,7 +14,7 @@ import org.enso.interpreter.runtime.state.Stateful;
|
||||
@NodeInfo(
|
||||
shortName = "Error.throw",
|
||||
description = "Root node for the builtin throw error function.")
|
||||
public class ThrowErrorNode extends RootNode {
|
||||
public class ThrowErrorNode extends BuiltinRootNode {
|
||||
|
||||
private ThrowErrorNode(Language language) {
|
||||
super(language);
|
||||
@ -29,7 +30,7 @@ public class ThrowErrorNode extends RootNode {
|
||||
* @return a runtime error wrapped argument
|
||||
*/
|
||||
@Override
|
||||
public Object execute(VirtualFrame frame) {
|
||||
public Stateful execute(VirtualFrame frame) {
|
||||
Object errorPayload = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1];
|
||||
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
|
||||
return new Stateful(state, new RuntimeError(errorPayload));
|
||||
@ -42,8 +43,9 @@ public class ThrowErrorNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new ThrowErrorNode(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "payload", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
}
|
||||
|
@ -5,18 +5,19 @@ import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
import java.io.PrintStream;
|
||||
|
||||
/** Allows for printing arbitrary values to the standard output. */
|
||||
@NodeInfo(shortName = "IO.println", description = "Root of the IO.println method.")
|
||||
public abstract class PrintNode extends RootNode {
|
||||
public abstract class PrintNode extends BuiltinRootNode {
|
||||
PrintNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
@ -42,8 +43,9 @@ public abstract class PrintNode extends RootNode {
|
||||
* @return a {@link Function} object wrapping the behavior of this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
PrintNodeGen.create(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "value", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
}
|
||||
|
@ -2,15 +2,16 @@ package org.enso.interpreter.node.expression.builtin.state;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
/** Root node for the builtin State.get function. */
|
||||
@NodeInfo(shortName = "State.get", description = "Root node for the builtin State.get function")
|
||||
public class GetStateNode extends RootNode {
|
||||
public class GetStateNode extends BuiltinRootNode {
|
||||
private GetStateNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
@ -34,8 +35,9 @@ public class GetStateNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new GetStateNode(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,16 @@ package org.enso.interpreter.node.expression.builtin.state;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
/** Root node for the builtin State.put function. */
|
||||
@NodeInfo(shortName = "State.put", description = "Root node for the builtin State.put function")
|
||||
public class PutStateNode extends RootNode {
|
||||
public class PutStateNode extends BuiltinRootNode {
|
||||
private PutStateNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
@ -35,8 +36,9 @@ public class PutStateNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new PutStateNode(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "newState", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
}
|
||||
|
@ -2,20 +2,19 @@ package org.enso.interpreter.node.expression.builtin.state;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import com.oracle.truffle.api.profiles.ConditionProfile;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
import org.enso.interpreter.runtime.type.TypesGen;
|
||||
|
||||
import javax.swing.plaf.nimbus.State;
|
||||
|
||||
/** Root for the builtin State.run function. */
|
||||
@NodeInfo(shortName = "State.run", description = "Root for the builtin State.run function")
|
||||
public class RunStateNode extends RootNode {
|
||||
public class RunStateNode extends BuiltinRootNode {
|
||||
private RunStateNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
@ -54,8 +53,9 @@ public class RunStateNode extends RootNode {
|
||||
* @return a function wrapping this node
|
||||
*/
|
||||
public static Function makeFunction(Language language) {
|
||||
return Function.fromRootNode(
|
||||
return Function.fromBuiltinRootNode(
|
||||
new RunStateNode(language),
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "state", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.runtime;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.IfZeroNode;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchErrorNode;
|
||||
import org.enso.interpreter.node.expression.builtin.error.CatchPanicNode;
|
||||
import org.enso.interpreter.node.expression.builtin.error.PanicNode;
|
||||
@ -53,10 +54,11 @@ public class Builtins {
|
||||
scope.registerMethod(error, "throw", ThrowErrorNode.makeFunction(language));
|
||||
scope.registerMethodForAny("catch", CatchErrorNode.makeFunction(language));
|
||||
|
||||
scope.registerMethodForNumber("ifZero", IfZeroNode.makeFunction(language));
|
||||
|
||||
scope.registerMethod(state, "get", GetStateNode.makeFunction(language));
|
||||
scope.registerMethod(state, "put", PutStateNode.makeFunction(language));
|
||||
scope.registerMethod(state, "run", RunStateNode.makeFunction(language));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,7 +51,7 @@ public class UnresolvedSymbol implements TruffleObject {
|
||||
*/
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Function resolveForNumber() {
|
||||
return scope.lookupMethodDefinitionForAny(name).orElse(null);
|
||||
return scope.lookupMethodDefinitionForNumber(name).orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,7 +2,7 @@ package org.enso.interpreter.runtime.callable.argument;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import java.util.function.Predicate;
|
||||
@ -33,9 +33,7 @@ public class CallArgumentInfo {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an unnamed call argument.
|
||||
*/
|
||||
/** Creates an unnamed call argument. */
|
||||
public CallArgumentInfo() {
|
||||
this.name = null;
|
||||
}
|
||||
@ -80,6 +78,7 @@ public class CallArgumentInfo {
|
||||
private boolean[] argumentUsed;
|
||||
private boolean[] callSiteArgApplied;
|
||||
private int oversaturatedWritePosition = 0;
|
||||
private FunctionSchema.CallStrategy callStrategy;
|
||||
|
||||
/**
|
||||
* Creates an unitialised object of this class. This instance is not safe for external use and
|
||||
@ -88,7 +87,7 @@ public class CallArgumentInfo {
|
||||
* @param schema the definition site arguments schema
|
||||
* @param callArgs the call site arguments schema
|
||||
*/
|
||||
private ArgumentMappingBuilder(ArgumentSchema schema, CallArgumentInfo[] callArgs) {
|
||||
private ArgumentMappingBuilder(FunctionSchema schema, CallArgumentInfo[] callArgs) {
|
||||
this.appliedMapping = new int[callArgs.length];
|
||||
this.oversaturatedArgumentMapping = new int[callArgs.length];
|
||||
this.callSiteArgApplied = new boolean[callArgs.length];
|
||||
@ -98,6 +97,7 @@ public class CallArgumentInfo {
|
||||
this.definitions = schema.getArgumentInfos();
|
||||
this.argumentUsed = schema.cloneHasPreApplied();
|
||||
this.existingOversaturatedArgs = schema.cloneOversaturatedArgs();
|
||||
this.callStrategy = schema.getCallStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -108,7 +108,7 @@ public class CallArgumentInfo {
|
||||
* @return the generated argument mapping
|
||||
*/
|
||||
public static ArgumentMappingBuilder generate(
|
||||
ArgumentSchema schema, CallArgumentInfo[] callArgs) {
|
||||
FunctionSchema schema, CallArgumentInfo[] callArgs) {
|
||||
ArgumentMappingBuilder mapping = new ArgumentMappingBuilder(schema, callArgs);
|
||||
mapping.processArguments();
|
||||
return mapping;
|
||||
@ -185,12 +185,12 @@ public class CallArgumentInfo {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an {@link ArgumentSchema} object resulting from filling in all the call site
|
||||
* Returns an {@link FunctionSchema} object resulting from filling in all the call site
|
||||
* arguments in the original definition site schema.
|
||||
*
|
||||
* @return the post-application arguments schema
|
||||
*/
|
||||
public ArgumentSchema getPostApplicationSchema() {
|
||||
public FunctionSchema getPostApplicationSchema() {
|
||||
CallArgumentInfo[] newOversaturatedArgInfo =
|
||||
IntStream.range(0, this.callArgs.length)
|
||||
.filter(i -> !this.callSiteArgApplied[i])
|
||||
@ -214,7 +214,7 @@ public class CallArgumentInfo {
|
||||
this.existingOversaturatedArgs.length,
|
||||
newOversaturatedArgInfo.length);
|
||||
|
||||
return new ArgumentSchema(definitions, argumentUsed, oversaturatedArgInfo);
|
||||
return new FunctionSchema(callStrategy, definitions, argumentUsed, oversaturatedArgInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
|
||||
import org.enso.interpreter.node.expression.atom.InstantiateNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
@ -68,7 +68,8 @@ public class AtomConstructor implements TruffleObject {
|
||||
new ClosureRootNode(
|
||||
null, new FrameDescriptor(), instantiateNode, null, "<constructor>:" + name);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
|
||||
return new Function(callTarget, null, new ArgumentSchema(args));
|
||||
return new Function(
|
||||
callTarget, null, new FunctionSchema(FunctionSchema.CallStrategy.ALWAYS_DIRECT, args));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -19,6 +19,7 @@ import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.node.callable.argument.sorter.ArgumentSorterNode;
|
||||
import org.enso.interpreter.node.callable.argument.sorter.ArgumentSorterNodeGen;
|
||||
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
@ -29,7 +30,7 @@ import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
public final class Function implements TruffleObject {
|
||||
private final RootCallTarget callTarget;
|
||||
private final MaterializedFrame scope;
|
||||
private final ArgumentSchema schema;
|
||||
private final FunctionSchema schema;
|
||||
private final @CompilerDirectives.CompilationFinal(dimensions = 1) Object[] preAppliedArguments;
|
||||
private final @CompilationFinal(dimensions = 1) Object[] oversaturatedArguments;
|
||||
|
||||
@ -38,7 +39,7 @@ public final class Function implements TruffleObject {
|
||||
*
|
||||
* @param callTarget the target containing the function's code
|
||||
* @param scope a frame representing the function's scope
|
||||
* @param schema the {@link ArgumentSchema} with which the function was defined
|
||||
* @param schema the {@link FunctionSchema} with which the function was defined
|
||||
* @param preappliedArguments the preapplied arguments for this function. The layout of this array
|
||||
* must be conforming to the {@code schema}. {@code null} is allowed if the function does not
|
||||
* have any partially applied arguments.
|
||||
@ -46,7 +47,7 @@ public final class Function implements TruffleObject {
|
||||
public Function(
|
||||
RootCallTarget callTarget,
|
||||
MaterializedFrame scope,
|
||||
ArgumentSchema schema,
|
||||
FunctionSchema schema,
|
||||
Object[] preappliedArguments,
|
||||
Object[] oversaturatedArguments) {
|
||||
this.callTarget = callTarget;
|
||||
@ -61,12 +62,27 @@ public final class Function implements TruffleObject {
|
||||
*
|
||||
* @param callTarget the target containing the function's code
|
||||
* @param scope a frame representing the function's scope
|
||||
* @param schema the {@link ArgumentSchema} with which the function was defined
|
||||
* @param schema the {@link FunctionSchema} with which the function was defined
|
||||
*/
|
||||
public Function(RootCallTarget callTarget, MaterializedFrame scope, ArgumentSchema schema) {
|
||||
public Function(RootCallTarget callTarget, MaterializedFrame scope, FunctionSchema schema) {
|
||||
this(callTarget, scope, schema, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Function object from a {@link RootNode} and argument definitions.
|
||||
*
|
||||
* @param node the {@link RootNode} for the function logic
|
||||
* @param callStrategy the {@link FunctionSchema.CallStrategy} to use for this function
|
||||
* @param args argument definitons
|
||||
* @return a Function object with specified behavior and arguments
|
||||
*/
|
||||
public static Function fromBuiltinRootNode(
|
||||
BuiltinRootNode node, FunctionSchema.CallStrategy callStrategy, ArgumentDefinition... args) {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
FunctionSchema schema = new FunctionSchema(callStrategy, args);
|
||||
return new Function(callTarget, null, schema);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the target containing the function's code.
|
||||
*
|
||||
@ -76,6 +92,15 @@ public final class Function implements TruffleObject {
|
||||
return callTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the call strategy that should be used for this function.
|
||||
*
|
||||
* @return this function's call strategy
|
||||
*/
|
||||
public FunctionSchema.CallStrategy getCallStrategy() {
|
||||
return getSchema().getCallStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the function's scope.
|
||||
*
|
||||
@ -90,7 +115,7 @@ public final class Function implements TruffleObject {
|
||||
*
|
||||
* @return the function's argument schema
|
||||
*/
|
||||
public ArgumentSchema getSchema() {
|
||||
public FunctionSchema getSchema() {
|
||||
return schema;
|
||||
}
|
||||
|
||||
@ -257,17 +282,4 @@ public final class Function implements TruffleObject {
|
||||
return (MaterializedFrame) arguments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a Function object from a {@link RootNode} and argument definitions.
|
||||
*
|
||||
* @param node the {@link RootNode} for the function logic
|
||||
* @param args argument definitons
|
||||
* @return a Function object with specified behavior and arguments
|
||||
*/
|
||||
public static Function fromRootNode(RootNode node, ArgumentDefinition... args) {
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
ArgumentSchema schema = new ArgumentSchema(args);
|
||||
return new Function(callTarget, null, schema);
|
||||
}
|
||||
}
|
||||
|
@ -8,24 +8,60 @@ import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
* Holds the definition site argument information together with information on the partially applied
|
||||
* arguments positions.
|
||||
*/
|
||||
public class ArgumentSchema {
|
||||
public class FunctionSchema {
|
||||
|
||||
/**
|
||||
* Denotes the call strategy that should be used whenever a function with this schema is called.
|
||||
*
|
||||
* <p>For builtin functions, the algorithm for choosing the proper {@link CallStrategy} is as
|
||||
* follows: if the node executes user-provided code ({@link
|
||||
* org.enso.interpreter.runtime.callable.argument.Thunk} or a {@link Function}) using the Tail
|
||||
* Call Optimization machinery (i.e. marking the execution node as tail position), the right
|
||||
* choice is {@code DIRECT_WHEN_TAIL}. Otherwise {@code ALWAYS_DIRECT} should be chosen.
|
||||
*/
|
||||
public enum CallStrategy {
|
||||
|
||||
/** Always call the function directly. */
|
||||
ALWAYS_DIRECT,
|
||||
/** Call the function directly when said function is in tail call position. */
|
||||
DIRECT_WHEN_TAIL,
|
||||
/** Always call the function using standard tail call machinery. */
|
||||
CALL_LOOP;
|
||||
|
||||
/**
|
||||
* Should this function be called directly in the given position?
|
||||
*
|
||||
* @param isTail is this a tail position?
|
||||
* @return {@code true} if the function should be called directly, {@code false} otherwise
|
||||
*/
|
||||
public boolean shouldCallDirect(boolean isTail) {
|
||||
return this == ALWAYS_DIRECT || (this == DIRECT_WHEN_TAIL && isTail);
|
||||
}
|
||||
}
|
||||
|
||||
private final @CompilationFinal(dimensions = 1) ArgumentDefinition[] argumentInfos;
|
||||
private final @CompilationFinal(dimensions = 1) boolean[] hasPreApplied;
|
||||
private final @CompilationFinal(dimensions = 1) CallArgumentInfo[] oversaturatedArguments;
|
||||
private final CallStrategy callStrategy;
|
||||
private final boolean hasAnyPreApplied;
|
||||
private final boolean hasOversaturatedArguments;
|
||||
|
||||
/**
|
||||
* Creates an {@link ArgumentSchema} instance.
|
||||
* Creates an {@link FunctionSchema} instance.
|
||||
*
|
||||
* @param callStrategy the call strategy to use for functions having this schema
|
||||
* @param argumentInfos Definition site arguments information
|
||||
* @param hasPreApplied A flags collection such that {@code hasPreApplied[i]} is true iff a
|
||||
* function has a partially applied argument at position {@code i}
|
||||
* @param oversaturatedArguments information about any unused, oversaturated arguments passed to
|
||||
* this function so far
|
||||
*/
|
||||
public ArgumentSchema(
|
||||
public FunctionSchema(
|
||||
CallStrategy callStrategy,
|
||||
ArgumentDefinition[] argumentInfos,
|
||||
boolean[] hasPreApplied,
|
||||
CallArgumentInfo[] oversaturatedArguments) {
|
||||
this.callStrategy = callStrategy;
|
||||
this.argumentInfos = argumentInfos;
|
||||
this.oversaturatedArguments = oversaturatedArguments;
|
||||
this.hasPreApplied = hasPreApplied;
|
||||
@ -43,13 +79,13 @@ public class ArgumentSchema {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link ArgumentSchema} instance assuming the function has no partially applied
|
||||
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied
|
||||
* arguments.
|
||||
*
|
||||
* @param argumentInfos Definition site arguments information
|
||||
*/
|
||||
public ArgumentSchema(ArgumentDefinition... argumentInfos) {
|
||||
this(argumentInfos, new boolean[argumentInfos.length], new CallArgumentInfo[0]);
|
||||
public FunctionSchema(CallStrategy callStrategy, ArgumentDefinition... argumentInfos) {
|
||||
this(callStrategy, argumentInfos, new boolean[argumentInfos.length], new CallArgumentInfo[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,4 +174,13 @@ public class ArgumentSchema {
|
||||
public CallArgumentInfo[] getOversaturatedArguments() {
|
||||
return oversaturatedArguments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the call strategy to use for functions with this schema.
|
||||
*
|
||||
* @return the call strategy to use
|
||||
*/
|
||||
public CallStrategy getCallStrategy() {
|
||||
return callStrategy;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ public class ModuleScope {
|
||||
private final Map<String, AtomConstructor> constructors = new HashMap<>();
|
||||
private final Map<AtomConstructor, Map<String, Function>> methods = new HashMap<>();
|
||||
private final Map<String, Function> anyMethods = new HashMap<>();
|
||||
private final Map<String, Function> numberMethods = new HashMap<>();
|
||||
private final Set<ModuleScope> imports = new HashSet<>();
|
||||
private final Set<ModuleScope> transitiveImports = new HashSet<>();
|
||||
|
||||
@ -70,6 +71,16 @@ public class ModuleScope {
|
||||
anyMethods.put(methodName, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a method for the {@code Number} type.
|
||||
*
|
||||
* @param methodName the name of the method to register
|
||||
* @param function the {@link Function} associated with this definition
|
||||
*/
|
||||
public void registerMethodForNumber(String methodName, Function function) {
|
||||
numberMethods.put(methodName, function);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up the definition for a given type and method name.
|
||||
*
|
||||
@ -102,14 +113,7 @@ public class ModuleScope {
|
||||
*/
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Optional<Function> lookupMethodDefinitionForAny(String name) {
|
||||
Function definedHere = anyMethods.get(name);
|
||||
if (definedHere != null) {
|
||||
return Optional.of(definedHere);
|
||||
}
|
||||
return transitiveImports.stream()
|
||||
.map(scope -> scope.getMethodsOfAny().get(name))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst();
|
||||
return searchAuxiliaryMethodsMap(ModuleScope::getMethodsOfAny, name);
|
||||
}
|
||||
|
||||
private Optional<Function> lookupSpecificMethodDefinitionForAtom(
|
||||
@ -128,6 +132,43 @@ public class ModuleScope {
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
private Optional<Function> lookupSpecificMethodDefinitionForNumber(String name) {
|
||||
return searchAuxiliaryMethodsMap(ModuleScope::getMethodsOfNumber, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a method definition by-name, for methods defined on the type Number.
|
||||
*
|
||||
* <p>The resolution algorithm prefers methods defined locally over any other method. The
|
||||
* definitions are imported into scope transitively.
|
||||
*
|
||||
* <p>If the specific search fails, methods defined for any type are searched, first looking at *
|
||||
* locally defined methods and then all the transitive imports.
|
||||
*
|
||||
* @param name the name of the method to look up
|
||||
* @return {@code Optional.of(resultMethod)} if the method existed, {@code Optional.empty()}
|
||||
* otherwise
|
||||
*/
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
public Optional<Function> lookupMethodDefinitionForNumber(String name) {
|
||||
return Optional.ofNullable(
|
||||
lookupSpecificMethodDefinitionForNumber(name)
|
||||
.orElseGet(() -> lookupMethodDefinitionForAny(name).orElse(null)));
|
||||
}
|
||||
|
||||
private Optional<Function> searchAuxiliaryMethodsMap(
|
||||
java.util.function.Function<ModuleScope, Map<String, Function>> mapGetter,
|
||||
String methodName) {
|
||||
Function definedHere = mapGetter.apply(this).get(methodName);
|
||||
if (definedHere != null) {
|
||||
return Optional.of(definedHere);
|
||||
}
|
||||
return transitiveImports.stream()
|
||||
.map(scope -> mapGetter.apply(scope).get(methodName))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the transitive dependencies of this module.
|
||||
*
|
||||
@ -141,6 +182,10 @@ public class ModuleScope {
|
||||
return anyMethods;
|
||||
}
|
||||
|
||||
private Map<String, Function> getMethodsOfNumber() {
|
||||
return numberMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a dependency for this module.
|
||||
*
|
||||
|
@ -33,12 +33,6 @@ trait AstExpressionVisitor[+T] {
|
||||
defaultsSuspended: Boolean
|
||||
): T
|
||||
|
||||
def visitIf(
|
||||
cond: AstExpression,
|
||||
ifTrue: AstExpression,
|
||||
ifFalse: AstExpression
|
||||
): T
|
||||
|
||||
def visitAssignment(varName: String, expr: AstExpression): T
|
||||
|
||||
def visitMatch(
|
||||
@ -203,15 +197,6 @@ case class AstAssignment(name: String, body: AstExpression)
|
||||
visitor.visitAssignment(name, body)
|
||||
}
|
||||
|
||||
case class AstIfZero(
|
||||
cond: AstExpression,
|
||||
ifTrue: AstExpression,
|
||||
ifFalse: AstExpression
|
||||
) extends AstExpression {
|
||||
override def visit[T](visitor: AstExpressionVisitor[T]): T =
|
||||
visitor.visitIf(cond, ifTrue, ifFalse)
|
||||
}
|
||||
|
||||
case class AstCase(cons: AstExpression, function: AstCaseFunction)
|
||||
|
||||
case class AstMatch(
|
||||
@ -288,7 +273,7 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
}
|
||||
|
||||
def expression: Parser[AstExpression] =
|
||||
desuspend | ifZero | matchClause | arith | function
|
||||
desuspend | matchClause | arith | function
|
||||
|
||||
def functionCall: Parser[AstApply] =
|
||||
"@" ~> expression ~ (argList ?) ~ defaultSuspend ^^ {
|
||||
@ -308,11 +293,6 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
case v ~ exp => AstAssignment(v, exp)
|
||||
}
|
||||
|
||||
def ifZero: Parser[AstIfZero] =
|
||||
"ifZero:" ~> "[" ~> (expression ~ ("," ~> expression ~ ("," ~> expression))) <~ "]" ^^ {
|
||||
case cond ~ (ift ~ iff) => AstIfZero(cond, ift, iff)
|
||||
}
|
||||
|
||||
def function: Parser[AstFunction] =
|
||||
("{" ~> (inArgList ?) ~ ((statement <~ ";") *) ~ expression <~ "}") ^^ {
|
||||
case args ~ stmts ~ expr => AstFunction(args.getOrElse(Nil), stmts, expr)
|
||||
|
@ -22,7 +22,7 @@ class ConstructorsTest extends InterpreterTest {
|
||||
val testCode =
|
||||
"""
|
||||
|@{
|
||||
| genList = { |i| ifZero: [i, @Nil, @Cons [i, @genList [i-1]]] };
|
||||
| genList = { |i| @ifZero [i, @Nil, @Cons [i, @genList [i-1]]] };
|
||||
| sumList = { |list| match list <
|
||||
| Cons ~ { |h, t| h + (@sumList [t]) };
|
||||
| Nil ~ { 0 };
|
||||
@ -82,7 +82,7 @@ class ConstructorsTest extends InterpreterTest {
|
||||
"""
|
||||
|type Cons2 a b;
|
||||
|
|
||||
|Unit.genList = { |i| ifZero: [i, @Nil2, @Cons2 [i, @genList [@Unit, i-1]]] }
|
||||
|Unit.genList = { |i| @ifZero [i, @Nil2, @Cons2 [i, @genList [@Unit, i-1]]] }
|
||||
|
|
||||
|type Nil2;
|
||||
|
|
||||
|
@ -32,7 +32,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|@{
|
||||
| sumTo = { |x| ifZero: [x, 0, x + (@sumTo [x - 1])] };
|
||||
| sumTo = { |x| @ifZero [x, 0, x + (@sumTo [x - 1])] };
|
||||
| @sumTo [10]
|
||||
|}
|
||||
""".stripMargin
|
||||
@ -103,7 +103,7 @@ class FunctionArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|@{
|
||||
| summator = { |current| ifZero: [current, 0, @{@summator [current - 1]} ] };
|
||||
| summator = { |current| @ifZero [current, 0, @{@summator [current - 1]} ] };
|
||||
| res = @summator [0];
|
||||
| res
|
||||
|}
|
||||
|
@ -68,7 +68,7 @@ class GlobalScopeTest extends InterpreterTest {
|
||||
|}
|
||||
|
|
||||
|Unit.fn1 = { |number|
|
||||
| ifZero: [number % 3, number, @decrementCall [@Unit, number]]
|
||||
| @ifZero [number % 3, number, @decrementCall [@Unit, number]]
|
||||
|}
|
||||
|
|
||||
|@fn1 [@Unit, 5]
|
||||
|
@ -7,7 +7,7 @@ class InteropTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|@{
|
||||
| recurFun = { |i| ifZero: [i, 0, @recurFun [i - 1]] };
|
||||
| recurFun = { |i| @ifZero [i, 0, @recurFun [i - 1]] };
|
||||
| recurFun
|
||||
|}
|
||||
|""".stripMargin
|
||||
|
@ -9,7 +9,7 @@ class LazyArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|@{
|
||||
| foo = { |i, $x, $y| ifZero: [i, $x, $y] };
|
||||
| foo = { |i, $x, $y| @ifZero [i, $x, $y] };
|
||||
| @foo [1, @println [@IO, 1], @println [@IO, 2]]
|
||||
|}
|
||||
|""".stripMargin
|
||||
@ -22,7 +22,7 @@ class LazyArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|@{
|
||||
| if = { |c, $ifT, $ifF| ifZero: [c, $ifT, $ifF] };
|
||||
| if = { |c, $ifT, $ifF| @ifZero [c, $ifT, $ifF] };
|
||||
| sum = { |c, acc| @if [c, acc, @sum [c-1, acc + c]] };
|
||||
| res = @sum [10000, 0];
|
||||
| res
|
||||
@ -68,7 +68,7 @@ class LazyArgumentsTest extends InterpreterTest {
|
||||
val code =
|
||||
"""
|
||||
|@{
|
||||
| if = { |c, $ifT, $ifF| ifZero: [c, $ifT, $ifF] };
|
||||
| if = { |c, $ifT, $ifF| @ifZero [c, $ifT, $ifF] };
|
||||
| foo = { |c| @if [c] };
|
||||
| @foo [0, @println [@IO, 1], @println [@IO, 2]];
|
||||
| @foo [1, @println [@IO, 3], @println [@IO, 4]]
|
||||
|
@ -119,7 +119,7 @@ class NamedArgumentsTest extends InterpreterTest {
|
||||
"""
|
||||
|Unit.summer = { |sumTo|
|
||||
| summator = { |acc = 0, current|
|
||||
| ifZero: [current, acc, @summator [current = current - 1, acc = acc + current]]
|
||||
| @ifZero [current, acc, @summator [current = current - 1, acc = acc + current]]
|
||||
| };
|
||||
| res = @summator [current = sumTo];
|
||||
| res
|
||||
@ -189,7 +189,7 @@ class NamedArgumentsTest extends InterpreterTest {
|
||||
|type Nil2;
|
||||
|
|
||||
|@{
|
||||
| genList = { |i| ifZero: [i, @Nil2, @Cons2 [rest = @genList [i-1], head = i]] };
|
||||
| genList = { |i| @ifZero [i, @Nil2, @Cons2 [rest = @genList [i-1], head = i]] };
|
||||
|
|
||||
| sumList = { |list| match list <
|
||||
| Cons2 ~ { |head, rest| head + @sumList [rest] };
|
||||
@ -210,7 +210,7 @@ class NamedArgumentsTest extends InterpreterTest {
|
||||
|type Nil2;
|
||||
|type Cons2 head (rest = Nil2);
|
||||
|@{
|
||||
| genList = { |i| ifZero: [i, @Nil2, @Cons2 [rest = @genList [i-1], head = i]] };
|
||||
| genList = { |i| @ifZero [i, @Nil2, @Cons2 [rest = @genList [i-1], head = i]] };
|
||||
|
|
||||
| sumList = { |list| match list <
|
||||
| Cons2 ~ { |head, rest| head + @sumList [rest] };
|
||||
|
@ -65,7 +65,7 @@ class StateTest extends InterpreterTest {
|
||||
| acc = @get [@State];
|
||||
| @println[@IO, acc];
|
||||
| @put [@State, acc + n];
|
||||
| ifZero: [n, @get [@State], @stateSum [n-1]]
|
||||
| @ifZero [n, @get [@State], @stateSum [n-1]]
|
||||
| };
|
||||
| @run [@State, 0, @stateSum [10]]
|
||||
|}
|
||||
|
Loading…
Reference in New Issue
Block a user