mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 09:22:41 +03:00
Better way of registering builtin functions (#255)
This commit is contained in:
parent
3c4fd0c55b
commit
6569cc5cb0
@ -32,7 +32,6 @@ import org.enso.interpreter.node.controlflow.DefaultFallbackNode;
|
||||
import org.enso.interpreter.node.controlflow.FallbackNode;
|
||||
import org.enso.interpreter.node.controlflow.IfZeroNode;
|
||||
import org.enso.interpreter.node.controlflow.MatchNode;
|
||||
import org.enso.interpreter.node.expression.builtin.PrintNodeGen;
|
||||
import org.enso.interpreter.node.expression.constant.ConstructorNode;
|
||||
import org.enso.interpreter.node.expression.constant.DynamicSymbolNode;
|
||||
import org.enso.interpreter.node.expression.literal.IntegerLiteralNode;
|
||||
@ -370,17 +369,6 @@ public class ExpressionFactory implements AstExpressionVisitor<ExpressionNode> {
|
||||
return AssignmentNodeGen.create(expr.visit(this), slot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a runtime node representing a print expression.
|
||||
*
|
||||
* @param body an expression that computes the value to print
|
||||
* @return a runtime node representing the print
|
||||
*/
|
||||
@Override
|
||||
public ExpressionNode visitPrint(AstExpression body) {
|
||||
return PrintNodeGen.create(body.visit(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a runtime node representing a pattern match.
|
||||
*
|
||||
|
@ -14,7 +14,7 @@ import org.enso.interpreter.runtime.type.TypesGen;
|
||||
*
|
||||
* <p>Enso is an expression-oriented language, and hence doesn't have any statements. This means
|
||||
* that all expression execution will return a value, even if that is just the {@link
|
||||
* Builtins#UNIT} type.
|
||||
* Builtins#unit()} type.
|
||||
*
|
||||
* <p>This class contains specialisations of the {@link #executeGeneric(VirtualFrame)
|
||||
* executeGeneric} method for various scenarios in order to improve performance.
|
||||
|
@ -1,52 +1,62 @@
|
||||
package org.enso.interpreter.node.expression.builtin;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.RootCallTarget;
|
||||
import com.oracle.truffle.api.Truffle;
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.nodes.RootNode;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.Builtins;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.function.ArgumentSchema;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
|
||||
/** This node allows for printing the result of an arbitrary expression to standard output. */
|
||||
@NodeInfo(shortName = "print", description = "Prints the value of child expression.")
|
||||
public abstract class PrintNode extends ExpressionNode {
|
||||
@Child private ExpressionNode expression;
|
||||
/** This node allows for printing the result of arbitrary values to standard output. */
|
||||
@NodeInfo(shortName = "print", description = "Root of the IO.println method.")
|
||||
public abstract class PrintNode extends RootNode {
|
||||
|
||||
/**
|
||||
* Creates a node that prints the result of its expression.
|
||||
* Creates a root node for the builtin IO.println method.
|
||||
*
|
||||
* @param expression the expression to print the result of
|
||||
* @param language the current {@link Language} instance.
|
||||
*/
|
||||
public PrintNode(ExpressionNode expression) {
|
||||
this.expression = expression;
|
||||
public PrintNode(Language language) {
|
||||
super(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the print node.
|
||||
*
|
||||
* @param frame the stack frame for execution
|
||||
* @return unit {@link Builtins#UNIT unit} type
|
||||
*/
|
||||
@Specialization
|
||||
public Object doPrint(VirtualFrame frame, @CachedContext(Language.class) Context ctx) {
|
||||
doPrint(ctx.getOut(), expression.executeGeneric(frame));
|
||||
protected Object doPrint(VirtualFrame frame, @CachedContext(Language.class) Context ctx) {
|
||||
doPrint(ctx.getOut(), Function.ArgumentsHelper.getPositionalArguments(frame.getArguments())[1]);
|
||||
|
||||
return Builtins.UNIT.newInstance();
|
||||
return ctx.getUnit().newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the provided value to standard output.
|
||||
*
|
||||
* @param object the value to print
|
||||
*/
|
||||
@CompilerDirectives.TruffleBoundary
|
||||
private void doPrint(PrintStream out, Object object) {
|
||||
out.println(object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Function} object ignoring its first argument and printing the second to the
|
||||
* standard output.
|
||||
*
|
||||
* @param language the current {@link Language} instance
|
||||
* @return a {@link Function} object wrapping the behavior of this node
|
||||
*/
|
||||
public static Function toFunction(Language language) {
|
||||
PrintNode node = PrintNodeGen.create(language);
|
||||
RootCallTarget callTarget = Truffle.getRuntime().createCallTarget(node);
|
||||
ArgumentSchema schema =
|
||||
new ArgumentSchema(
|
||||
new ArgumentDefinition(0, "this", false), new ArgumentDefinition(1, "value", false));
|
||||
return new Function(callTarget, null, schema);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.enso.interpreter.node.scope;
|
||||
|
||||
import com.oracle.truffle.api.dsl.CachedContext;
|
||||
import com.oracle.truffle.api.dsl.NodeChild;
|
||||
import com.oracle.truffle.api.dsl.NodeField;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
@ -7,8 +8,10 @@ import com.oracle.truffle.api.frame.FrameSlot;
|
||||
import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.ExpressionNode;
|
||||
import org.enso.interpreter.runtime.Builtins;
|
||||
import org.enso.interpreter.runtime.Context;
|
||||
|
||||
/** This node represents an assignment to a variable in a given scope. */
|
||||
@NodeInfo(shortName = "=", description = "Assigns expression result to a variable.")
|
||||
@ -20,14 +23,16 @@ public abstract class AssignmentNode extends ExpressionNode {
|
||||
*
|
||||
* @param frame the frame to write to
|
||||
* @param value the value to write
|
||||
* @return the {@link Builtins#UNIT unit} type
|
||||
* @param ctx language context for global values access
|
||||
* @return the unit type
|
||||
*/
|
||||
@Specialization
|
||||
protected Object writeLong(VirtualFrame frame, long value) {
|
||||
protected Object writeLong(
|
||||
VirtualFrame frame, long value, @CachedContext(Language.class) Context ctx) {
|
||||
frame.getFrameDescriptor().setFrameSlotKind(getFrameSlot(), FrameSlotKind.Long);
|
||||
frame.setLong(getFrameSlot(), value);
|
||||
|
||||
return Builtins.UNIT.newInstance();
|
||||
return ctx.getUnit().newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,14 +40,16 @@ public abstract class AssignmentNode extends ExpressionNode {
|
||||
*
|
||||
* @param frame the frame to write to
|
||||
* @param value the value to write
|
||||
* @return the {@link Builtins#UNIT unit} type
|
||||
* @param ctx language context for global values access
|
||||
* @return the unit type
|
||||
*/
|
||||
@Specialization
|
||||
protected Object writeObject(VirtualFrame frame, Object value) {
|
||||
protected Object writeObject(
|
||||
VirtualFrame frame, Object value, @CachedContext(Language.class) Context ctx) {
|
||||
frame.getFrameDescriptor().setFrameSlotKind(getFrameSlot(), FrameSlotKind.Object);
|
||||
frame.setObject(getFrameSlot(), value);
|
||||
|
||||
return Builtins.UNIT.newInstance();
|
||||
return ctx.getUnit().newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,24 +1,55 @@
|
||||
package org.enso.interpreter.runtime;
|
||||
|
||||
import org.enso.interpreter.Language;
|
||||
import org.enso.interpreter.node.expression.builtin.PrintNode;
|
||||
import org.enso.interpreter.runtime.callable.argument.ArgumentDefinition;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
|
||||
/** Container class for static predefined atoms and their containing scope. */
|
||||
/** Container class for static predefined atoms, methods, and their containing scope. */
|
||||
public class Builtins {
|
||||
public static final ModuleScope BUILTIN_SCOPE = new ModuleScope();
|
||||
public static final AtomConstructor UNIT =
|
||||
new AtomConstructor("Unit", BUILTIN_SCOPE).initializeFields();
|
||||
public static final AtomConstructor NIL =
|
||||
new AtomConstructor("Nil", BUILTIN_SCOPE).initializeFields();
|
||||
public static final AtomConstructor CONS =
|
||||
new AtomConstructor("Cons", BUILTIN_SCOPE)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "head", false), new ArgumentDefinition(1, "rest", false));
|
||||
private final ModuleScope scope;
|
||||
private final AtomConstructor unit;
|
||||
|
||||
static {
|
||||
BUILTIN_SCOPE.registerConstructor(Builtins.CONS);
|
||||
BUILTIN_SCOPE.registerConstructor(Builtins.NIL);
|
||||
BUILTIN_SCOPE.registerConstructor(Builtins.UNIT);
|
||||
/**
|
||||
* Creates an instance with builtin methods installed.
|
||||
*
|
||||
* @param language the current {@link Language} instance
|
||||
*/
|
||||
public Builtins(Language language) {
|
||||
scope = new ModuleScope();
|
||||
unit = new AtomConstructor("Unit", scope).initializeFields();
|
||||
|
||||
AtomConstructor nil = new AtomConstructor("Nil", scope).initializeFields();
|
||||
AtomConstructor cons =
|
||||
new AtomConstructor("Cons", scope)
|
||||
.initializeFields(
|
||||
new ArgumentDefinition(0, "head", false), new ArgumentDefinition(1, "rest", false));
|
||||
AtomConstructor io = new AtomConstructor("IO", scope).initializeFields();
|
||||
|
||||
scope.registerConstructor(cons);
|
||||
scope.registerConstructor(nil);
|
||||
scope.registerConstructor(unit);
|
||||
scope.registerConstructor(io);
|
||||
|
||||
scope.registerMethod(io, "println", PrintNode.toFunction(language));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Unit} atom constructor.
|
||||
*
|
||||
* @return the {@code Unit} atom constructor
|
||||
*/
|
||||
public AtomConstructor unit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the builtin module scope.
|
||||
*
|
||||
* @return the builtin module scope
|
||||
*/
|
||||
public ModuleScope getScope() {
|
||||
return scope;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ 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.error.ModuleDoesNotExistException;
|
||||
import org.enso.interpreter.runtime.scope.ModuleScope;
|
||||
import org.enso.interpreter.util.ScalaConversions;
|
||||
@ -36,6 +37,7 @@ public class Context {
|
||||
private final Env environment;
|
||||
private final Map<String, Module> knownFiles;
|
||||
private final PrintStream out;
|
||||
private final Builtins builtins;
|
||||
|
||||
/**
|
||||
* Creates a new Enso context.
|
||||
@ -47,6 +49,7 @@ public class Context {
|
||||
this.language = language;
|
||||
this.environment = environment;
|
||||
this.out = new PrintStream(environment.out());
|
||||
this.builtins = new Builtins(language);
|
||||
|
||||
List<File> packagePaths = RuntimeOptions.getPackagesPaths(environment);
|
||||
// TODO [MK] Replace getTruffleFile with getInternalTruffleFile when Graal 19.3.0 comes out.
|
||||
@ -86,7 +89,7 @@ public class Context {
|
||||
* @return a call target which execution corresponds to the toplevel executable bits in the module
|
||||
*/
|
||||
public CallTarget parse(Source source) {
|
||||
return parse(source, new ModuleScope());
|
||||
return parse(source, createScope());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -131,4 +134,29 @@ public class Context {
|
||||
public PrintStream getOut() {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new module scope that automatically imports all the builtin types and methods.
|
||||
*
|
||||
* @return a new module scope with automatic builtins dependency.
|
||||
*/
|
||||
public ModuleScope createScope() {
|
||||
ModuleScope moduleScope = new ModuleScope();
|
||||
moduleScope.addImport(getBuiltins().getScope());
|
||||
return moduleScope;
|
||||
}
|
||||
|
||||
private Builtins getBuiltins() {
|
||||
return builtins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the atom constructor corresponding to the {@code Unit} type, for builtin constructs
|
||||
* that need to return an atom of this type.
|
||||
*
|
||||
* @return the builtin {@code Unit} atom constructor
|
||||
*/
|
||||
public AtomConstructor getUnit() {
|
||||
return getBuiltins().unit();
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ public class Module {
|
||||
*/
|
||||
public ModuleScope requestParse(Context context) throws IOException {
|
||||
if (cachedScope == null) {
|
||||
cachedScope = new ModuleScope();
|
||||
cachedScope = context.createScope();
|
||||
context.parse(file, cachedScope);
|
||||
}
|
||||
return cachedScope;
|
||||
|
@ -48,7 +48,7 @@ public class ArgumentSchema {
|
||||
*
|
||||
* @param argumentInfos Definition site arguments information
|
||||
*/
|
||||
public ArgumentSchema(ArgumentDefinition[] argumentInfos) {
|
||||
public ArgumentSchema(ArgumentDefinition... argumentInfos) {
|
||||
this(argumentInfos, new boolean[argumentInfos.length], new CallArgumentInfo[0]);
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.enso.interpreter.runtime.scope;
|
||||
|
||||
import org.enso.interpreter.runtime.Builtins;
|
||||
import org.enso.interpreter.runtime.Module;
|
||||
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
|
||||
import org.enso.interpreter.runtime.callable.function.Function;
|
||||
|
||||
@ -14,11 +15,6 @@ public class ModuleScope {
|
||||
private final Set<ModuleScope> imports = new HashSet<>();
|
||||
private final Set<ModuleScope> transitiveImports = new HashSet<>();
|
||||
|
||||
/** Creates a new scope. Every scope implicitly imports the builtin scope. */
|
||||
public ModuleScope() {
|
||||
imports.add(Builtins.BUILTIN_SCOPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an Atom constructor definition to the module scope.
|
||||
*
|
||||
@ -81,9 +77,13 @@ public class ModuleScope {
|
||||
*/
|
||||
public Function lookupMethodDefinition(AtomConstructor atom, String name) {
|
||||
Function definedWithAtom = atom.getDefinitionScope().getMethodMapFor(atom).get(name);
|
||||
if (definedWithAtom != null) { return definedWithAtom; }
|
||||
if (definedWithAtom != null) {
|
||||
return definedWithAtom;
|
||||
}
|
||||
Function definedHere = getMethodMapFor(atom).get(name);
|
||||
if (definedHere != null) { return definedHere; }
|
||||
if (definedHere != null) {
|
||||
return definedHere;
|
||||
}
|
||||
return transitiveImports.stream()
|
||||
.map(scope -> scope.getMethodMapFor(atom).get(name))
|
||||
.filter(Objects::nonNull)
|
||||
|
@ -41,8 +41,6 @@ trait AstExpressionVisitor[+T] {
|
||||
|
||||
def visitAssignment(varName: String, expr: AstExpression): T
|
||||
|
||||
def visitPrint(body: AstExpression): T
|
||||
|
||||
def visitMatch(
|
||||
target: AstExpression,
|
||||
branches: java.util.List[AstCase],
|
||||
@ -203,11 +201,6 @@ case class AstAssignment(name: String, body: AstExpression)
|
||||
visitor.visitAssignment(name, body)
|
||||
}
|
||||
|
||||
case class AstPrint(body: AstExpression) extends AstExpression {
|
||||
override def visit[T](visitor: AstExpressionVisitor[T]): T =
|
||||
visitor.visitPrint(body)
|
||||
}
|
||||
|
||||
case class AstIfZero(
|
||||
cond: AstExpression,
|
||||
ifTrue: AstExpression,
|
||||
@ -293,7 +286,7 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
}
|
||||
|
||||
def expression: Parser[AstExpression] =
|
||||
desuspend | print | ifZero | matchClause | arith | function
|
||||
desuspend | ifZero | matchClause | arith | function
|
||||
|
||||
def functionCall: Parser[AstApply] =
|
||||
"@" ~> expression ~ (argList ?) ~ defaultSuspend ^^ {
|
||||
@ -313,8 +306,6 @@ class EnsoParserInternal extends JavaTokenParsers {
|
||||
case v ~ exp => AstAssignment(v, exp)
|
||||
}
|
||||
|
||||
def print: Parser[AstPrint] = "print:" ~> expression ^^ AstPrint
|
||||
|
||||
def ifZero: Parser[AstIfZero] =
|
||||
"ifZero:" ~> "[" ~> (expression ~ ("," ~> expression ~ ("," ~> expression))) <~ "]" ^^ {
|
||||
case cond ~ (ift ~ iff) => AstIfZero(cond, ift, iff)
|
||||
|
@ -8,7 +8,7 @@ class LazyArgumentsTest extends LanguageTest {
|
||||
"""
|
||||
|@{
|
||||
| foo = { |i, $x, $y| ifZero: [i, $x, $y] };
|
||||
| @foo [1, (print: 1), (print: 2)]
|
||||
| @foo [1, @println [@IO, 1], @println [@IO, 2]]
|
||||
|}
|
||||
|""".stripMargin
|
||||
noException should be thrownBy parse(code)
|
||||
@ -53,9 +53,9 @@ class LazyArgumentsTest extends LanguageTest {
|
||||
|Bar.method = { |x| 10 }
|
||||
|
|
||||
|@{
|
||||
| @method [@Foo, (print: 1)];
|
||||
| @method [@Bar, (print: 2)];
|
||||
| @method [@Foo, (print: 3)]
|
||||
| @method [@Foo, @println [@IO, 1]];
|
||||
| @method [@Bar, @println [@IO, 2]];
|
||||
| @method [@Foo, @println [@IO, 3]]
|
||||
|}
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
@ -68,8 +68,8 @@ class LazyArgumentsTest extends LanguageTest {
|
||||
|@{
|
||||
| if = { |c, $ifT, $ifF| ifZero: [c, $ifT, $ifF] };
|
||||
| foo = { |c| @if [c] };
|
||||
| @foo [0, (print: 1), (print: 2)];
|
||||
| @foo [1, (print: 3), (print: 4)]
|
||||
| @foo [0, @println [@IO, 1], @println [@IO, 2]];
|
||||
| @foo [1, @println [@IO, 3], @println [@IO, 4]]
|
||||
|}
|
||||
|""".stripMargin
|
||||
eval(code)
|
||||
|
Loading…
Reference in New Issue
Block a user