From 6f554d8e265ad2664c97d9d8d6efef2f53e9eb5f Mon Sep 17 00:00:00 2001 From: Michael Williamson Date: Sun, 12 Jan 2025 21:25:32 +0000 Subject: Parse native function declarations --- .../ast/untyped/UntypedNamespaceStatementNode.java | 2 +- .../UntypedNativeFunctionDeclarationNode.java | 104 +++++++++++++++++++++ .../java/org/shedlang/compiler/parser/Parser.java | 32 +++++++ .../org/shedlang/compiler/parser/TokenType.java | 1 + .../org/shedlang/compiler/parser/Tokeniser.java | 1 + .../compiler/typechecker/DeclaredVariables.java | 6 ++ .../shedlang/compiler/typechecker/TypeChecker.java | 7 ++ ...ntypedNativeFunctionDeclarationNodeMatcher.java | 52 +++++++++++ .../compiler/ast/untyped/UntypedNodeMatchers.java | 4 + .../ParserNativeFunctionDeclarationTests.java | 60 ++++++++++++ 10 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNode.java create mode 100644 src/test/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNodeMatcher.java create mode 100644 src/test/java/org/shedlang/compiler/parser/ParserNativeFunctionDeclarationTests.java (limited to 'src') diff --git a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNamespaceStatementNode.java b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNamespaceStatementNode.java index 8d94d5b8..c4981b68 100644 --- a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNamespaceStatementNode.java +++ b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNamespaceStatementNode.java @@ -1,4 +1,4 @@ package org.shedlang.compiler.ast.untyped; -public sealed interface UntypedNamespaceStatementNode extends UntypedNode permits UntypedAssignNode, UntypedEffectDefinitionNode, UntypedFunctionDefinitionNode, UntypedStructDefinitionNode, UntypedSumTypeDefinitionNode { +public sealed interface UntypedNamespaceStatementNode extends UntypedNode permits UntypedAssignNode, UntypedEffectDefinitionNode, UntypedFunctionDefinitionNode, UntypedNativeFunctionDeclarationNode, UntypedStructDefinitionNode, UntypedSumTypeDefinitionNode { } diff --git a/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNode.java b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNode.java new file mode 100644 index 00000000..989d6044 --- /dev/null +++ b/src/main/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNode.java @@ -0,0 +1,104 @@ +package org.shedlang.compiler.ast.untyped; + +import org.shedlang.compiler.sources.Source; +import org.shedlang.compiler.sources.Sources; +import org.shedlang.compiler.types.Visibility; + +import java.util.Optional; + +public record UntypedNativeFunctionDeclarationNode( + String name, + Visibility visibility, + UntypedFunctionSignatureNode signature, + Source source +) implements UntypedNamespaceStatementNode, UntypedVariableDeclarationNode { + public static Builder builder() { + return new Builder( + "f", + Visibility.PRIVATE, + UntypedFunctionSignatureNode.builder() + ); + } + + public record Builder( + String name, + Visibility visibility, + UntypedFunctionSignatureNode.Builder signature + ) { + public UntypedNativeFunctionDeclarationNode build() { + return new UntypedNativeFunctionDeclarationNode( + name, + visibility, + signature.build(), + Sources.NULL + ); + } + + public Builder withName(String name) { + return new Builder( + name, + this.visibility, + this.signature + ); + } + + public Builder addPositionalParam(String name, UntypedTypeLevelExpressionNode type) { + return addPositionalParam(name, Optional.of(type)); + } + + public Builder addPositionalParam( + String name, + Optional type + ) { + return new Builder( + this.name, + this.visibility, + this.signature.addPositionalParam(name, type) + ); + } + + public Builder addNamedParam(String name, UntypedTypeLevelExpressionNode type) { + return new Builder( + this.name, + this.visibility, + this.signature.addNamedParam(name, type) + ); + } + + public Builder withEffect(UntypedTypeLevelExpressionNode effect) { + return new Builder( + this.name, + this.visibility, + this.signature.withEffect(effect) + ); + } + + public Builder withReturnTypeUnit() { + return withReturnType(new UntypedTypeLevelReferenceNode("Unit", Sources.NULL)); + } + + public Builder withReturnType(UntypedTypeLevelExpressionNode returnType) { + return new Builder( + this.name, + this.visibility, + this.signature.withReturnType(returnType) + ); + } + + public Builder withoutReturnType() { + return new Builder( + this.name, + this.visibility, + this.signature.withoutReturnType() + ); + } + + public Builder withVisibility(Visibility visibility) { + return new Builder( + this.name, + visibility, + this.signature + ); + } + } +} diff --git a/src/main/java/org/shedlang/compiler/parser/Parser.java b/src/main/java/org/shedlang/compiler/parser/Parser.java index cda360e3..45533683 100644 --- a/src/main/java/org/shedlang/compiler/parser/Parser.java +++ b/src/main/java/org/shedlang/compiler/parser/Parser.java @@ -776,6 +776,9 @@ public class Parser { case KEYWORD_FUN -> parseFunctionDefinition(tokens); + case KEYWORD_NATIVE -> + parseNativeFunctionDeclaration(tokens); + case KEYWORD_PUB -> switch (tokens.peek(1).tokenType()) { case KEYWORD_EFFECT -> @@ -784,6 +787,9 @@ public class Parser { case KEYWORD_FUN -> parseFunctionDefinition(tokens); + case KEYWORD_NATIVE -> + parseNativeFunctionDeclaration(tokens); + case KEYWORD_STRUCT -> parseStructDefinition(tokens); @@ -811,6 +817,32 @@ public class Parser { }; } + private static UntypedNativeFunctionDeclarationNode parseNativeFunctionDeclaration( + TokenIterator tokens + ) { + var start = tokens.startPosition(); + + var visibility = parseVisibility(tokens); + + tokens.skip(TokenType.KEYWORD_NATIVE); + tokens.skip(TokenType.KEYWORD_FUN); + var name = tokens.nextValue(TokenType.IDENTIFIER); + + var signature = parseFunctionSignature(tokens); + + tokens.skip(TokenType.SYMBOL_SEMICOLON); + + var end = tokens.endPosition(); + var source = new FileFragmentSource(start.to(end)); + + return new UntypedNativeFunctionDeclarationNode( + name, + visibility, + signature, + source + ); + } + private static UntypedParamsNode parseParams(TokenIterator tokens) { var paramsStart = tokens.startPosition(); diff --git a/src/main/java/org/shedlang/compiler/parser/TokenType.java b/src/main/java/org/shedlang/compiler/parser/TokenType.java index dbe32b0d..5eee208c 100644 --- a/src/main/java/org/shedlang/compiler/parser/TokenType.java +++ b/src/main/java/org/shedlang/compiler/parser/TokenType.java @@ -21,6 +21,7 @@ public enum TokenType { KEYWORD_IF, KEYWORD_IMPORT, KEYWORD_IS, + KEYWORD_NATIVE, KEYWORD_PUB, KEYWORD_RESUME, KEYWORD_RETURN, diff --git a/src/main/java/org/shedlang/compiler/parser/Tokeniser.java b/src/main/java/org/shedlang/compiler/parser/Tokeniser.java index dfac78a0..6e5bed3f 100644 --- a/src/main/java/org/shedlang/compiler/parser/Tokeniser.java +++ b/src/main/java/org/shedlang/compiler/parser/Tokeniser.java @@ -25,6 +25,7 @@ public class Tokeniser { Map.entry("if", TokenType.KEYWORD_IF), Map.entry("import", TokenType.KEYWORD_IMPORT), Map.entry("is", TokenType.KEYWORD_IS), + Map.entry("native", TokenType.KEYWORD_NATIVE), Map.entry("pub", TokenType.KEYWORD_PUB), Map.entry("resume", TokenType.KEYWORD_RESUME), Map.entry("return", TokenType.KEYWORD_RETURN), diff --git a/src/main/java/org/shedlang/compiler/typechecker/DeclaredVariables.java b/src/main/java/org/shedlang/compiler/typechecker/DeclaredVariables.java index 2a842d93..a2a8b253 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/DeclaredVariables.java +++ b/src/main/java/org/shedlang/compiler/typechecker/DeclaredVariables.java @@ -104,6 +104,12 @@ class DeclaredVariables { functionDefinition )); + case UntypedNativeFunctionDeclarationNode nativeFunctionDeclaration -> + variables.plus(DeclaredVariable.fromNode( + VariableType.CONSTANT, + nativeFunctionDeclaration + )); + case UntypedStructDefinitionNode struct -> variables.plus(DeclaredVariable.fromNode( VariableType.CONSTANT, diff --git a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java index daee11c4..4486e457 100644 --- a/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/shedlang/compiler/typechecker/TypeChecker.java @@ -1484,6 +1484,10 @@ public class TypeChecker { ); } + case UntypedNativeFunctionDeclarationNode untypedNativeFunctionDeclaration -> { + throw new UnsupportedOperationException("TODO"); + } + case UntypedStructDefinitionNode untypedStruct -> { dependencyChecker = dependencyChecker.addDeferredDependencies( untypedStruct.name(), @@ -1556,6 +1560,9 @@ public class TypeChecker { case UntypedFunctionDefinitionNode functionDefinition -> typeCheckStepsFunctionDefinition(functionDefinition); + case UntypedNativeFunctionDeclarationNode nativeFunctionDeclaration -> + throw new UnsupportedOperationException("TODO"); + case UntypedStructDefinitionNode structDefinition -> typeCheckStepsStructDefinition(structDefinition); diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNodeMatcher.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNodeMatcher.java new file mode 100644 index 00000000..9eb338fd --- /dev/null +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNativeFunctionDeclarationNodeMatcher.java @@ -0,0 +1,52 @@ +package org.shedlang.compiler.ast.untyped; + +import org.pcollections.Empty; +import org.pcollections.PVector; +import org.shedlang.compiler.testing.MatcherBuilder; +import org.shedlang.compiler.types.Visibility; +import org.zwobble.precisely.Matcher; + +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.has; + +public class UntypedNativeFunctionDeclarationNodeMatcher extends MatcherBuilder< + UntypedNamespaceStatementNode, + UntypedNativeFunctionDeclarationNode, + UntypedNativeFunctionDeclarationNodeMatcher +> { + public static UntypedNativeFunctionDeclarationNodeMatcher empty() { + return new UntypedNativeFunctionDeclarationNodeMatcher(Empty.vector()); + } + + public UntypedNativeFunctionDeclarationNodeMatcher( + PVector> submatchers + ) { + super(submatchers); + } + + @Override + protected Class expectedType() { + return UntypedNativeFunctionDeclarationNode.class; + } + + @Override + protected UntypedNativeFunctionDeclarationNodeMatcher create( + PVector> submatchers + ) { + return new UntypedNativeFunctionDeclarationNodeMatcher(submatchers); + } + + public UntypedNativeFunctionDeclarationNodeMatcher withSignature( + Matcher signature + ) { + return with(has("signature", x -> x.signature(), signature)); + } + + public UntypedNativeFunctionDeclarationNodeMatcher withName(String name) { + return with(has("name", x -> x.name(), equalTo(name))); + } + + public UntypedNativeFunctionDeclarationNodeMatcher withVisibility(Visibility visibility) { + return with(has("visibility", x -> x.visibility(), equalTo(visibility))); + } +} diff --git a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java index 0e11231b..94fc68e1 100644 --- a/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java +++ b/src/test/java/org/shedlang/compiler/ast/untyped/UntypedNodeMatchers.java @@ -235,6 +235,10 @@ public class UntypedNodeMatchers { ); } + public static UntypedNativeFunctionDeclarationNodeMatcher isUntypedNativeFunctionDeclarationNode() { + return UntypedNativeFunctionDeclarationNodeMatcher.empty(); + } + public static Matcher isUntypedNotEqualNode( Matcher left, Matcher right diff --git a/src/test/java/org/shedlang/compiler/parser/ParserNativeFunctionDeclarationTests.java b/src/test/java/org/shedlang/compiler/parser/ParserNativeFunctionDeclarationTests.java new file mode 100644 index 00000000..f11a673c --- /dev/null +++ b/src/test/java/org/shedlang/compiler/parser/ParserNativeFunctionDeclarationTests.java @@ -0,0 +1,60 @@ +package org.shedlang.compiler.parser; + +import org.junit.jupiter.api.Test; +import org.shedlang.compiler.types.Visibility; + +import static org.shedlang.compiler.ast.untyped.UntypedNodeMatchers.*; +import static org.shedlang.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.isOptionalEmpty; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserNativeFunctionDeclarationTests { + @Test + public void canParseMinimalFunction() { + var source = """ + native fun f() -> Unit;"""; + + var node = parseString(source, Parser::parseNamespaceStatement); + + assertThat( + node, + isUntypedNativeFunctionDeclarationNode() + .withName("f") + .withSignature( + isUntypedFunctionSignatureNode() + .withParams(isUntypedParamsNode(isSequence(), isSequence())) + .withEffect(isOptionalEmpty()) + .withReturnType(isUntypedTypeLevelReferenceNode("Unit")) + ) + ); + } + + @Test + public void defaultVisibilityIsPrivate() { + var source = """ + native fun f() -> Unit;"""; + + var node = parseString(source, Parser::parseNamespaceStatement); + + assertThat( + node, + isUntypedNativeFunctionDeclarationNode() + .withVisibility(Visibility.PRIVATE) + ); + } + + @Test + public void whenFunIsPrefixedWithPubThenVisibilityIsPublic() { + var source = """ + pub native fun f() -> Unit;"""; + + var node = parseString(source, Parser::parseNamespaceStatement); + + assertThat( + node, + isUntypedNativeFunctionDeclarationNode() + .withVisibility(Visibility.PUBLIC) + ); + } +} -- cgit v1.2.3