diff options
6 files changed, 112 insertions, 28 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java index 5ce2be4..c3f664c 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -717,8 +717,8 @@ public class TypeChecker { case UntypedMemberAccessNode memberAccess -> { return evalMemberAccess(memberAccess, context); } - case UntypedNumberLiteralNode _ -> { - throw new UnsupportedOperationException("TODO"); + case UntypedNumberLiteralNode numberLiteral -> { + return evalNumberLiteral(numberLiteral); } case UntypedReferenceNode reference -> { return lookupTypeLevelValue(reference.name(), reference.source(), context); @@ -740,7 +740,17 @@ public class TypeChecker { } } - case Table _, Type _, TypeConstructor<?> _ -> { + case Table table -> { + switch (memberAccess.memberName()) { + case "where": + return new RelationMethod(new RelationTable(table), "where"); + + default: + break; + } + } + + case ConstantNumber _, Type _, TypeConstructor<?> _, RelationMethod _ -> { } } @@ -750,4 +760,10 @@ public class TypeChecker { memberAccess.source() ); } + + private static TypeLevelValue evalNumberLiteral( + UntypedNumberLiteralNode numberLiteral + ) { + return new ConstantNumber(numberLiteral.value()); + } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstantNumber.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstantNumber.java new file mode 100644 index 0000000..bd16d81 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/ConstantNumber.java @@ -0,0 +1,12 @@ +package org.zwobble.hobgoblin.compiler.types; + +import java.math.BigDecimal; + +public record ConstantNumber( + BigDecimal value +) implements TypeLevelValue { + @Override + public String describe() { + return value.toString(); + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/RelationMethod.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/RelationMethod.java new file mode 100644 index 0000000..87ba7fa --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/RelationMethod.java @@ -0,0 +1,10 @@ +package org.zwobble.hobgoblin.compiler.types; + +import org.zwobble.hobgoblin.compiler.ast.typed.Relation; + +public record RelationMethod(Relation relation, String name) implements TypeLevelValue { + @Override + public String describe() { + return name; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java index 8d12c2e..e0f7042 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TypeLevelValue.java @@ -1,5 +1,5 @@ package org.zwobble.hobgoblin.compiler.types; -public sealed interface TypeLevelValue permits DatabaseSchema, Table, Type, TypeConstructor { +public sealed interface TypeLevelValue permits ConstantNumber, DatabaseSchema, RelationMethod, Table, Type, TypeConstructor { String describe(); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java index 0d321a0..756f3bf 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java @@ -31,6 +31,10 @@ public class Types { return new TypeLevelValueType(databaseSchema); } + public static Type relationMethod(RelationMethod method) { + return new TypeLevelValueType(method); + } + public static Type table(Table table) { return new TypeLevelValueType(table); } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerEvalExpressionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerEvalExpressionTests.java index cf1e73c..48dde6e 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerEvalExpressionTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerEvalExpressionTests.java @@ -1,7 +1,9 @@ package org.zwobble.hobgoblin.compiler.typechecker; import org.junit.jupiter.api.Test; +import org.zwobble.hobgoblin.compiler.ast.typed.RelationTable; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNumberLiteralNode; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.sources.NullSource; @@ -10,6 +12,7 @@ import org.zwobble.hobgoblin.compiler.typechecker.errors.UnexpectedTypeError; import org.zwobble.hobgoblin.compiler.types.*; import org.zwobble.hobgoblin.compiler.util.SequencedMaps; +import java.math.BigDecimal; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -17,6 +20,69 @@ import static org.zwobble.precisely.AssertThat.assertThat; import static org.zwobble.precisely.Matchers.equalTo; public class TypeCheckerEvalExpressionTests { + // == Member access == + + @Test + public void memberAccessCanReadFieldOnStruct() { + var untyped = UntypedMemberAccessNode.arbitrary() + .withReceiver(UntypedReferenceNode.arbitrary().withName("blogApp")) + .withMemberName("authors") + .build(); + var globalContext = TypeCheckerContextArb.globalContext(); + var namespaceName = NamespaceName.of("App"); + var namespaceContext = globalContext.enterNamespace(namespaceName); + var databaseSchema = new DatabaseSchema(namespaceName, "blogApp"); + namespaceContext.declare("blogApp", Types.databaseSchema(databaseSchema), NullSource.INSTANCE); + var table = new Table(databaseSchema, "authors"); + var rowType = new SimpleStructType(namespaceName, "Author"); + var idField = new Field("id", NativeTypes.INT_32, NullSource.INSTANCE); + var databaseSchemaInfo = new DatabaseSchemaInfo(SequencedMaps.ofEntries( + Map.entry("authors", new TableInfo(table, rowType, idField)) + )); + namespaceContext.defineDatabaseSchema(databaseSchema, databaseSchemaInfo); + + var typed = TypeChecker.evalExpression(untyped, namespaceContext); + + assertThat(typed, equalTo(table)); + } + + @Test + public void memberAccessOnTableCanReadRelationMethod() { + var untyped = UntypedMemberAccessNode.arbitrary() + .withReceiver(UntypedReferenceNode.arbitrary().withName("authors")) + .withMemberName("where") + .build(); + var globalContext = TypeCheckerContextArb.globalContext(); + var namespaceName = NamespaceName.of("App"); + var namespaceContext = globalContext.enterNamespace(namespaceName); + var databaseSchema = new DatabaseSchema(namespaceName, "blogApp"); + var table = new Table(databaseSchema, "authors"); + namespaceContext.declare("authors", Types.table(table), NullSource.INSTANCE); + + var typed = TypeChecker.evalExpression(untyped, namespaceContext); + + assertThat(typed, equalTo(new RelationMethod(new RelationTable(table), "where"))); + } + + // == Number literal == + + @Test + public void canEvaluateNumber() { + // TODO: infer more precise type for number + var untyped = UntypedNumberLiteralNode.arbitrary() + .withValue(BigDecimal.valueOf(42)) + .build(); + var globalContext = TypeCheckerContextArb.globalContext(); + var namespaceName = NamespaceName.of("App"); + var namespaceContext = globalContext.enterNamespace(namespaceName); + + var typed = TypeChecker.evalExpression(untyped, namespaceContext); + + assertThat(typed, equalTo(new ConstantNumber(BigDecimal.valueOf(42)))); + } + + // == References == + @Test public void whenVariableIsTypeLevelValueThenCanEvaluateReference() { var untyped = UntypedReferenceNode.arbitrary() @@ -68,28 +134,4 @@ public class TypeCheckerEvalExpressionTests { assertThat(error.variableName(), equalTo("count")); } - - @Test - public void canReadMemberField() { - var untyped = UntypedMemberAccessNode.arbitrary() - .withReceiver(UntypedReferenceNode.arbitrary().withName("blogApp")) - .withMemberName("authors") - .build(); - var globalContext = TypeCheckerContextArb.globalContext(); - var namespaceName = NamespaceName.of("App"); - var namespaceContext = globalContext.enterNamespace(namespaceName); - var databaseSchema = new DatabaseSchema(namespaceName, "blogApp"); - namespaceContext.declare("blogApp", Types.databaseSchema(databaseSchema), NullSource.INSTANCE); - var table = new Table(databaseSchema, "authors"); - var rowType = new SimpleStructType(namespaceName, "Author"); - var idField = new Field("id", NativeTypes.INT_32, NullSource.INSTANCE); - var databaseSchemaInfo = new DatabaseSchemaInfo(SequencedMaps.ofEntries( - Map.entry("authors", new TableInfo(table, rowType, idField)) - )); - namespaceContext.defineDatabaseSchema(databaseSchema, databaseSchemaInfo); - - var typed = TypeChecker.evalExpression(untyped, namespaceContext); - - assertThat(typed, equalTo(table)); - } } |
