Пример #1
0
        private static RowRecogExprNode Replace(
            RowRecogExprNode optionalParent,
            RowRecogExprNode originalNode,
            IList<RowRecogExprNode> expandedRepeat)
        {
            if (optionalParent == null) {
                var newParentNode = new RowRecogExprNodeConcatenation();
                newParentNode.ChildNodes.AddAll(expandedRepeat);
                return newParentNode;
            }

            // for nested nodes, use a concatenation instead
            if (optionalParent is RowRecogExprNodeNested ||
                optionalParent is RowRecogExprNodeAlteration) {
                var concatenation = new RowRecogExprNodeConcatenation();
                concatenation.ChildNodes.AddAll(expandedRepeat);
                optionalParent.ReplaceChildNode(
                    originalNode,
                    Collections.SingletonList<RowRecogExprNode>(concatenation));
            }
            else {
                // concatenations are simply changed
                optionalParent.ReplaceChildNode(originalNode, expandedRepeat);
            }

            return null;
        }
Пример #2
0
        /// <summary>
        ///     Build a list of start states from the parent node.
        /// </summary>
        /// <param name="parent">to build start state for</param>
        /// <param name="variableDefinitions">each variable and its expressions</param>
        /// <param name="variableStreams">variable name and its stream number</param>
        /// <param name="exprRequiresMultimatchState">indicator whether multi-match state required</param>
        /// <returns>strand of regex state nodes</returns>
        protected internal static RowRecogNFAStrandResult BuildStartStates(
            RowRecogExprNode parent,
            IDictionary<string, ExprNode> variableDefinitions,
            IDictionary<string, Pair<int, bool>> variableStreams,
            bool[] exprRequiresMultimatchState
        )
        {
            var nodeNumStack = new Stack<int>();

            RowRecogNFAStrand strand = RecursiveBuildStatesInternal(
                parent,
                variableDefinitions,
                variableStreams,
                nodeNumStack,
                exprRequiresMultimatchState);

            // add end state
            var end = new RowRecogNFAStateEndForge();
            end.NodeNumFlat = -1;
            foreach (RowRecogNFAStateForgeBase endStates in strand.EndStates) {
                endStates.AddState(end);
            }

            // assign node num as a counter
            var nodeNumberFlat = 0;
            foreach (RowRecogNFAStateForgeBase theBase in strand.AllStates) {
                theBase.NodeNumFlat = nodeNumberFlat++;
            }

            return new RowRecogNFAStrandResult(new List<RowRecogNFAStateForge>(strand.StartStates), strand.AllStates);
        }
Пример #3
0
 public static IDictionary<string, ISet<string>> DetermineVisibility(RowRecogExprNode pattern)
 {
     IDictionary<string, ISet<string>> map = new Dictionary<string, ISet<string>>();
     var path = new ArrayDeque<RowRecogExprNode>();
     RecursiveFindPatternAtoms(pattern, path, map);
     return map;
 }
Пример #4
0
        /// <summary>
        ///     Inspect variables recursively.
        /// </summary>
        /// <param name="parent">parent regex expression node</param>
        /// <param name="isMultiple">if the variable in the stack is multiple of single</param>
        /// <param name="variablesSingle">single variables list</param>
        /// <param name="variablesMultiple">group variables list</param>
        public static void RecursiveInspectVariables(
            RowRecogExprNode parent,
            bool isMultiple,
            ISet<string> variablesSingle,
            ISet<string> variablesMultiple)
        {
            if (parent is RowRecogExprNodeNested) {
                var nested = (RowRecogExprNodeNested) parent;
                foreach (var child in parent.ChildNodes) {
                    RecursiveInspectVariables(
                        child,
                        nested.Type.IsMultipleMatches() || isMultiple,
                        variablesSingle,
                        variablesMultiple);
                }
            }
            else if (parent is RowRecogExprNodeAlteration) {
                foreach (var childAlteration in parent.ChildNodes) {
                    var singles = new LinkedHashSet<string>();
                    var multiples = new LinkedHashSet<string>();

                    RecursiveInspectVariables(childAlteration, isMultiple, singles, multiples);

                    variablesMultiple.AddAll(multiples);
                    variablesSingle.AddAll(singles);
                }

                variablesSingle.RemoveAll(variablesMultiple);
            }
            else if (parent is RowRecogExprNodeAtom) {
                var atom = (RowRecogExprNodeAtom) parent;
                var name = atom.Tag;
                if (variablesMultiple.Contains(name)) {
                    return;
                }

                if (variablesSingle.Contains(name)) {
                    variablesSingle.Remove(name);
                    variablesMultiple.Add(name);
                    return;
                }

                if (atom.Type.IsMultipleMatches()) {
                    variablesMultiple.Add(name);
                    return;
                }

                if (isMultiple) {
                    variablesMultiple.Add(name);
                }
                else {
                    variablesSingle.Add(name);
                }
            }
            else {
                foreach (var child in parent.ChildNodes) {
                    RecursiveInspectVariables(child, isMultiple, variablesSingle, variablesMultiple);
                }
            }
        }
Пример #5
0
 public RowRecogExprNode Copy(
     RowRecogExprNode nodeToCopy,
     RowRecogNFATypeEnum newType,
     ExpressionCopier expressionCopier)
 {
     var atom = (RowRecogExprNodeAtom) nodeToCopy;
     return new RowRecogExprNodeAtom(atom.Tag, newType, null);
 }
Пример #6
0
 public static void RegExCollectAddSubNodesAddParentNode(
     RowRecogExprNode exprNode,
     ITree node,
     IDictionary<ITree, RowRecogExprNode> astRegExNodeMap)
 {
     RegExCollectAddSubNodes(exprNode, node, astRegExNodeMap);
     astRegExNodeMap.Put(node, exprNode);
 }
Пример #7
0
            public RowRecogExprNode Copy(
                RowRecogExprNode nodeToCopy,
                RowRecogNFATypeEnum newType,
                ExpressionCopier expressionCopier)
            {
                var nested = (RowRecogExprNodeNested) nodeToCopy;
                var nestedCopy = new RowRecogExprNodeNested(newType, null);
                foreach (var inner in nested.ChildNodes) {
                    var innerCopy = inner.CheckedCopy(expressionCopier);
                    nestedCopy.AddChildNode(innerCopy);
                }

                return nestedCopy;
            }
Пример #8
0
        private static void RecursiveCollectAtomsWExclude(
            RowRecogExprNode node,
            ISet<string> identifiers,
            string excludedTag)
        {
            if (node is RowRecogExprNodeAtom) {
                var atom = (RowRecogExprNodeAtom) node;
                if (!excludedTag.Equals(atom.Tag)) {
                    identifiers.Add(atom.Tag);
                }
            }

            foreach (var child in node.ChildNodes) {
                RecursiveCollectAtomsWExclude(child, identifiers, excludedTag);
            }
        }
Пример #9
0
        private static void RecursiveFindPatternAtoms(
            RowRecogExprNode parent,
            ArrayDeque<RowRecogExprNode> path,
            IDictionary<string, ISet<string>> map)
        {
            path.Add(parent);
            foreach (var child in parent.ChildNodes) {
                if (child is RowRecogExprNodeAtom) {
                    HandleAtom((RowRecogExprNodeAtom) child, path, map);
                }
                else {
                    RecursiveFindPatternAtoms(child, path, map);
                }
            }

            path.RemoveLast();
        }
Пример #10
0
        public static void RegExApplyActionRecursive(
            ITree node,
            IDictionary<ITree, RowRecogExprNode> astRegExNodeMap,
            RegExAction action)
        {
            RowRecogExprNode expr = astRegExNodeMap.Get(node);
            if (expr != null)
            {
                action.Invoke(expr, astRegExNodeMap, node);
                return;
            }

            for (int i = 0; i < node.ChildCount; i++)
            {
                RegExApplyActionRecursive(node.GetChild(i), astRegExNodeMap, action);
            }
        }
Пример #11
0
        public static void RegExCollectAddSubNodes(
            RowRecogExprNode regexNode,
            ITree node,
            IDictionary<ITree, RowRecogExprNode> astRegExNodeMap)
        {
            if (regexNode == null)
            {
                throw ASTWalkException.From("Invalid null expression node for '" + ASTUtil.PrintNode(node) + "'");
            }

            RegExAction action = (exprNode, astExprNodeMapX, nodeX) => {
                astExprNodeMapX.Remove(nodeX);
                regexNode.AddChildNode(exprNode);
            };
            for (int i = 0; i < node.ChildCount; i++)
            {
                ITree childNode = node.GetChild(i);
                RegExApplyActionRecursive(childNode, astRegExNodeMap, action);
            }
        }
Пример #12
0
        internal static void RunEquivalent(
            RegressionEnvironment env,
            string before,
            string after)
        {
            var hook = "@Hook(HookType=" +
                       typeof(HookType).FullName +
                       ".INTERNAL_COMPILE,Hook='" +
                       SupportStatementCompileHook.ResetGetClassName() +
                       "')";
            var epl = hook +
                      "@Name('s0') select * from SupportBean#keepall " +
                      "match_recognize (" +
                      " measures A as a" +
                      " pattern (" +
                      before +
                      ")" +
                      " define" +
                      " A as A.TheString like \"A%\"" +
                      ")";

            var model = env.EplToModel(epl);
            env.CompileDeploy(model);
            env.UndeployAll();

            var spec = SupportStatementCompileHook.GetSpecs()[0];
            RowRecogExprNode expanded = null;
            try {
                expanded = RowRecogPatternExpandUtil.Expand(
                    spec.Raw.MatchRecognizeSpec.Pattern,
                    null);
            }
            catch (ExprValidationException e) {
                Assert.Fail(e.Message);
            }

            var writer = new StringWriter();
            expanded.ToEPL(writer, RowRecogExprNodePrecedenceEnum.MINIMUM);
            Assert.AreEqual(after, writer.ToString());
        }
Пример #13
0
        public static RowRecogExprNode RegExGetRemoveTopNode(
            ITree node,
            IDictionary<ITree, RowRecogExprNode> astRowRegexNodeMap)
        {
            RowRecogExprNode regex = astRowRegexNodeMap.Get(node);
            if (regex != null)
            {
                astRowRegexNodeMap.Remove(node);
                return regex;
            }

            for (int i = 0; i < node.ChildCount; i++)
            {
                regex = RegExGetRemoveTopNode(node.GetChild(i), astRowRegexNodeMap);
                if (regex != null)
                {
                    return regex;
                }
            }

            return null;
        }
Пример #14
0
        private static IList<RowRecogExprNode> ExpandRepeat(
            RowRecogExprNode node,
            RowRecogExprRepeatDesc repeat,
            RowRecogNFATypeEnum type,
            RowRegexExprNodeCopier copier, 
            ExpressionCopier expressionCopier)
        {
            // handle single-bounds (no ranges)
            IList<RowRecogExprNode> repeated = new List<RowRecogExprNode>();
            if (repeat.Single != null) {
                ValidateExpression(repeat.Single);
                var numRepeated = (int) repeat.Single.Forge.ExprEvaluator.Evaluate(null, true, null);
                ValidateRange(numRepeated, 1, int.MaxValue);
                for (var i = 0; i < numRepeated; i++) {
                    var copy = copier.Copy(node, type, expressionCopier);
                    repeated.Add(copy);
                }

                return repeated;
            }

            // evaluate bounds
            int? lower = null;
            int? upper = null;
            if (repeat.Lower != null) {
                ValidateExpression(repeat.Lower);
                lower = (int?) repeat.Lower.Forge.ExprEvaluator.Evaluate(null, true, null);
            }

            if (repeat.Upper != null) {
                ValidateExpression(repeat.Upper);
                upper = (int?) repeat.Upper.Forge.ExprEvaluator.Evaluate(null, true, null);
            }

            // handle range
            if (lower != null && upper != null) {
                ValidateRange(lower.Value, 1, int.MaxValue);
                ValidateRange(upper.Value, 1, int.MaxValue);
                ValidateRange(lower.Value, 1, upper.Value);
                for (var i = 0; i < lower; i++) {
                    var copy = copier.Copy(node, type, expressionCopier);
                    repeated.Add(copy);
                }

                for (var i = lower.Value; i < upper; i++) {
                    // makeInline type optional
                    var newType = type;
                    if (type == RowRecogNFATypeEnum.SINGLE) {
                        newType = RowRecogNFATypeEnum.ONE_OPTIONAL;
                    }
                    else if (type == RowRecogNFATypeEnum.ONE_TO_MANY) {
                        newType = RowRecogNFATypeEnum.ZERO_TO_MANY;
                    }
                    else if (type == RowRecogNFATypeEnum.ONE_TO_MANY_RELUCTANT) {
                        newType = RowRecogNFATypeEnum.ZERO_TO_MANY_RELUCTANT;
                    }

                    var copy = copier.Copy(node, newType, expressionCopier);
                    repeated.Add(copy);
                }

                return repeated;
            }

            // handle lower-bounds only
            if (upper == null) {
                ValidateRange(lower.Value, 1, int.MaxValue);
                for (var i = 0; i < lower; i++) {
                    repeated.Add(copier.Copy(node, type, expressionCopier));
                }

                // makeInline type optional
                var newType = type;
                if (type == RowRecogNFATypeEnum.SINGLE) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY;
                }
                else if (type == RowRecogNFATypeEnum.ONE_OPTIONAL) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY;
                }
                else if (type == RowRecogNFATypeEnum.ONE_OPTIONAL_RELUCTANT) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY_RELUCTANT;
                }
                else if (type == RowRecogNFATypeEnum.ONE_TO_MANY) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY;
                }
                else if (type == RowRecogNFATypeEnum.ONE_TO_MANY_RELUCTANT) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY_RELUCTANT;
                }

                var copy = copier.Copy(node, newType, expressionCopier);
                repeated.Add(copy);
                return repeated;
            }

            // handle upper-bounds only
            ValidateRange(upper.Value, 1, int.MaxValue);
            for (var i = 0; i < upper; i++) {
                // makeInline type optional
                var newType = type;
                if (type == RowRecogNFATypeEnum.SINGLE) {
                    newType = RowRecogNFATypeEnum.ONE_OPTIONAL;
                }
                else if (type == RowRecogNFATypeEnum.ONE_TO_MANY) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY;
                }
                else if (type == RowRecogNFATypeEnum.ONE_TO_MANY_RELUCTANT) {
                    newType = RowRecogNFATypeEnum.ZERO_TO_MANY_RELUCTANT;
                }

                var copy = copier.Copy(node, newType, expressionCopier);
                repeated.Add(copy);
            }

            return repeated;
        }
Пример #15
0
        public static RowRecogExprNode Expand(
            RowRecogExprNode pattern,
            ExpressionCopier expressionCopier)
        {
            var visitor = new RowRecogExprNodeVisitorRepeat();
            pattern.Accept(visitor);
            var newParentNode = pattern;

            // expand permutes
            var permutes = visitor.Permutes;
            permutes.SortInPlace(
                (
                    o1,
                    o2) => {
                    if (o1.Level > o2.Level) {
                        return -1;
                    }

                    return o1.Level == o2.Level ? 0 : 1;
                });

            foreach (var permute in permutes) {
                var alteration = ExpandPermute(permute.Permute, expressionCopier);
                var optionalNewParent = Replace(
                    permute.OptionalParent,
                    permute.Permute,
                    Collections.SingletonList<RowRecogExprNode>(alteration));
                if (optionalNewParent != null) {
                    newParentNode = optionalNewParent;
                }
            }

            // expand atoms
            var atomPairs = visitor.Atoms;
            foreach (var pair in atomPairs) {
                var atom = pair.First;
                var expandedRepeat = ExpandRepeat(atom, atom.OptionalRepeat, atom.Type, ATOM_HANDLER, expressionCopier);
                var optionalNewParent = Replace(pair.Second, pair.First, expandedRepeat);
                if (optionalNewParent != null) {
                    newParentNode = optionalNewParent;
                }
            }

            // expand nested
            var nestedPairs = visitor.Nesteds;
            nestedPairs.SortInPlace(
                (
                    o1,
                    o2) => {
                    if (o1.Level > o2.Level) {
                        return -1;
                    }

                    return o1.Level == o2.Level ? 0 : 1;
                });

            foreach (var pair in nestedPairs) {
                var nested = pair.Nested;
                var expandedRepeat = ExpandRepeat(
                    nested,
                    nested.OptionalRepeat,
                    nested.Type,
                    NESTED_HANDLER,
                    expressionCopier);
                
                var optionalNewParent = Replace(pair.OptionalParent, pair.Nested, expandedRepeat);
                if (optionalNewParent != null) {
                    newParentNode = optionalNewParent;
                }
            }

            return newParentNode;
        }
Пример #16
0
        private static RowRecogNFAStrand RecursiveBuildStatesInternal(
            RowRecogExprNode node,
            IDictionary<string, ExprNode> variableDefinitions,
            IDictionary<string, Pair<int, bool>> variableStreams,
            Stack<int> nodeNumStack,
            bool[] exprRequiresMultimatchState
        )
        {
            if (node is RowRecogExprNodeAlteration) {
                var nodeNum = 0;

                IList<RowRecogNFAStateForgeBase> cumulativeStartStates = new List<RowRecogNFAStateForgeBase>();
                IList<RowRecogNFAStateForgeBase> cumulativeStates = new List<RowRecogNFAStateForgeBase>();
                IList<RowRecogNFAStateForgeBase> cumulativeEndStates = new List<RowRecogNFAStateForgeBase>();

                var isPassthrough = false;
                foreach (var child in node.ChildNodes) {
                    nodeNumStack.Push(nodeNum);
                    var strand = RecursiveBuildStatesInternal(
                        child,
                        variableDefinitions,
                        variableStreams,
                        nodeNumStack,
                        exprRequiresMultimatchState);
                    nodeNumStack.Pop();

                    cumulativeStartStates.AddAll(strand.StartStates);
                    cumulativeStates.AddAll(strand.AllStates);
                    cumulativeEndStates.AddAll(strand.EndStates);
                    if (strand.IsPassthrough) {
                        isPassthrough = true;
                    }

                    nodeNum++;
                }

                return new RowRecogNFAStrand(
                    cumulativeStartStates,
                    cumulativeEndStates,
                    cumulativeStates,
                    isPassthrough);
            }

            if (node is RowRecogExprNodeConcatenation) {
                var nodeNum = 0;

                var isPassthrough = true;
                IList<RowRecogNFAStateForgeBase> cumulativeStates = new List<RowRecogNFAStateForgeBase>();
                var strands = new RowRecogNFAStrand[node.ChildNodes.Count];

                foreach (var child in node.ChildNodes) {
                    nodeNumStack.Push(nodeNum);
                    strands[nodeNum] = RecursiveBuildStatesInternal(
                        child,
                        variableDefinitions,
                        variableStreams,
                        nodeNumStack,
                        exprRequiresMultimatchState);
                    nodeNumStack.Pop();

                    cumulativeStates.AddAll(strands[nodeNum].AllStates);
                    if (!strands[nodeNum].IsPassthrough) {
                        isPassthrough = false;
                    }

                    nodeNum++;
                }

                // determine start states: all states until the first non-passthrough start state
                IList<RowRecogNFAStateForgeBase> startStates = new List<RowRecogNFAStateForgeBase>();
                for (var i = 0; i < strands.Length; i++) {
                    startStates.AddAll(strands[i].StartStates);
                    if (!strands[i].IsPassthrough) {
                        break;
                    }
                }

                // determine end states: all states from the back until the last non-passthrough end state
                IList<RowRecogNFAStateForgeBase> endStates = new List<RowRecogNFAStateForgeBase>();
                for (var i = strands.Length - 1; i >= 0; i--) {
                    endStates.AddAll(strands[i].EndStates);
                    if (!strands[i].IsPassthrough) {
                        break;
                    }
                }

                // hook up the end state of each strand with the start states of each next strand
                for (var i = strands.Length - 1; i >= 1; i--) {
                    var current = strands[i];
                    for (var j = i - 1; j >= 0; j--) {
                        var prior = strands[j];

                        foreach (RowRecogNFAStateForgeBase endState in prior.EndStates) {
                            foreach (RowRecogNFAStateForgeBase startState in current.StartStates) {
                                endState.AddState(startState);
                            }
                        }

                        if (!prior.IsPassthrough) {
                            break;
                        }
                    }
                }

                return new RowRecogNFAStrand(startStates, endStates, cumulativeStates, isPassthrough);
            }

            if (node is RowRecogExprNodeNested) {
                var nested = (RowRecogExprNodeNested) node;
                nodeNumStack.Push(0);
                var strand = RecursiveBuildStatesInternal(
                    node.ChildNodes[0],
                    variableDefinitions,
                    variableStreams,
                    nodeNumStack,
                    exprRequiresMultimatchState);
                nodeNumStack.Pop();

                var isPassthrough = strand.IsPassthrough || nested.Type.IsOptional();

                // if this is a repeating node then pipe back each end state to each begin state
                if (nested.Type.IsMultipleMatches()) {
                    foreach (RowRecogNFAStateForgeBase endstate in strand.EndStates) {
                        foreach (RowRecogNFAStateForgeBase startstate in strand.StartStates) {
                            if (!endstate.NextStates.Contains(startstate)) {
                                endstate.NextStates.Add(startstate);
                            }
                        }
                    }
                }

                return new RowRecogNFAStrand(strand.StartStates, strand.EndStates, strand.AllStates, isPassthrough);
            }

            var atom = (RowRecogExprNodeAtom) node;

            // assign stream number for single-variables for most direct expression eval; multiple-variable gets -1
            var streamNum = variableStreams.Get(atom.Tag).First;
            var multiple = variableStreams.Get(atom.Tag).Second;
            var expression = variableDefinitions.Get(atom.Tag);
            var exprRequiresMultimatch = exprRequiresMultimatchState[streamNum];

            RowRecogNFAStateForgeBase nextState;
            if (atom.Type == RowRecogNFATypeEnum.ZERO_TO_MANY ||
                atom.Type == RowRecogNFATypeEnum.ZERO_TO_MANY_RELUCTANT) {
                nextState = new RowRecogNFAStateZeroToManyForge(
                    ToString(nodeNumStack),
                    atom.Tag,
                    streamNum,
                    multiple,
                    atom.Type.IsGreedy(),
                    exprRequiresMultimatch,
                    expression);
            }
            else if (atom.Type == RowRecogNFATypeEnum.ONE_TO_MANY ||
                     atom.Type == RowRecogNFATypeEnum.ONE_TO_MANY_RELUCTANT) {
                nextState = new RowRecogNFAStateOneToManyForge(
                    ToString(nodeNumStack),
                    atom.Tag,
                    streamNum,
                    multiple,
                    atom.Type.IsGreedy(),
                    exprRequiresMultimatch,
                    expression);
            }
            else if (atom.Type == RowRecogNFATypeEnum.ONE_OPTIONAL ||
                     atom.Type == RowRecogNFATypeEnum.ONE_OPTIONAL_RELUCTANT) {
                nextState = new RowRecogNFAStateOneOptionalForge(
                    ToString(nodeNumStack),
                    atom.Tag,
                    streamNum,
                    multiple,
                    atom.Type.IsGreedy(),
                    exprRequiresMultimatch,
                    expression);
            }
            else if (expression == null) {
                nextState = new RowRecogNFAStateAnyOneForge(ToString(nodeNumStack), atom.Tag, streamNum, multiple);
            }
            else {
                nextState = new RowRecogNFAStateFilterForge(
                    ToString(nodeNumStack),
                    atom.Tag,
                    streamNum,
                    multiple,
                    exprRequiresMultimatch,
                    expression);
            }

            return new RowRecogNFAStrand(
                Collections.SingletonList(nextState),
                Collections.SingletonList(nextState),
                Collections.SingletonList(nextState),
                atom.Type.IsOptional());
        }
Пример #17
0
        public static RowRecogPlan ValidateAndPlan(
            IContainer container,
            EventType parentEventType,
            bool unbound,
            StatementBaseInfo @base,
            StatementCompileTimeServices services)
        {
            var statementRawInfo = @base.StatementRawInfo;
            var matchRecognizeSpec = @base.StatementSpec.Raw.MatchRecognizeSpec;
            var annotations = statementRawInfo.Annotations;
            var iterateOnly = HintEnum.ITERATE_ONLY.GetHint(annotations) != null;
            var additionalForgeables = new List<StmtClassForgeableFactory>();

            // Expanded pattern already there
            RowRecogExprNode expandedPatternNode = matchRecognizeSpec.Pattern;

            // Determine single-row and multiple-row variables
            var variablesSingle = new LinkedHashSet<string>();
            var variablesMultiple = new LinkedHashSet<string>();
            RowRecogHelper.RecursiveInspectVariables(expandedPatternNode, false, variablesSingle, variablesMultiple);

            // each variable gets associated with a stream number (multiple-row variables as well to hold the current event for the expression).
            var streamNum = 0;
            var variableStreams = new LinkedHashMap<string, Pair<int, bool>>();
            foreach (var variableSingle in variablesSingle) {
                variableStreams.Put(variableSingle, new Pair<int, bool>(streamNum, false));
                streamNum++;
            }

            foreach (var variableMultiple in variablesMultiple) {
                variableStreams.Put(variableMultiple, new Pair<int, bool>(streamNum, true));
                streamNum++;
            }

            // mapping of stream to variable
            var streamVariables = new OrderedListDictionary<int, string>();
            foreach (var entry in variableStreams) {
                streamVariables.Put(entry.Value.First, entry.Key);
            }

            // determine visibility rules
            var visibility = RowRecogHelper.DetermineVisibility(expandedPatternNode);

            // assemble all single-row variables for expression validation
            var allStreamNames = new string[variableStreams.Count];
            var allTypes = new EventType[variableStreams.Count];

            streamNum = 0;
            foreach (var variableSingle in variablesSingle) {
                allStreamNames[streamNum] = variableSingle;
                allTypes[streamNum] = parentEventType;
                streamNum++;
            }

            foreach (var variableMultiple in variablesMultiple) {
                allStreamNames[streamNum] = variableMultiple;
                allTypes[streamNum] = parentEventType;
                streamNum++;
            }

            // determine type service for use with DEFINE
            // validate each DEFINE clause expression
            ISet<string> definedVariables = new HashSet<string>();
            IList<ExprAggregateNode> aggregateNodes = new List<ExprAggregateNode>();
            var isExprRequiresMultimatchState = new bool[variableStreams.Count];
            var previousNodes = new OrderedListDictionary<int, IList<ExprPreviousMatchRecognizeNode>>();

            for (var defineIndex = 0; defineIndex < matchRecognizeSpec.Defines.Count; defineIndex++) {
                MatchRecognizeDefineItem defineItem = matchRecognizeSpec.Defines[defineIndex];
                if (definedVariables.Contains(defineItem.Identifier)) {
                    throw new ExprValidationException(
                        "Variable '" + defineItem.Identifier + "' has already been defined");
                }

                definedVariables.Add(defineItem.Identifier);

                // stream-type visibilities handled here
                var typeServiceDefines = BuildDefineStreamTypeServiceDefine(
                    defineIndex,
                    variableStreams,
                    defineItem,
                    visibility,
                    parentEventType,
                    statementRawInfo,
                    services);

                var exprNodeResult = HandlePreviousFunctions(defineItem.Expression, previousNodes);
                var validationContext = new ExprValidationContextBuilder(typeServiceDefines, statementRawInfo, services)
                    .WithAllowBindingConsumption(true)
                    .WithDisablePropertyExpressionEventCollCache(true)
                    .Build();

                ExprNode validated;
                try {
                    // validate
                    validated = ExprNodeUtilityValidate.GetValidatedSubtree(
                        ExprNodeOrigin.MATCHRECOGDEFINE,
                        exprNodeResult,
                        validationContext);

                    // check aggregates
                    defineItem.Expression = validated;
                    ExprAggregateNodeUtil.GetAggregatesBottomUp(validated, aggregateNodes);
                    if (!aggregateNodes.IsEmpty()) {
                        throw new ExprValidationException("An aggregate function may not appear in a DEFINE clause");
                    }
                }
                catch (ExprValidationException ex) {
                    throw new ExprValidationException(
                        "Failed to validate condition expression for variable '" +
                        defineItem.Identifier +
                        "': " +
                        ex.Message,
                        ex);
                }

                // determine access to event properties from multi-matches
                var visitor = new ExprNodeStreamRequiredVisitor();
                validated.Accept(visitor);
                var streamsRequired = visitor.StreamsRequired;
                foreach (var streamRequired in streamsRequired) {
                    if (streamRequired >= variableStreams.Count) {
                        var streamNumIdent = variableStreams.Get(defineItem.Identifier).First;
                        isExprRequiresMultimatchState[streamNumIdent] = true;
                        break;
                    }
                }
            }

            var defineAsksMultimatches = CollectionUtil.IsAnySet(isExprRequiresMultimatchState);

            // determine type service for use with MEASURE
            IDictionary<string, object> measureTypeDef = new LinkedHashMap<string, object>();
            foreach (var variableSingle in variablesSingle) {
                measureTypeDef.Put(variableSingle, parentEventType);
            }

            foreach (var variableMultiple in variablesMultiple) {
                measureTypeDef.Put(variableMultiple, new[] {parentEventType});
            }

            var compositeTypeName = services.EventTypeNameGeneratorStatement.AnonymousRowrecogCompositeName;
            var compositeTypeMetadata = new EventTypeMetadata(
                compositeTypeName,
                @base.ModuleName,
                EventTypeTypeClass.MATCHRECOGDERIVED,
                EventTypeApplicationType.OBJECTARR,
                NameAccessModifier.TRANSIENT,
                EventTypeBusModifier.NONBUS,
                false,
                EventTypeIdPair.Unassigned());
            var compositeEventType = BaseNestableEventUtil.MakeOATypeCompileTime(
                compositeTypeMetadata,
                measureTypeDef,
                null,
                null,
                null,
                null,
                services.BeanEventTypeFactoryPrivate,
                services.EventTypeCompileTimeResolver);
            services.EventTypeCompileTimeRegistry.NewType(compositeEventType);
            StreamTypeService compositeTypeServiceMeasure =
                new StreamTypeServiceImpl(compositeEventType, "MATCH_RECOGNIZE", true);

            // find MEASURE clause aggregations
            var measureReferencesMultivar = false;
            IList<ExprAggregateNode> measureAggregateExprNodes = new List<ExprAggregateNode>();
            foreach (var measureItem in matchRecognizeSpec.Measures) {
                ExprAggregateNodeUtil.GetAggregatesBottomUp(measureItem.Expr, measureAggregateExprNodes);
            }

            AggregationServiceForgeDesc[] aggregationServices = null;
            if (!measureAggregateExprNodes.IsEmpty()) {
                aggregationServices = PlanAggregations(
                    measureAggregateExprNodes,
                    compositeTypeServiceMeasure,
                    allStreamNames,
                    allTypes,
                    streamVariables,
                    variablesMultiple,
                    @base,
                    services);
                foreach (AggregationServiceForgeDesc svc in aggregationServices) {
                    if (svc != null) {
                        additionalForgeables.AddAll(svc.AdditionalForgeables);
                    }
                }
            }

            // validate each MEASURE clause expression
            IDictionary<string, object> rowTypeDef = new LinkedHashMap<string, object>();
            var streamRefVisitor = new ExprNodeStreamUseCollectVisitor();
            foreach (var measureItem in matchRecognizeSpec.Measures) {
                if (measureItem.Name == null) {
                    throw new ExprValidationException(
                        "The measures clause requires that each expression utilizes the AS keyword to assign a column name");
                }

                var validated = ValidateMeasureClause(
                    measureItem.Expr,
                    compositeTypeServiceMeasure,
                    variablesMultiple,
                    variablesSingle,
                    statementRawInfo,
                    services);
                measureItem.Expr = validated;
                rowTypeDef.Put(measureItem.Name, validated.Forge.EvaluationType);
                validated.Accept(streamRefVisitor);
            }

            // Determine if any of the multi-var streams are referenced in the measures (non-aggregated only)
            foreach (var @ref in streamRefVisitor.Referenced) {
                var rootPropName = @ref.RootPropertyNameIfAny;
                if (rootPropName != null) {
                    if (variablesMultiple.Contains(rootPropName)) {
                        measureReferencesMultivar = true;
                        break;
                    }
                }

                var streamRequired = @ref.StreamReferencedIfAny;
                if (streamRequired != null) {
                    var streamVariable = streamVariables.Get(streamRequired.Value);
                    if (streamVariable != null) {
                        var def = variableStreams.Get(streamVariable);
                        if (def != null && def.Second) {
                            measureReferencesMultivar = true;
                            break;
                        }
                    }
                }
            }

            var collectMultimatches = measureReferencesMultivar || defineAsksMultimatches;

            // create rowevent type
            var rowTypeName = services.EventTypeNameGeneratorStatement.AnonymousRowrecogRowName;
            var rowTypeMetadata = new EventTypeMetadata(
                rowTypeName,
                @base.ModuleName,
                EventTypeTypeClass.MATCHRECOGDERIVED,
                EventTypeApplicationType.MAP,
                NameAccessModifier.TRANSIENT,
                EventTypeBusModifier.NONBUS,
                false,
                EventTypeIdPair.Unassigned());
            var rowEventType = BaseNestableEventUtil.MakeMapTypeCompileTime(
                rowTypeMetadata,
                rowTypeDef,
                null,
                null,
                null,
                null,
                services.BeanEventTypeFactoryPrivate,
                services.EventTypeCompileTimeResolver);
            services.EventTypeCompileTimeRegistry.NewType(rowEventType);

            // validate partition-by expressions, if any
            ExprNode[] partitionBy;
            MultiKeyClassRef partitionMultiKey;
            if (!matchRecognizeSpec.PartitionByExpressions.IsEmpty()) {
                StreamTypeService typeServicePartition = new StreamTypeServiceImpl(
                    parentEventType,
                    "MATCH_RECOGNIZE_PARTITION",
                    true);
                IList<ExprNode> validated = new List<ExprNode>();
                var validationContext =
                    new ExprValidationContextBuilder(typeServicePartition, statementRawInfo, services)
                        .WithAllowBindingConsumption(true)
                        .Build();
                foreach (var partitionExpr in matchRecognizeSpec.PartitionByExpressions) {
                    validated.Add(
                        ExprNodeUtilityValidate.GetValidatedSubtree(
                            ExprNodeOrigin.MATCHRECOGPARTITION,
                            partitionExpr,
                            validationContext));
                }

                matchRecognizeSpec.PartitionByExpressions = validated;
                partitionBy = ExprNodeUtilityQuery.ToArray(validated);
                MultiKeyPlan multiKeyPlan = MultiKeyPlanner.PlanMultiKey(partitionBy, false, @base.StatementRawInfo, services.SerdeResolver);
                partitionMultiKey = multiKeyPlan.ClassRef;
                additionalForgeables.AddAll(multiKeyPlan.MultiKeyForgeables);
            }
            else {
                partitionBy = null;
                partitionMultiKey = null;
            }

            // validate interval if present
            if (matchRecognizeSpec.Interval != null) {
                var validationContext =
                    new ExprValidationContextBuilder(new StreamTypeServiceImpl(false), statementRawInfo, services)
                        .WithAllowBindingConsumption(true)
                        .Build();
                var validated = (ExprTimePeriod) ExprNodeUtilityValidate.GetValidatedSubtree(
                    ExprNodeOrigin.MATCHRECOGINTERVAL,
                    matchRecognizeSpec.Interval.TimePeriodExpr,
                    validationContext);
                matchRecognizeSpec.Interval.TimePeriodExpr = validated;
            }

            // compile variable definition expressions
            IDictionary<string, ExprNode> variableDefinitions = new Dictionary<string, ExprNode>();
            foreach (var defineItem in matchRecognizeSpec.Defines) {
                variableDefinitions.Put(defineItem.Identifier, defineItem.Expression);
            }

            // create evaluators
            var columnNames = new string[matchRecognizeSpec.Measures.Count];
            var columnForges = new ExprNode[matchRecognizeSpec.Measures.Count];
            var count = 0;
            foreach (var measureItem in matchRecognizeSpec.Measures) {
                columnNames[count] = measureItem.Name;
                columnForges[count] = measureItem.Expr;
                count++;
            }

            // build states
            var strand = RowRecogHelper.BuildStartStates(
                expandedPatternNode,
                variableDefinitions,
                variableStreams,
                isExprRequiresMultimatchState);
            var startStates = strand.StartStates.ToArray();
            RowRecogNFAStateForge[] allStates = strand.AllStates.ToArray();

            if (Log.IsInfoEnabled) {
                Log.Info("NFA tree:\n" + RowRecogNFAViewUtil.Print(startStates));
            }

            // determine names of multimatching variables
            string[] multimatchVariablesArray;
            int[] multimatchStreamNumToVariable;
            int[] multimatchVariableToStreamNum;
            if (variablesSingle.Count == variableStreams.Count) {
                multimatchVariablesArray = new string[0];
                multimatchStreamNumToVariable = new int[0];
                multimatchVariableToStreamNum = new int[0];
            }
            else {
                multimatchVariablesArray = new string[variableStreams.Count - variablesSingle.Count];
                multimatchVariableToStreamNum = new int[multimatchVariablesArray.Length];
                multimatchStreamNumToVariable = new int[variableStreams.Count];
                CompatExtensions.Fill(multimatchStreamNumToVariable, -1);
                count = 0;
                foreach (var entry in variableStreams) {
                    if (entry.Value.Second) {
                        var index = count;
                        multimatchVariablesArray[index] = entry.Key;
                        multimatchVariableToStreamNum[index] = entry.Value.First;
                        multimatchStreamNumToVariable[entry.Value.First] = index;
                        count++;
                    }
                }
            }

            var numEventsEventsPerStreamDefine =
                defineAsksMultimatches ? variableStreams.Count + 1 : variableStreams.Count;

            // determine interval-or-terminated
            var orTerminated = matchRecognizeSpec.Interval != null && matchRecognizeSpec.Interval.IsOrTerminated;
            TimePeriodComputeForge intervalCompute = null;
            if (matchRecognizeSpec.Interval != null) {
                intervalCompute = matchRecognizeSpec.Interval.TimePeriodExpr.TimePeriodComputeForge;
            }

            EventType multimatchEventType = null;
            if (defineAsksMultimatches) {
                multimatchEventType = GetDefineMultimatchEventType(variableStreams, parentEventType, @base, services);
            }

            // determine previous-access indexes and assign "prev" node indexes
            // Since an expression such as "prior(2, price), prior(8, price)" translates into {2, 8} the relative index is {0, 1}.
            // Map the expression-supplied index to a relative index
            int[] previousRandomAccessIndexes = null;
            if (!previousNodes.IsEmpty()) {
                previousRandomAccessIndexes = new int[previousNodes.Count];
                var countPrev = 0;
                foreach (var entry in previousNodes) {
                    previousRandomAccessIndexes[countPrev] = entry.Key;
                    foreach (var callback in entry.Value) {
                        callback.AssignedIndex = countPrev;
                    }

                    countPrev++;
                }
            }

            RowRecogDescForge forge = new RowRecogDescForge(
                parentEventType,
                rowEventType,
                compositeEventType,
                multimatchEventType,
                multimatchStreamNumToVariable,
                multimatchVariableToStreamNum,
                partitionBy,
                partitionMultiKey,
                variableStreams,
                matchRecognizeSpec.Interval != null,
                iterateOnly,
                unbound,
                orTerminated,
                collectMultimatches,
                defineAsksMultimatches,
                numEventsEventsPerStreamDefine,
                multimatchVariablesArray,
                startStates,
                allStates,
                matchRecognizeSpec.IsAllMatches,
                matchRecognizeSpec.Skip.Skip,
                columnForges,
                columnNames,
                intervalCompute,
                previousRandomAccessIndexes,
                aggregationServices);
            
            return new RowRecogPlan(forge, additionalForgeables);
        }