From bafab68eac11f6f60381ace76dc99b8c5a4dd2a6 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Mon, 9 Mar 2026 06:18:45 +0000 Subject: [PATCH] implements PR-05.0.2 --- .../services/PBSFrontendPhaseService.java | 113 ++++++++++-------- .../source/tables/ModuleReference.java | 17 +++ .../compiler/source/tables/ModuleTable.java | 10 ++ .../source/tables/ModuleTableTest.java | 31 +++++ 4 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleReference.java create mode 100644 prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleTable.java create mode 100644 prometeu-compiler/prometeu-compiler-core/src/test/java/p/studio/compiler/source/tables/ModuleTableTest.java diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PBSFrontendPhaseService.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PBSFrontendPhaseService.java index 5beaed3f..b5fc23ff 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PBSFrontendPhaseService.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PBSFrontendPhaseService.java @@ -21,6 +21,9 @@ import p.studio.compiler.source.Span; import p.studio.compiler.source.diagnostics.DiagnosticSink; import p.studio.compiler.source.diagnostics.DiagnosticPhase; import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.identifiers.ModuleId; +import p.studio.compiler.source.tables.ModuleReference; +import p.studio.compiler.source.tables.ModuleTable; import p.studio.utilities.structures.ReadOnlyList; import p.studio.utilities.logs.LogAggregator; @@ -62,8 +65,9 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { final var irBackendAggregator = IRBackend.aggregator(); final var parsedSourceFiles = new ArrayList(); final Map modulesByCoordinates = new LinkedHashMap<>(); - final var moduleKeyByFile = new HashMap(); - final var failedModuleKeys = new HashSet(); + final var moduleTable = new ModuleTable(); + final var moduleIdByFile = new HashMap(); + final var failedModuleIds = new HashSet(); for (final var pId : ctx.stack.reverseTopologicalOrder) { final var projectDescriptor = ctx.projectTable.get(pId); @@ -75,36 +79,36 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { sourceHandle.readUtf8().ifPresentOrElse( utf8Content -> { final var coordinates = resolveModuleCoordinates(projectDescriptor, sourceHandle); - final var moduleKey = moduleKey(coordinates); + final var moduleId = moduleId(moduleTable, coordinates); final var moduleUnit = modulesByCoordinates.computeIfAbsent( coordinates, ignored -> new MutableModuleUnit()); switch (sourceHandle.getExtension()) { case "pbs" -> { - moduleKeyByFile.put(fId, moduleKey); + moduleIdByFile.put(fId, moduleId); final var parseErrorBaseline = diagnostics.errorCount(); final var ast = parseSourceFile(fId, utf8Content, diagnostics, projectSourceKind); moduleUnit.sources.add(new PbsModuleVisibilityValidator.SourceFile(fId, ast)); - parsedSourceFiles.add(new ParsedSourceFile(fId, ast, moduleKey, projectSourceKind)); + parsedSourceFiles.add(new ParsedSourceFile(fId, ast, moduleId, projectSourceKind)); if (diagnostics.errorCount() > parseErrorBaseline) { - failedModuleKeys.add(moduleKey); + failedModuleIds.add(moduleId); } } case "barrel" -> { - moduleKeyByFile.put(fId, moduleKey); + moduleIdByFile.put(fId, moduleId); if ("mod.barrel".equals(sourceHandle.getFilename())) { final var parseErrorBaseline = diagnostics.errorCount(); final var barrelAst = parseBarrelFile(fId, utf8Content, diagnostics); moduleUnit.barrels.add(new PbsModuleVisibilityValidator.BarrelFile(fId, barrelAst)); if (diagnostics.errorCount() > parseErrorBaseline) { - failedModuleKeys.add(moduleKey); + failedModuleIds.add(moduleId); } } else { p.studio.compiler.source.diagnostics.Diagnostics.error(diagnostics, PbsLinkErrors.E_LINK_INVALID_BARREL_FILENAME.name(), "Only 'mod.barrel' is allowed as barrel filename", new Span(fId, 0, utf8Content.getBytes(StandardCharsets.UTF_8).length)); - failedModuleKeys.add(moduleKey); + failedModuleIds.add(moduleId); } } default -> { @@ -120,7 +124,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { loadReservedStdlibModules( modulesByCoordinates, parsedSourceFiles, - moduleKeyByFile, + moduleIdByFile, + moduleTable, diagnostics, ctx.stdlibVersion()); @@ -134,13 +139,13 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { ReadOnlyList.wrap(moduleUnit.barrels))); } moduleVisibilityValidator.validate(ReadOnlyList.wrap(modules), nameTable, diagnostics); - markModulesWithLinkingErrors(diagnostics, moduleKeyByFile, failedModuleKeys); - final var moduleDependencyGraph = buildModuleDependencyGraph(parsedSourceFiles); + markModulesWithLinkingErrors(diagnostics, moduleIdByFile, failedModuleIds); + final var moduleDependencyGraph = buildModuleDependencyGraph(parsedSourceFiles, moduleTable); final var compiledSourceFiles = new ArrayList(parsedSourceFiles.size()); for (final var parsedSource : parsedSourceFiles) { - final var blockedModuleKeys = blockedModulesByDependency(failedModuleKeys, moduleDependencyGraph); - if (blockedModuleKeys.contains(parsedSource.moduleKey())) { + final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph); + if (blockedModuleIds.contains(parsedSource.moduleId())) { continue; } final var compileErrorBaseline = diagnostics.errorCount(); @@ -149,18 +154,18 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { parsedSource.ast(), diagnostics, parsedSource.sourceKind(), - parsedSource.moduleKey(), + renderModuleKey(moduleTable, parsedSource.moduleId()), ctx.hostAdmissionContext(), nameTable); if (diagnostics.errorCount() > compileErrorBaseline) { - failedModuleKeys.add(parsedSource.moduleKey()); + failedModuleIds.add(parsedSource.moduleId()); } - compiledSourceFiles.add(new CompiledSourceFile(parsedSource.moduleKey(), irBackendFile)); + compiledSourceFiles.add(new CompiledSourceFile(parsedSource.moduleId(), irBackendFile)); } - final var blockedModuleKeys = blockedModulesByDependency(failedModuleKeys, moduleDependencyGraph); + final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph); for (final var compiledSource : compiledSourceFiles) { - if (blockedModuleKeys.contains(compiledSource.moduleKey())) { + if (blockedModuleIds.contains(compiledSource.moduleId())) { continue; } irBackendAggregator.merge(compiledSource.irBackendFile()); @@ -174,18 +179,19 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { private void loadReservedStdlibModules( final Map modulesByCoordinates, final ArrayList parsedSourceFiles, - final Map moduleKeyByFile, + final Map moduleIdByFile, + final ModuleTable moduleTable, final DiagnosticSink diagnostics, final int stdlibVersion) { final var stdlibEnvironment = stdlibEnvironmentResolver.resolve(stdlibVersion); final var pending = new ArrayDeque(); - final var resolved = new HashSet(); + final var resolved = new HashSet(); enqueueReservedImportsFromKnownModules(modulesByCoordinates, pending); while (!pending.isEmpty()) { final var target = pending.removeFirst(); - final var targetKey = moduleKey(target); - if (!resolved.add(targetKey)) { + final var targetId = moduleId(moduleTable, target); + if (!resolved.add(targetId)) { continue; } if (modulesByCoordinates.containsKey(target)) { @@ -203,15 +209,15 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { moduleData.barrels.addAll(loadedModule.barrelFiles().asList()); modulesByCoordinates.put(loadedModule.coordinates(), moduleData); for (final var sourceFile : loadedModule.sourceFiles()) { - moduleKeyByFile.put(sourceFile.fileId(), targetKey); + moduleIdByFile.put(sourceFile.fileId(), targetId); parsedSourceFiles.add(new ParsedSourceFile( sourceFile.fileId(), sourceFile.ast(), - targetKey, + targetId, SourceKind.SDK_INTERFACE)); } for (final var barrelFile : loadedModule.barrelFiles()) { - moduleKeyByFile.put(barrelFile.fileId(), targetKey); + moduleIdByFile.put(barrelFile.fileId(), targetId); } enqueueReservedImportsFromSourceFiles(loadedModule.sourceFiles().asList(), pending); } @@ -296,8 +302,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { private void markModulesWithLinkingErrors( final DiagnosticSink diagnostics, - final Map moduleKeyByFile, - final Set failedModuleKeys) { + final Map moduleIdByFile, + final Set failedModuleIds) { for (final var diagnostic : diagnostics) { if (!diagnostic.getSeverity().isError()) { continue; @@ -305,32 +311,33 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { if (diagnostic.getPhase() != DiagnosticPhase.LINKING) { continue; } - final var moduleKey = moduleKeyByFile.get(diagnostic.getSpan().getFileId()); - if (moduleKey != null) { - failedModuleKeys.add(moduleKey); + final var moduleId = moduleIdByFile.get(diagnostic.getSpan().getFileId()); + if (moduleId != null) { + failedModuleIds.add(moduleId); } } } - private Map> buildModuleDependencyGraph( - final ArrayList parsedSourceFiles) { - final Map> dependenciesByModule = new HashMap<>(); + private Map> buildModuleDependencyGraph( + final ArrayList parsedSourceFiles, + final ModuleTable moduleTable) { + final Map> dependenciesByModule = new HashMap<>(); for (final var parsedSource : parsedSourceFiles) { final var moduleDependencies = dependenciesByModule.computeIfAbsent( - parsedSource.moduleKey(), + parsedSource.moduleId(), ignored -> new HashSet<>()); for (final var importDecl : parsedSource.ast().imports()) { final var moduleRef = importDecl.moduleRef(); - moduleDependencies.add(moduleKey(moduleRef.project(), moduleRef.pathSegments())); + moduleDependencies.add(moduleId(moduleTable, moduleRef.project(), moduleRef.pathSegments())); } } return dependenciesByModule; } - private Set blockedModulesByDependency( - final Set failedModuleKeys, - final Map> dependenciesByModule) { - final Map> dependentsByModule = new HashMap<>(); + private Set blockedModulesByDependency( + final Set failedModuleIds, + final Map> dependenciesByModule) { + final Map> dependentsByModule = new HashMap<>(); for (final var entry : dependenciesByModule.entrySet()) { final var importer = entry.getKey(); for (final var dependency : entry.getValue()) { @@ -338,8 +345,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { } } - final var blocked = new HashSet(failedModuleKeys); - final var pending = new ArrayDeque(failedModuleKeys); + final var blocked = new HashSet(failedModuleIds); + final var pending = new ArrayDeque(failedModuleIds); while (!pending.isEmpty()) { final var failedOrBlocked = pending.removeFirst(); final var dependents = dependentsByModule.getOrDefault(failedOrBlocked, Set.of()); @@ -353,14 +360,24 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { return blocked; } - private String moduleKey(final PbsModuleVisibilityValidator.ModuleCoordinates coordinates) { - return coordinates.project() + ":" + String.join("/", coordinates.pathSegments().asList()); + private ModuleId moduleId( + final ModuleTable moduleTable, + final PbsModuleVisibilityValidator.ModuleCoordinates coordinates) { + return moduleId(moduleTable, coordinates.project(), coordinates.pathSegments()); } - private String moduleKey( + private ModuleId moduleId( + final ModuleTable moduleTable, final String project, final ReadOnlyList pathSegments) { - return project + ":" + String.join("/", pathSegments.asList()); + return moduleTable.register(new ModuleReference(project, pathSegments)); + } + + private String renderModuleKey( + final ModuleTable moduleTable, + final ModuleId moduleId) { + final var moduleReference = moduleTable.get(moduleId); + return moduleReference.project() + ":" + String.join("/", moduleReference.pathSegments().asList()); } private static final class MutableModuleUnit { @@ -371,12 +388,12 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { private record ParsedSourceFile( FileId fileId, PbsAst.File ast, - String moduleKey, + ModuleId moduleId, SourceKind sourceKind) { } private record CompiledSourceFile( - String moduleKey, + ModuleId moduleId, p.studio.compiler.models.IRBackendFile irBackendFile) { } } diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleReference.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleReference.java new file mode 100644 index 00000000..adf9f683 --- /dev/null +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleReference.java @@ -0,0 +1,17 @@ +package p.studio.compiler.source.tables; + +import p.studio.utilities.structures.ReadOnlyList; + +import java.util.Objects; + +public record ModuleReference( + String project, + ReadOnlyList pathSegments) { + public ModuleReference { + project = Objects.requireNonNull(project, "project"); + if (project.isBlank()) { + throw new IllegalArgumentException("project must not be blank"); + } + pathSegments = pathSegments == null ? ReadOnlyList.empty() : pathSegments; + } +} diff --git a/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleTable.java b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleTable.java new file mode 100644 index 00000000..2b4ae97c --- /dev/null +++ b/prometeu-compiler/prometeu-compiler-core/src/main/java/p/studio/compiler/source/tables/ModuleTable.java @@ -0,0 +1,10 @@ +package p.studio.compiler.source.tables; + +import p.studio.compiler.source.identifiers.ModuleId; + +public class ModuleTable extends InternTable { + + public ModuleTable() { + super(ModuleId::new); + } +} diff --git a/prometeu-compiler/prometeu-compiler-core/src/test/java/p/studio/compiler/source/tables/ModuleTableTest.java b/prometeu-compiler/prometeu-compiler-core/src/test/java/p/studio/compiler/source/tables/ModuleTableTest.java new file mode 100644 index 00000000..d68c9e39 --- /dev/null +++ b/prometeu-compiler/prometeu-compiler-core/src/test/java/p/studio/compiler/source/tables/ModuleTableTest.java @@ -0,0 +1,31 @@ +package p.studio.compiler.source.tables; + +import org.junit.jupiter.api.Test; +import p.studio.utilities.structures.ReadOnlyList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class ModuleTableTest { + + @Test + void shouldInternSameReferenceWithStableId() { + final var table = new ModuleTable(); + final var reference = new ModuleReference("core", ReadOnlyList.wrap(java.util.List.of("math", "vec"))); + + final var first = table.register(reference); + final var second = table.register(reference); + + assertEquals(first, second); + } + + @Test + void shouldAllocateDifferentIdForDifferentReference() { + final var table = new ModuleTable(); + + final var first = table.register(new ModuleReference("core", ReadOnlyList.wrap(java.util.List.of("math")))); + final var second = table.register(new ModuleReference("core", ReadOnlyList.wrap(java.util.List.of("gfx")))); + + assertNotEquals(first, second); + } +}