/// <summary>
        /// Returns the index in the fragment text corresponding to the given absolute position.
        /// </summary>
        /// <exception cref="ArgumentException">Thrown when the position is outside the fragment range.</exception>
        /// <exception cref="ArgumentNullException">Thrown when any argument is null.</exception>
        private static int GetTextIndexFromPosition(CodeFragment fragment, Position position)
        {
            if (fragment == null)
            {
                throw new ArgumentNullException(nameof(fragment));
            }
            if (position == null)
            {
                throw new ArgumentNullException(nameof(position));
            }

            var relativeLine      = position.Line - fragment.GetRange().Start.Line;
            var lines             = Utils.SplitLines(fragment.Text).DefaultIfEmpty("");
            var relativeCharacter =
                relativeLine == 0 ? position.Character - fragment.GetRange().Start.Character : position.Character;

            if (relativeLine < 0 ||
                relativeLine >= lines.Count() ||
                relativeCharacter < 0 ||
                relativeCharacter > lines.ElementAt(relativeLine).Length)
            {
                throw new ArgumentException("Position is outside the fragment range", nameof(position));
            }
            return(lines.Take(relativeLine).Sum(line => line.Length) + relativeCharacter);
        }
示例#2
0
        /// <summary>
        /// Calls the Q# parser on each fragment, splitting one fragment into several if necessary
        /// (i.e. modifies the list of given fragments!).
        /// Fragments for which the code only consists of whitespace are left unchanged (i.e. the Kind remains set to null).
        /// Adds a suitable error to the returned diagnostics for each fragment that cannot be processed.
        /// Raises an ArgumentNullException if the given diagnostics or fragments are null.
        /// </summary>
        private static IEnumerable <Diagnostic> ParseCode(ref List <CodeFragment> fragments, string filename)
        {
            if (fragments == null)
            {
                throw new ArgumentNullException(nameof(fragments));
            }
            var processedFragments = new List <CodeFragment>(fragments.Count());
            var diagnostics        = new List <Diagnostic>();

            foreach (var snippet in fragments)
            {
                var snippetStart = snippet.GetRange().Start;
                var outputs      = Parsing.ProcessCodeFragment(snippet.Text);
                for (var outputIndex = 0; outputIndex < outputs.Length; ++outputIndex)
                {
                    var output        = outputs[outputIndex];
                    var fragmentRange = DiagnosticTools.GetAbsoluteRange(snippetStart, output.Range);
                    var fragment      = new CodeFragment(
                        snippet.Indentation,
                        fragmentRange,
                        output.Text.Value,
                        outputIndex == outputs.Length - 1 ? snippet.FollowedBy : CodeFragment.MissingDelimiter,
                        output.Kind);
                    processedFragments.Add(fragment);

                    var checkEnding = true; // if there is already a diagnostic overlapping with the ending, then don't bother checking the ending
                    foreach (var fragmentDiagnostic in output.Diagnostics)
                    {
                        var generated = Diagnostics.Generate(filename, fragmentDiagnostic, fragmentRange.Start);
                        diagnostics.Add(generated);

                        var fragmentEnd = fragment.GetRange().End;
                        var diagnosticGoesUpToFragmentEnd = fragmentEnd.IsWithinRange(generated.Range) || fragmentEnd.Equals(generated.Range.End);
                        if (fragmentDiagnostic.Diagnostic.IsError && diagnosticGoesUpToFragmentEnd)
                        {
                            checkEnding = false;
                        }
                    }
                    if (checkEnding)
                    {
                        diagnostics.AddRange(fragment.CheckFragmentDelimiters(filename));
                    }
                }
                if (outputs.Length == 0)
                {
                    processedFragments.Add(snippet); // keep empty fragments around (note that the kind is set to null in this case!)
                }
            }
            QsCompilerError.RaiseOnFailure(() => ContextBuilder.VerifyTokenOrdering(processedFragments), "processed fragments are not ordered properly and/or overlap");
            fragments = processedFragments;
            return(diagnostics);
        }
示例#3
0
        /// <summary>
        /// Compares the saved fragment ending of the given fragment against the expected continuation and
        /// adds the corresponding error to the returned diagnostics if they don't match.
        /// Throws an ArgumentException if the code fragment kind of the given fragment is unspecified (i.e. null).
        /// Throws an ArgumentNullException if the diagnostics are null.
        /// </summary>
        private static IEnumerable <Diagnostic> CheckFragmentDelimiters(this CodeFragment fragment, string filename)
        {
            if (fragment?.Kind == null)
            {
                throw new ArgumentException("missing specification of the fragment kind");
            }
            var code = fragment.Kind.InvalidEnding;

            if (Diagnostics.ExpectedEnding(code) != fragment.FollowedBy)
            {
                yield return(Errors.InvalidFragmentEnding(filename, code, fragment.GetRange().End));
            }
        }
示例#4
0
        /// <summary>
        /// Returns true if the given token is fully included in the given range.
        /// Throws an ArgumentNullException if token or the range delimiters are null.
        /// Throws an ArgumentException if the given range is not valid.
        /// </summary>
        internal static bool IsWithinRange(this CodeFragment token, Range range)
        {
            if (token == null)
            {
                throw new ArgumentNullException(nameof(token));
            }
            if (!Utils.IsValidRange(range))
            {
                throw new ArgumentException("invalid range");
            }
            var tokenRange = token.GetRange();

            return(tokenRange.Start.IsWithinRange(range) && tokenRange.End.IsWithinRange(range, includeEnd: true));
        }
        /// <summary>
        /// Returns a substring of the fragment text before the given position.
        /// <para/>
        /// If the fragment is null or the position is after the fragment's delimiter, returns the empty string. If the
        /// position is after the end of the fragment text but before the delimiter, the entire text is returned with a
        /// space character appended to it.
        /// </summary>
        /// <exception cref="ArgumentNullException">Thrown when file or position is null.</exception>
        /// <exception cref="ArgumentException">Thrown when the position is invalid.</exception>
        private static string GetFragmentTextBeforePosition(
            FileContentManager file, CodeFragment fragment, Position position)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (!Utils.IsValidPosition(position))
            {
                throw new ArgumentException(nameof(position));
            }

            if (fragment == null || IsPositionAfterDelimiter(file, fragment, position))
            {
                return("");
            }
            return(fragment.GetRange().End.IsSmallerThan(position)
                ? fragment.Text + " "
                : fragment.Text.Substring(0, GetTextIndexFromPosition(fragment, position)));
        }
        /// <summary>
        /// Returns the position of the delimiting character that follows the given code fragment.
        /// </summary>
        /// <exception cref="ArgumentException">Thrown when the code fragment has a missing delimiter.</exception>
        /// <exception cref="ArgumentNullException">Thrown when any argument is null.</exception>
        private static Position GetDelimiterPosition(FileContentManager file, CodeFragment fragment)
        {
            if (file == null)
            {
                throw new ArgumentNullException(nameof(file));
            }
            if (fragment == null)
            {
                throw new ArgumentNullException(nameof(fragment));
            }
            if (fragment.FollowedBy == CodeFragment.MissingDelimiter)
            {
                throw new ArgumentException("Code fragment has a missing delimiter", nameof(fragment));
            }

            var end      = fragment.GetRange().End;
            var position = file.FragmentEnd(ref end);

            return(new Position(position.Line, position.Character - 1));
        }
示例#7
0
        /// <summary>
        /// Comparing for equality by value,
        /// returns the index of the first element in the given list of CodeFragments that matches the given token,
        /// or -1 if no such element exists.
        /// Throws an ArgumentNullException if the given list is null.
        /// </summary>
        internal static int FindByValue(this IReadOnlyList <CodeFragment> list, CodeFragment token)
        {
            if (list == null)
            {
                throw new ArgumentNullException(nameof(list));
            }
            if (token == null)
            {
                var nrNonNull = list.TakeWhile(x => x != null).Count();
                return(nrNonNull == list.Count ? -1 : nrNonNull);
            }

            var index      = -1;
            var tokenRange = token.GetRange();

            while (++index < list.Count && list[index].GetRange().Start.IsSmallerThan(tokenRange.Start))
            {
            }
            return(index < list.Count && list[index].Equals(token) ? index : -1);
        }