refactoring and reducing complexity

This commit is contained in:
bQUARKz 2026-03-10 07:10:22 +00:00
parent 2bc99cc901
commit 2288ed75f9
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
7 changed files with 887 additions and 746 deletions

View File

@ -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,

View File

@ -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<ParsedSourceFile>();
final Map<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates = new LinkedHashMap<>();
final var moduleTable = new ModuleTable();
final var moduleIdByFile = new HashMap<FileId, ModuleId>();
final var failedModuleIds = new HashSet<ModuleId>();
final var projectIdByName = new HashMap<String, ProjectId>();
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<PbsModuleVisibilityValidator.ModuleUnit>(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<CompiledSourceFile>(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<CompiledSourceFile> compiledSourceFiles,
final Set<ModuleId> failedModuleIds,
final Map<ModuleId, Set<ModuleId>> moduleDependencyGraph) {
final var irBackendAggregator = IRBackend.aggregator();
final var blockedModuleIds = blockedModulesByDependency(failedModuleIds, moduleDependencyGraph);
final var entryPointCallableName = PBSDefinitions.PBS.getEntryPointCallableName();
final var entryPointModuleCandidates = new LinkedHashSet<ModuleId>();
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<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates,
final ArrayList<ParsedSourceFile> parsedSourceFiles,
final Map<FileId, ModuleId> moduleIdByFile,
final ModuleTable moduleTable,
final FileTable fileTable,
final Map<String, ProjectId> projectIdByName,
final ProjectId defaultSyntheticOwnerProjectId,
final DiagnosticSink diagnostics,
final int stdlibVersion) {
final var stdlibEnvironment = stdlibEnvironmentResolver.resolve(stdlibVersion);
final var pending = new ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates>();
final var resolved = new HashSet<ModuleId>();
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<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates,
final ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates> pending) {
for (final var moduleUnit : modulesByCoordinates.values()) {
enqueueReservedImportsFromSourceFiles(moduleUnit.sources, pending);
}
}
private void enqueueReservedImportsFromSourceFiles(
final Iterable<PbsModuleVisibilityValidator.SourceFile> sourceFiles,
final ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates> 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<String>();
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<FileId, ModuleId> moduleIdByFile,
final Set<ModuleId> 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<ModuleId, Set<ModuleId>> buildModuleDependencyGraph(
final ArrayList<ParsedSourceFile> parsedSourceFiles,
final ModuleTable moduleTable) {
final Map<ModuleId, Set<ModuleId>> 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<FileId, ImportedSemanticContext> buildImportedSemanticContexts(
final ArrayList<ParsedSourceFile> parsedSourceFiles,
final ModuleTable moduleTable) {
final Map<ModuleId, ArrayList<ParsedSourceFile>> sourcesByModule = new HashMap<>();
for (final var parsedSource : parsedSourceFiles) {
sourcesByModule
.computeIfAbsent(parsedSource.moduleId(), ignored -> new ArrayList<>())
.add(parsedSource);
}
final Map<ModuleId, IRReservedMetadata> 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<ModuleId, Map<String, ArrayList<PbsAst.TopDecl>>> topDeclsByNameByModule = new HashMap<>();
for (final var entry : sourcesByModule.entrySet()) {
topDeclsByNameByModule.put(entry.getKey(), indexTopDeclsByName(entry.getValue()));
}
final Map<FileId, ImportedSemanticContext> contexts = new HashMap<>();
for (final var parsedSource : parsedSourceFiles) {
final var supplementalTopDecls = new ArrayList<PbsAst.TopDecl>();
final var importedCallables = new ArrayList<PbsFrontendCompiler.ImportedCallableSurface>();
final var importedCallableKeys = new HashSet<String>();
final var supplementalKeys = new HashSet<String>();
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<String, ArrayList<PbsAst.TopDecl>> indexTopDeclsByName(
final ArrayList<ParsedSourceFile> moduleSources) {
final Map<String, ArrayList<PbsAst.TopDecl>> 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<String, ArrayList<PbsAst.TopDecl>> topDeclsByName,
final ArrayList<PbsAst.TopDecl> supplementalTopDecls,
final Set<String> supplementalKeys) {
final var pendingTypeNames = new ArrayDeque<String>();
final var visitedTypeNames = new HashSet<String>();
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<String> 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<String> sink) {
collectReferencedTypeNames(
functionDecl.parameters(),
functionDecl.returnType(),
functionDecl.resultErrorType(),
sink);
}
private void collectReferencedTypeNames(
final PbsAst.FunctionSignature functionSignature,
final ArrayDeque<String> sink) {
collectReferencedTypeNames(
functionSignature.parameters(),
functionSignature.returnType(),
functionSignature.resultErrorType(),
sink);
}
private void collectReferencedTypeNames(
final ReadOnlyList<PbsAst.Parameter> parameters,
final PbsAst.TypeRef returnType,
final PbsAst.TypeRef resultErrorType,
final ArrayDeque<String> 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<String> 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<PbsAst.TopDecl> supplementalTopDecls,
final Set<String> supplementalKeys) {
final var key = topDeclKey(topDecl);
if (key == null || key.isBlank()) {
return;
}
if (supplementalKeys.add(key)) {
supplementalTopDecls.add(topDecl);
}
}
private void appendImportedCallable(
final ArrayList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables,
final Set<String> 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<IRReservedMetadata.HostMethodBinding>();
hostBindings.addAll(left.hostMethodBindings().asList());
hostBindings.addAll(right.hostMethodBindings().asList());
final var builtinTypeSurfaces = new ArrayList<IRReservedMetadata.BuiltinTypeSurface>();
builtinTypeSurfaces.addAll(left.builtinTypeSurfaces().asList());
builtinTypeSurfaces.addAll(right.builtinTypeSurfaces().asList());
final var builtinConstSurfaces = new ArrayList<IRReservedMetadata.BuiltinConstSurface>();
builtinConstSurfaces.addAll(left.builtinConstSurfaces().asList());
builtinConstSurfaces.addAll(right.builtinConstSurfaces().asList());
final var requiredCapabilities = new HashSet<String>();
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<ModuleId> 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<String> pathSegments) {
return moduleTable.register(new ModuleReference(project, pathSegments));
}
private ReadOnlyList<ModuleReference> emitModulePool(final ModuleTable moduleTable) {
final var pool = new ArrayList<ModuleReference>(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<PbsModuleVisibilityValidator.SourceFile> sources = new ArrayList<>();
private final ArrayList<PbsModuleVisibilityValidator.BarrelFile> 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<PbsAst.TopDecl> supplementalTopDecls,
ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables,
IRReservedMetadata importedReservedMetadata) {
private static ImportedSemanticContext empty() {
return new ImportedSemanticContext(ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty());
}
}
}

View File

@ -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<PbsAst.TopDecl> supplementalTopDecls,
ReadOnlyList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables,
IRReservedMetadata importedReservedMetadata) {
static PbsImportedSemanticContext empty() {
return new PbsImportedSemanticContext(ReadOnlyList.empty(), ReadOnlyList.empty(), IRReservedMetadata.empty());
}
}

View File

@ -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<FileId, PbsImportedSemanticContext> build(
final ReadOnlyList<PbsParsedSourceFile> parsedSourceFiles,
final ModuleTable moduleTable) {
final Map<ModuleId, ArrayList<PbsParsedSourceFile>> sourcesByModule = new HashMap<>();
for (final var parsedSource : parsedSourceFiles) {
sourcesByModule
.computeIfAbsent(parsedSource.moduleId(), ignored -> new ArrayList<>())
.add(parsedSource);
}
final Map<ModuleId, IRReservedMetadata> 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<ModuleId, Map<String, ArrayList<PbsAst.TopDecl>>> topDeclsByNameByModule = new HashMap<>();
for (final var entry : sourcesByModule.entrySet()) {
topDeclsByNameByModule.put(entry.getKey(), indexTopDeclsByName(entry.getValue()));
}
final Map<FileId, PbsImportedSemanticContext> contexts = new HashMap<>();
for (final var parsedSource : parsedSourceFiles) {
final var supplementalTopDecls = new ArrayList<PbsAst.TopDecl>();
final var importedCallables = new ArrayList<PbsFrontendCompiler.ImportedCallableSurface>();
final var importedCallableKeys = new HashSet<String>();
final var supplementalKeys = new HashSet<String>();
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<String, ArrayList<PbsAst.TopDecl>> indexTopDeclsByName(
final List<PbsParsedSourceFile> moduleSources) {
final Map<String, ArrayList<PbsAst.TopDecl>> 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<String, ArrayList<PbsAst.TopDecl>> topDeclsByName,
final ArrayList<PbsAst.TopDecl> supplementalTopDecls,
final Set<String> supplementalKeys) {
final var pendingTypeNames = new ArrayDeque<String>();
final var visitedTypeNames = new HashSet<String>();
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<String> 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<String> sink) {
collectReferencedTypeNames(
functionDecl.parameters(),
functionDecl.returnType(),
functionDecl.resultErrorType(),
sink);
}
private void collectReferencedTypeNames(
final PbsAst.FunctionSignature functionSignature,
final ArrayDeque<String> sink) {
collectReferencedTypeNames(
functionSignature.parameters(),
functionSignature.returnType(),
functionSignature.resultErrorType(),
sink);
}
private void collectReferencedTypeNames(
final ReadOnlyList<PbsAst.Parameter> parameters,
final PbsAst.TypeRef returnType,
final PbsAst.TypeRef resultErrorType,
final ArrayDeque<String> 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<String> 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<PbsAst.TopDecl> supplementalTopDecls,
final Set<String> supplementalKeys) {
final var key = topDeclKey(topDecl);
if (key == null || key.isBlank()) {
return;
}
if (supplementalKeys.add(key)) {
supplementalTopDecls.add(topDecl);
}
}
private void appendImportedCallable(
final ArrayList<PbsFrontendCompiler.ImportedCallableSurface> importedCallables,
final Set<String> 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<IRReservedMetadata.HostMethodBinding>();
hostBindings.addAll(left.hostMethodBindings().asList());
hostBindings.addAll(right.hostMethodBindings().asList());
final var builtinTypeSurfaces = new ArrayList<IRReservedMetadata.BuiltinTypeSurface>();
builtinTypeSurfaces.addAll(left.builtinTypeSurfaces().asList());
builtinTypeSurfaces.addAll(right.builtinTypeSurfaces().asList());
final var builtinConstSurfaces = new ArrayList<IRReservedMetadata.BuiltinConstSurface>();
builtinConstSurfaces.addAll(left.builtinConstSurfaces().asList());
builtinConstSurfaces.addAll(right.builtinConstSurfaces().asList());
final var requiredCapabilities = new HashSet<String>();
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();
}
}

View File

@ -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<PbsParsedSourceFile> parsedSourceFiles,
Map<ModuleId, Set<ModuleId>> moduleDependencyGraph,
ReadOnlyList<ModuleReference> canonicalModulePool,
Set<ModuleId> 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<ModuleId, Set<ModuleId>>();
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<ModuleId> mutableFailedModuleIds() {
return new HashSet<>(failedModuleIds);
}
}

View File

@ -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<PbsParsedSourceFile>();
final Map<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates = new LinkedHashMap<>();
final var moduleTable = new ModuleTable();
final var moduleIdByFile = new HashMap<FileId, ModuleId>();
final var failedModuleIds = new HashSet<ModuleId>();
final var projectIdByName = new HashMap<String, ProjectId>();
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<PbsModuleVisibilityValidator.ModuleUnit>(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<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates,
final ModuleTable moduleTable,
final Map<FileId, ModuleId> moduleIdByFile,
final Set<ModuleId> failedModuleIds,
final ArrayList<PbsParsedSourceFile> 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<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates,
final ArrayList<PbsParsedSourceFile> parsedSourceFiles,
final Map<FileId, ModuleId> moduleIdByFile,
final ModuleTable moduleTable,
final FileTable fileTable,
final Map<String, ProjectId> projectIdByName,
final ProjectId defaultSyntheticOwnerProjectId,
final DiagnosticSink diagnostics,
final int stdlibVersion) {
final var stdlibEnvironment = stdlibEnvironmentResolver.resolve(stdlibVersion);
final var pending = new ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates>();
final var resolved = new HashSet<ModuleId>();
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<PbsModuleVisibilityValidator.ModuleCoordinates, MutableModuleUnit> modulesByCoordinates,
final ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates> pending) {
for (final var moduleUnit : modulesByCoordinates.values()) {
enqueueReservedImportsFromSourceFiles(moduleUnit.sources, pending);
}
}
private void enqueueReservedImportsFromSourceFiles(
final Iterable<PbsModuleVisibilityValidator.SourceFile> sourceFiles,
final ArrayDeque<PbsModuleVisibilityValidator.ModuleCoordinates> 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<String>();
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<FileId, ModuleId> moduleIdByFile,
final Set<ModuleId> 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<ModuleId, Set<ModuleId>> buildModuleDependencyGraph(
final ArrayList<PbsParsedSourceFile> parsedSourceFiles,
final ModuleTable moduleTable) {
final Map<ModuleId, Set<ModuleId>> 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<String> pathSegments) {
return moduleTable.register(new ModuleReference(project, pathSegments));
}
private ReadOnlyList<ModuleReference> emitModulePool(final ModuleTable moduleTable) {
final var pool = new ArrayList<ModuleReference>(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<PbsModuleVisibilityValidator.SourceFile> sources = new ArrayList<>();
private final ArrayList<PbsModuleVisibilityValidator.BarrelFile> barrels = new ArrayList<>();
}
}

View File

@ -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) {
}