From c02fac3e8b50b1941a8d3f35a033212c1e51d5d4 Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Fri, 2 Oct 2015 19:06:57 +0100 Subject: Preserve environment across calls --- .../java/org/zwobble/couscous/JavaProject.java | 10 ++++++ .../org/zwobble/couscous/MapBackedProject.java | 23 +++++++++++++ .../java/org/zwobble/couscous/ast/ClassNode.java | 27 +++++++++++++-- .../zwobble/couscous/interpreter/Environment.java | 4 +++ .../zwobble/couscous/interpreter/Evaluator.java | 2 +- .../org/zwobble/couscous/interpreter/Executor.java | 38 +++++++--------------- .../org/zwobble/couscous/values/ConcreteType.java | 19 ++++++++--- .../org/zwobble/couscous/values/IntegerValue.java | 2 +- .../zwobble/couscous/values/StaticMethodValue.java | 9 ++--- .../couscous/tests/interpreter/EvaluatorTests.java | 25 +++++++++++++- .../tests/interpreter/InterpreterTests.java | 5 +-- .../tests/interpreter/MapBackedProject.java | 19 ----------- 12 files changed, 121 insertions(+), 62 deletions(-) create mode 100644 src/main/java/org/zwobble/couscous/JavaProject.java create mode 100644 src/main/java/org/zwobble/couscous/MapBackedProject.java delete mode 100644 src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java diff --git a/src/main/java/org/zwobble/couscous/JavaProject.java b/src/main/java/org/zwobble/couscous/JavaProject.java new file mode 100644 index 0000000..49e9355 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/JavaProject.java @@ -0,0 +1,10 @@ +package org.zwobble.couscous; + +import org.zwobble.couscous.values.IntegerValue; + +public class JavaProject { + public static MapBackedProject.MapBackedProjectBuilder builder() { + return MapBackedProject.builder() + .addClass("java.lang.Integer", IntegerValue.TYPE); + } +} diff --git a/src/main/java/org/zwobble/couscous/MapBackedProject.java b/src/main/java/org/zwobble/couscous/MapBackedProject.java new file mode 100644 index 0000000..66b3681 --- /dev/null +++ b/src/main/java/org/zwobble/couscous/MapBackedProject.java @@ -0,0 +1,23 @@ +package org.zwobble.couscous; + +import java.util.Map; + +import org.zwobble.couscous.values.ConcreteType; + +import lombok.Builder; +import lombok.Singular; + +@Builder +public class MapBackedProject implements Project { + @Singular(value="addClass") + private Map> classes; + + public MapBackedProject(Map> classes) { + this.classes = classes; + } + + @Override + public ConcreteType findClass(String name) { + return classes.get(name); + } +} diff --git a/src/main/java/org/zwobble/couscous/ast/ClassNode.java b/src/main/java/org/zwobble/couscous/ast/ClassNode.java index 6dd7b12..72e57d2 100644 --- a/src/main/java/org/zwobble/couscous/ast/ClassNode.java +++ b/src/main/java/org/zwobble/couscous/ast/ClassNode.java @@ -2,13 +2,36 @@ package org.zwobble.couscous.ast; import java.util.List; -import lombok.Builder; +import com.google.common.collect.ImmutableList; + import lombok.Singular; import lombok.Value; -@Builder @Value public class ClassNode { + public static ClassNode.Builder builder(String name) { + return new Builder(name); + } + + public static class Builder { + private final String name; + private final ImmutableList.Builder 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(name, methods.build()); + } + } + String name; @Singular List methods; diff --git a/src/main/java/org/zwobble/couscous/interpreter/Environment.java b/src/main/java/org/zwobble/couscous/interpreter/Environment.java index 76aa56b..b4decd8 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Environment.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Environment.java @@ -28,4 +28,8 @@ public class Environment { // TODO: handle missing classes return project.findClass(className); } + + public Environment withStackFrame(Map stackFrame) { + return new Environment(project, stackFrame); + } } diff --git a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java index 5acef94..6287348 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Evaluator.java @@ -79,6 +79,6 @@ public class Evaluator implements ExpressionNodeVisitor { public InterpreterValue visit(StaticMethodCallNode staticMethodCall) { val clazz = environment.findClass(staticMethodCall.getClassName()); val arguments = evalArguments(staticMethodCall.getArguments()); - return clazz.callStaticMethod(staticMethodCall.getMethodName(), arguments); + return clazz.callStaticMethod(environment, staticMethodCall.getMethodName(), arguments); } } diff --git a/src/main/java/org/zwobble/couscous/interpreter/Executor.java b/src/main/java/org/zwobble/couscous/interpreter/Executor.java index cbeeaa4..4002238 100644 --- a/src/main/java/org/zwobble/couscous/interpreter/Executor.java +++ b/src/main/java/org/zwobble/couscous/interpreter/Executor.java @@ -1,20 +1,14 @@ package org.zwobble.couscous.interpreter; -import java.util.List; import java.util.Optional; -import java.util.function.Supplier; -import java.util.stream.Collectors; import java.util.stream.IntStream; -import org.zwobble.couscous.Project; import org.zwobble.couscous.ast.ExpressionStatementNode; import org.zwobble.couscous.ast.MethodNode; import org.zwobble.couscous.ast.ReturnNode; import org.zwobble.couscous.ast.StatementNode; import org.zwobble.couscous.ast.visitors.StatementNodeVisitor; -import org.zwobble.couscous.values.ConcreteType; import org.zwobble.couscous.values.InterpreterValue; -import org.zwobble.couscous.values.StaticMethodValue; import org.zwobble.couscous.values.UnitValue; import static java.util.stream.Collectors.toMap; @@ -23,25 +17,20 @@ import static org.zwobble.couscous.interpreter.Evaluator.eval; import lombok.val; public class Executor implements StatementNodeVisitor> { - public static StaticMethodValue callMethod(MethodNode method) { - Supplier>> argumentTypes = () -> method.getArguments() - .stream() - .>map(arg -> arg.getType()) - .collect(Collectors.toList()); - return new StaticMethodValue(argumentTypes, arguments -> { - val environment = buildEnvironment(method, arguments); - - for (val statement : method.getBody()) { - val result = exec(environment, statement); - if (result.isPresent()) { - return result.get(); - } + public static InterpreterValue callMethod(Environment environment, MethodNode method, Arguments arguments) { + val innerEnvironment = buildEnvironment(environment, method, arguments); + + for (val statement : method.getBody()) { + val result = exec(innerEnvironment, statement); + if (result.isPresent()) { + return result.get(); } - return UnitValue.UNIT; - }); + } + return UnitValue.UNIT; } private static Environment buildEnvironment( + final Environment environment, final MethodNode method, Arguments arguments) { @@ -50,12 +39,7 @@ public class Executor implements StatementNodeVisitor .collect(toMap( index -> method.getArguments().get(index).getId(), index -> arguments.get(index))); - return new Environment(new Project() { - @Override - public ConcreteType findClass(String name) { - throw new UnsupportedOperationException(); - } - }, stackFrame); + return environment.withStackFrame(stackFrame); } public static Optional exec(Environment environment, StatementNode statement) { diff --git a/src/main/java/org/zwobble/couscous/values/ConcreteType.java b/src/main/java/org/zwobble/couscous/values/ConcreteType.java index 7d296db..43016d1 100644 --- a/src/main/java/org/zwobble/couscous/values/ConcreteType.java +++ b/src/main/java/org/zwobble/couscous/values/ConcreteType.java @@ -3,11 +3,12 @@ package org.zwobble.couscous.values; import java.util.List; import java.util.Map; import java.util.function.BiFunction; -import java.util.function.Function; import java.util.function.Supplier; +import java.util.stream.Collectors; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.interpreter.Arguments; +import org.zwobble.couscous.interpreter.Environment; import org.zwobble.couscous.interpreter.Executor; import org.zwobble.couscous.interpreter.NoSuchMethod; import org.zwobble.couscous.interpreter.UnexpectedValueType; @@ -42,7 +43,7 @@ public class ConcreteType { public Builder staticMethod( String name, Supplier>> argumentsTypes, - Function method) { + BiFunction method) { staticMethods.put(name, new StaticMethodValue(argumentsTypes, method)); return this; } @@ -58,7 +59,15 @@ public class ConcreteType { .filter(method -> method.isStatic()) .collect(toMap( method -> method.getName(), - Executor::callMethod)); + method -> { + Supplier>> argumentTypes = () -> method.getArguments() + .stream() + .>map(arg -> arg.getType()) + .collect(Collectors.toList()); + return new StaticMethodValue(argumentTypes, (environment, arguments) -> { + return Executor.callMethod(environment, method, arguments); + }); + })); return new ConcreteType( classNode.getName(), ImmutableMap.of(), @@ -88,9 +97,9 @@ public class ConcreteType { return method.apply((T)receiver, new Arguments(arguments)); } - public InterpreterValue callStaticMethod(String methodName, List arguments) { + public InterpreterValue callStaticMethod(Environment environment, String methodName, List arguments) { val method = findMethod(staticMethods, methodName, arguments); - return method.apply(new Arguments(arguments)); + return method.apply(environment, new Arguments(arguments)); } private static T findMethod(Map methods, String methodName, List arguments) { diff --git a/src/main/java/org/zwobble/couscous/values/IntegerValue.java b/src/main/java/org/zwobble/couscous/values/IntegerValue.java index 1b20a01..76d0c59 100644 --- a/src/main/java/org/zwobble/couscous/values/IntegerValue.java +++ b/src/main/java/org/zwobble/couscous/values/IntegerValue.java @@ -9,7 +9,7 @@ import lombok.val; public class IntegerValue implements InterpreterValue { public static final ConcreteType TYPE = ConcreteType.builder("Integer") .staticMethod("parseInt", () -> asList(StringValue.TYPE), - arguments -> { + (environment, arguments) -> { val value = (StringValue)arguments.get(0); return new IntegerValue(Integer.parseInt(value.getValue())); }) diff --git a/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java b/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java index 15a70d3..80ace03 100644 --- a/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java +++ b/src/main/java/org/zwobble/couscous/values/StaticMethodValue.java @@ -1,10 +1,11 @@ package org.zwobble.couscous.values; import java.util.List; -import java.util.function.Function; +import java.util.function.BiFunction; import java.util.function.Supplier; import org.zwobble.couscous.interpreter.Arguments; +import org.zwobble.couscous.interpreter.Environment; import lombok.AccessLevel; import lombok.Getter; @@ -15,15 +16,15 @@ public class StaticMethodValue implements Callable { @Getter(value=AccessLevel.NONE) Supplier>> argumentTypes; @Getter(value=AccessLevel.NONE) - Function apply; + BiFunction apply; @Override public List> getArgumentTypes() { return argumentTypes.get(); } - public InterpreterValue apply(Arguments arguments) { - return apply.apply(arguments); + public InterpreterValue apply(Environment environment, Arguments arguments) { + return apply.apply(environment, arguments); } } 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 72a3ab4..e2cf1b8 100644 --- a/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java +++ b/src/test/java/org/zwobble/couscous/tests/interpreter/EvaluatorTests.java @@ -1,13 +1,19 @@ package org.zwobble.couscous.tests.interpreter; import org.junit.Test; +import org.zwobble.couscous.JavaProject; +import org.zwobble.couscous.MapBackedProject; +import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.FormalArgumentNode; +import org.zwobble.couscous.ast.MethodNode; +import org.zwobble.couscous.ast.ReturnNode; import org.zwobble.couscous.ast.TernaryConditionalNode; import org.zwobble.couscous.interpreter.ConditionMustBeBoolean; import org.zwobble.couscous.interpreter.Environment; import org.zwobble.couscous.interpreter.NoSuchMethod; import org.zwobble.couscous.interpreter.UnexpectedValueType; import org.zwobble.couscous.interpreter.WrongNumberOfArguments; +import org.zwobble.couscous.values.ConcreteType; import org.zwobble.couscous.values.IntegerValue; import org.zwobble.couscous.values.StringValue; @@ -101,9 +107,26 @@ public class EvaluatorTests { assertEquals(new IntegerValue(42), result); } + @Test + public void canCallStaticMethodFromAnotherStaticMethod() { + val classNode = ClassNode.builder("com.example.Program") + .method(MethodNode.staticMethod("main") + .statement(new ReturnNode(staticMethodCall("java.lang.Integer", "parseInt", literal("42")))) + .build()) + .build(); + val environment = new Environment( + JavaProject.builder() + .addClass("com.example.Program", ConcreteType.fromNode(classNode)) + .build(), + ImmutableMap.of()); + val result = eval(environment, + staticMethodCall("com.example.Program", "main")); + assertEquals(new IntegerValue(42), result); + } + private static Environment emptyEnvironment() { return new Environment( - new MapBackedProject(ImmutableMap.of("java.lang.Integer", IntegerValue.TYPE)), + JavaProject.builder().build(), ImmutableMap.of()); } } diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java index c764261..d187c20 100644 --- a/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java +++ b/src/test/java/org/zwobble/couscous/tests/interpreter/InterpreterTests.java @@ -1,6 +1,7 @@ package org.zwobble.couscous.tests.interpreter; import org.junit.Test; +import org.zwobble.couscous.MapBackedProject; import org.zwobble.couscous.ast.Assignment; import org.zwobble.couscous.ast.ClassNode; import org.zwobble.couscous.ast.ExpressionStatementNode; @@ -78,10 +79,10 @@ public class InterpreterTests { private InterpreterValue runMethod(MethodNode.MethodNodeBuilder methodBuilder, InterpreterValue... arguments) { val method = methodBuilder.build(); - val classNode = ClassNode.builder() + val className = "com.example.Program"; + val classNode = ClassNode.builder(className) .method(method) .build(); - val className = "com.example.Program"; val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of( className, ConcreteType.fromNode(classNode)))); diff --git a/src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java b/src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java deleted file mode 100644 index 0874afb..0000000 --- a/src/test/java/org/zwobble/couscous/tests/interpreter/MapBackedProject.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.zwobble.couscous.tests.interpreter; - -import java.util.Map; - -import org.zwobble.couscous.Project; -import org.zwobble.couscous.values.ConcreteType; - -public class MapBackedProject implements Project { - private Map> classes; - - public MapBackedProject(Map> classes) { - this.classes = classes; - } - - @Override - public ConcreteType findClass(String name) { - return classes.get(name); - } -} -- cgit v1.2.3