From 9a4332108f9c47985ece5f2705d1ca36bad6eb99 Mon Sep 17 00:00:00 2001 From: Ara Adkins Date: Wed, 27 Nov 2019 11:32:36 +0000 Subject: [PATCH] Add lambdas, types, and methods support to new syntax (#358) --- CONTRIBUTING.md | 10 +- build.sbt | 2 - .../scala/org/enso/syntax/text/View.scala | 31 -- .../enso/syntax/text/ast/meta/Builtin.scala | 19 ++ .../enso/syntax/text/ast/meta/Pattern.scala | 68 ++-- .../scala/org/enso/syntax/text/Parser.scala | 4 +- .../builder/ExpressionFactory.java | 62 ++-- .../builder/ModuleScopeExpressionFactory.java | 42 ++- .../interpreter/node/ProgramRootNode.java | 31 +- .../node/callable/MethodResolverNode.java | 14 +- .../{FunctionBodyNode.java => BlockNode.java} | 4 +- .../scala/org/enso/compiler/Compiler.scala | 13 +- .../generate/AstToAstExpression.scala | 135 ++++++-- .../org/enso/compiler/generate/AstView.scala | 320 ++++++++++++++++++ .../scala/org/enso/interpreter/Parser.scala | 63 ++-- .../test/semantic/CurryingTest.scala | 14 +- .../test/semantic/ErrorsTest.scala | 59 ++-- .../test/semantic/FunctionArgumentsTest.scala | 73 ++-- .../test/semantic/GlobalScopeTest.scala | 59 ++-- .../test/semantic/InteropTest.scala | 24 +- .../test/semantic/LexicalScopeTest.scala | 64 ---- .../test/semantic/MethodsTest.scala | 102 +++++- .../test/semantic/NamedArgumentsTest.scala | 126 +++---- .../test/semantic/SimpleArithmeticTest.scala | 24 ++ .../interpreter/test/semantic/StateTest.scala | 60 ++-- 25 files changed, 939 insertions(+), 484 deletions(-) delete mode 100644 common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/View.scala rename engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/{FunctionBodyNode.java => BlockNode.java} (90%) create mode 100644 engine/runtime/src/main/scala/org/enso/compiler/generate/AstView.scala delete mode 100644 engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LexicalScopeTest.scala diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 083fcfec3cb..abccdba2182 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,9 +81,9 @@ part of the compiler that they relate to. In order to build and run Enso you will need the following tools: - [sbt](https://www.scala-sbt.org/) with version at least 1.3.0. -- [GraalVM](https://www.graalvm.org/) with version at least 19.2.0 and - configured as your default JVM. [Jenv](http://www.jenv.be/) is a useful tool - for managing multiple JVMs. +- [GraalVM](https://www.graalvm.org/) with version at least that described in + the [`build.sbt`](build.sbt) file configured as your default JVM. + [Jenv](http://www.jenv.be/) is a useful tool for managing multiple JVMs. ### Getting the Sources Given you've probably been reading this document on GitHub, you might have an @@ -243,7 +243,9 @@ need to follow these steps: 4. In the options for that configuration select 'Listen to remote JVM' under 'Debugger mode:' 5. Where it provides the command-line arguments for the remote JVM, copy these - and add them to `truffleRunOptions` in [`build.sbt`](build.sbt). + and add them to `truffleRunOptions` in [`build.sbt`](build.sbt). Remove the + portion of these options after `suspend=y`, including the comma. They are + placeholders that we don't use. 6. Now, when you want to debug something, you can place a breakpoint as usual in IntelliJ, and then execute your remote debugging configuration. Now, in the SBT shell, run a command to execute the code you want to debug (e.g. diff --git a/build.sbt b/build.sbt index 31ba9d0ae81..1095d098f77 100644 --- a/build.sbt +++ b/build.sbt @@ -356,9 +356,7 @@ lazy val runtime = (project in file("engine/runtime")) version := "0.1", commands += WithDebugCommand.withDebug, inConfig(Compile)(truffleRunOptionsSettings), - inConfig(Test)(truffleRunOptionsSettings), inConfig(Benchmark)(Defaults.testSettings), - inConfig(Benchmark)(truffleRunOptionsSettings), parallelExecution in Test := false, logBuffered in Test := false, libraryDependencies ++= jmh ++ Seq( diff --git a/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/View.scala b/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/View.scala deleted file mode 100644 index bcf1dc11f35..00000000000 --- a/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/View.scala +++ /dev/null @@ -1,31 +0,0 @@ -package org.enso.syntax.text - -/** This object contains view patterns that allow matching on the parser AST for - * more sophisticated constructs. - * - * These view patterns are implemented as custom unapply methods that only - * return [[Some]] when more complex conditions are met. - */ - -object View { - object Assignment { - val assignmentOpSym = AST.Ident.Opr("=") - - def unapply(ast: AST): Option[(AST, AST)] = { - ast match { - case AST.App.Infix.any(ast) => { - val left = ast.larg - val op = ast.opr - val right = ast.rarg - - if (op == assignmentOpSym) { - Some((left, right)) - } else { - None - } - } - case _ => None - } - } - } -} diff --git a/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala b/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala index 9c381d90fda..dbfad018f91 100644 --- a/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala +++ b/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Builtin.scala @@ -119,6 +119,24 @@ object Builtin { } } + val case_of = Definition( + Var("case") -> Pattern.Expr(), + Var("of") -> Pattern.Block() + ) { ctx => + ctx.body match { + case List(scrutineePart, branchesPart) => + (scrutineePart.body.toStream, branchesPart.body.toStream) match { + case (List(scrutinee), List(branches)) => + AST.Mixfix( + List1(scrutineePart.head, branchesPart.head), + List1(scrutinee.el, branches.el) + ) + case _ => internalError + } + case _ => internalError + } + } + val nonSpacedExpr = Pattern.Any(Some(false)).many1.build val arrow = Definition( @@ -226,6 +244,7 @@ object Builtin { Registry( group, + case_of, if_then, if_then_else, imp, diff --git a/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Pattern.scala b/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Pattern.scala index 345364151f6..916de72f015 100644 --- a/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Pattern.scala +++ b/common/scala/syntax/definition/src/main/scala/org/enso/syntax/text/ast/meta/Pattern.scala @@ -83,32 +83,32 @@ object Pattern { def apply(ast: AST): Tok = Tok(None, ast) } object Var { - def apply(): Var = Var(None) + def apply(): Var = Var(None) def apply(spaced: Boolean): Var = Var(Some(spaced)) } object Cons { - def apply(): Cons = Cons(None) + def apply(): Cons = Cons(None) def apply(spaced: Boolean): Cons = Cons(Some(spaced)) } object Opr { - def apply(): Opr = Opr(None, None) - def apply(spaced: Spaced): Opr = Opr(spaced, None) + def apply(): Opr = Opr(None, None) + def apply(spaced: Spaced): Opr = Opr(spaced, None) def apply(spaced: Boolean): Opr = Opr(Some(spaced)) } object Num { - def apply(): Num = Num(None) + def apply(): Num = Num(None) def apply(spaced: Boolean): Num = Num(Some(spaced)) } object Text { - def apply(): Text = Text(None) + def apply(): Text = Text(None) def apply(spaced: Boolean): Text = Text(Some(spaced)) } object Block { - def apply(): Block = Block(None) + def apply(): Block = Block(None) def apply(spaced: Boolean): Block = Block(Some(spaced)) } - def Any(spaced: Spaced = None): Pattern = + def Any(spaced: Spaced = None, allowBlocks: Boolean = true): Pattern = Blank(spaced) | Var(spaced) | Cons(spaced) | @@ -116,15 +116,21 @@ object Pattern { Mod(spaced) | Num(spaced) | Text(spaced) | - Block(spaced) | Macro(spaced) | - Invalid(spaced) - def Any(spaced: Boolean): Pattern = Any(Some(spaced)) - def ErrTillEnd(msg: String) = Any().tillEnd.err(msg) - def ErrUnmatched(msg: String) = End() | ErrTillEnd(msg) - def Expr() = Any().many1.build - def NonSpacedExpr() = Any(spaced = false).many1.build - def NonSpacedExpr_() = (Any().but(Block()) :: Any(spaced = false).many).build + Invalid(spaced) | + (if (allowBlocks) { + Block(spaced) + } else { + Nothing() + }) + def Any(spaced: Boolean): Pattern = Any(Some(spaced)) + def ErrTillEnd(msg: String): Pattern = Any().tillEnd.err(msg) + def ErrUnmatched(msg: String): Pattern = End() | ErrTillEnd(msg) + def Expr(allowBlocks: Boolean = true): Pattern = + Any(allowBlocks = allowBlocks).many1.build + def NonSpacedExpr(): Pattern = Any(spaced = false).many1.build + def NonSpacedExpr_(): Pattern = + (Any().but(Block()) :: Any(spaced = false).many).build def SepList(pat: Pattern, div: Pattern): Pattern = pat :: (div :: pat).many def SepList(pat: Pattern, div: Pattern, err: String): Pattern = { val seg = pat | Any().till(div).err(err) @@ -325,23 +331,23 @@ sealed trait Pattern { //////////////////////////// def ::(that: Pattern): Pattern = Seq(that, this) - def !(that: Pattern): Pattern = Except(that, this) - def |(that: Pattern): Pattern = Or(this, that) - def |(msg: String): Pattern = this | Err(msg, Nothing()) - def |?(tag: String): Pattern = Tag(tag, this) + def !(that: Pattern): Pattern = Except(that, this) + def |(that: Pattern): Pattern = Or(this, that) + def |(msg: String): Pattern = this | Err(msg, Nothing()) + def |?(tag: String): Pattern = Tag(tag, this) - def or(that: Pattern): Pattern = Or(this, that) - def or(msg: String): Pattern = this | Err(msg, Nothing()) - def err(msg: String): Pattern = Err(msg, this) - def but(pat: Pattern): Pattern = Except(pat, this) - def many: Pattern = Many(this) - def many1: Pattern = this :: this.many - def tag(tag: String): Pattern = Tag(tag, this) - def opt: Pattern = this | Nothing() - def build: Pattern = Build(this) + def or(that: Pattern): Pattern = Or(this, that) + def or(msg: String): Pattern = this | Err(msg, Nothing()) + def err(msg: String): Pattern = Err(msg, this) + def but(pat: Pattern): Pattern = Except(pat, this) + def many: Pattern = Many(this) + def many1: Pattern = this :: this.many + def tag(tag: String): Pattern = Tag(tag, this) + def opt: Pattern = this | Nothing() + def build: Pattern = Build(this) def till(end: Pattern): Pattern = this.but(end).many - def tillEnd: Pattern = this :: End() // fixme: rename - def fromBegin: Pattern = Begin() :: this + def tillEnd: Pattern = this :: End() // fixme: rename + def fromBegin: Pattern = Begin() :: this def matchRevUnsafe( stream: AST.Stream, diff --git a/common/scala/syntax/specialization/src/main/scala/org/enso/syntax/text/Parser.scala b/common/scala/syntax/specialization/src/main/scala/org/enso/syntax/text/Parser.scala index b18ffab1144..cd307e9114d 100644 --- a/common/scala/syntax/specialization/src/main/scala/org/enso/syntax/text/Parser.scala +++ b/common/scala/syntax/specialization/src/main/scala/org/enso/syntax/text/Parser.scala @@ -1,5 +1,7 @@ package org.enso.syntax.text +import java.util.UUID + import org.enso.data.Index import org.enso.data.Span import org.enso.flexer @@ -194,7 +196,7 @@ class Parser { Builtin.registry.get(resolvedAST.path) match { case None => throw MissingMacroDefinition case Some(spec) => - val id = resolvedAST.id.getOrElse(throw new Error(s"Missing ID")) + val id = resolvedAST.id.getOrElse(UUID.randomUUID) val segments = resolvedAST.segs.toList().map(_.el) val ctx = AST.Macro.Resolver.Context(resolvedAST.pfx, segments, id) resolvedAST.copy(shape = resolvedAST.shape.copy[AST](resolved = { diff --git a/engine/runtime/src/main/java/org/enso/interpreter/builder/ExpressionFactory.java b/engine/runtime/src/main/java/org/enso/interpreter/builder/ExpressionFactory.java index 0138c247da0..780d1d161b9 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/builder/ExpressionFactory.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/builder/ExpressionFactory.java @@ -12,7 +12,7 @@ import org.enso.interpreter.node.callable.ForceNodeGen; import org.enso.interpreter.node.callable.InvokeCallableNode; import org.enso.interpreter.node.callable.argument.ReadArgumentNode; import org.enso.interpreter.node.callable.function.CreateFunctionNode; -import org.enso.interpreter.node.callable.function.FunctionBodyNode; +import org.enso.interpreter.node.callable.function.BlockNode; import org.enso.interpreter.node.controlflow.*; import org.enso.interpreter.node.expression.constant.ConstructorNode; import org.enso.interpreter.node.expression.constant.DynamicSymbolNode; @@ -204,12 +204,11 @@ public class ExpressionFactory implements AstExpressionVisitor { * function arguments into a state where they can actually be read. * * @param arguments the arguments the function is defined for - * @param expressions the body of the function - * @param retValue the return value of the function + * @param body the body of the function * @return a runtime node representing the function body */ public CreateFunctionNode processFunctionBody( - List arguments, List expressions, AstExpression retValue) { + List arguments, AstExpression body) { ArgDefinitionFactory argFactory = new ArgDefinitionFactory(scope, language, scopeName, moduleScope); @@ -235,17 +234,9 @@ public class ExpressionFactory implements AstExpressionVisitor { } } - List fnBodyExpressionNodes = - expressions.stream().map(stmt -> stmt.visit(this)).collect(Collectors.toList()); + ExpressionNode bodyExpr = body.visit(this); - List allFnExpressions = new ArrayList<>(); - allFnExpressions.addAll(argExpressions); - allFnExpressions.addAll(fnBodyExpressionNodes); - - ExpressionNode returnExpr = retValue.visit(this); - - FunctionBodyNode fnBodyNode = - new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr); + BlockNode fnBodyNode = new BlockNode(argExpressions.toArray(new ExpressionNode[0]), bodyExpr); RootNode fnRootNode = new ClosureRootNode(language, scope, moduleScope, fnBodyNode, null, "lambda::" + scopeName); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode); @@ -278,20 +269,18 @@ public class ExpressionFactory implements AstExpressionVisitor { /** * Creates a runtime node representing a function. * - *

Given that most of the work takes place in {@link #processFunctionBody(List, List, - * AstExpression) processFunctionBody}, this node is solely responsible for handling the creation - * of a new scope for the function, and marking it as tail recursive. + *

Given that most of the work takes place in {@link #processFunctionBody(List, AstExpression) + * processFunctionBody}, this node is solely responsible for handling the creation of a new scope + * for the function, and marking it as tail recursive. * * @param arguments the arguments to the function - * @param expressions the expressions that make up the function body - * @param retValue the return value of the function + * @param body the body of the function * @return a runtime node representing the function */ @Override - public ExpressionNode visitFunction( - List arguments, List expressions, AstExpression retValue) { + public ExpressionNode visitFunction(List arguments, AstExpression body) { ExpressionFactory child = createChild(currentVarName); - ExpressionNode fun = child.processFunctionBody(arguments, expressions, retValue); + ExpressionNode fun = child.processFunctionBody(arguments, body); fun.markTail(); return fun; } @@ -299,20 +288,18 @@ public class ExpressionFactory implements AstExpressionVisitor { /** * Creates a runtime node representing a case function. * - *

Given that most of the work takes place in {@link #processFunctionBody(List, List, - * AstExpression) processFunctionBody}, this node is solely responsible for handling the creation - * of a new scope for the function. + *

Given that most of the work takes place in {@link #processFunctionBody(List, AstExpression) + * processFunctionBody}, this node is solely responsible for handling the creation of a new scope + * for the function. * * @param arguments the arguments to the function - * @param expressions the expressions that make up the function body - * @param retValue the return value of the function + * @param body the body of the function * @return a runtime node representing the function */ @Override - public ExpressionNode visitCaseFunction( - List arguments, List expressions, AstExpression retValue) { + public ExpressionNode visitCaseFunction(List arguments, AstExpression body) { ExpressionFactory child = createChild(currentVarName); - return child.processFunctionBody(arguments, expressions, retValue); + return child.processFunctionBody(arguments, body); } /** @@ -403,4 +390,19 @@ public class ExpressionFactory implements AstExpressionVisitor { public ExpressionNode visitDesuspend(AstExpression target) { return ForceNodeGen.create(target.visit(this)); } + + /** + * Creates a runtime representation of a block. + * + * @param statements the statements making up the body of this block + * @param retValue the return value expression + * @return AST fragment representing the block + */ + @Override + public ExpressionNode visitBlock(List statements, AstExpression retValue) { + ExpressionNode[] statementExprs = + statements.stream().map(expr -> expr.visit(this)).toArray(ExpressionNode[]::new); + ExpressionNode retExpr = retValue.visit(this); + return new BlockNode(statementExprs, retExpr); + } } diff --git a/engine/runtime/src/main/java/org/enso/interpreter/builder/ModuleScopeExpressionFactory.java b/engine/runtime/src/main/java/org/enso/interpreter/builder/ModuleScopeExpressionFactory.java index a1c34d32ef7..7b792d7e696 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/builder/ModuleScopeExpressionFactory.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/builder/ModuleScopeExpressionFactory.java @@ -1,6 +1,10 @@ package org.enso.interpreter.builder; +import com.oracle.truffle.api.RootCallTarget; +import com.oracle.truffle.api.Truffle; +import org.enso.compiler.core.IR; import org.enso.interpreter.*; +import org.enso.interpreter.node.ClosureRootNode; import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.callable.function.CreateFunctionNode; import org.enso.interpreter.runtime.Context; @@ -9,9 +13,12 @@ import org.enso.interpreter.runtime.callable.atom.AtomConstructor; 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.LocalScope; import org.enso.interpreter.runtime.scope.ModuleScope; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -19,7 +26,7 @@ import java.util.stream.IntStream; * A {@code GlobalScopeExpressionFactory} is responsible for converting the top-level definitions of * an Enso program into AST nodes for the interpreter to evaluate. */ -public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor { +public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor { private final Language language; private final ModuleScope moduleScope; @@ -40,7 +47,7 @@ public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor run(AstModuleScope expr) { return expr.visit(this); } @@ -54,11 +61,11 @@ public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor visitModuleScope( List imports, List typeDefs, List bindings, - AstExpression executableExpression) { + Optional executableExpression) { Context context = language.getCurrentContext(); for (AstImport imp : imports) { @@ -87,14 +94,21 @@ public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor scalaNone = scala.Option.apply(null); + AstArgDefinition thisArgument = + new AstArgDefinition(Constants.THIS_ARGUMENT_NAME, scalaNone, false); + ExpressionFactory expressionFactory = new ExpressionFactory( language, method.typeName() + Constants.SCOPE_SEPARATOR + method.methodName(), moduleScope); + + List realArgs = new ArrayList<>(method.fun().getArguments()); + realArgs.add(0, thisArgument); + CreateFunctionNode funNode = - expressionFactory.processFunctionBody( - method.fun().getArguments(), method.fun().getStatements(), method.fun().ret()); + expressionFactory.processFunctionBody(realArgs, method.fun().body()); funNode.markTail(); Function function = new Function( @@ -113,7 +127,19 @@ public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor program = context.compiler().run(this.sourceCode); + executableExpression = + program.orElseGet(() -> getContext().getUnit().getConstructorFunction()); } - Object state = getContext().getUnit().newInstance(); - frame.setObject(this.getStateFrameSlot(), state); - - return this.ensoProgram.executeGeneric(frame); + return this.executableExpressionCaller + .executeDispatch(executableExpression, null, context.getUnit().newInstance(), new Object[0]) + .getValue(); } /* Note [Static Passes] diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/MethodResolverNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/MethodResolverNode.java index a88ed1e0a79..82c748d3930 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/MethodResolverNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/MethodResolverNode.java @@ -37,18 +37,26 @@ public abstract class MethodResolverNode extends Node { Function resolveAtomCached( UnresolvedSymbol symbol, Atom atom, - @CachedContext(Language.class) TruffleLanguage.ContextReference contextRef, @Cached("symbol") UnresolvedSymbol cachedSymbol, @Cached("atom.getConstructor()") AtomConstructor cachedConstructor, @Cached("resolveMethodOnAtom(cachedConstructor, cachedSymbol)") Function function) { return function; } + @Specialization(guards = {"cachedSymbol == symbol", "atomConstructor == cachedConstructor"}) + Function resolveAtomConstructorCached( + UnresolvedSymbol symbol, + AtomConstructor atomConstructor, + @Cached("symbol") UnresolvedSymbol cachedSymbol, + @Cached("atomConstructor") AtomConstructor cachedConstructor, + @Cached("resolveMethodOnAtom(cachedConstructor, cachedSymbol)") Function function) { + return function; + } + @Specialization(guards = "cachedSymbol == symbol") Function resolveNumberCached( UnresolvedSymbol symbol, long self, - @CachedContext(Language.class) TruffleLanguage.ContextReference contextReference, @Cached("symbol") UnresolvedSymbol cachedSymbol, @Cached("resolveMethodOnNumber(cachedSymbol)") Function function) { return function; @@ -58,7 +66,6 @@ public abstract class MethodResolverNode extends Node { Function resolveFunctionCached( UnresolvedSymbol symbol, Function self, - @CachedContext(Language.class) TruffleLanguage.ContextReference contextReference, @Cached("symbol") UnresolvedSymbol cachedSymbol, @Cached("resolveMethodOnFunction(cachedSymbol)") Function function) { return function; @@ -68,7 +75,6 @@ public abstract class MethodResolverNode extends Node { Function resolveErrorCached( UnresolvedSymbol symbol, RuntimeError self, - @CachedContext(Language.class) TruffleLanguage.ContextReference contextReference, @Cached("symbol") UnresolvedSymbol cachedSymbol, @Cached("resolveMethodOnError(cachedSymbol)") Function function) { return function; diff --git a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/FunctionBodyNode.java b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java similarity index 90% rename from engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/FunctionBodyNode.java rename to engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java index 4c94ae42477..5b9d2cac282 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/FunctionBodyNode.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/node/callable/function/BlockNode.java @@ -10,7 +10,7 @@ import org.enso.interpreter.node.ExpressionNode; * function body. */ @NodeInfo(shortName = "{}") -public class FunctionBodyNode extends ExpressionNode { +public class BlockNode extends ExpressionNode { @Children private final ExpressionNode[] statements; @Child private ExpressionNode returnExpr; @@ -20,7 +20,7 @@ public class FunctionBodyNode extends ExpressionNode { * @param expressions the function body * @param returnExpr the return expression from the function */ - public FunctionBodyNode(ExpressionNode[] expressions, ExpressionNode returnExpr) { + public BlockNode(ExpressionNode[] expressions, ExpressionNode returnExpr) { this.statements = expressions; this.returnExpr = returnExpr; } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala index 0abe7c04066..981fc400097 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/Compiler.scala @@ -1,5 +1,7 @@ package org.enso.compiler +import java.util.Optional + import com.oracle.truffle.api.TruffleFile import com.oracle.truffle.api.source.Source import org.enso.compiler.generate.AstToAstExpression @@ -11,10 +13,11 @@ 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.callable.function.Function 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, Parser} +import org.enso.syntax.text.{AST, Debug, Parser} import scala.collection.JavaConverters._ import scala.collection.mutable @@ -41,7 +44,7 @@ class Compiler( * @return an interpreter node whose execution corresponds to the top-level * executable functionality in the module corresponding to `source`. */ - def run(source: Source, scope: ModuleScope): ExpressionNode = { + def run(source: Source, scope: ModuleScope): Optional[Function] = { val mimeType = source.getMimeType val expr: AstModuleScope = if (mimeType == Constants.MIME_TYPE) { @@ -63,7 +66,7 @@ class Compiler( * @return an interpreter node whose execution corresponds to the top-level * executable functionality in the module corresponding to `source`. */ - def run(file: TruffleFile, scope: ModuleScope): ExpressionNode = { + def run(file: TruffleFile, scope: ModuleScope): Optional[Function] = { run(Source.newBuilder(Constants.LANGUAGE_ID, file).build, scope) } @@ -75,7 +78,7 @@ class Compiler( * @return an interpreter node whose execution corresponds to the top-level * executable functionality in the module corresponding to `source`. */ - def run(source: Source): ExpressionNode = { + def run(source: Source): Optional[Function] = { run(source, context.createScope) } @@ -87,7 +90,7 @@ class Compiler( * @return an interpreter node whose execution corresponds to the top-level * executable functionality in the module corresponding to `source`. */ - def run(file: TruffleFile): ExpressionNode = { + def run(file: TruffleFile): Optional[Function] = { run(Source.newBuilder(Constants.LANGUAGE_ID, file).build) } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/generate/AstToAstExpression.scala b/engine/runtime/src/main/scala/org/enso/compiler/generate/AstToAstExpression.scala index 8a0b4c3db1a..3e21346e5fa 100644 --- a/engine/runtime/src/main/scala/org/enso/compiler/generate/AstToAstExpression.scala +++ b/engine/runtime/src/main/scala/org/enso/compiler/generate/AstToAstExpression.scala @@ -1,21 +1,17 @@ package org.enso.compiler.generate -import org.enso.compiler.exception.UnhandledEntity import org.enso.compiler.core.IR -import org.enso.interpreter.{ - AstArithOp, - AstExpression, - AstImport, - AstLong, - AstModuleScope, - AstModuleSymbol -} +import org.enso.compiler.exception.UnhandledEntity +import org.enso.interpreter._ import org.enso.syntax.text.{AST, Debug} // TODO [AA] Please note that this entire translation is _very_ work-in-progress // and is hence quite ugly right now. It will be cleaned up as work progresses, // but it was thought best to land in increments where possible. +// TODO [Generic] +// - Type signatures + /** * This is a representation of the raw conversion from the Parser [[AST AST]] * to the internal [[IR IR]] used by the static transformation passes. @@ -37,13 +33,32 @@ object AstToAstExpression { inputAST match { case AST.Module.any(inputAST) => translateModule(inputAST) case _ => { - throw new UnhandledEntity(inputAST, "process") + throw new UnhandledEntity(inputAST, "translate") } } } def translateModuleSymbol(inputAST: AST): AstModuleSymbol = { - ??? + inputAST match { + case AST.Def(consName, args, body) => + if (body.isDefined) { + throw new RuntimeException("Cannot support complex type defs yet!!!!") + } else { + AstTypeDef(consName.name, args.map(translateArgumentDefinition)) + } + case AstView.MethodDefinition(targetPath, name, definition) => + val path = + targetPath.collect { case AST.Ident.Cons(name) => name }.mkString(".") + val nameStr = name match { case AST.Ident.Var(name) => name } + val defExpression = translateExpression(definition) + val defExpr: AstFunction = defExpression match { + case fun: AstFunction => fun + case expr => AstFunction(List(), expr) + } + AstMethodDef(path, nameStr, defExpr) + case _ => + throw new UnhandledEntity(inputAST, "translateModuleSymbol") + } } def translateLiteral(literal: AST.Literal): AstLong = { @@ -60,9 +75,49 @@ object AstToAstExpression { } } - def translateApplication(application: AST): AstExpression = { + def translateArgumentDefinition(arg: AST): AstArgDefinition = { + // TODO [AA] Do this properly + arg match { + case AstView.DefinitionArgument(arg) => + AstArgDefinition(arg.name, None, false) + case AstView.AssignedArgument(name, value) => + AstArgDefinition(name.name, Some(translateExpression(value)), false) + case _ => + throw new UnhandledEntity(arg, "translateDefinitionArgument") + } + } + + def translateCallArgument(arg: AST): AstCallArg = arg match { + case AstView.AssignedArgument(left, right) => + AstNamedCallArg(left.name, translateExpression(right)) + case _ => AstUnnamedCallArg(translateExpression(arg)) + } + + def translateMethodCall( + target: AST, + ident: AST.Ident, + args: List[AST] + ): AstExpression = { + AstApply( + translateExpression(ident), + (target :: args).map(translateCallArgument), + false + ) + } + + def translateCallable(application: AST): AstExpression = { application match { - case AST.App.Infix(left, fn, right) => { + case AstView.Application(name, args) => + AstApply( + translateExpression(name), + args.map(translateCallArgument), + false + ) + case AstView.Lambda(args, body) => + val realArgs = args.map(translateArgumentDefinition) + val realBody = translateExpression(body) + AstFunction(realArgs, realBody) + case AST.App.Infix(left, fn, right) => // FIXME [AA] We should accept all ops when translating to core val validInfixOps = List("+", "/", "-", "*", "%") @@ -77,19 +132,46 @@ object AstToAstExpression { s"${fn.name} is not currently a valid infix operator" ) } - } -// case AST.App.Prefix(fn, arg) => + // case AST.App.Prefix(fn, arg) => // case AST.App.Section.any(application) => // TODO [AA] left, sides, right // case AST.Mixfix(application) => // TODO [AA] translate if case _ => throw new UnhandledEntity(application, "translateApplication") } } + def translateIdent(identifier: AST.Ident): AstExpression = { + identifier match { +// case AST.Ident.Blank(_) => throw new UnhandledEntity("Blank") IR.Identifier.Blank() + case AST.Ident.Var(name) => AstVariable(name) + case AST.Ident.Cons(name) => AstVariable(name) +// case AST.Ident.Opr.any(identifier) => processIdentOperator(identifier) +// case AST.Ident.Mod(name) => IR.Identifier.Module(name) + case _ => + throw new UnhandledEntity(identifier, "translateIdent") + } + } + + def translateAssignment(name: AST, expr: AST): AstAssignment = { + name match { + case AST.Ident.Var(name) => AstAssignment(name, translateExpression(expr)) + case _ => + throw new UnhandledEntity(name, "translateAssignment") + } + } + def translateExpression(inputAST: AST): AstExpression = { inputAST match { - case AST.App.any(inputAST) => translateApplication(inputAST) + case AstView.Assignment(name, expr) => translateAssignment(name, expr) + case AstView.MethodCall(target, name, args) => + translateMethodCall(target, name, args) + case AstView.CaseExpression(scrutinee, branches) => + ??? + case AST.App.any(inputAST) => translateCallable(inputAST) case AST.Literal.any(inputAST) => translateLiteral(inputAST) case AST.Group.any(inputAST) => translateGroup(inputAST) + case AST.Ident.any(inputAST) => translateIdent(inputAST) + case AstView.Block(lines, retLine) => + AstBlock(lines.map(translateExpression), translateExpression(retLine)) case _ => throw new UnhandledEntity(inputAST, "translateExpression") } @@ -137,15 +219,22 @@ object AstToAstExpression { case _ => true } - if (nonImportBlocks.isEmpty) { - // FIXME [AA] This is temporary, and should be moved to generate an - // error node in Core. - throw new RuntimeException("Cannot have no expressions") + val definitions = nonImportBlocks.takeWhile { + case AST.Def(_, _, _) => true + case AstView.MethodDefinition(_) => true + case _ => false } - val statements = nonImportBlocks.dropRight(1).map(translateModuleSymbol) - val expression = translateExpression(nonImportBlocks.last) - AstModuleScope(imports, statements, expression) + val executableExpressions = nonImportBlocks.drop(definitions.length) + + val statements = definitions.map(translateModuleSymbol) + val expressions = executableExpressions.map(translateExpression) + val block = expressions match { + case List() => None + case List(expr) => Some(expr) + case _ => Some(AstBlock(expressions.dropRight(1), expressions.last)) + } + AstModuleScope(imports, statements, block) } } } diff --git a/engine/runtime/src/main/scala/org/enso/compiler/generate/AstView.scala b/engine/runtime/src/main/scala/org/enso/compiler/generate/AstView.scala new file mode 100644 index 00000000000..eeea6d302ee --- /dev/null +++ b/engine/runtime/src/main/scala/org/enso/compiler/generate/AstView.scala @@ -0,0 +1,320 @@ +package org.enso.compiler.generate + +import org.enso.data +import org.enso.data.Shifted.List1 +import org.enso.interpreter.AstBlock +import org.enso.syntax.text.{AST, Debug} + +// TODO [AA] Handle arbitrary parens + +/** This object contains view patterns that allow matching on the parser AST for + * more sophisticated constructs. + * + * These view patterns are implemented as custom unapply methods that only + * return [[Some]] when more complex conditions are met. + */ +object AstView { + object Block { + def unapply(ast: AST): Option[(List[AST], AST)] = ast match { + case AST.Block(_, _, firstLine, lines) => + val actualLines = firstLine.elem :: lines.flatMap(_.elem) + if (actualLines.nonEmpty) { + Some((actualLines.dropRight(1), actualLines.last)) + } else { + None + } + case _ => None + } + } + + object Binding { + val bindingOpSym = AST.Ident.Opr("=") + + def unapply(ast: AST): Option[(AST, AST)] = { + ast match { + case AST.App.Infix.any(ast) => + val left = ast.larg + val op = ast.opr + val right = ast.rarg + + if (op == bindingOpSym) { + Some((left, right)) + } else { + None + } + case _ => None + } + } + } + + object Assignment { + val assignmentOpSym = AST.Ident.Opr("=") + + def unapply(ast: AST): Option[(AST.Ident.Var, AST)] = { + ast match { + case Binding(AST.Ident.Var.any(left), right) => Some((left, right)) + case _ => None + } + } + } + + object Lambda { + val lambdaOpSym = AST.Ident.Opr("->") + + def unapply(ast: AST): Option[(List[AST], AST)] = { + ast match { + case AST.App.Infix.any(ast) => + val left = ast.larg + val op = ast.opr + val right = ast.rarg + + if (op == lambdaOpSym) { + left match { + case LambdaParamList(args) => Some((args, right)) + case _ => None + } + } else { + None + } + case _ => None + } + } + } + + object PatternMatch { + // Cons, args + def unapply(ast: AST): Option[(AST.Ident.Cons, List[AST])] = { + ast match { + case SpacedList(AST.Ident.Cons.any(cons) :: xs) => + val realArgs = xs.collect { case a @ MatchParam(_) => a } + + if (realArgs.length == xs.length) { + Some((cons, xs)) + } else { + None + } + case _ => None + } + } + } + + object MatchParam { + def unapply(ast: AST): Option[AST] = ast match { + case DefinitionArgument(_) => Some(ast) + case PatternMatch(_, _) => Some(ast) + case _ => None + } + } + + object FunctionParam { + def unapply(ast: AST): Option[AST] = ast match { + case AssignedArgument(_, _) => Some(ast) + case DefinitionArgument(_) => Some(ast) + case PatternMatch(_) => Some(ast) + case _ => None + } + } + + object LambdaParamList { + //TODO suspended arguments + + def unapply(ast: AST): Option[List[AST]] = { + ast match { + case SpacedList(args) => + val realArgs = args.collect { case a @ FunctionParam(_) => a } + + if (realArgs.length == args.length) { + Some(args) + } else { + None + } + + // TODO [AA] This really isn't true........ + case _ => Some(List(ast)) + } + } + } + + object MaybeParensed { + def unapply(ast: AST): Option[AST] = { + ast match { + case AST.Group(mExpr) => mExpr.flatMap(unapply) + case a => Some(a) + } + } + } + + /** Used for named and defaulted argument syntactic forms. */ + object AssignedArgument { + def unapply(ast: AST): Option[(AST.Ident.Var, AST)] = + MaybeParensed.unapply(ast).flatMap(Assignment.unapply) + } + + object DefinitionArgument { + def unapply(ast: AST): Option[AST.Ident.Var] = ast match { + case AST.Ident.Var.any(ast) => Some(ast) + case _ => None + } + } + + /** Used for arguments declared as lazy. */ + object SuspendedArgument {} + + object Application { + def unapply(ast: AST): Option[(AST, List[AST])] = + SpacedList.unapply(ast).flatMap { + case fun :: args => + fun match { + case MethodCall(target, function, methodArgs) => + Some((function, target :: methodArgs ++ args)) + case _ => Some((fun, args)) + } + case _ => None + } + } + + object MethodCall { + def unapply(ast: AST): Option[(AST, AST.Ident, List[AST])] = ast match { + case OperatorDot(target, Application(ConsOrVar(ident), args)) => + Some((target, ident, args)) + case OperatorDot(target, ConsOrVar(ident)) => + Some((target, ident, List())) + case _ => None + } + } + + object SpacedList { + + /** + * + * @param ast + * @return the constructor, and a list of its arguments + */ + def unapply(ast: AST): Option[List[AST]] = { + matchSpacedList(ast) + } + + def matchSpacedList(ast: AST): Option[List[AST]] = { + ast match { + case AST.App.Prefix(fn, arg) => + val fnRecurse = matchSpacedList(fn) + + fnRecurse match { + case Some(headItems) => Some(headItems :+ arg) + case None => Some(List(fn, arg)) + } + + case _ => None + } + } + } + + object MethodDefinition { + def unapply(ast: AST): Option[(List[AST], AST, AST)] = { + ast match { + case Binding(lhs, rhs) => + lhs match { + case MethodReference(targetPath, name) => + Some((targetPath, name, rhs)) + case _ => + None + } + case _ => + None + } + } + } + + object ConsOrVar { + def unapply(arg: AST): Option[AST.Ident] = arg match { + case AST.Ident.Var.any(arg) => Some(arg) + case AST.Ident.Cons.any(arg) => Some(arg) + case _ => None + } + } + + object OperatorDot { + def unapply(ast: AST): Option[(AST, AST)] = ast match { + case AST.App.Infix(left, AST.Ident.Opr("."), right) => Some((left, right)) + case _ => + None + } + } + + object Path { + + def unapply(ast: AST): Option[List[AST]] = { + val path = matchPath(ast) + + if (path.isEmpty) { + None + } else { + Some(path) + } + } + + def matchPath(ast: AST): List[AST] = { + ast match { + case OperatorDot(left, right) => + right match { + case AST.Ident.any(right) => matchPath(left) :+ right + case _ => List() + } + case AST.Ident.any(ast) => List(ast) + case _ => List() + } + } + } + + object MethodReference { + def unapply(ast: AST): Option[(List[AST], AST)] = { + ast match { + case Path(segments) => + if (segments.length >= 2) { + val consPath = segments.dropRight(1) + val maybeVar = segments.last + + val isValid = consPath.collect { + case a @ AST.Ident.Cons(_) => a + }.length == consPath.length + + if (isValid) { + maybeVar match { + case AST.Ident.Var(_) => Some((consPath, maybeVar)) + case _ => None + } + } else { + None + } + } else { + None + } + case _ => None + } + } + } + + object CaseBranch { + // TODO [AA] + } + + object CaseExpression { + val caseName = data.List1(AST.Ident.Var("case"), AST.Ident.Var("of")) + + // scrutinee and branches + def unapply(ast: AST): Option[(AST, List[AST])] = { + ast match { + case AST.Mixfix(identSegments, argSegments) => + if (identSegments == caseName) { + println("==== MIXFIX ====") + println(Debug.pretty(ast.toString)) + + None + } else { + None + } + case _ => None + } + } + } +} diff --git a/engine/runtime/src/main/scala/org/enso/interpreter/Parser.scala b/engine/runtime/src/main/scala/org/enso/interpreter/Parser.scala index 73947324a56..986944ad999 100644 --- a/engine/runtime/src/main/scala/org/enso/interpreter/Parser.scala +++ b/engine/runtime/src/main/scala/org/enso/interpreter/Parser.scala @@ -19,14 +19,12 @@ trait AstExpressionVisitor[+T] { def visitFunction( arguments: java.util.List[AstArgDefinition], - statements: java.util.List[AstExpression], - retValue: AstExpression + body: AstExpression ): T def visitCaseFunction( arguments: java.util.List[AstArgDefinition], - statements: java.util.List[AstExpression], - retValue: AstExpression + body: AstExpression ): T def visitFunctionApplication( @@ -46,17 +44,22 @@ trait AstExpressionVisitor[+T] { def visitDesuspend(target: AstExpression): T def visitStringLiteral(string: String): T + + def visitBlock( + statements: java.util.List[AstExpression], + retValue: AstExpression + ): T } -trait AstModuleScopeVisitor[+T] { +trait AstModuleScopeVisitor[T] { @throws(classOf[Exception]) def visitModuleScope( imports: java.util.List[AstImport], typeDefs: java.util.List[AstTypeDef], bindings: java.util.List[AstMethodDef], - expression: AstExpression - ): T + expression: java.util.Optional[AstExpression] + ): java.util.Optional[T] } sealed trait AstModuleSymbol @@ -74,10 +77,10 @@ case class AstImport(name: String) case class AstModuleScope( imports: List[AstImport], bindings: List[AstModuleSymbol], - expression: AstExpression + expression: Option[AstExpression] ) { - def visit[T](visitor: AstModuleScopeVisitor[T]): T = { + def visit[T](visitor: AstModuleScopeVisitor[T]): Optional[T] = { val types = new java.util.ArrayList[AstTypeDef]() val defs = new java.util.ArrayList[AstMethodDef]() @@ -86,7 +89,12 @@ case class AstModuleScope( case typeDef: AstTypeDef => types.add(typeDef) } - visitor.visitModuleScope(imports.asJava, types, defs, expression) + visitor.visitModuleScope( + imports.asJava, + types, + defs, + Optional.ofNullable(expression.orNull) + ) } } @@ -180,24 +188,20 @@ case class AstApply( case class AstFunction( arguments: List[AstArgDefinition], - statements: List[AstExpression], - ret: AstExpression + body: AstExpression ) extends AstExpression { override def visit[T](visitor: AstExpressionVisitor[T]): T = - visitor.visitFunction(arguments.asJava, statements.asJava, ret) + visitor.visitFunction(arguments.asJava, body) def getArguments: java.util.List[AstArgDefinition] = arguments.asJava - - def getStatements: java.util.List[AstExpression] = statements.asJava } case class AstCaseFunction( arguments: List[AstArgDefinition], - statements: List[AstExpression], - ret: AstExpression + body: AstExpression ) extends AstExpression { override def visit[T](visitor: AstExpressionVisitor[T]): T = - visitor.visitCaseFunction(arguments.asJava, statements.asJava, ret) + visitor.visitCaseFunction(arguments.asJava, body) } case class AstAssignment(name: String, body: AstExpression) @@ -226,6 +230,12 @@ case class AstDesuspend(target: AstExpression) extends AstExpression { visitor.visitDesuspend(target) } +case class AstBlock(statements: List[AstExpression], retVal: AstExpression) + extends AstExpression { + override def visit[T](visitor: AstExpressionVisitor[T]): T = + visitor.visitBlock(statements.asJava, retVal) +} + class EnsoParserInternal extends JavaTokenParsers { override def skipWhitespace: Boolean = true @@ -310,17 +320,19 @@ class EnsoParserInternal extends JavaTokenParsers { def function: Parser[AstFunction] = ("{" ~> (inArgList ?) ~ ((statement <~ ";") *) ~ expression <~ "}") ^^ { - case args ~ stmts ~ expr => AstFunction(args.getOrElse(Nil), stmts, expr) + case args ~ stmts ~ expr => + AstFunction(args.getOrElse(Nil), AstBlock(stmts, expr)) } def caseFunction: Parser[AstCaseFunction] = function ^^ { - case AstFunction(args, stmts, ret) => AstCaseFunction(args, stmts, ret) + case AstFunction(args, AstBlock(stmts, ret)) => + AstCaseFunction(args, AstBlock(stmts, ret)) } def caseClause: Parser[AstCase] = (expression <~ "~") ~ (caseFunction <~ ";") ^^ { case cons ~ fun => - AstCase(cons, AstCaseFunction(fun.arguments, fun.statements, fun.ret)) + AstCase(cons, AstCaseFunction(fun.arguments, fun.body)) } def matchClause: Parser[AstMatch] = @@ -338,12 +350,9 @@ class EnsoParserInternal extends JavaTokenParsers { def methodDef: Parser[AstMethodDef] = (ident <~ ".") ~ (ident <~ "=") ~ expression ^^ { case typeName ~ methodName ~ body => - val thisArg = - AstArgDefinition(Constants.THIS_ARGUMENT_NAME, None, false) val fun = body match { - case b: AstFunction => - b.copy(arguments = thisArg :: b.arguments) - case _ => AstFunction(List(thisArg), List(), body) + case b: AstFunction => b + case _ => AstFunction(List(), body) } AstMethodDef(typeName, methodName, fun) } @@ -356,7 +365,7 @@ class EnsoParserInternal extends JavaTokenParsers { def globalScope: Parser[AstModuleScope] = (importStmt *) ~ ((typeDef | methodDef) *) ~ expression ^^ { case imports ~ assignments ~ expr => - AstModuleScope(imports, assignments, expr) + AstModuleScope(imports, assignments, Some(expr)) } def parseGlobalScope(code: String): AstModuleScope = { diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala index 635b3f7fb5f..fd071348726 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/CurryingTest.scala @@ -6,16 +6,14 @@ class CurryingTest extends InterpreterTest { "Functions" should "allow partial application" in { val code = """ - |@{ - | apply = { |v, f| @f [v] }; - | adder = { |a, b| a + b }; - | plusOne = @apply [f = @adder [1]]; - | result = @plusOne [10]; - | result - |} + |apply = v f -> f v + |adder = a b -> a + b + |plusOne = apply (f = adder 1) + |result = plusOne 10 + |result |""".stripMargin - evalOld(code) shouldEqual 11 + eval(code) shouldEqual 11 } "Functions" should "allow default arguments to be suspended" in { diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ErrorsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ErrorsTest.scala index 5ecba53c947..5a74d765751 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ErrorsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/ErrorsTest.scala @@ -6,36 +6,33 @@ class ErrorsTest extends InterpreterTest { "Panics" should "be thrown and stop evaluation" in { val code = """ - |type Foo; - |type Bar; - |type Baz; - |@{ - | @println [@IO, @Foo]; - | @throw [@Panic, @Bar]; - | @println [@IO, @Baz] - |} + |type Foo + |type Bar + |type Baz + | + |IO.println Foo + |Panic.throw Bar + |IO.println Baz |""".stripMargin - val exception = the[InterpreterException] thrownBy evalOld(code) + val exception = the[InterpreterException] thrownBy eval(code) exception.isGuestException shouldEqual true - exception.getGuestObject.toString shouldEqual "Bar<>" - consumeOut shouldEqual List("Foo<>") + exception.getGuestObject.toString shouldEqual "Bar" + consumeOut shouldEqual List("Foo") } "Panics" should "be recoverable and transformed into errors" in { val code = """ - |type MyError; + |type MyError | - |@{ - | thrower = { |x| @throw [@Panic, x] }; - | caught = @recover [@Panic, @thrower [@MyError]]; - | @println [@IO, caught] - |} + |thrower = x -> Panic.throw x + |caught = Panic.recover (thrower MyError) + |IO.println caught |""".stripMargin - noException shouldBe thrownBy(evalOld(code)) - consumeOut shouldEqual List("Error:MyError<>") + noException shouldBe thrownBy(eval(code)) + consumeOut shouldEqual List("Error:MyError") } "Errors" should "propagate through pattern matches" in { @@ -60,26 +57,22 @@ class ErrorsTest extends InterpreterTest { "Errors" should "be catchable by a user-provided special handling function" in { val code = """ - |@{ - | intError = @throw [@Error, 1]; - | @catch [intError, { |x| x + 3 }] - |} + |intError = Error.throw 1 + |intError.catch (x -> x + 3) |""".stripMargin - evalOld(code) shouldEqual 4 + eval(code) shouldEqual 4 } "Catch function" should "accept a constructor handler" in { val code = """ - |type MyCons err; + |type MyCons err | - |@{ - | unitErr = @throw [@Error, @Unit]; - | @println [@IO, @catch [unitErr, MyCons]] - |} + |unitErr = Error.throw Unit + |IO.println (unitErr.catch MyCons) |""".stripMargin - evalOld(code) - consumeOut shouldEqual List("MyCons>") + eval(code) + consumeOut shouldEqual List("MyCons") } "Catch function" should "accept a method handler" in { @@ -103,7 +96,7 @@ class ErrorsTest extends InterpreterTest { } "Catch function" should "act as identity for non-error values" in { - val code = "@catch [10, {|x| x + 1}]" - evalOld(code) shouldEqual 10 + val code = "10.catch (x -> x + 1)" + eval(code) shouldEqual 10 } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/FunctionArgumentsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/FunctionArgumentsTest.scala index 2f8e7eccce6..95faad9793b 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/FunctionArgumentsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/FunctionArgumentsTest.scala @@ -5,11 +5,11 @@ import org.enso.interpreter.test.InterpreterTest class FunctionArgumentsTest extends InterpreterTest { "Functions" should "take arguments and use them in their bodies" in { val code = - """ - |{ |x| x * x } + """ + |x -> x * x |""".stripMargin - val function = evalOld(code) + val function = eval(code) function.call(1) shouldEqual 1 function.call(4) shouldEqual 16 } @@ -17,53 +17,51 @@ class FunctionArgumentsTest extends InterpreterTest { "Function arguments from outer scope" should "be visible in the inner scope" in { val code = """ - |{ |a| - | add = { |a, b| a + b }; - | adder = { |b| @add [a,b] }; - | res = @adder [2]; - | res - |} + |a -> + | add = a b -> a + b + | adder = b -> add a b + | adder 2 """.stripMargin - evalOld(code).call(3) shouldEqual 5 + eval(code).call(3) shouldEqual 5 + } + + "Lambdas" should "be callable directly without assignment" in { + val code = + """ + |(x y -> x * y) 5 6 + |""".stripMargin + eval(code) shouldEqual 30 } "Recursion" should "work" in { val code = """ - |@{ - | sumTo = { |x| @ifZero [x, 0, x + (@sumTo [x - 1])] }; - | @sumTo [10] - |} + |sumTo = x -> ifZero x 0 (x + (sumTo (x-1))) + |sumTo 10 """.stripMargin - evalOld(code) shouldEqual 55 + eval(code) shouldEqual 55 } "Function calls" should "accept more arguments than needed and pass them to the result upon execution" in { val code = """ - |@{ - | f = { |x| { |z| x + z } }; - | - | @f [1, 2] - |} + |f = x -> z -> x + z + |f 1 2 |""".stripMargin - evalOld(code) shouldEqual 3 + eval(code) shouldEqual 3 } "Function calls" should "allow oversaturation and execute until completion" in { val code = """ - |@{ - | f = { | x, y | { | w | { | z | (x * y) + (w + z) } } }; - | - | @f [3, 3, 10, 1] - |} + |f = x y -> w -> z -> x * y + w + z + |f 3 3 10 1 |""".stripMargin - evalOld(code) shouldEqual 20 + eval(code) shouldEqual 20 } "Function calls" should "be able to return atoms that are evaluated with oversaturated args" in { @@ -88,27 +86,22 @@ class FunctionArgumentsTest extends InterpreterTest { """ |Unit.myMethod = 1 | - |@{ - | f = { |x| myMethod }; - | t = @f [10, @Unit]; - | - | t - |} + |f = x -> myMethod + |t = f 10 Unit + |t |""".stripMargin - evalOld(code) shouldEqual 1 + eval(code) shouldEqual 1 } "Recursion closing over lexical scope" should "work properly" in { val code = """ - |@{ - | summator = { |current| @ifZero [current, 0, @{@summator [current - 1]} ] }; - | res = @summator [0]; - | res - |} + |summator = current -> ifZero current 0 ((x -> summator (current - 1)) 0) + |res = summator 0 + |res |""".stripMargin - evalOld(code) shouldEqual 0 + eval(code) shouldEqual 0 } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/GlobalScopeTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/GlobalScopeTest.scala index c06e5f0dc66..8f73b117f4b 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/GlobalScopeTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/GlobalScopeTest.scala @@ -9,71 +9,68 @@ class GlobalScopeTest extends InterpreterTest { """ |Unit.a = 10 | - |@a [@Unit] + |a Unit """.stripMargin - evalOld(code) shouldEqual 10 + eval(code) shouldEqual 10 } "Functions" should "use values from the global scope in their bodies" in { val code = """ |Unit.a = 10 - |Unit.addTen = { |b| (@a [@Unit]) + b } + |Unit.addTen = b -> a Unit + b | - |@addTen [@Unit, 5] + |addTen Unit 5 """.stripMargin - evalOld(code) shouldEqual 15 + eval(code) shouldEqual 15 } "Functions" should "be able to call other functions in scope" in { val code = """ - |Unit.adder = { |a, b| a + b } + |Unit.adder = a b -> a + b | - |@{ |multiply| - | res = @adder [@Unit, 1, 2]; - | doubled = res * multiply; + |fn = multiply -> + | res = adder Unit 1 2 + | doubled = res * multiply | doubled - |} [2] + |fn 2 """.stripMargin - evalOld(code) shouldEqual 6 + eval(code) shouldEqual 6 } "Functions" should "be able to be passed as values when in scope" in { val code = """ - |Unit.adder = { |a, b| a + b } + |Unit.adder = a b -> a + b | - |Unit.binaryFn = { |a, b, function| - | result = @function [a, b]; + |Unit.binaryFn = a b function -> + | result = function a b | result - |} | - |@binaryFn [@Unit, 1, 2, { |a, b| @adder [@Unit, a, b] }] + |binaryFn Unit 1 2 (a b -> adder Unit a b) """.stripMargin - evalOld(code) shouldEqual 3 + eval(code) shouldEqual 3 } "Functions" should "be able to mutually recurse in the global scope" in { val code = """ - |Unit.decrementCall = { |number| - | res = number - 1; - | @fn1 [@Unit, res] - |} + |Unit.decrementCall = number -> + | res = number - 1 + | Unit.fn1 res | - |Unit.fn1 = { |number| - | @ifZero [number % 3, number, @decrementCall [@Unit, number]] - |} + |Unit.fn1 = number -> + | ifZero (number % 3) number (Unit.decrementCall number) | - |@fn1 [@Unit, 5] + |Unit.fn1 5 """.stripMargin - evalOld(code) shouldEqual 3 + eval(code) shouldEqual 3 } "Functions" should "be suspended within blocks" in { @@ -81,11 +78,11 @@ class GlobalScopeTest extends InterpreterTest { """ |Unit.a = 10/0 | - |Unit.b = { @a [@Unit] } + |Unit.b = Unit.a |b """.stripMargin - noException should be thrownBy evalOld(code) + noException should be thrownBy eval(code) } "Exceptions" should "be thrown when called" in { @@ -93,11 +90,11 @@ class GlobalScopeTest extends InterpreterTest { """ |Unit.a = 10/0 | - |Unit.b = { @a [@Unit] } - |@b [@Unit] + |Unit.b = Unit.a + |Unit.b """.stripMargin - an[InterpreterException] should be thrownBy evalOld(code) + an[InterpreterException] should be thrownBy eval(code) } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/InteropTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/InteropTest.scala index 4bd96da7ce4..1a02a73fc3a 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/InteropTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/InteropTest.scala @@ -6,36 +6,32 @@ class InteropTest extends InterpreterTest { "Interop library" should "support tail recursive functions" in { val code = """ - |@{ - | recurFun = { |i| @ifZero [i, 0, @recurFun [i - 1]] }; - | recurFun - |} + |recurFun = i -> ifZero i 0 (recurFun i-1) + |recurFun |""".stripMargin - val recurFun = evalOld(code) + val recurFun = eval(code) recurFun.call(15) shouldEqual 0 } "Interop library" should "support calling curried functions" in { val code = """ - |@{ - | fun = { |x, y, z| (x + y) + z }; - | @fun [y = 1] - |} + |fun = x y z -> x + y + z + |fun y=1 |""".stripMargin - val curriedFun = evalOld(code) + val curriedFun = eval(code) curriedFun.call(2, 3) shouldEqual 6 } "Interop library" should "support creating curried calls" in { val code = """ - |{ |x, y, z| (x + y) + z } + |x y z -> x + y + z |""".stripMargin - val fun = evalOld(code) + val fun = eval(code) fun.call(1).call(2).call(3) shouldEqual 6 } @@ -44,10 +40,10 @@ class InteropTest extends InterpreterTest { """ |Any.method = this | - |{ |x| method } + |x -> method |""".stripMargin - val fun = evalOld(code) + val fun = eval(code) fun.call(1, 2) shouldEqual 2 } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LexicalScopeTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LexicalScopeTest.scala deleted file mode 100644 index 88cfab94546..00000000000 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/LexicalScopeTest.scala +++ /dev/null @@ -1,64 +0,0 @@ -package org.enso.interpreter.test.semantic - -import org.enso.interpreter.test.{InterpreterException, InterpreterTest} - -class LexicalScopeTest extends InterpreterTest { - "Scope capture from outer scope" should "work" in { - val code = - """ - |@{ - | x = 10; - | @{ - | y = x; - | y + 5 - | } - |} - """.stripMargin - - evalOld(code) shouldEqual 15 - } - - "Variable shadowing" should "work" in { - val code = - """ - |@{ - | x = 10; - | @{ - | x = 5; - | x + 1 - | } - |} - """.stripMargin - evalOld(code) shouldEqual 6 - } - - "Variable redefinition in same scope" should "throw error" in { - val code = - """ - |@{ - | x = 10; - | @{ - | y = x; - | y = 5; - | y + 1 - | } - |} - """.stripMargin - the[InterpreterException] thrownBy evalOld(code) should have message "Variable y was already defined in this scope." - } - - "Reference to an undefined variable" should "throw error" in { - pending - //TODO [AA] Pending, because we're not yet sure what the behavior should be in the presence - // of dynamic dispatch. `y` in this code is actually equivalent to `x -> x.y`. - val code = - """ - |@{ - | x = 10; - | y - |} - """.stripMargin - the[InterpreterException] thrownBy evalOld(code) should have message "Variable y is not defined." - } - -} diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/MethodsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/MethodsTest.scala index 02729387acd..a0be449593b 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/MethodsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/MethodsTest.scala @@ -6,11 +6,75 @@ class MethodsTest extends InterpreterTest { "Methods" should "be defined in the global scope and dispatched to" in { val code = """ - |type Foo; - |Foo.bar = { |number| number + 1 } - |@bar [@Foo, 10] + |type Foo + |Foo.bar = number -> number + 1 + |bar Foo 10 |""".stripMargin - evalOld(code) shouldEqual 11 + eval(code) shouldEqual 11 + } + + "Methods" should "be callable with dot operator" in { + val code = + """ + |type Foo + |Foo.bar = number -> number + 1 + |Foo.bar 10 + |""".stripMargin + eval(code) shouldEqual 11 + } + + "Methods" should "be chainable with dot operator" in { + val code = + """ + |type Foo + |type Bar + |type Baz + | + |Foo.bar = Bar + |Bar.baz = x -> Baz + |Baz.spam = y -> y + 25 + | + |Foo.bar.baz 54 . spam 2 + |""".stripMargin + eval(code) shouldEqual 27 + } + + "Dot operator" should "behave like parenthesised when non-spaced" in { + val code = + """ + |type Foo + |type Bar + | + |Foo.bar = a b -> a + b + |Bar.constant = 10 + | + |Foo.bar Bar.constant Bar.constant + | + |""".stripMargin + eval(code) shouldEqual 20 + } + + "Methods" should "be able to be defined without arguments" in { + val code = + """ + |type Foo + |Foo.bar = 1 + |bar Foo + 5 + |""".stripMargin + eval(code) shouldEqual 6 + } + + "Methods" should "be definable as blocks without arguments" in { + val code = + """ + |Any.method = + | x = this * this + | y = x * 2 + | y + 1 + | + |3.method + |""".stripMargin + eval(code) shouldEqual 19 } "Methods" should "be dispatched to the proper constructor" in { @@ -30,19 +94,19 @@ class MethodsTest extends InterpreterTest { "Method call target" should "be passable by-name" in { val code = """ - |Unit.testMethod = { |x, y, z| (x + y) + z } - |@testMethod [x = 1, y = 2, this = @Unit, z = 3] + |Unit.testMethod = x y z -> x + y + z + |testMethod x=1 y=2 this=Unit z=3 |""".stripMargin - evalOld(code) shouldEqual 6 + eval(code) shouldEqual 6 } "Calling a non-existent method" should "throw an exception" in { val code = """ - |@foo [7] + |foo 7 |""".stripMargin - the[InterpreterException] thrownBy evalOld(code) should have message "Object Number does not define method foo." + the[InterpreterException] thrownBy eval(code) should have message "Object Number does not define method foo." } "Methods defined on Any type" should "be callable for any type" in { @@ -72,4 +136,24 @@ class MethodsTest extends InterpreterTest { evalOld(code) consumeOut shouldEqual List("1", "2", "3", "0", "0", "0") } + + "Test" should "test test" in { + pending +// val code = +// """ +// |Nil.sum = 0 +// |Cons.sum = case this of +// | Cons h t -> h + sum t +// | +// |myList = Cons (Cons (Cons 3 Nil) 2) 1 +// | +// |""".stripMargin + val code = + """ + |Cons.sum = case a of + | b + |""".stripMargin + + eval(code) shouldEqual 6 + } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala index 7d89f5a71e5..a7003b564e3 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/NamedArgumentsTest.scala @@ -7,178 +7,164 @@ class NamedArgumentsTest extends InterpreterTest { val code = """ |Unit.a = 10 - |Unit.addTen = { |b| (@a [@Unit]) + b } + |Unit.addTen = b -> a Unit + b | - |@addTen [@Unit, b = 10] + |addTen Unit (b = 10) """.stripMargin - evalOld(code) shouldEqual 20 + eval(code) shouldEqual 20 } "Functions" should "be able to have named arguments given out of order" in { val code = """ - |Unit.subtract = { |a, b| a - b } + |Unit.subtract = a b -> a - b | - |@subtract [@Unit, b = 10, a = 5] + |subtract Unit (b = 10) (a = 5) """.stripMargin - evalOld(code) shouldEqual -5 + eval(code) shouldEqual -5 } "Functions" should "be able to have scope values as named arguments" in { val code = """ - |@{ - | a = 10; - | addTen = { |num| num + a }; - | res = @addTen [num = a]; - | res - |} + |a = 10 + |addTen = num -> num + a + |res = addTen (num = a) + |res """.stripMargin - evalOld(code) shouldEqual 20 + eval(code) shouldEqual 20 } "Functions" should "be able to be defined with default argument values" in { val code = """ - |Unit.addNum = { |a, num = 10| a + num } + |Unit.addNum = a (num = 10) -> a + num | - |@addNum [@Unit, 5] + |addNum Unit 5 """.stripMargin - evalOld(code) shouldEqual 15 + eval(code) shouldEqual 15 } "Default arguments" should "be able to default to complex expressions" in { val code = """ - |Unit.add = { |a, b| a + b } + |Unit.add = a b -> a + b + |Unit.doThing = a (b = add Unit 1 2) -> a + b | - |Unit.doThing = { |a, b = @add [@Unit, 1, 2]| a + b } - | - |@doThing [@Unit, 10] + |doThing Unit 10 |""".stripMargin - evalOld(code) shouldEqual 13 + eval(code) shouldEqual 13 } "Default arguments" should "be able to close over their outer scope" in { val code = """ - |@{ - | id = { |x| x }; - | - | apply = { |val, fn = id| @fn [val] }; - | - | res = @apply [val = 1]; - | res - |} + |id = x -> x + |apply = val (fn = id) -> fn val + |res = apply (val = 1) + |res |""".stripMargin - evalOld(code) shouldEqual 1 + eval(code) shouldEqual 1 } "Functions" should "use their default values when none is supplied" in { val code = """ - |Unit.addTogether = { |a = 5, b = 6| a + b } + |Unit.addTogether = (a = 5) (b = 6) -> a + b | - |@addTogether [@Unit] + |addTogether Unit """.stripMargin - evalOld(code) shouldEqual 11 + eval(code) shouldEqual 11 } "Functions" should "override defaults by name" in { val code = """ - |Unit.addNum = { |a, num = 10| a + num } + |Unit.addNum = a (num = 10) -> a + num | - |@addNum [@Unit, 1, num = 1] + |addNum Unit 1 (num = 1) """.stripMargin - evalOld(code) shouldEqual 2 + eval(code) shouldEqual 2 } "Functions" should "override defaults by position" in { val code = """ - |Unit.addNum = { |a, num = 10| a + num } + |Unit.addNum = a (num = 10) -> a + num | - |@addNum [@Unit, 1, 2] + |addNum Unit 1 2 |""".stripMargin - evalOld(code) shouldEqual 3 + eval(code) shouldEqual 3 } "Defaulted arguments" should "work in a recursive context" in { val code = """ - |Unit.summer = { |sumTo| - | summator = { |acc = 0, current| - | @ifZero [current, acc, @summator [current = current - 1, acc = acc + current]] - | }; - | res = @summator [current = sumTo]; + |Unit.summer = sumTo -> + | summator = (acc = 0) current -> + | ifZero current acc (summator (current = current - 1) (acc = acc + current)) + | res = summator (current = sumTo) | res - |} | - |@summer [@Unit, 100] + |summer Unit 100 """.stripMargin - evalOld(code) shouldEqual 5050 + eval(code) shouldEqual 5050 } "Named Arguments" should "only be scoped to their definitions" in { val code = """ - |@{ - | foo = { |x, y| x - y }; - | bar = { |y, x| x - y }; - | - | baz = { |f| @f [x = 10, y = 11] }; - | - | a = @baz [foo]; - | b = @baz [bar]; - | - | a - b - |} + |foo = x y -> x - y + |bar = y x -> x - y + |baz = f -> f (x=10) (y=11) + |a = baz foo + |b = baz bar + |a - b |""".stripMargin - evalOld(code) shouldEqual 0 + eval(code) shouldEqual 0 } "Named arguments" should "be applied in a sequence compatible with Eta-expansions" in { pending val code = """ - |Unit.foo = { |a, b, c| a + b } - |@foo [@Unit, 20, a = 10] + |Unit.foo = a b c -> a + b + |foo Unit 20 (a = 10) |""".stripMargin } "Default arguments" should "be able to depend on prior arguments" in { val code = """ - |Unit.doubleOrAdd = { |a, b = a| a + b } + |Unit.doubleOrAdd = a (b = a) -> a + b | - |@doubleOrAdd [@Unit, 5] + |doubleOrAdd Unit 5 |""".stripMargin - evalOld(code) shouldEqual 10 + eval(code) shouldEqual 10 } "Default arguments" should "not be able to depend on later arguments" in { //TODO: Currently throws something equivalent to "Can't add dynamic symbol to Long". Needs rethinking. val code = """ - |Unit.badArgFn = { | a, b = c, c = a | (a + b) + c } + |Unit.badArgFn = a (b = c) (c = a) -> a + b + c | - |@badArgFn [@Unit, 3] + |badArgFn Unit 3 |""".stripMargin - an[InterpreterException] should be thrownBy evalOld(code) + an[InterpreterException] should be thrownBy eval(code) } "Constructors" should "be able to use named arguments" in { @@ -227,13 +213,13 @@ class NamedArgumentsTest extends InterpreterTest { "Default arguments to constructors" should "be resolved dynamically" in { val code = """ - |type Cons2 head (rest = Nil2); - |type Nil2; + |type Cons2 head (rest = Nil2) + |type Nil2 | |5 |""".stripMargin - evalOld(code) shouldEqual 5 + eval(code) shouldEqual 5 } "Constructors" should "be able to take and use default arguments" in { diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/SimpleArithmeticTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/SimpleArithmeticTest.scala index c6c3cf9fc18..32e4fc25a8c 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/SimpleArithmeticTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/SimpleArithmeticTest.scala @@ -22,4 +22,28 @@ class SimpleArithmeticTest extends InterpreterTest { "2 * 2 / 2" should "equal 2" in { eval("2 * 2 / 2") shouldEqual 2 } + + "Things" should "work" in { +// val code = +// """ +// |(Cons h t) -> h +// |""".stripMargin +// val code = +// """ +// |Cons h (Cons t Nil) +// |""".stripMargin +// val code = // type signature +// """ +// |a : Cons h t -> a +// |""".stripMargin +// val code = // lambda +// """ +// |(a : Cons h t) -> a +// |""".stripMargin +// val code = // lambda +// """ +// |(Cons h t : a) -> a +// |""".stripMargin + + } } diff --git a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/StateTest.scala b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/StateTest.scala index 11ac4c04e9a..8c0dced87d4 100644 --- a/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/StateTest.scala +++ b/engine/runtime/src/test/scala/org/enso/interpreter/test/semantic/StateTest.scala @@ -6,39 +6,35 @@ class StateTest extends InterpreterTest { "State" should "be accessible from functions" in { val code = """ - |@{ - | @put [@State, 10]; - | x = @get [@State]; - | @put [@State, x + 1]; - | @get [@State] - |} + |State.put 10 + |x = State.get + |State.put x+1 + |State.get |""".stripMargin - evalOld(code) shouldEqual 11 + eval(code) shouldEqual 11 } "State" should "be implicitly threaded through function executions" in { val code = """ - |Unit.incState = { - | x = @get [@State]; - | @put [@State, x + 1] - |} + |Unit.incState = + | x = State.get + | State.put x+1 | - |@{ - | @put [@State, 0]; - | @incState [@Unit]; - | @incState [@Unit]; - | @incState [@Unit]; - | @incState [@Unit]; - | @incState [@Unit]; - | @get [@State] - |} + |State.put 0 + |Unit.incState + |Unit.incState + |Unit.incState + |Unit.incState + |Unit.incState + |State.get |""".stripMargin - evalOld(code) shouldEqual 5 + eval(code) shouldEqual 5 } + // TODO [AA,MK]: New syntax must support suspended blocks like `myFun` here "State" should "be localized with State.run" in { val code = """ @@ -60,25 +56,22 @@ class StateTest extends InterpreterTest { "State" should "work well with recursive code" in { val code = """ - |@{ - | stateSum = { |n| - | acc = @get [@State]; - | @println[@IO, acc]; - | @put [@State, acc + n]; - | @ifZero [n, @get [@State], @stateSum [n-1]] - | }; - | @run [@State, 0, @stateSum [10]] - |} + |stateSum = n -> + | acc = State.get + | State.put acc+n + | ifZero n State.get (stateSum n-1) + | + |State.run 0 (stateSum 10) |""".stripMargin - evalOld(code) shouldEqual 55 + eval(code) shouldEqual 55 } "State" should "be initialized to a Unit by default" in { val code = """ - |@println[@IO, @get[@State]] + |IO.println State.get |""".stripMargin - evalOld(code) + eval(code) consumeOut shouldEqual List("Unit<>") } @@ -111,6 +104,7 @@ class StateTest extends InterpreterTest { | @recover[@Panic, @panicker]; | @get[@State] |} + | |""".stripMargin evalOld(code) shouldEqual (-5) }