/// <summary> /// Remove a group from the top of the group stack and create a GroupNode from it in response to a closing ')'. /// </summary> private void CloseGroup() { if (_groupStack.Count == 0) { throw MakeException(RegexParseError.TooManyParentheses); } RegexNode outerNode = CreateOuterNode(); GroupUnit currentGroup = _groupStack.Pop(); var currentGroupNode = currentGroup.Node.AddNode(outerNode); _group = _groupStack.FirstOrDefault(); // The first group "(...)" inside a conditional group goes directly to the condition group as it's condition. // TODO: "(...)" should be a reference node "(1)" without "\" or a named reference "(name)" without "?<>" if this is the name of a named group or a lookahead without "?=" otherwise. if (_group?.Node is ConditionalGroupNode && !_group.Node.ChildNodes.Any()) { _group.Node = _group.Node.AddNode(currentGroupNode); return; } // No more than two alternates allowed in a conditional group if (_group?.Node is ConditionalGroupNode && outerNode is AlternationNode && outerNode.ChildNodes.Count() > MaxAlternatesInConditionalGroup) { throw MakeException(RegexParseError.TooManyAlternates); } AddNode(currentGroupNode); }
/// <summary> /// Sets a new GroupUnit with a ModeModifierGroupNode as the current group /// </summary> private void StartModeModifierGroup() { string options = ScanOptions(); // Invalid grouping construct if (string.IsNullOrEmpty(options)) { throw MakeException(RegexParseError.UnrecognizedGroupingConstruct); } // Mode modifier group "(?imnsx-imnsx)" or "(?imnsx-imnsx:...)" _group = new GroupUnit(new ModeModifierGroupNode(options)); }
private void StartNamedOrLookbehindGroup() { if (CharsRight() > 0) { char ch = RightChar(); // Possitive lookbehind "(?<=...)" or negative lookbehind "(?<!...)" if (ch == '=' || ch == '!') { // Consume both the '<' and the '=' or '!' characters. MoveRight(); _group = new GroupUnit(new LookaroundGroupNode(false, ch == '=')); return; } } // Named capturing group "(?<name>...)" or balancing group "(?<name-balancedGroupName>...)" _group = new GroupUnit(CreateNamedGroup('>')); }
/// <summary> /// Adds a new GroupUnit to the group stack and sets it as the current group in response to an opening '('. /// </summary> private void StartGroup() { // Regular capturing group "(...)" if (IsCaptureGroup()) { _group = new GroupUnit(new CaptureGroupNode()); } // "(?...)" is a special group. else { MoveRight(); char ch = RightCharMoveRight(); switch (ch) { // Noncapturing group "(?:...)" case ':': _group = new GroupUnit(new NonCaptureGroupNode()); break; // Atomic group "(?>...)" case '>': _group = new GroupUnit(new AtomicGroupNode()); break; // Conditional group (?(condition)then|else) case '(': MoveLeft(); _group = new GroupUnit(new ConditionalGroupNode()); break; // Possitive lookahead "(?=...)" or negative lookahead "(?!...)" case '=': case '!': _group = new GroupUnit(new LookaroundGroupNode(true, ch == '=')); break; // Named capturing group "(?'name'...)" or balancing group "(?'name-balancedGroupName'...)" case '\'': _group = new GroupUnit(CreateNamedGroup(ch)); break; // Named capturing group "(?<name>...)" or possitive lookbehind "(?<=...)" or negative lookbehind "(?<!...)" case '<': StartNamedOrLookbehindGroup(); break; // Comment group "(?#...)" is not added to the group stack. It will be the prefix for the next node instead. case '#': string comment = ScanComment(); _prefix = new CommentGroupNode(comment) { Prefix = _prefix }; // Return here, so nothing gets pushed to the group stack. return; // Could be inline mode modifier group "(?imnsx-imnsx)" or "(?imnsx-imnsx:...)". Invalid grouping construct otherwise. default: MoveLeft(); StartModeModifierGroup(); break; } } _groupStack.Push(_group); }