mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 10:42:05 +03:00
Syntactic Java integration (#739)
This commit is contained in:
parent
817b2a9d11
commit
e8ede5114e
@ -10,7 +10,7 @@ import org.enso.languageserver.boot
|
|||||||
import org.enso.languageserver.boot.LanguageServerConfig
|
import org.enso.languageserver.boot.LanguageServerConfig
|
||||||
import org.enso.pkg.Package
|
import org.enso.pkg.Package
|
||||||
import org.enso.polyglot.{LanguageInfo, Module, PolyglotContext}
|
import org.enso.polyglot.{LanguageInfo, Module, PolyglotContext}
|
||||||
import org.graalvm.polyglot.{PolyglotException, Value}
|
import org.graalvm.polyglot.Value
|
||||||
|
|
||||||
import scala.annotation.unused
|
import scala.annotation.unused
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
@ -206,16 +206,9 @@ object Main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def runMain(mainModule: Module): Value = {
|
private def runMain(mainModule: Module): Value = {
|
||||||
try {
|
val mainCons = mainModule.getAssociatedConstructor
|
||||||
val mainCons = mainModule.getAssociatedConstructor
|
val mainFun = mainModule.getMethod(mainCons, "main")
|
||||||
val mainFun = mainModule.getMethod(mainCons, "main")
|
mainFun.execute(mainCons.newInstance())
|
||||||
mainFun.execute(mainCons.newInstance())
|
|
||||||
} catch {
|
|
||||||
case e: PolyglotException =>
|
|
||||||
System.err.println(e.getMessage)
|
|
||||||
exitFail()
|
|
||||||
throw new RuntimeException("Impossible to reach here.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package org.enso.interpreter.node.callable;
|
package org.enso.interpreter.node.callable;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.TruffleLanguage;
|
||||||
import com.oracle.truffle.api.dsl.Cached;
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
|
import com.oracle.truffle.api.dsl.CachedContext;
|
||||||
import com.oracle.truffle.api.dsl.Specialization;
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
import com.oracle.truffle.api.nodes.Node;
|
import com.oracle.truffle.api.nodes.Node;
|
||||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
import org.enso.interpreter.Language;
|
import org.enso.interpreter.Language;
|
||||||
import org.enso.interpreter.runtime.Builtins;
|
import org.enso.interpreter.runtime.Builtins;
|
||||||
|
import org.enso.interpreter.runtime.Context;
|
||||||
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
|
||||||
import org.enso.interpreter.runtime.callable.atom.Atom;
|
import org.enso.interpreter.runtime.callable.atom.Atom;
|
||||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||||
@ -75,10 +78,10 @@ public abstract class MethodResolverNode extends Node {
|
|||||||
|
|
||||||
@Specialization(guards = "cachedSymbol == symbol")
|
@Specialization(guards = "cachedSymbol == symbol")
|
||||||
Function resolveStringCached(
|
Function resolveStringCached(
|
||||||
UnresolvedSymbol symbol,
|
UnresolvedSymbol symbol,
|
||||||
String self,
|
String self,
|
||||||
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
||||||
@Cached("resolveMethodOnString(cachedSymbol)") Function function) {
|
@Cached("resolveMethodOnString(cachedSymbol)") Function function) {
|
||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +103,16 @@ public abstract class MethodResolverNode extends Node {
|
|||||||
return function;
|
return function;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Specialization(guards = {"cachedSymbol == symbol", "ctx.getEnvironment().isHostObject(target)"})
|
||||||
|
Function resolveHostCached(
|
||||||
|
UnresolvedSymbol symbol,
|
||||||
|
Object target,
|
||||||
|
@Cached("symbol") UnresolvedSymbol cachedSymbol,
|
||||||
|
@CachedContext(Language.class) Context ctx,
|
||||||
|
@Cached("buildHostResolver(cachedSymbol, ctx)") Function function) {
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
private Function ensureMethodExists(Function function, Object target, UnresolvedSymbol symbol) {
|
private Function ensureMethodExists(Function function, Object target, UnresolvedSymbol symbol) {
|
||||||
if (function == null) {
|
if (function == null) {
|
||||||
throw new MethodDoesNotExistException(target, symbol.getName(), this);
|
throw new MethodDoesNotExistException(target, symbol.getName(), this);
|
||||||
@ -118,7 +131,7 @@ public abstract class MethodResolverNode extends Node {
|
|||||||
|
|
||||||
Function resolveMethodOnString(UnresolvedSymbol symbol) {
|
Function resolveMethodOnString(UnresolvedSymbol symbol) {
|
||||||
return ensureMethodExists(
|
return ensureMethodExists(
|
||||||
symbol.resolveFor(getBuiltins().text(), getBuiltins().any()), "Text", symbol);
|
symbol.resolveFor(getBuiltins().text(), getBuiltins().any()), "Text", symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
Function resolveMethodOnFunction(UnresolvedSymbol symbol) {
|
Function resolveMethodOnFunction(UnresolvedSymbol symbol) {
|
||||||
@ -130,6 +143,14 @@ public abstract class MethodResolverNode extends Node {
|
|||||||
return ensureMethodExists(symbol.resolveFor(getBuiltins().any()), "Error", symbol);
|
return ensureMethodExists(symbol.resolveFor(getBuiltins().any()), "Error", symbol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Function buildHostResolver(UnresolvedSymbol symbol, Context context) {
|
||||||
|
if (symbol.getName().equals("new")) {
|
||||||
|
return context.getBuiltins().getConstructorDispatch();
|
||||||
|
} else {
|
||||||
|
return context.getBuiltins().buildPolyglotMethodDispatch(symbol.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
boolean isValidAtomCache(
|
boolean isValidAtomCache(
|
||||||
UnresolvedSymbol symbol,
|
UnresolvedSymbol symbol,
|
||||||
UnresolvedSymbol cachedSymbol,
|
UnresolvedSymbol cachedSymbol,
|
||||||
|
@ -0,0 +1,78 @@
|
|||||||
|
package org.enso.interpreter.node.expression.builtin.interop.syntax;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.interop.ArityException;
|
||||||
|
import com.oracle.truffle.api.interop.InteropLibrary;
|
||||||
|
import com.oracle.truffle.api.interop.UnsupportedMessageException;
|
||||||
|
import com.oracle.truffle.api.interop.UnsupportedTypeException;
|
||||||
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
|
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||||
|
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||||
|
import org.enso.interpreter.Constants;
|
||||||
|
import org.enso.interpreter.Language;
|
||||||
|
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||||
|
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||||
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
|
import org.enso.interpreter.runtime.callable.function.FunctionSchema.CallStrategy;
|
||||||
|
import org.enso.interpreter.runtime.error.PanicException;
|
||||||
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
import org.enso.interpreter.runtime.type.TypesGen;
|
||||||
|
|
||||||
|
@NodeInfo(shortName = "<new>", description = "Instantiates a polyglot constructor.")
|
||||||
|
public class ConstructorDispatchNode extends BuiltinRootNode {
|
||||||
|
private ConstructorDispatchNode(Language language) {
|
||||||
|
super(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Child InteropLibrary library =
|
||||||
|
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
|
||||||
|
private final BranchProfile err = BranchProfile.create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a function wrapping this node.
|
||||||
|
*
|
||||||
|
* @param language the current language instance
|
||||||
|
* @return a function wrapping this node
|
||||||
|
*/
|
||||||
|
public static Function makeFunction(Language language) {
|
||||||
|
return Function.fromBuiltinRootNode(
|
||||||
|
new ConstructorDispatchNode(language),
|
||||||
|
CallStrategy.ALWAYS_DIRECT,
|
||||||
|
new ArgumentDefinition(0, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||||
|
new ArgumentDefinition(1, "arguments", ArgumentDefinition.ExecutionMode.EXECUTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the node.
|
||||||
|
*
|
||||||
|
* @param frame current execution frame.
|
||||||
|
* @return the result of converting input into a string.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Stateful execute(VirtualFrame frame) {
|
||||||
|
Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());
|
||||||
|
Object cons = args[0];
|
||||||
|
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
|
||||||
|
try {
|
||||||
|
Object[] arguments = TypesGen.expectVector(args[1]).getItems();
|
||||||
|
Object res = library.instantiate(cons, arguments);
|
||||||
|
return new Stateful(state, res);
|
||||||
|
} catch (UnsupportedMessageException
|
||||||
|
| ArityException
|
||||||
|
| UnsupportedTypeException
|
||||||
|
| UnexpectedResultException e) {
|
||||||
|
err.enter();
|
||||||
|
throw new PanicException(e.getMessage(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a language-specific name for this node.
|
||||||
|
*
|
||||||
|
* @return the name of this node
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "<new>";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package org.enso.interpreter.node.expression.builtin.interop.syntax;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.interop.*;
|
||||||
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
|
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||||
|
import com.oracle.truffle.api.profiles.BranchProfile;
|
||||||
|
import org.enso.interpreter.Constants;
|
||||||
|
import org.enso.interpreter.Language;
|
||||||
|
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
|
||||||
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
|
import org.enso.interpreter.runtime.error.PanicException;
|
||||||
|
import org.enso.interpreter.runtime.state.Stateful;
|
||||||
|
import org.enso.interpreter.runtime.type.TypesGen;
|
||||||
|
|
||||||
|
@NodeInfo(shortName = "<polyglot_dispatch>", description = "Invokes a polyglot method by name.")
|
||||||
|
public class MethodDispatchNode extends BuiltinRootNode {
|
||||||
|
private MethodDispatchNode(Language language) {
|
||||||
|
super(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @Child InteropLibrary library =
|
||||||
|
InteropLibrary.getFactory().createDispatched(Constants.CacheSizes.BUILTIN_INTEROP_DISPATCH);
|
||||||
|
private final BranchProfile err = BranchProfile.create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of this node.
|
||||||
|
*
|
||||||
|
* @param language the current language instance
|
||||||
|
* @return a function wrapping this node
|
||||||
|
*/
|
||||||
|
public static MethodDispatchNode build(Language language) {
|
||||||
|
return new MethodDispatchNode(language);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the node.
|
||||||
|
*
|
||||||
|
* @param frame current execution frame.
|
||||||
|
* @return the result of converting input into a string.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Stateful execute(VirtualFrame frame) {
|
||||||
|
Object[] args = Function.ArgumentsHelper.getPositionalArguments(frame.getArguments());
|
||||||
|
Object callable = args[0];
|
||||||
|
Object state = Function.ArgumentsHelper.getState(frame.getArguments());
|
||||||
|
try {
|
||||||
|
String method = TypesGen.expectString(args[1]);
|
||||||
|
Object[] arguments = TypesGen.expectVector(args[2]).getItems();
|
||||||
|
Object res = library.invokeMember(callable, method, arguments);
|
||||||
|
return new Stateful(state, res);
|
||||||
|
} catch (UnsupportedMessageException
|
||||||
|
| ArityException
|
||||||
|
| UnsupportedTypeException
|
||||||
|
| UnexpectedResultException
|
||||||
|
| UnknownIdentifierException e) {
|
||||||
|
err.enter();
|
||||||
|
throw new PanicException(e.getMessage(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a language-specific name for this node.
|
||||||
|
*
|
||||||
|
* @return the name of this node
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "<polyglot_dispatch>";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package org.enso.interpreter.node.expression.constant;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
|
import org.enso.interpreter.node.ExpressionNode;
|
||||||
|
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||||
|
|
||||||
|
/** Represents a compile-time constant. */
|
||||||
|
@NodeInfo(shortName = "const", description = "Represents an arbitrary compile-time constant.")
|
||||||
|
public class ConstantObjectNode extends ExpressionNode {
|
||||||
|
private final Object object;
|
||||||
|
|
||||||
|
private ConstantObjectNode(Object object) {
|
||||||
|
this.object = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of this node.
|
||||||
|
*
|
||||||
|
* @param object the constant to represent
|
||||||
|
* @return a truffle node representing {@code constructor}
|
||||||
|
*/
|
||||||
|
public static ConstantObjectNode build(Object object) {
|
||||||
|
return new ConstantObjectNode(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the type constructor definition.
|
||||||
|
*
|
||||||
|
* @param frame the frame to execute in
|
||||||
|
* @return the constructor of the type defined
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
package org.enso.interpreter.runtime;
|
package org.enso.interpreter.runtime;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.RootCallTarget;
|
||||||
|
import com.oracle.truffle.api.Truffle;
|
||||||
import org.enso.interpreter.Language;
|
import org.enso.interpreter.Language;
|
||||||
import org.enso.interpreter.node.expression.builtin.IfZeroNode;
|
import org.enso.interpreter.node.expression.builtin.IfZeroNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointNode;
|
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointNode;
|
||||||
@ -10,6 +12,8 @@ import org.enso.interpreter.node.expression.builtin.error.PanicNode;
|
|||||||
import org.enso.interpreter.node.expression.builtin.error.ThrowErrorNode;
|
import org.enso.interpreter.node.expression.builtin.error.ThrowErrorNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionNode;
|
import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.interop.generic.*;
|
import org.enso.interpreter.node.expression.builtin.interop.generic.*;
|
||||||
|
import org.enso.interpreter.node.expression.builtin.interop.syntax.MethodDispatchNode;
|
||||||
|
import org.enso.interpreter.node.expression.builtin.interop.syntax.ConstructorDispatchNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.io.NanoTimeNode;
|
import org.enso.interpreter.node.expression.builtin.io.NanoTimeNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.io.PrintNode;
|
import org.enso.interpreter.node.expression.builtin.io.PrintNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.interop.java.*;
|
import org.enso.interpreter.node.expression.builtin.interop.java.*;
|
||||||
@ -25,7 +29,10 @@ import org.enso.interpreter.node.expression.builtin.text.AnyToTextNode;
|
|||||||
import org.enso.interpreter.node.expression.builtin.text.ConcatNode;
|
import org.enso.interpreter.node.expression.builtin.text.ConcatNode;
|
||||||
import org.enso.interpreter.node.expression.builtin.text.JsonSerializeNode;
|
import org.enso.interpreter.node.expression.builtin.text.JsonSerializeNode;
|
||||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||||
|
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
|
||||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||||
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
|
import org.enso.interpreter.runtime.callable.function.FunctionSchema;
|
||||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||||
import org.enso.pkg.QualifiedName;
|
import org.enso.pkg.QualifiedName;
|
||||||
|
|
||||||
@ -51,6 +58,10 @@ public class Builtins {
|
|||||||
private final AtomConstructor syntaxError;
|
private final AtomConstructor syntaxError;
|
||||||
private final AtomConstructor compileError;
|
private final AtomConstructor compileError;
|
||||||
|
|
||||||
|
private final RootCallTarget interopDispatchRoot;
|
||||||
|
private final FunctionSchema interopDispatchSchema;
|
||||||
|
private final Function newInstanceFunction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance with builtin methods installed.
|
* Creates an instance with builtin methods installed.
|
||||||
*
|
*
|
||||||
@ -138,6 +149,20 @@ public class Builtins {
|
|||||||
|
|
||||||
scope.registerMethod(java, "add_to_class_path", AddToClassPathNode.makeFunction(language));
|
scope.registerMethod(java, "add_to_class_path", AddToClassPathNode.makeFunction(language));
|
||||||
scope.registerMethod(java, "lookup_class", LookupClassNode.makeFunction(language));
|
scope.registerMethod(java, "lookup_class", LookupClassNode.makeFunction(language));
|
||||||
|
|
||||||
|
interopDispatchRoot = Truffle.getRuntime().createCallTarget(MethodDispatchNode.build(language));
|
||||||
|
interopDispatchSchema =
|
||||||
|
new FunctionSchema(
|
||||||
|
FunctionSchema.CallStrategy.ALWAYS_DIRECT,
|
||||||
|
FunctionSchema.CallerFrameAccess.NONE,
|
||||||
|
new ArgumentDefinition[] {
|
||||||
|
new ArgumentDefinition(1, "this", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||||
|
new ArgumentDefinition(2, "method_name", ArgumentDefinition.ExecutionMode.EXECUTE),
|
||||||
|
new ArgumentDefinition(3, "arguments", ArgumentDefinition.ExecutionMode.EXECUTE)
|
||||||
|
},
|
||||||
|
new boolean[] {false, true, false},
|
||||||
|
new CallArgumentInfo[0]);
|
||||||
|
newInstanceFunction = ConstructorDispatchNode.makeFunction(language);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtomConstructor createPolyglot(Language language) {
|
private AtomConstructor createPolyglot(Language language) {
|
||||||
@ -228,4 +253,20 @@ public class Builtins {
|
|||||||
public Module getModule() {
|
public Module getModule() {
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a function dispatching to a polyglot method call.
|
||||||
|
*
|
||||||
|
* @param method the name of the method this function will dispatch to.
|
||||||
|
* @return a function calling {@code method} with given arguments.
|
||||||
|
*/
|
||||||
|
public Function buildPolyglotMethodDispatch(String method) {
|
||||||
|
Object[] preAppliedArr = new Object[] {null, method, null};
|
||||||
|
return new Function(interopDispatchRoot, null, interopDispatchSchema, preAppliedArr, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return a function executing a constructor with given arguments. */
|
||||||
|
public Function getConstructorDispatch() {
|
||||||
|
return newInstanceFunction;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class Context {
|
|||||||
private final Env environment;
|
private final Env environment;
|
||||||
private final Compiler compiler;
|
private final Compiler compiler;
|
||||||
private final PrintStream out;
|
private final PrintStream out;
|
||||||
private List<Package> packages;
|
private final List<Package> packages;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Enso context.
|
* Creates a new Enso context.
|
||||||
@ -53,6 +53,16 @@ public class Context {
|
|||||||
.map(Optional::get)
|
.map(Optional::get)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
packages.forEach(
|
||||||
|
pkg -> {
|
||||||
|
List<File> classPathItems = ScalaConversions.asJava(pkg.listPolyglotExtensions("java"));
|
||||||
|
classPathItems.forEach(
|
||||||
|
cp -> {
|
||||||
|
TruffleFile f = getTruffleFile(cp);
|
||||||
|
getEnvironment().addToHostClassPath(f);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
Map<String, Module> knownFiles =
|
Map<String, Module> knownFiles =
|
||||||
packages.stream()
|
packages.stream()
|
||||||
.flatMap(p -> ScalaConversions.asJava(p.listSources()).stream())
|
.flatMap(p -> ScalaConversions.asJava(p.listSources()).stream())
|
||||||
|
@ -16,6 +16,7 @@ import org.enso.interpreter.runtime.error.RedefinedMethodException;
|
|||||||
public class ModuleScope {
|
public class ModuleScope {
|
||||||
private final AtomConstructor associatedType;
|
private final AtomConstructor associatedType;
|
||||||
private final Module module;
|
private final Module module;
|
||||||
|
private Map<String, Object> polyglotSymbols = new HashMap<>();
|
||||||
private Map<String, AtomConstructor> constructors = new HashMap<>();
|
private Map<String, AtomConstructor> constructors = new HashMap<>();
|
||||||
private Map<AtomConstructor, Map<String, Function>> methods = new HashMap<>();
|
private Map<AtomConstructor, Map<String, Function>> methods = new HashMap<>();
|
||||||
private Set<ModuleScope> imports = new HashSet<>();
|
private Set<ModuleScope> imports = new HashSet<>();
|
||||||
@ -113,6 +114,26 @@ public class ModuleScope {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a new symbol in the polyglot namespace.
|
||||||
|
*
|
||||||
|
* @param name the name of the symbol
|
||||||
|
* @param sym the value being exposed
|
||||||
|
*/
|
||||||
|
public void registerPolyglotSymbol(String name, Object sym) {
|
||||||
|
polyglotSymbols.put(name, sym);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up a polyglot symbol by name.
|
||||||
|
*
|
||||||
|
* @param name the name of the symbol being looked up
|
||||||
|
* @return the polyglot value registered for {@code name}, if exists.
|
||||||
|
*/
|
||||||
|
public Optional<Object> lookupPolyglotSymbol(String name) {
|
||||||
|
return Optional.ofNullable(polyglotSymbols.get(name));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up the definition for a given type and method name.
|
* Looks up the definition for a given type and method name.
|
||||||
*
|
*
|
||||||
|
@ -85,11 +85,19 @@ object AstToIr {
|
|||||||
|
|
||||||
val imports = presentBlocks.collect {
|
val imports = presentBlocks.collect {
|
||||||
case AST.Import.any(list) => translateImport(list)
|
case AST.Import.any(list) => translateImport(list)
|
||||||
|
case AST.JavaImport.any(imp) =>
|
||||||
|
val pkg = imp.path.init.map(_.name)
|
||||||
|
val cls = imp.path.last.name
|
||||||
|
Module.Scope.Import.Polyglot(
|
||||||
|
Module.Scope.Import.Polyglot.Java(pkg.mkString("."), cls),
|
||||||
|
getIdentifiedLocation(imp)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val nonImportBlocks = presentBlocks.filter {
|
val nonImportBlocks = presentBlocks.filter {
|
||||||
case AST.Import.any(_) => false
|
case AST.Import.any(_) => false
|
||||||
case _ => true
|
case AST.JavaImport.any(_) => false
|
||||||
|
case _ => true
|
||||||
}
|
}
|
||||||
|
|
||||||
val statements = nonImportBlocks.map(translateModuleSymbol)
|
val statements = nonImportBlocks.map(translateModuleSymbol)
|
||||||
@ -637,8 +645,8 @@ object AstToIr {
|
|||||||
* @param imp the import to translate
|
* @param imp the import to translate
|
||||||
* @return the [[Core]] representation of `imp`
|
* @return the [[Core]] representation of `imp`
|
||||||
*/
|
*/
|
||||||
def translateImport(imp: AST.Import): Module.Scope.Import = {
|
def translateImport(imp: AST.Import): Module.Scope.Import.Module = {
|
||||||
Module.Scope.Import(
|
Module.Scope.Import.Module(
|
||||||
imp.path.map(t => t.name).reduceLeft((l, r) => l + "." + r),
|
imp.path.map(t => t.name).reduceLeft((l, r) => l + "." + r),
|
||||||
getIdentifiedLocation(imp)
|
getIdentifiedLocation(imp)
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ package org.enso.compiler.codegen
|
|||||||
import com.oracle.truffle.api.Truffle
|
import com.oracle.truffle.api.Truffle
|
||||||
import com.oracle.truffle.api.source.{Source, SourceSection}
|
import com.oracle.truffle.api.source.{Source, SourceSection}
|
||||||
import org.enso.compiler.core.IR
|
import org.enso.compiler.core.IR
|
||||||
|
import org.enso.compiler.core.IR.Module.Scope.Import
|
||||||
import org.enso.compiler.core.IR.{Error, IdentifiedLocation}
|
import org.enso.compiler.core.IR.{Error, IdentifiedLocation}
|
||||||
import org.enso.compiler.exception.{CompilerError, UnhandledEntity}
|
import org.enso.compiler.exception.{CompilerError, UnhandledEntity}
|
||||||
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope}
|
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{Scope => AliasScope}
|
||||||
@ -26,6 +27,7 @@ import org.enso.interpreter.node.callable.{
|
|||||||
}
|
}
|
||||||
import org.enso.interpreter.node.controlflow._
|
import org.enso.interpreter.node.controlflow._
|
||||||
import org.enso.interpreter.node.expression.constant.{
|
import org.enso.interpreter.node.expression.constant.{
|
||||||
|
ConstantObjectNode,
|
||||||
ConstructorNode,
|
ConstructorNode,
|
||||||
DynamicSymbolNode,
|
DynamicSymbolNode,
|
||||||
ErrorNode
|
ErrorNode
|
||||||
@ -136,9 +138,16 @@ class IRToTruffle(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register the imports in scope
|
// Register the imports in scope
|
||||||
imports.foreach(i =>
|
imports.foreach {
|
||||||
this.moduleScope.addImport(context.getCompiler.processImport(i.name))
|
case Import.Polyglot(Import.Polyglot.Java(pkg, cls), _, _, _) =>
|
||||||
)
|
val fullName = s"$pkg.$cls"
|
||||||
|
this.moduleScope.registerPolyglotSymbol(
|
||||||
|
cls,
|
||||||
|
context.getEnvironment.lookupHostSymbol(fullName)
|
||||||
|
)
|
||||||
|
case i: Import.Module =>
|
||||||
|
this.moduleScope.addImport(context.getCompiler.processImport(i.name))
|
||||||
|
}
|
||||||
|
|
||||||
// Register the atoms and their constructors in scope
|
// Register the atoms and their constructors in scope
|
||||||
val atomConstructors =
|
val atomConstructors =
|
||||||
@ -542,14 +551,17 @@ class IRToTruffle(
|
|||||||
)
|
)
|
||||||
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
.unsafeAs[AliasAnalysis.Info.Occurrence]
|
||||||
|
|
||||||
val slot = scope.getFramePointer(useInfo.id)
|
val slot = scope.getFramePointer(useInfo.id)
|
||||||
val atomCons = moduleScope.getConstructor(nameStr).toScala
|
val atomCons = moduleScope.getConstructor(nameStr).toScala
|
||||||
|
val polySymbol = moduleScope.lookupPolyglotSymbol(nameStr).toScala
|
||||||
if (nameStr == Constants.Names.CURRENT_MODULE) {
|
if (nameStr == Constants.Names.CURRENT_MODULE) {
|
||||||
ConstructorNode.build(moduleScope.getAssociatedType)
|
ConstructorNode.build(moduleScope.getAssociatedType)
|
||||||
} else if (slot.isDefined) {
|
} else if (slot.isDefined) {
|
||||||
ReadLocalVariableNode.build(slot.get)
|
ReadLocalVariableNode.build(slot.get)
|
||||||
} else if (atomCons.isDefined) {
|
} else if (atomCons.isDefined) {
|
||||||
ConstructorNode.build(atomCons.get)
|
ConstructorNode.build(atomCons.get)
|
||||||
|
} else if (polySymbol.isDefined) {
|
||||||
|
ConstantObjectNode.build(polySymbol.get)
|
||||||
} else {
|
} else {
|
||||||
DynamicSymbolNode.build(
|
DynamicSymbolNode.build(
|
||||||
UnresolvedSymbol.build(nameStr, moduleScope)
|
UnresolvedSymbol.build(nameStr, moduleScope)
|
||||||
|
@ -267,57 +267,136 @@ object IR {
|
|||||||
}
|
}
|
||||||
object Scope {
|
object Scope {
|
||||||
|
|
||||||
/** An import statement.
|
/** Module-level import statements. */
|
||||||
*
|
sealed trait Import extends Scope {
|
||||||
* @param name the full `.`-separated path representing the import
|
override def mapExpressions(fn: Expression => Expression): Import
|
||||||
* @param location the source location that the node corresponds to
|
}
|
||||||
* @param passData the pass metadata associated with this node
|
|
||||||
* @param diagnostics compiler diagnostics for this node
|
|
||||||
*/
|
|
||||||
sealed case class Import(
|
|
||||||
name: String,
|
|
||||||
override val location: Option[IdentifiedLocation],
|
|
||||||
override val passData: MetadataStorage = MetadataStorage(),
|
|
||||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
|
||||||
) extends Scope
|
|
||||||
with IRKind.Primitive {
|
|
||||||
override protected var id: Identifier = randomId
|
|
||||||
|
|
||||||
/** Creates a copy of `this`.
|
object Import {
|
||||||
|
|
||||||
|
/** An import statement.
|
||||||
*
|
*
|
||||||
* @param name the full `.`-separated path representing the import
|
* @param name the full `.`-separated path representing the import
|
||||||
* @param location the source location that the node corresponds to
|
* @param location the source location that the node corresponds to
|
||||||
* @param passData the pass metadata associated with this node
|
* @param passData the pass metadata associated with this node
|
||||||
* @param diagnostics compiler diagnostics for this node
|
* @param diagnostics compiler diagnostics for this node
|
||||||
* @param id the identifier for the new node
|
|
||||||
* @return a copy of `this`, updated with the specified values
|
|
||||||
*/
|
*/
|
||||||
def copy(
|
sealed case class Module(
|
||||||
name: String = name,
|
name: String,
|
||||||
location: Option[IdentifiedLocation] = location,
|
override val location: Option[IdentifiedLocation],
|
||||||
passData: MetadataStorage = passData,
|
override val passData: MetadataStorage = MetadataStorage(),
|
||||||
diagnostics: DiagnosticStorage = diagnostics,
|
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||||
id: Identifier = id
|
) extends Import
|
||||||
): Import = {
|
with IRKind.Primitive {
|
||||||
val res = Import(name, location, passData, diagnostics)
|
override protected var id: Identifier = randomId
|
||||||
res.id = id
|
|
||||||
res
|
/** Creates a copy of `this`.
|
||||||
|
*
|
||||||
|
* @param name the full `.`-separated path representing the import
|
||||||
|
* @param location the source location that the node corresponds to
|
||||||
|
* @param passData the pass metadata associated with this node
|
||||||
|
* @param diagnostics compiler diagnostics for this node
|
||||||
|
* @param id the identifier for the new node
|
||||||
|
* @return a copy of `this`, updated with the specified values
|
||||||
|
*/
|
||||||
|
def copy(
|
||||||
|
name: String = name,
|
||||||
|
location: Option[IdentifiedLocation] = location,
|
||||||
|
passData: MetadataStorage = passData,
|
||||||
|
diagnostics: DiagnosticStorage = diagnostics,
|
||||||
|
id: Identifier = id
|
||||||
|
): Module = {
|
||||||
|
val res = Module(name, location, passData, diagnostics)
|
||||||
|
res.id = id
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
override def mapExpressions(
|
||||||
|
fn: Expression => Expression
|
||||||
|
): Module = this
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
s"""
|
||||||
|
|IR.Module.Scope.Import.Module(
|
||||||
|
|name = $name,
|
||||||
|
|location = $location,
|
||||||
|
|passData = ${this.showPassData},
|
||||||
|
|diagnostics = $diagnostics,
|
||||||
|
|id = $id
|
||||||
|
|)
|
||||||
|
|""".toSingleLine
|
||||||
|
|
||||||
|
override def children: List[IR] = List()
|
||||||
}
|
}
|
||||||
|
|
||||||
override def mapExpressions(fn: Expression => Expression): Import = this
|
object Polyglot {
|
||||||
|
|
||||||
override def toString: String =
|
/** Represents language-specific polyglot import data. */
|
||||||
s"""
|
sealed trait Entity
|
||||||
|IR.Module.Scope.Import(
|
|
||||||
|name = $name,
|
|
||||||
|location = $location,
|
|
||||||
|passData = ${this.showPassData},
|
|
||||||
|diagnostics = $diagnostics,
|
|
||||||
|id = $id
|
|
||||||
|)
|
|
||||||
|""".toSingleLine
|
|
||||||
|
|
||||||
override def children: List[IR] = List()
|
/** Represents an import of a Java class.
|
||||||
|
*
|
||||||
|
* @param packageName the name of the package containing the imported
|
||||||
|
* class
|
||||||
|
* @param className the class name
|
||||||
|
*/
|
||||||
|
case class Java(packageName: String, className: String) extends Entity
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An import of a polyglot class.
|
||||||
|
*
|
||||||
|
* @param entity language-specific information on the imported entity
|
||||||
|
* @param location the source location that the node corresponds to
|
||||||
|
* @param passData the pass metadata associated with this node
|
||||||
|
* @param diagnostics compiler diagnostics for this node
|
||||||
|
*/
|
||||||
|
sealed case class Polyglot(
|
||||||
|
entity: Polyglot.Entity,
|
||||||
|
override val location: Option[IdentifiedLocation],
|
||||||
|
override val passData: MetadataStorage = MetadataStorage(),
|
||||||
|
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||||
|
) extends Import
|
||||||
|
with IRKind.Primitive {
|
||||||
|
override protected var id: Identifier = randomId
|
||||||
|
|
||||||
|
/** Creates a copy of `this`.
|
||||||
|
*
|
||||||
|
* @param entity language-specific information on the imported entity
|
||||||
|
* @param location the source location that the node corresponds to
|
||||||
|
* @param passData the pass metadata associated with this node
|
||||||
|
* @param diagnostics compiler diagnostics for this node
|
||||||
|
* @param id the identifier for the new node
|
||||||
|
* @return a copy of `this`, updated with the specified values
|
||||||
|
*/
|
||||||
|
def copy(
|
||||||
|
entity: Polyglot.Entity,
|
||||||
|
location: Option[IdentifiedLocation] = location,
|
||||||
|
passData: MetadataStorage = passData,
|
||||||
|
diagnostics: DiagnosticStorage = diagnostics,
|
||||||
|
id: Identifier = id
|
||||||
|
): Polyglot = {
|
||||||
|
val res =
|
||||||
|
Polyglot(entity, location, passData, diagnostics)
|
||||||
|
res.id = id
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
override def mapExpressions(fn: Expression => Expression): Polyglot =
|
||||||
|
this
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
s"""
|
||||||
|
|IR.Module.Scope.Import.Polyglot(
|
||||||
|
|entity = $entity,
|
||||||
|
|location = $location,
|
||||||
|
|passData = ${this.showPassData},
|
||||||
|
|diagnostics = $diagnostics,
|
||||||
|
|id = $id
|
||||||
|
|)
|
||||||
|
|""".toSingleLine
|
||||||
|
|
||||||
|
override def children: List[IR] = List()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** A representation of top-level definitions. */
|
/** A representation of top-level definitions. */
|
||||||
|
@ -3,13 +3,12 @@ package org.enso.interpreter.test.semantic
|
|||||||
import org.enso.interpreter.test.InterpreterTest
|
import org.enso.interpreter.test.InterpreterTest
|
||||||
|
|
||||||
class JavaInteropTest extends InterpreterTest {
|
class JavaInteropTest extends InterpreterTest {
|
||||||
"Java interop" should "allow calling static methods from java classes" in {
|
"Java interop" should "allow importing classes and calling methods on them" in {
|
||||||
val code =
|
val code =
|
||||||
"""
|
"""
|
||||||
|main =
|
|polyglot java import org.enso.example.TestClass
|
||||||
| class = Java.lookup_class "org.enso.example.TestClass"
|
|
|
||||||
| method = Polyglot.get_member class "add"
|
|main = TestClass.add [1, 2]
|
||||||
| Polyglot.execute method [1, 2]
|
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
|
|
||||||
eval(code) shouldEqual 3
|
eval(code) shouldEqual 3
|
||||||
@ -18,33 +17,12 @@ class JavaInteropTest extends InterpreterTest {
|
|||||||
"Java interop" should "allow instantiating objects and calling methods on them" in {
|
"Java interop" should "allow instantiating objects and calling methods on them" in {
|
||||||
val code =
|
val code =
|
||||||
"""
|
"""
|
||||||
|
|polyglot java import org.enso.example.TestClass
|
||||||
|
|
|
||||||
|main =
|
|main =
|
||||||
| class = Java.lookup_class "org.enso.example.TestClass"
|
| instance = TestClass.new [x -> x * 2]
|
||||||
| instance = Polyglot.new class [x -> x * 2]
|
| instance.callFunctionAndIncrement [10]
|
||||||
| Polyglot.invoke instance "callFunctionAndIncrement" [10]
|
|
||||||
|""".stripMargin
|
|""".stripMargin
|
||||||
eval(code) shouldEqual 21
|
eval(code) shouldEqual 21
|
||||||
}
|
}
|
||||||
|
|
||||||
"Java interop" should "allow listing available members of an object" in {
|
|
||||||
val code =
|
|
||||||
"""
|
|
||||||
|main =
|
|
||||||
| class = Java.lookup_class "org.enso.example.TestClass"
|
|
||||||
| instance = Polyglot.new class []
|
|
||||||
| members = Polyglot.get_members instance
|
|
||||||
| IO.println (Polyglot.get_array_size members)
|
|
||||||
| IO.println (Polyglot.get_array_element members 0)
|
|
||||||
| IO.println (Polyglot.get_array_element members 1)
|
|
||||||
| IO.println (Polyglot.get_array_element members 2)
|
|
||||||
|""".stripMargin
|
|
||||||
eval(code)
|
|
||||||
val count :: methods = consumeOut
|
|
||||||
count shouldEqual "3"
|
|
||||||
methods.toSet shouldEqual Set(
|
|
||||||
"method1",
|
|
||||||
"method2",
|
|
||||||
"callFunctionAndIncrement"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
package org.enso.interpreter.test.semantic
|
||||||
|
|
||||||
|
import org.enso.interpreter.test.InterpreterTest
|
||||||
|
|
||||||
|
class PolyglotTest extends InterpreterTest {
|
||||||
|
"Polyglot" should "allow calling methods on static objects" in {
|
||||||
|
val code =
|
||||||
|
"""
|
||||||
|
|main =
|
||||||
|
| class = Java.lookup_class "org.enso.example.TestClass"
|
||||||
|
| method = Polyglot.get_member class "add"
|
||||||
|
| Polyglot.execute method [1, 2]
|
||||||
|
|""".stripMargin
|
||||||
|
|
||||||
|
eval(code) shouldEqual 3
|
||||||
|
}
|
||||||
|
|
||||||
|
"Polyglot" should "allow instantiating objects and calling methods on them" in {
|
||||||
|
val code =
|
||||||
|
"""
|
||||||
|
|main =
|
||||||
|
| class = Java.lookup_class "org.enso.example.TestClass"
|
||||||
|
| instance = Polyglot.new class [x -> x * 2]
|
||||||
|
| Polyglot.invoke instance "callFunctionAndIncrement" [10]
|
||||||
|
|""".stripMargin
|
||||||
|
eval(code) shouldEqual 21
|
||||||
|
}
|
||||||
|
|
||||||
|
"Polyglot" should "allow listing available members of an object" in {
|
||||||
|
val code =
|
||||||
|
"""
|
||||||
|
|main =
|
||||||
|
| class = Java.lookup_class "org.enso.example.TestClass"
|
||||||
|
| instance = Polyglot.new class []
|
||||||
|
| members = Polyglot.get_members instance
|
||||||
|
| IO.println (Polyglot.get_array_size members)
|
||||||
|
| IO.println (Polyglot.get_array_element members 0)
|
||||||
|
| IO.println (Polyglot.get_array_element members 1)
|
||||||
|
| IO.println (Polyglot.get_array_element members 2)
|
||||||
|
|""".stripMargin
|
||||||
|
eval(code)
|
||||||
|
val count :: methods = consumeOut
|
||||||
|
count shouldEqual "3"
|
||||||
|
methods.toSet shouldEqual Set(
|
||||||
|
"method1",
|
||||||
|
"method2",
|
||||||
|
"callFunctionAndIncrement"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -29,9 +29,10 @@ case class SourceFile(qualifiedName: QualifiedName, file: File)
|
|||||||
*/
|
*/
|
||||||
case class Package(root: File, config: Config) {
|
case class Package(root: File, config: Config) {
|
||||||
|
|
||||||
val sourceDir = new File(root, Package.sourceDirName)
|
val sourceDir = new File(root, Package.sourceDirName)
|
||||||
val configFile = new File(root, Package.configFileName)
|
val configFile = new File(root, Package.configFileName)
|
||||||
val thumbFile = new File(root, Package.thumbFileName)
|
val thumbFile = new File(root, Package.thumbFileName)
|
||||||
|
val polyglotDir = new File(root, Package.polyglotExtensionsDirName)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores the package metadata on the hard drive. If the package does not exist,
|
* Stores the package metadata on the hard drive. If the package does not exist,
|
||||||
@ -171,6 +172,24 @@ case class Package(root: File, config: Config) {
|
|||||||
SourceFile(moduleNameForFile(path.toFile), path.toFile)
|
SourceFile(moduleNameForFile(path.toFile), path.toFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists contents of the polyglot extensions directory for a given language.
|
||||||
|
*
|
||||||
|
* @param languageName the language to list extenstions for
|
||||||
|
* @return a list of files and directories contained in the relevant
|
||||||
|
* polyglot extensions directory.
|
||||||
|
*/
|
||||||
|
def listPolyglotExtensions(languageName: String): List[File] = {
|
||||||
|
val dir = new File(polyglotDir, languageName)
|
||||||
|
if (!dir.isDirectory) return List()
|
||||||
|
Files
|
||||||
|
.list(dir.toPath)
|
||||||
|
.map(_.toFile)
|
||||||
|
.iterator()
|
||||||
|
.asScala
|
||||||
|
.toList
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,19 +204,19 @@ case class QualifiedName(path: List[String], module: String) {
|
|||||||
(path :+ module).mkString(qualifiedNameSeparator)
|
(path :+ module).mkString(qualifiedNameSeparator)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the parent of this qualified name.
|
* Get the parent of this qualified name.
|
||||||
*
|
*
|
||||||
* @return the parent of this qualified name.
|
* @return the parent of this qualified name.
|
||||||
*/
|
*/
|
||||||
def getParent: Option[QualifiedName] =
|
def getParent: Option[QualifiedName] =
|
||||||
path.lastOption.map(QualifiedName(path.init, _))
|
path.lastOption.map(QualifiedName(path.init, _))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a child qualified name taking this name as a parent.
|
* Create a child qualified name taking this name as a parent.
|
||||||
*
|
*
|
||||||
* @param name the name of a child node.
|
* @param name the name of a child node.
|
||||||
* @return a new qualified name based on this name.
|
* @return a new qualified name based on this name.
|
||||||
*/
|
*/
|
||||||
def createChild(name: String): QualifiedName =
|
def createChild(name: String): QualifiedName =
|
||||||
QualifiedName(path :+ module, name)
|
QualifiedName(path :+ module, name)
|
||||||
}
|
}
|
||||||
@ -231,6 +250,7 @@ object Package {
|
|||||||
val fileExtension = "enso"
|
val fileExtension = "enso"
|
||||||
val configFileName = "package.yaml"
|
val configFileName = "package.yaml"
|
||||||
val sourceDirName = "src"
|
val sourceDirName = "src"
|
||||||
|
val polyglotExtensionsDirName = "polyglot"
|
||||||
val mainFileName = "Main.enso"
|
val mainFileName = "Main.enso"
|
||||||
val thumbFileName = "thumb.png"
|
val thumbFileName = "thumb.png"
|
||||||
val qualifiedNameSeparator = "."
|
val qualifiedNameSeparator = "."
|
||||||
|
@ -348,7 +348,8 @@ object Shape extends ShapeImplicit {
|
|||||||
with Phantom
|
with Phantom
|
||||||
final case class Documented[T](doc: Doc, emptyLinesBetween: Int, ast: T)
|
final case class Documented[T](doc: Doc, emptyLinesBetween: Int, ast: T)
|
||||||
extends SpacelessAST[T]
|
extends SpacelessAST[T]
|
||||||
final case class Import[T](path: List1[AST.Cons]) extends SpacelessAST[T]
|
final case class Import[T](path: List1[AST.Cons]) extends SpacelessAST[T]
|
||||||
|
final case class JavaImport[T](path: List1[AST.Ident]) extends SpacelessAST[T]
|
||||||
final case class Mixfix[T](name: List1[AST.Ident], args: List1[T])
|
final case class Mixfix[T](name: List1[AST.Ident], args: List1[T])
|
||||||
extends SpacelessAST[T]
|
extends SpacelessAST[T]
|
||||||
final case class Group[T](body: Option[T]) extends SpacelessAST[T]
|
final case class Group[T](body: Option[T]) extends SpacelessAST[T]
|
||||||
@ -958,6 +959,21 @@ object Shape extends ShapeImplicit {
|
|||||||
implicit def span[T]: HasSpan[Import[T]] = _ => 0
|
implicit def span[T]: HasSpan[Import[T]] = _ => 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object JavaImport {
|
||||||
|
implicit def ftor: Functor[JavaImport] = semi.functor
|
||||||
|
implicit def fold: Foldable[JavaImport] = semi.foldable
|
||||||
|
implicit def repr[T]: Repr[JavaImport[T]] =
|
||||||
|
t =>
|
||||||
|
R + ("polyglot java import " + t.path
|
||||||
|
.map(_.repr.build())
|
||||||
|
.toList
|
||||||
|
.mkString("."))
|
||||||
|
|
||||||
|
// FIXME: How to make it automatic for non-spaced AST?
|
||||||
|
implicit def ozip[T]: OffsetZip[JavaImport, T] = _.map(Index.Start -> _)
|
||||||
|
implicit def span[T]: HasSpan[JavaImport[T]] = _ => 0
|
||||||
|
}
|
||||||
|
|
||||||
object Mixfix {
|
object Mixfix {
|
||||||
implicit def ftor: Functor[Mixfix] = semi.functor
|
implicit def ftor: Functor[Mixfix] = semi.functor
|
||||||
implicit def fold: Foldable[Mixfix] = semi.foldable
|
implicit def fold: Foldable[Mixfix] = semi.foldable
|
||||||
@ -1083,6 +1099,7 @@ sealed trait ShapeImplicit {
|
|||||||
case s: Comment[T] => s.repr
|
case s: Comment[T] => s.repr
|
||||||
case s: Documented[T] => s.repr
|
case s: Documented[T] => s.repr
|
||||||
case s: Import[T] => s.repr
|
case s: Import[T] => s.repr
|
||||||
|
case s: JavaImport[T] => s.repr
|
||||||
case s: Mixfix[T] => s.repr
|
case s: Mixfix[T] => s.repr
|
||||||
case s: Group[T] => s.repr
|
case s: Group[T] => s.repr
|
||||||
case s: SequenceLiteral[T] => s.repr
|
case s: SequenceLiteral[T] => s.repr
|
||||||
@ -1120,6 +1137,7 @@ sealed trait ShapeImplicit {
|
|||||||
case s: Comment[T] => OffsetZip[Comment, T].zipWithOffset(s)
|
case s: Comment[T] => OffsetZip[Comment, T].zipWithOffset(s)
|
||||||
case s: Documented[T] => OffsetZip[Documented, T].zipWithOffset(s)
|
case s: Documented[T] => OffsetZip[Documented, T].zipWithOffset(s)
|
||||||
case s: Import[T] => OffsetZip[Import, T].zipWithOffset(s)
|
case s: Import[T] => OffsetZip[Import, T].zipWithOffset(s)
|
||||||
|
case s: JavaImport[T] => OffsetZip[JavaImport, T].zipWithOffset(s)
|
||||||
case s: Mixfix[T] => OffsetZip[Mixfix, T].zipWithOffset(s)
|
case s: Mixfix[T] => OffsetZip[Mixfix, T].zipWithOffset(s)
|
||||||
case s: Group[T] => OffsetZip[Group, T].zipWithOffset(s)
|
case s: Group[T] => OffsetZip[Group, T].zipWithOffset(s)
|
||||||
case s: SequenceLiteral[T] => OffsetZip[SequenceLiteral, T].zipWithOffset(s)
|
case s: SequenceLiteral[T] => OffsetZip[SequenceLiteral, T].zipWithOffset(s)
|
||||||
@ -1158,6 +1176,7 @@ sealed trait ShapeImplicit {
|
|||||||
case s: Comment[T] => s.span()
|
case s: Comment[T] => s.span()
|
||||||
case s: Documented[T] => s.span()
|
case s: Documented[T] => s.span()
|
||||||
case s: Import[T] => s.span()
|
case s: Import[T] => s.span()
|
||||||
|
case s: JavaImport[T] => s.span()
|
||||||
case s: Mixfix[T] => s.span()
|
case s: Mixfix[T] => s.span()
|
||||||
case s: Group[T] => s.span()
|
case s: Group[T] => s.span()
|
||||||
case s: SequenceLiteral[T] => s.span()
|
case s: SequenceLiteral[T] => s.span()
|
||||||
@ -2273,6 +2292,19 @@ object AST {
|
|||||||
val any = UnapplyByType[Import]
|
val any = UnapplyByType[Import]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//// Java Import /////////////////////////////////////////////////////////////
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
type JavaImport = ASTOf[Shape.JavaImport]
|
||||||
|
|
||||||
|
object JavaImport {
|
||||||
|
def apply(path: List1[Ident]): JavaImport = Shape.JavaImport[AST](path)
|
||||||
|
def unapply(t: AST): Option[List1[Ident]] =
|
||||||
|
Unapply[JavaImport].run(t => t.path)(t)
|
||||||
|
val any = UnapplyByType[JavaImport]
|
||||||
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
//// Mixfix //////////////////////////////////////////////////////////////////
|
//// Mixfix //////////////////////////////////////////////////////////////////
|
||||||
//////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -76,6 +76,30 @@ object Builtin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val polyglotJavaImport = {
|
||||||
|
val javaQualName =
|
||||||
|
Pattern.SepList(Pattern.Cons().or(Pattern.Var()), AST.Opr("."))
|
||||||
|
Definition(
|
||||||
|
Var("polyglot") -> Pattern.fromAST(Var("java")),
|
||||||
|
Var("import") -> javaQualName
|
||||||
|
) { ctx =>
|
||||||
|
ctx.body match {
|
||||||
|
case List(_, segments) =>
|
||||||
|
val nonDot: List[AST.Ident] =
|
||||||
|
segments.body.toStream.map(_.wrapped).collect {
|
||||||
|
case AST.Ident.Var.any(v) => v: AST.Ident
|
||||||
|
case AST.Ident.Cons.any(c) => c: AST.Ident
|
||||||
|
}
|
||||||
|
// The optional unwrap is safe by construction - the pattern
|
||||||
|
// guarantees at least one Var or Cons in the match result.
|
||||||
|
AST.JavaImport(
|
||||||
|
List1.fromListOption(nonDot).getOrElse(internalError)
|
||||||
|
)
|
||||||
|
case _ => internalError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val imp = Definition(
|
val imp = Definition(
|
||||||
Var("import") -> Pattern
|
Var("import") -> Pattern
|
||||||
.SepList(Pattern.Cons(), AST.Opr("."): AST, "expected module name")
|
.SepList(Pattern.Cons(), AST.Opr("."): AST, "expected module name")
|
||||||
@ -266,6 +290,7 @@ object Builtin {
|
|||||||
case_of,
|
case_of,
|
||||||
if_then,
|
if_then,
|
||||||
if_then_else,
|
if_then_else,
|
||||||
|
polyglotJavaImport,
|
||||||
imp,
|
imp,
|
||||||
defn,
|
defn,
|
||||||
arrow,
|
arrow,
|
||||||
|
Loading…
Reference in New Issue
Block a user