diff options
9 files changed, 301 insertions, 2 deletions
diff --git a/hobgoblin/src/ast/untyped.hob b/hobgoblin/src/ast/untyped.hob index 3a2b12e..6b74e3e 100644 --- a/hobgoblin/src/ast/untyped.hob +++ b/hobgoblin/src/ast/untyped.hob @@ -17,6 +17,7 @@ struct UntypedImportNode { } sum UntypedNamespaceStatementNode { + variant UntypedEnumDefinitionNode; variant UntypedNativeTypeDefinitionNode; variant UntypedStructDefinitionNode; variant UntypedSumDefinitionNode; @@ -24,6 +25,18 @@ sum UntypedNamespaceStatementNode { field name: String; } +struct UntypedEnumDefinitionNode { + field name: String; + field variants: List[UntypedEnumVariantDefinitionNode]; + field docComment: DocComment; + field source: Source; +} + +struct UntypedEnumVariantDefinitionNode { + field name: String; + field source: Source; +} + struct UntypedNativeTypeDefinitionNode { field name: String; field docComment: DocComment; diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumDefinitionNode.java new file mode 100644 index 0000000..42c28c1 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumDefinitionNode.java @@ -0,0 +1,37 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +public record UntypedEnumDefinitionNode(String name, java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode> variants, 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.UntypedEnumDefinitionNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder("", java.util.List.of(), org.zwobble.hobgoblin.compiler.ast.Native.arbitraryDocComment(), org.zwobble.hobgoblin.compiler.sources.Native.arbitrarySource()); + } + + public record Builder(String name, java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode> variants, org.zwobble.hobgoblin.compiler.ast.DocComment docComment, org.zwobble.hobgoblin.compiler.sources.Source source) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode(name, variants, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder(name, variants, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder withVariants(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode> variants) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder(name, variants, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder withDocComment(org.zwobble.hobgoblin.compiler.ast.DocComment docComment) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder(name, variants, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder(name, variants, docComment, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumVariantDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumVariantDefinitionNode.java new file mode 100644 index 0000000..25ffe87 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumVariantDefinitionNode.java @@ -0,0 +1,29 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +public record UntypedEnumVariantDefinitionNode(String name, org.zwobble.hobgoblin.compiler.sources.Source source) { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder("", org.zwobble.hobgoblin.compiler.sources.Native.arbitrarySource()); + } + + public record Builder(String name, org.zwobble.hobgoblin.compiler.sources.Source source) { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder(name, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedNamespaceStatementNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedNamespaceStatementNode.java index a2d3171..8cf12e1 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedNamespaceStatementNode.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedNamespaceStatementNode.java @@ -2,7 +2,7 @@ package org.zwobble.hobgoblin.compiler.ast.untyped; -public sealed interface UntypedNamespaceStatementNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNativeTypeDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumDefinitionNode { +public sealed interface UntypedNamespaceStatementNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNativeTypeDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumDefinitionNode { public interface Builder { public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode build(); 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 f3cb123..a9e5947 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -103,7 +103,9 @@ public class Parser { ) { var docComment = parseDocComment(tokens); - if (tokens.isNext(TokenType.KEYWORD_NATIVE)) { + if (tokens.isNext(TokenType.KEYWORD_ENUM)) { + return parseEnumDefinition(tokens, docComment); + } else if (tokens.isNext(TokenType.KEYWORD_NATIVE)) { return parseNativeTypeDefinition(tokens, docComment); } else if (tokens.isNext(TokenType.KEYWORD_STRUCT)) { return parseStructDefinition(tokens, docComment); @@ -114,6 +116,48 @@ public class Parser { } } + private static UntypedEnumDefinitionNode parseEnumDefinition( + TokenIterator tokens, + DocComment docComment + ) { + var start = tokens.startPosition(); + + tokens.skip(TokenType.KEYWORD_ENUM); + var name = tokens.next(TokenType.IDENTIFIER); + tokens.skip(TokenType.SYMBOL_BRACE_OPEN); + + var variants = parseMany( + () -> !tokens.isNext(TokenType.KEYWORD_VARIANT), + () -> parseEnumVariantDefinition(tokens), + () -> true + ); + + tokens.skip(TokenType.SYMBOL_BRACE_CLOSE); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedEnumDefinitionNode( + name.charSequence().toString(), + variants, + docComment, + source + ); + } + + private static UntypedEnumVariantDefinitionNode parseEnumVariantDefinition(TokenIterator tokens) { + var start = tokens.startPosition(); + + tokens.skip(TokenType.KEYWORD_VARIANT); + var name = parseIdentifier(tokens); + tokens.skip(TokenType.SYMBOL_SEMICOLON); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedEnumVariantDefinitionNode(name, source); + } + private static UntypedNativeTypeDefinitionNode parseNativeTypeDefinition( TokenIterator tokens, DocComment docComment 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 384ef19..f8fe696 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -69,6 +69,9 @@ public class TypeChecker { TypeCheckerNamespaceContext context ) { return switch (untyped) { + case UntypedEnumDefinitionNode untypedEnumDefinitionNode -> + throw new UnsupportedOperationException("TODO"); + case UntypedNativeTypeDefinitionNode untypedNativeTypeDefinition -> declareNativeTypeDefinition(untypedNativeTypeDefinition, context); @@ -85,6 +88,9 @@ public class TypeChecker { TypeCheckerNamespaceContext context ) { return switch (untyped) { + case UntypedEnumDefinitionNode untypedEnumDefinitionNode -> + throw new UnsupportedOperationException("TODO"); + case UntypedNativeTypeDefinitionNode untypedNativeTypeDefinition -> defineNativeTypeDefinition(untypedNativeTypeDefinition, context); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumDefinitionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumDefinitionNodeMatcher.java new file mode 100644 index 0000000..414c059 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumDefinitionNodeMatcher.java @@ -0,0 +1,46 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +public class UntypedEnumDefinitionNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher isUntypedEnumDefinitionNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode>> submatchers; + + private UntypedEnumDefinitionNodeMatcher(java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode>> submatchers) { + this.submatchers = submatchers; + } + + public org.zwobble.precisely.MatchResult match(java.lang.Object actual) { + return this.toMatcher().match(actual); + } + + public org.zwobble.precisely.TextTree describe() { + return this.toMatcher().describe(); + } + + private org.zwobble.precisely.Matcher<java.lang.Object> toMatcher() { + return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher withName(org.zwobble.precisely.Matcher<? super String> name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher(java.util.stream.Stream.concat(this.submatchers.stream(), java.util.stream.Stream.of(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode::name, name))).toList()); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher withVariants(org.zwobble.precisely.Matcher<? super java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode>> variants) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher(java.util.stream.Stream.concat(this.submatchers.stream(), java.util.stream.Stream.of(org.zwobble.precisely.Matchers.has("variants", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode::variants, variants))).toList()); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher withDocComment(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.DocComment> docComment) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher(java.util.stream.Stream.concat(this.submatchers.stream(), java.util.stream.Stream.of(org.zwobble.precisely.Matchers.has("docComment", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode::docComment, docComment))).toList()); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher(java.util.stream.Stream.concat(this.submatchers.stream(), java.util.stream.Stream.of(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode::source, source))).toList()); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumVariantDefinitionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumVariantDefinitionNodeMatcher.java new file mode 100644 index 0000000..4bc5a03 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedEnumVariantDefinitionNodeMatcher.java @@ -0,0 +1,38 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +public class UntypedEnumVariantDefinitionNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher isUntypedEnumVariantDefinitionNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode>> submatchers; + + private UntypedEnumVariantDefinitionNodeMatcher(java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode>> submatchers) { + this.submatchers = submatchers; + } + + public org.zwobble.precisely.MatchResult match(java.lang.Object actual) { + return this.toMatcher().match(actual); + } + + public org.zwobble.precisely.TextTree describe() { + return this.toMatcher().describe(); + } + + private org.zwobble.precisely.Matcher<java.lang.Object> toMatcher() { + return org.zwobble.precisely.Matchers.instanceOf(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher withName(org.zwobble.precisely.Matcher<? super String> name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher(java.util.stream.Stream.concat(this.submatchers.stream(), java.util.stream.Stream.of(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode::name, name))).toList()); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher(java.util.stream.Stream.concat(this.submatchers.stream(), java.util.stream.Stream.of(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNode::source, source))).toList()); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java new file mode 100644 index 0000000..925077f --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserEnumDefinitionTests.java @@ -0,0 +1,86 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; +import org.zwobble.hobgoblin.compiler.ast.DocComment; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumDefinitionNode; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNodeMatcher.isUntypedEnumDefinitionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumVariantDefinitionNodeMatcher.isUntypedEnumVariantDefinitionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStructFieldDefinitionNodeMatcher.isUntypedStructFieldDefinitionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedSumDefinitionNodeMatcher.isUntypedSumDefinitionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNodeMatcher.isUntypedTypeLevelReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.*; + +public class ParserEnumDefinitionTests { + @Test + public void nameIsParsed() { + var source = """ + enum SmallNumber { + }"""; + + var node = parseString( + source, + Parser::parseNamespaceStatement + ); + + assertThat(node, isUntypedEnumDefinitionNode() + .withName(equalTo("SmallNumber")) + ); + } + + @Test + public void emptyEnumHasNoVariants() { + var source = """ + enum SmallNumber { + }"""; + + var node = parseString( + source, + Parser::parseNamespaceStatement + ); + + assertThat(node, isUntypedEnumDefinitionNode() + .withVariants(isSequence()) + ); + } + + @Test + public void enumWithVariants() { + var source = """ + enum SmallNumber { + variant zero; + variant one; + }"""; + + var node = parseString( + source, + Parser::parseNamespaceStatement + ); + + assertThat(node, isUntypedEnumDefinitionNode() + .withVariants(isSequence( + isUntypedEnumVariantDefinitionNode().withName(equalTo("zero")), + isUntypedEnumVariantDefinitionNode().withName(equalTo("one")) + )) + ); + } + + @Test + public void enumWithDocComment() { + var source = """ + /// A small number. + enum SmallNumber { + }"""; + + var node = parseString( + source, + Parser::parseNamespaceStatement + ); + + assertThat(node, isUntypedEnumDefinitionNode() + .withDocComment(equalTo(new DocComment("A small number.\n"))) + ); + } +} |
