mirror of
https://github.com/enso-org/enso.git
synced 2025-01-03 11:11:50 +03:00
Integrate a compilation phase into the interpreter (#303)
This commit is contained in:
parent
2d81b111b7
commit
460205c2b9
1
.gitignore
vendored
1
.gitignore
vendored
@ -73,6 +73,7 @@ cabal.sandbox.config
|
|||||||
############################
|
############################
|
||||||
|
|
||||||
javadoc/
|
javadoc/
|
||||||
|
scaladoc/
|
||||||
|
|
||||||
#######################
|
#######################
|
||||||
## Benchmark Reports ##
|
## Benchmark Reports ##
|
||||||
|
@ -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.
|
@ -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 =
|
||||||
|
@ -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 =
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 =
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
*/
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
131
Interpreter/src/main/scala/org/enso/compiler/Compiler.scala
Normal file
131
Interpreter/src/main/scala/org/enso/compiler/Compiler.scala
Normal 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)
|
||||||
|
}
|
34
Interpreter/src/main/scala/org/enso/compiler/Pass.scala
Normal file
34
Interpreter/src/main/scala/org/enso/compiler/Pass.scala
Normal 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]
|
||||||
|
}
|
@ -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
|
||||||
|
) {}
|
@ -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")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.compiler.generate
|
||||||
|
|
||||||
|
class IrToTruffle {
|
||||||
|
|
||||||
|
}
|
185
Interpreter/src/main/scala/org/enso/compiler/ir/IR.scala
Normal file
185
Interpreter/src/main/scala/org/enso/compiler/ir/IR.scala
Normal 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.interpreter.analysis
|
||||||
|
|
||||||
|
class Pass {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.interpreter.analysis
|
||||||
|
|
||||||
|
class StaticAnalyser {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.interpreter.analysis.desugar
|
||||||
|
|
||||||
|
class DataTypeDefinition {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.interpreter.analysis.desugar
|
||||||
|
|
||||||
|
class DesugarPhase {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.interpreter.analysis.desugar
|
||||||
|
|
||||||
|
class NestedPatternMatch {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.enso.interpreter.analysis.desugar
|
||||||
|
|
||||||
|
class UnderscoreToLambda {
|
||||||
|
|
||||||
|
}
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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 =
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 =
|
||||||
"""
|
"""
|
||||||
|
@ -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
|
||||||
|
@ -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 =
|
||||||
|
@ -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."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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 =
|
||||||
"""
|
"""
|
||||||
|@{
|
|@{
|
||||||
|
@ -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 =
|
||||||
"""
|
"""
|
||||||
|
@ -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 }
|
||||||
|
@ -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)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 =
|
||||||
"""
|
"""
|
||||||
|
@ -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
|
||||||
|
@ -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("")
|
||||||
|
}
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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("=========================")
|
||||||
|
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
Loading…
Reference in New Issue
Block a user