1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
|
package org.zwobble.couscous.tests.interpreter;
import java.util.List;
import java.util.stream.Collectors;
import org.junit.Test;
import org.zwobble.couscous.MapBackedProject;
import org.zwobble.couscous.ast.AssignmentNode;
import org.zwobble.couscous.ast.ClassNode;
import org.zwobble.couscous.ast.ExpressionStatementNode;
import org.zwobble.couscous.ast.LiteralNode;
import org.zwobble.couscous.ast.ReturnNode;
import org.zwobble.couscous.interpreter.Interpreter;
import org.zwobble.couscous.interpreter.UnboundVariable;
import org.zwobble.couscous.interpreter.UnexpectedValueType;
import org.zwobble.couscous.interpreter.VariableNotInScope;
import org.zwobble.couscous.interpreter.WrongNumberOfArguments;
import org.zwobble.couscous.interpreter.values.ConcreteType;
import org.zwobble.couscous.interpreter.values.InterpreterValues;
import org.zwobble.couscous.tests.BackendTests;
import org.zwobble.couscous.tests.MethodRunner;
import org.zwobble.couscous.values.IntegerValue;
import org.zwobble.couscous.values.PrimitiveValue;
import org.zwobble.couscous.values.StringValue;
import com.google.common.collect.ImmutableMap;
import static org.junit.Assert.assertEquals;
import static org.zwobble.couscous.ast.FormalArgumentNode.formalArg;
import static org.zwobble.couscous.ast.LiteralNode.literal;
import static org.zwobble.couscous.ast.LocalVariableDeclarationNode.localVariableDeclaration;
import static org.zwobble.couscous.ast.MethodNode.staticMethod;
import static org.zwobble.couscous.ast.VariableDeclaration.var;
import static org.zwobble.couscous.ast.VariableReferenceNode.reference;
import static org.zwobble.couscous.tests.util.ExtraAsserts.assertThrows;
import static org.zwobble.couscous.values.PrimitiveValues.value;
import lombok.val;
public class InterpreterTests extends BackendTests {
@Test
public void errorIfWrongNumberOfArgumentsArePassed() {
val method = staticMethod("hello");
val exception = assertThrows(WrongNumberOfArguments.class,
() -> runMethod(method, value("hello, world!")));
assertEquals(new WrongNumberOfArguments(0, 1), exception);
}
@Test
public void errorIfTryingToAssignValueOfWrongTypeToVariable() {
val arg = formalArg(var(42, "x", StringValue.REF));
val method = staticMethod("hello")
.argument(arg)
.statement(new ExpressionStatementNode(new AssignmentNode(reference(arg), literal(0))));
val exception = assertThrows(UnexpectedValueType.class,
() -> runMethod(method, value("")));
assertEquals(new UnexpectedValueType(StringValue.REF, IntegerValue.REF), exception);
}
@Test
public void canDeclareVariableAndThenAssignValues() {
val localVariableDeclaration = localVariableDeclaration(
42, "x", StringValue.REF, LiteralNode.literal("[initial value]"));
val method = staticMethod("hello")
.statement(localVariableDeclaration)
.statement(new ExpressionStatementNode(new AssignmentNode(reference(localVariableDeclaration), LiteralNode.literal("[updated value]"))))
.statement(new ReturnNode(reference(localVariableDeclaration)));
val result = runMethod(method);
assertEquals(value("[updated value]"), result);
}
@Test
public void errorIfTryingToAssignToVariableNotInScope() {
val localVariableDeclaration = localVariableDeclaration(
42, "x", StringValue.REF, literal(""));
val method = staticMethod("hello")
.statement(new ExpressionStatementNode(new AssignmentNode(reference(localVariableDeclaration), LiteralNode.literal("[updated value]"))));
val exception = assertThrows(VariableNotInScope.class,
() -> runMethod(method));
assertEquals(new VariableNotInScope(42), exception);
}
@Test
public void errorIfTryingToGetValueOfVariableNotInScope() {
val localVariableDeclaration = localVariableDeclaration(
42, "x", StringValue.REF, literal(""));
val method = staticMethod("hello")
.statement(new ReturnNode(reference(localVariableDeclaration)));
val exception = assertThrows(VariableNotInScope.class,
() -> runMethod(method));
assertEquals(new VariableNotInScope(42), exception);
}
@Test
public void errorIfTryingToGetValueOfUnboundVariable() {
val localVariableDeclaration = localVariableDeclaration(
42, "x", StringValue.REF, literal(""));
val method = staticMethod("hello")
.statement(new ReturnNode(reference(localVariableDeclaration)))
.statement(localVariableDeclaration);
val exception = assertThrows(UnboundVariable.class,
() -> runMethod(method));
assertEquals(new UnboundVariable(42), exception);
}
@Override
protected MethodRunner buildMethodRunner() {
return new MethodRunner() {
@Override
public PrimitiveValue runMethod(
ClassNode classNode,
String methodName,
List<PrimitiveValue> arguments) {
val interpreter = new Interpreter(new MapBackedProject(ImmutableMap.of(
classNode.getName(), ConcreteType.fromNode(classNode))));
val argumentValues = arguments.stream()
.map(InterpreterValues::value)
.collect(Collectors.toList());
return interpreter.run(classNode.getName(), methodName, argumentValues)
.toPrimitiveValue().get();
}
};
}
}
|