mirror of
https://github.com/enso-org/enso.git
synced 2024-11-22 22:10:15 +03:00
Test Framework for Enso (#998)
This commit is contained in:
parent
f068509b2e
commit
40f44b5b9e
@ -24,10 +24,10 @@ Number.times = act ->
|
||||
res = here.reverse_list (go Nil this)
|
||||
res
|
||||
|
||||
measure = act -> label -> iter_size -> num_iters ->
|
||||
single_call = input ->
|
||||
measure = ~act -> label -> iter_size -> num_iters ->
|
||||
single_call = _ ->
|
||||
x1 = System.nano_time
|
||||
Runtime.no_inline (act input)
|
||||
Runtime.no_inline act
|
||||
x2 = System.nano_time
|
||||
x2 - x1
|
||||
iteration = it_num ->
|
||||
|
97
distribution/std-lib/Base/src/List.enso
Normal file
97
distribution/std-lib/Base/src/List.enso
Normal file
@ -0,0 +1,97 @@
|
||||
## PRIVATE
|
||||
A helper for the `map` function.
|
||||
|
||||
Uses unsafe field mutation under the hood, to rewrite `map` in
|
||||
a tail-recursive manner. The mutation is purely internal and does not leak
|
||||
to the user-facing API.
|
||||
map_helper list cons f = case list of
|
||||
Cons h t ->
|
||||
res = Cons (f h) Nil
|
||||
Unsafe.set_atom_field cons 1 res
|
||||
here.map_helper t res f
|
||||
Nil -> Unsafe.set_atom_field cons 1 Nil
|
||||
|
||||
## The basic cons-list type.
|
||||
|
||||
A cons-list allows to store an arbitrary number of elements.
|
||||
Prepending to the list can be achieved by using the `Cons` constructor,
|
||||
while an empty list is represented by `Nil`.
|
||||
> Example
|
||||
A list containing the elements `1`, `2`, and `3`, in this order is:
|
||||
Cons 1 (Cons 2 (Cons 3 Nil))
|
||||
type List
|
||||
Nil
|
||||
Cons
|
||||
|
||||
## Applies a function to each element of the list, returning the list of
|
||||
results.
|
||||
|
||||
> Example
|
||||
In the following example, we add `1` to each element of the list:
|
||||
(Cons 0 <| Cons 1 <| Cons 2 <| Nil) . map +1
|
||||
The result of running the code above is:
|
||||
Cons 1 <| Cons 2 <| Cons 3 <| Nil
|
||||
map : (Any -> Any) -> List
|
||||
map f = case this of
|
||||
Nil -> Nil
|
||||
Cons h t ->
|
||||
res = Cons (f h) Nil
|
||||
here.map_helper t res f
|
||||
res
|
||||
|
||||
## Applies a function to each element of the list.
|
||||
|
||||
Unlike `map`, this method does not return the individual results,
|
||||
therefore it is only useful for side-effecting computations.
|
||||
|
||||
> Example
|
||||
In the following example, we're printing each element of the list
|
||||
to the standard output:
|
||||
(Cons 0 <| Cons 1 <| Cons 2 <| Nil) . each IO.println
|
||||
each : (Any -> Any) -> Unit
|
||||
each f = case this of
|
||||
Nil -> Unit
|
||||
Cons h t ->
|
||||
f h
|
||||
t.each f
|
||||
|
||||
## Combines all the elements of the list, by iteratively applying the
|
||||
passed function with next elements of the list.
|
||||
|
||||
In general, the result of
|
||||
(Cons l0 <| Cons l1 <| ... <| Cons ln) . fold init f
|
||||
is the same as
|
||||
f (...(f (f init l0) l1)...) ln
|
||||
|
||||
> Example
|
||||
In the following example, we'll compute the sum of all elements of a
|
||||
list:
|
||||
(Cons 0 <| Cons 1 <| Cons 2 <| Nil) . fold 0 (+)
|
||||
fold : Any -> (Any -> Any -> Any) -> Any
|
||||
fold init f = case this of
|
||||
Nil -> init
|
||||
Cons h t -> t.fold (f init h) f
|
||||
|
||||
## Reverses the list, returning a list with the same elements, but in the
|
||||
opposite order.
|
||||
reverse : List
|
||||
reverse = this.fold Nil (l -> el -> Cons el l)
|
||||
|
||||
## Computes the number of elements in the list.
|
||||
length : Number
|
||||
length = this.fold 0 (acc -> _ -> acc + 1)
|
||||
|
||||
## Checks whether any element of the list matches the given predicate.
|
||||
|
||||
A predicate is a function that takes a list element and returns
|
||||
a Boolean value.
|
||||
|
||||
> Example
|
||||
In the following example, we'll check if any element of the list is
|
||||
larger than `1`:
|
||||
(Cons 0 <| Cons 1 <| Cons 2 <| Nil) . any (> 5)
|
||||
any : (Any -> Boolean) -> Boolean
|
||||
any predicate = case this of
|
||||
Nil -> False
|
||||
Cons h t -> if predicate h then True else t.any predicate
|
||||
|
111
distribution/std-lib/Base/src/Test.enso
Normal file
111
distribution/std-lib/Base/src/Test.enso
Normal file
@ -0,0 +1,111 @@
|
||||
import Base.List
|
||||
|
||||
## The top-level entry point for a test suite.
|
||||
type Suite specs
|
||||
|
||||
## PRIVATE
|
||||
type Spec name behaviors
|
||||
|
||||
## PRIVATE
|
||||
type Behavior name result
|
||||
|
||||
## PRIVATE
|
||||
Behavior.is_fail = this.result.is_fail
|
||||
|
||||
## PRIVATE
|
||||
Spec.is_fail = this.behaviors.any is_fail
|
||||
|
||||
## PRIVATE
|
||||
Suite.is_fail = this.specs.any is_fail
|
||||
|
||||
## PRIVATE
|
||||
type Assertion
|
||||
type Success
|
||||
type Fail message
|
||||
|
||||
is_fail = case this of
|
||||
Success -> False
|
||||
Fail _ -> True
|
||||
|
||||
## Asserts that `this` value is equal to the expected value.
|
||||
Any.should_equal that = case this == that of
|
||||
True -> Success
|
||||
False ->
|
||||
msg = this.to_text + " did not equal " + that.to_text + "."
|
||||
Panic.throw (Fail msg)
|
||||
|
||||
## Asserts that the given `Boolean` is `True`
|
||||
Boolean.should_be_true = case this of
|
||||
True -> Success
|
||||
False -> Panic.throw (Fail "Expected False to be True.")
|
||||
|
||||
## Asserts that the given `Boolean` is `False`
|
||||
Boolean.should_be_false = case this of
|
||||
True -> Panic.throw (Fail "Expected True to be False.")
|
||||
False -> Success
|
||||
|
||||
## PRIVATE
|
||||
Spec.print_report =
|
||||
IO.print_err (this.name + ":")
|
||||
this.behaviors.reverse.each behavior->
|
||||
case behavior.result of
|
||||
Success ->
|
||||
IO.print_err (" - " + behavior.name)
|
||||
Fail msg ->
|
||||
IO.print_err (" - [FAILED] " + behavior.name)
|
||||
IO.print_err (" Reason: " + msg)
|
||||
|
||||
## Creates a new test group, desribing properties of the object
|
||||
described by `this`.
|
||||
|
||||
> Example
|
||||
Suite.run <|
|
||||
describe "Number" <|
|
||||
it "should define addition" <|
|
||||
2+3 . should_equal 5
|
||||
it "should define multiplication" <|
|
||||
2*3 . should_equal 6
|
||||
Text.describe ~behaviors =
|
||||
r = State.run Spec (Spec this Nil) <|
|
||||
behaviors
|
||||
State.get Spec
|
||||
r.print_report
|
||||
suite = State.get Suite
|
||||
new_suite = Suite (Cons r suite.specs)
|
||||
State.put Suite new_suite
|
||||
|
||||
## Specifies a single behavior, described by `this`.
|
||||
|
||||
> Example
|
||||
Suite.run <|
|
||||
describe "Number" <|
|
||||
it "should define addition" <|
|
||||
2+3 . should_equal 5
|
||||
it "should define multiplication" <|
|
||||
2*3 . should_equal 6
|
||||
Text.it ~behavior =
|
||||
spec = State.get Spec
|
||||
maybeExc = case Panic.recover behavior of
|
||||
_ -> Success
|
||||
result = maybeExc.catch ex->
|
||||
case ex of
|
||||
Fail _ -> ex
|
||||
_ -> Fail ("Unexpected error has been thrown: " + ex.to_text)
|
||||
new_spec = Spec spec.name (Cons (Behavior this result) spec.behaviors)
|
||||
State.put Spec new_spec
|
||||
|
||||
## Runs a suite of tests, consisting of multiple `describe` blocks.
|
||||
|
||||
> Example
|
||||
Suite.run <|
|
||||
describe "Number" <|
|
||||
it "should define addition" <|
|
||||
2+3 . should_equal 5
|
||||
it "should define multiplication" <|
|
||||
2*3 . should_equal 6
|
||||
Suite.run ~specs =
|
||||
r = State.run Suite (Suite Nil) <|
|
||||
specs
|
||||
State.get Suite
|
||||
code = if r.is_fail then 1 else 0
|
||||
System.exit code
|
@ -0,0 +1,38 @@
|
||||
package org.enso.interpreter.node.expression.atom;
|
||||
|
||||
import com.oracle.truffle.api.TruffleLanguage;
|
||||
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.runtime.callable.atom.Atom;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@NodeInfo(shortName = "get_field", description = "A base for auto-generated Atom getters.")
|
||||
public class GetFieldNode extends RootNode {
|
||||
private final int index;
|
||||
|
||||
/**
|
||||
* Creates a new instance of this node.
|
||||
*
|
||||
* @param language the current language instance.
|
||||
* @param index the index this node should use for field lookup.
|
||||
*/
|
||||
public GetFieldNode(TruffleLanguage<?> language, int index) {
|
||||
super(language);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the node, by taking the first argument from the frame and plucking the proper field
|
||||
* from it.
|
||||
*
|
||||
* @param frame current execution frame
|
||||
* @return the field value at predefined index
|
||||
*/
|
||||
public Stateful execute(VirtualFrame frame) {
|
||||
Atom atom = (Atom) Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[0];
|
||||
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
|
||||
return new Stateful(state, atom.getFields()[index]);
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package org.enso.interpreter.node.expression.builtin.function;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.dsl.MonadicState;
|
||||
import org.enso.interpreter.node.callable.InvokeCallableNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||
import org.enso.interpreter.runtime.callable.argument.Thunk;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.state.Stateful;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Function",
|
||||
name = "<|",
|
||||
description = "Takes a function and an argument and applies the function to the argument.",
|
||||
alwaysDirect = false)
|
||||
public class ApplicationOperator extends Node {
|
||||
private @Child InvokeCallableNode invokeCallableNode;
|
||||
|
||||
ApplicationOperator() {
|
||||
invokeCallableNode =
|
||||
InvokeCallableNode.build(
|
||||
new CallArgumentInfo[] {new CallArgumentInfo()},
|
||||
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
|
||||
InvokeCallableNode.ArgumentsExecutionMode.EXECUTE);
|
||||
invokeCallableNode.markTail();
|
||||
}
|
||||
|
||||
Stateful execute(VirtualFrame frame, @MonadicState Object state, Function _this, Thunk argument) {
|
||||
return invokeCallableNode.execute(_this, frame, state, new Object[] {argument});
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.enso.interpreter.node.expression.builtin.runtime;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
|
||||
@BuiltinMethod(type="Runtime", name="gc", description = "Forces garbage collection")
|
||||
public class GCNode extends Node {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
Object execute(Object _this) {
|
||||
System.gc();
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package org.enso.interpreter.node.expression.builtin.system;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "System",
|
||||
name = "exit",
|
||||
description = "Exits the process, returning the provided code.")
|
||||
public class ExitNode extends Node {
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
Object execute(Object _this, long code) {
|
||||
System.exit((int) code);
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package org.enso.interpreter.node.expression.builtin.unsafe;
|
||||
|
||||
import com.oracle.truffle.api.nodes.Node;
|
||||
import org.enso.interpreter.dsl.BuiltinMethod;
|
||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||
|
||||
@BuiltinMethod(
|
||||
type = "Unsafe",
|
||||
name = "set_atom_field",
|
||||
description = "Unsafely, in place, sets the value of an atom field by index.")
|
||||
public class SetAtomFieldNode extends Node {
|
||||
Atom execute(Object _this, Atom atom, long index, Object value) {
|
||||
atom.getFields()[(int) index] = value;
|
||||
return atom;
|
||||
}
|
||||
}
|
@ -6,18 +6,22 @@ import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.debug.DebugEvalMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.error.*;
|
||||
import org.enso.interpreter.node.expression.builtin.function.ApplicationOperatorMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.generic.*;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.syntax.MethodDispatchNode;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.syntax.ConstructorDispatchNode;
|
||||
import org.enso.interpreter.node.expression.builtin.io.*;
|
||||
import org.enso.interpreter.node.expression.builtin.number.*;
|
||||
import org.enso.interpreter.node.expression.builtin.runtime.GCMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.runtime.NoInlineMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.state.*;
|
||||
import org.enso.interpreter.node.expression.builtin.system.ExitMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.system.NanoTimeMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.interop.java.*;
|
||||
import org.enso.interpreter.node.expression.builtin.text.*;
|
||||
import org.enso.interpreter.node.expression.builtin.thread.WithInterruptHandlerMethodGen;
|
||||
import org.enso.interpreter.node.expression.builtin.unsafe.SetAtomFieldMethodGen;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
@ -78,7 +82,7 @@ public class Builtins {
|
||||
new AtomConstructor("Cons", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "head", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||
new ArgumentDefinition(1, "rest", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
new ArgumentDefinition(1, "tail", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||
AtomConstructor io = new AtomConstructor("IO", scope).initializeFields();
|
||||
AtomConstructor system = new AtomConstructor("System", scope).initializeFields();
|
||||
AtomConstructor runtime = new AtomConstructor("Runtime", scope).initializeFields();
|
||||
@ -89,6 +93,8 @@ public class Builtins {
|
||||
AtomConstructor java = new AtomConstructor("Java", scope).initializeFields();
|
||||
AtomConstructor thread = new AtomConstructor("Thread", scope).initializeFields();
|
||||
|
||||
AtomConstructor unsafe = new AtomConstructor("Unsafe", scope).initializeFields();
|
||||
|
||||
scope.registerConstructor(unit);
|
||||
scope.registerConstructor(any);
|
||||
scope.registerConstructor(number);
|
||||
@ -108,6 +114,8 @@ public class Builtins {
|
||||
scope.registerConstructor(java);
|
||||
scope.registerConstructor(thread);
|
||||
|
||||
scope.registerConstructor(unsafe);
|
||||
|
||||
createPolyglot(language);
|
||||
|
||||
scope.registerMethod(io, "println", PrintlnMethodGen.makeFunction(language));
|
||||
@ -115,7 +123,9 @@ public class Builtins {
|
||||
scope.registerMethod(io, "readln", ReadlnMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(system, "nano_time", NanoTimeMethodGen.makeFunction(language));
|
||||
scope.registerMethod(system, "exit", ExitMethodGen.makeFunction(language));
|
||||
scope.registerMethod(runtime, "no_inline", NoInlineMethodGen.makeFunction(language));
|
||||
scope.registerMethod(runtime, "gc", GCMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(panic, "throw", ThrowPanicMethodGen.makeFunction(language));
|
||||
scope.registerMethod(panic, "recover", CatchPanicMethodGen.makeFunction(language));
|
||||
@ -138,6 +148,7 @@ public class Builtins {
|
||||
scope.registerMethod(debug, "breakpoint", DebugBreakpointMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(function, "call", ExplicitCallFunctionMethodGen.makeFunction(language));
|
||||
scope.registerMethod(function, "<|", ApplicationOperatorMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(text, "+", ConcatMethodGen.makeFunction(language));
|
||||
scope.registerMethod(any, "to_text", AnyToTextMethodGen.makeFunction(language));
|
||||
@ -149,6 +160,8 @@ public class Builtins {
|
||||
scope.registerMethod(
|
||||
thread, "with_interrupt_handler", WithInterruptHandlerMethodGen.makeFunction(language));
|
||||
|
||||
scope.registerMethod(unsafe, "set_atom_field", SetAtomFieldMethodGen.makeFunction(language));
|
||||
|
||||
interopDispatchRoot = Truffle.getRuntime().createCallTarget(MethodDispatchNode.build(language));
|
||||
interopDispatchSchema =
|
||||
new FunctionSchema(
|
||||
|
@ -14,7 +14,7 @@ import java.util.stream.Collectors;
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
public class Atom implements TruffleObject {
|
||||
private final AtomConstructor constructor;
|
||||
private @CompilationFinal(dimensions = 1) Object[] fields;
|
||||
private final Object[] fields;
|
||||
|
||||
/**
|
||||
* Creates a new Atom for a given constructor.
|
||||
|
@ -11,6 +11,7 @@ import com.oracle.truffle.api.library.ExportMessage;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
|
||||
import org.enso.interpreter.node.expression.atom.GetFieldNode;
|
||||
import org.enso.interpreter.node.expression.atom.InstantiateNode;
|
||||
import org.enso.interpreter.node.expression.builtin.InstantiateAtomNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
@ -18,6 +19,9 @@ import org.enso.interpreter.runtime.callable.function.Function;
|
||||
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** A representation of an Atom constructor. */
|
||||
@ExportLibrary(InteropLibrary.class)
|
||||
public class AtomConstructor implements TruffleObject {
|
||||
@ -48,6 +52,7 @@ public class AtomConstructor implements TruffleObject {
|
||||
public AtomConstructor initializeFields(ArgumentDefinition... args) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
this.constructorFunction = buildConstructorFunction(args);
|
||||
generateMethods(args);
|
||||
if (args.length == 0) {
|
||||
cachedInstance = new Atom(this);
|
||||
} else {
|
||||
@ -75,6 +80,23 @@ public class AtomConstructor implements TruffleObject {
|
||||
callTarget, null, new FunctionSchema(FunctionSchema.CallStrategy.ALWAYS_DIRECT, args));
|
||||
}
|
||||
|
||||
private void generateMethods(ArgumentDefinition[] args) {
|
||||
for (ArgumentDefinition arg : args) {
|
||||
definitionScope.registerMethod(this, arg.getName(), generateGetter(arg.getPosition()));
|
||||
}
|
||||
}
|
||||
|
||||
private Function generateGetter(int position) {
|
||||
GetFieldNode node = new GetFieldNode(null, position);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
return new Function(
|
||||
callTarget,
|
||||
null,
|
||||
new FunctionSchema(
|
||||
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the constructor.
|
||||
*
|
||||
|
@ -89,8 +89,8 @@ case object DemandAnalysis extends IRPass {
|
||||
case block @ IR.Expression.Block(expressions, retVal, _, _, _, _) =>
|
||||
block.copy(
|
||||
expressions =
|
||||
expressions.map(x => analyseExpression(x, isInsideCallArgument)),
|
||||
returnValue = analyseExpression(retVal, isInsideCallArgument)
|
||||
expressions.map(x => analyseExpression(x, isInsideCallArgument = false)),
|
||||
returnValue = analyseExpression(retVal, isInsideCallArgument = false)
|
||||
)
|
||||
case binding @ IR.Expression.Binding(_, expression, _, _, _) =>
|
||||
binding.copy(expression =
|
||||
|
@ -61,18 +61,24 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
*
|
||||
* @return a new inline context
|
||||
*/
|
||||
def mkContext: InlineContext = {
|
||||
def mkInlineContext: InlineContext = {
|
||||
InlineContext(
|
||||
localScope = Some(LocalScope.root),
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
}
|
||||
|
||||
def mkModuleContext: ModuleContext = {
|
||||
ModuleContext(
|
||||
freshNameSupply = Some(new FreshNameSupply)
|
||||
)
|
||||
}
|
||||
|
||||
// === The Tests ============================================================
|
||||
|
||||
"Suspended arguments" should {
|
||||
"be forced when assigned" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -95,7 +101,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"work correctly when deeply nested" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -111,7 +117,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"not be forced when passed to functions" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -134,7 +140,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"be forced when used in vector literals" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -153,7 +159,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"be marked as not to suspend during codegen when passed to a function" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -177,7 +183,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"Non-suspended arguments" should {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""x -> y ->
|
||||
@ -225,7 +231,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
|
||||
"Suspended blocks" should {
|
||||
"be forced when used" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -250,7 +256,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"not be forced when passed to a function" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -272,7 +278,7 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
}
|
||||
|
||||
"be marked as not to suspend during codegen when passed to a function" in {
|
||||
implicit val ctx: InlineContext = mkContext
|
||||
implicit val ctx: InlineContext = mkInlineContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
@ -292,5 +298,32 @@ class DemandAnalysisTest extends CompilerTest {
|
||||
.asInstanceOf[IR.CallArgument.Specified]
|
||||
.shouldBeSuspended shouldEqual Some(false)
|
||||
}
|
||||
|
||||
"force terms in blocks passed directly as arguments" in {
|
||||
implicit val ctx: ModuleContext = mkModuleContext
|
||||
|
||||
val ir =
|
||||
"""
|
||||
|bar ~x = foo <|
|
||||
| x
|
||||
|""".stripMargin.preprocessModule.analyse
|
||||
|
||||
val barFunc = ir.bindings.head
|
||||
.asInstanceOf[IR.Module.Scope.Definition.Method.Explicit]
|
||||
val oprCall = barFunc.body
|
||||
.asInstanceOf[IR.Function.Lambda]
|
||||
.body
|
||||
.asInstanceOf[IR.Application.Prefix]
|
||||
|
||||
oprCall.function.asInstanceOf[IR.Name].name shouldEqual "<|"
|
||||
oprCall.arguments.length shouldEqual 2
|
||||
|
||||
val xArg = oprCall.arguments(1).asInstanceOf[IR.CallArgument.Specified]
|
||||
|
||||
xArg.value shouldBe an[IR.Expression.Block]
|
||||
xArg.value
|
||||
.asInstanceOf[IR.Expression.Block]
|
||||
.returnValue shouldBe an[IR.Application.Force]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ object Assoc {
|
||||
def of(op: String): Assoc =
|
||||
if (isApplicative(op)) Assoc.Left
|
||||
else if (op == "in") Assoc.Left
|
||||
else if (op == "<|") Assoc.Right
|
||||
else if (op.foldLeft(0)(_ + charAssoc(_)) >= 0) Assoc.Left
|
||||
else Assoc.Right
|
||||
}
|
||||
|
@ -16,9 +16,10 @@ object Prec {
|
||||
List("&"),
|
||||
List("\\"),
|
||||
List("?"),
|
||||
List("|>", "<|", ">>", "<<"),
|
||||
List("<*", "<*>", "*>", "<$", "<$>", "$>", "<+", "<+>", "+>"),
|
||||
List("<", ">"),
|
||||
List(","),
|
||||
List("==", ">", "<", ">=", "<="),
|
||||
List("+", "-"),
|
||||
List("*", "/", "%"),
|
||||
List("^"),
|
||||
|
@ -1,4 +1,5 @@
|
||||
import Base.Bench_Utils
|
||||
import Base.List
|
||||
|
||||
type Counter
|
||||
type Sum
|
||||
@ -32,9 +33,9 @@ sum_state = sum_to ->
|
||||
main =
|
||||
hundred_mil = 100000000
|
||||
IO.println "Measuring SumTCO"
|
||||
Bench_Utils.measure (_ -> here.sum_tco hundred_mil) "sum_tco" 100 20
|
||||
Bench_Utils.measure (here.sum_tco hundred_mil) "sum_tco" 100 10
|
||||
IO.println "Measuring State"
|
||||
Bench_Utils.measure (_ -> here.sum_state hundred_mil) "sum_state" 100 20
|
||||
Bench_Utils.measure (here.sum_state hundred_mil) "sum_state" 100 10
|
||||
IO.println "Measuring Co-State"
|
||||
Bench_Utils.measure (_ -> here.sum_co_state hundred_mil) "sum_co_state" 100 20
|
||||
Bench_Utils.measure (here.sum_co_state hundred_mil) "sum_co_state" 100 10
|
||||
IO.println "Bye."
|
||||
|
5
test/Test/package.yaml
Normal file
5
test/Test/package.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
name: Test
|
||||
version: 0.0.1
|
||||
license: MIT
|
||||
author: enso-dev@enso.org
|
||||
maintainer: enso-dev@enso.org
|
31
test/Test/src/List_Spec.enso
Normal file
31
test/Test/src/List_Spec.enso
Normal file
@ -0,0 +1,31 @@
|
||||
import Base.Test
|
||||
import Base.List
|
||||
|
||||
spec = describe "List" <|
|
||||
l = Cons 1 <| Cons 2 <| Cons 3 <| Nil
|
||||
it "should have properly defined length" <|
|
||||
l.length.should_equal 3
|
||||
it "should have well defined length when empty" <|
|
||||
Nil.length.should_equal 0
|
||||
it "should allow mapping a function over its elements with .map" <|
|
||||
l.map +1 . head . should_equal 2
|
||||
it "should allow reversing with .reverse" <|
|
||||
l.reverse.head.should_equal 3
|
||||
it "should allow executing an action for each element with .each" <|
|
||||
sum = State.run Number 0 <|
|
||||
l.each el->
|
||||
s = State.get Number
|
||||
State.put Number s+el
|
||||
State.get Number
|
||||
sum.should_equal 6
|
||||
it "should allow folding the list with an arbitrary operation with .fold" <|
|
||||
sum = l.fold 0 (+)
|
||||
prod = l.fold 1 (*)
|
||||
sum.should_equal 6
|
||||
prod.should_equal 6
|
||||
it "should allow checking if an element satisfies a predicate with .any" <|
|
||||
any_even = l.any (x -> x % 2 == 0)
|
||||
any_eq_five = l.any (== 5)
|
||||
any_even.should_be_true
|
||||
any_eq_five.should_be_false
|
||||
|
7
test/Test/src/Main.enso
Normal file
7
test/Test/src/Main.enso
Normal file
@ -0,0 +1,7 @@
|
||||
import Base.Test
|
||||
import Test.List_Spec
|
||||
import Test.Number_Spec
|
||||
|
||||
main = Suite.run <|
|
||||
List_Spec.spec
|
||||
Number_Spec.spec
|
7
test/Test/src/Number_Spec.enso
Normal file
7
test/Test/src/Number_Spec.enso
Normal file
@ -0,0 +1,7 @@
|
||||
import Base.Test
|
||||
|
||||
spec = describe "Number" <|
|
||||
it "should define addition" <|
|
||||
2+3 . should_equal 5
|
||||
it "should define multiplication" <|
|
||||
2*3 . should_equal 6
|
Loading…
Reference in New Issue
Block a user