/// <inheritdoc /> protected override GrammarNode <Char>?VisitAlternation(Alternation <Char> alternation, OptimizeArgs argument) { var nodes = alternation.GrammarNodes.Select(node => this.Visit(node, argument)) .Where(node => node is not null) .ToList(); var matchedCharTerminals = new HashSet <Char>(); var matchedStringTerminals = new HashSet <String>(); var matchedCharRanges = new HashSet <Range <Char> >(); var matchedUnicodeCategories = new HashSet <UnicodeCategory>(); var matchedNodes = new HashSet <GrammarNode <Char> >(GrammarTreeStructuralComparer.Instance); for (var nodeIdx = 0; nodeIdx < nodes.Count; nodeIdx++) { loopStart: GrammarNode <Char> currentNode = nodes[nodeIdx] !; if (matchedNodes.Contains(currentNode)) { nodes.RemoveAt(nodeIdx); goto loopStart; } switch (currentNode.Kind) { case GrammarNodeKind.CharacterStringTerminal: { var strTerminal = (StringTerminal)currentNode; if (matchedStringTerminals.Contains(strTerminal.Value)) { nodes.RemoveAt(nodeIdx); goto loopStart; } matchedStringTerminals.Add(strTerminal.Value); break; } case GrammarNodeKind.CharacterTerminal: { var ch = ((CharacterTerminal)currentNode).Value; if (isCharMatched(ch)) { nodes.RemoveAt(nodeIdx); goto loopStart; } matchedCharTerminals.Add(ch); break; } case GrammarNodeKind.CharacterRange: { var characterRange = (CharacterRange)currentNode; var newStart = characterRange.Range.Start; while (isCharMatched(newStart) && newStart <= characterRange.Range.End) { newStart++; } var newEnd = characterRange.Range.End; while (isCharMatched(newEnd) && newEnd >= newStart) { newEnd--; } // The new start will be greater than the new end if all characters in the // range are already being matched. if (newStart > newEnd) { nodes.RemoveAt(nodeIdx); goto loopStart; } else if (newStart == newEnd) { nodes[nodeIdx] = new CharacterTerminal(newStart); goto loopStart; } else if (characterRange.Range.Start != newStart || characterRange.Range.End != newEnd) { nodes[nodeIdx] = currentNode = characterRange = new CharacterRange(newStart, newEnd); } break; } } matchedNodes.Add(currentNode); } if (nodes.Count == 0) { return(null); } else if (nodes.Count == 1) { return(nodes[0]); } else if (nodes.SequenceEqual(alternation.GrammarNodes)) { return(alternation); } else { return(new Alternation <Char>(nodes !)); } Boolean isCharMatched(Char ch) => matchedCharTerminals !.Contains(ch) || matchedCharRanges !.Any(range => CharUtils.IsInRange(range.Start, ch, range.End)) || matchedUnicodeCategories !.Contains(Char.GetUnicodeCategory(ch)); }
protected override Range <UInt32> VisitCharacterRange(CharacterRange characterRange, LengthCalculatorOptions argument) =>
/// <summary> /// Visits a character range. /// </summary> /// <param name="characterRange"></param> /// <param name="argument">The argument data passed by the caller.</param> /// <returns>The result of visiting this node.</returns> protected abstract TReturn VisitCharacterRange(CharacterRange characterRange, TArgument argument);