/// <nodoc /> protected async Task <Possible <ISourceFile> > TryParseSpec(SpecWithOwningModule specWithOwningModule) { var pathToSpec = specWithOwningModule.Path; var maybeResolver = GetOrFindModuleResolver(pathToSpec, specWithOwningModule.OwningModule.Descriptor); // TODO: if the spec we are parsing belongs to the prelude, we override the automatic export namespace configuration for that spec to false. This is a temporary hack! // Rationale: A top-level export is one of the ways to split internal vs external modules. With this DScript-specific // change around automatic namespace export, we've lost the ability to distinguish this as soon as an // inner member is exported. This means that the DS prelude is identified as an external module and therefore not merged. // A better solution: turn the DScript prelude into a true external module, where all declarations are exported. // but merge all those in the checker (where the prelude is nicely identified as such) as globals, so there is no // need to explicitly import the prelude anywhere (checked this already, and it works). Not doing it now because // that would mean a breaking change in the prelude that the typescript-based type checker won't be able to deal with // today. // If we're processing the prelude, then we need to use prelude-specific parsing options. // withQualifierFunction needs to be generated when the owning module is a V2 one // TODO: consider splitting the parsing options into UserConfigurableParsingOptions and ParsingOptions // The first one is the one that should be exposed beyond this class var parsingOptions = specWithOwningModule.OwningModule.Descriptor == m_designatedPrelude?.Descriptor ? ParsingOptions.GetPreludeParsingOptions(m_parsingOptions.EscapeIdentifiers) : m_parsingOptions .WithFailOnMissingSemicolons(true) .WithGenerateWithQualifierFunctionForEveryNamespace(specWithOwningModule.OwningModule.ResolutionSemantics == NameResolutionSemantics.ImplicitProjectReferences); // Intentionally switched from monadic syntax (with maybe.Then) to avoid additional closure allocations. if (!maybeResolver.Succeeded) { return(maybeResolver.Failure); } var maybeParsedFile = await maybeResolver.Result.TryParseAsync(pathToSpec, specWithOwningModule.OwningModule.ModuleConfigFile, parsingOptions); // Check if there are any diagnostics reported by the parser if (!maybeParsedFile.Succeeded) { return(maybeParsedFile.Failure); } var parsedFile = maybeParsedFile.Result; return(parsedFile.ParseDiagnostics.Count != 0 ? new ParsingFailure(specWithOwningModule.OwningModuleDescriptor, parsedFile) : new Possible <ISourceFile>(parsedFile)); }
/// <summary> /// Parses a <param name="specWithOwningModule"/> and adds it to the set of modules being constructed. /// Additionally, it finds out if the spec has external module dependencies and schedule those for parsing if needed. /// </summary> private async Task ProcessQueuedItemForParsing(SpecWithOwningModule specWithOwningModule) { if (!m_modulesToBeParsed.TryGetValue(specWithOwningModule.OwningModule.Descriptor, out ModuleUnderConstruction owningModule)) { // If the module is no longer under construction, that means that somebody else already completed it // so we just return return; } try { // Tries to parse the spec var maybeParsedFile = await TryParseSpec(specWithOwningModule); ISourceFile parsedFile; if (!maybeParsedFile.Succeeded) { ReportFailureAndCancelParsingIfNeeded(maybeParsedFile.Failure); // If there is a failure, then two things are possible: // 1) The spec was parsed but it contains parsing errors. In this case we add it to the module anyway // 2) Another type of failure happened (e.g. the module resolver for that spec could not be found). In this case // we notify that a failure happened so we can keep track of when a module under construction is complete var parsingFailure = maybeParsedFile.Failure as ParsingFailure; if (parsingFailure != null) { parsedFile = parsingFailure.SourceFile; } else { owningModule.ParsingFailed(); return; } } else { parsedFile = maybeParsedFile.Result; } // We enqueue the spec external dependencies and update the parsed file external dependencies var enqueueResult = await EnqueueSpecDependenciesIfAny(owningModule, parsedFile); if (!enqueueResult.Succeeded) { ReportFailureAndCancelParsingIfNeeded(enqueueResult.Failure); } // Now that we are done parsing the spec, we add it to the module-to-be // It is important to add the file to the module after scheduling dependencies, since scheduling // also updates the source file external references. Otherwise, some other thread // may complete the module and update all its internal references at the same time, which // is not thread safe var addResult = owningModule.AddParsedSpec(specWithOwningModule.Path, parsedFile); if (addResult != ModuleUnderConstruction.AddParsedSpecResult.SpecIsCandidateForInjectingQualifiers) { ScheduleSourceFileBinding(new ParsedSpecWithOwningModule(parsedFile: parsedFile, owningModule: specWithOwningModule.OwningModule)); } } finally { // If the module is completed and there are no more modules to complete, we are done CheckForCompletionAndSignalQueueIfDone(owningModule); } }