summaryrefslogtreecommitdiff
path: root/src/main/java/org/zwobble
diff options
context:
space:
mode:
authorMichael Williamson <mike@zwobble.org>2026-07-31 10:50:10 +0100
committerMichael Williamson <mike@zwobble.org>2026-07-31 10:50:10 +0100
commit578bf1f04b48f48225cfa794a27426cca4f6384a (patch)
treefc5c2737ffa65e20305f165a593111463a36166d /src/main/java/org/zwobble
parent1831d27671abe87bcbea5da40517093c37efb9b7 (diff)
Support singleton structs
Diffstat (limited to 'src/main/java/org/zwobble')
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedStructDefinitionNode.java3
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStructDefinitionNode.java20
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java2
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javapreciselymatchers/JavaPreciselyMatchersGenerator.java6
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java32
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java90
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java36
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java2
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java21
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java10
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java23
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java4
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java3
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java29
14 files changed, 170 insertions, 111 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedStructDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedStructDefinitionNode.java
index dc9a8fe..f2e6b97 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedStructDefinitionNode.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedStructDefinitionNode.java
@@ -5,10 +5,11 @@ import org.zwobble.hobgoblin.compiler.sources.Source;
import org.zwobble.hobgoblin.compiler.types.StructType;
import java.util.List;
+import java.util.Optional;
public record TypedStructDefinitionNode(
StructType type,
- List<TypedStructFieldDefinitionNode> fields,
+ Optional<List<TypedStructFieldDefinitionNode>> fields,
DocComment docComment,
Source source
) implements TypedNamespaceStatementNode {
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStructDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStructDefinitionNode.java
index 72f45eb..0239ff8 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStructDefinitionNode.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStructDefinitionNode.java
@@ -7,17 +7,17 @@ package org.zwobble.hobgoblin.compiler.ast.untyped;
public record UntypedStructDefinitionNode(
String name,
- java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode> fields,
+ java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode>> fields,
org.zwobble.hobgoblin.compiler.ast.DocComment docComment,
org.zwobble.hobgoblin.compiler.sources.Source source
) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode {
public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder arbitrary() {
- return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder("", java.util.List.of(), org.zwobble.hobgoblin.compiler.ast.HobgoblinNativeAst.arbitraryDocComment(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource());
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder("", java.util.Optional.empty(), org.zwobble.hobgoblin.compiler.ast.HobgoblinNativeAst.arbitraryDocComment(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource());
}
public record Builder(
String name,
- java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode> fields,
+ java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode>> fields,
org.zwobble.hobgoblin.compiler.ast.DocComment docComment,
org.zwobble.hobgoblin.compiler.sources.Source source
) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode.Builder {
@@ -29,20 +29,12 @@ public record UntypedStructDefinitionNode(
return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder(name, fields, docComment, source);
}
- public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder withFields(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode> fields) {
- return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder(name, fields, docComment, source);
- }
-
- public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder addField(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode field) {
- var fields = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode>(this.fields);
- fields.add(field);
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder withFields(java.util.Optional<java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode>> fields) {
return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder(name, fields, docComment, source);
}
- public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder addField(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode.Builder field) {
- var fields = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode>(this.fields);
- fields.add(field.build());
- return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder(name, fields, docComment, source);
+ public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder withFields(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNode> fields) {
+ return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder(name, java.util.Optional.of(fields), docComment, source);
}
public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode.Builder withDocComment(org.zwobble.hobgoblin.compiler.ast.DocComment docComment) {
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java
index d7f4bb0..35bb931 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/java/JavaGenerator.java
@@ -15,6 +15,8 @@ import java.util.Set;
import java.util.stream.Collectors;
public class JavaGenerator {
+ public static final JavaIdentifier INSTANCE_FIELD_NAME = JavaIdentifier.of("INSTANCE");
+
private final JavaGeneratorConfig config;
public JavaGenerator(JavaGeneratorConfig config) {
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javapreciselymatchers/JavaPreciselyMatchersGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javapreciselymatchers/JavaPreciselyMatchersGenerator.java
index 72f6339..1069f4a 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javapreciselymatchers/JavaPreciselyMatchersGenerator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javapreciselymatchers/JavaPreciselyMatchersGenerator.java
@@ -195,7 +195,7 @@ public class JavaPreciselyMatchersGenerator implements Generator {
)
));
- for (var field : structDefinition.fields()) {
+ for (var field : structDefinition.fields().orElse(List.of())) {
var fieldType = field.type().value();
var javaFieldTypeRef = this.javaGenerator.generateReferenceTypeRef(fieldType);
var javaFieldName = this.javaGenerator.generateFieldName(field.name());
@@ -258,10 +258,6 @@ public class JavaPreciselyMatchersGenerator implements Generator {
this.typesInfo = typesInfo;
}
- public List<Field> fieldsOf(StructType type) {
- return this.typesInfo.fieldsOf(type);
- }
-
public List<SumType> variantOf(StructType type) {
return this.typesInfo.variantOf(type);
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java
index d8a0f5f..1c7c962 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatransient0/JavaTransient0Generator.java
@@ -492,7 +492,7 @@ public class JavaTransient0Generator implements Generator {
return generateEncodeMethod(
structDefinition.type(),
new JavaBlock(
- structDefinition.fields().stream()
+ structDefinition.fields().orElse(List.of()).stream()
.flatMap(field -> generateEncode(
new JavaMethodCall(
new JavaRef(VALUE_NAME),
@@ -512,19 +512,23 @@ public class JavaTransient0Generator implements Generator {
return generateDecodeMethod(
structDefinition.type(),
new JavaBlock(
- Stream.concat(
- structDefinition.fields().stream()
- .flatMap(field -> generateDecode(
- this.javaGenerator.generateFieldName(field.name()),
- field.type().value()
- ).stream()),
- Stream.of(new JavaReturn(new JavaNewExpression(
- structJavaTypeRef,
- structDefinition.fields().stream()
- .<JavaExpression>map(field -> new JavaRef(this.javaGenerator.generateFieldName(field.name())))
- .toList()
- )))
- ).toList()
+ structDefinition.fields().isEmpty()
+ ? List.of(new JavaReturn(
+ new JavaStaticFieldAccess(structJavaTypeRef, JavaGenerator.INSTANCE_FIELD_NAME)
+ ))
+ : Stream.concat(
+ structDefinition.fields().get().stream()
+ .flatMap(field -> generateDecode(
+ this.javaGenerator.generateFieldName(field.name()),
+ field.type().value()
+ ).stream()),
+ Stream.of(new JavaReturn(new JavaNewExpression(
+ structJavaTypeRef,
+ structDefinition.fields().get().stream()
+ .<JavaExpression>map(field -> new JavaRef(this.javaGenerator.generateFieldName(field.name())))
+ .toList()
+ )))
+ ).toList()
)
);
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java
index 62c73ff..e8ecaa7 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/javatypes/JavaTypesGenerator.java
@@ -122,7 +122,7 @@ public class JavaTypesGenerator implements Generator {
TypedStructDefinitionNode structDefinition,
Context context
) {
- var components = structDefinition.fields().stream()
+ var components = structDefinition.fields().orElse(List.of()).stream()
.map(field -> new JavaRecordComponent(
generateTypeRef(field.type(), context),
generateFieldName(field.name())
@@ -135,31 +135,51 @@ public class JavaTypesGenerator implements Generator {
var builderJavaTypeRef = generateBuilderTypeRef(structDefinition.type());
- var arbitraryComponentValues = structDefinition.fields().stream()
- .map(field -> arbitraryValue(field.type(), context))
- .toList();
+ var arbitraryValueExpression = new JavaNewExpression(
+ builderJavaTypeRef,
+ structDefinition.fields().orElse(List.of()).stream()
+ .map(field -> arbitraryValue(field.type(), context))
+ .toList()
+ );
+
+ var body = new ArrayList<JavaClassBodyDeclaration>();
+
+ if (structDefinition.fields().isEmpty()) {
+ body.add(new JavaFieldDeclaration(
+ JavaVisibility.PUBLIC,
+ JavaMemberKind.STATIC,
+ this.generateTypeRef(structDefinition.type(), context),
+ JavaGenerator.INSTANCE_FIELD_NAME,
+ Optional.of(new JavaNewExpression(
+ generateTypeRef(structDefinition.type(), context),
+ List.of()
+ ))
+ ));
+ }
+
+ body.add(new JavaMethodDeclaration(
+ ARBITRARY_METHOD_NAME,
+ JavaVisibility.PUBLIC,
+ JavaMemberKind.STATIC,
+ builderJavaTypeRef,
+ List.of(),
+ List.of(),
+ new JavaBlock(List.of(
+ new JavaReturn(arbitraryValueExpression)
+ ))
+ ));
+
+ body.add(generateStructDefinitionBuilder(
+ structDefinition,
+ components,
+ context
+ ));
return new JavaRecordDeclaration(
generateTypeRef(structDefinition.type(), context),
components,
implementsTypes,
- List.of(
- new JavaMethodDeclaration(
- ARBITRARY_METHOD_NAME,
- JavaVisibility.PUBLIC,
- JavaMemberKind.STATIC,
- builderJavaTypeRef,
- List.of(),
- List.of(),
- new JavaBlock(List.of(
- new JavaReturn(new JavaNewExpression(
- builderJavaTypeRef,
- arbitraryComponentValues
- ))
- ))
- ),
- generateStructDefinitionBuilder(structDefinition, components, context)
- ),
+ body,
structDefinition.docComment()
);
}
@@ -169,9 +189,11 @@ public class JavaTypesGenerator implements Generator {
List<JavaRecordComponent> components,
Context context
) {
- var builderJavaTypeRef = generateBuilderTypeRef(structDefinition.type());
+ var structType = structDefinition.type();
- var implementsTypes = context.variantOf(structDefinition.type()).stream()
+ var builderJavaTypeRef = generateBuilderTypeRef(structType);
+
+ var implementsTypes = context.variantOf(structType).stream()
.map(sumType -> this.generateBuilderTypeRef(sumType))
.toList();
@@ -180,18 +202,22 @@ public class JavaTypesGenerator implements Generator {
BUILD_METHOD_NAME,
JavaVisibility.PUBLIC,
JavaMemberKind.INSTANCE,
- generateTypeRef(structDefinition.type(), context),
+ generateTypeRef(structType, context),
List.of(),
List.of(),
new JavaBlock(List.of(
- new JavaReturn(new JavaNewExpression(
- generateTypeRef(structDefinition.type(), context),
- components.stream().<JavaExpression>map(component -> new JavaRef(component.name())).toList()
- ))
+ new JavaReturn(
+ structDefinition.fields().isPresent()
+ ? new JavaNewExpression(
+ generateTypeRef(structType, context),
+ components.stream().<JavaExpression>map(component -> new JavaRef(component.name())).toList()
+ )
+ : new JavaStaticFieldAccess(this.generateTypeRef(structType, context), JavaGenerator.INSTANCE_FIELD_NAME)
+ )
))
));
- for (var field : structDefinition.fields()) {
+ for (var field : structDefinition.fields().orElse(List.of())) {
body.addAll(generateBuilderMethodsForField(field, components, builderJavaTypeRef, context));
}
@@ -585,9 +611,9 @@ public class JavaTypesGenerator implements Generator {
case StructType structType -> {
var newSeenTypes = new HashSet<>(seenTypes);
newSeenTypes.add(type);
- var result = context.fieldsOf(structType).stream()
+ var fields = context.fieldsOf(structType);
+ yield fields.orElse(List.of()).stream()
.allMatch(field -> isNonRecursivelyConstructable(field.type(), newSeenTypes, context));
- yield result;
}
case SumType sumType -> {
@@ -633,7 +659,7 @@ public class JavaTypesGenerator implements Generator {
return this.typesInfo.enumVariants(type);
}
- public List<Field> fieldsOf(StructType type) {
+ public Optional<List<Field>> fieldsOf(StructType type) {
return this.typesInfo.fieldsOf(type);
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java
index 15e9e9d..1401b83 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttransient0/RustTransient0Generator.java
@@ -386,7 +386,7 @@ public class RustTransient0Generator implements Generator {
private RustItem generateEncodeStructFunction(TypedStructDefinitionNode structDefinition) {
return generateEncodeFunction(
structDefinition.type(),
- structDefinition.fields().stream()
+ structDefinition.fields().orElse(List.of()).stream()
.map(field -> generateEncode(
new RustPrefixExpression(
RustPrefixOperator.BORROW,
@@ -406,24 +406,26 @@ public class RustTransient0Generator implements Generator {
return generateDecodeFunction(
structDefinition.type(),
- new RustBlockExpression(
- structDefinition.fields().stream()
- .<RustStatement>map(field -> new RustLetStatement(
- this.rustGenerator.generateFieldName(field.name()),
- false,
- generateDecode(field.type().value())
- ))
- .toList(),
- Optional.of(new RustStructExpression(
- rustType,
- structDefinition.fields().stream()
- .map(field -> new RustStructExprField(
+ structDefinition.fields().isEmpty()
+ ? new RustBlockExpression(List.of(), Optional.of(rustType))
+ : new RustBlockExpression(
+ structDefinition.fields().get().stream()
+ .<RustStatement>map(field -> new RustLetStatement(
this.rustGenerator.generateFieldName(field.name()),
- RustPath.of(this.rustGenerator.generateFieldName(field.name()))
+ false,
+ generateDecode(field.type().value())
))
- .toList()
- ))
- )
+ .toList(),
+ Optional.of(new RustStructExpression(
+ rustType,
+ structDefinition.fields().get().stream()
+ .map(field -> new RustStructExprField(
+ this.rustGenerator.generateFieldName(field.name()),
+ RustPath.of(this.rustGenerator.generateFieldName(field.name()))
+ ))
+ .toList()
+ ))
+ )
);
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java
index 99b4208..8ed9d9e 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rusttypes/RustTypesGenerator.java
@@ -127,7 +127,7 @@ public class RustTypesGenerator implements Generator {
var rustStructName = this.rustGenerator.generateTypeName(structDefinition.name());
- var rustFields = structDefinition.fields().stream()
+ var rustFields = structDefinition.fields().orElse(List.of()).stream()
.map(field -> new RustStructField(
this.rustGenerator.generateFieldName(field.name()),
generateRustTypeExpression(field.type(), context)
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java
index 47a8095..e312f30 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java
@@ -210,11 +210,7 @@ public class Parser {
var name = tokens.next(TokenType.IDENTIFIER);
tokens.skip(TokenType.SYMBOL_BRACE_OPEN);
- var fields = parseMany(
- () -> tokens.isNext(TokenType.SYMBOL_BRACE_CLOSE),
- () -> parseStructFieldDefinition(tokens),
- () -> true
- );
+ var fields = parseStructDefinitionFields(tokens);
tokens.skip(TokenType.SYMBOL_BRACE_CLOSE);
@@ -229,6 +225,21 @@ public class Parser {
);
}
+ private static Optional<List<UntypedStructFieldDefinitionNode>> parseStructDefinitionFields(TokenIterator tokens) {
+ var isSingleton = tokens.trySkip(TokenType.IDENTIFIER, "singleton");
+ if (isSingleton) {
+ tokens.skip(TokenType.SYMBOL_SEMICOLON);
+ return Optional.empty();
+ } else {
+ var fields = parseMany(
+ () -> tokens.isNext(TokenType.SYMBOL_BRACE_CLOSE),
+ () -> parseStructFieldDefinition(tokens),
+ () -> true
+ );
+ return Optional.of(fields);
+ }
+ }
+
private static UntypedStructFieldDefinitionNode parseStructFieldDefinition(
TokenIterator tokens
) {
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java
index 55edd97..43705a1 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenIterator.java
@@ -54,6 +54,16 @@ class TokenIterator {
}
}
+ public boolean trySkip(TokenType tokenType, String value) {
+ var token = peek();
+ if (token.tokenType().equals(tokenType) && value.contentEquals(token.charSequence())) {
+ this.tokenIndex++;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
private Token getAbsolute(int index) {
if (index < this.tokens.size()) {
return this.tokens.get(index);
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 d55c490..f03f979 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java
@@ -8,6 +8,7 @@ import org.zwobble.hobgoblin.compiler.types.*;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
import static org.zwobble.hobgoblin.compiler.typechecker.NamespaceNameResolution.resolveNamespaceName;
import static org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerSubtyping.isSubtype;
@@ -180,9 +181,13 @@ public class TypeChecker {
) {
var structType = (StructType) lookupMetaType(untyped.name(), untyped.source(), context);
- var typeCheckedFieldDefinitions = typeCheckFieldDefinitions(untyped.fields(), context);
+ var typeCheckedFieldDefinitions = untyped.fields().isPresent()
+ ? Optional.of(typeCheckFieldDefinitions(untyped.fields().get(), context))
+ : Optional.<TypeCheckFieldDefinitionsResult>empty();
+ var typedFields = typeCheckedFieldDefinitions.map(TypeCheckFieldDefinitionsResult::fields);
+ var typedFieldNodes = typeCheckedFieldDefinitions.map(TypeCheckFieldDefinitionsResult::typedFieldNodes);
- context.defineStructType(structType, typeCheckedFieldDefinitions.fields());
+ context.defineStructType(structType, typedFields);
for (var sumType : context.variantOf(structType)) {
var sumTypeFields = context.fieldsOf(sumType);
@@ -190,14 +195,14 @@ public class TypeChecker {
sumType,
sumTypeFields,
structType,
- typeCheckedFieldDefinitions.fields,
+ typedFields,
context.toTypesInfo()
);
}
return new TypedStructDefinitionNode(
structType,
- typeCheckedFieldDefinitions.typedFieldNodes(),
+ typedFieldNodes,
untyped.docComment(),
untyped.source()
);
@@ -303,13 +308,15 @@ public class TypeChecker {
SumType sumType,
List<Field> sumTypeFields,
StructType variantType,
- List<Field> variantTypeFields,
+ Optional<List<Field>> variantTypeFields,
TypesInfo typesInfo
) {
for (var sumTypeField : sumTypeFields) {
- var variantTypeField = variantTypeFields.stream()
- .filter(field -> field.name().equals(sumTypeField.name()))
- .findFirst();
+ var variantTypeField = variantTypeFields.flatMap(
+ fields -> fields.stream()
+ .filter(field -> field.name().equals(sumTypeField.name()))
+ .findFirst()
+ );
if (variantTypeField.isEmpty()) {
throw new SubtypeIsMissingFieldError(sumType, variantType, sumTypeField.name(), sumTypeField.source());
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java
index 0a61de7..5484904 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java
@@ -61,7 +61,7 @@ public class TypeCheckerNamespaceContext {
this.typesInfo.defineEnumType(enumType, enumVariants);
}
- public void defineStructType(StructType structType, List<Field> fields) {
+ public void defineStructType(StructType structType, Optional<List<Field>> fields) {
this.typesInfo.defineStructType(structType, fields);
}
@@ -69,7 +69,7 @@ public class TypeCheckerNamespaceContext {
return this.typesInfo.isDefined(type);
}
- public List<Field> fieldsOf(StructType type) {
+ public Optional<List<Field>> fieldsOf(StructType type) {
return this.typesInfo.fieldsOf(type);
}
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java
index 814466c..697be9b 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java
@@ -3,12 +3,13 @@ package org.zwobble.hobgoblin.compiler.typechecker;
import org.zwobble.hobgoblin.compiler.types.*;
import java.util.List;
+import java.util.Optional;
public interface TypesInfo {
TypesInfo EMPTY = TypesInfoInMemory.empty();
boolean isDefined(StructType type);
- List<Field> fieldsOf(StructType structType);
+ Optional<List<Field>> fieldsOf(StructType structType);
List<SumType> variantOf(StructType variantType);
List<Type> sumVariants(SumType type);
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java
index d9c0c85..821238b 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java
@@ -6,27 +6,32 @@ import org.zwobble.hobgoblin.compiler.util.ManyToMany;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
public class TypesInfoInMemory implements TypesInfo {
public static TypesInfoInMemory empty() {
return new TypesInfoInMemory(
new HashMap<>(),
new HashMap<>(),
+ new HashMap<>(),
new ManyToMany<>()
);
}
private final Map<EnumType, List<EnumVariant>> enumVariants;
- private final Map<Type, List<Field>> fieldsOf;
+ private final Map<Type, Optional<List<Field>>> structFields;
+ private final Map<Type, List<Field>> sumFields;
private final ManyToMany<SumType, Type> sumVariants;
public TypesInfoInMemory(
Map<EnumType, List<EnumVariant>> enumVariants,
- Map<Type, List<Field>> fieldsOf,
+ Map<Type, Optional<List<Field>>> structFields,
+ Map<Type, List<Field>> sumFields,
ManyToMany<SumType, Type> sumVariants
) {
this.enumVariants = enumVariants;
- this.fieldsOf = fieldsOf;
+ this.structFields = structFields;
+ this.sumFields = sumFields;
this.sumVariants = sumVariants;
}
@@ -38,19 +43,21 @@ public class TypesInfoInMemory implements TypesInfo {
return this.enumVariants.get(enumType);
}
- public void defineStructType(StructType structType, List<Field> fields) {
- this.fieldsOf.put(structType, fields);
+ public void defineStructType(StructType structType, Optional<List<Field>> fields) {
+ this.structFields.put(structType, fields);
}
@Override
public boolean isDefined(StructType type) {
- return this.fieldsOf.containsKey(type);
+ return this.structFields.containsKey(type);
}
- public List<Field> fieldsOf(StructType structType) {
- var fields = this.fieldsOf.get(structType);
+ @Override
+ public Optional<List<Field>> fieldsOf(StructType structType) {
+ var fields = this.structFields.get(structType);
if (fields == null) {
- return List.of();
+ // TODO: better error
+ throw new RuntimeException("struct not defined");
} else {
return fields;
}
@@ -64,7 +71,7 @@ public class TypesInfoInMemory implements TypesInfo {
for (var variantType : variantTypes) {
this.sumVariants.add(sumType, variantType);
}
- this.fieldsOf.put(sumType, fields);
+ this.sumFields.put(sumType, fields);
}
public List<SumType> variantOf(StructType variantType) {
@@ -76,7 +83,7 @@ public class TypesInfoInMemory implements TypesInfo {
}
public List<Field> fieldsOf(SumType sumType) {
- var fields = this.fieldsOf.get(sumType);
+ var fields = this.sumFields.get(sumType);
if (fields == null) {
return List.of();
} else {