/* 
 * Copyright (C) 2003-2004 Hye-Shik Chang. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include "Python.h"

/* anonfunc object ***********************************************************/

enum anonfunc_nodetype {
	AFTYPE_LEAF=0,		/* Leaf node. Just returns selected argument */
	AFTYPE_UNARY,		/* Node for unary operators. */
	AFTYPE_BINARY,		/* Node for binary operators. */
	AFTYPE_TERNARY,		/* Node for ternary operators. */
	AFTYPE_INTARG,		/* Node for intargfunc operators. */
	AFTYPE_INTINTARG,	/* Node for intintargfunc operators. */
	AFTYPE_RICHCMP,		/* Node for richcmpfunc operator. */
	AFTYPE_MAX,
};

static const int anonfunc_typeobjargs[] = {0, 1, 2, 3, 1, 1, 2};
static const int anonfunc_typeintargs[] = {0, 0, 0, 0, 1, 2, 1};

typedef struct {
	PyObject_HEAD
	enum anonfunc_nodetype nodetype;
	void *operator;
	PyObject *argfactor;
	PyObject *argdefault;
	void *argext;
} anonfuncobject;

static PyTypeObject anonfunc_type;

static anonfuncobject *
_anonfunc_new_internal(PyTypeObject *type, enum anonfunc_nodetype nodetype,
		       void *op, PyObject *argfactor, PyObject *argdefault)
{
	anonfuncobject *af;

	af = (anonfuncobject *)type->tp_alloc(type, 0);
	if (af == NULL)
		return NULL;
	af->nodetype = nodetype;
	af->operator = op;
	af->argfactor = argfactor;
	Py_XINCREF(argfactor);
	af->argdefault = argdefault;
	Py_XINCREF(argdefault);
	af->argext = NULL;

	return af;
}

/* anonfunc constructor for leaf nodes */
static PyObject *
anonfunc_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	PyObject *argkey, *argdefault = NULL;

	if (!PyArg_UnpackTuple(args, "anonfunc", 1, 2, &argkey, &argdefault))
		return NULL;

	if (PyInt_Check(argkey)) {
		if (PyInt_AS_LONG(argkey) < 0) {
			PyErr_SetString(PyExc_ValueError,
					"argkey is out of range.");
			return NULL;
		}
	}
	else if (!PyString_Check(argkey)) {
		PyErr_SetString(PyExc_TypeError,
				"argkey must be an integer or string.");
		return NULL;
	}

	return (PyObject *)_anonfunc_new_internal(type, AFTYPE_LEAF, NULL,
						  argkey, argdefault);
}

/* anonfunc constructor for operator instance nodes */
static PyObject *
anonfunc_new_ops(void *op, enum anonfunc_nodetype nodetype, ...)
{
	anonfuncobject *af;
	int i, objargs, intargs, *iargs;
	va_list va;

	assert(nodetype != AFTYPE_LEAF && nodetype < AFTYPE_MAX);
	af = _anonfunc_new_internal(&anonfunc_type, nodetype, op, NULL, NULL);
	if (af == NULL)
		return NULL;

	objargs = anonfunc_typeobjargs[nodetype];
	intargs = anonfunc_typeintargs[nodetype];
	af->argfactor = PyTuple_New(objargs);
	af->argext = PyMem_New(int, intargs);
	if (af->argfactor == NULL || af->argext == NULL) {
		Py_DECREF(af);
		return NULL;
	}

	iargs = af->argext;
	va_start(va, nodetype);
	for (i = 0; i < objargs; i++) {
		PyObject *arg;

		arg = va_arg(va, PyObject *);
		PyTuple_SET_ITEM(af->argfactor, i, arg);
		Py_INCREF(arg);
	}
	for (i = 0; i < intargs; i++)
		iargs[i] = va_arg(va, int);
	va_end(va);

	return (PyObject *)af;
}

static void
anonfunc_dealloc(anonfuncobject *af)
{
	PyObject_GC_UnTrack(af);
	Py_XDECREF(af->argfactor);
	Py_XDECREF(af->argdefault);
	if (af->argext != NULL)
		PyMem_Free(af->argext);
	af->ob_type->tp_free(af);
}

static int
anonfunc_traverse(anonfuncobject *af, visitproc visit, void *arg)
{
	int err;

	if (af->argfactor != NULL) {
		err = visit(af->argfactor, arg);
		if (err)
			return err;
	}
	if (af->argdefault != NULL) {
		err = visit(af->argdefault, arg);
		if (err)
			return err;
	}
	return 0;
}

static PyObject *
anonfunc_call_leafnode(anonfuncobject *af, PyObject *args, PyObject *kw)
{
	assert(af->argfactor != NULL);
	if (PyInt_Check(af->argfactor)) {
		int argidx;

		/* leaf node with argument index */
		argidx = PyInt_AS_LONG(af->argfactor);
		assert(PyTuple_Check(args));
		if (argidx < PyTuple_GET_SIZE(args)) {
			PyObject *arg;

			arg = PyTuple_GET_ITEM(args, argidx);
			Py_XINCREF(arg);
			return arg;
		}
		else if (af->argdefault != NULL) {
			Py_INCREF(af->argdefault);
			return af->argdefault;
		}
		else {
			PyErr_Format(PyExc_TypeError,
				     "argument %d is required.",
				     argidx);
			return NULL;
		}
	}
	else {
		assert(PyString_Check(af->argfactor));
		/* leaf node with keyword argument name */
		if (kw != NULL && PyDict_Check(kw)) {
			PyObject *arg;

			arg = PyDict_GetItem(kw, af->argfactor);
			if (arg != NULL) {
				Py_INCREF(arg);
				return arg;
			}
		}

		if (af->argdefault != NULL) {
			Py_INCREF(af->argdefault);
			return af->argdefault;
		}
		else {
			assert(PyString_Check(af->argfactor));
			PyErr_Format(PyExc_TypeError,
				"Keyword argument '%s' is required.",
				PyString_AS_STRING(af->argfactor));
			return NULL;
		}
	}
}

static PyObject *
anonfunc_call_opnode(anonfuncobject *af, PyObject *args, PyObject *kw)
{
	PyObject *realval, *r;
	int i, objargs, intargs, *iargs;

	assert(af->nodetype != AFTYPE_LEAF && af->nodetype < AFTYPE_MAX);
	objargs = anonfunc_typeobjargs[af->nodetype];
	intargs = anonfunc_typeintargs[af->nodetype];
	assert(af->argfactor != NULL);
	assert(PyTuple_Size(af->argfactor) == objargs);
	assert(intargs == 0 || af->argext != NULL);

	iargs = af->argext;
	realval = PyTuple_New(PyTuple_GET_SIZE(af->argfactor));
	if (realval == NULL)
		return NULL;

	/* get real values for arguments */
	for (i = 0; i < objargs; i++) {
		PyObject *ob;

		ob = PyTuple_GET_ITEM(af->argfactor, i);
		if (ob->ob_type == &anonfunc_type) {
			PyObject *elem;

			elem = PyObject_Call(ob, args, kw);
			if (elem == NULL) {
				Py_DECREF(realval);
				return NULL;
			}
			PyTuple_SET_ITEM(realval, i, elem);
		}
		else {
			PyTuple_SET_ITEM(realval, i, ob);
			Py_INCREF(ob);
		}
	}

	/* then, call the real operator. */
	switch (af->nodetype) {
	case AFTYPE_UNARY:
		r = ((unaryfunc)af->operator)(PyTuple_GET_ITEM(realval, 0));
		break;
	case AFTYPE_BINARY:
		r = ((binaryfunc)af->operator)(PyTuple_GET_ITEM(realval, 0),
					       PyTuple_GET_ITEM(realval, 1));
		break;
	case AFTYPE_TERNARY:
		r = ((ternaryfunc)af->operator)(PyTuple_GET_ITEM(realval, 0),
						PyTuple_GET_ITEM(realval, 1),
						PyTuple_GET_ITEM(realval, 2));
		break;
	case AFTYPE_INTARG:
		r = ((intargfunc)af->operator)(PyTuple_GET_ITEM(realval, 0),
					       iargs[0]);
		break;
	case AFTYPE_INTINTARG:
		r = ((intintargfunc)af->operator)(PyTuple_GET_ITEM(realval, 0),
						  iargs[0], iargs[1]);
		break;
	case AFTYPE_RICHCMP:
		r = ((richcmpfunc)af->operator)(PyTuple_GET_ITEM(realval, 0),
						PyTuple_GET_ITEM(realval, 1),
						iargs[0]);
		break;
	default:
		assert(0);
		return NULL;
	}
	Py_DECREF(realval);
	return r;
}

static PyObject *
anonfunc_call(anonfuncobject *af, PyObject *args, PyObject *kw)
{
	if (af->nodetype == AFTYPE_LEAF)
		return anonfunc_call_leafnode(af, args, kw);
	else
		return anonfunc_call_opnode(af, args, kw);
}

#define AF_UNARY(op, func) static PyObject * \
	anonfunc_##op(PyObject *o) \
	{ return anonfunc_new_ops(func, AFTYPE_UNARY, o); }
#define AF_BINARY(op, func) static PyObject * \
	anonfunc_##op(PyObject *o1, PyObject *o2) \
	{ return anonfunc_new_ops(func, AFTYPE_BINARY, o1, o2); }
#define AF_TERNARY(op, func) static PyObject * \
	anonfunc_##op(PyObject *o1, PyObject *o2, PyObject *o3) \
	{ return anonfunc_new_ops(func, AFTYPE_TERNARY, o1, o2, o3); }
#define AF_INTARG(op, func) static PyObject * \
	anonfunc_##op(PyObject *o, int v) \
	{ return anonfunc_new_ops(func, AFTYPE_INTARG, o, v); }
#define AF_INTINTARG(op, func) static PyObject * \
	anonfunc_##op(PyObject *o, int v, int w) \
	{ return anonfunc_new_ops(func, AFTYPE_INTINTARG, o, v, w); }
#define AF_RICHCMP(op, func) static PyObject * \
	anonfunc_##op(PyObject *o1, PyObject *o2, int v) \
	{ return anonfunc_new_ops(func, AFTYPE_RICHCMP, o1, o2, v); }
/* number */
AF_BINARY	(add,		PyNumber_Add)
AF_BINARY	(sub,		PyNumber_Subtract)
AF_BINARY	(mul,		PyNumber_Multiply)
AF_BINARY	(classic_div,	PyNumber_Divide)
AF_BINARY	(mod,		PyNumber_Remainder)
AF_BINARY	(divmod,	PyNumber_Divmod)
AF_TERNARY	(pow,		PyNumber_Power)
AF_UNARY	(neg,		PyNumber_Negative)
AF_UNARY	(pos,		PyNumber_Positive)
AF_UNARY	(abs,		PyNumber_Absolute)
AF_UNARY	(invert,	PyNumber_Invert)
AF_BINARY	(lshift,	PyNumber_Lshift)
AF_BINARY	(rshift,	PyNumber_Rshift)
AF_BINARY	(and,		PyNumber_And)
AF_BINARY	(xor,		PyNumber_Xor)
AF_BINARY	(or,		PyNumber_Or)
AF_UNARY	(int,		PyNumber_Int)
AF_UNARY	(long,		PyNumber_Long)
AF_UNARY	(float,		PyNumber_Float)
AF_BINARY	(floor_div,	PyNumber_FloorDivide)
AF_BINARY	(true_divide,	PyNumber_TrueDivide)
/* squence */
AF_BINARY	(concat,	PySequence_Concat)
AF_INTARG	(item,		PySequence_GetItem)
AF_INTINTARG	(slice,		PySequence_GetSlice)
/* mapping */
AF_BINARY	(subscript,	PyObject_GetItem)
/* object */
AF_BINARY	(getattr,	PyObject_GetAttr)
AF_RICHCMP	(richcompare,	PyObject_RichCompare)
#undef AF_UNARY
#undef AF_BINARY
#undef AF_TERNARY
#undef AF_INT
#undef AF_INTINT

static PyNumberMethods anonfunc_as_number = {
	(binaryfunc)anonfunc_add,	/* nb_add */
	(binaryfunc)anonfunc_sub,	/* nb_subtract */
	(binaryfunc)anonfunc_mul,	/* nb_multiply */
	(binaryfunc)anonfunc_classic_div, /* nb_divide */
	(binaryfunc)anonfunc_mod,	/* nb_remainder */
	(binaryfunc)anonfunc_divmod,	/* nb_divmod */
	(ternaryfunc)anonfunc_pow,	/* nb_power */
	(unaryfunc)anonfunc_neg,	/* nb_negative */
	(unaryfunc)anonfunc_pos,	/* nb_positive */
	(unaryfunc)anonfunc_abs,	/* nb_absolute */
	0,				/* nb_nonzero */
	(unaryfunc)anonfunc_invert,	/* nb_invert */
	(binaryfunc)anonfunc_lshift,	/* nb_lshift */
	(binaryfunc)anonfunc_rshift,	/* nb_rshift */
	(binaryfunc)anonfunc_and,	/* nb_and */
	(binaryfunc)anonfunc_xor,	/* nb_xor */
	(binaryfunc)anonfunc_or,	/* nb_or */
	0,				/* nb_coerce */
	(unaryfunc)anonfunc_int,	/* nb_int */
	(unaryfunc)anonfunc_long,	/* nb_long */
	(unaryfunc)anonfunc_float,	/* nb_float */
	0,				/* nb_oct */
	0,				/* nb_hex */
	0,				/* nb_inplace_add */
	0,				/* nb_inplace_subtract */
	0,				/* nb_inplace_multiply */
	0,				/* nb_inplace_divide */
	0,				/* nb_inplace_remainder */
	0,				/* nb_inplace_power */
	0,				/* nb_inplace_lshift */
	0,				/* nb_inplace_rshift */
	0,				/* nb_inplace_and */
	0,				/* nb_inplace_xor */
	0,				/* nb_inplace_or */
	(binaryfunc)anonfunc_floor_div,	/* nb_floor_divide */
	(binaryfunc)anonfunc_true_divide, /* nb_true_divide */
	0,				/* nb_inplace_floor_divide */
	0,				/* nb_inplace_true_divide */
};

static PySequenceMethods anonfunc_as_sequence = {
        0,				/* sq_length */
        (binaryfunc)anonfunc_concat,	/* sq_concat */
        0,				/* sq_repeat */
        (intargfunc)anonfunc_item,	/* sq_item */
        (intintargfunc)anonfunc_slice,	/* sq_slice */
        0,				/* sq_ass_item */
        0,				/* sq_ass_slice */
        0,				/* sq_contains */
        0,				/* sq_inplace_concat */
        0,				/* sq_inplace_repeat */
};

static PyMappingMethods anonfunc_as_mapping = {
	0, 				/* mp_length */
	(binaryfunc)anonfunc_subscript,	/* mp_subscript */
	0,				/* mp_ass_subscript */
};


PyDoc_STRVAR(anonfunc_doc,
"anonfunc(key[, default]) --> anonfunc object\n\
\n\
Return a proxy object that creates another proxy objects when\n\
they is called by some standard operators. The created objects\n\
can be used like anonymous functions. If key is a integer, the\n\
proxy function get the n'th argument. And if key is a string, it\n\
will get keyword argument named value of key. If default is\n\
specified and the argument isn't available when it is called,\n\
default is used. Unlike lambda, anonfunc can't work for complete\n\
set of python operators because its implementation relies on\n\
operator overloading. Allowed operators are:\n\
\t+ - * / // ** ~ << >> & ^ | int long float []\n\
\t< <= > >= == != .(getting attribute)");

static PyTypeObject anonfunc_type = {
	PyObject_HEAD_INIT(NULL)
	0,				/* ob_size */
	"operator.anonfunc",		/* tp_name */
	sizeof(anonfuncobject),		/* tp_basicsize */
	0,				/* tp_itemsize */
	/* methods */
	(destructor)anonfunc_dealloc,	/* tp_dealloc */
	0,				/* tp_print */
	0,				/* tp_getattr */
	0,				/* tp_setattr */
	0,				/* tp_compare */
	0,				/* tp_repr */
	&anonfunc_as_number,		/* tp_as_number */
	&anonfunc_as_sequence,		/* tp_as_sequence */
	&anonfunc_as_mapping,		/* tp_as_mapping */
	0,				/* tp_hash */
	(ternaryfunc)&anonfunc_call,	/* tp_call */
	0,				/* tp_str */
	(binaryfunc)anonfunc_getattr,	/* tp_getattro */
	0,				/* tp_setattro */
	0,				/* tp_as_buffer */
	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
		Py_TPFLAGS_CHECKTYPES,	/* tp_flags */
	anonfunc_doc,			/* tp_doc */
	(traverseproc)anonfunc_traverse, /* tp_traverse */
	0,				/* tp_clear */
	(richcmpfunc)anonfunc_richcompare, /* tp_richcompare */
	0,				/* tp_weaklistoffset */
	0,				/* tp_iter */
	0,				/* tp_iternext */
	0,				/* tp_methods */
	0,				/* tp_members */
	0,				/* tp_getset */
	0,				/* tp_base */
	0,				/* tp_dict */
	0,				/* tp_descr_get */
	0,				/* tp_descr_set */
	0,				/* tp_dictoffset */
	0,				/* tp_init */
	0,				/* tp_alloc */
	anonfunc_new,			/* tp_new */
	PyObject_GC_Del,		/* tp_free */
};

static int
anonfunc_setmodulevars(PyObject *mod)
{
	PyObject *argkey, *arginst;
	char vname[sizeof("arg") + 2];
	int i;

	for (i = 0; i < 3; i++) {
		argkey = PyInt_FromLong(i);
		if (argkey == NULL)
			return -1;
		arginst = (PyObject *)_anonfunc_new_internal(&anonfunc_type,
					AFTYPE_LEAF, NULL, argkey, NULL); 
		Py_DECREF(argkey);
		if (arginst == NULL)
			return -1;

		sprintf(vname, "arg%d", i);
		PyModule_AddObject(mod, vname, arginst);
	}

	return 0;
}

static struct PyMethodDef anonfunc_methods[] = {
	{NULL, NULL},
};

/* Initialization function for the module (*must* be called initoperator) */

PyMODINIT_FUNC
initanonfunc(void)
{
	PyObject *m;
        
	/* Create the module and add the functions */
        m = Py_InitModule("anonfunc", anonfunc_methods);

	if (PyType_Ready(&anonfunc_type) < 0)
		return;
	Py_INCREF(&anonfunc_type);
	PyModule_AddObject(m, "anonfunc", (PyObject *)&anonfunc_type);
	anonfunc_setmodulevars(m);
}

/*
 * ex: ts=8 sts=8 sw=8 noet
 * $Perky: anonfunc/anonfunc.c,v 1.1.1.1 2004/01/03 12:55:55 perky Exp $
 */
