protected override Int32 VisitNamedCapture(NamedCapture namedCapture, Unit argument) { var hash = new HashCode(); hash.Add(namedCapture.Name); hash.Add(namedCapture.InnerNode, this.treeComparer); return(hash.ToHashCode()); }
private IResult <RegexNode> GetContainer(Input input, Parser <QuantifierParams> quantifierParamsParser) { if (input.AtEnd) { return(new Failure <RegexNode>(input, () => "Unexpected end of input reached", () => new[] { "GetContainer" })); } if (input.Current == '|') { // It's possible that we're at a pipe that is creating a top-level alternation. Fail so a higher level // parser can deal with it. return(new Failure <RegexNode>(input, () => "Pipe was first character parsed", () => new[] { "GetContainer" })); } var containerStack = new Stack <ContainerInfo>(); var candidateNodes = new Dictionary <Guid, List <RegexNode> >(); while (!input.AtEnd) { var nonContainerSuccess = _nonContainer(input) as ISuccess <RegexNode>; if (nonContainerSuccess != null) { if (containerStack.Count == 0) { if (input.Position == 0) { // Assume that we might have a top-level alternation - if it turns out we don't, we'll fail anyway containerStack.Push(new ContainerInfo { Index = input.Position, ContainerType = ContainerType.Alternation }); } else { return(new Failure <RegexNode>(input, () => "Last character of input reached - captures are not possible", () => new[] { "GetContainer" })); } } var key = containerStack.Peek().Key; if (!candidateNodes.ContainsKey(key)) { candidateNodes.Add(key, new List <RegexNode>()); } candidateNodes[key].Add(nonContainerSuccess.Result); input = nonContainerSuccess.Remainder; } else { var quantifierParamsSuccess = quantifierParamsParser(input) as ISuccess <QuantifierParams>; if (quantifierParamsSuccess != null) { var parenKey = containerStack.Peek().Key; var nodes = candidateNodes[parenKey]; var quantifierPattern = input.Source.Substring(input.Position, quantifierParamsSuccess.Remainder.Position - input.Position); nodes[nodes.Count - 1] = CreateQuantifier( quantifierParamsSuccess.Result, nodes[nodes.Count - 1], nodes[nodes.Count - 1].Index, nodes[nodes.Count - 1].Pattern + quantifierPattern); input = quantifierParamsSuccess.Remainder; continue; } else { switch (input.Current) { case ')': var containerInfo = containerStack.Pop(); if (containerInfo.ContainerType == ContainerType.Alternation) { var alternationKey = containerInfo.Key; var alternationChildren = candidateNodes[alternationKey]; var alternation = CreateAlternation(input, alternationChildren); containerInfo = containerStack.Pop(); candidateNodes[containerInfo.Key] = new List <RegexNode> { alternation }; } var children = candidateNodes[containerInfo.Key]; candidateNodes.Remove(containerInfo.Key); var index = containerInfo.Index; var pattern = input.Source.Substring(containerInfo.Index, (input.Position - containerInfo.Index) + 1); ContainerNode paren; if (containerInfo.ParenType == ParenType.Capturing) { paren = new CapturingParens(children, index, pattern); } else if (containerInfo.ParenType == ParenType.NonCapturing) { paren = new NonCapturingParens(children, index, pattern); } else if (containerInfo.ParenType == ParenType.PositiveLookAhead) { paren = new LookAhead(false, children, index, pattern); } else if (containerInfo.ParenType == ParenType.NegativeLookAhead) { paren = new LookAhead(true, children, index, pattern); } else if (containerInfo.ParenType == ParenType.PositiveLookBehind) { paren = new LookBehind(false, children, index, pattern); } else if (containerInfo.ParenType == ParenType.NegativeLookBehind) { paren = new LookBehind(true, children, index, pattern); } else if (containerInfo.ParenType == ParenType.Atomic) { paren = new AtomicGrouping(children, index, pattern); } else { paren = new NamedCapture(containerInfo.ParenType.Name, children, index, pattern); } if (containerStack.Count > 0) { var parenKey = containerStack.Peek().Key; if (!candidateNodes.ContainsKey(parenKey)) { candidateNodes.Add(parenKey, new List <RegexNode>()); } candidateNodes[parenKey].Add(paren); } else { return(new Success <RegexNode>(paren, input.Advance())); } break; case '(': var parentTypeParser = from leftParen in Parse.Char('(') from p in (from question in Parse.Char('?') from p in Parse.Char(':').Select(x => ParenType.NonCapturing) .Or(Parse.Char('=').Select(x => ParenType.PositiveLookAhead)) .Or(Parse.Char('!').Select(x => ParenType.NegativeLookAhead)) .Or(Parse.Char('>').Select(x => ParenType.Atomic)) .Or(Parse.String("<=").Select(x => ParenType.PositiveLookBehind)) .Or(Parse.String("<!").Select(x => ParenType.NegativeLookBehind)) .Or( from open in Parse.Char('<') from name in Parse.LetterOrDigit.Or(Parse.Char('_')).AtLeastOnce().Text() from close in Parse.Char('>') select ParenType.NamedCapture(name)) .Or( from open in Parse.Char('\'') from name in Parse.LetterOrDigit.Or(Parse.Char('_')).AtLeastOnce().Text() from close in Parse.Char('\'') select ParenType.NamedCapture(name)) select p) .XOr(Parse.Return(ParenType.Capturing)) select p; var parenType = ((ISuccess <ParenType>)parentTypeParser(input)).Result; containerStack.Push(new ContainerInfo { Index = input.Position, ContainerType = ContainerType.Parens, ParenType = parenType }); input = parenType.Advance(input); break; case '|': var container = containerStack.Peek(); if (container.ContainerType != ContainerType.Alternation) { var alternationNodes = candidateNodes[container.Key]; var partialAlternation = new AlternationMarker(alternationNodes.First().Index); candidateNodes[container.Key] = new List <RegexNode> { partialAlternation }; var alternationInfo = new ContainerInfo { Index = partialAlternation.Index, ContainerType = ContainerType.Alternation }; containerStack.Push(alternationInfo); container = containerStack.Peek(); candidateNodes[container.Key] = new List <RegexNode>(alternationNodes); } candidateNodes[container.Key].Add(new AlternationMarker(input.Position)); break; default: return(new Failure <RegexNode>(input, () => "Unexpected character found inside parenthesis", () => new[] { "GetContainer" })); } } input = input.Advance(); } } if (containerStack.Count == 1 && containerStack.Peek().ContainerType == ContainerType.Alternation) { var containerInfo = containerStack.Pop(); var candidates = candidateNodes[containerInfo.Key]; if (candidates.Any(c => c is AlternationMarker)) { var alternation = CreateAlternation(input, candidates); return(new Success <RegexNode>(alternation, input.AtEnd ? input : input.Advance())); } } return(new Failure <RegexNode>(input, () => "Unmatched parentheses", () => new[] { "GetContainer" })); }