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); }