package scotch.compiler.analyzer;
import static java.util.Arrays.asList;
import static scotch.compiler.syntax.TypeError.typeError;
import static scotch.compiler.text.SourceLocation.NULL_SOURCE;
import static scotch.compiler.text.TextUtil.repeat;
import static scotch.util.TestUtil.access;
import static scotch.util.TestUtil.arg;
import static scotch.util.TestUtil.conditional;
import static scotch.util.TestUtil.fn;
import static scotch.util.TestUtil.isConstructor;
import static scotch.util.TestUtil.let;
import static scotch.util.TestUtil.raise;
import static scotch.util.TestUtil.scope;
import static scotch.compiler.syntax.type.Types.fn;
import static scotch.compiler.syntax.type.Types.sum;
import static scotch.compiler.syntax.type.Types.t;
import static scotch.compiler.syntax.type.Unification.mismatch;
import java.util.Optional;
import java.util.function.Function;
import org.junit.Test;
import scotch.compiler.ClassLoaderResolver;
import scotch.compiler.Compiler;
import scotch.compiler.CompilerTest;
import scotch.compiler.syntax.definition.DefinitionGraph;
import scotch.compiler.syntax.type.SumType;
import scotch.compiler.syntax.type.Type;
public class TypeCheckerIntegrationTest extends CompilerTest<ClassLoaderResolver> {
@Test
public void shouldHaveTypeOfTuple3OfInts() {
compile(
"module scotch.test",
"tuple = (1, 2, 3)"
);
shouldNotHaveErrors();
shouldHaveValue("scotch.test.tuple", sum("scotch.data.tuple.(,,)", asList(intType, intType, intType)));
}
@Test
public void shouldHaveError_whenListIsHeterogeneous() {
compile(
"module scotch.test",
"list = [1, 2, \"oops\"]"
);
shouldHaveErrors(typeError(
mismatch(intType, stringType),
NULL_SOURCE
));
}
@Test
public void shouldDetermineTypeOfSuccessfulChainedMaybe() {
compile(
"module scotch.test",
"import scotch.data.num", // TODO should not require import
"",
"addedStuff = do",
" x <- Just 3",
" y <- Just 2",
" return $ x + y"
);
shouldNotHaveErrors();
shouldHaveValue("scotch.test.addedStuff", sum("scotch.data.maybe.Maybe", asList(intType)));
}
@Test
public void shouldDetermineTypeOfFailedChainedMaybe() {
compile(
"module scotch.test",
"import scotch.data.num", // TODO should not require import
"",
"addedStuff = do",
" x <- Just 3",
" y <- Nothing",
" return $ x + y"
);
shouldNotHaveErrors();
shouldHaveValue("scotch.test.addedStuff", sum("scotch.data.maybe.Maybe", asList(intType)));
}
@Test
public void shouldDestructure2Tuples() {
compile(
"module scotch.test",
"second (_, b) = b",
"third (_, (_, c)) = c"
);
shouldNotHaveErrors();
Type tuple = tupleType(t(49), t(50));
String tag = "scotch.data.tuple.(,)";
shouldHaveValue("scotch.test.second", fn(tuple, t(50)));
shouldHaveValue("scotch.test.second", fn("scotch.test.(second#0)", arg("#0", tuple),
conditional(
isConstructor(arg("#0", tuple, tag), tag),
scope("scotch.test.(second#0#0)",
let(t(50), "b", access(arg("#0", tuple, tag), "_1", t(50)), arg("b", t(50)))),
raise("Incomplete match", t(50)),
t(50)
)
));
shouldHaveValue("scotch.test.third", fn(tupleType(t(45), tupleType(t(47), t(48))), t(48)));
}
private static SumType tupleType(Type... types) {
return sum("scotch.data.tuple.(" + repeat(",", types.length - 1) + ")", asList(types));
}
@Override
protected Function<Compiler, DefinitionGraph> compile() {
return Compiler::checkTypes;
}
@Override
protected ClassLoaderResolver initResolver() {
return new ClassLoaderResolver(Optional.empty(), getClass().getClassLoader());
}
}