Recurs into the last expression of a loop node, looking to see if it can find a node that could be made atomic _assuming_ the conditions exist for it with the loop's ancestors. Returns The found node that should be explored further for auto-atomicity; null if it doesn't exist.
()
| 932 | // that could be made atomic _assuming_ the conditions exist for it with the loop's ancestors. |
| 933 | // Returns The found node that should be explored further for auto-atomicity; null if it doesn't exist. |
| 934 | func (n *RegexNode) FindLastExpressionInLoopForAutoAtomic() *RegexNode { |
| 935 | node := n |
| 936 | |
| 937 | // Start by looking at the loop's sole child. |
| 938 | node = node.Children[0] |
| 939 | |
| 940 | // Skip past captures. |
| 941 | for node.T == NtCapture { |
| 942 | node = node.Children[0] |
| 943 | } |
| 944 | |
| 945 | // If the loop's body is a concatenate, we can skip to its last child iff that |
| 946 | // last child doesn't conflict with the first child, since this whole concatenation |
| 947 | // could be repeated, such that the first node ends up following the last. For |
| 948 | // example, in the expression (a+[def])*, the last child is [def] and the first is |
| 949 | // a+, which can't possibly overlap with [def]. In contrast, if we had (a+[ade])*, |
| 950 | // [ade] could potentially match the starting 'a'. |
| 951 | if node.T == NtConcatenate { |
| 952 | concatCount := len(node.Children) |
| 953 | lastConcatChild := node.Children[concatCount-1] |
| 954 | if lastConcatChild.canBeMadeAtomic(node.Children[0], false, false) { |
| 955 | return lastConcatChild |
| 956 | } |
| 957 | } |
| 958 | |
| 959 | // Otherwise, the loop has nothing that can participate in auto-atomicity. |
| 960 | return nil |
| 961 | } |
| 962 | |
| 963 | // Determines whether a node can be switched to an atomic loop. |
| 964 | // |
no test coverage detected