String literals, caller frame access, eval function (#333)

This commit is contained in:
Marcin Kostrzewa 2019-11-15 15:49:57 +01:00 committed by GitHub
parent 8da25bec2d
commit 0ec41b5bbd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
50 changed files with 848 additions and 152 deletions

View File

@ -18,6 +18,11 @@ public class RecursionBenchmarks {
recursionFixtures.sumTCO().execute(recursionFixtures.hundredMillion());
}
@Benchmark
public void benchSumTCOWithEval() {
recursionFixtures.sumTCOWithEval().execute(recursionFixtures.hundredMillion());
}
@Benchmark
public void benchSumTCOFoldLike() {
recursionFixtures.sumTCOFoldLike().execute(recursionFixtures.hundredMillion());

View File

@ -83,4 +83,16 @@ class RecursionFixtures extends InterpreterRunner {
|""".stripMargin
val sumStateTCO = eval(sumStateTCOCode)
val sumTCOWithEvalCode =
"""
|{ |sumTo|
| summator = { |acc, current|
| @ifZero [current, acc, @eval [@Debug, "@summator [acc + current, current - 1]"]]
| };
| res = @summator [0, sumTo];
| res
|}
|""".stripMargin
val sumTCOWithEval = eval(sumTCOWithEvalCode)
}

View File

@ -18,5 +18,6 @@ public class Constants {
public static final String ARGUMENT_SORTER_NODE = "10";
public static final String FUNCTION_INTEROP_LIBRARY = "10";
public static final String THUNK_EXECUTOR_NODE = "10";
public static final String EVAL_NODE = "10";
}
}

View File

@ -3,7 +3,6 @@ package org.enso.interpreter;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.instrumentation.ProvidedTags;
import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.nodes.RootNode;
@ -11,6 +10,8 @@ import org.enso.interpreter.builder.FileDetector;
import org.enso.interpreter.node.ProgramRootNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.RuntimeOptions;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.graalvm.options.OptionDescriptors;
/**
@ -82,7 +83,8 @@ public final class Language extends TruffleLanguage<Context> {
@Override
protected CallTarget parse(ParsingRequest request) {
RootNode root =
new ProgramRootNode(this, new FrameDescriptor(), "root", null, request.getSource());
new ProgramRootNode(
this, new LocalScope(), new ModuleScope(), "root", null, request.getSource());
return Truffle.getRuntime().createCallTarget(root);
}

View File

@ -61,7 +61,6 @@ public class CallArgFactory implements AstCallArgVisitor<CallArgument> {
name.orElse(null),
Truffle.getRuntime()
.createCallTarget(
new ClosureRootNode(
language, childScope.getFrameDescriptor(), expr, null, displayName)));
new ClosureRootNode(language, childScope, moduleScope, expr, null, displayName)));
}
}

View File

@ -17,6 +17,7 @@ import org.enso.interpreter.node.controlflow.*;
import org.enso.interpreter.node.expression.constant.ConstructorNode;
import org.enso.interpreter.node.expression.constant.DynamicSymbolNode;
import org.enso.interpreter.node.expression.literal.IntegerLiteralNode;
import org.enso.interpreter.node.expression.literal.StringLiteralNode;
import org.enso.interpreter.node.expression.operator.*;
import org.enso.interpreter.node.scope.AssignmentNode;
import org.enso.interpreter.node.scope.AssignmentNodeGen;
@ -113,10 +114,22 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
* @param l the value to represent
* @return a runtime node representing that value
*/
@Override
public ExpressionNode visitLong(long l) {
return new IntegerLiteralNode(l);
}
/**
* Creates a runtime String literal value from an AST node.
*
* @param string the string value of this literal
* @return a runtime node representing this literal
*/
@Override
public ExpressionNode visitStringLiteral(String string) {
return new StringLiteralNode(string);
}
/**
* Creates runtime nodes representing arithmetic expressions.
*
@ -234,8 +247,7 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
FunctionBodyNode fnBodyNode =
new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr);
RootNode fnRootNode =
new ClosureRootNode(
language, scope.getFrameDescriptor(), fnBodyNode, null, "lambda::" + scopeName);
new ClosureRootNode(language, scope, moduleScope, fnBodyNode, null, "lambda::" + scopeName);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode);
return new CreateFunctionNode(callTarget, argDefinitions);

View File

@ -6,8 +6,8 @@ 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.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.error.VariableDoesNotExistException;
import org.enso.interpreter.runtime.scope.ModuleScope;

View File

@ -2,13 +2,13 @@ package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.FrameUtil;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful;
/**
@ -26,23 +26,20 @@ public class ClosureRootNode extends EnsoRootNode {
* Creates a new root node.
*
* @param language the language identifier
* @param frameDescriptor a description of the stack frame
* @param localScope a description of the local scope
* @param moduleScope a description of the module scope
* @param body the program body to be executed
* @param section a mapping from {@code body} to the program source
* @param name a name for the node
*/
public ClosureRootNode(
Language language,
FrameDescriptor frameDescriptor,
LocalScope localScope,
ModuleScope moduleScope,
ExpressionNode body,
SourceSection section,
String name) {
super(
language,
frameDescriptor,
name,
section,
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
super(language, localScope, moduleScope, name, section);
this.body = body;
}

View File

@ -2,18 +2,22 @@ package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A common base class for all kinds of root node in Enso. */
public abstract class EnsoRootNode extends RootNode {
private final String name;
private final SourceSection sourceSection;
private final FrameSlot stateFrameSlot;
private final LocalScope localScope;
private final ModuleScope moduleScope;
private @CompilerDirectives.CompilationFinal TruffleLanguage.ContextReference<Context>
contextReference;
private @CompilerDirectives.CompilationFinal TruffleLanguage.LanguageReference<Language>
@ -23,21 +27,24 @@ public abstract class EnsoRootNode extends RootNode {
* Constructs the root node.
*
* @param language the language instance in which this will execute
* @param frameDescriptor a reference to the construct root frame
* @param localScope a reference to the construct local scope
* @param moduleScope a reference to the construct module scope
* @param name the name of the construct
* @param sourceSection a reference to the source code being executed
* @param stateFrameSlot the code to compile and execute
*/
public EnsoRootNode(
Language language,
FrameDescriptor frameDescriptor,
LocalScope localScope,
ModuleScope moduleScope,
String name,
SourceSection sourceSection,
FrameSlot stateFrameSlot) {
super(language, frameDescriptor);
SourceSection sourceSection) {
super(language, localScope.getFrameDescriptor());
this.name = name;
this.localScope = localScope;
this.moduleScope = moduleScope;
this.sourceSection = sourceSection;
this.stateFrameSlot = stateFrameSlot;
this.stateFrameSlot =
localScope.getFrameDescriptor().findOrAddFrameSlot("<<state>>", FrameSlotKind.Object);
}
/**
@ -89,4 +96,22 @@ public abstract class EnsoRootNode extends RootNode {
public SourceSection getSourceSection() {
return sourceSection;
}
/**
* Gets the local scope this node expects to work with
*
* @return the local scope for this node
*/
public LocalScope getLocalScope() {
return localScope;
}
/**
* Gets the module scope this node was defined with
*
* @return the module scope for this node
*/
public ModuleScope getModuleScope() {
return moduleScope;
}
}

View File

@ -1,13 +1,13 @@
package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/**
* This node handles static transformation of the input AST before execution and represents the root
@ -27,23 +27,25 @@ public class ProgramRootNode extends EnsoRootNode {
* Constructs the root node.
*
* @param language the language instance in which this will execute
* @param frameDescriptor a reference to the program root frame
* @param localScope a reference to the program local scope
* @param moduleScope a reference to the program module scope
* @param name the name of the program
* @param sourceSection a reference to the source code being executed
* @param sourceCode the code to compile and execute
*/
public ProgramRootNode(
Language language,
FrameDescriptor frameDescriptor,
LocalScope localScope,
ModuleScope moduleScope,
String name,
SourceSection sourceSection,
Source sourceCode) {
super(
language,
frameDescriptor,
localScope,
moduleScope,
name,
sourceSection,
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
sourceSection);
this.sourceCode = sourceCode;
}

View File

@ -90,8 +90,10 @@ public class ApplicationNode extends ExpressionNode {
@Override
public Object executeGeneric(VirtualFrame frame) {
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
Stateful result = this.invokeCallableNode.execute(
this.callable.executeGeneric(frame), state, evaluateArguments(frame));
Stateful result =
this.invokeCallableNode.execute(
this.callable.executeGeneric(frame), frame, state, evaluateArguments(frame));
frame.setObject(getStateFrameSlot(), result.getState());
return result.getValue();
}

View File

@ -0,0 +1,48 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.EnsoRootNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/**
* Captures the current caller info to pass to functions requiring it.
*
* <p>The information captured includes current execution frame, as well as static context,
* including the local and module scope metadata.
*/
@NodeInfo(
description = "Captures the caller info for use in functions called from this node's scope")
public class CaptureCallerInfoNode extends Node {
private @CompilerDirectives.CompilationFinal LocalScope localScope;
private @CompilerDirectives.CompilationFinal ModuleScope moduleScope;
/**
* Captures the caller info for use in functions called from the current scope.
*
* @param frame current execution frame
* @return caller information for the current scope
*/
public CallerInfo execute(VirtualFrame frame) {
if (localScope == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
EnsoRootNode rootNode = (EnsoRootNode) getRootNode();
localScope = rootNode.getLocalScope();
moduleScope = rootNode.getModuleScope();
}
return new CallerInfo(frame.materialize(), localScope, moduleScope);
}
/**
* Creates an instance of this node.
*
* @return an instance of this node
*/
public static CaptureCallerInfoNode build() {
return new CaptureCallerInfoNode();
}
}

View File

@ -6,14 +6,14 @@ 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 org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.state.Stateful;
/**
* This node is responsible for optimising function calls.
*
* <p>Where possible, it will make the call as a 'direct' call, one with no lookup needed, but will
* fall back to performing a lookup if necessary.
* <p>Where possible, it will make the call as a direct call, with potential for inlining.
*/
public abstract class ExecuteCallNode extends Node {
@ -24,6 +24,7 @@ public abstract class ExecuteCallNode extends Node {
* already cached. THis means that the call can be made quickly.
*
* @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the current state value
* @param arguments the arguments passed to {@code function} in the expected positional order
* @param cachedTarget the cached call target for {@code function}
@ -33,12 +34,14 @@ public abstract class ExecuteCallNode extends Node {
@Specialization(guards = "function.getCallTarget() == cachedTarget")
protected Stateful callDirect(
Function function,
CallerInfo callerInfo,
Object state,
Object[] arguments,
@Cached("function.getCallTarget()") RootCallTarget cachedTarget,
@Cached("create(cachedTarget)") DirectCallNode callNode) {
return (Stateful)
callNode.call(Function.ArgumentsHelper.buildArguments(function, state, arguments));
callNode.call(
Function.ArgumentsHelper.buildArguments(function, callerInfo, state, arguments));
}
/**
@ -48,6 +51,7 @@ public abstract class ExecuteCallNode extends Node {
* provided function. This is much slower and should, in general, be avoided.
*
* @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the current state value
* @param arguments the arguments passed to {@code function} in the expected positional order
* @param callNode the cached call node for making indirect calls
@ -55,23 +59,34 @@ public abstract class ExecuteCallNode extends Node {
*/
@Specialization(replaces = "callDirect")
protected Stateful callIndirect(
Function function, Object state, Object[] arguments, @Cached IndirectCallNode callNode) {
Function function,
CallerInfo callerInfo,
Object state,
Object[] arguments,
@Cached IndirectCallNode callNode) {
return (Stateful)
callNode.call(
function.getCallTarget(),
Function.ArgumentsHelper.buildArguments(function, state, arguments));
Function.ArgumentsHelper.buildArguments(function, callerInfo, state, arguments));
}
/**
* Executes the function call.
*
* @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the state value to pass to the function
* @param arguments the arguments to be passed to {@code function}
* @return the result of executing {@code function} on {@code arguments}
*/
public abstract Stateful executeCall(Object function, Object state, Object[] arguments);
public abstract Stateful executeCall(
Object function, CallerInfo callerInfo, Object state, Object[] arguments);
/**
* Creates an instance of this node.
*
* @return an instance of this node
*/
public static ExecuteCallNode build() {
return ExecuteCallNodeGen.create();
}

View File

@ -1,10 +1,8 @@
package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.FrameUtil;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.node.ExpressionNode;

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.callable;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
@ -114,26 +115,30 @@ public abstract class InvokeCallableNode extends BaseNode {
* Invokes a function directly on the arguments contained in this node.
*
* @param function the function to be executed
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to the function
* @return the result of executing {@code callable} on the known arguments
*/
@Specialization
public Stateful invokeFunction(Function function, Object state, Object[] arguments) {
return this.argumentSorter.execute(function, state, arguments);
Stateful invokeFunction(
Function function, VirtualFrame callerFrame, Object state, Object[] arguments) {
return this.argumentSorter.execute(function, callerFrame, state, arguments);
}
/**
* Invokes a constructor directly on the arguments contained in this node.
*
* @param constructor the constructor to be executed
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to the constructor
* @return the result of executing {@code constructor} on the known arguments
*/
@Specialization
public Stateful invokeConstructor(AtomConstructor constructor, Object state, Object[] arguments) {
return invokeFunction(constructor.getConstructorFunction(), state, arguments);
Stateful invokeConstructor(
AtomConstructor constructor, VirtualFrame callerFrame, Object state, Object[] arguments) {
return invokeFunction(constructor.getConstructorFunction(), callerFrame, state, arguments);
}
/**
@ -141,12 +146,14 @@ public abstract class InvokeCallableNode extends BaseNode {
* argument.
*
* @param symbol the name of the requested symbol
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to the dynamic symbol
* @return the result of resolving and executing the symbol for the {@code this} argument
*/
@Specialization
public Stateful invokeDynamicSymbol(UnresolvedSymbol symbol, Object state, Object[] arguments) {
public Stateful invokeDynamicSymbol(
UnresolvedSymbol symbol, VirtualFrame callerFrame, Object state, Object[] arguments) {
if (canApplyThis) {
Object selfArgument = arguments[thisArgumentPosition];
if (argumentsExecutionMode.shouldExecute()) {
@ -159,7 +166,7 @@ public abstract class InvokeCallableNode extends BaseNode {
state = selfResult.getState();
}
Function function = methodResolverNode.execute(symbol, selfArgument);
return this.argumentSorter.execute(function, state, arguments);
return this.argumentSorter.execute(function, callerFrame, state, arguments);
} else {
throw new RuntimeException("Currying without `this` argument is not yet supported.");
}
@ -172,12 +179,14 @@ public abstract class InvokeCallableNode extends BaseNode {
* NotInvokableException} to signal this.
*
* @param callable the callable to be executed
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to the callable
* @return error
*/
@Fallback
public Stateful invokeGeneric(Object callable, Object state, Object[] arguments) {
public Stateful invokeGeneric(
Object callable, VirtualFrame callerFrame, Object state, Object[] arguments) {
throw new NotInvokableException(callable, this);
}
@ -185,11 +194,13 @@ public abstract class InvokeCallableNode extends BaseNode {
* Executes the provided {@code callable} on the supplied {@code arguments}.
*
* @param callable the callable to evaluate
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to evaluate {@code callable} on
* @return the result of executing {@code callable} on the supplied {@code arguments}
*/
public abstract Stateful execute(Object callable, Object state, Object[] arguments);
public abstract Stateful execute(
Object callable, VirtualFrame callerFrame, Object state, Object[] arguments);
/**
* Sets whether or not the current node is tail-recursive.

View File

@ -38,15 +38,13 @@ public abstract class ThunkExecutorNode extends Node {
@Cached("createLoopingOptimizerIfNeeded()")
LoopingCallOptimiserNode loopingCallOptimiserNode) {
if (getIsTail()) {
return (Stateful)
callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
} else {
try {
return (Stateful)
callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
return (Stateful) callNode.call(Function.ArgumentsHelper.buildArguments(thunk, state));
} catch (TailCallException e) {
return loopingCallOptimiserNode.executeDispatch(
e.getFunction(), e.getState(), e.getArguments());
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
}
}
}
@ -58,14 +56,19 @@ public abstract class ThunkExecutorNode extends Node {
@Cached IndirectCallNode callNode,
@Cached("createLoopingOptimizerIfNeeded()")
LoopingCallOptimiserNode loopingCallOptimiserNode) {
if (getIsTail()) {
return (Stateful)
callNode.call(
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
} else {
try {
return (Stateful)
callNode.call(
thunk.getCallTarget(),
Function.ArgumentsHelper.buildArguments(thunk, state));
thunk.getCallTarget(), Function.ArgumentsHelper.buildArguments(thunk, state));
} catch (TailCallException e) {
return loopingCallOptimiserNode.executeDispatch(
e.getFunction(), e.getState(), e.getArguments());
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
}
}
}

View File

@ -3,11 +3,11 @@ package org.enso.interpreter.node.callable.argument.sorter;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Constants;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.state.Stateful;
@ -50,11 +50,11 @@ public abstract class ArgumentSorterNode extends BaseNode {
* matches with the one stored in the cached argument sorter object.
*
* @param function the function to sort arguments for
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments being passed to {@code callable}
* @param mappingNode a cached node that tracks information about the mapping to enable a fast
* path
* @param optimiser a cached call optimizer node, capable of performing the actual function call
* @return the result of applying the function with remapped arguments
*/
@Specialization(
@ -62,12 +62,13 @@ public abstract class ArgumentSorterNode extends BaseNode {
limit = Constants.CacheSizes.ARGUMENT_SORTER_NODE)
public Stateful invokeCached(
Function function,
VirtualFrame callerFrame,
Object state,
Object[] arguments,
@Cached(
"build(function, getSchema(), getDefaultsExecutionMode(), getArgumentsExecutionMode(), isTail())")
CachedArgumentSorterNode mappingNode) {
return mappingNode.execute(function, state, arguments);
return mappingNode.execute(function, callerFrame, state, arguments);
}
/**
@ -75,14 +76,17 @@ public abstract class ArgumentSorterNode extends BaseNode {
* perform any caching and is thus a slow-path operation.
*
* @param function the function to execute.
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to reorder and supply to the {@code function}.
* @return the result of calling {@code function} with the supplied {@code arguments}.
*/
@Specialization(replaces = "invokeCached")
public Stateful invokeUncached(Function function, Object state, Object[] arguments) {
public Stateful invokeUncached(
Function function, VirtualFrame callerFrame, Object state, Object[] arguments) {
return invokeCached(
function,
callerFrame,
state,
arguments,
CachedArgumentSorterNode.build(
@ -97,11 +101,13 @@ public abstract class ArgumentSorterNode extends BaseNode {
* Executes the {@link ArgumentSorterNode} to reorder the arguments.
*
* @param callable the function to sort arguments for
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments being passed to {@code function}
* @return the result of executing the {@code function} with reordered {@code arguments}
*/
public abstract Stateful execute(Function callable, Object state, Object[] arguments);
public abstract Stateful execute(
Function callable, VirtualFrame callerFrame, Object state, Object[] arguments);
CallArgumentInfo[] getSchema() {
return schema;

View File

@ -1,20 +1,23 @@
package org.enso.interpreter.node.callable.argument.sorter;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
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.CaptureCallerInfoNode;
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;
import org.enso.interpreter.node.callable.dispatch.CallOptimiserNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
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.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.state.Stateful;
@ -35,6 +38,7 @@ public class CachedArgumentSorterNode extends BaseNode {
private final InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode;
private @Child ExecuteCallNode directCall;
private @Child CallOptimiserNode loopingCall;
private @Child CaptureCallerInfoNode captureCallerInfoNode;
/**
* Creates a node that generates and then caches the argument mapping.
@ -64,8 +68,11 @@ public class CachedArgumentSorterNode extends BaseNode {
initializeOversaturatedCallNode(defaultsExecutionMode, argumentsExecutionMode);
argumentShouldExecute = this.mapping.getArgumentShouldExecute();
initializeCallNodes();
if (originalFunction.getSchema().getCallerFrameAccess().shouldFrameBePassed()) {
this.captureCallerInfoNode = CaptureCallerInfoNode.build();
}
}
private void initializeCallNodes() {
@ -156,11 +163,17 @@ public class CachedArgumentSorterNode extends BaseNode {
* Reorders the provided arguments into the necessary order for the cached callable.
*
* @param function the function this node is reordering arguments for
* @param callerFrame the caller frame to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to reorder
* @return the provided {@code arguments} in the order expected by the cached {@link Function}
*/
public Stateful execute(Function function, Object state, Object[] arguments) {
public Stateful execute(
Function function, VirtualFrame callerFrame, Object state, Object[] arguments) {
CallerInfo callerInfo = null;
if (captureCallerInfoNode != null) {
callerInfo = captureCallerInfoNode.execute(callerFrame);
}
if (argumentsExecutionMode.shouldExecute()) {
state = executeArguments(arguments, state);
}
@ -169,13 +182,14 @@ public class CachedArgumentSorterNode extends BaseNode {
if (this.appliesFully()) {
if (!postApplicationSchema.hasOversaturatedArgs()) {
return doCall(function, state, mappedAppliedArguments);
return doCall(function, callerInfo, state, mappedAppliedArguments);
} else {
Stateful evaluatedVal =
loopingCall.executeDispatch(function, state, mappedAppliedArguments);
loopingCall.executeDispatch(function, callerInfo, state, mappedAppliedArguments);
return this.oversaturatedCallableNode.execute(
evaluatedVal.getValue(),
callerFrame,
evaluatedVal.getState(),
generateOversaturatedArguments(function, arguments));
}
@ -232,13 +246,14 @@ public class CachedArgumentSorterNode extends BaseNode {
return oversaturatedArguments;
}
private Stateful doCall(Function function, Object state, Object[] arguments) {
private Stateful doCall(
Function function, CallerInfo callerInfo, Object state, Object[] arguments) {
if (getOriginalFunction().getCallStrategy().shouldCallDirect(isTail())) {
return directCall.executeCall(function, state, arguments);
return directCall.executeCall(function, callerInfo, state, arguments);
} else if (isTail()) {
throw new TailCallException(function, state, arguments);
throw new TailCallException(function, callerInfo, state, arguments);
} else {
return loopingCall.executeDispatch(function, state, arguments);
return loopingCall.executeDispatch(function, callerInfo, state, arguments);
}
}

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.node.callable.dispatch;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.state.Stateful;
/**
@ -13,10 +14,13 @@ public abstract class CallOptimiserNode extends Node {
* Calls the provided {@code callable} using the provided {@code arguments}.
*
* @param callable the callable to execute
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to {@code callable}
* @return the result of executing {@code callable} using {@code arguments}
*/
public abstract Stateful executeDispatch(Object callable, Object state, Object[] arguments);
public abstract Stateful executeDispatch(
Object callable, CallerInfo callerInfo, Object state, Object[] arguments);
/**
* Creates an instance of default implementation of {@link CallOptimiserNode}.

View File

@ -7,6 +7,7 @@ import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.nodes.RepeatingNode;
import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.state.Stateful;
@ -33,14 +34,17 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
* Calls the provided {@code function} using the provided {@code arguments}.
*
* @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to {@code function}
* @return the result of executing {@code function} using {@code arguments}
*/
@Override
public Stateful executeDispatch(Object function, Object state, Object[] arguments) {
public Stateful executeDispatch(
Object function, CallerInfo callerInfo, Object state, Object[] arguments) {
VirtualFrame frame = Truffle.getRuntime().createVirtualFrame(null, loopFrameDescriptor);
((RepeatedCallNode) loopNode.getRepeatingNode()).setNextCall(frame, function, state, arguments);
((RepeatedCallNode) loopNode.getRepeatingNode())
.setNextCall(frame, function, callerInfo, state, arguments);
loopNode.executeLoop(frame);
return ((RepeatedCallNode) loopNode.getRepeatingNode()).getResult(frame);
@ -56,6 +60,7 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
private final FrameSlot functionSlot;
private final FrameSlot argsSlot;
private final FrameSlot stateSlot;
private final FrameSlot callerInfoSlot;
@Child private ExecuteCallNode dispatchNode;
/**
@ -68,6 +73,7 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
resultSlot = descriptor.findOrAddFrameSlot("<TCO Result>", FrameSlotKind.Object);
argsSlot = descriptor.findOrAddFrameSlot("<TCO Arguments>", FrameSlotKind.Object);
stateSlot = descriptor.findOrAddFrameSlot("<TCO State>", FrameSlotKind.Object);
callerInfoSlot = descriptor.findOrAddFrameSlot("<TCO Caller Info>", FrameSlotKind.Object);
dispatchNode = ExecuteCallNodeGen.create();
}
@ -76,11 +82,18 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
*
* @param frame the stack frame for execution
* @param function the function to execute in {@code frame}
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to execute {@code function} with
*/
public void setNextCall(VirtualFrame frame, Object function, Object state, Object[] arguments) {
public void setNextCall(
VirtualFrame frame,
Object function,
CallerInfo callerInfo,
Object state,
Object[] arguments) {
frame.setObject(functionSlot, function);
frame.setObject(callerInfoSlot, callerInfo);
frame.setObject(stateSlot, state);
frame.setObject(argsSlot, arguments);
}
@ -95,6 +108,12 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
return (Stateful) FrameUtil.getObjectSafe(frame, resultSlot);
}
private CallerInfo getCallerInfo(VirtualFrame frame) {
CallerInfo result = (CallerInfo) FrameUtil.getObjectSafe(frame, callerInfoSlot);
frame.setObject(callerInfoSlot, null);
return result;
}
/**
* Generates the next call to be made during looping execution.
*
@ -143,10 +162,12 @@ public class LoopingCallOptimiserNode extends CallOptimiserNode {
Object function = getNextFunction(frame);
Object state = getNextState(frame);
Object[] arguments = getNextArgs(frame);
frame.setObject(resultSlot, dispatchNode.executeCall(function, state, arguments));
CallerInfo callerInfo = getCallerInfo(frame);
frame.setObject(
resultSlot, dispatchNode.executeCall(function, callerInfo, state, arguments));
return false;
} catch (TailCallException e) {
setNextCall(frame, e.getFunction(), e.getState(), e.getArguments());
setNextCall(frame, e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
return true;
}
}

View File

@ -3,6 +3,7 @@ package org.enso.interpreter.node.callable.dispatch;
import com.oracle.truffle.api.CompilerDirectives;
import org.enso.interpreter.node.callable.ExecuteCallNode;
import org.enso.interpreter.node.callable.ExecuteCallNodeGen;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.control.TailCallException;
import org.enso.interpreter.runtime.state.Stateful;
@ -19,19 +20,22 @@ public class SimpleCallOptimiserNode extends CallOptimiserNode {
* Calls the provided {@code function} using the provided {@code arguments}.
*
* @param function the function to execute
* @param callerInfo the caller info to pass to the function
* @param state the state to pass to the function
* @param arguments the arguments to {@code function}
* @return the result of executing {@code function} using {@code arguments}
*/
@Override
public Stateful executeDispatch(Object function, Object state, Object[] arguments) {
public Stateful executeDispatch(
Object function, CallerInfo callerInfo, Object state, Object[] arguments) {
try {
return executeCallNode.executeCall(function, state, arguments);
return executeCallNode.executeCall(function, callerInfo, state, arguments);
} catch (TailCallException e) {
CompilerDirectives.transferToInterpreterAndInvalidate();
CallOptimiserNode replacement = new LoopingCallOptimiserNode();
this.replace(replacement);
return replacement.executeDispatch(e.getFunction(), e.getState(), e.getArguments());
return replacement.executeDispatch(
e.getFunction(), e.getCallerInfo(), e.getState(), e.getArguments());
}
}
}

View File

@ -6,8 +6,8 @@ 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.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
/**
* This node is responsible for representing the definition of a function. It contains information

View File

@ -56,10 +56,19 @@ public class ConstructorCaseNode extends CaseNode {
if (profile.profile(matcherVal == target.getConstructor())) {
Function function = branch.executeFunction(frame);
throw new BranchSelectedException(
executeCallNode.executeCall(function, state, target.getFields()));
executeCallNode.executeCall(
function, null, state, target.getFields())); // Note [Caller Info For Case Branches]
}
}
/* Note [Caller Info For Case Branches]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* It is assumed that functions serving as pattern match logic branches are always function
* literals, not references, curried functions etc. Therefore, as function literals, they
* have no way of accessing the caller frame and can safely be passed null.
*/
/**
* Handles the function scrutinee case, by not matching it at all.
*

View File

@ -29,7 +29,9 @@ public class FallbackNode extends CaseNode {
private void execute(VirtualFrame frame, Object target) throws UnexpectedResultException {
Function function = functionNode.executeFunction(frame);
Object state = FrameUtil.getObjectSafe(frame, getStateFrameSlot());
throw new BranchSelectedException(executeCallNode.executeCall(function, state, new Object[0]));
throw new BranchSelectedException(
executeCallNode.executeCall(
function, null, state, new Object[0])); // Note [Caller Info For Case Branches]
}
/**

View File

@ -0,0 +1,55 @@
package org.enso.interpreter.node.expression.builtin.debug;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.node.expression.debug.EvalNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
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 Debug.eval function. */
@NodeInfo(shortName = "Debug.eval", description = "Root node for the builtin Debug.eval function")
public class DebugEvalNode extends BuiltinRootNode {
private @Child EvalNode evalNode = EvalNode.build();
private DebugEvalNode(Language language) {
super(language);
evalNode.markTail();
}
/**
* Executes the function in the given execution frame.
*
* <p>Requires a non-null {@link CallerInfo} passed to it. The string argument containing code to
* evaluate is this function's second (non-this) argument.
*
* @param frame current execution frame
* @return
*/
@Override
public Stateful execute(VirtualFrame frame) {
CallerInfo callerInfo = Function.ArgumentsHelper.getCallerInfo(frame.getArguments());
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
String code = (String) Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1];
return evalNode.execute(callerInfo, state, code);
}
/**
* Returns a two argument function wrapping this node.
*
* @param language current language instance
* @return a function wrapping this node's logic
*/
public static Function makeFunction(Language language) {
return Function.fromBuiltinRootNodeWithCallerFrameAccess(
new DebugEvalNode(language),
FunctionSchema.CallStrategy.DIRECT_WHEN_TAIL,
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(1, "expression", ArgumentDefinition.ExecutionMode.EXECUTE));
}
}

View File

@ -6,10 +6,13 @@ 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.CallerInfo;
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.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful;
import org.enso.interpreter.runtime.type.TypesGen;
@ -45,7 +48,7 @@ public class CatchErrorNode extends BuiltinRootNode {
Object handler = arguments[1];
if (executionProfile.profile(TypesGen.isRuntimeError(scrutinee))) {
return invokeCallableNode.execute(
handler, state, new Object[] {TypesGen.asRuntimeError(scrutinee).getPayload()});
handler, frame, state, new Object[] {TypesGen.asRuntimeError(scrutinee).getPayload()});
} else {
return new Stateful(state, scrutinee);
}

View File

@ -40,7 +40,9 @@ public class RunStateNode extends BuiltinRootNode {
if (thunksProfile.profile(TypesGen.isThunk(maybeThunk))) {
return new Stateful(
state,
thunkExecutorNode.executeThunk(TypesGen.asThunk(maybeThunk), localState).getValue());
thunkExecutorNode
.executeThunk(TypesGen.asThunk(maybeThunk), localState)
.getValue());
} else {
return new Stateful(state, maybeThunk);
}

View File

@ -0,0 +1,95 @@
package org.enso.interpreter.node.expression.debug;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.Constants;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.argument.ThunkExecutorNode;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.Thunk;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.state.Stateful;
/** Node running Enso expressions passed to it as strings. */
@NodeInfo(description = "Evaluates code passed to it as string")
public abstract class EvalNode extends BaseNode {
/**
* Creates an instance of this node.
*
* @return an instance of this node
*/
public static EvalNode build() {
return EvalNodeGen.create();
}
/**
* Executes a string expression in the given execution context
*
* @param callerInfo captured information about the execution context
* @param state current value of the monadic state
* @param expression the string containing expression to evaluate
* @return the result of evaluating {@code expression} in the {@code callerInfo} context
*/
public abstract Stateful execute(CallerInfo callerInfo, Object state, String expression);
RootCallTarget parseExpression(LocalScope scope, ModuleScope moduleScope, String expression) {
LocalScope localScope = new LocalScope(scope);
Language language = lookupLanguageReference(Language.class).get();
ExpressionNode expr =
lookupContextReference(Language.class)
.get()
.compiler()
.runInline(expression, language, localScope, moduleScope);
ClosureRootNode framedNode =
new ClosureRootNode(
lookupLanguageReference(Language.class).get(),
localScope,
moduleScope,
expr,
null,
"<dynamic_eval>");
framedNode.setTail(isTail());
return Truffle.getRuntime().createCallTarget(framedNode);
}
@Specialization(
guards = {
"expression == cachedExpression",
"callerInfo.getLocalScope() == cachedCallerInfo.getLocalScope()",
"callerInfo.getModuleScope() == cachedCallerInfo.getModuleScope()",
},
limit = Constants.CacheSizes.EVAL_NODE)
Stateful doCached(
CallerInfo callerInfo,
Object state,
String expression,
@Cached("expression") String cachedExpression,
@Cached("callerInfo") CallerInfo cachedCallerInfo,
@Cached(
"parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expression)")
RootCallTarget cachedCallTarget,
@Cached("build(isTail())") ThunkExecutorNode thunkExecutorNode) {
Thunk thunk = new Thunk(cachedCallTarget, callerInfo.getFrame());
return thunkExecutorNode.executeThunk(thunk, state);
}
@Specialization
Stateful doUncached(
CallerInfo callerInfo,
Object state,
String expression,
@Cached("build(isTail())") ThunkExecutorNode thunkExecutorNode) {
RootCallTarget callTarget =
parseExpression(callerInfo.getLocalScope(), callerInfo.getModuleScope(), expression);
Thunk thunk = new Thunk(callTarget, callerInfo.getFrame());
return thunkExecutorNode.executeThunk(thunk, state);
}
}

View File

@ -2,10 +2,11 @@ package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
/** A representation of integer literals in Enso. */
@NodeInfo(shortName = "IntegerLiteral")
public final class IntegerLiteralNode extends LiteralNode {
public final class IntegerLiteralNode extends ExpressionNode {
private final long value;
/**

View File

@ -1,12 +0,0 @@
package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
/**
* A base class for all Enso literals.
*
* <p>It exists only to enable working with literal values as an aggregate.
*/
@NodeInfo(shortName = "Literal", description = "A representation of literal values.")
public abstract class LiteralNode extends ExpressionNode {}

View File

@ -0,0 +1,31 @@
package org.enso.interpreter.node.expression.literal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeInfo;
import org.enso.interpreter.node.ExpressionNode;
/** Node representing a constant String value. */
@NodeInfo(shortName = "StringLiteral", description = "Constant string literal expression")
public class StringLiteralNode extends ExpressionNode {
private final String value;
/**
* Creates a new instance of this node.
*
* @param value the literal value this node represents
*/
public StringLiteralNode(String value) {
this.value = value;
}
/**
* Returns the constant value of this string literal.
*
* @param frame the stack frame for execution
* @return the string value this node was created with
*/
@Override
public Object executeGeneric(VirtualFrame frame) {
return value;
}
}

View File

@ -2,6 +2,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.debug.DebugEvalNode;
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;
@ -38,6 +39,7 @@ public class Builtins {
AtomConstructor panic = new AtomConstructor("Panic", scope).initializeFields();
AtomConstructor error = new AtomConstructor("Error", scope).initializeFields();
AtomConstructor state = new AtomConstructor("State", scope).initializeFields();
AtomConstructor debug = new AtomConstructor("Debug", scope).initializeFields();
scope.registerConstructor(cons);
scope.registerConstructor(nil);
@ -46,6 +48,7 @@ public class Builtins {
scope.registerConstructor(panic);
scope.registerConstructor(error);
scope.registerConstructor(state);
scope.registerConstructor(debug);
scope.registerMethod(io, "println", PrintNode.makeFunction(language));
@ -59,6 +62,8 @@ public class Builtins {
scope.registerMethod(state, "get", GetStateNode.makeFunction(language));
scope.registerMethod(state, "put", PutStateNode.makeFunction(language));
scope.registerMethod(state, "run", RunStateNode.makeFunction(language));
scope.registerMethod(debug, "eval", DebugEvalNode.makeFunction(language));
}
/**

View File

@ -0,0 +1,54 @@
package org.enso.interpreter.runtime.callable;
import com.oracle.truffle.api.frame.MaterializedFrame;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/**
* Represents the caller execution context, to be passed to functions that declare the need for it.
*/
public class CallerInfo {
private final MaterializedFrame frame;
private final LocalScope localScope;
private final ModuleScope moduleScope;
/**
* Creates a new instance of caller information
*
* @param frame the caller's execution frame
* @param localScope the local scope caller uses
* @param moduleScope the module scope caller was defined in
*/
public CallerInfo(MaterializedFrame frame, LocalScope localScope, ModuleScope moduleScope) {
this.frame = frame;
this.localScope = localScope;
this.moduleScope = moduleScope;
}
/**
* Gets the caller's execution frame.
*
* @return the caller's execution frame
*/
public MaterializedFrame getFrame() {
return frame;
}
/**
* Gets the caller's local scope metadata.
*
* @return the caller's local scope metadata
*/
public LocalScope getLocalScope() {
return localScope;
}
/**
* Gets the caller's module scope.
*
* @return the caller's module scope.
*/
public ModuleScope getModuleScope() {
return moduleScope;
}
}

View File

@ -73,4 +73,9 @@ public class UnresolvedSymbol implements TruffleObject {
public Function resolveForError() {
return scope.lookupMethodDefinitionForAny(name).orElse(null);
}
@Override
public String toString() {
return "UnresolvedSymbol<" + this.name + ">";
}
}

View File

@ -77,8 +77,8 @@ public class CallArgumentInfo {
private CallArgumentInfo[] existingOversaturatedArgs;
private boolean[] argumentUsed;
private boolean[] callSiteArgApplied;
private FunctionSchema originalSchema;
private int oversaturatedWritePosition = 0;
private FunctionSchema.CallStrategy callStrategy;
/**
* Creates an unitialised object of this class. This instance is not safe for external use and
@ -97,7 +97,7 @@ public class CallArgumentInfo {
this.definitions = schema.getArgumentInfos();
this.argumentUsed = schema.cloneHasPreApplied();
this.existingOversaturatedArgs = schema.cloneOversaturatedArgs();
this.callStrategy = schema.getCallStrategy();
this.originalSchema = schema;
}
/**
@ -214,7 +214,12 @@ public class CallArgumentInfo {
this.existingOversaturatedArgs.length,
newOversaturatedArgInfo.length);
return new FunctionSchema(callStrategy, definitions, argumentUsed, oversaturatedArgInfo);
return new FunctionSchema(
originalSchema.getCallStrategy(),
originalSchema.getCallerFrameAccess(),
definitions,
argumentUsed,
oversaturatedArgInfo);
}
}

View File

@ -3,15 +3,15 @@ package org.enso.interpreter.runtime.callable.atom;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.interop.TruffleObject;
import org.enso.interpreter.node.ClosureRootNode;
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.FunctionSchema;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
import org.enso.interpreter.runtime.scope.LocalScope;
import org.enso.interpreter.runtime.scope.ModuleScope;
/** A representation of an Atom constructor. */
@ -66,7 +66,12 @@ public class AtomConstructor implements TruffleObject {
ExpressionNode instantiateNode = new InstantiateNode(this, argumentReaders);
ClosureRootNode rootNode =
new ClosureRootNode(
null, new FrameDescriptor(), instantiateNode, null, "<constructor>:" + name);
null,
new LocalScope(),
new ModuleScope(),
instantiateNode,
null,
"<constructor>:" + name);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
return new Function(
callTarget, null, new FunctionSchema(FunctionSchema.CallStrategy.ALWAYS_DIRECT, args));

View File

@ -21,6 +21,7 @@ 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.CallerInfo;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.argument.Thunk;
@ -43,6 +44,9 @@ public final class Function implements TruffleObject {
* @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.
* @param oversaturatedArguments the oversaturated arguments this function may have accumulated.
* The layout of this array must be conforming to the {@code schema}. @{code null} is allowed
* if the function does not carry any oversaturated arguments.
*/
public Function(
RootCallTarget callTarget,
@ -69,7 +73,7 @@ public final class Function implements TruffleObject {
}
/**
* Creates a Function object from a {@link RootNode} and argument definitions.
* Creates a Function object from a {@link BuiltinRootNode} and argument definitions.
*
* @param node the {@link RootNode} for the function logic
* @param callStrategy the {@link FunctionSchema.CallStrategy} to use for this function
@ -83,6 +87,25 @@ public final class Function implements TruffleObject {
return new Function(callTarget, null, schema);
}
/**
* Creates a Function object from a {@link BuiltinRootNode} and argument definitions.
*
* <p>The root node wrapped using this method can safely assume the {@link CallerInfo} argument
* will be non-null.
*
* @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 fromBuiltinRootNodeWithCallerFrameAccess(
BuiltinRootNode node, FunctionSchema.CallStrategy callStrategy, ArgumentDefinition... args) {
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
FunctionSchema schema =
new FunctionSchema(callStrategy, FunctionSchema.CallerFrameAccess.FULL, args);
return new Function(callTarget, null, schema);
}
/**
* Gets the target containing the function's code.
*
@ -194,7 +217,9 @@ public final class Function implements TruffleObject {
@CachedContext(Language.class) Context context,
@Cached(value = "arguments.length") int cachedArgsLength,
@Cached(value = "buildSorter(cachedArgsLength)") ArgumentSorterNode sorterNode) {
return sorterNode.execute(function, context.getUnit().newInstance(), arguments).getValue();
return sorterNode
.execute(function, null, context.getUnit().newInstance(), arguments)
.getValue();
}
/**
@ -234,8 +259,8 @@ public final class Function implements TruffleObject {
* @return an array containing the necessary information to call an Enso function
*/
public static Object[] buildArguments(
Function function, Object state, Object[] positionalArguments) {
return new Object[] {function.getScope(), state, positionalArguments};
Function function, CallerInfo callerInfo, Object state, Object[] positionalArguments) {
return new Object[] {function.getScope(), callerInfo, state, positionalArguments};
}
/**
@ -246,36 +271,51 @@ public final class Function implements TruffleObject {
* @return an array containing the necessary information to call an Enso thunk
*/
public static Object[] buildArguments(Thunk thunk, Object state) {
return new Object[] {thunk.getScope(), state, new Object[0]};
return new Object[] {thunk.getScope(), null, state, new Object[0]};
}
/**
* Gets the positional arguments out of the array.
*
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object,
* Object[])}
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
* CallerInfo, Object, Object[])}
* @return the positional arguments to the function
*/
public static Object[] getPositionalArguments(Object[] arguments) {
return (Object[]) arguments[2];
return (Object[]) arguments[3];
}
/**
* Gets the state out of the array.
*
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object,
* Object[])}
* @param arguments an array produced by {@link
* ArgumentsHelper#buildArguments(Function,CallerInfo, Object, Object[])}
* @return the state for the function
*/
public static Object getState(Object[] arguments) {
return arguments[1];
return arguments[2];
}
/**
* Gets the caller info out of the array.
*
* <p>Any function using this method should declare {@link
* FunctionSchema.CallerFrameAccess#FULL} in its schema for the result to be guaranteed
* non-null.
*
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
* CallerInfo, Object, Object[])}
* @return the caller info for the function
*/
public static CallerInfo getCallerInfo(Object[] arguments) {
return (CallerInfo) arguments[1];
}
/**
* Gets the function's local scope out of the array.
*
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function, Object,
* Object[])}
* @param arguments an array produced by {@link ArgumentsHelper#buildArguments(Function,
* CallerInfo, Object, Object[])}
* @return the local scope for the associated function
*/
public static MaterializedFrame getLocalScope(Object[] arguments) {

View File

@ -39,17 +39,38 @@ public class FunctionSchema {
}
}
/** Denotes the caller frame access functions with this schema require to run properly. */
public enum CallerFrameAccess {
/** Requires full access to the (materialized) caller frame. */
FULL,
/** Does not use the caller frame at all. */
NONE;
/**
* Is there any level of caller frame access required by the function?
*
* @return {@code true} if the function must be passed the caller frame, {@code false}
* otherwise.
*/
public boolean shouldFrameBePassed() {
return this != NONE;
}
}
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;
private final CallerFrameAccess callerFrameAccess;
/**
* Creates an {@link FunctionSchema} instance.
*
* @param callStrategy the call strategy to use for functions having this schema
* @param callerFrameAccess the declaration of whether access to caller frame is required for this
* function
* @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}
@ -58,6 +79,7 @@ public class FunctionSchema {
*/
public FunctionSchema(
CallStrategy callStrategy,
CallerFrameAccess callerFrameAccess,
ArgumentDefinition[] argumentInfos,
boolean[] hasPreApplied,
CallArgumentInfo[] oversaturatedArguments) {
@ -65,8 +87,8 @@ public class FunctionSchema {
this.argumentInfos = argumentInfos;
this.oversaturatedArguments = oversaturatedArguments;
this.hasPreApplied = hasPreApplied;
this.callerFrameAccess = callerFrameAccess;
boolean hasAnyPreApplied = false;
for (boolean b : hasPreApplied) {
if (b) {
hasAnyPreApplied = true;
@ -82,10 +104,33 @@ public class FunctionSchema {
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied
* arguments.
*
* @param callStrategy the call strategy to use for this function
* @param callerFrameAccess the declaration of need to access the caller frame from the function
* @param argumentInfos Definition site arguments information
*/
public FunctionSchema(
CallStrategy callStrategy,
CallerFrameAccess callerFrameAccess,
ArgumentDefinition... argumentInfos) {
this(
callStrategy,
callerFrameAccess,
argumentInfos,
new boolean[argumentInfos.length],
new CallArgumentInfo[0]);
}
/**
* Creates an {@link FunctionSchema} instance assuming the function has no partially applied
* arguments.
*
* <p>Caller frame access is assumed to be {@link CallerFrameAccess#NONE}.
*
* @param callStrategy the call strategy to use for this function
* @param argumentInfos Definition site arguments information
*/
public FunctionSchema(CallStrategy callStrategy, ArgumentDefinition... argumentInfos) {
this(callStrategy, argumentInfos, new boolean[argumentInfos.length], new CallArgumentInfo[0]);
this(callStrategy, CallerFrameAccess.NONE, argumentInfos);
}
/**
@ -183,4 +228,13 @@ public class FunctionSchema {
public CallStrategy getCallStrategy() {
return callStrategy;
}
/**
* Returns the caller frame access declaration for this function.
*
* @return the caller frame access declaration
*/
public CallerFrameAccess getCallerFrameAccess() {
return callerFrameAccess;
}
}

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.runtime.control;
import com.oracle.truffle.api.nodes.ControlFlowException;
import org.enso.interpreter.runtime.callable.CallerInfo;
import org.enso.interpreter.runtime.callable.function.Function;
/**
@ -10,6 +11,7 @@ import org.enso.interpreter.runtime.callable.function.Function;
*/
public class TailCallException extends ControlFlowException {
private final Function function;
private final CallerInfo callerInfo;
private final Object state;
private final Object[] arguments;
@ -20,8 +22,10 @@ public class TailCallException extends ControlFlowException {
* @param state the state to pass to the function
* @param arguments the arguments to {@code function}
*/
public TailCallException(Function function, Object state, Object[] arguments) {
public TailCallException(
Function function, CallerInfo callerInfo, Object state, Object[] arguments) {
this.function = function;
this.callerInfo = callerInfo;
this.arguments = arguments;
this.state = state;
}
@ -45,11 +49,20 @@ public class TailCallException extends ControlFlowException {
}
/**
* Gets the state to pass to the function
* Gets the state to pass to the function.
*
* @return the state to pass for next call
*/
public Object getState() {
return state;
}
/**
* Gets the caller info to pass to the function.
*
* @return the state to pass for next call
*/
public CallerInfo getCallerInfo() {
return callerInfo;
}
}

View File

@ -14,24 +14,23 @@ import java.util.Optional;
* frames.
*/
public class LocalScope {
private Map<String, FrameSlot> items;
private FrameDescriptor frameDescriptor;
private LocalScope parent;
public final Map<String, FrameSlot> items;
private final FrameDescriptor frameDescriptor;
public final LocalScope parent;
/** Creates a new local scope with defaulted arguments. */
/** Creates a root local scope. */
public LocalScope() {
items = new HashMap<>();
frameDescriptor = new FrameDescriptor();
parent = null;
this(null);
}
/**
* Creates a new local scope with a known parent.
* Creates a child local scope with a given parent.
*
* @param parent the parent scope
*/
public LocalScope(LocalScope parent) {
this();
items = new HashMap<>();
frameDescriptor = new FrameDescriptor();
this.parent = parent;
}

View File

@ -5,14 +5,17 @@ import com.oracle.truffle.api.source.Source
import org.enso.compiler.generate.AstToIr
import org.enso.compiler.ir.IR
import org.enso.flexer.Reader
import org.enso.interpreter.AstExpression
import org.enso.interpreter.Constants
import org.enso.interpreter.EnsoParser
import org.enso.interpreter.Language
import org.enso.interpreter.builder.ExpressionFactory
import org.enso.interpreter.builder.ModuleScopeExpressionFactory
import org.enso.interpreter.node.ExpressionNode
import org.enso.interpreter.runtime.Context
import org.enso.interpreter.runtime.Module
import org.enso.interpreter.runtime.error.ModuleDoesNotExistException
import org.enso.interpreter.runtime.scope.LocalScope
import org.enso.interpreter.runtime.scope.ModuleScope
import org.enso.syntax.text.AST
import org.enso.syntax.text.Parser
@ -88,6 +91,27 @@ class Compiler(
run(Source.newBuilder(Constants.LANGUAGE_ID, file).build)
}
/**
* Processes the language source, interpreting it as an expression.
* Processes the source in the context of given local and module scopes.
*
* @param source string representing the expression to process
* @param language current language instance
* @param localScope local scope to process the source in
* @param moduleScope module scope to process the source in
* @return an expression node representing the parsed and analyzed source
*/
def runInline(
source: String,
language: Language,
localScope: LocalScope,
moduleScope: ModuleScope
): ExpressionNode = {
val parsed = parseInline(source)
new ExpressionFactory(language, localScope, "<inline_source>", moduleScope)
.run(parsed)
}
/**
* Finds and processes a language source by its qualified name.
*
@ -119,6 +143,17 @@ class Compiler(
resolvedAST
}
/**
* Parses the provided language source expression in inline mode.
*
* @param source the code to parse
* @return an AST representation of `source`
*/
def parseInline(source: String): AstExpression = {
val parsed = new EnsoParser().parseEnsoInline(source)
parsed
}
/**
* Lowers the input AST to the compiler's high-level intermediate
* representation.

View File

@ -2,6 +2,8 @@ package org.enso.interpreter
import java.util.Optional
import org.apache.commons.lang3.StringEscapeUtils
import scala.collection.JavaConverters._
import scala.language.postfixOps
import scala.util.parsing.combinator._
@ -42,6 +44,8 @@ trait AstExpressionVisitor[+T] {
): T
def visitDesuspend(target: AstExpression): T
def visitStringLiteral(string: String): T
}
trait AstModuleScopeVisitor[+T] {
@ -144,6 +148,11 @@ case class AstLong(l: Long) extends AstExpression {
visitor.visitLong(l)
}
case class AstStringLiteral(string: String) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitStringLiteral(string)
}
case class AstArithOp(op: String, left: AstExpression, right: AstExpression)
extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T =
@ -261,6 +270,12 @@ class EnsoParserInternal extends JavaTokenParsers {
def foreignLiteral: Parser[String] = "**" ~> "[^\\*]*".r <~ "**"
def string: Parser[AstStringLiteral] = stringLiteral ^^ { lit =>
AstStringLiteral(
StringEscapeUtils.unescapeJava(lit.substring(1, lit.length - 1))
)
}
def variable: Parser[AstVariable] = ident ^^ AstVariable
def operand: Parser[AstExpression] =
@ -273,7 +288,7 @@ class EnsoParserInternal extends JavaTokenParsers {
}
def expression: Parser[AstExpression] =
desuspend | matchClause | arith | function
desuspend | matchClause | arith | function | string
def functionCall: Parser[AstApply] =
"@" ~> expression ~ (argList ?) ~ defaultSuspend ^^ {
@ -351,6 +366,10 @@ class EnsoParserInternal extends JavaTokenParsers {
def parse(code: String): AstExpression = {
parseAll(expression | function, code).get
}
def parseLine(code: String): AstExpression = {
parseAll(statement, code).get
}
}
class EnsoParser {
@ -358,4 +377,8 @@ class EnsoParser {
def parseEnso(code: String): AstModuleScope = {
new EnsoParserInternal().parseGlobalScope(code)
}
def parseEnsoInline(code: String): AstExpression = {
new EnsoParserInternal().parseLine(code)
}
}

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test
import org.graalvm.polyglot.PolyglotException
import org.graalvm.polyglot.Value
case class InterpreterException(
@transient polyglotException: PolyglotException

View File

@ -3,10 +3,8 @@ package org.enso.interpreter.test
import java.io.ByteArrayOutputStream
import org.enso.interpreter.Constants
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Value
import org.scalatest.FlatSpec
import org.scalatest.Matchers
import org.graalvm.polyglot.{Context, Value}
import org.scalatest.{FlatSpec, Matchers}
trait InterpreterRunner {
implicit class RichValue(value: Value) {

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterException
import org.enso.interpreter.test.InterpreterTest
import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
class ErrorsTest extends InterpreterTest {
"Panics" should "be thrown and stop evaluation" in {

View File

@ -0,0 +1,85 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterTest
class EvalTest extends InterpreterTest {
"Debug.eval" should "evaluate a string expression" in {
val code =
"""
|@{
| @eval [@Debug, "@println[@IO, \"foo\"]"]
|}
|""".stripMargin
eval(code)
consumeOut shouldEqual List("foo")
}
"Debug.eval" should "have access to the caller scope" in {
val code =
"""
|@{
| x = "Hello World!";
| @eval [@Debug, "@println[@IO, x]"]
|}
|""".stripMargin
eval(code)
consumeOut shouldEqual List("Hello World!")
}
"Debug.eval" should "have access to the caller module scope" in {
val code =
"""
|type MyType x;
|
|@{
| x = 10;
| @eval [@Debug, "@println[@IO, @MyType[x]]"]
|}
|""".stripMargin
eval(code)
consumeOut shouldEqual List("MyType<10>")
}
"Debug.eval" should "return a value usable in the caller scope" in {
val code =
"""
|@{
| x = 1;
| y = 2;
| res = @eval [@Debug, "x + y"];
| res + 1
|}
|""".stripMargin
eval(code) shouldEqual 4
}
"Debug.eval" should "work in a recursive setting" in {
val code =
"""
|{ |sumTo|
| summator = { |acc, current|
| @eval [@Debug, "@ifZero [current, acc, @summator [acc + current, current - 1]]"]
| };
| res = @summator [0, sumTo];
| res
|}
|""".stripMargin
val fun = eval(code)
fun.call(100) shouldEqual 5050
}
"Debug.eval" should "work inside a thunk passed to another function" in {
val code =
"""
|{ |sumTo|
| summator = { |acc, current|
| @ifZero [current, acc, @eval [@Debug, "@summator [acc + current, current - 1]"]]
| };
| res = @summator [0, sumTo];
| res
|}
|""".stripMargin
val fun = eval(code)
fun.call(100) shouldEqual 5050
}
}

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterException
import org.enso.interpreter.test.InterpreterTest
import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
class GlobalScopeTest extends InterpreterTest {

View File

@ -1,7 +1,6 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
import org.graalvm.polyglot.PolyglotException
class NamedArgumentsTest extends InterpreterTest {
"Functions" should "take arguments by name and use them in their bodies" in {

View File

@ -4,14 +4,10 @@ import java.io.File
import org.enso.interpreter.Constants
import org.enso.interpreter.runtime.RuntimeOptions
import org.enso.interpreter.test.InterpreterException
import org.enso.interpreter.test.ValueEquality
import org.enso.interpreter.test.{InterpreterException, ValueEquality}
import org.enso.pkg.Package
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Source
import org.graalvm.polyglot.Value
import org.scalatest.FlatSpec
import org.scalatest.Matchers
import org.graalvm.polyglot.{Context, Source, Value}
import org.scalatest.{FlatSpec, Matchers}
trait PackageTest extends FlatSpec with Matchers with ValueEquality {

View File

@ -0,0 +1,15 @@
package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterTest
class StringTest extends InterpreterTest {
"Strings" should "exist in the language and be printable" in {
val code =
"""
|@println [@IO, "hello world!"]
|""".stripMargin
noException shouldBe thrownBy(eval(code))
consumeOut shouldEqual List("hello world!")
}
}