summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/18-database/hobgoblin.json54
-rw-r--r--examples/18-database/output/sqlite3/scripts/data/Data.sql7
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java2
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java60
-rw-r--r--src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java121
5 files changed, 194 insertions, 0 deletions
diff --git a/examples/18-database/hobgoblin.json5 b/examples/18-database/hobgoblin.json5
index 3b36468..8955a5d 100644
--- a/examples/18-database/hobgoblin.json5
+++ b/examples/18-database/hobgoblin.json5
@@ -3,6 +3,10 @@
langs: {
},
generators: [
+ {
+ generator: "sql-scripts",
+ path: "output/sqlite3/scripts",
+ },
],
}
}
diff --git a/examples/18-database/output/sqlite3/scripts/data/Data.sql b/examples/18-database/output/sqlite3/scripts/data/Data.sql
new file mode 100644
index 0000000..af1060e
--- /dev/null
+++ b/examples/18-database/output/sqlite3/scripts/data/Data.sql
@@ -0,0 +1,7 @@
+-- Generated by hobgoblin.
+
+CREATE TABLE authors (
+ id INT,
+ name TEXT,
+ PRIMARY KEY (id)
+);
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java
index 92b78b8..c1325dd 100644
--- a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/Generators.java
@@ -5,6 +5,7 @@ import org.zwobble.hobgoblin.compiler.output.generators.javatransient0.JavaTrans
import org.zwobble.hobgoblin.compiler.output.generators.javatypes.JavaTypesGenerator;
import org.zwobble.hobgoblin.compiler.output.generators.rusttransient0.RustTransient0Generator;
import org.zwobble.hobgoblin.compiler.output.generators.rusttypes.RustTypesGenerator;
+import org.zwobble.hobgoblin.compiler.output.generators.sqlscripts.SqlScriptsGenerator;
import org.zwobble.hobgoblin.compiler.output.generators.typescripttypes.TypescriptTypesGenerator;
import java.util.List;
@@ -21,6 +22,7 @@ public class Generators {
JavaTransient0Generator.DEFINITION,
RustTypesGenerator.DEFINITION,
RustTransient0Generator.DEFINITION,
+ SqlScriptsGenerator.DEFINITION,
TypescriptTypesGenerator.DEFINITION
);
diff --git a/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java
new file mode 100644
index 0000000..be847d5
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sql/SqlGenerator.java
@@ -0,0 +1,60 @@
+package org.zwobble.hobgoblin.compiler.output.generators.sql;
+
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedTableDefinitionNode;
+import org.zwobble.hobgoblin.compiler.builtins.NativeTypes;
+import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.*;
+import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo;
+import org.zwobble.hobgoblin.compiler.types.SimpleStructType;
+import org.zwobble.hobgoblin.compiler.types.Type;
+import org.zwobble.hobgoblin.compiler.util.Casing;
+
+import java.util.List;
+
+public class SqlGenerator {
+ public SqlStatement generateTableDefinition(
+ TypedTableDefinitionNode tableDefinition,
+ TypesInfo typesInfo
+ ) {
+ var sqlTableName = this.generateTableName(tableDefinition.name());
+
+ // TODO: constructed struct type.
+ var sqlColumns = typesInfo.fieldsOf((SimpleStructType) tableDefinition.rowType())
+ .orElse(List.of())
+ .stream()
+ .map(field -> new SqlColumnDefinition(
+ this.generateColumnName(field.name()),
+ this.generateSqlType(field.type())
+ ))
+ .toList();
+
+ var sqlTableConstraints = List.<SqlTableConstraint>of(
+ new SqlPrimaryKeyConstraint(List.of(
+ this.generateColumnName(tableDefinition.primaryKey().name())
+ ))
+ );
+
+ return new SqlCreateTableStatement(
+ sqlTableName,
+ sqlColumns,
+ sqlTableConstraints
+ );
+ }
+
+ private SqlIdentifier generateSqlType(Type type) {
+ if (type.equals(NativeTypes.STRING)) {
+ return new SqlIdentifier("TEXT");
+ } else if (type.equals(NativeTypes.INT_32)) {
+ return new SqlIdentifier("INT");
+ } else {
+ throw new UnsupportedOperationException("TODO");
+ }
+ }
+
+ private SqlIdentifier generateColumnName(String fieldName) {
+ return new SqlIdentifier(Casing.lowerCamelCaseToSnakeCase(fieldName));
+ }
+
+ private SqlIdentifier generateTableName(String tableName) {
+ return new SqlIdentifier(Casing.lowerCamelCaseToSnakeCase(tableName));
+ }
+}
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
new file mode 100644
index 0000000..60daf40
--- /dev/null
+++ b/src/main/java/org/zwobble/hobgoblin/compiler/output/generators/sqlscripts/SqlScriptsGenerator.java
@@ -0,0 +1,121 @@
+package org.zwobble.hobgoblin.compiler.output.generators.sqlscripts;
+
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaDefinitionNode;
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedDatabaseSchemaStatementNode;
+import org.zwobble.hobgoblin.compiler.ast.typed.TypedNamespaceNode;
+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.sql.SqlGenerator;
+import org.zwobble.hobgoblin.compiler.output.lang.sql.SqlWriter;
+import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.SqlScript;
+import org.zwobble.hobgoblin.compiler.output.lang.sql.ast.SqlStatement;
+import org.zwobble.hobgoblin.compiler.typechecker.TypesInfo;
+import org.zwobble.hobgoblin.compiler.types.NamespaceName;
+import org.zwobble.json5.reader.Json5ObjectReader;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.List;
+
+public class SqlScriptsGenerator implements Generator {
+ private static final String NAME = "sql-scripts";
+
+ public static final GeneratorDefinition DEFINITION = new GeneratorDefinition() {
+ @Override
+ public String name() {
+ return NAME;
+ }
+
+ @Override
+ public OutputConfig parseGeneratorConfig(
+ Path projectRoot,
+ Json5ObjectReader generatorJson,
+ Json5ObjectReader langsJson
+ ) {
+ return SqlScriptsGenerator.parseGeneratorConfig(projectRoot, generatorJson, langsJson);
+ }
+ };
+
+ private static OutputConfig parseGeneratorConfig(
+ Path projectRoot,
+ Json5ObjectReader generatorJson,
+ Json5ObjectReader langsJson
+ ) {
+ return () -> {
+ var relativeOutputPath = generatorJson.getString("path").value();
+ var outputPath = projectRoot.resolve(relativeOutputPath);
+
+ var sqlGenerator = new SqlGenerator();
+
+ return new SqlScriptsGenerator(outputPath, sqlGenerator);
+ };
+ }
+
+ private final Path outputPath;
+ private final SqlGenerator sqlGenerator;
+
+ private SqlScriptsGenerator(Path outputPath, SqlGenerator sqlGenerator) {
+ this.outputPath = outputPath;
+ this.sqlGenerator = sqlGenerator;
+ }
+
+ @Override
+ public void generate(
+ List<TypedNamespaceNode> namespaces,
+ TypesInfo typesInfo
+ ) throws IOException {
+ for (var namespace : namespaces) {
+ for (var namespaceStatement : namespace.body()) {
+ if (namespaceStatement instanceof TypedDatabaseSchemaDefinitionNode databaseSchemaDefinition) {
+ generateDatabaseSchemaDefinition(
+ databaseSchemaDefinition,
+ namespace.namespaceName(),
+ typesInfo
+ );
+ }
+ }
+ }
+ }
+
+ private void generateDatabaseSchemaDefinition(
+ TypedDatabaseSchemaDefinitionNode databaseSchemaDefinition,
+ NamespaceName namespaceName,
+ TypesInfo typesInfo
+ ) throws IOException {
+ var sqlStatements = databaseSchemaDefinition.body()
+ .stream()
+ .map(statement -> generateDatabaseSchemaStatement(statement, typesInfo))
+ .toList();
+ var sqlScript = new SqlScript(sqlStatements);
+
+ write(namespaceName, databaseSchemaDefinition.name(), sqlScript);
+ }
+
+ private SqlStatement generateDatabaseSchemaStatement(
+ TypedDatabaseSchemaStatementNode statement,
+ TypesInfo typesInfo
+ ) {
+ return switch (statement) {
+ case TypedTableDefinitionNode tableDefinition ->
+ this.sqlGenerator.generateTableDefinition(tableDefinition, typesInfo);
+ };
+ }
+
+ private void write(
+ NamespaceName namespaceName,
+ String schemaName,
+ SqlScript script
+ ) throws IOException {
+ var scriptPath = this.outputPath
+ .resolve(String.join(File.separator, namespaceName.parts()))
+ .resolve(schemaName + ".sql");
+
+ SqlWriter.writeTo(
+ scriptPath,
+ writer -> writer.writeScript(script)
+ );
+ }
+}