refactoring and reducing complexity
This commit is contained in:
parent
2bc99cc901
commit
2288ed75f9
@ -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,
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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<>();
|
||||
}
|
||||
}
|
||||
@ -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) {
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user