public IToken Interact(int x, int y)
        {
            System.Diagnostics.Trace.WriteLine($"Interact({x}, {y});");

            var t = new Thread(() =>
            {
                interactToken = Reduce(ApOperator.Acquire(interactToken, ConsOperator.Acquire(ConstantOperator.Acquire(x), ConstantOperator.Acquire(y))));
            }, 1024 * 1024 * 1024);

            t.Start();
            t.Join();
            ClearCaches();
            GC.Collect();
            GC.WaitForPendingFinalizers();
            return(lastInteractResult);
        }
        public IToken ParseTokens(string[] ops)
        {
            var    stack = new Stack <ApOperator>();
            IToken token = null;

            for (int i = 0; i < ops.Length; i++)
            {
                var op = ops[i];

                switch (op)
                {
                case "ap":
                    token = new ApOperator();
                    break;

                case "inc":
                    token = IncOperator.Acquire();
                    break;

                case "dec":
                    token = DecOperator.Acquire();
                    break;

                case "neg":
                    token = NegOperator.Acquire();
                    break;

                case "add":
                    token = AddOperator.Acquire();
                    break;

                case "mul":
                    token = MulOperator.Acquire();
                    break;

                //case "l":
                //    token = new LOperator();
                //    break;

                case "div":
                    token = DivOperator.Acquire();
                    break;

                //case "pwr2":
                //    return (new Pwr2Operator(), index);

                case "t":
                    token = KComb.Acquire();
                    break;

                //case "f":
                //    return (new FComb(), index);

                case "s":
                    token = SComb.Acquire();
                    break;

                case "c":
                    token = CComb.Acquire();
                    break;

                case "b":
                    token = BComb.Acquire();
                    break;

                case "i":
                    token = IComb.Acquire();
                    break;

                case "cons":
                case "vec":
                    token = ConsOperator.Acquire();
                    break;

                case "car":
                    token = CarOperator.Acquire();
                    break;

                case "cdr":
                    token = CdrOperator.Acquire();
                    break;

                case "nil":
                    token = NilOperator.Acquire();
                    break;

                case "isnil":
                    token = IsNilOperator.Acquire();
                    break;

                case "eq":
                    token = EqOperator.Acquire();
                    break;

                case "if0":
                    token = new If0Operator();
                    break;

                case "lt":
                    token = LtOperator.Acquire();
                    break;

                //case "mod":
                //    token = new ModOperator();
                //    break;

                //case "dem":
                //    token = new DemodOperator();
                //    break;

                case "interact":
                    token = new InteractOperator();
                    break;

                default:
                    if (decimal.TryParse(op, out var constant))     // int constant
                    {
                        token = ConstantOperator.Acquire(decimal.Parse(op));
                    }
                    else if (op.StartsWith("x"))
                    {
                        token = VarOperator.Acquire(op);
                    }
                    else if (op.StartsWith(":"))     // variable reference
                    {
                        token = LateBoundToken.Acquire(op);
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }

                    break;
                }

                if (stack.Count == 0)
                {
                    if (!(token is ApOperator))
                    {
                        if (i != ops.Length - 1)
                        {
                            throw new InvalidOperationException();
                        }

                        return(token);
                    }

                    stack.Push((ApOperator)token);
                }
                else
                {
                    var top = stack.Peek();
                    if (top.f == null)
                    {
                        top.f = token;
                        if (token is ApOperator ap)
                        {
                            stack.Push(ap);
                        }
                    }
                    else if (top.x == null)
                    {
                        top.x = token;
                        if (token is ApOperator ap)
                        {
                            stack.Push(ap);
                        }
                        else
                        {
                            while (stack.Count > 0 && stack.Peek().x != null)
                            {
                                token = stack.Pop();
                            }
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }
                }
            }

            if (stack.Count == 1)
            {
                return(stack.Pop());
            }

            return(token);
        }