-
Notifications
You must be signed in to change notification settings - Fork 1
/
CompilationUnitTests.cs
158 lines (130 loc) · 7.02 KB
/
CompilationUnitTests.cs
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
using System.Linq;
using Parsley;
using Rook.Compiling.Types;
using Rook.Core.Collections;
using Should;
namespace Rook.Compiling.Syntax
{
public class CompilationUnitTests : SyntaxTreeTests<CompilationUnit>
{
protected override Parser<CompilationUnit> Parser { get { return RookGrammar.CompilationUnit; } }
public void ParsesZeroOrMoreClasses()
{
Parses(" \t\r\n").IntoTree("");
Parses(" \t\r\n class Foo {} class Bar {} class Baz {} \t\r\n")
.IntoTree("class Foo {} class Bar {} class Baz {}");
}
public void ParsesZeroOrMoreFunctions()
{
Parses(" \t\r\n").IntoTree("");
Parses(" \t\r\n int life() {42} int universe() {42} int everything() {42} \t\r\n")
.IntoTree("int life() {42} int universe() {42} int everything() {42}");
}
public void DemandsClassesAppearBeforeFunctions()
{
Parses(" \t\r\n class Foo {} class Bar {} int life() {42} int universe() {42} int everything() {42} \t\r\n")
.IntoTree("class Foo {} class Bar {} int life() {42} int universe() {42} int everything() {42}");
FailsToParse("int square(int x) {x*x} class Foo { }").LeavingUnparsedTokens("class", "Foo", "{", "}").WithMessage("(1, 25): end of input expected");
}
public void DemandsEndOfInputAfterLastValidClassOrFunction()
{
FailsToParse("int life() {42} int univ").AtEndOfInput().WithMessage("(1, 25): ( expected");
FailsToParse("class Foo { } class").AtEndOfInput().WithMessage("(1, 20): identifier expected");
}
public void TypesAllClassesAndFunctions()
{
var compilationUnit = Parse(
@"class Foo { }
class Bar { }
bool Even(int n) {if (n==0) true else Odd(n-1)}
bool Odd(int n) {if (n==0) false else Even(n-1)}
int Main() {if (Even(4)) 0 else 1}");
var fooType = new NamedType(compilationUnit.Classes.Single(c => c.Name.Identifier == "Foo"));
var barType = new NamedType(compilationUnit.Classes.Single(c => c.Name.Identifier == "Bar"));
var fooConstructorType = NamedType.Constructor(fooType);
var barConstructorType = NamedType.Constructor(barType);
var typeChecker = new TypeChecker();
var typedCompilationUnit = typeChecker.TypeCheck(compilationUnit);
compilationUnit.Classes.ShouldList(
foo => foo.Type.ShouldEqual(Unknown),
bar => bar.Type.ShouldEqual(Unknown));
compilationUnit.Functions.ShouldList(
even =>
{
even.Name.Identifier.ShouldEqual("Even");
even.Type.ShouldEqual(Unknown);
even.Body.Type.ShouldEqual(Unknown);
},
odd =>
{
odd.Name.Identifier.ShouldEqual("Odd");
odd.Type.ShouldEqual(Unknown);
odd.Body.Type.ShouldEqual(Unknown);
},
main =>
{
main.Name.Identifier.ShouldEqual("Main");
main.Type.ShouldEqual(Unknown);
main.Body.Type.ShouldEqual(Unknown);
});
typedCompilationUnit.Classes.ShouldList(
foo => foo.Type.ShouldEqual(fooConstructorType),
bar => bar.Type.ShouldEqual(barConstructorType));
typedCompilationUnit.Functions.ShouldList(
even =>
{
even.Name.Identifier.ShouldEqual("Even");
even.Type.ToString().ShouldEqual("System.Func<int, bool>");
even.Body.Type.ShouldEqual(Boolean);
},
odd =>
{
odd.Name.Identifier.ShouldEqual("Odd");
odd.Type.ToString().ShouldEqual("System.Func<int, bool>");
odd.Body.Type.ShouldEqual(Boolean);
},
main =>
{
main.Name.Identifier.ShouldEqual("Main");
main.Type.ToString().ShouldEqual("System.Func<int>");
main.Body.Type.ShouldEqual(Integer);
});
}
public void FailsValidationWhenFunctionsFailValidation()
{
ShouldFailTypeChecking("int a() {0} int b() {true+0} int Main() {1}").WithError("Type mismatch: expected int, found bool.", 1, 26);
ShouldFailTypeChecking("int Main() { int x = 0; int x = 1; x }").WithError("Duplicate identifier: x", 1, 29);
ShouldFailTypeChecking("int Main() {(1)()}").WithError("Attempted to call a noncallable object.", 1, 14);
ShouldFailTypeChecking("int Square(int x) {x*x} int Main() {Square(1, 2)}").WithError("Type mismatch: expected System.Func<int, int>, found System.Func<int, int, int>.", 1, 37);
ShouldFailTypeChecking("int Main() {Square(2)}")
.WithErrors(
error => error.ShouldEqual("Reference to undefined identifier: Square", 1, 13),
error => error.ShouldEqual("Attempted to call a noncallable object.", 1, 13));
}
public void FailsValidationWhenClassesFailValidation()
{
ShouldFailTypeChecking("class Foo { int A() {0} int B() {true+0} }").WithError("Type mismatch: expected int, found bool.", 1, 38);
ShouldFailTypeChecking("class Foo { int A() { int x = 0; int x = 1; x } }").WithError("Duplicate identifier: x", 1, 38);
ShouldFailTypeChecking("class Foo { int A() {(1)()} }").WithError("Attempted to call a noncallable object.", 1, 23);
ShouldFailTypeChecking("class Foo { int Square(int x) {x*x} int Mismatch() {Square(1, 2)} }").WithError("Type mismatch: expected System.Func<int, int>, found System.Func<int, int, int>.", 1, 53);
ShouldFailTypeChecking("class Foo { int A() {Square(2)} }")
.WithErrors(
error => error.ShouldEqual("Reference to undefined identifier: Square", 1, 22),
error => error.ShouldEqual("Attempted to call a noncallable object.", 1, 22));
}
public void FailsValidationWhenFunctionAndClassNamesAreNotUnique()
{
ShouldFailTypeChecking("int a() {0} int b() {1} int a() {2} int Main() {1}").WithError("Duplicate identifier: a", 1, 29);
ShouldFailTypeChecking("class Foo { } class Bar { } class Foo { }").WithError("Duplicate identifier: Foo", 1, 29);
ShouldFailTypeChecking("class Zero { } int Zero() {0}").WithError("Duplicate identifier: Zero", 1, 20);
}
private Vector<CompilerError> ShouldFailTypeChecking(string source)
{
var compilationUnit = Parse(source);
var typeChecker = new TypeChecker();
typeChecker.TypeCheck(compilationUnit);
typeChecker.HasErrors.ShouldBeTrue();
return typeChecker.Errors;
}
}
}