aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2015-10-06 16:24:37 +0100
committerMichael Williamson <mike@zwobble.org>2015-10-06 16:24:37 +0100
commite6f463257720bee91a1b4954d97684dbb3398cbf (patch)
treec8d1f04dfe8928d33361108aefee7e7572dae1c7
parent9685c8fb7fa14d5b75d828f8443dd948715ac4af (diff)
Add support for constructors and fields
-rw-r--r--src/main/java/org/zwobble/couscous/ast/AssignableExpressionNode.java7
-rw-r--r--src/main/java/org/zwobble/couscous/ast/AssignmentNode.java14
-rw-r--r--src/main/java/org/zwobble/couscous/ast/CallableNode.java9
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNode.java26
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java103
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ConstructorNode.java18
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ExpressionNode.java12
-rw-r--r--src/main/java/org/zwobble/couscous/ast/FieldAccessNode.java23
-rw-r--r--src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java9
-rw-r--r--src/main/java/org/zwobble/couscous/ast/MethodNode.java2
-rw-r--r--src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java15
-rw-r--r--src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java8
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/AssignableExpressionNodeVisitor.java9
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java4
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java6
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java15
-rw-r--r--src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java23
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java58
-rw-r--r--src/main/java/org/zwobble/couscous/backends/python/ast/PythonAssignmentNode.java2
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Environment.java15
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Evaluator.java34
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Executor.java20
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/Interpreter.java3
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java12
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java66
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java1
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java17
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java6
-rw-r--r--src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java6
-rw-r--r--src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java28
-rw-r--r--src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java4
33 files changed, 525 insertions, 62 deletions
diff --git a/src/main/java/org/zwobble/couscous/ast/AssignableExpressionNode.java b/src/main/java/org/zwobble/couscous/ast/AssignableExpressionNode.java
new file mode 100644
index 0000000..eba641f
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/AssignableExpressionNode.java
@@ -0,0 +1,7 @@
+package org.zwobble.couscous.ast;
+
+import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor;
+
+public interface AssignableExpressionNode extends ExpressionNode {
+ void accept(AssignableExpressionNodeVisitor mapper);
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java b/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java
index d7ea09f..483d4c2 100644
--- a/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/AssignmentNode.java
@@ -8,11 +8,23 @@ import lombok.Value;
@Value
public class AssignmentNode implements ExpressionNode {
+ public static StatementNode assignStatement(AssignableExpressionNode target, ExpressionNode value) {
+ return new ExpressionStatementNode(assign(target, value));
+ }
+
+ public static AssignmentNode assign(AssignableExpressionNode target, ExpressionNode value) {
+ return new AssignmentNode(target, value);
+ }
+
+ public static StatementNode assignStatement(VariableNode target, ExpressionNode value) {
+ return new ExpressionStatementNode(assign(target, value));
+ }
+
public static AssignmentNode assign(VariableNode target, ExpressionNode value) {
return new AssignmentNode(reference(target), value);
}
- VariableReferenceNode target;
+ AssignableExpressionNode target;
ExpressionNode value;
@Override
diff --git a/src/main/java/org/zwobble/couscous/ast/CallableNode.java b/src/main/java/org/zwobble/couscous/ast/CallableNode.java
new file mode 100644
index 0000000..79d2e84
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/CallableNode.java
@@ -0,0 +1,9 @@
+package org.zwobble.couscous.ast;
+
+import java.util.List;
+
+public interface CallableNode extends Node {
+ List<FormalArgumentNode> getArguments();
+ List<StatementNode> getBody();
+
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
index 5f08d38..280b772 100644
--- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java
@@ -4,37 +4,17 @@ import java.util.List;
import org.zwobble.couscous.ast.visitors.NodeVisitor;
-import com.google.common.collect.ImmutableList;
-
import lombok.Singular;
import lombok.Value;
@Value
public class ClassNode implements Node {
- public static ClassNode.Builder builder(String name) {
- return new Builder(name);
- }
-
- public static class Builder {
- private final String name;
- private final ImmutableList.Builder<MethodNode> methods;
-
- public Builder(String name) {
- this.name = name;
- this.methods = ImmutableList.builder();
- }
-
- public Builder method(MethodNode method) {
- this.methods.add(method);
- return this;
- }
-
- public ClassNode build() {
- return new ClassNode(TypeName.of(name), methods.build());
- }
+ public static ClassNodeBuilder builder(String name) {
+ return new ClassNodeBuilder(name);
}
TypeName name;
+ ConstructorNode constructor;
@Singular
List<MethodNode> methods;
diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
new file mode 100644
index 0000000..cd324b9
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/ClassNodeBuilder.java
@@ -0,0 +1,103 @@
+package org.zwobble.couscous.ast;
+
+import java.util.Optional;
+import java.util.function.Function;
+
+import com.google.common.collect.ImmutableList;
+
+public class ClassNodeBuilder {
+ private final TypeName name;
+ private final ImmutableList.Builder<FieldDeclarationNode> fields;
+ private Optional<ConstructorNode> constructor;
+ private final ImmutableList.Builder<MethodNode> methods;
+
+ public ClassNodeBuilder(String name) {
+ this.name = TypeName.of(name);
+ this.fields = ImmutableList.builder();
+ this.constructor = Optional.empty();
+ this.methods = ImmutableList.builder();
+ }
+
+ public ClassNodeBuilder field(String name, TypeName type) {
+ this.fields.add(FieldDeclarationNode.field(name, type));
+ return this;
+ }
+
+ public ClassNodeBuilder constructor(Function<ConstructorBuilder, ConstructorBuilder> build) {
+ this.constructor = Optional.of(build.apply(new ConstructorBuilder()).build());
+ return this;
+ }
+
+ public ClassNodeBuilder method(MethodNode method) {
+ this.methods.add(method);
+ return this;
+ }
+
+ public ClassNodeBuilder method(String name, Function<MethodBuilder, MethodBuilder> build) {
+ this.methods.add(build.apply(new MethodBuilder(name)).build());
+ return this;
+ }
+
+ public ClassNode build() {
+ return new ClassNode(
+ name,
+ constructor.orElse(new ConstructorBuilder().build()),
+ methods.build());
+ }
+
+ public class ConstructorBuilder {
+ private final ImmutableList.Builder<FormalArgumentNode> arguments;
+ private final ImmutableList.Builder<StatementNode> statements;
+
+ public ConstructorBuilder() {
+ this.arguments = ImmutableList.builder();
+ this.statements = ImmutableList.builder();
+ }
+
+ public ThisReferenceNode thisReference() {
+ return ThisReferenceNode.thisReference(name);
+ }
+
+ public ConstructorBuilder argument(FormalArgumentNode argument) {
+ arguments.add(argument);
+ return this;
+ }
+
+ public ConstructorBuilder statement(StatementNode statement) {
+ statements.add(statement);
+ return this;
+ }
+
+ public ConstructorNode build() {
+ return ConstructorNode.constructor(
+ arguments.build(),
+ statements.build());
+ }
+ }
+
+ public class MethodBuilder {
+ private final String methodName;
+ private final ImmutableList.Builder<StatementNode> statements;
+
+ public MethodBuilder(String name) {
+ this.methodName = name;
+ this.statements = ImmutableList.builder();
+ }
+
+ public ThisReferenceNode thisReference() {
+ return ThisReferenceNode.thisReference(name);
+ }
+
+ public MethodBuilder statement(StatementNode statement) {
+ statements.add(statement);
+ return this;
+ }
+
+ public MethodNode build() {
+ return MethodNode.builder()
+ .name(methodName)
+ .body(statements.build())
+ .build();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java b/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java
new file mode 100644
index 0000000..468ef9e
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/ConstructorNode.java
@@ -0,0 +1,18 @@
+package org.zwobble.couscous.ast;
+
+import java.util.List;
+
+import org.zwobble.couscous.ast.visitors.NodeVisitor;
+
+import lombok.Value;
+
+@Value(staticConstructor="constructor")
+public class ConstructorNode implements CallableNode {
+ List<FormalArgumentNode> arguments;
+ List<StatementNode> body;
+
+ @Override
+ public void accept(NodeVisitor visitor) {
+ 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 2e1cded..8210ae1 100644
--- a/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/ExpressionNode.java
@@ -23,6 +23,12 @@ public interface ExpressionNode extends Node {
}
@Override
+ public Void visit(ThisReferenceNode reference) {
+ visitor.visit(reference);
+ return null;
+ }
+
+ @Override
public Void visit(AssignmentNode assignment) {
visitor.visit(assignment);
return null;
@@ -51,6 +57,12 @@ public interface ExpressionNode extends Node {
visitor.visit(call);
return null;
}
+
+ @Override
+ public Void visit(FieldAccessNode fieldAccess) {
+ visitor.visit(fieldAccess);
+ return null;
+ }
});
}
}
diff --git a/src/main/java/org/zwobble/couscous/ast/FieldAccessNode.java b/src/main/java/org/zwobble/couscous/ast/FieldAccessNode.java
new file mode 100644
index 0000000..8de1323
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/FieldAccessNode.java
@@ -0,0 +1,23 @@
+package org.zwobble.couscous.ast;
+
+import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor;
+import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
+
+import lombok.Value;
+
+@Value(staticConstructor="fieldAccess")
+public class FieldAccessNode implements AssignableExpressionNode {
+ ExpressionNode left;
+ String fieldName;
+ TypeName type;
+
+ @Override
+ public <T> T accept(ExpressionNodeMapper<T> visitor) {
+ return visitor.visit(this);
+ }
+
+ @Override
+ public void accept(AssignableExpressionNodeVisitor mapper) {
+ mapper.visit(this);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java b/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java
new file mode 100644
index 0000000..49a45d3
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/FieldDeclarationNode.java
@@ -0,0 +1,9 @@
+package org.zwobble.couscous.ast;
+
+import lombok.Value;
+
+@Value(staticConstructor="field")
+public class FieldDeclarationNode {
+ String name;
+ TypeName type;
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/MethodNode.java b/src/main/java/org/zwobble/couscous/ast/MethodNode.java
index b547c92..700e9ec 100644
--- a/src/main/java/org/zwobble/couscous/ast/MethodNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/MethodNode.java
@@ -10,7 +10,7 @@ import lombok.Value;
@Builder
@Value
-public class MethodNode implements Node {
+public class MethodNode implements CallableNode {
public static MethodNodeBuilder staticMethod(String name) {
return builder().isStatic(true).name(name);
}
diff --git a/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java
new file mode 100644
index 0000000..c8fa54a
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/ThisReferenceNode.java
@@ -0,0 +1,15 @@
+package org.zwobble.couscous.ast;
+
+import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
+
+import lombok.Value;
+
+@Value(staticConstructor="thisReference")
+public class ThisReferenceNode implements ExpressionNode {
+ TypeName type;
+
+ @Override
+ public <T> T accept(ExpressionNodeMapper<T> visitor) {
+ return visitor.visit(this);
+ }
+}
diff --git a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java
index a07006e..ebe79b0 100644
--- a/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java
+++ b/src/main/java/org/zwobble/couscous/ast/VariableReferenceNode.java
@@ -1,11 +1,12 @@
package org.zwobble.couscous.ast;
+import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
import lombok.Value;
@Value
-public class VariableReferenceNode implements ExpressionNode {
+public class VariableReferenceNode implements AssignableExpressionNode {
public static VariableReferenceNode reference(VariableNode variable) {
return new VariableReferenceNode(variable.getDeclaration());
}
@@ -25,4 +26,9 @@ public class VariableReferenceNode implements ExpressionNode {
public TypeName getType() {
return referent.getType();
}
+
+ @Override
+ public void accept(AssignableExpressionNodeVisitor mapper) {
+ mapper.visit(this);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/ast/visitors/AssignableExpressionNodeVisitor.java b/src/main/java/org/zwobble/couscous/ast/visitors/AssignableExpressionNodeVisitor.java
new file mode 100644
index 0000000..57cb105
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/AssignableExpressionNodeVisitor.java
@@ -0,0 +1,9 @@
+package org.zwobble.couscous.ast.visitors;
+
+import org.zwobble.couscous.ast.FieldAccessNode;
+import org.zwobble.couscous.ast.VariableReferenceNode;
+
+public interface AssignableExpressionNodeVisitor {
+ void visit(VariableReferenceNode reference);
+ void visit(FieldAccessNode fieldAccess);
+}
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 f924797..c1c3e88 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/ExpressionNodeMapper.java
@@ -2,18 +2,22 @@ package org.zwobble.couscous.ast.visitors;
import org.zwobble.couscous.ast.AssignmentNode;
import org.zwobble.couscous.ast.ConstructorCallNode;
+import org.zwobble.couscous.ast.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.MethodCallNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
public interface ExpressionNodeMapper<T> {
T visit(LiteralNode literal);
T visit(VariableReferenceNode variableReference);
+ T visit(ThisReferenceNode reference);
T visit(AssignmentNode assignment);
T visit(TernaryConditionalNode ternaryConditional);
T visit(MethodCallNode methodCall);
T visit(StaticMethodCallNode staticMethodCall);
T visit(ConstructorCallNode call);
+ T visit(FieldAccessNode fieldAccess);
}
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 7377986..8a88052 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitor.java
@@ -3,7 +3,9 @@ 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.ConstructorNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
+import org.zwobble.couscous.ast.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
import org.zwobble.couscous.ast.MethodCallNode;
@@ -11,16 +13,19 @@ import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
public interface NodeVisitor {
void visit(LiteralNode literal);
void visit(VariableReferenceNode variableReference);
+ void visit(ThisReferenceNode reference);
void visit(AssignmentNode assignment);
void visit(TernaryConditionalNode ternaryConditional);
void visit(MethodCallNode methodCall);
void visit(StaticMethodCallNode staticMethodCall);
void visit(ConstructorCallNode call);
+ void visit(FieldAccessNode fieldAccess);
void visit(ReturnNode returnNode);
void visit(ExpressionStatementNode expressionStatement);
@@ -28,4 +33,5 @@ public interface NodeVisitor {
void visit(ClassNode classNode);
void visit(MethodNode methodNode);
+ void visit(ConstructorNode constructorNode);
}
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 ac3786b..8408b7a 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitorWithEmptyDefaults.java
@@ -3,7 +3,9 @@ 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.ConstructorNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
+import org.zwobble.couscous.ast.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
import org.zwobble.couscous.ast.MethodCallNode;
@@ -11,6 +13,7 @@ import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
public interface NodeVisitorWithEmptyDefaults extends NodeVisitor {
@@ -23,6 +26,10 @@ public interface NodeVisitorWithEmptyDefaults extends NodeVisitor {
}
@Override
+ default void visit(ThisReferenceNode reference) {
+ }
+
+ @Override
default void visit(AssignmentNode assignment) {
}
@@ -41,6 +48,10 @@ public interface NodeVisitorWithEmptyDefaults extends NodeVisitor {
@Override
default void visit(ConstructorCallNode call) {
}
+
+ @Override
+ default void visit(FieldAccessNode fieldAccess) {
+ }
@Override
default void visit(ReturnNode returnNode) {
@@ -61,4 +72,8 @@ public interface NodeVisitorWithEmptyDefaults extends NodeVisitor {
@Override
default void visit(MethodNode methodNode) {
}
+
+ @Override
+ default void visit(ConstructorNode constructorNode) {
+ }
}
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 835d96f..bb5b054 100644
--- a/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java
+++ b/src/main/java/org/zwobble/couscous/ast/visitors/NodeVisitors.java
@@ -3,7 +3,9 @@ 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.ConstructorNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
+import org.zwobble.couscous.ast.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
import org.zwobble.couscous.ast.MethodCallNode;
@@ -12,6 +14,7 @@ import org.zwobble.couscous.ast.Node;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
import lombok.val;
@@ -28,12 +31,17 @@ public class NodeVisitors {
public void visit(VariableReferenceNode variableReference) {
visitor.visit(variableReference);
}
+
+ @Override
+ public void visit(ThisReferenceNode reference) {
+ visitor.visit(reference);
+ }
@Override
public void visit(AssignmentNode assignment) {
visitor.visit(assignment);
assignment.getValue().accept(this);
- visit(assignment.getTarget());
+ assignment.getTarget().accept(this);
}
@Override
@@ -68,6 +76,11 @@ public class NodeVisitors {
argument.accept(this);
}
}
+
+ @Override
+ public void visit(FieldAccessNode fieldAccess) {
+ visitor.visit(fieldAccess);
+ }
@Override
public void visit(ReturnNode returnNode) {
@@ -90,6 +103,7 @@ public class NodeVisitors {
@Override
public void visit(ClassNode classNode) {
visitor.visit(classNode);
+ classNode.getConstructor().accept(this);
for (val method : classNode.getMethods()) {
method.accept(this);
}
@@ -101,6 +115,13 @@ public class NodeVisitors {
statement.accept(this);
}
}
+
+ @Override
+ public void visit(ConstructorNode constructorNode) {
+ for (val statement : constructorNode.getBody()) {
+ statement.accept(this);
+ }
+ }
});
}
}
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 1065f65..9b3367f 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/PythonCodeGenerator.java
@@ -8,8 +8,10 @@ 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.ConstructorNode;
import org.zwobble.couscous.ast.ExpressionNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
+import org.zwobble.couscous.ast.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
import org.zwobble.couscous.ast.MethodCallNode;
@@ -18,6 +20,7 @@ import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StatementNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.ast.VariableReferenceNode;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
@@ -65,19 +68,27 @@ import lombok.val;
public class PythonCodeGenerator {
public static PythonModuleNode generateCode(ClassNode classNode) {
val imports = generateImports(classNode).iterator();
+
+ val constructor = generateConstructor(classNode.getConstructor());
+
+ val pythonMethods = Iterables.transform(
+ classNode.getMethods(),
+ PythonCodeGenerator::generateFunction);
- val pythonBody = classNode.getMethods()
- .stream()
- .map(PythonCodeGenerator::generateFunction)
- .collect(Collectors.toList());
- val pythonClass = pythonClass(classNode.getSimpleName(), pythonBody);
+ val pythonBody = Iterables.concat(
+ asList(constructor),
+ pythonMethods);
+
+ val pythonClass = pythonClass(
+ classNode.getSimpleName(),
+ ImmutableList.copyOf(pythonBody));
return pythonModule(ImmutableList.copyOf(Iterators.concat(
imports,
singletonIterator(pythonClass))));
}
-
+
private static Stream<PythonImportNode> generateImports(ClassNode classNode) {
val classes = findReferencedClasses(classNode);
return classes.stream()
@@ -126,6 +137,27 @@ public class PythonCodeGenerator {
});
}
+ private static PythonFunctionDefinitionNode generateConstructor(
+ ConstructorNode constructor) {
+ val explicitArgumentNames = Iterables.transform(
+ constructor.getArguments(),
+ argument -> argument.getName());
+
+ val argumentNames = Iterables.concat(
+ asList("self"),
+ explicitArgumentNames);
+
+ val pythonBody = constructor.getBody()
+ .stream()
+ .map(PythonCodeGenerator::generateStatement)
+ .collect(Collectors.toList());
+
+ return pythonFunctionDefinition(
+ "__init__",
+ ImmutableList.copyOf(argumentNames),
+ new PythonBlock(pythonBody));
+ }
+
private static PythonFunctionDefinitionNode generateFunction(MethodNode method) {
val explicitArgumentNames = Iterables.transform(
method.getArguments(),
@@ -161,7 +193,7 @@ public class PythonCodeGenerator {
if (expressionStatement.getExpression() instanceof AssignmentNode) {
val assignment = (AssignmentNode) expressionStatement.getExpression();
return pythonAssignment(
- EXPRESSION_GENERATOR.visit(assignment.getTarget()),
+ assignment.getTarget().accept(EXPRESSION_GENERATOR),
generateExpression(assignment.getValue()));
} else {
throw new UnsupportedOperationException();
@@ -199,6 +231,11 @@ public class PythonCodeGenerator {
}
@Override
+ public PythonExpressionNode visit(ThisReferenceNode reference) {
+ return pythonVariableReference("self");
+ }
+
+ @Override
public PythonExpressionNode visit(AssignmentNode assignment) {
throw new UnsupportedOperationException();
}
@@ -249,6 +286,13 @@ public class PythonCodeGenerator {
return pythonCall(classReference, arguments);
}
+
+ @Override
+ public PythonExpressionNode visit(FieldAccessNode fieldAccess) {
+ return pythonAttributeAccess(
+ generateExpression(fieldAccess.getLeft()),
+ fieldAccess.getFieldName());
+ }
}
private static boolean isPrimitive(ExpressionNode value) {
diff --git a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonAssignmentNode.java b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonAssignmentNode.java
index 3624908..9c5b20a 100644
--- a/src/main/java/org/zwobble/couscous/backends/python/ast/PythonAssignmentNode.java
+++ b/src/main/java/org/zwobble/couscous/backends/python/ast/PythonAssignmentNode.java
@@ -6,7 +6,7 @@ import lombok.Value;
@Value(staticConstructor="pythonAssignment")
public class PythonAssignmentNode implements PythonStatementNode {
- PythonVariableReferenceNode target;
+ PythonExpressionNode target;
PythonExpressionNode value;
@Override
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
index 6da54ce..0bbc808 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java
@@ -19,16 +19,25 @@ import lombok.val;
public class Environment {
private final Project project;
+ private final Optional<InterpreterValue> thisValue;
private final Map<Integer, VariableEntry> stackFrame;
- public Environment(Project project, Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
+ public Environment(
+ Project project,
+ Optional<InterpreterValue> thisValue,
+ Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
this.project = project;
+ this.thisValue = thisValue;
this.stackFrame = stackFrame.entrySet()
.stream()
.collect(toMap(
entry -> entry.getKey().getId(),
entry -> VariableEntry.of(entry.getKey().getType(), entry.getValue())));
}
+
+ public Optional<InterpreterValue> getThis() {
+ return thisValue;
+ }
public InterpreterValue get(int variableId) {
checkVariableIsInScope(variableId);
@@ -51,8 +60,8 @@ public class Environment {
return project.findClass(className);
}
- public Environment withStackFrame(Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
- return new Environment(project, stackFrame);
+ public Environment withStackFrame(Optional<InterpreterValue> thisValue, Map<VariableDeclaration, Optional<InterpreterValue>> stackFrame) {
+ return new Environment(project, thisValue, stackFrame);
}
private void checkVariableIsInScope(int variableId) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
index ee67da0..18d7109 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java
@@ -6,15 +6,20 @@ 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.FieldAccessNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.MethodCallNode;
import org.zwobble.couscous.ast.StaticMethodCallNode;
import org.zwobble.couscous.ast.TernaryConditionalNode;
+import org.zwobble.couscous.ast.ThisReferenceNode;
import org.zwobble.couscous.ast.VariableReferenceNode;
+import org.zwobble.couscous.ast.visitors.AssignableExpressionNodeVisitor;
import org.zwobble.couscous.ast.visitors.ExpressionNodeMapper;
import org.zwobble.couscous.interpreter.values.BooleanInterpreterValue;
import org.zwobble.couscous.interpreter.values.InterpreterValue;
import org.zwobble.couscous.interpreter.values.InterpreterValues;
+import org.zwobble.couscous.interpreter.values.ObjectInterpreterValue;
+import org.zwobble.couscous.util.Casts;
import lombok.val;
@@ -44,9 +49,28 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
}
@Override
+ public InterpreterValue visit(ThisReferenceNode reference) {
+ return environment.getThis().get();
+ }
+
+ @Override
public InterpreterValue visit(AssignmentNode assignment) {
val value = eval(assignment.getValue());
- environment.put(assignment.getTarget().getReferentId(), value);
+ assignment.getTarget().accept(new AssignableExpressionNodeVisitor() {
+ @Override
+ public void visit(FieldAccessNode fieldAccess) {
+ val maybeLeft = Casts.tryCast(
+ ObjectInterpreterValue.class,
+ eval(fieldAccess.getLeft()));
+ val left = maybeLeft.orElseThrow(() -> new UnsupportedOperationException());
+ left.setField(fieldAccess.getFieldName(), value);
+ }
+
+ @Override
+ public void visit(VariableReferenceNode reference) {
+ environment.put(reference.getReferentId(), value);
+ }
+ });
return value;
}
@@ -84,6 +108,14 @@ public class Evaluator implements ExpressionNodeMapper<InterpreterValue> {
return clazz.callConstructor(environment, arguments);
}
+ @Override
+ public InterpreterValue visit(FieldAccessNode fieldAccess) {
+ // TODO: handle unbound fields
+ // TODO: handle non-existent fields
+ val left = eval(fieldAccess.getLeft());
+ return left.getField(fieldAccess.getFieldName());
+ }
+
private List<InterpreterValue> evalArguments(List<ExpressionNode> arguments) {
return arguments
.stream()
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
index e9304cb..5c26244 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java
@@ -4,9 +4,9 @@ import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
+import org.zwobble.couscous.ast.CallableNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
import org.zwobble.couscous.ast.LocalVariableDeclarationNode;
-import org.zwobble.couscous.ast.MethodNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.ast.StatementNode;
import org.zwobble.couscous.ast.VariableNode;
@@ -19,8 +19,13 @@ import static org.zwobble.couscous.interpreter.Evaluator.eval;
import lombok.val;
public class Executor implements StatementNodeMapper<Optional<InterpreterValue>> {
- public static InterpreterValue callMethod(Environment environment, MethodNode method, PositionalArguments arguments) {
- val innerEnvironment = buildEnvironment(environment, method, arguments);
+ public static InterpreterValue callMethod(
+ Environment environment,
+ CallableNode method,
+ Optional<InterpreterValue> thisValue,
+ PositionalArguments arguments) {
+
+ val innerEnvironment = buildEnvironment(environment, method, thisValue, arguments);
for (val statement : method.getBody()) {
val result = exec(innerEnvironment, statement);
@@ -32,9 +37,10 @@ public class Executor implements StatementNodeMapper<Optional<InterpreterValue>>
}
private static Environment buildEnvironment(
- final Environment environment,
- final MethodNode method,
- PositionalArguments arguments) {
+ final Environment environment,
+ final CallableNode method,
+ Optional<InterpreterValue> thisValue,
+ PositionalArguments arguments) {
val stackFrame = new StackFrameBuilder();
for (int index = 0; index < method.getArguments().size(); index++) {
@@ -44,7 +50,7 @@ public class Executor implements StatementNodeMapper<Optional<InterpreterValue>>
findDeclarations(method.getBody()).forEach(declaration ->
stackFrame.declare(declaration));
- return environment.withStackFrame(stackFrame.build());
+ return environment.withStackFrame(thisValue, stackFrame.build());
}
private static Stream<VariableNode> findDeclarations(List<StatementNode> body) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
index 38ea2ac..88e9d0e 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/Interpreter.java
@@ -1,6 +1,7 @@
package org.zwobble.couscous.interpreter;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Collectors;
import org.zwobble.couscous.Project;
@@ -28,7 +29,7 @@ public class Interpreter {
.stream()
.<ExpressionNode>map(argument -> literal(argument.toPrimitiveValue().get()))
.collect(Collectors.toList());
- return eval(new Environment(project, ImmutableMap.of()),
+ return eval(new Environment(project, Optional.empty(), ImmutableMap.of()),
staticMethodCall(className, methodName, argumentExpressions));
}
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java b/src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java
new file mode 100644
index 0000000..31feaab
--- /dev/null
+++ b/src/main/java/org/zwobble/couscous/interpreter/NoSuchField.java
@@ -0,0 +1,12 @@
+package org.zwobble.couscous.interpreter;
+
+import lombok.EqualsAndHashCode;
+import lombok.Value;
+
+@Value
+@EqualsAndHashCode(callSuper=false)
+public class NoSuchField extends InterpreterException {
+ private static final long serialVersionUID = 1L;
+
+ String fieldName;
+}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java
index b98eb20..06e8bee 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/BooleanInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -30,4 +31,9 @@ public class BooleanInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.value(value));
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
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 70ba96a..bd63d7f 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ConcreteType.java
@@ -2,10 +2,12 @@ package org.zwobble.couscous.interpreter.values;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import org.zwobble.couscous.ast.ClassNode;
+import org.zwobble.couscous.ast.FormalArgumentNode;
import org.zwobble.couscous.ast.TypeName;
import org.zwobble.couscous.interpreter.PositionalArguments;
import org.zwobble.couscous.interpreter.Environment;
@@ -15,6 +17,7 @@ import org.zwobble.couscous.interpreter.UnexpectedValueType;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
import org.zwobble.couscous.util.Casts;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import static java.util.stream.Collectors.toMap;
@@ -57,22 +60,38 @@ public class ConcreteType {
}
public ConcreteType build() {
- return new ConcreteType(name, methods.build(), staticMethods.build());
+ return new ConcreteType(
+ name,
+ new MethodValue(
+ ImmutableList.of(),
+ (environment, arguments) -> InterpreterValues.UNIT),
+ methods.build(),
+ staticMethods.build());
}
}
public static ConcreteType fromNode(ClassNode classNode) {
+ val constructor = new MethodValue(
+ getArgumentTypes(classNode.getConstructor().getArguments()),
+ (environment, arguments) -> Executor.callMethod(
+ environment,
+ classNode.getConstructor(),
+ Optional.of(arguments.getReceiver()),
+ arguments.getPositionalArguments()));
+
val methods = classNode.getMethods().stream()
.filter(method -> !method.isStatic())
.collect(toMap(
method -> method.getName(),
method -> {
- List<TypeName> argumentTypes = method.getArguments()
- .stream()
- .map(arg -> arg.getType())
- .collect(Collectors.toList());
+ val argumentTypes = getArgumentTypes(method.getArguments());
+
return new MethodValue(argumentTypes, (environment, arguments) -> {
- return Executor.callMethod(environment, method, arguments.getPositionalArguments());
+ return Executor.callMethod(
+ environment,
+ method,
+ Optional.of(arguments.getReceiver()),
+ arguments.getPositionalArguments());
});
}));
@@ -82,29 +101,42 @@ public class ConcreteType {
.collect(toMap(
method -> method.getName(),
method -> {
- List<TypeName> argumentTypes = method.getArguments()
- .stream()
- .map(arg -> arg.getType())
- .collect(Collectors.toList());
+ val argumentTypes = getArgumentTypes(method.getArguments());
+
return new StaticMethodValue(argumentTypes, (environment, arguments) -> {
- return Executor.callMethod(environment, method, arguments);
+ return Executor.callMethod(
+ environment,
+ method,
+ Optional.empty(),
+ arguments);
});
}));
return new ConcreteType(
classNode.getName(),
+ constructor,
methods,
staticMethods);
}
+
+ private static List<TypeName> getArgumentTypes(List<FormalArgumentNode> arguments) {
+ return arguments
+ .stream()
+ .map(arg -> arg.getType())
+ .collect(Collectors.toList());
+ }
- private TypeName name;
- private Map<String, MethodValue> methods;
- private Map<String, StaticMethodValue> staticMethods;
+ private final TypeName name;
+ private final MethodValue constructor;
+ private final Map<String, MethodValue> methods;
+ private final Map<String, StaticMethodValue> staticMethods;
public ConcreteType(
TypeName name,
+ MethodValue constructor,
Map<String, MethodValue> methods,
Map<String, StaticMethodValue> staticMethods) {
this.name = name;
+ this.constructor = constructor;
this.methods = methods;
this.staticMethods = staticMethods;
}
@@ -136,7 +168,11 @@ public class ConcreteType {
public InterpreterValue callConstructor(
Environment environment,
List<InterpreterValue> arguments) {
- return new ObjectInterpreterValue(this);
+ val object = new ObjectInterpreterValue(this);
+ constructor.apply(
+ environment,
+ MethodCallArguments.of(object, new PositionalArguments(arguments)));
+ return object;
}
private static <T extends Callable> T findMethod(Map<String, T> methods, String methodName, List<InterpreterValue> arguments) {
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java
index 92ba306..1effe6a 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/IntegerInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -34,4 +35,9 @@ public class IntegerInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.value(value));
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java
index 6c3c2ec..78d27d2 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/InterpreterValue.java
@@ -7,4 +7,5 @@ import org.zwobble.couscous.values.PrimitiveValue;
public interface InterpreterValue {
ConcreteType getType();
Optional<PrimitiveValue> toPrimitiveValue();
+ InterpreterValue getField(String fieldName);
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
index 4383353..acb6ef7 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/ObjectInterpreterValue.java
@@ -1,14 +1,18 @@
package org.zwobble.couscous.interpreter.values;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Optional;
import org.zwobble.couscous.values.PrimitiveValue;
public class ObjectInterpreterValue implements InterpreterValue {
private final ConcreteType type;
+ private final Map<String, InterpreterValue> fields;
public ObjectInterpreterValue(ConcreteType type) {
this.type = type;
+ this.fields = new HashMap<>();
}
@Override
@@ -20,4 +24,17 @@ public class ObjectInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.empty();
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ if (fields.containsKey(fieldName)) {
+ return fields.get(fieldName);
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ public void setField(String fieldName, InterpreterValue value) {
+ fields.put(fieldName, value);
+ }
}
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 aa3432a..a4dde19 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/StringInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -39,4 +40,9 @@ public class StringInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.value(value));
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
diff --git a/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java b/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java
index 253932f..e221fee 100644
--- a/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java
+++ b/src/main/java/org/zwobble/couscous/interpreter/values/UnitInterpreterValue.java
@@ -2,6 +2,7 @@ package org.zwobble.couscous.interpreter.values;
import java.util.Optional;
+import org.zwobble.couscous.interpreter.NoSuchField;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.PrimitiveValues;
@@ -26,4 +27,9 @@ public class UnitInterpreterValue implements InterpreterValue {
public Optional<PrimitiveValue> toPrimitiveValue() {
return Optional.of(PrimitiveValues.UNIT);
}
+
+ @Override
+ public InterpreterValue getField(String fieldName) {
+ throw new NoSuchField(fieldName);
+ }
}
diff --git a/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java b/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
index d1d72c0..765be9c 100644
--- a/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/BackendEvalTests.java
@@ -16,7 +16,9 @@ import com.google.common.collect.ImmutableList;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
+import static org.zwobble.couscous.ast.AssignmentNode.assignStatement;
import static org.zwobble.couscous.ast.ConstructorCallNode.constructorCall;
+import static org.zwobble.couscous.ast.FieldAccessNode.fieldAccess;
import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
import static org.zwobble.couscous.ast.LiteralNode.literal;
import static org.zwobble.couscous.ast.MethodCallNode.methodCall;
@@ -120,6 +122,32 @@ public abstract class BackendEvalTests {
assertEquals(value(42), result);
}
+ @Test
+ public void constructorIsExecutedOnConstruction() {
+ val argument = formalArg(var(42, "x", IntegerValue.REF));
+ val classNode = ClassNode.builder("com.example.Example")
+ .field("value", IntegerValue.REF)
+ .constructor(constructor -> constructor
+ .argument(argument)
+ .statement(assignStatement(
+ fieldAccess(constructor.thisReference(), "value", IntegerValue.REF),
+ reference(argument))))
+ .method("main", method -> method
+ .statement(new ReturnNode(
+ fieldAccess(method.thisReference(), "value", IntegerValue.REF))))
+ .build();
+
+ val result = evalExpression(
+ asList(classNode),
+ methodCall(
+ constructorCall(classNode.getName(), asList(literal(42))),
+ "main",
+ asList(),
+ IntegerValue.REF));
+
+ assertEquals(value(42), result);
+ }
+
protected PrimitiveValue evalExpression(ExpressionNode expression) {
return evalExpression(ImmutableList.of(), expression);
}
diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
index 4ec8428..9cf87a4 100644
--- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
+++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java
@@ -1,6 +1,7 @@
package org.zwobble.couscous.tests.interpreter;
import java.util.List;
+import java.util.Optional;
import org.junit.Test;
import org.zwobble.couscous.JavaProject;
@@ -42,6 +43,7 @@ public class EvaluatorTests extends BackendEvalTests {
val arg = formalArg(var(42, "x", StringValue.REF));
val environment = new Environment(
new MapBackedProject(ImmutableMap.of()),
+ Optional.empty(),
new StackFrameBuilder().declare(arg, value("[initial value]")).build());
val result = eval(environment, assign(arg, literal("[updated value]")));
assertEquals(new StringInterpreterValue("[updated value]"), result);
@@ -94,6 +96,7 @@ public class EvaluatorTests extends BackendEvalTests {
private static Environment emptyEnvironment() {
return new Environment(
JavaProject.builder().build(),
+ Optional.empty(),
ImmutableMap.of());
}
@@ -104,6 +107,7 @@ public class EvaluatorTests extends BackendEvalTests {
val environment = new Environment(
JavaProject.of(classes),
+ Optional.empty(),
ImmutableMap.of());
return eval(environment, expression).toPrimitiveValue().get();
}