public void FailsTypeCheckingForIncorrectNumberOfArguments() { var twoArgsToInteger = NamedType.Function(new[] { NamedType.Boolean, NamedType.Integer }, NamedType.Integer); ShouldFailTypeChecking("foo(true, 1, false)", foo => twoArgsToInteger).WithError( "Type mismatch: expected System.Func<bool, int, int>, found System.Func<bool, int, bool, int>.", 1, 1); }
public Expression TypeCheck(Call call, Scope scope) { var position = call.Position; var callable = call.Callable; var arguments = call.Arguments; var isOperator = call.IsOperator; var typedCallable = TypeCheck(callable, scope); var typedArguments = TypeCheck(arguments, scope); var calleeType = typedCallable.Type; var calleeFunctionType = calleeType as NamedType; if (calleeFunctionType == null || calleeFunctionType.Name != "System.Func") { LogError(CompilerError.ObjectNotCallable(position)); return(call); } var returnType = calleeType.GenericArguments.Last(); var argumentTypes = typedArguments.Select(x => x.Type).ToVector(); Unify(position, calleeType, NamedType.Function(argumentTypes, returnType)); var callType = unifier.Normalize(returnType); return(new Call(position, typedCallable, typedArguments, isOperator, callType)); }
public void FailsTypeCheckingForMismatchedArgumentTypes() { var integerToBoolean = NamedType.Function(new[] { NamedType.Integer }, NamedType.Boolean); ShouldFailTypeChecking("even(true)", even => integerToBoolean).WithError( "Type mismatch: expected int, found bool.", 1, 1); }
public Expression TypeCheck(MethodInvocation methodInvocation, Scope scope) { var position = methodInvocation.Position; var instance = methodInvocation.Instance; var methodName = methodInvocation.MethodName; var arguments = methodInvocation.Arguments; var typedInstance = TypeCheck(instance, scope); var instanceType = typedInstance.Type; var instanceNamedType = instanceType as NamedType; if (instanceNamedType == null) { LogError(CompilerError.AmbiguousMethodInvocation(position)); return(methodInvocation); } var typeMemberScope = new TypeMemberScope(typeRegistry.MembersOf(instanceNamedType)); //TODO: This block is suspiciously like Call type checking, but Callable/MethodName is evaluated in a special scope and the successful return is different. var Callable = methodName; //Attempt to treat this method invocation as an extension method, if we fail to find the method in the type member scope. if (!typeMemberScope.Contains(Callable.Identifier)) { var extensionMethodCall = TypeCheck(new Call(position, methodName, new[] { instance }.Concat(arguments)), scope); if (extensionMethodCall != null) { return(extensionMethodCall); } } var typedCallable = TypeCheck(Callable, typeMemberScope); var typedArguments = TypeCheck(arguments, scope); var calleeType = typedCallable.Type; var calleeFunctionType = calleeType as NamedType; if (calleeFunctionType == null || calleeFunctionType.Name != "System.Func") { LogError(CompilerError.ObjectNotCallable(position)); return(methodInvocation); } var returnType = calleeType.GenericArguments.Last(); var argumentTypes = typedArguments.Select(x => x.Type).ToVector(); Unify(position, calleeType, NamedType.Function(argumentTypes, returnType)); var callType = unifier.Normalize(returnType); var typedMethodName = (Name)typedCallable; return(new MethodInvocation(position, typedInstance, typedMethodName, typedArguments, callType)); }
public void HasAFunctionTypeWithReturnTypeEqualToTheTypeOfTheBodyExpression() { Type("fn () 1").ShouldEqual(NamedType.Function(Integer)); Type("fn () true").ShouldEqual(NamedType.Function(Boolean)); Type("fn () x", x => Integer).ShouldEqual(NamedType.Function(Integer)); Type("fn () x", x => Boolean).ShouldEqual(NamedType.Function(Boolean)); Type("fn (int x) x+1").ShouldEqual(NamedType.Function(new[] { Integer }, Integer)); Type("fn (int x) x+1 > 0").ShouldEqual(NamedType.Function(new[] { Integer }, Boolean)); }
public GlobalScope() { globals = new BindingDictionary(); DataType @int = NamedType.Integer; DataType @bool = NamedType.Boolean; DataType integerOperation = NamedType.Function(new[] { @int, @int }, @int); DataType integerComparison = NamedType.Function(new[] { @int, @int }, @bool); DataType booleanOperation = NamedType.Function(new[] { @bool, @bool }, @bool); globals["<"] = integerComparison; globals["<="] = integerComparison; globals[">"] = integerComparison; globals[">="] = integerComparison; globals["=="] = integerComparison; globals["!="] = integerComparison; globals["+"] = integerOperation; globals["*"] = integerOperation; globals["/"] = integerOperation; globals["-"] = integerOperation; globals["&&"] = booleanOperation; globals["||"] = booleanOperation; globals["!"] = NamedType.Function(new[] { @bool }, @bool); var T = TypeVariable.CreateGeneric(); //TypeVariable 0 var S = TypeVariable.CreateGeneric(); //TypeVariable 1 var enumerableT = NamedType.Enumerable(T); var nullableT = NamedType.Nullable(T); var vectorT = NamedType.Vector(T); var enumerableS = NamedType.Enumerable(S); globals["??"] = NamedType.Function(new DataType[] { nullableT, T }, T); globals["Print"] = NamedType.Function(new[] { T }, NamedType.Void); globals["Nullable"] = NamedType.Function(new[] { T }, nullableT); globals["First"] = NamedType.Function(new[] { enumerableT }, T); globals["Take"] = NamedType.Function(new[] { enumerableT, @int }, enumerableT); globals["Skip"] = NamedType.Function(new[] { enumerableT, @int }, enumerableT); globals["Any"] = NamedType.Function(new[] { enumerableT }, @bool); globals["Count"] = NamedType.Function(new[] { enumerableT }, @int); globals["Select"] = NamedType.Function(new[] { enumerableT, NamedType.Function(new[] { T }, S) }, enumerableS); globals["Where"] = NamedType.Function(new[] { enumerableT, NamedType.Function(new[] { T }, @bool) }, enumerableT); globals["Each"] = NamedType.Function(new[] { vectorT }, enumerableT); globals[ReservedName.__index__] = NamedType.Function(new[] { vectorT, @int }, T); globals[ReservedName.__slice__] = NamedType.Function(new[] { vectorT, @int, @int }, vectorT); globals["Append"] = NamedType.Function(new DataType[] { vectorT, T }, vectorT); globals["With"] = NamedType.Function(new[] { vectorT, @int, T }, vectorT); }
public void CanCreateFullyTypedInstance() { var lambda = (Lambda)Parse("fn (x, int y, bool z) x+y>0 && z"); lambda.Parameters.ShouldHaveTypes(Unknown, Unknown, Unknown); lambda.Body.Type.ShouldEqual(Unknown); lambda.Type.ShouldEqual(Unknown); var typedLambda = WithTypes(lambda); typedLambda.Parameters.ShouldHaveTypes(Integer, Integer, Boolean); typedLambda.Body.Type.ShouldEqual(Boolean); typedLambda.Type.ShouldEqual(NamedType.Function(new[] { Integer, Integer, Boolean }, Boolean)); }
public void PassesTypeCheckingEvenWhenMethodNamesAreTheSameAsNamesInTheSurroundingScope() { var fooClass = "class Foo { int A() {0} int B() {2} }".ParseClass(); var typeRegistry = new TypeRegistry(); var constructorReturningFoo = NamedType.Constructor(new NamedType(fooClass)); typeRegistry.Add(fooClass); var typeChecker = new TypeChecker(typeRegistry); typeChecker.TypeCheck(fooClass, Scope(B => NamedType.Function(NamedType.Boolean))).Type.ShouldEqual(constructorReturningFoo); typeChecker.HasErrors.ShouldBeFalse(); }
public void CanCreateFullyTypedInstance() { var node = Parse("bool foo(int x, int y, bool z) {x+y>0 && z}"); node.Parameters.ShouldHaveTypes(Unknown, Unknown, Unknown); node.Body.Type.ShouldEqual(Unknown); node.Type.ShouldEqual(Unknown); var typeChecker = new TypeChecker(); var typedNode = typeChecker.TypeCheck(node, Scope()); typedNode.Parameters.ShouldHaveTypes(Integer, Integer, Boolean); typedNode.Body.Type.ShouldEqual(Boolean); typedNode.Type.ShouldEqual(NamedType.Function(new[] { Integer, Integer, Boolean }, Boolean)); }
public void CanCreateFullyTypedInstance() { var node = (MethodInvocation)Parse("math.Even(two)"); node.Instance.Type.ShouldEqual(Unknown); node.MethodName.Type.ShouldEqual(Unknown); node.Arguments.Single().Type.ShouldEqual(Unknown); node.Type.ShouldEqual(Unknown); var typedNode = WithTypes(node, math => mathType, two => Integer); typedNode.Instance.Type.ShouldEqual(mathType); typedNode.MethodName.Type.ShouldEqual(NamedType.Function(new[] { Integer }, Boolean)); typedNode.Arguments.Single().Type.ShouldEqual(Integer); typedNode.Type.ShouldEqual(Boolean); }
public void CanCreateFullyTypedInstance() { var call = (Call)Parse("func(yes, zero)"); call.Callable.Type.ShouldEqual(Unknown); call.Arguments.ShouldHaveTypes(Unknown, Unknown); call.Type.ShouldEqual(Unknown); var typedCall = WithTypes(call, func => Function(new[] { Boolean, Integer }, Integer), yes => Boolean, zero => Integer); typedCall.Callable.Type.ShouldEqual(NamedType.Function(new[] { Boolean, Integer }, Integer)); typedCall.Arguments.ShouldHaveTypes(Boolean, Integer); typedCall.Type.ShouldEqual(Integer); }
public Expression TypeCheck(Lambda lambda, Scope scope) { var position = lambda.Position; var parameters = lambda.Parameters; var body = lambda.Body; var typedParameters = GetTypedParameters(parameters).ToVector(); var localScope = CreateLocalScope(scope, typedParameters); var typedBody = TypeCheck(body, localScope); var normalizedParameters = NormalizeTypes(typedParameters); var parameterTypes = normalizedParameters.Select(p => p.Type).ToArray(); return(new Lambda(position, normalizedParameters, typedBody, NamedType.Function(parameterTypes, typedBody.Type))); }
public void ShouldGetMemberBindingsWhenGivenTheNamedTypeOfRegisteredClasses() { var math = "class Math { int Square(int x) {x*x} bool Zero(int x) {x==0} int Max(int* ints) {0} }".ParseClass(); var mathType = new NamedType(math); typeRegistry.Add(math); var members = typeRegistry.MembersOf(mathType); var square = members[0]; var zero = members[1]; var max = members[2]; square.Identifier.ShouldEqual("Square"); square.Type.ShouldEqual(NamedType.Function(new[] { NamedType.Integer }, NamedType.Integer)); zero.Identifier.ShouldEqual("Zero"); zero.Type.ShouldEqual(NamedType.Function(new[] { NamedType.Integer }, NamedType.Boolean)); max.Identifier.ShouldEqual("Max"); max.Type.ShouldEqual(NamedType.Function(new[] { NamedType.Enumerable(NamedType.Integer) }, NamedType.Integer)); }
public void HasATypeIncludingInputTypesAndReturnType() { Type("int foo() {1}").ShouldEqual(NamedType.Function(Integer)); Type("bool foo(int x) {false}").ShouldEqual(NamedType.Function(new[] { Integer }, Boolean)); Type("int foo(int x, bool y) {1}").ShouldEqual(NamedType.Function(new[] { Integer, Boolean }, Integer)); }
public void FailsTypeCheckingForNamesThatAreNotConstructorNames() { ShouldFailTypeChecking("new Foo()", Foo => Integer).WithError("Cannot construct 'Foo' because it is not a type.", 1, 5); ShouldFailTypeChecking("new Foo()", Foo => NamedType.Function(Integer)).WithError("Cannot construct 'Foo' because it is not a type.", 1, 5); }
public void InfersParameterTypesFromUsages() { Type("fn (x) x+1").ShouldEqual(NamedType.Function(new[] { Integer }, Integer)); Type("fn (x, y) x+y").ShouldEqual(NamedType.Function(new[] { Integer, Integer }, Integer)); Type("fn (x) x+1 > 0").ShouldEqual(NamedType.Function(new[] { Integer }, Boolean)); }
public NamedType DeclaredType(Function function) { var parameterTypes = function.Parameters.Select(p => TypeOf(p.DeclaredTypeName)).ToArray(); return(NamedType.Function(parameterTypes, TypeOf(function.ReturnTypeName))); }
private static DataType Function(IEnumerable <DataType> parameterTypes, DataType returnType) { return(NamedType.Function(parameterTypes, returnType)); }
public void EvaluatesBodyExpressionTypesInANewScopeIncludingParameters() { Type("int foo(int x) {x}").ShouldEqual(NamedType.Function(new[] { Integer }, Integer)); Type("bool foo(int x, int y, bool b) {x==y || b}").ShouldEqual( NamedType.Function(new[] { Integer, Integer, Boolean }, Boolean)); }