Example #1
0
        /// <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));
        }
Example #2
0
        /// <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);
            }
        }