Example #1
0
        internal static void MatchSquareBracketStack(InlineStack opener, Subject subj, Reference details, InlineParserParameters parameters)
        {
            if (details != null)
            {
                var inl = opener.StartingInline;
                var isImage = 0 != (opener.Flags & InlineStack.InlineStackFlags.ImageLink);
                inl.Tag = isImage ? InlineTag.Image : InlineTag.Link;
                inl.FirstChild = inl.NextSibling;
                inl.NextSibling = null;
                inl.SourceLastPosition = subj.Position;

                inl.TargetUrl = details.Url;
                inl.LiteralContent = details.Title;

                if (!isImage)
                {
                    // since there cannot be nested links, remove any other link openers before this
                    var temp = opener.Previous;
                    while (temp != null && temp.Priority <= InlineStack.InlineStackPriority.Links)
                    {
                        if (temp.Delimeter == '[' && temp.Flags == opener.Flags)
                        {
                            // mark the previous entries as "inactive"
                            if (temp.DelimeterCount == -1)
                                break;

                            temp.DelimeterCount = -1;
                        }

                        temp = temp.Previous;
                    }
                }

                InlineStack.RemoveStackEntry(opener, subj, null, parameters);

                subj.LastInline = inl;
            }
            else
            {
                // this looked like a link, but was not.
                // remove the opener stack entry but leave the inbetween intact
                InlineStack.RemoveStackEntry(opener, subj, opener, parameters);

                var inl = new Inline("]", subj.Position - 1, subj.Position);
                subj.LastInline.LastSibling.NextSibling = inl;
                subj.LastInline = inl;
            }
        }
Example #2
0
        private static Inline HandleOpenerCloser(Subject subj, InlineTag singleCharTag, InlineTag doubleCharTag, InlineParserParameters parameters)
        {
            bool canOpen, canClose;
            var c = subj.Buffer[subj.Position];
            var numdelims = ScanEmphasisDelimeters(subj, c, out canOpen, out canClose);

            if (canClose)
            {
                // walk the stack and find a matching opener, if there is one
                var istack = InlineStack.FindMatchingOpener(subj.LastPendingInline, InlineStack.InlineStackPriority.Emphasis, c, out canClose);
                if (istack != null)
                {
                    var useDelims = MatchInlineStack(istack, subj, numdelims, null, singleCharTag, doubleCharTag, parameters);

                    // if the closer was not fully used, move back a char or two and try again.
                    if (useDelims < numdelims)
                    {
                        subj.Position = subj.Position - numdelims + useDelims;

                        // use recursion only if it will not be very deep.
                        if (numdelims < 10)
                            return HandleOpenerCloser(subj, singleCharTag, doubleCharTag, parameters);
                    }

                    return null;
                }
            }

            var inlText = new Inline(subj.Buffer, subj.Position - numdelims, numdelims, subj.Position - numdelims, subj.Position, c);

            if (canOpen || canClose)
            {
                var istack = new InlineStack();
                istack.DelimeterCount = numdelims;
                istack.Delimeter = c;
                istack.StartingInline = inlText;
                istack.Priority = InlineStack.InlineStackPriority.Emphasis;
                istack.Flags = (canOpen ? InlineStack.InlineStackFlags.Opener : 0)
                             | (canClose ? InlineStack.InlineStackFlags.Closer : 0);

                InlineStack.AppendStackEntry(istack, subj);
            }

            return inlText;
        }
Example #3
0
 /// <summary>
 /// Initializes the array of delegates for inline emphasis parsing.
 /// </summary>
 /// <returns></returns>
 internal static Func<Subject, Inline>[] InitializeEmphasisParsers(InlineParserParameters parameters)
 {
     var p = new Func<Subject, Inline>[96];
     p['_'] = p['*'] = s => HandleOpenerCloser(s, InlineTag.Emphasis, InlineTag.Strong, parameters);
     return p;
 }
Example #4
0
        internal static int MatchInlineStack(InlineStack opener, Subject subj, int closingDelimeterCount, InlineStack closer, InlineTag singleCharTag, InlineTag doubleCharTag, InlineParserParameters parameters)
        {
            // calculate the actual number of delimeters used from this closer
            int useDelims;
            var openerDelims = opener.DelimeterCount;

            if (closingDelimeterCount < 3 || openerDelims < 3)
            {
                useDelims = closingDelimeterCount <= openerDelims ? closingDelimeterCount : openerDelims;
                if (useDelims == 2 && doubleCharTag == 0)
                    useDelims = 1;
                if (useDelims == 1 && singleCharTag == 0)
                    return 0;
            }
            else if (singleCharTag == 0)
                useDelims = 2;
            else if (doubleCharTag == 0)
                useDelims = 1;
            else
                useDelims = closingDelimeterCount % 2 == 0 ? 2 : 1;

            Inline inl = opener.StartingInline;
            InlineTag tag = useDelims == 1 ? singleCharTag : doubleCharTag;
            if (openerDelims == useDelims)
            {
                // the opener is completely used up - remove the stack entry and reuse the inline element
                inl.Tag = tag;
                inl.LiteralContent = null;
                inl.FirstChild = inl.NextSibling;
                inl.NextSibling = null;

                InlineStack.RemoveStackEntry(opener, subj, closer?.Previous, parameters);
            }
            else
            {
                // the opener will only partially be used - stack entry remains (truncated) and a new inline is added.
                opener.DelimeterCount -= useDelims;
                inl.LiteralContent = inl.LiteralContent.Substring(0, opener.DelimeterCount);
                inl.SourceLastPosition -= useDelims;

                inl.NextSibling = new Inline(tag, inl.NextSibling);
                inl = inl.NextSibling;

                inl.SourcePosition = opener.StartingInline.SourcePosition + opener.DelimeterCount;
            }

            // there are two callers for this method, distinguished by the `closer` argument.
            // if closer == null it means the method is called during the initial subject parsing and the closer
            //   characters are at the current position in the subject. The main benefit is that there is nothing
            //   parsed that is located after the matched inline element.
            // if closer != null it means the method is called when the second pass for previously unmatched
            //   stack elements is done. The drawback is that there can be other elements after the closer.
            if (closer != null)
            {
                var clInl = closer.StartingInline;
                if ((closer.DelimeterCount -= useDelims) > 0)
                {
                    // a new inline element must be created because the old one has to be the one that
                    // finalizes the children of the emphasis
                    var newCloserInline = new Inline(clInl.LiteralContent.Substring(useDelims));
                    newCloserInline.SourcePosition = inl.SourceLastPosition = clInl.SourcePosition + useDelims;
                    newCloserInline.SourceLength = closer.DelimeterCount;
                    newCloserInline.NextSibling = clInl.NextSibling;

                    clInl.LiteralContent = null;
                    clInl.NextSibling = null;
                    inl.NextSibling = closer.StartingInline = newCloserInline;
                }
                else
                {
                    inl.SourceLastPosition = clInl.SourceLastPosition;

                    clInl.LiteralContent = null;
                    inl.NextSibling = clInl.NextSibling;
                    clInl.NextSibling = null;
                }
            }
            else if (subj != null)
            {
                inl.SourceLastPosition = subj.Position - closingDelimeterCount + useDelims;
                subj.LastInline = inl;
            }

            return useDelims;
        }
Example #5
0
        public static Inline parse_inlines(Subject subj, Dictionary<string, Reference> refmap, InlineParserParameters parameters)
        {
            var len = subj.Length;

            if (len == 0)
                return null;

            var first = ParseInline(subj, parameters);
            subj.LastInline = first.LastSibling;

            Inline cur;
            while (subj.Position < len)
            {
                cur = ParseInline(subj, parameters);
                if (cur != null)
                {
                    subj.LastInline.NextSibling = cur;
                    subj.LastInline = cur.LastSibling;
                }
            }

            InlineStack.PostProcessInlineStack(subj, subj.FirstPendingInline, subj.LastPendingInline, InlineStack.InlineStackPriority.Maximum, parameters);

            return first;
        }
Example #6
0
        /// <summary>
        /// Parse an inline element from the subject. The subject position is updated to after the element.
        /// </summary>
        public static Inline ParseInline(Subject subj, InlineParserParameters parameters)
        {
            var parsers = parameters.Parsers;
            var specialCharacters = parameters.SpecialCharacters;

            var c = subj.Buffer[subj.Position];

            var parser = c < parsers.Length ? parsers[c] : null;

            if (parser != null)
                return parser(subj);

            var startpos = subj.Position;

            // we read until we hit a special character
            // +1 is so that any special character at the current position is ignored.
            var endpos = subj.Buffer.IndexOfAny(specialCharacters, startpos + 1, subj.Length - startpos - 1);

            if (endpos == -1)
                endpos = subj.Length;

            subj.Position = endpos;

            // if we're at a newline, strip trailing spaces.
            if (endpos < subj.Length && subj.Buffer[endpos] == '\n')
                while (endpos > startpos && subj.Buffer[endpos - 1] == ' ')
                    endpos--;

            return new Inline(subj.Buffer, startpos, endpos - startpos, startpos, endpos, c);
        }
Example #7
0
        public static void PostProcessInlineStack(Subject subj, InlineStack first, InlineStack last, InlineStackPriority ignorePriority, InlineParserParameters parameters)
        {
            var singleCharTags = parameters.SingleCharTags;
            var doubleCharTags = parameters.DoubleCharTags;
            while (ignorePriority > 0)
            {
                var istack = first;
                while (istack != null)
                {
                    if (istack.Priority >= ignorePriority)
                    {
                        RemoveStackEntry(istack, subj, istack, parameters);
                    }
                    else if (0 != (istack.Flags & InlineStackFlags.Closer))
                    {
                        bool canClose;
                        var iopener = FindMatchingOpener(istack.Previous, istack.Priority, istack.Delimeter, out canClose);
                        if (iopener != null)
                        {
                            bool retry = false;
                            var singleCharTag = iopener.Delimeter < singleCharTags.Length ? singleCharTags[iopener.Delimeter] : (InlineTag)0;
                            var doubleCharTag = iopener.Delimeter < doubleCharTags.Length ? doubleCharTags[iopener.Delimeter] : (InlineTag)0;
                            if (singleCharTag != 0 || doubleCharTag != 0)
                            {
                                var useDelims = InlineMethods.MatchInlineStack(iopener, subj, istack.DelimeterCount, istack, singleCharTag, doubleCharTag, parameters);
                                if (istack.DelimeterCount > 0)
                                    retry = true;
                            }

                            if (retry)
                            {
                                // remove everything between opened and closer (not inclusive).
                                if (istack.Previous != null && iopener.Next != istack.Previous)
                                    RemoveStackEntry(iopener.Next, subj, istack.Previous, parameters);

                                continue;
                            }
                            else
                            {
                                // remove opener, everything in between, and the closer
                                RemoveStackEntry(iopener, subj, istack, parameters);
                            }
                        }
                        else if (!canClose)
                        {
                            // this case means that a matching opener does not exist
                            // remove the Closer flag so that a future Opener can be matched against it.
                            istack.Flags &= ~InlineStackFlags.Closer;
                        }
                    }

                    if (istack == last)
                        break;

                    istack = istack.Next;
                }

                ignorePriority--;
            }
        }
Example #8
0
        /// <summary>
        /// Removes a subset of the stack.
        /// </summary>
        /// <param name="first">The first entry to be removed.</param>
        /// <param name="subj">The subject associated with this stack. Can be <c>null</c> if the pointers in the subject should not be updated.</param>
        /// <param name="last">The last entry to be removed. Can be <c>null</c> if everything starting from <paramref name="first"/> has to be removed.</param>
        /// <param name="parameters">Inline parser parameters.</param>
        public static void RemoveStackEntry(InlineStack first, Subject subj, InlineStack last, InlineParserParameters parameters)
        {
            var curPriority = first.Priority;

            if (last == null)
            {
                if (first.Previous != null)
                    first.Previous.Next = null;
                else if (subj != null)
                    subj.FirstPendingInline = null;

                if (subj != null)
                {
                    last = subj.LastPendingInline;
                    subj.LastPendingInline = first.Previous;
                }

                first = first.Next;
            }
            else
            {
                if (first.Previous != null)
                    first.Previous.Next = last.Next;
                else if (subj != null)
                    subj.FirstPendingInline = last.Next;

                if (last.Next != null)
                    last.Next.Previous = first.Previous;
                else if (subj != null)
                    subj.LastPendingInline = first.Previous;

                if (first == last)
                    return;

                first = first.Next;
                last = last.Previous;
            }

            if (last == null || first == null)
                return;

            first.Previous = null;
            last.Next = null;

            // handle case like [*b*] (the whole [..] is being removed but the inner *..* must still be matched).
            // this is not done automatically because the initial * is recognized as a potential closer (assuming
            // potential scenario '*[*' ).
            if (curPriority > 0)
                PostProcessInlineStack(null, first, last, curPriority, parameters);
        }