Integrate a compilation phase into the interpreter (#303)

This commit is contained in:
Ara Adkins 2019-11-07 12:10:21 +00:00 committed by GitHub
parent 2d81b111b7
commit 460205c2b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 1334 additions and 504 deletions

1
.gitignore vendored
View File

@ -73,6 +73,7 @@ cabal.sandbox.config
############################ ############################
javadoc/ javadoc/
scaladoc/
####################### #######################
## Benchmark Reports ## ## Benchmark Reports ##

View File

@ -3,7 +3,7 @@
// All Scala files should be reformatted through this formatter before being // All Scala files should be reformatted through this formatter before being
// committed to the repositories. // committed to the repositories.
version = "2.0.0-RC8" version = "2.1.1"
// Wrapping and Alignment // Wrapping and Alignment
align = most align = most
@ -77,6 +77,5 @@ rewrite.sortModifiers.order = [
verticalMultiline.atDefnSite = false verticalMultiline.atDefnSite = false
verticalMultiline.arityThreshold = 6 verticalMultiline.arityThreshold = 6
// Please remember that `//format: off` and `//format: on` directives should be // Please remember that `//format: off` and `//format: on` directives should be
// used sparingly, if at all. // used sparingly, if at all.

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.bench.fixtures.semantic package org.enso.interpreter.bench.fixtures.semantic
import org.enso.interpreter.test.LanguageRunner import org.enso.interpreter.test.InterpreterRunner
class AtomFixtures extends LanguageRunner { class AtomFixtures extends InterpreterRunner {
val million: Long = 1000000 val million: Long = 1000000
val generateListCode = val generateListCode =

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.bench.fixtures.semantic package org.enso.interpreter.bench.fixtures.semantic
import org.enso.interpreter.test.LanguageRunner import org.enso.interpreter.test.InterpreterRunner
class NamedDefaultedArgumentFixtures extends LanguageRunner { class NamedDefaultedArgumentFixtures extends InterpreterRunner {
val hundredMillion: Long = 100000000 val hundredMillion: Long = 100000000
val sumTCOWithNamedArgumentsCode = val sumTCOWithNamedArgumentsCode =

View File

@ -1,9 +1,9 @@
package org.enso.interpreter.bench.fixtures.semantic package org.enso.interpreter.bench.fixtures.semantic
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import org.enso.interpreter.test.LanguageRunner import org.enso.interpreter.test.InterpreterRunner
class RecursionFixtures extends LanguageRunner { class RecursionFixtures extends InterpreterRunner {
val hundredMillion: Long = 100000000 val hundredMillion: Long = 100000000
val million: Long = 1000000 val million: Long = 1000000
val thousand: Long = 1000 val thousand: Long = 1000

View File

@ -1,10 +1,14 @@
package org.enso.interpreter; package org.enso.interpreter;
import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.instrumentation.ProvidedTags; import com.oracle.truffle.api.instrumentation.ProvidedTags;
import com.oracle.truffle.api.instrumentation.StandardTags; import com.oracle.truffle.api.instrumentation.StandardTags;
import com.oracle.truffle.api.nodes.RootNode;
import org.enso.interpreter.builder.FileDetector; import org.enso.interpreter.builder.FileDetector;
import org.enso.interpreter.node.ProgramRootNode;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.RuntimeOptions; import org.enso.interpreter.runtime.RuntimeOptions;
import org.graalvm.options.OptionDescriptors; import org.graalvm.options.OptionDescriptors;
@ -77,7 +81,10 @@ public final class Language extends TruffleLanguage<Context> {
*/ */
@Override @Override
protected CallTarget parse(ParsingRequest request) { protected CallTarget parse(ParsingRequest request) {
return getCurrentContext().parse(request.getSource()); RootNode root =
new ProgramRootNode(this, new FrameDescriptor(), "root", null, request.getSource());
return Truffle.getRuntime().createCallTarget(root);
} }
/** /**
@ -91,6 +98,7 @@ public final class Language extends TruffleLanguage<Context> {
/** /**
* Returns the supported options descriptors, for use by Graal's engine. * Returns the supported options descriptors, for use by Graal's engine.
*
* @return The supported options descriptors * @return The supported options descriptors
*/ */
@Override @Override

View File

@ -4,7 +4,7 @@ import com.oracle.truffle.api.Truffle;
import org.enso.interpreter.AstCallArgVisitor; import org.enso.interpreter.AstCallArgVisitor;
import org.enso.interpreter.AstExpression; import org.enso.interpreter.AstExpression;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.argument.CallArgument; import org.enso.interpreter.runtime.callable.argument.CallArgument;
import org.enso.interpreter.runtime.scope.LocalScope; import org.enso.interpreter.runtime.scope.LocalScope;
@ -61,7 +61,7 @@ public class CallArgFactory implements AstCallArgVisitor<CallArgument> {
name.orElse(null), name.orElse(null),
Truffle.getRuntime() Truffle.getRuntime()
.createCallTarget( .createCallTarget(
new EnsoRootNode( new ClosureRootNode(
language, childScope.getFrameDescriptor(), expr, null, displayName))); language, childScope.getFrameDescriptor(), expr, null, displayName)));
} }
} }

View File

@ -5,7 +5,7 @@ import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameSlot; import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.RootNode;
import org.enso.interpreter.*; import org.enso.interpreter.*;
import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.ApplicationNode; import org.enso.interpreter.node.callable.ApplicationNode;
import org.enso.interpreter.node.callable.ForceNodeGen; import org.enso.interpreter.node.callable.ForceNodeGen;
@ -234,7 +234,7 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
FunctionBodyNode fnBodyNode = FunctionBodyNode fnBodyNode =
new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr); new FunctionBodyNode(allFnExpressions.toArray(new ExpressionNode[0]), returnExpr);
RootNode fnRootNode = RootNode fnRootNode =
new EnsoRootNode( new ClosureRootNode(
language, scope.getFrameDescriptor(), fnBodyNode, null, "lambda::" + scopeName); language, scope.getFrameDescriptor(), fnBodyNode, null, "lambda::" + scopeName);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(fnRootNode);

View File

@ -11,7 +11,6 @@ import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.error.VariableDoesNotExistException; import org.enso.interpreter.runtime.error.VariableDoesNotExistException;
import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@ -20,7 +19,7 @@ import java.util.stream.IntStream;
* A {@code GlobalScopeExpressionFactory} is responsible for converting the top-level definitions of * A {@code GlobalScopeExpressionFactory} is responsible for converting the top-level definitions of
* an Enso program into AST nodes for the interpreter to evaluate. * an Enso program into AST nodes for the interpreter to evaluate.
*/ */
public class ModuleScopeExpressionFactory implements AstGlobalScopeVisitor<ExpressionNode> { public class ModuleScopeExpressionFactory implements AstModuleScopeVisitor<ExpressionNode> {
private final Language language; private final Language language;
private final ModuleScope moduleScope; private final ModuleScope moduleScope;
@ -41,7 +40,7 @@ public class ModuleScopeExpressionFactory implements AstGlobalScopeVisitor<Expre
* @param expr the expression to execute on * @param expr the expression to execute on
* @return a runtime node representing the top-level expression * @return a runtime node representing the top-level expression
*/ */
public ExpressionNode run(AstGlobalScope expr) { public ExpressionNode run(AstModuleScope expr) {
return expr.visit(this); return expr.visit(this);
} }
@ -55,17 +54,15 @@ public class ModuleScopeExpressionFactory implements AstGlobalScopeVisitor<Expre
* @return a runtime node representing the whole top-level program scope * @return a runtime node representing the whole top-level program scope
*/ */
@Override @Override
public ExpressionNode visitGlobalScope( public ExpressionNode visitModuleScope(
List<AstImport> imports, List<AstImport> imports,
List<AstTypeDef> typeDefs, List<AstTypeDef> typeDefs,
List<AstMethodDef> bindings, List<AstMethodDef> bindings,
AstExpression executableExpression) AstExpression executableExpression) {
throws IOException {
Context context = language.getCurrentContext(); Context context = language.getCurrentContext();
for (AstImport imp : imports) { for (AstImport imp : imports) {
moduleScope.addImport(context.requestParse(imp.name())); this.moduleScope.addImport(context.compiler().requestProcess(imp.name()));
} }
List<AtomConstructor> constructors = List<AtomConstructor> constructors =

View File

@ -0,0 +1,74 @@
package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.ReportPolymorphism;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.FrameUtil;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.state.Stateful;
/**
* This node represents the root of Enso closures and closure-like structures.
*
* <p>All new computations in Enso must be executed from within an {@link ClosureRootNode}, as
* determined by the API provided by Truffle.
*/
@ReportPolymorphism
public class ClosureRootNode extends EnsoRootNode {
@Child private ExpressionNode body;
/**
* Creates a new root node.
*
* @param language the language identifier
* @param frameDescriptor a description of the stack frame
* @param body the program body to be executed
* @param section a mapping from {@code body} to the program source
* @param name a name for the node
*/
public ClosureRootNode(
Language language,
FrameDescriptor frameDescriptor,
ExpressionNode body,
SourceSection section,
String name) {
super(
language,
frameDescriptor,
name,
section,
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
this.body = body;
}
/**
* Executes the node.
*
* @param frame the stack frame to execute in
* @return the result of executing this node
*/
@Override
public Object execute(VirtualFrame frame) {
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
frame.setObject(this.getStateFrameSlot(), state);
Object result = body.executeGeneric(frame);
state = FrameUtil.getObjectSafe(frame, this.getStateFrameSlot());
return new Stateful(state, result);
}
/**
* Sets whether the node is tail-recursive.
*
* @param isTail whether or not the node is tail-recursive.
*/
@Override
public void setTail(boolean isTail) {
CompilerDirectives.transferToInterpreterAndInvalidate();
body.setTail(isTail);
}
}

View File

@ -2,158 +2,91 @@ package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.dsl.ReportPolymorphism; import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.*; import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.nodes.RootNode;
import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.Context; import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.state.Stateful;
/**
* This node represents the root of all Enso computations.
*
* <p>All new computations in Enso must be executed from within an {@link EnsoRootNode}, as
* determined by the API provided by Truffle.
*/
@ReportPolymorphism
public class EnsoRootNode extends RootNode {
/** Flag wrapper for whether the resulting state should be returned or ignored. */
public enum ResultStateHandlingMode {
/** Return the new state. */
RETURN,
/** Ignore the new state. */
IGNORE;
/**
* Should the new state be returned?
*
* @return {@code true} if the new state should be returned, {@code false} otherwise.
*/
public boolean shouldReturn() {
return this == RETURN;
}
}
/** A common base class for all kinds of root node in Enso. */
public abstract class EnsoRootNode extends RootNode {
private final String name; private final String name;
private final SourceSection sourceSection; private final SourceSection sourceSection;
@Child private ExpressionNode body;
private final FrameSlot stateFrameSlot; private final FrameSlot stateFrameSlot;
private final ResultStateHandlingMode resultStateHandlingMode;
private @CompilerDirectives.CompilationFinal TruffleLanguage.ContextReference<Context> private @CompilerDirectives.CompilationFinal TruffleLanguage.ContextReference<Context>
contextReference; contextReference;
private @CompilerDirectives.CompilationFinal TruffleLanguage.LanguageReference<Language>
languageReference;
/** /**
* Creates a new root node. * Constructs the root node.
* *
* @param language the language identifier * @param language the language instance in which this will execute
* @param frameDescriptor a description of the stack frame * @param frameDescriptor a reference to the construct root frame
* @param body the program body to be executed * @param name the name of the construct
* @param section a mapping from {@code body} to the program source * @param sourceSection a reference to the source code being executed
* @param name a name for the node * @param stateFrameSlot the code to compile and execute
* @param resultStateHandlingMode whether this node should return the final state together with
* the result
*/ */
public EnsoRootNode( public EnsoRootNode(
Language language, Language language,
FrameDescriptor frameDescriptor, FrameDescriptor frameDescriptor,
ExpressionNode body,
SourceSection section,
String name, String name,
ResultStateHandlingMode resultStateHandlingMode) { SourceSection sourceSection,
FrameSlot stateFrameSlot) {
super(language, frameDescriptor); super(language, frameDescriptor);
this.body = body;
this.sourceSection = section;
this.name = name; this.name = name;
this.stateFrameSlot = frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object); this.sourceSection = sourceSection;
this.resultStateHandlingMode = resultStateHandlingMode; this.stateFrameSlot = stateFrameSlot;
} }
/** /**
* Creates a new root node. * Gets a reference to the language context associated with this program.
* *
* @param language the language identifier * @return a reference to the language context
* @param frameDescriptor a description of the stack frame
* @param body the program body to be executed
* @param section a mapping from {@code body} to the program source
* @param name a name for the node
*/ */
public EnsoRootNode( public Context getContext() {
Language language,
FrameDescriptor frameDescriptor,
ExpressionNode body,
SourceSection section,
String name) {
this(language, frameDescriptor, body, section, name, ResultStateHandlingMode.RETURN);
}
private Context getContext() {
if (contextReference == null) { if (contextReference == null) {
CompilerDirectives.transferToInterpreterAndInvalidate(); CompilerDirectives.transferToInterpreterAndInvalidate();
contextReference = lookupContextReference(Language.class); contextReference = lookupContextReference(Language.class);
} }
return contextReference.get(); return contextReference.get();
} }
/** /**
* Executes the node. * Creates a string representation of this node.
* *
* @param frame the stack frame to execute in * @return a string representation of the node
* @return the result of executing this node
*/
@Override
public Object execute(VirtualFrame frame) {
Object state =
frame.getArguments().length == 0
? getContext().getUnit().newInstance()
: Function.ArgumentsHelper.getState(frame.getArguments());
frame.setObject(stateFrameSlot, state);
Object result = body.executeGeneric(frame);
state = FrameUtil.getObjectSafe(frame, stateFrameSlot);
if (resultStateHandlingMode.shouldReturn()) {
return new Stateful(state, result);
} else {
return result;
}
}
/**
* Converts this node to a textual representation good for debugging.
*
* @return a {@link String} representation of this node
*/ */
@Override @Override
public String toString() { public String toString() {
return this.name; return this.name;
} }
/** Marks the node as tail-recursive. */
public void markTail() {
body.markTail();
}
/** Marks the node as not tail-recursive. */
public void markNotTail() {
body.markNotTail();
}
/** /**
* Sets whether the node is tail-recursive. * Sets whether the node is tail-recursive.
* *
* @param isTail whether or not the node is tail-recursive. * @param isTail whether or not the node is tail-recursive
*/ */
public void setTail(boolean isTail) { public abstract void setTail(boolean isTail);
body.setTail(isTail);
/**
* Gets the frame slot containing the program state.
*
* @return the state frame slot
*/
public FrameSlot getStateFrameSlot() {
return this.stateFrameSlot;
} }
/** /**
* Returns the frame slot reference to state variable. * Gets the source code represented by this node.
* *
* @return the frame slot corresponding to state monad * @return a reference to the source code
*/ */
FrameSlot getStateFrameSlot() { @Override
return stateFrameSlot; public SourceSection getSourceSection() {
return sourceSection;
} }
} }

View File

@ -0,0 +1,120 @@
package org.enso.interpreter.node;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.frame.FrameSlotKind;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import org.enso.interpreter.Language;
import org.enso.interpreter.runtime.Context;
/**
* This node handles static transformation of the input AST before execution and represents the root
* of an Enso program.
*
* <p>As much of the static transformation and analysis functionality required by the interpreter
* must have access to the interpreter, it must take place as part of the interpreter context. As a
* result, this node handles the transformations and re-writes
*/
public class ProgramRootNode extends EnsoRootNode {
private final Source sourceCode;
@Child private ExpressionNode ensoProgram = null;
private boolean programShouldBeTailRecursive = false;
/**
* Constructs the root node.
*
* @param language the language instance in which this will execute
* @param frameDescriptor a reference to the program root frame
* @param name the name of the program
* @param sourceSection a reference to the source code being executed
* @param sourceCode the code to compile and execute
*/
public ProgramRootNode(
Language language,
FrameDescriptor frameDescriptor,
String name,
SourceSection sourceSection,
Source sourceCode) {
super(
language,
frameDescriptor,
name,
sourceSection,
frameDescriptor.findOrAddFrameSlot("<<state>>", FrameSlotKind.Object));
this.sourceCode = sourceCode;
}
/**
* Executes the static analysis passes before executing the resultant program.
*
* @param frame the stack frame to execute in
* @return the result of executing this node
*/
@Override
public Object execute(VirtualFrame frame) {
Context context = getContext();
// Note [Static Passes]
if (this.ensoProgram == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
this.ensoProgram = this.insert(context.compiler().run(this.sourceCode));
this.ensoProgram.setTail(programShouldBeTailRecursive);
}
Object state = getContext().getUnit().newInstance();
frame.setObject(this.getStateFrameSlot(), state);
return this.ensoProgram.executeGeneric(frame);
}
/* Note [Static Passes]
* ~~~~~~~~~~~~~~~~~~~~
* Almost all of the static analysis functionality required by the interpreter requires access to
* the interpreter to execute small amounts of code. This is for purposes such as:
* - Type-level computation and evaluation during typechecking.
* - Compile-Time Function Evaluation (CTFE) for optimisation.
* - Various other re-write mechanisms that involve code execution.
*
* The contract expected from a Truffle Language states that there is to be no access to the
* interpreter context during parsing, which is the most natural time to perform these
* transformation passes. As a result, we have to perform them inside the interpreter once parsing
* is completed.
*
* To that end, we have a special kind of root node. It is constructed with the input AST only,
* and when executed acts as follows:
* 1. It takes the input source and executes a sequence of analyses and transformations such that
* the end result is a `Node`-based AST representing the program.
* 2. It rewrites itself to contain the program, and then executes that program.
*
* Note [Static Passes (Lack of Profiling)]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* While it is, in general, good practice to profile branches that don't depend on compilation
* final values in a truffle interpreter, this `if` is only ever executed once. This means that
* there is no need to profile it as the knowledge can't be used by the partial evaluator in any
* case.
*/
/**
* Sets whether the node is tail-recursive.
*
* @param isTail whether or not the node is tail-recursive.
*/
@Override
public void setTail(boolean isTail) {
// Note [Delayed Tail Calls]
this.programShouldBeTailRecursive = isTail;
}
/* Note [Delayed Tail Calls]
* ~~~~~~~~~~~~~~~~~~~~~~~~~
* As there is no guarantee that the program has been generated at the point at which setTail is
* called, we need to ensure that the tail-calledness information still makes its way to the
* program itself.
*
* To do this, we set a variable internally, that is then passed to the program just before it is
* executed.
*/
}

View File

@ -3,7 +3,7 @@ package org.enso.interpreter.node.callable.function;
import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.frame.VirtualFrame;
import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition; import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
import org.enso.interpreter.runtime.callable.function.ArgumentSchema; import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
@ -35,7 +35,7 @@ public class CreateFunctionNode extends ExpressionNode {
*/ */
@Override @Override
public void setTail(boolean isTail) { public void setTail(boolean isTail) {
((EnsoRootNode) callTarget.getRootNode()).setTail(isTail); ((ClosureRootNode) callTarget.getRootNode()).setTail(isTail);
} }
/** /**

View File

@ -1,28 +1,16 @@
package org.enso.interpreter.runtime; package org.enso.interpreter.runtime;
import com.oracle.truffle.api.CallTarget;
import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.TruffleLanguage;
import com.oracle.truffle.api.TruffleLanguage.Env; import com.oracle.truffle.api.TruffleLanguage.Env;
import com.oracle.truffle.api.frame.FrameDescriptor; import org.enso.compiler.Compiler;
import com.oracle.truffle.api.source.Source;
import org.enso.interpreter.AstGlobalScope;
import org.enso.interpreter.Constants;
import org.enso.interpreter.EnsoParser;
import org.enso.interpreter.Language; import org.enso.interpreter.Language;
import org.enso.interpreter.builder.ModuleScopeExpressionFactory;
import org.enso.interpreter.node.EnsoRootNode;
import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor; import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
import org.enso.interpreter.runtime.error.ModuleDoesNotExistException;
import org.enso.interpreter.runtime.scope.ModuleScope; import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.util.ScalaConversions; import org.enso.interpreter.util.ScalaConversions;
import org.enso.pkg.Package; import org.enso.pkg.Package;
import org.enso.pkg.SourceFile; import org.enso.pkg.SourceFile;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -37,7 +25,7 @@ public class Context {
private final Language language; private final Language language;
private final Env environment; private final Env environment;
private final Map<String, Module> knownFiles; private final Compiler compiler;
private final PrintStream out; private final PrintStream out;
private final Builtins builtins; private final Builtins builtins;
@ -55,7 +43,7 @@ public class Context {
List<File> packagePaths = RuntimeOptions.getPackagesPaths(environment); List<File> packagePaths = RuntimeOptions.getPackagesPaths(environment);
// TODO [MK] Replace getTruffleFile with getInternalTruffleFile when Graal 19.3.0 comes out. // TODO [MK] Replace getTruffleFile with getInternalTruffleFile when Graal 19.3.0 comes out.
this.knownFiles = Map<String, Module> knownFiles =
packagePaths.stream() packagePaths.stream()
.map(Package::fromDirectory) .map(Package::fromDirectory)
.map(ScalaConversions::asJava) .map(ScalaConversions::asJava)
@ -68,62 +56,23 @@ public class Context {
srcFile -> srcFile ->
new Module( new Module(
getEnvironment().getTruffleFile(srcFile.file().getAbsolutePath())))); getEnvironment().getTruffleFile(srcFile.file().getAbsolutePath()))));
this.compiler = new Compiler(this.language, knownFiles, this);
} }
/** /**
* Parses language sources, registering bindings in the given scope. * Gets the compiler instance.
* *
* @param source the source to be parsed * <p>The compiler is the portion of the interpreter that performs static analysis and
* @param scope the scope in which to register any new bindings * transformation passes on the input program. A handle to the compiler lets you execute various
* @return a call target which execution corresponds to the toplevel executable bits in the module * portions of the compilation pipeline, including parsing, analysis, and final code generation.
*/
public CallTarget parse(Source source, ModuleScope scope) {
AstGlobalScope parsed = new EnsoParser().parseEnso(source.getCharacters().toString());
ExpressionNode result = new ModuleScopeExpressionFactory(language, scope).run(parsed);
EnsoRootNode root =
new EnsoRootNode(
language,
new FrameDescriptor(),
result,
null,
"root",
EnsoRootNode.ResultStateHandlingMode.IGNORE);
return Truffle.getRuntime().createCallTarget(root);
}
/**
* Parses language sources.
* *
* @param source the source to be parsed * <p>Having this access available means that Enso programs can metaprogram Enso itself.
* @return a call target which execution corresponds to the toplevel executable bits in the module
*/
public CallTarget parse(Source source) {
return parse(source, createScope());
}
/**
* Parses language sources from file, registering bindings in the given scope.
* *
* @param file file containing the source to be parsed * @return a handle to the compiler
* @param scope the scope in which to register any new bindings
* @return a call target which execution corresponds to the toplevel executable bits in the module
* @throws IOException when the file could not be read
*/ */
public CallTarget parse(TruffleFile file, ModuleScope scope) throws IOException { public final Compiler compiler() {
return parse(Source.newBuilder(Constants.LANGUAGE_ID, file).build(), scope); return compiler;
}
/**
* Finds and parses a language source by its qualified name. Results of this operation are cached.
*
* @param qualifiedName the qualified name of module to parse
* @return the scope of the requested module
* @throws IOException when the source file could not be read
*/
public ModuleScope requestParse(String qualifiedName) throws IOException {
Module module = knownFiles.get(qualifiedName);
if (module == null) throw new ModuleDoesNotExistException(qualifiedName);
return module.requestParse(this);
} }
/** /**
@ -135,6 +84,15 @@ public class Context {
return environment; return environment;
} }
/**
* Gets the language to which this context belongs.
*
* @return the language to which this context belongs
*/
public Language getLanguage() {
return language;
}
/** /**
* Returns the standard output stream for this context. * Returns the standard output stream for this context.
* *
@ -155,8 +113,13 @@ public class Context {
return moduleScope; return moduleScope;
} }
private Builtins getBuiltins() { /**
return builtins; * Gets the builtin functions from the compiler.
*
* @return an object containing the builtin functions
*/
Builtins getBuiltins() {
return this.builtins;
} }
/** /**

View File

@ -27,9 +27,10 @@ public class Module {
* @throws IOException when the source file could not be read * @throws IOException when the source file could not be read
*/ */
public ModuleScope requestParse(Context context) throws IOException { public ModuleScope requestParse(Context context) throws IOException {
// TODO [AA] This needs to evolve to support scope execution
if (cachedScope == null) { if (cachedScope == null) {
cachedScope = context.createScope(); cachedScope = context.createScope();
context.parse(file, cachedScope); context.compiler().run(file, cachedScope);
} }
return cachedScope; return cachedScope;
} }

View File

@ -5,7 +5,7 @@ import com.oracle.truffle.api.RootCallTarget;
import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.Truffle;
import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameDescriptor;
import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.interop.TruffleObject;
import org.enso.interpreter.node.EnsoRootNode; import org.enso.interpreter.node.ClosureRootNode;
import org.enso.interpreter.node.ExpressionNode; import org.enso.interpreter.node.ExpressionNode;
import org.enso.interpreter.node.callable.argument.ReadArgumentNode; import org.enso.interpreter.node.callable.argument.ReadArgumentNode;
import org.enso.interpreter.node.expression.atom.InstantiateNode; import org.enso.interpreter.node.expression.atom.InstantiateNode;
@ -64,8 +64,8 @@ public class AtomConstructor implements TruffleObject {
argumentReaders[i] = new ReadArgumentNode(i, args[i].getDefaultValue().orElse(null)); argumentReaders[i] = new ReadArgumentNode(i, args[i].getDefaultValue().orElse(null));
} }
ExpressionNode instantiateNode = new InstantiateNode(this, argumentReaders); ExpressionNode instantiateNode = new InstantiateNode(this, argumentReaders);
EnsoRootNode rootNode = ClosureRootNode rootNode =
new EnsoRootNode( new ClosureRootNode(
null, new FrameDescriptor(), instantiateNode, null, "<constructor>:" + name); null, new FrameDescriptor(), instantiateNode, null, "<constructor>:" + name);
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode); RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);
return new Function(callTarget, null, new ArgumentSchema(args)); return new Function(callTarget, null, new ArgumentSchema(args));

View File

@ -46,12 +46,7 @@ public class ModuleScope {
* @return a map containing all the defined methods by name * @return a map containing all the defined methods by name
*/ */
private Map<String, Function> getMethodMapFor(AtomConstructor cons) { private Map<String, Function> getMethodMapFor(AtomConstructor cons) {
Map<String, Function> result = methods.get(cons); return methods.computeIfAbsent(cons, k -> new HashMap<>());
if (result == null) {
result = new HashMap<>();
methods.put(cons, result);
}
return result;
} }
/** /**
@ -154,6 +149,6 @@ public class ModuleScope {
public void addImport(ModuleScope scope) { public void addImport(ModuleScope scope) {
imports.add(scope); imports.add(scope);
transitiveImports.add(scope); transitiveImports.add(scope);
transitiveImports.addAll(scope.transitiveImports); transitiveImports.addAll(scope.getTransitiveImports());
} }
} }

View File

@ -0,0 +1,131 @@
package org.enso.compiler
import com.oracle.truffle.api.TruffleFile
import com.oracle.truffle.api.source.Source
import org.enso.compiler.generate.AstToIr
import org.enso.compiler.ir.IR
import org.enso.flexer.Reader
import org.enso.interpreter.Constants
import org.enso.interpreter.EnsoParser
import org.enso.interpreter.Language
import org.enso.interpreter.builder.ModuleScopeExpressionFactory
import org.enso.interpreter.node.ExpressionNode
import org.enso.interpreter.runtime.Context
import org.enso.interpreter.runtime.Module
import org.enso.interpreter.runtime.error.ModuleDoesNotExistException
import org.enso.interpreter.runtime.scope.ModuleScope
import org.enso.syntax.text.AST
import org.enso.syntax.text.Parser
import scala.collection.JavaConverters._
import scala.collection.mutable
/**
* This class encapsulates the static transformation processes that take place
* on source code, including parsing, desugaring, type-checking, static
* analysis, and optimisation.
*/
class Compiler(
val language: Language,
val files: java.util.Map[String, Module],
val context: Context
) {
val knownFiles: mutable.Map[String, Module] = files.asScala
/**
* Processes the provided language sources, registering any bindings in the
* given scope.
*
* @param source the source code to be processed
* @param scope the scope into which new bindings are registered
* @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 = {
val parsed =
new EnsoParser().parseEnso(source.getCharacters.toString)
new ModuleScopeExpressionFactory(language, scope).run(parsed)
}
// TODO [AA] This needs to evolve to support scope execution
/**
* Processes the language sources in the provided file, registering any
* bindings in the given scope.
*
* @param file the file containing the source code
* @param scope the scope into which new bindings are registered
* @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 = {
run(Source.newBuilder(Constants.LANGUAGE_ID, file).build, scope)
}
/**
* Processes the provided language sources, registering their bindings in a
* new scope.
*
* @param source the source code to be processed
* @return an interpreter node whose execution corresponds to the top-level
* executable functionality in the module corresponding to `source`.
*/
def run(source: Source): ExpressionNode = {
run(source, context.createScope)
}
/**
* Processes the language sources in the provided file, registering any
* bindings in a new scope.
*
* @param file the file containing the source code
* @return an interpreter node whose execution corresponds to the top-level
* executable functionality in the module corresponding to `source`.
*/
def run(file: TruffleFile): ExpressionNode = {
run(Source.newBuilder(Constants.LANGUAGE_ID, file).build)
}
/**
* Finds and processes a language source by its qualified name.
*
* The results of this operation are cached internally so we do not need to
* process the same source file multiple times.
*
* @param qualifiedName the qualified name of the module
* @return the scope containing all definitions in the requested module
*/
def requestProcess(qualifiedName: String): ModuleScope = {
knownFiles.get(qualifiedName) match {
case Some(module) => module.requestParse(language.getCurrentContext)
case None => throw new ModuleDoesNotExistException(qualifiedName)
}
}
/**
* Parses the provided language sources.
*
* @param source the code to parse
* @return an AST representation of `source`
*/
def parse(source: Source): AST = {
val parser: Parser = Parser()
val unresolvedAST: AST.Module =
parser.run(new Reader(source.getCharacters.toString))
val resolvedAST: AST.Module = parser.dropMacroMeta(unresolvedAST)
resolvedAST
}
/**
* Lowers the input AST to the compiler's high-level intermediate
* representation.
*
* @param sourceAST the parser AST input
* @return an IR representation with a 1:1 mapping to the parser AST
* constructs
*/
def translate(sourceAST: AST): IR = AstToIr.process(sourceAST)
}

View File

@ -0,0 +1,34 @@
package org.enso.compiler
/**
* A pass is a transformation from source type to sink type.
*
* Passes may take in additional information when run (e.g. analysis output),
* and may also output additional information.
*
* @tparam In the source type
* @tparam Out the sink type
*/
trait Pass[In, Out] {
/**
* A class representing the output of a pass.
*
* @param result the result of running the pass
* @param metadata any metadata produced by the pass
* @tparam TOut the type of the pass output metadata
*/
final case class Output[TOut](result: Out, metadata: TOut)
/**
* Executes the pass on the source, with optional input metadata.
*
* @param input the source to transform or analyse
* @param data metadata necessary foe the pass to execute correctly
* @tparam TIn the type of the input metadata
* @tparam TOut the type of the output metadata
* @return the results of executing the pass on `input` and `data`, as well
* as any metadata the pass produces
*/
def run[TIn, TOut](input: In, data: TIn): Output[TOut]
}

View File

@ -0,0 +1,13 @@
package org.enso.compiler.exception
/**
* This exception is thrown when compiler internal processing encounters an
* entity that it doesn't know how to deal with.
*
* @param entity the undhandled entity
* @param methodName the method throwing the exception
*/
class UnhandledEntity(entity: Any, methodName: String)
extends RuntimeException(
"Fatal: Unhandled entity in " + methodName + " = " + entity.toString
) {}

View File

@ -0,0 +1,260 @@
package org.enso.compiler.generate
import org.enso.compiler.exception.UnhandledEntity
import org.enso.compiler.ir.IR
import org.enso.syntax.text.AST
/**
* This is a representation of the raw conversion from the Parser [[AST AST]]
* to the internal [[IR IR]] used by the static transformation passes.
*/
object AstToIr {
/**
* Transforms the input [[AST]] into the compiler's high-level intermediate
* representation.
*
* @param inputAST the AST to transform
* @return a representation of the program construct represented by
* `inputAST` in the compiler's [[IR IR]]
*/
def process(inputAST: AST): IR = inputAST match {
case AST.App.any(inputAST) => processApplication(inputAST)
case AST.Block.any(inputAST) => processBlock(inputAST)
case AST.Comment.any(inputAST) => processComment(inputAST)
case AST.Ident.any(inputAST) => processIdent(inputAST)
case AST.Import.any(inputAST) => processBinding(inputAST)
case AST.Invalid.any(inputAST) => processInvalid(inputAST)
case AST.Literal.any(inputAST) => processLiteral(inputAST)
case AST.Mixfix.any(inputAST) => processApplication(inputAST)
case AST.Module.any(inputAST) => processModule(inputAST)
case AST.Group.any(inputAST) => processGroup(inputAST)
case AST.Def.any(inputAST) => processBinding(inputAST)
case AST.Foreign.any(inputAST) => processBlock(inputAST)
case _ =>
IR.Error.UnhandledAST(inputAST)
}
/**
* Transforms invalid entities from the parser AST.
*
* @param invalid the invalid entity
* @return a representation of `invalid` in the compiler's [[IR IR]]
*/
def processInvalid(invalid: AST.Invalid): IR.Error = invalid match {
case AST.Invalid.Unexpected(str, unexpectedTokens) =>
IR.Error.UnexpectedToken(str, unexpectedTokens.map(t => process(t.el)))
case AST.Invalid.Unrecognized(str) => IR.Error.UnrecognisedSymbol(str)
case AST.Ident.InvalidSuffix(identifier, suffix) =>
IR.Error.InvalidSuffix(processIdent(identifier), suffix)
case AST.Literal.Text.Unclosed(text) =>
IR.Error.UnclosedText(text.body.lines.toList.map(processLine))
case _ =>
throw new RuntimeException(
"Fatal: Unhandled entity in processInvalid = " + invalid
)
}
/**
* Transforms identifiers from the parser AST.
*
* @param identifier the identifier
* @return a representation of `identifier` in the compiler's [[IR IR]]
*/
def processIdent(identifier: AST.Ident): IR.Identifier = identifier match {
case AST.Ident.Blank(_) => IR.Identifier.Blank()
case AST.Ident.Var(name) => IR.Identifier.Variable(name)
case AST.Ident.Cons.any(identifier) => processIdentConstructor(identifier)
case AST.Ident.Opr.any(identifier) => processIdentOperator(identifier)
case AST.Ident.Mod(name) => IR.Identifier.Module(name)
case _ =>
throw new RuntimeException(
"Fatal: Unhandled entity in processIdent = " + identifier
)
}
/**
* Transforms an operator identifier from the parser AST.
*
* @param operator the operator to transform
* @return a representation of `operator` in the compiler's [[IR IR]]
*/
def processIdentOperator(
operator: AST.Ident.Opr
): IR.Identifier.Operator = IR.Identifier.Operator(operator.name)
/**
* Transforms a constructor identifier from the parser AST.
*
* @param constructor the constructor name to transform
* @return a representation of `constructor` in the compiler's [[IR IR]]
*/
def processIdentConstructor(
constructor: AST.Ident.Cons
): IR.Identifier.Constructor = IR.Identifier.Constructor(constructor.name)
/**
* Transforms a literal from the parser AST.
*
* @param literal the literal to transform
* @return a representation of `literal` in the compiler's [[IR IR]]
*/
def processLiteral(literal: AST.Literal): IR.Literal = {
literal match {
case AST.Literal.Number(base, number) => IR.Literal.Number(number, base)
case AST.Literal.Text.Raw(body) => {
IR.Literal.Text.Raw(body.lines.toList.map(processLine))
}
case AST.Literal.Text.Fmt(body) => {
IR.Literal.Text.Format(body.lines.toList.map(processLine))
}
case _ => throw new UnhandledEntity(literal, "processLiteral")
}
}
/**
* Transforms a line of a text literal from the parser AST.
*
* @param line the literal line to transform
* @return a representation of `line` in the compiler's [[IR IR]]
*/
def processLine(
line: AST.Literal.Text.LineOf[AST.Literal.Text.Segment[AST]]
): IR.Literal.Text.Line =
IR.Literal.Text.Line(line.elem.map(processTextSegment))
/**
* Transforms a segment of text from the parser AST.
*
* @param segment the text segment to transform
* @return a representation of `segment` in the compiler's [[IR IR]]
*/
def processTextSegment(
segment: AST.Literal.Text.Segment[AST]
): IR.Literal.Text.Segment = segment match {
case AST.Literal.Text.Segment._Plain(str) =>
IR.Literal.Text.Segment.Plain(str)
case AST.Literal.Text.Segment._Expr(expr) =>
IR.Literal.Text.Segment.Expression(expr.map(process))
case AST.Literal.Text.Segment._Escape(code) =>
IR.Literal.Text.Segment.EscapeCode(code)
case _ => throw new UnhandledEntity(segment, "processTextSegment")
}
/**
* Transforms a function application from the parser AST.
*
* @param application the function application to transform
* @return a representation of `application` in the compiler's [[IR IR]]
*/
def processApplication(application: AST): IR.Application =
application match {
case AST.App.Prefix(fn, arg) =>
IR.Application.Prefix(process(fn), process(arg))
case AST.App.Infix(leftArg, fn, rightArg) =>
IR.Application.Infix(
process(leftArg),
processIdentOperator(fn),
process(rightArg)
)
case AST.App.Section.Left(arg, fn) =>
IR.Application.Section.Left(process(arg), processIdentOperator(fn))
case AST.App.Section.Right(fn, arg) =>
IR.Application.Section.Right(processIdentOperator(fn), process(arg))
case AST.App.Section.Sides(fn) =>
IR.Application.Section.Sides(processIdentOperator(fn))
case AST.Mixfix(fnSegments, args) =>
IR.Application
.Mixfix(fnSegments.toList.map(processIdent), args.toList.map(process))
case _ =>
throw new UnhandledEntity(application, "processApplication")
}
/**
* Transforms a source code block from the parser AST.
*
* This handles both blocks of Enso-native code, and blocks of foreign
* language code.
*
* @param block the block to transform
* @return a representation of `block` in the compiler's [[IR IR]]
*/
def processBlock(block: AST): IR.Block = block match {
case AST.Block(_, _, firstLine, lines) =>
IR.Block
.Enso(
process(firstLine.elem) ::
lines.filter(t => t.elem.isDefined).map(t => process(t.elem.get))
)
case AST.Foreign(_, language, code) => IR.Block.Foreign(language, code)
case _ => throw new UnhandledEntity(block, "processBlock")
}
/**
* Transforms a module top-level from the parser AST.
*
* @param module the module to transform
* @return a representation of `module` in the compiler's [[IR IR]]
*/
def processModule(module: AST.Module): IR.Module = module match {
case AST.Module(lines) =>
IR.Module(
lines.filter(t => t.elem.isDefined).map(t => process(t.elem.get))
)
case _ => throw new UnhandledEntity(module, "processModule")
}
/**
* Transforms a comment from the parser AST.
*
* @param comment the comment to transform
* @return a representation of `comment` in the compiler's [[IR IR]]
*/
def processComment(comment: AST): IR.Comment = comment match {
case AST.Comment(lines) => IR.Comment(lines)
case _ => throw new UnhandledEntity(comment, "processComment")
}
/**
* Transforms a group from the parser AST.
*
* In [[IR]], groups are actually non-entities, as all grouping is handled
* implicitly by the IR format. A valid group is replaced by its contents in
* the IR, while invalid groups are replaced by error nodes.
*
* @param group the group to transform
* @return a representation of `group` in the compiler's [[IR IR]]
*/
def processGroup(group: AST): IR = group match {
case AST.Group(maybeAST) =>
maybeAST match {
case Some(ast) => process(ast)
case None => IR.Error.EmptyGroup()
}
case _ => throw new UnhandledEntity(group, "processGroup")
}
/**
* Transforms a binding from the parser AST.
*
* Bindings are any constructs that give some Enso language construct a name.
* This includes type definitions, imports, assignments, and so on.
*
* @param binding the binding to transform
* @return a representation of `binding` in the compiler's [[IR IR]]
*/
def processBinding(binding: AST): IR.Binding = binding match {
case AST.Def(constructor, arguments, optBody) =>
IR.Binding.RawType(
processIdentConstructor(constructor),
arguments.map(process),
optBody.map(process)
)
case AST.Import(components) => {
IR.Binding.Import(
components.toList.map(t => processIdentConstructor(t))
)
}
case _ => throw new UnhandledEntity(binding, "processBinding")
}
}

View File

@ -0,0 +1,5 @@
package org.enso.compiler.generate
class IrToTruffle {
}

View File

@ -0,0 +1,185 @@
package org.enso.compiler.ir
import org.enso.compiler.ir.IR.Literal.Text
import org.enso.syntax.text.AST
import org.enso.syntax.text.ast.text.Escape
/**
* This is the compiler's high-level intermediate representation.
*
* [[IR]] is a close match for the program structure of the source language,
* allowing it to be used for a number of high-level operations, including
* desugaring and analysis passes that rely on the structure of the source
* program to operate.
*/
sealed trait IR
object IR {
/**
* An expression is any language construct that returns a value, even if that
* value is `Unit`.
*/
sealed trait Expression extends IR
/**
* A module is the top-level construct of an Enso program.
*
* Modules currently have a one-to-one correspondence with the file scope,
* but this design may change in the future.
*
* @param elements all constructs contained within the module
*/
final case class Module(elements: List[IR]) extends Expression
/**
* An identifier is a name given to an Enso language construct.
*
* Each kind of identifier has different rules as to what constitutes
* validity, but these rules are enforced by the parser and so need not be
* checked at IR construction time.
*/
sealed trait Identifier extends Expression
object Identifier {
final case class Blank() extends Identifier
final case class Variable(name: String) extends Identifier
final case class Constructor(name: String) extends Identifier
final case class Operator(name: String) extends Identifier
final case class Module(name: String) extends Identifier
}
/**
* A binding is any top-level construct that creates a source-level primitive
* entity.
*/
sealed trait Binding extends Expression
object Binding {
final case class Import(modulePath: List[Identifier.Constructor])
extends Binding
final case class Type() extends Binding
final case class RawType(
constructor: Identifier.Constructor,
arguments: List[IR],
body: Option[IR]
) extends Binding
final case class Function() extends Binding
final case class Lambda() extends Binding
final case class Assignment() extends Binding
}
/**
* An application is any IR entity that applies a function to zero or more
* arguments.
*/
sealed trait Application extends Expression
object Application {
final case class Prefix(fn: IR, arg: IR) extends Application
final case class Infix(
left: IR,
fn: Identifier.Operator,
right: IR
) extends Application
final case class Mixfix(
segments: List[Identifier],
args: List[IR]
) extends Application
/**
* Operator sections are a syntactic construct that provide a short-hand
* for partial application of operators.
*/
sealed trait Section extends Application
object Section {
final case class Left(arg: IR, operator: Identifier.Operator)
extends Section
final case class Right(operator: Identifier.Operator, arg: IR)
extends Section
final case class Sides(operator: Identifier.Operator) extends Section
}
}
/** Literals are constant values provided as part of the program's source. */
sealed trait Literal extends Expression
object Literal {
final case class Number(number: String, base: Option[String])
extends Literal
/**
* Text literals in Enso come in two main types.
*
* Raw text literals are uninterpolated, and are passed through as they are
* provided in the program's source.
*
* Format text literals are literals that can contain Enso source-code
* expressions spliced into the literal. These expressions can be as simple
* as variable references, but are allowed to be arbitrary programs.
*/
sealed trait Text extends Literal
object Text {
final case class Raw(body: List[Text.Line]) extends Text
final case class Format(body: List[Text.Line]) extends Text
final case class Line(lineSegments: List[Segment])
sealed trait Segment extends Text
object Segment {
final case class Plain(text: String) extends Segment
final case class Expression(expr: Option[IR]) extends Segment
final case class EscapeCode(escape: Escape) extends Segment
}
}
}
/**
* Control flow constructs allow encoding non-linear programs.
*
* Enso technically only has the `case ... of` statement as its sole control
* flow construct. However, performance reasons force us to encode `if then`
* and `if then else` as independent constructs rather than as part of the
* standard library, so they are represented here.
*/
sealed trait Control extends Expression
object Control {
final case class Case() extends Expression
final case class IfThen() extends Expression
final case class IfThenElse() extends Expression
}
/** Constructs that purely represent program structure. */
sealed trait Block extends Expression
object Block {
final case class Enso(lines: List[IR]) extends Block
final case class Foreign(language: String, code: List[String]) extends Block
}
/** Constructs that represent various kinds of invalid programs. */
sealed trait Error extends IR
object Error {
final case class UnexpectedToken(msg: String, unexpectedIR: List[IR])
extends Error
final case class UnrecognisedSymbol(symbol: String) extends Error
final case class EmptyGroup() extends Error
final case class UnhandledAST(ast: AST) extends Error
final case class InvalidSuffix(identifier: IR, suffix: String) extends Error
final case class UnclosedText(lines: List[Text.Line]) extends Error
}
/** Comments in the program source. */
final case class Comment(lines: List[String]) extends IR
/** A representation of type signatures */
final case class Signature() extends IR
// UTILITY FUNCTIONS ========================================================
/**
* Checks whether a given IR node represents an invalid portion of a program.
*
* @param ir the node to analyse
* @return `true` if `ir` represents an invalid portion of the program,
* otherwise `false`
*/
def isErrorNode(ir: IR): Boolean = ir match {
case _: Error => true
}
}

View File

@ -50,10 +50,10 @@ trait AstExpressionVisitor[+T] {
def visitDesuspend(target: AstExpression): T def visitDesuspend(target: AstExpression): T
} }
trait AstGlobalScopeVisitor[+T] { trait AstModuleScopeVisitor[+T] {
@throws(classOf[Exception]) @throws(classOf[Exception])
def visitGlobalScope( def visitModuleScope(
imports: java.util.List[AstImport], imports: java.util.List[AstImport],
typeDefs: java.util.List[AstTypeDef], typeDefs: java.util.List[AstTypeDef],
bindings: java.util.List[AstMethodDef], bindings: java.util.List[AstMethodDef],
@ -73,12 +73,13 @@ case class AstMethodDef(typeName: String, methodName: String, fun: AstFunction)
case class AstImport(name: String) case class AstImport(name: String)
case class AstGlobalScope( case class AstModuleScope(
imports: List[AstImport], imports: List[AstImport],
bindings: List[AstGlobalSymbol], bindings: List[AstGlobalSymbol],
expression: AstExpression) { expression: AstExpression
) {
def visit[T](visitor: AstGlobalScopeVisitor[T]): T = { def visit[T](visitor: AstModuleScopeVisitor[T]): T = {
val types = new java.util.ArrayList[AstTypeDef]() val types = new java.util.ArrayList[AstTypeDef]()
val defs = new java.util.ArrayList[AstMethodDef]() val defs = new java.util.ArrayList[AstMethodDef]()
@ -87,7 +88,7 @@ case class AstGlobalScope(
case typeDef: AstTypeDef => types.add(typeDef) case typeDef: AstTypeDef => types.add(typeDef)
} }
visitor.visitGlobalScope(imports.asJava, types, defs, expression) visitor.visitModuleScope(imports.asJava, types, defs, expression)
} }
} }
@ -108,7 +109,8 @@ trait AstArgDefinitionVisitor[+T] {
case class AstArgDefinition( case class AstArgDefinition(
name: String, name: String,
defaultValue: Option[AstExpression], defaultValue: Option[AstExpression],
suspended: Boolean) { suspended: Boolean
) {
def visit[T](visitor: AstArgDefinitionVisitor[T], position: Int): T = def visit[T](visitor: AstArgDefinitionVisitor[T], position: Int): T =
visitor.visitArg( visitor.visitArg(
@ -167,8 +169,8 @@ case class AstVariable(name: String) extends AstExpression {
case class AstApply( case class AstApply(
fun: AstExpression, fun: AstExpression,
args: List[AstCallArg], args: List[AstCallArg],
hasDefaultsSuspended: Boolean) hasDefaultsSuspended: Boolean
extends AstExpression { ) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T = override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitFunctionApplication(fun, args.asJava, hasDefaultsSuspended) visitor.visitFunctionApplication(fun, args.asJava, hasDefaultsSuspended)
} }
@ -176,8 +178,8 @@ case class AstApply(
case class AstFunction( case class AstFunction(
arguments: List[AstArgDefinition], arguments: List[AstArgDefinition],
statements: List[AstExpression], statements: List[AstExpression],
ret: AstExpression) ret: AstExpression
extends AstExpression { ) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T = override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitFunction(arguments.asJava, statements.asJava, ret) visitor.visitFunction(arguments.asJava, statements.asJava, ret)
@ -189,8 +191,8 @@ case class AstFunction(
case class AstCaseFunction( case class AstCaseFunction(
arguments: List[AstArgDefinition], arguments: List[AstArgDefinition],
statements: List[AstExpression], statements: List[AstExpression],
ret: AstExpression) ret: AstExpression
extends AstExpression { ) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T = override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitCaseFunction(arguments.asJava, statements.asJava, ret) visitor.visitCaseFunction(arguments.asJava, statements.asJava, ret)
} }
@ -204,8 +206,8 @@ case class AstAssignment(name: String, body: AstExpression)
case class AstIfZero( case class AstIfZero(
cond: AstExpression, cond: AstExpression,
ifTrue: AstExpression, ifTrue: AstExpression,
ifFalse: AstExpression) ifFalse: AstExpression
extends AstExpression { ) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T = override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitIf(cond, ifTrue, ifFalse) visitor.visitIf(cond, ifTrue, ifFalse)
} }
@ -215,8 +217,8 @@ case class AstCase(cons: AstExpression, function: AstCaseFunction)
case class AstMatch( case class AstMatch(
target: AstExpression, target: AstExpression,
branches: Seq[AstCase], branches: Seq[AstCase],
fallback: Option[AstCaseFunction]) fallback: Option[AstCaseFunction]
extends AstExpression { ) extends AstExpression {
override def visit[T](visitor: AstExpressionVisitor[T]): T = override def visit[T](visitor: AstExpressionVisitor[T]): T =
visitor.visitMatch( visitor.visitMatch(
target, target,
@ -356,13 +358,13 @@ class EnsoParserInternal extends JavaTokenParsers {
case seg ~ segs => AstImport((seg :: segs).mkString(".")) case seg ~ segs => AstImport((seg :: segs).mkString("."))
} }
def globalScope: Parser[AstGlobalScope] = def globalScope: Parser[AstModuleScope] =
(importStmt *) ~ ((typeDef | methodDef) *) ~ expression ^^ { (importStmt *) ~ ((typeDef | methodDef) *) ~ expression ^^ {
case imports ~ assignments ~ expr => case imports ~ assignments ~ expr =>
AstGlobalScope(imports, assignments, expr) AstModuleScope(imports, assignments, expr)
} }
def parseGlobalScope(code: String): AstGlobalScope = { def parseGlobalScope(code: String): AstModuleScope = {
parseAll(globalScope, code).get parseAll(globalScope, code).get
} }
@ -373,7 +375,7 @@ class EnsoParserInternal extends JavaTokenParsers {
class EnsoParser { class EnsoParser {
def parseEnso(code: String): AstGlobalScope = { def parseEnso(code: String): AstModuleScope = {
new EnsoParserInternal().parseGlobalScope(code) new EnsoParserInternal().parseGlobalScope(code)
} }
} }

View File

@ -0,0 +1,5 @@
package org.enso.interpreter.analysis
class Pass {
}

View File

@ -0,0 +1,5 @@
package org.enso.interpreter.analysis
class StaticAnalyser {
}

View File

@ -0,0 +1,5 @@
package org.enso.interpreter.analysis.desugar
class DataTypeDefinition {
}

View File

@ -0,0 +1,5 @@
package org.enso.interpreter.analysis.desugar
class DesugarPhase {
}

View File

@ -0,0 +1,5 @@
package org.enso.interpreter.analysis.desugar
class NestedPatternMatch {
}

View File

@ -0,0 +1,5 @@
package org.enso.interpreter.analysis.desugar
class UnderscoreToLambda {
}

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.test package org.enso.interpreter.test
import org.graalvm.polyglot.{PolyglotException, Value} import org.graalvm.polyglot.PolyglotException
import org.graalvm.polyglot.Value
case class InterpreterException( case class InterpreterException(
@transient polyglotException: PolyglotException @transient polyglotException: PolyglotException
@ -13,8 +14,13 @@ case class InterpreterException(
} }
object InterpreterException { object InterpreterException {
def rethrowPolyglot(compute: => Value): Value = def rethrowPolyglot[T](compute: => T): T =
try { try {
compute compute
} catch { case e: PolyglotException => throw InterpreterException(e) } } catch { case e: PolyglotException => throw InterpreterException(e) }
implicit def toPolyglotException(
interpreterException: InterpreterException
): PolyglotException = interpreterException.polyglotException
} }

View File

@ -1,19 +1,14 @@
package org.enso.interpreter.test package org.enso.interpreter.test
import org.enso.interpreter.AstGlobalScope
import org.enso.interpreter.Constants
import org.enso.interpreter.EnsoParser
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.PolyglotException
import org.graalvm.polyglot.Value
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import org.scalactic.Equality import org.enso.interpreter.Constants
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Value
import org.scalatest.FlatSpec import org.scalatest.FlatSpec
import org.scalatest.Matchers import org.scalatest.Matchers
trait LanguageRunner { trait InterpreterRunner {
implicit class RichValue(value: Value) { implicit class RichValue(value: Value) {
def call(l: Long*): Value = def call(l: Long*): Value =
InterpreterException.rethrowPolyglot( InterpreterException.rethrowPolyglot(
@ -34,25 +29,12 @@ trait LanguageRunner {
result.lines.toList result.lines.toList
} }
def parse(code: String): AstGlobalScope = def parse(code: String): Value =
new EnsoParser().parseEnso(code) InterpreterException.rethrowPolyglot(eval(code))
implicit def toPolyglotException(
interpreterException: InterpreterException
): PolyglotException = interpreterException.polyglotException
} }
trait ValueEquality { trait InterpreterTest
implicit val valueEquality: Equality[Value] = (a: Value, b: Any) =>
b match {
case _: Long => a.isNumber && a.fitsInLong && a.asLong == b
case _: Int => a.isNumber && a.fitsInInt && a.asInt == b
case _ => false
}
}
trait LanguageTest
extends FlatSpec extends FlatSpec
with Matchers with Matchers
with LanguageRunner with InterpreterRunner
with ValueEquality with ValueEquality

View File

@ -0,0 +1,13 @@
package org.enso.interpreter.test
import org.graalvm.polyglot.Value
import org.scalactic.Equality
trait ValueEquality {
implicit val valueEquality: Equality[Value] = (a: Value, b: Any) =>
b match {
case _: Long => a.isNumber && a.fitsInLong && a.asLong == b
case _: Int => a.isNumber && a.fitsInInt && a.asInt == b
case _ => false
}
}

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterException, LanguageTest} import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
class ConstructorsTest extends LanguageTest { class ConstructorsTest extends InterpreterTest {
"Pattern matching" should "dispatch to the proper branch" in { "Pattern matching" should "dispatch to the proper branch" in {
val patternMatchingCode = val patternMatchingCode =

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
class CurryingTest extends LanguageTest { class CurryingTest extends InterpreterTest {
"Functions" should "allow partial application" in { "Functions" should "allow partial application" in {
val code = val code =
""" """
@ -14,6 +14,7 @@ class CurryingTest extends LanguageTest {
| result | result
|} |}
|""".stripMargin |""".stripMargin
eval(code) shouldEqual 11 eval(code) shouldEqual 11
} }

View File

@ -1,9 +1,9 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterException import org.enso.interpreter.test.InterpreterException
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
class ErrorsTest extends LanguageTest { class ErrorsTest extends InterpreterTest {
"Panics" should "be thrown and stop evaluation" in { "Panics" should "be thrown and stop evaluation" in {
val code = val code =
""" """

View File

@ -1,10 +1,14 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
class FunctionArgumentsTest extends LanguageTest { class FunctionArgumentsTest extends InterpreterTest {
"Functions" should "take arguments and use them in their bodies" in { "Functions" should "take arguments and use them in their bodies" in {
val code = "{ |x| x * x }" val code =
"""
|{ |x| x * x }
|""".stripMargin
val function = eval(code) val function = eval(code)
function.call(1) shouldEqual 1 function.call(1) shouldEqual 1
function.call(4) shouldEqual 16 function.call(4) shouldEqual 16
@ -18,7 +22,7 @@ class FunctionArgumentsTest extends LanguageTest {
| adder = { |b| @add [a,b] }; | adder = { |b| @add [a,b] };
| res = @adder [2]; | res = @adder [2];
| res | res
|} |}
""".stripMargin """.stripMargin
eval(code).call(3) shouldEqual 5 eval(code).call(3) shouldEqual 5

View File

@ -1,8 +1,9 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterException, LanguageTest} import org.enso.interpreter.test.InterpreterException
import org.enso.interpreter.test.InterpreterTest
class GlobalScopeTest extends LanguageTest { class GlobalScopeTest extends InterpreterTest {
"Variables" should "be able to be read from the global scope" in { "Variables" should "be able to be read from the global scope" in {
val code = val code =

View File

@ -1,6 +1,6 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.graalvm.polyglot.PolyglotException import org.enso.interpreter.test.InterpreterException
class ImportsTest extends PackageTest { class ImportsTest extends PackageTest {
"Atoms and methods" should "be available for import" in { "Atoms and methods" should "be available for import" in {
@ -16,6 +16,8 @@ class ImportsTest extends PackageTest {
} }
"Overloaded methods" should "not be visible when not imported" in { "Overloaded methods" should "not be visible when not imported" in {
the[PolyglotException] thrownBy evalTestProject("TestNonImportedOverloads") should have message "Object X does not define method method." the[InterpreterException] thrownBy evalTestProject(
"TestNonImportedOverloads"
) should have message "Object X does not define method method."
} }
} }

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
class InteropTest extends LanguageTest { class InteropTest extends InterpreterTest {
"Interop library" should "support tail recursive functions" in { "Interop library" should "support tail recursive functions" in {
val code = val code =
""" """
@ -11,6 +11,7 @@ class InteropTest extends LanguageTest {
| recurFun | recurFun
|} |}
|""".stripMargin |""".stripMargin
val recurFun = eval(code) val recurFun = eval(code)
recurFun.call(15) shouldEqual 0 recurFun.call(15) shouldEqual 0
} }
@ -23,6 +24,7 @@ class InteropTest extends LanguageTest {
| @fun [y = 1] | @fun [y = 1]
|} |}
|""".stripMargin |""".stripMargin
val curriedFun = eval(code) val curriedFun = eval(code)
curriedFun.call(2, 3) shouldEqual 6 curriedFun.call(2, 3) shouldEqual 6
} }
@ -32,6 +34,7 @@ class InteropTest extends LanguageTest {
""" """
|{ |x, y, z| (x + y) + z } |{ |x, y, z| (x + y) + z }
|""".stripMargin |""".stripMargin
val fun = eval(code) val fun = eval(code)
fun.call(1).call(2).call(3) shouldEqual 6 fun.call(1).call(2).call(3) shouldEqual 6
} }
@ -43,6 +46,7 @@ class InteropTest extends LanguageTest {
| |
|{ |x| method } |{ |x| method }
|""".stripMargin |""".stripMargin
val fun = eval(code) val fun = eval(code)
fun.call(1, 2) shouldEqual 2 fun.call(1, 2) shouldEqual 2
} }

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
class LazyArgumentsTest extends LanguageTest { class LazyArgumentsTest extends InterpreterTest {
val subject = "Lazy arguments" val subject = "Lazy arguments"
subject should "not get executed upfront" in { subject should "not get executed upfront" in {

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterException, LanguageTest} import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
class LexicalScopeTest extends LanguageTest { class LexicalScopeTest extends InterpreterTest {
"Scope capture from outer scope" should "work" in { "Scope capture from outer scope" should "work" in {
val code = val code =
""" """
@ -48,9 +48,9 @@ class LexicalScopeTest extends LanguageTest {
} }
"Reference to an undefined variable" should "throw error" in { "Reference to an undefined variable" should "throw error" in {
//TODO: 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`.
pending 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 = val code =
""" """
|@{ |@{

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.{InterpreterException, LanguageTest} import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
class MethodsTest extends LanguageTest { class MethodsTest extends InterpreterTest {
"Methods" should "be defined in the global scope and dispatched to" in { "Methods" should "be defined in the global scope and dispatched to" in {
val code = val code =
""" """

View File

@ -1,9 +1,9 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.InterpreterException import org.enso.interpreter.test.{InterpreterException, InterpreterTest}
import org.enso.interpreter.test.LanguageTest import org.graalvm.polyglot.PolyglotException
class NamedArgumentsTest extends LanguageTest { class NamedArgumentsTest extends InterpreterTest {
"Functions" should "take arguments by name and use them in their bodies" in { "Functions" should "take arguments by name and use them in their bodies" in {
val code = val code =
""" """
@ -151,6 +151,7 @@ class NamedArgumentsTest extends LanguageTest {
} }
"Named arguments" should "be applied in a sequence compatible with Eta-expansions" in { "Named arguments" should "be applied in a sequence compatible with Eta-expansions" in {
pending
val code = val code =
""" """
|Unit.foo = { |a, b, c| a + b } |Unit.foo = { |a, b, c| a + b }

View File

@ -4,10 +4,14 @@ import java.io.File
import org.enso.interpreter.Constants import org.enso.interpreter.Constants
import org.enso.interpreter.runtime.RuntimeOptions import org.enso.interpreter.runtime.RuntimeOptions
import org.enso.interpreter.test.InterpreterException
import org.enso.interpreter.test.ValueEquality import org.enso.interpreter.test.ValueEquality
import org.enso.pkg.Package import org.enso.pkg.Package
import org.graalvm.polyglot.{Context, Source, Value} import org.graalvm.polyglot.Context
import org.scalatest.{FlatSpec, Matchers} import org.graalvm.polyglot.Source
import org.graalvm.polyglot.Value
import org.scalatest.FlatSpec
import org.scalatest.Matchers
trait PackageTest extends FlatSpec with Matchers with ValueEquality { trait PackageTest extends FlatSpec with Matchers with ValueEquality {
@ -23,6 +27,8 @@ trait PackageTest extends FlatSpec with Matchers with ValueEquality {
.out(System.out) .out(System.out)
.in(System.in) .in(System.in)
.build() .build()
context.eval(Source.newBuilder(Constants.LANGUAGE_ID, mainFile).build) InterpreterException.rethrowPolyglot(
context.eval(Source.newBuilder(Constants.LANGUAGE_ID, mainFile).build)
)
} }
} }

View File

@ -1,8 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
class SimpleArithmeticTest extends LanguageTest { class SimpleArithmeticTest extends InterpreterTest {
"1 + 1" should "equal 2" in { "1 + 1" should "equal 2" in {
eval("1 + 1") shouldEqual 2 eval("1 + 1") shouldEqual 2
} }

View File

@ -1,10 +1,8 @@
package org.enso.interpreter.test.semantic package org.enso.interpreter.test.semantic
import org.enso.interpreter.test.LanguageTest import org.enso.interpreter.test.InterpreterTest
import scala.util.Try class StateTest extends InterpreterTest {
class StateTest extends LanguageTest {
"State" should "be accessible from functions" in { "State" should "be accessible from functions" in {
val code = val code =
""" """

View File

@ -8,13 +8,19 @@ import cats.Functor
import cats.derived._ import cats.derived._
import cats.implicits._ import cats.implicits._
import org.enso.data.List1._ import org.enso.data.List1._
import org.enso.data.{Index, List1, Pool, Shifted, Size, Span, Tree} import org.enso.data.Index
import org.enso.data.List1
import org.enso.data.Pool
import org.enso.data.Shifted
import org.enso.data.Size
import org.enso.data.Span
import org.enso.data.Tree
import org.enso.lint.Unused import org.enso.lint.Unused
import org.enso.syntax.text.ast.Repr.R import org.enso.syntax.text.ast.Repr.R
import org.enso.syntax.text.ast.Repr._ import org.enso.syntax.text.ast.Repr._
import org.enso.syntax.text.ast.Doc
import org.enso.syntax.text.ast.Repr import org.enso.syntax.text.ast.Repr
import org.enso.syntax.text.ast.opr import org.enso.syntax.text.ast.opr
import org.enso.syntax.text.ast.Doc
import scala.annotation.tailrec import scala.annotation.tailrec
import scala.reflect.ClassTag import scala.reflect.ClassTag
@ -260,19 +266,19 @@ object AST {
case a: ASTOf[_] => shape == a.shape case a: ASTOf[_] => shape == a.shape
case _ => false case _ => false
} }
val repr: Repr.Builder = cls.repr(shape) val repr: Repr.Builder = cls.repr(shape)
val span: Int = cls.repr(shape).span val span: Int = cls.repr(shape).span
def show(): String = repr.build() def show(): String = repr.build()
def setID(newID: ID): ASTOf[T] = copy(id = Some(newID)) def setID(newID: ID): ASTOf[T] = copy(id = Some(newID))
def withNewID(): ASTOf[T] = copy(id = Some(UUID.randomUUID())) def withNewID(): ASTOf[T] = copy(id = Some(UUID.randomUUID()))
def map(f: AST => AST): ASTOf[T] = copy(shape = cls.map(shape)(f)) def map(f: AST => AST): ASTOf[T] = copy(shape = cls.map(shape)(f))
def mapWithOff(f: (Index, AST) => AST): ASTOf[T] = def mapWithOff(f: (Index, AST) => AST): ASTOf[T] =
copy(shape = cls.mapWithOff(shape)(f)) copy(shape = cls.mapWithOff(shape)(f))
def zipWithOffset(): T[(Index, AST)] = cls.zipWithOffset(shape) def zipWithOffset(): T[(Index, AST)] = cls.zipWithOffset(shape)
} }
object ASTOf { object ASTOf {
implicit def repr[T[_]]: Repr[ASTOf[T]] = _.repr implicit def repr[T[_]]: Repr[ASTOf[T]] = _.repr
implicit def unwrap[T[_]](t: ASTOf[T]): T[AST] = t.shape implicit def unwrap[T[_]](t: ASTOf[T]): T[AST] = t.shape
implicit def wrap[T[_]](t: T[AST])( implicit def wrap[T[_]](t: T[AST])(
implicit implicit
ev: ASTClass[T] ev: ASTClass[T]
@ -287,10 +293,10 @@ object AST {
* is used to cache all necessary operations during AST construction. * is used to cache all necessary operations during AST construction.
*/ */
trait ASTClass[T[_]] { trait ASTClass[T[_]] {
def repr(t: T[AST]): Repr.Builder def repr(t: T[AST]): Repr.Builder
def map(t: T[AST])(f: AST => AST): T[AST] def map(t: T[AST])(f: AST => AST): T[AST]
def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST] def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST]
def zipWithOffset(t: T[AST]): T[(Index, AST)] def zipWithOffset(t: T[AST]): T[(Index, AST)]
} }
object ASTClass { object ASTClass {
def apply[T[_]](implicit cls: ASTClass[T]): ASTClass[T] = cls def apply[T[_]](implicit cls: ASTClass[T]): ASTClass[T] = cls
@ -301,9 +307,9 @@ object AST {
evOzip: OffsetZip[T, AST] evOzip: OffsetZip[T, AST]
): ASTClass[T] = ): ASTClass[T] =
new ASTClass[T] { new ASTClass[T] {
def repr(t: T[AST]): Repr.Builder = evRepr.repr(t) def repr(t: T[AST]): Repr.Builder = evRepr.repr(t)
def map(t: T[AST])(f: AST => AST): T[AST] = Functor[T].map(t)(f) def map(t: T[AST])(f: AST => AST): T[AST] = Functor[T].map(t)(f)
def zipWithOffset(t: T[AST]): T[(Index, AST)] = OffsetZip(t) def zipWithOffset(t: T[AST]): T[(Index, AST)] = OffsetZip(t)
def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST] = def mapWithOff(t: T[AST])(f: (Index, AST) => AST): T[AST] =
Functor[T].map(zipWithOffset(t))(f.tupled) Functor[T].map(zipWithOffset(t))(f.tupled)
} }
@ -374,8 +380,8 @@ object AST {
val any = UnapplyByType[Invalid] val any = UnapplyByType[Invalid]
object Unrecognized { object Unrecognized {
val any = UnapplyByType[Unrecognized] val any = UnapplyByType[Unrecognized]
def unapply(t: AST) = Unapply[Unrecognized].run(_.str)(t) def unapply(t: AST) = Unapply[Unrecognized].run(_.str)(t)
def apply(str: String): Unrecognized = UnrecognizedOf[AST](str) def apply(str: String): Unrecognized = UnrecognizedOf[AST](str)
} }
object Unexpected { object Unexpected {
@ -388,15 +394,15 @@ object AST {
//// Instances //// //// Instances ////
object UnrecognizedOf { object UnrecognizedOf {
implicit def ftor: Functor[UnrecognizedOf] = semi.functor implicit def ftor: Functor[UnrecognizedOf] = semi.functor
implicit def fold: Foldable[UnrecognizedOf] = semi.foldable implicit def fold: Foldable[UnrecognizedOf] = semi.foldable
implicit def repr[T]: Repr[UnrecognizedOf[T]] = _.str implicit def repr[T]: Repr[UnrecognizedOf[T]] = _.str
implicit def ozip[T]: OffsetZip[UnrecognizedOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[UnrecognizedOf, T] = t => t.coerce
} }
object UnexpectedOf { object UnexpectedOf {
implicit def ftor: Functor[UnexpectedOf] = semi.functor implicit def ftor: Functor[UnexpectedOf] = semi.functor
implicit def fold: Foldable[UnexpectedOf] = semi.foldable implicit def fold: Foldable[UnexpectedOf] = semi.foldable
implicit def repr[T: Repr]: Repr[UnexpectedOf[T]] = t => Repr(t.stream) implicit def repr[T: Repr]: Repr[UnexpectedOf[T]] = t => Repr(t.stream)
implicit def ozip[T: Repr]: OffsetZip[UnexpectedOf, T] = implicit def ozip[T: Repr]: OffsetZip[UnexpectedOf, T] =
t => t.copy(stream = OffsetZip(t.stream)) t => t.copy(stream = OffsetZip(t.stream))
} }
@ -446,32 +452,32 @@ object AST {
//// Instances //// //// Instances ////
object BlankOf { object BlankOf {
implicit def ftor: Functor[BlankOf] = semi.functor implicit def ftor: Functor[BlankOf] = semi.functor
implicit def fold: Foldable[BlankOf] = semi.foldable implicit def fold: Foldable[BlankOf] = semi.foldable
implicit def repr[T]: Repr[BlankOf[T]] = _.name implicit def repr[T]: Repr[BlankOf[T]] = _.name
implicit def ozip[T]: OffsetZip[BlankOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[BlankOf, T] = t => t.coerce
} }
object VarOf { object VarOf {
implicit def ftor: Functor[VarOf] = semi.functor implicit def ftor: Functor[VarOf] = semi.functor
implicit def fold: Foldable[VarOf] = semi.foldable implicit def fold: Foldable[VarOf] = semi.foldable
implicit def repr[T]: Repr[VarOf[T]] = _.name implicit def repr[T]: Repr[VarOf[T]] = _.name
implicit def ozip[T]: OffsetZip[VarOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[VarOf, T] = t => t.coerce
} }
object ConsOf { object ConsOf {
implicit def ftor: Functor[ConsOf] = semi.functor implicit def ftor: Functor[ConsOf] = semi.functor
implicit def fold: Foldable[ConsOf] = semi.foldable implicit def fold: Foldable[ConsOf] = semi.foldable
implicit def repr[T]: Repr[ConsOf[T]] = _.name implicit def repr[T]: Repr[ConsOf[T]] = _.name
implicit def ozip[T]: OffsetZip[ConsOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[ConsOf, T] = t => t.coerce
} }
object OprOf { object OprOf {
implicit def ftor: Functor[OprOf] = semi.functor implicit def ftor: Functor[OprOf] = semi.functor
implicit def fold: Foldable[OprOf] = semi.foldable implicit def fold: Foldable[OprOf] = semi.foldable
implicit def repr[T]: Repr[OprOf[T]] = _.name implicit def repr[T]: Repr[OprOf[T]] = _.name
implicit def ozip[T]: OffsetZip[OprOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[OprOf, T] = t => t.coerce
} }
object ModOf { object ModOf {
implicit def ftor: Functor[ModOf] = semi.functor implicit def ftor: Functor[ModOf] = semi.functor
implicit def fold: Foldable[ModOf] = semi.foldable implicit def fold: Foldable[ModOf] = semi.foldable
implicit def repr[T]: Repr[ModOf[T]] = R + _.name + "=" implicit def repr[T]: Repr[ModOf[T]] = R + _.name + "="
implicit def ozip[T]: OffsetZip[ModOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[ModOf, T] = t => t.coerce
} }
@ -479,10 +485,10 @@ object AST {
//// Conversions //// //// Conversions ////
trait Conversions1 { trait Conversions1 {
implicit def strToVar(str: String): Var = Var(str) implicit def strToVar(str: String): Var = Var(str)
implicit def strToCons(str: String): Cons = Cons(str) implicit def strToCons(str: String): Cons = Cons(str)
implicit def strToOpr(str: String): Opr = Opr(str) implicit def strToOpr(str: String): Opr = Opr(str)
implicit def strToMod(str: String): Mod = Mod(str) implicit def strToMod(str: String): Mod = Mod(str)
} }
trait conversions extends Conversions1 { trait conversions extends Conversions1 {
@ -503,31 +509,31 @@ object AST {
private val blank = BlankOf[AST]() private val blank = BlankOf[AST]()
val any = UnapplyByType[Blank] val any = UnapplyByType[Blank]
def unapply(t: AST) = Unapply[Blank].run(_ => true)(t) def unapply(t: AST) = Unapply[Blank].run(_ => true)(t)
def apply(): Blank = blank def apply(): Blank = blank
} }
object Var { object Var {
private val pool = new Pool[VarOf[AST]]() private val pool = new Pool[VarOf[AST]]()
val any = UnapplyByType[Var] val any = UnapplyByType[Var]
def unapply(t: AST) = Unapply[Var].run(_.name)(t) def unapply(t: AST) = Unapply[Var].run(_.name)(t)
def apply(name: String): Var = pool.get(VarOf[AST](name)) def apply(name: String): Var = pool.get(VarOf[AST](name))
} }
object Cons { object Cons {
private val pool = new Pool[ConsOf[AST]]() private val pool = new Pool[ConsOf[AST]]()
val any = UnapplyByType[Cons] val any = UnapplyByType[Cons]
def unapply(t: AST) = Unapply[Cons].run(_.name)(t) def unapply(t: AST) = Unapply[Cons].run(_.name)(t)
def apply(name: String): Cons = pool.get(ConsOf[AST](name)) def apply(name: String): Cons = pool.get(ConsOf[AST](name))
} }
object Mod { object Mod {
private val pool = new Pool[ModOf[AST]]() private val pool = new Pool[ModOf[AST]]()
val any = UnapplyByType[Mod] val any = UnapplyByType[Mod]
def unapply(t: AST) = Unapply[Mod].run(_.name)(t) def unapply(t: AST) = Unapply[Mod].run(_.name)(t)
def apply(name: String): Mod = pool.get(ModOf[AST](name)) def apply(name: String): Mod = pool.get(ModOf[AST](name))
} }
object Opr { object Opr {
private val pool = new Pool[OprOf[AST]]() private val pool = new Pool[OprOf[AST]]()
val app = Opr(" ") val app = Opr(" ")
val any = UnapplyByType[Opr] val any = UnapplyByType[Opr]
def unapply(t: AST) = Unapply[Opr].run(_.name)(t) def unapply(t: AST) = Unapply[Opr].run(_.name)(t)
def apply(name: String): Opr = pool.get(OprOf[AST](name)) def apply(name: String): Opr = pool.get(OprOf[AST](name))
} }
@ -540,8 +546,8 @@ object AST {
extends InvalidOf[T] extends InvalidOf[T]
with Phantom with Phantom
object InvalidSuffixOf { object InvalidSuffixOf {
implicit def ftor: Functor[InvalidSuffixOf] = semi.functor implicit def ftor: Functor[InvalidSuffixOf] = semi.functor
implicit def fold: Foldable[InvalidSuffixOf] = semi.foldable implicit def fold: Foldable[InvalidSuffixOf] = semi.foldable
implicit def ozip[T]: OffsetZip[InvalidSuffixOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[InvalidSuffixOf, T] = t => t.coerce
implicit def repr[T]: Repr[InvalidSuffixOf[T]] = implicit def repr[T]: Repr[InvalidSuffixOf[T]] =
t => R + t.elem + t.suffix t => R + t.elem + t.suffix
@ -592,12 +598,12 @@ object AST {
//// Smart Constructors //// //// Smart Constructors ////
def apply(i: String): Number = Number(None, i) def apply(i: String): Number = Number(None, i)
def apply(b: String, i: String): Number = Number(Some(b), i) def apply(b: String, i: String): Number = Number(Some(b), i)
def apply(i: Int): Number = Number(i.toString) def apply(i: Int): Number = Number(i.toString)
def apply(b: Int, i: String): Number = Number(b.toString, i) def apply(b: Int, i: String): Number = Number(b.toString, i)
def apply(b: String, i: Int): Number = Number(b, i.toString) def apply(b: String, i: Int): Number = Number(b, i.toString)
def apply(b: Int, i: Int): Number = Number(b.toString, i.toString) def apply(b: Int, i: Int): Number = Number(b.toString, i.toString)
def apply(b: Option[String], i: String): Number = def apply(b: Option[String], i: String): Number =
NumberOf[AST](b, i) NumberOf[AST](b, i)
def unapply(t: AST) = Unapply[Number].run(t => (t.base, t.int))(t) def unapply(t: AST) = Unapply[Number].run(t => (t.base, t.int))(t)
@ -610,14 +616,14 @@ object AST {
extends InvalidOf[T] extends InvalidOf[T]
with Phantom with Phantom
object DanglingBase { object DanglingBase {
val any = UnapplyByType[DanglingBase] val any = UnapplyByType[DanglingBase]
def apply(base: String): DanglingBase = DanglingBaseOf[AST](base) def apply(base: String): DanglingBase = DanglingBaseOf[AST](base)
def unapply(t: AST) = def unapply(t: AST) =
Unapply[DanglingBase].run(_.base)(t) Unapply[DanglingBase].run(_.base)(t)
} }
object DanglingBaseOf { object DanglingBaseOf {
implicit def ftor: Functor[DanglingBaseOf] = semi.functor implicit def ftor: Functor[DanglingBaseOf] = semi.functor
implicit def fold: Foldable[DanglingBaseOf] = semi.foldable implicit def fold: Foldable[DanglingBaseOf] = semi.foldable
implicit def ozip[T]: OffsetZip[DanglingBaseOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[DanglingBaseOf, T] = t => t.coerce
implicit def repr[T]: Repr[DanglingBaseOf[T]] = R + _.base + '_' implicit def repr[T]: Repr[DanglingBaseOf[T]] = R + _.base + '_'
} }
@ -626,10 +632,10 @@ object AST {
//// Instances //// //// Instances ////
object NumberOf { object NumberOf {
implicit def fromInt[T](int: Int): Number = Number(int) implicit def fromInt[T](int: Int): Number = Number(int)
implicit def ftor: Functor[NumberOf] = semi.functor implicit def ftor: Functor[NumberOf] = semi.functor
implicit def fold: Foldable[NumberOf] = semi.foldable implicit def fold: Foldable[NumberOf] = semi.foldable
implicit def ozip[T]: OffsetZip[NumberOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[NumberOf, T] = t => t.coerce
implicit def repr[T]: Repr[NumberOf[T]] = implicit def repr[T]: Repr[NumberOf[T]] =
t => t.base.map(_ + "_").getOrElse("") + t.int t => t.base.map(_ + "_").getOrElse("") + t.int
} }
@ -640,7 +646,7 @@ object AST {
type Text = ASTOf[TextOf] type Text = ASTOf[TextOf]
sealed trait TextOf[T] extends ShapeOf[T] { sealed trait TextOf[T] extends ShapeOf[T] with LiteralOf[T] {
val body: Text.BodyOf[Text.Segment[T]] val body: Text.BodyOf[Text.Segment[T]]
val quoteChar: Char val quoteChar: Char
def quoteRepr: String = quoteChar.toString * body.quote.asInt def quoteRepr: String = quoteChar.toString * body.quote.asInt
@ -686,21 +692,34 @@ object AST {
} }
case class UnclosedOf[T](text: TextOf[T]) extends AST.InvalidOf[T] case class UnclosedOf[T](text: TextOf[T]) extends AST.InvalidOf[T]
// The body of the text. For a given quote type `t`, `q` can be either
// single or triple. Triple allows for using that quote type within the
// body. `q` is the number of the quote type `t`.
// - Body contains expression segments for the interpolation.
object Body { object Body {
def apply[S <: Segment[AST]](q: Quote, s: S*) = def apply[S <: Segment[AST]](q: Quote, s: S*) =
BodyOf(q, List1(LineOf(0, s.to[List]))) BodyOf(q, List1(LineOf(0, s.to[List])))
} }
// These are non-interpolated strings, using `"` as the quote type.
object Raw { object Raw {
def apply(body: BodyOf[Segment._Raw[AST]]): Raw = RawOf(body) def apply(body: BodyOf[Segment._Raw[AST]]): Raw = RawOf(body)
def unapply(t: AST): Option[BodyOf[Segment._Raw[AST]]] =
Unapply[Raw].run(t => t.body)(t)
} }
// These are interpolated strings, using `'` as the quote type.
object Fmt { object Fmt {
def apply(body: BodyOf[Segment._Fmt[AST]]): Fmt = FmtOf(body) def apply(body: BodyOf[Segment._Fmt[AST]]): Fmt = FmtOf(body)
def unapply(t: AST): Option[BodyOf[Segment._Fmt[AST]]] =
Unapply[Fmt].run(t => t.body)(t)
} }
// An unclosed text literal (of either kind).
object Unclosed { object Unclosed {
def apply(text: TextOf[AST]): Unclosed = UnclosedOf(text) def apply(text: TextOf[AST]): Unclosed = UnclosedOf(text)
def unapply(t: AST): Option[TextOf[AST]] =
Unapply[Unclosed].run(t => t.text)(t)
} }
//// Instances //// //// Instances ////
@ -730,9 +749,9 @@ object AST {
} }
} }
object LineOf { object LineOf {
implicit def ftor: Functor[LineOf] = semi.functor implicit def ftor: Functor[LineOf] = semi.functor
implicit def fold: Foldable[LineOf] = semi.foldable implicit def fold: Foldable[LineOf] = semi.foldable
implicit def repr[T: Repr]: Repr[LineOf[T]] = R + _.elem.map(R + _) implicit def repr[T: Repr]: Repr[LineOf[T]] = R + _.elem.map(R + _)
implicit def ozip[T: Repr]: OffsetZip[LineOf, T] = t => { implicit def ozip[T: Repr]: OffsetZip[LineOf, T] = t => {
var offset = Index(t.off) var offset = Index(t.off)
val elem = for (elem <- t.elem) yield { val elem = for (elem <- t.elem) yield {
@ -784,7 +803,7 @@ object AST {
final case class _Escape[T](code: Escape) extends _Fmt[T] with Phantom final case class _Escape[T](code: Escape) extends _Fmt[T] with Phantom
object Expr { def apply(t: Option[AST]): Fmt = _Expr(t) } object Expr { def apply(t: Option[AST]): Fmt = _Expr(t) }
object Plain { def apply(s: String): Raw = _Plain(s) } object Plain { def apply(s: String): Raw = _Plain(s) }
//// Instances //// //// Instances ////
@ -797,19 +816,20 @@ object AST {
t => R + ("\\" + t.code.repr) t => R + ("\\" + t.code.repr)
implicit def ozipEscape[T]: OffsetZip[_Escape, T] = t => t.coerce implicit def ozipEscape[T]: OffsetZip[_Escape, T] = t => t.coerce
implicit def foldPlain: Foldable[_Plain] = semi.foldable implicit def foldPlain: Foldable[_Plain] = semi.foldable
implicit def ftorPlain[T]: Functor[_Plain] = semi.functor implicit def ftorPlain[T]: Functor[_Plain] = semi.functor
implicit def reprPlain[T]: Repr[_Plain[T]] = _.value implicit def reprPlain[T]: Repr[_Plain[T]] = _.value
implicit def ozipPlain[T]: OffsetZip[_Plain, T] = t => t.coerce implicit def ozipPlain[T]: OffsetZip[_Plain, T] = t => t.coerce
implicit def ftorExpr[T]: Functor[_Expr] = semi.functor implicit def ftorExpr[T]: Functor[_Expr] = semi.functor
implicit def foldExpr: Foldable[_Expr] = semi.foldable implicit def foldExpr: Foldable[_Expr] = semi.foldable
implicit def reprExpr[T: Repr]: Repr[_Expr[T]] = implicit def reprExpr[T: Repr]: Repr[_Expr[T]] =
R + '`' + _.value + '`' R + '`' + _.value + '`'
implicit def ozipExpr[T]: OffsetZip[_Expr, T] = _.map(Index.Start -> _) implicit def ozipExpr[T]: OffsetZip[_Expr, T] =
_.map(Index.Start -> _)
implicit def ftorRaw[T]: Functor[_Raw] = semi.functor implicit def ftorRaw[T]: Functor[_Raw] = semi.functor
implicit def foldRaw: Foldable[_Raw] = semi.foldable implicit def foldRaw: Foldable[_Raw] = semi.foldable
implicit def reprRaw[T]: Repr[_Raw[T]] = { implicit def reprRaw[T]: Repr[_Raw[T]] = {
case t: _Plain[T] => Repr(t) case t: _Plain[T] => Repr(t)
} }
@ -817,8 +837,8 @@ object AST {
case t: _Plain[T] => OffsetZip(t) case t: _Plain[T] => OffsetZip(t)
} }
implicit def ftorFmt[T]: Functor[_Fmt] = semi.functor implicit def ftorFmt[T]: Functor[_Fmt] = semi.functor
implicit def foldFmt: Foldable[_Fmt] = semi.foldable implicit def foldFmt: Foldable[_Fmt] = semi.foldable
implicit def reprFmt[T: Repr]: Repr[_Fmt[T]] = { implicit def reprFmt[T: Repr]: Repr[_Fmt[T]] = {
case t: _Plain[T] => Repr(t) case t: _Plain[T] => Repr(t)
case t: _Expr[T] => Repr(t) case t: _Expr[T] => Repr(t)
@ -876,10 +896,10 @@ object AST {
//// Smart Constructors //// //// Smart Constructors ////
object Prefix { object Prefix {
val any = UnapplyByType[Prefix] val any = UnapplyByType[Prefix]
def unapply(t: AST) = Unapply[Prefix].run(t => (t.fn, t.arg))(t) def unapply(t: AST) = Unapply[Prefix].run(t => (t.fn, t.arg))(t)
def apply(fn: AST, off: Int, arg: AST): Prefix = PrefixOf(fn, off, arg) def apply(fn: AST, off: Int, arg: AST): Prefix = PrefixOf(fn, off, arg)
def apply(fn: AST, arg: AST): Prefix = Prefix(fn, 1, arg) def apply(fn: AST, arg: AST): Prefix = Prefix(fn, 1, arg)
} }
object Infix { object Infix {
@ -903,7 +923,11 @@ object AST {
implicit def repr[T: Repr]: Repr[PrefixOf[T]] = implicit def repr[T: Repr]: Repr[PrefixOf[T]] =
t => R + t.fn + t.off + t.arg t => R + t.fn + t.off + t.arg
implicit def ozip[T: Repr]: OffsetZip[PrefixOf, T] = implicit def ozip[T: Repr]: OffsetZip[PrefixOf, T] =
t => t.copy(fn = (Index.Start, t.fn), arg = (Index(t.fn.span + t.off), t.arg)) t =>
t.copy(
fn = (Index.Start, t.fn),
arg = (Index(t.fn.span + t.off), t.arg)
)
} }
object InfixOf { object InfixOf {
implicit def ftor: Functor[InfixOf] = semi.functor implicit def ftor: Functor[InfixOf] = semi.functor
@ -957,18 +981,18 @@ object AST {
def unapply(t: AST) = Unapply[Left].run(t => (t.arg, t.opr))(t) def unapply(t: AST) = Unapply[Left].run(t => (t.arg, t.opr))(t)
def apply(arg: AST, off: Int, opr: Opr): Left = LeftOf(arg, off, opr) def apply(arg: AST, off: Int, opr: Opr): Left = LeftOf(arg, off, opr)
def apply(arg: AST, opr: Opr): Left = Left(arg, 1, opr) def apply(arg: AST, opr: Opr): Left = Left(arg, 1, opr)
} }
object Right { object Right {
val any = UnapplyByType[Right] val any = UnapplyByType[Right]
def unapply(t: AST) = Unapply[Right].run(t => (t.opr, t.arg))(t) def unapply(t: AST) = Unapply[Right].run(t => (t.opr, t.arg))(t)
def apply(opr: Opr, off: Int, arg: AST): Right = RightOf(opr, off, arg) def apply(opr: Opr, off: Int, arg: AST): Right = RightOf(opr, off, arg)
def apply(opr: Opr, arg: AST): Right = Right(opr, 1, arg) def apply(opr: Opr, arg: AST): Right = Right(opr, 1, arg)
} }
object Sides { object Sides {
val any = UnapplyByType[Sides] val any = UnapplyByType[Sides]
def unapply(t: AST) = Unapply[Sides].run(_.opr)(t) def unapply(t: AST) = Unapply[Sides].run(_.opr)(t)
def apply(opr: Opr): Sides = SidesOf[AST](opr) def apply(opr: Opr): Sides = SidesOf[AST](opr)
} }
@ -991,10 +1015,10 @@ object AST {
t => t.copy(arg = (Index(t.opr.span + t.off), t.arg)) t => t.copy(arg = (Index(t.opr.span + t.off), t.arg))
} }
object SidesOf { object SidesOf {
implicit def ftor: Functor[SidesOf] = semi.functor implicit def ftor: Functor[SidesOf] = semi.functor
implicit def fold: Foldable[SidesOf] = semi.foldable implicit def fold: Foldable[SidesOf] = semi.foldable
implicit def repr[T: Repr]: Repr[SidesOf[T]] = t => R + t.opr implicit def repr[T: Repr]: Repr[SidesOf[T]] = t => R + t.opr
implicit def ozip[T]: OffsetZip[SidesOf, T] = t => t.coerce implicit def ozip[T]: OffsetZip[SidesOf, T] = t => t.coerce
} }
} }
} }
@ -1067,26 +1091,26 @@ object AST {
def toOptional: LineOf[Option[T]] = copy(elem = Some(elem)) def toOptional: LineOf[Option[T]] = copy(elem = Some(elem))
} }
object LineOf { object LineOf {
implicit def ftorLine: Functor[LineOf] = semi.functor implicit def ftorLine: Functor[LineOf] = semi.functor
implicit def fold: Foldable[LineOf] = semi.foldable implicit def fold: Foldable[LineOf] = semi.foldable
implicit def reprLine[T: Repr]: Repr[LineOf[T]] = t => R + t.elem + t.off implicit def reprLine[T: Repr]: Repr[LineOf[T]] = t => R + t.elem + t.off
} }
object Line { object Line {
// FIXME: Compatibility mode // FIXME: Compatibility mode
type NonEmpty = Line type NonEmpty = Line
val Required = Line val Required = Line
def apply[T](elem: T, off: Int) = LineOf(elem, off) def apply[T](elem: T, off: Int) = LineOf(elem, off)
def apply[T](elem: T): LineOf[T] = LineOf(elem, 0) def apply[T](elem: T): LineOf[T] = LineOf(elem, 0)
} }
object OptLine { object OptLine {
def apply(): OptLine = Line(None, 0) def apply(): OptLine = Line(None, 0)
def apply(elem: AST): OptLine = Line(Some(elem)) def apply(elem: AST): OptLine = Line(Some(elem))
def apply(off: Int): OptLine = Line(None, off) def apply(off: Int): OptLine = Line(None, off)
} }
} }
object BlockOf { object BlockOf {
implicit def ftorBlock: Functor[BlockOf] = semi.functor implicit def ftorBlock: Functor[BlockOf] = semi.functor
implicit def fold: Foldable[BlockOf] = semi.foldable implicit def fold: Foldable[BlockOf] = semi.foldable
implicit def reprBlock[T: Repr]: Repr[BlockOf[T]] = t => { implicit def reprBlock[T: Repr]: Repr[BlockOf[T]] = t => {
val headRepr = if (t.isOrphan) R else newline val headRepr = if (t.isOrphan) R else newline
val emptyLinesRepr = t.emptyLines.map(R + _ + newline) val emptyLinesRepr = t.emptyLines.map(R + _ + newline)
@ -1119,11 +1143,11 @@ object AST {
object Module { object Module {
import Block._ import Block._
type M = Module type M = Module
val any = UnapplyByType[M] val any = UnapplyByType[M]
def unapply(t: AST) = Unapply[M].run(_.lines)(t) def unapply(t: AST) = Unapply[M].run(_.lines)(t)
def apply(ls: List1[OptLine]): M = ModuleOf(ls) def apply(ls: List1[OptLine]): M = ModuleOf(ls)
def apply(l: OptLine): M = Module(List1(l)) def apply(l: OptLine): M = Module(List1(l))
def apply(l: OptLine, ls: OptLine*): M = Module(List1(l, ls.to[List])) def apply(l: OptLine, ls: OptLine*): M = Module(List1(l, ls.to[List]))
def apply(l: OptLine, ls: List[OptLine]): M = Module(List1(l, ls)) def apply(l: OptLine, ls: List[OptLine]): M = Module(List1(l, ls))
def traverseWithOff(m: M)(f: (Index, AST) => AST): M = { def traverseWithOff(m: M)(f: (Index, AST) => AST): M = {
val lines2 = m.lines.map { line: OptLine => val lines2 = m.lines.map { line: OptLine =>
@ -1134,8 +1158,8 @@ object AST {
} }
} }
object ModuleOf { object ModuleOf {
implicit def ftor: Functor[ModuleOf] = semi.functor implicit def ftor: Functor[ModuleOf] = semi.functor
implicit def fold: Foldable[ModuleOf] = semi.foldable implicit def fold: Foldable[ModuleOf] = semi.foldable
implicit def ozip[T]: OffsetZip[ModuleOf, T] = _.map(Index.Start -> _) implicit def ozip[T]: OffsetZip[ModuleOf, T] = _.map(Index.Start -> _)
implicit def repr[T: Repr]: Repr[ModuleOf[T]] = implicit def repr[T: Repr]: Repr[ModuleOf[T]] =
t => R + t.lines.head + t.lines.tail.map(newline + _) t => R + t.lines.head + t.lines.tail.map(newline + _)
@ -1235,14 +1259,14 @@ object AST {
final case class Segment(head: AST, body: Option[SAST]) final case class Segment(head: AST, body: Option[SAST])
object Segment { object Segment {
def apply(head: AST): Segment = Segment(head, None) def apply(head: AST): Segment = Segment(head, None)
implicit def repr: Repr[Segment] = t => R + t.head + t.body implicit def repr: Repr[Segment] = t => R + t.head + t.body
} }
} }
object AmbiguousOf { object AmbiguousOf {
implicit def ftor: Functor[AmbiguousOf] = semi.functor implicit def ftor: Functor[AmbiguousOf] = semi.functor
implicit def fold: Foldable[AmbiguousOf] = semi.foldable implicit def fold: Foldable[AmbiguousOf] = semi.foldable
implicit def repr[T]: Repr[AmbiguousOf[T]] = t => R + t.segs.map(Repr(_)) implicit def repr[T]: Repr[AmbiguousOf[T]] = t => R + t.segs.map(Repr(_))
implicit def ozip[T]: OffsetZip[AmbiguousOf, T] = _.map(Index.Start -> _) implicit def ozip[T]: OffsetZip[AmbiguousOf, T] = _.map(Index.Start -> _)
} }
@ -1299,19 +1323,27 @@ object AST {
def apply(t: Tup): LastSegment = LastSegment(t._1, t._2) def apply(t: Tup): LastSegment = LastSegment(t._1, t._2)
} }
def apply(back: Option[Pattern], t1: Segment.Tup, ts: List[Segment.Tup])( def apply(
precSection: Option[Pattern],
t1: Segment.Tup,
ts: List[Segment.Tup]
)(
fin: Resolver fin: Resolver
): Definition = { ): Definition = {
val segs = List1(t1, ts) val segs = List1(t1, ts)
val init = segs.init val init = segs.init
val lastTup = segs.last val lastTup = segs.last
val last = (lastTup._1, Some(lastTup._2)) val last = (lastTup._1, Some(lastTup._2))
Definition(back, init, last, fin) Definition(precSection, init, last, fin)
} }
def apply(back: Option[Pattern], t1: Segment.Tup, ts: Segment.Tup*)( def apply(
precSection: Option[Pattern],
t1: Segment.Tup,
ts: Segment.Tup*
)(
fin: Resolver fin: Resolver
): Definition = Definition(back, t1, ts.toList)(fin) ): Definition = Definition(precSection, t1, ts.toList)(fin)
def apply(t1: Segment.Tup, t2_ : Segment.Tup*)( def apply(t1: Segment.Tup, t2_ : Segment.Tup*)(
fin: Resolver fin: Resolver
@ -1418,9 +1450,11 @@ object AST {
extends SpacelessASTOf[T] extends SpacelessASTOf[T]
with Phantom with Phantom
object Comment { object Comment {
val any = UnapplyByType[Comment] val any = UnapplyByType[Comment]
val symbol = "#" val symbol = "#"
def apply(lines: List[String]): Comment = ASTOf(CommentOf(lines)) def apply(lines: List[String]): Comment = ASTOf(CommentOf(lines))
def unapply(t: AST): Option[List[String]] =
Unapply[Comment].run(t => t.lines)(t)
} }
//// Instances //// //// Instances ////
@ -1446,6 +1480,8 @@ object AST {
val any = UnapplyByType[Documented] val any = UnapplyByType[Documented]
def apply(doc: Doc, emp: Int, ast: AST): Documented = def apply(doc: Doc, emp: Int, ast: AST): Documented =
ASTOf(DocumentedOf(doc, emp, ast)) ASTOf(DocumentedOf(doc, emp, ast))
def unapply(t: AST): Option[(Doc, Int, AST)] =
Unapply[Documented].run(t => (t.doc, t.emptyLinesBetween, t.ast))(t)
} }
//// Instances //// //// Instances ////
@ -1458,7 +1494,8 @@ object AST {
val betweenDocAstRepr = R + newline + newline.build * t.emptyLinesBetween val betweenDocAstRepr = R + newline + newline.build * t.emptyLinesBetween
R + symbolRepr + t.doc + betweenDocAstRepr + t.ast R + symbolRepr + t.doc + betweenDocAstRepr + t.ast
} }
implicit def offsetZip[T]: OffsetZip[DocumentedOf, T] = _.map(Index.Start -> _) implicit def offsetZip[T]: OffsetZip[DocumentedOf, T] =
_.map(Index.Start -> _)
} }
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -1468,10 +1505,13 @@ object AST {
type Import = ASTOf[ImportOf] type Import = ASTOf[ImportOf]
final case class ImportOf[T](path: List1[Cons]) extends SpacelessASTOf[T] final case class ImportOf[T](path: List1[Cons]) extends SpacelessASTOf[T]
object Import { object Import {
def apply(path: List1[Cons]): Import = ImportOf[AST](path) def apply(path: List1[Cons]): Import = ImportOf[AST](path)
def apply(head: Cons): Import = Import(head, List()) def apply(head: Cons): Import = Import(head, List())
def apply(head: Cons, tail: List[Cons]): Import = Import(List1(head, tail)) def apply(head: Cons, tail: List[Cons]): Import = Import(List1(head, tail))
def apply(head: Cons, tail: Cons*): Import = Import(head, tail.toList) def apply(head: Cons, tail: Cons*): Import = Import(head, tail.toList)
def unapply(t: AST): Option[List1[Cons]] =
Unapply[Import].run(t => t.path)(t)
val any = UnapplyByType[Import]
} }
object ImportOf { object ImportOf {
implicit def ftor: Functor[ImportOf] = semi.functor implicit def ftor: Functor[ImportOf] = semi.functor
@ -1487,13 +1527,15 @@ object AST {
//// Mixfix ////////////////////////////////////////////////////////////////// //// Mixfix //////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
type Mixfix = MixfixOf[AST] type Mixfix = ASTOf[MixfixOf]
final case class MixfixOf[T](name: List1[Ident], args: List1[T]) final case class MixfixOf[T](name: List1[Ident], args: List1[T])
extends SpacelessASTOf[T] extends SpacelessASTOf[T]
object Mixfix { object Mixfix {
def apply(name: List1[Ident], args: List1[AST]): Mixfix = def apply(name: List1[Ident], args: List1[AST]): Mixfix =
MixfixOf(name, args) MixfixOf(name, args)
def unapply(t: AST) = Unapply[Mixfix].run(t => (t.name, t.args))(t)
val any = UnapplyByType[Mixfix]
} }
object MixfixOf { object MixfixOf {
implicit def ftor: Functor[MixfixOf] = semi.functor implicit def ftor: Functor[MixfixOf] = semi.functor
@ -1515,12 +1557,12 @@ object AST {
type Group = ASTOf[GroupOf] type Group = ASTOf[GroupOf]
final case class GroupOf[T](body: Option[T]) extends SpacelessASTOf[T] final case class GroupOf[T](body: Option[T]) extends SpacelessASTOf[T]
object Group { object Group {
val any = UnapplyByType[Group] val any = UnapplyByType[Group]
def unapply(t: AST) = Unapply[Group].run(_.body)(t) def unapply(t: AST) = Unapply[Group].run(_.body)(t)
def apply(body: Option[AST]): Group = GroupOf(body) def apply(body: Option[AST]): Group = GroupOf(body)
def apply(body: AST): Group = Group(Some(body)) def apply(body: AST): Group = Group(Some(body))
def apply(body: SAST): Group = Group(body.el) def apply(body: SAST): Group = Group(body.el)
def apply(): Group = Group(None) def apply(): Group = Group(None)
} }
object GroupOf { object GroupOf {
implicit def ftor: Functor[GroupOf] = semi.functor implicit def ftor: Functor[GroupOf] = semi.functor
@ -1539,12 +1581,14 @@ object AST {
final case class DefOf[T](name: Cons, args: List[T], body: Option[T]) final case class DefOf[T](name: Cons, args: List[T], body: Option[T])
extends SpacelessASTOf[T] extends SpacelessASTOf[T]
object Def { object Def {
val any = UnapplyByType[Def] val any = UnapplyByType[Def]
val symbol = "def" val symbol = "def"
def apply(name: Cons): Def = Def(name, List()) def apply(name: Cons): Def = Def(name, List())
def apply(name: Cons, args: List[AST]): Def = Def(name, args, None) def apply(name: Cons, args: List[AST]): Def = Def(name, args, None)
def apply(name: Cons, args: List[AST], body: Option[AST]): Def = def apply(name: Cons, args: List[AST], body: Option[AST]): Def =
DefOf(name, args, body) DefOf(name, args, body)
def unapply(t: AST): Option[(Cons, List[AST], Option[AST])] =
Unapply[Def].run(t => (t.name, t.args, t.body))(t)
} }
object DefOf { object DefOf {
implicit def ftor: Functor[DefOf] = semi.functor implicit def ftor: Functor[DefOf] = semi.functor
@ -1559,12 +1603,15 @@ object AST {
//// Foreign ///////////////////////////////////////////////////////////////// //// Foreign /////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
type Foreign = ForeignOf[AST] type Foreign = ASTOf[ForeignOf]
final case class ForeignOf[T](indent: Int, lang: String, code: List[String]) final case class ForeignOf[T](indent: Int, lang: String, code: List[String])
extends SpacelessASTOf[T] extends SpacelessASTOf[T]
object Foreign { object Foreign {
def apply(indent: Int, lang: String, code: List[String]): Foreign = def apply(indent: Int, lang: String, code: List[String]): Foreign =
ForeignOf(indent, lang, code) Foreign(indent, lang, code)
def unapply(t: AST): Option[(Int, String, List[String])] =
Unapply[Foreign].run(t => (t.indent, t.lang, t.code))(t)
val any = UnapplyByType[Foreign]
} }
object ForeignOf { object ForeignOf {
implicit def ftor: Functor[ForeignOf] = semi.functor implicit def ftor: Functor[ForeignOf] = semi.functor

View File

@ -0,0 +1,64 @@
package org.enso.syntax.text
import scala.annotation.tailrec
/**
* Useful debugging tools for the parser AST.
*/
object Debug {
/**
* Pretty prints the parser AST string representation in a more readable
* format.
*
* @param str the string representation of the parser AST
* @return the pretty-printed version of `str`
*/
def pretty(str: String): String = {
def checkClosing(in: List[Char]): Int = {
@tailrec
def go(i: Int, rest: Int, in: List[Char], bias: Int): Int =
(rest, bias, in) match {
case (0, _, _) => 0
case (_, 0, _) => i
case (_, _, Nil) => i
case (_, _, s :: ss) =>
s match {
case '(' => go(i + 1, rest - 1, ss, bias - 1)
case ')' => go(i + 1, rest - 1, ss, bias + 1)
case _ => go(i + 1, rest - 1, ss, bias)
}
}
go(0, 10, in, -1)
}
@tailrec
def go(ind: Int, in: List[Char], out: List[String]): List[String] = {
def newline(i: Int) = "\n" + " " * i * 2
in match {
case Nil => out
case s :: ss =>
val s2 = s.toString
s match {
case '(' =>
checkClosing(ss) match {
case 0 => go(ind + 1, ss, newline(ind + 1) :: s2 :: out)
case i =>
go(
ind,
ss.drop(i),
ss.take(i).mkString("") :: s2 :: out
)
}
case ')' => go(ind - 1, ss, s2 :: newline(ind - 1) :: out)
case ',' => go(ind, ss, newline(ind) :: s2 :: out)
case _ => go(ind, ss, s2 :: out)
}
}
}
go(0, str.toList, List()).reverse.mkString("")
}
}

View File

@ -31,7 +31,7 @@ object Builtin {
} }
} }
val defn = Definition(Var("def") -> { val defn = Definition(Var("type") -> {
val head = Pattern.Cons().or("missing name").tag("name") val head = Pattern.Cons().or("missing name").tag("name")
val args = val args =
Pattern.NonSpacedExpr_().tag("parameter").many.tag("parameters") Pattern.NonSpacedExpr_().tag("parameter").many.tag("parameters")
@ -122,7 +122,7 @@ object Builtin {
val nonSpacedExpr = Pattern.Any(Some(false)).many1.build val nonSpacedExpr = Pattern.Any(Some(false)).many1.build
val arrow = Definition( val arrow = Definition(
Some(nonSpacedExpr.or(Pattern.OprExpr("->"))), Some(nonSpacedExpr.or(Pattern.ExprUntilOpr("->"))),
Opr("->") -> Pattern.NonSpacedExpr().or(Pattern.Expr()) Opr("->") -> Pattern.NonSpacedExpr().or(Pattern.Expr())
) { ctx => ) { ctx =>
(ctx.prefix, ctx.body) match { (ctx.prefix, ctx.body) match {

View File

@ -13,13 +13,11 @@ import org.enso.syntax.text.ast.Repr
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
object Pattern { object Pattern {
import cats.Functor import cats.{Foldable, Functor, Traverse}
import cats.Foldable
import cats.Traverse
import cats.derived._ import cats.derived._
type P = Pattern type P = Pattern
type Spaced = Option[Boolean] type Spaced = Option[Boolean] // TODO [AA] Make this an actual ADT
// TODO: Refactorme // TODO: Refactorme
def streamShift_(off: Int, revStream: AST.Stream): AST.Stream = def streamShift_(off: Int, revStream: AST.Stream): AST.Stream =
@ -130,7 +128,7 @@ object Pattern {
SepList(seg, div) SepList(seg, div)
} }
def OprExpr(opr: String) = { def ExprUntilOpr(opr: String) = {
val base = Except(Opr(None, Some(AST.Opr(opr).prec)), Any()) val base = Except(Opr(None, Some(AST.Opr(opr).prec)), Any())
base.many1.build base.many1.build
} }
@ -205,8 +203,8 @@ object Pattern {
type Match = MatchOf[SAST] type Match = MatchOf[SAST]
sealed trait MatchOf[T] { sealed trait MatchOf[T] {
import cats.implicits._
import MatchOf._ import MatchOf._
import cats.implicits._
val M = Match val M = Match
val pat: Pattern val pat: Pattern
@ -303,7 +301,6 @@ object Pattern {
def ftorMatch: Functor[MatchOf] = semi.functor def ftorMatch: Functor[MatchOf] = semi.functor
def travMatch: Traverse[MatchOf] = semi.traverse[MatchOf] def travMatch: Traverse[MatchOf] = semi.traverse[MatchOf]
def foldMatch: Foldable[MatchOf] = { def foldMatch: Foldable[MatchOf] = {
import cats.derived.auto.foldable._
semi.foldable[MatchOf] semi.foldable[MatchOf]
} }
} }

View File

@ -240,54 +240,6 @@ object Parser {
object Main extends App { object Main extends App {
def pretty(str: String): String = {
def checkClosing(in: List[Char]): Int = {
@tailrec
def go(i: Int, rest: Int, in: List[Char], bias: Int): Int =
(rest, bias, in) match {
case (0, _, _) => 0
case (_, 0, _) => i
case (_, _, Nil) => i
case (_, _, s :: ss) =>
s match {
case '(' => go(i + 1, rest - 1, ss, bias - 1)
case ')' => go(i + 1, rest - 1, ss, bias + 1)
case _ => go(i + 1, rest - 1, ss, bias)
}
}
go(0, 10, in, -1)
}
@tailrec
def go(ind: Int, in: List[Char], out: List[String]): List[String] = {
def newline(i: Int) = "\n" + " " * i * 2
in match {
case Nil => out
case s :: ss =>
val s2 = s.toString
s match {
case '(' =>
checkClosing(ss) match {
case 0 => go(ind + 1, ss, newline(ind + 1) :: s2 :: out)
case i =>
go(
ind,
ss.drop(i),
ss.take(i).mkString("") :: s2 :: out
)
}
case ')' => go(ind - 1, ss, s2 :: newline(ind - 1) :: out)
case ',' => go(ind, ss, newline(ind) :: s2 :: out)
case _ => go(ind, ss, s2 :: out)
}
}
}
go(0, str.toList, List()).reverse.mkString("")
}
println("--- START ---") println("--- START ---")
val parser = new Parser() val parser = new Parser()
@ -333,14 +285,14 @@ object Main extends App {
val mod = parser.run(new Reader(inp)) val mod = parser.run(new Reader(inp))
println(pretty(mod.toString)) println(Debug.pretty(mod.toString))
println("=========================") println("=========================")
println(pretty(parser.dropMacroMeta(mod).toString)) println(Debug.pretty(parser.dropMacroMeta(mod).toString))
val rmod = parser.resolveMacros(mod) val rmod = parser.resolveMacros(mod)
if (mod != rmod) { if (mod != rmod) {
println("\n---\n") println("\n---\n")
println(pretty(rmod.toString)) println(Debug.pretty(rmod.toString))
} }
println("------") println("------")
@ -356,7 +308,7 @@ object Main extends App {
val documentation = DocParserRunner.createDocs(droppedMeta) val documentation = DocParserRunner.createDocs(droppedMeta)
val documentationHTML = val documentationHTML =
DocParserRunner.generateHTMLForEveryDocumented(documentation) DocParserRunner.generateHTMLForEveryDocumented(documentation)
println(pretty(documentation.toString)) println(Debug.pretty(documentation.toString))
println("------") println("------")
println(documentation.show()) println(documentation.show())
println("=========================") println("=========================")

View File

@ -341,9 +341,9 @@ class ParserTest extends FlatSpec with Matchers {
"import Std . Math .Vector".stripMargin ?= Import("Std", "Math", "Vector") "import Std . Math .Vector".stripMargin ?= Import("Std", "Math", "Vector")
"""def Maybe a """type Maybe a
| def Just val:a | type Just val:a
| def Nothing""".stripMargin ?= { | type Nothing""".stripMargin ?= {
val defJust = Def("Just", List("val" $ ":" $ "a")) val defJust = Def("Just", List("val" $ ":" $ "a"))
val defNothing = Def("Nothing") val defNothing = Def("Nothing")
Def( Def(

View File

@ -30,23 +30,23 @@ jobs:
options: "--name ci-container -v /usr/bin/docker:/tmp/docker:ro" options: "--name ci-container -v /usr/bin/docker:/tmp/docker:ro"
steps: steps:
- script: /tmp/docker exec -t -u 0 ci-container sh -c "yum install -y sudo" - script: /tmp/docker exec -t -u 0 ci-container sh -c "yum install -y sudo"
displayName: Setup sudo displayName: Setup Sudo
- script: | - script: |
curl -fSL -o sbt.tgz https://piccolo.link/sbt-$(sbtVersion).tgz curl -fSL -o sbt.tgz https://piccolo.link/sbt-$(sbtVersion).tgz
tar -x -z -C ~ -f sbt.tgz tar -x -z -C ~ -f sbt.tgz
echo "##vso[task.setvariable variable=PATH]~/sbt/bin/:$PATH" echo "##vso[task.setvariable variable=PATH]~/sbt/bin/:$PATH"
displayName: "Install sbt" displayName: Install SBT
- script: | - script: |
sbt -no-colors test sbt -no-colors test
displayName: sbt test displayName: Test All
continueOnError: true continueOnError: true
- script: | - script: |
sbt -no-colors syntax/bench sbt -no-colors syntax/bench
displayName: sbt bench parser displayName: Benchmark the Parser
continueOnError: true continueOnError: true
- script: | - script: |
sbt -no-colors interpreter/bench sbt -no-colors interpreter/Benchmark/compile
displayName: sbt bench interpreter displayName: Check Interpreter Benchmarks Compile
continueOnError: true continueOnError: true
- task: PublishTestResults@2 - task: PublishTestResults@2
inputs: inputs:
@ -54,7 +54,7 @@ jobs:
testResultsFiles: '**/TEST-*.xml' testResultsFiles: '**/TEST-*.xml'
- script: | - script: |
sbt interpreter/assembly sbt interpreter/assembly
displayName: sbt assembly displayName: Build the Uberjar
continueOnError: true continueOnError: true
- task: CopyFiles@2 - task: CopyFiles@2
inputs: inputs:
@ -67,7 +67,7 @@ jobs:
- script: | - script: |
echo "##vso[task.complete result=Failed;]DONE" echo "##vso[task.complete result=Failed;]DONE"
condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues') condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues')
displayName: "Fail if there were issues" displayName: Fail on Issues
- job: macOS - job: macOS
timeoutInMinutes: 90 timeoutInMinutes: 90
pool: pool:
@ -85,18 +85,18 @@ jobs:
curl -fSL -o sbt.tgz https://piccolo.link/sbt-$(sbtVersion).tgz curl -fSL -o sbt.tgz https://piccolo.link/sbt-$(sbtVersion).tgz
tar -x -z -C ~ -f sbt.tgz tar -x -z -C ~ -f sbt.tgz
echo "##vso[task.setvariable variable=PATH]~/sbt/bin/:$PATH" echo "##vso[task.setvariable variable=PATH]~/sbt/bin/:$PATH"
displayName: Install sbt displayName: Install SBT
- script: | - script: |
sbt -no-colors test sbt -no-colors test
displayName: sbt test displayName: Test All
continueOnError: true continueOnError: true
- script: | - script: |
sbt -no-colors syntax/bench sbt -no-colors syntax/bench
displayName: sbt bench parser displayName: Benchmark the Parser
continueOnError: true continueOnError: true
- script: | - script: |
sbt -no-colors interpreter/bench sbt -no-colors interpreter/Benchmark/compile
displayName: sbt bench interpreter displayName: Check Interpreter Benchmarks Compile
continueOnError: true continueOnError: true
- task: PublishTestResults@2 - task: PublishTestResults@2
inputs: inputs:
@ -105,7 +105,7 @@ jobs:
- script: | - script: |
echo "##vso[task.complete result=Failed;]DONE" echo "##vso[task.complete result=Failed;]DONE"
condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues') condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues')
displayName: "Fail if there were issues" displayName: Fail on Issues
- job: Windows - job: Windows
timeoutInMinutes: 90 timeoutInMinutes: 90
pool: pool:
@ -114,28 +114,28 @@ jobs:
- script: | - script: |
curl -fSL -o graal.zip https://github.com/oracle/graal/releases/download/vm-$(graalVersion)/graalvm-ce-windows-amd64-$(graalVersion).zip curl -fSL -o graal.zip https://github.com/oracle/graal/releases/download/vm-$(graalVersion)/graalvm-ce-windows-amd64-$(graalVersion).zip
7z x -y -oC:\ graal.zip 7z x -y -oC:\ graal.zip
displayName: "Install GraalVM" displayName: Install GraalVM
- script: | - script: |
curl -fSL -o sbt.tgz https://piccolo.link/sbt-$(sbtVersion).tgz curl -fSL -o sbt.tgz https://piccolo.link/sbt-$(sbtVersion).tgz
tar -x -z -C %USERPROFILE% -f sbt.tgz tar -x -z -C %USERPROFILE% -f sbt.tgz
displayName: "Install sbt" displayName: Install SBT
- script: | - script: |
call refreshenv call refreshenv
echo ##vso[task.setvariable variable=JAVA_HOME]C:\graalvm-ce-$(graalVersion) echo ##vso[task.setvariable variable=JAVA_HOME]C:\graalvm-ce-$(graalVersion)
echo ##vso[task.setvariable variable=PATH]C:\graalvm-ce-$(graalVersion)\bin;%PATH% echo ##vso[task.setvariable variable=PATH]C:\graalvm-ce-$(graalVersion)\bin;%PATH%
echo ##vso[task.setvariable variable=PATH]%USERPROFILE%\sbt\bin\;%PATH% echo ##vso[task.setvariable variable=PATH]%USERPROFILE%\sbt\bin\;%PATH%
displayName: "Adjust environment variables" displayName: Adjust Environment Variables
- script: | - script: |
sbt.bat test sbt.bat test
continueOnError: true continueOnError: true
displayName: "sbt test" displayName: Test All
- script: | - script: |
sbt syntax/bench sbt syntax/bench
displayName: sbt bench parser displayName: Benchmark the Parser
continueOnError: true continueOnError: true
- script: | - script: |
sbt interpreter/bench sbt interpreter/Benchmark/compile
displayName: sbt bench interpreter displayName: Check Interpreter Benchmarks Compile
continueOnError: true continueOnError: true
- task: PublishTestResults@2 - task: PublishTestResults@2
inputs: inputs:
@ -144,4 +144,4 @@ jobs:
- script: | - script: |
echo "##vso[task.complete result=Failed;]DONE" echo "##vso[task.complete result=Failed;]DONE"
condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues') condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues')
displayName: "Fail if there were issues" displayName: Fail on Issues

View File

@ -306,6 +306,7 @@ lazy val interpreter = (project in file("Interpreter"))
parallelExecution in Benchmark := false parallelExecution in Benchmark := false
) )
.dependsOn(pkg) .dependsOn(pkg)
.dependsOn(syntax)
lazy val fileManager = (project in file("FileManager")) lazy val fileManager = (project in file("FileManager"))
.settings( .settings(