diff options
Diffstat (limited to 'src')
68 files changed, 2082 insertions, 70 deletions
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCli.java b/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCli.java index 7e2dcee..9a1e054 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCli.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCli.java @@ -3,6 +3,7 @@ package org.zwobble.hobgoblin.compiler; import net.sourceforge.argparse4j.ArgumentParsers; import net.sourceforge.argparse4j.inf.ArgumentParserException; import net.sourceforge.argparse4j.inf.Namespace; +import net.sourceforge.argparse4j.inf.Subparser; import org.zwobble.hobgoblin.compiler.errors.SourceError; import org.zwobble.hobgoblin.compiler.sources.NullSource; import org.zwobble.hobgoblin.compiler.util.argparse4j.PathArgumentType; @@ -31,6 +32,9 @@ public class HobgoblinCli { case COMPILE -> { HobgoblinCompiler.compile(sourcePath); } + case STRIP -> { + HobgoblinCompiler.strip(sourcePath); + } } } @@ -40,12 +44,13 @@ public class HobgoblinCli { var subparsers = parser.addSubparsers(); - var compileSubParser = subparsers.addParser("compile"); - compileSubParser.setDefault("command", Command.COMPILE); - compileSubParser - .addArgument("source") - .type(new PathArgumentType()) - .required(true); + var compileSubparser = subparsers.addParser("compile"); + compileSubparser.setDefault("command", Command.COMPILE); + addSourceArgument(compileSubparser); + + var stripSubparser = subparsers.addParser("strip"); + stripSubparser.setDefault("command", Command.STRIP); + addSourceArgument(stripSubparser); try { return parser.parseArgs(args); @@ -56,7 +61,15 @@ public class HobgoblinCli { } } + private static void addSourceArgument(Subparser subparser) { + subparser + .addArgument("source") + .type(new PathArgumentType()) + .required(true); + } + private enum Command { COMPILE, + STRIP, } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java b/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java index 0bd9192..1a34f2b 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/HobgoblinCompiler.java @@ -3,6 +3,7 @@ package org.zwobble.hobgoblin.compiler; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceNode; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.config.ProjectConfig; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.parser.Parser; import org.zwobble.hobgoblin.compiler.typechecker.TypeChecker; import org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerGlobalContext; @@ -17,6 +18,7 @@ import java.nio.file.Path; import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; public class HobgoblinCompiler { @@ -63,6 +65,23 @@ public class HobgoblinCompiler { } } + public static void strip(Path projectRoot) throws IOException { + var config = ProjectConfig.read(projectRoot); + var generators = config.createGenerators(); + + // Multiple generators may write the same language to the same + // directory, so we de-duplicate strippers rather than asking the + // generator to strip directly. + var strippers = new HashSet<Stripper>(); + for (var generator : generators) { + strippers.addAll(generator.strippers()); + } + + for (var stripper : strippers) { + stripper.strip(); + } + } + static NamespaceName sourceFileNamespaceName(Path sourceDirectory, Path sourceFilePath) { var relativePath = sourceDirectory.relativize(sourceFilePath); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNamespaceStatementNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNamespaceStatementNode.java index 8e8bd77..a20416e 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNamespaceStatementNode.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedNamespaceStatementNode.java @@ -5,7 +5,7 @@ package org.zwobble.hobgoblin.compiler.ast.typed; // Custom area start: org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceStatementNode imports // Custom area end: org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceStatementNode imports -public sealed interface TypedNamespaceStatementNode permits org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedEnumDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedNativeTypeDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedStructDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedSumDefinitionNode { +public sealed interface TypedNamespaceStatementNode permits org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedEnumDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedNativeTypeDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedStructDefinitionNode, org.zwobble.hobgoblin.compiler.ast.typed.TypedSumDefinitionNode { public interface Builder { public org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceStatementNode build(); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedQueryDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedQueryDefinitionNode.java new file mode 100644 index 0000000..2c3f518 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedQueryDefinitionNode.java @@ -0,0 +1,50 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.typed; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode imports + +public record TypedQueryDefinitionNode( + String name, + org.zwobble.hobgoblin.compiler.types.Table table, + org.zwobble.hobgoblin.compiler.ast.DocComment docComment, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceStatementNode { + public static org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder("", org.zwobble.hobgoblin.compiler.types.HobgoblinNativeTypes.arbitraryTable(), org.zwobble.hobgoblin.compiler.ast.HobgoblinNativeAst.arbitraryDocComment(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + String name, + org.zwobble.hobgoblin.compiler.types.Table table, + org.zwobble.hobgoblin.compiler.ast.DocComment docComment, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceStatementNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode build() { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode(name, table, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder(name, table, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder withTable(org.zwobble.hobgoblin.compiler.types.Table table) { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder(name, table, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder withDocComment(org.zwobble.hobgoblin.compiler.ast.DocComment docComment) { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder(name, table, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder(name, table, docComment, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNode.java new file mode 100644 index 0000000..2c19adb --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNode.java @@ -0,0 +1,60 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode imports + +public record UntypedCallNode( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver, + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> args, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), java.util.List.of(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver, + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> args, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder receiver) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver.build(), args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withArgs(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> args) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder addArg(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode arg) { + var args = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode>(this.args); + args.add(arg); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder addArg(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder arg) { + var args = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode>(this.args); + args.add(arg.build()); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder(receiver, args, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java new file mode 100644 index 0000000..a5c54a0 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedExpressionNode.java @@ -0,0 +1,18 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode imports + +public sealed interface UntypedExpressionNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode { + public interface Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode build(); + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java new file mode 100644 index 0000000..4e410f3 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNode.java @@ -0,0 +1,60 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode imports + +public record UntypedFunctionExpressionNode( + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode> params, + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(java.util.List.of(), org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode> params, + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withParams(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode> params) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder addParam(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode param) { + var params = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>(this.params); + params.add(param); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder addParam(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder param) { + var params = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>(this.params); + params.add(param.build()); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withBody(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode body) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withBody(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder body) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body.build(), source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder(params, body, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNode.java new file mode 100644 index 0000000..6a6e406 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNode.java @@ -0,0 +1,48 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode imports + +public record UntypedMemberAccessNode( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver, + String fieldName, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), "", org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver, + String fieldName, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode(receiver, fieldName, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode receiver) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver, fieldName, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withReceiver(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder receiver) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver.build(), fieldName, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withFieldName(String fieldName) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver, fieldName, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder(receiver, fieldName, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode 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 3f805bf..2e78f39 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 @@ -5,7 +5,7 @@ package org.zwobble.hobgoblin.compiler.ast.untyped; // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode imports // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNamespaceStatementNode imports -public sealed interface UntypedNamespaceStatementNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedDatabaseSchemaDefinitionNode, 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 sealed interface UntypedNamespaceStatementNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedDatabaseSchemaDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedEnumDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedNativeTypeDefinitionNode, org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode, 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/ast/untyped/UntypedParamNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNode.java new file mode 100644 index 0000000..068097f --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNode.java @@ -0,0 +1,38 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode imports + +public record UntypedParamNode( + String name, + org.zwobble.hobgoblin.compiler.sources.Source source +) { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder("", org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + String name, + org.zwobble.hobgoblin.compiler.sources.Source source + ) { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder(name, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedQueryDefinitionNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedQueryDefinitionNode.java new file mode 100644 index 0000000..ee1ddbc --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedQueryDefinitionNode.java @@ -0,0 +1,72 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode imports + +public record UntypedQueryDefinitionNode( + String name, + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode databaseSchema, + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode> statements, + 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.UntypedQueryDefinitionNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder("", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode.arbitrary().build(), java.util.List.of(), org.zwobble.hobgoblin.compiler.ast.HobgoblinNativeAst.arbitraryDocComment(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + String name, + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode databaseSchema, + java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode> statements, + 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.UntypedQueryDefinitionNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder withDatabaseSchema(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode databaseSchema) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder withDatabaseSchema(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode.Builder databaseSchema) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema.build(), statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder withStatements(java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode> statements) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder addStatement(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode statement) { + var statements = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode>(this.statements); + statements.add(statement); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder addStatement(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode.Builder statement) { + var statements = new java.util.ArrayList<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode>(this.statements); + statements.add(statement.build()); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder withDocComment(org.zwobble.hobgoblin.compiler.ast.DocComment docComment) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder(name, databaseSchema, statements, docComment, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReferenceNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReferenceNode.java new file mode 100644 index 0000000..edd3066 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReferenceNode.java @@ -0,0 +1,38 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode imports + +public record UntypedReferenceNode( + String name, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder("", org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + String name, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder withName(String name) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder(name, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder(name, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReturnNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReturnNode.java new file mode 100644 index 0000000..f115b24 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReturnNode.java @@ -0,0 +1,42 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode imports + +public record UntypedReturnNode( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode expression, + org.zwobble.hobgoblin.compiler.sources.Source source +) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder arbitrary() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode.arbitrary().build(), org.zwobble.hobgoblin.compiler.sources.HobgoblinNativeSources.arbitrarySource()); + } + + public record Builder( + org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode expression, + org.zwobble.hobgoblin.compiler.sources.Source source + ) implements org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode.Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode build() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode(expression, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder withExpression(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode expression) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder(expression, source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder withExpression(org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode.Builder expression) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder(expression.build(), source); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder withSource(org.zwobble.hobgoblin.compiler.sources.Source source) { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder(expression, source); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStatementNode.java b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStatementNode.java new file mode 100644 index 0000000..8121f93 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedStatementNode.java @@ -0,0 +1,18 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode imports + +public sealed interface UntypedStatementNode permits org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode { + public interface Builder { + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode build(); + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode.Builder body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode.Builder body + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode body +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/CodeWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/CodeWriter.java index 81fd091..551683e 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/CodeWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/CodeWriter.java @@ -1,5 +1,7 @@ package org.zwobble.hobgoblin.compiler.output; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; + import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; @@ -10,6 +12,30 @@ import java.nio.file.Path; import java.util.*; public class CodeWriter implements AutoCloseable { + public static class CodeWriterConfig { + private final String header; + private final String customAreaStart; + private final String customAreaEnd; + + public CodeWriterConfig(String lineCommentStart) { + this.header = lineCommentStart + " Generated by hobgoblin.\n\n"; + this.customAreaStart = lineCommentStart + " Custom area start: "; + this.customAreaEnd = lineCommentStart + " Custom area end: "; + } + + public String header() { + return this.header; + } + + public String customAreaStart() { + return customAreaStart; + } + + public String customAreaEnd() { + return customAreaEnd; + } + } + public interface CodeWriterFile { String readString() throws IOException; Writer openWriter() throws IOException; @@ -50,26 +76,26 @@ public class CodeWriter implements AutoCloseable { } public record CustomAreas( - Map<String, List<String>> existing, - String customAreaStart, - String customAreaEnd + SequencedMap<String, List<String>> existing, + CodeWriterConfig config ) { public static CustomAreas read( String existingString, - String customAreaStart, - String customAreaEnd + CodeWriterConfig config ) { - var existing = new HashMap<String, List<String>>(); + var existing = new LinkedHashMap<String, List<String>>(); Iterable<String> lines = () -> existingString.lines().iterator(); Optional<String> name = Optional.empty(); var customAreaLines = new ArrayList<String>(); for (var line : lines) { var trimmedLine = line.trim(); - if (trimmedLine.startsWith(customAreaStart)) { - name = Optional.of(trimmedLine.substring(customAreaStart.length())); - } else if (name.isPresent() && trimmedLine.startsWith(customAreaEnd + name.get())) { - existing.put(name.get(), customAreaLines); + if (trimmedLine.startsWith(config.customAreaStart)) { + name = Optional.of(trimmedLine.substring(config.customAreaStart.length())); + } else if (name.isPresent() && trimmedLine.startsWith(config.customAreaEnd + name.get())) { + if (!customAreaLines.isEmpty()) { + existing.put(name.get(), customAreaLines); + } customAreaLines = new ArrayList<>(); name = Optional.empty(); } else if (name.isPresent()) { @@ -77,14 +103,19 @@ public class CodeWriter implements AutoCloseable { } } - return new CustomAreas(existing, customAreaStart, customAreaEnd); + return new CustomAreas(existing, config); } - public static CustomAreas empty( - String customAreaStart, - String customAreaEnd - ) { - return new CustomAreas(Map.of(), customAreaStart, customAreaEnd); + public static CustomAreas empty(CodeWriterConfig config) { + return new CustomAreas(new LinkedHashMap<>(), config); + } + + public String customAreaStart() { + return this.config.customAreaStart; + } + + public String customAreaEnd() { + return this.config.customAreaEnd; } public Iterable<String> lines(String name) { @@ -103,7 +134,7 @@ public class CodeWriter implements AutoCloseable { public static void writeTo( Path path, - String lineCommentStart, + CodeWriterConfig config, Write write ) throws IOException { writeTo( @@ -119,43 +150,104 @@ public class CodeWriter implements AutoCloseable { return new FileWriter(path.toFile()); } }, - lineCommentStart, + config, write ); } public static void writeTo( CodeWriterFile file, - String lineCommentStart, + CodeWriterConfig config, Write write ) throws IOException { var originalContents = readFile(file); - var header = lineCommentStart + " Generated by hobgoblin.\n\n"; - if (originalContents.isPresent() && !originalContents.get().startsWith(header)) { + if (originalContents.isPresent() && !isGeneratedFile(originalContents.get(), config)) { return; } - var customAreaStart = lineCommentStart + " Custom area start: "; - var customAreaEnd = lineCommentStart + " Custom area end: "; - - var customAreas = readCustomAreas(file, customAreaStart, customAreaEnd); + var customAreas = readCustomAreas(file, config); try (var writer = file.openWriter()) { // TODO: warn if custom areas are unused? - write.write(new CodeWriter(writer, header, customAreas)); + write.write(new CodeWriter(writer, config.header, customAreas)); + } + } + + private static boolean isGeneratedFile(String contents, CodeWriterConfig config) { + return contents.startsWith(config.header); + } + + public static Stripper stripper( + Path outputPath, + CodeWriterConfig config + ) { + return new Stripper(outputPath, config); + } + + public static void strip(Path outputPath, CodeWriterConfig config) throws IOException { + try (var filesStream = Files.walk(outputPath)) { + var filesIterator = filesStream.iterator(); + while (filesIterator.hasNext()) { + var path = filesIterator.next(); + if (path.toFile().isFile()) { + // TODO: filter by extension? + // TODO: don't read entire string if not necessary. + var contents = Files.readString(path); + switch (stripFileContents(contents, config)) { + case StripResult.Delete _ -> { + Files.delete(path); + } + case StripResult.NewContent newContent -> { + Files.writeString(path, newContent.newContents); + } + case StripResult.Unchanged _ -> { + } + } + } + } + } + } + + sealed interface StripResult { + record Unchanged() implements StripResult {} + record Delete() implements StripResult {} + record NewContent(String newContents) implements StripResult {} + } + + static StripResult stripFileContents(String contents, CodeWriterConfig config) { + if (!isGeneratedFile(contents, config)) { + return new StripResult.Unchanged(); + } + + var stringWriter = new StringWriter(); + var customAreas = CustomAreas.read(contents, config); + if (customAreas.existing.isEmpty()) { + return new StripResult.Delete(); + } + + var writer = new CodeWriter(stringWriter, config.header, customAreas); + try { + writer.writeHeader(); + for (var customAreaName : customAreas.existing.keySet()) { + writer.writeCustomArea(customAreaName); + } + return new StripResult.NewContent(stringWriter.toString()); + } catch (IOException exception) { + // In theory, writing to a StringWriter will always succeed, so + // we don't need to handle this especially well. + throw new RuntimeException(exception); } } private static CustomAreas readCustomAreas( CodeWriterFile file, - String customAreaStart, - String customAreaEnd + CodeWriterConfig config ) throws IOException { try { var contents = file.readString(); - return CustomAreas.read(contents, customAreaStart, customAreaEnd); + return CustomAreas.read(contents, config); } catch (NoSuchFileException exception) { - return CustomAreas.empty(customAreaStart, customAreaEnd); + return CustomAreas.empty(config); } } @@ -210,7 +302,7 @@ public class CodeWriter implements AutoCloseable { } public void writeCustomArea(String name) throws IOException { - this.write(this.customAreas.customAreaStart); + this.write(this.customAreas.customAreaStart()); this.write(name); for (var line : this.customAreas.lines(name)) { @@ -219,7 +311,7 @@ public class CodeWriter implements AutoCloseable { } this.newLine(); - this.write(this.customAreas.customAreaEnd); + this.write(this.customAreas.customAreaEnd()); this.write(name); this.newLine(); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generator.java index 4a73000..dd8f46d 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generator.java @@ -8,4 +8,5 @@ import java.util.List; public interface Generator { void generate(List<TypedNamespaceNode> namespaces, TypesInfo typesInfo) throws IOException; + List<Stripper> strippers(); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Stripper.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Stripper.java new file mode 100644 index 0000000..27241eb --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Stripper.java @@ -0,0 +1,12 @@ +package org.zwobble.hobgoblin.compiler.output.generators; + +import org.zwobble.hobgoblin.compiler.output.CodeWriter; + +import java.io.IOException; +import java.nio.file.Path; + +public record Stripper(Path path, CodeWriter.CodeWriterConfig codeWriterConfig) { + public void strip() throws IOException { + CodeWriter.strip(path, codeWriterConfig); + } +} 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 61fc237..f202f61 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 @@ -1,6 +1,7 @@ package org.zwobble.hobgoblin.compiler.output.generators.java; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.java.JavaWriter; import org.zwobble.hobgoblin.compiler.output.lang.java.ast.*; import org.zwobble.hobgoblin.compiler.types.*; @@ -40,6 +41,10 @@ public class JavaGenerator { } } + public List<Stripper> strippers(Path sourceRootDirectory) { + return List.of(JavaWriter.stripper(sourceRootDirectory)); + } + public JavaPackageName namespaceToJavaPackageName(NamespaceName namespaceName) { var ancestorNamespaceName = namespaceName; var remainingNamespaceNameParts = new ArrayList<String>(); 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 1ae368e..9b751d0 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 @@ -5,6 +5,7 @@ import org.zwobble.hobgoblin.compiler.ast.typed.*; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGenerator; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.lang.java.ast.*; @@ -80,6 +81,11 @@ public class JavaPreciselyMatchersGenerator implements Generator { } } + @Override + public List<Stripper> strippers() { + return this.javaGenerator.strippers(this.sourceRootDirectory); + } + private List<JavaCompilationUnit> generateNamespace(TypedNamespaceNode namespace, Context context) { var javaCompilationUnits = new ArrayList<JavaCompilationUnit>(); @@ -94,6 +100,9 @@ public class JavaPreciselyMatchersGenerator implements Generator { case TypedNativeTypeDefinitionNode _ -> { } + case TypedQueryDefinitionNode _ -> { + } + case TypedStructDefinitionNode structDefinition -> { var javaCompilationUnit = new JavaCompilationUnit( generateStructDefinition(structDefinition, context) 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 38bf9b6..0220ba9 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 @@ -6,6 +6,7 @@ import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGenerator; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.generators.transient0.Transient0TypeLevelValuesUnsupported; @@ -96,6 +97,11 @@ public class JavaTransient0Generator implements Generator { } } + @Override + public List<Stripper> strippers() { + return this.javaGenerator.strippers(this.sourceRootDirectory); + } + private void write(List<JavaCompilationUnit> javaCompilationUnits) throws IOException { JavaGenerator.write(this.sourceRootDirectory, javaCompilationUnits); } @@ -662,6 +668,10 @@ public class JavaTransient0Generator implements Generator { yield List.of(); } + case TypedQueryDefinitionNode _ -> { + yield List.of(); + } + case TypedStructDefinitionNode structDefinition -> { yield List.of( generateEncodeStructMethod(structDefinition, context), 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 e3f54cf..17065e0 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 @@ -7,6 +7,7 @@ import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGenerator; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.generators.java.JavaNativeTypeConfig; @@ -90,6 +91,11 @@ public class JavaTypesGenerator implements Generator { } } + @Override + public List<Stripper> strippers() { + return this.javaGenerator.strippers(this.sourceRootDirectory); + } + private List<JavaCompilationUnit> generateNamespace(TypedNamespaceNode namespace, Context context) { var javaCompilationUnits = new ArrayList<JavaCompilationUnit>(); @@ -109,6 +115,9 @@ public class JavaTypesGenerator implements Generator { case TypedNativeTypeDefinitionNode nativeTypeDefinition -> { } + case TypedQueryDefinitionNode _ -> { + } + case TypedStructDefinitionNode structDefinition -> { var javaCompilationUnit = new JavaCompilationUnit( generateStructDefinition(structDefinition, context) diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java index fae9066..1fd342f 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/rust/RustGenerator.java @@ -1,5 +1,6 @@ package org.zwobble.hobgoblin.compiler.output.generators.rust; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.rust.RustWriter; import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.*; import org.zwobble.hobgoblin.compiler.types.*; @@ -32,6 +33,10 @@ public class RustGenerator { }); } + public List<Stripper> strippers() { + return List.of(RustWriter.stripper(this.outputPath)); + } + public RustPath generateRustTypeExpression(Type type) { return switch (type) { case ConstructedType constructedType -> { 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 8ecc7cc..8f2f4ad 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 @@ -5,6 +5,7 @@ import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.rust.RustGenerator; import org.zwobble.hobgoblin.compiler.output.generators.rust.RustGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.generators.transient0.Transient0TypeLevelValuesUnsupported; @@ -90,6 +91,11 @@ public class RustTransient0Generator implements Generator { } } + @Override + public List<Stripper> strippers() { + return this.rustGenerator.strippers(); + } + private void generateBuiltins() throws IOException { var rustModuleName = builtinsModuleName(); var rustModule = new RustModule( @@ -417,6 +423,10 @@ public class RustTransient0Generator implements Generator { yield Stream.of(); } + case TypedQueryDefinitionNode _ -> { + yield Stream.of(); + } + case TypedStructDefinitionNode structDefinition -> { yield Stream.of( generateEncodeStructFunction(structDefinition), 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 f01e143..f57ec33 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 @@ -4,6 +4,7 @@ import org.zwobble.hobgoblin.compiler.ast.typed.*; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.rust.RustGenerator; import org.zwobble.hobgoblin.compiler.output.generators.rust.RustGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.*; @@ -76,6 +77,11 @@ public class RustTypesGenerator implements Generator { } } + @Override + public List<Stripper> strippers() { + return this.rustGenerator.strippers(); + } + private RustModule generateNamespace(TypedNamespaceNode namespace, TypesInfo typesInfo) { var rustModuleName = this.rustGenerator.namespaceNameToRustCrateModulePath(namespace.namespaceName()); @@ -103,6 +109,10 @@ public class RustTypesGenerator implements Generator { yield List.of(); } + case TypedQueryDefinitionNode _ -> { + yield List.of(); + } + case TypedStructDefinitionNode structDefinition -> { yield generateStructDefinition(structDefinition); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java index 60daf40..5089271 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java @@ -7,6 +7,7 @@ import org.zwobble.hobgoblin.compiler.ast.typed.TypedTableDefinitionNode; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.sql.SqlGenerator; import org.zwobble.hobgoblin.compiler.output.lang.sql.SqlWriter; import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.SqlScript; @@ -80,6 +81,11 @@ public class SqlScriptsGenerator implements Generator { } } + @Override + public List<Stripper> strippers() { + return List.of(SqlWriter.stripper(this.outputPath)); + } + private void generateDatabaseSchemaDefinition( TypedDatabaseSchemaDefinitionNode databaseSchemaDefinition, NamespaceName namespaceName, diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java index 68d1610..a16d1f3 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescript/TypescriptGenerator.java @@ -1,6 +1,7 @@ package org.zwobble.hobgoblin.compiler.output.generators.typescript; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.typescript.TypescriptTypes; import org.zwobble.hobgoblin.compiler.output.lang.typescript.TypescriptWriter; import org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.*; @@ -33,6 +34,10 @@ public class TypescriptGenerator { TypescriptWriter.writeTo(path, writer -> writer.writeModule(module)); } + public List<Stripper> strippers() { + return List.of(TypescriptWriter.stripper(this.outputPath)); + } + public record RequiredType(SimpleType type, TypescriptIdentifier importName) {} public static class Context { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java index 6dd8592..cbdf016 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/typescripttypes/TypescriptTypesGenerator.java @@ -4,6 +4,7 @@ import org.zwobble.hobgoblin.compiler.ast.typed.*; import org.zwobble.hobgoblin.compiler.config.OutputConfig; import org.zwobble.hobgoblin.compiler.output.generators.Generator; import org.zwobble.hobgoblin.compiler.output.generators.GeneratorDefinition; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.generators.typescript.TypescriptGenerator; import org.zwobble.hobgoblin.compiler.output.generators.typescript.TypescriptGeneratorConfig; import org.zwobble.hobgoblin.compiler.output.lang.typescript.TypescriptTypes; @@ -78,6 +79,11 @@ public class TypescriptTypesGenerator implements Generator { } } + @Override + public List<Stripper> strippers() { + return this.typescriptGenerator.strippers(); + } + private TypescriptModule generateNamespace( TypedNamespaceNode namespace, TypesInfo typesInfo @@ -148,6 +154,10 @@ public class TypescriptTypesGenerator implements Generator { return List.of(); } + case TypedQueryDefinitionNode _ -> { + return List.of(); + } + case TypedStructDefinitionNode structDefinition -> { return generateStructDefinition(structDefinition, context); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java index d2f46ad..2d17085 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/java/JavaWriter.java @@ -2,6 +2,7 @@ package org.zwobble.hobgoblin.compiler.output.lang.java; import org.zwobble.hobgoblin.compiler.ast.DocComment; import org.zwobble.hobgoblin.compiler.output.CodeWriter; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.java.ast.*; import java.io.IOException; @@ -14,6 +15,8 @@ import static org.zwobble.hobgoblin.compiler.output.CodeWriter.writeWithSeparato public class JavaWriter implements AutoCloseable { private static final String LINE_COMMENT_START = "//"; + private static final CodeWriter.CodeWriterConfig CONFIG = + new CodeWriter.CodeWriterConfig(LINE_COMMENT_START); public interface Write { void write(JavaWriter writer) throws IOException; @@ -22,7 +25,7 @@ public class JavaWriter implements AutoCloseable { public static void writeTo(Path path, Write write) throws IOException { CodeWriter.writeTo( path, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new JavaWriter(writer)) ); } @@ -30,11 +33,15 @@ public class JavaWriter implements AutoCloseable { public static void writeTo(CodeWriter.CodeWriterFile file, Write write) throws IOException { CodeWriter.writeTo( file, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new JavaWriter(writer)) ); } + public static Stripper stripper(Path sourceRootDirectory) { + return CodeWriter.stripper(sourceRootDirectory, CONFIG); + } + private final CodeWriter writer; public JavaWriter(CodeWriter writer) { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java index df8aa8c..076e150 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/rust/RustWriter.java @@ -2,6 +2,7 @@ package org.zwobble.hobgoblin.compiler.output.lang.rust; import org.zwobble.hobgoblin.compiler.ast.DocComment; import org.zwobble.hobgoblin.compiler.output.CodeWriter; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.rust.ast.*; import java.io.IOException; @@ -14,6 +15,8 @@ import static org.zwobble.hobgoblin.compiler.output.CodeWriter.writeWithSeparato public class RustWriter implements AutoCloseable { private static final String LINE_COMMENT_START = "//"; + private static final CodeWriter.CodeWriterConfig CONFIG = + new CodeWriter.CodeWriterConfig(LINE_COMMENT_START); public interface Write { void write(RustWriter writer) throws IOException; @@ -22,7 +25,7 @@ public class RustWriter implements AutoCloseable { public static void writeTo(Path path, Write write) throws IOException { CodeWriter.writeTo( path, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new RustWriter(writer)) ); } @@ -30,11 +33,15 @@ public class RustWriter implements AutoCloseable { public static void writeTo(CodeWriter.CodeWriterFile file, Write write) throws IOException { CodeWriter.writeTo( file, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new RustWriter(writer)) ); } + public static Stripper stripper(Path outputPath) { + return CodeWriter.stripper(outputPath, CONFIG); + } + private final CodeWriter writer; public RustWriter(CodeWriter writer) { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/sql/SqlWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/sql/SqlWriter.java index b6edc1b..ef1562e 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/sql/SqlWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/sql/SqlWriter.java @@ -1,6 +1,7 @@ package org.zwobble.hobgoblin.compiler.output.lang.sql; import org.zwobble.hobgoblin.compiler.output.CodeWriter; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.*; import java.io.IOException; @@ -10,6 +11,8 @@ import static org.zwobble.hobgoblin.compiler.output.CodeWriter.writeWithSeparato public class SqlWriter { private static final String LINE_COMMENT_START = "--"; + private static final CodeWriter.CodeWriterConfig CONFIG = + new CodeWriter.CodeWriterConfig(LINE_COMMENT_START); public interface Write { void write(SqlWriter writer) throws IOException; @@ -18,7 +21,7 @@ public class SqlWriter { public static void writeTo(Path path, Write write) throws IOException { CodeWriter.writeTo( path, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new SqlWriter(writer)) ); } @@ -26,11 +29,15 @@ public class SqlWriter { public static void writeTo(CodeWriter.CodeWriterFile file, Write write) throws IOException { CodeWriter.writeTo( file, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new SqlWriter(writer)) ); } + public static Stripper stripper(Path outputPath) { + return CodeWriter.stripper(outputPath, CONFIG); + } + private final CodeWriter writer; public SqlWriter(CodeWriter writer) { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriter.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriter.java index 3d5d99f..72e9cf5 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriter.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/lang/typescript/TypescriptWriter.java @@ -2,6 +2,7 @@ package org.zwobble.hobgoblin.compiler.output.lang.typescript; import org.zwobble.hobgoblin.compiler.ast.DocComment; import org.zwobble.hobgoblin.compiler.output.CodeWriter; +import org.zwobble.hobgoblin.compiler.output.generators.Stripper; import org.zwobble.hobgoblin.compiler.output.lang.typescript.ast.*; import java.io.IOException; @@ -11,6 +12,8 @@ import static org.zwobble.hobgoblin.compiler.output.CodeWriter.writeWithSeparato public class TypescriptWriter implements AutoCloseable { private static final String LINE_COMMENT_START = "//"; + private static final CodeWriter.CodeWriterConfig CONFIG = + new CodeWriter.CodeWriterConfig(LINE_COMMENT_START); public interface Write { void write(TypescriptWriter writer) throws IOException; @@ -19,7 +22,7 @@ public class TypescriptWriter implements AutoCloseable { public static void writeTo(Path path, Write write) throws IOException { CodeWriter.writeTo( path, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new TypescriptWriter(writer)) ); } @@ -27,11 +30,15 @@ public class TypescriptWriter implements AutoCloseable { public static void writeTo(CodeWriter.CodeWriterFile file, Write write) throws IOException { CodeWriter.writeTo( file, - LINE_COMMENT_START, + CONFIG, writer -> write.write(new TypescriptWriter(writer)) ); } + public static Stripper stripper(Path outputPath) { + return CodeWriter.stripper(outputPath, CONFIG); + } + private final CodeWriter writer; public TypescriptWriter(CodeWriter writer) { diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java new file mode 100644 index 0000000..27c2740 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperationParser.java @@ -0,0 +1,15 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.zwobble.sourcetext.SourcePosition; + +import java.util.List; + +interface OperationParser<TOperatorPrecedence, TNode> { + TOperatorPrecedence precedence(); + List<TokenType> operatorTokenTypes(); + TNode parse( + SourcePosition start, + TNode left, + TokenIterator tokens + ); +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java new file mode 100644 index 0000000..b63ca86 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/OperatorPrecedence.java @@ -0,0 +1,5 @@ +package org.zwobble.hobgoblin.compiler.parser; + +enum OperatorPrecedence { + CALL, +} 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 3563d0a..cdeeedd 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Parser.java @@ -11,9 +11,12 @@ import org.zwobble.sourcetext.SourceText; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; public class Parser { private Parser() { @@ -113,6 +116,8 @@ public class Parser { return parseEnumDefinition(tokens, docComment); } else if (tokens.isNext(TokenType.KEYWORD_NATIVE)) { return parseNativeTypeDefinition(tokens, docComment); + } else if (tokens.isNext(TokenType.IDENTIFIER, "query")) { + return parseQueryDefinition(tokens, docComment); } else if (tokens.isNext(TokenType.KEYWORD_STRUCT)) { return parseStructDefinition(tokens, docComment); } else if (tokens.isNext(TokenType.KEYWORD_SUM)) { @@ -239,6 +244,43 @@ public class Parser { return new DocComment(docComment.toString()); } + private static UntypedQueryDefinitionNode parseQueryDefinition( + TokenIterator tokens, + DocComment docComment + ) { + var start = tokens.startPosition(); + + tokens.skip(TokenType.IDENTIFIER, "query"); + var name = parseIdentifier(tokens); + + tokens.skip(TokenType.SYMBOL_PAREN_OPEN); + tokens.skip(TokenType.SYMBOL_PAREN_CLOSE); + + tokens.skip(TokenType.IDENTIFIER, "on"); + var databaseSchema = parseTypeLevelExpression(tokens); + + tokens.skip(TokenType.SYMBOL_BRACE_OPEN); + + var statements = parseMany( + () -> tokens.isNext(TokenType.SYMBOL_BRACE_CLOSE), + () -> parseStatement(tokens), + () -> true + ); + + tokens.skip(TokenType.SYMBOL_BRACE_CLOSE); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedQueryDefinitionNode( + name, + databaseSchema, + statements, + docComment, + source + ); + } + private static UntypedStructDefinitionNode parseStructDefinition( TokenIterator tokens, DocComment docComment @@ -415,6 +457,19 @@ public class Parser { return Optional.of(typeParams); } + // == Params == + + private static UntypedParamNode parseParam(TokenIterator tokens) { + var start = tokens.startPosition(); + + var name = parseIdentifier(tokens); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedParamNode(name, source); + } + // == Type-level expressions == static UntypedTypeLevelExpressionNode parseTypeLevelExpression( @@ -455,6 +510,176 @@ public class Parser { ); } + // == Statements == + + static UntypedStatementNode parseStatement(TokenIterator tokens) { + if (tokens.isNext(TokenType.IDENTIFIER, "return")) { + return parseReturn(tokens); + } else { + throw new UnsupportedOperationException("TODO"); + } + } + + private static UntypedReturnNode parseReturn(TokenIterator tokens) { + var start = tokens.startPosition(); + + tokens.skip(TokenType.IDENTIFIER, "return"); + var expression = parseTopLevelExpression(tokens); + tokens.skip(TokenType.SYMBOL_SEMICOLON); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedReturnNode(expression, source); + } + + // == Expressions == + + static UntypedExpressionNode parseTopLevelExpression(TokenIterator tokens) { + return parseExpression(tokens, Optional.empty()); + } + + private static UntypedExpressionNode parseExpression( + TokenIterator tokens, + Optional<OperatorPrecedence> parentPrecedence + ) { + var start = tokens.startPosition(); + var left = parsePrimaryExpression(tokens); + + while (true) { + var operationParser = OPERATION_PARSERS.get(tokens.peek().tokenType()); + if (operationParser == null || parentPrecedence.isPresent() && operationParser.precedence().ordinal() <= parentPrecedence.get().ordinal()) { + return left; + } + left = operationParser.parse(start, left, tokens); + } + } + + private static UntypedExpressionNode parsePrimaryExpression( + TokenIterator tokens + ) { + if (tokens.isNext(TokenType.SYMBOL_DOLLAR)) { + return parseFunctionExpression(tokens); + } else if (tokens.isNext(TokenType.IDENTIFIER)) { + return parseReference(tokens); + } else { + throw new UnsupportedOperationException("TODO"); + } + } + + private static UntypedFunctionExpressionNode parseFunctionExpression(TokenIterator tokens) { + var start = tokens.startPosition(); + + tokens.skip(TokenType.SYMBOL_DOLLAR); + tokens.skip(TokenType.SYMBOL_PAREN_OPEN); + + var params = parseMany( + () -> tokens.isNext(TokenType.SYMBOL_PAREN_CLOSE), + () -> parseParam(tokens), + () -> tokens.trySkip(TokenType.SYMBOL_COMMA) + ); + + tokens.skip(TokenType.SYMBOL_PAREN_CLOSE); + tokens.skip(TokenType.SYMBOL_FAT_ARROW); + + var body = parseTopLevelExpression(tokens); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedFunctionExpressionNode( + params, + body, + source + ); + } + + private static UntypedReferenceNode parseReference(TokenIterator tokens) { + var start = tokens.startPosition(); + + var name = parseIdentifier(tokens); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedReferenceNode(name, source); + } + + private static final OperationParser<OperatorPrecedence, UntypedExpressionNode> CALL_PARSER = new OperationParser<>() { + @Override + public OperatorPrecedence precedence() { + return OperatorPrecedence.CALL; + } + + @Override + public List<TokenType> operatorTokenTypes() { + return List.of(TokenType.SYMBOL_PAREN_OPEN); + } + + @Override + public UntypedExpressionNode parse( + SourcePosition start, + UntypedExpressionNode left, + TokenIterator tokens + ) { + tokens.skip(TokenType.SYMBOL_PAREN_OPEN); + + var args = parseMany( + () -> tokens.isNext(TokenType.SYMBOL_PAREN_CLOSE), + () -> parseTopLevelExpression(tokens), + () -> tokens.trySkip(TokenType.SYMBOL_COMMA) + ); + + tokens.skip(TokenType.SYMBOL_PAREN_CLOSE); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedCallNode(left, args, source); + } + }; + + private static final OperationParser<OperatorPrecedence, UntypedExpressionNode> MEMBER_ACCESS_PARSER = new OperationParser<>() { + @Override + public OperatorPrecedence precedence() { + return OperatorPrecedence.CALL; + } + + @Override + public List<TokenType> operatorTokenTypes() { + return List.of(TokenType.SYMBOL_DOT); + } + + @Override + public UntypedExpressionNode parse( + SourcePosition start, + UntypedExpressionNode left, + TokenIterator tokens + ) { + tokens.skip(TokenType.SYMBOL_DOT); + var fieldName = parseIdentifier(tokens); + + var end = tokens.endPosition(); + var source = createSource(start, end); + + return new UntypedMemberAccessNode(left, fieldName, source); + } + }; + + private static final Map<TokenType, OperationParser<OperatorPrecedence, UntypedExpressionNode>> OPERATION_PARSERS = Stream.of( + CALL_PARSER, + MEMBER_ACCESS_PARSER + ) + .flatMap(parser -> + parser.operatorTokenTypes().stream().map(tokenType -> Map.entry(tokenType, parser)) + ) + .collect(Collectors.toMap( + x -> x.getKey(), + x -> x.getValue() + )); + + // == Qualified type names == + public static QualifiedTypeName parseQualifiedTypeName(SourceText sourceText) { var tokens = Tokenizer.tokenize(sourceText); var tokenIterator = tokens.iterator(); diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java index 2a85072..1181fca 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/TokenType.java @@ -19,8 +19,10 @@ enum TokenType { SYMBOL_BRACE_CLOSE, SYMBOL_COLON, SYMBOL_COMMA, + SYMBOL_DOLLAR, SYMBOL_DOT, SYMBOL_DOT_DOT, + SYMBOL_FAT_ARROW, SYMBOL_FORWARD_SLASH, SYMBOL_PAREN_OPEN, SYMBOL_PAREN_CLOSE, diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java index 1a775b0..56c2123 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/parser/Tokenizer.java @@ -154,6 +154,10 @@ class Tokenizer { tokenType = TokenType.SYMBOL_DOT_DOT; iterator.skip(); iterator.skip(); + } else if ("=>".contentEquals(iterator.peekSequence(2))) { + tokenType = TokenType.SYMBOL_FAT_ARROW; + iterator.skip(); + iterator.skip(); } else { switch (iterator.peek()) { case '{': @@ -176,6 +180,11 @@ class Tokenizer { iterator.skip(); break; + case '$': + tokenType = TokenType.SYMBOL_DOLLAR; + iterator.skip(); + break; + case '.': tokenType = TokenType.SYMBOL_DOT; iterator.skip(); 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 c8dfc05..32acc82 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeChecker.java @@ -9,6 +9,7 @@ import org.zwobble.hobgoblin.compiler.typechecker.errors.NoSuchFieldError; import org.zwobble.hobgoblin.compiler.types.*; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -21,6 +22,8 @@ public class TypeChecker { private TypeChecker() { } + // == Namespaces == + public static List<TypedNamespaceNode> typeCheckNamespaces( List<UntypedNamespaceNode> untypedNamespaces, TypeCheckerGlobalContext context @@ -89,13 +92,18 @@ public class TypeChecker { return typedNamespaceNodes; } + // == Namespace statements == + static Optional<Type> declareNamespaceStatement( UntypedNamespaceStatementNode untyped, TypeCheckerNamespaceContext context ) { return switch (untyped) { - case UntypedDatabaseSchemaDefinitionNode _ -> - Optional.empty(); + case UntypedDatabaseSchemaDefinitionNode untypedDatabaseSchemaDefinition -> + Optional.of(declareDatabaseSchemaDefinition( + untypedDatabaseSchemaDefinition, + context + )); case UntypedEnumDefinitionNode untypedEnumDefinitionNode -> Optional.of(declareEnumDefinitionNode(untyped, context)); @@ -103,6 +111,9 @@ public class TypeChecker { case UntypedNativeTypeDefinitionNode untypedNativeTypeDefinition -> Optional.of(declareNativeTypeDefinition(untypedNativeTypeDefinition, context)); + case UntypedQueryDefinitionNode _ -> + Optional.empty(); + case UntypedStructDefinitionNode untypedStructDefinition -> Optional.of(declareStructDefinition(untypedStructDefinition, context)); @@ -125,6 +136,9 @@ public class TypeChecker { case UntypedNativeTypeDefinitionNode untypedNativeTypeDefinition -> defineNativeTypeDefinition(untypedNativeTypeDefinition, context); + case UntypedQueryDefinitionNode untypedQueryDefinition -> + defineQueryDefinition(untypedQueryDefinition, context); + case UntypedStructDefinitionNode untypedStructDefinition -> defineStructDefinition(untypedStructDefinition, context); @@ -133,15 +147,36 @@ public class TypeChecker { }; } + private static Type declareDatabaseSchemaDefinition( + UntypedDatabaseSchemaDefinitionNode untyped, + TypeCheckerNamespaceContext context + ) { + var databaseSchema = new DatabaseSchema(context.namespaceName(), untyped.name()); + var type = Types.databaseSchema(databaseSchema); + + context.declare(untyped.name(), type, untyped.source()); + + return type; + } + private static TypedNamespaceStatementNode defineDatabaseSchemaDefinition( UntypedDatabaseSchemaDefinitionNode untyped, TypeCheckerNamespaceContext context ) { + var databaseSchemaInfo = new DatabaseSchemaInfo(new LinkedHashMap<>()); + + var typedBody = untyped.body().stream() + .map(statement -> typeCheckDatabaseSchemaStatement(statement, databaseSchemaInfo, context)) + .toList(); + + context.defineDatabaseSchema( + new DatabaseSchema(context.namespaceName(), untyped.name()), + databaseSchemaInfo + ); + return new TypedDatabaseSchemaDefinitionNode( untyped.name(), - untyped.body().stream() - .map(statement -> typeCheckDatabaseSchemaStatement(statement, context)) - .toList(), + typedBody, untyped.docComment(), untyped.source() ); @@ -247,6 +282,21 @@ public class TypeChecker { return metaType; } + private static TypedQueryDefinitionNode defineQueryDefinition( + UntypedQueryDefinitionNode untyped, + TypeCheckerNamespaceContext context + ) { + var value = evalStatements(untyped.statements(), context); + var table = (Table) value; + + return new TypedQueryDefinitionNode( + untyped.name(), + table, + untyped.docComment(), + untyped.source() + ); + } + private static TypedStructDefinitionNode defineStructDefinition( UntypedStructDefinitionNode untyped, TypeCheckerNamespaceContext context @@ -455,18 +505,22 @@ public class TypeChecker { } } + // == Database schema statements == + static TypedDatabaseSchemaStatementNode typeCheckDatabaseSchemaStatement( UntypedDatabaseSchemaStatementNode statement, + DatabaseSchemaInfo databaseSchemaInfo, TypeCheckerNamespaceContext context ) { return switch (statement) { case UntypedTableDefinitionNode untypedTableDefinition -> - typeCheckTableDefinition(untypedTableDefinition, context); + typeCheckTableDefinition(untypedTableDefinition, databaseSchemaInfo, context); }; } private static TypedTableDefinitionNode typeCheckTableDefinition( UntypedTableDefinitionNode untyped, + DatabaseSchemaInfo databaseSchemaInfo, TypeCheckerNamespaceContext context ) { var rowTypeTypedNode = typeCheckMetaType(untyped.rowType(), context); @@ -488,6 +542,10 @@ public class TypeChecker { throw new NoSuchFieldError(rowType, untyped.primaryKey(), untyped.source()); } + var tableInfo = new TableInfo(rowType, primaryKeyField.get()); + + databaseSchemaInfo.tables().put(untyped.name(), tableInfo); + return new TypedTableDefinitionNode( untyped.name(), rowType, @@ -497,6 +555,8 @@ public class TypeChecker { ); } + // == Type-level expressions == + static TypedTypeLevelExpressionNode<? extends TypeLevelValue> typeCheckTypeLevelExpression( UntypedTypeLevelExpressionNode untyped, TypeCheckerNamespaceContext context @@ -602,4 +662,44 @@ public class TypeChecker { ); } } + + // == Statements == + + private static TypeLevelValue evalStatements( + List<UntypedStatementNode> statements, + TypeCheckerNamespaceContext context + ) { + for (var statement : statements) { + switch (statement) { + case UntypedReturnNode untypedReturn -> { + // TODO: test multiple returns. + return evalExpression(untypedReturn.expression(), context); + } + } + } + + throw new UnsupportedOperationException("TODO"); + } + + // == Expressions == + + private static TypeLevelValue evalExpression( + UntypedExpressionNode expression, + TypeCheckerNamespaceContext context + ) { + switch (expression) { + case UntypedCallNode _ -> { + throw new UnsupportedOperationException("TODO"); + } + case UntypedFunctionExpressionNode _ -> { + throw new UnsupportedOperationException("TODO"); + } + case UntypedMemberAccessNode _ -> { + throw new UnsupportedOperationException("TODO"); + } + case UntypedReferenceNode reference -> { + return lookupTypeLevelValue(reference.name(), reference.source(), context); + } + } + } } 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 ce7153f..b18769d 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerNamespaceContext.java @@ -57,6 +57,13 @@ public class TypeCheckerNamespaceContext { return Optional.ofNullable(this.variables.get(name)); } + public void defineDatabaseSchema( + DatabaseSchema databaseSchema, + DatabaseSchemaInfo databaseSchemaInfo + ) { + this.typesInfo.defineDatabaseSchema(databaseSchema, databaseSchemaInfo); + } + public void defineEnumType(EnumType enumType, List<EnumVariant> enumVariants) { this.typesInfo.defineEnumType(enumType, enumVariants); } 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 d13d2c6..e05ddc3 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfo.java @@ -8,6 +8,8 @@ import java.util.Optional; public interface TypesInfo { TypesInfo EMPTY = TypesInfoInMemory.empty(); + DatabaseSchemaInfo databaseSchemaInfo(DatabaseSchema databaseSchema); + boolean isDefined(SimpleStructType type); Optional<List<Field>> fieldsOf(SimpleStructType structType); 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 67d5c3b..011b7e5 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/typechecker/TypesInfoInMemory.java @@ -11,10 +11,13 @@ public class TypesInfoInMemory implements TypesInfo { new HashMap<>(), new HashMap<>(), new HashMap<>(), + new HashMap<>(), new HashMap<>() ); } + private final Map<DatabaseSchema, DatabaseSchemaInfo> databaseSchemaInfo; + private final Map<EnumType, List<EnumVariant>> enumVariants; private final Map<Type, Optional<List<Field>>> structFields; @@ -23,17 +26,37 @@ public class TypesInfoInMemory implements TypesInfo { private final Map<SimpleStructType, List<SumType>> variantToSums; private TypesInfoInMemory( + Map<DatabaseSchema, DatabaseSchemaInfo> databaseSchemaInfo, Map<EnumType, List<EnumVariant>> enumVariants, Map<Type, Optional<List<Field>>> structFields, Map<SumType, SumTypeInfo> sumTypeInfos, Map<SimpleStructType, List<SumType>> variantToSums ) { + this.databaseSchemaInfo = databaseSchemaInfo; this.enumVariants = enumVariants; this.structFields = structFields; this.sumTypeInfos = sumTypeInfos; this.variantToSums = variantToSums; } + public void defineDatabaseSchema( + DatabaseSchema databaseSchema, + DatabaseSchemaInfo databaseSchemaInfo + ) { + this.databaseSchemaInfo.put(databaseSchema, databaseSchemaInfo); + } + + @Override + public DatabaseSchemaInfo databaseSchemaInfo(DatabaseSchema databaseSchema) { + var info = this.databaseSchemaInfo.get(databaseSchema); + if (info == null) { + // TODO: better error + throw new UnsupportedOperationException("TODO"); + } else { + return info; + } + } + public void defineEnumType(EnumType enumType, List<EnumVariant> enumVariants) { this.enumVariants.put(enumType, enumVariants); } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/DatabaseSchema.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/DatabaseSchema.java new file mode 100644 index 0000000..e57d587 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/DatabaseSchema.java @@ -0,0 +1,8 @@ +package org.zwobble.hobgoblin.compiler.types; + +public record DatabaseSchema(NamespaceName namespaceName, String name) implements TypeLevelValue { + @Override + public String describe() { + return namespaceName.toString() + "." + name; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/DatabaseSchemaInfo.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/DatabaseSchemaInfo.java new file mode 100644 index 0000000..5ba5712 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/DatabaseSchemaInfo.java @@ -0,0 +1,6 @@ +package org.zwobble.hobgoblin.compiler.types; + +import java.util.SequencedMap; + +public record DatabaseSchemaInfo(SequencedMap<String, TableInfo> tables) { +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/HobgoblinNativeTypes.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/HobgoblinNativeTypes.java index 96efbd2..186557e 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/HobgoblinNativeTypes.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/HobgoblinNativeTypes.java @@ -40,4 +40,12 @@ public class HobgoblinNativeTypes { public static Field arbitraryField() { return new Field("", arbitraryType(), NullSource.INSTANCE); } + + public static DatabaseSchema arbitraryDatabaseSchema() { + return new DatabaseSchema(arbitraryNamespaceName(), ""); + } + + public static Table arbitraryTable() { + return new Table(arbitraryDatabaseSchema(), ""); + } } diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/Table.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/Table.java new file mode 100644 index 0000000..a33e226 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Table.java @@ -0,0 +1,8 @@ +package org.zwobble.hobgoblin.compiler.types; + +public record Table(DatabaseSchema databaseSchema, String name) implements TypeLevelValue { + @Override + public String describe() { + return databaseSchema.describe() + "." + name; + } +} diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/types/TableInfo.java b/src/main/java/org/zwobble/hobgoblin/compiler/types/TableInfo.java new file mode 100644 index 0000000..6632e51 --- /dev/null +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/TableInfo.java @@ -0,0 +1,7 @@ +package org.zwobble.hobgoblin.compiler.types; + +public record TableInfo( + SimpleStructType rowType, + Field primaryKey +) { +} 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 cf62eb1..8d12c2e 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 Type, TypeConstructor { +public sealed interface TypeLevelValue permits DatabaseSchema, 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 cfbb5ba..0d321a0 100644 --- a/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java +++ b/src/main/java/org/zwobble/hobgoblin/compiler/types/Types.java @@ -26,4 +26,12 @@ public class Types { public static Type metaType(TypeConstructor<?> constructor) { return new TypeLevelValueType(constructor); } + + public static Type databaseSchema(DatabaseSchema databaseSchema) { + return new TypeLevelValueType(databaseSchema); + } + + public static Type table(Table table) { + return new TypeLevelValueType(table); + } } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java index dbd1227..d1f177d 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ExampleTests.java @@ -32,6 +32,7 @@ public class ExampleTests { var javaOutputPath = verifyJavaOutput(exampleSet); var rustOutputPath = verifyRustOutput(exampleSet); + verifySqlite3Output(exampleSet); verifyTypescriptOutput(exampleSet); var transient0OutputDirs = Stream.of(javaOutputPath, rustOutputPath) @@ -68,6 +69,18 @@ public class ExampleTests { return verifyOutput(exampleSet, "rust", List.of(List.of("cargo", "test"))); } + private void verifySqlite3Output( + ExampleSet exampleSet + ) throws NoSuchAlgorithmException, IOException, InterruptedException { + verifyOutput( + exampleSet, + "sqlite3", + List.of( + List.of("python3", "-m", "unittest", "test.py") + ) + ); + } + private Optional<Path> verifyTypescriptOutput(ExampleSet exampleSet) throws NoSuchAlgorithmException, IOException, InterruptedException { return verifyOutput(exampleSet, "typescript", List.of( List.of("pnpm", "install"), diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedQueryDefinitionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedQueryDefinitionNodeMatcher.java new file mode 100644 index 0000000..651ede0 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/typed/TypedQueryDefinitionNodeMatcher.java @@ -0,0 +1,59 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.typed; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher imports + +public final class TypedQueryDefinitionNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher isTypedQueryDefinitionNode() { + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode>> submatchers; + + private TypedQueryDefinitionNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode>> 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.typed.TypedQueryDefinitionNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher withName(org.zwobble.precisely.Matcher<? super String> name) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode::name, (org.zwobble.precisely.Matcher) name)); + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher withTable(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.types.Table> table) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("table", org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode::table, (org.zwobble.precisely.Matcher) table)); + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher withDocComment(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.DocComment> docComment) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("docComment", org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode::docComment, (org.zwobble.precisely.Matcher) docComment)); + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNodeMatcher.java new file mode 100644 index 0000000..a9ce6a5 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedCallNodeMatcher.java @@ -0,0 +1,53 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher imports + +public final class UntypedCallNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher isUntypedCallNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>> submatchers; + + private UntypedCallNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>> 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.UntypedCallNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher withReceiver(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> receiver) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("receiver", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode::receiver, (org.zwobble.precisely.Matcher) receiver)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher withArgs(org.zwobble.precisely.Matcher<? super java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode>> args) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("args", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode::args, (org.zwobble.precisely.Matcher) args)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java new file mode 100644 index 0000000..71356e8 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedFunctionExpressionNodeMatcher.java @@ -0,0 +1,53 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher imports + +public final class UntypedFunctionExpressionNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher isUntypedFunctionExpressionNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode>> submatchers; + + private UntypedFunctionExpressionNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode>> 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.UntypedFunctionExpressionNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher withParams(org.zwobble.precisely.Matcher<? super java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>> params) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("params", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode::params, (org.zwobble.precisely.Matcher) params)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher withBody(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> body) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("body", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode::body, (org.zwobble.precisely.Matcher) body)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNodeMatcher.java new file mode 100644 index 0000000..ffe9804 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedMemberAccessNodeMatcher.java @@ -0,0 +1,53 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher imports + +public final class UntypedMemberAccessNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher isUntypedMemberAccessNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode>> submatchers; + + private UntypedMemberAccessNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode>> 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.UntypedMemberAccessNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher withReceiver(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> receiver) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("receiver", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode::receiver, (org.zwobble.precisely.Matcher) receiver)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher withFieldName(org.zwobble.precisely.Matcher<? super String> fieldName) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("fieldName", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode::fieldName, (org.zwobble.precisely.Matcher) fieldName)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java new file mode 100644 index 0000000..c140fb2 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedParamNodeMatcher.java @@ -0,0 +1,47 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher imports + +public final class UntypedParamNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher isUntypedParamNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>> submatchers; + + private UntypedParamNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>> 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.UntypedParamNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher withName(org.zwobble.precisely.Matcher<? super String> name) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode::name, (org.zwobble.precisely.Matcher) name)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedQueryDefinitionNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedQueryDefinitionNodeMatcher.java new file mode 100644 index 0000000..2e0ab23 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedQueryDefinitionNodeMatcher.java @@ -0,0 +1,65 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher imports + +public final class UntypedQueryDefinitionNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher isUntypedQueryDefinitionNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>> submatchers; + + private UntypedQueryDefinitionNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>> 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.UntypedQueryDefinitionNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher withName(org.zwobble.precisely.Matcher<? super String> name) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode::name, (org.zwobble.precisely.Matcher) name)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher withDatabaseSchema(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelExpressionNode> databaseSchema) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("databaseSchema", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode::databaseSchema, (org.zwobble.precisely.Matcher) databaseSchema)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher withStatements(org.zwobble.precisely.Matcher<? super java.util.List<org.zwobble.hobgoblin.compiler.ast.untyped.UntypedStatementNode>> statements) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("statements", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode::statements, (org.zwobble.precisely.Matcher) statements)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher withDocComment(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.DocComment> docComment) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("docComment", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode::docComment, (org.zwobble.precisely.Matcher) docComment)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReferenceNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReferenceNodeMatcher.java new file mode 100644 index 0000000..311f24d --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReferenceNodeMatcher.java @@ -0,0 +1,47 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher imports + +public final class UntypedReferenceNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher isUntypedReferenceNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode>> submatchers; + + private UntypedReferenceNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode>> 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.UntypedReferenceNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher withName(org.zwobble.precisely.Matcher<? super String> name) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("name", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode::name, (org.zwobble.precisely.Matcher) name)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReturnNodeMatcher.java b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReturnNodeMatcher.java new file mode 100644 index 0000000..7825b0d --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/ast/untyped/UntypedReturnNodeMatcher.java @@ -0,0 +1,47 @@ +// Generated by hobgoblin. + +package org.zwobble.hobgoblin.compiler.ast.untyped; + +// Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher imports +// Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher imports + +public final class UntypedReturnNodeMatcher implements org.zwobble.precisely.Matcher<java.lang.Object> { + public static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher isUntypedReturnNode() { + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher(java.util.List.of()); + } + + private final java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode>> submatchers; + + private UntypedReturnNodeMatcher( + java.util.List<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode>> 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.UntypedReturnNode.class, this.submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher withExpression(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedExpressionNode> expression) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("expression", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode::expression, (org.zwobble.precisely.Matcher) expression)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher(submatchers); + } + + public org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher withSource(org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.sources.Source> source) { + var submatchers = new java.util.ArrayList<org.zwobble.precisely.Matcher<? super org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode>>(this.submatchers); + submatchers.add(org.zwobble.precisely.Matchers.has("source", org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode::source, (org.zwobble.precisely.Matcher) source)); + return new org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher(submatchers); + } + + // Custom area start: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher body + // Custom area end: org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher body +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/CodeWriterTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/output/CodeWriterTests.java index 2b3ad6d..9e3f900 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/output/lang/CodeWriterTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/output/CodeWriterTests.java @@ -1,7 +1,6 @@ -package org.zwobble.hobgoblin.compiler.output.lang; +package org.zwobble.hobgoblin.compiler.output; import org.junit.jupiter.api.Test; -import org.zwobble.hobgoblin.compiler.output.CodeWriter; import java.io.IOException; import java.io.StringWriter; @@ -18,7 +17,7 @@ public class CodeWriterTests { CodeWriter.writeTo( file, - "//", + new CodeWriter.CodeWriterConfig("//"), codeWriter -> { codeWriter.writeHeader(); codeWriter.write("Before"); @@ -51,7 +50,7 @@ public class CodeWriterTests { CodeWriter.writeTo( file, - "//", + new CodeWriter.CodeWriterConfig("//"), codeWriter -> { codeWriter.writeHeader(); codeWriter.write("Before"); @@ -89,7 +88,7 @@ public class CodeWriterTests { CodeWriter.writeTo( file, - "//", + new CodeWriter.CodeWriterConfig("//"), codeWriter -> { codeWriter.writeHeader(); codeWriter.writeCustomArea("head"); @@ -124,7 +123,7 @@ public class CodeWriterTests { CodeWriter.writeTo( file, - "//", + new CodeWriter.CodeWriterConfig("//"), codeWriter -> { codeWriter.writeHeader(); codeWriter.writeCustomArea("head"); @@ -135,4 +134,81 @@ public class CodeWriterTests { assertThat(stringWriter.toString(), equalTo("")); assertThat(file.isWriterOpened(), equalTo(false)); } + + @Test + public void strippingDoesNothingToFileWithoutHeader() { + var originalContents = """ + Hello, world! + """; + + var result = CodeWriter.stripFileContents( + originalContents, + new CodeWriter.CodeWriterConfig("//") + ); + + assertThat(result, equalTo(new CodeWriter.StripResult.Unchanged())); + } + + @Test + public void whenFileHasNoCustomAreasThenFileIsDeleted() { + var originalContents = """ + // Generated by hobgoblin. + + Hello, world! + """; + + var result = CodeWriter.stripFileContents( + originalContents, + new CodeWriter.CodeWriterConfig("//") + ); + + assertThat(result, equalTo(new CodeWriter.StripResult.Delete())); + } + + @Test + public void whenCustomAreasAreAllEmptyThenFileIsDeleted() { + var originalContents = """ + // Generated by hobgoblin. + + Before + // Custom area start: body + // Custom area end: body + After + """; + + var result = CodeWriter.stripFileContents( + originalContents, + new CodeWriter.CodeWriterConfig("//") + ); + + assertThat(result, equalTo(new CodeWriter.StripResult.Delete())); + } + + @Test + public void nonEmptyCustomAreasArePreserved() { + var originalContents = """ + // Generated by hobgoblin. + + Before + // Custom area start: head + Custom head. + // Custom area end: head + // Custom area start: body + // Custom area end: body + After + """; + + var result = CodeWriter.stripFileContents( + originalContents, + new CodeWriter.CodeWriterConfig("//") + ); + + assertThat(result, equalTo(new CodeWriter.StripResult.NewContent(""" + // Generated by hobgoblin. + + // Custom area start: head + Custom head. + // Custom area end: head + """))); + } } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserCallTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserCallTests.java new file mode 100644 index 0000000..64e84de --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserCallTests.java @@ -0,0 +1,61 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedCallNodeMatcher.isUntypedCallNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserCallTests { + @Test + public void canParseCallWithNoArguments() { + var source = "f()"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedCallNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("f")) + ) + .withArgs(isSequence()) + ); + } + + @Test + public void canParseCallWithOnePositionalArgument() { + var source = "f(a)"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedCallNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("f")) + ) + .withArgs(isSequence( + isUntypedReferenceNode().withName(equalTo("a")) + )) + ); + } + + @Test + public void canParseCallWithMultiplePositionalArguments() { + var source = "f(a, b, c)"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedCallNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("f")) + ) + .withArgs(isSequence( + isUntypedReferenceNode().withName(equalTo("a")), + isUntypedReferenceNode().withName(equalTo("b")), + isUntypedReferenceNode().withName(equalTo("c")) + )) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java new file mode 100644 index 0000000..99cf3c0 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserFunctionExpressionTests.java @@ -0,0 +1,55 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedFunctionExpressionNodeMatcher.isUntypedFunctionExpressionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedParamNodeMatcher.isUntypedParamNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserFunctionExpressionTests { + @Test + public void canParseFunctionExpressionWithNoParams() { + var source = "$() => x"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedFunctionExpressionNode() + .withParams(isSequence()) + .withBody(isUntypedReferenceNode().withName(equalTo("x"))) + ); + } + + @Test + public void canParseFunctionExpressionWithOneParam() { + var source = "$(a) => x"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedFunctionExpressionNode() + .withParams(isSequence( + isUntypedParamNode().withName(equalTo("a")) + )) + .withBody(isUntypedReferenceNode().withName(equalTo("x"))) + ); + } + @Test + public void canParseFunctionExpressionWithMultipleParams() { + var source = "$(a, b, c) => x"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedFunctionExpressionNode() + .withParams(isSequence( + isUntypedParamNode().withName(equalTo("a")), + isUntypedParamNode().withName(equalTo("b")), + isUntypedParamNode().withName(equalTo("c")) + )) + .withBody(isUntypedReferenceNode().withName(equalTo("x"))) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserMemberAccessTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserMemberAccessTests.java new file mode 100644 index 0000000..d28cc2b --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserMemberAccessTests.java @@ -0,0 +1,26 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedMemberAccessNodeMatcher.isUntypedMemberAccessNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; + +public class ParserMemberAccessTests { + @Test + public void canParseMemberAccess() { + var source = "author.name"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedMemberAccessNode() + .withReceiver(isUntypedReferenceNode() + .withName(equalTo("author")) + ) + .withFieldName(equalTo("name")) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserQueryDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserQueryDefinitionTests.java new file mode 100644 index 0000000..e7676c9 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserQueryDefinitionTests.java @@ -0,0 +1,78 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNodeMatcher.isUntypedQueryDefinitionNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher.isUntypedReturnNode; +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.equalTo; +import static org.zwobble.precisely.Matchers.isSequence; + +public class ParserQueryDefinitionTests { + @Test + public void canParseQueryDefinitionWithNoStatements() { + var source = """ + query allAuthors() on BlogAppDatabase { + }"""; + + var node = parseString(source, Parser::parseNamespaceStatement); + + assertThat(node, isUntypedQueryDefinitionNode() + .withName(equalTo("allAuthors")) + .withDatabaseSchema(isUntypedTypeLevelReferenceNode().withName(equalTo("BlogAppDatabase"))) + .withStatements(isSequence()) + ); + } + + @Test + public void canParseQueryDefinitionWithSingleStatement() { + var source = """ + query allAuthors() on BlogAppDatabase { + return x; + }"""; + + var node = parseString(source, Parser::parseNamespaceStatement); + + assertThat(node, isUntypedQueryDefinitionNode() + .withName(equalTo("allAuthors")) + .withDatabaseSchema(isUntypedTypeLevelReferenceNode().withName(equalTo("BlogAppDatabase"))) + .withStatements(isSequence( + isUntypedReturnNode() + .withExpression(isUntypedReferenceNode() + .withName(equalTo("x")) + ) + )) + ); + + } + + @Test + public void canParseQueryDefinitionWithMultipleStatements() { + var source = """ + query allAuthors() on BlogAppDatabase { + return x; + return y; + }"""; + + var node = parseString(source, Parser::parseNamespaceStatement); + + assertThat(node, isUntypedQueryDefinitionNode() + .withName(equalTo("allAuthors")) + .withDatabaseSchema(isUntypedTypeLevelReferenceNode().withName(equalTo("BlogAppDatabase"))) + .withStatements(isSequence( + isUntypedReturnNode() + .withExpression(isUntypedReferenceNode() + .withName(equalTo("x")) + ), + isUntypedReturnNode() + .withExpression(isUntypedReferenceNode() + .withName(equalTo("y")) + ) + )) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserReferenceTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserReferenceTests.java new file mode 100644 index 0000000..fdd3add --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserReferenceTests.java @@ -0,0 +1,22 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; + +public class ParserReferenceTests { + @Test + public void canParseReference() { + var source = "authors"; + + var node = parseString(source, Parser::parseTopLevelExpression); + + assertThat(node, isUntypedReferenceNode() + .withName(equalTo("authors")) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserReturnTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserReturnTests.java new file mode 100644 index 0000000..64ce562 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/ParserReturnTests.java @@ -0,0 +1,25 @@ +package org.zwobble.hobgoblin.compiler.parser; + +import org.junit.jupiter.api.Test; + +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNodeMatcher.isUntypedReferenceNode; +import static org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNodeMatcher.isUntypedReturnNode; +import static org.zwobble.hobgoblin.compiler.parser.ParserTesting.parseString; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; + +public class ParserReturnTests { + @Test + public void canParseReturn() { + var source = "return x;"; + + var node = parseString(source, Parser::parseStatement); + + assertThat(node, isUntypedReturnNode() + .withExpression(isUntypedReferenceNode() + .withName(equalTo("x")) + ) + ); + } +} + diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java index 39aded5..d5b7347 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/parser/TokenizerTests.java @@ -173,6 +173,17 @@ public class TokenizerTests { } @Test + public void symbolDollarIsTokenized() { + var sourceText = SourceText.fromString("<string>", "$"); + + var tokens = Tokenizer.tokenize(sourceText); + + assertThat(tokens.tokens(), isSequence( + equalTo(new Token(TokenType.SYMBOL_DOLLAR, sourceRange(sourceText, 0, 1))) + )); + } + + @Test public void symbolDotIsTokenized() { var sourceText = SourceText.fromString("<string>", "."); @@ -195,6 +206,17 @@ public class TokenizerTests { } @Test + public void symbolFatArrowIsTokenized() { + var sourceText = SourceText.fromString("<string>", "=>"); + + var tokens = Tokenizer.tokenize(sourceText); + + assertThat(tokens.tokens(), isSequence( + equalTo(new Token(TokenType.SYMBOL_FAT_ARROW, sourceRange(sourceText, 0, 2))) + )); + } + + @Test public void symbolForwardSlashIsTokenized() { var sourceText = SourceText.fromString("<string>", "/"); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerDatabaseSchemaDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerDatabaseSchemaDefinitionTests.java index 08fdd47..daecb54 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerDatabaseSchemaDefinitionTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerDatabaseSchemaDefinitionTests.java @@ -6,20 +6,17 @@ import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTableDefinitionNode; import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode; import org.zwobble.hobgoblin.compiler.builtins.NativeTypes; import org.zwobble.hobgoblin.compiler.sources.NullSource; -import org.zwobble.hobgoblin.compiler.types.Field; -import org.zwobble.hobgoblin.compiler.types.NamespaceName; -import org.zwobble.hobgoblin.compiler.types.SimpleStructType; -import org.zwobble.hobgoblin.compiler.types.Types; +import org.zwobble.hobgoblin.compiler.types.*; import java.util.List; +import java.util.Map; import java.util.Optional; import static org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaDefinitionNodeMatcher.isTypedDatabaseSchemaDefinitionNode; import static org.zwobble.hobgoblin.compiler.ast.typed.TypedTableDefinitionNodeMatcher.isTypedTableDefinitionNode; import static org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; import static org.zwobble.precisely.AssertThat.assertThat; -import static org.zwobble.precisely.Matchers.equalTo; -import static org.zwobble.precisely.Matchers.isSequence; +import static org.zwobble.precisely.Matchers.*; public class TypeCheckerDatabaseSchemaDefinitionTests { @Test @@ -54,5 +51,20 @@ public class TypeCheckerDatabaseSchemaDefinitionTests { .withRowType(equalTo(authorType)) )) ); + assertThat( + namespaceContext.lookupType("blogApp"), + isOptionalOf(equalTo(Types.databaseSchema(new DatabaseSchema(NamespaceName.of("App"), "blogApp")))) + ); + var typesInfo = namespaceContext.toTypesInfo(); + var schemaInfo = typesInfo.databaseSchemaInfo(new DatabaseSchema(NamespaceName.of("App"), "blogApp")); + assertThat(schemaInfo.tables().entrySet(), isSequence( + allOf( + has("key", Map.Entry::getKey, equalTo("authors")), + has("value", Map.Entry::getValue, allOf( + has("rowType", TableInfo::rowType, equalTo(authorType)), + has("primaryKey", TableInfo::primaryKey, equalTo(new Field("id", NativeTypes.INT_32, NullSource.INSTANCE))) + )) + ) + )); } } diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerQueryDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerQueryDefinitionTests.java new file mode 100644 index 0000000..4ecc1c6 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerQueryDefinitionTests.java @@ -0,0 +1,40 @@ +package org.zwobble.hobgoblin.compiler.typechecker; + +import org.junit.jupiter.api.Test; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedQueryDefinitionNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReferenceNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedReturnNode; +import org.zwobble.hobgoblin.compiler.ast.untyped.UntypedTypeLevelReferenceNode; +import org.zwobble.hobgoblin.compiler.sources.NullSource; +import org.zwobble.hobgoblin.compiler.types.*; + +import static org.zwobble.hobgoblin.compiler.ast.typed.TypedQueryDefinitionNodeMatcher.isTypedQueryDefinitionNode; +import static org.zwobble.hobgoblin.compiler.typechecker.TypeCheckerTesting.typeCheckNamespaceStatement; +import static org.zwobble.precisely.AssertThat.assertThat; +import static org.zwobble.precisely.Matchers.equalTo; + +public class TypeCheckerQueryDefinitionTests { + @Test + public void queryForAllTableRows() { + var untyped = UntypedQueryDefinitionNode.arbitrary() + .withName("allAuthors") + .withDatabaseSchema(UntypedTypeLevelReferenceNode.arbitrary().withName("blogApp")) + .addStatement(UntypedReturnNode.arbitrary().withExpression( + UntypedReferenceNode.arbitrary().withName("authors") + )) + .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 = typeCheckNamespaceStatement(untyped, namespaceContext); + + assertThat(typed, isTypedQueryDefinitionNode() + .withName(equalTo("allAuthors")) + .withTable(equalTo(table)) + ); + } +} diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java index a32945a..989fa39 100644 --- a/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java +++ b/src/test/java/org/zwobble/hobgoblin/compiler/typechecker/TypeCheckerTableDefinitionTests.java @@ -9,6 +9,7 @@ import org.zwobble.hobgoblin.compiler.typechecker.errors.NoSuchFieldError; import org.zwobble.hobgoblin.compiler.typechecker.errors.TableRowTypeMustBeStructError; import org.zwobble.hobgoblin.compiler.types.*; +import java.util.LinkedHashMap; import java.util.List; import java.util.Optional; @@ -37,11 +38,16 @@ public class TypeCheckerTableDefinitionTests { new Field("id", NativeTypes.INT_32, NullSource.INSTANCE) ))); - var typed = typeCheckDatabaseSchemaStatement(untyped, namespaceContext); + var typed = typeCheckDatabaseSchemaStatement( + untyped, + new DatabaseSchemaInfo(new LinkedHashMap<>()), + namespaceContext + ); assertThat(typed, isTypedTableDefinitionNode() .withName(equalTo("authors")) .withRowType(equalTo(authorType)) + .withPrimaryKey(equalTo(new Field("id", NativeTypes.INT_32, NullSource.INSTANCE))) ); } @@ -61,7 +67,11 @@ public class TypeCheckerTableDefinitionTests { var error = assertThrows( TableRowTypeMustBeStructError.class, - () -> typeCheckDatabaseSchemaStatement(untyped, namespaceContext) + () -> typeCheckDatabaseSchemaStatement( + untyped, + new DatabaseSchemaInfo(new LinkedHashMap<>()), + namespaceContext + ) ); assertThat(error.rowType(), equalTo(authorType)); @@ -87,7 +97,11 @@ public class TypeCheckerTableDefinitionTests { var error = assertThrows( NoSuchFieldError.class, - () -> typeCheckDatabaseSchemaStatement(untyped, namespaceContext) + () -> typeCheckDatabaseSchemaStatement( + untyped, + new DatabaseSchemaInfo(new LinkedHashMap<>()), + namespaceContext + ) ); assertThat(error.type(), equalTo(authorType)); diff --git a/src/test/java/org/zwobble/hobgoblin/compiler/util/SequencedMaps.java b/src/test/java/org/zwobble/hobgoblin/compiler/util/SequencedMaps.java new file mode 100644 index 0000000..b19d3d1 --- /dev/null +++ b/src/test/java/org/zwobble/hobgoblin/compiler/util/SequencedMaps.java @@ -0,0 +1,23 @@ +package org.zwobble.hobgoblin.compiler.util; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.SequencedMap; + +public class SequencedMaps { + private SequencedMaps() { + } + + @SafeVarargs + public static <K, V> SequencedMap<K, V> ofEntries( + Map.Entry<K, V>... entries + ) { + var map = new LinkedHashMap<K, V>(entries.length); + + for (var entry : entries) { + map.put(entry.getKey(), entry.getValue()); + } + + return map; + } +} |
