Ejemplo n.º 1
0
        public IEnumerable <IParseResult <IEnumerable <TResult> > > Parse(ICursor <TSource> source)
        {
            firstParser = null;
            bool first = true;

            // See the AllParser.Parse method for an explanation of the optimization that is provided by this stack
            var branches = new Stack <ICursor <TSource> >();

            using (var root = source.Branch())
            {
                branches.Push(root);

                var query = ParseResult.ReturnSuccessMany <TResult>(0)
                            .SelectMany(
                    parsers.Select(
                        parser => (Func <Tuple <IParseResult <IEnumerable <TResult> >, bool>, IEnumerable <IParseResult <IEnumerable <TResult> > > >)
                            (value =>
                {
                    if (first)
                    {
                        first       = false;
                        firstParser = parser;
                    }

                    var branch = branches.Peek();

                    // Item2 is only true when value.Item1 is the last element of its sequence.
                    if (value.Item2)
                    {
                        branch.Move(value.Item1.Length);

                        return(parser.Parse(branch));
                    }
                    else
                    {
                        var remainder = branch.Remainder(value.Item1.Length);

                        branches.Push(remainder);

                        return(parser.Parse(remainder)
                               .OnErrorOrDisposed(() =>
                        {
                            /* Finally() cannot be used here.  SelectMany generates each value as a Tuple, indicating whether it's
                             * the last value in the sequence.  To gather that info it uses a side-effect that causes it to look-ahead
                             * in the enumerator, which would cause Finally to execute and dispose of the branch before it's used
                             * by the child sequence.  SelectMany only calls Dispose when the sequence is no longer in use.
                             */
                            branches.Pop().Dispose();
                        }));
                    }
                })),
                    (firstResult, secondResult) => firstResult.Concat(secondResult));

                // Use an iterator block to ensure that the local variables in this method aren't shared between enumerators.
                foreach (var result in query)
                {
                    yield return(result);
                }
            }
        }
        public ICursor <T> Branch()
        {
            var branch = cursor.Branch();

            branches.Add(branch);

            return(new ParserCursorBranch(branch, () => branches.Remove(branch)));
        }
Ejemplo n.º 3
0
            public ICursor <T> Branch()
            {
                var next = branch.Branch();

                branches.Add(next);

                return(new ParserCursorBranch(next, () => branches.Remove(next)));
            }
        public IEnumerable <IParseResult <IEnumerable <TResult> > > Parse(ICursor <TSource> source)
        {
            int matchCount      = 0;
            int remainingLength = 0;

            while (!source.AtEndOfSequence &&
                   (untilCount == unlimitedCount || matchCount < untilCount) &&
                   (untilPredicate == null || !untilPredicate(source)))
            {
                bool hasResult = false;
                int  length    = 0;

                var branch = source.Branch();

                var values = parser.Parse(branch)
                             .Finally(branch.Dispose)
                             .Select(
                    result =>
                {
                    if (!hasResult)
                    {
                        matchCount++;
                        hasResult = true;
                    }

                    length = Math.Max(length, result.Length);

                    return(result.Value);
                });

                yield return(ParseResult.Create(values, length: 1));

                /* Assume that the results have been iterated by the caller at this point;
                 * otherwise, the cursor's state cannot be determined when the results
                 * are eventually iterated, causing unpredictable results.
                 *
                 * We must respect the greediness of the results unless the length is zero since the
                 * cursor would have already moved to the following element.  It is acceptable to ignore
                 * zero-length results because marking an entirely non-greedy parser as ambiguous would
                 * otherwise cause the parser to continously parse the first element indefinitely.
                 */
                if (length > 0)
                {
                    remainingLength = length - 1;
                }
                else if (remainingLength > 0)
                {
                    remainingLength--;
                }
            }

            if (remainingLength > 0)
            {
                yield return(ParseResult.SuccessMany <TResult>(remainingLength));
            }
        }
Ejemplo n.º 5
0
        public static ICursor <TSource> Remainder <TSource>(this ICursor <TSource> cursor, int skip)
        {
            Contract.Requires(cursor != null);
            Contract.Requires(skip >= 0);
            Contract.Ensures(cursor.IsForwardOnly == Contract.OldValue(cursor.IsForwardOnly));
            Contract.Ensures(Contract.Result <ICursor <TSource> >() != null);
            Contract.Ensures(Contract.Result <ICursor <TSource> >().IsForwardOnly == cursor.IsForwardOnly);
            Contract.Ensures(Contract.Result <ICursor <TSource> >().IsSequenceTerminated == cursor.IsSequenceTerminated);
            Contract.Ensures(Contract.Result <ICursor <TSource> >().LatestIndex == cursor.LatestIndex);
            Contract.Ensures(Contract.Result <ICursor <TSource> >().CurrentIndex ==
                             (cursor.AtEndOfSequence
        ? cursor.CurrentIndex
        : cursor.IsSequenceTerminated
          ? Math.Min(cursor.CurrentIndex + skip, cursor.LatestIndex + 1)
          : cursor.CurrentIndex + skip));

            var branch = cursor.Branch();

            Contract.Assert(branch.IsSequenceTerminated == cursor.IsSequenceTerminated);
            Contract.Assert(branch.CurrentIndex == cursor.CurrentIndex);
            Contract.Assert(branch.LatestIndex == cursor.LatestIndex);
            Contract.Assert(branch.AtEndOfSequence == cursor.AtEndOfSequence);

            if (!branch.AtEndOfSequence)
            {
                int count = skip;

                if (branch.IsSequenceTerminated && branch.CurrentIndex + count > branch.LatestIndex + 1)
                {
                    count = (branch.LatestIndex + 1) - branch.CurrentIndex;

                    Contract.Assert(cursor.CurrentIndex + count == cursor.LatestIndex + 1);
                }

                branch.Move(count);

                Contract.Assert(branch.CurrentIndex == cursor.CurrentIndex + count);
                Contract.Assert(cursor.IsSequenceTerminated || branch.CurrentIndex == cursor.CurrentIndex + skip);
                Contract.Assert(!cursor.IsSequenceTerminated || cursor.CurrentIndex + skip <= cursor.LatestIndex + 1 || branch.CurrentIndex == cursor.LatestIndex + 1);
                Contract.Assume(!cursor.IsSequenceTerminated || branch.CurrentIndex == Math.Min(cursor.CurrentIndex + skip, cursor.LatestIndex + 1));
            }
            else
            {
                Contract.Assert(branch.CurrentIndex == cursor.CurrentIndex);
            }

            return(branch);
        }
Ejemplo n.º 6
0
        private static IEnumerable <IParseResult <IEnumerable <TResult> > > AtLeastIterator <TSource, TSeparator, TResult>(
            ICursor <TSource> source,
            IParser <TSource, TResult> parser,
            int count,
            int maximum,
            IParser <TSource, TSeparator> separator,
            bool nonGreedy)
        {
            Contract.Assume(source != null);
            Contract.Assume(source.IsForwardOnly);
            Contract.Assume(parser != null);
            Contract.Assume(count >= 0);
            Contract.Assume(maximum == -1 || maximum >= count);
            Contract.Assume(maximum != 0);

            // TODO: Update this method to properly support multi-result parsers.

            /* The current implementation just uses Math.Max for the lengths and aggregates all of the results into a single list.
             * The correct behavior is more like a SelectMany query, so consider using the new SelectMany overload that AllParser uses.
             */

            /* This method is optimized to prevent stack overflows due to two factors: recursion and using Skip to move the source cursor.
             *
             * The previous implementation used recursive calls to NoneOrMore, in which there was a linear relationship between the number
             * of stack frames and the number of elements in the input sequence.  As an input sequence grew and the parser continued matching
             * elements, the number of calls to the Skip operator (via the Remainder extension) grew linearly, and so did the number of branches
             * due to NoneOrMore using the Or operator, which not only added the Or operator to the stack but added all of the calls to the
             * quantified parser between the stack frames that Or added, for every subsequent element in the sequence that the parser matched.
             */
            using (var branch = source.Branch())
            {
                var list = new List <TResult>();

                int total       = 0;
                int totalLength = 0;

                bool iterate = true;

                if (nonGreedy && count == 0)
                {
                    using (var lookAhead = new LookAheadParseResult <IEnumerable <TResult> >(Enumerable.Empty <TResult>(), length: 0))
                    {
                        yield return(lookAhead);

                        Contract.Assume(lookAhead.Succeeded.HasValue);

                        if (lookAhead.Succeeded.Value)
                        {
                            iterate = false;
                        }
                    }
                }

                while (iterate)
                {
                    bool hasResult          = false;
                    bool hasSeparatorResult = false;

                    int length          = 0;
                    int separatorLength = 0;

                    foreach (var result in parser.Parse(branch))
                    {
                        hasResult = true;
                        length    = Math.Max(length, result.Length);

                        list.Add(result.Value);
                    }

                    branch.Move(length);

                    if (separator != null)
                    {
                        foreach (var separatorResult in separator.Parse(branch))
                        {
                            hasSeparatorResult = true;
                            separatorLength    = Math.Max(separatorLength, separatorResult.Length);
                        }

                        branch.Move(separatorLength);
                    }

                    if (hasResult)
                    {
                        totalLength += length + separatorLength;

                        if (total < (maximum == -1 ? count : maximum))
                        {
                            total++;

                            if (total == maximum)
                            {
                                break;
                            }
                        }

                        if (separator == null || hasSeparatorResult)
                        {
                            if (nonGreedy && total >= count)
                            {
                                using (var lookAhead = new LookAheadParseResult <IEnumerable <TResult> >(list.AsReadOnly(), totalLength))
                                {
                                    yield return(lookAhead);

                                    Contract.Assume(lookAhead.Succeeded.HasValue);

                                    if (lookAhead.Succeeded.Value)
                                    {
                                        break;
                                    }
                                }
                            }

                            continue;
                        }
                    }

                    break;
                }

                if (total >= count)
                {
                    yield return(new ParseResult <IEnumerable <TResult> >(list.AsReadOnly(), totalLength));
                }
            }
        }
Ejemplo n.º 7
0
        public IEnumerable <IParseResult <IEnumerable <TResult> > > Parse(ICursor <TSource> source)
        {
            firstParser = null;

            bool first = true;

            /* The stack of branches allows for an optimization on top of the previous implementation, which used to create a new
             * branch for every parse result in every parse result sequence.  This new implementation changes that behavior slightly
             * by not creating a new branch for the last result in each sequence.  All parser rules (at the time of writing) in Rxx
             * generate zero or one result only; therefore, the current branch can be moved forward and reused by child sequences
             * without affecting the parent result sequence, because it's already completed.  This optimization has proven to be
             * greatly beneficial across normal parser queries that use And, All and Exactly operators.  It should also be beneficial
             * for multi-result queries since most of the individual parser rules in these queries will only generate a single result.
             * A new branch would only be created for each result in the multi-result sequence.  Scalar-result parsers that follow
             * sequentially in the All query would simply move their shared parent branch instead of creating new branches.
             */
            var branches = new Stack <ICursor <TSource> >();

            using (var root = source.Branch())
            {
                branches.Push(root);

                var query = ParseResult.ReturnSuccessMany <TResult>(0)
                            .SelectMany(
                    parsers.Select(
                        parser => (Func <Tuple <IParseResult <IEnumerable <TResult> >, bool>, IEnumerable <IParseResult <IEnumerable <TResult> > > >)
                            (value =>
                {
                    if (first)
                    {
                        first       = false;
                        firstParser = parser;
                    }

                    var branch = branches.Peek();

                    // Item2 is only true when value.Item1 is the last element of its sequence.
                    if (value.Item2)
                    {
                        branch.Move(value.Item1.Length);

                        return(parser.Parse(branch).Select(result => result.YieldMany()));
                    }
                    else
                    {
                        var remainder = branch.Remainder(value.Item1.Length);

                        branches.Push(remainder);

                        return(parser.Parse(remainder)
                               .Select(result => result.YieldMany())
                               .OnErrorOrDisposed(() =>
                        {
                            /* Finally() cannot be used here.  SelectMany generates each value as a Tuple, indicating whether it's
                             * the last value in the sequence.  To gather that info it uses a side-effect that causes it to look-ahead
                             * in the enumerator, which would cause Finally to execute and dispose of the branch before it's used
                             * by the child sequence.  SelectMany only calls Dispose when the sequence is no longer in use.
                             */
                            branches.Pop().Dispose();
                        }));
                    }
                })),
                    (firstResult, otherResults) => firstResult.Concat(otherResults));

                // Use an iterator block to ensure that the local variables in this method aren't shared between enumerators.
                foreach (var result in query)
                {
                    yield return(result);
                }
            }
        }