/// <summary> /// Lexes and parses the specified string. /// </summary> /// <param name="input">The <see cref="StringBuilder"/> to parse.</param> /// <param name="output">The parsed token stream.</param> /// <param name="options">A set of <see cref="TextParserOptions"/> values that specify how the text should be parsed.</param> public void Parse(StringBuilder input, TextParserTokenStream output, TextParserOptions options = TextParserOptions.None) { Contract.Require(input, nameof(input)); Contract.Require(output, nameof(output)); output.Clear(); Parse(new StringBuilderSource(input), output, 0, input.Length, options); }
/// <summary> /// Lexes and parses the specified string. /// </summary> private void Parse <TSource>(TSource input, TextParserTokenStream output, Int32 index, Int32 count, TextParserOptions options = TextParserOptions.None) where TSource : ISegmentableStringSource { var bound = index + count; while (index < bound) { if (IsStartOfNewline(input, index)) { output.Add(ConsumeNewlineToken(input, options, ref index)); continue; } if (IsStartOfNonBreakingSpace(input, index)) { output.Add(ConsumeNonBreakingSpaceToken(input, options, ref index)); continue; } if (IsStartOfBreakingSpace(input, index)) { output.Add(ConsumeBreakingSpaceToken(input, options, ref index)); continue; } if (IsEscapedPipe(input, index, options)) { output.Add(ConsumeEscapedPipeToken(input, options, ref index)); continue; } if (IsStartOfCommand(input, index)) { output.Add(ConsumeCommandToken(input, options, ref index)); continue; } if (IsStartOfWord(input, index)) { output.Add(ConsumeWordToken(input, options, ref index)); continue; } index++; } output.SourceText = input.CreateStringSegment(); output.ParserOptions = options; }
/// <summary> /// Finds the index of the first and last tokens which are potentially affected by changes in the specified substring of the source text. /// </summary> private void FindTokensInfluencedBySubstring(TextParserTokenStream result, Int32 start, Int32 count, out Int32 ix1, out Int32 ix2) { var position = 0; var end = start + count; var ix1Found = false; var ix2Found = false; ix1 = 0; ix2 = result.Count - 1; for (int i = 0; i < result.Count; i++) { var token = result[i]; var tokenStart = token.SourceOffset; var tokenEnd = tokenStart + token.SourceLength; if (!ix1Found && (start >= tokenStart && start <= tokenEnd)) { ix1 = i; ix1Found = true; } if (!ix2Found && (end >= tokenStart && end < tokenEnd)) { ix2 = i; ix2Found = true; } if (ix1Found && ix2Found) { break; } position += token.SourceLength; } }
/// <summary> /// Incrementally lexes and parses the specified string. /// </summary> /// <param name="input">The <see cref="StringBuilder"/> to parse.</param> /// <param name="start">The index of the first character that was changed.</param> /// <param name="count">The number of characters that were changed.</param> /// <param name="result">The parsed token stream.</param> /// <param name="options">A set of <see cref="TextParserOptions"/> values that specify how the text should be parsed.</param> /// <returns>An <see cref="IncrementalResult"/> structure that represents the result of the operation.</returns> /// <remarks>Incremental parsing provides a performance benefit when relatively small changes are being made /// to a large source text. Only tokens which are potentially influenced by changes within the specified substring /// of the source text are re-parsed by this operation.</remarks> public IncrementalResult ParseIncremental(StringBuilder input, Int32 start, Int32 count, TextParserTokenStream result, TextParserOptions options = TextParserOptions.None) { Contract.Require(input, nameof(input)); Contract.Require(result, nameof(result)); Contract.EnsureRange(start >= 0, nameof(start)); Contract.EnsureRange(count >= 0 && start + count <= input.Length, nameof(count)); return(ParseIncremental(new StringBuilderSource(input), start, count, result, options)); }
/// <summary> /// Incrementally lexes and parses the specified string. /// </summary> private IncrementalResult ParseIncremental <TSource>(TSource input, Int32 start, Int32 count, TextParserTokenStream output, TextParserOptions options = TextParserOptions.None) where TSource : ISegmentableStringSource { var inputLengthOld = output.SourceText.Length; var inputLengthNew = input.Length; var inputLengthDiff = inputLengthNew - inputLengthOld; Int32 ix1, ix2; FindTokensInfluencedBySubstring(output, start, count - inputLengthDiff, out ix1, out ix2); var token1 = output[ix1]; var token2 = output[ix2]; var invalidatedTokenCount = 1 + (ix2 - ix1); output.RemoveRange(ix1, invalidatedTokenCount); var lexStart = token1.SourceOffset; var lexCount = inputLengthDiff + (token2.SourceOffset + token2.SourceLength) - lexStart; var parserBuffer = incrementalParserBuffer.Value; Parse(input, parserBuffer, lexStart, lexCount); output.SourceText = input.CreateStringSegment(); output.InsertRange(ix1, parserBuffer); var affectedOffset = ix1; var affectedCount = parserBuffer.Count; parserBuffer.Clear(); return(new IncrementalResult(affectedOffset, affectedCount)); }