diff --git a/python/requirements.txt b/python/requirements.txt new file mode 100644 index 0000000..b677f06 --- /dev/null +++ b/python/requirements.txt @@ -0,0 +1 @@ +Cython~=3.0.11 diff --git a/python/setup.py b/python/setup.py new file mode 100644 index 0000000..d736ded --- /dev/null +++ b/python/setup.py @@ -0,0 +1,11 @@ +from distutils.core import setup, Extension +from Cython.Build import cythonize + + +ext = [Extension("tinyexpy", + sources=["tinyexpy.pyx", "../tinyexpr.c"], + include_dirs=['../'], + depends=["../tinyexpr.h"], + )] + +setup(name="tinyexpy", ext_modules=cythonize(ext, language_level=3)) diff --git a/python/setup.sh b/python/setup.sh new file mode 100644 index 0000000..d447b42 --- /dev/null +++ b/python/setup.sh @@ -0,0 +1 @@ +python3 setup.py build_ext --inplace diff --git a/python/test.py b/python/test.py new file mode 100644 index 0000000..feaf2f2 --- /dev/null +++ b/python/test.py @@ -0,0 +1,25 @@ +from tinyexpy import Tinyexpy + + +def test_py_te_interp(): + T = Tinyexpy() + return T.py_te_interp(expression="3+3", error=0) + + +def test_py_te_interp_customize(): + T = Tinyexpy() + return T.py_te_interp_customize(expression="x+1+z", + py_vars=[{"name": "x", "value": 2.0}, {"name": "y", "value": 5}, + {"name": "z", "value": 7}]) + + +def test_te_interp_my_func(): + T = Tinyexpy() + return T.py_te_interp_my_func("my_add", "my_add(5, 10)") + + +if __name__ == '__main__': + for i in dir(): + if i.startswith('test'): + globals()[i]() + diff --git a/python/tinyexpy.pxd b/python/tinyexpy.pxd new file mode 100644 index 0000000..9720850 --- /dev/null +++ b/python/tinyexpy.pxd @@ -0,0 +1,37 @@ +ctypedef union te_expr_u: + double value + const double *bound + const void *function + +ctypedef struct te_expr: + int type + te_expr_u u + void *parameters[1] + +ctypedef struct te_variable: + const char *name + const void *address + int type + void *context + +cdef enum: + TE_VARIABLE = 0, + TE_FUNCTION0 = 8, TE_FUNCTION1, TE_FUNCTION2, TE_FUNCTION3, + TE_FUNCTION4, TE_FUNCTION5, TE_FUNCTION6, TE_FUNCTION7, + TE_CLOSURE0 = 16, TE_CLOSURE1, TE_CLOSURE2, TE_CLOSURE3, + TE_CLOSURE4, TE_CLOSURE5, TE_CLOSURE6, TE_CLOSURE7, + TE_FLAG_PURE = 32 + + +cdef extern from "tinyexpr.h" nogil: + + + double te_interp(const char *expression, int *error) + + te_expr *te_compile(const char *expression, const te_variable *variables, int var_count, int *error) + + double te_eval(const te_expr *n) + + void te_print(const te_expr *n) + + void te_free(te_expr *n) diff --git a/python/tinyexpy.pyx b/python/tinyexpy.pyx new file mode 100644 index 0000000..56f7d1e --- /dev/null +++ b/python/tinyexpy.pyx @@ -0,0 +1,82 @@ +cimport tinyexpy +from libc cimport stdlib +from typing import List + +DEF SIZE_VARS = 100 + + +cdef class Tinyexpy: + + @staticmethod + cdef double add(double a, double b): + return a + b + + + def py_te_interp(self, expression: str, error: int) -> float: + py_expression = expression.encode("utf8") + cdef int c_error = error + cdef char *c_expression = py_expression + cdef double res = te_interp(c_expression, &c_error) + print(f"Evaluating:\n\t{c_expression.decode('utf8')}\nResult:\n\t{res}\n") + return res + + + def py_te_interp_customize(self, expression: str, py_vars: List[dict]): + cdef char *te_variable_name + cdef char *c_expression + cdef int var_count = len(py_vars) + cdef int c_err + cdef te_expr *n + cdef double[SIZE_VARS] x + cdef double r + cdef te_variable c_te_variable + cdef te_variable[SIZE_VARS] vars + py_expression = expression.encode("utf8") + c_expression = py_expression + for i, each_py_vars in enumerate(py_vars): + each_value = each_py_vars.get("value") + if isinstance(each_value, float) or isinstance(each_value, int): + each_name = each_py_vars.get("name").encode("utf8") + te_variable_name = each_name + x[i] = float(each_value) + c_te_variable = te_variable(te_variable_name, &x[i]) + vars[i] = c_te_variable + n = te_compile(c_expression, vars, var_count, &c_err) + if n != NULL: + # te_print(n) + r = te_eval(n) + te_free(n) + print(f"Evaluating:\n\t{c_expression.decode('utf8')}\nResult:\n\t{r}\n") + return r + + + + def py_te_interp_my_func(self, func_name: str, expression: str): + cdef te_variable[1] vars + cdef char *c_expression + cdef char *myfunc + cdef int c_err + cdef te_expr *n + cdef double r + py_expression = expression.encode("utf8") + py_func_name = func_name.encode("utf8") + c_expression = py_expression + myfunc = py_func_name + vars[0] = te_variable(name=myfunc, address=self.add, type=TE_FUNCTION2) + n = te_compile(c_expression, vars, 1, &c_err) + if n != NULL: + # te_print(n) + r = te_eval(n) + te_free(n) + print(f"Evaluating:\n\t{c_expression.decode('utf8')}\nResult:\n\t{r}\n") + return r + + + + + + + + + +