From 7324dc0bd7c70c0be4a28ca2bba5522cceaf277f Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Tue, 6 Oct 2015 13:44:18 +0100 Subject: Add basic implementation of instance methods --- .../zwobble/couscous/ast/ConstructorCallNode.java | 18 +++++++++ .../org/zwobble/couscous/ast/ExpressionNode.java | 6 +++ .../java/org/zwobble/couscous/ast/MethodNode.java | 4 ++ .../ast/visitors/ExpressionNodeMapper.java | 2 + .../zwobble/couscous/ast/visitors/NodeVisitor.java | 2 + .../ast/visitors/NodeVisitorWithEmptyDefaults.java | 5 +++ .../couscous/ast/visitors/NodeVisitors.java | 9 +++++ .../backends/python/PythonCodeGenerator.java | 38 ++++++++++++++++--- .../zwobble/couscous/interpreter/Arguments.java | 17 --------- .../zwobble/couscous/interpreter/Evaluator.java | 24 ++++++++---- .../org/zwobble/couscous/interpreter/Executor.java | 4 +- .../couscous/interpreter/PositionalArguments.java | 17 +++++++++ .../couscous/interpreter/values/ConcreteType.java | 43 +++++++++++++++++----- .../interpreter/values/MethodCallArguments.java | 17 +++++++++ .../couscous/interpreter/values/MethodValue.java | 8 ++-- .../interpreter/values/ObjectInterpreterValue.java | 23 ++++++++++++ .../interpreter/values/StaticMethodValue.java | 6 +-- .../interpreter/values/StringInterpreterValue.java | 7 ++-- 18 files changed, 197 insertions(+), 53 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java delete mode 100644 src/main/java/org/zwobble/couscous/interpreter/Arguments.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/PositionalArguments.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/values/MethodCallArguments.java create mode 100644 src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java (limited to 'src/main/java/org/zwobble') diff --git a/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java b/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java new file mode 100644 index 0000000..2aecc6c --- /dev/null +++ b/src/main/java/org/zwobble/couscous/ast/ConstructorCallNode.java @@ -0,0 +1,18 @@ +package org.zwobble.couscous.ast; + +import java.util.List; + +import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper; + +import lombok.Value; + +@Value(staticConstructor="constructorCall") +public class ConstructorCallNode implements ExpressionNode { + TypeName type; + List arguments; + + @Override + public T accept(ExpressionNodeMapper visitor) { + return visitor.visit(this); + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java index 5add277..2e1cded 100644 --- a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java @@ -45,6 +45,12 @@ public interface ExpressionNode extends Node { visitor.visit(staticMethodCall); return null; } + + @Override + public Void visit(ConstructorCallNode call) { + visitor.visit(call); + return null; + } }); } } diff --git a/src/main/java/org/zwobble/couscous/ast/MethodNode.java b/src/main/java/org/zwobble/couscous/ast/MethodNode.java index e924299..b547c92 100644 --- a/src/main/java/org/zwobble/couscous/ast/MethodNode.java +++ b/src/main/java/org/zwobble/couscous/ast/MethodNode.java @@ -15,6 +15,10 @@ public class MethodNode implements Node { return builder().isStatic(true).name(name); } + public static MethodNodeBuilder method(String name) { + return builder().isStatic(false).name(name); + } + boolean isStatic; String name; @Singular diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java index 2be2894..f924797 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java @@ -1,6 +1,7 @@ package org.zwobble.couscous.ast.visitors; import org.zwobble.couscous.ast.AssignmentNode; +import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.MethodCallNode; import org.zwobble.couscous.ast.StaticMethodCallNode; @@ -14,4 +15,5 @@ public interface ExpressionNodeMapper { T visit(TernaryConditionalNode ternaryConditional); T visit(MethodCallNode methodCall); T visit(StaticMethodCallNode staticMethodCall); + T visit(ConstructorCallNode call); } diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java index 014b48e..7377986 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java @@ -2,6 +2,7 @@ package org.zwobble.couscous.ast.visitors; import org.zwobble.couscous.ast.AssignmentNode; import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; @@ -19,6 +20,7 @@ public interface NodeVisitor { void visit(TernaryConditionalNode ternaryConditional); void visit(MethodCallNode methodCall); void visit(StaticMethodCallNode staticMethodCall); + void visit(ConstructorCallNode call); void visit(ReturnNode returnNode); void visit(ExpressionStatementNode expressionStatement); diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java index 3a58614..ac3786b 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java @@ -2,6 +2,7 @@ package org.zwobble.couscous.ast.visitors; import org.zwobble.couscous.ast.AssignmentNode; import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; @@ -36,6 +37,10 @@ public interface NodeVisitorWithEmptyDefaults extends NodeVisitor { @Override default void visit(StaticMethodCallNode staticMethodCall) { } + + @Override + default void visit(ConstructorCallNode call) { + } @Override default void visit(ReturnNode returnNode) { diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java index 88c6e10..835d96f 100644 --- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java +++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java @@ -2,6 +2,7 @@ package org.zwobble.couscous.ast.visitors; import org.zwobble.couscous.ast.AssignmentNode; import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.LocalVariableDeclarationNode; @@ -59,6 +60,14 @@ public class NodeVisitors { argument.accept(this); } } + + @Override + public void visit(ConstructorCallNode call) { + visitor.visit(call); + for (val argument : call.getArguments()) { + argument.accept(this); + } + } @Override public void visit(ReturnNode returnNode) { diff --git a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java index 8b26b10..be24996 100644 --- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java +++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java @@ -7,6 +7,7 @@ import java.util.stream.Stream; import org.zwobble.couscous.ast.AssignmentNode; import org.zwobble.couscous.ast.ClassNode; +import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ExpressionNode; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.LiteralNode; @@ -39,6 +40,7 @@ import org.zwobble.couscous.values.UnitValue; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import static com.google.common.collect.Iterators.singletonIterator; @@ -91,6 +93,11 @@ public class PythonCodeGenerator { public void visit(StaticMethodCallNode staticMethodCall) { imports.add(staticMethodCall.getClassName()); } + + @Override + public void visit(ConstructorCallNode call) { + imports.add(call.getType()); + } }); return imports.build(); } @@ -120,16 +127,23 @@ public class PythonCodeGenerator { } private static PythonFunctionDefinitionNode generateFunction(MethodNode method) { - val argumentNames = method.getArguments().stream() - .map(argument -> argument.getName()) - .collect(Collectors.toList()); - + val explicitArgumentNames = Iterables.transform( + method.getArguments(), + argument -> argument.getName()); + + val argumentNames = Iterables.concat( + method.isStatic() ? asList() : asList("self"), + explicitArgumentNames); + val pythonBody = method.getBody() .stream() .map(PythonCodeGenerator::generateStatement) .collect(Collectors.toList()); - return pythonFunctionDefinition(method.getName(), argumentNames, new PythonBlock(pythonBody)); + return pythonFunctionDefinition( + method.getName(), + ImmutableList.copyOf(argumentNames), + new PythonBlock(pythonBody)); } private static PythonStatementNode generateStatement(StatementNode statement) { @@ -219,11 +233,23 @@ public class PythonCodeGenerator { val arguments = staticMethodCall.getArguments().stream() .map(PythonCodeGenerator::generateExpression) - .collect(Collectors.toList());; + .collect(Collectors.toList()); return pythonCall(methodReference, arguments); } + @Override + public PythonExpressionNode visit(ConstructorCallNode call) { + val className = call.getType(); + val classReference = pythonVariableReference(className.getSimpleName()); + + val arguments = call.getArguments().stream() + .map(PythonCodeGenerator::generateExpression) + .collect(Collectors.toList()); + + return pythonCall(classReference, arguments); + } + private List generateArguments(MethodCallNode methodCall) { return methodCall.getArguments().stream() .map(PythonCodeGenerator::generateExpression) diff --git a/src/main/java/org/zwobble/couscous/interpreter/Arguments.java b/src/main/java/org/zwobble/couscous/interpreter/Arguments.java deleted file mode 100644 index f34a456..0000000 --- a/src/main/java/org/zwobble/couscous/interpreter/Arguments.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.zwobble.couscous.interpreter; - -import java.util.List; - -import org.zwobble.couscous.interpreter.values.InterpreterValue; - -public class Arguments { - private List values; - - public Arguments(List values) { - this.values = values; - } - - public InterpreterValue get(int index) { - return values.get(index); - } -} diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java index 9f183ec..ee67da0 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.stream.Collectors; import org.zwobble.couscous.ast.AssignmentNode; +import org.zwobble.couscous.ast.ConstructorCallNode; import org.zwobble.couscous.ast.ExpressionNode; import org.zwobble.couscous.ast.LiteralNode; import org.zwobble.couscous.ast.MethodCallNode; @@ -66,14 +67,7 @@ public class Evaluator implements ExpressionNodeMapper { val receiver = eval(methodCall.getReceiver()); val type = receiver.getType(); val arguments = evalArguments(methodCall.getArguments()); - return type.callMethod(receiver, methodCall.getMethodName(), arguments); - } - - private List evalArguments(List arguments) { - return arguments - .stream() - .map(argument -> eval(argument)) - .collect(Collectors.toList()); + return type.callMethod(environment, receiver, methodCall.getMethodName(), arguments); } @Override @@ -82,4 +76,18 @@ public class Evaluator implements ExpressionNodeMapper { val arguments = evalArguments(staticMethodCall.getArguments()); return clazz.callStaticMethod(environment, staticMethodCall.getMethodName(), arguments); } + + @Override + public InterpreterValue visit(ConstructorCallNode call) { + val clazz = environment.findClass(call.getType()); + val arguments = evalArguments(call.getArguments()); + return clazz.callConstructor(environment, arguments); + } + + private List evalArguments(List arguments) { + return arguments + .stream() + .map(argument -> eval(argument)) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java index 93adf16..e9304cb 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java @@ -19,7 +19,7 @@ import static org.zwobble.couscous.interpreter.Evaluator.eval; import lombok.val; public class Executor implements StatementNodeMapper> { - public static InterpreterValue callMethod(Environment environment, MethodNode method, Arguments arguments) { + public static InterpreterValue callMethod(Environment environment, MethodNode method, PositionalArguments arguments) { val innerEnvironment = buildEnvironment(environment, method, arguments); for (val statement : method.getBody()) { @@ -34,7 +34,7 @@ public class Executor implements StatementNodeMapper> private static Environment buildEnvironment( final Environment environment, final MethodNode method, - Arguments arguments) { + PositionalArguments arguments) { val stackFrame = new StackFrameBuilder(); for (int index = 0; index < method.getArguments().size(); index++) { diff --git a/src/main/java/org/zwobble/couscous/interpreter/PositionalArguments.java b/src/main/java/org/zwobble/couscous/interpreter/PositionalArguments.java new file mode 100644 index 0000000..429b529 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/PositionalArguments.java @@ -0,0 +1,17 @@ +package org.zwobble.couscous.interpreter; + +import java.util.List; + +import org.zwobble.couscous.interpreter.values.InterpreterValue; + +public class PositionalArguments { + private List values; + + public PositionalArguments(List values) { + this.values = values; + } + + public InterpreterValue get(int index) { + return values.get(index); + } +} diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java b/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java index a4902a4..70ba96a 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java +++ b/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java @@ -7,7 +7,7 @@ import java.util.stream.Collectors; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.TypeName; -import org.zwobble.couscous.interpreter.Arguments; +import org.zwobble.couscous.interpreter.PositionalArguments; import org.zwobble.couscous.interpreter.Environment; import org.zwobble.couscous.interpreter.Executor; import org.zwobble.couscous.interpreter.NoSuchMethod; @@ -38,10 +38,11 @@ public class ConcreteType { public Builder method( String name, List argumentsTypes, - BiFunction method) { - methods.put(name, new MethodValue(argumentsTypes, (receiver, arguments) -> { - return Casts.tryCast(interpreterValueType, receiver) - .map(typedReceiver -> method.apply(typedReceiver, arguments)) + BiFunction, InterpreterValue> method) { + methods.put(name, new MethodValue(argumentsTypes, (environment, arguments) -> { + return Casts.tryCast(interpreterValueType, arguments.getReceiver()) + .map(typedReceiver -> + method.apply(environment, MethodCallArguments.of(typedReceiver, arguments.getPositionalArguments()))) .orElseThrow(() -> new RuntimeException("receiver is of wrong type")); })); return this; @@ -50,7 +51,7 @@ public class ConcreteType { public Builder staticMethod( String name, List argumentsTypes, - BiFunction method) { + BiFunction method) { staticMethods.put(name, new StaticMethodValue(argumentsTypes, method)); return this; } @@ -61,6 +62,20 @@ public class ConcreteType { } public static ConcreteType fromNode(ClassNode classNode) { + val methods = classNode.getMethods().stream() + .filter(method -> !method.isStatic()) + .collect(toMap( + method -> method.getName(), + method -> { + List argumentTypes = method.getArguments() + .stream() + .map(arg -> arg.getType()) + .collect(Collectors.toList()); + return new MethodValue(argumentTypes, (environment, arguments) -> { + return Executor.callMethod(environment, method, arguments.getPositionalArguments()); + }); + })); + val staticMethods = classNode.getMethods() .stream() .filter(method -> method.isStatic()) @@ -77,7 +92,7 @@ public class ConcreteType { })); return new ConcreteType( classNode.getName(), - ImmutableMap.of(), + methods, staticMethods); } @@ -106,14 +121,22 @@ public class ConcreteType { return builder(interpreterValueType, TypeName.of(name)); } - public InterpreterValue callMethod(InterpreterValue receiver, String methodName, List arguments) { + public InterpreterValue callMethod(Environment environment, InterpreterValue receiver, String methodName, List arguments) { val method = findMethod(methods, methodName, arguments); - return method.apply(receiver, new Arguments(arguments)); + return method.apply( + environment, + MethodCallArguments.of(receiver, new PositionalArguments(arguments))); } public InterpreterValue callStaticMethod(Environment environment, String methodName, List arguments) { val method = findMethod(staticMethods, methodName, arguments); - return method.apply(environment, new Arguments(arguments)); + return method.apply(environment, new PositionalArguments(arguments)); + } + + public InterpreterValue callConstructor( + Environment environment, + List arguments) { + return new ObjectInterpreterValue(this); } private static T findMethod(Map methods, String methodName, List arguments) { diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/MethodCallArguments.java b/src/main/java/org/zwobble/couscous/interpreter/values/MethodCallArguments.java new file mode 100644 index 0000000..9f3e937 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/values/MethodCallArguments.java @@ -0,0 +1,17 @@ +package org.zwobble.couscous.interpreter.values; + +import org.zwobble.couscous.interpreter.PositionalArguments; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor(staticName="of") +@Getter +public class MethodCallArguments { + private final T receiver; + private final PositionalArguments positionalArguments; + + public InterpreterValue get(int index) { + return positionalArguments.get(index); + } +} diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/MethodValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/MethodValue.java index c1f2c6e..b5068fc 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/values/MethodValue.java +++ b/src/main/java/org/zwobble/couscous/interpreter/values/MethodValue.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.function.BiFunction; import org.zwobble.couscous.ast.TypeName; -import org.zwobble.couscous.interpreter.Arguments; +import org.zwobble.couscous.interpreter.Environment; import lombok.AccessLevel; import lombok.Getter; @@ -14,9 +14,9 @@ import lombok.Value; public class MethodValue implements Callable { List argumentTypes; @Getter(value=AccessLevel.NONE) - BiFunction apply; + BiFunction, InterpreterValue> apply; - public InterpreterValue apply(InterpreterValue receiver, Arguments arguments) { - return apply.apply(receiver, arguments); + public InterpreterValue apply(Environment environment, MethodCallArguments arguments) { + return apply.apply(environment, arguments); } } diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java new file mode 100644 index 0000000..4383353 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java @@ -0,0 +1,23 @@ +package org.zwobble.couscous.interpreter.values; + +import java.util.Optional; + +import org.zwobble.couscous.values.PrimitiveValue; + +public class ObjectInterpreterValue implements InterpreterValue { + private final ConcreteType type; + + public ObjectInterpreterValue(ConcreteType type) { + this.type = type; + } + + @Override + public ConcreteType getType() { + return type; + } + + @Override + public Optional toPrimitiveValue() { + return Optional.empty(); + } +} diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/StaticMethodValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/StaticMethodValue.java index 4754d80..37f3c56 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/values/StaticMethodValue.java +++ b/src/main/java/org/zwobble/couscous/interpreter/values/StaticMethodValue.java @@ -4,7 +4,7 @@ import java.util.List; import java.util.function.BiFunction; import org.zwobble.couscous.ast.TypeName; -import org.zwobble.couscous.interpreter.Arguments; +import org.zwobble.couscous.interpreter.PositionalArguments; import org.zwobble.couscous.interpreter.Environment; import lombok.AccessLevel; @@ -15,9 +15,9 @@ import lombok.Value; public class StaticMethodValue implements Callable { List argumentTypes; @Getter(value=AccessLevel.NONE) - BiFunction apply; + BiFunction apply; - public InterpreterValue apply(Environment environment, Arguments arguments) { + public InterpreterValue apply(Environment environment, PositionalArguments arguments) { return apply.apply(environment, arguments); } diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java index 98165b2..aa3432a 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java +++ b/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java @@ -16,13 +16,14 @@ import lombok.val; public class StringInterpreterValue implements InterpreterValue { public static final ConcreteType TYPE = ConcreteType.builder(StringInterpreterValue.class, StringValue.REF) .method("length", asList(), - (receiver, arguments) -> new IntegerInterpreterValue(receiver.value.length())) + (environment, arguments) -> + new IntegerInterpreterValue(arguments.getReceiver().value.length())) .method("substring", asList(IntegerValue.REF, IntegerValue.REF), - (receiver, arguments) -> { + (environment, arguments) -> { val startIndex = (IntegerInterpreterValue)arguments.get(0); val endIndex = (IntegerInterpreterValue)arguments.get(1); - return new StringInterpreterValue(receiver.value.substring(startIndex.getValue(), endIndex.getValue())); + return new StringInterpreterValue(arguments.getReceiver().value.substring(startIndex.getValue(), endIndex.getValue())); }) .build(); -- cgit v1.2.3