From 2288ed75f9ddfb195dfd63b229d1619bad4545cd Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 10 Mar 2026 07:10:22 +0000 Subject: [PATCH] refactoring and reducing complexity --- .../compiler/pbs/PbsFrontendCompiler.java | 34 - .../services/PBSFrontendPhaseService.java | 748 +----------------- .../services/PbsImportedSemanticContext.java | 15 + .../PbsImportedSemanticContextService.java | 409 ++++++++++ .../compiler/services/PbsModuleAssembly.java | 42 + .../services/PbsModuleAssemblyService.java | 372 +++++++++ .../services/PbsParsedSourceFile.java | 13 + 7 files changed, 887 insertions(+), 746 deletions(-) create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContext.java create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContextService.java create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssembly.java create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssemblyService.java create mode 100644 prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsParsedSourceFile.java diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java index c0a73e13..e5fe2bb4 100644 --- a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/pbs/PbsFrontendCompiler.java @@ -18,7 +18,6 @@ import p.studio.compiler.source.diagnostics.DiagnosticPhase; import p.studio.compiler.source.identifiers.CallableShapeId; import p.studio.compiler.source.identifiers.CallableId; import p.studio.compiler.source.identifiers.FileId; -import p.studio.compiler.source.identifiers.IntrinsicId; import p.studio.compiler.source.identifiers.ModuleId; import p.studio.compiler.source.identifiers.NameId; import p.studio.compiler.source.identifiers.TypeSurfaceId; @@ -38,7 +37,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; public final class PbsFrontendCompiler { private final PbsFlowSemanticsValidator flowSemanticsValidator = new PbsFlowSemanticsValidator(); @@ -88,38 +86,6 @@ public final class PbsFrontendCompiler { return irBackendFile; } - public IRBackendFile compileParsedFile( - final FileId fileId, - final PbsAst.File ast, - final DiagnosticSink diagnostics) { - return compileParsedFile(fileId, ast, diagnostics, SourceKind.PROJECT, HostAdmissionContext.permissiveDefault()); - } - - public IRBackendFile compileParsedFile( - final FileId fileId, - final PbsAst.File ast, - final DiagnosticSink diagnostics, - final SourceKind sourceKind) { - return compileParsedFile(fileId, ast, diagnostics, sourceKind, HostAdmissionContext.permissiveDefault()); - } - - public IRBackendFile compileParsedFile( - final FileId fileId, - final PbsAst.File ast, - final DiagnosticSink diagnostics, - final SourceKind sourceKind, - final HostAdmissionContext hostAdmissionContext) { - return compileParsedFile( - fileId, - ast, - diagnostics, - sourceKind, - ModuleId.none(), - ReadOnlyList.empty(), - hostAdmissionContext, - new NameTable()); - } - public IRBackendFile compileParsedFile( final FileId fileId, final PbsAst.File ast, 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 360e4505..c7ccce64 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 @@ -5,51 +5,28 @@ import p.studio.compiler.PBSDefinitions; import p.studio.compiler.messages.BuildingIssueSink; import p.studio.compiler.messages.FrontendPhaseContext; import p.studio.compiler.models.IRBackend; -import p.studio.compiler.models.IRReservedMetadata; -import p.studio.compiler.models.ProjectDescriptor; -import p.studio.compiler.models.SourceHandle; -import p.studio.compiler.models.SourceKind; import p.studio.compiler.pbs.PbsFrontendCompiler; -import p.studio.compiler.pbs.ast.PbsAst; import p.studio.compiler.pbs.metadata.PbsReservedMetadataExtractor; -import p.studio.compiler.pbs.lexer.PbsLexer; -import p.studio.compiler.pbs.linking.PbsLinkErrors; -import p.studio.compiler.pbs.linking.PbsModuleVisibilityValidator; -import p.studio.compiler.pbs.parser.PbsBarrelParser; -import p.studio.compiler.pbs.parser.PbsParser; import p.studio.compiler.pbs.stdlib.InterfaceModuleLoader; import p.studio.compiler.pbs.stdlib.ResourceStdlibEnvironmentResolver; import p.studio.compiler.pbs.stdlib.StdlibEnvironmentResolver; -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.identifiers.ProjectId; -import p.studio.compiler.source.tables.FileTable; -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; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @Slf4j public class PBSFrontendPhaseService implements FrontendPhaseService { - private final PbsFrontendCompiler frontendCompiler = new PbsFrontendCompiler(); - private final PbsReservedMetadataExtractor reservedMetadataExtractor = new PbsReservedMetadataExtractor(); - private final PbsModuleVisibilityValidator moduleVisibilityValidator = new PbsModuleVisibilityValidator(); - private final StdlibEnvironmentResolver stdlibEnvironmentResolver; - private final InterfaceModuleLoader interfaceModuleLoader; + private final PbsFrontendCompiler frontendCompiler; + private final PbsModuleAssemblyService moduleAssemblyService; + private final PbsImportedSemanticContextService importedSemanticContextService; public PBSFrontendPhaseService() { this(new ResourceStdlibEnvironmentResolver(), new InterfaceModuleLoader()); @@ -58,8 +35,16 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { PBSFrontendPhaseService( final StdlibEnvironmentResolver stdlibEnvironmentResolver, final InterfaceModuleLoader interfaceModuleLoader) { - this.stdlibEnvironmentResolver = stdlibEnvironmentResolver; - this.interfaceModuleLoader = interfaceModuleLoader; + final var reservedMetadataExtractor = new PbsReservedMetadataExtractor(); + final var moduleVisibilityValidator = new p.studio.compiler.pbs.linking.PbsModuleVisibilityValidator(); + this.frontendCompiler = new PbsFrontendCompiler(); + this.moduleAssemblyService = new PbsModuleAssemblyService( + stdlibEnvironmentResolver, + interfaceModuleLoader, + moduleVisibilityValidator); + this.importedSemanticContextService = new PbsImportedSemanticContextService( + reservedMetadataExtractor, + frontendCompiler); } @Override @@ -69,98 +54,12 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { final LogAggregator logs, final BuildingIssueSink issues) { final var nameTable = ctx.nameTable(); - if (!(ctx.fileTable instanceof FileTable writableFileTable)) { - throw new IllegalStateException("PBS frontend requires writable FileTable for synthetic stdlib sources"); - } - final var irBackendAggregator = IRBackend.aggregator(); - final var parsedSourceFiles = new ArrayList(); - final Map modulesByCoordinates = new LinkedHashMap<>(); - final var moduleTable = new ModuleTable(); - final var moduleIdByFile = new HashMap(); - final var failedModuleIds = new HashSet(); - final var projectIdByName = new HashMap(); - final var defaultSyntheticOwnerProjectId = ctx.stack.reverseTopologicalOrder.isEmpty() - ? null - : ctx.stack.reverseTopologicalOrder.getFirst(); - - for (final var pId : ctx.stack.reverseTopologicalOrder) { - final var projectDescriptor = ctx.projectTable.get(pId); - final var projectSourceKind = ctx.sourceKind(pId); - final var fileIds = ctx.fileTable.getFiles(pId); - projectIdByName.putIfAbsent(projectDescriptor.getName(), pId); - - for (final var fId : fileIds) { - final var sourceHandle = ctx.fileTable.get(fId); - sourceHandle.readUtf8().ifPresentOrElse( - utf8Content -> { - final var coordinates = resolveModuleCoordinates(projectDescriptor, sourceHandle); - final var moduleId = moduleId(moduleTable, coordinates); - final var moduleUnit = modulesByCoordinates.computeIfAbsent( - coordinates, - ignored -> new MutableModuleUnit()); - switch (sourceHandle.getExtension()) { - case "pbs" -> { - 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, moduleId, projectSourceKind)); - if (diagnostics.errorCount() > parseErrorBaseline) { - failedModuleIds.add(moduleId); - } - } - case "barrel" -> { - 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) { - 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)); - failedModuleIds.add(moduleId); - } - } - default -> { - } - } - }, - () -> issues.report(builder -> builder - .error(true) - .message("Failed to read file content: %s".formatted(sourceHandle.toString())))); - } - } - - loadReservedStdlibModules( - modulesByCoordinates, - parsedSourceFiles, - moduleIdByFile, - moduleTable, - writableFileTable, - projectIdByName, - defaultSyntheticOwnerProjectId, - diagnostics, - ctx.stdlibVersion()); - - final var modules = new ArrayList(modulesByCoordinates.size()); - for (final var entry : modulesByCoordinates.entrySet()) { - final var coordinates = entry.getKey(); - final var moduleUnit = entry.getValue(); - modules.add(new PbsModuleVisibilityValidator.ModuleUnit( - coordinates, - ReadOnlyList.wrap(moduleUnit.sources), - ReadOnlyList.wrap(moduleUnit.barrels))); - } - moduleVisibilityValidator.validate(ReadOnlyList.wrap(modules), nameTable, diagnostics); - markModulesWithLinkingErrors(diagnostics, moduleIdByFile, failedModuleIds); - final var moduleDependencyGraph = buildModuleDependencyGraph(parsedSourceFiles, moduleTable); - final var importedSemanticContexts = buildImportedSemanticContexts(parsedSourceFiles, moduleTable); - final var canonicalModulePool = emitModulePool(moduleTable); + final var assembly = moduleAssemblyService.assemble(ctx, nameTable, diagnostics, issues); + final var parsedSourceFiles = assembly.parsedSourceFiles(); + final var importedSemanticContexts = importedSemanticContextService.build(parsedSourceFiles, assembly.moduleTable()); + final var failedModuleIds = assembly.mutableFailedModuleIds(); + final var moduleDependencyGraph = assembly.moduleDependencyGraph(); + final var canonicalModulePool = assembly.canonicalModulePool(); final var compiledSourceFiles = new ArrayList(parsedSourceFiles.size()); for (final var parsedSource : parsedSourceFiles) { @@ -170,7 +69,7 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { } final var importedSemanticContext = importedSemanticContexts.getOrDefault( parsedSource.fileId(), - ImportedSemanticContext.empty()); + PbsImportedSemanticContext.empty()); final var compileErrorBaseline = diagnostics.errorCount(); final var irBackendFile = frontendCompiler.compileParsedFile( parsedSource.fileId(), @@ -190,9 +89,23 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { compiledSourceFiles.add(new CompiledSourceFile(parsedSource.moduleId(), irBackendFile)); } + final var irBackend = mergeCompiledSources( + compiledSourceFiles, + failedModuleIds, + moduleDependencyGraph); + logs.using(log).debug("PBS frontend lowered to IR BE:\n%s".formatted(irBackend)); + return irBackend; + } + + private IRBackend mergeCompiledSources( + final ArrayList compiledSourceFiles, + final Set failedModuleIds, + final Map> moduleDependencyGraph) { + final var irBackendAggregator = IRBackend.aggregator(); final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph); final var entryPointCallableName = PBSDefinitions.PBS.getEntryPointCallableName(); final var entryPointModuleCandidates = new LinkedHashSet(); + for (final var compiledSource : compiledSourceFiles) { if (blockedModuleIds.contains(compiledSource.moduleId())) { continue; @@ -207,559 +120,12 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { } } } + irBackendAggregator.entryPointCallableName(entryPointCallableName); if (entryPointModuleCandidates.size() == 1) { irBackendAggregator.entryPointModuleId(entryPointModuleCandidates.iterator().next()); } - - final var irBackend = irBackendAggregator.emit(); - logs.using(log).debug("PBS frontend lowered to IR BE:\n%s".formatted(irBackend)); - return irBackend; - } - - private void loadReservedStdlibModules( - final Map modulesByCoordinates, - final ArrayList parsedSourceFiles, - final Map moduleIdByFile, - final ModuleTable moduleTable, - final FileTable fileTable, - final Map projectIdByName, - final ProjectId defaultSyntheticOwnerProjectId, - final DiagnosticSink diagnostics, - final int stdlibVersion) { - final var stdlibEnvironment = stdlibEnvironmentResolver.resolve(stdlibVersion); - final var pending = new ArrayDeque(); - final var resolved = new HashSet(); - - enqueueReservedImportsFromKnownModules(modulesByCoordinates, pending); - while (!pending.isEmpty()) { - final var target = pending.removeFirst(); - final var targetId = moduleId(moduleTable, target); - if (!resolved.add(targetId)) { - continue; - } - if (modulesByCoordinates.containsKey(target)) { - continue; - } - - final var moduleSource = stdlibEnvironment.resolveModule(target.project(), target.pathSegments()); - if (moduleSource.isEmpty()) { - continue; - } - - final var syntheticOwnerProjectId = projectIdByName.getOrDefault(target.project(), defaultSyntheticOwnerProjectId); - if (syntheticOwnerProjectId == null) { - continue; - } - final var loadedModule = interfaceModuleLoader.load( - moduleSource.get(), - syntheticOwnerProjectId, - fileTable, - diagnostics); - final var moduleData = new MutableModuleUnit(); - moduleData.sources.addAll(loadedModule.sourceFiles().asList()); - moduleData.barrels.addAll(loadedModule.barrelFiles().asList()); - modulesByCoordinates.put(loadedModule.coordinates(), moduleData); - for (final var sourceFile : loadedModule.sourceFiles()) { - moduleIdByFile.put(sourceFile.fileId(), targetId); - parsedSourceFiles.add(new ParsedSourceFile( - sourceFile.fileId(), - sourceFile.ast(), - targetId, - SourceKind.SDK_INTERFACE)); - } - for (final var barrelFile : loadedModule.barrelFiles()) { - moduleIdByFile.put(barrelFile.fileId(), targetId); - } - enqueueReservedImportsFromSourceFiles(loadedModule.sourceFiles().asList(), pending); - } - } - - private void enqueueReservedImportsFromKnownModules( - final Map modulesByCoordinates, - final ArrayDeque pending) { - for (final var moduleUnit : modulesByCoordinates.values()) { - enqueueReservedImportsFromSourceFiles(moduleUnit.sources, pending); - } - } - - private void enqueueReservedImportsFromSourceFiles( - final Iterable sourceFiles, - final ArrayDeque pending) { - for (final var source : sourceFiles) { - for (final var importDecl : source.ast().imports()) { - final var moduleRef = importDecl.moduleRef(); - if (!isReservedProjectSpace(moduleRef.project())) { - continue; - } - pending.addLast(new PbsModuleVisibilityValidator.ModuleCoordinates( - moduleRef.project(), - moduleRef.pathSegments())); - } - } - } - - private boolean isReservedProjectSpace(final String projectName) { - return "core".equals(projectName) || "sdk".equals(projectName); - } - - private PbsAst.File parseSourceFile( - final FileId fileId, - final String source, - final DiagnosticSink diagnostics, - final SourceKind sourceKind) { - final var tokens = PbsLexer.lex(source, fileId, diagnostics); - final var parseMode = sourceKind == SourceKind.SDK_INTERFACE - ? PbsParser.ParseMode.INTERFACE_MODULE - : PbsParser.ParseMode.ORDINARY; - return PbsParser.parse(tokens, fileId, diagnostics, parseMode); - } - - private PbsAst.BarrelFile parseBarrelFile( - final FileId fileId, - final String source, - final DiagnosticSink diagnostics) { - final var tokens = PbsLexer.lex(source, fileId, diagnostics); - return PbsBarrelParser.parse(tokens, fileId, diagnostics); - } - - private PbsModuleVisibilityValidator.ModuleCoordinates resolveModuleCoordinates( - final ProjectDescriptor projectDescriptor, - final SourceHandle sourceHandle) { - final var sourceRelativePath = sourceRelativePath(projectDescriptor, sourceHandle); - final var modulePath = sourceRelativePath.getParent(); - final var pathSegments = new ArrayList(); - if (modulePath != null) { - for (final var segment : modulePath) { - pathSegments.add(segment.toString()); - } - } - - return new PbsModuleVisibilityValidator.ModuleCoordinates( - projectDescriptor.getName(), - ReadOnlyList.wrap(pathSegments)); - } - - private Path sourceRelativePath( - final ProjectDescriptor projectDescriptor, - final SourceHandle sourceHandle) { - final var canonPath = sourceHandle.getCanonPath(); - for (final var sourceRoot : projectDescriptor.getSourceRoots()) { - if (canonPath.startsWith(sourceRoot)) { - return sourceRoot.relativize(canonPath); - } - } - return sourceHandle.getRelativePath(); - } - - private void markModulesWithLinkingErrors( - final DiagnosticSink diagnostics, - final Map moduleIdByFile, - final Set failedModuleIds) { - for (final var diagnostic : diagnostics) { - if (!diagnostic.getSeverity().isError()) { - continue; - } - if (diagnostic.getPhase() != DiagnosticPhase.LINKING) { - continue; - } - final var moduleId = moduleIdByFile.get(diagnostic.getSpan().getFileId()); - if (moduleId != null) { - failedModuleIds.add(moduleId); - } - } - } - - 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.moduleId(), - ignored -> new HashSet<>()); - for (final var importDecl : parsedSource.ast().imports()) { - final var moduleRef = importDecl.moduleRef(); - moduleDependencies.add(moduleId(moduleTable, moduleRef.project(), moduleRef.pathSegments())); - } - } - return dependenciesByModule; - } - - private Map buildImportedSemanticContexts( - final ArrayList parsedSourceFiles, - final ModuleTable moduleTable) { - final Map> sourcesByModule = new HashMap<>(); - for (final var parsedSource : parsedSourceFiles) { - sourcesByModule - .computeIfAbsent(parsedSource.moduleId(), ignored -> new ArrayList<>()) - .add(parsedSource); - } - - final Map reservedMetadataByModule = new HashMap<>(); - for (final var entry : sourcesByModule.entrySet()) { - var merged = IRReservedMetadata.empty(); - for (final var source : entry.getValue()) { - final var extracted = reservedMetadataExtractor.extract(source.ast(), source.sourceKind()); - merged = mergeReservedMetadata(merged, extracted); - } - reservedMetadataByModule.put(entry.getKey(), merged); - } - final Map>> topDeclsByNameByModule = new HashMap<>(); - for (final var entry : sourcesByModule.entrySet()) { - topDeclsByNameByModule.put(entry.getKey(), indexTopDeclsByName(entry.getValue())); - } - - final Map contexts = new HashMap<>(); - for (final var parsedSource : parsedSourceFiles) { - final var supplementalTopDecls = new ArrayList(); - final var importedCallables = new ArrayList(); - final var importedCallableKeys = new HashSet(); - final var supplementalKeys = new HashSet(); - var importedReservedMetadata = IRReservedMetadata.empty(); - - for (final var importDecl : parsedSource.ast().imports()) { - final var moduleRef = importDecl.moduleRef(); - final var importedModuleId = moduleId(moduleTable, moduleRef.project(), moduleRef.pathSegments()); - final var importedSources = sourcesByModule.get(importedModuleId); - if (importedSources == null || importedSources.isEmpty()) { - continue; - } - final var importedTopDeclsByName = topDeclsByNameByModule.get(importedModuleId); - if (importedTopDeclsByName == null) { - continue; - } - importedReservedMetadata = mergeReservedMetadata( - importedReservedMetadata, - reservedMetadataByModule.getOrDefault(importedModuleId, IRReservedMetadata.empty())); - - for (final var importItem : importDecl.items()) { - final var importName = importItem.name(); - final var localName = importItemLocalName(importItem); - final var directDecls = importedTopDeclsByName.getOrDefault(importName, new ArrayList<>()); - for (final var topDecl : directDecls) { - appendSupplementalTopDecl(topDecl, supplementalTopDecls, supplementalKeys); - appendTypeDependencyDecls(topDecl, importedTopDeclsByName, supplementalTopDecls, supplementalKeys); - if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { - for (final var method : serviceDecl.methods()) { - appendImportedCallable( - importedCallables, - importedCallableKeys, - new PbsFrontendCompiler.ImportedCallableSurface( - importedModuleId, - localName + "." + method.name(), - method.parameters().size(), - returnSlotsFor(method), - frontendCompiler.callableShapeSurfaceOf(method))); - } - continue; - } - if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { - appendImportedCallable( - importedCallables, - importedCallableKeys, - new PbsFrontendCompiler.ImportedCallableSurface( - importedModuleId, - localName, - functionDecl.parameters().size(), - returnSlotsFor(functionDecl), - frontendCompiler.callableShapeSurfaceOf(functionDecl))); - } - } - } - } - - contexts.put(parsedSource.fileId(), new ImportedSemanticContext( - ReadOnlyList.wrap(supplementalTopDecls), - ReadOnlyList.wrap(importedCallables), - importedReservedMetadata)); - } - return contexts; - } - - private Map> indexTopDeclsByName( - final ArrayList moduleSources) { - final Map> index = new HashMap<>(); - for (final var source : moduleSources) { - for (final var topDecl : source.ast().topDecls()) { - final var declName = topDeclName(topDecl); - if (declName == null || declName.isBlank()) { - continue; - } - index.computeIfAbsent(declName, ignored -> new ArrayList<>()).add(topDecl); - } - } - return index; - } - - private void appendTypeDependencyDecls( - final PbsAst.TopDecl rootDecl, - final Map> topDeclsByName, - final ArrayList supplementalTopDecls, - final Set supplementalKeys) { - final var pendingTypeNames = new ArrayDeque(); - final var visitedTypeNames = new HashSet(); - collectReferencedTypeNames(rootDecl, pendingTypeNames); - while (!pendingTypeNames.isEmpty()) { - final var typeName = pendingTypeNames.removeFirst(); - if (!visitedTypeNames.add(typeName)) { - continue; - } - final var candidates = topDeclsByName.get(typeName); - if (candidates == null) { - continue; - } - for (final var candidate : candidates) { - if (!isTypeDecl(candidate)) { - continue; - } - appendSupplementalTopDecl(candidate, supplementalTopDecls, supplementalKeys); - collectReferencedTypeNames(candidate, pendingTypeNames); - } - } - } - - private void collectReferencedTypeNames( - final PbsAst.TopDecl topDecl, - final ArrayDeque sink) { - if (topDecl instanceof PbsAst.StructDecl structDecl) { - for (final var field : structDecl.fields()) { - collectReferencedTypeNames(field.typeRef(), sink); - } - for (final var method : structDecl.methods()) { - collectReferencedTypeNames(method, sink); - } - for (final var ctor : structDecl.ctors()) { - for (final var parameter : ctor.parameters()) { - collectReferencedTypeNames(parameter.typeRef(), sink); - } - } - return; - } - if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) { - for (final var field : builtinTypeDecl.fields()) { - collectReferencedTypeNames(field.typeRef(), sink); - } - for (final var signature : builtinTypeDecl.signatures()) { - collectReferencedTypeNames(signature, sink); - } - return; - } - if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { - for (final var method : serviceDecl.methods()) { - collectReferencedTypeNames(method, sink); - } - return; - } - if (topDecl instanceof PbsAst.HostDecl hostDecl) { - for (final var signature : hostDecl.signatures()) { - collectReferencedTypeNames(signature, sink); - } - return; - } - if (topDecl instanceof PbsAst.ContractDecl contractDecl) { - for (final var signature : contractDecl.signatures()) { - collectReferencedTypeNames(signature, sink); - } - return; - } - if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) { - collectReferencedTypeNames(callbackDecl.parameters(), callbackDecl.returnType(), callbackDecl.resultErrorType(), sink); - return; - } - if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { - collectReferencedTypeNames(functionDecl, sink); - return; - } - if (topDecl instanceof PbsAst.ConstDecl constDecl && constDecl.explicitType() != null) { - collectReferencedTypeNames(constDecl.explicitType(), sink); - } - } - - private void collectReferencedTypeNames( - final PbsAst.FunctionDecl functionDecl, - final ArrayDeque sink) { - collectReferencedTypeNames( - functionDecl.parameters(), - functionDecl.returnType(), - functionDecl.resultErrorType(), - sink); - } - - private void collectReferencedTypeNames( - final PbsAst.FunctionSignature functionSignature, - final ArrayDeque sink) { - collectReferencedTypeNames( - functionSignature.parameters(), - functionSignature.returnType(), - functionSignature.resultErrorType(), - sink); - } - - private void collectReferencedTypeNames( - final ReadOnlyList parameters, - final PbsAst.TypeRef returnType, - final PbsAst.TypeRef resultErrorType, - final ArrayDeque sink) { - for (final var parameter : parameters) { - collectReferencedTypeNames(parameter.typeRef(), sink); - } - collectReferencedTypeNames(returnType, sink); - collectReferencedTypeNames(resultErrorType, sink); - } - - private void collectReferencedTypeNames( - final PbsAst.TypeRef typeRef, - final ArrayDeque sink) { - if (typeRef == null) { - return; - } - switch (typeRef.kind()) { - case SIMPLE -> { - final var simpleName = typeRef.name(); - if (!isPrimitiveTypeName(simpleName)) { - sink.addLast(simpleName); - } - } - case OPTIONAL, GROUP -> collectReferencedTypeNames(typeRef.inner(), sink); - case NAMED_TUPLE -> { - for (final var field : typeRef.fields()) { - collectReferencedTypeNames(field.typeRef(), sink); - } - } - case UNIT, SELF, ERROR -> { - } - } - } - - private boolean isPrimitiveTypeName(final String typeName) { - return "int".equals(typeName) - || "float".equals(typeName) - || "bool".equals(typeName) - || "str".equals(typeName) - || "unit".equals(typeName); - } - - private boolean isTypeDecl(final PbsAst.TopDecl topDecl) { - return topDecl instanceof PbsAst.StructDecl - || topDecl instanceof PbsAst.BuiltinTypeDecl - || topDecl instanceof PbsAst.ServiceDecl - || topDecl instanceof PbsAst.HostDecl - || topDecl instanceof PbsAst.ContractDecl - || topDecl instanceof PbsAst.CallbackDecl - || topDecl instanceof PbsAst.EnumDecl - || topDecl instanceof PbsAst.ErrorDecl; - } - - private void appendSupplementalTopDecl( - final PbsAst.TopDecl topDecl, - final ArrayList supplementalTopDecls, - final Set supplementalKeys) { - final var key = topDeclKey(topDecl); - if (key == null || key.isBlank()) { - return; - } - if (supplementalKeys.add(key)) { - supplementalTopDecls.add(topDecl); - } - } - - private void appendImportedCallable( - final ArrayList importedCallables, - final Set importedCallableKeys, - final PbsFrontendCompiler.ImportedCallableSurface importedCallableSurface) { - final var callableKey = importedCallableSurface.moduleId().getIndex() - + "#" - + importedCallableSurface.callableName() - + "#" - + importedCallableSurface.arity() - + "#" - + importedCallableSurface.shapeSurface(); - if (importedCallableKeys.add(callableKey)) { - importedCallables.add(importedCallableSurface); - } - } - - private String topDeclKey(final PbsAst.TopDecl topDecl) { - final var declName = topDeclName(topDecl); - if (declName == null || declName.isBlank()) { - return null; - } - return topDecl.getClass().getSimpleName() + ":" + declName; - } - - private String topDeclName(final PbsAst.TopDecl topDecl) { - if (topDecl instanceof PbsAst.StructDecl structDecl) { - return structDecl.name(); - } - if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) { - return builtinTypeDecl.name(); - } - if (topDecl instanceof PbsAst.ConstDecl constDecl) { - return constDecl.name(); - } - if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { - return serviceDecl.name(); - } - if (topDecl instanceof PbsAst.HostDecl hostDecl) { - return hostDecl.name(); - } - if (topDecl instanceof PbsAst.ContractDecl contractDecl) { - return contractDecl.name(); - } - if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { - return functionDecl.name(); - } - if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) { - return callbackDecl.name(); - } - if (topDecl instanceof PbsAst.EnumDecl enumDecl) { - return enumDecl.name(); - } - if (topDecl instanceof PbsAst.ErrorDecl errorDecl) { - return errorDecl.name(); - } - return null; - } - - private IRReservedMetadata mergeReservedMetadata( - final IRReservedMetadata left, - final IRReservedMetadata right) { - final var hostBindings = new ArrayList(); - hostBindings.addAll(left.hostMethodBindings().asList()); - hostBindings.addAll(right.hostMethodBindings().asList()); - - final var builtinTypeSurfaces = new ArrayList(); - builtinTypeSurfaces.addAll(left.builtinTypeSurfaces().asList()); - builtinTypeSurfaces.addAll(right.builtinTypeSurfaces().asList()); - - final var builtinConstSurfaces = new ArrayList(); - builtinConstSurfaces.addAll(left.builtinConstSurfaces().asList()); - builtinConstSurfaces.addAll(right.builtinConstSurfaces().asList()); - - final var requiredCapabilities = new HashSet(); - requiredCapabilities.addAll(left.requiredCapabilities().asList()); - requiredCapabilities.addAll(right.requiredCapabilities().asList()); - - return new IRReservedMetadata( - ReadOnlyList.wrap(hostBindings), - ReadOnlyList.wrap(builtinTypeSurfaces), - ReadOnlyList.wrap(builtinConstSurfaces), - ReadOnlyList.wrap(requiredCapabilities.stream().toList())); - } - - private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) { - return switch (functionDecl.returnKind()) { - case INFERRED_UNIT, EXPLICIT_UNIT -> 0; - case PLAIN, RESULT -> 1; - }; - } - - private String importItemLocalName(final PbsAst.ImportItem importItem) { - if (importItem.alias() == null || importItem.alias().isBlank()) { - return importItem.name(); - } - return importItem.alias(); + return irBackendAggregator.emit(); } private Set blockedModulesByDependency( @@ -788,50 +154,8 @@ public class PBSFrontendPhaseService implements FrontendPhaseService { return blocked; } - private ModuleId moduleId( - final ModuleTable moduleTable, - final PbsModuleVisibilityValidator.ModuleCoordinates coordinates) { - return moduleId(moduleTable, coordinates.project(), coordinates.pathSegments()); - } - - private ModuleId moduleId( - final ModuleTable moduleTable, - final String project, - final ReadOnlyList pathSegments) { - return moduleTable.register(new ModuleReference(project, pathSegments)); - } - - private ReadOnlyList emitModulePool(final ModuleTable moduleTable) { - final var pool = new ArrayList(moduleTable.size()); - for (final var moduleId : moduleTable.identifiers()) { - pool.add(moduleTable.get(moduleId)); - } - return ReadOnlyList.wrap(pool); - } - - private static final class MutableModuleUnit { - private final ArrayList sources = new ArrayList<>(); - private final ArrayList barrels = new ArrayList<>(); - } - - private record ParsedSourceFile( - FileId fileId, - PbsAst.File ast, - ModuleId moduleId, - SourceKind sourceKind) { - } - private record CompiledSourceFile( ModuleId moduleId, p.studio.compiler.models.IRBackendFile irBackendFile) { } - - private record ImportedSemanticContext( - ReadOnlyList supplementalTopDecls, - ReadOnlyList importedCallables, - IRReservedMetadata importedReservedMetadata) { - private static ImportedSemanticContext empty() { - return new ImportedSemanticContext(ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty()); - } - } } diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContext.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContext.java new file mode 100644 index 00000000..0b2a838e --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContext.java @@ -0,0 +1,15 @@ +package p.studio.compiler.services; + +import p.studio.compiler.models.IRReservedMetadata; +import p.studio.compiler.pbs.PbsFrontendCompiler; +import p.studio.compiler.pbs.ast.PbsAst; +import p.studio.utilities.structures.ReadOnlyList; + +record PbsImportedSemanticContext( + ReadOnlyList supplementalTopDecls, + ReadOnlyList importedCallables, + IRReservedMetadata importedReservedMetadata) { + static PbsImportedSemanticContext empty() { + return new PbsImportedSemanticContext(ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty()); + } +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContextService.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContextService.java new file mode 100644 index 00000000..9548768e --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsImportedSemanticContextService.java @@ -0,0 +1,409 @@ +package p.studio.compiler.services; + +import p.studio.compiler.models.IRReservedMetadata; +import p.studio.compiler.pbs.PbsFrontendCompiler; +import p.studio.compiler.pbs.ast.PbsAst; +import p.studio.compiler.pbs.metadata.PbsReservedMetadataExtractor; +import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.identifiers.ModuleId; +import p.studio.compiler.source.tables.ModuleTable; +import p.studio.utilities.structures.ReadOnlyList; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +final class PbsImportedSemanticContextService { + private final PbsReservedMetadataExtractor reservedMetadataExtractor; + private final PbsFrontendCompiler frontendCompiler; + + PbsImportedSemanticContextService( + final PbsReservedMetadataExtractor reservedMetadataExtractor, + final PbsFrontendCompiler frontendCompiler) { + this.reservedMetadataExtractor = reservedMetadataExtractor; + this.frontendCompiler = frontendCompiler; + } + + Map build( + final ReadOnlyList parsedSourceFiles, + final ModuleTable moduleTable) { + final Map> sourcesByModule = new HashMap<>(); + for (final var parsedSource : parsedSourceFiles) { + sourcesByModule + .computeIfAbsent(parsedSource.moduleId(), ignored -> new ArrayList<>()) + .add(parsedSource); + } + + final Map reservedMetadataByModule = new HashMap<>(); + for (final var entry : sourcesByModule.entrySet()) { + var merged = IRReservedMetadata.empty(); + for (final var source : entry.getValue()) { + final var extracted = reservedMetadataExtractor.extract(source.ast(), source.sourceKind()); + merged = mergeReservedMetadata(merged, extracted); + } + reservedMetadataByModule.put(entry.getKey(), merged); + } + + final Map>> topDeclsByNameByModule = new HashMap<>(); + for (final var entry : sourcesByModule.entrySet()) { + topDeclsByNameByModule.put(entry.getKey(), indexTopDeclsByName(entry.getValue())); + } + + final Map contexts = new HashMap<>(); + for (final var parsedSource : parsedSourceFiles) { + final var supplementalTopDecls = new ArrayList(); + final var importedCallables = new ArrayList(); + final var importedCallableKeys = new HashSet(); + final var supplementalKeys = new HashSet(); + var importedReservedMetadata = IRReservedMetadata.empty(); + + for (final var importDecl : parsedSource.ast().imports()) { + final var moduleRef = importDecl.moduleRef(); + final var importedModuleId = moduleTable.register( + new p.studio.compiler.source.tables.ModuleReference(moduleRef.project(), moduleRef.pathSegments())); + final var importedSources = sourcesByModule.get(importedModuleId); + if (importedSources == null || importedSources.isEmpty()) { + continue; + } + final var importedTopDeclsByName = topDeclsByNameByModule.get(importedModuleId); + if (importedTopDeclsByName == null) { + continue; + } + importedReservedMetadata = mergeReservedMetadata( + importedReservedMetadata, + reservedMetadataByModule.getOrDefault(importedModuleId, IRReservedMetadata.empty())); + + for (final var importItem : importDecl.items()) { + final var importName = importItem.name(); + final var localName = importItemLocalName(importItem); + final var directDecls = importedTopDeclsByName.getOrDefault(importName, new ArrayList<>()); + for (final var topDecl : directDecls) { + appendSupplementalTopDecl(topDecl, supplementalTopDecls, supplementalKeys); + appendTypeDependencyDecls(topDecl, importedTopDeclsByName, supplementalTopDecls, supplementalKeys); + if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { + for (final var method : serviceDecl.methods()) { + appendImportedCallable( + importedCallables, + importedCallableKeys, + new PbsFrontendCompiler.ImportedCallableSurface( + importedModuleId, + localName + "." + method.name(), + method.parameters().size(), + returnSlotsFor(method), + frontendCompiler.callableShapeSurfaceOf(method))); + } + continue; + } + if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { + appendImportedCallable( + importedCallables, + importedCallableKeys, + new PbsFrontendCompiler.ImportedCallableSurface( + importedModuleId, + localName, + functionDecl.parameters().size(), + returnSlotsFor(functionDecl), + frontendCompiler.callableShapeSurfaceOf(functionDecl))); + } + } + } + } + + contexts.put(parsedSource.fileId(), new PbsImportedSemanticContext( + ReadOnlyList.wrap(supplementalTopDecls), + ReadOnlyList.wrap(importedCallables), + importedReservedMetadata)); + } + + return contexts; + } + + private Map> indexTopDeclsByName( + final List moduleSources) { + final Map> index = new HashMap<>(); + for (final var source : moduleSources) { + for (final var topDecl : source.ast().topDecls()) { + final var declName = topDeclName(topDecl); + if (declName == null || declName.isBlank()) { + continue; + } + index.computeIfAbsent(declName, ignored -> new ArrayList<>()).add(topDecl); + } + } + return index; + } + + private void appendTypeDependencyDecls( + final PbsAst.TopDecl rootDecl, + final Map> topDeclsByName, + final ArrayList supplementalTopDecls, + final Set supplementalKeys) { + final var pendingTypeNames = new ArrayDeque(); + final var visitedTypeNames = new HashSet(); + collectReferencedTypeNames(rootDecl, pendingTypeNames); + while (!pendingTypeNames.isEmpty()) { + final var typeName = pendingTypeNames.removeFirst(); + if (!visitedTypeNames.add(typeName)) { + continue; + } + final var candidates = topDeclsByName.get(typeName); + if (candidates == null) { + continue; + } + for (final var candidate : candidates) { + if (!isTypeDecl(candidate)) { + continue; + } + appendSupplementalTopDecl(candidate, supplementalTopDecls, supplementalKeys); + collectReferencedTypeNames(candidate, pendingTypeNames); + } + } + } + + private void collectReferencedTypeNames( + final PbsAst.TopDecl topDecl, + final ArrayDeque sink) { + if (topDecl instanceof PbsAst.StructDecl structDecl) { + for (final var field : structDecl.fields()) { + collectReferencedTypeNames(field.typeRef(), sink); + } + for (final var method : structDecl.methods()) { + collectReferencedTypeNames(method, sink); + } + for (final var ctor : structDecl.ctors()) { + for (final var parameter : ctor.parameters()) { + collectReferencedTypeNames(parameter.typeRef(), sink); + } + } + return; + } + if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) { + for (final var field : builtinTypeDecl.fields()) { + collectReferencedTypeNames(field.typeRef(), sink); + } + for (final var signature : builtinTypeDecl.signatures()) { + collectReferencedTypeNames(signature, sink); + } + return; + } + if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { + for (final var method : serviceDecl.methods()) { + collectReferencedTypeNames(method, sink); + } + return; + } + if (topDecl instanceof PbsAst.HostDecl hostDecl) { + for (final var signature : hostDecl.signatures()) { + collectReferencedTypeNames(signature, sink); + } + return; + } + if (topDecl instanceof PbsAst.ContractDecl contractDecl) { + for (final var signature : contractDecl.signatures()) { + collectReferencedTypeNames(signature, sink); + } + return; + } + if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) { + collectReferencedTypeNames(callbackDecl.parameters(), callbackDecl.returnType(), callbackDecl.resultErrorType(), sink); + return; + } + if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { + collectReferencedTypeNames(functionDecl, sink); + return; + } + if (topDecl instanceof PbsAst.ConstDecl constDecl && constDecl.explicitType() != null) { + collectReferencedTypeNames(constDecl.explicitType(), sink); + } + } + + private void collectReferencedTypeNames( + final PbsAst.FunctionDecl functionDecl, + final ArrayDeque sink) { + collectReferencedTypeNames( + functionDecl.parameters(), + functionDecl.returnType(), + functionDecl.resultErrorType(), + sink); + } + + private void collectReferencedTypeNames( + final PbsAst.FunctionSignature functionSignature, + final ArrayDeque sink) { + collectReferencedTypeNames( + functionSignature.parameters(), + functionSignature.returnType(), + functionSignature.resultErrorType(), + sink); + } + + private void collectReferencedTypeNames( + final ReadOnlyList parameters, + final PbsAst.TypeRef returnType, + final PbsAst.TypeRef resultErrorType, + final ArrayDeque sink) { + for (final var parameter : parameters) { + collectReferencedTypeNames(parameter.typeRef(), sink); + } + collectReferencedTypeNames(returnType, sink); + collectReferencedTypeNames(resultErrorType, sink); + } + + private void collectReferencedTypeNames( + final PbsAst.TypeRef typeRef, + final ArrayDeque sink) { + if (typeRef == null) { + return; + } + switch (typeRef.kind()) { + case SIMPLE -> { + final var simpleName = typeRef.name(); + if (!isPrimitiveTypeName(simpleName)) { + sink.addLast(simpleName); + } + } + case OPTIONAL, GROUP -> collectReferencedTypeNames(typeRef.inner(), sink); + case NAMED_TUPLE -> { + for (final var field : typeRef.fields()) { + collectReferencedTypeNames(field.typeRef(), sink); + } + } + case UNIT, SELF, ERROR -> { + } + } + } + + private boolean isPrimitiveTypeName(final String typeName) { + return "int".equals(typeName) + || "float".equals(typeName) + || "bool".equals(typeName) + || "str".equals(typeName) + || "unit".equals(typeName); + } + + private boolean isTypeDecl(final PbsAst.TopDecl topDecl) { + return topDecl instanceof PbsAst.StructDecl + || topDecl instanceof PbsAst.BuiltinTypeDecl + || topDecl instanceof PbsAst.ServiceDecl + || topDecl instanceof PbsAst.HostDecl + || topDecl instanceof PbsAst.ContractDecl + || topDecl instanceof PbsAst.CallbackDecl + || topDecl instanceof PbsAst.EnumDecl + || topDecl instanceof PbsAst.ErrorDecl; + } + + private void appendSupplementalTopDecl( + final PbsAst.TopDecl topDecl, + final ArrayList supplementalTopDecls, + final Set supplementalKeys) { + final var key = topDeclKey(topDecl); + if (key == null || key.isBlank()) { + return; + } + if (supplementalKeys.add(key)) { + supplementalTopDecls.add(topDecl); + } + } + + private void appendImportedCallable( + final ArrayList importedCallables, + final Set importedCallableKeys, + final PbsFrontendCompiler.ImportedCallableSurface importedCallableSurface) { + final var callableKey = importedCallableSurface.moduleId().getIndex() + + "#" + + importedCallableSurface.callableName() + + "#" + + importedCallableSurface.arity() + + "#" + + importedCallableSurface.shapeSurface(); + if (importedCallableKeys.add(callableKey)) { + importedCallables.add(importedCallableSurface); + } + } + + private String topDeclKey(final PbsAst.TopDecl topDecl) { + final var declName = topDeclName(topDecl); + if (declName == null || declName.isBlank()) { + return null; + } + return topDecl.getClass().getSimpleName() + ":" + declName; + } + + private String topDeclName(final PbsAst.TopDecl topDecl) { + if (topDecl instanceof PbsAst.StructDecl structDecl) { + return structDecl.name(); + } + if (topDecl instanceof PbsAst.BuiltinTypeDecl builtinTypeDecl) { + return builtinTypeDecl.name(); + } + if (topDecl instanceof PbsAst.ConstDecl constDecl) { + return constDecl.name(); + } + if (topDecl instanceof PbsAst.ServiceDecl serviceDecl) { + return serviceDecl.name(); + } + if (topDecl instanceof PbsAst.HostDecl hostDecl) { + return hostDecl.name(); + } + if (topDecl instanceof PbsAst.ContractDecl contractDecl) { + return contractDecl.name(); + } + if (topDecl instanceof PbsAst.FunctionDecl functionDecl) { + return functionDecl.name(); + } + if (topDecl instanceof PbsAst.CallbackDecl callbackDecl) { + return callbackDecl.name(); + } + if (topDecl instanceof PbsAst.EnumDecl enumDecl) { + return enumDecl.name(); + } + if (topDecl instanceof PbsAst.ErrorDecl errorDecl) { + return errorDecl.name(); + } + return null; + } + + private IRReservedMetadata mergeReservedMetadata( + final IRReservedMetadata left, + final IRReservedMetadata right) { + final var hostBindings = new ArrayList(); + hostBindings.addAll(left.hostMethodBindings().asList()); + hostBindings.addAll(right.hostMethodBindings().asList()); + + final var builtinTypeSurfaces = new ArrayList(); + builtinTypeSurfaces.addAll(left.builtinTypeSurfaces().asList()); + builtinTypeSurfaces.addAll(right.builtinTypeSurfaces().asList()); + + final var builtinConstSurfaces = new ArrayList(); + builtinConstSurfaces.addAll(left.builtinConstSurfaces().asList()); + builtinConstSurfaces.addAll(right.builtinConstSurfaces().asList()); + + final var requiredCapabilities = new HashSet(); + requiredCapabilities.addAll(left.requiredCapabilities().asList()); + requiredCapabilities.addAll(right.requiredCapabilities().asList()); + + return new IRReservedMetadata( + ReadOnlyList.wrap(hostBindings), + ReadOnlyList.wrap(builtinTypeSurfaces), + ReadOnlyList.wrap(builtinConstSurfaces), + ReadOnlyList.wrap(requiredCapabilities.stream().toList())); + } + + private int returnSlotsFor(final PbsAst.FunctionDecl functionDecl) { + return switch (functionDecl.returnKind()) { + case INFERRED_UNIT, EXPLICIT_UNIT -> 0; + case PLAIN, RESULT -> 1; + }; + } + + private String importItemLocalName(final PbsAst.ImportItem importItem) { + if (importItem.alias() == null || importItem.alias().isBlank()) { + return importItem.name(); + } + return importItem.alias(); + } +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssembly.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssembly.java new file mode 100644 index 00000000..e1f05835 --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssembly.java @@ -0,0 +1,42 @@ +package p.studio.compiler.services; + +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 java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +record PbsModuleAssembly( + ReadOnlyList parsedSourceFiles, + Map> moduleDependencyGraph, + ReadOnlyList canonicalModulePool, + Set failedModuleIds, + ModuleTable moduleTable) { + PbsModuleAssembly { + parsedSourceFiles = parsedSourceFiles == null ? ReadOnlyList.empty() : parsedSourceFiles; + canonicalModulePool = canonicalModulePool == null ? ReadOnlyList.empty() : canonicalModulePool; + failedModuleIds = failedModuleIds == null ? Set.of() : Set.copyOf(failedModuleIds); + + if (moduleDependencyGraph == null || moduleDependencyGraph.isEmpty()) { + moduleDependencyGraph = Map.of(); + } else { + final var copiedGraph = new HashMap>(); + for (final var entry : moduleDependencyGraph.entrySet()) { + copiedGraph.put(entry.getKey(), Set.copyOf(entry.getValue() == null ? Set.of() : entry.getValue())); + } + moduleDependencyGraph = Map.copyOf(copiedGraph); + } + + if (moduleTable == null) { + moduleTable = new ModuleTable(); + } + } + + Set mutableFailedModuleIds() { + return new HashSet<>(failedModuleIds); + } +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssemblyService.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssemblyService.java new file mode 100644 index 00000000..77824804 --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsModuleAssemblyService.java @@ -0,0 +1,372 @@ +package p.studio.compiler.services; + +import p.studio.compiler.messages.BuildingIssueSink; +import p.studio.compiler.messages.FrontendPhaseContext; +import p.studio.compiler.models.ProjectDescriptor; +import p.studio.compiler.models.SourceHandle; +import p.studio.compiler.models.SourceKind; +import p.studio.compiler.pbs.ast.PbsAst; +import p.studio.compiler.pbs.lexer.PbsLexer; +import p.studio.compiler.pbs.linking.PbsLinkErrors; +import p.studio.compiler.pbs.linking.PbsModuleVisibilityValidator; +import p.studio.compiler.pbs.parser.PbsBarrelParser; +import p.studio.compiler.pbs.parser.PbsParser; +import p.studio.compiler.pbs.stdlib.InterfaceModuleLoader; +import p.studio.compiler.pbs.stdlib.StdlibEnvironmentResolver; +import p.studio.compiler.source.Span; +import p.studio.compiler.source.diagnostics.DiagnosticPhase; +import p.studio.compiler.source.diagnostics.DiagnosticSink; +import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.identifiers.ModuleId; +import p.studio.compiler.source.identifiers.ProjectId; +import p.studio.compiler.source.tables.FileTable; +import p.studio.compiler.source.tables.ModuleReference; +import p.studio.compiler.source.tables.ModuleTable; +import p.studio.compiler.source.tables.NameTable; +import p.studio.utilities.structures.ReadOnlyList; + +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +final class PbsModuleAssemblyService { + private final StdlibEnvironmentResolver stdlibEnvironmentResolver; + private final InterfaceModuleLoader interfaceModuleLoader; + private final PbsModuleVisibilityValidator moduleVisibilityValidator; + + PbsModuleAssemblyService( + final StdlibEnvironmentResolver stdlibEnvironmentResolver, + final InterfaceModuleLoader interfaceModuleLoader, + final PbsModuleVisibilityValidator moduleVisibilityValidator) { + this.stdlibEnvironmentResolver = stdlibEnvironmentResolver; + this.interfaceModuleLoader = interfaceModuleLoader; + this.moduleVisibilityValidator = moduleVisibilityValidator; + } + + PbsModuleAssembly assemble( + final FrontendPhaseContext ctx, + final NameTable nameTable, + final DiagnosticSink diagnostics, + final BuildingIssueSink issues) { + if (!(ctx.fileTable instanceof FileTable writableFileTable)) { + throw new IllegalStateException("PBS frontend requires writable FileTable for synthetic stdlib sources"); + } + + final var parsedSourceFiles = new ArrayList(); + final Map modulesByCoordinates = new LinkedHashMap<>(); + final var moduleTable = new ModuleTable(); + final var moduleIdByFile = new HashMap(); + final var failedModuleIds = new HashSet(); + final var projectIdByName = new HashMap(); + final var defaultSyntheticOwnerProjectId = ctx.stack.reverseTopologicalOrder.isEmpty() + ? null + : ctx.stack.reverseTopologicalOrder.getFirst(); + + for (final var projectId : ctx.stack.reverseTopologicalOrder) { + final var projectDescriptor = ctx.projectTable.get(projectId); + final var projectSourceKind = ctx.sourceKind(projectId); + final var fileIds = ctx.fileTable.getFiles(projectId); + projectIdByName.putIfAbsent(projectDescriptor.getName(), projectId); + + for (final var fileId : fileIds) { + final var sourceHandle = ctx.fileTable.get(fileId); + sourceHandle.readUtf8().ifPresentOrElse( + utf8Content -> consumeSourceFile( + projectDescriptor, + projectSourceKind, + sourceHandle, + fileId, + utf8Content, + diagnostics, + modulesByCoordinates, + moduleTable, + moduleIdByFile, + failedModuleIds, + parsedSourceFiles), + () -> issues.report(builder -> builder + .error(true) + .message("Failed to read file content: %s".formatted(sourceHandle.toString())))); + } + } + + loadReservedStdlibModules( + modulesByCoordinates, + parsedSourceFiles, + moduleIdByFile, + moduleTable, + writableFileTable, + projectIdByName, + defaultSyntheticOwnerProjectId, + diagnostics, + ctx.stdlibVersion()); + + final var modules = new ArrayList(modulesByCoordinates.size()); + for (final var entry : modulesByCoordinates.entrySet()) { + modules.add(new PbsModuleVisibilityValidator.ModuleUnit( + entry.getKey(), + ReadOnlyList.wrap(entry.getValue().sources), + ReadOnlyList.wrap(entry.getValue().barrels))); + } + + moduleVisibilityValidator.validate(ReadOnlyList.wrap(modules), nameTable, diagnostics); + markModulesWithLinkingErrors(diagnostics, moduleIdByFile, failedModuleIds); + + return new PbsModuleAssembly( + ReadOnlyList.wrap(parsedSourceFiles), + buildModuleDependencyGraph(parsedSourceFiles, moduleTable), + emitModulePool(moduleTable), + failedModuleIds, + moduleTable); + } + + private void consumeSourceFile( + final ProjectDescriptor projectDescriptor, + final SourceKind projectSourceKind, + final SourceHandle sourceHandle, + final FileId fileId, + final String utf8Content, + final DiagnosticSink diagnostics, + final Map modulesByCoordinates, + final ModuleTable moduleTable, + final Map moduleIdByFile, + final Set failedModuleIds, + final ArrayList parsedSourceFiles) { + final var coordinates = resolveModuleCoordinates(projectDescriptor, sourceHandle); + final var moduleId = moduleId(moduleTable, coordinates); + final var moduleUnit = modulesByCoordinates.computeIfAbsent(coordinates, ignored -> new MutableModuleUnit()); + + switch (sourceHandle.getExtension()) { + case "pbs" -> { + moduleIdByFile.put(fileId, moduleId); + final var parseErrorBaseline = diagnostics.errorCount(); + final var ast = parseSourceFile(fileId, utf8Content, diagnostics, projectSourceKind); + moduleUnit.sources.add(new PbsModuleVisibilityValidator.SourceFile(fileId, ast)); + parsedSourceFiles.add(new PbsParsedSourceFile(fileId, ast, moduleId, projectSourceKind)); + if (diagnostics.errorCount() > parseErrorBaseline) { + failedModuleIds.add(moduleId); + } + } + case "barrel" -> { + moduleIdByFile.put(fileId, moduleId); + if ("mod.barrel".equals(sourceHandle.getFilename())) { + final var parseErrorBaseline = diagnostics.errorCount(); + final var barrelAst = parseBarrelFile(fileId, utf8Content, diagnostics); + moduleUnit.barrels.add(new PbsModuleVisibilityValidator.BarrelFile(fileId, barrelAst)); + if (diagnostics.errorCount() > parseErrorBaseline) { + 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(fileId, 0, utf8Content.getBytes(StandardCharsets.UTF_8).length)); + failedModuleIds.add(moduleId); + } + } + default -> { + } + } + } + + private void loadReservedStdlibModules( + final Map modulesByCoordinates, + final ArrayList parsedSourceFiles, + final Map moduleIdByFile, + final ModuleTable moduleTable, + final FileTable fileTable, + final Map projectIdByName, + final ProjectId defaultSyntheticOwnerProjectId, + final DiagnosticSink diagnostics, + final int stdlibVersion) { + final var stdlibEnvironment = stdlibEnvironmentResolver.resolve(stdlibVersion); + final var pending = new ArrayDeque(); + final var resolved = new HashSet(); + + enqueueReservedImportsFromKnownModules(modulesByCoordinates, pending); + while (!pending.isEmpty()) { + final var target = pending.removeFirst(); + final var targetId = moduleId(moduleTable, target); + if (!resolved.add(targetId)) { + continue; + } + if (modulesByCoordinates.containsKey(target)) { + continue; + } + + final var moduleSource = stdlibEnvironment.resolveModule(target.project(), target.pathSegments()); + if (moduleSource.isEmpty()) { + continue; + } + + final var syntheticOwnerProjectId = projectIdByName.getOrDefault(target.project(), defaultSyntheticOwnerProjectId); + if (syntheticOwnerProjectId == null) { + continue; + } + final var loadedModule = interfaceModuleLoader.load( + moduleSource.get(), + syntheticOwnerProjectId, + fileTable, + diagnostics); + final var moduleData = new MutableModuleUnit(); + moduleData.sources.addAll(loadedModule.sourceFiles().asList()); + moduleData.barrels.addAll(loadedModule.barrelFiles().asList()); + modulesByCoordinates.put(loadedModule.coordinates(), moduleData); + for (final var sourceFile : loadedModule.sourceFiles()) { + moduleIdByFile.put(sourceFile.fileId(), targetId); + parsedSourceFiles.add(new PbsParsedSourceFile( + sourceFile.fileId(), + sourceFile.ast(), + targetId, + SourceKind.SDK_INTERFACE)); + } + for (final var barrelFile : loadedModule.barrelFiles()) { + moduleIdByFile.put(barrelFile.fileId(), targetId); + } + enqueueReservedImportsFromSourceFiles(loadedModule.sourceFiles().asList(), pending); + } + } + + private void enqueueReservedImportsFromKnownModules( + final Map modulesByCoordinates, + final ArrayDeque pending) { + for (final var moduleUnit : modulesByCoordinates.values()) { + enqueueReservedImportsFromSourceFiles(moduleUnit.sources, pending); + } + } + + private void enqueueReservedImportsFromSourceFiles( + final Iterable sourceFiles, + final ArrayDeque pending) { + for (final var source : sourceFiles) { + for (final var importDecl : source.ast().imports()) { + final var moduleRef = importDecl.moduleRef(); + if (!isReservedProjectSpace(moduleRef.project())) { + continue; + } + pending.addLast(new PbsModuleVisibilityValidator.ModuleCoordinates( + moduleRef.project(), + moduleRef.pathSegments())); + } + } + } + + private boolean isReservedProjectSpace(final String projectName) { + return "core".equals(projectName) || "sdk".equals(projectName); + } + + private PbsAst.File parseSourceFile( + final FileId fileId, + final String source, + final DiagnosticSink diagnostics, + final SourceKind sourceKind) { + final var tokens = PbsLexer.lex(source, fileId, diagnostics); + final var parseMode = sourceKind == SourceKind.SDK_INTERFACE + ? PbsParser.ParseMode.INTERFACE_MODULE + : PbsParser.ParseMode.ORDINARY; + return PbsParser.parse(tokens, fileId, diagnostics, parseMode); + } + + private PbsAst.BarrelFile parseBarrelFile( + final FileId fileId, + final String source, + final DiagnosticSink diagnostics) { + final var tokens = PbsLexer.lex(source, fileId, diagnostics); + return PbsBarrelParser.parse(tokens, fileId, diagnostics); + } + + private PbsModuleVisibilityValidator.ModuleCoordinates resolveModuleCoordinates( + final ProjectDescriptor projectDescriptor, + final SourceHandle sourceHandle) { + final var sourceRelativePath = sourceRelativePath(projectDescriptor, sourceHandle); + final var modulePath = sourceRelativePath.getParent(); + final var pathSegments = new ArrayList(); + if (modulePath != null) { + for (final var segment : modulePath) { + pathSegments.add(segment.toString()); + } + } + + return new PbsModuleVisibilityValidator.ModuleCoordinates( + projectDescriptor.getName(), + ReadOnlyList.wrap(pathSegments)); + } + + private Path sourceRelativePath( + final ProjectDescriptor projectDescriptor, + final SourceHandle sourceHandle) { + final var canonPath = sourceHandle.getCanonPath(); + for (final var sourceRoot : projectDescriptor.getSourceRoots()) { + if (canonPath.startsWith(sourceRoot)) { + return sourceRoot.relativize(canonPath); + } + } + return sourceHandle.getRelativePath(); + } + + private void markModulesWithLinkingErrors( + final DiagnosticSink diagnostics, + final Map moduleIdByFile, + final Set failedModuleIds) { + for (final var diagnostic : diagnostics) { + if (!diagnostic.getSeverity().isError()) { + continue; + } + if (diagnostic.getPhase() != DiagnosticPhase.LINKING) { + continue; + } + final var moduleId = moduleIdByFile.get(diagnostic.getSpan().getFileId()); + if (moduleId != null) { + failedModuleIds.add(moduleId); + } + } + } + + 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.moduleId(), + ignored -> new HashSet<>()); + for (final var importDecl : parsedSource.ast().imports()) { + final var moduleRef = importDecl.moduleRef(); + moduleDependencies.add(moduleId(moduleTable, moduleRef.project(), moduleRef.pathSegments())); + } + } + return dependenciesByModule; + } + + private ModuleId moduleId( + final ModuleTable moduleTable, + final PbsModuleVisibilityValidator.ModuleCoordinates coordinates) { + return moduleId(moduleTable, coordinates.project(), coordinates.pathSegments()); + } + + private ModuleId moduleId( + final ModuleTable moduleTable, + final String project, + final ReadOnlyList pathSegments) { + return moduleTable.register(new ModuleReference(project, pathSegments)); + } + + private ReadOnlyList emitModulePool(final ModuleTable moduleTable) { + final var pool = new ArrayList(moduleTable.size()); + for (final var moduleId : moduleTable.identifiers()) { + pool.add(moduleTable.get(moduleId)); + } + return ReadOnlyList.wrap(pool); + } + + private static final class MutableModuleUnit { + private final ArrayList sources = new ArrayList<>(); + private final ArrayList barrels = new ArrayList<>(); + } +} diff --git a/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsParsedSourceFile.java b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsParsedSourceFile.java new file mode 100644 index 00000000..3e107337 --- /dev/null +++ b/prometeu-compiler/frontends/prometeu-frontend-pbs/src/main/java/p/studio/compiler/services/PbsParsedSourceFile.java @@ -0,0 +1,13 @@ +package p.studio.compiler.services; + +import p.studio.compiler.models.SourceKind; +import p.studio.compiler.pbs.ast.PbsAst; +import p.studio.compiler.source.identifiers.FileId; +import p.studio.compiler.source.identifiers.ModuleId; + +record PbsParsedSourceFile( + FileId fileId, + PbsAst.File ast, + ModuleId moduleId, + SourceKind sourceKind) { +}