コード例 #1
0
        // routine(s) called by the FileContentManager upon updating a file

        /// <summary>
        /// Attempts to compute an incremental update for the change specified by start, count and newText, and updates file accordingly.
        /// The given argument newText replaces the entire lines from start to (but not including) start + count.
        /// If the given change is null, then (only) the currently queued unprocessed changes are processed.
        /// Throws an ArgumentNullException if file is null.
        /// Any other exceptions should be throws (and caught, and possibly re-thrown) during the updating.
        /// </summary>
        internal static void UpdateScopeTacking(this FileContentManager file, TextDocumentContentChangeEvent change)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }

            /// <summary>
            /// Replaces the lines in the range [start, end] with those for the given text.
            /// </summary>
            void ComputeUpdate(int start, int end, string text)
            {
                QsCompilerError.Verify(start >= 0 && end >= start && end < file.NrLines(), "invalid range for update");

                // since both LF and CR in VS cause a line break on their own,
                // we need to check if the change causes subequent CR LF to merge into a single line break
                if (text.StartsWith(Utils.LF) && start > 0 && file.GetLine(start - 1).Text.EndsWith(Utils.CR))
                {
                    text = file.GetLine(--start).Text + text;
                }

                // we need to check if the change causes the next line to merge with the (last) changed line
                if (end + 1 < file.NrLines() && !Utils.EndOfLine.Match(text).Success)
                {
                    text = text + file.GetLine(++end).Text;
                }

                var newLines = Utils.SplitLines(text);
                var count    = end - start + 1;

                // note that the last line in the file won't end with a line break,
                // and is hence only captured by SplitLines if it is not empty
                // -> we therefore manually add the last line in the file if it is empty
                if (newLines.Length == 0 || // the case if the file will be empty after the update
                    (start + count == file.NrLines() && Utils.EndOfLine.Match(newLines.Last()).Success))
                {
                    newLines = newLines.Concat(new string[] { String.Empty }).ToArray();
                }

                QsCompilerError.Verify(newLines.Any(), "should have at least one line to replace");
                file.Update(start, count, newLines);
            }

            file.SyncRoot.EnterUpgradeableReadLock();
            try
            {
                // process the currently queued changes if necessary
                if (file.DequeueUnprocessedChanges(out int start, out string text))
                {
                    ComputeUpdate(start, start, text);
                }

                // process the given change if necessary
                if (change != null)
                {
                    ComputeUpdate(change.Range.Start.Line, change.Range.End.Line, Utils.GetTextChangedLines(file, change));
                }
            }
            finally { file.SyncRoot.ExitUpgradeableReadLock(); }
        }
コード例 #2
0
        /// <summary>
        /// Given the initial indentation of a sequence of CodeLines, and a sequence of code lines with the correct string delimiters set,
        /// computes and sets the correct indentation level and excess bracket positions for each line.
        /// The previous line being null or not provided indicates that there is no previous line.
        /// </summary>
        private static IEnumerable <CodeLine> SetIndentations(IEnumerable <CodeLine> lines, int currentIndentation)
        {
            foreach (var line in lines)
            {
                var updated = line.SetIndentation(currentIndentation);

                var stripped    = RemoveStringsAndComment(updated);
                var nrIndents   = NrIndents(stripped);
                var nrUnindents = NrUnindents(stripped);

                if (currentIndentation - nrUnindents < 0)
                {
                    // in this case it is possible (but not necessarily the case) that there are excess brackets
                    // if closings occur before openings, then we can have excess brackets even when the indentation is larger than zero
                    updated             = updated.SetExcessBrackets(ComputeExcessClosings(updated, currentIndentation));
                    currentIndentation += updated.ExcessBracketPositions.Count();
                }
                else
                {
                    updated = updated.SetExcessBrackets(new List <int>().AsReadOnly());
                }

                currentIndentation += nrIndents - nrUnindents;
                QsCompilerError.Verify(currentIndentation >= 0, "initial indentation should always be larger or equal to zero");
                yield return(updated);
            }
        }
コード例 #3
0
        // TODO: this needs to be made more robust.
        // We currently rely on the fact that all attributes defined by the Q# compiler
        // have a single constructor taking a single string argument.
        private static (string, string)? GetAttribute(MetadataReader metadataReader, CustomAttribute attribute)
        {
            var attrType = GetAttributeType(metadataReader, attribute);

            QsCompilerError.Verify(attrType.HasValue, "the type of the custom attribute could not be determined");
            var(ns, name) = attrType.Value;

            var attrNS = metadataReader.GetString(ns);

            if (attrNS.StartsWith("Microsoft.Quantum", StringComparison.InvariantCulture))
            {
                var attrReader = metadataReader.GetBlobReader(attribute.Value);
                _ = attrReader.ReadUInt16(); // All custom attributes start with 0x0001, so read that now and discard it.
                try
                {
                    var serialization = attrReader.ReadSerializedString(); // FIXME: this needs to be made more robust
                    return(metadataReader.GetString(name), serialization);
                }
                catch
                {
                    return(null);
                }
            }
            return(null);
        }
コード例 #4
0
ファイル: TextProcessor.cs プロジェクト: sw23/qsharp-compiler
        /// <summary>
        /// Extracts the code fragments based on the current file content that need to be re-processed due to content changes on the given lines.
        /// Ignores any whitespace or comments at the beginning of the file (whether they have changed or not).
        /// Ignores any whitespace or comments that occur after the last piece of code in the file.
        /// Throws an ArgumentNullException if any of the arguments is null.
        /// </summary>
        private static IEnumerable <CodeFragment> FragmentsToProcess(this FileContentManager file, SortedSet <int> changedLines)
        {
            // NOTE: I suggest not to touch this routine unless absolutely necessary...(things *will* break)
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (changedLines == null)
            {
                throw new ArgumentNullException(nameof(changedLines));
            }

            var iter       = changedLines.GetEnumerator();
            var lastInFile = LastInFile(file);

            Position processed = new Position(0, 0);

            while (iter.MoveNext())
            {
                QsCompilerError.Verify(0 <= iter.Current && iter.Current < file.NrLines(), "index out of range for changed line");
                if (processed.Line < iter.Current)
                {
                    var statementStart = file.PositionAfterPrevious(new Position(iter.Current, 0));
                    if (processed.IsSmallerThan(statementStart))
                    {
                        processed = statementStart;
                    }
                }

                while (processed.Line <= iter.Current && processed.IsSmallerThan(lastInFile))
                {
                    processed = processed.Copy(); // because we don't want to modify the ending of the previous code fragment ...
                    var nextEnding     = file.FragmentEnd(ref processed);
                    var extractedPiece = file.GetCodeSnippet(new Range {
                        Start = processed, End = nextEnding
                    });

                    // constructing the CodeFragment -
                    // NOTE: its Range.End is the position of the delimiting char (if such a char exists), i.e. the position right after Code ends

                    if (extractedPiece.Length > 0) // length = 0 can occur e.g. if the last piece of code in the file does not terminate with a statement ending
                    {
                        var code = file.GetLine(nextEnding.Line).ExcessBracketPositions.Contains(nextEnding.Character - 1)
                            ? extractedPiece.Substring(0, extractedPiece.Length - 1)
                            : extractedPiece;
                        if (code.Length == 0 || !CodeFragment.DelimitingChars.Contains(code.Last()))
                        {
                            code = $"{code}{CodeFragment.MissingDelimiter}";
                        }

                        var endChar   = nextEnding.Character - (extractedPiece.Length - code.Length) - 1;
                        var codeRange = new Range {
                            Start = processed, End = new Position(nextEnding.Line, endChar)
                        };
                        yield return(new CodeFragment(file.IndentationAt(codeRange.Start), codeRange, code.Substring(0, code.Length - 1), code.Last()));
                    }
                    processed = nextEnding;
                }
            }
        }
コード例 #5
0
        /// <summary>
        /// Logs the given text in the editor.
        /// </summary>
        internal static void LogToWindow(this QsLanguageServer server, string text, MessageType severity)
        {
            var message = AsMessageParams(text, severity);

            QsCompilerError.Verify(server != null && message != null, "cannot log message - given server or text was null");
            _ = server.NotifyClientAsync(Methods.WindowLogMessageName, message);
        }
コード例 #6
0
        /// <summary>
        /// Returns an array with all usages of the identifier at the given position (if any) as DocumentHighlights.
        /// Returns null if some parameters are unspecified (null),
        /// or if the specified position is not a valid position within the currently processed file content,
        /// or if no identifier exists at the specified position at this time.
        /// </summary>
        public static DocumentHighlight[]? DocumentHighlights(this FileContentManager file, CompilationUnit compilation, Position?position)
        {
            DocumentHighlight AsHighlight(Lsp.Range range) =>
            new DocumentHighlight
            {
                Range = range, Kind = DocumentHighlightKind.Read
            };

            if (file == null || position is null)
            {
                return(null);
            }
            var found = file.TryGetReferences(
                compilation,
                position,
                out var declLocation,
                out var locations,
                limitToSourceFiles: ImmutableHashSet.Create(file.FileName));

            if (!found)
            {
                return(null);
            }

            QsCompilerError.Verify(declLocation == null || declLocation.Uri == file.Uri, "location outside current file");
            var highlights = locations.Select(loc =>
            {
                QsCompilerError.Verify(loc.Uri == file.Uri, "location outside current file");
                return(AsHighlight(loc.Range));
            });

            return(declLocation != null
                ? new[] { AsHighlight(declLocation.Range) }.Concat(highlights).ToArray()
                : highlights.ToArray());
        }
コード例 #7
0
        /// <summary>
        /// Logs the tokenization of each file in the given compilation as Information using the given logger.
        /// If the id of a file is consistent with the one assigned to a code snippet,
        /// strips the tokens that correspond to the wrapping defined by WrapSnippet.
        /// Throws an ArgumentNullException if the given compilation is null.
        /// </summary>
        private static void PrintContentTokenization(Compilation compilation, ILogger logger)
        {
            if (compilation == null)
            {
                throw new ArgumentNullException(nameof(compilation));
            }
            foreach (var file in compilation.SourceFiles)
            {
                var tokenization  = compilation.Tokenization[file].Select(tokens => tokens.Select(token => token.Kind));
                var stripWrapping = Options.IsCodeSnippet(file);
                QsCompilerError.Verify(!stripWrapping || tokenization.Count() >= 4,
                                       "expecting at least four lines of code for the compilation of a code snippet");

                if (stripWrapping)
                {
                    tokenization = tokenization.Skip(2).Take(tokenization.Count() - 4).ToImmutableArray();
                }
                var serialization = tokenization
                                    .Select(line => line.Select(item => JsonConvert.SerializeObject(item, Newtonsoft.Json.Formatting.Indented)))
                                    .Zip(Enumerable.Range(1, tokenization.Count()),
                                         (ts, i) => ts.Any() ? $"\n[ln {i}]: \n{String.Join("\n", ts)} \n" : "");

                serialization = new string[] { "" }.Concat(serialization);
                logger.Log(
                    InformationCode.BuiltTokenization,
                    Enumerable.Empty <string>(),
                    stripWrapping ? null : file.Value,
                    messageParam: serialization.ToArray()
                    );
            }
        }
コード例 #8
0
        /// <summary>
        /// Givent a position, verifies that the position is within the given file, and
        /// returns the effective indentation (i.e. the indentation when ignoring excess brackets throughout the file)
        /// at that position (i.e. not including the char at the given position).
        /// </summary>
        internal static int IndentationAt(this FileContentManager file, Position pos)
        {
            if (!file.ContainsPosition(pos))
            {
                throw new ArgumentException("given position is not within file");
            }
            var line  = file.GetLine(pos.Line);
            var index = pos.Column;

            // check if the given position is within a string or a comment, or denotes an excess bracket,
            // and find the next closest position that isn't
            if (index >= line.WithoutEnding.Length)
            {
                return(line.FinalIndentation());                                   // not necessary, but saves doing the rest of the computation
            }
            index = line.FindInCode(trimmed => trimmed.Length - 1, 0, pos.Column); // if the given position is within a string, then this is the most convenient way to get the closest position that isn't..
            if (index < 0)
            {
                return(line.Indentation); // perfectly valid scenario (if there is no relevant code before the given position)
            }

            // check how much the indentation changes in all valid (i.e. non-comment, non-string, non-excess-brackets) code up to that point
            // NOTE: per specification, this routine returns the indentation not incuding the char at the given position,
            // but if this char was within non-code text, then we need to include this char ... see (*)
            var indexInCode = IndexInRelevantCode(index, line);

            QsCompilerError.Verify(indexInCode >= 0, "index in code should be positive");
            var code        = RelevantCode(line).Substring(0, index < pos.Column ? indexInCode + 1 : indexInCode); // (*) - yep, that's a bit awkward, but the cleanest I can come up with right now
            var indentation = line.Indentation + NrIndents(code) - NrUnindents(code);

            QsCompilerError.Verify(indentation >= 0, "computed indentation at any position in the file should be positive");
            return(indentation);
        }
コード例 #9
0
        /// <summary>
        /// Logs the content of each file in the given compilation as Information using the given logger.
        /// If the id of a file is consistent with the one assigned to a code snippet,
        /// strips the lines of code that correspond to the wrapping defined by WrapSnippet.
        /// Throws an ArgumentNullException if the given compilation is null.
        /// </summary>
        private static void PrintFileContentInMemory(Compilation compilation, ILogger logger)
        {
            if (compilation == null)
            {
                throw new ArgumentNullException(nameof(compilation));
            }
            foreach (var file in compilation.SourceFiles)
            {
                IEnumerable <string> inMemory = compilation.FileContent[file];
                var stripWrapping             = Options.IsCodeSnippet(file);
                QsCompilerError.Verify(!stripWrapping || inMemory.Count() >= 4,
                                       "expecting at least four lines of code for the compilation of a code snippet");

                if (stripWrapping)
                {
                    inMemory = inMemory.Skip(2).Take(inMemory.Count() - 4);
                }
                logger.Log(
                    InformationCode.FileContentInMemory,
                    Enumerable.Empty <string>(),
                    stripWrapping ? null : file.Value,
                    messageParam: $"{Environment.NewLine}{String.Concat(inMemory)}"
                    );
            }
        }
コード例 #10
0
                private (TypedExpression Id, TypedExpression Args)? IsValidScope(QsScope? scope)
                {
                    // if the scope has exactly one statement in it and that statement is a call like expression statement
                    if (scope != null &&
                        scope.Statements.Length == 1 &&
                        scope.Statements[0].Statement is QsStatementKind.QsExpressionStatement expr &&
                        expr.Item.ResolvedType.Resolution.IsUnitType &&
                        expr.Item.Expression is ExpressionKind.CallLikeExpression call &&
                        !TypedExpression.IsPartialApplication(expr.Item.Expression) &&
                        call.Item1.Expression is ExpressionKind.Identifier)
                    {
                        var newCallIdentifier = call.Item1;
                        var callTypeArguments = expr.Item.TypeParameterResolutions;

                        // This relies on anything having type parameters must be a global callable.
                        if (newCallIdentifier.Expression is ExpressionKind.Identifier id &&
                            id.Item1 is Identifier.GlobalCallable global &&
                            callTypeArguments.Any())
                        {
                            // We are dissolving the application of arguments here, so the call's type argument
                            // resolutions have to be moved to the 'identifier' sub expression.
                            var combination           = new TypeResolutionCombination(expr.Item);
                            var combinedTypeArguments = combination.CombinedResolutionDictionary.FilterByOrigin(global.Item);
                            QsCompilerError.Verify(combination.IsValid, "failed to combine type parameter resolution");

                            var globalCallable = this.SharedState.Compilation.Namespaces
                                                 .Where(ns => ns.Name.Equals(global.Item.Namespace))
                                                 .Callables()
                                                 .FirstOrDefault(c => c.FullName.Name.Equals(global.Item.Name));

                            QsCompilerError.Verify(globalCallable != null, $"Could not find the global reference {global.Item}.");

                            var callableTypeParameters = globalCallable.Signature.TypeParameters.Select(param =>
                            {
                                var name = param as QsLocalSymbol.ValidName;
                                QsCompilerError.Verify(!(name is null), "Invalid type parameter name.");
                                return(name);
                            });

                            newCallIdentifier = new TypedExpression(
                                ExpressionKind.NewIdentifier(
                                    id.Item1,
                                    QsNullable <ImmutableArray <ResolvedType> > .NewValue(
                                        callableTypeParameters
                                        .Select(x => combinedTypeArguments[Tuple.Create(global.Item, x.Item)]).ToImmutableArray())),
                                TypedExpression.AsTypeArguments(combinedTypeArguments),
                                call.Item1.ResolvedType,
                                call.Item1.InferredInformation,
                                call.Item1.Range);
                        }

                        return(newCallIdentifier, call.Item2);
                    }

                    return(null);
                }
コード例 #11
0
        // external routines for context verification

        /// <summary>
        /// Given the line number of the lines that contain tokens that (possibly) have been modified,
        /// checks which callable declaration they can potentially belong to and returns the fully qualified name of those callables.
        /// Throws an ArgumentNullException if the given file or the collection of changed lines is null.
        /// </summary>
        internal static IEnumerable <(Range, QsQualifiedName)> CallablesWithContentModifications(this FileContentManager file, IEnumerable <int> changedLines)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (changedLines == null)
            {
                throw new ArgumentNullException(nameof(changedLines));
            }

            var lastInFile = file.LastToken()?.GetFragment()?.GetRange()?.End ?? file.End();
            var callables  = file.GetCallableDeclarations().Select(tuple => // these are sorted according to their line number
            {
                var ns = file.TryGetNamespaceAt(tuple.Item2.Start);
                QsCompilerError.Verify(ns != null, "namespace for callable declaration should not be null"); // invalid namespace names default to an unknown namespace name, but remain included in the compilation
                return(tuple.Item2.Start, new QsQualifiedName(NonNullable <string> .New(ns), tuple.Item1));
            }).ToList();

            // NOTE: The range of modifications that has to trigger an update of the syntax tree for a callable
            // does need to go up to and include modifications to the line containing the next callable!
            // Otherwise inserting a callable declaration in the middle of an existing callable does not trigger the right behavior!
            (Range, QsQualifiedName) TypeCheckingRange((Position, QsQualifiedName) lastPreceding, IEnumerable <(Position, QsQualifiedName)> next)
            {
                var callableStart = lastPreceding.Item1;
                var callableEnd   = next.Any() ? next.First().Item1 : lastInFile;

                return(new Range {
                    Start = callableStart, End = callableEnd
                }, lastPreceding.Item2);
            }

            foreach (var lineNr in changedLines)
            {
                bool precedes((Position, QsQualifiedName) tuple) => tuple.Item1.Line < lineNr;

                var preceding = callables.TakeWhile(precedes);
                var following = callables.SkipWhile(precedes);

                if (preceding.Any())
                {
                    yield return(TypeCheckingRange(preceding.Last(), following));
                }
                if (following.Any() && following.First().Item1.Line == lineNr)
                {
                    yield return(TypeCheckingRange(following.First(), following.Skip(1)));
                }
            }
        }
コード例 #12
0
ファイル: Utils.cs プロジェクト: swosti99/qsharp-compiler
        /// <summary>
        /// Returns the position of fst relative to snd under the assumption that both positions are zero-based.
        /// Throws an ArgumentNullException if a given position is null.
        /// Throws an ArgumentException if a given position is not valid, or if fst is smaller than snd.
        /// </summary>
        internal static Position Subtract(this Position fst, Position snd)
        {
            if (!IsValidPosition(fst))
            {
                throw new ArgumentException(nameof(fst));
            }
            if (!IsValidPosition(snd))
            {
                throw new ArgumentException(nameof(snd));
            }
            if (fst.IsSmallerThan(snd))
            {
                throw new ArgumentException(nameof(snd), "the position to subtract from needs to be larger than the position to subract");
            }
            var relPos = new Position(fst.Line - snd.Line, fst.Line == snd.Line ? fst.Character - snd.Character : fst.Character);

            QsCompilerError.Verify(snd.Add(relPos).Equals(fst), "adding the relative position to snd does not equal fst");
            return(relPos);
        }
コード例 #13
0
        /// <summary>
        /// Returns the completion environment at the given position in the file or null if the environment cannot be
        /// determined. Stores the code fragment found at or before the given position into an out parameter.
        /// </summary>
        private static (CompletionScope?, QsFragmentKind?) GetCompletionEnvironment(
            FileContentManager file, Position position, out CodeFragment?fragment)
        {
            if (!file.ContainsPosition(position))
            {
                fragment = null;
                return(null, null);
            }
            var token = GetTokenAtOrBefore(file, position);

            if (token is null)
            {
                fragment = null;
                return(null, null);
            }

            fragment = token.GetFragment();
            var relativeIndentation = fragment.Indentation - file.IndentationAt(position);

            QsCompilerError.Verify(Math.Abs(relativeIndentation) <= 1);

            var parents = new[] { token }
            .Concat(token.GetNonEmptyParents())
            .Skip(relativeIndentation + 1)
            .Select(index => index.GetFragment())
            .ToImmutableList();
            var scope =
                parents.IsEmpty ? CompletionScope.TopLevel
                : parents.FirstOrDefault()?.Kind?.IsNamespaceDeclaration ?? false ? CompletionScope.NamespaceTopLevel
                : parents.Any(parent => parent.Kind?.IsFunctionDeclaration ?? false) ? CompletionScope.Function
                : parents.FirstOrDefault()?.Kind?.IsOperationDeclaration ?? false ? CompletionScope.OperationTopLevel
                : parents.Any(parent => parent.Kind?.IsOperationDeclaration ?? false) ? CompletionScope.Operation
                : null;
            var previous =
                relativeIndentation == 0 && IsPositionAfterDelimiter(file, fragment, position) ? fragment.Kind
                : relativeIndentation == 0 ? token.PreviousOnScope()?.GetFragment().Kind
                : relativeIndentation == 1 ? token.GetNonEmptyParent()?.GetFragment().Kind
                : null;

            return(scope, previous);
        }
コード例 #14
0
                /// <summary>
                /// Creates an operation call from the conditional control API for non-literal Result comparisons.
                /// The equalityScope and inequalityScope cannot both be null.
                /// </summary>
                private TypedExpression?CreateApplyConditionallyExpression(TypedExpression conditionExpr1, TypedExpression conditionExpr2, QsScope?equalityScope, QsScope?inequalityScope)
                {
                    QsCompilerError.Verify(equalityScope != null || inequalityScope != null, $"Cannot have null for both equality and inequality scopes when creating ApplyConditionally expressions.");

                    var equalityInfo   = this.IsValidScope(equalityScope);
                    var inequalityInfo = this.IsValidScope(inequalityScope);

                    if (!equalityInfo.HasValue && equalityScope != null)
                    {
                        return(null); // ToDo: Diagnostic message - equality block exists, but is not valid
                    }

                    if (!inequalityInfo.HasValue && inequalityScope != null)
                    {
                        return(null); // ToDo: Diagnostic message - inequality block exists, but is not valid
                    }

                    equalityInfo ??= this.GetNoOp();
                    inequalityInfo ??= this.GetNoOp();

                    // Get characteristic properties from global id
                    var props = ImmutableHashSet <OpProperty> .Empty;

                    if (equalityInfo.Value.Id.ResolvedType.Resolution is ResolvedTypeKind.Operation op)
                    {
                        props = op.Item2.Characteristics.GetProperties();
                        if (inequalityInfo.HasValue && inequalityInfo.Value.Id.ResolvedType.Resolution is ResolvedTypeKind.Operation defaultOp)
                        {
                            props = props.Intersect(defaultOp.Item2.Characteristics.GetProperties());
                        }
                    }

                    var controlOpInfo = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable)) switch
                    {
                        (true, true) => BuiltIn.ApplyConditionallyCA,
                        (true, false) => BuiltIn.ApplyConditionallyA,
                        (false, true) => BuiltIn.ApplyConditionallyC,
                        (false, false) => BuiltIn.ApplyConditionally
                    };
コード例 #15
0
        /// <summary>
        /// Creates an array literal with the given items, setting the range information to Null.
        /// If no items are given, creates an empty array of type Unit[].
        /// The resolved types for all of the given expressions must match,
        /// none of the given expressions should have a local quantum dependency,
        /// and all range information should be stripped from each given expression.
        /// </summary>
        internal static TypedExpression CreateValueArray(params TypedExpression[] expressions)
        {
            ResolvedType type = null;

            if (expressions.Any())
            {
                type = expressions.First().ResolvedType;
                QsCompilerError.Verify(expressions.All(expr => expr.ResolvedType.Equals(type)));
            }
            else
            {
                type = ResolvedType.New(ResolvedTypeKind.UnitType);
            }

            return(new TypedExpression
                   (
                       ExpressionKind.NewValueArray(expressions.ToImmutableArray()),
                       TypeArgsResolution.Empty,
                       ResolvedType.New(ResolvedTypeKind.NewArrayType(type)),
                       new InferredExpressionInformation(false, false),
                       QsNullable <Tuple <QsPositionInfo, QsPositionInfo> > .Null
                   ));
        }
コード例 #16
0
                /// <summary>
                /// Creates an operation call from the conditional control API for non-literal Result comparisons.
                /// The equalityScope and inequalityScope cannot both be null.
                /// </summary>
                private TypedExpression CreateApplyConditionallyExpression(TypedExpression conditionExpr1, TypedExpression conditionExpr2, QsScope equalityScope, QsScope inequalityScope)
                {
                    QsCompilerError.Verify(equalityScope != null || inequalityScope != null, $"Cannot have null for both equality and inequality scopes when creating ApplyConditionally expressions.");

                    var(isEqualityValid, equalityId, equalityArgs)       = this.IsValidScope(equalityScope);
                    var(isInequaltiyValid, inequalityId, inequalityArgs) = this.IsValidScope(inequalityScope);

                    if (!isEqualityValid && equalityScope != null)
                    {
                        return(null); // ToDo: Diagnostic message - equality block exists, but is not valid
                    }

                    if (!isInequaltiyValid && inequalityScope != null)
                    {
                        return(null); // ToDo: Diagnostic message - inequality block exists, but is not valid
                    }

                    if (equalityScope == null)
                    {
                        (equalityId, equalityArgs) = this.GetNoOp();
                    }
                    else if (inequalityScope == null)
                    {
                        (inequalityId, inequalityArgs) = this.GetNoOp();
                    }

                    // Get characteristic properties from global id
                    var props = ImmutableHashSet <OpProperty> .Empty;

                    if (equalityId.ResolvedType.Resolution is ResolvedTypeKind.Operation op)
                    {
                        props = op.Item2.Characteristics.GetProperties();
                        if (inequalityId != null && inequalityId.ResolvedType.Resolution is ResolvedTypeKind.Operation defaultOp)
                        {
                            props = props.Intersect(defaultOp.Item2.Characteristics.GetProperties());
                        }
                    }

                    BuiltIn controlOpInfo;

                    (bool adj, bool ctl) = (props.Contains(OpProperty.Adjointable), props.Contains(OpProperty.Controllable));
                    if (adj && ctl)
                    {
                        controlOpInfo = BuiltIn.ApplyConditionallyCA;
                    }
                    else if (adj)
                    {
                        controlOpInfo = BuiltIn.ApplyConditionallyA;
                    }
                    else if (ctl)
                    {
                        controlOpInfo = BuiltIn.ApplyConditionallyC;
                    }
                    else
                    {
                        controlOpInfo = BuiltIn.ApplyConditionally;
                    }

                    // Takes a single TypedExpression of type Result and puts in into a
                    // value array expression with the given expression as its only item.
                    TypedExpression BoxResultInArray(TypedExpression expression) =>
                    new TypedExpression(
                        ExpressionKind.NewValueArray(ImmutableArray.Create(expression)),
                        TypeArgsResolution.Empty,
                        ResolvedType.New(ResolvedTypeKind.NewArrayType(ResolvedType.New(ResolvedTypeKind.Result))),
                        new InferredExpressionInformation(false, expression.InferredInformation.HasLocalQuantumDependency),
                        QsNullable <Tuple <QsPositionInfo, QsPositionInfo> > .Null);

                    var equality    = this.CreateValueTupleExpression(equalityId, equalityArgs);
                    var inequality  = this.CreateValueTupleExpression(inequalityId, inequalityArgs);
                    var controlArgs = this.CreateValueTupleExpression(
                        BoxResultInArray(conditionExpr1),
                        BoxResultInArray(conditionExpr2),
                        equality,
                        inequality);
                    var targetArgsTypes = ImmutableArray.Create(equalityArgs.ResolvedType, inequalityArgs.ResolvedType);

                    return(this.CreateControlCall(controlOpInfo, props, controlArgs, targetArgsTypes));
                }
コード例 #17
0
        /// <summary>
        /// Returns the completion environment at the given position in the file or null if the environment cannot be
        /// determined. Stores the code fragment found at or before the given position into an out parameter.
        /// </summary>
        /// <exception cref="ArgumentNullException">Thrown when any argument is null.</exception>
        /// <exception cref="ArgumentException">Thrown when the position is invalid.</exception>
        private static (CompletionScope, QsFragmentKind) GetCompletionEnvironment(
            FileContentManager file, Position position, out CodeFragment fragment)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (!Utils.IsValidPosition(position))
            {
                throw new ArgumentException(nameof(position));
            }
            if (!Utils.IsValidPosition(position, file))
            {
                // FileContentManager.IndentationAt will fail if the position is not within the file.
                fragment = null;
                return(null, null);
            }

            var token = GetTokenAtOrBefore(file, position);

            if (token == null)
            {
                fragment = null;
                return(null, null);
            }

            fragment = token.GetFragment();
            var relativeIndentation = fragment.Indentation - file.IndentationAt(position);

            QsCompilerError.Verify(Math.Abs(relativeIndentation) <= 1);
            var parents =
                new[] { token }.Concat(token.GetNonEmptyParents())
            .Skip(relativeIndentation + 1)
            .Select(t => t.GetFragment());

            CompletionScope scope = null;

            if (!parents.Any())
            {
                scope = CompletionScope.TopLevel;
            }
            else if (parents.Any() && parents.First().Kind.IsNamespaceDeclaration)
            {
                scope = CompletionScope.NamespaceTopLevel;
            }
            else if (parents.Where(parent => parent.Kind.IsFunctionDeclaration).Any())
            {
                scope = CompletionScope.Function;
            }
            else if (parents.Any() && parents.First().Kind.IsOperationDeclaration)
            {
                scope = CompletionScope.OperationTopLevel;
            }
            else if (parents.Where(parent => parent.Kind.IsOperationDeclaration).Any())
            {
                scope = CompletionScope.Operation;
            }

            QsFragmentKind previous = null;

            if (relativeIndentation == 0 && IsPositionAfterDelimiter(file, fragment, position))
            {
                previous = fragment.Kind;
            }
            else if (relativeIndentation == 0)
            {
                previous = token.PreviousOnScope()?.GetFragment().Kind;
            }
            else if (relativeIndentation == 1)
            {
                previous = token.GetNonEmptyParent()?.GetFragment().Kind;
            }

            return(scope, previous);
        }