Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

python extension for tinyexpr #118

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Cython~=3.0.11
11 changes: 11 additions & 0 deletions python/setup.py
Original file line number Diff line number Diff line change
@@ -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))
1 change: 1 addition & 0 deletions python/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python3 setup.py build_ext --inplace
25 changes: 25 additions & 0 deletions python/test.py
Original file line number Diff line number Diff line change
@@ -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]()

37 changes: 37 additions & 0 deletions python/tinyexpy.pxd
Original file line number Diff line number Diff line change
@@ -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)
82 changes: 82 additions & 0 deletions python/tinyexpy.pyx
Original file line number Diff line number Diff line change
@@ -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=<double (*)(double, double)>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