/// <summary>
        /// Parse one line expression with ++ -- (postfix), function call, array subscript, slice subscript operators.
        /// Enumerator must be given with reversed ordering of arguments. This corresponds to Operator2Expression from Grammar.
        /// </summary>
        public static ExpressionNode ParseOperator2 <TSymbol>(IEnumerator <IParseTree <TSymbol> > opExpr) where TSymbol : ISymbol <TSymbol>
        {
            // O2 -> O1 (     "++" | "--"
            //           |    "(" (E ",")* E? ")"
            //           |    "[" E "]"
            //           |    "[" E? ".." E? "]"
            //          )*
            if (opExpr.Current.Symbol.IsTerminal)
            {
                string curText = ASTBuilder.EatTerminal(opExpr);
                switch (curText)
                {
                case "++":
                case "--":
                    return(OperatorNode.BuildUnaryOperator(2, curText, ParseOperator2(opExpr)));

                case ")":
                    var args = new LinkedList <ExpressionNode>();
                    while (!ASTBuilder.EatSymbol("(", opExpr))
                    {
                        ASTBuilder.EatSymbol(",", opExpr); // eat ',' before arg - f(x,y,z,) is also possible
                        args.AddFirst(GetExpressionNode(opExpr.Current));
                        opExpr.MoveNext();                 // move to next param with ','
                    }
                    var res = new FunctionCallNode();
                    res.Arguments = new List <ExpressionNode>(args);
                    // TODO: can something return a 'pointer' to function which we can call to execute?
                    res.Name = opExpr.Current.Fragment.GetOriginText();
                    return(res);

                case "]":
                    var  toExpr   = ASTBuilder.EatNonTerminal(opExpr);  // slice to or index
                    bool isSlice  = ASTBuilder.EatSymbol("..", opExpr);
                    var  fromExpr = ASTBuilder.EatNonTerminal(opExpr);
                    Debug.Assert(ASTBuilder.EatSymbol("[", opExpr));
                    if (isSlice)     // slice subscript
                    {
                        return(SliceNode.CreateSliceNode(ParseOperator2(opExpr),
                                                         GetExpressionOrNull(fromExpr), GetExpressionOrNull(toExpr)));
                    }
                    else     // array subscript
                    {
                        return(ElementNode.Create(ParseOperator2(opExpr), GetExpressionNode(toExpr)));
                    }

                default:
                    Debug.Assert(false);
                    break;
                }
            }
            else // non-terminal -> Operator1Expression
            {
                var op1 = opExpr.Current;
                Debug.Assert(!opExpr.MoveNext());      // it must be final symbol in this enumeration
                var node = ASTBuilder.FirstChild(op1); // Operator1Expression -> Operator0Expression
                // Operator0Expression -> AtomicExpression | "(" Expression ")";
                var childs = ASTBuilder.ChildrenEnumerator(node);
                Debug.Assert(childs.MoveNext());
                if (ASTBuilder.EatSymbol("(", childs))
                {
                    return(GetExpressionNode(childs.Current)); // Expression
                }
                else // proceed with AtomicExpression
                {
                    return(ResolveAtomicExpression(childs.Current));
                }
            }
            return(null);
        }