/* * 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"}, {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_getstate(PyObject *self, PyObject *noargs) { PyObject *res, *part; PyRPNExpr_state_t resbuf; PyRPNExpr_t *expr_self; size_t total_sz; 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) { if(!(part=PyBytes_FromStringAndSize( (char*)expr_self->rpn->toks.tokens, sizeof(rpn_token_t) * resbuf.token_sz))) { return NULL; } 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; } 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; }