public ParserType Visit(ParserApp app)
        {
            var lambdaType = app.Lambda.Accept(this);
            var expr       = app.Expression.Accept(this);

            if (!(lambdaType is ParserLambdaType))
            {
                throw new ArgumentException("In an application left expression must be a lambda. Type: " + lambdaType.ToString());
            }

            ParserLambdaType lambda = lambdaType as ParserLambdaType;

            if (!lambda.InputType.Equivalent(expr))
            {
                throw new ArgumentException(String.Format(
                                                "In an application the type of the right expression must match the type of the bound variable " +
                                                "Expr type: {0} Variable type: {1}",
                                                expr.ToString(), lambda.InputType.ToString()));
            }

            ParserType logicType = new ParserLogicType();

            app.Type = logicType;
            return(logicType);
        }
        public ParserType Visit(ParserBinder binder)
        {
            // idea: copy our current context, add the new bound variable, visit, remove bound variable
            //   then merge back into our current context
            var newCtx = CloneContext();

            newCtx[binder.VariableName] = binder.VariableType;
            var        visitor = new TypeVisitor(newCtx);
            ParserType subExpr = binder.Expr.Accept(visitor);

            newCtx.Remove(binder.VariableName);
            JoinContext(newCtx);

            if (subExpr.Typ != ParserTypeEnum.Logic || !(subExpr is ParserLogicType))
            {
                throw new ArgumentException("Bound subexpression must be a logical expression. Type: " + subExpr.ToString());
            }

            var logicalExpr = subExpr as ParserLogicType;

            switch (binder.OpType)
            {
            case ParserBinderType.Exists:
                if ((binder.VariableType.Flags & ParserTypeFlags.Overt) == 0)
                {
                    throw new ArgumentException("Exists can only bind overt variables");
                }
                ParserType logicType = new ParserLogicType();
                binder.Type = logicType;
                return(logicType);

            case ParserBinderType.Lambda:
                ParserType newType = new ParserLambdaType(binder.VariableType);
                binder.Type = newType;
                return(newType);

            case ParserBinderType.The:
                if (binder.VariableType is ParserNumType)
                {
                    ParserType numType = new ParserNumType();
                    binder.Type = numType;
                    return(numType);
                }
                else
                {
                    throw new ArgumentException("'The' binder can only bind Nat types");
                }

            default:
                throw new ArgumentException("Unknown Binder");
            }
        }
        public ParserType Visit(ParserStringConstant constant)
        {
            switch (constant.Value)
            {
            case "T":
            case "F":
                ParserType logicType = new ParserLogicType();
                constant.Type = logicType;
                return(logicType);

            default:
                throw new ArgumentException("Unknown Constant: " + constant.Value);
            }
        }
        public ParserType Visit(ParserBinaryOp op)
        {
            ParserType leftType  = op.Left.Accept(this);
            ParserType rightType = op.Right.Accept(this);

            if (leftType.Typ != rightType.Typ)
            {
                throw new ArgumentException(String.Format("Types in a binary operation must be the same. Left: {0} Right: {0}",
                                                          leftType.ToString(), rightType.ToString()));
            }

            switch (op.OpType)
            {
            case ParserBinaryOpType.Add:
                if (leftType.Flags.HasFlag(ParserTypeFlags.Number) && leftType.Flags.HasFlag(ParserTypeFlags.Number))
                {
                    op.Type = leftType;
                    return(leftType);
                }
                else
                {
                    throw new ArgumentException("Cannot Add these expressions");
                }

            case ParserBinaryOpType.Mul:
                if (leftType.Flags.HasFlag(ParserTypeFlags.Number) && leftType.Flags.HasFlag(ParserTypeFlags.Number))
                {
                    op.Type = leftType;
                    return(leftType);
                }
                else
                {
                    throw new ArgumentException("Cannot Multiply these expressions");
                }

            case ParserBinaryOpType.And:
                if (leftType.Flags.HasFlag(ParserTypeFlags.Logic) && leftType.Flags.HasFlag(ParserTypeFlags.Logic))
                {
                    op.Type = leftType;
                    return(leftType);
                }
                else
                {
                    throw new ArgumentException("Cannot & these expressions");
                }

            case ParserBinaryOpType.Or:
                if (leftType.Flags.HasFlag(ParserTypeFlags.Logic) && leftType.Flags.HasFlag(ParserTypeFlags.Logic))
                {
                    op.Type = leftType;
                    return(leftType);
                }
                else
                {
                    throw new ArgumentException("Cannot | these expressions");
                }

            case ParserBinaryOpType.EQ:
                if (leftType.Flags.HasFlag(ParserTypeFlags.Discrete) && leftType.Flags.HasFlag(ParserTypeFlags.Discrete))
                {
                    ParserType logicType = new ParserLogicType();
                    op.Type = logicType;
                    return(logicType);
                }
                else
                {
                    throw new ArgumentException("Cannot == these expressions");
                }

            case ParserBinaryOpType.NEQ:
                if (leftType.Flags.HasFlag(ParserTypeFlags.Hausdorff) && leftType.Flags.HasFlag(ParserTypeFlags.Hausdorff))
                {
                    ParserType logicType = new ParserLogicType();
                    op.Type = logicType;
                    return(logicType);
                }
                else
                {
                    throw new ArgumentException("Cannot != these expressions");
                }

            case ParserBinaryOpType.GT:
                if (leftType.Flags.HasFlag(ParserTypeFlags.StrictOrder) && leftType.Flags.HasFlag(ParserTypeFlags.StrictOrder))
                {
                    ParserType logicType = new ParserLogicType();
                    op.Type = logicType;
                    return(logicType);
                }
                else
                {
                    throw new ArgumentException("Cannot > these expressions");
                }

            case ParserBinaryOpType.LT:
                if (leftType.Flags.HasFlag(ParserTypeFlags.StrictOrder) && leftType.Flags.HasFlag(ParserTypeFlags.StrictOrder))
                {
                    ParserType logicType = new ParserLogicType();
                    op.Type = logicType;
                    return(logicType);
                }
                else
                {
                    throw new ArgumentException("Cannot < these expressions");
                }

            case ParserBinaryOpType.GTE:
                if (leftType.Flags.HasFlag(ParserTypeFlags.LooseOrder) && leftType.Flags.HasFlag(ParserTypeFlags.LooseOrder))
                {
                    ParserType logicType = new ParserLogicType();
                    op.Type = logicType;
                    return(logicType);
                }
                else
                {
                    throw new ArgumentException("Cannot >= these expressions");
                }

            case ParserBinaryOpType.LTE:
                if (leftType.Flags.HasFlag(ParserTypeFlags.LooseOrder) && leftType.Flags.HasFlag(ParserTypeFlags.LooseOrder))
                {
                    ParserType logicType = new ParserLogicType();
                    op.Type = logicType;
                    return(logicType);
                }
                else
                {
                    throw new ArgumentException("Cannot <= these expressions");
                }

            default:
                throw new ArgumentException("Unknown Binary Operation");
            }
        }