diff --git a/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs b/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs index d5f4ffa..3d91c86 100644 --- a/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs +++ b/src/Matcher.Interop.Ebnf/CharacterClassMatchEngine.cs @@ -6,1187 +6,1181 @@ namespace Synfron.Staxe.Matcher.Interop.Bnf { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "")] - public class CharacterClassMatchEngine : AbstractLanguageMatchEngine - { - public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) - { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; - Span checkFlags = stackalloc int[7]; - State state = new State() - { Code = code, DistinctStringMatches = new List(2000), CheckFlags = checkFlags }; - bool success = false; - switch (fragmentMatcher) - { - case "NotCharacterClassBody": - success = MatchFragmentNotCharacterClassBody(ref state, matchData); - break; - case "CharacterClassBody": - success = MatchFragmentCharacterClassBody(ref state, matchData); - break; - case "CharacterRange": - success = MatchFragmentCharacterRange(ref state, matchData); - break; - case "HexRange": - success = MatchFragmentHexRange(ref state, matchData); - break; - } - - IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - { - success = false; - failureIndex = state.CurrentIndex; - } - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); - } - - public override MatcherResult Match(string code, bool matchFullText = true) - { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; - Span checkFlags = stackalloc int[7]; - State state = new State() - { Code = code, DistinctStringMatches = new List(2000), CheckFlags = checkFlags }; - bool success = MatchFragmentCharacterClass(ref state, matchData); - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - { - success = false; - failureIndex = state.CurrentIndex; - } - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); - } - - private (bool success, int offset) MatchLiteral0(string text, int startOffset) - { - string literal = "^"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternCaret(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[3] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 3; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 3; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsCaret(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternCaret(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, int offset) MatchInsensitiveLiteral0(string text, int startOffset) - { - string literal = "#x"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (char.ToLowerInvariant(text[i + startOffset]) != char.ToLowerInvariant(literal[i])) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchCharBounds0(string text, int startOffset) - { - int charVal = text[startOffset]; - return (97 <= charVal && 102 >= charVal) ? (true, 1) : (false, 0); - } - - private (bool success, int offset) MatchCharBounds1(string text, int startOffset) - { - int charVal = text[startOffset]; - return (65 <= charVal && 70 >= charVal) ? (true, 1) : (false, 0); - } - - private (bool success, int offset) MatchCharBounds2(string text, int startOffset) - { - int charVal = text[startOffset]; - return (48 <= charVal && 57 >= charVal) ? (true, 1) : (false, 0); - } - - private (bool success, int offset) MatchOr0(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchCharBounds0(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchCharBounds1(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchCharBounds2(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, int offset) MatchCountBounds0(string text, int startOffset) - { - int offset = startOffset; - bool subSuccess = true; - int subOffset; - int matches = 0; - for (; matches < 4 && subSuccess; matches++) - { - (subSuccess, subOffset) = MatchOr0(text, offset); - offset += subOffset; - } - - bool success = subSuccess || matches >= 1; - return success ? (true, offset - startOffset) : (false, 0); - } - - private (bool success, int offset) MatchGroup0(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchInsensitiveLiteral0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchCountBounds0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, StringMatchData matchData) MatchPatternHexChar(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup0(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[4] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 4; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 4; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup0(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternHexChar(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral1(string text, int startOffset) - { - string literal = "-"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternDash(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 5 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[5] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 5; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 5; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 5 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - ; - success = MatchPartByTextMatcherHexChar(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternDash(ref state, true, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchPartByTextMatcherHexChar(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "HexRange", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeHexRange(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral2(string text, int startOffset) - { - string literal = "]"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchNot0(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral2(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchLiteral3(string text, int startOffset) - { - string literal = "\\"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchGroup1(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral3(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchAny(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchOr1(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchGroup1(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchAny(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, int offset) MatchGroup2(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchNot0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchOr1(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, StringMatchData matchData) MatchPatternAnyChar(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[6] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 6; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 6; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup2(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherAnyChar(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternAnyChar(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - ; - success = MatchPartByTextMatcherAnyChar(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternDash(ref state, true, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchPartByTextMatcherAnyChar(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentCharacterRange(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CharacterRange", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeCharacterRange(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, FragmentMatchData matchData) - { - bool overallSuccess = false; - bool subSuccess = false; - bool delimiterSuccess = false; - StringMatchData range = default; - int matchCount = 0; - int distinctIndex = state.DistinctIndex; - ; - do - { - subSuccess = false; - bool individualSuccess; - individualSuccess = MatchFragmentHexRange(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - individualSuccess = MatchPartByTextMatcherHexChar(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - individualSuccess = MatchFragmentCharacterRange(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - individualSuccess = MatchPartByTextMatcherAnyChar(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - Break: - overallSuccess |= subSuccess; - } - while (subSuccess && delimiterSuccess); - if (delimiterSuccess && range != null) - { - state.CurrentIndex = range.StartIndex; - state.DistinctIndex = distinctIndex; - } - - bool thresholdSuccess = 1 <= matchCount; - bool success = overallSuccess || thresholdSuccess; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentCharacterClassBody(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CharacterClassBody", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsMultipleModeCharacterClassBody(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeNotCharacterClassBody(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchFragmentCharacterClassBody(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentNotCharacterClassBody(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "NotCharacterClassBody", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsCaret(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeNotCharacterClassBody(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral4(string text, int startOffset) - { - string literal = "["; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternOpenBracket(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral4(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[1] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 1; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 1; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral4(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternOpenBracket(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentPartsOneModeCharacterClass(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchFragmentNotCharacterClassBody(ref state, matchData) || MatchFragmentCharacterClassBody(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, StringMatchData matchData) MatchPatternCloseBracket(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[2] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 2; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 2; - } - else - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternCloseBracket(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentCharacterClass(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CharacterClass", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenBracket(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeCharacterClass(ref state, partMatcherData) && MatchFragmentBoundsCloseBracket(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.AddRange(partMatcherData.Parts); - } - - return success; - } - } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "")] + public class CharacterClassMatchEngine : AbstractLanguageMatchEngine + { + public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) + { + FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; + Span checkFlags = stackalloc int[7]; + State state = new State() + { Code = code, DistinctStringMatches = new List(2000) }; + bool success = false; + switch (fragmentMatcher) + { + case "NotCharacterClassBody": + success = MatchFragmentNotCharacterClassBody(ref state, matchData); + break; + case "CharacterClassBody": + success = MatchFragmentCharacterClassBody(ref state, matchData); + break; + case "CharacterRange": + success = MatchFragmentCharacterRange(ref state, matchData); + break; + case "HexRange": + success = MatchFragmentHexRange(ref state, matchData); + break; + } + + IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + if (success && matchFullText && state.CurrentIndex != state.Code.Length) + { + success = false; + failureIndex = state.CurrentIndex; + } + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); + } + + public override MatcherResult Match(string code, bool matchFullText = true) + { + FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; + Span checkFlags = stackalloc int[7]; + State state = new State() + { Code = code, DistinctStringMatches = new List(2000) }; + bool success = MatchFragmentCharacterClass(ref state, matchData); + IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + if (success && matchFullText && state.CurrentIndex != state.Code.Length) + { + success = false; + failureIndex = state.CurrentIndex; + } + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); + } + + private (bool success, int offset) MatchLiteral0(string text, int startOffset) + { + string literal = "^"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternCaret(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + state.MaxDistinctIndex++; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 3; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 3; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral0(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Caret", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsCaret(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternCaret(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, int offset) MatchInsensitiveLiteral0(string text, int startOffset) + { + string literal = "#x"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (char.ToLowerInvariant(text[i + startOffset]) != char.ToLowerInvariant(literal[i])) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchCharBounds0(string text, int startOffset) + { + int charVal = text[startOffset]; + return (97 <= charVal && 102 >= charVal) ? (true, 1) : (false, 0); + } + + private (bool success, int offset) MatchCharBounds1(string text, int startOffset) + { + int charVal = text[startOffset]; + return (65 <= charVal && 70 >= charVal) ? (true, 1) : (false, 0); + } + + private (bool success, int offset) MatchCharBounds2(string text, int startOffset) + { + int charVal = text[startOffset]; + return (48 <= charVal && 57 >= charVal) ? (true, 1) : (false, 0); + } + + private (bool success, int offset) MatchOr0(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchCharBounds0(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchCharBounds1(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchCharBounds2(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, int offset) MatchCountBounds0(string text, int startOffset) + { + int offset = startOffset; + bool subSuccess = true; + int subOffset; + int matches = 0; + for (; matches < 4 && subSuccess; matches++) + { + (subSuccess, subOffset) = MatchOr0(text, offset); + offset += subOffset; + } + + bool success = subSuccess || matches >= 1; + return success ? (true, offset - startOffset) : (false, 0); + } + + private (bool success, int offset) MatchGroup0(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchInsensitiveLiteral0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchCountBounds0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, StringMatchData matchData) MatchPatternHexChar(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup0(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + state.MaxDistinctIndex++; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 4; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 4; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup0(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternHexChar(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral1(string text, int startOffset) + { + string literal = "-"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternDash(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 5 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + state.MaxDistinctIndex++; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 5; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 5; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral1(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 5 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentPartsOrderedModeHexRange(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + ; + success = MatchPartByTextMatcherHexChar(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternDash(ref state, true, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchPartByTextMatcherHexChar(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentHexRange(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "HexRange", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOrderedModeHexRange(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral2(string text, int startOffset) + { + string literal = "]"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchNot0(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral2(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchLiteral3(string text, int startOffset) + { + string literal = "\\"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchGroup1(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral3(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchAny(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchOr1(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchGroup1(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchAny(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, int offset) MatchGroup2(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchNot0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchOr1(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, StringMatchData matchData) MatchPatternAnyChar(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup2(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + state.MaxDistinctIndex++; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 6; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 6; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup2(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "AnyChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherAnyChar(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternAnyChar(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOrderedModeCharacterRange(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + ; + success = MatchPartByTextMatcherAnyChar(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternDash(ref state, true, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchPartByTextMatcherAnyChar(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentCharacterRange(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "CharacterRange", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOrderedModeCharacterRange(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsMultipleModeCharacterClassBody(ref State state, FragmentMatchData matchData) + { + bool overallSuccess = false; + bool subSuccess = false; + bool delimiterSuccess = false; + StringMatchData range = default; + int matchCount = 0; + int distinctIndex = state.DistinctIndex; + ; + do + { + subSuccess = false; + bool individualSuccess; + individualSuccess = MatchFragmentHexRange(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + individualSuccess = MatchPartByTextMatcherHexChar(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + individualSuccess = MatchFragmentCharacterRange(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + individualSuccess = MatchPartByTextMatcherAnyChar(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + Break: + overallSuccess |= subSuccess; + } + while (subSuccess && delimiterSuccess); + if (delimiterSuccess && range != null) + { + state.CurrentIndex = range.StartIndex; + state.DistinctIndex = distinctIndex; + } + + bool thresholdSuccess = 1 <= matchCount; + bool success = overallSuccess || thresholdSuccess; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentCharacterClassBody(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "CharacterClassBody", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsMultipleModeCharacterClassBody(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeNotCharacterClassBody(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchFragmentCharacterClassBody(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentNotCharacterClassBody(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "NotCharacterClassBody", StartIndex = state.CurrentIndex }; + success = (MatchFragmentBoundsCaret(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeNotCharacterClassBody(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral4(string text, int startOffset) + { + string literal = "["; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternOpenBracket(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral4(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + state.MaxDistinctIndex++; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 1; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 1; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral4(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternOpenBracket(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentPartsOneModeCharacterClass(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchFragmentNotCharacterClassBody(ref state, matchData) || MatchFragmentCharacterClassBody(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, StringMatchData matchData) MatchPatternCloseBracket(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + state.MaxDistinctIndex++; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 2; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 2; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral2(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternCloseBracket(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentCharacterClass(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "CharacterClass", StartIndex = state.CurrentIndex }; + success = (MatchFragmentBoundsOpenBracket(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeCharacterClass(ref state, partMatcherData) && MatchFragmentBoundsCloseBracket(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.AddRange(partMatcherData.Parts); + } + + return success; + } + } } \ No newline at end of file diff --git a/src/Matcher.Interop.Ebnf/EbnfConverter.cs b/src/Matcher.Interop.Ebnf/EbnfConverter.cs index 2a7fa02..84884b3 100644 --- a/src/Matcher.Interop.Ebnf/EbnfConverter.cs +++ b/src/Matcher.Interop.Ebnf/EbnfConverter.cs @@ -18,6 +18,12 @@ public class EbnfConverter private Dictionary> _fragmentMatcherMap; private string _ebnfText = null; + public bool IgnoreWhitespace + { + get; + set; + } + public List DefaultFragments { get; @@ -29,14 +35,22 @@ public LanguageMatcher Convert(string name, string ebnfText) _ebnfText = ebnfText; _patternMatcherMap = new Dictionary(StringComparer.Ordinal); _fragmentMatcherMap = new Dictionary>(StringComparer.Ordinal); + _languageMatcher = new LanguageMatcher() { Name = name, Patterns = new List(), Fragments = new List() - }; + if (IgnoreWhitespace) + { + + PatternMatcher whitepacePattern = PatternReader.Parse(-2, "IgnoredWhitespace", "\\s+"); + whitepacePattern.IsNoise = true; + _languageMatcher.Patterns.Add(whitepacePattern); + } + MatcherResult matcherResult = _ebnfMatchEngine.Match(ebnfText); if (!matcherResult.Success) @@ -51,7 +65,7 @@ public LanguageMatcher Convert(string name, string ebnfText) AddRules((FragmentMatchData)matcherResult.MatchData); - _languageMatcher.IndexingMode = IndexingMode.Lazy; + _languageMatcher.IndexingMode = IndexingMode.Eager; _languageMatcher.StartingFragment = _languageMatcher.Fragments.FirstOrDefault(); return _languageMatcher; diff --git a/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs b/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs index 3fabd5d..ea6cdac 100644 --- a/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs +++ b/src/Matcher.Interop.Ebnf/EbnfMatchEngine.cs @@ -7,4703 +7,4588 @@ namespace Synfron.Staxe.Matcher.Interop.Bnf { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "")] - public class EbnfMatchEngine : AbstractLanguageMatchEngine - { - public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) - { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; - State state = new State() - { Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>() }; - PreMatchPatterns(ref state); - bool success = false; - switch (fragmentMatcher) - { - case "Rules": - success = MatchFragmentRules(ref state, matchData); - break; - case "NotRulePrefix": - success = MatchFragmentNotRulePrefix(ref state, matchData); - break; - case "RuleName": - success = MatchFragmentRuleName(ref state, matchData); - break; - case "Rule": - success = MatchFragmentRule(ref state, matchData); - break; - case "Expression": - success = MatchFragmentExpression(ref state, matchData); - break; - case "RepetitionSuffix": - success = MatchFragmentRepetitionSuffix(ref state, matchData); - break; - case "ExceptSuffix": - success = MatchFragmentExceptSuffix(ref state, matchData); - break; - case "CommaSuffix": - success = MatchFragmentCommaSuffix(ref state, matchData); - break; - case "OrSuffix": - success = MatchFragmentOrSuffix(ref state, matchData); - break; - case "OptionalComma": - success = MatchFragmentOptionalComma(ref state, matchData); - break; - case "RepetitionGroup": - success = MatchFragmentRepetitionGroup(ref state, matchData); - break; - case "OptionalGroup": - success = MatchFragmentOptionalGroup(ref state, matchData); - break; - case "ZeroOrOneItem": - success = MatchFragmentZeroOrOneItem(ref state, matchData); - break; - case "OneOrMoreItem": - success = MatchFragmentOneOrMoreItem(ref state, matchData); - break; - case "ZeroOrMoreItem": - success = MatchFragmentZeroOrMoreItem(ref state, matchData); - break; - case "Item": - success = MatchFragmentItem(ref state, matchData); - break; - } - - IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - { - success = false; - failureIndex = state.CurrentIndex; - } - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); - } - - public override MatcherResult Match(string code, bool matchFullText = true) - { - FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; - State state = new State() - { Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>() }; - PreMatchPatterns(ref state); - bool success = MatchFragmentRules(ref state, matchData); - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - { - success = false; - failureIndex = state.CurrentIndex; - } - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); - } - - private bool PreMatchPatterns(ref State state) - { - int codeLength = state.Code.Length; - bool success = true; - while (state.CurrentIndex < codeLength) - { - success = false; - success = MatchPatternNumber(ref state, true, false).success || MatchPatternDoubleQuoteLiteral(ref state, true, false).success || MatchPatternSingleQuoteLiteral(ref state, true, false).success || MatchPatternSpecialGroup(ref state, true, false).success || MatchPatternComment(ref state, true, false).success || MatchPatternIs(ref state, true, false).success || MatchPatternRuleName(ref state, true, false).success || MatchPatternOr(ref state, true, false).success || MatchPatternAsterisk(ref state, true, false).success || MatchPatternPlus(ref state, true, false).success || MatchPatternQuestionMark(ref state, true, false).success || MatchPatternSemicolon(ref state, true, false).success || MatchPatternPeriod(ref state, true, false).success || MatchPatternOpenBrace(ref state, true, false).success || MatchPatternCloseBrace(ref state, true, false).success || MatchPatternOpenBracket(ref state, true, false).success || MatchPatternCloseBracket(ref state, true, false).success || MatchPatternOpenParens(ref state, true, false).success || MatchPatternCloseParens(ref state, true, false).success || MatchPatternWhitespace(ref state, true, false).success || MatchPatternComma(ref state, true, false).success || MatchPatternHexChar(ref state, true, false).success || MatchPatternDash(ref state, true, false).success; - if (!success) - { - break; - } - } - - state.CurrentIndex = 0; - state.DistinctIndex = 0; - return success; - } - - private (bool success, int offset) MatchOneOrMore2(string text, int startOffset) - { - int offset = startOffset; - bool subSuccess = true; - bool success = false; - int subOffset; - while (subSuccess) - { - (subSuccess, subOffset) = MatchWhitespace(text, offset); - offset += subOffset; - success |= subSuccess; - } - - return success ? (true, offset - startOffset) : (false, 0); - } - - private (bool success, StringMatchData matchData) MatchPatternWhitespace(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOneOrMore2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 20 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 20; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 20; - } - else - { - if (state.CheckFlags[20] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOneOrMore2(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 20 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[20] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private (bool success, int offset) MatchLiteral28(string text, int startOffset) - { - string literal = "<"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchLiteral29(string text, int startOffset) - { - string literal = "-"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchLiteral30(string text, int startOffset) - { - string literal = "_"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchOr4(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchWordChar(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchLiteral29(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchLiteral30(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, int offset) MatchWildcard4(string text, int startOffset) - { - int offset = startOffset; - bool success = true; - int subOffset; - while (success) - { - (success, subOffset) = MatchOr4(text, offset); - offset += subOffset; - } - - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchLiteral31(string text, int startOffset) - { - string literal = ">"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchGroup12(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral28(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLetter(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchWildcard4(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral31(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchGroup13(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLetter(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchWildcard4(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchOr6(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchGroup12(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchGroup13(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, StringMatchData matchData) MatchPatternRuleName(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr6(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 7 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 7; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 7; - } - else - { - if (state.CheckFlags[7] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr6(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 7 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[7] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherRuleName(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternRuleName(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeRuleName(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchPartByTextMatcherRuleName(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentRuleName(ref State state, FragmentMatchData matchData) - { - bool success = false; - if (!state.MatchCache.TryGetValue(new ValueTuple("RuleName", state.CurrentIndex), out FragmentMatchData partMatcherData)) - { - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RuleName", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeRuleName(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - state.MatchCache[new ValueTuple("RuleName", startIndex)] = partMatcherData; - } - else - { - state.MatchCache[new ValueTuple("RuleName", startIndex)] = null; - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - } - else if (success = partMatcherData != null) - { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentRuleName(ref state, matchData); - if (success) - { - matchCount++; - } - - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, int offset) MatchLiteral24(string text, int startOffset) - { - string literal = "::="; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchLiteral25(string text, int startOffset) - { - string literal = ":="; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchLiteral26(string text, int startOffset) - { - string literal = ":"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchLiteral27(string text, int startOffset) - { - string literal = "="; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchOr3(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchLiteral24(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchLiteral25(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchLiteral26(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchLiteral27(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, StringMatchData matchData) MatchPatternIs(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr3(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 6; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 6; - } - else - { - if (state.CheckFlags[6] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr3(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[6] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsIs(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternIs(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchData) - { - bool success = false; - if (!state.MatchCache.TryGetValue(new ValueTuple("RulePrefix", state.CurrentIndex), out FragmentMatchData partMatcherData)) - { - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RulePrefix", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeRulePrefix(ref state, partMatcherData) && MatchFragmentBoundsIs(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - state.MatchCache[new ValueTuple("RulePrefix", startIndex)] = partMatcherData; - } - else - { - state.MatchCache[new ValueTuple("RulePrefix", startIndex)] = null; - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - } - else if (success = partMatcherData != null) - { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; - } - - if (success) - { - matchData.Parts.AddRange(partMatcherData.Parts); - } - - return success; - } - - private (bool success, int offset) MatchLiteral0(string text, int startOffset) - { - string literal = "\""; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchNot0(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral0(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchGroup0(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchNot0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchAny(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchWildcard0(string text, int startOffset) - { - int offset = startOffset; - bool success = true; - int subOffset; - while (success) - { - (success, subOffset) = MatchGroup0(text, offset); - offset += subOffset; - } - - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchGroup1(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchWildcard0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, StringMatchData matchData) MatchPatternDoubleQuoteLiteral(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup1(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 2; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 2; - } - else - { - if (state.CheckFlags[2] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup1(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[2] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternDoubleQuoteLiteral(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral3(string text, int startOffset) - { - string literal = "'"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchNot1(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral3(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchGroup2(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchNot1(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchAny(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchWildcard1(string text, int startOffset) - { - int offset = startOffset; - bool success = true; - int subOffset; - while (success) - { - (success, subOffset) = MatchGroup2(text, offset); - offset += subOffset; - } - - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchGroup3(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral3(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchWildcard1(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral3(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, StringMatchData matchData) MatchPatternSingleQuoteLiteral(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup3(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 3; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 3; - } - else - { - if (state.CheckFlags[3] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup3(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[3] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternSingleQuoteLiteral(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral44(string text, int startOffset) - { - string literal = "("; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternOpenParens(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral44(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 18 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 18; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 18; - } - else - { - if (state.CheckFlags[18] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral44(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 18 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[18] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsOpenParens(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternOpenParens(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentExpression(ref state, matchData); - if (success) - { - matchCount++; - } - - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, int offset) MatchLiteral45(string text, int startOffset) - { - string literal = ")"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternCloseParens(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral45(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 19 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 19; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 19; - } - else - { - if (state.CheckFlags[19] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral45(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 19 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[19] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsCloseParens(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternCloseParens(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExpressionGroup", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenParens(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseParens(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.AddRange(partMatcherData.Parts); - } - - return success; - } - - private (bool success, int offset) MatchLiteral40(string text, int startOffset) - { - string literal = "{"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternOpenBrace(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 14 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 14; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 14; - } - else - { - if (state.CheckFlags[14] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 14 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[14] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsOpenBrace(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternOpenBrace(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, int offset) MatchLiteral41(string text, int startOffset) - { - string literal = "}"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternCloseBrace(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 15 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 15; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 15; - } - else - { - if (state.CheckFlags[15] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 15 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[15] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsCloseBrace(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternCloseBrace(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RepetitionGroup", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenBrace(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseBrace(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral42(string text, int startOffset) - { - string literal = "["; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternOpenBracket(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 16 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 16; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 16; - } - else - { - if (state.CheckFlags[16] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 16 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[16] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternOpenBracket(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, int offset) MatchLiteral43(string text, int startOffset) - { - string literal = "]"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternCloseBracket(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 17 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 17; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 17; - } - else - { - if (state.CheckFlags[17] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 17 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[17] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternCloseBracket(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OptionalGroup", StartIndex = state.CurrentIndex }; - success = (MatchFragmentBoundsOpenBracket(ref state, false, out _) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseBracket(ref state, false, out _)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchNot2(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral42(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchNot3(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral43(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchGroup4(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchNot2(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchNot3(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchAny(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchLiteral9(string text, int startOffset) - { - string literal = "\\"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchGroup5(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral9(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral42(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchGroup6(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral9(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral43(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchOr0(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchGroup5(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchGroup6(text, startOffset); - return success ? (true, subOffset) : (false, 0); - } - - private (bool success, int offset) MatchOr1(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchGroup4(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchOr0(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, int offset) MatchOneOrMore1(string text, int startOffset) - { - int offset = startOffset; - bool subSuccess = true; - bool success = false; - int subOffset; - while (subSuccess) - { - (subSuccess, subOffset) = MatchOr1(text, offset); - offset += subOffset; - success |= subSuccess; - } - - return success ? (true, offset - startOffset) : (false, 0); - } - - private (bool success, int offset) MatchGroup7(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral42(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchOneOrMore1(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral43(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, StringMatchData matchData) MatchPatternSpecialGroup(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup7(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 4; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 4; - } - else - { - if (state.CheckFlags[4] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup7(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[4] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternSpecialGroup(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private (bool success, int offset) MatchInsensitiveLiteral0(string text, int startOffset) - { - string literal = "#x"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (char.ToLowerInvariant(text[i + startOffset]) != char.ToLowerInvariant(literal[i])) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchCharBounds0(string text, int startOffset) - { - int charVal = text[startOffset]; - return (97 <= charVal && 102 >= charVal) ? (true, 1) : (false, 0); - } - - private (bool success, int offset) MatchCharBounds1(string text, int startOffset) - { - int charVal = text[startOffset]; - return (65 <= charVal && 70 >= charVal) ? (true, 1) : (false, 0); - } - - private (bool success, int offset) MatchCharBounds2(string text, int startOffset) - { - int charVal = text[startOffset]; - return (48 <= charVal && 57 >= charVal) ? (true, 1) : (false, 0); - } - - private (bool success, int offset) MatchOr7(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchCharBounds0(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchCharBounds1(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchCharBounds2(text, startOffset); - if (success) - { - return (true, subOffset); - } - - return (false, 0); - } - - private (bool success, int offset) MatchCountBounds0(string text, int startOffset) - { - int offset = startOffset; - bool subSuccess = true; - int subOffset; - int matches = 0; - for (; matches < 4 && subSuccess; matches++) - { - (subSuccess, subOffset) = MatchOr7(text, offset); - offset += subOffset; - } - - bool success = subSuccess || matches >= 1; - return success ? (true, offset - startOffset) : (false, 0); - } - - private (bool success, int offset) MatchGroup14(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchInsensitiveLiteral0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchCountBounds0(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, StringMatchData matchData) MatchPatternHexChar(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup14(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 22 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 22; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 22; - } - else - { - if (state.CheckFlags[22] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchGroup14(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 22 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[22] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternHexChar(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private (bool success, int offset) MatchOneOrMore0(string text, int startOffset) - { - int offset = startOffset; - bool subSuccess = true; - bool success = false; - int subOffset; - while (subSuccess) - { - (subSuccess, subOffset) = MatchDigit(text, offset); - offset += subOffset; - success |= subSuccess; - } - - return success ? (true, offset - startOffset) : (false, 0); - } - - private (bool success, StringMatchData matchData) MatchPatternNumber(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOneOrMore0(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 1; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 1; - } - else - { - if (state.CheckFlags[1] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOneOrMore0(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[1] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherNumber(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternNumber(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeItem(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchPartByTextMatcherDoubleQuoteLiteral(ref state, matchData) || MatchPartByTextMatcherSingleQuoteLiteral(ref state, matchData) || MatchFragmentExpressionGroup(ref state, matchData) || MatchFragmentRepetitionGroup(ref state, matchData) || MatchFragmentOptionalGroup(ref state, matchData) || MatchPartByTextMatcherSpecialGroup(ref state, matchData) || MatchFragmentRuleName(ref state, matchData) || MatchPartByTextMatcherHexChar(ref state, matchData) || MatchPartByTextMatcherNumber(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentItem(ref State state, FragmentMatchData matchData) - { - bool success = false; - if (!state.MatchCache.TryGetValue(new ValueTuple("Item", state.CurrentIndex), out FragmentMatchData partMatcherData)) - { - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Item", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeItem(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - state.MatchCache[new ValueTuple("Item", startIndex)] = partMatcherData; - } - else - { - state.MatchCache[new ValueTuple("Item", startIndex)] = null; - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - } - else if (success = partMatcherData != null) - { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchFragmentItem(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private (bool success, int offset) MatchLiteral37(string text, int startOffset) - { - string literal = "?"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternQuestionMark(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 11 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 11; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 11; - } - else - { - if (state.CheckFlags[11] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 11 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[11] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsQuestionMark(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternQuestionMark(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ZeroOrOneItem", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsQuestionMark(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral36(string text, int startOffset) - { - string literal = "+"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternPlus(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 10 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 10; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 10; - } - else - { - if (state.CheckFlags[10] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 10 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[10] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsPlus(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternPlus(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OneOrMoreItem", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsPlus(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral35(string text, int startOffset) - { - string literal = "*"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternAsterisk(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral35(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 9 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 9; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 9; - } - else - { - if (state.CheckFlags[9] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral35(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 9 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[9] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchFragmentBoundsAsterisk(ref State state, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPatternAsterisk(ref state, true, readOnly); - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentZeroOrMoreItem(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ZeroOrMoreItem", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsAsterisk(ref state, false, out StringMatchData endMatchData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeExpressionValue(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentZeroOrOneItem(ref state, matchData) || MatchFragmentOneOrMoreItem(ref state, matchData) || MatchFragmentZeroOrMoreItem(ref state, matchData) || MatchFragmentItem(ref state, matchData); - if (success) - { - matchCount++; - } - - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExpressionValue", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeExpressionValue(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.AddRange(partMatcherData.Parts); - } - - return success; - } - - private (bool success, int offset) MatchLiteral34(string text, int startOffset) - { - string literal = "|"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternOr(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral34(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 8 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 8; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 8; - } - else - { - if (state.CheckFlags[8] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral34(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 8 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[8] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherOr(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternOr(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - MatchPatternWhitespace(ref state, false, false); - success = MatchPartByTextMatcherOr(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentExpressionValue(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } - - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentOrSuffix(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OrSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 4 }; - success = (MatchFragmentPartsOrderedModeOrSuffix(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchPartByTextMatcherAsterisk(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternAsterisk(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - ; - success = MatchPartByTextMatcherAsterisk(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentExpressionValue(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "RepetitionSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 1 }; - success = (MatchFragmentPartsOrderedModeRepetitionSuffix(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, StringMatchData matchData) MatchPatternDash(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral29(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 23 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 23; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 23; - } - else - { - if (state.CheckFlags[23] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral29(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 23 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[23] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherDash(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternDash(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - ; - success = MatchPartByTextMatcherDash(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentExpressionValue(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExceptSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 2 }; - success = (MatchFragmentPartsOrderedModeExceptSuffix(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral46(string text, int startOffset) - { - string literal = ","; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternComma(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral46(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 21 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 21; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 21; - } - else - { - if (state.CheckFlags[21] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral46(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 21 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[21] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherComma(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternComma(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeOptionalComma(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchPartByTextMatcherComma(ref state, matchData); - if (success) - { - matchCount++; - } - - success = true || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentOptionalComma(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "OptionalComma", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeOptionalComma(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeNotRulePrefix(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchFragmentRulePrefix(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentNotRulePrefix(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "NotRulePrefix", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeNotRulePrefix(ref state, partMatcherData)); - if (success) - { - } - else - { - } - - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - return !success; - } - - private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - ; - success = MatchFragmentOptionalComma(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentNotRulePrefix(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentExpressionValue(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - success = success || 3 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentCommaSuffix(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "CommaSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 3 }; - success = (MatchFragmentPartsOrderedModeCommaSuffix(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, FragmentMatchData matchData) - { - bool overallSuccess = false; - bool subSuccess = false; - bool delimiterSuccess = false; - StringMatchData range = default; - int matchCount = 0; - int distinctIndex = state.DistinctIndex; - ; - do - { - subSuccess = false; - bool individualSuccess; - individualSuccess = MatchFragmentOrSuffix(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - individualSuccess = MatchFragmentRepetitionSuffix(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - individualSuccess = MatchFragmentExceptSuffix(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - individualSuccess = MatchFragmentCommaSuffix(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = (true, null); - goto Break; - } - - Break: - overallSuccess |= subSuccess; - } - while (subSuccess && delimiterSuccess); - if (delimiterSuccess && range != null) - { - state.CurrentIndex = range.StartIndex; - state.DistinctIndex = distinctIndex; - } - - bool thresholdSuccess = 0 <= matchCount; - bool success = overallSuccess || thresholdSuccess; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentExpressionSuffix(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "ExpressionSuffix", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsMultipleModeExpressionSuffix(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.AddRange(partMatcherData.Parts); - } - - return success; - } - - private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - MatchPatternWhitespace(ref state, false, false); - success = MatchFragmentExpressionValue(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentExpressionSuffix(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - if (success) - { - MatchPatternWhitespace(ref state, false, false); - } - - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentExpression(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Expression", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeExpression(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - ConvertToExpressionTree(partMatcherData, ExpressionMode.LikeNameTree); - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral38(string text, int startOffset) - { - string literal = ";"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternSemicolon(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 12 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 12; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 12; - } - else - { - if (state.CheckFlags[12] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 12 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[12] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternSemicolon(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private (bool success, int offset) MatchLiteral39(string text, int startOffset) - { - string literal = "."; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, StringMatchData matchData) MatchPatternPeriod(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 13 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 13; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 13; - } - else - { - if (state.CheckFlags[13] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 13 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[13] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - - private bool MatchPartByTextMatcherPeriod(ref State state, FragmentMatchData matchData) - { - (bool success, StringMatchData partMatchData) = MatchPatternPeriod(ref state, true, false); - if (success) - { - matchData.Parts.Add(partMatchData); - } - - return success; - } - - private bool MatchFragmentPartsOneModeEndMark(ref State state, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - success = MatchPartByTextMatcherSemicolon(ref state, matchData) || MatchPartByTextMatcherPeriod(ref state, matchData); - if (success) - { - matchCount++; - } - - success = false || matchCount > 0; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentEndMark(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "EndMark", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOneModeEndMark(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - } - - return success; - } - - private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - ; - success = MatchFragmentRulePrefix(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentExpression(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); - success = partSuccess; - if (!success) - { - goto Break; - } - - success = MatchFragmentEndMark(ref state, matchData); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - - goto Break; - } - else - { - matchCount++; - } - - Break: - success = success || 2 <= matchCount; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentRule(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Rule", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsOrderedModeRule(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - state.MatchCache.Clear(); - } - - return success; - } - - private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchData matchData) - { - bool overallSuccess = false; - bool subSuccess = false; - bool delimiterSuccess = false; - StringMatchData range = default; - int matchCount = 0; - int distinctIndex = state.DistinctIndex; - MatchPatternWhitespace(ref state, false, false); - do - { - subSuccess = false; - bool individualSuccess; - individualSuccess = MatchFragmentRule(ref state, matchData); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = MatchPatternWhitespace(ref state, false, false); - goto Break; - } - - Break: - overallSuccess |= subSuccess; - } - while (subSuccess && delimiterSuccess); - if (delimiterSuccess && range != null) - { - state.CurrentIndex = range.StartIndex; - state.DistinctIndex = distinctIndex; - } - - if (overallSuccess) - { - MatchPatternWhitespace(ref state, false, false); - } - - bool thresholdSuccess = 1 <= matchCount; - bool success = overallSuccess || thresholdSuccess; - if (!success) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return success; - } - - private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) - { - bool success = false; - FragmentMatchData partMatcherData = null; - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData { Name = "Rules", StartIndex = state.CurrentIndex }; - success = (MatchFragmentPartsMultipleModeRules(ref state, partMatcherData)); - if (success) - { - partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex; - } - else - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - - if (success) - { - matchData.Parts.Add(partMatcherData); - } - - return success; - } - - private (bool success, int offset) MatchNot4(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral45(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchGroup8(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral35(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchNot4(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchAny(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchWildcard2(string text, int startOffset) - { - int offset = startOffset; - bool success = true; - int subOffset; - while (success) - { - (success, subOffset) = MatchGroup8(text, offset); - offset += subOffset; - } - - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchGroup9(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral44(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral35(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchWildcard2(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral35(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral45(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchLiteral20(string text, int startOffset) - { - string literal = "/"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchLiteral22(string text, int startOffset) - { - string literal = "*/"; - int length = literal.Length; - if (startOffset + length > text.Length) - { - return (false, 0); - } - - for (int i = 0; i < length; i++) - { - if (text[i + startOffset] != literal[i]) - { - return (false, 0); - } - } - - return (true, length); - } - - private (bool success, int offset) MatchNot5(string text, int startOffset) - { - return text.Length > startOffset && !MatchLiteral22(text, startOffset).success ? (true, 0) : (false, 0); - } - - private (bool success, int offset) MatchGroup10(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchNot5(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchAny(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchWildcard3(string text, int startOffset) - { - int offset = startOffset; - bool success = true; - int subOffset; - while (success) - { - (success, subOffset) = MatchGroup10(text, offset); - offset += subOffset; - } - - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchGroup11(string text, int startOffset) - { - int offset = startOffset; - bool success; - int subOffset; - (success, subOffset) = MatchLiteral20(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral35(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchWildcard3(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - (success, subOffset) = MatchLiteral22(text, offset); - if (!success) - { - return (false, 0); - } - - offset += subOffset; - return (true, offset - startOffset); - } - - private (bool success, int offset) MatchOr2(string text, int startOffset) - { - bool success; - int subOffset; - (success, subOffset) = MatchGroup9(text, startOffset); - if (success) - { - return (true, subOffset); - } - - (success, subOffset) = MatchGroup11(text, startOffset); - return success ? (true, subOffset) : (false, 0); - } - - private (bool success, StringMatchData matchData) MatchPatternComment(ref State state, bool required, bool readOnly = false) - { - Rerun: - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr2(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData { Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = true, Id = 5 }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id != 5; - if (stringMatchData.IsNoise) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - goto Rerun; - } - - success = stringMatchData.Id == 5; - } - else - { - if (state.CheckFlags[5] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = MatchOr2(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData { Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = true, Id = 5 }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - - state.CheckFlags[5] = distinctIndex + 1; - } - } - - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - - if (!required) - { - success = true; - } - - return (success, stringMatchData); - } - } - } + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0059:Unnecessary assignment of a value", Justification = "")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "")] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0046:Convert to conditional expression", Justification = "")] + public class EbnfMatchEngine : AbstractLanguageMatchEngine + { + public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) + { + FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; + State state = new State() + { Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>() }; + PreMatchPatterns(ref state); + bool success = false; + switch (fragmentMatcher) + { + case "Rules": + success = MatchFragmentRules(ref state, matchData); + break; + case "NotRulePrefix": + success = MatchFragmentNotRulePrefix(ref state, matchData); + break; + case "RuleName": + success = MatchFragmentRuleName(ref state, matchData); + break; + case "Rule": + success = MatchFragmentRule(ref state, matchData); + break; + case "Expression": + success = MatchFragmentExpression(ref state, matchData); + break; + case "RepetitionSuffix": + success = MatchFragmentRepetitionSuffix(ref state, matchData); + break; + case "ExceptSuffix": + success = MatchFragmentExceptSuffix(ref state, matchData); + break; + case "CommaSuffix": + success = MatchFragmentCommaSuffix(ref state, matchData); + break; + case "OrSuffix": + success = MatchFragmentOrSuffix(ref state, matchData); + break; + case "OptionalComma": + success = MatchFragmentOptionalComma(ref state, matchData); + break; + case "RepetitionGroup": + success = MatchFragmentRepetitionGroup(ref state, matchData); + break; + case "OptionalGroup": + success = MatchFragmentOptionalGroup(ref state, matchData); + break; + case "ZeroOrOneItem": + success = MatchFragmentZeroOrOneItem(ref state, matchData); + break; + case "OneOrMoreItem": + success = MatchFragmentOneOrMoreItem(ref state, matchData); + break; + case "ZeroOrMoreItem": + success = MatchFragmentZeroOrMoreItem(ref state, matchData); + break; + case "Item": + success = MatchFragmentItem(ref state, matchData); + break; + } + + IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + if (success && matchFullText && state.CurrentIndex != state.Code.Length) + { + success = false; + failureIndex = state.CurrentIndex; + } + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); + } + + public override MatcherResult Match(string code, bool matchFullText = true) + { + FragmentMatchData matchData = new FragmentMatchData { StartIndex = 0 }; + State state = new State() + { Code = code, DistinctStringMatches = new List(2000), MatchCache = new Dictionary, FragmentMatchData>() }; + PreMatchPatterns(ref state); + bool success = MatchFragmentRules(ref state, matchData); + IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + if (success && matchFullText && state.CurrentIndex != state.Code.Length) + { + success = false; + failureIndex = state.CurrentIndex; + } + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); + } + + private bool PreMatchPatterns(ref State state) + { + int codeLength = state.Code.Length; + bool success = true; + while (state.CurrentIndex < codeLength) + { + success = false; + success = MatchPatternNumber(ref state, true, false).success || MatchPatternDoubleQuoteLiteral(ref state, true, false).success || MatchPatternSingleQuoteLiteral(ref state, true, false).success || MatchPatternSpecialGroup(ref state, true, false).success || MatchPatternComment(ref state, true, false).success || MatchPatternIs(ref state, true, false).success || MatchPatternRuleName(ref state, true, false).success || MatchPatternOr(ref state, true, false).success || MatchPatternAsterisk(ref state, true, false).success || MatchPatternPlus(ref state, true, false).success || MatchPatternQuestionMark(ref state, true, false).success || MatchPatternSemicolon(ref state, true, false).success || MatchPatternPeriod(ref state, true, false).success || MatchPatternOpenBrace(ref state, true, false).success || MatchPatternCloseBrace(ref state, true, false).success || MatchPatternOpenBracket(ref state, true, false).success || MatchPatternCloseBracket(ref state, true, false).success || MatchPatternOpenParens(ref state, true, false).success || MatchPatternCloseParens(ref state, true, false).success || MatchPatternWhitespace(ref state, true, false).success || MatchPatternComma(ref state, true, false).success || MatchPatternHexChar(ref state, true, false).success || MatchPatternDash(ref state, true, false).success; + if (!success) + { + break; + } + } + + state.CurrentIndex = 0; + state.DistinctIndex = 0; + return success; + } + + private (bool success, int offset) MatchOneOrMore2(string text, int startOffset) + { + int offset = startOffset; + bool subSuccess = true; + bool success = false; + int subOffset; + while (subSuccess) + { + (subSuccess, subOffset) = MatchWhitespace(text, offset); + offset += subOffset; + success |= subSuccess; + } + + return success ? (true, offset - startOffset) : (false, 0); + } + + private (bool success, StringMatchData matchData) MatchPatternWhitespace(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOneOrMore2(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 20 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 20; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 20; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOneOrMore2(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Whitespace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 20 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private (bool success, int offset) MatchLiteral28(string text, int startOffset) + { + string literal = "<"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchLiteral29(string text, int startOffset) + { + string literal = "-"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchLiteral30(string text, int startOffset) + { + string literal = "_"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchOr4(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchWordChar(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchLiteral29(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchLiteral30(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, int offset) MatchWildcard4(string text, int startOffset) + { + int offset = startOffset; + bool success = true; + int subOffset; + while (success) + { + (success, subOffset) = MatchOr4(text, offset); + offset += subOffset; + } + + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchLiteral31(string text, int startOffset) + { + string literal = ">"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchGroup12(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral28(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLetter(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard4(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral31(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchGroup13(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLetter(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard4(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchOr6(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchGroup12(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchGroup13(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, StringMatchData matchData) MatchPatternRuleName(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOr6(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 7 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 7; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 7; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOr6(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "RuleName", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 7 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherRuleName(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternRuleName(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeRuleName(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchPartByTextMatcherRuleName(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentRuleName(ref State state, FragmentMatchData matchData) + { + bool success = false; + if (!state.MatchCache.TryGetValue(new ValueTuple("RuleName", state.CurrentIndex), out FragmentMatchData partMatcherData)) + { + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "RuleName", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeRuleName(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("RuleName", startIndex)] = partMatcherData; + } + else + { + state.MatchCache[new ValueTuple("RuleName", startIndex)] = null; + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + } + else if (success = partMatcherData != null) + { + state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; + state.DistinctIndex = partMatcherData.EndDistinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeRulePrefix(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + MatchPatternWhitespace(ref state, false, false); + success = MatchFragmentRuleName(ref state, matchData); + if (success) + { + matchCount++; + } + + if (success) + { + MatchPatternWhitespace(ref state, false, false); + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, int offset) MatchLiteral24(string text, int startOffset) + { + string literal = "::="; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchLiteral25(string text, int startOffset) + { + string literal = ":="; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchLiteral26(string text, int startOffset) + { + string literal = ":"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchLiteral27(string text, int startOffset) + { + string literal = "="; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchOr3(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchLiteral24(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchLiteral25(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchLiteral26(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchLiteral27(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, StringMatchData matchData) MatchPatternIs(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOr3(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 6; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 6; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOr3(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Is", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 6 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsIs(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternIs(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentRulePrefix(ref State state, FragmentMatchData matchData) + { + bool success = false; + if (!state.MatchCache.TryGetValue(new ValueTuple("RulePrefix", state.CurrentIndex), out FragmentMatchData partMatcherData)) + { + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "RulePrefix", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeRulePrefix(ref state, partMatcherData) && MatchFragmentBoundsIs(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("RulePrefix", startIndex)] = partMatcherData; + } + else + { + state.MatchCache[new ValueTuple("RulePrefix", startIndex)] = null; + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + } + else if (success = partMatcherData != null) + { + state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; + state.DistinctIndex = partMatcherData.EndDistinctIndex; + } + + if (success) + { + matchData.Parts.AddRange(partMatcherData.Parts); + } + + return success; + } + + private (bool success, int offset) MatchLiteral0(string text, int startOffset) + { + string literal = "\""; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchNot0(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral0(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchGroup0(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchNot0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchAny(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchWildcard0(string text, int startOffset) + { + int offset = startOffset; + bool success = true; + int subOffset; + while (success) + { + (success, subOffset) = MatchGroup0(text, offset); + offset += subOffset; + } + + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchGroup1(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, StringMatchData matchData) MatchPatternDoubleQuoteLiteral(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup1(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 2; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 2; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup1(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "DoubleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 2 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherDoubleQuoteLiteral(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternDoubleQuoteLiteral(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral3(string text, int startOffset) + { + string literal = "'"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchNot1(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral3(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchGroup2(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchNot1(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchAny(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchWildcard1(string text, int startOffset) + { + int offset = startOffset; + bool success = true; + int subOffset; + while (success) + { + (success, subOffset) = MatchGroup2(text, offset); + offset += subOffset; + } + + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchGroup3(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral3(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard1(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral3(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, StringMatchData matchData) MatchPatternSingleQuoteLiteral(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup3(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 3; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 3; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup3(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "SingleQuoteLiteral", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 3 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherSingleQuoteLiteral(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternSingleQuoteLiteral(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral44(string text, int startOffset) + { + string literal = "("; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternOpenParens(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral44(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 18 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 18; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 18; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral44(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 18 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsOpenParens(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternOpenParens(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentPartsOneModeExpressionGroup(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + MatchPatternWhitespace(ref state, false, false); + success = MatchFragmentExpression(ref state, matchData); + if (success) + { + matchCount++; + } + + if (success) + { + MatchPatternWhitespace(ref state, false, false); + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, int offset) MatchLiteral45(string text, int startOffset) + { + string literal = ")"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternCloseParens(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral45(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 19 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 19; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 19; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral45(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseParens", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 19 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsCloseParens(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternCloseParens(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentExpressionGroup(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "ExpressionGroup", StartIndex = state.CurrentIndex }; + success = (MatchFragmentBoundsOpenParens(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseParens(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.AddRange(partMatcherData.Parts); + } + + return success; + } + + private (bool success, int offset) MatchLiteral40(string text, int startOffset) + { + string literal = "{"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternOpenBrace(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 14 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 14; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 14; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral40(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 14 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsOpenBrace(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternOpenBrace(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, int offset) MatchLiteral41(string text, int startOffset) + { + string literal = "}"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternCloseBrace(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 15 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 15; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 15; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral41(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseBrace", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 15 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsCloseBrace(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternCloseBrace(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentRepetitionGroup(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "RepetitionGroup", StartIndex = state.CurrentIndex }; + success = (MatchFragmentBoundsOpenBrace(ref state, false, out StringMatchData startMatchData) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseBrace(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral42(string text, int startOffset) + { + string literal = "["; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternOpenBracket(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 16 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 16; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 16; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral42(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "OpenBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 16 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsOpenBracket(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternOpenBracket(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, int offset) MatchLiteral43(string text, int startOffset) + { + string literal = "]"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternCloseBracket(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 17 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 17; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 17; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral43(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "CloseBracket", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 17 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsCloseBracket(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternCloseBracket(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentOptionalGroup(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "OptionalGroup", StartIndex = state.CurrentIndex }; + success = (MatchFragmentBoundsOpenBracket(ref state, false, out _) && MatchFragmentPartsOneModeExpressionGroup(ref state, partMatcherData) && MatchFragmentBoundsCloseBracket(ref state, false, out _)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchNot2(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral42(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchNot3(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral43(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchGroup4(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchNot2(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchNot3(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchAny(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchLiteral9(string text, int startOffset) + { + string literal = "\\"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchGroup5(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral9(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral42(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchGroup6(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral9(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral43(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchOr0(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchGroup5(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchGroup6(text, startOffset); + return success ? (true, subOffset) : (false, 0); + } + + private (bool success, int offset) MatchOr1(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchGroup4(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchOr0(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, int offset) MatchOneOrMore1(string text, int startOffset) + { + int offset = startOffset; + bool subSuccess = true; + bool success = false; + int subOffset; + while (subSuccess) + { + (subSuccess, subOffset) = MatchOr1(text, offset); + offset += subOffset; + success |= subSuccess; + } + + return success ? (true, offset - startOffset) : (false, 0); + } + + private (bool success, int offset) MatchGroup7(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral42(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchOneOrMore1(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral43(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, StringMatchData matchData) MatchPatternSpecialGroup(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup7(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 4; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 4; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup7(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "SpecialGroup", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 4 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherSpecialGroup(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternSpecialGroup(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private (bool success, int offset) MatchInsensitiveLiteral0(string text, int startOffset) + { + string literal = "#x"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (char.ToLowerInvariant(text[i + startOffset]) != char.ToLowerInvariant(literal[i])) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchCharBounds0(string text, int startOffset) + { + int charVal = text[startOffset]; + return (97 <= charVal && 102 >= charVal) ? (true, 1) : (false, 0); + } + + private (bool success, int offset) MatchCharBounds1(string text, int startOffset) + { + int charVal = text[startOffset]; + return (65 <= charVal && 70 >= charVal) ? (true, 1) : (false, 0); + } + + private (bool success, int offset) MatchCharBounds2(string text, int startOffset) + { + int charVal = text[startOffset]; + return (48 <= charVal && 57 >= charVal) ? (true, 1) : (false, 0); + } + + private (bool success, int offset) MatchOr7(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchCharBounds0(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchCharBounds1(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchCharBounds2(text, startOffset); + if (success) + { + return (true, subOffset); + } + + return (false, 0); + } + + private (bool success, int offset) MatchCountBounds0(string text, int startOffset) + { + int offset = startOffset; + bool subSuccess = true; + int subOffset; + int matches = 0; + for (; matches < 4 && subSuccess; matches++) + { + (subSuccess, subOffset) = MatchOr7(text, offset); + offset += subOffset; + } + + bool success = subSuccess || matches >= 1; + return success ? (true, offset - startOffset) : (false, 0); + } + + private (bool success, int offset) MatchGroup14(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchInsensitiveLiteral0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchCountBounds0(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, StringMatchData matchData) MatchPatternHexChar(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup14(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 22 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 22; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 22; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchGroup14(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "HexChar", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 22 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherHexChar(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternHexChar(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private (bool success, int offset) MatchOneOrMore0(string text, int startOffset) + { + int offset = startOffset; + bool subSuccess = true; + bool success = false; + int subOffset; + while (subSuccess) + { + (subSuccess, subOffset) = MatchDigit(text, offset); + offset += subOffset; + success |= subSuccess; + } + + return success ? (true, offset - startOffset) : (false, 0); + } + + private (bool success, StringMatchData matchData) MatchPatternNumber(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOneOrMore0(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 1; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 1; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOneOrMore0(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Number", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 1 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherNumber(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternNumber(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeItem(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchPartByTextMatcherDoubleQuoteLiteral(ref state, matchData) || MatchPartByTextMatcherSingleQuoteLiteral(ref state, matchData) || MatchFragmentExpressionGroup(ref state, matchData) || MatchFragmentRepetitionGroup(ref state, matchData) || MatchFragmentOptionalGroup(ref state, matchData) || MatchPartByTextMatcherSpecialGroup(ref state, matchData) || MatchFragmentRuleName(ref state, matchData) || MatchPartByTextMatcherHexChar(ref state, matchData) || MatchPartByTextMatcherNumber(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentItem(ref State state, FragmentMatchData matchData) + { + bool success = false; + if (!state.MatchCache.TryGetValue(new ValueTuple("Item", state.CurrentIndex), out FragmentMatchData partMatcherData)) + { + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "Item", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeItem(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + state.MatchCache[new ValueTuple("Item", startIndex)] = partMatcherData; + } + else + { + state.MatchCache[new ValueTuple("Item", startIndex)] = null; + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + } + else if (success = partMatcherData != null) + { + state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; + state.DistinctIndex = partMatcherData.EndDistinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeZeroOrOneItem(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchFragmentItem(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private (bool success, int offset) MatchLiteral37(string text, int startOffset) + { + string literal = "?"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternQuestionMark(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 11 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 11; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 11; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral37(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "QuestionMark", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 11 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsQuestionMark(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternQuestionMark(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentZeroOrOneItem(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "ZeroOrOneItem", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsQuestionMark(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral36(string text, int startOffset) + { + string literal = "+"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternPlus(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 10 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 10; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 10; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral36(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Plus", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 10 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsPlus(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternPlus(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentOneOrMoreItem(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "OneOrMoreItem", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsPlus(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral35(string text, int startOffset) + { + string literal = "*"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternAsterisk(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral35(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 9 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 9; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 9; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral35(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Asterisk", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 9 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchFragmentBoundsAsterisk(ref State state, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPatternAsterisk(ref state, true, readOnly); + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentZeroOrMoreItem(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "ZeroOrMoreItem", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeZeroOrOneItem(ref state, partMatcherData) && MatchFragmentBoundsAsterisk(ref state, false, out StringMatchData endMatchData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeExpressionValue(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + MatchPatternWhitespace(ref state, false, false); + success = MatchFragmentZeroOrOneItem(ref state, matchData) || MatchFragmentOneOrMoreItem(ref state, matchData) || MatchFragmentZeroOrMoreItem(ref state, matchData) || MatchFragmentItem(ref state, matchData); + if (success) + { + matchCount++; + } + + if (success) + { + MatchPatternWhitespace(ref state, false, false); + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentExpressionValue(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "ExpressionValue", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeExpressionValue(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.AddRange(partMatcherData.Parts); + } + + return success; + } + + private (bool success, int offset) MatchLiteral34(string text, int startOffset) + { + string literal = "|"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternOr(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral34(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 8 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 8; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 8; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral34(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Or", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 8 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherOr(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternOr(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOrderedModeOrSuffix(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + MatchPatternWhitespace(ref state, false, false); + success = MatchPartByTextMatcherOr(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentExpressionValue(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + if (success) + { + MatchPatternWhitespace(ref state, false, false); + } + + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentOrSuffix(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "OrSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 4 }; + success = (MatchFragmentPartsOrderedModeOrSuffix(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchPartByTextMatcherAsterisk(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternAsterisk(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOrderedModeRepetitionSuffix(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + ; + success = MatchPartByTextMatcherAsterisk(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentExpressionValue(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentRepetitionSuffix(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "RepetitionSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 1 }; + success = (MatchFragmentPartsOrderedModeRepetitionSuffix(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, StringMatchData matchData) MatchPatternDash(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral29(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 23 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 23; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 23; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral29(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Dash", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 23 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherDash(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternDash(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOrderedModeExceptSuffix(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + ; + success = MatchPartByTextMatcherDash(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentExpressionValue(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentExceptSuffix(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "ExceptSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 2 }; + success = (MatchFragmentPartsOrderedModeExceptSuffix(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral46(string text, int startOffset) + { + string literal = ","; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternComma(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral46(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 21 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 21; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 21; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral46(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Comma", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 21 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherComma(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternComma(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeOptionalComma(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchPartByTextMatcherComma(ref state, matchData); + if (success) + { + matchCount++; + } + + success = true || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentOptionalComma(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "OptionalComma", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeOptionalComma(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeNotRulePrefix(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchFragmentRulePrefix(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentNotRulePrefix(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "NotRulePrefix", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeNotRulePrefix(ref state, partMatcherData)); + if (success) + { + } + else + { + } + + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + return !success; + } + + private bool MatchFragmentPartsOrderedModeCommaSuffix(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + ; + success = MatchFragmentOptionalComma(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentNotRulePrefix(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentExpressionValue(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + success = success || 3 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentCommaSuffix(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "CommaSuffix", StartIndex = state.CurrentIndex, ExpressionOrder = 3 }; + success = (MatchFragmentPartsOrderedModeCommaSuffix(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private bool MatchFragmentPartsMultipleModeExpressionSuffix(ref State state, FragmentMatchData matchData) + { + bool overallSuccess = false; + bool subSuccess = false; + bool delimiterSuccess = false; + StringMatchData range = default; + int matchCount = 0; + int distinctIndex = state.DistinctIndex; + ; + do + { + subSuccess = false; + bool individualSuccess; + individualSuccess = MatchFragmentOrSuffix(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + individualSuccess = MatchFragmentRepetitionSuffix(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + individualSuccess = MatchFragmentExceptSuffix(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + individualSuccess = MatchFragmentCommaSuffix(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = (true, null); + goto Break; + } + + Break: + overallSuccess |= subSuccess; + } + while (subSuccess && delimiterSuccess); + if (delimiterSuccess && range != null) + { + state.CurrentIndex = range.StartIndex; + state.DistinctIndex = distinctIndex; + } + + bool thresholdSuccess = 0 <= matchCount; + bool success = overallSuccess || thresholdSuccess; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentExpressionSuffix(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "ExpressionSuffix", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsMultipleModeExpressionSuffix(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.AddRange(partMatcherData.Parts); + } + + return success; + } + + private bool MatchFragmentPartsOrderedModeExpression(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + MatchPatternWhitespace(ref state, false, false); + success = MatchFragmentExpressionValue(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentExpressionSuffix(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + if (success) + { + MatchPatternWhitespace(ref state, false, false); + } + + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentExpression(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "Expression", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOrderedModeExpression(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + ConvertToExpressionTree(partMatcherData, ExpressionMode.LikeNameTree); + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral38(string text, int startOffset) + { + string literal = ";"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternSemicolon(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 12 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 12; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 12; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral38(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Semicolon", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 12 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherSemicolon(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternSemicolon(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private (bool success, int offset) MatchLiteral39(string text, int startOffset) + { + string literal = "."; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, StringMatchData matchData) MatchPatternPeriod(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 13 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 13; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 13; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchLiteral39(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Period", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = false, Id = 13 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + + private bool MatchPartByTextMatcherPeriod(ref State state, FragmentMatchData matchData) + { + (bool success, StringMatchData partMatchData) = MatchPatternPeriod(ref state, true, false); + if (success) + { + matchData.Parts.Add(partMatchData); + } + + return success; + } + + private bool MatchFragmentPartsOneModeEndMark(ref State state, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + success = MatchPartByTextMatcherSemicolon(ref state, matchData) || MatchPartByTextMatcherPeriod(ref state, matchData); + if (success) + { + matchCount++; + } + + success = false || matchCount > 0; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentEndMark(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "EndMark", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOneModeEndMark(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + } + + return success; + } + + private bool MatchFragmentPartsOrderedModeRule(ref State state, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + ; + success = MatchFragmentRulePrefix(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentExpression(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPatternWhitespace(ref state, false, false); + success = partSuccess; + if (!success) + { + goto Break; + } + + success = MatchFragmentEndMark(ref state, matchData); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + + goto Break; + } + else + { + matchCount++; + } + + Break: + success = success || 2 <= matchCount; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentRule(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "Rule", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsOrderedModeRule(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + state.MatchCache.Clear(); + } + + return success; + } + + private bool MatchFragmentPartsMultipleModeRules(ref State state, FragmentMatchData matchData) + { + bool overallSuccess = false; + bool subSuccess = false; + bool delimiterSuccess = false; + StringMatchData range = default; + int matchCount = 0; + int distinctIndex = state.DistinctIndex; + MatchPatternWhitespace(ref state, false, false); + do + { + subSuccess = false; + bool individualSuccess; + individualSuccess = MatchFragmentRule(ref state, matchData); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = MatchPatternWhitespace(ref state, false, false); + goto Break; + } + + Break: + overallSuccess |= subSuccess; + } + while (subSuccess && delimiterSuccess); + if (delimiterSuccess && range != null) + { + state.CurrentIndex = range.StartIndex; + state.DistinctIndex = distinctIndex; + } + + if (overallSuccess) + { + MatchPatternWhitespace(ref state, false, false); + } + + bool thresholdSuccess = 1 <= matchCount; + bool success = overallSuccess || thresholdSuccess; + if (!success) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return success; + } + + private bool MatchFragmentRules(ref State state, FragmentMatchData matchData) + { + bool success = false; + FragmentMatchData partMatcherData = null; + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + partMatcherData = new FragmentMatchData { Name = "Rules", StartIndex = state.CurrentIndex }; + success = (MatchFragmentPartsMultipleModeRules(ref state, partMatcherData)); + if (success) + { + partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex; + } + else + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + + if (success) + { + matchData.Parts.Add(partMatcherData); + } + + return success; + } + + private (bool success, int offset) MatchNot4(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral45(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchGroup8(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral35(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchNot4(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchAny(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchWildcard2(string text, int startOffset) + { + int offset = startOffset; + bool success = true; + int subOffset; + while (success) + { + (success, subOffset) = MatchGroup8(text, offset); + offset += subOffset; + } + + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchGroup9(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral44(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral35(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard2(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral35(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral45(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchLiteral20(string text, int startOffset) + { + string literal = "/"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchLiteral22(string text, int startOffset) + { + string literal = "*/"; + int length = literal.Length; + if (startOffset + length > text.Length) + { + return (false, 0); + } + + for (int i = 0; i < length; i++) + { + if (text[i + startOffset] != literal[i]) + { + return (false, 0); + } + } + + return (true, length); + } + + private (bool success, int offset) MatchNot5(string text, int startOffset) + { + return text.Length > startOffset && !MatchLiteral22(text, startOffset).success ? (true, 0) : (false, 0); + } + + private (bool success, int offset) MatchGroup10(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchNot5(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchAny(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchWildcard3(string text, int startOffset) + { + int offset = startOffset; + bool success = true; + int subOffset; + while (success) + { + (success, subOffset) = MatchGroup10(text, offset); + offset += subOffset; + } + + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchGroup11(string text, int startOffset) + { + int offset = startOffset; + bool success; + int subOffset; + (success, subOffset) = MatchLiteral20(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral35(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchWildcard3(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + (success, subOffset) = MatchLiteral22(text, offset); + if (!success) + { + return (false, 0); + } + + offset += subOffset; + return (true, offset - startOffset); + } + + private (bool success, int offset) MatchOr2(string text, int startOffset) + { + bool success; + int subOffset; + (success, subOffset) = MatchGroup9(text, startOffset); + if (success) + { + return (true, subOffset); + } + + (success, subOffset) = MatchGroup11(text, startOffset); + return success ? (true, subOffset) : (false, 0); + } + + private (bool success, StringMatchData matchData) MatchPatternComment(ref State state, bool required, bool readOnly = false) + { + Rerun: + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOr2(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData { Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = true, Id = 5 }; + state.DistinctStringMatches.Add(stringMatchData); + success = stringMatchData != null; + if (!readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + } + else if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + else + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id != 5; + if (stringMatchData.IsNoise) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + goto Rerun; + } + + success = stringMatchData.Id == 5; + } + else + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = MatchOr2(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData { Name = "Comment", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, IsNoise = true, Id = 5 }; + state.DistinctStringMatches[distinctIndex] = stringMatchData; + } + } + + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex += stringMatchData.Length; + } + + if (!required) + { + success = true; + } + + return (success, stringMatchData); + } + } + } } \ No newline at end of file diff --git a/src/Matcher.Interop.Model/DefinitionConverter.cs b/src/Matcher.Interop.Model/DefinitionConverter.cs index 9c37715..ebc29e4 100644 --- a/src/Matcher.Interop.Model/DefinitionConverter.cs +++ b/src/Matcher.Interop.Model/DefinitionConverter.cs @@ -1,4 +1,5 @@ using Synfron.Staxe.Matcher.Input; +using Synfron.Staxe.Matcher.Input.Actions; using Synfron.Staxe.Matcher.Input.Patterns; using System; using System.Collections.Generic; @@ -10,84 +11,211 @@ public static class DefinitionConverter { public static LanguageMatcher Convert(LanguageMatcherDefinition languageMatcherModel) - { - Dictionary patternMatcherMap = new Dictionary(StringComparer.Ordinal); - List patternMatchers = new List(languageMatcherModel.Patterns.Count); - Dictionary fragmentMatcherMap = new Dictionary(StringComparer.Ordinal); - List fragmentMatchers = new List(languageMatcherModel.Fragments.Count); + { - bool isEagar = languageMatcherModel.IndexingMode == IndexingMode.Eager; + Dictionary fragmentMatcherMap = new Dictionary(StringComparer.Ordinal); + Dictionary patternMatcherMap = new Dictionary(StringComparer.Ordinal); + Dictionary matcherActionMap = new Dictionary(StringComparer.Ordinal); - for (int patternIndex = 0; patternIndex < languageMatcherModel.Patterns.Count; patternIndex++) - { - PatternMatcherDefinition model = languageMatcherModel.Patterns[patternIndex]; - PatternMatcher patternMatcher = isEagar ? PatternReader.Parse(patternIndex + 1, model.Name, model.Pattern) : PatternReader.LazyParse(patternIndex + 1, model.Name, model.Pattern); - patternMatcher.IsNoise = model.IsNoise; - patternMatcher.Mergable = model.Mergable; - patternMatcherMap.Add(patternMatcher.Name, patternMatcher); - patternMatchers.Add(patternMatcher); - } - - for (int matcherIndex = 0; matcherIndex < languageMatcherModel.Fragments.Count; matcherIndex++) - { - FragmentMatcherDefinition model = languageMatcherModel.Fragments[matcherIndex]; - FragmentMatcher fragmentMatcher = new FragmentMatcher - ( - id: matcherIndex + 1, - name: model.Name, - parts: new List(), - fallThroughMode: model.FallThroughMode, - isNoise: model.IsNoise, - partsDelimiterRequired: model.PartsDelimiterRequired, - partsMatchMode: model.PartsMatchMode, - minMatchedParts: model.MinMatchedParts, - cacheable: model.Cacheable, - clearCache: model.ClearCache, - expressionMode: model.ExpressionMode, - expressionOrder: model.ExpressionOrder, - boundsAsParts: model.BoundsAsParts, - discardBounds: model.DiscardBounds, - end: model.End != null ? patternMatcherMap[model.End] : null, - partsDelimiter: model.PartsDelimiter != null ? patternMatcherMap[model.PartsDelimiter] : null, - partsPadding: model.PartsPadding != null ? patternMatcherMap[model.PartsPadding] : null, - start: model.Start != null ? patternMatcherMap[model.Start] : null, - negate: model.Negate - ); - fragmentMatcherMap.Add(fragmentMatcher.Name, fragmentMatcher); - fragmentMatchers.Add(fragmentMatcher); - }; + List fragmentPatternModels = new List(); - foreach (FragmentMatcherDefinition model in languageMatcherModel.Fragments) - { - FragmentMatcher fragmentMatcher = fragmentMatcherMap[model.Name]; - foreach (IMatcher part in model.Parts.Select(GetPartMatcher)) - { - fragmentMatcher.Parts.Add(part); - } - } + IList blobs = ProcessMatcherActions(languageMatcherModel, matcherActionMap); + List patternMatchers = ProcessPatternMatchers(languageMatcherModel, patternMatcherMap, fragmentPatternModels); + List fragmentMatchers = ProcessFragmentMatchers(languageMatcherModel, fragmentMatcherMap, patternMatcherMap, matcherActionMap); - FragmentMatcher startingMatcher = fragmentMatcherMap[languageMatcherModel.StartingFragment]; + foreach (PatternMatcherDefinition model in fragmentPatternModels) + { + FragmentPatternMatcher fragmentPatternMatcher = (FragmentPatternMatcher)patternMatcherMap[model.Name]; + fragmentPatternMatcher.Fragment = fragmentMatcherMap[model.Fragment]; + } - return new LanguageMatcher - { - Name = languageMatcherModel.Name, - Fragments = fragmentMatchers, - Patterns = patternMatchers, - StartingFragment = startingMatcher, - LogMatches = languageMatcherModel.LogMatches, - IndexingMode = languageMatcherModel.IndexingMode - }; + return new LanguageMatcher + { + Name = languageMatcherModel.Name, + Fragments = fragmentMatchers.ToArray(), + Patterns = patternMatchers.ToArray(), + StartingFragment = fragmentMatcherMap[languageMatcherModel.StartingFragment], + LogMatches = languageMatcherModel.LogMatches, + IndexingMode = languageMatcherModel.IndexingMode, + Blobs = blobs.ToArray() + }; + } - IMatcher GetPartMatcher(string name) - { - return name.StartsWith("[") ? fragmentMatcherMap[name.Substring(1, name.Length - 2)] : (IMatcher)patternMatcherMap[name]; - } - } - public static LanguageMatcherDefinition Convert(LanguageMatcher languageMatcher) + private static IList ProcessMatcherActions(LanguageMatcherDefinition languageMatcherModel, Dictionary matcherActionMap) + { + int blobId = 0; + List blobs = new List(); + if (languageMatcherModel.Actions != null) + { + Dictionary createVariableMap = new Dictionary(); + foreach (MatcherActionDefinition actionDefinition in languageMatcherModel.Actions) + { + if (actionDefinition.ActionType == MatcherActionType.CreateVariable) + { + CreateVariableMatcherAction action = new CreateVariableMatcherAction + { + Name = actionDefinition.Name, + BlobId = blobId++, + Source = actionDefinition.Source ?? VariableValueSource.Value, + Value = actionDefinition.Value ?? string.Empty + }; + blobs.Add(actionDefinition.FirstVariableName); + createVariableMap.Add(actionDefinition.FirstVariableName, action); + matcherActionMap.Add(actionDefinition.Name, action); + } + } + + foreach (MatcherActionDefinition actionDefinition in languageMatcherModel.Actions) + { + MatcherAction action = null; + switch (actionDefinition.ActionType) + { + case MatcherActionType.Assert: + { + action = new AssertMatcherAction + { + Name = actionDefinition.Name, + FirstBlobId = createVariableMap[actionDefinition.FirstVariableName].BlobId, + SecondBlobId = createVariableMap[actionDefinition.SecondVariableName].BlobId, + Assert = actionDefinition.Assert + }; + matcherActionMap.Add(actionDefinition.Name, action); + break; + } + case MatcherActionType.UpdateVariable: + { + action = new UpdateVariableMatcherAction + { + Name = actionDefinition.Name, + Change = actionDefinition.Change ?? VariableUpdateAction.Set, + TargetBlobId = createVariableMap[actionDefinition.FirstVariableName].BlobId, + SourceBlobId = createVariableMap[actionDefinition.SecondVariableName].BlobId + }; + matcherActionMap.Add(actionDefinition.Name, action); + break; + } + case MatcherActionType.CreateVariable: + break; + default: + throw new InvalidOperationException($"Action type {actionDefinition.ActionType} is not supported"); + } + } + } + return blobs; + } + + private static List ProcessPatternMatchers(LanguageMatcherDefinition languageMatcherModel, Dictionary patternMatcherMap, List fragmentPatternModels) + { + List patternMatchers = new List(languageMatcherModel.Patterns?.Count ?? 0); + if (languageMatcherModel.Patterns != null) + { + bool isEager = languageMatcherModel.IndexingMode == IndexingMode.Eager; + for (int patternIndex = 0; patternIndex < languageMatcherModel.Patterns.Count; patternIndex++) + { + PatternMatcherDefinition model = languageMatcherModel.Patterns[patternIndex]; + if (string.IsNullOrEmpty(model.Fragment)) + { + PatternMatcher patternMatcher = isEager ? PatternReader.Parse(patternIndex + 1, model.Name, model.Pattern) : PatternReader.LazyParse(patternIndex + 1, model.Name, model.Pattern); + patternMatcher.IsNoise = model.IsNoise; + patternMatcher.Mergable = model.Mergable; + patternMatcherMap.Add(patternMatcher.Name, patternMatcher); + if (!model.IsAuxiliary) + { + patternMatchers.Add(patternMatcher); + } + } + else + { + FragmentPatternMatcher patternMatcher = new FragmentPatternMatcher() + { + Name = model.Name, + IsNoise = model.IsNoise + + }; + patternMatcherMap.Add(patternMatcher.Name, patternMatcher); + fragmentPatternModels.Add(model); + if (!model.IsAuxiliary) + { + patternMatchers.Add(patternMatcher); + } + } + } + } + return patternMatchers; + } + + private static List ProcessFragmentMatchers(LanguageMatcherDefinition languageMatcherModel, Dictionary fragmentMatcherMap, Dictionary patternMatcherMap, Dictionary matcherActionMap) + { + List fragmentMatchers = new List(languageMatcherModel.Fragments?.Count ?? 0); + HashSet actionsReferenced = new HashSet(); + if (languageMatcherModel.Fragments != null) + { + for (int matcherIndex = 0; matcherIndex < languageMatcherModel.Fragments.Count; matcherIndex++) + { + FragmentMatcherDefinition model = languageMatcherModel.Fragments[matcherIndex]; + FragmentMatcher fragmentMatcher = new FragmentMatcher + ( + id: matcherIndex + 1, + name: model.Name, + parts: new IMatcher[0], + fallThroughMode: model.FallThroughMode, + isNoise: model.IsNoise, + partsDelimiterRequired: model.PartsDelimiterRequired, + partsMatchMode: model.PartsMatchMode, + minMatchedParts: model.MinMatchedParts, + cacheable: model.Cacheable, + clearCache: model.ClearCache, + expressionMode: model.ExpressionMode, + expressionOrder: model.ExpressionOrder, + boundsAsParts: model.BoundsAsParts, + discardBounds: model.DiscardBounds, + end: model.End != null ? patternMatcherMap[model.End] : null, + partsDelimiter: model.PartsDelimiter != null ? patternMatcherMap[model.PartsDelimiter] : null, + partsPadding: model.PartsPadding != null ? patternMatcherMap[model.PartsPadding] : null, + start: model.Start != null ? patternMatcherMap[model.Start] : null, + negate: model.Negate, + actions: model.Actions != null && model.Actions.Count > 0 ? + model.Actions.Select(actionName => matcherActionMap[actionName]).ToArray() : null + ); + fragmentMatcherMap.Add(fragmentMatcher.Name, fragmentMatcher); + fragmentMatchers.Add(fragmentMatcher); + if (fragmentMatcher.Actions != null) + { + foreach (MatcherAction action in fragmentMatcher.Actions) + { + actionsReferenced.Add(action.Name); + } + } + }; + + foreach (FragmentMatcherDefinition model in languageMatcherModel.Fragments) + { + FragmentMatcher fragmentMatcher = fragmentMatcherMap[model.Name]; + fragmentMatcher.Parts = model.Parts.Select(name => GetPartMatcher(patternMatcherMap, fragmentMatcherMap, name)).ToArray(); + } + + foreach (CreateVariableMatcherAction matcherAction in matcherActionMap.Values.OfType()) + { + if (!actionsReferenced.Contains(matcherAction.Name)) + { + throw new InvalidOperationException($"CreateVariable Action {matcherAction.Name} is not referenced by a Fragment"); + } + } + } + return fragmentMatchers; + } + + private static IMatcher GetPartMatcher(Dictionary patternMatcherMap, Dictionary fragmentMatcherMap, string name) + { + return name.StartsWith("[") ? fragmentMatcherMap[name.Substring(1, name.Length - 2)] : (IMatcher)patternMatcherMap[name]; + } + + public static LanguageMatcherDefinition Convert(LanguageMatcher languageMatcher) { + Dictionary matcherActions = new Dictionary(); HashSet convertedPatterns = new HashSet(); - List patterns = new List(languageMatcher.Patterns.Select(ConvertPattern)); + List patterns = new List(languageMatcher.Patterns.Select(pattern => ConvertPattern(pattern, false))); List fragments = new List(languageMatcher.Fragments.Select(ConvertFragment)); LanguageMatcherDefinition matcherDefinition = new LanguageMatcherDefinition @@ -97,33 +225,87 @@ public static LanguageMatcherDefinition Convert(LanguageMatcher languageMatcher) IndexingMode = languageMatcher.IndexingMode, StartingFragment = languageMatcher.StartingFragment.Name, Patterns = patterns, - Fragments = fragments + Fragments = fragments, + Actions = matcherActions.Count > 0 ? matcherActions.Values.ToList() : null }; return matcherDefinition; - PatternMatcherDefinition ConvertPattern(PatternMatcher patternMatcher) + PatternMatcherDefinition ConvertPattern(PatternMatcher patternMatcher, bool isAuxilary) { convertedPatterns.Add(patternMatcher.Name); - return new PatternMatcherDefinition - { - IsNoise = patternMatcher.IsNoise, - Mergable = patternMatcher.Mergable, - Name = patternMatcher.Name, - Pattern = patternMatcher is GroupPatternMatcher groupPatternMatcher ? groupPatternMatcher.ToString(true) : patternMatcher.ToString() - }; + return patternMatcher is FragmentPatternMatcher fragmentPatternMatcher ? + new PatternMatcherDefinition + { + Name = patternMatcher.Name, + Fragment = fragmentPatternMatcher.Fragment.Name, + IsNoise = patternMatcher.IsNoise + } : + new PatternMatcherDefinition + { + IsAuxiliary = isAuxilary, + IsNoise = patternMatcher.IsNoise, + Mergable = patternMatcher.Mergable, + Name = patternMatcher.Name, + Pattern = patternMatcher is GroupPatternMatcher groupPatternMatcher ? groupPatternMatcher.ToString(true) : patternMatcher.ToString() + }; }; - string AddPatternAndGetName(PatternMatcher patternMatcher) + MatcherActionDefinition ConvertMatcherAction(MatcherAction matcherAction) + { + switch (matcherAction) + { + case UpdateVariableMatcherAction updateVariableMatcherAction: + return new MatcherActionDefinition + { + ActionType = updateVariableMatcherAction.ActionType, + Name = updateVariableMatcherAction.Name, + Change = updateVariableMatcherAction.Change, + FirstVariableName = languageMatcher.Blobs[updateVariableMatcherAction.TargetBlobId], + SecondVariableName = languageMatcher.Blobs[updateVariableMatcherAction.SourceBlobId] + }; + case CreateVariableMatcherAction createVariableMatcherAction: + return new MatcherActionDefinition + { + ActionType = createVariableMatcherAction.ActionType, + Name = createVariableMatcherAction.Name, + Source = createVariableMatcherAction.Source, + FirstVariableName = languageMatcher.Blobs[createVariableMatcherAction.BlobId], + Value = createVariableMatcherAction.Value + }; + case AssertMatcherAction assertMatcherAction: + return new MatcherActionDefinition + { + ActionType = assertMatcherAction.ActionType, + Name = assertMatcherAction.Name, + Assert = assertMatcherAction.Assert, + FirstVariableName = languageMatcher.Blobs[assertMatcherAction.FirstBlobId], + SecondVariableName = languageMatcher.Blobs[assertMatcherAction.SecondBlobId] + }; + default: + return null; + }; + }; + + string AddPatternAndGetName(PatternMatcher patternMatcher) { if (patternMatcher != null && !convertedPatterns.Contains(patternMatcher.Name)) { - patterns.Add(ConvertPattern(patternMatcher)); + patterns.Add(ConvertPattern(patternMatcher, true)); } return patternMatcher?.Name; }; - FragmentMatcherDefinition ConvertFragment(FragmentMatcher fragmentMatcher) + string AddActionAndGetName(MatcherAction matcherAction) + { + if (matcherAction != null && !matcherActions.ContainsKey(matcherAction.Name)) + { + matcherActions.Add(matcherAction.Name, ConvertMatcherAction(matcherAction)); + } + return matcherAction?.Name; + }; + + FragmentMatcherDefinition ConvertFragment(FragmentMatcher fragmentMatcher) { return new FragmentMatcherDefinition { @@ -147,8 +329,9 @@ FragmentMatcherDefinition ConvertFragment(FragmentMatcher fragmentMatcher) Parts = fragmentMatcher.Parts.Select(part => { return part is FragmentMatcher fragmentPart ? $"[{fragmentPart.Name}]" : AddPatternAndGetName((PatternMatcher)part); - }).ToList() - }; + }).ToList(), + Actions = fragmentMatcher.Actions?.Select(action => AddActionAndGetName(action)).ToList() + }; }; } } diff --git a/src/Matcher.Interop.Model/FragmentMatcherDefinition.cs b/src/Matcher.Interop.Model/FragmentMatcherDefinition.cs index 6eb0a72..02ade7d 100644 --- a/src/Matcher.Interop.Model/FragmentMatcherDefinition.cs +++ b/src/Matcher.Interop.Model/FragmentMatcherDefinition.cs @@ -26,7 +26,9 @@ public string Name public string End { get; set; } - public List Parts { get; set; } = new List(); + public IList Parts { get; set; } = new List(); + + public IList Actions { get; set; } public string PartsDelimiter { get; set; } diff --git a/src/Matcher.Interop.Model/LanguageMatcherDefinition.cs b/src/Matcher.Interop.Model/LanguageMatcherDefinition.cs index 71a3f3d..8a92048 100644 --- a/src/Matcher.Interop.Model/LanguageMatcherDefinition.cs +++ b/src/Matcher.Interop.Model/LanguageMatcherDefinition.cs @@ -6,11 +6,14 @@ namespace Synfron.Staxe.Matcher.Interop.Model public class LanguageMatcherDefinition { - public List Patterns { get; set; } + public IList Patterns { get; set; } public string StartingFragment { get; set; } - public List Fragments { get; set; } + public IList Fragments { get; set; } + + public IList Actions { get; set; } + public string Name { get; set; } public bool LogMatches { get; set; } diff --git a/src/Matcher.Interop.Model/MatcherActionDefinition.cs b/src/Matcher.Interop.Model/MatcherActionDefinition.cs new file mode 100644 index 0000000..39ca2b5 --- /dev/null +++ b/src/Matcher.Interop.Model/MatcherActionDefinition.cs @@ -0,0 +1,23 @@ +using Synfron.Staxe.Matcher.Input.Actions; + +namespace Synfron.Staxe.Matcher.Interop.Model +{ + public class MatcherActionDefinition + { + public string Name { get; set; } + + public MatcherActionType ActionType { get; set; } + + public string FirstVariableName { get; set; } + + public string SecondVariableName { get; set; } + + public VariableValueSource? Source { get; set; } + + public VariableUpdateAction? Change { get; set; } + + public AssertType Assert { get; set; } + + public object Value { get; set; } + } +} diff --git a/src/Matcher.Interop.Model/PatternMatcherDefinition.cs b/src/Matcher.Interop.Model/PatternMatcherDefinition.cs index bc9ed82..54ab979 100644 --- a/src/Matcher.Interop.Model/PatternMatcherDefinition.cs +++ b/src/Matcher.Interop.Model/PatternMatcherDefinition.cs @@ -9,5 +9,9 @@ public class PatternMatcherDefinition public bool IsNoise { get; set; } public bool Mergable { get; set; } + + public string Fragment { get; set; } + + public bool IsAuxiliary { get; set; } } } diff --git a/src/Matcher/AbstractLanguageMatchEngine.cs b/src/Matcher/AbstractLanguageMatchEngine.cs index b32940f..6cadcc3 100644 --- a/src/Matcher/AbstractLanguageMatchEngine.cs +++ b/src/Matcher/AbstractLanguageMatchEngine.cs @@ -8,13 +8,13 @@ namespace Synfron.Staxe.Matcher { - public abstract class AbstractLanguageMatchEngine : ILanguageMatchEngine + public abstract class AbstractLanguageMatchEngine : ILanguageMatchEngine { protected ref struct State { public int CurrentIndex; + public int MaxIndex; public List DistinctStringMatches; - public Span CheckFlags; public int Id; public StringBuilder MatchLogBuilder; public string Code; @@ -22,7 +22,9 @@ protected ref struct State public int MaxDistinctIndex; public Dictionary, FragmentMatchData> MatchCache; public int? FailureIndex; - } + public Span BlobDatas; + public bool PreMatchSuccess; + } public abstract MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true); @@ -53,6 +55,26 @@ protected ref struct State return startOffset < text.Length && char.IsLetterOrDigit(text[startOffset]) ? (true, 1) : (false, 0); } + protected int GetLength(IList matchDatas) + { + int length = 0; + foreach (IMatchData matchData in matchDatas) + { + length += matchData.Length; + } + return length; + } + + protected string GetText(IList matchDatas) + { + StringBuilder textBuilder = new StringBuilder(); + foreach (IMatchData matchData in matchDatas) + { + textBuilder.Append(matchData.ToString()); + } + return textBuilder.ToString(); + } + protected void ConvertToExpressionTree(FragmentMatchData matchData, ExpressionMode expressionMode) { IMatchData rootPart = matchData.Parts.ElementAtOrDefault(0); diff --git a/src/Matcher/Data/BlobData.cs b/src/Matcher/Data/BlobData.cs new file mode 100644 index 0000000..1c8969f --- /dev/null +++ b/src/Matcher/Data/BlobData.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Data +{ + public struct BlobData + { + public object Value { get; set; } + } +} diff --git a/src/Matcher/Data/ExpressionPartInfo.cs b/src/Matcher/Data/ExpressionPartInfo.cs new file mode 100644 index 0000000..8b71e2d --- /dev/null +++ b/src/Matcher/Data/ExpressionPartInfo.cs @@ -0,0 +1,13 @@ +namespace Synfron.Staxe.Matcher.Data +{ + public class ExpressionPartInfo + { + public IMatchData Part { get; set; } + + public ExpressionPartInfo Previous { get; set; } + + public ExpressionPartInfo Next { get; set; } + + public int? ExpressionOrder { get; set; } + } +} diff --git a/src/Matcher/Data/Extensions.cs b/src/Matcher/Data/Extensions.cs new file mode 100644 index 0000000..a2d01fc --- /dev/null +++ b/src/Matcher/Data/Extensions.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Data +{ + internal static class Extensions + { + + public static int GetEndIndex(this IMatchData matchData) + { + if (matchData != null) + { + return matchData.StartIndex + matchData.Length; + } + return 0; + } + + + public static int GetLength(this IList matchDatas, bool includeFragments) + { + int length = 0; + foreach (IMatchData matchData in matchDatas) + { + if (includeFragments || matchData is StringMatchData) + { + length += matchData.Length; + } + } + return length; + } + + public static string GetText(this IList matchDatas, bool includeFragments) + { + StringBuilder textBuilder = new StringBuilder(); + foreach (IMatchData matchData in matchDatas) + { + if (includeFragments || matchData is StringMatchData) + { + textBuilder.Append(matchData.ToString()); + } + } + return textBuilder.ToString(); + } + } +} diff --git a/src/Matcher/Data/FragmentMatchData.cs b/src/Matcher/Data/FragmentMatchData.cs index afe3858..d293c0c 100644 --- a/src/Matcher/Data/FragmentMatchData.cs +++ b/src/Matcher/Data/FragmentMatchData.cs @@ -22,7 +22,8 @@ public string Name set; } - public string ToXml() + + public string ToXml() { return $"<{Name ?? string.Empty}>{string.Join("", Parts.Select(part => part.ToXml()))}"; } diff --git a/src/Matcher/Data/IMatchData.cs b/src/Matcher/Data/IMatchData.cs index c4bd04d..a2ce115 100644 --- a/src/Matcher/Data/IMatchData.cs +++ b/src/Matcher/Data/IMatchData.cs @@ -9,5 +9,6 @@ public interface IMatchData string Name { get; } string ToXml(); + } } diff --git a/src/Matcher/ExpressionPartInfo.cs b/src/Matcher/ExpressionPartInfo.cs deleted file mode 100644 index 3448244..0000000 --- a/src/Matcher/ExpressionPartInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Synfron.Staxe.Matcher.Data; - -namespace Synfron.Staxe.Matcher -{ - public class ExpressionPartInfo - { - public IMatchData Part { get; set; } - - public ExpressionPartInfo Previous { get; set; } - - public ExpressionPartInfo Next { get; set; } - - public int? ExpressionOrder { get; set; } - } -} diff --git a/src/Matcher/Input/Actions/AssertMatcherAction.cs b/src/Matcher/Input/Actions/AssertMatcherAction.cs new file mode 100644 index 0000000..ff1e483 --- /dev/null +++ b/src/Matcher/Input/Actions/AssertMatcherAction.cs @@ -0,0 +1,120 @@ +using Synfron.Staxe.Matcher.Data; +using Synfron.Staxe.Matcher.Input.Patterns; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public class AssertMatcherAction : MatcherAction + { + public int FirstBlobId { get; set; } + + public int SecondBlobId { get; set; } + + public AssertType Assert { get; set; } + + public override MatcherActionType ActionType => MatcherActionType.Assert; + + public override bool Perform(Span blobDatas, IList matchDatas) + { + switch (Assert) + { + case AssertType.Equals: + return Equals(blobDatas[FirstBlobId].Value?.ToString(), blobDatas[SecondBlobId].Value?.ToString()); + case AssertType.NotEquals: + return !Equals(blobDatas[FirstBlobId].Value?.ToString(), blobDatas[SecondBlobId].Value?.ToString()); + case AssertType.GreaterThan: + { + return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + num1 > num2 : false; + } + case AssertType.GreaterThanOrEquals: + { + return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + num1 >= num2 : false; + } + case AssertType.LessThan: + { + return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + num1 < num2 : false; + } + case AssertType.LessThanOrEquals: + { + return double.TryParse(blobDatas[FirstBlobId].Value.ToString(), out double num1) + && double.TryParse(blobDatas[SecondBlobId].Value.ToString(), out double num2) ? + num1 <= num2 : false; + } + case AssertType.Contains: + return blobDatas[FirstBlobId].Value?.ToString().Contains(blobDatas[SecondBlobId].Value?.ToString() ?? string.Empty) ?? false; + case AssertType.StartsWith: + return blobDatas[FirstBlobId].Value?.ToString().StartsWith(blobDatas[SecondBlobId].Value?.ToString() ?? string.Empty) ?? false; + case AssertType.EndsWith: + return blobDatas[FirstBlobId].Value?.ToString().EndsWith(blobDatas[SecondBlobId].Value?.ToString() ?? string.Empty) ?? false; + case AssertType.MatchesPattern: + return PatternReader.Parse(blobDatas[SecondBlobId].Value?.ToString() ?? string.Empty).IsMatch(blobDatas[FirstBlobId].Value?.ToString() ?? string.Empty).success; + } + return false; + } + + internal override string Generate(MatcherEngineGenerator generator) + { + string methodName = $"AssertMatcherAction{GetSafeMethodName(Name)}"; + string method = $"{methodName}({{0}}, {{1}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(Span blobDatas, IList matchDatas) + {{ + {(Assert == AssertType.Equals ? $@" + return Equals(blobDatas[{FirstBlobId}]?.Value, blobDatas[{SecondBlobId}]?.Value); + " : null)} + {(Assert == AssertType.NotEquals ? $@" + return !Equals(blobDatas[{FirstBlobId}]?.Value, blobDatas[{SecondBlobId}]?.Value); + " : null)} + {(Assert == AssertType.GreaterThan ? $@" + return double.TryParse(blobDatas[{FirstBlobId}].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[{SecondBlobId}].Value?.ToString(), out double num2) ? + num1 > num2 : false; + " : null)} + {(Assert == AssertType.GreaterThanOrEquals ? $@" + return double.TryParse(blobDatas[{FirstBlobId}].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[{SecondBlobId}].Value?.ToString(), out double num2) ? + num1 >= num2 : false; + " : null)} + {(Assert == AssertType.LessThan ? $@" + return double.TryParse(blobDatas[{FirstBlobId}].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[{SecondBlobId}].Value?.ToString(), out double num2) ? + num1 < num2 : false; + " : null)} + {(Assert == AssertType.LessThanOrEquals ? $@" + return double.TryParse(blobDatas[{FirstBlobId}].Value?.ToString(), out double num1) + && double.TryParse(blobDatas[{SecondBlobId}].Value?.ToString(), out double num2) ? + num1 <= num2 : false; + " : null)} + {(Assert == AssertType.Contains ? $@" + return blobDatas[{FirstBlobId}].Value?.ToString().Contains(blobDatas[{SecondBlobId}].Value?.ToString() ?? string.Empty) ?? false; + " : null)} + {(Assert == AssertType.StartsWith ? $@" + return blobDatas[{FirstBlobId}].Value?.ToString().StartsWith(blobDatas[{SecondBlobId}].Value?.ToString() ?? string.Empty) ?? false; + " : null)} + {(Assert == AssertType.EndsWith ? $@" + return blobDatas[{FirstBlobId}].Value?.ToString().EndsWith(blobDatas[{SecondBlobId}].Value?.ToString() ?? string.Empty) ?? false; + " : null)} + {(Assert == AssertType.MatchesPattern ? $@" + return Synfron.Staxe.Matcher.Input.Patterns.PatternReader.Parse(blobDatas[{SecondBlobId}].Value?.ToString() ?? string.Empty).IsMatch(blobDatas[{FirstBlobId}].Value?.ToString() ?? string.Empty).success; + " : null)} + {(Assert < AssertType.Equals || Assert > AssertType.MatchesPattern ? $@" + return false; + " : null)} + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + } +} diff --git a/src/Matcher/Input/Actions/AssertType.cs b/src/Matcher/Input/Actions/AssertType.cs new file mode 100644 index 0000000..e616825 --- /dev/null +++ b/src/Matcher/Input/Actions/AssertType.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public enum AssertType + { + Equals, + NotEquals, + GreaterThan, + GreaterThanOrEquals, + LessThan, + LessThanOrEquals, + Contains, + StartsWith, + EndsWith, + MatchesPattern + } +} diff --git a/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs b/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs new file mode 100644 index 0000000..b07e8e5 --- /dev/null +++ b/src/Matcher/Input/Actions/CreateVariableMatcherAction.cs @@ -0,0 +1,96 @@ +using Synfron.Staxe.Matcher.Data; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public class CreateVariableMatcherAction : MatcherAction + { + public int BlobId { get; set; } + + public VariableValueSource Source { get; set; } + + public object Value { get; set; } + + public override MatcherActionType ActionType => MatcherActionType.CreateVariable; + + public override bool Perform(Span blobDatas, IList matchDatas) + { + switch (Source) + { + case VariableValueSource.Value: + blobDatas[BlobId] = new BlobData + { + Value = Value + }; + break; + case VariableValueSource.PartsText: + blobDatas[BlobId] = new BlobData + { + Value = matchDatas.GetText(true) + }; + break; + case VariableValueSource.PartsLength: + blobDatas[BlobId] = new BlobData + { + Value = matchDatas.GetLength(true) + }; + break; + case VariableValueSource.StringPartsText: + blobDatas[BlobId] = new BlobData + { + Value = matchDatas.GetText(false) + }; + break; + case VariableValueSource.StringPartsLength: + blobDatas[BlobId] = new BlobData + { + Value = matchDatas.GetLength(false) + }; + break; + case VariableValueSource.PartsCount: + blobDatas[BlobId] = new BlobData + { + Value = matchDatas.Count + }; + break; + } + return true; + } + + internal override string Generate(MatcherEngineGenerator generator) + { + string methodName = $"CreateVariableMatcherAction{GetSafeMethodName(Name)}"; + string method = $"{methodName}({{0}}, {{1}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(Span blobDatas, IList matchDatas) + {{ + {(Source == VariableValueSource.Value ? $@" + blobDatas[{BlobId}] = new BlobData + {{ + Value = Value + }}; + " : null)} + {(Source == VariableValueSource.PartsText ? $@" + blobDatas[{BlobId}] = new BlobData + {{ + Value = GetText(matchDatas) + }}; + " : null)} + {(Source == VariableValueSource.PartsLength ? $@" + blobDatas[{BlobId}] = new BlobData + {{ + Value = GetLength(matchDatas) + }}; + " : null)} + return true; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + } +} diff --git a/src/Matcher/Input/Actions/MatcherAction.cs b/src/Matcher/Input/Actions/MatcherAction.cs new file mode 100644 index 0000000..fefcf3e --- /dev/null +++ b/src/Matcher/Input/Actions/MatcherAction.cs @@ -0,0 +1,24 @@ +using Synfron.Staxe.Matcher.Data; +using System; +using System.Collections.Generic; +using System.Text; +using System.Text.RegularExpressions; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public abstract class MatcherAction + { + public string Name { get; set; } + + public abstract MatcherActionType ActionType { get; } + + public abstract bool Perform(Span blobDatas, IList matchDatas); + + internal abstract string Generate(MatcherEngineGenerator generator); + + protected static string GetSafeMethodName(string name) + { + return Regex.Replace(name, @"\W", (match) => $"x{(int)match.Value[0]}"); + } + } +} diff --git a/src/Matcher/Input/Actions/MatcherActionType.cs b/src/Matcher/Input/Actions/MatcherActionType.cs new file mode 100644 index 0000000..33227b5 --- /dev/null +++ b/src/Matcher/Input/Actions/MatcherActionType.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public enum MatcherActionType + { + Assert, + CreateVariable, + UpdateVariable + } +} diff --git a/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs b/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs new file mode 100644 index 0000000..7c8cf72 --- /dev/null +++ b/src/Matcher/Input/Actions/UpdateVariableMatcherAction.cs @@ -0,0 +1,119 @@ +using Synfron.Staxe.Matcher.Data; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public class UpdateVariableMatcherAction : MatcherAction + { + public int TargetBlobId { get; set; } + + public VariableUpdateAction Change { get; set; } + + public int SourceBlobId { get; set; } + + public override MatcherActionType ActionType => MatcherActionType.UpdateVariable; + + public override bool Perform(Span blobDatas, IList matchDatas) + { + try + { + switch (Change) + { + case VariableUpdateAction.Add: + { + string value1 = blobDatas[TargetBlobId].Value?.ToString(); + string value2 = blobDatas[SourceBlobId].Value?.ToString(); + blobDatas[TargetBlobId].Value = double.TryParse(value1, out double num1) + && double.TryParse(value2, out double num2) ? + num1 + num2 : blobDatas[TargetBlobId].Value; + break; + } + case VariableUpdateAction.Subtract: + { + string value1 = blobDatas[TargetBlobId].Value?.ToString(); + string value2 = blobDatas[SourceBlobId].Value?.ToString(); + blobDatas[TargetBlobId].Value = double.TryParse(value1, out double num1) + && double.TryParse(value2, out double num2) ? + num1 - num2 : blobDatas[TargetBlobId].Value; + break; + } + case VariableUpdateAction.Concat: + { + string value1 = blobDatas[TargetBlobId].Value?.ToString(); + string value2 = blobDatas[SourceBlobId].Value?.ToString(); + blobDatas[TargetBlobId].Value = value1 + value2; + break; + } + case VariableUpdateAction.Remove: + { + string value1 = blobDatas[TargetBlobId].Value?.ToString(); + string value2 = blobDatas[SourceBlobId].Value?.ToString(); + blobDatas[TargetBlobId].Value = value1?.Replace(value2 ?? string.Empty, ""); + break; + } + case VariableUpdateAction.Set: + { + blobDatas[TargetBlobId].Value = blobDatas[SourceBlobId].Value; + break; + } + } + return true; + } + catch + { + return false; + } + } + + internal override string Generate(MatcherEngineGenerator generator) + { + string methodName = $"UpdateVariableMatcherAction{GetSafeMethodName(Name)}"; + string method = $"{methodName}({{0}}, {{1}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(Span blobDatas, IList matchDatas) + {{ + try {{ + {(Change == VariableUpdateAction.Add ? $@" + string value1 = blobDatas[{TargetBlobId}].Value?.ToString(); + string value2 = blobDatas[{SourceBlobId}].Value?.ToString(); + blobDatas[{TargetBlobId}].Value = double.TryParse(value1, out double num1) + && double.TryParse(value2, out double num2) ? + num1 + num2 : blobDatas[{TargetBlobId}].Value; + " : null)} + {(Change == VariableUpdateAction.Subtract ? $@" + string value1 = blobDatas[{TargetBlobId}].Value?.ToString(); + string value2 = blobDatas[{SourceBlobId}].Value?.ToString(); + blobDatas[{TargetBlobId}].Value = double.TryParse(value1, out double num1) + && double.TryParse(value2, out double num2) ? + num1 - num2 : blobDatas[{TargetBlobId}].Value; + " : null)} + {(Change == VariableUpdateAction.Concat ? $@" + string value1 = blobDatas[{TargetBlobId}].Value?.ToString(); + string value2 = blobDatas[{SourceBlobId}].Value?.ToString(); + blobDatas[{TargetBlobId}].Value = value1 + value2; + " : null)} + {(Change == VariableUpdateAction.Remove ? $@" + string value1 = blobDatas[{TargetBlobId}].Value?.ToString(); + string value2 = blobDatas[{SourceBlobId}].Value?.ToString(); + blobDatas[{TargetBlobId}].Value = value1?.Replace(value2, """"); + " : null)} + {(Change == VariableUpdateAction.Set ? $@" + blobDatas[{TargetBlobId}].Value = blobDatas[{SourceBlobId}].Value; + " : null)} + return true; + }} + catch + {{ + return false; + }} + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + } +} diff --git a/src/Matcher/Input/Actions/VariableUpdateAction.cs b/src/Matcher/Input/Actions/VariableUpdateAction.cs new file mode 100644 index 0000000..ebda23c --- /dev/null +++ b/src/Matcher/Input/Actions/VariableUpdateAction.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public enum VariableUpdateAction + { + Add, + Subtract, + Concat, + Remove, + Set + } +} diff --git a/src/Matcher/Input/Actions/VariableValueSource.cs b/src/Matcher/Input/Actions/VariableValueSource.cs new file mode 100644 index 0000000..e9ea409 --- /dev/null +++ b/src/Matcher/Input/Actions/VariableValueSource.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Actions +{ + public enum VariableValueSource + { + Value, + PartsCount, + PartsText, + PartsLength, + StringPartsText, + StringPartsLength + } +} diff --git a/src/Matcher/Input/FragmentMatcher.cs b/src/Matcher/Input/FragmentMatcher.cs index 94d58a8..65d52b4 100644 --- a/src/Matcher/Input/FragmentMatcher.cs +++ b/src/Matcher/Input/FragmentMatcher.cs @@ -1,4 +1,5 @@ -using Synfron.Staxe.Matcher.Input.Patterns; +using Synfron.Staxe.Matcher.Input.Actions; +using Synfron.Staxe.Matcher.Input.Patterns; using System; using System.Collections.Generic; using System.Linq; @@ -31,7 +32,8 @@ public FragmentMatcher( int? expressionOrder = default, bool boundsAsParts = default, bool discardBounds = default, - bool negate = default + bool negate = default, + IList actions = default ) { Id = id; @@ -53,6 +55,7 @@ public FragmentMatcher( BoundsAsParts = boundsAsParts; DiscardBounds = discardBounds; Negate = negate; + Actions = actions; _hashCode = name.GetHashCode(); } @@ -93,7 +96,9 @@ public FragmentMatcher( public bool DiscardBounds { get; set; } - public bool Negate { get; } + public bool Negate { get; set; } + + public IList Actions { get; set; } public bool Equals(FragmentMatcher other) { @@ -105,331 +110,6 @@ public override bool Equals(object obj) return (obj is FragmentMatcher other) && Equals(other); } - private string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerator generator) - { - string methodName = $"MatchFragmentPartsOrderedMode{GetSafeMethodName(Name)}"; - string method = $"{methodName}(ref state, partMatcherData)"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - StringBuilder functionText = new StringBuilder(); - for (int partIndex = 0; partIndex < Parts.Count; partIndex++) - { - functionText.AppendLine($@"{(partIndex > 0 ? $@"distinctIndex = state.DistinctIndex; - partSuccess = {(PartsDelimiter != null ? string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsDelimiter), "stringMatchData", PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; - success = partSuccess; - if (!success) - {{ - goto Break; - }}" : null)} - - success = {GenerateMatchFragmentPart(generator, Parts[partIndex])}; - if (!success) - {{ - if (stringMatchData != null) - {{ - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - }} - goto Break; - }} - else - {{ - matchCount++; - }}"); - } - - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) - {{ - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = null; - int distinctIndex = state.DistinctIndex; - {(PartsPadding != null ? string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsPadding), "_", "false", "false") : null)}; - - {functionText.ToString()} - - Break: - {(PartsPadding != null ? - $@"if (success) - {{ - {string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsPadding), "_", "false", "false")}; - }}" : null)} - - success = success || {(MinMatchedParts ?? Parts.Count)} <= matchCount; - if ({(!Negate ? "!" : null)}success) - {{ - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - }} - return success; - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - private string GenerateMatchFragmentPartsOneMode(MatcherEngineGenerator generator) - { - string methodName = $"MatchFragmentPartsOneMode{GetSafeMethodName(Name)}"; - string method = $"{methodName}(ref state, partMatcherData)"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) - {{ - bool success = false; - int matchCount = 0; - {(PartsPadding != null ? $"{string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsPadding), "_", "false", "false")};" : null)} - - {(Parts.Count > 0 ? $@" - success = {string.Join(" || ", Parts.Select(part => GenerateMatchFragmentPart(generator, part)))}; - if (success) - {{ - matchCount++; - }}" : null)} - - {(PartsPadding != null ? - $@"if (success) - {{ - {string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsPadding), "_", "false", "false")}; - }}" : null)} - - success = {((MinMatchedParts ?? 1) <= 0).ToString().ToLower()} || matchCount > 0; - if ({(!Negate ? "!" : null)}success) - {{ - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - }} - return success; - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - private string GenerateMatchFragmentPartsMultipleMode(MatcherEngineGenerator generator) - { - string methodName = $"MatchFragmentPartsMultipleMode{GetSafeMethodName(Name)}"; - string method = $"{methodName}(ref state, partMatcherData)"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - StringBuilder functionText = new StringBuilder(); - for (int partIndex = 0; partIndex < Parts.Count; partIndex++) - { - functionText.AppendLine($@"individualSuccess = {GenerateMatchFragmentPart(generator, Parts[partIndex])}; - subSuccess |= individualSuccess; - if (individualSuccess) - {{ - matchCount++; - distinctIndex = state.DistinctIndex; - delimiterSuccess = {(PartsDelimiter != null ? string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsDelimiter), "range", PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; - goto Break; - }} - "); - } - - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) - {{ - bool overallSuccess = false; - bool subSuccess = false; - bool delimiterSuccess = false; - StringMatchData range = default; - int matchCount = 0; - int distinctIndex = state.DistinctIndex; - {(PartsPadding != null ? string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsPadding), "_", "false", "false") : null)}; - - do - {{ - subSuccess = false; - bool individualSuccess; - {functionText.ToString()} - - Break: - overallSuccess |= subSuccess; - }} - while (subSuccess && delimiterSuccess); - if (delimiterSuccess && range != null) - {{ - state.CurrentIndex = range.StartIndex; - state.DistinctIndex = distinctIndex; - }} - {(PartsPadding != null ? - $@"if (overallSuccess) - {{ - {string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, PartsPadding), "_", "false", "false")}; - }}" : null)} - - bool thresholdSuccess = {MinMatchedParts ?? 1} <= matchCount; - bool success = overallSuccess || thresholdSuccess; - if ({(!Negate ? "!" : null)}success) - {{ - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - }} - return success; - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - private string GenerateMatchFragmentPart(MatcherEngineGenerator generator, IMatcher part) - { - return part is FragmentMatcher fragmentMatcher - ? fragmentMatcher.Generate(generator) - : GenerateMatchPartByTextMatcher(generator, (PatternMatcher)part); - } - - private string GenerateMatchFragmentBounds(MatcherEngineGenerator generator, PatternMatcher matcher) - { - string methodName = $"MatchFragmentBounds{GetSafeMethodName(matcher.Name)}"; - string method = $"{methodName}(ref state, {{0}}, out {{1}})"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, bool readOnly, out StringMatchData matchData) - {{ - bool success = {string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, matcher), "matchData", "true", "readOnly")}; - if ({(!Negate ? "!" : null)}success) - {{ - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - }} - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{state.CurrentIndex}}. {{(success ? ""Passed"" : ""Failed"")}} Bounds: {{""{HttpUtility.JavaScriptStringEncode(matcher.ToString())}""}}"");" : null)} - return success; - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - - - private string GenerateMatchPartByTextMatcher(MatcherEngineGenerator generator, PatternMatcher matcher) - { - string methodName = $"MatchPartByTextMatcher{GetSafeMethodName(matcher.Name)}"; - string method = $"{methodName}(ref state, matchData)"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) - {{ - - {(generator.LanguageMatcher.LogMatches ? $@"int currentId = ++state.Id; - state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. Try: {HttpUtility.JavaScriptStringEncode(matcher.Name)}"");" : null)} - StringMatchData partMatchData; - bool success = {string.Format(generator.LanguageMatcher.GenerateMatchPattern(generator, matcher), "partMatchData", "true", "false")}; - if (success) - {{ - matchData.Parts.Add(partMatchData); - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{state.CurrentIndex}}. Matched: {{partMatchData.Text}}"");" : null)} - }} - - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. {{(success ? ""Passed"" : ""Failed"")}}: {HttpUtility.JavaScriptStringEncode(matcher.Name)}""); - state.Id = currentId - 1;" : null)} - return success; - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - - - public string Generate(MatcherEngineGenerator generator) - { - string methodName = $"MatchFragment{GetSafeMethodName(Name)}"; - string method = $"{methodName}(ref state, matchData)"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) - {{ - {(generator.LanguageMatcher.LogMatches ? $@"int currentId = ++state.Id; - state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. Try: {GetEscapedName()}"");" : null)} - bool success = false; - FragmentMatchData partMatcherData = null; - {(Cacheable ? $@"if (!state.MatchCache.TryGetValue(new ValueTuple(""{GetEscapedName()}"", state.CurrentIndex), out partMatcherData)) - {{" : null)} - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - partMatcherData = new FragmentMatchData - {{ - Name = ""{GetEscapedName()}"", - StartIndex = state.CurrentIndex{(ExpressionOrder != null ? $@", - ExpressionOrder = {ExpressionOrder}" : null)} - }}; - - {(Start != null ? $"StringMatchData startMatchData;" : null)} - {(End != null ? $"StringMatchData endMatchData;" : null)} - success = ({(Start != null ? $"{string.Format(GenerateMatchFragmentBounds(generator, Start), DiscardBounds.ToString().ToLower(), "startMatchData")} && " : null)}{GenerateMatchFragmentParts(generator)}{(End != null ? $" && {string.Format(GenerateMatchFragmentBounds(generator, End), DiscardBounds.ToString().ToLower(), "endMatchData")}" : null)}); - - - if (success) - {{ - {(!Negate ? $@"partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; - partMatcherData.EndDistinctIndex = state.DistinctIndex;" : null)} - {(Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName()}"", startIndex)] = partMatcherData;" : null)} - {(!IsNoise && ExpressionMode != ExpressionMode.None && !Negate ? $"ConvertToExpressionTree(partMatcherData, ExpressionMode.{ExpressionMode.ToString()});" : null)} - {(BoundsAsParts && Start != null && !Negate ? $@"if (startMatchData != null) - {{ - partMatcherData.Parts.Insert(0, startMatchData); - }}" : null)} - {(BoundsAsParts && End != null && !Negate ? $@"if (endMatchData != null) - {{ - partMatcherData.Parts.Add(endMatchData); - }}" : null)} - }} - else - {{ - {(Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName()}"", startIndex)] = null;" : null)} - {(!Negate ? $@"state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex;" : null)} - }} - {(Negate ? $@"state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex;" : null)} - {(Cacheable ? $@"}}" : null)} - {(Cacheable && !Negate ? $@"else if (success = partMatcherData != null) - {{ - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; - }}" : null)} - {(!Negate ? $@"if (success) - {{ - {(!IsNoise && FallThroughMode == FallThroughMode.All ? "matchData.Parts.AddRange(partMatcherData.Parts);" : null)} - {(!IsNoise && FallThroughMode == FallThroughMode.None ? "matchData.Parts.Add(partMatcherData);" : null)} - {(!IsNoise && FallThroughMode != FallThroughMode.None && FallThroughMode != FallThroughMode.All ? $@"if (partMatcherData.Parts.Count <= {(int)FallThroughMode}) - {{ - matchData.Parts.AddRange(partMatcherData.Parts); - }} - else - {{ - matchData.Parts.Add(partMatcherData); - }}" : null)} - {(ClearCache ? "state.MatchCache.Clear();" : null)} - }}" : null)} - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. {{({(Negate ? "!" : null)}success ? ""Passed"" : ""Failed"")}}: {GetEscapedName()}""); - state.Id = currentId - 1;" : null)} - return {(Negate ? "!" : null)}success; - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - private string GenerateMatchFragmentParts(MatcherEngineGenerator generator) - { - switch (PartsMatchMode) - { - case MatchMode.Ordered: - return GenerateMatchFragmentPartsOrderedMode(generator); - case MatchMode.One: - return GenerateMatchFragmentPartsOneMode(generator); - case MatchMode.Multiple: - default: - return GenerateMatchFragmentPartsMultipleMode(generator); - } - } - public override int GetHashCode() { return _hashCode; @@ -439,15 +119,5 @@ public override string ToString() { return Name; } - - private string GetEscapedName() - { - return HttpUtility.JavaScriptStringEncode(Name); - } - - private static string GetSafeMethodName(string name) - { - return Regex.Replace(name, @"\W", (match) => $"x{(int)match.Value[0]:X}"); - } } } diff --git a/src/Matcher/Input/LanguageMatcher.cs b/src/Matcher/Input/LanguageMatcher.cs index 1b6a41b..185eea1 100644 --- a/src/Matcher/Input/LanguageMatcher.cs +++ b/src/Matcher/Input/LanguageMatcher.cs @@ -11,267 +11,16 @@ public class LanguageMatcher { public string Name { get; set; } - public List Patterns { get; set; } + public IList Patterns { get; set; } = new PatternMatcher[0]; public FragmentMatcher StartingFragment { get; set; } - public List Fragments { get; set; } + public IList Fragments { get; set; } = new FragmentMatcher[0]; public bool LogMatches { get; set; } public IndexingMode IndexingMode { get; set; } = IndexingMode.Lazy; - - public string Generate(MatcherEngineGenerator generator) - { - string version = AssemblyName.GetAssemblyName(GetType().Assembly.Location).Version.ToString(); - return $@" -/* Generated by Synfron.Staxe.Matcher v{version}*/ - -using Synfron.Staxe.Matcher.Data; -using System.Collections.Generic; -using System; -using System.Linq; -using Synfron.Staxe.Matcher.Input; -{(generator.LanguageMatcher.LogMatches ? "using System.Text;" : null)} - -namespace Synfron.Staxe.Matcher -{{ - public class {GetSafeMethodName(Name)}MatchEngine : AbstractLanguageMatchEngine - {{ - - {GenerateMatch(generator)} - - <> - }} -}}"; - } - - private string GenerateMatch(MatcherEngineGenerator generator) - { - return $@" - public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) - {{ - FragmentMatchData matchData = new FragmentMatchData - {{ - StartIndex = 0 - }}; - - {(IndexingMode == IndexingMode.Lazy ? $"Span checkFlags = stackalloc int[{Patterns.Count + 1}];" : null)} - - State state = new State() - {{ - Code = code{(IndexingMode != IndexingMode.None ? @", - DistinctStringMatches = new List(2000)" : null)}{(IndexingMode == IndexingMode.Lazy ? @", - CheckFlags = checkFlags" : null)}{(Fragments.Any(fragment => fragment.Cacheable) ? @", - MatchCache = new Dictionary, FragmentMatchData>()" : null)}{(LogMatches ? $@", - MatchLogBuilder = new StringBuilder()" : null)} - }}; - - {(IndexingMode == IndexingMode.Eager ? "PreMatchPatterns(ref state);" : null)} - - bool success = false; - switch (fragmentMatcher) - {{ - {string.Join("\n", Fragments.Where(matcher => !matcher.IsNoise).Select(matcher => $@" - case ""{HttpUtility.JavaScriptStringEncode(matcher.Name)}"": - success = {matcher.Generate(generator)}; - break; - "))} - }} - - IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; - - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - {{ - success = false; - failureIndex = state.CurrentIndex; - }} - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); - }} - - public override MatcherResult Match(string code, bool matchFullText = true) - {{ - FragmentMatchData matchData = new FragmentMatchData - {{ - StartIndex = 0 - }}; - - {(IndexingMode == IndexingMode.Lazy ? $"Span checkFlags = stackalloc int[{Patterns.Count + 1}];" : null)} - - State state = new State() - {{ - Code = code{(IndexingMode != IndexingMode.None ? @", - DistinctStringMatches = new List(2000)" : null)}{(IndexingMode == IndexingMode.Lazy ? @", - CheckFlags = checkFlags" : null)}{(Fragments.Any(fragment => fragment.Cacheable) ? @", - MatchCache = new Dictionary, FragmentMatchData>()" : null)}{(LogMatches ? $@", - MatchLogBuilder = new StringBuilder()" : null)} - }}; - - {(IndexingMode == IndexingMode.Eager ? "PreMatchPatterns(ref state);" : null)} - - bool success = {StartingFragment.Generate(generator)}; - - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; - - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - {{ - success = false; - failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - }} - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); - }} - - {(IndexingMode == IndexingMode.Eager ? $@"private bool PreMatchPatterns(ref State state) - {{ - int codeLength = state.Code.Length; - bool success = true; - bool previousNoise = false; - StringMatchData matchData = null; - int currentIndex = 0; - while ((currentIndex = state.CurrentIndex) < codeLength) - {{ - success = {string.Join(" ||\n", Patterns.Select(pattern => $@"{string.Format(GenerateMatchPattern(generator, pattern), "matchData", "true", "false")}"))}; - if (!success) - {{ - break; - }} - else {{ - {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{currentIndex}}. Prematched {{matchData.Name}}: {{matchData.Text}}"");" : null)} - if (matchData.IsNoise) - {{ - previousNoise = true; - }} - else if (previousNoise) - {{ - if (state.DistinctIndex > 1) - {{ - StringMatchData previousMatchData = state.DistinctStringMatches[state.DistinctIndex - 2]; - if (previousMatchData.Name == matchData.Name && previousMatchData.Mergable) - {{ - previousMatchData.Text += matchData.Text; - previousMatchData.Length = state.CurrentIndex - previousMatchData.StartIndex; - state.DistinctIndex--; - state.MaxDistinctIndex--; - state.DistinctStringMatches.RemoveAt(state.DistinctIndex); - }} - }} - previousNoise = false; - }} - }} - }} - state.CurrentIndex = 0; - {(IndexingMode != IndexingMode.None ? "state.DistinctIndex = 0;" : null)} - return success; - }}" : null)}"; - } - - - public string GenerateMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) - { - string methodName = $"MatchPattern{GetSafeMethodName(pattern.Name)}"; - string method = $"{methodName}(ref state, out {{0}}, {{1}}, {{2}})"; - if (!generator.TryGetMethod(methodName, ref method)) - { - generator.Add(methodName, method); - string code = $@"private bool {methodName}(ref State state, out StringMatchData matchData, bool required, bool readOnly = false) - {{ - bool success = false; - {(generator.IndexingMode != IndexingMode.None ? $@"int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - {{" : null)} - int length; - int startOffset = state.CurrentIndex; - (success, length) = {GenerateRawMatchPattern(generator, pattern)}; - matchData = default; - if (success) - {{ - matchData = new StringMatchData - {{ - Name = ""{HttpUtility.JavaScriptStringEncode(pattern.Name)}"", - Text = state.Code.Substring(startOffset, length), - StartIndex = startOffset, - Length = length, - IsNoise = {pattern.IsNoise.ToString().ToLower()}, - Mergable = {pattern.Mergable.ToString().ToLower()}, - Id = {pattern.Id} - }}; - {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.DistinctStringMatches.Add(matchData);" : null)} - success = matchData != null; - {(generator.IndexingMode == IndexingMode.Lazy ? $@"state.CheckFlags[{pattern.Id}] = distinctIndex + 1;" : null)} - {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.MaxDistinctIndex++;" : null)} - if (!readOnly) - {{ - {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.DistinctIndex++;" : null)} - state.CurrentIndex += matchData.Length; - }} - }} - else if (!required) - {{ - success = true; - }} - return success; - {(generator.IndexingMode != IndexingMode.None ? $@"}} - else - {{ - matchData = state.DistinctStringMatches[distinctIndex]; - if (matchData != null) - {{ - success = matchData.Id == {pattern.Id}; - }} - else - {{ - {(generator.IndexingMode == IndexingMode.Eager ? $@"if (state.CheckFlags[{pattern.Id}] < distinctIndex + 1) - {{" : null)} - int length; - int startOffset = state.CurrentIndex; - (success, length) = {GenerateRawMatchPattern(generator, pattern)}; - if (success) - {{ - matchData = new StringMatchData - {{ - Name = ""{GetSafeMethodName(pattern.Name)}"", - Text = state.Code.Substring(startOffset, length), - StartIndex = startOffset, - Length = length, - IsNoise = {pattern.IsNoise.ToString().ToLower()}, - Mergable = {pattern.Mergable.ToString().ToLower()}, - Id = {pattern.Id} - }}; - {(!pattern.IsNoise ? $@"state.DistinctStringMatches[distinctIndex] = matchData;" : null)} - }} - {(generator.IndexingMode == IndexingMode.Eager ? $@"state.CheckFlags[{pattern.Id}] = distinctIndex + 1; - }}" : null)} - }} - if (success && !readOnly) - {{ - {(!pattern.IsNoise ? $@"state.DistinctIndex++;" : null)} - state.CurrentIndex += matchData.Length; - }} - if (!required) - {{ - success = true; - }} - return success; - }}" : null)} - }}"; - method = generator.Add(method, methodName, code); - } - return method; - } - - private string GenerateRawMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) - { - return string.Format(pattern.Generate(generator), "state.Code", "state.CurrentIndex"); - } - - private static string GetSafeMethodName(string name) - { - return Regex.Replace(name, @"\W", (match) => $"x{((int)match.Value[0]).ToString()}"); - } + public IList Blobs { get; set; } = new string[0]; } } diff --git a/src/Matcher/Input/Patterns/FragmentPatternMatcher.cs b/src/Matcher/Input/Patterns/FragmentPatternMatcher.cs new file mode 100644 index 0000000..a27b46b --- /dev/null +++ b/src/Matcher/Input/Patterns/FragmentPatternMatcher.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Synfron.Staxe.Matcher.Input.Patterns +{ + public class FragmentPatternMatcher : PatternMatcher + { + public FragmentMatcher Fragment { get; set; } + + public FragmentPatternMatcher() + { + IsStandard = false; + } + + public override (bool success, int offset) IsMatch(string text, int startOffset = 0) + { + return (false, 0); + } + + public override string ToString() + { + return $"[{Fragment.Name}]"; + } + + internal override string Generate(MatcherEngineGenerator generator) + { + string methodName = $"MatchFragmentPattern"; + string method = $"{methodName}({{0}}, {{1}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private (bool success, int offset) {methodName}(string text, int startOffset) + {{ + return (false, 0); + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + } +} diff --git a/src/Matcher/Input/Patterns/PatternMatcher.cs b/src/Matcher/Input/Patterns/PatternMatcher.cs index 586d2e9..e7a9c08 100644 --- a/src/Matcher/Input/Patterns/PatternMatcher.cs +++ b/src/Matcher/Input/Patterns/PatternMatcher.cs @@ -1,4 +1,7 @@ -namespace Synfron.Staxe.Matcher.Input.Patterns +using Synfron.Staxe.Matcher.Data; +using System.Collections.Generic; + +namespace Synfron.Staxe.Matcher.Input.Patterns { public abstract class PatternMatcher : IMatcher { @@ -11,6 +14,8 @@ public abstract class PatternMatcher : IMatcher public bool Mergable { get; set; } + public bool IsStandard { get; set; } = true; + public abstract (bool success, int offset) IsMatch(string text, int startOffset = 0); internal abstract string Generate(MatcherEngineGenerator generator); diff --git a/src/Matcher/LanguageMatchEngine.cs b/src/Matcher/LanguageMatchEngine.cs index 97cbf3c..0add4e4 100644 --- a/src/Matcher/LanguageMatchEngine.cs +++ b/src/Matcher/LanguageMatchEngine.cs @@ -1,637 +1,1239 @@ using Synfron.Staxe.Matcher.Data; using Synfron.Staxe.Matcher.Input; +using Synfron.Staxe.Matcher.Input.Actions; using Synfron.Staxe.Matcher.Input.Patterns; using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; +using System.Text.RegularExpressions; +using System.Web; namespace Synfron.Staxe.Matcher { - public abstract class LanguageMatchEngine : AbstractLanguageMatchEngine - { - private readonly bool _logMatches; - protected bool _hasCheckFlags; - - private sealed class EagerIndexLanguageMatchEngine : LanguageMatchEngine - { - public EagerIndexLanguageMatchEngine(LanguageMatcher languageMatcher) : base(languageMatcher) - { - _hasCheckFlags = false; - } - - protected override void BuildState(ref State state, string code) - { - state.Code = code; - state.DistinctStringMatches = new List(2000); - state.MatchLogBuilder = new StringBuilder(); - state.MatchCache = new Dictionary, FragmentMatchData>(); - - PreMatchPatterns(ref state); - } - - private bool PreMatchPatterns(ref State state) - { - int codeLength = state.Code.Length; - List patterns = LanguageMatcher.Patterns; - int patternsCount = LanguageMatcher.Patterns.Count; - bool success = true; - bool previousNoise = false; - - int currentIndex; - while ((currentIndex = state.CurrentIndex) < codeLength) - { - success = false; - for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) - { - PatternMatcher pattern = patterns[patternIndex]; - StringMatchData matchData = PreMatchPattern(ref state, pattern); - success = matchData != null; - if (success) - { - if (_logMatches) - { - state.MatchLogBuilder.AppendLine($"{currentIndex}. Prematched {matchData.Name}: {matchData.Text}"); - } - if (matchData.IsNoise) - { - previousNoise = true; - } - else if (previousNoise) - { - if (state.DistinctIndex > 1) - { - StringMatchData previousMatchData = state.DistinctStringMatches[state.DistinctIndex - 2]; - if (previousMatchData.Name == matchData.Name && previousMatchData.Mergable) - { - previousMatchData.Text += matchData.Text; - previousMatchData.Length = state.CurrentIndex - previousMatchData.StartIndex; - state.DistinctIndex--; - state.MaxDistinctIndex--; - state.DistinctStringMatches.RemoveAt(state.DistinctIndex); - } - } - previousNoise = false; - } - break; - } - } - if (!success) - { - break; - } - } - state.CurrentIndex = 0; - state.DistinctIndex = 0; - return success; - } - - private StringMatchData PreMatchPattern(ref State state, PatternMatcher pattern) - { - int startOffset = state.CurrentIndex; - StringMatchData stringMatchData = null; - (bool success, int length) = pattern.IsMatch(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData - { - Name = pattern.Name, - Text = state.Code.Substring(startOffset, length), - StartIndex = startOffset, - Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable - }; - if (!stringMatchData.IsNoise) - { - state.DistinctStringMatches.Add(stringMatchData); - state.DistinctIndex++; - state.MaxDistinctIndex++; - } - state.CurrentIndex += stringMatchData.Length; - } - return stringMatchData; - } - - protected override (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) - { - if (pattern != null) - { - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex < state.MaxDistinctIndex) - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id == pattern.Id; - } - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - else if (!required) - { - success = true; - } - return (success, stringMatchData); - } - else - { - return (false, default); - } - - } - return (true, default); - } - } - private sealed class NoIndexLanguageMatchEngine : LanguageMatchEngine - { - public NoIndexLanguageMatchEngine(LanguageMatcher languageMatcher) : base(languageMatcher) - { - _hasCheckFlags = false; - } - - protected override void BuildState(ref State state, string code) - { - state.Code = code; - state.MatchLogBuilder = new StringBuilder(); - state.MatchCache = new Dictionary, FragmentMatchData>(); - } - - protected override (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) - { - if (pattern != null) - { - int startOffset = state.CurrentIndex; - (bool success, int length) = pattern.IsMatch(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; + public abstract class LanguageMatchEngine : AbstractLanguageMatchEngine + { + private readonly bool _logMatches; + + private sealed class EagerIndexLanguageMatchEngine : LanguageMatchEngine + { + public EagerIndexLanguageMatchEngine(LanguageMatcher languageMatcher) : base(languageMatcher) + { + } + + protected override void BuildState(ref State state, string code) + { + state.Code = code; + state.DistinctStringMatches = new List(2000); + state.MatchLogBuilder = new StringBuilder(); + state.MatchCache = new Dictionary, FragmentMatchData>(); + + state.PreMatchSuccess = PreMatchPatterns(ref state); + } + + private bool PreMatchPatterns(ref State state) + { + int codeLength = state.Code.Length; + IList patterns = LanguageMatcher.Patterns; + int patternsCount = LanguageMatcher.Patterns.Count; + bool success = true; + bool previousNoise = false; + + int currentIndex; + while ((currentIndex = state.CurrentIndex) < codeLength) + { + success = false; + for (int patternIndex = 0; patternIndex < patternsCount; patternIndex++) + { + PatternMatcher pattern = patterns[patternIndex]; + StringMatchData matchData; + (success, matchData) = PreMatchPattern(ref state, pattern); + if (success) + { + if (pattern.IsStandard) + { + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{currentIndex}. Prematched {matchData.Name}: {matchData.Text}"); + } + if (matchData.IsNoise) + { + previousNoise = true; + } + else if (previousNoise) + { + if (state.DistinctIndex > 1) + { + StringMatchData previousMatchData = state.DistinctStringMatches[state.DistinctIndex - 2]; + if (previousMatchData.Name == matchData.Name && previousMatchData.Mergable) + { + previousMatchData.Text += matchData.Text; + previousMatchData.Length = state.CurrentIndex - previousMatchData.StartIndex; + state.DistinctIndex--; + state.MaxDistinctIndex--; + state.DistinctStringMatches.RemoveAt(state.DistinctIndex); + } + } + previousNoise = false; + } + } + break; + } + } + if (!success) + { + break; + } + } + state.CurrentIndex = 0; + state.DistinctIndex = 0; + return success; + } + + private (bool, StringMatchData) PreMatchPattern(ref State state, PatternMatcher pattern) + { + int startOffset = state.CurrentIndex; + if (pattern.IsStandard) + { + StringMatchData stringMatchData = null; + (bool success, int length) = pattern.IsMatch(state.Code, state.CurrentIndex); + if (success) + { + stringMatchData = new StringMatchData + { + Name = pattern.Name, + Text = state.Code.Substring(startOffset, length), + StartIndex = startOffset, + Length = length, + Id = pattern.Id, + IsNoise = pattern.IsNoise, + Mergable = pattern.Mergable + }; + if (!stringMatchData.IsNoise) + { + state.DistinctStringMatches.Add(stringMatchData); + state.DistinctIndex++; + state.MaxDistinctIndex++; + } + state.CurrentIndex = startOffset + length; + state.MaxIndex = state.CurrentIndex; + } + return (success, stringMatchData); + } + else if (pattern is FragmentPatternMatcher fragmentPatternMatcher && fragmentPatternMatcher.Fragment is FragmentMatcher fragmentMatcher) + { + int startIndex = state.CurrentIndex; + int distinctStringMatchesCount = state.DistinctStringMatches.Count; + int distinctIndex = state.DistinctIndex; + int maxDistinctIndex = state.MaxDistinctIndex; + + (bool _, FragmentMatchData partMatcherData) = MatchByFragmentMatcher(ref state, fragmentPatternMatcher.Fragment); + if (fragmentMatcher.Cacheable) + { + state.MatchCache[new ValueTuple(fragmentMatcher.Name, startIndex)] = partMatcherData; + } + if (pattern.IsNoise) + { + state.DistinctStringMatches.RemoveRange(distinctStringMatchesCount, state.DistinctStringMatches.Count - distinctStringMatchesCount); + state.DistinctIndex = distinctIndex; + state.MaxDistinctIndex = maxDistinctIndex; + } + state.CurrentIndex = state.MaxIndex; + bool success = state.CurrentIndex > startIndex; + return (success, null); + } + return (false, null); + } + } + + private sealed class NoIndexLanguageMatchEngine : LanguageMatchEngine + { + public NoIndexLanguageMatchEngine(LanguageMatcher languageMatcher) : base(languageMatcher) + { + } + + protected override void BuildState(ref State state, string code) + { + state.Code = code; + state.MatchLogBuilder = new StringBuilder(); + state.MatchCache = new Dictionary, FragmentMatchData>(); + } + + protected override (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) + { + if (pattern != null) + { + int startOffset = state.CurrentIndex; + (bool success, int length) = pattern.IsMatch(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData + { + Name = pattern.Name, + Text = state.Code.Substring(startOffset, length), + StartIndex = startOffset, + Length = length, + Id = pattern.Id, + IsNoise = pattern.IsNoise, + Mergable = pattern.Mergable + }; + if (!readOnly) + { + state.CurrentIndex = startOffset + length; + state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + } + } + else if (!required) + { + success = true; + } + return (success, stringMatchData); + + } + return (true, default); + } + } + + private sealed class LazyIndexLanguageMatchEngine : LanguageMatchEngine + { + public LazyIndexLanguageMatchEngine(LanguageMatcher languageMatcher) : base(languageMatcher) + { + } + + protected override void BuildState(ref State state, string code) + { + state.Code = code; + state.DistinctStringMatches = new List(2000); + state.MatchLogBuilder = new StringBuilder(); + state.MatchCache = new Dictionary, FragmentMatchData>(); + } + } + + public static LanguageMatchEngine Build(LanguageMatcher languageMatcher) + { + switch (languageMatcher.IndexingMode) + { + case IndexingMode.Lazy: + return new LazyIndexLanguageMatchEngine(languageMatcher); + case IndexingMode.Eager: + return new EagerIndexLanguageMatchEngine(languageMatcher); + default: + return new NoIndexLanguageMatchEngine(languageMatcher); + } + } + + public LanguageMatchEngine(LanguageMatcher languageMatcher) + { + LanguageMatcher = languageMatcher; + _logMatches = languageMatcher.LogMatches; + } + + public LanguageMatcher LanguageMatcher { get; private set; } + + #region Post-Gen + + public override MatcherResult Match(string code, bool matchFullText = true) + { + return Match(code, LanguageMatcher.StartingFragment, matchFullText); + } + + public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) + { + return Match(code, LanguageMatcher.Fragments.First(matcher => matcher.Name == fragmentMatcher), matchFullText); + } + + public MatcherResult Match(string code, FragmentMatcher startingMatcher = null, bool matchFullText = true) + { + + FragmentMatchData matchData = new FragmentMatchData + { + StartIndex = 0 + }; + + State state = new State(); + if (LanguageMatcher.Blobs.Count > 0) { + state.BlobDatas = new Span(new BlobData[LanguageMatcher.Blobs.Count]); + } + BuildState(ref state, code); + + bool success = MatchPartByFragmentMatcher(ref state, matchData, startingMatcher ?? LanguageMatcher.StartingFragment); + + IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + + if ( + success && + matchFullText && + state.CurrentIndex != (state.PreMatchSuccess ? + state.DistinctStringMatches.LastOrDefault().GetEndIndex() : state.Code.Length + )) + { + success = false; + failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, _logMatches ? state.MatchLogBuilder.ToString() : string.Empty); + } + + protected virtual (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) + { + if (pattern == null) + { + return (true, default); + } + bool success = false; + int distinctIndex = state.DistinctIndex; + if (distinctIndex < state.MaxDistinctIndex) + { + StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; + if (stringMatchData != null) + { + success = stringMatchData.Id == pattern.Id; + } + if (success && !readOnly) + { + state.DistinctIndex++; + state.CurrentIndex = stringMatchData.StartIndex + stringMatchData.Length; + } + else if (!required) + { + success = true; + } + return (success, stringMatchData); + } + else if (state.CurrentIndex < state.Code.Length) + { + int length; + int startOffset = state.CurrentIndex; + (success, length) = pattern.IsMatch(state.Code, state.CurrentIndex); + StringMatchData stringMatchData = default; + if (success) + { + stringMatchData = new StringMatchData + { + Name = pattern.Name, + Text = state.Code.Substring(startOffset, length), + StartIndex = startOffset, + Length = length, + Id = pattern.Id, + IsNoise = pattern.IsNoise, + Mergable = pattern.Mergable + }; + if (!pattern.IsNoise) + { + state.DistinctStringMatches.Add(stringMatchData); + state.MaxDistinctIndex++; + state.DistinctIndex++; + } + if (!readOnly) + { + state.CurrentIndex = startOffset + length; + state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + } + } + else if (!required) + { + success = true; + } + return (success, stringMatchData); + } + return (false, default); + } + + private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData matchData, FragmentMatcher part) + { + bool success; + bool negate = part.Negate; + if (!part.Cacheable || !state.MatchCache.TryGetValue(new ValueTuple(part.Name, state.CurrentIndex), out FragmentMatchData partMatcherData)) + { + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + (success, partMatcherData) = MatchByFragmentMatcher(ref state, part); + if (part.Cacheable) + { + state.MatchCache[new ValueTuple(part.Name, startIndex)] = partMatcherData; + } + if (!success || negate) + { + state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex; + } + } + else if ((success = partMatcherData != null) && !negate) + { + state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; + state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + state.DistinctIndex = partMatcherData.EndDistinctIndex; + } + if (success && !negate) + { + if (!part.IsNoise) + { + if (part.FallThroughMode == FallThroughMode.All || partMatcherData.Parts.Count <= (int)part.FallThroughMode) + { + matchData.Parts.AddRange(partMatcherData.Parts); + } + else + { + matchData.Parts.Add(partMatcherData); + } + } + if (part.ClearCache) + { + state.MatchCache.Clear(); + } + } + return success ^ negate; + } + + protected (bool, FragmentMatchData) MatchByFragmentMatcher(ref State state, FragmentMatcher matcher) + { + FragmentMatchData matchData = new FragmentMatchData + { + Name = matcher.Name, + StartIndex = state.CurrentIndex, + ExpressionOrder = matcher.ExpressionOrder + }; + StringMatchData endMatchData = default; + bool success = MatchFragmentBounds(ref state, matcher.Start, matcher, matcher.DiscardBounds, out StringMatchData startMatchData) && MatchFragmentParts(ref state, matcher, matchData) && MatchFragmentBounds(ref state, matcher.End, matcher, matcher.DiscardBounds, out endMatchData); + + if (success && matcher.Actions != null) + { + IEnumerator actionEnumerator = matcher.Actions.GetEnumerator(); + actionEnumerator.Reset(); + while (success && actionEnumerator.MoveNext()) + { + MatcherAction action = actionEnumerator.Current; + success = action.Perform(state.BlobDatas, matchData.Parts); + } + } + + if (success) + { + if (!matcher.Negate) + { + matchData.Length = state.CurrentIndex - matchData.StartIndex; + matchData.EndDistinctIndex = state.DistinctIndex; + if (matcher.ExpressionMode != ExpressionMode.None) + { + ConvertToExpressionTree(matchData, matcher.ExpressionMode); + } + if (matcher.BoundsAsParts) + { + if (startMatchData != null) + { + matchData.Parts.Insert(0, startMatchData); + } + if (endMatchData != null) + { + matchData.Parts.Add(endMatchData); + } + } + } + return (true, matchData); + } + return (false, null); + } + + private bool MatchFragmentParts(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + { + bool success = true; + if (matcher.Parts.Count > 0) + { + switch (matcher.PartsMatchMode) + { + case MatchMode.Multiple: + success = MatchFragmentPartsMultipleMode(ref state, matcher, matchData); + break; + case MatchMode.One: + success = MatchFragmentPartsOneMode(ref state, matcher, matchData); + break; + case MatchMode.Ordered: + success = MatchFragmentPartsOrderedMode(ref state, matcher, matchData); + break; + } + } + if (!success ^ matcher.Negate) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + return success; + } + + private bool MatchFragmentPartsMultipleMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + { + bool overallSuccess = false; + bool subSuccess; + bool delimiterSuccess = false; + StringMatchData range = default; + int matchCount = 0; + int distinctIndex = state.DistinctIndex; + MatchPattern(ref state, matcher.PartsPadding, false); + do + { + subSuccess = false; + foreach (IMatcher part in matcher.Parts) + { + bool individualSuccess = MatchFragmentPart(ref state, matchData, part); + subSuccess |= individualSuccess; + if (individualSuccess) + { + matchCount++; + distinctIndex = state.DistinctIndex; + (delimiterSuccess, range) = MatchPattern(ref state, matcher.PartsDelimiter, matcher.PartsDelimiterRequired); + break; + } + } + overallSuccess |= subSuccess; + } + while (subSuccess && delimiterSuccess); + if (delimiterSuccess && range != null) + { + state.CurrentIndex = range.StartIndex; + state.DistinctIndex = distinctIndex; + } + if (overallSuccess) + { + MatchPattern(ref state, matcher.PartsPadding, false); + } + bool thresholdSuccess = (matcher.MinMatchedParts ?? 1) <= matchCount; + return overallSuccess && thresholdSuccess; + } + + private bool MatchFragmentPartsOneMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + { + bool success = false; + int matchCount = 0; + MatchPattern(ref state, matcher.PartsPadding, false); + foreach (IMatcher part in matcher.Parts) + { + success = MatchFragmentPart(ref state, matchData, part); + if (success) + { + matchCount++; + break; + } + } + if (success) + { + MatchPattern(ref state, matcher.PartsPadding, false); + } + bool thresholdSuccess = (matcher.MinMatchedParts ?? 1) <= 0 || matchCount > 0; + return thresholdSuccess; + } + + private bool MatchFragmentPartsOrderedMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) + { + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = default; + int distinctIndex = state.DistinctIndex; + MatchPattern(ref state, matcher.PartsPadding, false); + for (int partIndex = 0; partIndex < matcher.Parts.Count; partIndex++) + { + if (partIndex > 0) + { + distinctIndex = state.DistinctIndex; + (partSuccess, stringMatchData) = MatchPattern(ref state, matcher.PartsDelimiter, matcher.PartsDelimiterRequired); + success = partSuccess; + if (!success) + { + break; + } + } + + IMatcher part = matcher.Parts[partIndex]; + success = MatchFragmentPart(ref state, matchData, part); + if (!success) + { + if (stringMatchData != null) + { + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + } + break; + } + else + { + matchCount++; + } + } + if (success) + { + MatchPattern(ref state, matcher.PartsPadding, false); + } + bool thresholdSuccess = (matcher.MinMatchedParts ?? matcher.Parts.Count) <= matchCount; + return success || thresholdSuccess; + } + + private bool MatchFragmentPart(ref State state, FragmentMatchData matchData, IMatcher part) + { + int currentId = ++state.Id; + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', currentId)} {state.CurrentIndex}. Try: {part}"); + } + bool success = false; + switch (part) + { + case FragmentMatcher partFragmentMatcher: + success = MatchPartByFragmentMatcher(ref state, matchData, partFragmentMatcher); + break; + case PatternMatcher partPatternMatcher: + success = MatchPartByTextMatcher(ref state, matchData, partPatternMatcher); + break; + } + + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', currentId)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: {part}"); + } + state.Id = currentId - 1; + return success; + } + + private bool MatchPartByTextMatcher(ref State state, FragmentMatchData matchData, PatternMatcher part) + { + (bool success, StringMatchData stringMatchData) = MatchPattern(ref state, part, true); + if (success) + { + matchData.Parts.Add(stringMatchData); + if (_logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {state.CurrentIndex}. Matched: {stringMatchData.Text}"); + } + } + return success; + } + + private bool MatchFragmentBounds(ref State state, PatternMatcher patternMatcher, FragmentMatcher matcher, bool readOnly, out StringMatchData matchData) + { + bool success; + (success, matchData) = MatchPattern(ref state, patternMatcher, true, readOnly); + if (!success ^ matcher.Negate) + { + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + } + if (patternMatcher != null && _logMatches) + { + state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")} Bounds: {patternMatcher}"); + } + return success; + } + + protected abstract void BuildState(ref State state, string code); + + #endregion + + #region Pre-Gen + + + internal static string Generate(MatcherEngineGenerator generator) + { + string version = AssemblyName.GetAssemblyName(generator.GetType().Assembly.Location).Version.ToString(); + return $@" +/* Generated by Synfron.Staxe.Matcher v{version}*/ + +using Synfron.Staxe.Matcher.Data; +using System.Collections.Generic; +using System; +using System.Linq; +using Synfron.Staxe.Matcher.Input; +{(generator.LanguageMatcher.LogMatches ? "using System.Text;" : null)} + +namespace Synfron.Staxe.Matcher +{{ + public class {GetSafeMethodName(generator.LanguageMatcher.Name)}MatchEngine : AbstractLanguageMatchEngine + {{ + + {GenerateMatch(generator)} + + <> + }} +}}"; + } + + private static string GenerateMatch(MatcherEngineGenerator generator) + { + LanguageMatcher languageMatcher = generator.LanguageMatcher; + return $@" + public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) + {{ + FragmentMatchData matchData = new FragmentMatchData + {{ + StartIndex = 0 + }}; + + State state = new State() + {{ + Code = code{(languageMatcher.IndexingMode != IndexingMode.None ? @", + DistinctStringMatches = new List(2000)" : null)}{(languageMatcher.Fragments.Any(fragment => fragment.Cacheable) ? @", + MatchCache = new Dictionary, FragmentMatchData>()" : null)}{(languageMatcher.LogMatches ? $@", + MatchLogBuilder = new StringBuilder()" : null)}{(languageMatcher.Blobs.Count > 0 ? $@", + BlobDatas = new Span(new BlobData[{languageMatcher.Blobs.Count}]);" : null)} + }}; + + {(languageMatcher.IndexingMode == IndexingMode.Eager ? "state.PreMatchSuccess = PreMatchPatterns(ref state);" : null)} + + bool success = false; + switch (fragmentMatcher) + {{ + {string.Join("\n", languageMatcher.Fragments.Where(matcher => !matcher.IsNoise).Select(matcher => $@" + case ""{HttpUtility.JavaScriptStringEncode(matcher.Name)}"": + success = {Generate(generator, matcher)}; + break; + "))} + }} + + IMatchData resultMatchData = matchData.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + + if (success && matchFullText && (state.PreMatchSuccess ? state.MaxIndex : state.CurrentIndex) != state.Code.Length) + {{ + success = false; + failureIndex = state.CurrentIndex; + }} + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); + }} + + public override MatcherResult Match(string code, bool matchFullText = true) + {{ + FragmentMatchData matchData = new FragmentMatchData + {{ + StartIndex = 0 + }}; + + State state = new State() + {{ + Code = code{(languageMatcher.IndexingMode != IndexingMode.None ? @", + DistinctStringMatches = new List(2000)" : null)}{(languageMatcher.Fragments.Any(fragment => fragment.Cacheable) ? @", + MatchCache = new Dictionary, FragmentMatchData>()" : null)}{(languageMatcher.LogMatches ? $@", + MatchLogBuilder = new StringBuilder()" : null)} + }}; + + {(languageMatcher.IndexingMode == IndexingMode.Eager ? "PreMatchPatterns(ref state);" : null)} + + bool success = {Generate(generator, languageMatcher.StartingFragment)}; + + IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); + int? failureIndex = success ? null : state.FailureIndex; + + if (success && matchFullText && state.CurrentIndex != state.Code.Length) + {{ + success = false; + failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + }} + + return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, state.MatchLogBuilder?.ToString()); + }} + + {(languageMatcher.IndexingMode == IndexingMode.Eager ? $@"private bool PreMatchPatterns(ref State state) + {{ + int codeLength = state.Code.Length; + bool success = true; + bool previousNoise = false; + StringMatchData matchData = null; + int currentIndex = 0; + while ((currentIndex = state.CurrentIndex) < codeLength) + {{ + success = {string.Join(" ||\n", languageMatcher.Patterns.Select(pattern => $@"{string.Format(GenerateMatchPattern(generator, pattern), "matchData", "true", "false")}"))}; + if (!success) + {{ + break; + }} + else if (matchData != null) {{ + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{currentIndex}}. Prematched {{matchData.Name}}: {{matchData.Text}}"");" : null)} + if (matchData.IsNoise) + {{ + previousNoise = true; + }} + else if (previousNoise) + {{ + if (state.DistinctIndex > 1) + {{ + StringMatchData previousMatchData = state.DistinctStringMatches[state.DistinctIndex - 2]; + if (previousMatchData.Name == matchData.Name && previousMatchData.Mergable) + {{ + previousMatchData.Text += matchData.Text; + previousMatchData.Length = state.CurrentIndex - previousMatchData.StartIndex; + state.DistinctIndex--; + state.MaxDistinctIndex--; + state.DistinctStringMatches.RemoveAt(state.DistinctIndex); + }} + }} + previousNoise = false; + }} + }} + }} + state.CurrentIndex = 0; + {(languageMatcher.IndexingMode != IndexingMode.None ? "state.DistinctIndex = 0;" : null)} + return success; + }}" : null)}"; + } + + + private static string GenerateMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) + { + string methodName = $"MatchPattern{GetSafeMethodName(pattern.Name)}"; + string method = $"{methodName}(ref state, out {{0}}, {{1}}, {{2}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(ref State state, out StringMatchData matchData, bool required, bool readOnly = false) + {{ + bool success = false; + {(generator.IndexingMode != IndexingMode.None && pattern.IsStandard ? $@"int distinctIndex = state.DistinctIndex; + if (distinctIndex >= state.MaxDistinctIndex) + {{" : null)} + int length; + int startOffset = state.CurrentIndex; + (success, length) = {GenerateRawMatchPattern(generator, pattern)}; + matchData = default; if (success) - { - stringMatchData = new StringMatchData - { - Name = pattern.Name, + {{ + matchData = new StringMatchData + {{ + Name = ""{HttpUtility.JavaScriptStringEncode(pattern.Name)}"", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable - }; - success = stringMatchData != null; + IsNoise = {pattern.IsNoise.ToString().ToLower()}, + Mergable = {pattern.Mergable.ToString().ToLower()}, + Id = {pattern.Id} + }}; + {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.DistinctStringMatches.Add(matchData);" : null)} + {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.MaxDistinctIndex++;" : null)} + {(!pattern.IsNoise && generator.IndexingMode != IndexingMode.None ? $@"state.DistinctIndex++;" : null)} if (!readOnly) - { - state.CurrentIndex += stringMatchData.Length; - } - } + {{ + state.CurrentIndex = startOffset + length; + state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + }} + }} else if (!required) - { + {{ success = true; - } - return (success, stringMatchData); - - } - return (true, default); - } - } - - private sealed class LazyIndexLanguageMatchEngine : LanguageMatchEngine - { - public LazyIndexLanguageMatchEngine(LanguageMatcher languageMatcher) : base(languageMatcher) - { - _hasCheckFlags = true; - } - - protected override void BuildState(ref State state, string code) - { - state.Code = code; - state.DistinctStringMatches = new List(2000); - state.MatchLogBuilder = new StringBuilder(); - state.MatchCache = new Dictionary, FragmentMatchData>(); - } - - protected override (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false) - { - if (pattern != null) - { - bool success = false; - int distinctIndex = state.DistinctIndex; - if (distinctIndex >= state.MaxDistinctIndex) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = pattern.IsMatch(state.Code, state.CurrentIndex); - StringMatchData stringMatchData = default; - if (success) - { - stringMatchData = new StringMatchData - { - Name = pattern.Name, + }} + return success; + {(generator.IndexingMode != IndexingMode.None && pattern.IsStandard ? $@"}} + else + {{ + matchData = state.DistinctStringMatches[distinctIndex]; + if (matchData != null) + {{ + success = matchData.Id == {pattern.Id}; + }} + else + {{ + int length; + int startOffset = state.CurrentIndex; + (success, length) = {GenerateRawMatchPattern(generator, pattern)}; + if (success) + {{ + matchData = new StringMatchData + {{ + Name = ""{GetSafeMethodName(pattern.Name)}"", Text = state.Code.Substring(startOffset, length), StartIndex = startOffset, Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable - }; - state.DistinctStringMatches.Add(stringMatchData); - success = stringMatchData != null; - state.CheckFlags[pattern.Id] = distinctIndex + 1; - state.MaxDistinctIndex++; - if (!readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - } - else if (!required) - { - success = true; - } - return (success, stringMatchData); - } - else - { - StringMatchData stringMatchData = state.DistinctStringMatches[distinctIndex]; - if (stringMatchData != null) - { - success = stringMatchData.Id == pattern.Id; - } - else - { - if (state.CheckFlags[pattern.Id] < distinctIndex + 1) - { - int length; - int startOffset = state.CurrentIndex; - (success, length) = pattern.IsMatch(state.Code, state.CurrentIndex); - if (success) - { - stringMatchData = new StringMatchData - { - Name = pattern.Name, - Text = state.Code.Substring(startOffset, length), - StartIndex = startOffset, - Length = length, - Id = pattern.Id, - IsNoise = pattern.IsNoise, - Mergable = pattern.Mergable - }; - state.DistinctStringMatches[distinctIndex] = stringMatchData; - } - state.CheckFlags[pattern.Id] = distinctIndex + 1; - } - } - if (success && !readOnly) - { - state.DistinctIndex++; - state.CurrentIndex += stringMatchData.Length; - } - else if (!required) - { - success = true; - } - return (success, stringMatchData); - } - - } - return (true, default); - } - } - - public static LanguageMatchEngine Build(LanguageMatcher languageMatcher) - { - switch (languageMatcher.IndexingMode) - { - case IndexingMode.Lazy: - return new LazyIndexLanguageMatchEngine(languageMatcher); - case IndexingMode.Eager: - return new EagerIndexLanguageMatchEngine(languageMatcher); - default: - return new NoIndexLanguageMatchEngine(languageMatcher); - } - } - - public LanguageMatchEngine(LanguageMatcher languageMatcher) - { - LanguageMatcher = languageMatcher; - _logMatches = languageMatcher.LogMatches; - } - - public LanguageMatcher LanguageMatcher { get; private set; } - - public override MatcherResult Match(string code, bool matchFullText = true) - { - return Match(code, LanguageMatcher.StartingFragment, matchFullText); - } - - public override MatcherResult Match(string code, string fragmentMatcher, bool matchFullText = true) - { - return Match(code, LanguageMatcher.Fragments.First(matcher => matcher.Name == fragmentMatcher), matchFullText); - } - - public MatcherResult Match(string code, FragmentMatcher startingMatcher = null, bool matchFullText = true) - { - - FragmentMatchData matchData = new FragmentMatchData - { - StartIndex = 0 - }; - - Span checkFlags = _hasCheckFlags ? stackalloc int[LanguageMatcher.Patterns.Count + 1] : default; - State state = new State() - { - CheckFlags = checkFlags - }; - BuildState(ref state, code); - - bool success = MatchPartByFragmentMatcher(ref state, matchData, startingMatcher ?? LanguageMatcher.StartingFragment); + IsNoise = {pattern.IsNoise.ToString().ToLower()}, + Mergable = {pattern.Mergable.ToString().ToLower()}, + Id = {pattern.Id} + }}; + {(!pattern.IsNoise ? $@"state.DistinctStringMatches[distinctIndex] = matchData;" : null)} + }} + }} + if (success && !readOnly) + {{ + {(!pattern.IsNoise ? $@"state.DistinctIndex++;" : null)} + state.CurrentIndex = startOffset + length; + state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + }} + if (!required) + {{ + success = true; + }} + return success; + }}" : null)} + {(!pattern.IsStandard && pattern is FragmentPatternMatcher fragmentPatternMatcher && fragmentPatternMatcher.Fragment is FragmentMatcher fragmentMatcher ? $@" + int startOffset = state.CurrentIndex;{(pattern.IsNoise ? $@" + int distinctStringMatchesCount = state.DistinctStringMatches.Count; + int distinctIndex = state.DistinctIndex; + int maxDistinctIndex = state.MaxDistinctIndex; + " : null)} + FragmentMatchData partMatcherData; + {GenerateMatchByFragmentMatcherSection(generator, fragmentMatcher)} + {(fragmentMatcher.Cacheable ? $@" + state.MatchCache[new ValueTuple({fragmentMatcher.Name}, startOffset)] = partMatcherData; + " : null)}{(pattern.IsNoise ? $@" + state.DistinctStringMatches.RemoveRange(distinctStringMatchesCount, state.DistinctStringMatches.Count - distinctStringMatchesCount); + state.DistinctIndex = distinctIndex; + state.MaxDistinctIndex = maxDistinctIndex; + " : null)} + success = success && state.CurrentIndex > startIndex;" : null)} + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } - IMatchData resultMatchData = matchData?.Parts.FirstOrDefault(); - int? failureIndex = success ? null : state.FailureIndex; + private static string GenerateRawMatchPattern(MatcherEngineGenerator generator, PatternMatcher pattern) + { + return string.Format(pattern.Generate(generator), "state.Code", "state.CurrentIndex"); + } - if (success && matchFullText && state.CurrentIndex != state.Code.Length) - { - success = false; - failureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - - return new MatcherResult(resultMatchData, success, state.CurrentIndex, failureIndex, _logMatches ? state.MatchLogBuilder.ToString() : string.Empty); - } - - private bool MatchPartByFragmentMatcher(ref State state, FragmentMatchData matchData, FragmentMatcher part) - { - bool success; - bool negate = part.Negate; - if (!part.Cacheable || !state.MatchCache.TryGetValue(new ValueTuple(part.Name, state.CurrentIndex), out FragmentMatchData partMatcherData)) - { - int startIndex = state.CurrentIndex; - int distinctIndex = state.DistinctIndex; - (success, partMatcherData) = MatchByFragmentMatcher(ref state, part); - if (part.Cacheable) - { - state.MatchCache[new ValueTuple(part.Name, startIndex)] = partMatcherData; - } - if (!success || negate) - { - state.CurrentIndex = startIndex; - state.DistinctIndex = distinctIndex; - } - } - else if ((success = partMatcherData != null) && !negate) - { - state.CurrentIndex = partMatcherData.StartIndex + partMatcherData.Length; - state.DistinctIndex = partMatcherData.EndDistinctIndex; - } - if (success && !negate) - { - if (!part.IsNoise) - { - if (part.FallThroughMode == FallThroughMode.All || partMatcherData.Parts.Count <= (int)part.FallThroughMode) - { - matchData.Parts.AddRange(partMatcherData.Parts); - } - else - { - matchData.Parts.Add(partMatcherData); - } - } - if (part.ClearCache) - { - state.MatchCache.Clear(); - } - } - return success ^ negate; - } - - private (bool, FragmentMatchData) MatchByFragmentMatcher(ref State state, FragmentMatcher matcher) - { - FragmentMatchData matchData = new FragmentMatchData - { - Name = matcher.Name, - StartIndex = state.CurrentIndex, - ExpressionOrder = matcher.ExpressionOrder - }; - StringMatchData endMatchData = default; - bool success = MatchFragmentBounds(ref state, matcher.Start, matcher, matcher.DiscardBounds, out StringMatchData startMatchData) && MatchFragmentParts(ref state, matcher, matchData) && MatchFragmentBounds(ref state, matcher.End, matcher, matcher.DiscardBounds, out endMatchData); + private static string GenerateMatcherAction(MatcherEngineGenerator generator, MatcherAction action) + { + return string.Format(action.Generate(generator), "state.BlobDatas", "partMatcherData.Parts"); + } - if (success) - { - if (!matcher.Negate) - { - matchData.Length = state.CurrentIndex - matchData.StartIndex; - matchData.EndDistinctIndex = state.DistinctIndex; - if (matcher.ExpressionMode != ExpressionMode.None) - { - ConvertToExpressionTree(matchData, matcher.ExpressionMode); - } - if (matcher.BoundsAsParts) - { - if (startMatchData != null) - { - matchData.Parts.Insert(0, startMatchData); - } - if (endMatchData != null) - { - matchData.Parts.Add(endMatchData); - } - } - } - return (true, matchData); - } - return (false, null); - } - - private bool MatchFragmentParts(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) - { - bool success = true; - if (matcher.Parts.Count > 0) - { - switch (matcher.PartsMatchMode) - { - case MatchMode.Multiple: - success = MatchFragmentPartsMultipleMode(ref state, matcher, matchData); - break; - case MatchMode.One: - success = MatchFragmentPartsOneMode(ref state, matcher, matchData); - break; - case MatchMode.Ordered: - success = MatchFragmentPartsOrderedMode(ref state, matcher, matchData); - break; - } - } - if (!success ^ matcher.Negate) - { + private static string GenerateMatchFragmentPartsOrderedMode(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + { + string methodName = $"MatchFragmentPartsOrderedMode{GetSafeMethodName(fragmentMatcher.Name)}"; + string method = $"{methodName}(ref state, partMatcherData)"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + StringBuilder functionText = new StringBuilder(); + for (int partIndex = 0; partIndex < fragmentMatcher.Parts.Count; partIndex++) + { + functionText.AppendLine($@"{(partIndex > 0 ? $@"distinctIndex = state.DistinctIndex; + partSuccess = {(fragmentMatcher.PartsDelimiter != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsDelimiter), "stringMatchData", fragmentMatcher.PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; + success = partSuccess; + if (!success) + {{ + goto Break; + }}" : null)} + + success = {GenerateMatchFragmentPart(generator, fragmentMatcher.Parts[partIndex])}; + if (!success) + {{ + if (stringMatchData != null) + {{ + state.CurrentIndex = stringMatchData.StartIndex; + state.DistinctIndex = distinctIndex; + }} + goto Break; + }} + else + {{ + matchCount++; + }}"); + } + + string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + {{ + bool success = true; + bool partSuccess; + int matchCount = 0; + StringMatchData stringMatchData = null; + int distinctIndex = state.DistinctIndex; + {(fragmentMatcher.PartsPadding != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false") : null)}; + + {functionText} + + Break: + {(fragmentMatcher.PartsPadding != null ? + $@"if (success) + {{ + {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")}; + }}" : null)} + + success = success && {(fragmentMatcher.MinMatchedParts ?? fragmentMatcher.Parts.Count)} <= matchCount; + if ({(!fragmentMatcher.Negate ? "!" : null)}success) + {{ + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + }} + return success; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + + private static string GenerateMatchFragmentPartsOneMode(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + { + string methodName = $"MatchFragmentPartsOneMode{GetSafeMethodName(fragmentMatcher.Name)}"; + string method = $"{methodName}(ref state, partMatcherData)"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + {{ + bool success = false; + int matchCount = 0; + {(fragmentMatcher.PartsPadding != null ? $"{string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")};" : null)} + + {(fragmentMatcher.Parts.Count > 0 ? $@" + success = {string.Join(" || ", fragmentMatcher.Parts.Select(part => GenerateMatchFragmentPart(generator, part)))}; + if (success) + {{ + matchCount++; + }}" : null)} + + {(fragmentMatcher.PartsPadding != null ? + $@"if (success) + {{ + {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")}; + }}" : null)} + + success = {((fragmentMatcher.MinMatchedParts ?? 1) <= 0).ToString().ToLower()} && matchCount > 0; + if ({(!fragmentMatcher.Negate ? "!" : null)}success) + {{ + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + }} + return success; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + + private static string GenerateMatchFragmentPartsMultipleMode(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + { + string methodName = $"MatchFragmentPartsMultipleMode{GetSafeMethodName(fragmentMatcher.Name)}"; + string method = $"{methodName}(ref state, partMatcherData)"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + StringBuilder functionText = new StringBuilder(); + for (int partIndex = 0; partIndex < fragmentMatcher.Parts.Count; partIndex++) + { + functionText.AppendLine($@"individualSuccess = {GenerateMatchFragmentPart(generator, fragmentMatcher.Parts[partIndex])}; + subSuccess |= individualSuccess; + if (individualSuccess) + {{ + matchCount++; + distinctIndex = state.DistinctIndex; + delimiterSuccess = {(fragmentMatcher.PartsDelimiter != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsDelimiter), "range", fragmentMatcher.PartsDelimiterRequired.ToString().ToLower(), "false") : "true")}; + goto Break; + }} + "); + } + + string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + {{ + bool overallSuccess = false; + bool subSuccess = false; + bool delimiterSuccess = false; + StringMatchData range = default; + int matchCount = 0; + int distinctIndex = state.DistinctIndex; + {(fragmentMatcher.PartsPadding != null ? string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false") : null)}; + + do + {{ + subSuccess = false; + bool individualSuccess; + {functionText.ToString()} + + Break: + overallSuccess |= subSuccess; + }} + while (subSuccess && delimiterSuccess); + if (delimiterSuccess && range != null) + {{ + state.CurrentIndex = range.StartIndex; + state.DistinctIndex = distinctIndex; + }} + {(fragmentMatcher.PartsPadding != null ? + $@"if (overallSuccess) + {{ + {string.Format(GenerateMatchPattern(generator, fragmentMatcher.PartsPadding), "_", "false", "false")}; + }}" : null)} + + bool thresholdSuccess = {fragmentMatcher.MinMatchedParts ?? 1} <= matchCount; + bool success = overallSuccess && thresholdSuccess; + if ({(!fragmentMatcher.Negate ? "!" : null)}success) + {{ + state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); + }} + return success; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + + private static string GenerateMatchFragmentPart(MatcherEngineGenerator generator, IMatcher part) + { + return part is FragmentMatcher fragmentMatcher + ? Generate(generator, fragmentMatcher) + : GenerateMatchPartByTextMatcher(generator, (PatternMatcher)part); + } + + private static string GenerateMatchFragmentBounds(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher, PatternMatcher matcher) + { + string methodName = $"MatchFragmentBounds{GetSafeMethodName(matcher.Name)}"; + string method = $"{methodName}(ref state, {{0}}, out {{1}})"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(ref State state, bool readOnly, out StringMatchData matchData) + {{ + bool success = {string.Format(GenerateMatchPattern(generator, matcher), "matchData", "true", "readOnly")}; + if ({(!fragmentMatcher.Negate ? "!" : null)}success) + {{ state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - return success; - } - - private bool MatchFragmentPartsMultipleMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) - { - bool overallSuccess = false; - bool subSuccess; - bool delimiterSuccess = false; - StringMatchData range = default; - int matchCount = 0; - int distinctIndex = state.DistinctIndex; - MatchPattern(ref state, matcher.PartsPadding, false); - do - { - subSuccess = false; - foreach (IMatcher part in matcher.Parts) - { - bool individualSuccess = MatchFragmentPart(ref state, matchData, part); - subSuccess |= individualSuccess; - if (individualSuccess) - { - matchCount++; - distinctIndex = state.DistinctIndex; - (delimiterSuccess, range) = MatchPattern(ref state, matcher.PartsDelimiter, matcher.PartsDelimiterRequired); - break; - } - } - overallSuccess |= subSuccess; - } - while (subSuccess && delimiterSuccess); - if (delimiterSuccess && range != null) - { - state.CurrentIndex = range.StartIndex; - state.DistinctIndex = distinctIndex; - } - if (overallSuccess) - { - MatchPattern(ref state, matcher.PartsPadding, false); - } - bool thresholdSuccess = (matcher.MinMatchedParts ?? 1) <= matchCount; - return overallSuccess || thresholdSuccess; - } - - private bool MatchFragmentPartsOneMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) - { - bool success = false; - int matchCount = 0; - MatchPattern(ref state, matcher.PartsPadding, false); - foreach (IMatcher part in matcher.Parts) - { - success = MatchFragmentPart(ref state, matchData, part); - if (success) - { - matchCount++; - break; - } - } + }} + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{state.CurrentIndex}}. {{(success ? ""Passed"" : ""Failed"")}} Bounds: {{""{HttpUtility.JavaScriptStringEncode(matcher.ToString())}""}}"");" : null)} + return success; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + + private static string GenerateMatchPartByTextMatcher(MatcherEngineGenerator generator, PatternMatcher matcher) + { + string methodName = $"MatchPartByTextMatcher{GetSafeMethodName(matcher.Name)}"; + string method = $"{methodName}(ref state, matchData)"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + {{ + + {(generator.LanguageMatcher.LogMatches ? $@"int currentId = ++state.Id; + state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. Try: {HttpUtility.JavaScriptStringEncode(matcher.Name)}"");" : null)} + StringMatchData partMatchData; + bool success = {string.Format(GenerateMatchPattern(generator, matcher), "partMatchData", "true", "false")}; + if (success) + {{ + matchData.Parts.Add(partMatchData); + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', state.Id + 1)}} {{state.CurrentIndex}}. Matched: {{partMatchData.Text}}"");" : null)} + }} + + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. {{(success ? ""Passed"" : ""Failed"")}}: {HttpUtility.JavaScriptStringEncode(matcher.Name)}""); + state.Id = currentId - 1;" : null)} + return success; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } + + private static string GenerateMatchByFragmentMatcherSection(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + { + return $@"partMatcherData = new FragmentMatchData + {{ + Name = ""{GetEscapedName(fragmentMatcher.Name)}"", + StartIndex = state.CurrentIndex{(fragmentMatcher.ExpressionOrder != null ? $@", + ExpressionOrder = {fragmentMatcher.ExpressionOrder}" : null)} + }}; + + {(fragmentMatcher.Start != null ? $"StringMatchData startMatchData;" : null)} + {(fragmentMatcher.End != null ? $"StringMatchData endMatchData;" : null)} + success = ({(fragmentMatcher.Start != null ? $"{string.Format(GenerateMatchFragmentBounds(generator, fragmentMatcher, fragmentMatcher.Start), fragmentMatcher.DiscardBounds.ToString().ToLower(), "startMatchData")} && " : null)}{GenerateMatchFragmentParts(generator, fragmentMatcher)}{(fragmentMatcher.End != null ? $" && {string.Format(GenerateMatchFragmentBounds(generator, fragmentMatcher, fragmentMatcher.End), fragmentMatcher.DiscardBounds.ToString().ToLower(), "endMatchData")}" : null)}); + + {(fragmentMatcher.Actions != null && fragmentMatcher.Actions.Count > 0 ? $@" if (success) - { - MatchPattern(ref state, matcher.PartsPadding, false); - } - bool thresholdSuccess = (matcher.MinMatchedParts ?? 1) <= 0 || matchCount > 0; - return thresholdSuccess; - } - - private bool MatchFragmentPartsOrderedMode(ref State state, FragmentMatcher matcher, FragmentMatchData matchData) - { - bool success = true; - bool partSuccess; - int matchCount = 0; - StringMatchData stringMatchData = default; - int distinctIndex = state.DistinctIndex; - MatchPattern(ref state, matcher.PartsPadding, false); - for (int partIndex = 0; partIndex < matcher.Parts.Count; partIndex++) - { - if (partIndex > 0) - { - distinctIndex = state.DistinctIndex; - (partSuccess, stringMatchData) = MatchPattern(ref state, matcher.PartsDelimiter, matcher.PartsDelimiterRequired); - success = partSuccess; - if (!success) - { - break; - } - } - - IMatcher part = matcher.Parts[partIndex]; - success = MatchFragmentPart(ref state, matchData, part); - if (!success) - { - if (stringMatchData != null) - { - state.CurrentIndex = stringMatchData.StartIndex; - state.DistinctIndex = distinctIndex; - } - break; - } - else - { - matchCount++; - } - } + {{ + success = {string.Join(" && ", fragmentMatcher.Actions.Select(action => GenerateMatcherAction(generator, action)))}; + }} + " : null)} + if (success) - { - MatchPattern(ref state, matcher.PartsPadding, false); - } - bool thresholdSuccess = (matcher.MinMatchedParts ?? matcher.Parts.Count) <= matchCount; - return success || thresholdSuccess; - } - - private bool MatchFragmentPart(ref State state, FragmentMatchData matchData, IMatcher part) - { - int currentId = ++state.Id; - if (_logMatches) - { - state.MatchLogBuilder.AppendLine($"{new string('\t', currentId)} {state.CurrentIndex}. Try: {part}"); - } + {{ + {(!fragmentMatcher.Negate ? $@"partMatcherData.Length = state.CurrentIndex - partMatcherData.StartIndex; + partMatcherData.EndDistinctIndex = state.DistinctIndex;" : null)} + {(fragmentMatcher.Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", startIndex)] = partMatcherData;" : null)} + {(!fragmentMatcher.IsNoise && fragmentMatcher.ExpressionMode != ExpressionMode.None && !fragmentMatcher.Negate ? $"ConvertToExpressionTree(partMatcherData, ExpressionMode.{fragmentMatcher.ExpressionMode});" : null)} + {(fragmentMatcher.BoundsAsParts && fragmentMatcher.Start != null && !fragmentMatcher.Negate ? $@"if (startMatchData != null) + {{ + partMatcherData.Parts.Insert(0, startMatchData); + }}" : null)} + {(fragmentMatcher.BoundsAsParts && fragmentMatcher.End != null && !fragmentMatcher.Negate ? $@"if (endMatchData != null) + {{ + partMatcherData.Parts.Add(endMatchData); + }}" : null)} + }}"; + } + + private static string Generate(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + { + string methodName = $"MatchFragment{GetSafeMethodName(fragmentMatcher.Name)}"; + string method = $"{methodName}(ref state, matchData)"; + if (!generator.TryGetMethod(methodName, ref method)) + { + generator.Add(methodName, method); + string code = $@"private bool {methodName}(ref State state, FragmentMatchData matchData) + {{ + {(generator.LanguageMatcher.LogMatches ? $@"int currentId = ++state.Id; + state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. Try: {GetEscapedName(fragmentMatcher.Name)}"");" : null)} bool success = false; - switch (part) - { - case FragmentMatcher partFragmentMatcher: - success = MatchPartByFragmentMatcher(ref state, matchData, partFragmentMatcher); - break; - case PatternMatcher partPatternMatcher: - success = MatchPartByTextMatcher(ref state, matchData, partPatternMatcher); - break; - } + FragmentMatchData partMatcherData = null; + {(fragmentMatcher.Cacheable ? $@"if (!state.MatchCache.TryGetValue(new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", state.CurrentIndex), out partMatcherData)) + {{" : null)} + int startIndex = state.CurrentIndex; + int distinctIndex = state.DistinctIndex; + {GenerateMatchByFragmentMatcherSection(generator, fragmentMatcher)} + else + {{ + {(fragmentMatcher.Cacheable ? $@"state.MatchCache[new ValueTuple(""{GetEscapedName(fragmentMatcher.Name)}"", startIndex)] = null;" : null)} + {(!fragmentMatcher.Negate ? $@"state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex;" : null)} + }} + {(fragmentMatcher.Negate ? $@"state.CurrentIndex = startIndex; + state.DistinctIndex = distinctIndex;" : null)} + {(fragmentMatcher.Cacheable ? $@"}}" : null)} + {(fragmentMatcher.Cacheable && !fragmentMatcher.Negate ? $@"else if (success = partMatcherData != null) + {{ + state.CurrentIndex = startIndex + partMatcherData.Length; + state.MaxIndex = Math.Max(state.MaxIndex, state.CurrentIndex); + state.DistinctIndex = partMatcherData.EndDistinctIndex; + }}" : null)} + {(!fragmentMatcher.Negate ? $@"if (success) + {{ + {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode == FallThroughMode.All ? "matchData.Parts.AddRange(partMatcherData.Parts);" : null)} + {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode == FallThroughMode.None ? "matchData.Parts.Add(partMatcherData);" : null)} + {(!fragmentMatcher.IsNoise && fragmentMatcher.FallThroughMode != FallThroughMode.None && fragmentMatcher.FallThroughMode != FallThroughMode.All ? $@"if (partMatcherData.Parts.Count <= {(int)fragmentMatcher.FallThroughMode}) + {{ + matchData.Parts.AddRange(partMatcherData.Parts); + }} + else + {{ + matchData.Parts.Add(partMatcherData); + }}" : null)} + {(fragmentMatcher.ClearCache ? "state.MatchCache.Clear();" : null)} + }}" : null)} + {(generator.LanguageMatcher.LogMatches ? $@"state.MatchLogBuilder.AppendLine($""{{new String('\t', currentId)}} {{state.CurrentIndex}}. {{({(fragmentMatcher.Negate ? "!" : null)}success ? ""Passed"" : ""Failed"")}}: {GetEscapedName(fragmentMatcher.Name)}""); + state.Id = currentId - 1;" : null)} + return {(fragmentMatcher.Negate ? "!" : null)}success; + }}"; + method = generator.Add(method, methodName, code); + } + return method; + } - if (_logMatches) - { - state.MatchLogBuilder.AppendLine($"{new string('\t', currentId)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")}: {part}"); - } - state.Id = currentId - 1; - return success; - } + private static string GenerateMatchFragmentParts(MatcherEngineGenerator generator, FragmentMatcher fragmentMatcher) + { + switch (fragmentMatcher.PartsMatchMode) + { + case MatchMode.Ordered: + return GenerateMatchFragmentPartsOrderedMode(generator, fragmentMatcher); + case MatchMode.One: + return GenerateMatchFragmentPartsOneMode(generator, fragmentMatcher); + case MatchMode.Multiple: + default: + return GenerateMatchFragmentPartsMultipleMode(generator, fragmentMatcher); + } + } - private bool MatchPartByTextMatcher(ref State state, FragmentMatchData matchData, PatternMatcher part) - { - (bool success, StringMatchData stringMatchData) = MatchPattern(ref state, part, true); - if (success) - { - matchData.Parts.Add(stringMatchData); - if (_logMatches) - { - state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {state.CurrentIndex}. Matched: {stringMatchData.Text}"); - } - } - return success; - } - - private bool MatchFragmentBounds(ref State state, PatternMatcher patternMatcher, FragmentMatcher matcher, bool readOnly, out StringMatchData matchData) - { - bool success; - (success, matchData) = MatchPattern(ref state, patternMatcher, true, readOnly); - if (!success ^ matcher.Negate) - { - state.FailureIndex = Math.Max(state.FailureIndex ?? 0, state.CurrentIndex); - } - if (patternMatcher != null && _logMatches) - { - state.MatchLogBuilder.AppendLine($"{new string('\t', state.Id + 1)} {state.CurrentIndex}. {(success ? "Passed" : "Failed")} Bounds: {patternMatcher}"); - } - return success; - } + private static string GetEscapedName(string name) + { + return HttpUtility.JavaScriptStringEncode(name); + } - protected abstract void BuildState(ref State state, string code); + private static string GetSafeMethodName(string name) + { + return Regex.Replace(name, @"\W", (match) => $"x{(int)match.Value[0]}"); + } - protected abstract (bool success, StringMatchData matchData) MatchPattern(ref State state, PatternMatcher pattern, bool required, bool readOnly = false); - } + #endregion + } } diff --git a/src/Matcher/MatcherEngineGenerator.cs b/src/Matcher/MatcherEngineGenerator.cs index 8733969..bc49ff1 100644 --- a/src/Matcher/MatcherEngineGenerator.cs +++ b/src/Matcher/MatcherEngineGenerator.cs @@ -35,7 +35,7 @@ public IndexingMode IndexingMode private string Generate() { - return LanguageMatcher.Generate(this).Replace("<>", _codeBuilder.ToString()); + return LanguageMatchEngine.Generate(this).Replace("<>", _codeBuilder.ToString()); } public bool TryGetMethod(string methodName, ref string method) diff --git a/src/Shared/Collections/StackList.cs b/src/Shared/Collections/StackList.cs index 7eb38dd..12df141 100644 --- a/src/Shared/Collections/StackList.cs +++ b/src/Shared/Collections/StackList.cs @@ -1,5 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation and Joel Mueller licenses this file to you under the MIT license. +// Heavily modified for use in Staxe (https://github.com/synfron/Staxe) // See the LICENSE file in the project root for more information. using System; @@ -165,6 +166,23 @@ public void Insert(int index, T item) _version++; } } + public void InsertRange(int index, StackList items) + { + // Note that insertions at the end are legal. + if ((uint)index > (uint)_size) + { + throw new ArgumentOutOfRangeException("argument", "Insertion index was out of the range of valid values."); + } + + if (_size == _array.Length) InsertRangeWithReize(index, items); + else if (index < _size) + { + Array.Copy(_array, index, _array, index + items._size, _size - index); + Array.Copy(items._array, 0, _array, index, items._size); + _size++; + _version++; + } + } private void InsertWithReize(int index, T item) { @@ -182,6 +200,22 @@ private void InsertWithReize(int index, T item) _size++; } + private void InsertRangeWithReize(int index, StackList items) + { + if (_size + items.Count > MaxSize) + { + ThrowForMaxSize(); + } + T[] array = _array; + T[] newArray = new T[2 * _size]; + Array.Copy(array, newArray, index); + Array.Copy(items._array, 0, array, index, items._size); + Array.Copy(array, index, array, index + items.Count, _size - index); + _array = newArray; + _version++; + _size++; + } + // Copies the stack into an array. public void CopyTo(T[] array, int arrayIndex) { @@ -416,8 +450,14 @@ public int Reserve(int count) return originalSize; } + public void AddRange(StackList items) + { + AddRange(items._array, items.Count); + } + public void AddRange(T[] items, int count) { + Debug.Assert(items != _array); int size = _size; T[] array = _array; diff --git a/src/Staxe.sln b/src/Staxe.sln index c7eb8e2..31e6083 100644 --- a/src/Staxe.sln +++ b/src/Staxe.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28606.126 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Executor", "Executor\Executor.csproj", "{C9367C01-2B8C-440E-94AF-D96257B12842}" EndProject diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Files/CharacterClassDefinition.json b/src/Tests/MatcherTests/Files/CharacterClassDefinition.json similarity index 100% rename from src/Tests/MatcherTests/Interop/Ebnf/Files/CharacterClassDefinition.json rename to src/Tests/MatcherTests/Files/CharacterClassDefinition.json diff --git a/src/Tests/MatcherTests/Interop/Json/Files/TestDefinition.json b/src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json similarity index 96% rename from src/Tests/MatcherTests/Interop/Json/Files/TestDefinition.json rename to src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json index 9826038..13838ad 100644 --- a/src/Tests/MatcherTests/Interop/Json/Files/TestDefinition.json +++ b/src/Tests/MatcherTests/Files/EagerIndexTestDefinition.json @@ -2,7 +2,6 @@ "name": "StaxeTestComplexLang", "startingFragment": "Script", "indexingMode": "Eager", - "logMatches": true, "patterns": [ { "name": "Null", @@ -81,11 +80,11 @@ "pattern": "`static" }, { - "name": "Identifier", + "name": "Id", "pattern": "`\\l(\\w|_)*" }, { - "name": "Number", + "name": "Num", "pattern": "~(\\d*\\.\\d+(e(-|\\+)?\\d+)?|\\d+)" }, { @@ -210,7 +209,7 @@ { "name": "Native", "pattern": "$" - } + }, ], "fragments": [ { @@ -271,7 +270,7 @@ { "name": "NamespaceIdentifier", "parts": [ - "Identifier" + "Id" ], "partsMatchMode": "Multiple", "partsDelimiter": "Dot" @@ -279,7 +278,7 @@ { "name": "NativeIdentifier", "parts": [ - "Identifier" + "Id" ], "start": "Native", "partsMatchMode": "Multiple", @@ -329,18 +328,20 @@ { "name": "Identifier", "parts": [ - "Identifier" + "Id" ], "partsMatchMode": "One", + "fallThroughMode": "All", "cacheable": true }, { "name": "Number", "parts": [ "[Negative]", - "Number" + "Num" ], - "partsMatchMode": "Ordered" + "partsMatchMode": "Ordered", + "fallThroughMode": "One" }, { "name": "Negative", @@ -461,8 +462,7 @@ "partsMatchMode": "Multiple", "partsPadding": "Whitespace", "start": "OpenBrace", - "end": "CloseBrace", - "failureReportStrategy": 6 + "end": "CloseBrace" }, { "name": "SetterBlock", @@ -543,9 +543,8 @@ "name": "OptionalElseIfStatements", "parts": [ "[ElseIfStatement]" - ], - "MinMatchedParts": 0, + "minMatchedParts": 0, "partsMatchMode": "Multiple", "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, @@ -555,9 +554,8 @@ "name": "OptionalElseStatement", "parts": [ "[ElseStatement]" - ], - "MinMatchedParts": 0, + "minMatchedParts": 0, "partsMatchMode": "One", "fallThroughMode": "All" }, @@ -568,7 +566,8 @@ ], "start": "Else", "partsPadding": "Whitespace", - "partsMatchMode": "One" + "partsMatchMode": "One", + "fallThroughMode": "All" }, { "name": "ElseStatement", @@ -738,13 +737,6 @@ "partsDelimiterRequired": false, "partsMatchMode": "Ordered" }, - { - "name": "Self", - "parts": [ - "Self" - ], - "partsMatchMode": "One" - }, { "name": "Equal", "parts": [ @@ -779,7 +771,8 @@ ], "partsDelimiter": "Whitespace", "partsDelimiterRequired": false, - "partsMatchMode": "Ordered" + "partsMatchMode": "Ordered", + "fallThroughMode": "One" }, { "name": "ValuableChainStart", @@ -806,7 +799,7 @@ { "name": "ValuableChainSelfStart", "parts": [ - "[Self]", + "Self", "[DotIdentifier]" ], "partsMatchMode": "Ordered", @@ -899,9 +892,10 @@ "StringLiteral", "[Number]", "[DirectedValuableChain]", - "[Self]" + "Self" ], "partsMatchMode": "One", + "fallThroughMode": "All", "cacheable": true }, { @@ -912,7 +906,8 @@ "StringLiteral", "[Number]" ], - "partsMatchMode": "One" + "partsMatchMode": "One", + "fallThroughMode": "All" }, { "name": "DirectedValuableChain", @@ -921,7 +916,8 @@ "[ValuableChain]", "[ValuableSuffix]" ], - "partsMatchMode": "Ordered" + "partsMatchMode": "Ordered", + "fallThroughMode": "One" }, { "name": "ValuablePrefix", @@ -931,7 +927,8 @@ "[Decrement]" ], "minMatchedParts": 0, - "partsMatchMode": "One" + "partsMatchMode": "One", + "fallThroughMode": "Empty" }, { "name": "ValuableSuffix", @@ -940,7 +937,8 @@ "[Decrement]" ], "minMatchedParts": 0, - "partsMatchMode": "One" + "partsMatchMode": "One", + "fallThroughMode": "Empty" }, { "name": "Decrement", @@ -1006,9 +1004,9 @@ ], "partsMatchMode": "Ordered", "partsDelimiter": "Whitespace", - "fallThroughMode": "All", "partsDelimiterRequired": false, - "expressionMode": "BinaryTree" + "expressionMode": "BinaryTree", + "fallThroughMode": "All" }, { "name": "ExpressionSuffix", @@ -1036,7 +1034,6 @@ "partsMatchMode": "One", "boundsAsParts": true, "expressionOrder": 1 - }, { "name": "AdditiveSuffix", @@ -1162,7 +1159,8 @@ "partsPadding": "Whitespace", "start": "OpenBracket", "end": "CloseBracket", + "fallThroughMode": "All", "cacheable": true } ] -} +} \ No newline at end of file diff --git a/src/Tests/MatcherTests/Files/EagerIndexTestDefinition2.json b/src/Tests/MatcherTests/Files/EagerIndexTestDefinition2.json new file mode 100644 index 0000000..ac2a248 --- /dev/null +++ b/src/Tests/MatcherTests/Files/EagerIndexTestDefinition2.json @@ -0,0 +1,710 @@ +{ + "name": "StaxeTestSimpleLang", + "startingFragment": "Script", + "indexingMode": "Eager", + "patterns": [ + { + "name": "Whitespace", + "pattern": "\\s+", + "isNoise": true + }, + { + "name": "Null", + "pattern": "`null" + }, + { + "name": "Boolean", + "pattern": "`true|false" + }, + { + "name": "Break", + "pattern": "`break" + }, + { + "name": "Continue", + "pattern": "`continue" + }, + { + "name": "Else", + "pattern": "`else" + }, + { + "name": "If", + "pattern": "`if" + }, + { + "name": "While", + "pattern": "`while" + }, + { + "name": "Return", + "pattern": "`return" + }, + { + "name": "New", + "pattern": "`new" + }, + { + "name": "Variable", + "pattern": "`var" + }, + { + "name": "Id", + "pattern": "`\\l(\\w|_)*" + }, + { + "name": "Num", + "pattern": "~(\\d*\\.\\d+(e(-|\\+)?\\d+)?|\\d+)" + }, + { + "name": "CommaSeparator", + "pattern": "\\s*,\\s*" + }, + { + "name": "SemiColon", + "pattern": ";" + }, + { + "name": "OpenPesoParens", + "pattern": "$\\(" + }, + { + "name": "OpenParens", + "pattern": "\\(" + }, + { + "name": "CloseParens", + "pattern": "\\)" + }, + { + "name": "Relational", + "pattern": "\\<|\\>|(\\<=)|(\\>=)" + }, + { + "name": "Equality", + "pattern": "(==)|(\\!=)" + }, + { + "name": "And", + "pattern": "&&" + }, + { + "name": "Or", + "pattern": "\\|\\|" + }, + { + "name": "BitwiseAnd", + "pattern": "&" + }, + { + "name": "BitwiseOr", + "pattern": "\\|" + }, + { + "name": "Multiplicative", + "pattern": "\\*|/|%" + }, + { + "name": "PlusEqual", + "pattern": "\\+=" + }, + { + "name": "MinusEqual", + "pattern": "-=" + }, + { + "name": "Increment", + "pattern": "\\+\\+" + }, + { + "name": "Additive", + "pattern": "\\+" + }, + { + "name": "Subtractive", + "pattern": "-" + }, + { + "name": "Equal", + "pattern": "=" + }, + { + "name": "OpenBrace", + "pattern": "\\{" + }, + { + "name": "CloseBrace", + "pattern": "\\}" + }, + { + "name": "OpenBracket", + "pattern": "\\[" + }, + { + "name": "CloseBracket", + "pattern": "\\]" + }, + { + "name": "Colon", + "pattern": ":" + }, + { + "name": "Not", + "pattern": "\\!" + }, + { + "name": "StringLiteral", + "pattern": "\"(((\\\\|\")!.)|\\\\.)*\"" + } + ], + "fragments": [ + { + "name": "Script", + "parts": [ + "[BlockOrStatement]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Multiple" + }, + { + "name": "Identifier", + "parts": [ + "Id" + ], + "partsMatchMode": "One", + "fallThroughMode": "All", + "cacheable": true + }, + { + "name": "Number", + "parts": [ + "[Negative]", + "Num" + ], + "partsMatchMode": "Ordered", + "fallThroughMode": "One" + }, + { + "name": "Negative", + "parts": [ + "Subtractive" + ], + "partsMatchMode": "One", + "minMatchedParts": 0, + "fallThroughMode": "All" + }, + { + "name": "AnonymousFunction", + "parts": [ + "[FunctionParameters]", + "[BlockOrStatement]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "clearCache": true + }, + { + "name": "NewArray", + "parts": [ + "[ArrayInitializer]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "One", + "start": "New" + }, + { + "name": "FunctionParameters", + "parts": [ + "[Identifier]" + ], + "partsDelimiter": "CommaSeparator", + "minMatchedParts": 0, + "partsMatchMode": "Multiple", + "start": "OpenPesoParens", + "end": "CloseParens" + }, + { + "name": "Block", + "parts": [ + "[BlockOrStatement]" + ], + "partsDelimiterRequired": false, + "minMatchedParts": 0, + "start": "OpenBrace", + "end": "CloseBrace", + "failureReportStrategy": 6 + }, + { + "name": "BlockOrStatement", + "parts": [ + "[WhileBlock]", + "[IfElseBlock]", + "[Block]", + "[Statement]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "IfElseBlock", + "parts": [ + "[IfStatement]", + "[OptionalElseIfStatements]", + "[OptionalElseStatement]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "OptionalElseIfStatements", + "parts": [ + "[ElseIfStatement]" + ], + "MinMatchedParts": 0, + "partsMatchMode": "Multiple", + "partsDelimiterRequired": false, + "fallThroughMode": "All" + }, + { + "name": "OptionalElseStatement", + "parts": [ + "[ElseStatement]" + ], + "MinMatchedParts": 0, + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "ElseIfStatement", + "parts": [ + "[IfStatement]" + ], + "start": "Else", + "partsMatchMode": "One" + }, + { + "name": "ElseStatement", + "parts": [ + "[BlockOrStatement]" + ], + "start": "Else", + "partsDelimiterRequired": false, + "partsMatchMode": "One" + }, + { + "name": "IfStatement", + "parts": [ + "[Condition]", + "[BlockOrStatement]" + ], + "start": "If", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "Condition", + "parts": [ + "[Evaluable]" + ], + "partsMatchMode": "One", + "start": "OpenParens", + "end": "CloseParens", + "fallThroughMode": "All" + }, + { + "name": "WhileBlock", + "parts": [ + "[Condition]", + "[BlockOrStatement]" + ], + "start": "While", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "Statement", + "parts": [ + "[StatementBody]" + ], + "partsMatchMode": "One", + "minMatchedParts": 1, + "fallThroughMode": "All", + "end": "SemiColon" + }, + { + "name": "Statements", + "parts": [ + "[Statement]" + ], + "partsMatchMode": "Multiple", + "minMatchedParts": 0, + "fallThroughMode": "All" + }, + { + "name": "OpenEndedStatements", + "parts": [ + "[StatementBody]" + ], + "partsMatchMode": "Multiple", + "partsDelimiter": "SemiColon", + "minMatchedParts": 0 + }, + { + "name": "StatementBody", + "parts": [ + "[ItemReturn]", + "Break", + "Continue", + "[DeclarationAssignment]", + "[Declaration]", + "[Assignment]", + "[DirectedValuableChain]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Declaration", + "parts": [ + "[Identifier]" + ], + "partsMatchMode": "One", + "start": "Variable" + }, + { + "name": "AssignmentTarget", + "parts": [ + "[Valuable]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Assignment", + "parts": [ + "[AssignmentTarget]", + "[AssignmentEqual]", + "[Evaluable]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "Equal", + "parts": [ + "Equal" + ], + "partsMatchMode": "One", + "isNoise": true + }, + { + "name": "AssignmentEqual", + "parts": [ + "Equal", + "PlusEqual", + "MinusEqual" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "ValuableChain", + "parts": [ + "[ValuableChainStart]", + "[OptionalChainables]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "fallThroughMode": "One" + }, + { + "name": "ValuableChainStart", + "parts": [ + "[NewArray]", + "[ParensValuable]", + "[Identifier]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Chainable", + "parts": [ + "[ValuedIndex]", + "[ArgumentValues]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Multiple", + "fallThroughMode": "All" + }, + { + "name": "OptionalChainables", + "parts": [ + "[Chainable]" + ], + "partsMatchMode": "Multiple", + "minMatchedParts": 0, + "fallThroughMode": "All" + }, + { + "name": "ItemReturn", + "parts": [ + "[Return]", + "[Evaluable]" + ], + "partsMatchMode": "Ordered", + "minMatchedParts": 1 + }, + { + "name": "Return", + "parts": [ + "Return" + ], + "partsMatchMode": "One", + "fallThroughMode": "All", + "isNoise": true + }, + { + "name": "DeclarationAssignment", + "parts": [ + "[Identifier]", + "[Equal]", + "[Evaluable]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "start": "Variable" + }, + { + "name": "Valuable", + "parts": [ + "Boolean", + "Null", + "StringLiteral", + "[Number]", + "[DirectedValuableChain]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All", + "cacheable": true + }, + { + "name": "DirectedValuableChain", + "parts": [ + "[ValuablePrefix]", + "[ValuableChain]", + "[ValuableSuffix]" + ], + "partsMatchMode": "Ordered", + "fallThroughMode": "One" + }, + { + "name": "ValuablePrefix", + "parts": [ + "[Not]", + "Increment", + "[Decrement]" + ], + "minMatchedParts": 0, + "partsMatchMode": "One", + "fallThroughMode": "Empty" + }, + { + "name": "ValuableSuffix", + "parts": [ + "Increment", + "[Decrement]" + ], + "minMatchedParts": 0, + "partsMatchMode": "One", + "fallThroughMode": "Empty" + }, + { + "name": "Decrement", + "parts": [ + "Subtractive", + "Subtractive" + ], + "partsMatchMode": "Ordered" + }, + { + "name": "Not", + "parts": [ + "Not" + ], + "partsMatchMode": "Multiple" + }, + { + "name": "ArgumentValues", + "parts": [ + "[Evaluable]" + ], + "partsDelimiter": "CommaSeparator", + "minMatchedParts": 0, + "partsMatchMode": "Multiple", + "start": "OpenParens", + "end": "CloseParens" + }, + { + "name": "Evaluable", + "parts": [ + "[AnonymousFunction]", + "[Expression]", + "[Valuable]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Expression", + "parts": [ + "[Valuable]", + "[ExpressionSuffix]" + ], + "partsMatchMode": "Ordered", + "partsDelimiterRequired": false, + "expressionMode": "BinaryTree", + "fallThroughMode": "All" + }, + { + "name": "ExpressionSuffix", + "parts": [ + "[MultiplicativeSuffix]", + "[AdditiveSuffix]", + "[SubtractiveSuffix]", + "[EqualitySuffix]", + "[RelationalSuffix]", + "[AndSuffix]", + "[OrSuffix]", + "[BitwiseAndSuffix]", + "[BitwiseOrSuffix]" + ], + "partsMatchMode": "Multiple", + "fallThroughMode": "All" + }, + { + "name": "MultiplicativeSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Multiplicative", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 1 + }, + { + "name": "AdditiveSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Additive", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 2 + }, + { + "name": "SubtractiveSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Subtractive", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 2 + }, + { + "name": "EqualitySuffix", + "parts": [ + "[Valuable]" + ], + "start": "Equality", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 4 + }, + { + "name": "RelationalSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Relational", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 3 + }, + { + "name": "AndSuffix", + "parts": [ + "[Valuable]" + ], + "start": "And", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 7 + }, + { + "name": "OrSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Or", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 8 + }, + { + "name": "BitwiseAndSuffix", + "parts": [ + "[Valuable]" + ], + "start": "BitwiseAnd", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 5 + }, + { + "name": "BitwiseOrSuffix", + "parts": [ + "[Valuable]" + ], + "start": "BitwiseOr", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 6 + }, + { + "name": "ParensValuable", + "parts": [ + "[Evaluable]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "One", + "start": "OpenParens", + "end": "CloseParens", + "cacheable": true + }, + { + "name": "ValuedIndex", + "parts": [ + "[Evaluable]" + ], + "partsDelimiterRequired": false, + "partsMatchMode": "One", + "start": "OpenBracket", + "end": "CloseBracket", + "cacheable": true + }, + { + "name": "ArrayInitializer", + "parts": [ + "[Evaluable]" + ], + "minMatchedParts": 0, + "partsMatchMode": "One", + "start": "OpenBracket", + "end": "CloseBracket", + "fallThroughMode": "All", + "cacheable": true + } + ] +} \ No newline at end of file diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Files/EbnfDefinition.json b/src/Tests/MatcherTests/Files/EbnfDefinition.json similarity index 100% rename from src/Tests/MatcherTests/Interop/Ebnf/Files/EbnfDefinition.json rename to src/Tests/MatcherTests/Files/EbnfDefinition.json diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Files/Grammars/abc-iso.ebnf b/src/Tests/MatcherTests/Files/Grammars/abc-iso.ebnf similarity index 100% rename from src/Tests/MatcherTests/Interop/Ebnf/Files/Grammars/abc-iso.ebnf rename to src/Tests/MatcherTests/Files/Grammars/abc-iso.ebnf diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Files/Grammars/abc-w3c.ebnf b/src/Tests/MatcherTests/Files/Grammars/abc-w3c.ebnf similarity index 100% rename from src/Tests/MatcherTests/Interop/Ebnf/Files/Grammars/abc-w3c.ebnf rename to src/Tests/MatcherTests/Files/Grammars/abc-w3c.ebnf diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Files/Grammars/abc.bnf b/src/Tests/MatcherTests/Files/Grammars/abc.bnf similarity index 100% rename from src/Tests/MatcherTests/Interop/Ebnf/Files/Grammars/abc.bnf rename to src/Tests/MatcherTests/Files/Grammars/abc.bnf diff --git a/src/Tests/MatcherTests/Files/LazyIndexTestDefinition.json b/src/Tests/MatcherTests/Files/LazyIndexTestDefinition.json new file mode 100644 index 0000000..d94c7e8 --- /dev/null +++ b/src/Tests/MatcherTests/Files/LazyIndexTestDefinition.json @@ -0,0 +1,754 @@ +{ + "name": "StaxeTestSimpleLang", + "startingFragment": "Script", + "indexingMode": "Lazy", + "patterns": [ + { + "name": "Null", + "pattern": "`null" + }, + { + "name": "Boolean", + "pattern": "`true|false" + }, + { + "name": "Break", + "pattern": "`break" + }, + { + "name": "Continue", + "pattern": "`continue" + }, + { + "name": "Else", + "pattern": "`else" + }, + { + "name": "If", + "pattern": "`if" + }, + { + "name": "While", + "pattern": "`while" + }, + { + "name": "Return", + "pattern": "`return" + }, + { + "name": "New", + "pattern": "`new" + }, + { + "name": "Variable", + "pattern": "`var" + }, + { + "name": "Id", + "pattern": "`\\l(\\w|_)*" + }, + { + "name": "Num", + "pattern": "~(\\d*\\.\\d+(e(-|\\+)?\\d+)?|\\d+)" + }, + { + "name": "CommaSeparator", + "pattern": "\\s*,\\s*" + }, + { + "name": "SemiColon", + "pattern": ";" + }, + { + "name": "OpenPesoParens", + "pattern": "$\\(" + }, + { + "name": "OpenParens", + "pattern": "\\(" + }, + { + "name": "CloseParens", + "pattern": "\\)" + }, + { + "name": "Relational", + "pattern": "\\<|\\>|(\\<=)|(\\>=)" + }, + { + "name": "Equality", + "pattern": "(==)|(\\!=)" + }, + { + "name": "And", + "pattern": "&&" + }, + { + "name": "Or", + "pattern": "\\|\\|" + }, + { + "name": "BitwiseAnd", + "pattern": "&" + }, + { + "name": "BitwiseOr", + "pattern": "\\|" + }, + { + "name": "Multiplicative", + "pattern": "\\*|/|%" + }, + { + "name": "PlusEqual", + "pattern": "\\+=" + }, + { + "name": "MinusEqual", + "pattern": "-=" + }, + { + "name": "Increment", + "pattern": "\\+\\+" + }, + { + "name": "Additive", + "pattern": "\\+" + }, + { + "name": "Subtractive", + "pattern": "-" + }, + { + "name": "Equal", + "pattern": "=" + }, + { + "name": "OpenBrace", + "pattern": "\\{" + }, + { + "name": "CloseBrace", + "pattern": "\\}" + }, + { + "name": "OpenBracket", + "pattern": "\\[" + }, + { + "name": "CloseBracket", + "pattern": "\\]" + }, + { + "name": "Colon", + "pattern": ":" + }, + { + "name": "Not", + "pattern": "\\!" + }, + { + "name": "StringLiteral", + "pattern": "\"(((\\\\|\")!.)|\\\\.)*\"" + }, + { + "name": "Whitespace", + "pattern": "\\s+" + } + ], + "fragments": [ + { + "name": "Script", + "parts": [ + "[BlockOrStatement]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Multiple", + "partsPadding": "Whitespace" + }, + { + "name": "Identifier", + "parts": [ + "Id" + ], + "partsMatchMode": "One", + "fallThroughMode": "All", + "cacheable": true + }, + { + "name": "Number", + "parts": [ + "[Negative]", + "Num" + ], + "partsMatchMode": "Ordered", + "fallThroughMode": "One" + }, + { + "name": "Negative", + "parts": [ + "Subtractive" + ], + "partsMatchMode": "One", + "minMatchedParts": 0, + "fallThroughMode": "All" + }, + { + "name": "AnonymousFunction", + "parts": [ + "[FunctionParameters]", + "[BlockOrStatement]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "clearCache": true + }, + { + "name": "NewArray", + "parts": [ + "[ArrayInitializer]" + ], + "partsDelimiter": "Whitespace", + "partsPadding": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "One", + "start": "New" + }, + { + "name": "FunctionParameters", + "parts": [ + "[Identifier]" + ], + "partsDelimiter": "CommaSeparator", + "minMatchedParts": 0, + "partsMatchMode": "Multiple", + "partsPadding": "Whitespace", + "start": "OpenPesoParens", + "end": "CloseParens" + }, + { + "name": "Block", + "parts": [ + "[BlockOrStatement]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "minMatchedParts": 0, + "partsMatchMode": "Multiple", + "partsPadding": "Whitespace", + "start": "OpenBrace", + "end": "CloseBrace", + "failureReportStrategy": 6 + }, + { + "name": "BlockOrStatement", + "parts": [ + "[WhileBlock]", + "[IfElseBlock]", + "[Block]", + "[Statement]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "IfElseBlock", + "parts": [ + "[IfStatement]", + "[OptionalElseIfStatements]", + "[OptionalElseStatement]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "OptionalElseIfStatements", + "parts": [ + "[ElseIfStatement]" + ], + "MinMatchedParts": 0, + "partsMatchMode": "Multiple", + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "fallThroughMode": "All" + }, + { + "name": "OptionalElseStatement", + "parts": [ + "[ElseStatement]" + ], + "MinMatchedParts": 0, + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "ElseIfStatement", + "parts": [ + "[IfStatement]" + ], + "start": "Else", + "partsPadding": "Whitespace", + "partsMatchMode": "One" + }, + { + "name": "ElseStatement", + "parts": [ + "[BlockOrStatement]" + ], + "start": "Else", + "partsPadding": "Whitespace", + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "One" + }, + { + "name": "IfStatement", + "parts": [ + "[Condition]", + "[BlockOrStatement]" + ], + "start": "If", + "partsPadding": "Whitespace", + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "Condition", + "parts": [ + "[Evaluable]" + ], + "partsMatchMode": "One", + "partsPadding": "Whitespace", + "start": "OpenParens", + "end": "CloseParens", + "fallThroughMode": "All" + }, + { + "name": "WhileBlock", + "parts": [ + "[Condition]", + "[BlockOrStatement]" + ], + "start": "While", + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "partsPadding": "Whitespace" + }, + { + "name": "Statement", + "parts": [ + "[StatementBody]" + ], + "partsMatchMode": "One", + "minMatchedParts": 1, + "fallThroughMode": "All", + "end": "SemiColon" + }, + { + "name": "Statements", + "parts": [ + "[Statement]" + ], + "partsMatchMode": "Multiple", + "minMatchedParts": 0, + "fallThroughMode": "All" + }, + { + "name": "OpenEndedStatements", + "parts": [ + "[StatementBody]" + ], + "partsMatchMode": "Multiple", + "partsDelimiter": "SemiColon", + "minMatchedParts": 0 + }, + { + "name": "StatementBody", + "parts": [ + "[ItemReturn]", + "Break", + "Continue", + "[DeclarationAssignment]", + "[Declaration]", + "[Assignment]", + "[DirectedValuableChain]" + ], + "partsMatchMode": "One", + "partsPadding": "Whitespace", + "fallThroughMode": "All" + }, + { + "name": "Declaration", + "parts": [ + "[Identifier]" + ], + "partsMatchMode": "One", + "partsPadding": "Whitespace", + "start": "Variable" + }, + { + "name": "AssignmentTarget", + "parts": [ + "[Valuable]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Assignment", + "parts": [ + "[AssignmentTarget]", + "[AssignmentEqual]", + "[Evaluable]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered" + }, + { + "name": "Equal", + "parts": [ + "Equal" + ], + "partsMatchMode": "One", + "isNoise": true + }, + { + "name": "AssignmentEqual", + "parts": [ + "Equal", + "PlusEqual", + "MinusEqual" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "ValuableChain", + "parts": [ + "[ValuableChainStart]", + "[OptionalChainables]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "fallThroughMode": "One" + }, + { + "name": "ValuableChainStart", + "parts": [ + "[NewArray]", + "[ParensValuable]", + "[Identifier]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Chainable", + "parts": [ + "[ValuedIndex]", + "[ArgumentValues]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Multiple", + "fallThroughMode": "All" + }, + { + "name": "OptionalChainables", + "parts": [ + "[Chainable]" + ], + "partsPadding": "Whitespace", + "partsDelimiter": "Whitespace", + "partsMatchMode": "Multiple", + "minMatchedParts": 0, + "fallThroughMode": "All" + }, + { + "name": "ItemReturn", + "parts": [ + "[Return]", + "[Evaluable]" + ], + "partsDelimiter": "Whitespace", + "partsMatchMode": "Ordered", + "minMatchedParts": 1 + }, + { + "name": "Return", + "parts": [ + "Return" + ], + "partsMatchMode": "One", + "fallThroughMode": "All", + "isNoise": true + }, + { + "name": "DeclarationAssignment", + "parts": [ + "[Identifier]", + "[Equal]", + "[Evaluable]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "Ordered", + "partsPadding": "Whitespace", + "start": "Variable" + }, + { + "name": "Valuable", + "parts": [ + "Boolean", + "Null", + "StringLiteral", + "[Number]", + "[DirectedValuableChain]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All", + "cacheable": true + }, + { + "name": "DirectedValuableChain", + "parts": [ + "[ValuablePrefix]", + "[ValuableChain]", + "[ValuableSuffix]" + ], + "partsMatchMode": "Ordered", + "fallThroughMode": "One" + }, + { + "name": "ValuablePrefix", + "parts": [ + "[Not]", + "Increment", + "[Decrement]" + ], + "minMatchedParts": 0, + "partsMatchMode": "One", + "fallThroughMode": "Empty" + }, + { + "name": "ValuableSuffix", + "parts": [ + "Increment", + "[Decrement]" + ], + "minMatchedParts": 0, + "partsMatchMode": "One", + "fallThroughMode": "Empty" + }, + { + "name": "Decrement", + "parts": [ + "Subtractive", + "Subtractive" + ], + "partsMatchMode": "Ordered" + }, + { + "name": "Not", + "parts": [ + "Not" + ], + "partsMatchMode": "Multiple" + }, + { + "name": "ArgumentValues", + "parts": [ + "[Evaluable]" + ], + "partsDelimiter": "CommaSeparator", + "minMatchedParts": 0, + "partsMatchMode": "Multiple", + "partsPadding": "Whitespace", + "start": "OpenParens", + "end": "CloseParens" + }, + { + "name": "Evaluable", + "parts": [ + "[AnonymousFunction]", + "[Expression]", + "[Valuable]" + ], + "partsMatchMode": "One", + "fallThroughMode": "All" + }, + { + "name": "Expression", + "parts": [ + "[Valuable]", + "[ExpressionSuffix]" + ], + "partsMatchMode": "Ordered", + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "expressionMode": "BinaryTree", + "fallThroughMode": "All" + }, + { + "name": "ExpressionSuffix", + "parts": [ + "[MultiplicativeSuffix]", + "[AdditiveSuffix]", + "[SubtractiveSuffix]", + "[EqualitySuffix]", + "[RelationalSuffix]", + "[AndSuffix]", + "[OrSuffix]", + "[BitwiseAndSuffix]", + "[BitwiseOrSuffix]" + ], + "partsMatchMode": "Multiple", + "fallThroughMode": "All" + }, + { + "name": "MultiplicativeSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Multiplicative", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 1 + }, + { + "name": "AdditiveSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Additive", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 2 + }, + { + "name": "SubtractiveSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Subtractive", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 2 + }, + { + "name": "EqualitySuffix", + "parts": [ + "[Valuable]" + ], + "start": "Equality", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 4 + }, + { + "name": "RelationalSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Relational", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 3 + }, + { + "name": "AndSuffix", + "parts": [ + "[Valuable]" + ], + "start": "And", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 7 + }, + { + "name": "OrSuffix", + "parts": [ + "[Valuable]" + ], + "start": "Or", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 8 + }, + { + "name": "BitwiseAndSuffix", + "parts": [ + "[Valuable]" + ], + "start": "BitwiseAnd", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 5 + }, + { + "name": "BitwiseOrSuffix", + "parts": [ + "[Valuable]" + ], + "start": "BitwiseOr", + "partsPadding": "Whitespace", + "partsMatchMode": "One", + "boundsAsParts": true, + "expressionOrder": 6 + }, + { + "name": "ParensValuable", + "parts": [ + "[Evaluable]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "One", + "partsPadding": "Whitespace", + "start": "OpenParens", + "end": "CloseParens", + "cacheable": true + }, + { + "name": "ValuedIndex", + "parts": [ + "[Evaluable]" + ], + "partsDelimiter": "Whitespace", + "partsDelimiterRequired": false, + "partsMatchMode": "One", + "partsPadding": "Whitespace", + "start": "OpenBracket", + "end": "CloseBracket", + "cacheable": true + }, + { + "name": "ArrayInitializer", + "parts": [ + "[Evaluable]" + ], + "minMatchedParts": 0, + "partsMatchMode": "One", + "partsPadding": "Whitespace", + "start": "OpenBracket", + "end": "CloseBracket", + "fallThroughMode": "All", + "cacheable": true + } + ] +} \ No newline at end of file diff --git a/src/Tests/MatcherTests/Files/Parsables/JibberishScript.lz b/src/Tests/MatcherTests/Files/Parsables/JibberishScript.lz new file mode 100644 index 0000000..c690bdf --- /dev/null +++ b/src/Tests/MatcherTests/Files/Parsables/JibberishScript.lz @@ -0,0 +1,89 @@ +var fibonacci = $(n) { + var a = 0; + var b = 1; + var i = 0; + while (i < n) { + i = 1 + i; + var temp = a; + a = b; + b = temp + b; + } + var number1 = 10; + var number2 = 10; + var number3 = number1-- - --number2; + var conditionTest = $() { + var greeting; + var condition = true; + + if (condition) { + greeting = "Hello"; + } + else if (condition) { + greeting = "Goodbye"; + } + else { + greeting = "Greetings"; + } + var value = 1; + var i = 1; + while (i < 10) { + i++; + if (i == 5) { + continue; + } + value++; + } + if (!condition) { + greeting = greeting + " cruel"; + } + else if (true) { + greeting = greeting + " beautiful"; + } + else { + greeting = greeting + " great"; + } + + if (false) { + greeting = greeting + " planet."; + } + else if (!condition) { + greeting = greeting + " earth."; + } + else { + greeting = greeting + " world."; + } + }; + return a; +}; + +var getFib = $(n) { + var i = 0; + var fibonacciSum = 0; + while (i < n) { + var array = new []; + array[0] = 20; + array[1] = "world"; + array[2] = true; + array[3] = "hello"; + array["ab"] = array[1]; + array[1] = 10.5; + var combo = "" + array[0] + array[1] + array[2] + array[3] + array["ab"]; + i = i + 1; + var outsider = 50; + var modifier = $() { + var insider = outsider; + outsider = 60; + return insider; + }; + var number = 10; + number += 20; + var insider = modifier(); + var number1 = 10; + var number2 = 10; + var number3 = number1++ + ++number2; + fibonacciSum = fibonacciSum + fibonacci(i); + } + return fibonacciSum; +}; + +getFib(44); \ No newline at end of file diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Files/Parsables/abc.txt b/src/Tests/MatcherTests/Files/Parsables/abc.txt similarity index 100% rename from src/Tests/MatcherTests/Interop/Ebnf/Files/Parsables/abc.txt rename to src/Tests/MatcherTests/Files/Parsables/abc.txt diff --git a/src/Tests/MatcherTests/Interop/Json/Tests/DefinitionConverterTests.cs b/src/Tests/MatcherTests/Interop/Json/Tests/DefinitionConverterTests.cs deleted file mode 100644 index 331e0c6..0000000 --- a/src/Tests/MatcherTests/Interop/Json/Tests/DefinitionConverterTests.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using Synfron.Staxe.Matcher.Input; -using Synfron.Staxe.Matcher.Interop.Model; -using System.IO; -using Xunit; - -namespace MatcherTests.Interop.Model.Tests -{ - public class DefinitionConverterTests - { - [Fact] - public void DefinitionConverter_Convert() - { - string definition = File.ReadAllText("Interop/Json/Files/TestDefinition.json"); - LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); - - LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); - LanguageMatcherDefinition newLanguageDefinition = DefinitionConverter.Convert(languageMatcher); - - Assert.Equal(languageMatcherDefinition, newLanguageDefinition, new LanguageMatcherDefinitionComparer()); - } - } -} diff --git a/src/Tests/MatcherTests/Interop/Json/Tests/FragmentMatcherDefintionComparer.cs b/src/Tests/MatcherTests/Interop/Json/Tests/FragmentMatcherDefintionComparer.cs deleted file mode 100644 index 24fae49..0000000 --- a/src/Tests/MatcherTests/Interop/Json/Tests/FragmentMatcherDefintionComparer.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Synfron.Staxe.Matcher.Interop.Model; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace MatcherTests.Interop.Model.Tests -{ - public class FragmentMatcherDefintionComparer : IEqualityComparer - { - public bool Equals(FragmentMatcherDefinition x, FragmentMatcherDefinition y) - { - return Enumerable.SequenceEqual(x.Parts, y.Parts) - && x.BoundsAsParts == y.BoundsAsParts - && x.Cacheable == y.Cacheable - && x.ClearCache == y.ClearCache - && x.DiscardBounds == y.DiscardBounds - && x.End == y.End - && x.ExpressionMode == y.ExpressionMode - && x.ExpressionOrder == y.ExpressionOrder - && x.FallThroughMode == y.FallThroughMode - && x.IsNoise == y.IsNoise - && x.MinMatchedParts == y.MinMatchedParts - && x.Name == y.Name - && x.Negate == y.Negate - && x.PartsDelimiter == y.PartsDelimiter - && x.PartsDelimiterRequired == y.PartsDelimiterRequired - && x.PartsMatchMode == y.PartsMatchMode - && x.PartsPadding == y.PartsPadding - && x.Start == y.Start; - } - - public int GetHashCode(FragmentMatcherDefinition obj) => throw new NotImplementedException(); - } -} diff --git a/src/Tests/MatcherTests/Interop/Json/Tests/LanguageMatcherDefinitionComparer.cs b/src/Tests/MatcherTests/Interop/Json/Tests/LanguageMatcherDefinitionComparer.cs deleted file mode 100644 index aa23327..0000000 --- a/src/Tests/MatcherTests/Interop/Json/Tests/LanguageMatcherDefinitionComparer.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Synfron.Staxe.Matcher.Interop.Model; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace MatcherTests.Interop.Model.Tests -{ - public class LanguageMatcherDefinitionComparer : IEqualityComparer - { - public bool Equals(LanguageMatcherDefinition x, LanguageMatcherDefinition y) - { - return Enumerable.SequenceEqual(x.Fragments, y.Fragments, new FragmentMatcherDefintionComparer()) - && Enumerable.SequenceEqual(x.Patterns, y.Patterns, new PatternMatcherDefinitionComparer()) - && x.IndexingMode == y.IndexingMode - && x.LogMatches == y.LogMatches - && x.Name == y.Name - && x.StartingFragment == y.StartingFragment; - } - - public int GetHashCode(LanguageMatcherDefinition obj) => throw new NotImplementedException(); - } -} diff --git a/src/Tests/MatcherTests/Interop/Json/Tests/PatternMatcherDefinitionComparer.cs b/src/Tests/MatcherTests/Interop/Json/Tests/PatternMatcherDefinitionComparer.cs deleted file mode 100644 index d801d22..0000000 --- a/src/Tests/MatcherTests/Interop/Json/Tests/PatternMatcherDefinitionComparer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Synfron.Staxe.Matcher.Input.Patterns; -using Synfron.Staxe.Matcher.Interop.Model; -using System; -using System.Collections.Generic; - -namespace MatcherTests.Interop.Model.Tests -{ - public class PatternMatcherDefinitionComparer : IEqualityComparer - { - public bool Equals(PatternMatcherDefinition x, PatternMatcherDefinition y) - { - return x.IsNoise == y.IsNoise - && x.Mergable == y.Mergable - && x.Name == y.Name - && PatternReader.Parse(x.Pattern).ToString() == PatternReader.Parse(y.Pattern).ToString(); - } - - public int GetHashCode(PatternMatcherDefinition obj) => throw new NotImplementedException(); - } -} diff --git a/src/Tests/MatcherTests/MatcherTests.csproj b/src/Tests/MatcherTests/MatcherTests.csproj index b61fd20..9a2f810 100644 --- a/src/Tests/MatcherTests/MatcherTests.csproj +++ b/src/Tests/MatcherTests/MatcherTests.csproj @@ -9,37 +9,42 @@ - - - - - - - - - + + + + + + + + - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + PreserveNewest - + + PreserveNewest + + + PreserveNewest + + PreserveNewest @@ -58,6 +63,12 @@ + + + + PreserveNewest + + diff --git a/src/Tests/MatcherTests/Shared/GenerationType.cs b/src/Tests/MatcherTests/Shared/GenerationType.cs new file mode 100644 index 0000000..53f4161 --- /dev/null +++ b/src/Tests/MatcherTests/Shared/GenerationType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MatcherTests.Shared +{ + public enum GenerationType + { + PreGen, + PostGen + } +} diff --git a/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs b/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs new file mode 100644 index 0000000..6e4986a --- /dev/null +++ b/src/Tests/MatcherTests/Shared/LanguageMatchEngineFactory.cs @@ -0,0 +1,35 @@ +using Synfron.Staxe.Matcher; +using Synfron.Staxe.Matcher.Input; +using Synfron.Staxe.Matcher.Interop; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace MatcherTests.Shared +{ + public class LanguageMatchEngineFactory + { + public static ILanguageMatchEngine Get(GenerationType generationType, LanguageMatcher languageMatcher) + { + switch (generationType) + { + case GenerationType.PreGen: + { + MatcherClassGenerator generator = new MatcherClassGenerator(languageMatcher); + Assembly assembly = generator.GetAssembly(); + return (ILanguageMatchEngine)Activator.CreateInstance(assembly.GetType($"Synfron.Staxe.Matcher.{languageMatcher.Name}")); + } + case GenerationType.PostGen: + { + return LanguageMatchEngine.Build(languageMatcher); + } + default: + throw new NotSupportedException($"Unsupported generation type '{generationType.ToString()}'"); + } + + } + } +} diff --git a/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs b/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs new file mode 100644 index 0000000..850abdf --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Actions/MatcherActionTests.cs @@ -0,0 +1,167 @@ +using Synfron.Staxe.Matcher; +using Synfron.Staxe.Matcher.Data; +using Synfron.Staxe.Matcher.Input.Actions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace MatcherTests.Tests.Actions +{ + public class MatcherActionTests + { + [Theory] + [InlineData(AssertType.Equals, "abc", "abc", true)] + [InlineData(AssertType.Equals, "abc", "abd", false)] + [InlineData(AssertType.Equals, 1, 1, true)] + [InlineData(AssertType.Equals, 1, 2, false)] + [InlineData(AssertType.NotEquals, "abc", "abc", false)] + [InlineData(AssertType.NotEquals, "abc", "abd", true)] + [InlineData(AssertType.NotEquals, 1, 1, false)] + [InlineData(AssertType.NotEquals, 1, 2, true)] + [InlineData(AssertType.GreaterThan, 3, 2, true)] + [InlineData(AssertType.GreaterThan, 2, 2, false)] + [InlineData(AssertType.GreaterThanOrEquals, 3, 2, true)] + [InlineData(AssertType.GreaterThanOrEquals, 2, 2, true)] + [InlineData(AssertType.GreaterThanOrEquals, 1, 2, false)] + [InlineData(AssertType.LessThan, 1, 2, true)] + [InlineData(AssertType.LessThan, 2, 2, false)] + [InlineData(AssertType.LessThanOrEquals, 1, 2, true)] + [InlineData(AssertType.LessThanOrEquals, 2, 2, true)] + [InlineData(AssertType.LessThanOrEquals, 3, 2, false)] + [InlineData(AssertType.Contains, 12, 2, true)] + [InlineData(AssertType.Contains, "abc", "b", true)] + [InlineData(AssertType.Contains, "12", 2, true)] + [InlineData(AssertType.Contains, 12, "2", true)] + [InlineData(AssertType.Contains, 12, 4, false)] + [InlineData(AssertType.Contains, "abc", "d", false)] + [InlineData(AssertType.Contains, "12", 4, false)] + [InlineData(AssertType.Contains, 12, "4", false)] + [InlineData(AssertType.StartsWith, 12, 1, true)] + [InlineData(AssertType.StartsWith, "abc", "a", true)] + [InlineData(AssertType.StartsWith, "12", 1, true)] + [InlineData(AssertType.StartsWith, 12, "1", true)] + [InlineData(AssertType.StartsWith, 12, 2, false)] + [InlineData(AssertType.StartsWith, "abc", "b", false)] + [InlineData(AssertType.StartsWith, "12", 2, false)] + [InlineData(AssertType.StartsWith, 12, "2", false)] + [InlineData(AssertType.EndsWith, 12, 2, true)] + [InlineData(AssertType.EndsWith, "abc", "c", true)] + [InlineData(AssertType.EndsWith, "12", 2, true)] + [InlineData(AssertType.EndsWith, 12, "2", true)] + [InlineData(AssertType.EndsWith, 12, 1, false)] + [InlineData(AssertType.EndsWith, "abc", "b", false)] + [InlineData(AssertType.EndsWith, "12", 1, false)] + [InlineData(AssertType.EndsWith, 12, "1", false)] + [InlineData(AssertType.MatchesPattern, "12", "\\d+", true)] + [InlineData(AssertType.MatchesPattern, 12, "\\d+", true)] + [InlineData(AssertType.MatchesPattern, "12", "\\l", false)] + [InlineData(AssertType.MatchesPattern, 12, "\\l", false)] + public void AssertMatcherAction_Perform(AssertType assertType, object firstValue, object secondValue, bool expected) + { + Span blobDatas = new Span(new BlobData[] + { + new BlobData + { + Value = firstValue + }, + new BlobData + { + Value = secondValue + } + }); + AssertMatcherAction action = new AssertMatcherAction + { + Name = "Test", + FirstBlobId = 0, + SecondBlobId = 1, + Assert = assertType + }; + bool result = action.Perform(blobDatas, new IMatchData[0]); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData(VariableUpdateAction.Set, "abc", "cba", "cba")] + [InlineData(VariableUpdateAction.Set, "abc", 1, 1)] + [InlineData(VariableUpdateAction.Set, 1, "cba", "cba")] + [InlineData(VariableUpdateAction.Add, 1, 2, 3.0)] + [InlineData(VariableUpdateAction.Add, "1", "2", 3.0)] + [InlineData(VariableUpdateAction.Add, "abc", "cba", "abc")] + [InlineData(VariableUpdateAction.Subtract, 3, 2, 1.0)] + [InlineData(VariableUpdateAction.Subtract, "3", "2", 1.0)] + [InlineData(VariableUpdateAction.Concat, 1, 2, "12")] + [InlineData(VariableUpdateAction.Concat, "abc", "1", "abc1")] + [InlineData(VariableUpdateAction.Concat, "abc", 1, "abc1")] + [InlineData(VariableUpdateAction.Concat, 1, "cba", "1cba")] + [InlineData(VariableUpdateAction.Concat, "abc", "cba", "abccba")] + [InlineData(VariableUpdateAction.Remove, "abc", "b", "ac")] + [InlineData(VariableUpdateAction.Remove, "abc1", "1", "abc")] + [InlineData(VariableUpdateAction.Remove, "abc1", 1, "abc")] + public void UpdateVariableMatcherAction_Perform(VariableUpdateAction updateAction, object firstValue, object secondValue, object expected) + { + Span blobDatas = new Span(new BlobData[] + { + new BlobData + { + Value = firstValue + }, + new BlobData + { + Value = secondValue + } + }); + UpdateVariableMatcherAction action = new UpdateVariableMatcherAction + { + Name = "Test", + TargetBlobId = 0, + SourceBlobId = 1, + Change = updateAction + }; + bool result = action.Perform(blobDatas, Array.Empty()); + Assert.True(result); + Assert.Equal(expected, blobDatas[0].Value); + } + + [Theory] + [InlineData(VariableValueSource.StringPartsText, "abcd")] + [InlineData(VariableValueSource.StringPartsLength, 4)] + [InlineData(VariableValueSource.PartsCount, 3)] + [InlineData(VariableValueSource.PartsLength, 14)] + [InlineData(VariableValueSource.PartsText, "abcTestd")] + [InlineData(VariableValueSource.Value, "abc")] + public void CreateVariableMatcherAction_Perform(VariableValueSource source, object expected) + { + Span blobDatas = new Span(new BlobData[1]); + CreateVariableMatcherAction action = new CreateVariableMatcherAction + { + Name = "Test", + Value = "abc", + Source = source + }; + IMatchData[] matchDatas = new IMatchData[] + { + new StringMatchData + { + Length = 3, + Text = "abc" + }, + new FragmentMatchData + { + Name = "Test", + Length = 10 + }, + new StringMatchData + { + Length = 1, + Text = "d" + } + }; + bool result = action.Perform(blobDatas, matchDatas); + Assert.True(result); + Assert.Equal(expected, blobDatas[0].Value); + } + } +} diff --git a/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs b/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs new file mode 100644 index 0000000..cf565b1 --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Engine/EngineTests.cs @@ -0,0 +1,1174 @@ +using MatcherTests.Shared; +using Newtonsoft.Json; +using Synfron.Staxe.Matcher; +using Synfron.Staxe.Matcher.Input; +using Synfron.Staxe.Matcher.Interop.Model; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; +using Xunit; + +namespace MatcherTests.Tests.Engine +{ + public class PostGen_EngineTests : EngineTests + { + public override GenerationType GenerationType => GenerationType.PostGen; + } + + public class PreGen_EngineTests : EngineTests + { + public override GenerationType GenerationType => GenerationType.PreGen; + } + + public abstract class EngineTests + { + + public abstract GenerationType GenerationType { get; } + + [Fact] + public void LazyIndexTest() + { + string definition = File.ReadAllText("Files/LazyIndexTestDefinition.json"); + string script = File.ReadAllText("Files/Parsables/JibberishScript.lz"); + LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + } + + [Fact] + public void EagerIndexTest() + { + string definition = File.ReadAllText("Files/EagerIndexTestDefinition2.json"); + string script = File.ReadAllText("Files/Parsables/JibberishScript.lz"); + LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script.TrimEnd()); + + Assert.True(result.Success); + } + + [Fact] + public void FragmentPatternMatcherTest() + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = IndexingMode.Eager, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "FragB", + Fragment = "Bs" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b", + IsAuxiliary = true + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "A", "[Bs]", "C" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B", "B" }, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"abbc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"abbc", result.MatchData.ToXml())); + } + + [Fact] + public void FragmentPatternMatcherNoiseTest() + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = IndexingMode.Eager, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "FragB", + Fragment = "Bs", + IsNoise = true + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b", + IsAuxiliary = true + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B", "B" }, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"ac"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void SimpleTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "A", "B", "C" }, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"abc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"abc", result.MatchData.ToXml())); + } + + [Fact] + public void PatternNoiseTest() + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = IndexingMode.Eager, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b", + IsNoise = true + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"ac"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); + } + + [Fact] + public void MergableTest() + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = IndexingMode.Eager, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a+", + Mergable = true + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b", + IsNoise = true + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + }, + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"abac"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"aac", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void BoundsAsPartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + }, + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Start = "A", + Parts = new [] { "B" }, + End = "C", + BoundsAsParts = true, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"abc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"abc", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void BoundsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + }, + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Start = "A", + Parts = new [] { "B" }, + End = "C", + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"abc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"b", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void DiscardBoundsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + }, + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Start = "A", + Parts = new [] { "A" ,"B", "C" }, + DiscardBounds = true, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"abc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"abc", result.MatchData.ToXml())); + } + + [Fact] + public void PreMatchSkipAuxiliaryTest() + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = IndexingMode.Eager, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "aB", + Pattern = "b", + IsAuxiliary = true + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "A", "aB", "C", "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"abc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"abc", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void LessThanMinMatchedPartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[MoreBs]", "[Bs]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "MoreBs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple, + MinMatchedParts = 3 + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"bb"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void EqualToMinMatchedMultiplePartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[MoreBs]", "[Bs]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "MoreBs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple, + MinMatchedParts = 2 + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"bb"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void EqualToMinMatchedOrderedPartsTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[MoreBs]", "[Bs]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "MoreBs", + Parts = new [] { "B", "B", "B", "B" }, + PartsMatchMode = MatchMode.Ordered, + MinMatchedParts = 2 + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple + } + } + }; + string script = @"bb"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bb", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void NegateTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition + { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[NotBC]", "[NotCB]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "NotBC", + Parts = new [] { "[NotB]", "C" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "NotB", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.One, + Negate = true + }, + new FragmentMatcherDefinition + { + Name = "NotCB", + Parts = new [] { "[NotC]", "B" }, + PartsMatchMode = MatchMode.Ordered + }, + new FragmentMatcherDefinition + { + Name = "NotC", + Parts = new [] { "C" }, + PartsMatchMode = MatchMode.One, + Negate = true + } + } + }; + string script = @"b"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"b", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void PartsDelimiterNotRequiredTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[WithDelimiter]", "[NoDelimiter]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "WithDelimiter", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered, + PartsDelimiter = "B" + }, + new FragmentMatcherDefinition + { + Name = "NoDelimiter", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered, + PartsDelimiterRequired = false + } + } + }; + string script = @"ac"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void PartsDelimiterRequiredTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[WithDelimiter]", "[NoDelimiter]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "WithDelimiter", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered, + PartsDelimiter = "B" + }, + new FragmentMatcherDefinition + { + Name = "NoDelimiter", + Parts = new [] { "A", "C" }, + PartsMatchMode = MatchMode.Ordered + } + } + }; + string script = @"abc"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"ac", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void FallThroughOneModeTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[AB]" }, + PartsMatchMode = MatchMode.Multiple + }, + new FragmentMatcherDefinition + { + Name = "AB", + Parts = new [] { "A", "B" }, + PartsMatchMode = MatchMode.Ordered, + FallThroughMode = FallThroughMode.One, + MinMatchedParts = 1 + }, + } + }; + string script = @"aaba"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"aaba", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void FallThroughAllModeTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[AB]" }, + PartsMatchMode = MatchMode.One + }, + new FragmentMatcherDefinition + { + Name = "AB", + Parts = new [] { "A", "B" }, + PartsMatchMode = MatchMode.Multiple, + FallThroughMode = FallThroughMode.All + }, + } + }; + string script = @"abab"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"abab", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void PartsPaddingTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "A", + Pattern = "a" + }, + new PatternMatcherDefinition + { + Name = "B", + Pattern = "b" + }, + new PatternMatcherDefinition + { + Name = "C", + Pattern = "c" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[Bs]", "[Cs]" }, + PartsMatchMode = MatchMode.Multiple + }, + new FragmentMatcherDefinition + { + Name = "Bs", + Parts = new [] { "B" }, + PartsMatchMode = MatchMode.Multiple, + PartsPadding = "A" + }, + new FragmentMatcherDefinition + { + Name = "Cs", + Parts = new [] { "C" }, + PartsMatchMode = MatchMode.Multiple, + PartsPadding = "A" + } + } + }; + string script = @"abbaccbba"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"bbccbb", result.MatchData.ToXml())); + } + + [Theory] + [InlineData(IndexingMode.Eager)] + [InlineData(IndexingMode.Lazy)] + [InlineData(IndexingMode.None)] + public void BinaryTreeExpressionTest(IndexingMode indexingMode) + { + LanguageMatcherDefinition languageMatcherDefinition = new LanguageMatcherDefinition + { + Name = "Test", + StartingFragment = "Start", + IndexingMode = indexingMode, + Patterns = new PatternMatcherDefinition[] + { + new PatternMatcherDefinition { + Name = "E", + Pattern = "^" + }, + new PatternMatcherDefinition + { + Name = "M", + Pattern = "\\*" + }, + new PatternMatcherDefinition + { + Name = "D", + Pattern = "/" + }, + new PatternMatcherDefinition + { + Name = "A", + Pattern = "\\+" + }, + new PatternMatcherDefinition + { + Name = "S", + Pattern = "-" + }, + new PatternMatcherDefinition + { + Name = "Number", + Pattern = "2" + } + }, + Fragments = new FragmentMatcherDefinition[] + { + new FragmentMatcherDefinition + { + Name = "Start", + Parts = new [] { "[Exponent]", "[Multiplication]", "[Division]", "[Addition]", "[Subtraction]", "Number" }, + ExpressionMode = ExpressionMode.BinaryTree + }, + new FragmentMatcherDefinition + { + Name = "Exponent", + Parts = new [] { "E", "Number" }, + PartsMatchMode = MatchMode.Ordered, + ExpressionOrder = 1 + }, + new FragmentMatcherDefinition + { + Name = "Multiplication", + Parts = new [] { "M", "Number" }, + PartsMatchMode = MatchMode.Ordered, + ExpressionOrder = 2 + }, + new FragmentMatcherDefinition + { + Name = "Division", + Parts = new [] { "D", "Number" }, + PartsMatchMode = MatchMode.Ordered, + ExpressionOrder = 3 + }, + new FragmentMatcherDefinition + { + Name = "Addition", + Parts = new [] { "A", "Number" }, + PartsMatchMode = MatchMode.Ordered, + ExpressionOrder = 4 + }, + new FragmentMatcherDefinition + { + Name = "Subtraction", + Parts = new [] { "S", "Number" }, + PartsMatchMode = MatchMode.Ordered, + ExpressionOrder = 5 + } + } + }; + string script = @"2+2-2^2*2/2"; + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + MatcherResult result = engine.Match(script); + + Assert.True(result.Success); + Assert.True(CompareXml(@"2+2-2^2*2/2", result.MatchData.ToXml())); + } + + //[Fact] + public void LazyIndexPerformanceTest() + { + string definition = File.ReadAllText("Files/LazyIndexTestDefinition.json"); + string script = File.ReadAllText("Files/Parsables/JibberishScript.lz"); + LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + + + //GC.TryStartNoGCRegion(200 * 1000 * 1000); + Stopwatch timer = new Stopwatch(); + timer.Start(); + + for (int i = 0; i < 150; i++) + { + engine.Match(script); + } + + timer.Stop(); + //System.GC.EndNoGCRegion(); + Assert.Equal(0, timer.ElapsedTicks); + } + + //[Fact] + public void EagerIndexPerformanceTest() + { + string definition = File.ReadAllText("Files/EagerIndexTestDefinition2.json"); + string script = File.ReadAllText("Files/Parsables/JibberishScript.lz"); + LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + + ILanguageMatchEngine engine = LanguageMatchEngineFactory.Get(GenerationType, languageMatcher); + + + //GC.TryStartNoGCRegion(200 * 1000 * 1000); + Stopwatch timer = new Stopwatch(); + timer.Start(); + + for (int i = 0; i < 150; i++) + { + engine.Match(script); + } + + timer.Stop(); + //System.GC.EndNoGCRegion(); + Assert.Equal(0, timer.ElapsedTicks); + } + + private bool CompareXml(string expected, string actual) + { + return XNode.DeepEquals(XDocument.Parse(expected), XDocument.Parse(actual)); + } + } +} diff --git a/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs b/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs new file mode 100644 index 0000000..c304226 --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Interop/DefinitionConverterTests.cs @@ -0,0 +1,153 @@ +using Newtonsoft.Json; +using Synfron.Staxe.Matcher.Input; +using Synfron.Staxe.Matcher.Input.Actions; +using Synfron.Staxe.Matcher.Interop.Model; +using System; +using System.IO; +using Xunit; + +namespace MatcherTests.Tests.Interop +{ + public class DefinitionConverterTests + { + [Fact] + public void DefinitionConverter_Convert() + { + string definition = File.ReadAllText("Files/EagerIndexTestDefinition.json"); + LanguageMatcherDefinition languageMatcherDefinition = JsonConvert.DeserializeObject(definition); + + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageMatcherDefinition); + LanguageMatcherDefinition newLanguageDefinition = DefinitionConverter.Convert(languageMatcher); + + Assert.Equal(languageMatcherDefinition, newLanguageDefinition, new LanguageMatcherDefinitionComparer()); + } + + [Fact] + public void DefinitionConverter_ConvertLanguageMatcher() + { + LanguageMatcherDefinition languageDefinition = new LanguageMatcherDefinition + { + Name = "TestLang", + IndexingMode = IndexingMode.Eager, + LogMatches = true, + StartingFragment = "TestFrag1", + Actions = new[] + { + new MatcherActionDefinition + { + Name = "TestAction1", + ActionType = MatcherActionType.UpdateVariable, + Change = VariableUpdateAction.Subtract, + FirstVariableName = "Var1", + SecondVariableName = "Var2" + }, + new MatcherActionDefinition + { + Name = "TestAction2", + ActionType = MatcherActionType.Assert, + Assert = AssertType.LessThanOrEquals, + FirstVariableName = "Var1", + SecondVariableName = "Var2" + }, + new MatcherActionDefinition + { + Name = "TestAction3", + ActionType = MatcherActionType.CreateVariable, + Source = VariableValueSource.PartsText, + FirstVariableName = "Var1", + Value = 20 + }, + new MatcherActionDefinition + { + Name = "TestAction4", + ActionType = MatcherActionType.CreateVariable, + Source = VariableValueSource.PartsText, + FirstVariableName = "Var2", + Value = 10 + } + }, + Patterns = new[] + { + new PatternMatcherDefinition + { + Name = "TestPattern2", + Pattern = "B" + }, + new PatternMatcherDefinition + { + Name = "TestPattern3", + Pattern = "C" + }, + new PatternMatcherDefinition + { + Name = "TestPattern4", + Pattern = "D" + }, + new PatternMatcherDefinition + { + Name = "TestPattern5", + Pattern = "E" + }, + new PatternMatcherDefinition + { + Name = "TestPattern6", + Fragment = "TestFrag2", + IsNoise = true + }, + new PatternMatcherDefinition + { + Name = "TestPattern1", + Pattern = "A", + IsAuxiliary = true, + IsNoise = true, + Mergable = true + } + }, + Fragments = new[] + { + new FragmentMatcherDefinition + { + Name = "TestFrag1" + }, + new FragmentMatcherDefinition + { + Name = "TestFrag2", + Actions = new[] + { + "TestAction1", + "TestAction2", + "TestAction3", + "TestAction4" + }, + Start = "TestPattern1", + End = "TestPattern2", + ExpressionMode = ExpressionMode.BinaryTree, + ExpressionOrder = 1, + BoundsAsParts = true, + DiscardBounds = true, + PartsDelimiter = "TestPattern3", + PartsDelimiterRequired = true, + Cacheable = true, + ClearCache = true, + FallThroughMode = FallThroughMode.All, + IsNoise = true, + MinMatchedParts = 3, + PartsMatchMode = MatchMode.Ordered, + Negate = true, + PartsPadding = "TestPattern4", + Parts = + { + "[TestFrag1]", + "TestPattern5" + } + } + } + }; + + LanguageMatcher languageMatcher = DefinitionConverter.Convert(languageDefinition); + LanguageMatcherDefinition newLanguageDefinition = DefinitionConverter.Convert(languageMatcher); + + Assert.Equal(languageDefinition, newLanguageDefinition, new LanguageMatcherDefinitionComparer()); + } + } +} diff --git a/src/Tests/MatcherTests/Interop/Ebnf/Tests/ConversionTests.cs b/src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs similarity index 95% rename from src/Tests/MatcherTests/Interop/Ebnf/Tests/ConversionTests.cs rename to src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs index dc6e6ce..6b1b996 100644 --- a/src/Tests/MatcherTests/Interop/Ebnf/Tests/ConversionTests.cs +++ b/src/Tests/MatcherTests/Tests/Interop/Ebnf/ConversionTests.cs @@ -17,7 +17,7 @@ public class ConversionTests [InlineData("abc-w3c.ebnf", 10)] public void ConversionTest(string grammarFileName, int numRules) { - const string fileDirectory = "Interop/Ebnf/Files"; + const string fileDirectory = "Files"; string ebnf = File.ReadAllText(Path.Combine(fileDirectory, "Grammars", grammarFileName)); EbnfConverter ebnfConverter = new EbnfConverter(); diff --git a/src/Tests/MatcherTests/Tests/Interop/FragmentMatcherDefintionComparer.cs b/src/Tests/MatcherTests/Tests/Interop/FragmentMatcherDefintionComparer.cs new file mode 100644 index 0000000..39d1ae0 --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Interop/FragmentMatcherDefintionComparer.cs @@ -0,0 +1,35 @@ +using Synfron.Staxe.Matcher.Interop.Model; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MatcherTests.Tests.Interop +{ + public class FragmentMatcherDefintionComparer : IEqualityComparer + { + public bool Equals(FragmentMatcherDefinition x, FragmentMatcherDefinition y) + { + return x.Parts.SequenceEqual(y.Parts ?? Enumerable.Empty()) + && x.BoundsAsParts == y.BoundsAsParts + && x.Cacheable == y.Cacheable + && x.ClearCache == y.ClearCache + && x.DiscardBounds == y.DiscardBounds + && x.End == y.End + && x.ExpressionMode == y.ExpressionMode + && x.ExpressionOrder == y.ExpressionOrder + && x.FallThroughMode == y.FallThroughMode + && x.IsNoise == y.IsNoise + && x.MinMatchedParts == y.MinMatchedParts + && x.Name == y.Name + && x.Negate == y.Negate + && x.PartsDelimiter == y.PartsDelimiter + && x.PartsDelimiterRequired == y.PartsDelimiterRequired + && x.PartsMatchMode == y.PartsMatchMode + && x.PartsPadding == y.PartsPadding + && x.Start == y.Start + && (x.Actions ?? Enumerable.Empty()).SequenceEqual(y.Actions ?? Enumerable.Empty()); + } + + public int GetHashCode(FragmentMatcherDefinition obj) => throw new NotImplementedException(); + } +} diff --git a/src/Tests/MatcherTests/Tests/Interop/LanguageMatcherDefinitionComparer.cs b/src/Tests/MatcherTests/Tests/Interop/LanguageMatcherDefinitionComparer.cs new file mode 100644 index 0000000..5610de1 --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Interop/LanguageMatcherDefinitionComparer.cs @@ -0,0 +1,26 @@ +using Synfron.Staxe.Matcher.Interop.Model; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MatcherTests.Tests.Interop +{ + public class LanguageMatcherDefinitionComparer : IEqualityComparer + { + public bool Equals(LanguageMatcherDefinition x, LanguageMatcherDefinition y) + { + return x.Fragments.SequenceEqual(y.Fragments, new FragmentMatcherDefintionComparer()) + && x.Patterns.SequenceEqual(y.Patterns, new PatternMatcherDefinitionComparer()) + && x.IndexingMode == y.IndexingMode + && x.LogMatches == y.LogMatches + && x.Name == y.Name + && x.StartingFragment == y.StartingFragment + && (x.Actions == y.Actions || + (x.Actions?.OrderBy(action => action.Name) ?? Enumerable.Empty()) + .SequenceEqual(y.Actions?.OrderBy(action => action.Name) ?? Enumerable.Empty(), + new MatcherActionDefintionComparer())); + } + + public int GetHashCode(LanguageMatcherDefinition obj) => throw new NotImplementedException(); + } +} diff --git a/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs b/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs new file mode 100644 index 0000000..f881be5 --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Interop/MatcherActionDefintionComparer.cs @@ -0,0 +1,24 @@ +using Synfron.Staxe.Matcher.Interop.Model; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace MatcherTests.Tests.Interop +{ + public class MatcherActionDefintionComparer : IEqualityComparer + { + public bool Equals(MatcherActionDefinition x, MatcherActionDefinition y) + { + return x.Change == y.Change + && x.Source == y.Source + && x.FirstVariableName == y.FirstVariableName + && x.SecondVariableName == y.SecondVariableName + && x.Name == y.Name + && x.Assert == y.Assert + && x.ActionType == y.ActionType + && x.Value == y.Value; + } + + public int GetHashCode(MatcherActionDefinition obj) => throw new NotImplementedException(); + } +} diff --git a/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs b/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs new file mode 100644 index 0000000..6213b18 --- /dev/null +++ b/src/Tests/MatcherTests/Tests/Interop/PatternMatcherDefinitionComparer.cs @@ -0,0 +1,28 @@ +using Synfron.Staxe.Matcher.Input.Patterns; +using Synfron.Staxe.Matcher.Interop.Model; +using System; +using System.Collections.Generic; + +namespace MatcherTests.Tests.Interop +{ + public class PatternMatcherDefinitionComparer : IEqualityComparer + { + public bool Equals(PatternMatcherDefinition x, PatternMatcherDefinition y) + { + bool result = x.IsNoise == y.IsNoise + && x.Mergable == y.Mergable + && x.Name == y.Name + && x.IsAuxiliary == y.IsAuxiliary + && x.Fragment == y.Fragment + && PatternReader.Parse(x.Pattern ?? string.Empty).ToString() == PatternReader.Parse(y.Pattern ?? string.Empty).ToString(); + return x.IsNoise == y.IsNoise + && x.Mergable == y.Mergable + && x.Name == y.Name + && x.IsAuxiliary == y.IsAuxiliary + && x.Fragment == y.Fragment + && PatternReader.Parse(x.Pattern ?? string.Empty).ToString() == PatternReader.Parse(y.Pattern ?? string.Empty).ToString(); + } + + public int GetHashCode(PatternMatcherDefinition obj) => throw new NotImplementedException(); + } +} diff --git a/src/Tests/MatcherTests/Patterns/PatternReaderTests.cs b/src/Tests/MatcherTests/Tests/Patterns/PatternReaderTests.cs similarity index 100% rename from src/Tests/MatcherTests/Patterns/PatternReaderTests.cs rename to src/Tests/MatcherTests/Tests/Patterns/PatternReaderTests.cs diff --git a/src/Tests/Shared/Shared.csproj b/src/Tests/Shared/Shared.csproj new file mode 100644 index 0000000..f208d30 --- /dev/null +++ b/src/Tests/Shared/Shared.csproj @@ -0,0 +1,7 @@ + + + + net5.0 + + + diff --git a/src/Tests/StaxeTests/Shared/Matcher/LanguageMatchEngineFactory.cs b/src/Tests/StaxeTests/Shared/Matcher/GeneratedLanguageMatchEngineRegistry.cs similarity index 100% rename from src/Tests/StaxeTests/Shared/Matcher/LanguageMatchEngineFactory.cs rename to src/Tests/StaxeTests/Shared/Matcher/GeneratedLanguageMatchEngineRegistry.cs diff --git a/src/Tests/StaxeTests/TestComplexLang/Tests/ScriptTests.cs b/src/Tests/StaxeTests/TestComplexLang/Tests/ScriptTests.cs index cc01817..abe8343 100644 --- a/src/Tests/StaxeTests/TestComplexLang/Tests/ScriptTests.cs +++ b/src/Tests/StaxeTests/TestComplexLang/Tests/ScriptTests.cs @@ -59,7 +59,7 @@ private ExecutionState Run(string code, InstructionExecutor