예제 #1
0
        public static VoteLine?ParseLine(ReadOnlySpan <char> line)
        {
            if (line.Length == 0)
            {
                return(null);
            }

            StringBuilder prefixSB    = new StringBuilder();
            StringBuilder markerSB    = new StringBuilder();
            StringBuilder taskSB      = new StringBuilder();
            StringBuilder contentSB   = new StringBuilder();
            StringBuilder tempContent = new StringBuilder();

            MarkerType markerType  = MarkerType.None;
            int        markerValue = 0;

            Stack <TokenState> state        = new Stack <TokenState>();
            TokenState         currentState = TokenState.None;

            for (int c = 0; c < line.Length; c++)
            {
                char ch = line[c];

                // Skip newlines entirely, if they somehow get into the line we're parsing.
                if (newlineChars.Contains(ch))
                {
                    continue;
                }

                switch (currentState)
                {
                case TokenState.None:
                    if (ch == whitespace)
                    {
                        continue;
                    }
                    else if (prefixChars.Contains(ch))
                    {
                        prefixSB.Append(ch);
                        currentState = TokenState.Prefix;
                    }
                    else if (ch == openBracket)
                    {
                        currentState = TokenState.Marker;
                    }
                    else if (ch == xBox || ch == checkBox)
                    {
                        // Shortcut for a complete marker
                        markerSB.Append(ch);
                        (markerType, markerValue) = GetMarkerType(markerSB.ToString());
                        currentState = TokenState.PostMarker;
                    }
                    else if (ch == openBBCode)
                    {
                        state.Push(currentState);
                        currentState = TokenState.BBCode;
                    }
                    else
                    {
                        goto doneExamining;
                    }
                    break;

                case TokenState.Prefix:
                    if (ch == whitespace)
                    {
                        continue;
                    }
                    else if (prefixChars.Contains(ch))
                    {
                        prefixSB.Append(ch);
                    }
                    else if (ch == openBracket)
                    {
                        currentState = TokenState.Marker;
                    }
                    else if (ch == xBox || ch == checkBox)
                    {
                        // Shortcut for a complete marker
                        markerSB.Append(ch);
                        (markerType, markerValue) = GetMarkerType(markerSB.ToString());
                        currentState = TokenState.PostMarker;
                    }
                    else if (ch == openBBCode)
                    {
                        state.Push(currentState);
                        currentState = TokenState.BBCode;
                    }
                    else
                    {
                        goto doneExamining;
                    }
                    break;

                case TokenState.Marker:
                    if (ch == whitespace)
                    {
                        continue;
                    }
                    else if (markerChars.Contains(ch))
                    {
                        markerSB.Append(ch);
                    }
                    else if (ch == closeBracket)
                    {
                        (markerType, markerValue) = GetMarkerType(markerSB.ToString());
                        if (markerType != MarkerType.None)
                        {
                            currentState = TokenState.PostMarker;
                        }
                        else
                        {
                            goto doneExamining;
                        }
                    }
                    else if (ch == openBBCode)
                    {
                        state.Push(currentState);
                        currentState = TokenState.BBCode;
                    }
                    else
                    {
                        goto doneExamining;
                    }
                    break;

                case TokenState.PostMarker:
                    if (ch == whitespace)
                    {
                        if (tempContent.Length > 0)
                        {
                            tempContent.Append(ch);
                        }

                        continue;
                    }
                    else if (ch == openBracket && taskSB.Length == 0)
                    {
                        state.Push(currentState);
                        currentState = TokenState.Task;
                    }
                    else if (ch == openBBCode && taskSB.Length == 0)
                    {
                        state.Push(currentState);
                        currentState = TokenState.BBCode;
                        tempContent.Append(ch);
                    }
                    else if (ch == openStrike)
                    {
                        tempContent.Append("『s』");
                        state.Push(currentState);
                        currentState = TokenState.Strike;
                    }
                    else
                    {
                        contentSB.Append(tempContent);
                        tempContent.Clear();
                        contentSB.Append(ch);
                        currentState = TokenState.Content;
                    }
                    break;

                case TokenState.Task:
                    tempContent.Clear();
                    if (ch == closeBracket)
                    {
                        currentState = state.Pop();
                    }
                    else if (ch == openBBCode)
                    {
                        state.Push(currentState);
                        currentState = TokenState.BBCode;
                    }
                    else if (ch == openStrike)
                    {
                        state.Push(currentState);
                        currentState = TokenState.Strike;
                    }
                    else
                    {
                        taskSB.Append(ch);
                    }
                    break;

                case TokenState.Content:
                    if (tempContent.Length > 0)
                    {
                        contentSB.Append(tempContent);
                        tempContent.Clear();
                    }

                    if (ch == openStrike)
                    {
                        tempContent.Append("『s』");
                        state.Push(currentState);
                        currentState = TokenState.Strike;
                    }
                    else if (apostraphes.Contains(ch))
                    {
                        contentSB.Append('\'');
                    }
                    else if (quotations.Contains(ch))
                    {
                        contentSB.Append('"');
                    }
                    else
                    {
                        contentSB.Append(ch);
                    }
                    break;

                case TokenState.BBCode:
                    if (state.Peek() == TokenState.PostMarker)
                    {
                        tempContent.Append(ch);
                    }
                    if (ch == closeBBCode)
                    {
                        currentState = state.Pop();
                    }
                    break;

                case TokenState.Strike:
                    // Strike-through text is only preserved in the content area
                    if (ch == closeStrike)
                    {
                        tempContent.Append("『/s』");
                        currentState = state.Pop();
                    }
                    else if (ch == strikeNewline)
                    {
                        // If we hit embedded newlines, bail out entirely.
                        // Take whatever's been done up to that point.
                        tempContent.Clear();
                        currentState = state.Pop();
                        goto doneExamining;
                    }
                    else
                    {
                        tempContent.Append(ch);
                    }
                    break;

                default:
                    throw new InvalidOperationException($"Unknown token state value: {currentState}.");
                }
            }

doneExamining:

            if (currentState == TokenState.Content)
            {
                string content = VoteString.NormalizeContentBBCode(contentSB.ToString());
                return(new VoteLine(prefixSB.ToString(), markerSB.ToString(), taskSB.ToString(), content, markerType, markerValue));
            }

            return(null);
        }