/*
* Copyright (C) 2020 Weber Yann
*
* This file is part of pyrpn.
*
* pyrpn is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* pyrpn is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with pyrpn. If not, see .
*/
#include "python_rpnexpr.h"
PyMethodDef RPNExpr_methods[] = {
{"eval", (PyCFunction)rpnexpr_eval, METH_FASTCALL, "Evaluate an expression"},
{"reset_stack", (PyCFunction)rpnexpr_reset_stack, METH_NOARGS,
"Reset stack memory storage (set all items to 0)"},
{"__getstate__", (PyCFunction)rpnexpr_getstate, METH_NOARGS,
"Pickling method. Return a bytes repr of tokenized expression \
and the stack state."},
{"__setstate__", (PyCFunction)rpnexpr_setstate, METH_O,
"Unpickling method"},
{"uid", (PyCFunction)rpnexpr_getexprstate, METH_NOARGS,
"Return a base64 uid for expression"},
{NULL} //Sentinel
};
PyMemberDef RPNExpr_members[] = {
{NULL}
};
PyTypeObject RPNExprType = {
PyVarObject_HEAD_INIT(NULL, 0)
"pyrpn.RPNExpr", /* tp_name */
sizeof(PyRPNExpr_t), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)rpnexpr_del, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_reserved */
rpnexpr_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
rpnexpr_str, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT |
Py_TPFLAGS_BASETYPE, /* tp_flags */
"RPN expression evaluator", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
RPNExpr_methods, /* tp_methods */
RPNExpr_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
rpnexpr_init, /* tp_init */
0, /* tp_alloc */
rpnexpr_new, /* tp_new */
};
PyObject* rpnexpr_new(PyTypeObject *subtype, PyObject *args, PyObject* kwds)
{
PyObject *ret, *err;
PyRPNExpr_t *expr;
ret = PyType_GenericNew(subtype, args, kwds);
if((err = PyErr_Occurred()))
{
Py_DECREF(err);
return ret;
}
expr = (PyRPNExpr_t*)ret;
expr->rpn = NULL;
expr->args = NULL;
return ret;
}
int rpnexpr_init(PyObject *self, PyObject *args, PyObject *kwds)
{
PyRPNExpr_t *expr_self;
char *names[] = {"expression", "args_count", "stack_size", NULL};
char err_str[256];
const char *expr;
long long int args_count, stack_size;
expr_self = (PyRPNExpr_t*)self;
stack_size = 16;
expr_self->rpn = NULL;
if(!PyArg_ParseTupleAndKeywords(args, kwds, "sL|L:RPNExpr.__init__", names, &expr,
&args_count, &stack_size))
{
return -1;
}
if(strlen(expr) == 0)
{
PyErr_SetString(PyExc_ValueError,
"RpnExpr.__init__() expect expression argument to be not empty");
return -1;
}
if(args_count < 0 || args_count > 255)
{
snprintf(err_str, 128,
"Argument count should be in [4..255] but %lld given",
args_count);
PyErr_SetString(PyExc_ValueError, err_str);
return -1;
}
if(stack_size < 4 || stack_size > 255)
{
snprintf(err_str, 128,
"Stack size should be in [0..255] but %lld given",
stack_size);
PyErr_SetString(PyExc_ValueError, err_str);
return -1;
}
expr_self->rpn = malloc(sizeof(rpn_expr_t));
if(!expr_self->rpn)
{
snprintf(err_str, 256,
"Expression memory allocation error : %s",
strerror(errno));
}
bzero(expr_self->rpn, sizeof(rpn_expr_t));
if(rpn_expr_init(expr_self->rpn, stack_size, args_count) < 0)
{
snprintf(err_str, 256,
"Expression init error : %s",
expr_self->rpn->err_reason);
PyErr_SetString(PyExc_ValueError, err_str);
return -1;
}
expr_self->args = malloc(sizeof(rpn_value_t) * args_count);
if(!expr_self->args)
{
snprintf(err_str, 256,
"Error allocating arguments memory : %s",
strerror(errno));
return -1;
}
if(rpn_expr_compile(expr_self->rpn, expr))
{
PyErr_SetString(PyExc_ValueError, expr_self->rpn->err_reason);
return -1;
}
return 0;
}
void rpnexpr_del(PyObject *self)
{
PyRPNExpr_t *expr_self;
expr_self = (PyRPNExpr_t*)self;
if(expr_self->rpn)
{
rpn_expr_close(expr_self->rpn);
free(expr_self->rpn);
expr_self->rpn = NULL;
}
if(expr_self->args)
{
free(expr_self->args);
expr_self->args = NULL;
}
}
PyObject* rpnexpr_getexprstate(PyObject *self, PyObject *noargs)
{
/*
PyObject *base64_mod, *base64_encode, *gzip, *compress;
PyObject *bytes_repr, *comp, *res;
if(!(base64_mod = PyImport_ImportModule("base64")))
{
return NULL;
}
if(!(base64_encode = PyObject_GetAttrString(base64_mod, "b64encode")))
{
return NULL;
}
if(!(gzip = PyImport_ImportModule("gzip")))
{
return NULL;
}
if(!(compress = PyObject_GetAttrString(gzip, "compress")))
{
return NULL;
}
bytes_repr = _rpnexpr_getstate(self, noargs, 0);
res = PyObject_CallOneArg(base64_encode, bytes_repr);
Py_DECREF(bytes_repr);
return res;
comp = PyObject_CallOneArg(compress, bytes_repr);
Py_DECREF(bytes_repr);
res = PyObject_CallOneArg(base64_encode, comp);
Py_DECREF(comp);
return res;
*/
return NULL;
}
PyObject* rpnexpr_getstate(PyObject *self, PyObject *noargs)
{
PyObject *res, *part;
PyRPNExpr_state_t resbuf;
PyRPNExpr_t *expr_self;
size_t total_sz, i;
char err_str[128];
expr_self = (PyRPNExpr_t*)self;
if(expr_self->rpn->state != RPN_READY)
{
snprintf(err_str, 128,
"RPNExpr.__getstate__() instance in bad state : %s",
expr_self->rpn->state==RPN_ERROR?"error":"init");
PyErr_SetString(PyExc_ValueError, err_str);
return NULL;
}
total_sz = sizeof(PyRPNExpr_state_t);
total_sz += expr_self->rpn->toks.tokens_sz * sizeof(rpn_token_t);
total_sz += expr_self->rpn->stack_sz * sizeof(rpn_value_t);
resbuf.total_sz = total_sz;
resbuf.argc = expr_self->rpn->args_count;
resbuf.stack_sz = expr_self->rpn->stack_sz;
resbuf.token_sz = expr_self->rpn->toks.tokens_sz;
if(!(res = PyBytes_FromStringAndSize((char*)&resbuf, sizeof(resbuf))))
{
return NULL;
}
if(resbuf.stack_sz)
{
if(!(part=PyBytes_FromStringAndSize(
(char*)expr_self->rpn->stack,
sizeof(rpn_value_t) * resbuf.stack_sz)))
{
return NULL;
}
PyBytes_ConcatAndDel(&res, part);
if(!res)
{
return NULL;
}
}
if(resbuf.token_sz)
{
for(i=0; irpn->toks.tokens_sz;i++)
{
// restore op pointers
expr_self->rpn->toks.tokens[i].op = NULL;
}
if(!(part=PyBytes_FromStringAndSize(
(char*)expr_self->rpn->toks.tokens,
sizeof(rpn_token_t) * resbuf.token_sz)))
{
return NULL;
}
for(i=0; irpn->toks.tokens_sz;i++)
{
// restore op pointers
expr_self->rpn->toks.tokens[i].op = &(rpn_ops[expr_self->rpn->toks.tokens[i].op_n]);
}
PyBytes_ConcatAndDel(&res, part);
if(!res)
{
return NULL;
}
}
return res;
}
PyObject* rpnexpr_setstate(PyObject *self, PyObject *state_bytes)
{
PyObject *tmp, *tmp2;
PyRPNExpr_state_t *state;
PyRPNExpr_t *expr_self;
rpn_value_t *stack;
rpn_tokenized_t toks;
const char *data;
size_t bsize, csize;
int err;
char err_str[256];
size_t i;
expr_self = (PyRPNExpr_t*)self;
if(!PyBytes_Check(state_bytes)) /* Arg check */
{
tmp2 = NULL;
tmp = PyObject_Type(state_bytes);
if(tmp)
{
tmp2 = PyObject_Str(tmp);
}
if(tmp2)
{
snprintf(err_str, 128,
"RPNExpr.__setstate__() expected a bytes as \
argument but %s found",
PyUnicode_AsUTF8(tmp2));
PyErr_SetString(PyExc_ValueError, err_str);
}
else
{
PyErr_SetString(PyExc_RuntimeError,
"Failing to fetch arguments type will \
generating exception message !");
}
return NULL;
}
if(expr_self->rpn || expr_self->args) /* checking instance state */
{
PyErr_SetString(PyExc_ValueError,
"RPNExpr.__getstate__() instance in bad state : \
should not be initialized");
return NULL;
}
/* Checking data size */
bsize = PyBytes_GET_SIZE(state_bytes);
data = PyBytes_AS_STRING(state_bytes);
state = (PyRPNExpr_state_t*)data;
if(bsize < sizeof(size_t))
{
PyErr_SetString(PyExc_ValueError, "Invalid argument");
}
if(bsize != state->total_sz)
{
snprintf(err_str, 128,
"RPNExpr.__setstate__() error : expected state to \
contains %ld bytes but %ld found",
state->total_sz, bsize);
PyErr_SetString(PyExc_ValueError, err_str);
}
/* Checking data "integrity" */
csize = sizeof(PyRPNExpr_state_t) +
state->token_sz * sizeof(rpn_token_t) +
state->stack_sz * sizeof(rpn_value_t);
if(state->total_sz != csize)
{
snprintf(err_str, 128,
"RPNExpr.__setstate__() error : should have %ld as \
total size but %ld found",
csize, state->total_sz);
PyErr_SetString(PyExc_ValueError, err_str);
return NULL;
}
/* Alloc and init rpn expression & args buffer*/
if(!(expr_self->rpn = malloc(sizeof(rpn_expr_t))))
{
err = errno;
snprintf(err_str, 128,
"RPNExpr.__setstate__() failed to allocate memory for \
rpn_expr_t : %s",
strerror(err));
PyErr_SetString(PyExc_MemoryError, err_str);
return NULL;
}
bzero(expr_self->rpn, sizeof(rpn_expr_t));
if(!(expr_self->args = malloc(sizeof(rpn_value_t)*state->argc)))
{
err = errno;
snprintf(err_str, 128,
"RPNExpr.__setstate__() failed to allocate memory for \
args buffer : %s",
strerror(err));
PyErr_SetString(PyExc_MemoryError, err_str);
return NULL;
}
if(rpn_expr_init(expr_self->rpn, state->stack_sz, state->argc) < 0)
{
snprintf(err_str, 256,
"RPNExpr.__setstate__() failed to init RPN expression \
: %s",
expr_self->rpn->err_reason);
PyErr_SetString(PyExc_RuntimeError, err_str);
goto free_err;
}
/* restore stack & untokenize expression */
stack = (rpn_value_t*)(state+1);
memcpy(expr_self->rpn->stack, stack,
sizeof(rpn_value_t)*state->stack_sz);
toks.argc = state->argc;
toks.tokens_sz = state->token_sz;
toks.tokens = malloc(sizeof(rpn_token_t)*state->token_sz);
if(!toks.tokens)
{
snprintf(err_str, 128,
"RPNExpr.__setstate__() failed to allocate tokens \
: %s",
strerror(errno));
PyErr_SetString(PyExc_RuntimeError, err_str);
goto close_err;
}
memcpy(toks.tokens, (rpn_token_t*)(stack+state->stack_sz),
sizeof(rpn_token_t)*state->token_sz);
for(i=0; irpn, &toks, 0) < 0)
{
snprintf(err_str, 256,
"RPNExpr.__setstate__() unable to untokenize : %s",
expr_self->rpn->err_reason);
PyErr_SetString(PyExc_RuntimeError, err_str);
goto close_err;
}
expr_self->rpn->toks = toks;
Py_RETURN_NONE;
close_err:
rpn_expr_close(expr_self->rpn);
free_err:
return NULL;
}
PyObject* rpnexpr_eval(PyObject* self, PyObject** argv, Py_ssize_t argc)
{
PyRPNExpr_t *expr_self;
unsigned long res;
char err_str[128];
Py_ssize_t i;
PyObject *cur, *ret;
expr_self = (PyRPNExpr_t*)self;
if((unsigned long)argc != expr_self->rpn->args_count)
{
snprintf(err_str, 128,
"RPNExpr expected %ld arguments but %ld given",
expr_self->rpn->args_count,
argc);
PyErr_SetString(PyExc_ValueError, err_str);
return NULL;
}
for(i=0; iargs[i] = PyLong_AsUnsignedLong(argv[i]);
if((ret = PyErr_Occurred()))
{
Py_DECREF(ret);
return NULL;
}
}
res = rpn_expr_eval(expr_self->rpn, expr_self->args);
//dprintf(2, "[RES=%lu]\n", res);
return PyLong_FromUnsignedLong(res);
}
PyObject* rpnexpr_reset_stack(PyObject *self, PyObject *noargs)
{
rpn_expr_reset_stack(((PyRPNExpr_t*)self)->rpn);
Py_RETURN_NONE;
}
PyObject* rpnexpr_str(PyObject *self)
{
PyRPNExpr_t *expr_self;
PyObject *res;
expr_self = (PyRPNExpr_t*)self;
res = Py_BuildValue("s", expr_self->rpn->expr);
return res;
}
PyObject* rpnexpr_repr(PyObject *self)
{
PyRPNExpr_t *expr_self;
PyObject *res;
size_t sz;
char *buff, err_str[128];
expr_self = (PyRPNExpr_t*)self;
sz = snprintf(NULL, 0, "",
expr_self->rpn->args_count, expr_self->rpn->stack_sz,
expr_self->rpn->expr);
buff = malloc(sizeof(char) * (sz + 1));
if(!buff)
{
snprintf(err_str, 128,
"Error allocating repr : %s",
strerror(errno));
PyErr_SetString(PyExc_RuntimeError, err_str);
return NULL;
}
snprintf(buff, sz+1, "",
expr_self->rpn->args_count, expr_self->rpn->stack_sz,
expr_self->rpn->expr);
res = Py_BuildValue("s", buff);
free(buff);
return res;
}