public override void EnterRollGroup([NotNull] DiceGrammarParser.RollGroupContext context)
 {
     // add GroupPartialNode to act as a sentinel and accumulator for all group stuff
     // note that there might be a node after GroupPartialNode on the stack to indicate NumTimes
     // (said node is rolled into the GroupPartialNode upon parsing the inner group contents)
     Stack.Push(new GroupPartialNode());
 }
        public override void ExitRollGroup([NotNull] DiceGrammarParser.RollGroupContext context)
        {
            // our stack looks like a GroupPartialNode followed by all group_extras and group_functions
            List <DiceAST> groupNodes = new List <DiceAST>();

            for (int i = 0; i < context.grouped_extras().Length + context.group_function().Length; i++)
            {
                groupNodes.Add(Stack.Pop());
            }

            groupNodes.Reverse();
            var partial = (GroupPartialNode)Stack.Pop();

            foreach (var node in groupNodes)
            {
                if (node is RerollNode)
                {
                    partial.AddReroll((RerollNode)node);
                }
                else if (node is KeepNode)
                {
                    partial.AddKeep((KeepNode)node);
                }
                else if (node is SuccessNode)
                {
                    partial.AddSuccess((SuccessNode)node);
                }
                else if (node is SortNode)
                {
                    partial.AddSort((SortNode)node);
                }
                else if (node is FunctionNode)
                {
                    partial.AddFunction((FunctionNode)node);
                }
                else
                {
                    throw new InvalidOperationException("Unexpected node type when resolving group node");
                }
            }

            Stack.Push(partial.CreateGroupNode());
        }